mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-03-07 06:09:42 +00:00
Compare commits
1719 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
deca217544 | ||
|
|
db8d93d308 | ||
|
|
4c96106666 | ||
|
|
8e849ea6f8 | ||
|
|
82d80d2ead | ||
|
|
a5da55d0f7 | ||
|
|
702e6c8080 | ||
|
|
46e957ab7e | ||
|
|
c27f8a5c1e | ||
|
|
8e835f2f6b | ||
|
|
35e8a027ad | ||
|
|
94642f9066 | ||
|
|
2dd5de4373 | ||
|
|
793e80490c | ||
|
|
727a1f4ddd | ||
|
|
6a752a56ff | ||
|
|
32466e3804 | ||
|
|
a530503c0c | ||
|
|
7ba4af7e2e | ||
|
|
c9c05ad2a8 | ||
|
|
ab1df3a1d0 | ||
|
|
56a60772a4 | ||
|
|
63e6731207 | ||
|
|
8b53ded53a | ||
|
|
d5075d706c | ||
|
|
0d88b8012b | ||
|
|
a583c21136 | ||
|
|
b8ec63cf8c | ||
|
|
4b9afdf53a | ||
|
|
788d1650a2 | ||
|
|
8f58886a21 | ||
|
|
94b3bb2391 | ||
|
|
5698ff9c4c | ||
|
|
47b562b032 | ||
|
|
69234db848 | ||
|
|
ebc132ea65 | ||
|
|
0899eeddc0 | ||
|
|
1062776762 | ||
|
|
b1aeae6772 | ||
|
|
5a6bd38d22 | ||
|
|
97da8e2f2e | ||
|
|
10be150503 | ||
|
|
36aa248556 | ||
|
|
183c22cc84 | ||
|
|
63f4cf3d07 | ||
|
|
061720bcf0 | ||
|
|
11585327bf | ||
|
|
2793eeb10a | ||
|
|
8fb093b272 | ||
|
|
3d1a7f173c | ||
|
|
ba078f3ff5 | ||
|
|
6a1049bfb7 | ||
|
|
a4112ebed2 | ||
|
|
3b9b827ebf | ||
|
|
abcf030181 | ||
|
|
22a16da09e | ||
|
|
61e6a03d70 | ||
|
|
3f2119556f | ||
|
|
77493d0d09 | ||
|
|
340c73cbdf | ||
|
|
ea7f4447b2 | ||
|
|
19f3c75a8d | ||
|
|
25ba08abcf | ||
|
|
26be0c7c82 | ||
|
|
b7c5e3b5d5 | ||
|
|
701653a6bd | ||
|
|
2c099c7f0e | ||
|
|
fba53117d8 | ||
|
|
e1bf53d90a | ||
|
|
ecc82739d8 | ||
|
|
d16afa9692 | ||
|
|
9f3ce09e88 | ||
|
|
cbfb1edb79 | ||
|
|
b6c336bf72 | ||
|
|
403e34506e | ||
|
|
83db868542 | ||
|
|
1b6e673b50 | ||
|
|
a0144f093f | ||
|
|
a5d84bf8a9 | ||
|
|
84bb740e62 | ||
|
|
d37482ada1 | ||
|
|
75fc8202ab | ||
|
|
24aff15752 | ||
|
|
338b9928f0 | ||
|
|
b9607b4b8e | ||
|
|
586f241074 | ||
|
|
e298987d9e | ||
|
|
71cc4b5bf2 | ||
|
|
4fdd9feddc | ||
|
|
caf7da1053 | ||
|
|
7d04ba0fc3 | ||
|
|
d98d091c43 | ||
|
|
3ad196c4c7 | ||
|
|
562f320198 | ||
|
|
3e5581e094 | ||
|
|
c4721e1020 | ||
|
|
728f2670f3 | ||
|
|
8dd157d2eb | ||
|
|
2d40d69fa2 | ||
|
|
f2f0d69bce | ||
|
|
812f5045b0 | ||
|
|
9a8e7b11e5 | ||
|
|
e213e695c8 | ||
|
|
e24eea313c | ||
|
|
a3286ebac3 | ||
|
|
95ae23a32c | ||
|
|
d240f3242c | ||
|
|
814f60a512 | ||
|
|
fac6229e43 | ||
|
|
c528d739c8 | ||
|
|
c664be52d7 | ||
|
|
4ac4f44ba7 | ||
|
|
174430e3b5 | ||
|
|
762b21f809 | ||
|
|
9340bf385e | ||
|
|
9f5be52a97 | ||
|
|
9dc5a4fce3 | ||
|
|
fda3cd5fe7 | ||
|
|
6b8469e9a3 | ||
|
|
7dbbe5a7d8 | ||
|
|
2d6fdeb7ad | ||
|
|
4d3a01a5fe | ||
|
|
5967ab75b1 | ||
|
|
69c954760a | ||
|
|
40a4c3ccbd | ||
|
|
aacb9d9570 | ||
|
|
9b6c229b71 | ||
|
|
1da5be2871 | ||
|
|
66dafca61a | ||
|
|
1f22b5b083 | ||
|
|
5be0b7a731 | ||
|
|
b64b5d9103 | ||
|
|
953d78da9e | ||
|
|
ce9e0981a2 | ||
|
|
76e1114a1f | ||
|
|
cfc80b491f | ||
|
|
43ed05d3c2 | ||
|
|
4cf5ce871f | ||
|
|
91ec08df4e | ||
|
|
cc6672198a | ||
|
|
17cdf7c79d | ||
|
|
725f939f35 | ||
|
|
e2df00bb2e | ||
|
|
a9a33c6179 | ||
|
|
8e7eb87a2d | ||
|
|
217004e7a5 | ||
|
|
4d10848984 | ||
|
|
da2c04f681 | ||
|
|
8deb327b3b | ||
|
|
642b01bf0d | ||
|
|
9fd78b1eb1 | ||
|
|
66c09fc44c | ||
|
|
2cb5e1a6c2 | ||
|
|
02ac638bd4 | ||
|
|
323f74c43a | ||
|
|
1e56d17d39 | ||
|
|
16af8e082b | ||
|
|
9215a54c23 | ||
|
|
fab34d3dbb | ||
|
|
346b0c9d68 | ||
|
|
c29359e7a8 | ||
|
|
4b903931bc | ||
|
|
4e0929e71a | ||
|
|
1bad097a13 | ||
|
|
331065eec6 | ||
|
|
f51ba499d5 | ||
|
|
f62ccc2d48 | ||
|
|
cf485aa62e | ||
|
|
6b16a48568 | ||
|
|
ff7cf503ae | ||
|
|
2412a0d502 | ||
|
|
b1612bb1ed | ||
|
|
b8a205f755 | ||
|
|
eab08ea78c | ||
|
|
d7653769b4 | ||
|
|
c72d9695da | ||
|
|
0493f00a7a | ||
|
|
ff757ddc88 | ||
|
|
c4c495948a | ||
|
|
986ee6bac3 | ||
|
|
6ab7e79987 | ||
|
|
c6e35876fa | ||
|
|
eb31accf20 | ||
|
|
ea1ba0f09b | ||
|
|
10911f5b64 | ||
|
|
0eab8e9322 | ||
|
|
fb2602716e | ||
|
|
be68906ae9 | ||
|
|
c53d6d80ba | ||
|
|
1362177352 | ||
|
|
28ab1230e2 | ||
|
|
0784934520 | ||
|
|
79fbf9d47f | ||
|
|
dabdc8e4c5 | ||
|
|
466ad192b0 | ||
|
|
ac59d9e1b6 | ||
|
|
d44245e1e9 | ||
|
|
c50105493a | ||
|
|
2768a62f92 | ||
|
|
5841d0d87d | ||
|
|
2a796051bf | ||
|
|
50286fd173 | ||
|
|
44f0bad2a6 | ||
|
|
611b9c4fd1 | ||
|
|
3f50776062 | ||
|
|
c70d2ad6fa | ||
|
|
9dd0bd604c | ||
|
|
0565519509 | ||
|
|
c282d95be1 | ||
|
|
766286b8bc | ||
|
|
f405c62f1e | ||
|
|
93fbd7b3ba | ||
|
|
ae5cea7f36 | ||
|
|
8b7b6cfbc5 | ||
|
|
f6e988d6fd | ||
|
|
f88f68f248 | ||
|
|
14f2b24b16 | ||
|
|
f2dde98e2f | ||
|
|
90d8ec0e81 | ||
|
|
14cdb531c8 | ||
|
|
3a50320f79 | ||
|
|
597b5e6cfb | ||
|
|
2cd056cfb3 | ||
|
|
be6aab4c40 | ||
|
|
d030df925f | ||
|
|
50756eb94a | ||
|
|
21b3576b66 | ||
|
|
2e5226356b | ||
|
|
21b76d3d2b | ||
|
|
2d252e6459 | ||
|
|
43be363542 | ||
|
|
8cb3e3418a | ||
|
|
cd47ddd539 | ||
|
|
2be1c10522 | ||
|
|
7f26670173 | ||
|
|
d838ce85c3 | ||
|
|
6350f5e6e8 | ||
|
|
10f3690ede | ||
|
|
06daa8bb0e | ||
|
|
34a90f442e | ||
|
|
afe81dcdbe | ||
|
|
24d616672b | ||
|
|
9cfc61cd45 | ||
|
|
487df84b90 | ||
|
|
614c1306f6 | ||
|
|
07dca9bd16 | ||
|
|
1ebcbd5b0e | ||
|
|
32644ddada | ||
|
|
cf3bab996e | ||
|
|
926ffe2581 | ||
|
|
fff3587d99 | ||
|
|
866cf940da | ||
|
|
7868e1527e | ||
|
|
556bfb752a | ||
|
|
a3b08654b4 | ||
|
|
4cf4436169 | ||
|
|
e2acc55819 | ||
|
|
6b29d6b8dc | ||
|
|
b668c4c302 | ||
|
|
646778227a | ||
|
|
a973630cb4 | ||
|
|
4e7375c09c | ||
|
|
881d0652e7 | ||
|
|
0c46993baa | ||
|
|
09b15f4940 | ||
|
|
ebf7be56bb | ||
|
|
727068cc4b | ||
|
|
6f77c6f3f4 | ||
|
|
c5e3e17eae | ||
|
|
effdb70417 | ||
|
|
e28f910c88 | ||
|
|
c84468dbed | ||
|
|
4b9e39ac64 | ||
|
|
6b0c05ee7c | ||
|
|
5c6ec70126 | ||
|
|
2757ef94c9 | ||
|
|
9ba7120011 | ||
|
|
124f9ec44e | ||
|
|
0d229175cd | ||
|
|
10638b6e40 | ||
|
|
134baad56d | ||
|
|
9a5984d750 | ||
|
|
073f42e64a | ||
|
|
096927beed | ||
|
|
4bc76995d1 | ||
|
|
9f41151156 | ||
|
|
6b3bd755b0 | ||
|
|
7e580e6a0b | ||
|
|
047c8eda22 | ||
|
|
f22e5c209c | ||
|
|
8cc3a08871 | ||
|
|
35f6c6cb98 | ||
|
|
814b174f25 | ||
|
|
5fbaf0bc7d | ||
|
|
ba772ab481 | ||
|
|
0a4888a18f | ||
|
|
fedbf2cc44 | ||
|
|
0f68bbac8e | ||
|
|
13e965096b | ||
|
|
92961bb7bf | ||
|
|
340686ba06 | ||
|
|
d8906f508c | ||
|
|
dde53ea4ba | ||
|
|
225ed5b662 | ||
|
|
02857cf2b5 | ||
|
|
118a771980 | ||
|
|
359123564f | ||
|
|
49d2878938 | ||
|
|
f2bc71bc2a | ||
|
|
2b1e40c6c6 | ||
|
|
105c2deeb3 | ||
|
|
c3dbbc9144 | ||
|
|
ca55a9a8a6 | ||
|
|
7ea5af448e | ||
|
|
2f6898142e | ||
|
|
02e0b8cc32 | ||
|
|
b962a4c69b | ||
|
|
ed3e83df67 | ||
|
|
b07bff61f0 | ||
|
|
a7c955055c | ||
|
|
13f33a9d19 | ||
|
|
be3fa6091d | ||
|
|
8ffddf06e4 | ||
|
|
9b7f583b2b | ||
|
|
9fe4f3adea | ||
|
|
74d9f89c09 | ||
|
|
63c36e917e | ||
|
|
184c6ee252 | ||
|
|
1c024afc1b | ||
|
|
51519361e2 | ||
|
|
bd092295a4 | ||
|
|
153c275d74 | ||
|
|
db0dfa1bf1 | ||
|
|
debd13d8c3 | ||
|
|
4b8466e5e5 | ||
|
|
9d9793e1af | ||
|
|
46fafebade | ||
|
|
1dae3d951a | ||
|
|
93deb37c94 | ||
|
|
004a93a841 | ||
|
|
da5be9f01d | ||
|
|
344d0ae3ec | ||
|
|
3639c86adf | ||
|
|
4fc80fd366 | ||
|
|
336ab2d82a | ||
|
|
74a7e67002 | ||
|
|
f76c04b7a6 | ||
|
|
8130487b18 | ||
|
|
83790a5a73 | ||
|
|
1b35f68de9 | ||
|
|
3e912c6198 | ||
|
|
6264569ca0 | ||
|
|
e868d427dd | ||
|
|
b9cbdb2dc4 | ||
|
|
7ae563867c | ||
|
|
f2f760bda4 | ||
|
|
ed561ad86b | ||
|
|
675861c323 | ||
|
|
ba330a42d6 | ||
|
|
2a9727c6b7 | ||
|
|
5be147e8cc | ||
|
|
eb96edbd31 | ||
|
|
14c85fa975 | ||
|
|
b0e3339370 | ||
|
|
70e502e55d | ||
|
|
ff38a3bbfe | ||
|
|
b5723a6c18 | ||
|
|
89012cd73b | ||
|
|
756e86662b | ||
|
|
27ca3b4b01 | ||
|
|
a5be4c9d0e | ||
|
|
07cc547bab | ||
|
|
f672af9706 | ||
|
|
58b058ab3a | ||
|
|
28c2ca8bf8 | ||
|
|
8970e6a48a | ||
|
|
b8eef181b9 | ||
|
|
fb94d6ae2b | ||
|
|
fa68e392c8 | ||
|
|
9eaa51442f | ||
|
|
68482d712b | ||
|
|
09fc767bb0 | ||
|
|
ea7e6615f2 | ||
|
|
a183ca8661 | ||
|
|
05939a2bbc | ||
|
|
543a372435 | ||
|
|
e795de5562 | ||
|
|
ae6877ce2f | ||
|
|
a0f3e81b11 | ||
|
|
88f52c4902 | ||
|
|
bf8db7725f | ||
|
|
f4d8c3304a | ||
|
|
44556b7f5e | ||
|
|
2e1e95d483 | ||
|
|
b15b38868d | ||
|
|
8feca6874a | ||
|
|
ecd3a49d48 | ||
|
|
6de7cd5063 | ||
|
|
f6d7f7d984 | ||
|
|
d5d501875e | ||
|
|
88561c22d3 | ||
|
|
b786576bcb | ||
|
|
21b5f2c96a | ||
|
|
d8f24b442b | ||
|
|
a1c81a63dd | ||
|
|
4d2b535b04 | ||
|
|
3cfbc05bf9 | ||
|
|
895820f14c | ||
|
|
37fc21f3cf | ||
|
|
5e068c3af5 | ||
|
|
e2c192d254 | ||
|
|
e481ed37ce | ||
|
|
4d7c089b09 | ||
|
|
a4dc67cba0 | ||
|
|
03973cc6d4 | ||
|
|
66c301c031 | ||
|
|
e4edc59689 | ||
|
|
f2d9d38c6f | ||
|
|
667ea43b3c | ||
|
|
f3856819fe | ||
|
|
d6bfe7810a | ||
|
|
7a52ae18f1 | ||
|
|
444539b826 | ||
|
|
c8d6425123 | ||
|
|
e50c35d38c | ||
|
|
aa764fbd1c | ||
|
|
2628426084 | ||
|
|
aa6bc8042a | ||
|
|
26a6c9e932 | ||
|
|
0f9376e959 | ||
|
|
ace3e86546 | ||
|
|
d79c6b8f06 | ||
|
|
6538a2e673 | ||
|
|
153d883aeb | ||
|
|
689432f627 | ||
|
|
cd237219e4 | ||
|
|
8589493581 | ||
|
|
ca2e148ad7 | ||
|
|
f7ca44cad8 | ||
|
|
1b2ac38a50 | ||
|
|
f62d25fa5f | ||
|
|
025eec1782 | ||
|
|
846ff46b2e | ||
|
|
f9718bccb9 | ||
|
|
f66f4ffee6 | ||
|
|
a47417ff49 | ||
|
|
c9836cf0f7 | ||
|
|
289b679e3c | ||
|
|
23e019ec83 | ||
|
|
eeffcea69e | ||
|
|
ae10793d0f | ||
|
|
a062bca431 | ||
|
|
6a453bcc8a | ||
|
|
5a2c4919c6 | ||
|
|
09a80ed654 | ||
|
|
72e954b78f | ||
|
|
a1c27aed6a | ||
|
|
0b3a719a95 | ||
|
|
6c9b4a8c5d | ||
|
|
d9babda1b8 | ||
|
|
ea8e1be294 | ||
|
|
44eccd85fd | ||
|
|
a62720b9d8 | ||
|
|
1a9422c3f9 | ||
|
|
5e52b3609c | ||
|
|
8622385e88 | ||
|
|
d0ffaab339 | ||
|
|
347157b999 | ||
|
|
a9f3235fd3 | ||
|
|
4098a5c08e | ||
|
|
dba7a2ee4f | ||
|
|
a5f49550b3 | ||
|
|
5c9a69e0e8 | ||
|
|
2bf32fb3fa | ||
|
|
0de1e2c6fc | ||
|
|
61868d97c4 | ||
|
|
c994c11d8c | ||
|
|
5ad10955be | ||
|
|
95f100f378 | ||
|
|
3d6c93cd6b | ||
|
|
fc25da37c5 | ||
|
|
896bb2187e | ||
|
|
99398bf0da | ||
|
|
827a54435d | ||
|
|
3c9459e489 | ||
|
|
9291f5c9c6 | ||
|
|
0ab5f993c7 | ||
|
|
4f8db487e7 | ||
|
|
0e1765e045 | ||
|
|
ebc411bbbd | ||
|
|
a76d8f0f9f | ||
|
|
f245feb0b0 | ||
|
|
43a90d7b98 | ||
|
|
2e1a9a8df9 | ||
|
|
57bb0da1d6 | ||
|
|
0d2df22074 | ||
|
|
c7173d5e1c | ||
|
|
789eb48698 | ||
|
|
e686fad546 | ||
|
|
4e4f9b6f8b | ||
|
|
f2292fd618 | ||
|
|
7035ead9e7 | ||
|
|
f01f6e94d1 | ||
|
|
f10064ce39 | ||
|
|
b68f06ca83 | ||
|
|
2ce61402bb | ||
|
|
70e9d85a75 | ||
|
|
a461f462d2 | ||
|
|
50ff0d251a | ||
|
|
f6103d3841 | ||
|
|
cb68d19bed | ||
|
|
89d2505a7c | ||
|
|
9ddfc750e5 | ||
|
|
3b80de1747 | ||
|
|
90ea714e48 | ||
|
|
f9e4182624 | ||
|
|
0291cc2ef4 | ||
|
|
caf2e469a6 | ||
|
|
45da2843ee | ||
|
|
8353f928a1 | ||
|
|
9c1a6d042e | ||
|
|
448b25a8b2 | ||
|
|
4c2d4009da | ||
|
|
67f1e07508 | ||
|
|
c49fdf1233 | ||
|
|
7c835bae20 | ||
|
|
ae81cc2644 | ||
|
|
3907b4101a | ||
|
|
aa5ea0e3a1 | ||
|
|
d21043802e | ||
|
|
995bdb3f9e | ||
|
|
8363b4fda7 | ||
|
|
23979f4ce6 | ||
|
|
28b5f39b84 | ||
|
|
47ce2398a4 | ||
|
|
0a83d8e6a0 | ||
|
|
aa215f2a5a | ||
|
|
b03a6a5327 | ||
|
|
ca36a6fe41 | ||
|
|
bd6285c8b1 | ||
|
|
00cfdc7d92 | ||
|
|
5e2dc14dd5 | ||
|
|
c5f2890cbe | ||
|
|
36aaca997a | ||
|
|
e9f7c61113 | ||
|
|
2373b94d3e | ||
|
|
f131e31949 | ||
|
|
8fd55a210a | ||
|
|
678650beaf | ||
|
|
e09386be44 | ||
|
|
75db2867dc | ||
|
|
80e37df012 | ||
|
|
1f404bb622 | ||
|
|
54078087e5 | ||
|
|
23b8df1c36 | ||
|
|
65395516b0 | ||
|
|
849308e28d | ||
|
|
4d98a64000 | ||
|
|
0c8fdfca7d | ||
|
|
fd928e8d12 | ||
|
|
2a1fe99a29 | ||
|
|
4fa4ba6301 | ||
|
|
48b3959cfb | ||
|
|
9bbff744e9 | ||
|
|
19b0c266f9 | ||
|
|
fead940d10 | ||
|
|
687e17ac52 | ||
|
|
b1c85dcb74 | ||
|
|
a15aad9f9c | ||
|
|
06a1a8690d | ||
|
|
42b9b6426a | ||
|
|
332f0118a2 | ||
|
|
6ed709d6e6 | ||
|
|
7a461c1684 | ||
|
|
7cf171671d | ||
|
|
ebee94fb11 | ||
|
|
61e8becd38 | ||
|
|
a78caa2976 | ||
|
|
c54f7c81c4 | ||
|
|
85840872ab | ||
|
|
d582c30f6e | ||
|
|
392f5f914a | ||
|
|
799d25925a | ||
|
|
4431d50635 | ||
|
|
e120e9a78e | ||
|
|
b6e379d14e | ||
|
|
9a86034162 | ||
|
|
8456c8b47b | ||
|
|
bb656ce44b | ||
|
|
3c2a3898e1 | ||
|
|
c265bd6c4d | ||
|
|
aff65083cc | ||
|
|
aff8cd478c | ||
|
|
bce2a63772 | ||
|
|
3f9d2601b4 | ||
|
|
04bfd52fba | ||
|
|
87dd890eb0 | ||
|
|
a5c0b48b57 | ||
|
|
5d38693b4d | ||
|
|
a4773d259d | ||
|
|
f9b6b1bf76 | ||
|
|
ef106f3232 | ||
|
|
c0b0df34d2 | ||
|
|
d15cc7cc47 | ||
|
|
6336d38a3e | ||
|
|
6a9d2ba653 | ||
|
|
34a8d4a57d | ||
|
|
ffc666eaaa | ||
|
|
c45aab7cef | ||
|
|
eefff148e9 | ||
|
|
05f7578928 | ||
|
|
2ebb2d8f0e | ||
|
|
afe2935c9d | ||
|
|
380c7b7720 | ||
|
|
8657226594 | ||
|
|
41da48f5ff | ||
|
|
405aa906c5 | ||
|
|
1c507a47d2 | ||
|
|
f48a7df80f | ||
|
|
5f73f09836 | ||
|
|
f63dd75f08 | ||
|
|
cc55335a8d | ||
|
|
b5875f3a0a | ||
|
|
cb8333a48f | ||
|
|
f412f4ca88 | ||
|
|
941f30d1ea | ||
|
|
97afa502c5 | ||
|
|
9ae9ea18e1 | ||
|
|
0bf2abaa4c | ||
|
|
924f281536 | ||
|
|
1fc5dacd87 | ||
|
|
5c877de2c2 | ||
|
|
751b95d4af | ||
|
|
e0d5ba9915 | ||
|
|
b8a6946661 | ||
|
|
e5fac08d1d | ||
|
|
df5b7c7d0d | ||
|
|
27649f7d4c | ||
|
|
350dea6228 | ||
|
|
aef6b7712c | ||
|
|
642bcfcdea | ||
|
|
e625d8aabc | ||
|
|
5888ecbdcd | ||
|
|
e2a76056b8 | ||
|
|
a98498eb06 | ||
|
|
8366c8d2a7 | ||
|
|
ed8d441a02 | ||
|
|
f1fb265119 | ||
|
|
6c628094ce | ||
|
|
a60c52e2f0 | ||
|
|
ac2e1709f8 | ||
|
|
db88183a23 | ||
|
|
c7d55ad858 | ||
|
|
06a4e6c323 | ||
|
|
d1de89f387 | ||
|
|
bbba01da92 | ||
|
|
25dbf62274 | ||
|
|
ed6851863b | ||
|
|
ba924e295e | ||
|
|
0828065a62 | ||
|
|
68c789dceb | ||
|
|
6424084502 | ||
|
|
4abea18afe | ||
|
|
0a3c4f131e | ||
|
|
f5e1077e20 | ||
|
|
44d1c3fd2f | ||
|
|
e345161763 | ||
|
|
64d7c87591 | ||
|
|
1fae3baaa3 | ||
|
|
38103aaac5 | ||
|
|
cc25b22f11 | ||
|
|
e6dbeda18e | ||
|
|
8437d45866 | ||
|
|
0bb89de821 | ||
|
|
905cad56d8 | ||
|
|
65eeb70eb3 | ||
|
|
266744f640 | ||
|
|
23d6739580 | ||
|
|
5c9970c786 | ||
|
|
3eae716a2d | ||
|
|
c57b13d922 | ||
|
|
17fb419fb1 | ||
|
|
598d0e216a | ||
|
|
7bbe926232 | ||
|
|
2e848a7c9a | ||
|
|
437225b43e | ||
|
|
d39229713f | ||
|
|
93911be1b9 | ||
|
|
b74055478c | ||
|
|
8614c4db73 | ||
|
|
215d39fc54 | ||
|
|
c4e5a130ee | ||
|
|
630072b574 | ||
|
|
5261a3e845 | ||
|
|
0096a91a57 | ||
|
|
56699a9f89 | ||
|
|
31ff1372ae | ||
|
|
3afb1922bb | ||
|
|
83c0a8b047 | ||
|
|
6699bd47b5 | ||
|
|
34223b8d4f | ||
|
|
5befe1f019 | ||
|
|
87f86e72f4 | ||
|
|
53b7eba31a | ||
|
|
12c12a8ad1 | ||
|
|
897cc7d355 | ||
|
|
2e5c56205c | ||
|
|
bc5ff37e37 | ||
|
|
20341a381f | ||
|
|
926b945846 | ||
|
|
aa877a73ba | ||
|
|
b28208d1bf | ||
|
|
9bd97383bd | ||
|
|
522c7b2f9d | ||
|
|
1833c0acbc | ||
|
|
c5644ee3f9 | ||
|
|
447566fe14 | ||
|
|
9692c34f6c | ||
|
|
37c450f1e1 | ||
|
|
a003e396c5 | ||
|
|
996f61efe1 | ||
|
|
40cdcf8b06 | ||
|
|
5947364846 | ||
|
|
9470107bba | ||
|
|
54b945511b | ||
|
|
acfaa0041e | ||
|
|
aeed2dbc3e | ||
|
|
0c6befe8a5 | ||
|
|
bdcb26edae | ||
|
|
e091667b42 | ||
|
|
d35b14f4cc | ||
|
|
87996c6811 | ||
|
|
a880c733c8 | ||
|
|
ca10dfeb5f | ||
|
|
91f55a637b | ||
|
|
8ae43cfd14 | ||
|
|
83d9513c4a | ||
|
|
1c76d43e44 | ||
|
|
1036ce0fa5 | ||
|
|
3dbab68f17 | ||
|
|
5896cebeaa | ||
|
|
fbe629154d | ||
|
|
364136213b | ||
|
|
136b663cef | ||
|
|
803f11bebb | ||
|
|
7c8036807a | ||
|
|
84ccca0e98 | ||
|
|
74efdb95e8 | ||
|
|
10e45ac493 | ||
|
|
60befdb36e | ||
|
|
59f99ea9bb | ||
|
|
1a894abcff | ||
|
|
4934fc8809 | ||
|
|
18cc6a184f | ||
|
|
0a08765d73 | ||
|
|
355c7437ed | ||
|
|
3c55c2d777 | ||
|
|
94806ad0b3 | ||
|
|
6840259734 | ||
|
|
a1fc48f2a6 | ||
|
|
400e3d21f9 | ||
|
|
8f3daad502 | ||
|
|
b0395933de | ||
|
|
f8f2ab9cba | ||
|
|
ae5f5375da | ||
|
|
ab5f1e712b | ||
|
|
4532ca97fa | ||
|
|
5a9ef57f78 | ||
|
|
8791f382b3 | ||
|
|
abdef67ccc | ||
|
|
33494c4f4b | ||
|
|
daad975f5d | ||
|
|
18c00f0a4b | ||
|
|
e7f46b4fbe | ||
|
|
74827cd8cf | ||
|
|
5ffe1893cd | ||
|
|
f24618e8df | ||
|
|
0e5b32ef13 | ||
|
|
0493a321d2 | ||
|
|
38b6c12153 | ||
|
|
74d4b8e0b9 | ||
|
|
f843d34234 | ||
|
|
95b2bf3645 | ||
|
|
121ac4f1de | ||
|
|
ec8550d587 | ||
|
|
e403c419e5 | ||
|
|
4b0d587fe1 | ||
|
|
ebd356c7bd | ||
|
|
507093dbad | ||
|
|
4cfdc77015 | ||
|
|
9096cacba8 | ||
|
|
607336d3ce | ||
|
|
6383fc3575 | ||
|
|
a5576ddbf3 | ||
|
|
e2a70873b8 | ||
|
|
23c7340afe | ||
|
|
380b56a89d | ||
|
|
8e09f3478f | ||
|
|
c1ce51eb12 | ||
|
|
9aeb773169 | ||
|
|
091c13ff41 | ||
|
|
ef0bab0c6e | ||
|
|
70bd16adf6 | ||
|
|
96a713afeb | ||
|
|
bf3615fb32 | ||
|
|
0f56b1c943 | ||
|
|
d541572882 | ||
|
|
ecfdc377ec | ||
|
|
fa67e90767 | ||
|
|
81b72d5481 | ||
|
|
ef6028e933 | ||
|
|
5d41fe4a35 | ||
|
|
1dc6cec1aa | ||
|
|
9378668e52 | ||
|
|
eb96ead80e | ||
|
|
9403fbaf81 | ||
|
|
79190f313d | ||
|
|
4c124284b6 | ||
|
|
6d892179c8 | ||
|
|
61675c20d8 | ||
|
|
4aae878db8 | ||
|
|
918884bd11 | ||
|
|
8799f9079b | ||
|
|
0b471cfd06 | ||
|
|
7b39a12396 | ||
|
|
57a53b4b6c | ||
|
|
f6d0b3368f | ||
|
|
0fe7bdf849 | ||
|
|
a26dc39a6d | ||
|
|
e45cfe7d0c | ||
|
|
efefa8caf5 | ||
|
|
cc13db9b1f | ||
|
|
f339544256 | ||
|
|
1a05bcb295 | ||
|
|
190e26276a | ||
|
|
bb33760e87 | ||
|
|
9e105b4983 | ||
|
|
8dcf70408d | ||
|
|
9d6d1825c7 | ||
|
|
1a4923cdce | ||
|
|
316e440390 | ||
|
|
7d66019220 | ||
|
|
f98a6fb665 | ||
|
|
dbdc7279c4 | ||
|
|
7726705b5c | ||
|
|
34b7e8815a | ||
|
|
8ac2b58a44 | ||
|
|
fe97f0929b | ||
|
|
6eec353c2b | ||
|
|
2b4c3b8d1f | ||
|
|
df99b37c4d | ||
|
|
ab6f3fcf8e | ||
|
|
ca6f656e1b | ||
|
|
88798b1a9e | ||
|
|
c197270125 | ||
|
|
dc344d4658 | ||
|
|
b4864831e0 | ||
|
|
476dffff13 | ||
|
|
389ee974f3 | ||
|
|
0d15eceacb | ||
|
|
b69fbdda9a | ||
|
|
d3746e0119 | ||
|
|
230af9cafa | ||
|
|
4db63d113c | ||
|
|
008583396d | ||
|
|
33a33e3c71 | ||
|
|
d312d753e9 | ||
|
|
02310d4af6 | ||
|
|
0e6d8c4e25 | ||
|
|
55315fca80 | ||
|
|
4eef9e780f | ||
|
|
7bfc3562af | ||
|
|
5b0b0d6d36 | ||
|
|
cb64072f7b | ||
|
|
c5b6da7201 | ||
|
|
f1d4818045 | ||
|
|
76b49f6985 | ||
|
|
094d9193b9 | ||
|
|
3053a9b6a0 | ||
|
|
47bf0ef591 | ||
|
|
e2aa2709ac | ||
|
|
9a6d478eb1 | ||
|
|
4f37e7dc3c | ||
|
|
2a4ba8d349 | ||
|
|
85bd7a63c6 | ||
|
|
138d57143a | ||
|
|
464a228106 | ||
|
|
2b92a039bb | ||
|
|
f190ee951c | ||
|
|
68cc75cada | ||
|
|
b4e324ec0e | ||
|
|
32fe2e7974 | ||
|
|
713513aacc | ||
|
|
b4ffca56a3 | ||
|
|
f2168774a5 | ||
|
|
febc00d357 | ||
|
|
01a8c507e5 | ||
|
|
bf7982cc2e | ||
|
|
2e9689886b | ||
|
|
2003b34036 | ||
|
|
e1995b5c70 | ||
|
|
3890acabc4 | ||
|
|
ba6c0d0423 | ||
|
|
882e7a845e | ||
|
|
ca56d3fc23 | ||
|
|
49b1e76585 | ||
|
|
80f81685d1 | ||
|
|
21dead3125 | ||
|
|
1521d08285 | ||
|
|
59b2e31add | ||
|
|
b5feb3fd66 | ||
|
|
7785e6ebd2 | ||
|
|
c561d71dc0 | ||
|
|
2cfb697867 | ||
|
|
c680ff006e | ||
|
|
333103f50e | ||
|
|
517385fb63 | ||
|
|
ee8ab58d64 | ||
|
|
b967acda58 | ||
|
|
d81ca5f919 | ||
|
|
07adf64aec | ||
|
|
fbb98e1aec | ||
|
|
2fdf927704 | ||
|
|
4b84656133 | ||
|
|
97c136d043 | ||
|
|
79bf44b3f5 | ||
|
|
ddd8d4aeb2 | ||
|
|
bfcb6f577f | ||
|
|
2b137b43e6 | ||
|
|
6d74493491 | ||
|
|
6f4271c054 | ||
|
|
f24054100e | ||
|
|
6e98649607 | ||
|
|
b2108ff2d0 | ||
|
|
8949ebf041 | ||
|
|
576801cd32 | ||
|
|
2f2b12811f | ||
|
|
d8ea3a9035 | ||
|
|
45c3b3987b | ||
|
|
93720fffd4 | ||
|
|
61ad6a2b88 | ||
|
|
c9d5b3c0ff | ||
|
|
d51bf735c4 | ||
|
|
22c388ab18 | ||
|
|
d5f831301f | ||
|
|
dcab37a148 | ||
|
|
60b2da3671 | ||
|
|
5c1b5816d4 | ||
|
|
7a0a45e9d2 | ||
|
|
70f72a78f6 | ||
|
|
e056c9c135 | ||
|
|
c754b5ae18 | ||
|
|
481fafc11d | ||
|
|
7d927b0e28 | ||
|
|
c314b07136 | ||
|
|
16fe13bf4a | ||
|
|
d19eda7e08 | ||
|
|
6f0a136727 | ||
|
|
e2e101e4fb | ||
|
|
74f03202b7 | ||
|
|
3d19e92059 | ||
|
|
bfff125cc5 | ||
|
|
e90baf3ca6 | ||
|
|
f3b277aeef | ||
|
|
76096747b6 | ||
|
|
4c6ef32d72 | ||
|
|
a8e12e624d | ||
|
|
88a43bfc28 | ||
|
|
3b268fe3cc | ||
|
|
4c72d43a8a | ||
|
|
0a5f8527b2 | ||
|
|
9f1b84d6f2 | ||
|
|
babcbcbcea | ||
|
|
823a6017fe | ||
|
|
f034aef2ae | ||
|
|
bf38bd5a1d | ||
|
|
b922809c9d | ||
|
|
05b0bda8bb | ||
|
|
0aa3aa1b8d | ||
|
|
d4febb4e84 | ||
|
|
21090eaa39 | ||
|
|
d0ea59c568 | ||
|
|
a292bc77ba | ||
|
|
98d5e0b56d | ||
|
|
7ca1cfab1a | ||
|
|
2e7ce38552 | ||
|
|
0ef3a2472d | ||
|
|
b97f095de4 | ||
|
|
10e2b35483 | ||
|
|
16920a89f3 | ||
|
|
1a5b9de82e | ||
|
|
4ef183fee6 | ||
|
|
2115ce6606 | ||
|
|
61d1b733f7 | ||
|
|
4978edb8be | ||
|
|
51f7aba807 | ||
|
|
b9b143e4e7 | ||
|
|
0e7596a205 | ||
|
|
8c401cf01b | ||
|
|
6782e6a532 | ||
|
|
4386bd93c3 | ||
|
|
72b3c10ebd | ||
|
|
62cec2a31c | ||
|
|
0c442622af | ||
|
|
bf3c4bc588 | ||
|
|
d98dd83369 | ||
|
|
21ecf309bb | ||
|
|
4bb4012d87 | ||
|
|
10fd8eb709 | ||
|
|
b1cc1db967 | ||
|
|
77d8bae2c2 | ||
|
|
7274d43645 | ||
|
|
3eeee1b08d | ||
|
|
64b2a32c9a | ||
|
|
4ced1e5075 | ||
|
|
8de15c9d0d | ||
|
|
31d716bd0c | ||
|
|
3da6b3930b | ||
|
|
900fc1cb46 | ||
|
|
deb87f1d4c | ||
|
|
ed44d23afb | ||
|
|
8baf7f3f6a | ||
|
|
d2d4fa29e4 | ||
|
|
0c56cd63bd | ||
|
|
c9cf84f2f4 | ||
|
|
0966369723 | ||
|
|
4f6c3d52b3 | ||
|
|
97f8ab5c51 | ||
|
|
8805f1e4d6 | ||
|
|
3ae57e0ca9 | ||
|
|
a8e4301f23 | ||
|
|
68bc78d00b | ||
|
|
1dc9e74df4 | ||
|
|
a69cee03e5 | ||
|
|
bf15ad3bba | ||
|
|
bb3f50f967 | ||
|
|
1042e19845 | ||
|
|
85830d5076 | ||
|
|
c053bebccd | ||
|
|
d6d6ae8af2 | ||
|
|
6d8b0e3a5d | ||
|
|
cfd7f1571b | ||
|
|
f31c04d92a | ||
|
|
89b58ec3af | ||
|
|
ab0d66c2ef | ||
|
|
9774865d4a | ||
|
|
3817a0c2a1 | ||
|
|
5215bdc035 | ||
|
|
8061d306dd | ||
|
|
30f68759ff | ||
|
|
3f0b595085 | ||
|
|
0c9ce6258c | ||
|
|
7da17ba21e | ||
|
|
7b23d79dc2 | ||
|
|
415314a90d | ||
|
|
0f7e2ad11a | ||
|
|
26d232c567 | ||
|
|
efa48a7e39 | ||
|
|
022642f4d5 | ||
|
|
e6e2f04a10 | ||
|
|
f7e21dbe5c | ||
|
|
f593802a51 | ||
|
|
f545e6eb27 | ||
|
|
1778d82bc3 | ||
|
|
03587d7035 | ||
|
|
6663788612 | ||
|
|
ac2cb773df | ||
|
|
b70b3ec85b | ||
|
|
1e69b8c41d | ||
|
|
d5aa1a4880 | ||
|
|
de0658eaab | ||
|
|
939c28b74b | ||
|
|
c10d628a45 | ||
|
|
92830172f9 | ||
|
|
431af2c0dd | ||
|
|
97ca8b7ada | ||
|
|
f3a7c233b3 | ||
|
|
928abf7094 | ||
|
|
2cace0008e | ||
|
|
db9c20f3dd | ||
|
|
e1a1aef990 | ||
|
|
23cf6ebc89 | ||
|
|
55c279cc7e | ||
|
|
4e89f90c4f | ||
|
|
bd0eb81c1b | ||
|
|
a77a0d98e0 | ||
|
|
e5037fc9f9 | ||
|
|
7ac2022159 | ||
|
|
bc41a15eba | ||
|
|
8aa158c1e0 | ||
|
|
1f6f4d9c49 | ||
|
|
3686a27c19 | ||
|
|
1bcc311738 | ||
|
|
2335d3879e | ||
|
|
209934ad67 | ||
|
|
35200a1ee5 | ||
|
|
6c4977ee78 | ||
|
|
5482a57c45 | ||
|
|
18914978d5 | ||
|
|
36750ab900 | ||
|
|
c5f6a690de | ||
|
|
9611f80a39 | ||
|
|
eb2d68fc28 | ||
|
|
937d346676 | ||
|
|
7565843fbe | ||
|
|
6740ec464c | ||
|
|
314e1e4bfe | ||
|
|
45d68d89a9 | ||
|
|
1d5194a138 | ||
|
|
05043f30dc | ||
|
|
cd549937c5 | ||
|
|
efdea07b7b | ||
|
|
06d4998d87 | ||
|
|
02b566055e | ||
|
|
c312dbaac1 | ||
|
|
b6dcb2f4c0 | ||
|
|
a85d3f2573 | ||
|
|
0ca3fb5af0 | ||
|
|
2a4d78d9bf | ||
|
|
d9e199092d | ||
|
|
02bbb46d2e | ||
|
|
13ffdc6dd2 | ||
|
|
c8c2c4d376 | ||
|
|
01f7343781 | ||
|
|
3acc244692 | ||
|
|
094068e4ff | ||
|
|
ec958697e2 | ||
|
|
208e8f8247 | ||
|
|
3d4890a28b | ||
|
|
fe4362f459 | ||
|
|
81d3ad2d35 | ||
|
|
ffb8c3e53c | ||
|
|
2d4d2374e3 | ||
|
|
09f31a9278 | ||
|
|
80b0a3cdec | ||
|
|
c533bfc83d | ||
|
|
8fa053f7c7 | ||
|
|
b152bb26e3 | ||
|
|
a0816b04e5 | ||
|
|
0819517902 | ||
|
|
55ea8c82e9 | ||
|
|
ffbbf88de4 | ||
|
|
e2ff49825f | ||
|
|
7f325827c4 | ||
|
|
cae9ccfda1 | ||
|
|
248ae7d4d5 | ||
|
|
7f08bbe938 | ||
|
|
81b2f2114d | ||
|
|
5eee430be3 | ||
|
|
623edf3bc9 | ||
|
|
bd4a224051 | ||
|
|
870e84a700 | ||
|
|
8d4fae24ea | ||
|
|
7a84daf3f7 | ||
|
|
7968279bc2 | ||
|
|
258be40285 | ||
|
|
b2ae30eba1 | ||
|
|
daaba1dbc0 | ||
|
|
a3c6ed4dd2 | ||
|
|
e4255ed712 | ||
|
|
5d510f1cf4 | ||
|
|
1819bd910a | ||
|
|
43eecdbb3f | ||
|
|
108c1bcac4 | ||
|
|
4b7e5864d4 | ||
|
|
fb1d2abbfa | ||
|
|
0c290e65ef | ||
|
|
5487fad2ce | ||
|
|
d41f930f69 | ||
|
|
595b2619fd | ||
|
|
26d305d866 | ||
|
|
c9d95ff161 | ||
|
|
9cc592b564 | ||
|
|
ff48422ec0 | ||
|
|
a26c5f85c3 | ||
|
|
727436e1cf | ||
|
|
d1c57a1872 | ||
|
|
b7c021af8c | ||
|
|
7149b509d7 | ||
|
|
45e7111dda | ||
|
|
9fc69db9eb | ||
|
|
2ba314d9d9 | ||
|
|
f35660c8e2 | ||
|
|
68b1fe8631 | ||
|
|
4242c86d40 | ||
|
|
ef4dc3cbc9 | ||
|
|
8daa7561fa | ||
|
|
2cc3dfc2ce | ||
|
|
459800568a | ||
|
|
3a35b84b03 | ||
|
|
79cfa52bf9 | ||
|
|
a0e8fe5848 | ||
|
|
2dae5bccb2 | ||
|
|
8e867ab0c0 | ||
|
|
1b2c88fe38 | ||
|
|
f3bee5ff3f | ||
|
|
196d7e8f72 | ||
|
|
16596c18fb | ||
|
|
7ea3a87bfc | ||
|
|
a57905b6cd | ||
|
|
f9c592ca22 | ||
|
|
aecac0ef85 | ||
|
|
ca315c51a0 | ||
|
|
45c8858140 | ||
|
|
06e45bff24 | ||
|
|
2635a658d0 | ||
|
|
f48a98f691 | ||
|
|
3badda95c1 | ||
|
|
364ccc05d5 | ||
|
|
d09fedf208 | ||
|
|
7936f8730f | ||
|
|
6c0dfc4356 | ||
|
|
d9af8c31a2 | ||
|
|
ca375314f0 | ||
|
|
5266d4d79c | ||
|
|
1cb0826de0 | ||
|
|
89e3178ea3 | ||
|
|
3b5d9d6cee | ||
|
|
ce4ed19029 | ||
|
|
01a502339c | ||
|
|
642d0e6f74 | ||
|
|
d9e659deb0 | ||
|
|
830fe7f9b8 | ||
|
|
3e8c247c05 | ||
|
|
16880074fa | ||
|
|
19c74ce9fa | ||
|
|
56ef0dad9c | ||
|
|
8d99808821 | ||
|
|
1cb08fdecc | ||
|
|
e8952d7e02 | ||
|
|
18fad9c9d9 | ||
|
|
89a0a94f3e | ||
|
|
0859cf30f8 | ||
|
|
a0fe02a560 | ||
|
|
3156f7dacd | ||
|
|
c3958bf042 | ||
|
|
facc5f8aa7 | ||
|
|
8170257c26 | ||
|
|
489e37b2a1 | ||
|
|
4899e0d2d5 | ||
|
|
762f9c4b23 | ||
|
|
6d3dac0ec1 | ||
|
|
f684815272 | ||
|
|
8e04218c95 | ||
|
|
23cb45454b | ||
|
|
7fc9a161b1 | ||
|
|
95a5473051 | ||
|
|
66ceb573dc | ||
|
|
5f8223ebb5 | ||
|
|
51146d4152 | ||
|
|
3334281949 | ||
|
|
8e85d9ac00 | ||
|
|
e1c69a6250 | ||
|
|
edd9a18257 | ||
|
|
65f993677f | ||
|
|
bc775140bb | ||
|
|
4b2bd6e18f | ||
|
|
c36a810bcb | ||
|
|
a994bbc36b | ||
|
|
c3238f4d0b | ||
|
|
632d26e398 | ||
|
|
214cc8b810 | ||
|
|
8f218141f4 | ||
|
|
3676304751 | ||
|
|
c605fd57aa | ||
|
|
4599f6919c | ||
|
|
8ad20c0db3 | ||
|
|
638a69e5f0 | ||
|
|
9fa6b1ebe1 | ||
|
|
5930e2d221 | ||
|
|
fdd96975fb | ||
|
|
de6dd77046 | ||
|
|
1b6ad8413e | ||
|
|
6096d572f3 | ||
|
|
badcd64b62 | ||
|
|
a7b8b52dbd | ||
|
|
d89f0f51df | ||
|
|
be358f3f2e | ||
|
|
f122da1485 | ||
|
|
0dda4728b6 | ||
|
|
45fd95e02b | ||
|
|
91aa2d7f6f | ||
|
|
a96b7d2a80 | ||
|
|
8f9cea54c5 | ||
|
|
045558bede | ||
|
|
58124ebaab | ||
|
|
0c87dd5624 | ||
|
|
b87f986a49 | ||
|
|
c6a6035bb9 | ||
|
|
1ef12f0645 | ||
|
|
ef3ec33ba3 | ||
|
|
c82ef1ee8f | ||
|
|
23b8a60242 | ||
|
|
ac9511165e | ||
|
|
9d70851eb9 | ||
|
|
759dfb28ce | ||
|
|
ff356b1f21 | ||
|
|
b2a6c1bc68 | ||
|
|
76549d0a4a | ||
|
|
e5c72cae83 | ||
|
|
bf47df46c9 | ||
|
|
0ef42870e5 | ||
|
|
da8a6a4c2b | ||
|
|
988007a8c9 | ||
|
|
710439e83c | ||
|
|
80a0a3d4fb | ||
|
|
43299aea10 | ||
|
|
f5aea766a7 | ||
|
|
c5308e3f2f | ||
|
|
2b8e662f81 | ||
|
|
0a6d849435 | ||
|
|
a0106fe5d8 | ||
|
|
cee1b8a64a | ||
|
|
4e2ba71d59 | ||
|
|
fb2bdfb9ee | ||
|
|
72785f6740 | ||
|
|
a94a05fac9 | ||
|
|
430368de97 | ||
|
|
7bfb499549 | ||
|
|
9bc477e1b6 | ||
|
|
f84ac18472 | ||
|
|
cd515a2e54 | ||
|
|
c73c8fdc47 | ||
|
|
e755a32b23 | ||
|
|
d4d1768575 | ||
|
|
0a5745c559 | ||
|
|
b24959205b | ||
|
|
d69f297c05 | ||
|
|
3c8e331809 | ||
|
|
d169471e8c | ||
|
|
56453f6b5c | ||
|
|
dac2e8c79e | ||
|
|
ccc96bc610 | ||
|
|
654371cb6a | ||
|
|
1af8d873bb | ||
|
|
b7a0e23309 | ||
|
|
4a0f868941 | ||
|
|
448073cdd6 | ||
|
|
ad79ec7b1f | ||
|
|
e194854c6d | ||
|
|
d01d033209 | ||
|
|
06c4aca490 | ||
|
|
885d57138a | ||
|
|
9e2a770a26 | ||
|
|
942b699bb9 | ||
|
|
c9d03a8094 | ||
|
|
d015538bb4 | ||
|
|
90d6c5c5bb | ||
|
|
387ce4b6fa | ||
|
|
7943b13891 | ||
|
|
50a7cd19b4 | ||
|
|
53e9335bb0 | ||
|
|
e5cb70972e | ||
|
|
0d84871037 | ||
|
|
1d37745c0c | ||
|
|
ad9ade7849 | ||
|
|
c1e2ee32b4 | ||
|
|
1588d2734c | ||
|
|
50dda4263f | ||
|
|
a8f2239495 | ||
|
|
c42636b0ee | ||
|
|
54b2c8bd7e | ||
|
|
d01a21a867 | ||
|
|
5d43052c05 | ||
|
|
4109ab1590 | ||
|
|
f6eabd695b | ||
|
|
24d9dacfd9 | ||
|
|
66d51a9eb1 | ||
|
|
302df75d83 | ||
|
|
11b7e637e9 | ||
|
|
135c92bd85 | ||
|
|
c15c26a233 | ||
|
|
5d94760cce | ||
|
|
79517a0ba3 | ||
|
|
64295e3541 | ||
|
|
cc2816aaf5 | ||
|
|
4a2fcb9deb | ||
|
|
7f27580f1b | ||
|
|
94d0915004 | ||
|
|
88db99e593 | ||
|
|
593b25a5cd | ||
|
|
5c58bf44c0 | ||
|
|
73ae6cf164 | ||
|
|
7749319c75 | ||
|
|
73037b86ac | ||
|
|
d50ba1259c | ||
|
|
962261fee7 | ||
|
|
4dea2ef1a4 | ||
|
|
aa12eb4ed4 | ||
|
|
8a75363784 | ||
|
|
01dd982587 | ||
|
|
62cf83921b | ||
|
|
73d4025256 | ||
|
|
3405ffd8d8 | ||
|
|
e03f1597a0 | ||
|
|
c5644e0e32 | ||
|
|
bf14b7da9a | ||
|
|
0c8fb376db | ||
|
|
17acdcc4d5 | ||
|
|
654357f5ce | ||
|
|
fbebdd3055 | ||
|
|
83e76c6b53 | ||
|
|
adf12b6084 | ||
|
|
047c6a93a3 | ||
|
|
bf4c33325c | ||
|
|
be1a4548e6 | ||
|
|
d8cd2afd12 | ||
|
|
6ff3f8df87 | ||
|
|
95c4a87ccc | ||
|
|
206f094dd4 | ||
|
|
a05a20440e | ||
|
|
ff12421d60 | ||
|
|
2cbd6e85c6 | ||
|
|
1fc50a59f5 | ||
|
|
9c9401ce2f | ||
|
|
f732a84a7c | ||
|
|
efe7e469ce | ||
|
|
ed136c9d8b | ||
|
|
60e2722a21 | ||
|
|
4fab07b4da | ||
|
|
d07c68bd9a | ||
|
|
2738169a9d | ||
|
|
490b65dfe2 | ||
|
|
38ebe28923 | ||
|
|
4ed7e29896 | ||
|
|
122b8c2a84 | ||
|
|
98c91a01e3 | ||
|
|
a7cd16c159 | ||
|
|
5ca86b87f5 | ||
|
|
25a163cdeb | ||
|
|
3a63f6775a | ||
|
|
d65257c7b0 | ||
|
|
465945f8a8 | ||
|
|
a0de60e179 | ||
|
|
b48682012d | ||
|
|
e624cb31bd | ||
|
|
20e43951e5 | ||
|
|
576802a1d6 | ||
|
|
23a3d48611 | ||
|
|
b6ec0a3526 | ||
|
|
ef6a038451 | ||
|
|
0354685e35 | ||
|
|
ba2b792916 | ||
|
|
44768e92ad | ||
|
|
0e8bdf8299 | ||
|
|
09298d7457 | ||
|
|
e8d80e16ba | ||
|
|
e461982a31 | ||
|
|
c896f6d0d7 | ||
|
|
9a9b38a8c3 | ||
|
|
b26b52cca8 | ||
|
|
b5ee997da9 | ||
|
|
046ffd8648 | ||
|
|
d7e7823606 | ||
|
|
2d3493a225 | ||
|
|
a3b08c0016 | ||
|
|
d9c0f52846 | ||
|
|
a96482b186 | ||
|
|
10e78785cd | ||
|
|
da56397b39 | ||
|
|
abc05b4485 | ||
|
|
09fd0baf78 | ||
|
|
d7deb938c5 | ||
|
|
68834df271 | ||
|
|
8a3c276e66 | ||
|
|
6a043649f5 | ||
|
|
019af7bd3a | ||
|
|
4f2f67d5b1 | ||
|
|
2a59ae294d | ||
|
|
6d586bde6c | ||
|
|
9510bba3b0 | ||
|
|
eb559f7b6a | ||
|
|
64dbd9abdf | ||
|
|
dfd41385b1 | ||
|
|
2b797fcd54 | ||
|
|
5cd557ef9d | ||
|
|
8baab2de37 | ||
|
|
c266cff956 | ||
|
|
53affa3303 | ||
|
|
ec772c5d46 | ||
|
|
7b5a7e10a9 | ||
|
|
188f1fcff8 | ||
|
|
39c346df10 | ||
|
|
490e829083 | ||
|
|
846128a791 | ||
|
|
6bad2daa62 | ||
|
|
4c91d08cea | ||
|
|
2442d0e910 | ||
|
|
7c13194d5a | ||
|
|
0ae7bbd34d | ||
|
|
0b2654f6b1 | ||
|
|
42d49bde86 | ||
|
|
d2b4a6fd50 | ||
|
|
7f172964f6 | ||
|
|
b8b8d70c7f | ||
|
|
969695f318 | ||
|
|
7ec701a816 | ||
|
|
c96b81206d | ||
|
|
5f8356741e | ||
|
|
3987d5e5a0 | ||
|
|
fcb56db224 | ||
|
|
873754c6ca | ||
|
|
12465f840a | ||
|
|
e8c9d2db10 | ||
|
|
a8b4f38865 | ||
|
|
27bd193708 | ||
|
|
c56ddce2f6 | ||
|
|
5d2f9f9f0b | ||
|
|
864aba9f4e | ||
|
|
4d27399ce3 | ||
|
|
76c54ffdef | ||
|
|
c873e9dd68 | ||
|
|
ce99357ebe | ||
|
|
562de3514a | ||
|
|
128a8f3b48 | ||
|
|
1839b85d97 | ||
|
|
f0f154cd10 | ||
|
|
624bff3036 | ||
|
|
1d2950b4a7 | ||
|
|
9072a018dd | ||
|
|
2a997d94bf | ||
|
|
2741e94a72 | ||
|
|
7c660ee556 | ||
|
|
51b850aa85 | ||
|
|
b29e94005d | ||
|
|
ddd506fde7 | ||
|
|
20310cb109 | ||
|
|
11177d37ea | ||
|
|
da006a1d6e | ||
|
|
451b0382ea | ||
|
|
950f250d66 | ||
|
|
01913d2b14 | ||
|
|
e0b19a6383 | ||
|
|
48289845df | ||
|
|
454f2dabbd | ||
|
|
8891d9aa4d | ||
|
|
49d59fc116 | ||
|
|
8c92c50f9a | ||
|
|
75d45ae988 | ||
|
|
d5e1d5db9c | ||
|
|
9ce9d9b7fc | ||
|
|
e9edc7b205 | ||
|
|
56822d9424 | ||
|
|
2c480bee9a | ||
|
|
3983838694 | ||
|
|
1e74ff8a85 | ||
|
|
8c47bf9dd3 | ||
|
|
3a26383c4d | ||
|
|
634976cdde | ||
|
|
bc21f5955f | ||
|
|
e72eb35cc2 | ||
|
|
fbe4e64e44 | ||
|
|
be301dc090 | ||
|
|
250af7f247 | ||
|
|
10577cd1e5 | ||
|
|
62593f60c5 | ||
|
|
89ed8c2173 | ||
|
|
321dd252ea | ||
|
|
19325d552a | ||
|
|
43f8ec46cc | ||
|
|
cb9f78540a | ||
|
|
0a15088572 | ||
|
|
ec68338a20 | ||
|
|
173b35f77d | ||
|
|
f47831c339 | ||
|
|
e7d4f63884 | ||
|
|
4e81083bb4 | ||
|
|
0446a5ae73 | ||
|
|
a2aec4340d | ||
|
|
fda68c114c | ||
|
|
de5c55160b | ||
|
|
d0ac6345c2 | ||
|
|
d9911f4314 | ||
|
|
92bd29ebf1 | ||
|
|
00ac1f7ec9 | ||
|
|
12641ab0c0 | ||
|
|
629b5ff171 | ||
|
|
6ec5858561 | ||
|
|
ab7550947a | ||
|
|
f2078f6d5b | ||
|
|
1a440e3a83 | ||
|
|
b8acce115f | ||
|
|
577ba9b397 | ||
|
|
5f199432f0 | ||
|
|
8b10fc497d | ||
|
|
e80d09aa17 | ||
|
|
4c1e12921a | ||
|
|
1824a9a38e | ||
|
|
3e59535a53 | ||
|
|
8c174cd548 | ||
|
|
2a4d1c978e | ||
|
|
cc91a6d96f | ||
|
|
96ace89789 | ||
|
|
d226477852 | ||
|
|
eddb5fa91e | ||
|
|
c92e00c21f | ||
|
|
f07d29bc8a | ||
|
|
f7b4cbc94a | ||
|
|
d226a4a0b7 | ||
|
|
c62659cdbc | ||
|
|
4831e9705c | ||
|
|
fce74a710f | ||
|
|
39641f05b9 | ||
|
|
2a23537dbd | ||
|
|
ca3b9f253d | ||
|
|
1a0abfbc06 | ||
|
|
6e9149afd4 | ||
|
|
0a7bf4db47 | ||
|
|
8a790fd0b3 | ||
|
|
fd3dab35cc | ||
|
|
5fd179524f | ||
|
|
f8a7beb001 | ||
|
|
02903cc98c | ||
|
|
7c5993b1cd | ||
|
|
a6f78134c0 | ||
|
|
21d4f49d31 | ||
|
|
3f4bd13091 | ||
|
|
6125288e95 | ||
|
|
7f91c9e63e | ||
|
|
ec319f950f | ||
|
|
217ddfe98d | ||
|
|
57578a3aa3 | ||
|
|
9a7e21778e | ||
|
|
cdb42b33ce | ||
|
|
4b47bfb5db | ||
|
|
18deb8b4f2 | ||
|
|
e58ebb8656 | ||
|
|
05d7b79d07 | ||
|
|
d5c43e66fa | ||
|
|
5f3b17af64 | ||
|
|
7bec2b8417 | ||
|
|
70396240c3 | ||
|
|
ac79d8ed3e | ||
|
|
02de91f7f2 | ||
|
|
77fd296095 | ||
|
|
1cc3dd5ee4 | ||
|
|
41ad5d12a3 | ||
|
|
b26c960450 | ||
|
|
7ea12ddd61 | ||
|
|
4e1e47d9e5 | ||
|
|
3ccc8d9508 | ||
|
|
123e6b7de4 | ||
|
|
1caacaacf0 | ||
|
|
0b754ec65d | ||
|
|
647bb501d1 | ||
|
|
47c3d5ed23 | ||
|
|
b693d13144 | ||
|
|
a5ada6f487 | ||
|
|
60351f2677 | ||
|
|
1e2f038ef5 | ||
|
|
708e17162c | ||
|
|
60c60b4db1 | ||
|
|
a09c67772c | ||
|
|
028e3a6c35 | ||
|
|
a72d7652af | ||
|
|
76ad7f24ee | ||
|
|
1ac689b886 | ||
|
|
f32399454c | ||
|
|
f2665262f7 | ||
|
|
fee68cf333 | ||
|
|
1d737409ec | ||
|
|
897af615f9 | ||
|
|
ea6597c3ad | ||
|
|
5a31d2e817 | ||
|
|
09f1966e5f | ||
|
|
ad649aba48 | ||
|
|
c7edc36106 | ||
|
|
57d4ccfdfd | ||
|
|
550f9de41a | ||
|
|
f3aab569ca | ||
|
|
2f9e510f4f | ||
|
|
1d7fd8ac96 | ||
|
|
ccb4e967cf | ||
|
|
08f5af68f0 | ||
|
|
d51b87e80a | ||
|
|
c2edbdc487 | ||
|
|
5bc854bedc | ||
|
|
3e889ee06c | ||
|
|
9c338a5c81 | ||
|
|
d13b4f6698 | ||
|
|
cd0933522d | ||
|
|
dc599bbc63 | ||
|
|
4b82e90ffb | ||
|
|
36de881041 | ||
|
|
e0f80c9f91 | ||
|
|
55f6fe6d3a | ||
|
|
42f314669f | ||
|
|
9d273acd42 | ||
|
|
7fbe2754a4 | ||
|
|
c0043e5098 | ||
|
|
756e15fe19 | ||
|
|
d9218134e2 | ||
|
|
c66ba370d5 | ||
|
|
6fc0b2ecfb | ||
|
|
7b938b246a | ||
|
|
45cb98c8de | ||
|
|
0f8ea92a53 | ||
|
|
ac57e7ced2 | ||
|
|
513dc2fcc5 | ||
|
|
3c10ba4511 | ||
|
|
6f9d8ed01b | ||
|
|
3977cec408 | ||
|
|
7949ffe41e | ||
|
|
bd9e68e69f | ||
|
|
ccf689ffd5 | ||
|
|
b95b49bd19 | ||
|
|
dc8209837c | ||
|
|
582daffd7f | ||
|
|
30715c2512 | ||
|
|
51aea367c3 | ||
|
|
34e31f3d78 | ||
|
|
e250628174 | ||
|
|
eee968ce56 | ||
|
|
52f806ff94 | ||
|
|
58ebd8cc59 | ||
|
|
d08d1c9127 | ||
|
|
1a307f3093 | ||
|
|
dc18c012ed | ||
|
|
c46a82420d |
9
.gitignore
vendored
9
.gitignore
vendored
@@ -3,7 +3,12 @@ obj/*.o
|
||||
router.info
|
||||
router.keys
|
||||
i2p
|
||||
libi2pd.so
|
||||
netDb
|
||||
/i2pd
|
||||
/libi2pd.a
|
||||
/libi2pdclient.a
|
||||
|
||||
|
||||
# Autotools
|
||||
autom4te.cache
|
||||
@@ -229,3 +234,7 @@ pip-log.txt
|
||||
|
||||
#Mr Developer
|
||||
.mr.developer.cfg
|
||||
|
||||
# Sphinx
|
||||
docs/_build
|
||||
/androidIdea/
|
||||
|
||||
35
.travis.yml
Normal file
35
.travis.yml
Normal file
@@ -0,0 +1,35 @@
|
||||
language: cpp
|
||||
cache:
|
||||
apt: true
|
||||
os:
|
||||
- linux
|
||||
sudo: required
|
||||
dist: trusty
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- build-essential
|
||||
- cmake
|
||||
- g++
|
||||
- clang
|
||||
- libboost-chrono-dev
|
||||
- libboost-date-time-dev
|
||||
- libboost-filesystem-dev
|
||||
- libboost-program-options-dev
|
||||
- libboost-system-dev
|
||||
- libboost-thread-dev
|
||||
- libminiupnpc-dev
|
||||
- libssl-dev
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
before_install:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install openssl miniupnpc ; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew unlink boost openssl && brew link boost openssl -f ; fi
|
||||
env:
|
||||
matrix:
|
||||
- BUILD_TYPE=Release UPNP=ON
|
||||
- BUILD_TYPE=Release UPNP=OFF
|
||||
script:
|
||||
- cd build && cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DWITH_UPNP=${UPNP} && make
|
||||
903
AddressBook.cpp
903
AddressBook.cpp
File diff suppressed because it is too large
Load Diff
@@ -7,22 +7,30 @@
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <boost/asio.hpp>
|
||||
#include "base64.h"
|
||||
#include "util.h"
|
||||
#include "Base.h"
|
||||
#include "Identity.h"
|
||||
#include "Log.h"
|
||||
#include "Destination.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
const char DEFAULT_SUBSCRIPTION_ADDRESS[] = "http://udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p/hosts.txt";
|
||||
#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)
|
||||
const int CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT = 5; // in minutes
|
||||
const int SUBSCRIPTION_REQUEST_TIMEOUT = 60; //in second
|
||||
|
||||
const uint16_t ADDRESS_RESOLVER_DATAGRAM_PORT = 53;
|
||||
const uint16_t ADDRESS_RESPONSE_DATAGRAM_PORT = 54;
|
||||
|
||||
inline std::string GetB32Address(const i2p::data::IdentHash& ident) { return ident.ToBase32().append(".b32.i2p"); }
|
||||
|
||||
@@ -31,50 +39,71 @@ namespace client
|
||||
public:
|
||||
|
||||
virtual ~AddressBookStorage () {};
|
||||
virtual bool GetAddress (const i2p::data::IdentHash& ident, i2p::data::IdentityEx& address) const = 0;
|
||||
virtual void AddAddress (const i2p::data::IdentityEx& address) = 0;
|
||||
virtual std::shared_ptr<const i2p::data::IdentityEx> GetAddress (const i2p::data::IdentHash& ident) const = 0;
|
||||
virtual void AddAddress (std::shared_ptr<const i2p::data::IdentityEx> address) = 0;
|
||||
virtual void RemoveAddress (const i2p::data::IdentHash& ident) = 0;
|
||||
|
||||
virtual bool Init () = 0;
|
||||
virtual int Load (std::map<std::string, i2p::data::IdentHash>& addresses) = 0;
|
||||
virtual int LoadLocal (std::map<std::string, i2p::data::IdentHash>& addresses) = 0;
|
||||
virtual int Save (const std::map<std::string, i2p::data::IdentHash>& addresses) = 0;
|
||||
|
||||
virtual void SaveEtag (const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified) = 0;
|
||||
virtual bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified) = 0;
|
||||
};
|
||||
|
||||
class AddressBookSubscription;
|
||||
class AddressResolver;
|
||||
class AddressBook
|
||||
{
|
||||
public:
|
||||
|
||||
AddressBook ();
|
||||
~AddressBook ();
|
||||
void Start ();
|
||||
void StartResolvers ();
|
||||
void Stop ();
|
||||
bool GetIdentHash (const std::string& address, i2p::data::IdentHash& ident);
|
||||
bool GetAddress (const std::string& address, i2p::data::IdentityEx& identity);
|
||||
std::shared_ptr<const i2p::data::IdentityEx> GetAddress (const std::string& address);
|
||||
const i2p::data::IdentHash * FindAddress (const std::string& address);
|
||||
void LookupAddress (const std::string& address);
|
||||
void InsertAddress (const std::string& address, const std::string& base64); // for jump service
|
||||
void InsertAddress (const i2p::data::IdentityEx& address);
|
||||
void InsertAddress (std::shared_ptr<const i2p::data::IdentityEx> address);
|
||||
|
||||
bool LoadHostsFromStream (std::istream& f, bool is_update);
|
||||
void DownloadComplete (bool success, const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified);
|
||||
//This method returns the ".b32.i2p" address
|
||||
std::string ToAddress(const i2p::data::IdentHash& ident) { return GetB32Address(ident); }
|
||||
std::string ToAddress(std::shared_ptr<const i2p::data::IdentityEx> ident) { return ToAddress(ident->GetIdentHash ()); }
|
||||
|
||||
bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified);
|
||||
|
||||
private:
|
||||
|
||||
void StartSubscriptions ();
|
||||
void StopSubscriptions ();
|
||||
void LoadHostsFromStream (std::istream& f);
|
||||
void DownloadComplete (bool success);
|
||||
//This method returns the ".b32.i2p" address
|
||||
std::string ToAddress(const i2p::data::IdentHash& ident) { return GetB32Address(ident); }
|
||||
std::string ToAddress(const i2p::data::IdentityEx& ident) { return ToAddress(ident.GetIdentHash ()); }
|
||||
private:
|
||||
|
||||
AddressBookStorage * CreateStorage ();
|
||||
|
||||
void LoadHosts ();
|
||||
void LoadSubscriptions ();
|
||||
void LoadLocal ();
|
||||
|
||||
void HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode);
|
||||
|
||||
void StartLookups ();
|
||||
void StopLookups ();
|
||||
void HandleLookupResponse (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
||||
|
||||
private:
|
||||
|
||||
std::mutex m_AddressBookMutex;
|
||||
std::map<std::string, i2p::data::IdentHash> m_Addresses;
|
||||
std::map<i2p::data::IdentHash, std::shared_ptr<AddressResolver> > m_Resolvers; // local destination->resolver
|
||||
std::mutex m_LookupsMutex;
|
||||
std::map<uint32_t, std::string> m_Lookups; // nonce -> address
|
||||
AddressBookStorage * m_Storage;
|
||||
volatile bool m_IsLoaded, m_IsDownloading;
|
||||
std::vector<AddressBookSubscription *> m_Subscriptions;
|
||||
AddressBookSubscription * m_DefaultSubscription; // in case if we don't know any addresses yet
|
||||
std::vector<std::shared_ptr<AddressBookSubscription> > m_Subscriptions;
|
||||
std::shared_ptr<AddressBookSubscription> m_DefaultSubscription; // in case if we don't know any addresses yet
|
||||
boost::asio::deadline_timer * m_SubscriptionsUpdateTimer;
|
||||
};
|
||||
|
||||
@@ -83,16 +112,36 @@ namespace client
|
||||
public:
|
||||
|
||||
AddressBookSubscription (AddressBook& book, const std::string& link);
|
||||
void CheckSubscription ();
|
||||
void CheckUpdates ();
|
||||
|
||||
private:
|
||||
|
||||
void Request ();
|
||||
bool MakeRequest ();
|
||||
|
||||
private:
|
||||
|
||||
AddressBook& m_Book;
|
||||
std::string m_Link, m_Etag, m_LastModified;
|
||||
i2p::data::IdentHash m_Ident;
|
||||
// m_Etag must be surrounded by ""
|
||||
};
|
||||
|
||||
class AddressResolver
|
||||
{
|
||||
public:
|
||||
|
||||
AddressResolver (std::shared_ptr<ClientDestination> destination);
|
||||
~AddressResolver ();
|
||||
void AddAddress (const std::string& name, const i2p::data::IdentHash& ident);
|
||||
|
||||
private:
|
||||
|
||||
void HandleRequest (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
||||
|
||||
private:
|
||||
|
||||
std::shared_ptr<ClientDestination> m_LocalDestination;
|
||||
std::map<std::string, i2p::data::IdentHash> m_LocalAddresses;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
242
BOB.cpp
242
BOB.cpp
@@ -8,9 +8,9 @@ namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
BOBI2PInboundTunnel::BOBI2PInboundTunnel (int port, ClientDestination * localDestination):
|
||||
BOBI2PInboundTunnel::BOBI2PInboundTunnel (int port, std::shared_ptr<ClientDestination> localDestination):
|
||||
BOBI2PTunnel (localDestination),
|
||||
m_Acceptor (localDestination->GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)), m_Timer (localDestination->GetService ())
|
||||
m_Acceptor (localDestination->GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -33,27 +33,22 @@ namespace client
|
||||
|
||||
void BOBI2PInboundTunnel::Accept ()
|
||||
{
|
||||
auto receiver = new AddressReceiver ();
|
||||
receiver->socket = new boost::asio::ip::tcp::socket (GetService ());
|
||||
auto receiver = std::make_shared<AddressReceiver> ();
|
||||
receiver->socket = std::make_shared<boost::asio::ip::tcp::socket> (GetService ());
|
||||
m_Acceptor.async_accept (*receiver->socket, std::bind (&BOBI2PInboundTunnel::HandleAccept, this,
|
||||
std::placeholders::_1, receiver));
|
||||
}
|
||||
|
||||
void BOBI2PInboundTunnel::HandleAccept (const boost::system::error_code& ecode, AddressReceiver * receiver)
|
||||
void BOBI2PInboundTunnel::HandleAccept (const boost::system::error_code& ecode, std::shared_ptr<AddressReceiver> receiver)
|
||||
{
|
||||
if (!ecode)
|
||||
{
|
||||
Accept ();
|
||||
ReceiveAddress (receiver);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete receiver->socket;
|
||||
delete receiver;
|
||||
}
|
||||
}
|
||||
|
||||
void BOBI2PInboundTunnel::ReceiveAddress (AddressReceiver * receiver)
|
||||
void BOBI2PInboundTunnel::ReceiveAddress (std::shared_ptr<AddressReceiver> receiver)
|
||||
{
|
||||
receiver->socket->async_read_some (boost::asio::buffer(
|
||||
receiver->buffer + receiver->bufferOffset,
|
||||
@@ -63,14 +58,10 @@ namespace client
|
||||
}
|
||||
|
||||
void BOBI2PInboundTunnel::HandleReceivedAddress (const boost::system::error_code& ecode, std::size_t bytes_transferred,
|
||||
AddressReceiver * receiver)
|
||||
std::shared_ptr<AddressReceiver> receiver)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("BOB inbound tunnel read error: ", ecode.message ());
|
||||
delete receiver->socket;
|
||||
delete receiver;
|
||||
}
|
||||
LogPrint (eLogError, "BOB: inbound tunnel read error: ", ecode.message ());
|
||||
else
|
||||
{
|
||||
receiver->bufferOffset += bytes_transferred;
|
||||
@@ -79,70 +70,51 @@ namespace client
|
||||
if (eol)
|
||||
{
|
||||
*eol = 0;
|
||||
|
||||
if (eol != receiver->buffer && eol[-1] == '\r') eol[-1] = 0; // workaround for Transmission, it sends '\r\n' terminated address
|
||||
receiver->data = (uint8_t *)eol + 1;
|
||||
receiver->dataLen = receiver->bufferOffset - (eol - receiver->buffer + 1);
|
||||
i2p::data::IdentHash ident;
|
||||
if (!context.GetAddressBook ().GetIdentHash (receiver->buffer, ident))
|
||||
{
|
||||
LogPrint (eLogError, "BOB address ", receiver->buffer, " not found");
|
||||
delete receiver->socket;
|
||||
delete receiver;
|
||||
LogPrint (eLogError, "BOB: address ", receiver->buffer, " not found");
|
||||
return;
|
||||
}
|
||||
auto leaseSet = GetLocalDestination ()->FindLeaseSet (ident);
|
||||
if (leaseSet)
|
||||
CreateConnection (receiver, leaseSet);
|
||||
else
|
||||
{
|
||||
GetLocalDestination ()->RequestDestination (ident);
|
||||
m_Timer.expires_from_now (boost::posix_time::seconds (I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT));
|
||||
m_Timer.async_wait (std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestTimer,
|
||||
this, std::placeholders::_1, receiver, ident));
|
||||
}
|
||||
GetLocalDestination ()->RequestDestination (ident,
|
||||
std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestComplete,
|
||||
this, std::placeholders::_1, receiver));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (receiver->bufferOffset < BOB_COMMAND_BUFFER_SIZE)
|
||||
ReceiveAddress (receiver);
|
||||
else
|
||||
{
|
||||
LogPrint ("BOB missing inbound address ");
|
||||
delete receiver->socket;
|
||||
delete receiver;
|
||||
}
|
||||
LogPrint (eLogError, "BOB: missing inbound address");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BOBI2PInboundTunnel::HandleDestinationRequestTimer (const boost::system::error_code& ecode, AddressReceiver * receiver, i2p::data::IdentHash ident)
|
||||
void BOBI2PInboundTunnel::HandleDestinationRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::shared_ptr<AddressReceiver> receiver)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
auto leaseSet = GetLocalDestination ()->FindLeaseSet (ident);
|
||||
if (leaseSet)
|
||||
{
|
||||
CreateConnection (receiver, leaseSet);
|
||||
return;
|
||||
}
|
||||
else
|
||||
LogPrint ("LeaseSet for BOB inbound destination not found");
|
||||
}
|
||||
delete receiver->socket;
|
||||
delete receiver;
|
||||
if (leaseSet)
|
||||
CreateConnection (receiver, leaseSet);
|
||||
else
|
||||
LogPrint (eLogError, "BOB: LeaseSet for inbound destination not found");
|
||||
}
|
||||
|
||||
void BOBI2PInboundTunnel::CreateConnection (AddressReceiver * receiver, std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
|
||||
void BOBI2PInboundTunnel::CreateConnection (std::shared_ptr<AddressReceiver> receiver, std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
|
||||
{
|
||||
LogPrint ("New BOB inbound connection");
|
||||
LogPrint (eLogDebug, "BOB: New inbound connection");
|
||||
auto connection = std::make_shared<I2PTunnelConnection>(this, receiver->socket, leaseSet);
|
||||
AddHandler (connection);
|
||||
connection->I2PConnect (receiver->data, receiver->dataLen);
|
||||
delete receiver;
|
||||
}
|
||||
|
||||
BOBI2POutboundTunnel::BOBI2POutboundTunnel (const std::string& address, int port,
|
||||
ClientDestination * localDestination, bool quiet): BOBI2PTunnel (localDestination),
|
||||
std::shared_ptr<ClientDestination> localDestination, bool quiet): BOBI2PTunnel (localDestination),
|
||||
m_Endpoint (boost::asio::ip::address::from_string (address), port), m_IsQuiet (quiet)
|
||||
{
|
||||
}
|
||||
@@ -163,20 +135,20 @@ namespace client
|
||||
if (localDestination)
|
||||
localDestination->AcceptStreams (std::bind (&BOBI2POutboundTunnel::HandleAccept, this, std::placeholders::_1));
|
||||
else
|
||||
LogPrint ("Local destination not set for server tunnel");
|
||||
LogPrint (eLogError, "BOB: Local destination not set for server tunnel");
|
||||
}
|
||||
|
||||
void BOBI2POutboundTunnel::HandleAccept (std::shared_ptr<i2p::stream::Stream> stream)
|
||||
{
|
||||
if (stream)
|
||||
{
|
||||
auto conn = std::make_shared<I2PTunnelConnection> (this, stream, new boost::asio::ip::tcp::socket (GetService ()), m_Endpoint, m_IsQuiet);
|
||||
auto conn = std::make_shared<I2PTunnelConnection> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), m_Endpoint, m_IsQuiet);
|
||||
AddHandler (conn);
|
||||
conn->Connect ();
|
||||
}
|
||||
}
|
||||
|
||||
BOBDestination::BOBDestination (ClientDestination& localDestination):
|
||||
BOBDestination::BOBDestination (std::shared_ptr<ClientDestination> localDestination):
|
||||
m_LocalDestination (localDestination),
|
||||
m_OutboundTunnel (nullptr), m_InboundTunnel (nullptr)
|
||||
{
|
||||
@@ -186,7 +158,7 @@ namespace client
|
||||
{
|
||||
delete m_OutboundTunnel;
|
||||
delete m_InboundTunnel;
|
||||
i2p::client::context.DeleteLocalDestination (&m_LocalDestination);
|
||||
i2p::client::context.DeleteLocalDestination (m_LocalDestination);
|
||||
}
|
||||
|
||||
void BOBDestination::Start ()
|
||||
@@ -198,7 +170,7 @@ namespace client
|
||||
void BOBDestination::Stop ()
|
||||
{
|
||||
StopTunnels ();
|
||||
m_LocalDestination.Stop ();
|
||||
m_LocalDestination->Stop ();
|
||||
}
|
||||
|
||||
void BOBDestination::StopTunnels ()
|
||||
@@ -220,19 +192,19 @@ namespace client
|
||||
void BOBDestination::CreateInboundTunnel (int port)
|
||||
{
|
||||
if (!m_InboundTunnel)
|
||||
m_InboundTunnel = new BOBI2PInboundTunnel (port, &m_LocalDestination);
|
||||
m_InboundTunnel = new BOBI2PInboundTunnel (port, m_LocalDestination);
|
||||
}
|
||||
|
||||
void BOBDestination::CreateOutboundTunnel (const std::string& address, int port, bool quiet)
|
||||
{
|
||||
if (!m_OutboundTunnel)
|
||||
m_OutboundTunnel = new BOBI2POutboundTunnel (address, port, &m_LocalDestination, quiet);
|
||||
m_OutboundTunnel = new BOBI2POutboundTunnel (address, port, m_LocalDestination, quiet);
|
||||
}
|
||||
|
||||
BOBCommandSession::BOBCommandSession (BOBCommandChannel& owner):
|
||||
m_Owner (owner), m_Socket (m_Owner.GetService ()), m_ReceiveBufferOffset (0),
|
||||
m_IsOpen (true), m_IsQuiet (false), m_InPort (0), m_OutPort (0),
|
||||
m_CurrentDestination (nullptr)
|
||||
m_Owner (owner), m_Socket (m_Owner.GetService ()),
|
||||
m_ReceiveBufferOffset (0), m_IsOpen (true), m_IsQuiet (false), m_IsActive (false),
|
||||
m_InPort (0), m_OutPort (0), m_CurrentDestination (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -257,7 +229,7 @@ namespace client
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("BOB command channel read error: ", ecode.message ());
|
||||
LogPrint (eLogError, "BOB: command channel read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
@@ -284,7 +256,7 @@ namespace client
|
||||
(this->*(it->second))(operand, eol - operand);
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "BOB unknown command ", m_ReceiveBuffer);
|
||||
LogPrint (eLogError, "BOB: unknown command ", m_ReceiveBuffer);
|
||||
SendReplyError ("unknown command");
|
||||
}
|
||||
|
||||
@@ -297,7 +269,7 @@ namespace client
|
||||
m_ReceiveBufferOffset = size;
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "Malformed input of the BOB command channel");
|
||||
LogPrint (eLogError, "BOB: Malformed input of the command channel");
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
@@ -316,7 +288,7 @@ namespace client
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("BOB command channel send error: ", ecode.message ());
|
||||
LogPrint (eLogError, "BOB: command channel send error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
@@ -382,9 +354,14 @@ namespace client
|
||||
void BOBCommandSession::StartCommandHandler (const char * operand, size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "BOB: start ", m_Nickname);
|
||||
if (m_IsActive)
|
||||
{
|
||||
SendReplyError ("tunnel is active");
|
||||
return;
|
||||
}
|
||||
if (!m_CurrentDestination)
|
||||
{
|
||||
m_CurrentDestination = new BOBDestination (*i2p::client::context.CreateNewLocalDestination (m_Keys, true, &m_Options));
|
||||
m_CurrentDestination = new BOBDestination (i2p::client::context.CreateNewLocalDestination (m_Keys, true, &m_Options));
|
||||
m_Owner.AddDestination (m_Nickname, m_CurrentDestination);
|
||||
}
|
||||
if (m_InPort)
|
||||
@@ -392,19 +369,27 @@ namespace client
|
||||
if (m_OutPort && !m_Address.empty ())
|
||||
m_CurrentDestination->CreateOutboundTunnel (m_Address, m_OutPort, m_IsQuiet);
|
||||
m_CurrentDestination->Start ();
|
||||
SendReplyOK ("tunnel starting");
|
||||
SendReplyOK ("Tunnel starting");
|
||||
m_IsActive = true;
|
||||
}
|
||||
|
||||
|
||||
void BOBCommandSession::StopCommandHandler (const char * operand, size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "BOB: stop ", m_Nickname);
|
||||
if (!m_IsActive)
|
||||
{
|
||||
SendReplyError ("tunnel is inactive");
|
||||
return;
|
||||
}
|
||||
auto dest = m_Owner.FindDestination (m_Nickname);
|
||||
if (dest)
|
||||
{
|
||||
dest->StopTunnels ();
|
||||
SendReplyOK ("tunnel stopping");
|
||||
SendReplyOK ("Tunnel stopping");
|
||||
}
|
||||
else
|
||||
SendReplyError ("tunnel not found");
|
||||
m_IsActive = false;
|
||||
}
|
||||
|
||||
void BOBCommandSession::SetNickCommandHandler (const char * operand, size_t len)
|
||||
@@ -412,7 +397,7 @@ namespace client
|
||||
LogPrint (eLogDebug, "BOB: setnick ", operand);
|
||||
m_Nickname = operand;
|
||||
std::string msg ("Nickname set to ");
|
||||
msg += operand;
|
||||
msg += m_Nickname;
|
||||
SendReplyOK (msg.c_str ());
|
||||
}
|
||||
|
||||
@@ -424,26 +409,29 @@ namespace client
|
||||
{
|
||||
m_Keys = m_CurrentDestination->GetKeys ();
|
||||
m_Nickname = operand;
|
||||
std::string msg ("Nickname set to ");
|
||||
msg += operand;
|
||||
SendReplyOK (msg.c_str ());
|
||||
}
|
||||
if (m_Nickname == operand)
|
||||
{
|
||||
std::string msg ("Nickname set to ");
|
||||
msg += m_Nickname;
|
||||
SendReplyOK (msg.c_str ());
|
||||
}
|
||||
else
|
||||
SendReplyError ("tunnel not found");
|
||||
SendReplyError ("no nickname has been set");
|
||||
}
|
||||
|
||||
void BOBCommandSession::NewkeysCommandHandler (const char * operand, size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "BOB: newkeys");
|
||||
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys ();
|
||||
SendReplyOK (m_Keys.GetPublic ().ToBase64 ().c_str ());
|
||||
SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ());
|
||||
}
|
||||
|
||||
void BOBCommandSession::SetkeysCommandHandler (const char * operand, size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "BOB: setkeys ", operand);
|
||||
m_Keys.FromBase64 (operand);
|
||||
SendReplyOK (m_Keys.GetPublic ().ToBase64 ().c_str ());
|
||||
SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ());
|
||||
}
|
||||
|
||||
void BOBCommandSession::GetkeysCommandHandler (const char * operand, size_t len)
|
||||
@@ -455,7 +443,7 @@ namespace client
|
||||
void BOBCommandSession::GetdestCommandHandler (const char * operand, size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "BOB: getdest");
|
||||
SendReplyOK (m_Keys.GetPublic ().ToBase64 ().c_str ());
|
||||
SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ());
|
||||
}
|
||||
|
||||
void BOBCommandSession::OuthostCommandHandler (const char * operand, size_t len)
|
||||
@@ -469,7 +457,10 @@ namespace client
|
||||
{
|
||||
LogPrint (eLogDebug, "BOB: outport ", operand);
|
||||
m_OutPort = boost::lexical_cast<int>(operand);
|
||||
SendReplyOK ("outbound port set");
|
||||
if (m_OutPort >= 0)
|
||||
SendReplyOK ("outbound port set");
|
||||
else
|
||||
SendReplyError ("port out of range");
|
||||
}
|
||||
|
||||
void BOBCommandSession::InhostCommandHandler (const char * operand, size_t len)
|
||||
@@ -483,40 +474,70 @@ namespace client
|
||||
{
|
||||
LogPrint (eLogDebug, "BOB: inport ", operand);
|
||||
m_InPort = boost::lexical_cast<int>(operand);
|
||||
SendReplyOK ("inbound port set");
|
||||
if (m_InPort >= 0)
|
||||
SendReplyOK ("inbound port set");
|
||||
else
|
||||
SendReplyError ("port out of range");
|
||||
}
|
||||
|
||||
void BOBCommandSession::QuietCommandHandler (const char * operand, size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "BOB: quiet");
|
||||
m_IsQuiet = true;
|
||||
SendReplyOK ("quiet");
|
||||
if (m_Nickname.length () > 0)
|
||||
{
|
||||
if (!m_IsActive)
|
||||
{
|
||||
m_IsQuiet = true;
|
||||
SendReplyOK ("Quiet set");
|
||||
}
|
||||
else
|
||||
SendReplyError ("tunnel is active");
|
||||
}
|
||||
else
|
||||
SendReplyError ("no nickname has been set");
|
||||
}
|
||||
|
||||
void BOBCommandSession::LookupCommandHandler (const char * operand, size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "BOB: lookup ", operand);
|
||||
i2p::data::IdentityEx addr;
|
||||
if (!context.GetAddressBook ().GetAddress (operand, addr))
|
||||
i2p::data::IdentHash ident;
|
||||
if (!context.GetAddressBook ().GetIdentHash (operand, ident) || !m_CurrentDestination)
|
||||
{
|
||||
SendReplyError ("Address Not found");
|
||||
return;
|
||||
}
|
||||
SendReplyOK (addr.ToBase64 ().c_str ());
|
||||
}
|
||||
auto localDestination = m_CurrentDestination->GetLocalDestination ();
|
||||
auto leaseSet = localDestination->FindLeaseSet (ident);
|
||||
if (leaseSet)
|
||||
SendReplyOK (leaseSet->GetIdentity ()->ToBase64 ().c_str ());
|
||||
else
|
||||
{
|
||||
auto s = shared_from_this ();
|
||||
localDestination->RequestDestination (ident,
|
||||
[s](std::shared_ptr<i2p::data::LeaseSet> ls)
|
||||
{
|
||||
if (ls)
|
||||
s->SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ());
|
||||
else
|
||||
s->SendReplyError ("LeaseSet Not found");
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void BOBCommandSession::ClearCommandHandler (const char * operand, size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "BOB: clear");
|
||||
m_Owner.DeleteDestination (m_Nickname);
|
||||
m_Nickname = "";
|
||||
SendReplyOK ("cleared");
|
||||
}
|
||||
|
||||
void BOBCommandSession::ListCommandHandler (const char * operand, size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "BOB: list");
|
||||
auto& destinations = m_Owner.GetDestinations ();
|
||||
for (auto it: destinations)
|
||||
const auto& destinations = m_Owner.GetDestinations ();
|
||||
for (const auto& it: destinations)
|
||||
SendData (it.first.c_str ());
|
||||
SendReplyOK ("Listing done");
|
||||
}
|
||||
@@ -527,18 +548,50 @@ namespace client
|
||||
const char * value = strchr (operand, '=');
|
||||
if (value)
|
||||
{
|
||||
std::string msg ("option ");
|
||||
*(const_cast<char *>(value)) = 0;
|
||||
m_Options[operand] = value + 1;
|
||||
msg += operand;
|
||||
*(const_cast<char *>(value)) = '=';
|
||||
SendReplyOK ("option");
|
||||
msg += " set to ";
|
||||
msg += value;
|
||||
SendReplyOK (msg.c_str ());
|
||||
}
|
||||
else
|
||||
SendReplyError ("malformed");
|
||||
}
|
||||
|
||||
void BOBCommandSession::StatusCommandHandler (const char * operand, size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "BOB: status ", operand);
|
||||
if (m_Nickname == operand)
|
||||
{
|
||||
std::stringstream s;
|
||||
s << "DATA"; s << " NICKNAME: "; s << m_Nickname;
|
||||
if (m_CurrentDestination->GetLocalDestination ()->IsReady ())
|
||||
s << " STARTING: false RUNNING: true STOPPING: false";
|
||||
else
|
||||
s << " STARTING: true RUNNING: false STOPPING: false";
|
||||
s << " KEYS: true"; s << " QUIET: "; s << (m_IsQuiet ? "true":"false");
|
||||
if (m_InPort)
|
||||
{
|
||||
s << " INPORT: " << m_InPort;
|
||||
s << " INHOST: " << (m_Address.length () > 0 ? m_Address : "127.0.0.1");
|
||||
}
|
||||
if (m_OutPort)
|
||||
{
|
||||
s << " OUTPORT: " << m_OutPort;
|
||||
s << " OUTHOST: " << (m_Address.length () > 0 ? m_Address : "127.0.0.1");
|
||||
}
|
||||
SendReplyOK (s.str().c_str());
|
||||
}
|
||||
else
|
||||
SendReplyError ("no nickname has been set");
|
||||
}
|
||||
|
||||
BOBCommandChannel::BOBCommandChannel (int port):
|
||||
BOBCommandChannel::BOBCommandChannel (const std::string& address, int port):
|
||||
m_IsRunning (false), m_Thread (nullptr),
|
||||
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port))
|
||||
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port))
|
||||
{
|
||||
// command -> handler
|
||||
m_CommandHandlers[BOB_COMMAND_ZAP] = &BOBCommandSession::ZapCommandHandler;
|
||||
@@ -560,12 +613,13 @@ namespace client
|
||||
m_CommandHandlers[BOB_COMMAND_CLEAR] = &BOBCommandSession::ClearCommandHandler;
|
||||
m_CommandHandlers[BOB_COMMAND_LIST] = &BOBCommandSession::ListCommandHandler;
|
||||
m_CommandHandlers[BOB_COMMAND_OPTION] = &BOBCommandSession::OptionCommandHandler;
|
||||
m_CommandHandlers[BOB_COMMAND_STATUS] = &BOBCommandSession::StatusCommandHandler;
|
||||
}
|
||||
|
||||
BOBCommandChannel::~BOBCommandChannel ()
|
||||
{
|
||||
Stop ();
|
||||
for (auto it: m_Destinations)
|
||||
for (const auto& it: m_Destinations)
|
||||
delete it.second;
|
||||
}
|
||||
|
||||
@@ -579,7 +633,7 @@ namespace client
|
||||
void BOBCommandChannel::Stop ()
|
||||
{
|
||||
m_IsRunning = false;
|
||||
for (auto it: m_Destinations)
|
||||
for (auto& it: m_Destinations)
|
||||
it.second->Stop ();
|
||||
m_Acceptor.cancel ();
|
||||
m_Service.stop ();
|
||||
@@ -601,7 +655,7 @@ namespace client
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "BOB: ", ex.what ());
|
||||
LogPrint (eLogError, "BOB: runtime exception: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -644,11 +698,11 @@ namespace client
|
||||
|
||||
if (!ecode)
|
||||
{
|
||||
LogPrint (eLogInfo, "New BOB command connection from ", session->GetSocket ().remote_endpoint ());
|
||||
LogPrint (eLogInfo, "BOB: New command connection from ", session->GetSocket ().remote_endpoint ());
|
||||
session->SendVersion ();
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "BOB accept error: ", ecode.message ());
|
||||
LogPrint (eLogError, "BOB: accept error: ", ecode.message ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
34
BOB.h
34
BOB.h
@@ -36,6 +36,7 @@ namespace client
|
||||
const char BOB_COMMAND_CLEAR[] = "clear";
|
||||
const char BOB_COMMAND_LIST[] = "list";
|
||||
const char BOB_COMMAND_OPTION[] = "option";
|
||||
const char BOB_COMMAND_STATUS[] = "status";
|
||||
|
||||
const char BOB_VERSION[] = "BOB 00.00.10\nOK\n";
|
||||
const char BOB_REPLY_OK[] = "OK %s\n";
|
||||
@@ -46,7 +47,7 @@ namespace client
|
||||
{
|
||||
public:
|
||||
|
||||
BOBI2PTunnel (ClientDestination * localDestination):
|
||||
BOBI2PTunnel (std::shared_ptr<ClientDestination> localDestination):
|
||||
I2PService (localDestination) {};
|
||||
|
||||
virtual void Start () {};
|
||||
@@ -57,9 +58,9 @@ namespace client
|
||||
{
|
||||
struct AddressReceiver
|
||||
{
|
||||
boost::asio::ip::tcp::socket * socket;
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> socket;
|
||||
char buffer[BOB_COMMAND_BUFFER_SIZE + 1]; // for destination base64 address
|
||||
uint8_t * data;
|
||||
uint8_t * data; // pointer to buffer
|
||||
size_t dataLen, bufferOffset;
|
||||
|
||||
AddressReceiver (): data (nullptr), dataLen (0), bufferOffset (0) {};
|
||||
@@ -67,7 +68,7 @@ namespace client
|
||||
|
||||
public:
|
||||
|
||||
BOBI2PInboundTunnel (int port, ClientDestination * localDestination);
|
||||
BOBI2PInboundTunnel (int port, std::shared_ptr<ClientDestination> localDestination);
|
||||
~BOBI2PInboundTunnel ();
|
||||
|
||||
void Start ();
|
||||
@@ -76,27 +77,26 @@ namespace client
|
||||
private:
|
||||
|
||||
void Accept ();
|
||||
void HandleAccept (const boost::system::error_code& ecode, AddressReceiver * receiver);
|
||||
void HandleAccept (const boost::system::error_code& ecode, std::shared_ptr<AddressReceiver> receiver);
|
||||
|
||||
void ReceiveAddress (AddressReceiver * receiver);
|
||||
void ReceiveAddress (std::shared_ptr<AddressReceiver> receiver);
|
||||
void HandleReceivedAddress (const boost::system::error_code& ecode, std::size_t bytes_transferred,
|
||||
AddressReceiver * receiver);
|
||||
std::shared_ptr<AddressReceiver> receiver);
|
||||
|
||||
void HandleDestinationRequestTimer (const boost::system::error_code& ecode, AddressReceiver * receiver, i2p::data::IdentHash ident);
|
||||
void HandleDestinationRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::shared_ptr<AddressReceiver> receiver);
|
||||
|
||||
void CreateConnection (AddressReceiver * receiver, std::shared_ptr<const i2p::data::LeaseSet> leaseSet);
|
||||
void CreateConnection (std::shared_ptr<AddressReceiver> receiver, std::shared_ptr<const i2p::data::LeaseSet> leaseSet);
|
||||
|
||||
private:
|
||||
|
||||
boost::asio::ip::tcp::acceptor m_Acceptor;
|
||||
boost::asio::deadline_timer m_Timer;
|
||||
};
|
||||
|
||||
class BOBI2POutboundTunnel: public BOBI2PTunnel
|
||||
{
|
||||
public:
|
||||
|
||||
BOBI2POutboundTunnel (const std::string& address, int port, ClientDestination * localDestination, bool quiet);
|
||||
BOBI2POutboundTunnel (const std::string& address, int port, std::shared_ptr<ClientDestination> localDestination, bool quiet);
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
@@ -119,7 +119,7 @@ namespace client
|
||||
{
|
||||
public:
|
||||
|
||||
BOBDestination (ClientDestination& localDestination);
|
||||
BOBDestination (std::shared_ptr<ClientDestination> localDestination);
|
||||
~BOBDestination ();
|
||||
|
||||
void Start ();
|
||||
@@ -127,11 +127,12 @@ namespace client
|
||||
void StopTunnels ();
|
||||
void CreateInboundTunnel (int port);
|
||||
void CreateOutboundTunnel (const std::string& address, int port, bool quiet);
|
||||
const i2p::data::PrivateKeys& GetKeys () const { return m_LocalDestination.GetPrivateKeys (); };
|
||||
const i2p::data::PrivateKeys& GetKeys () const { return m_LocalDestination->GetPrivateKeys (); };
|
||||
std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDestination; };
|
||||
|
||||
private:
|
||||
|
||||
ClientDestination& m_LocalDestination;
|
||||
std::shared_ptr<ClientDestination> m_LocalDestination;
|
||||
BOBI2POutboundTunnel * m_OutboundTunnel;
|
||||
BOBI2PInboundTunnel * m_InboundTunnel;
|
||||
};
|
||||
@@ -168,6 +169,7 @@ namespace client
|
||||
void ClearCommandHandler (const char * operand, size_t len);
|
||||
void ListCommandHandler (const char * operand, size_t len);
|
||||
void OptionCommandHandler (const char * operand, size_t len);
|
||||
void StatusCommandHandler (const char * operand, size_t len);
|
||||
|
||||
private:
|
||||
|
||||
@@ -186,7 +188,7 @@ namespace client
|
||||
boost::asio::ip::tcp::socket m_Socket;
|
||||
char m_ReceiveBuffer[BOB_COMMAND_BUFFER_SIZE + 1], m_SendBuffer[BOB_COMMAND_BUFFER_SIZE + 1];
|
||||
size_t m_ReceiveBufferOffset;
|
||||
bool m_IsOpen, m_IsQuiet;
|
||||
bool m_IsOpen, m_IsQuiet, m_IsActive;
|
||||
std::string m_Nickname, m_Address;
|
||||
int m_InPort, m_OutPort;
|
||||
i2p::data::PrivateKeys m_Keys;
|
||||
@@ -199,7 +201,7 @@ namespace client
|
||||
{
|
||||
public:
|
||||
|
||||
BOBCommandChannel (int port);
|
||||
BOBCommandChannel (const std::string& address, int port);
|
||||
~BOBCommandChannel ();
|
||||
|
||||
void Start ();
|
||||
|
||||
@@ -1,10 +1,23 @@
|
||||
#include <stdlib.h>
|
||||
#include "base64.h"
|
||||
#include <string.h>
|
||||
|
||||
#include "Base.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
static const char T32[32] = {
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
|
||||
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
|
||||
'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
|
||||
'y', 'z', '2', '3', '4', '5', '6', '7',
|
||||
};
|
||||
|
||||
const char * GetBase32SubstitutionTable ()
|
||||
{
|
||||
return T32;
|
||||
}
|
||||
|
||||
static void iT64Build(void);
|
||||
|
||||
@@ -16,7 +29,7 @@ namespace data
|
||||
* Direct Substitution Table
|
||||
*/
|
||||
|
||||
static char T64[64] = {
|
||||
static const char T64[64] = {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
@@ -154,7 +167,7 @@ namespace data
|
||||
if (isFirstTime) iT64Build();
|
||||
n = InCount/4;
|
||||
m = InCount%4;
|
||||
if (!m)
|
||||
if (InCount && !m)
|
||||
outCount = 3*n;
|
||||
else {
|
||||
outCount = 0;
|
||||
@@ -190,6 +203,13 @@ namespace data
|
||||
return outCount;
|
||||
}
|
||||
|
||||
size_t Base64EncodingBufferSize (const size_t input_size)
|
||||
{
|
||||
auto d = div (input_size, 3);
|
||||
if (d.rem) d.quot++;
|
||||
return 4*d.quot;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* iT64
|
||||
@@ -267,3 +287,4 @@ namespace data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
#ifndef BASE64_H
|
||||
#define BASE64_H
|
||||
#ifndef BASE_H__
|
||||
#define BASE_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
namespace i2p {
|
||||
namespace data {
|
||||
size_t ByteStreamToBase64 (const uint8_t * InBuffer, size_t InCount, char * OutBuffer, size_t len);
|
||||
size_t Base64ToByteStream (const char * InBuffer, size_t InCount, uint8_t * OutBuffer, size_t len );
|
||||
const char * GetBase32SubstitutionTable ();
|
||||
const char * GetBase64SubstitutionTable ();
|
||||
|
||||
size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen);
|
||||
size_t ByteStreamToBase32 (const uint8_t * InBuf, size_t len, char * outBuf, size_t outLen);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Compute the size for a buffer to contain encoded base64 given that the size of the input is input_size bytes
|
||||
*/
|
||||
size_t Base64EncodingBufferSize(const size_t input_size);
|
||||
} // data
|
||||
} // i2p
|
||||
|
||||
#endif
|
||||
|
||||
95
ChangeLog
Normal file
95
ChangeLog
Normal file
@@ -0,0 +1,95 @@
|
||||
# for this file format description,
|
||||
# see https://github.com/olivierlacan/keep-a-changelog
|
||||
|
||||
## [2.9.0] - UNRELEASED
|
||||
### Changed
|
||||
- Proxy refactoring & speedup
|
||||
|
||||
## [2.8.0] - 2016-06-20
|
||||
### Added
|
||||
- Basic Android support
|
||||
- I2CP implementation
|
||||
- 'doxygen' target
|
||||
|
||||
### Changed
|
||||
- I2PControl refactoring & fixes (proper jsonrpc responses on errors)
|
||||
- boost::regex no more needed
|
||||
|
||||
### Fixed
|
||||
- initscripts: added openrc one, in sysv-ish make I2PD_PORT optional
|
||||
- properly close NTCP sessions (memleak)
|
||||
|
||||
## [2.7.0] - 2016-05-18
|
||||
### Added
|
||||
- Precomputed El-Gamal/DH tables
|
||||
- Configurable limit of transit tunnels
|
||||
|
||||
### Changed
|
||||
- Speed-up of assymetric crypto for non-x64 platforms
|
||||
- Refactoring of web-console
|
||||
|
||||
## [2.6.0] - 2016-03-31
|
||||
### Added
|
||||
- Gracefull shutdown on SIGINT
|
||||
- Numeric bandwidth limits (was: by router class)
|
||||
- Jumpservices in web-console
|
||||
- Logging to syslog
|
||||
- Tray icon for windows application
|
||||
|
||||
### Changed
|
||||
- Logs refactoring
|
||||
- Improved statistics in web-console
|
||||
|
||||
### Deprecated:
|
||||
- Renamed main/tunnels config files (will use old, if found, but emits warning)
|
||||
|
||||
## [2.5.1] - 2016-03-10
|
||||
### Fixed
|
||||
- Doesn't create ~/.i2pd dir if missing
|
||||
|
||||
## [2.5.0] - 2016-03-04
|
||||
### Added
|
||||
- IRC server tunnels
|
||||
- SOCKS outproxy support
|
||||
- Support for gzipped addressbook updates
|
||||
- Support for router families
|
||||
|
||||
### Changed
|
||||
- Shared RTT/RTO between streams
|
||||
- Filesystem work refactoring
|
||||
|
||||
## [2.4.0] - 2016-02-03
|
||||
### Added
|
||||
- X-I2P-* headers for server http-tunnels
|
||||
- I2CP options for I2P tunnels
|
||||
- Show I2P tunnels in webconsole
|
||||
|
||||
### Changed
|
||||
- Refactoring of cmdline/config parsing
|
||||
|
||||
## [2.3.0] - 2016-01-12
|
||||
### Added
|
||||
- Support for new router bandwidth class codes (P and X)
|
||||
- I2PControl supports external webui
|
||||
- Added --pidfile and --notransit parameters
|
||||
- Ability to specify signature type for i2p tunnel
|
||||
|
||||
### Changed
|
||||
- Fixed multiple floodfill-related bugs
|
||||
- New webconsole layout
|
||||
|
||||
## [2.2.0] - 2015-12-22
|
||||
### Added
|
||||
- Ability to connect to router without ip via introducer
|
||||
|
||||
### Changed
|
||||
- Persist temporary encryption keys for local destinations
|
||||
- Performance improvements for EdDSA
|
||||
- New addressbook structure
|
||||
|
||||
## [2.1.0] - 2015-11-12
|
||||
### Added
|
||||
- Implementation of EdDSA
|
||||
|
||||
### Changed
|
||||
- EdDSA is default signature type for new RouterInfos
|
||||
@@ -1,10 +1,12 @@
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
#include <boost/program_options/variables_map.hpp>
|
||||
#include "util.h"
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/ini_parser.hpp>
|
||||
#include "Config.h"
|
||||
#include "FS.h"
|
||||
#include "Log.h"
|
||||
#include "Identity.h"
|
||||
#include "util.h"
|
||||
#include "ClientContext.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -15,7 +17,7 @@ namespace client
|
||||
|
||||
ClientContext::ClientContext (): m_SharedLocalDestination (nullptr),
|
||||
m_HttpProxy (nullptr), m_SocksProxy (nullptr), m_SamBridge (nullptr),
|
||||
m_BOBCommandChannel (nullptr), m_I2PControlService (nullptr)
|
||||
m_BOBCommandChannel (nullptr), m_I2CPServer (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -25,7 +27,7 @@ namespace client
|
||||
delete m_SocksProxy;
|
||||
delete m_SamBridge;
|
||||
delete m_BOBCommandChannel;
|
||||
delete m_I2PControlService;
|
||||
delete m_I2CPServer;
|
||||
}
|
||||
|
||||
void ClientContext::Start ()
|
||||
@@ -33,132 +35,180 @@ namespace client
|
||||
if (!m_SharedLocalDestination)
|
||||
{
|
||||
m_SharedLocalDestination = CreateNewLocalDestination (); // non-public, DSA
|
||||
m_Destinations[m_SharedLocalDestination->GetIdentity ().GetIdentHash ()] = m_SharedLocalDestination;
|
||||
m_Destinations[m_SharedLocalDestination->GetIdentity ()->GetIdentHash ()] = m_SharedLocalDestination;
|
||||
m_SharedLocalDestination->Start ();
|
||||
}
|
||||
|
||||
// proxies
|
||||
m_HttpProxy = new i2p::proxy::HTTPProxy(i2p::util::config::GetArg("-httpproxyport", 4446));
|
||||
m_HttpProxy->Start();
|
||||
LogPrint("HTTP Proxy started");
|
||||
m_SocksProxy = new i2p::proxy::SOCKSProxy(i2p::util::config::GetArg("-socksproxyport", 4447));
|
||||
m_SocksProxy->Start();
|
||||
LogPrint("SOCKS Proxy Started");
|
||||
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);
|
||||
LogPrint(eLogInfo, "Clients: starting HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort);
|
||||
if (httpProxyKeys.length () > 0)
|
||||
{
|
||||
i2p::data::PrivateKeys keys;
|
||||
LoadPrivateKeys (keys, httpProxyKeys);
|
||||
localDestination = CreateNewLocalDestination (keys, false);
|
||||
}
|
||||
try {
|
||||
m_HttpProxy = new i2p::proxy::HTTPProxy(httpProxyAddr, httpProxyPort, localDestination);
|
||||
m_HttpProxy->Start();
|
||||
} catch (std::exception& e) {
|
||||
LogPrint(eLogError, "Clients: Exception in HTTP Proxy: ", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
bool socksproxy; i2p::config::GetOption("socksproxy.enabled", 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);
|
||||
LogPrint(eLogInfo, "Clients: starting SOCKS Proxy at ", socksProxyAddr, ":", socksProxyPort);
|
||||
if (socksProxyKeys.length () > 0)
|
||||
{
|
||||
i2p::data::PrivateKeys keys;
|
||||
LoadPrivateKeys (keys, socksProxyKeys);
|
||||
localDestination = CreateNewLocalDestination (keys, false);
|
||||
}
|
||||
try {
|
||||
m_SocksProxy = new i2p::proxy::SOCKSProxy(socksProxyAddr, socksProxyPort, socksOutProxyAddr, socksOutProxyPort, localDestination);
|
||||
m_SocksProxy->Start();
|
||||
} catch (std::exception& e) {
|
||||
LogPrint(eLogError, "Clients: Exception in SOCKS Proxy: ", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
// I2P tunnels
|
||||
std::string ircDestination = i2p::util::config::GetArg("-ircdest", "");
|
||||
if (ircDestination.length () > 0) // ircdest is presented
|
||||
{
|
||||
ClientDestination * localDestination = nullptr;
|
||||
std::string ircKeys = i2p::util::config::GetArg("-irckeys", "");
|
||||
if (ircKeys.length () > 0)
|
||||
localDestination = LoadLocalDestination (ircKeys, false);
|
||||
auto ircPort = i2p::util::config::GetArg("-ircport", 6668);
|
||||
auto ircTunnel = new I2PClientTunnel (ircDestination, ircPort, localDestination);
|
||||
ircTunnel->Start ();
|
||||
m_ClientTunnels.insert (std::make_pair(ircPort, std::unique_ptr<I2PClientTunnel>(ircTunnel)));
|
||||
LogPrint("IRC tunnel started");
|
||||
}
|
||||
std::string eepKeys = i2p::util::config::GetArg("-eepkeys", "");
|
||||
if (eepKeys.length () > 0) // eepkeys file is presented
|
||||
{
|
||||
auto localDestination = LoadLocalDestination (eepKeys, true);
|
||||
auto serverTunnel = new I2PServerTunnel (i2p::util::config::GetArg("-eephost", "127.0.0.1"),
|
||||
i2p::util::config::GetArg("-eepport", 80), localDestination);
|
||||
serverTunnel->Start ();
|
||||
m_ServerTunnels.insert (std::make_pair(localDestination->GetIdentHash (), std::unique_ptr<I2PServerTunnel>(serverTunnel)));
|
||||
LogPrint("Server tunnel started");
|
||||
}
|
||||
ReadTunnels ();
|
||||
|
||||
// SAM
|
||||
int samPort = i2p::util::config::GetArg("-samport", 0);
|
||||
if (samPort)
|
||||
{
|
||||
m_SamBridge = new SAMBridge (samPort);
|
||||
m_SamBridge->Start ();
|
||||
LogPrint("SAM bridge started");
|
||||
bool sam; i2p::config::GetOption("sam.enabled", sam);
|
||||
if (sam) {
|
||||
std::string samAddr; i2p::config::GetOption("sam.address", samAddr);
|
||||
uint16_t samPort; i2p::config::GetOption("sam.port", samPort);
|
||||
LogPrint(eLogInfo, "Clients: starting SAM bridge at ", samAddr, ":", samPort);
|
||||
try {
|
||||
m_SamBridge = new SAMBridge (samAddr, samPort);
|
||||
m_SamBridge->Start ();
|
||||
} catch (std::exception& e) {
|
||||
LogPrint(eLogError, "Clients: Exception in SAM bridge: ", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
// BOB
|
||||
int bobPort = i2p::util::config::GetArg("-bobport", 0);
|
||||
if (bobPort)
|
||||
{
|
||||
m_BOBCommandChannel = new BOBCommandChannel (bobPort);
|
||||
m_BOBCommandChannel->Start ();
|
||||
LogPrint("BOB command channel started");
|
||||
bool bob; i2p::config::GetOption("bob.enabled", bob);
|
||||
if (bob) {
|
||||
std::string bobAddr; i2p::config::GetOption("bob.address", bobAddr);
|
||||
uint16_t bobPort; i2p::config::GetOption("bob.port", bobPort);
|
||||
LogPrint(eLogInfo, "Clients: starting BOB command channel at ", bobAddr, ":", bobPort);
|
||||
try {
|
||||
m_BOBCommandChannel = new BOBCommandChannel (bobAddr, bobPort);
|
||||
m_BOBCommandChannel->Start ();
|
||||
} catch (std::exception& e) {
|
||||
LogPrint(eLogError, "Clients: Exception in BOB bridge: ", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
// I2P Control
|
||||
int i2pcontrolPort = i2p::util::config::GetArg("-i2pcontrolport", 0);
|
||||
if (i2pcontrolPort)
|
||||
// I2CP
|
||||
bool i2cp; i2p::config::GetOption("i2cp.enabled", i2cp);
|
||||
if (i2cp)
|
||||
{
|
||||
m_I2PControlService = new I2PControlService (i2pcontrolPort);
|
||||
m_I2PControlService->Start ();
|
||||
LogPrint("I2PControl started");
|
||||
}
|
||||
m_AddressBook.StartSubscriptions ();
|
||||
std::string i2cpAddr; i2p::config::GetOption("i2cp.address", i2cpAddr);
|
||||
uint16_t i2cpPort; i2p::config::GetOption("i2cp.port", i2cpPort);
|
||||
LogPrint(eLogInfo, "Clients: starting I2CP at ", i2cpAddr, ":", i2cpPort);
|
||||
try
|
||||
{
|
||||
m_I2CPServer = new I2CPServer (i2cpAddr, i2cpPort);
|
||||
m_I2CPServer->Start ();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
LogPrint(eLogError, "Clients: Exception in I2CP: ", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
m_AddressBook.StartResolvers ();
|
||||
}
|
||||
|
||||
void ClientContext::Stop ()
|
||||
{
|
||||
m_AddressBook.StopSubscriptions ();
|
||||
m_HttpProxy->Stop();
|
||||
delete m_HttpProxy;
|
||||
m_HttpProxy = nullptr;
|
||||
LogPrint("HTTP Proxy stopped");
|
||||
m_SocksProxy->Stop();
|
||||
delete m_SocksProxy;
|
||||
m_SocksProxy = nullptr;
|
||||
LogPrint("SOCKS Proxy stopped");
|
||||
if (m_HttpProxy)
|
||||
{
|
||||
LogPrint(eLogInfo, "Clients: stopping HTTP Proxy");
|
||||
m_HttpProxy->Stop();
|
||||
delete m_HttpProxy;
|
||||
m_HttpProxy = nullptr;
|
||||
}
|
||||
|
||||
if (m_SocksProxy)
|
||||
{
|
||||
LogPrint(eLogInfo, "Clients: stopping SOCKS Proxy");
|
||||
m_SocksProxy->Stop();
|
||||
delete m_SocksProxy;
|
||||
m_SocksProxy = nullptr;
|
||||
}
|
||||
|
||||
for (auto& it: m_ClientTunnels)
|
||||
{
|
||||
LogPrint(eLogInfo, "Clients: stopping I2P client tunnel on port ", it.first);
|
||||
it.second->Stop ();
|
||||
LogPrint("I2P client tunnel on port ", it.first, " stopped");
|
||||
}
|
||||
m_ClientTunnels.clear ();
|
||||
|
||||
for (auto& it: m_ServerTunnels)
|
||||
{
|
||||
LogPrint(eLogInfo, "Clients: stopping I2P server tunnel");
|
||||
it.second->Stop ();
|
||||
LogPrint("I2P server tunnel stopped");
|
||||
}
|
||||
m_ServerTunnels.clear ();
|
||||
|
||||
if (m_SamBridge)
|
||||
{
|
||||
LogPrint(eLogInfo, "Clients: stopping SAM bridge");
|
||||
m_SamBridge->Stop ();
|
||||
delete m_SamBridge;
|
||||
m_SamBridge = nullptr;
|
||||
LogPrint("SAM brdige stopped");
|
||||
}
|
||||
|
||||
if (m_BOBCommandChannel)
|
||||
{
|
||||
LogPrint(eLogInfo, "Clients: stopping BOB command channel");
|
||||
m_BOBCommandChannel->Stop ();
|
||||
delete m_BOBCommandChannel;
|
||||
m_BOBCommandChannel = nullptr;
|
||||
LogPrint("BOB command channel stopped");
|
||||
}
|
||||
if (m_I2PControlService)
|
||||
{
|
||||
m_I2PControlService->Stop ();
|
||||
delete m_I2PControlService;
|
||||
m_I2PControlService = nullptr;
|
||||
LogPrint("I2PControl stopped");
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it: m_Destinations)
|
||||
{
|
||||
if (m_I2CPServer)
|
||||
{
|
||||
LogPrint(eLogInfo, "Clients: stopping I2CP");
|
||||
m_I2CPServer->Stop ();
|
||||
delete m_I2CPServer;
|
||||
m_I2CPServer = nullptr;
|
||||
}
|
||||
|
||||
LogPrint(eLogInfo, "Clients: stopping AddressBook");
|
||||
m_AddressBook.Stop ();
|
||||
for (auto& it: m_Destinations)
|
||||
it.second->Stop ();
|
||||
delete it.second;
|
||||
}
|
||||
m_Destinations.clear ();
|
||||
m_SharedLocalDestination = 0; // deleted through m_Destination
|
||||
m_SharedLocalDestination = nullptr;
|
||||
}
|
||||
|
||||
ClientDestination * ClientContext::LoadLocalDestination (const std::string& filename, bool isPublic)
|
||||
|
||||
void ClientContext::ReloadConfig ()
|
||||
{
|
||||
i2p::data::PrivateKeys keys;
|
||||
std::string fullPath = i2p::util::filesystem::GetFullPath (filename);
|
||||
std::ifstream s(fullPath.c_str (), std::ifstream::binary);
|
||||
ReadTunnels (); // TODO: it reads new tunnels only, should be implemented better
|
||||
}
|
||||
|
||||
void ClientContext::LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, i2p::data::SigningKeyType sigType)
|
||||
{
|
||||
std::string fullPath = i2p::fs::DataDirPath (filename);
|
||||
std::ifstream s(fullPath, std::ifstream::binary);
|
||||
if (s.is_open ())
|
||||
{
|
||||
s.seekg (0, std::ios::end);
|
||||
@@ -168,12 +218,12 @@ namespace client
|
||||
s.read ((char *)buf, len);
|
||||
keys.FromBuffer (buf, len);
|
||||
delete[] buf;
|
||||
LogPrint ("Local address ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " loaded");
|
||||
LogPrint (eLogInfo, "Clients: Local address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " loaded");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("Can't open file ", fullPath, " Creating new one");
|
||||
keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_DSA_SHA1);
|
||||
LogPrint (eLogError, "Clients: can't open file ", fullPath, " Creating new one with signature type ", sigType);
|
||||
keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType);
|
||||
std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out);
|
||||
size_t len = keys.GetFullLen ();
|
||||
uint8_t * buf = new uint8_t[len];
|
||||
@@ -181,38 +231,22 @@ namespace client
|
||||
f.write ((char *)buf, len);
|
||||
delete[] buf;
|
||||
|
||||
LogPrint ("New private keys file ", fullPath, " for ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " created");
|
||||
LogPrint (eLogInfo, "Clients: New private keys file ", fullPath, " for ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " created");
|
||||
}
|
||||
|
||||
ClientDestination * localDestination = nullptr;
|
||||
std::unique_lock<std::mutex> l(m_DestinationsMutex);
|
||||
auto it = m_Destinations.find (keys.GetPublic ().GetIdentHash ());
|
||||
if (it != m_Destinations.end ())
|
||||
{
|
||||
LogPrint (eLogWarning, "Local destination ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " alreday exists");
|
||||
localDestination = it->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
localDestination = new ClientDestination (keys, isPublic);
|
||||
m_Destinations[localDestination->GetIdentHash ()] = localDestination;
|
||||
localDestination->Start ();
|
||||
}
|
||||
return localDestination;
|
||||
}
|
||||
|
||||
ClientDestination * ClientContext::CreateNewLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType,
|
||||
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType,
|
||||
const std::map<std::string, std::string> * params)
|
||||
{
|
||||
i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType);
|
||||
auto localDestination = new ClientDestination (keys, isPublic, params);
|
||||
auto localDestination = std::make_shared<ClientDestination> (keys, isPublic, params);
|
||||
std::unique_lock<std::mutex> l(m_DestinationsMutex);
|
||||
m_Destinations[localDestination->GetIdentHash ()] = localDestination;
|
||||
localDestination->Start ();
|
||||
return localDestination;
|
||||
}
|
||||
|
||||
void ClientContext::DeleteLocalDestination (ClientDestination * destination)
|
||||
void ClientContext::DeleteLocalDestination (std::shared_ptr<ClientDestination> destination)
|
||||
{
|
||||
if (!destination) return;
|
||||
auto it = m_Destinations.find (destination->GetIdentHash ());
|
||||
@@ -224,17 +258,16 @@ namespace client
|
||||
m_Destinations.erase (it);
|
||||
}
|
||||
d->Stop ();
|
||||
delete d;
|
||||
}
|
||||
}
|
||||
|
||||
ClientDestination * ClientContext::CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic,
|
||||
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic,
|
||||
const std::map<std::string, std::string> * params)
|
||||
{
|
||||
auto it = m_Destinations.find (keys.GetPublic ().GetIdentHash ());
|
||||
auto it = m_Destinations.find (keys.GetPublic ()->GetIdentHash ());
|
||||
if (it != m_Destinations.end ())
|
||||
{
|
||||
LogPrint ("Local destination ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " exists");
|
||||
LogPrint (eLogWarning, "Clients: Local destination ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " exists");
|
||||
if (!it->second->IsRunning ())
|
||||
{
|
||||
it->second->Start ();
|
||||
@@ -242,14 +275,14 @@ namespace client
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
auto localDestination = new ClientDestination (keys, isPublic, params);
|
||||
auto localDestination = std::make_shared<ClientDestination> (keys, isPublic, params);
|
||||
std::unique_lock<std::mutex> l(m_DestinationsMutex);
|
||||
m_Destinations[keys.GetPublic ().GetIdentHash ()] = localDestination;
|
||||
m_Destinations[keys.GetPublic ()->GetIdentHash ()] = localDestination;
|
||||
localDestination->Start ();
|
||||
return localDestination;
|
||||
}
|
||||
|
||||
ClientDestination * ClientContext::FindLocalDestination (const i2p::data::IdentHash& destination) const
|
||||
std::shared_ptr<ClientDestination> ClientContext::FindLocalDestination (const i2p::data::IdentHash& destination) const
|
||||
{
|
||||
auto it = m_Destinations.find (destination);
|
||||
if (it != m_Destinations.end ())
|
||||
@@ -257,79 +290,155 @@ namespace client
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename Section, typename Type>
|
||||
std::string ClientContext::GetI2CPOption (const Section& section, const std::string& name, const Type& value) const
|
||||
{
|
||||
return section.second.get (boost::property_tree::ptree::path_type (name, '/'), std::to_string (value));
|
||||
}
|
||||
|
||||
template<typename Section>
|
||||
void ClientContext::ReadI2CPOptions (const Section& section, std::map<std::string, std::string>& options) const
|
||||
{
|
||||
options[I2CP_PARAM_INBOUND_TUNNEL_LENGTH] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNEL_LENGTH, DEFAULT_INBOUND_TUNNEL_LENGTH);
|
||||
options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, DEFAULT_OUTBOUND_TUNNEL_LENGTH);
|
||||
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);
|
||||
}
|
||||
|
||||
void ClientContext::ReadTunnels ()
|
||||
{
|
||||
std::ifstream ifs (i2p::util::filesystem::GetFullPath (TUNNELS_CONFIG_FILENAME));
|
||||
if (ifs.good ())
|
||||
{
|
||||
boost::program_options::options_description params ("I2P tunnels parameters");
|
||||
params.add_options ()
|
||||
// client
|
||||
(I2P_CLIENT_TUNNEL_NAME, boost::program_options::value<std::vector<std::string> >(), "tunnel name")
|
||||
(I2P_CLIENT_TUNNEL_PORT, boost::program_options::value<std::vector<int> >(), "Local port")
|
||||
(I2P_CLIENT_TUNNEL_DESTINATION, boost::program_options::value<std::vector<std::string> >(), "destination")
|
||||
(I2P_CLIENT_TUNNEL_KEYS, boost::program_options::value<std::vector<std::string> >(), "keys")
|
||||
// server
|
||||
(I2P_SERVER_TUNNEL_NAME, boost::program_options::value<std::vector<std::string> >(), "tunnel name")
|
||||
(I2P_SERVER_TUNNEL_HOST, boost::program_options::value<std::vector<std::string> >(), "host")
|
||||
(I2P_SERVER_TUNNEL_PORT, boost::program_options::value<std::vector<int> >(), "port")
|
||||
(I2P_SERVER_TUNNEL_KEYS, boost::program_options::value<std::vector<std::string> >(), "keys")
|
||||
;
|
||||
|
||||
|
||||
boost::program_options::variables_map vm;
|
||||
try
|
||||
{
|
||||
boost::program_options::store (boost::program_options::parse_config_file (ifs, params), vm);
|
||||
boost::program_options::notify (vm);
|
||||
}
|
||||
catch (boost::program_options::error& ex)
|
||||
{
|
||||
LogPrint (eLogError, "Can't parse ", TUNNELS_CONFIG_FILENAME,": ", ex.what ());
|
||||
return;
|
||||
}
|
||||
|
||||
if (vm.count (I2P_CLIENT_TUNNEL_NAME) > 0)
|
||||
{
|
||||
auto names = vm[I2P_CLIENT_TUNNEL_NAME].as<std::vector<std::string> >();
|
||||
int numClientTunnels = names.size ();
|
||||
auto ports = vm[I2P_CLIENT_TUNNEL_PORT].as<std::vector<int> >();
|
||||
auto destinations = vm[I2P_CLIENT_TUNNEL_DESTINATION].as<std::vector<std::string> >();
|
||||
auto keys = vm[I2P_CLIENT_TUNNEL_KEYS].as<std::vector<std::string> >();
|
||||
|
||||
for (int i = 0; i < numClientTunnels; i++)
|
||||
{
|
||||
ClientDestination * localDestination = nullptr;
|
||||
if (keys[i].length () > 0)
|
||||
localDestination = LoadLocalDestination (keys[i], false);
|
||||
auto clientTunnel = new I2PClientTunnel (destinations[i], ports[i], localDestination);
|
||||
if (m_ClientTunnels.insert (std::make_pair (ports[i], std::unique_ptr<I2PClientTunnel>(clientTunnel))).second)
|
||||
clientTunnel->Start ();
|
||||
else
|
||||
LogPrint (eLogError, "I2P client tunnel with port ", ports[i], " already exists");
|
||||
}
|
||||
LogPrint (eLogInfo, numClientTunnels, " I2P client tunnels created");
|
||||
}
|
||||
|
||||
if (vm.count (I2P_SERVER_TUNNEL_NAME) > 0)
|
||||
{
|
||||
auto names = vm[I2P_SERVER_TUNNEL_NAME].as<std::vector<std::string> >();
|
||||
int numServerTunnels = names.size ();
|
||||
auto hosts = vm[I2P_SERVER_TUNNEL_HOST].as<std::vector<std::string> >();
|
||||
auto ports = vm[I2P_SERVER_TUNNEL_PORT].as<std::vector<int> >();
|
||||
auto keys = vm[I2P_SERVER_TUNNEL_KEYS].as<std::vector<std::string> >();
|
||||
for (int i = 0; i < numServerTunnels; i++)
|
||||
{
|
||||
auto localDestination = LoadLocalDestination (keys[i], true);
|
||||
auto serverTunnel = new I2PServerTunnel (hosts[i], ports[i], localDestination);
|
||||
if (m_ServerTunnels.insert (std::make_pair (localDestination->GetIdentHash (), std::unique_ptr<I2PServerTunnel>(serverTunnel))).second)
|
||||
serverTunnel->Start ();
|
||||
else
|
||||
LogPrint (eLogError, "I2P server tunnel for destination ", m_AddressBook.ToAddress(localDestination->GetIdentHash ()), " already exists");
|
||||
}
|
||||
LogPrint (eLogInfo, numServerTunnels, " I2P server tunnels created");
|
||||
boost::property_tree::ptree pt;
|
||||
std::string tunConf; i2p::config::GetOption("tunconf", tunConf);
|
||||
if (tunConf == "") {
|
||||
// TODO: cleanup this in 2.8.0
|
||||
tunConf = i2p::fs::DataDirPath ("tunnels.cfg");
|
||||
if (i2p::fs::Exists(tunConf)) {
|
||||
LogPrint(eLogWarning, "FS: please rename tunnels.cfg -> tunnels.conf here: ", tunConf);
|
||||
} else {
|
||||
tunConf = i2p::fs::DataDirPath ("tunnels.conf");
|
||||
}
|
||||
}
|
||||
}
|
||||
LogPrint(eLogDebug, "FS: tunnels config file: ", tunConf);
|
||||
try
|
||||
{
|
||||
boost::property_tree::read_ini (tunConf, pt);
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogWarning, "Clients: Can't read ", tunConf, ": ", ex.what ());
|
||||
return;
|
||||
}
|
||||
|
||||
int numClientTunnels = 0, numServerTunnels = 0;
|
||||
for (auto& section: pt)
|
||||
{
|
||||
std::string name = section.first;
|
||||
try
|
||||
{
|
||||
std::string type = section.second.get<std::string> (I2P_TUNNELS_SECTION_TYPE);
|
||||
if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT)
|
||||
{
|
||||
// mandatory params
|
||||
std::string dest = section.second.get<std::string> (I2P_CLIENT_TUNNEL_DESTINATION);
|
||||
int port = section.second.get<int> (I2P_CLIENT_TUNNEL_PORT);
|
||||
// optional params
|
||||
std::string keys = section.second.get (I2P_CLIENT_TUNNEL_KEYS, "");
|
||||
std::string address = section.second.get (I2P_CLIENT_TUNNEL_ADDRESS, "127.0.0.1");
|
||||
int destinationPort = section.second.get (I2P_CLIENT_TUNNEL_DESTINATION_PORT, 0);
|
||||
i2p::data::SigningKeyType sigType = section.second.get (I2P_CLIENT_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
|
||||
// I2CP
|
||||
std::map<std::string, std::string> options;
|
||||
ReadI2CPOptions (section, options);
|
||||
|
||||
std::shared_ptr<ClientDestination> localDestination = nullptr;
|
||||
if (keys.length () > 0)
|
||||
{
|
||||
i2p::data::PrivateKeys k;
|
||||
LoadPrivateKeys (k, keys, sigType);
|
||||
localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ());
|
||||
if (!localDestination)
|
||||
localDestination = CreateNewLocalDestination (k, false, &options);
|
||||
}
|
||||
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)
|
||||
{
|
||||
// mandatory params
|
||||
std::string host = section.second.get<std::string> (I2P_SERVER_TUNNEL_HOST);
|
||||
int port = section.second.get<int> (I2P_SERVER_TUNNEL_PORT);
|
||||
std::string keys = section.second.get<std::string> (I2P_SERVER_TUNNEL_KEYS);
|
||||
// optional params
|
||||
int inPort = section.second.get (I2P_SERVER_TUNNEL_INPORT, 0);
|
||||
std::string accessList = section.second.get (I2P_SERVER_TUNNEL_ACCESS_LIST, "");
|
||||
std::string hostOverride = section.second.get (I2P_SERVER_TUNNEL_HOST_OVERRIDE, "");
|
||||
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);
|
||||
// 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);
|
||||
localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ());
|
||||
if (!localDestination)
|
||||
localDestination = CreateNewLocalDestination (k, true, &options);
|
||||
|
||||
I2PServerTunnel * serverTunnel;
|
||||
if (type == I2P_TUNNELS_SECTION_TYPE_HTTP)
|
||||
serverTunnel = new I2PServerTunnelHTTP (name, host, port, localDestination, hostOverride, inPort, gzip);
|
||||
else if (type == I2P_TUNNELS_SECTION_TYPE_IRC)
|
||||
serverTunnel = new I2PServerTunnelIRC (name, host, port, localDestination, webircpass, inPort, gzip);
|
||||
else // regular server tunnel by default
|
||||
serverTunnel = new I2PServerTunnel (name, host, port, localDestination, inPort, gzip);
|
||||
|
||||
if (accessList.length () > 0)
|
||||
{
|
||||
std::set<i2p::data::IdentHash> idents;
|
||||
size_t pos = 0, comma;
|
||||
do
|
||||
{
|
||||
comma = accessList.find (',', pos);
|
||||
i2p::data::IdentHash ident;
|
||||
ident.FromBase32 (accessList.substr (pos, comma != std::string::npos ? comma - pos : std::string::npos));
|
||||
idents.insert (ident);
|
||||
pos = comma + 1;
|
||||
}
|
||||
while (comma != std::string::npos);
|
||||
serverTunnel->SetAccessList (idents);
|
||||
}
|
||||
if (m_ServerTunnels.insert (std::make_pair (
|
||||
std::make_pair (localDestination->GetIdentHash (), inPort),
|
||||
std::unique_ptr<I2PServerTunnel>(serverTunnel))).second)
|
||||
{
|
||||
serverTunnel->Start ();
|
||||
numServerTunnels++;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Clients: I2P server tunnel for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash ()), "/", inPort, " already exists");
|
||||
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Clients: Unknown section type=", type, " of ", name, " in ", tunConf);
|
||||
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "Clients: Can't read tunnel ", name, " params: ", ex.what ());
|
||||
}
|
||||
}
|
||||
LogPrint (eLogInfo, "Clients: ", numClientTunnels, " I2P client tunnels created");
|
||||
LogPrint (eLogInfo, "Clients: ", numServerTunnels, " I2P server tunnels created");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,28 +4,41 @@
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <boost/asio.hpp>
|
||||
#include "Destination.h"
|
||||
#include "I2PService.h"
|
||||
#include "HTTPProxy.h"
|
||||
#include "SOCKS.h"
|
||||
#include "I2PTunnel.h"
|
||||
#include "SAM.h"
|
||||
#include "BOB.h"
|
||||
#include "I2CP.h"
|
||||
#include "AddressBook.h"
|
||||
#include "I2PControl.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
const char I2P_CLIENT_TUNNEL_NAME[] = "client.name";
|
||||
const char I2P_CLIENT_TUNNEL_PORT[] = "client.port";
|
||||
const char I2P_CLIENT_TUNNEL_DESTINATION[] = "client.destination";
|
||||
const char I2P_CLIENT_TUNNEL_KEYS[] = "client.keys";
|
||||
const char I2P_SERVER_TUNNEL_NAME[] = "server.name";
|
||||
const char I2P_SERVER_TUNNEL_HOST[] = "server.host";
|
||||
const char I2P_SERVER_TUNNEL_PORT[] = "server.port";
|
||||
const char I2P_SERVER_TUNNEL_KEYS[] = "server.keys";
|
||||
const char TUNNELS_CONFIG_FILENAME[] = "tunnels.cfg";
|
||||
const char I2P_TUNNELS_SECTION_TYPE[] = "type";
|
||||
const char I2P_TUNNELS_SECTION_TYPE_CLIENT[] = "client";
|
||||
const char I2P_TUNNELS_SECTION_TYPE_SERVER[] = "server";
|
||||
const char I2P_TUNNELS_SECTION_TYPE_HTTP[] = "http";
|
||||
const char I2P_TUNNELS_SECTION_TYPE_IRC[] = "irc";
|
||||
const char I2P_CLIENT_TUNNEL_PORT[] = "port";
|
||||
const char I2P_CLIENT_TUNNEL_ADDRESS[] = "address";
|
||||
const char I2P_CLIENT_TUNNEL_DESTINATION[] = "destination";
|
||||
const char I2P_CLIENT_TUNNEL_KEYS[] = "keys";
|
||||
const char I2P_CLIENT_TUNNEL_SIGNATURE_TYPE[] = "signaturetype";
|
||||
const char I2P_CLIENT_TUNNEL_DESTINATION_PORT[] = "destinationport";
|
||||
const char I2P_SERVER_TUNNEL_HOST[] = "host";
|
||||
const char I2P_SERVER_TUNNEL_HOST_OVERRIDE[] = "hostoverride";
|
||||
const char I2P_SERVER_TUNNEL_PORT[] = "port";
|
||||
const char I2P_SERVER_TUNNEL_KEYS[] = "keys";
|
||||
const char I2P_SERVER_TUNNEL_SIGNATURE_TYPE[] = "signaturetype";
|
||||
const char I2P_SERVER_TUNNEL_INPORT[] = "inport";
|
||||
const char I2P_SERVER_TUNNEL_ACCESS_LIST[] = "accesslist";
|
||||
const char I2P_SERVER_TUNNEL_GZIP[] = "gzip";
|
||||
const char I2P_SERVER_TUNNEL_WEBIRC_PASSWORD[] = "webircpassword";
|
||||
|
||||
class ClientContext
|
||||
{
|
||||
@@ -37,14 +50,16 @@ namespace client
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
ClientDestination * GetSharedLocalDestination () const { return m_SharedLocalDestination; };
|
||||
ClientDestination * CreateNewLocalDestination (bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1,
|
||||
void ReloadConfig ();
|
||||
|
||||
std::shared_ptr<ClientDestination> GetSharedLocalDestination () const { return m_SharedLocalDestination; };
|
||||
std::shared_ptr<ClientDestination> CreateNewLocalDestination (bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1,
|
||||
const std::map<std::string, std::string> * params = nullptr); // transient
|
||||
ClientDestination * CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true,
|
||||
std::shared_ptr<ClientDestination> CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true,
|
||||
const std::map<std::string, std::string> * params = nullptr);
|
||||
void DeleteLocalDestination (ClientDestination * destination);
|
||||
ClientDestination * FindLocalDestination (const i2p::data::IdentHash& destination) const;
|
||||
ClientDestination * LoadLocalDestination (const std::string& filename, bool isPublic);
|
||||
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);
|
||||
|
||||
AddressBook& GetAddressBook () { return m_AddressBook; };
|
||||
const SAMBridge * GetSAMBridge () const { return m_SamBridge; };
|
||||
@@ -52,26 +67,32 @@ namespace client
|
||||
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;
|
||||
|
||||
private:
|
||||
|
||||
std::mutex m_DestinationsMutex;
|
||||
std::map<i2p::data::IdentHash, ClientDestination *> m_Destinations;
|
||||
ClientDestination * m_SharedLocalDestination;
|
||||
std::map<i2p::data::IdentHash, std::shared_ptr<ClientDestination> > m_Destinations;
|
||||
std::shared_ptr<ClientDestination> m_SharedLocalDestination;
|
||||
|
||||
AddressBook m_AddressBook;
|
||||
|
||||
i2p::proxy::HTTPProxy * m_HttpProxy;
|
||||
i2p::proxy::SOCKSProxy * m_SocksProxy;
|
||||
std::map<int, std::unique_ptr<I2PClientTunnel> > m_ClientTunnels; // port->tunnel
|
||||
std::map<i2p::data::IdentHash, std::unique_ptr<I2PServerTunnel> > m_ServerTunnels; // destination->tunnel
|
||||
std::map<boost::asio::ip::tcp::endpoint, std::unique_ptr<I2PClientTunnel> > m_ClientTunnels; // local endpoint->tunnel
|
||||
std::map<std::pair<i2p::data::IdentHash, int>, std::unique_ptr<I2PServerTunnel> > m_ServerTunnels; // <destination,port>->tunnel
|
||||
SAMBridge * m_SamBridge;
|
||||
BOBCommandChannel * m_BOBCommandChannel;
|
||||
I2PControlService * m_I2PControlService;
|
||||
I2CPServer * m_I2CPServer;
|
||||
|
||||
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; };
|
||||
};
|
||||
|
||||
extern ClientContext context;
|
||||
|
||||
232
Config.cpp
Normal file
232
Config.cpp
Normal file
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016, 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 <cstdlib>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <boost/program_options/cmdline.hpp>
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
#include <boost/program_options/variables_map.hpp>
|
||||
|
||||
#include "Config.h"
|
||||
#include "version.h"
|
||||
|
||||
using namespace boost::program_options;
|
||||
|
||||
namespace i2p {
|
||||
namespace config {
|
||||
options_description m_OptionsDesc;
|
||||
variables_map m_Options;
|
||||
|
||||
void Init() {
|
||||
bool nat = true;
|
||||
#ifdef MESHNET
|
||||
nat = false;
|
||||
#endif
|
||||
|
||||
options_description general("General options");
|
||||
general.add_options()
|
||||
("help", "Show this message")
|
||||
("conf", value<std::string>()->default_value(""), "Path to main i2pd config file (default: try ~/.i2pd/i2pd.conf or /var/lib/i2pd/i2pd.conf)")
|
||||
("tunconf", value<std::string>()->default_value(""), "Path to config with tunnels list and options (default: try ~/.i2pd/tunnels.conf or /var/lib/i2pd/tunnels.conf)")
|
||||
("pidfile", value<std::string>()->default_value(""), "Path to pidfile (default: ~/i2pd/i2pd.pid or /var/lib/i2pd/i2pd.pid)")
|
||||
("log", value<std::string>()->default_value(""), "Logs destination: stdout, file, syslog (stdout if not set)")
|
||||
("logfile", value<std::string>()->default_value(""), "Path to logfile (stdout if not set, autodetect if daemon)")
|
||||
("loglevel", value<std::string>()->default_value("info"), "Set the minimal level of log messages (debug, info, warn, error)")
|
||||
("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?")
|
||||
("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")
|
||||
("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")
|
||||
#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.transittunnels", value<uint16_t>()->default_value(2500), "Maximum active transit sessions (default:2500)")
|
||||
;
|
||||
|
||||
options_description httpserver("HTTP Server options");
|
||||
httpserver.add_options()
|
||||
("http.enabled", value<bool>()->default_value(true), "Enable or disable webconsole")
|
||||
("http.address", value<std::string>()->default_value("127.0.0.1"), "Webconsole listen address")
|
||||
("http.port", value<uint16_t>()->default_value(7070), "Webconsole listen port")
|
||||
("http.auth", value<bool>()->default_value(false), "Enable Basic HTTP auth for webconsole")
|
||||
("http.user", value<std::string>()->default_value("i2pd"), "Username for basic auth")
|
||||
("http.pass", value<std::string>()->default_value(""), "Password for basic auth (default: random, see logs)")
|
||||
;
|
||||
|
||||
options_description httpproxy("HTTP Proxy options");
|
||||
httpproxy.add_options()
|
||||
("httpproxy.enabled", value<bool>()->default_value(true), "Enable or disable HTTP Proxy")
|
||||
("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")
|
||||
;
|
||||
|
||||
options_description socksproxy("SOCKS Proxy options");
|
||||
socksproxy.add_options()
|
||||
("socksproxy.enabled", value<bool>()->default_value(true), "Enable or disable SOCKS Proxy")
|
||||
("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.outproxyport", value<uint16_t>()->default_value(9050), "Upstream outproxy port for SOCKS Proxy")
|
||||
;
|
||||
|
||||
options_description sam("SAM bridge options");
|
||||
sam.add_options()
|
||||
("sam.enabled", value<bool>()->default_value(false), "Enable or disable SAM Application bridge")
|
||||
("sam.address", value<std::string>()->default_value("127.0.0.1"), "SAM listen address")
|
||||
("sam.port", value<uint16_t>()->default_value(7656), "SAM listen port")
|
||||
;
|
||||
|
||||
options_description bob("BOB options");
|
||||
bob.add_options()
|
||||
("bob.enabled", value<bool>()->default_value(false), "Enable or disable BOB command channel")
|
||||
("bob.address", value<std::string>()->default_value("127.0.0.1"), "BOB listen address")
|
||||
("bob.port", value<uint16_t>()->default_value(2827), "BOB listen port")
|
||||
;
|
||||
|
||||
options_description i2cp("I2CP options");
|
||||
i2cp.add_options()
|
||||
("i2cp.enabled", value<bool>()->default_value(false), "Enable or disable I2CP")
|
||||
("i2cp.address", value<std::string>()->default_value("127.0.0.1"), "I2CP listen address")
|
||||
("i2cp.port", value<uint16_t>()->default_value(7654), "I2CP listen port")
|
||||
;
|
||||
|
||||
options_description i2pcontrol("I2PControl options");
|
||||
i2pcontrol.add_options()
|
||||
("i2pcontrol.enabled", value<bool>()->default_value(false), "Enable or disable I2P Control Protocol")
|
||||
("i2pcontrol.address", value<std::string>()->default_value("127.0.0.1"), "I2PCP listen address")
|
||||
("i2pcontrol.port", value<uint16_t>()->default_value(7650), "I2PCP listen port")
|
||||
("i2pcontrol.password", value<std::string>()->default_value("itoopie"), "I2PCP access password")
|
||||
("i2pcontrol.cert", value<std::string>()->default_value("i2pcontrol.crt.pem"), "I2PCP connection cerificate")
|
||||
("i2pcontrol.key", value<std::string>()->default_value("i2pcontrol.key.pem"), "I2PCP connection cerificate key")
|
||||
;
|
||||
|
||||
bool upnp_default = false;
|
||||
#if (defined(USE_UPNP) && (defined(WIN32_APP) || defined(ANDROID)))
|
||||
upnp_default = true; // enable UPNP for windows GUI and android by default
|
||||
#endif
|
||||
options_description upnp("UPnP options");
|
||||
upnp.add_options()
|
||||
("upnp.enabled", value<bool>()->default_value(upnp_default), "Enable or disable UPnP: automatic port forwarding")
|
||||
("upnp.name", value<std::string>()->default_value("I2Pd"), "Name i2pd appears in UPnP forwardings list")
|
||||
;
|
||||
|
||||
options_description precomputation("Precomputation options");
|
||||
precomputation.add_options()
|
||||
("precomputation.elgamal",
|
||||
#if defined(__x86_64__)
|
||||
value<bool>()->default_value(false),
|
||||
#else
|
||||
value<bool>()->default_value(true),
|
||||
#endif
|
||||
"Enable or disable elgamal precomputation table")
|
||||
;
|
||||
|
||||
options_description reseed("Reseed options");
|
||||
reseed.add_options()
|
||||
("reseed.file", value<std::string>()->default_value(""), "Path to .su3 file")
|
||||
;
|
||||
|
||||
options_description trust("Trust options");
|
||||
trust.add_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?");
|
||||
|
||||
m_OptionsDesc
|
||||
.add(general)
|
||||
.add(limits)
|
||||
.add(httpserver)
|
||||
.add(httpproxy)
|
||||
.add(socksproxy)
|
||||
.add(sam)
|
||||
.add(bob)
|
||||
.add(i2cp)
|
||||
.add(i2pcontrol)
|
||||
.add(upnp)
|
||||
.add(precomputation)
|
||||
.add(reseed)
|
||||
.add(trust)
|
||||
;
|
||||
}
|
||||
|
||||
void ParseCmdline(int argc, char* argv[]) {
|
||||
try {
|
||||
auto style = boost::program_options::command_line_style::unix_style
|
||||
| boost::program_options::command_line_style::allow_long_disguise;
|
||||
style &= ~ boost::program_options::command_line_style::allow_guessing;
|
||||
store(parse_command_line(argc, argv, m_OptionsDesc, style), m_Options);
|
||||
} catch (boost::program_options::error& e) {
|
||||
std::cerr << "args: " << e.what() << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (m_Options.count("help") || m_Options.count("h")) {
|
||||
std::cout << "i2pd version " << I2PD_VERSION << " (" << I2P_VERSION << ")" << std::endl;
|
||||
std::cout << m_OptionsDesc;
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
void ParseConfig(const std::string& path) {
|
||||
if (path == "") return;
|
||||
|
||||
std::ifstream config(path, std::ios::in);
|
||||
|
||||
if (!config.is_open())
|
||||
{
|
||||
std::cerr << "missing/unreadable config file: " << path << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
store(boost::program_options::parse_config_file(config, m_OptionsDesc), m_Options);
|
||||
}
|
||||
catch (boost::program_options::error& e)
|
||||
{
|
||||
std::cerr << e.what() << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
};
|
||||
}
|
||||
|
||||
void Finalize() {
|
||||
notify(m_Options);
|
||||
}
|
||||
|
||||
bool IsDefault(const char *name) {
|
||||
if (!m_Options.count(name))
|
||||
throw "try to check non-existent option";
|
||||
|
||||
if (m_Options[name].defaulted())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
} // namespace config
|
||||
} // namespace i2p
|
||||
107
Config.h
Normal file
107
Config.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
#include <string>
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
#include <boost/program_options/variables_map.hpp>
|
||||
|
||||
/**
|
||||
* Functions to parse and store i2pd parameters
|
||||
*
|
||||
* General usage flow:
|
||||
* Init() -- early as possible
|
||||
* ParseCmdline() -- somewhere close to main()
|
||||
* ParseConfig() -- after detecting path to config
|
||||
* Finalize() -- right after all Parse*() functions called
|
||||
* GetOption() -- may be called after Finalize()
|
||||
*/
|
||||
|
||||
namespace i2p {
|
||||
namespace config {
|
||||
extern boost::program_options::variables_map m_Options;
|
||||
|
||||
/**
|
||||
* @brief Initialize list of acceptable parameters
|
||||
*
|
||||
* Should be called before any Parse* functions.
|
||||
*/
|
||||
void Init();
|
||||
|
||||
/**
|
||||
* @brief Parse cmdline parameters, and show help if requested
|
||||
* @param argc Cmdline arguments count, should be passed from main().
|
||||
* @param argv Cmdline parameters array, should be passed from main()
|
||||
*
|
||||
* If --help is given in parameters, shows it's list with description
|
||||
* terminates the program with exitcode 0.
|
||||
*
|
||||
* In case of parameter misuse boost throws an exception.
|
||||
* We internally handle type boost::program_options::unknown_option,
|
||||
* and then terminate program with exitcode 1.
|
||||
*
|
||||
* Other exceptions will be passed to higher level.
|
||||
*/
|
||||
void ParseCmdline(int argc, char* argv[]);
|
||||
|
||||
/**
|
||||
* @brief Load and parse given config file
|
||||
* @param path Path to config file
|
||||
*
|
||||
* If error occured when opening file path is points to,
|
||||
* we show the error message and terminate program.
|
||||
*
|
||||
* In case of parameter misuse boost throws an exception.
|
||||
* We internally handle type boost::program_options::unknown_option,
|
||||
* and then terminate program with exitcode 1.
|
||||
*
|
||||
* Other exceptions will be passed to higher level.
|
||||
*/
|
||||
void ParseConfig(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief Used to combine options from cmdline, config and default values
|
||||
*/
|
||||
void Finalize();
|
||||
|
||||
/* @brief Accessor to parameters by name
|
||||
* @param name Name of the requested parameter
|
||||
* @param value Variable where to store option
|
||||
* @return this function returns false if parameter not found
|
||||
*
|
||||
* Example: uint16_t port; GetOption("sam.port", port);
|
||||
*/
|
||||
template<typename T>
|
||||
bool GetOption(const char *name, T& value) {
|
||||
if (!m_Options.count(name))
|
||||
return false;
|
||||
value = m_Options[name].as<T>();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set value of given parameter
|
||||
* @param name Name of settable parameter
|
||||
* @param value New parameter value
|
||||
* @return true if value set up successful, false otherwise
|
||||
*
|
||||
* Example: uint16_t port = 2827; SetOption("bob.port", port);
|
||||
*/
|
||||
template<typename T>
|
||||
bool SetOption(const char *name, const T& value) {
|
||||
if (!m_Options.count(name))
|
||||
return false;
|
||||
m_Options.at(name).value() = value;
|
||||
notify(m_Options);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check is value explicitly given or default
|
||||
* @param name Name of checked parameter
|
||||
* @return true if value set to default, false othervise
|
||||
*/
|
||||
bool IsDefault(const char *name);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CONFIG_H
|
||||
839
Crypto.cpp
Normal file
839
Crypto.cpp
Normal file
@@ -0,0 +1,839 @@
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <openssl/dh.h>
|
||||
#include <openssl/md5.h>
|
||||
#include <openssl/crypto.h>
|
||||
#include "TunnelBase.h"
|
||||
#include <openssl/ssl.h>
|
||||
#include "Log.h"
|
||||
#include "Crypto.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace crypto
|
||||
{
|
||||
const uint8_t elgp_[256]=
|
||||
{
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
|
||||
0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
|
||||
0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
|
||||
0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
|
||||
0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
|
||||
0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
|
||||
0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
|
||||
0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
|
||||
0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
|
||||
0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
|
||||
0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
|
||||
0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
|
||||
0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
|
||||
0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
|
||||
0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
|
||||
0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
const int elgg_ = 2;
|
||||
|
||||
const uint8_t dsap_[128]=
|
||||
{
|
||||
0x9c, 0x05, 0xb2, 0xaa, 0x96, 0x0d, 0x9b, 0x97, 0xb8, 0x93, 0x19, 0x63, 0xc9, 0xcc, 0x9e, 0x8c,
|
||||
0x30, 0x26, 0xe9, 0xb8, 0xed, 0x92, 0xfa, 0xd0, 0xa6, 0x9c, 0xc8, 0x86, 0xd5, 0xbf, 0x80, 0x15,
|
||||
0xfc, 0xad, 0xae, 0x31, 0xa0, 0xad, 0x18, 0xfa, 0xb3, 0xf0, 0x1b, 0x00, 0xa3, 0x58, 0xde, 0x23,
|
||||
0x76, 0x55, 0xc4, 0x96, 0x4a, 0xfa, 0xa2, 0xb3, 0x37, 0xe9, 0x6a, 0xd3, 0x16, 0xb9, 0xfb, 0x1c,
|
||||
0xc5, 0x64, 0xb5, 0xae, 0xc5, 0xb6, 0x9a, 0x9f, 0xf6, 0xc3, 0xe4, 0x54, 0x87, 0x07, 0xfe, 0xf8,
|
||||
0x50, 0x3d, 0x91, 0xdd, 0x86, 0x02, 0xe8, 0x67, 0xe6, 0xd3, 0x5d, 0x22, 0x35, 0xc1, 0x86, 0x9c,
|
||||
0xe2, 0x47, 0x9c, 0x3b, 0x9d, 0x54, 0x01, 0xde, 0x04, 0xe0, 0x72, 0x7f, 0xb3, 0x3d, 0x65, 0x11,
|
||||
0x28, 0x5d, 0x4c, 0xf2, 0x95, 0x38, 0xd9, 0xe3, 0xb6, 0x05, 0x1f, 0x5b, 0x22, 0xcc, 0x1c, 0x93
|
||||
};
|
||||
|
||||
const uint8_t dsaq_[20]=
|
||||
{
|
||||
0xa5, 0xdf, 0xc2, 0x8f, 0xef, 0x4c, 0xa1, 0xe2, 0x86, 0x74, 0x4c, 0xd8, 0xee, 0xd9, 0xd2, 0x9d,
|
||||
0x68, 0x40, 0x46, 0xb7
|
||||
};
|
||||
|
||||
const uint8_t dsag_[128]=
|
||||
{
|
||||
0x0c, 0x1f, 0x4d, 0x27, 0xd4, 0x00, 0x93, 0xb4, 0x29, 0xe9, 0x62, 0xd7, 0x22, 0x38, 0x24, 0xe0,
|
||||
0xbb, 0xc4, 0x7e, 0x7c, 0x83, 0x2a, 0x39, 0x23, 0x6f, 0xc6, 0x83, 0xaf, 0x84, 0x88, 0x95, 0x81,
|
||||
0x07, 0x5f, 0xf9, 0x08, 0x2e, 0xd3, 0x23, 0x53, 0xd4, 0x37, 0x4d, 0x73, 0x01, 0xcd, 0xa1, 0xd2,
|
||||
0x3c, 0x43, 0x1f, 0x46, 0x98, 0x59, 0x9d, 0xda, 0x02, 0x45, 0x18, 0x24, 0xff, 0x36, 0x97, 0x52,
|
||||
0x59, 0x36, 0x47, 0xcc, 0x3d, 0xdc, 0x19, 0x7d, 0xe9, 0x85, 0xe4, 0x3d, 0x13, 0x6c, 0xdc, 0xfc,
|
||||
0x6b, 0xd5, 0x40, 0x9c, 0xd2, 0xf4, 0x50, 0x82, 0x11, 0x42, 0xa5, 0xe6, 0xf8, 0xeb, 0x1c, 0x3a,
|
||||
0xb5, 0xd0, 0x48, 0x4b, 0x81, 0x29, 0xfc, 0xf1, 0x7b, 0xce, 0x4f, 0x7f, 0x33, 0x32, 0x1c, 0x3c,
|
||||
0xb3, 0xdb, 0xb1, 0x4a, 0x90, 0x5e, 0x7b, 0x2b, 0x3e, 0x93, 0xbe, 0x47, 0x08, 0xcb, 0xcc, 0x82
|
||||
};
|
||||
|
||||
const int rsae_ = 65537;
|
||||
|
||||
struct CryptoConstants
|
||||
{
|
||||
// DH/ElGamal
|
||||
BIGNUM * elgp;
|
||||
BIGNUM * elgg;
|
||||
|
||||
// DSA
|
||||
BIGNUM * dsap;
|
||||
BIGNUM * dsaq;
|
||||
BIGNUM * dsag;
|
||||
|
||||
// RSA
|
||||
BIGNUM * rsae;
|
||||
|
||||
CryptoConstants (const uint8_t * elgp_, int elgg_, const uint8_t * dsap_,
|
||||
const uint8_t * dsaq_, const uint8_t * dsag_, int rsae_)
|
||||
{
|
||||
elgp = BN_new ();
|
||||
BN_bin2bn (elgp_, 256, elgp);
|
||||
elgg = BN_new ();
|
||||
BN_set_word (elgg, elgg_);
|
||||
dsap = BN_new ();
|
||||
BN_bin2bn (dsap_, 128, dsap);
|
||||
dsaq = BN_new ();
|
||||
BN_bin2bn (dsaq_, 20, dsaq);
|
||||
dsag = BN_new ();
|
||||
BN_bin2bn (dsag_, 128, dsag);
|
||||
rsae = BN_new ();
|
||||
BN_set_word (rsae, rsae_);
|
||||
}
|
||||
|
||||
~CryptoConstants ()
|
||||
{
|
||||
BN_free (elgp); BN_free (elgg); BN_free (dsap); BN_free (dsaq); BN_free (dsag); BN_free (rsae);
|
||||
}
|
||||
};
|
||||
|
||||
static const CryptoConstants& GetCryptoConstants ()
|
||||
{
|
||||
static CryptoConstants cryptoConstants (elgp_, elgg_, dsap_, dsaq_, dsag_, rsae_);
|
||||
return cryptoConstants;
|
||||
}
|
||||
|
||||
bool bn2buf (const BIGNUM * bn, uint8_t * buf, size_t len)
|
||||
{
|
||||
int offset = len - BN_num_bytes (bn);
|
||||
if (offset < 0) return false;
|
||||
BN_bn2bin (bn, buf + offset);
|
||||
memset (buf, 0, offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
// RSA
|
||||
#define rsae GetCryptoConstants ().rsae
|
||||
const BIGNUM * GetRSAE ()
|
||||
{
|
||||
return rsae;
|
||||
}
|
||||
|
||||
// DSA
|
||||
#define dsap GetCryptoConstants ().dsap
|
||||
#define dsaq GetCryptoConstants ().dsaq
|
||||
#define dsag GetCryptoConstants ().dsag
|
||||
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;
|
||||
return dsa;
|
||||
}
|
||||
|
||||
// DH/ElGamal
|
||||
|
||||
const int ELGAMAL_SHORT_EXPONENT_NUM_BITS = 226;
|
||||
const int ELGAMAL_SHORT_EXPONENT_NUM_BYTES = ELGAMAL_SHORT_EXPONENT_NUM_BITS/8+1;
|
||||
const int ELGAMAL_FULL_EXPONENT_NUM_BITS = 2048;
|
||||
const int ELGAMAL_FULL_EXPONENT_NUM_BYTES = ELGAMAL_FULL_EXPONENT_NUM_BITS/8;
|
||||
|
||||
#define elgp GetCryptoConstants ().elgp
|
||||
#define elgg GetCryptoConstants ().elgg
|
||||
|
||||
static BN_MONT_CTX * g_MontCtx = nullptr;
|
||||
static void PrecalculateElggTable (BIGNUM * table[][255], int len) // table is len's array of array of 255 bignums
|
||||
{
|
||||
if (len <= 0) return;
|
||||
BN_CTX * ctx = BN_CTX_new ();
|
||||
g_MontCtx = BN_MONT_CTX_new ();
|
||||
BN_MONT_CTX_set (g_MontCtx, elgp, ctx);
|
||||
auto montCtx = BN_MONT_CTX_new ();
|
||||
BN_MONT_CTX_copy (montCtx, g_MontCtx);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
table[i][0] = BN_new ();
|
||||
if (!i)
|
||||
BN_to_montgomery (table[0][0], elgg, montCtx, ctx);
|
||||
else
|
||||
BN_mod_mul_montgomery (table[i][0], table[i-1][254], table[i-1][0], montCtx, ctx);
|
||||
for (int j = 1; j < 255; j++)
|
||||
{
|
||||
table[i][j] = BN_new ();
|
||||
BN_mod_mul_montgomery (table[i][j], table[i][j-1], table[i][0], montCtx, ctx);
|
||||
}
|
||||
}
|
||||
BN_MONT_CTX_free (montCtx);
|
||||
BN_CTX_free (ctx);
|
||||
}
|
||||
|
||||
static void DestroyElggTable (BIGNUM * table[][255], int len)
|
||||
{
|
||||
for (int i = 0; i < len; i++)
|
||||
for (int j = 0; j < 255; j++)
|
||||
{
|
||||
BN_free (table[i][j]);
|
||||
table[i][j] = nullptr;
|
||||
}
|
||||
BN_MONT_CTX_free (g_MontCtx);
|
||||
}
|
||||
|
||||
static BIGNUM * ElggPow (const uint8_t * exp, int len, BIGNUM * table[][255], BN_CTX * ctx)
|
||||
// exp is in Big Endian
|
||||
{
|
||||
if (len <= 0) return nullptr;
|
||||
auto montCtx = BN_MONT_CTX_new ();
|
||||
BN_MONT_CTX_copy (montCtx, g_MontCtx);
|
||||
BIGNUM * res = nullptr;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if (res)
|
||||
{
|
||||
if (exp[i])
|
||||
BN_mod_mul_montgomery (res, res, table[len-1-i][exp[i]-1], montCtx, ctx);
|
||||
}
|
||||
else if (exp[i])
|
||||
res = BN_dup (table[len-i-1][exp[i]-1]);
|
||||
}
|
||||
if (res)
|
||||
BN_from_montgomery (res, res, montCtx, ctx);
|
||||
BN_MONT_CTX_free (montCtx);
|
||||
return res;
|
||||
}
|
||||
|
||||
static BIGNUM * ElggPow (const BIGNUM * exp, BIGNUM * table[][255], BN_CTX * ctx)
|
||||
{
|
||||
auto len = BN_num_bytes (exp);
|
||||
uint8_t * buf = new uint8_t[len];
|
||||
BN_bn2bin (exp, buf);
|
||||
auto ret = ElggPow (buf, len, table, ctx);
|
||||
delete[] buf;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BIGNUM * (* g_ElggTable)[255] = nullptr;
|
||||
|
||||
// DH
|
||||
|
||||
DHKeys::DHKeys (): m_IsUpdated (true)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
DHKeys::~DHKeys ()
|
||||
{
|
||||
DH_free (m_DH);
|
||||
}
|
||||
|
||||
void DHKeys::GenerateKeys (uint8_t * priv, uint8_t * pub)
|
||||
{
|
||||
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; };
|
||||
#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);
|
||||
#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);
|
||||
#endif
|
||||
auto ctx = BN_CTX_new ();
|
||||
m_DH->pub_key = ElggPow (m_DH->priv_key, g_ElggTable, ctx);
|
||||
BN_CTX_free (ctx);
|
||||
}
|
||||
else
|
||||
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;
|
||||
}
|
||||
return m_PublicKey;
|
||||
}
|
||||
|
||||
void DHKeys::Agree (const uint8_t * pub, uint8_t * shared)
|
||||
{
|
||||
BIGNUM * pk = BN_bin2bn (pub, 256, NULL);
|
||||
DH_compute_key (shared, pk, m_DH);
|
||||
BN_free (pk);
|
||||
}
|
||||
|
||||
// ElGamal
|
||||
|
||||
ElGamalEncryption::ElGamalEncryption (const uint8_t * key)
|
||||
{
|
||||
ctx = BN_CTX_new ();
|
||||
// select random k
|
||||
BIGNUM * k = BN_new ();
|
||||
#if defined(__x86_64__)
|
||||
BN_rand (k, ELGAMAL_FULL_EXPONENT_NUM_BITS, -1, 1); // full exponent for x64
|
||||
#else
|
||||
BN_rand (k, ELGAMAL_SHORT_EXPONENT_NUM_BITS, -1, 1); // short exponent of 226 bits
|
||||
#endif
|
||||
// calculate a
|
||||
if (g_ElggTable)
|
||||
a = ElggPow (k, g_ElggTable, ctx);
|
||||
else
|
||||
{
|
||||
a = BN_new ();
|
||||
BN_mod_exp (a, elgg, k, elgp, ctx);
|
||||
}
|
||||
|
||||
BIGNUM * y = BN_new ();
|
||||
BN_bin2bn (key, 256, y);
|
||||
// calculate b1
|
||||
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);
|
||||
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);
|
||||
// copy a and b
|
||||
if (zeroPadding)
|
||||
{
|
||||
encrypted[0] = 0;
|
||||
bn2buf (a, encrypted + 1, 256);
|
||||
encrypted[257] = 0;
|
||||
bn2buf (b, encrypted + 258, 256);
|
||||
}
|
||||
else
|
||||
{
|
||||
bn2buf (a, encrypted, 256);
|
||||
bn2buf (b, encrypted + 256, 256);
|
||||
}
|
||||
BN_free (b);
|
||||
}
|
||||
|
||||
bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted,
|
||||
uint8_t * data, bool zeroPadding)
|
||||
{
|
||||
BN_CTX * ctx = BN_CTX_new ();
|
||||
BIGNUM * x = BN_new (), * a = BN_new (), * b = BN_new ();
|
||||
BN_bin2bn (key, 256, x);
|
||||
BN_sub (x, elgp, x); BN_sub_word (x, 1); // x = elgp - x- 1
|
||||
BN_bin2bn (zeroPadding ? encrypted + 1 : encrypted, 256, a);
|
||||
BN_bin2bn (zeroPadding ? encrypted + 258 : encrypted + 256, 256, b);
|
||||
// m = b*(a^x mod p) mod p
|
||||
BN_mod_exp (x, a, x, elgp, ctx);
|
||||
BN_mod_mul (b, b, x, elgp, ctx);
|
||||
uint8_t m[255];
|
||||
bn2buf (b, m, 255);
|
||||
BN_free (x); BN_free (a); BN_free (b);
|
||||
BN_CTX_free (ctx);
|
||||
uint8_t hash[32];
|
||||
SHA256 (m + 33, 222, hash);
|
||||
if (memcmp (m + 1, hash, 32))
|
||||
{
|
||||
LogPrint (eLogError, "ElGamal decrypt hash doesn't match");
|
||||
return false;
|
||||
}
|
||||
memcpy (data, m + 33, 222);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GenerateElGamalKeyPair (uint8_t * priv, uint8_t * pub)
|
||||
{
|
||||
#if defined(__x86_64__) || defined(__i386__) || defined(_MSC_VER)
|
||||
RAND_bytes (priv, 256);
|
||||
#else
|
||||
// lower 226 bits (28 bytes and 2 bits) only. short exponent
|
||||
auto numBytes = (ELGAMAL_SHORT_EXPONENT_NUM_BITS)/8 + 1; // 29
|
||||
auto numZeroBytes = 256 - numBytes;
|
||||
RAND_bytes (priv + numZeroBytes, numBytes);
|
||||
memset (priv, 0, numZeroBytes);
|
||||
priv[numZeroBytes] &= 0x03;
|
||||
#endif
|
||||
BN_CTX * ctx = BN_CTX_new ();
|
||||
BIGNUM * p = BN_new ();
|
||||
BN_bin2bn (priv, 256, p);
|
||||
BN_mod_exp (p, elgg, p, elgp, ctx);
|
||||
bn2buf (p, pub, 256);
|
||||
BN_free (p);
|
||||
BN_CTX_free (ctx);
|
||||
}
|
||||
|
||||
// HMAC
|
||||
const uint64_t IPAD = 0x3636363636363636;
|
||||
const uint64_t OPAD = 0x5C5C5C5C5C5C5C5C;
|
||||
|
||||
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];
|
||||
// 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;
|
||||
// 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);
|
||||
|
||||
// calculate digest
|
||||
MD5((uint8_t *)buf, 96, digest);
|
||||
}
|
||||
|
||||
// AES
|
||||
#ifdef AESNI
|
||||
|
||||
#define KeyExpansion256(round0,round1) \
|
||||
"pshufd $0xff, %%xmm2, %%xmm2 \n" \
|
||||
"movaps %%xmm1, %%xmm4 \n" \
|
||||
"pslldq $4, %%xmm4 \n" \
|
||||
"pxor %%xmm4, %%xmm1 \n" \
|
||||
"pslldq $4, %%xmm4 \n" \
|
||||
"pxor %%xmm4, %%xmm1 \n" \
|
||||
"pslldq $4, %%xmm4 \n" \
|
||||
"pxor %%xmm4, %%xmm1 \n" \
|
||||
"pxor %%xmm2, %%xmm1 \n" \
|
||||
"movaps %%xmm1, "#round0"(%[sched]) \n" \
|
||||
"aeskeygenassist $0, %%xmm1, %%xmm4 \n" \
|
||||
"pshufd $0xaa, %%xmm4, %%xmm2 \n" \
|
||||
"movaps %%xmm3, %%xmm4 \n" \
|
||||
"pslldq $4, %%xmm4 \n" \
|
||||
"pxor %%xmm4, %%xmm3 \n" \
|
||||
"pslldq $4, %%xmm4 \n" \
|
||||
"pxor %%xmm4, %%xmm3 \n" \
|
||||
"pslldq $4, %%xmm4 \n" \
|
||||
"pxor %%xmm4, %%xmm3 \n" \
|
||||
"pxor %%xmm2, %%xmm3 \n" \
|
||||
"movaps %%xmm3, "#round1"(%[sched]) \n"
|
||||
|
||||
void ECBCryptoAESNI::ExpandKey (const AESKey& key)
|
||||
{
|
||||
__asm__
|
||||
(
|
||||
"movups (%[key]), %%xmm1 \n"
|
||||
"movups 16(%[key]), %%xmm3 \n"
|
||||
"movaps %%xmm1, (%[sched]) \n"
|
||||
"movaps %%xmm3, 16(%[sched]) \n"
|
||||
"aeskeygenassist $1, %%xmm3, %%xmm2 \n"
|
||||
KeyExpansion256(32,48)
|
||||
"aeskeygenassist $2, %%xmm3, %%xmm2 \n"
|
||||
KeyExpansion256(64,80)
|
||||
"aeskeygenassist $4, %%xmm3, %%xmm2 \n"
|
||||
KeyExpansion256(96,112)
|
||||
"aeskeygenassist $8, %%xmm3, %%xmm2 \n"
|
||||
KeyExpansion256(128,144)
|
||||
"aeskeygenassist $16, %%xmm3, %%xmm2 \n"
|
||||
KeyExpansion256(160,176)
|
||||
"aeskeygenassist $32, %%xmm3, %%xmm2 \n"
|
||||
KeyExpansion256(192,208)
|
||||
"aeskeygenassist $64, %%xmm3, %%xmm2 \n"
|
||||
// key expansion final
|
||||
"pshufd $0xff, %%xmm2, %%xmm2 \n"
|
||||
"movaps %%xmm1, %%xmm4 \n"
|
||||
"pslldq $4, %%xmm4 \n"
|
||||
"pxor %%xmm4, %%xmm1 \n"
|
||||
"pslldq $4, %%xmm4 \n"
|
||||
"pxor %%xmm4, %%xmm1 \n"
|
||||
"pslldq $4, %%xmm4 \n"
|
||||
"pxor %%xmm4, %%xmm1 \n"
|
||||
"pxor %%xmm2, %%xmm1 \n"
|
||||
"movups %%xmm1, 224(%[sched]) \n"
|
||||
: // output
|
||||
: [key]"r"((const uint8_t *)key), [sched]"r"(GetKeySchedule ()) // input
|
||||
: "%xmm1", "%xmm2", "%xmm3", "%xmm4", "memory" // clogged
|
||||
);
|
||||
}
|
||||
|
||||
#define EncryptAES256(sched) \
|
||||
"pxor (%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 16(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 32(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 48(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 64(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 80(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 96(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 112(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 128(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 144(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 160(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 176(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 192(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenc 208(%["#sched"]), %%xmm0 \n" \
|
||||
"aesenclast 224(%["#sched"]), %%xmm0 \n"
|
||||
|
||||
void ECBEncryptionAESNI::Encrypt (const ChipherBlock * in, ChipherBlock * out)
|
||||
{
|
||||
__asm__
|
||||
(
|
||||
"movups (%[in]), %%xmm0 \n"
|
||||
EncryptAES256(sched)
|
||||
"movups %%xmm0, (%[out]) \n"
|
||||
: : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
#define DecryptAES256(sched) \
|
||||
"pxor 224(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 208(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 192(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 176(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 160(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 144(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 128(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 112(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 96(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 80(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 64(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 48(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 32(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdec 16(%["#sched"]), %%xmm0 \n" \
|
||||
"aesdeclast (%["#sched"]), %%xmm0 \n"
|
||||
|
||||
void ECBDecryptionAESNI::Decrypt (const ChipherBlock * in, ChipherBlock * out)
|
||||
{
|
||||
__asm__
|
||||
(
|
||||
"movups (%[in]), %%xmm0 \n"
|
||||
DecryptAES256(sched)
|
||||
"movups %%xmm0, (%[out]) \n"
|
||||
: : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
#define CallAESIMC(offset) \
|
||||
"movaps "#offset"(%[shed]), %%xmm0 \n" \
|
||||
"aesimc %%xmm0, %%xmm0 \n" \
|
||||
"movaps %%xmm0, "#offset"(%[shed]) \n"
|
||||
|
||||
void ECBDecryptionAESNI::SetKey (const AESKey& key)
|
||||
{
|
||||
ExpandKey (key); // expand encryption key first
|
||||
// then invert it using aesimc
|
||||
__asm__
|
||||
(
|
||||
CallAESIMC(16)
|
||||
CallAESIMC(32)
|
||||
CallAESIMC(48)
|
||||
CallAESIMC(64)
|
||||
CallAESIMC(80)
|
||||
CallAESIMC(96)
|
||||
CallAESIMC(112)
|
||||
CallAESIMC(128)
|
||||
CallAESIMC(144)
|
||||
CallAESIMC(160)
|
||||
CallAESIMC(176)
|
||||
CallAESIMC(192)
|
||||
CallAESIMC(208)
|
||||
: : [shed]"r"(GetKeySchedule ()) : "%xmm0", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
void CBCEncryption::Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out)
|
||||
{
|
||||
#ifdef AESNI
|
||||
__asm__
|
||||
(
|
||||
"movups (%[iv]), %%xmm1 \n"
|
||||
"1: \n"
|
||||
"movups (%[in]), %%xmm0 \n"
|
||||
"pxor %%xmm1, %%xmm0 \n"
|
||||
EncryptAES256(sched)
|
||||
"movaps %%xmm0, %%xmm1 \n"
|
||||
"movups %%xmm0, (%[out]) \n"
|
||||
"add $16, %[in] \n"
|
||||
"add $16, %[out] \n"
|
||||
"dec %[num] \n"
|
||||
"jnz 1b \n"
|
||||
"movups %%xmm1, (%[iv]) \n"
|
||||
:
|
||||
: [iv]"r"(&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;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CBCEncryption::Encrypt (const uint8_t * in, std::size_t len, uint8_t * out)
|
||||
{
|
||||
// len/16
|
||||
int numBlocks = len >> 4;
|
||||
if (numBlocks > 0)
|
||||
Encrypt (numBlocks, (const ChipherBlock *)in, (ChipherBlock *)out);
|
||||
}
|
||||
|
||||
void CBCEncryption::Encrypt (const uint8_t * in, uint8_t * out)
|
||||
{
|
||||
#ifdef AESNI
|
||||
__asm__
|
||||
(
|
||||
"movups (%[iv]), %%xmm1 \n"
|
||||
"movups (%[in]), %%xmm0 \n"
|
||||
"pxor %%xmm1, %%xmm0 \n"
|
||||
EncryptAES256(sched)
|
||||
"movups %%xmm0, (%[out]) \n"
|
||||
"movups %%xmm0, (%[iv]) \n"
|
||||
:
|
||||
: [iv]"r"(&m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()),
|
||||
[in]"r"(in), [out]"r"(out)
|
||||
: "%xmm0", "%xmm1", "memory"
|
||||
);
|
||||
#else
|
||||
Encrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CBCDecryption::Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out)
|
||||
{
|
||||
#ifdef AESNI
|
||||
__asm__
|
||||
(
|
||||
"movups (%[iv]), %%xmm1 \n"
|
||||
"1: \n"
|
||||
"movups (%[in]), %%xmm0 \n"
|
||||
"movaps %%xmm0, %%xmm2 \n"
|
||||
DecryptAES256(sched)
|
||||
"pxor %%xmm1, %%xmm0 \n"
|
||||
"movups %%xmm0, (%[out]) \n"
|
||||
"movaps %%xmm2, %%xmm1 \n"
|
||||
"add $16, %[in] \n"
|
||||
"add $16, %[out] \n"
|
||||
"dec %[num] \n"
|
||||
"jnz 1b \n"
|
||||
"movups %%xmm1, (%[iv]) \n"
|
||||
:
|
||||
: [iv]"r"(&m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
|
||||
[in]"r"(in), [out]"r"(out), [num]"r"(numBlocks)
|
||||
: "%xmm0", "%xmm1", "%xmm2", "cc", "memory"
|
||||
);
|
||||
#else
|
||||
for (int i = 0; i < numBlocks; i++)
|
||||
{
|
||||
ChipherBlock tmp = in[i];
|
||||
m_ECBDecryption.Decrypt (in + i, out + i);
|
||||
out[i] ^= m_IV;
|
||||
m_IV = tmp;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CBCDecryption::Decrypt (const uint8_t * in, std::size_t len, uint8_t * out)
|
||||
{
|
||||
int numBlocks = len >> 4;
|
||||
if (numBlocks > 0)
|
||||
Decrypt (numBlocks, (const ChipherBlock *)in, (ChipherBlock *)out);
|
||||
}
|
||||
|
||||
void CBCDecryption::Decrypt (const uint8_t * in, uint8_t * out)
|
||||
{
|
||||
#ifdef AESNI
|
||||
__asm__
|
||||
(
|
||||
"movups (%[iv]), %%xmm1 \n"
|
||||
"movups (%[in]), %%xmm0 \n"
|
||||
"movups %%xmm0, (%[iv]) \n"
|
||||
DecryptAES256(sched)
|
||||
"pxor %%xmm1, %%xmm0 \n"
|
||||
"movups %%xmm0, (%[out]) \n"
|
||||
:
|
||||
: [iv]"r"(&m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
|
||||
[in]"r"(in), [out]"r"(out)
|
||||
: "%xmm0", "%xmm1", "memory"
|
||||
);
|
||||
#else
|
||||
Decrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out);
|
||||
#endif
|
||||
}
|
||||
|
||||
void TunnelEncryption::Encrypt (const uint8_t * in, uint8_t * out)
|
||||
{
|
||||
#ifdef AESNI
|
||||
__asm__
|
||||
(
|
||||
// encrypt IV
|
||||
"movups (%[in]), %%xmm0 \n"
|
||||
EncryptAES256(sched_iv)
|
||||
"movaps %%xmm0, %%xmm1 \n"
|
||||
// double IV encryption
|
||||
EncryptAES256(sched_iv)
|
||||
"movups %%xmm0, (%[out]) \n"
|
||||
// encrypt data, IV is xmm1
|
||||
"1: \n"
|
||||
"add $16, %[in] \n"
|
||||
"add $16, %[out] \n"
|
||||
"movups (%[in]), %%xmm0 \n"
|
||||
"pxor %%xmm1, %%xmm0 \n"
|
||||
EncryptAES256(sched_l)
|
||||
"movaps %%xmm0, %%xmm1 \n"
|
||||
"movups %%xmm0, (%[out]) \n"
|
||||
"dec %[num] \n"
|
||||
"jnz 1b \n"
|
||||
:
|
||||
: [sched_iv]"r"(m_IVEncryption.GetKeySchedule ()), [sched_l]"r"(m_LayerEncryption.GetKeySchedule ()),
|
||||
[in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes
|
||||
: "%xmm0", "%xmm1", "cc", "memory"
|
||||
);
|
||||
#else
|
||||
m_IVEncryption.Encrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv
|
||||
m_LayerEncryption.SetIV (out);
|
||||
m_LayerEncryption.Encrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data
|
||||
m_IVEncryption.Encrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv
|
||||
#endif
|
||||
}
|
||||
|
||||
void TunnelDecryption::Decrypt (const uint8_t * in, uint8_t * out)
|
||||
{
|
||||
#ifdef AESNI
|
||||
__asm__
|
||||
(
|
||||
// decrypt IV
|
||||
"movups (%[in]), %%xmm0 \n"
|
||||
DecryptAES256(sched_iv)
|
||||
"movaps %%xmm0, %%xmm1 \n"
|
||||
// double IV encryption
|
||||
DecryptAES256(sched_iv)
|
||||
"movups %%xmm0, (%[out]) \n"
|
||||
// decrypt data, IV is xmm1
|
||||
"1: \n"
|
||||
"add $16, %[in] \n"
|
||||
"add $16, %[out] \n"
|
||||
"movups (%[in]), %%xmm0 \n"
|
||||
"movaps %%xmm0, %%xmm2 \n"
|
||||
DecryptAES256(sched_l)
|
||||
"pxor %%xmm1, %%xmm0 \n"
|
||||
"movups %%xmm0, (%[out]) \n"
|
||||
"movaps %%xmm2, %%xmm1 \n"
|
||||
"dec %[num] \n"
|
||||
"jnz 1b \n"
|
||||
:
|
||||
: [sched_iv]"r"(m_IVDecryption.GetKeySchedule ()), [sched_l]"r"(m_LayerDecryption.GetKeySchedule ()),
|
||||
[in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes
|
||||
: "%xmm0", "%xmm1", "%xmm2", "cc", "memory"
|
||||
);
|
||||
#else
|
||||
m_IVDecryption.Decrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv
|
||||
m_LayerDecryption.SetIV (out);
|
||||
m_LayerDecryption.Decrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data
|
||||
m_IVDecryption.Decrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv
|
||||
#endif
|
||||
}
|
||||
|
||||
/* std::vector <std::unique_ptr<std::mutex> > m_OpenSSLMutexes;
|
||||
static void OpensslLockingCallback(int mode, int type, const char * file, int line)
|
||||
{
|
||||
if (type > 0 && (size_t)type < m_OpenSSLMutexes.size ())
|
||||
{
|
||||
if (mode & CRYPTO_LOCK)
|
||||
m_OpenSSLMutexes[type]->lock ();
|
||||
else
|
||||
m_OpenSSLMutexes[type]->unlock ();
|
||||
}
|
||||
}*/
|
||||
|
||||
void InitCrypto (bool precomputation)
|
||||
{
|
||||
SSL_library_init ();
|
||||
/* auto numLocks = CRYPTO_num_locks();
|
||||
for (int i = 0; i < numLocks; i++)
|
||||
m_OpenSSLMutexes.emplace_back (new std::mutex);
|
||||
CRYPTO_set_locking_callback (OpensslLockingCallback);*/
|
||||
if (precomputation)
|
||||
{
|
||||
#if defined(__x86_64__)
|
||||
g_ElggTable = new BIGNUM * [ELGAMAL_FULL_EXPONENT_NUM_BYTES][255];
|
||||
PrecalculateElggTable (g_ElggTable, ELGAMAL_FULL_EXPONENT_NUM_BYTES);
|
||||
#else
|
||||
g_ElggTable = new BIGNUM * [ELGAMAL_SHORT_EXPONENT_NUM_BYTES][255];
|
||||
PrecalculateElggTable (g_ElggTable, ELGAMAL_SHORT_EXPONENT_NUM_BYTES);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void TerminateCrypto ()
|
||||
{
|
||||
if (g_ElggTable)
|
||||
{
|
||||
DestroyElggTable (g_ElggTable,
|
||||
#if defined(__x86_64__)
|
||||
ELGAMAL_FULL_EXPONENT_NUM_BYTES
|
||||
#else
|
||||
ELGAMAL_SHORT_EXPONENT_NUM_BYTES
|
||||
#endif
|
||||
);
|
||||
delete[] g_ElggTable; g_ElggTable = nullptr;
|
||||
}
|
||||
/* CRYPTO_set_locking_callback (nullptr);
|
||||
m_OpenSSLMutexes.clear ();*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
100
aes.h → Crypto.h
100
aes.h → Crypto.h
@@ -1,22 +1,80 @@
|
||||
#ifndef AES_H__
|
||||
#define AES_H__
|
||||
#ifndef CRYPTO_H__
|
||||
#define CRYPTO_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cryptopp/modes.h>
|
||||
#include <cryptopp/aes.h>
|
||||
#include "Identity.h"
|
||||
#include <string>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/dh.h>
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/dsa.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#include "Base.h"
|
||||
#include "Tag.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace crypto
|
||||
{
|
||||
{
|
||||
bool bn2buf (const BIGNUM * bn, uint8_t * buf, size_t len);
|
||||
|
||||
// DSA
|
||||
DSA * CreateDSA ();
|
||||
|
||||
// RSA
|
||||
const BIGNUM * GetRSAE ();
|
||||
|
||||
// DH
|
||||
class DHKeys
|
||||
{
|
||||
public:
|
||||
|
||||
DHKeys ();
|
||||
~DHKeys ();
|
||||
|
||||
void GenerateKeys (uint8_t * priv = nullptr, uint8_t * pub = nullptr);
|
||||
const uint8_t * GetPublicKey ();
|
||||
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;
|
||||
};
|
||||
|
||||
bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, uint8_t * data, bool zeroPadding = false);
|
||||
void GenerateElGamalKeyPair (uint8_t * priv, uint8_t * pub);
|
||||
|
||||
// HMAC
|
||||
typedef i2p::data::Tag<32> MACKey;
|
||||
void HMACMD5Digest (uint8_t * msg, size_t len, const MACKey& key, uint8_t * digest);
|
||||
|
||||
// AES
|
||||
struct ChipherBlock
|
||||
{
|
||||
uint8_t buf[16];
|
||||
|
||||
void operator^=(const ChipherBlock& other) // XOR
|
||||
{
|
||||
#if defined(__x86_64__) // for Intel x64
|
||||
#if defined(__x86_64__) || defined(__SSE__) // for Intel x84 or with SSE
|
||||
__asm__
|
||||
(
|
||||
"movups (%[buf]), %%xmm0 \n"
|
||||
@@ -95,7 +153,7 @@ namespace crypto
|
||||
typedef ECBEncryptionAESNI ECBEncryption;
|
||||
typedef ECBDecryptionAESNI ECBDecryption;
|
||||
|
||||
#else // use crypto++
|
||||
#else // use openssl
|
||||
|
||||
class ECBEncryption
|
||||
{
|
||||
@@ -103,16 +161,16 @@ namespace crypto
|
||||
|
||||
void SetKey (const AESKey& key)
|
||||
{
|
||||
m_Encryption.SetKey (key, 32);
|
||||
AES_set_encrypt_key (key, 256, &m_Key);
|
||||
}
|
||||
void Encrypt (const ChipherBlock * in, ChipherBlock * out)
|
||||
{
|
||||
m_Encryption.ProcessData (out->buf, in->buf, 16);
|
||||
AES_encrypt (in->buf, out->buf, &m_Key);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
CryptoPP::ECB_Mode<CryptoPP::AES>::Encryption m_Encryption;
|
||||
AES_KEY m_Key;
|
||||
};
|
||||
|
||||
class ECBDecryption
|
||||
@@ -121,16 +179,16 @@ namespace crypto
|
||||
|
||||
void SetKey (const AESKey& key)
|
||||
{
|
||||
m_Decryption.SetKey (key, 32);
|
||||
AES_set_decrypt_key (key, 256, &m_Key);
|
||||
}
|
||||
void Decrypt (const ChipherBlock * in, ChipherBlock * out)
|
||||
{
|
||||
m_Decryption.ProcessData (out->buf, in->buf, 16);
|
||||
AES_decrypt (in->buf, out->buf, &m_Key);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
CryptoPP::ECB_Mode<CryptoPP::AES>::Decryption m_Decryption;
|
||||
AES_KEY m_Key;
|
||||
};
|
||||
|
||||
|
||||
@@ -185,7 +243,7 @@ namespace crypto
|
||||
m_IVEncryption.SetKey (ivKey);
|
||||
}
|
||||
|
||||
void Encrypt (uint8_t * payload); // 1024 bytes (16 IV + 1008 data)
|
||||
void Encrypt (const uint8_t * in, uint8_t * out); // 1024 bytes (16 IV + 1008 data)
|
||||
|
||||
private:
|
||||
|
||||
@@ -207,7 +265,7 @@ namespace crypto
|
||||
m_IVDecryption.SetKey (ivKey);
|
||||
}
|
||||
|
||||
void Decrypt (uint8_t * payload); // 1024 bytes (16 IV + 1008 data)
|
||||
void Decrypt (const uint8_t * in, uint8_t * out); // 1024 bytes (16 IV + 1008 data)
|
||||
|
||||
private:
|
||||
|
||||
@@ -217,9 +275,11 @@ namespace crypto
|
||||
#else
|
||||
CBCDecryption m_LayerDecryption;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void InitCrypto (bool precomputation);
|
||||
void TerminateCrypto ();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
#include <inttypes.h>
|
||||
#include "CryptoConst.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace crypto
|
||||
{
|
||||
const uint8_t elgp_[256]=
|
||||
{
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
|
||||
0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
|
||||
0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
|
||||
0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
|
||||
0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
|
||||
0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
|
||||
0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
|
||||
0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
|
||||
0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
|
||||
0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
|
||||
0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
|
||||
0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
|
||||
0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
|
||||
0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
|
||||
0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
|
||||
0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
const uint8_t dsap_[128]=
|
||||
{
|
||||
0x9c, 0x05, 0xb2, 0xaa, 0x96, 0x0d, 0x9b, 0x97, 0xb8, 0x93, 0x19, 0x63, 0xc9, 0xcc, 0x9e, 0x8c,
|
||||
0x30, 0x26, 0xe9, 0xb8, 0xed, 0x92, 0xfa, 0xd0, 0xa6, 0x9c, 0xc8, 0x86, 0xd5, 0xbf, 0x80, 0x15,
|
||||
0xfc, 0xad, 0xae, 0x31, 0xa0, 0xad, 0x18, 0xfa, 0xb3, 0xf0, 0x1b, 0x00, 0xa3, 0x58, 0xde, 0x23,
|
||||
0x76, 0x55, 0xc4, 0x96, 0x4a, 0xfa, 0xa2, 0xb3, 0x37, 0xe9, 0x6a, 0xd3, 0x16, 0xb9, 0xfb, 0x1c,
|
||||
0xc5, 0x64, 0xb5, 0xae, 0xc5, 0xb6, 0x9a, 0x9f, 0xf6, 0xc3, 0xe4, 0x54, 0x87, 0x07, 0xfe, 0xf8,
|
||||
0x50, 0x3d, 0x91, 0xdd, 0x86, 0x02, 0xe8, 0x67, 0xe6, 0xd3, 0x5d, 0x22, 0x35, 0xc1, 0x86, 0x9c,
|
||||
0xe2, 0x47, 0x9c, 0x3b, 0x9d, 0x54, 0x01, 0xde, 0x04, 0xe0, 0x72, 0x7f, 0xb3, 0x3d, 0x65, 0x11,
|
||||
0x28, 0x5d, 0x4c, 0xf2, 0x95, 0x38, 0xd9, 0xe3, 0xb6, 0x05, 0x1f, 0x5b, 0x22, 0xcc, 0x1c, 0x93
|
||||
};
|
||||
|
||||
const uint8_t dsaq_[20]=
|
||||
{
|
||||
0xa5, 0xdf, 0xc2, 0x8f, 0xef, 0x4c, 0xa1, 0xe2, 0x86, 0x74, 0x4c, 0xd8, 0xee, 0xd9, 0xd2, 0x9d,
|
||||
0x68, 0x40, 0x46, 0xb7
|
||||
};
|
||||
|
||||
const uint8_t dsag_[128]=
|
||||
{
|
||||
0x0c, 0x1f, 0x4d, 0x27, 0xd4, 0x00, 0x93, 0xb4, 0x29, 0xe9, 0x62, 0xd7, 0x22, 0x38, 0x24, 0xe0,
|
||||
0xbb, 0xc4, 0x7e, 0x7c, 0x83, 0x2a, 0x39, 0x23, 0x6f, 0xc6, 0x83, 0xaf, 0x84, 0x88, 0x95, 0x81,
|
||||
0x07, 0x5f, 0xf9, 0x08, 0x2e, 0xd3, 0x23, 0x53, 0xd4, 0x37, 0x4d, 0x73, 0x01, 0xcd, 0xa1, 0xd2,
|
||||
0x3c, 0x43, 0x1f, 0x46, 0x98, 0x59, 0x9d, 0xda, 0x02, 0x45, 0x18, 0x24, 0xff, 0x36, 0x97, 0x52,
|
||||
0x59, 0x36, 0x47, 0xcc, 0x3d, 0xdc, 0x19, 0x7d, 0xe9, 0x85, 0xe4, 0x3d, 0x13, 0x6c, 0xdc, 0xfc,
|
||||
0x6b, 0xd5, 0x40, 0x9c, 0xd2, 0xf4, 0x50, 0x82, 0x11, 0x42, 0xa5, 0xe6, 0xf8, 0xeb, 0x1c, 0x3a,
|
||||
0xb5, 0xd0, 0x48, 0x4b, 0x81, 0x29, 0xfc, 0xf1, 0x7b, 0xce, 0x4f, 0x7f, 0x33, 0x32, 0x1c, 0x3c,
|
||||
0xb3, 0xdb, 0xb1, 0x4a, 0x90, 0x5e, 0x7b, 0x2b, 0x3e, 0x93, 0xbe, 0x47, 0x08, 0xcb, 0xcc, 0x82
|
||||
};
|
||||
|
||||
const CryptoConstants& GetCryptoConstants ()
|
||||
{
|
||||
static CryptoConstants cryptoConstants =
|
||||
{
|
||||
{elgp_, 256}, // elgp
|
||||
{2}, // elgg
|
||||
{dsap_, 128}, // dsap
|
||||
{dsaq_, 20}, // dsaq
|
||||
{dsag_, 128} // dsag
|
||||
};
|
||||
return cryptoConstants;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
#ifndef CRYPTO_CONST_H__
|
||||
#define CRYPTO_CONST_H__
|
||||
|
||||
#include <cryptopp/integer.h>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace crypto
|
||||
{
|
||||
struct CryptoConstants
|
||||
{
|
||||
// DH/ElGamal
|
||||
const CryptoPP::Integer elgp;
|
||||
const CryptoPP::Integer elgg;
|
||||
|
||||
// DSA
|
||||
const CryptoPP::Integer dsap;
|
||||
const CryptoPP::Integer dsaq;
|
||||
const CryptoPP::Integer dsag;
|
||||
};
|
||||
|
||||
const CryptoConstants& GetCryptoConstants ();
|
||||
|
||||
// DH/ElGamal
|
||||
#define elgp GetCryptoConstants ().elgp
|
||||
#define elgg GetCryptoConstants ().elgg
|
||||
|
||||
// DSA
|
||||
#define dsap GetCryptoConstants ().dsap
|
||||
#define dsaq GetCryptoConstants ().dsaq
|
||||
#define dsag GetCryptoConstants ().dsag
|
||||
|
||||
// RSA
|
||||
const int rsae = 65537;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
321
Daemon.cpp
321
Daemon.cpp
@@ -1,27 +1,29 @@
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
|
||||
#include "Daemon.h"
|
||||
|
||||
#include "Config.h"
|
||||
#include "Log.h"
|
||||
#include "base64.h"
|
||||
#include "FS.h"
|
||||
#include "Base.h"
|
||||
#include "version.h"
|
||||
#include "Transports.h"
|
||||
#include "NTCPSession.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "RouterContext.h"
|
||||
#include "Tunnel.h"
|
||||
#include "HTTP.h"
|
||||
#include "NetDb.h"
|
||||
#include "Garlic.h"
|
||||
#include "util.h"
|
||||
#include "Streaming.h"
|
||||
#include "Destination.h"
|
||||
#include "HTTPServer.h"
|
||||
#include "I2PControl.h"
|
||||
#include "ClientContext.h"
|
||||
|
||||
#ifdef USE_UPNP
|
||||
#include "Crypto.h"
|
||||
#include "UPnP.h"
|
||||
#endif
|
||||
|
||||
#include "util.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -30,121 +32,268 @@ namespace i2p
|
||||
class Daemon_Singleton::Daemon_Singleton_Private
|
||||
{
|
||||
public:
|
||||
Daemon_Singleton_Private() : httpServer(nullptr)
|
||||
{};
|
||||
~Daemon_Singleton_Private()
|
||||
{
|
||||
delete httpServer;
|
||||
};
|
||||
Daemon_Singleton_Private() {};
|
||||
~Daemon_Singleton_Private() {};
|
||||
|
||||
i2p::util::HTTPServer *httpServer;
|
||||
std::unique_ptr<i2p::http::HTTPServer> httpServer;
|
||||
std::unique_ptr<i2p::client::I2PControlService> m_I2PControlService;
|
||||
std::unique_ptr<i2p::transport::UPnP> UPnP;
|
||||
};
|
||||
|
||||
Daemon_Singleton::Daemon_Singleton() : running(1), d(*new Daemon_Singleton_Private()) {};
|
||||
Daemon_Singleton::Daemon_Singleton() : isDaemon(false), running(true), d(*new Daemon_Singleton_Private()) {}
|
||||
Daemon_Singleton::~Daemon_Singleton() {
|
||||
delete &d;
|
||||
};
|
||||
}
|
||||
|
||||
bool Daemon_Singleton::IsService () const
|
||||
{
|
||||
bool service = false;
|
||||
#ifndef _WIN32
|
||||
return i2p::util::config::GetArg("-service", 0);
|
||||
#else
|
||||
return false;
|
||||
i2p::config::GetOption("service", service);
|
||||
#endif
|
||||
return service;
|
||||
}
|
||||
|
||||
bool Daemon_Singleton::init(int argc, char* argv[])
|
||||
{
|
||||
i2p::util::config::OptionParser(argc, argv);
|
||||
i2p::config::Init();
|
||||
i2p::config::ParseCmdline(argc, argv);
|
||||
|
||||
std::string config; i2p::config::GetOption("conf", config);
|
||||
std::string datadir; i2p::config::GetOption("datadir", datadir);
|
||||
i2p::fs::DetectDataDir(datadir, IsService());
|
||||
i2p::fs::Init();
|
||||
|
||||
datadir = i2p::fs::GetDataDir();
|
||||
// TODO: drop old name detection in v2.8.0
|
||||
if (config == "")
|
||||
{
|
||||
config = i2p::fs::DataDirPath("i2p.conf");
|
||||
if (i2p::fs::Exists (config)) {
|
||||
LogPrint(eLogWarning, "Daemon: please rename i2p.conf to i2pd.conf here: ", config);
|
||||
} else {
|
||||
config = i2p::fs::DataDirPath("i2pd.conf");
|
||||
if (!i2p::fs::Exists (config)) {
|
||||
// use i2pd.conf only if exists
|
||||
config = ""; /* reset */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i2p::config::ParseConfig(config);
|
||||
i2p::config::Finalize();
|
||||
|
||||
i2p::config::GetOption("daemon", isDaemon);
|
||||
|
||||
std::string logs = ""; i2p::config::GetOption("log", logs);
|
||||
std::string logfile = ""; i2p::config::GetOption("logfile", logfile);
|
||||
std::string loglevel = ""; i2p::config::GetOption("loglevel", loglevel);
|
||||
|
||||
/* setup logging */
|
||||
if (isDaemon && (logs == "" || logs == "stdout"))
|
||||
logs = "file";
|
||||
|
||||
i2p::log::Logger().SetLogLevel(loglevel);
|
||||
if (logs == "file") {
|
||||
if (logfile == "")
|
||||
logfile = i2p::fs::DataDirPath("i2pd.log");
|
||||
LogPrint(eLogInfo, "Log: will send messages to ", logfile);
|
||||
i2p::log::Logger().SendTo (logfile);
|
||||
#ifndef _WIN32
|
||||
} else if (logs == "syslog") {
|
||||
LogPrint(eLogInfo, "Log: will send messages to syslog");
|
||||
i2p::log::Logger().SendTo("i2pd", LOG_DAEMON);
|
||||
#endif
|
||||
} else {
|
||||
// use stdout -- default
|
||||
}
|
||||
i2p::log::Logger().Ready();
|
||||
|
||||
LogPrint(eLogInfo, "i2pd v", VERSION, " starting");
|
||||
LogPrint(eLogDebug, "FS: main config file: ", config);
|
||||
LogPrint(eLogDebug, "FS: data directory: ", datadir);
|
||||
|
||||
bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation);
|
||||
i2p::crypto::InitCrypto (precomputation);
|
||||
i2p::context.Init ();
|
||||
|
||||
LogPrint("\n\n\n\ni2pd starting\n");
|
||||
LogPrint("Version ", VERSION);
|
||||
LogPrint("data directory: ", i2p::util::filesystem::GetDataDir().string());
|
||||
i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs);
|
||||
|
||||
isDaemon = i2p::util::config::GetArg("-daemon", 0);
|
||||
isLogging = i2p::util::config::GetArg("-log", 1);
|
||||
|
||||
int port = i2p::util::config::GetArg("-port", 0);
|
||||
if (port)
|
||||
i2p::context.UpdatePort (port);
|
||||
const char * host = i2p::util::config::GetCharArg("-host", "");
|
||||
if (host && host[0])
|
||||
i2p::context.UpdateAddress (boost::asio::ip::address::from_string (host));
|
||||
|
||||
if (i2p::util::config::GetArg("-unreachable", 0))
|
||||
i2p::context.SetUnreachable ();
|
||||
|
||||
i2p::context.SetSupportsV6 (i2p::util::config::GetArg("-v6", 0));
|
||||
i2p::context.SetFloodfill (i2p::util::config::GetArg("-floodfill", 0));
|
||||
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
|
||||
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
|
||||
#ifdef MESHNET
|
||||
// manual override for meshnet
|
||||
ipv4 = false;
|
||||
ipv6 = true;
|
||||
#endif
|
||||
uint16_t port; i2p::config::GetOption("port", port);
|
||||
if (!i2p::config::IsDefault("port"))
|
||||
{
|
||||
LogPrint(eLogInfo, "Daemon: accepting incoming connections at port ", port);
|
||||
i2p::context.UpdatePort (port);
|
||||
}
|
||||
i2p::context.SetSupportsV6 (ipv6);
|
||||
i2p::context.SetSupportsV4 (ipv4);
|
||||
|
||||
LogPrint("CMD parameters:");
|
||||
for (int i = 0; i < argc; ++i)
|
||||
LogPrint(i, " ", argv[i]);
|
||||
bool transit; i2p::config::GetOption("notransit", transit);
|
||||
i2p::context.SetAcceptsTunnels (!transit);
|
||||
uint16_t transitTunnels; i2p::config::GetOption("limits.transittunnels", transitTunnels);
|
||||
SetMaxNumTransitTunnels (transitTunnels);
|
||||
|
||||
return true;
|
||||
bool isFloodfill; i2p::config::GetOption("floodfill", isFloodfill);
|
||||
if (isFloodfill) {
|
||||
LogPrint(eLogInfo, "Daemon: router will be floodfill");
|
||||
i2p::context.SetFloodfill (true);
|
||||
} else {
|
||||
i2p::context.SetFloodfill (false);
|
||||
}
|
||||
|
||||
/* this section also honors 'floodfill' flag, if set above */
|
||||
std::string bandwidth; i2p::config::GetOption("bandwidth", bandwidth);
|
||||
if (bandwidth.length () > 0)
|
||||
{
|
||||
if (bandwidth[0] >= 'K' && bandwidth[0] <= 'X')
|
||||
{
|
||||
i2p::context.SetBandwidth (bandwidth[0]);
|
||||
LogPrint(eLogInfo, "Daemon: bandwidth set to ", i2p::context.GetBandwidthLimit (), "KBps");
|
||||
}
|
||||
else
|
||||
{
|
||||
auto value = std::atoi(bandwidth.c_str());
|
||||
if (value > 0)
|
||||
{
|
||||
i2p::context.SetBandwidth (value);
|
||||
LogPrint(eLogInfo, "Daemon: bandwidth set to ", i2p::context.GetBandwidthLimit (), " KBps");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint(eLogInfo, "Daemon: unexpected bandwidth ", bandwidth, ". Set to 'low'");
|
||||
i2p::context.SetBandwidth (i2p::data::CAPS_FLAG_LOW_BANDWIDTH2);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (isFloodfill)
|
||||
{
|
||||
LogPrint(eLogInfo, "Daemon: floodfill bandwidth set to 'extra'");
|
||||
i2p::context.SetBandwidth (i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH1);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint(eLogInfo, "Daemon: bandwidth set to 'low'");
|
||||
i2p::context.SetBandwidth (i2p::data::CAPS_FLAG_LOW_BANDWIDTH2);
|
||||
}
|
||||
|
||||
std::string family; i2p::config::GetOption("family", family);
|
||||
i2p::context.SetFamily (family);
|
||||
if (family.length () > 0)
|
||||
LogPrint(eLogInfo, "Daemon: family set to ", family);
|
||||
|
||||
bool trust; i2p::config::GetOption("trust.enabled", trust);
|
||||
if (trust)
|
||||
{
|
||||
LogPrint(eLogInfo, "Daemon: explicit trust enabled");
|
||||
std::string fam; i2p::config::GetOption("trust.family", fam);
|
||||
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");
|
||||
}
|
||||
bool hidden; i2p::config::GetOption("trust.hidden", hidden);
|
||||
if (hidden)
|
||||
{
|
||||
LogPrint(eLogInfo, "Daemon: using hidden mode");
|
||||
i2p::data::netdb.SetHidden(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Daemon_Singleton::start()
|
||||
{
|
||||
// initialize log
|
||||
if (isLogging)
|
||||
{
|
||||
if (isDaemon)
|
||||
{
|
||||
std::string logfile_path = IsService () ? "/var/log" : i2p::util::filesystem::GetDataDir().string();
|
||||
#ifndef _WIN32
|
||||
logfile_path.append("/i2pd.log");
|
||||
#else
|
||||
logfile_path.append("\\i2pd.log");
|
||||
#endif
|
||||
StartLog (logfile_path);
|
||||
}
|
||||
else
|
||||
StartLog (""); // write to stdout
|
||||
LogPrint(eLogInfo, "Daemon: starting NetDB");
|
||||
i2p::data::netdb.Start();
|
||||
|
||||
bool upnp; i2p::config::GetOption("upnp.enabled", upnp);
|
||||
if (upnp) {
|
||||
d.UPnP = std::unique_ptr<i2p::transport::UPnP>(new i2p::transport::UPnP);
|
||||
d.UPnP->Start ();
|
||||
}
|
||||
|
||||
d.httpServer = new i2p::util::HTTPServer(i2p::util::config::GetArg("-httpport", 7070));
|
||||
d.httpServer->Start();
|
||||
LogPrint("HTTP Server started");
|
||||
i2p::data::netdb.Start();
|
||||
LogPrint("NetDB started");
|
||||
i2p::transport::transports.Start();
|
||||
LogPrint("Transports started");
|
||||
bool ntcp; i2p::config::GetOption("ntcp", ntcp);
|
||||
bool ssu; i2p::config::GetOption("ssu", ssu);
|
||||
LogPrint(eLogInfo, "Daemon: starting Transports");
|
||||
if(!ssu) LogPrint(eLogInfo, "Daemon: ssu disabled");
|
||||
if(!ntcp) LogPrint(eLogInfo, "Daemon: ntcp disabled");
|
||||
i2p::transport::transports.Start(ntcp, ssu);
|
||||
if (i2p::transport::transports.IsBoundNTCP() || i2p::transport::transports.IsBoundSSU()) {
|
||||
LogPrint(eLogInfo, "Daemon: Transports started");
|
||||
} else {
|
||||
LogPrint(eLogError, "Daemon: failed to start Transports");
|
||||
/** shut down netdb right away */
|
||||
i2p::transport::transports.Stop();
|
||||
i2p::data::netdb.Stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool http; i2p::config::GetOption("http.enabled", http);
|
||||
if (http) {
|
||||
std::string httpAddr; i2p::config::GetOption("http.address", httpAddr);
|
||||
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
|
||||
LogPrint(eLogInfo, "Daemon: starting HTTP Server at ", httpAddr, ":", httpPort);
|
||||
d.httpServer = std::unique_ptr<i2p::http::HTTPServer>(new i2p::http::HTTPServer(httpAddr, httpPort));
|
||||
d.httpServer->Start();
|
||||
}
|
||||
|
||||
|
||||
LogPrint(eLogInfo, "Daemon: starting Tunnels");
|
||||
i2p::tunnel::tunnels.Start();
|
||||
LogPrint("Tunnels started");
|
||||
|
||||
LogPrint(eLogInfo, "Daemon: starting Client");
|
||||
i2p::client::context.Start ();
|
||||
LogPrint("Client started");
|
||||
#ifdef USE_UPNP
|
||||
i2p::UPnP::upnpc.Start();
|
||||
LogPrint("UPnP module loaded");
|
||||
#endif
|
||||
|
||||
// I2P Control Protocol
|
||||
bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol);
|
||||
if (i2pcontrol) {
|
||||
std::string i2pcpAddr; i2p::config::GetOption("i2pcontrol.address", i2pcpAddr);
|
||||
uint16_t i2pcpPort; i2p::config::GetOption("i2pcontrol.port", i2pcpPort);
|
||||
LogPrint(eLogInfo, "Daemon: starting I2PControl at ", i2pcpAddr, ":", i2pcpPort);
|
||||
d.m_I2PControlService = std::unique_ptr<i2p::client::I2PControlService>(new i2p::client::I2PControlService (i2pcpAddr, i2pcpPort));
|
||||
d.m_I2PControlService->Start ();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Daemon_Singleton::stop()
|
||||
{
|
||||
LogPrint("Shutdown started.");
|
||||
LogPrint(eLogInfo, "Daemon: shutting down");
|
||||
LogPrint(eLogInfo, "Daemon: stopping Client");
|
||||
i2p::client::context.Stop();
|
||||
LogPrint("Client stopped");
|
||||
LogPrint(eLogInfo, "Daemon: stopping Tunnels");
|
||||
i2p::tunnel::tunnels.Stop();
|
||||
LogPrint("Tunnels stopped");
|
||||
i2p::transport::transports.Stop();
|
||||
LogPrint("Transports stopped");
|
||||
i2p::data::netdb.Stop();
|
||||
LogPrint("NetDB stopped");
|
||||
d.httpServer->Stop();
|
||||
LogPrint("HTTP Server stopped");
|
||||
#ifdef USE_UPNP
|
||||
i2p::UPnP::upnpc.Stop();
|
||||
#endif
|
||||
StopLog ();
|
||||
|
||||
delete d.httpServer; d.httpServer = nullptr;
|
||||
if (d.UPnP) {
|
||||
d.UPnP->Stop ();
|
||||
d.UPnP = nullptr;
|
||||
}
|
||||
|
||||
LogPrint(eLogInfo, "Daemon: stopping Transports");
|
||||
i2p::transport::transports.Stop();
|
||||
LogPrint(eLogInfo, "Daemon: stopping NetDB");
|
||||
i2p::data::netdb.Stop();
|
||||
if (d.httpServer) {
|
||||
LogPrint(eLogInfo, "Daemon: stopping HTTP Server");
|
||||
d.httpServer->Stop();
|
||||
d.httpServer = nullptr;
|
||||
}
|
||||
if (d.m_I2PControlService)
|
||||
{
|
||||
LogPrint(eLogInfo, "Daemon: stopping I2PControl");
|
||||
d.m_I2PControlService->Stop ();
|
||||
d.m_I2PControlService = nullptr;
|
||||
}
|
||||
i2p::crypto::TerminateCrypto ();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
92
Daemon.h
92
Daemon.h
@@ -1,11 +1,8 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#ifndef DAEMON_H__
|
||||
#define DAEMON_H__
|
||||
|
||||
#ifdef _WIN32
|
||||
#define Daemon i2p::util::DaemonWin32::Instance()
|
||||
#else
|
||||
#define Daemon i2p::util::DaemonLinux::Instance()
|
||||
#endif
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -18,24 +15,52 @@ namespace i2p
|
||||
virtual bool init(int argc, char* argv[]);
|
||||
virtual bool start();
|
||||
virtual bool stop();
|
||||
virtual void run () {};
|
||||
|
||||
int isLogging;
|
||||
int isDaemon;
|
||||
|
||||
int running;
|
||||
bool isDaemon;
|
||||
bool running;
|
||||
|
||||
protected:
|
||||
Daemon_Singleton();
|
||||
virtual ~Daemon_Singleton();
|
||||
|
||||
bool IsService () const;
|
||||
bool IsService () const;
|
||||
|
||||
// d-pointer for httpServer, httpProxy, etc.
|
||||
class Daemon_Singleton_Private;
|
||||
Daemon_Singleton_Private &d;
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
#if defined(QT_GUI_LIB) // check if QT
|
||||
#define Daemon i2p::util::DaemonQT::Instance()
|
||||
// dummy, invoked from RunQT
|
||||
class DaemonQT: public i2p::util::Daemon_Singleton
|
||||
{
|
||||
public:
|
||||
|
||||
static DaemonQT& Instance()
|
||||
{
|
||||
static DaemonQT instance;
|
||||
return instance;
|
||||
}
|
||||
};
|
||||
|
||||
#elif defined(ANDROID)
|
||||
#define Daemon i2p::util::DaemonAndroid::Instance()
|
||||
// dummy, invoked from android/jni/DaemonAndroid.*
|
||||
class DaemonAndroid: public i2p::util::Daemon_Singleton
|
||||
{
|
||||
public:
|
||||
|
||||
static DaemonAndroid& Instance()
|
||||
{
|
||||
static DaemonAndroid instance;
|
||||
return instance;
|
||||
}
|
||||
};
|
||||
|
||||
#elif defined(_WIN32)
|
||||
#define Daemon i2p::util::DaemonWin32::Instance()
|
||||
class DaemonWin32 : public Daemon_Singleton
|
||||
{
|
||||
public:
|
||||
@@ -45,27 +70,38 @@ namespace i2p
|
||||
return instance;
|
||||
}
|
||||
|
||||
virtual bool init(int argc, char* argv[]);
|
||||
virtual bool start();
|
||||
virtual bool stop();
|
||||
bool init(int argc, char* argv[]);
|
||||
bool start();
|
||||
bool stop();
|
||||
void run ();
|
||||
};
|
||||
#else
|
||||
class DaemonLinux : public Daemon_Singleton
|
||||
#define Daemon i2p::util::DaemonLinux::Instance()
|
||||
class DaemonLinux : public Daemon_Singleton
|
||||
{
|
||||
public:
|
||||
static DaemonLinux& Instance()
|
||||
{
|
||||
static DaemonLinux instance;
|
||||
return instance;
|
||||
}
|
||||
public:
|
||||
static DaemonLinux& Instance()
|
||||
{
|
||||
static DaemonLinux instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
virtual bool start();
|
||||
virtual bool stop();
|
||||
private:
|
||||
std::string pidfile;
|
||||
int pidFilehandle;
|
||||
bool start();
|
||||
bool stop();
|
||||
void run ();
|
||||
|
||||
private:
|
||||
|
||||
std::string pidfile;
|
||||
int pidFH;
|
||||
|
||||
public:
|
||||
|
||||
int gracefullShutdownInterval; // in seconds
|
||||
|
||||
};
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif // DAEMON_H__
|
||||
|
||||
126
DaemonLinux.cpp
126
DaemonLinux.cpp
@@ -4,47 +4,50 @@
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "Config.h"
|
||||
#include "FS.h"
|
||||
#include "Log.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "RouterContext.h"
|
||||
#include "ClientContext.h"
|
||||
|
||||
void handle_signal(int sig)
|
||||
{
|
||||
switch (sig)
|
||||
{
|
||||
case SIGHUP:
|
||||
if (i2p::util::config::GetArg("daemon", 0) == 1)
|
||||
{
|
||||
static bool first=true;
|
||||
if (first)
|
||||
{
|
||||
first=false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
LogPrint("Reloading config.");
|
||||
i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs);
|
||||
case SIGHUP:
|
||||
LogPrint(eLogInfo, "Daemon: Got SIGHUP, reopening log...");
|
||||
i2p::log::Logger().Reopen ();
|
||||
i2p::client::context.ReloadConfig();
|
||||
break;
|
||||
case SIGABRT:
|
||||
case SIGTERM:
|
||||
case SIGINT:
|
||||
Daemon.running = 0; // Exit loop
|
||||
case SIGINT:
|
||||
if (i2p::context.AcceptsTunnels () && !Daemon.gracefullShutdownInterval)
|
||||
{
|
||||
i2p::context.SetAcceptsTunnels (false);
|
||||
Daemon.gracefullShutdownInterval = 10*60; // 10 minutes
|
||||
LogPrint(eLogInfo, "Graceful shutdown after ", Daemon.gracefullShutdownInterval, " seconds");
|
||||
}
|
||||
else
|
||||
Daemon.running = 0;
|
||||
break;
|
||||
case SIGABRT:
|
||||
case SIGTERM:
|
||||
Daemon.running = 0; // Exit loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
bool DaemonLinux::start()
|
||||
{
|
||||
if (isDaemon == 1)
|
||||
if (isDaemon)
|
||||
{
|
||||
pid_t pid;
|
||||
pid = fork();
|
||||
@@ -52,44 +55,60 @@ namespace i2p
|
||||
::exit (EXIT_SUCCESS);
|
||||
|
||||
if (pid < 0) // error
|
||||
{
|
||||
LogPrint(eLogError, "Daemon: could not fork: ", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
// child
|
||||
umask(0);
|
||||
umask(S_IWGRP | S_IRWXO); // 0027
|
||||
int sid = setsid();
|
||||
if (sid < 0)
|
||||
{
|
||||
LogPrint("Error, could not create process group.");
|
||||
LogPrint(eLogError, "Daemon: could not create process group.");
|
||||
return false;
|
||||
}
|
||||
std::string d = i2p::fs::GetDataDir();
|
||||
if (chdir(d.c_str()) != 0)
|
||||
{
|
||||
LogPrint(eLogError, "Daemon: could not chdir: ", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
chdir(i2p::util::filesystem::GetDataDir().string().c_str());
|
||||
|
||||
// close stdin/stdout/stderr descriptors
|
||||
::close (0);
|
||||
::open ("/dev/null", O_RDWR);
|
||||
::close (1);
|
||||
::open ("/dev/null", O_RDWR);
|
||||
::close (2);
|
||||
::open ("/dev/null", O_RDWR);
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Pidfile
|
||||
pidfile = IsService () ? "/var/run" : i2p::util::filesystem::GetDataDir().string();
|
||||
pidfile.append("/i2pd.pid");
|
||||
pidFilehandle = open(pidfile.c_str(), O_RDWR | O_CREAT, 0600);
|
||||
if (pidFilehandle == -1)
|
||||
{
|
||||
LogPrint("Error, could not create pid file (", pidfile, ")\nIs an instance already running?");
|
||||
return false;
|
||||
// this code is c-styled and a bit ugly, but we need fd for locking pidfile
|
||||
std::string pidfile; i2p::config::GetOption("pidfile", pidfile);
|
||||
if (pidfile == "") {
|
||||
pidfile = i2p::fs::DataDirPath("i2pd.pid");
|
||||
}
|
||||
if (lockf(pidFilehandle, F_TLOCK, 0) == -1)
|
||||
{
|
||||
LogPrint("Error, could not lock pid file (", pidfile, ")\nIs an instance already running?");
|
||||
return false;
|
||||
if (pidfile != "") {
|
||||
pidFH = open(pidfile.c_str(), O_RDWR | O_CREAT, 0600);
|
||||
if (pidFH < 0)
|
||||
{
|
||||
LogPrint(eLogError, "Daemon: could not create pid file ", pidfile, ": ", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
if (lockf(pidFH, F_TLOCK, 0) != 0)
|
||||
{
|
||||
LogPrint(eLogError, "Daemon: could not lock pid file ", pidfile, ": ", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
char pid[10];
|
||||
sprintf(pid, "%d\n", getpid());
|
||||
ftruncate(pidFH, 0);
|
||||
if (write(pidFH, pid, strlen(pid)) < 0)
|
||||
{
|
||||
LogPrint(eLogError, "Daemon: could not write pidfile: ", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
char pid[10];
|
||||
sprintf(pid, "%d\n", getpid());
|
||||
write(pidFilehandle, pid, strlen(pid));
|
||||
gracefullShutdownInterval = 0; // not specified
|
||||
|
||||
// Signal handler
|
||||
struct sigaction sa;
|
||||
@@ -106,12 +125,27 @@ namespace i2p
|
||||
|
||||
bool DaemonLinux::stop()
|
||||
{
|
||||
close(pidFilehandle);
|
||||
unlink(pidfile.c_str());
|
||||
i2p::fs::Remove(pidfile);
|
||||
|
||||
return Daemon_Singleton::stop();
|
||||
}
|
||||
|
||||
void DaemonLinux::run ()
|
||||
{
|
||||
while (running)
|
||||
{
|
||||
std::this_thread::sleep_for (std::chrono::seconds(1));
|
||||
if (gracefullShutdownInterval)
|
||||
{
|
||||
gracefullShutdownInterval--; // - 1 second
|
||||
if (gracefullShutdownInterval <= 0)
|
||||
{
|
||||
LogPrint(eLogInfo, "Graceful shutdown");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
#include <thread>
|
||||
#include <clocale>
|
||||
#include "Config.h"
|
||||
#include "Daemon.h"
|
||||
#include "util.h"
|
||||
#include "Log.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "./Win32/Win32Service.h"
|
||||
#include "Win32/Win32Service.h"
|
||||
#ifdef WIN32_APP
|
||||
#include "Win32/Win32App.h"
|
||||
#endif
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -17,15 +23,13 @@ namespace i2p
|
||||
SetConsoleOutputCP(1251);
|
||||
setlocale(LC_ALL, "Russian");
|
||||
|
||||
if (!Daemon_Singleton::init(argc, argv)) return false;
|
||||
if (I2PService::isService())
|
||||
isDaemon = 1;
|
||||
else
|
||||
isDaemon = 0;
|
||||
if (!Daemon_Singleton::init(argc, argv))
|
||||
return false;
|
||||
|
||||
std::string serviceControl = i2p::util::config::GetArg("-service", "none");
|
||||
std::string serviceControl; i2p::config::GetOption("svcctl", serviceControl);
|
||||
if (serviceControl == "install")
|
||||
{
|
||||
LogPrint(eLogInfo, "WinSVC: installing ", SERVICE_NAME, " as service");
|
||||
InstallService(
|
||||
SERVICE_NAME, // Name of service
|
||||
SERVICE_DISPLAY_NAME, // Name to display
|
||||
@@ -34,50 +38,78 @@ namespace i2p
|
||||
SERVICE_ACCOUNT, // Service running account
|
||||
SERVICE_PASSWORD // Password of the account
|
||||
);
|
||||
exit(0);
|
||||
return false;
|
||||
}
|
||||
else if (serviceControl == "remove")
|
||||
{
|
||||
LogPrint(eLogInfo, "WinSVC: uninstalling ", SERVICE_NAME, " service");
|
||||
UninstallService(SERVICE_NAME);
|
||||
exit(0);
|
||||
return false;
|
||||
}
|
||||
else if (serviceControl != "none")
|
||||
|
||||
if (isDaemon)
|
||||
{
|
||||
printf(" --service=install to install the service.\n");
|
||||
printf(" --service=remove to remove the service.\n");
|
||||
}
|
||||
|
||||
if (isDaemon == 1)
|
||||
{
|
||||
LogPrint("Service session");
|
||||
LogPrint(eLogDebug, "Daemon: running as service");
|
||||
I2PService service(SERVICE_NAME);
|
||||
if (!I2PService::Run(service))
|
||||
{
|
||||
LogPrint("Service failed to run w/err 0x%08lx\n", GetLastError());
|
||||
exit(EXIT_FAILURE);
|
||||
LogPrint(eLogError, "Daemon: Service failed to run w/err 0x%08lx\n", GetLastError());
|
||||
return false;
|
||||
}
|
||||
exit(EXIT_SUCCESS);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
LogPrint("User session");
|
||||
LogPrint(eLogDebug, "Daemon: running as user");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DaemonWin32::start()
|
||||
{
|
||||
setlocale(LC_CTYPE, "");
|
||||
SetConsoleCP(1251);
|
||||
SetConsoleOutputCP(1251);
|
||||
setlocale(LC_ALL, "Russian");
|
||||
#ifdef WIN32_APP
|
||||
if (!i2p::win32::StartWin32App ()) return false;
|
||||
|
||||
return Daemon_Singleton::start();
|
||||
// override log
|
||||
i2p::config::SetOption("log", std::string ("file"));
|
||||
#endif
|
||||
bool ret = Daemon_Singleton::start();
|
||||
if (ret && i2p::log::Logger().GetLogType() == eLogFile)
|
||||
{
|
||||
// TODO: find out where this garbage to console comes from
|
||||
SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE);
|
||||
SetStdHandle(STD_ERROR_HANDLE, INVALID_HANDLE_VALUE);
|
||||
}
|
||||
bool insomnia; i2p::config::GetOption("insomnia", insomnia);
|
||||
if (insomnia)
|
||||
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool DaemonWin32::stop()
|
||||
{
|
||||
#ifdef WIN32_APP
|
||||
i2p::win32::StopWin32App ();
|
||||
#endif
|
||||
return Daemon_Singleton::stop();
|
||||
}
|
||||
|
||||
void DaemonWin32::run ()
|
||||
{
|
||||
#ifdef WIN32_APP
|
||||
i2p::win32::RunWin32App ();
|
||||
#else
|
||||
while (running)
|
||||
{
|
||||
std::this_thread::sleep_for (std::chrono::seconds(1));
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
112
Datagram.cpp
112
Datagram.cpp
@@ -1,7 +1,6 @@
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <cryptopp/sha.h>
|
||||
#include <cryptopp/gzip.h>
|
||||
#include "Crypto.h"
|
||||
#include "Log.h"
|
||||
#include "TunnelBase.h"
|
||||
#include "RouterContext.h"
|
||||
@@ -12,47 +11,61 @@ namespace i2p
|
||||
{
|
||||
namespace datagram
|
||||
{
|
||||
DatagramDestination::DatagramDestination (i2p::client::ClientDestination& owner):
|
||||
DatagramDestination::DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner):
|
||||
m_Owner (owner), m_Receiver (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void DatagramDestination::SendDatagramTo (const uint8_t * payload, size_t len, std::shared_ptr<const i2p::data::LeaseSet> remote)
|
||||
|
||||
DatagramDestination::~DatagramDestination ()
|
||||
{
|
||||
}
|
||||
|
||||
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);
|
||||
auto identityLen = m_Owner->GetIdentity ()->ToBuffer (buf, MAX_DATAGRAM_SIZE);
|
||||
uint8_t * signature = buf + identityLen;
|
||||
auto signatureLen = m_Owner.GetIdentity ().GetSignatureLen ();
|
||||
auto signatureLen = m_Owner->GetIdentity ()->GetSignatureLen ();
|
||||
uint8_t * buf1 = signature + signatureLen;
|
||||
size_t headerLen = identityLen + signatureLen;
|
||||
|
||||
memcpy (buf1, payload, len);
|
||||
if (m_Owner.GetIdentity ().GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
|
||||
if (m_Owner->GetIdentity ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
|
||||
{
|
||||
uint8_t hash[32];
|
||||
CryptoPP::SHA256().CalculateDigest (hash, buf1, len);
|
||||
m_Owner.Sign (hash, 32, signature);
|
||||
SHA256(buf1, len, hash);
|
||||
m_Owner->Sign (hash, 32, signature);
|
||||
}
|
||||
else
|
||||
m_Owner.Sign (buf1, len, signature);
|
||||
|
||||
m_Owner.GetService ().post (std::bind (&DatagramDestination::SendMsg, this,
|
||||
CreateDataMessage (buf, len + headerLen), remote));
|
||||
m_Owner->Sign (buf1, len, signature);
|
||||
|
||||
auto msg = CreateDataMessage (buf, len + headerLen, fromPort, toPort);
|
||||
auto remote = m_Owner->FindLeaseSet (ident);
|
||||
if (remote)
|
||||
m_Owner->GetService ().post (std::bind (&DatagramDestination::SendMsg, this, msg, remote));
|
||||
else
|
||||
m_Owner->RequestDestination (ident, std::bind (&DatagramDestination::HandleLeaseSetRequestComplete, this, std::placeholders::_1, msg));
|
||||
}
|
||||
|
||||
void DatagramDestination::SendMsg (I2NPMessage * msg, std::shared_ptr<const i2p::data::LeaseSet> remote)
|
||||
void DatagramDestination::HandleLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> remote, std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
auto outboundTunnel = m_Owner.GetTunnelPool ()->GetNextOutboundTunnel ();
|
||||
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 = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (0, leases.size () - 1);
|
||||
auto garlic = m_Owner.WrapMessage (remote, msg, true);
|
||||
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,
|
||||
leases[i]->tunnelGateway, leases[i]->tunnelID,
|
||||
garlic
|
||||
});
|
||||
outboundTunnel->SendTunnelDataMsg (msgs);
|
||||
@@ -63,11 +76,10 @@ namespace datagram
|
||||
LogPrint (eLogWarning, "Failed to send datagram. All leases expired");
|
||||
else
|
||||
LogPrint (eLogWarning, "Failed to send datagram. No outbound tunnels");
|
||||
DeleteI2NPMessage (msg);
|
||||
}
|
||||
}
|
||||
|
||||
void DatagramDestination::HandleDatagram (const uint8_t * buf, size_t len)
|
||||
void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
|
||||
{
|
||||
i2p::data::IdentityEx identity;
|
||||
size_t identityLen = identity.FromBuffer (buf, len);
|
||||
@@ -76,14 +88,21 @@ namespace datagram
|
||||
|
||||
bool verified = false;
|
||||
if (identity.GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
|
||||
verified = CryptoPP::SHA256().VerifyDigest (signature, buf + headerLen, len - headerLen);
|
||||
{
|
||||
uint8_t hash[32];
|
||||
SHA256(buf + headerLen, len - headerLen, hash);
|
||||
verified = identity.Verify (hash, 32, signature);
|
||||
}
|
||||
else
|
||||
verified = identity.Verify (buf + headerLen, len - headerLen, signature);
|
||||
|
||||
if (verified)
|
||||
{
|
||||
if (m_Receiver != nullptr)
|
||||
m_Receiver (identity, buf + headerLen, len -headerLen);
|
||||
auto it = m_ReceiversByPorts.find (toPort);
|
||||
if (it != m_ReceiversByPorts.end ())
|
||||
it->second (identity, fromPort, toPort, buf + headerLen, len -headerLen);
|
||||
else if (m_Receiver != nullptr)
|
||||
m_Receiver (identity, fromPort, toPort, buf + headerLen, len -headerLen);
|
||||
else
|
||||
LogPrint (eLogWarning, "Receiver for datagram is not set");
|
||||
}
|
||||
@@ -91,39 +110,32 @@ namespace datagram
|
||||
LogPrint (eLogWarning, "Datagram signature verification failed");
|
||||
}
|
||||
|
||||
void DatagramDestination::HandleDataMessagePayload (const uint8_t * buf, size_t len)
|
||||
void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
|
||||
{
|
||||
// unzip it
|
||||
CryptoPP::Gunzip decompressor;
|
||||
decompressor.Put (buf, len);
|
||||
decompressor.MessageEnd();
|
||||
uint8_t uncompressed[MAX_DATAGRAM_SIZE];
|
||||
auto uncompressedLen = decompressor.MaxRetrievable ();
|
||||
if (uncompressedLen <= MAX_DATAGRAM_SIZE)
|
||||
{
|
||||
decompressor.Get (uncompressed, uncompressedLen);
|
||||
HandleDatagram (uncompressed, uncompressedLen);
|
||||
}
|
||||
else
|
||||
LogPrint ("Received datagram size ", uncompressedLen, " exceeds max size");
|
||||
|
||||
size_t uncompressedLen = m_Inflator.Inflate (buf, len, uncompressed, MAX_DATAGRAM_SIZE);
|
||||
if (uncompressedLen)
|
||||
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen);
|
||||
}
|
||||
|
||||
I2NPMessage * DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len)
|
||||
std::shared_ptr<I2NPMessage> DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort)
|
||||
{
|
||||
I2NPMessage * msg = NewI2NPMessage ();
|
||||
CryptoPP::Gzip compressor; // default level
|
||||
compressor.Put (payload, len);
|
||||
compressor.MessageEnd();
|
||||
int size = compressor.MaxRetrievable ();
|
||||
auto msg = NewI2NPMessage ();
|
||||
uint8_t * buf = msg->GetPayload ();
|
||||
htobe32buf (buf, size); // length
|
||||
buf += 4;
|
||||
compressor.Get (buf, size);
|
||||
memset (buf + 4, 0, 4); // source and destination are zeroes
|
||||
buf[9] = i2p::client::PROTOCOL_TYPE_DATAGRAM; // datagram protocol
|
||||
msg->len += size + 4;
|
||||
FillI2NPMessageHeader (msg, eI2NPData);
|
||||
buf += 4; // reserve for length
|
||||
size_t size = m_Deflator.Deflate (payload, len, buf, msg->maxLen - msg->len);
|
||||
if (size)
|
||||
{
|
||||
htobe32buf (msg->GetPayload (), size); // length
|
||||
htobe16buf (buf + 4, fromPort); // source port
|
||||
htobe16buf (buf + 6, toPort); // destination port
|
||||
buf[9] = i2p::client::PROTOCOL_TYPE_DATAGRAM; // datagram protocol
|
||||
msg->len += size + 4;
|
||||
msg->FillI2NPMessageHeader (eI2NPData);
|
||||
}
|
||||
else
|
||||
msg = nullptr;
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
35
Datagram.h
35
Datagram.h
@@ -4,6 +4,8 @@
|
||||
#include <inttypes.h>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include "Base.h"
|
||||
#include "Identity.h"
|
||||
#include "LeaseSet.h"
|
||||
#include "I2NPProtocol.h"
|
||||
@@ -19,29 +21,38 @@ namespace datagram
|
||||
const size_t MAX_DATAGRAM_SIZE = 32768;
|
||||
class DatagramDestination
|
||||
{
|
||||
typedef std::function<void (const i2p::data::IdentityEx& ident, const uint8_t *, size_t)> Receiver;
|
||||
typedef std::function<void (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)> Receiver;
|
||||
|
||||
public:
|
||||
|
||||
DatagramDestination (i2p::client::ClientDestination& owner);
|
||||
~DatagramDestination () {};
|
||||
DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner);
|
||||
~DatagramDestination ();
|
||||
|
||||
void SendDatagramTo (const uint8_t * payload, size_t len, std::shared_ptr<const i2p::data::LeaseSet> remote);
|
||||
void HandleDataMessagePayload (const uint8_t * buf, size_t len);
|
||||
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; };
|
||||
|
||||
private:
|
||||
|
||||
I2NPMessage * CreateDataMessage (const uint8_t * payload, size_t len);
|
||||
void SendMsg (I2NPMessage * msg, std::shared_ptr<const i2p::data::LeaseSet> remote);
|
||||
void HandleDatagram (const uint8_t * buf, size_t len);
|
||||
void SetReceiver (const Receiver& receiver, uint16_t port) { m_ReceiversByPorts[port] = receiver; };
|
||||
void ResetReceiver (uint16_t port) { m_ReceiversByPorts.erase (port); };
|
||||
|
||||
private:
|
||||
|
||||
i2p::client::ClientDestination& m_Owner;
|
||||
Receiver m_Receiver;
|
||||
void HandleLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::shared_ptr<I2NPMessage> msg);
|
||||
|
||||
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);
|
||||
|
||||
private:
|
||||
|
||||
std::shared_ptr<i2p::client::ClientDestination> m_Owner;
|
||||
Receiver m_Receiver; // default
|
||||
std::map<uint16_t, Receiver> m_ReceiversByPorts;
|
||||
|
||||
i2p::data::GzipInflator m_Inflator;
|
||||
i2p::data::GzipDeflator m_Deflator;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
712
Destination.cpp
712
Destination.cpp
File diff suppressed because it is too large
Load Diff
172
Destination.h
172
Destination.h
@@ -11,7 +11,7 @@
|
||||
#include <boost/asio.hpp>
|
||||
#include "Identity.h"
|
||||
#include "TunnelPool.h"
|
||||
#include "CryptoConst.h"
|
||||
#include "Crypto.h"
|
||||
#include "LeaseSet.h"
|
||||
#include "Garlic.h"
|
||||
#include "NetDb.h"
|
||||
@@ -26,23 +26,34 @@ namespace client
|
||||
const uint8_t PROTOCOL_TYPE_DATAGRAM = 17;
|
||||
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_REGULAR_VERIFICATION_INTERNAL = 100; // in seconds periodically
|
||||
const int LEASESET_REQUEST_TIMEOUT = 5; // in seconds
|
||||
const int MAX_LEASESET_REQUEST_TIMEOUT = 40; // in seconds
|
||||
const int MAX_NUM_FLOODFILLS_PER_REQUEST = 7;
|
||||
const int DESTINATION_CLEANUP_TIMEOUT = 20; // in minutes
|
||||
const int DESTINATION_CLEANUP_TIMEOUT = 3; // in minutes
|
||||
const unsigned int MAX_NUM_FLOODFILLS_PER_REQUEST = 7;
|
||||
|
||||
// I2CP
|
||||
const char I2CP_PARAM_INBOUND_TUNNEL_LENGTH[] = "inbound.length";
|
||||
const int DEFAULT_INBOUND_TUNNEL_LENGTH = 3;
|
||||
const char I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH[] = "outbound.length";
|
||||
const int DEFAULT_OUTBOUND_TUNNEL_LENGTH = 3;
|
||||
const char I2CP_PARAM_INBOUND_TUNNELS_QUANTITY[] = "inbound.quantity";
|
||||
const int DEFAULT_INBOUND_TUNNELS_QUANTITY = 5;
|
||||
const char I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY[] = "outbound.quantity";
|
||||
const int DEFAULT_OUTBOUND_TUNNELS_QUANTITY = 5;
|
||||
const char I2CP_PARAM_EXPLICIT_PEERS[] = "explicitPeers";
|
||||
const int STREAM_REQUEST_TIMEOUT = 60; //in seconds
|
||||
const char I2CP_PARAM_TAGS_TO_SEND[] = "crypto.tagsToSend";
|
||||
const int DEFAULT_TAGS_TO_SEND = 40;
|
||||
|
||||
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
|
||||
|
||||
class ClientDestination: public i2p::garlic::GarlicDestination
|
||||
class LeaseSetDestination: public i2p::garlic::GarlicDestination,
|
||||
public std::enable_shared_from_this<LeaseSetDestination>
|
||||
{
|
||||
typedef std::function<void (bool success)> RequestComplete;
|
||||
typedef std::function<void (std::shared_ptr<i2p::data::LeaseSet> leaseSet)> RequestComplete;
|
||||
// leaseSet = nullptr means not found
|
||||
struct LeaseSetRequest
|
||||
{
|
||||
LeaseSetRequest (boost::asio::io_service& service): requestTime (0), requestTimeoutTimer (service) {};
|
||||
@@ -50,25 +61,101 @@ namespace client
|
||||
uint64_t requestTime;
|
||||
boost::asio::deadline_timer requestTimeoutTimer;
|
||||
RequestComplete requestComplete;
|
||||
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
|
||||
std::shared_ptr<i2p::tunnel::InboundTunnel> replyTunnel;
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
|
||||
ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
|
||||
~ClientDestination ();
|
||||
LeaseSetDestination (bool isPublic, const std::map<std::string, std::string> * params = nullptr);
|
||||
~LeaseSetDestination ();
|
||||
|
||||
virtual void Start ();
|
||||
virtual void Stop ();
|
||||
virtual bool Start ();
|
||||
virtual bool Stop ();
|
||||
bool IsRunning () const { return m_IsRunning; };
|
||||
boost::asio::io_service& GetService () { return m_Service; };
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () { return m_Pool; };
|
||||
bool IsReady () const { return m_LeaseSet && m_LeaseSet->HasNonExpiredLeases (); };
|
||||
bool IsReady () const { return m_LeaseSet && !m_LeaseSet->IsExpired () && m_Pool->GetOutboundTunnels ().size () > 0; };
|
||||
std::shared_ptr<const i2p::data::LeaseSet> FindLeaseSet (const i2p::data::IdentHash& ident);
|
||||
bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr);
|
||||
void CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify = true);
|
||||
|
||||
// implements GarlicDestination
|
||||
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet ();
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const { return m_Pool; }
|
||||
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
||||
|
||||
// override GarlicDestination
|
||||
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag);
|
||||
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void SetLeaseSetUpdated ();
|
||||
|
||||
protected:
|
||||
|
||||
void SetLeaseSet (i2p::data::LocalLeaseSet * newLeaseSet);
|
||||
// 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;
|
||||
|
||||
private:
|
||||
|
||||
void Run ();
|
||||
void UpdateLeaseSet ();
|
||||
void Publish ();
|
||||
void HandlePublishConfirmationTimer (const boost::system::error_code& ecode);
|
||||
void HandlePublishVerificationTimer (const boost::system::error_code& ecode);
|
||||
void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len);
|
||||
void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len);
|
||||
void HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
|
||||
void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete);
|
||||
bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request);
|
||||
void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest);
|
||||
void HandleCleanupTimer (const boost::system::error_code& ecode);
|
||||
void CleanupRemoteLeaseSets ();
|
||||
|
||||
private:
|
||||
|
||||
volatile bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_service::work m_Work;
|
||||
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::shared_ptr<i2p::data::LocalLeaseSet> m_LeaseSet;
|
||||
bool m_IsPublic;
|
||||
uint32_t m_PublishReplyToken;
|
||||
std::set<i2p::data::IdentHash> m_ExcludedFloodfills; // for publishing
|
||||
|
||||
boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer, m_CleanupTimer;
|
||||
|
||||
public:
|
||||
|
||||
// for HTTP only
|
||||
int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); };
|
||||
};
|
||||
|
||||
class ClientDestination: public LeaseSetDestination
|
||||
{
|
||||
public:
|
||||
|
||||
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); };
|
||||
|
||||
// streaming
|
||||
i2p::stream::StreamingDestination * GetStreamingDestination () const { return m_StreamingDestination; };
|
||||
std::shared_ptr<i2p::stream::StreamingDestination> CreateStreamingDestination (int port, bool gzip = true); // additional
|
||||
std::shared_ptr<i2p::stream::StreamingDestination> GetStreamingDestination (int port = 0) const;
|
||||
// following methods operate with default streaming destination
|
||||
void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0);
|
||||
std::shared_ptr<i2p::stream::Stream> CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
|
||||
void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor);
|
||||
@@ -78,67 +165,36 @@ namespace client
|
||||
// datagram
|
||||
i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; };
|
||||
i2p::datagram::DatagramDestination * CreateDatagramDestination ();
|
||||
|
||||
// implements LocalDestination
|
||||
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
|
||||
const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; };
|
||||
const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionPublicKey; };
|
||||
|
||||
// implements GarlicDestination
|
||||
const i2p::data::LeaseSet * GetLeaseSet ();
|
||||
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
||||
|
||||
// override GarlicDestination
|
||||
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag);
|
||||
void ProcessGarlicMessage (I2NPMessage * msg);
|
||||
void ProcessDeliveryStatusMessage (I2NPMessage * msg);
|
||||
void SetLeaseSetUpdated ();
|
||||
// implements LocalDestination
|
||||
const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; };
|
||||
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
|
||||
|
||||
protected:
|
||||
|
||||
// I2CP
|
||||
void HandleDataMessage (const uint8_t * buf, size_t len);
|
||||
|
||||
private:
|
||||
|
||||
void Run ();
|
||||
void UpdateLeaseSet ();
|
||||
void Publish ();
|
||||
void HandlePublishConfirmationTimer (const boost::system::error_code& ecode);
|
||||
void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len);
|
||||
void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len);
|
||||
void HandleDeliveryStatusMessage (I2NPMessage * msg);
|
||||
|
||||
void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete);
|
||||
bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, LeaseSetRequest * request);
|
||||
void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest);
|
||||
void HandleCleanupTimer (const boost::system::error_code& ecode);
|
||||
void CleanupRemoteLeaseSets ();
|
||||
void CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
|
||||
|
||||
private:
|
||||
|
||||
volatile bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_service::work m_Work;
|
||||
std::shared_ptr<ClientDestination> GetSharedFromThis ()
|
||||
{ return std::static_pointer_cast<ClientDestination>(shared_from_this ()); }
|
||||
void PersistTemporaryKeys ();
|
||||
|
||||
private:
|
||||
|
||||
i2p::data::PrivateKeys m_Keys;
|
||||
uint8_t m_EncryptionPublicKey[256], m_EncryptionPrivateKey[256];
|
||||
std::map<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets;
|
||||
std::map<i2p::data::IdentHash, LeaseSetRequest *> m_LeaseSetRequests;
|
||||
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> m_Pool;
|
||||
i2p::data::LeaseSet * m_LeaseSet;
|
||||
bool m_IsPublic;
|
||||
uint32_t m_PublishReplyToken;
|
||||
std::set<i2p::data::IdentHash> m_ExcludedFloodfills; // for publishing
|
||||
|
||||
i2p::stream::StreamingDestination * m_StreamingDestination;
|
||||
std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default
|
||||
std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > m_StreamingDestinationsByPorts;
|
||||
i2p::datagram::DatagramDestination * m_DatagramDestination;
|
||||
|
||||
boost::asio::deadline_timer m_PublishConfirmationTimer, m_CleanupTimer;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
// for HTTP only
|
||||
int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); };
|
||||
std::vector<std::shared_ptr<const i2p::stream::Stream> > GetAllStreams () const;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
88
ElGamal.h
88
ElGamal.h
@@ -1,88 +0,0 @@
|
||||
#ifndef EL_GAMAL_H__
|
||||
#define EL_GAMAL_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cryptopp/integer.h>
|
||||
#include <cryptopp/osrng.h>
|
||||
#include <cryptopp/dh.h>
|
||||
#include <cryptopp/sha.h>
|
||||
#include "CryptoConst.h"
|
||||
#include "Log.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace crypto
|
||||
{
|
||||
|
||||
class ElGamalEncryption
|
||||
{
|
||||
public:
|
||||
|
||||
ElGamalEncryption (const uint8_t * key):
|
||||
y (key, 256), k (rnd, CryptoPP::Integer::One(), elgp-1),
|
||||
a (a_exp_b_mod_c (elgg, k, elgp)), b1 (a_exp_b_mod_c (y, k, elgp))
|
||||
{
|
||||
}
|
||||
|
||||
void Encrypt (const uint8_t * data, int len, uint8_t * encrypted, bool zeroPadding = false)
|
||||
{
|
||||
// calculate b = b1*m mod p
|
||||
uint8_t m[255];
|
||||
m[0] = 0xFF;
|
||||
memcpy (m+33, data, len);
|
||||
CryptoPP::SHA256().CalculateDigest(m+1, m+33, 222);
|
||||
CryptoPP::Integer b (a_times_b_mod_c (b1, CryptoPP::Integer (m, 255), elgp));
|
||||
|
||||
// copy a and b
|
||||
if (zeroPadding)
|
||||
{
|
||||
encrypted[0] = 0;
|
||||
a.Encode (encrypted + 1, 256);
|
||||
encrypted[257] = 0;
|
||||
b.Encode (encrypted + 258, 256);
|
||||
}
|
||||
else
|
||||
{
|
||||
a.Encode (encrypted, 256);
|
||||
b.Encode (encrypted + 256, 256);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
CryptoPP::AutoSeededRandomPool rnd;
|
||||
CryptoPP::Integer y, k, a, b1;
|
||||
};
|
||||
|
||||
inline bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted,
|
||||
uint8_t * data, bool zeroPadding = false)
|
||||
{
|
||||
CryptoPP::Integer x(key, 256), a(zeroPadding? encrypted +1 : encrypted, 256),
|
||||
b(zeroPadding? encrypted + 258 :encrypted + 256, 256);
|
||||
uint8_t m[255], hash[32];
|
||||
a_times_b_mod_c (b, a_exp_b_mod_c (a, elgp - x - 1, elgp), elgp).Encode (m, 255);
|
||||
CryptoPP::SHA256().CalculateDigest(hash, m+33, 222);
|
||||
for (int i = 0; i < 32; i++)
|
||||
if (hash[i] != m[i+1])
|
||||
{
|
||||
LogPrint ("ElGamal decrypt hash doesn't match");
|
||||
return false;
|
||||
}
|
||||
memcpy (data, m + 33, 222);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void GenerateElGamalKeyPair (CryptoPP::RandomNumberGenerator& rnd, uint8_t * priv, uint8_t * pub)
|
||||
{
|
||||
#if defined(__x86_64__) || defined(__i386__) || defined(_MSC_VER)
|
||||
rnd.GenerateBlock (priv, 256);
|
||||
a_exp_b_mod_c (elgg, CryptoPP::Integer (priv, 256), elgp).Encode (pub, 256);
|
||||
#else
|
||||
CryptoPP::DH dh (elgp, elgg);
|
||||
dh.GenerateKeyPair(rnd, priv, pub);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
173
FS.cpp
Normal file
173
FS.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016, 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 <boost/filesystem.hpp>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <shlobj.h>
|
||||
#endif
|
||||
|
||||
#include "Base.h"
|
||||
#include "FS.h"
|
||||
#include "Log.h"
|
||||
|
||||
namespace i2p {
|
||||
namespace fs {
|
||||
std::string appName = "i2pd";
|
||||
std::string dataDir = "";
|
||||
#ifdef _WIN32
|
||||
std::string dirSep = "\\";
|
||||
#else
|
||||
std::string dirSep = "/";
|
||||
#endif
|
||||
|
||||
const std::string & GetAppName () {
|
||||
return appName;
|
||||
}
|
||||
|
||||
void SetAppName (const std::string& name) {
|
||||
appName = name;
|
||||
}
|
||||
|
||||
const std::string & GetDataDir () {
|
||||
return dataDir;
|
||||
}
|
||||
|
||||
void DetectDataDir(const std::string & cmdline_param, bool isService) {
|
||||
if (cmdline_param != "") {
|
||||
dataDir = cmdline_param;
|
||||
return;
|
||||
}
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
char localAppData[MAX_PATH];
|
||||
SHGetFolderPath(NULL, CSIDL_APPDATA, 0, NULL, localAppData);
|
||||
dataDir = std::string(localAppData) + "\\" + appName;
|
||||
return;
|
||||
#elif defined(MAC_OSX)
|
||||
char *home = getenv("HOME");
|
||||
dataDir = (home != NULL && strlen(home) > 0) ? home : "";
|
||||
dataDir += "/Library/Application Support/" + appName;
|
||||
return;
|
||||
#else /* other unix */
|
||||
#if defined(ANDROID)
|
||||
if (boost::filesystem::exists("/sdcard"))
|
||||
{
|
||||
dataDir = "/sdcard/" + appName;
|
||||
return;
|
||||
}
|
||||
// otherwise use /data/files
|
||||
#endif
|
||||
char *home = getenv("HOME");
|
||||
if (isService) {
|
||||
dataDir = "/var/lib/" + appName;
|
||||
} else if (home != NULL && strlen(home) > 0) {
|
||||
dataDir = std::string(home) + "/." + appName;
|
||||
} else {
|
||||
dataDir = "/tmp/" + appName;
|
||||
}
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Init() {
|
||||
if (!boost::filesystem::exists(dataDir))
|
||||
boost::filesystem::create_directory(dataDir);
|
||||
std::string destinations = DataDirPath("destinations");
|
||||
if (!boost::filesystem::exists(destinations))
|
||||
boost::filesystem::create_directory(destinations);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadDir(const std::string & path, std::vector<std::string> & files) {
|
||||
if (!boost::filesystem::exists(path))
|
||||
return false;
|
||||
boost::filesystem::directory_iterator it(path);
|
||||
boost::filesystem::directory_iterator end;
|
||||
|
||||
for ( ; it != end; it++) {
|
||||
if (!boost::filesystem::is_regular_file(it->status()))
|
||||
continue;
|
||||
files.push_back(it->path().string());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Exists(const std::string & path) {
|
||||
return boost::filesystem::exists(path);
|
||||
}
|
||||
|
||||
bool Remove(const std::string & path) {
|
||||
if (!boost::filesystem::exists(path))
|
||||
return false;
|
||||
return boost::filesystem::remove(path);
|
||||
}
|
||||
|
||||
bool CreateDirectory (const std::string& 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;
|
||||
}
|
||||
|
||||
bool HashedStorage::Init(const char * chars, size_t count) {
|
||||
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))
|
||||
continue;
|
||||
if (boost::filesystem::create_directory(p))
|
||||
continue; /* ^ throws exception on failure */
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string HashedStorage::Path(const std::string & ident) const {
|
||||
std::string safe_ident = ident;
|
||||
std::replace(safe_ident.begin(), safe_ident.end(), '/', '-');
|
||||
std::replace(safe_ident.begin(), safe_ident.end(), '\\', '-');
|
||||
|
||||
std::stringstream t("");
|
||||
t << this->root << i2p::fs::dirSep;
|
||||
t << prefix1 << safe_ident[0] << i2p::fs::dirSep;
|
||||
t << prefix2 << safe_ident << "." << suffix;
|
||||
|
||||
return t.str();
|
||||
}
|
||||
|
||||
void HashedStorage::Remove(const std::string & ident) {
|
||||
std::string path = Path(ident);
|
||||
if (!boost::filesystem::exists(path))
|
||||
return;
|
||||
boost::filesystem::remove(path);
|
||||
}
|
||||
|
||||
void HashedStorage::Traverse(std::vector<std::string> & files) {
|
||||
boost::filesystem::path p(root);
|
||||
boost::filesystem::recursive_directory_iterator it(p);
|
||||
boost::filesystem::recursive_directory_iterator end;
|
||||
|
||||
for ( ; it != end; it++) {
|
||||
if (!boost::filesystem::is_regular_file( it->status() ))
|
||||
continue;
|
||||
const std::string & t = it->path().string();
|
||||
files.push_back(t);
|
||||
}
|
||||
}
|
||||
} // fs
|
||||
} // i2p
|
||||
155
FS.h
Normal file
155
FS.h
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016, 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
|
||||
*/
|
||||
|
||||
#ifndef FS_H__
|
||||
#define FS_H__
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace i2p {
|
||||
namespace fs {
|
||||
extern std::string dirSep;
|
||||
|
||||
/**
|
||||
* @brief Class to work with NetDb & Router profiles
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* const char alphabet[8] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};
|
||||
* auto h = HashedStorage("name", "y", "z-", ".txt");
|
||||
* h.SetPlace("/tmp/hs-test");
|
||||
* h.GetName() -> gives "name"
|
||||
* h.GetRoot() -> gives "/tmp/hs-test/name"
|
||||
* h.Init(alphabet, 8); <- creates needed dirs, 8 is size of alphabet
|
||||
* h.Path("abcd"); <- returns /tmp/hs-test/name/ya/z-abcd.txt
|
||||
* h.Remove("abcd"); <- removes /tmp/hs-test/name/ya/z-abcd.txt, if it exists
|
||||
* std::vector<std::string> files;
|
||||
* h.Traverse(files); <- finds all files in storage and saves in given vector
|
||||
*/
|
||||
class HashedStorage {
|
||||
protected:
|
||||
std::string root; /**< path to storage with it's name included */
|
||||
std::string name; /**< name of the storage */
|
||||
std::string prefix1; /**< hashed directory prefix */
|
||||
std::string prefix2; /**< prefix of file in storage */
|
||||
std::string suffix; /**< suffix of file in storage (extension) */
|
||||
|
||||
public:
|
||||
HashedStorage(const char *n, const char *p1, const char *p2, const char *s):
|
||||
name(n), prefix1(p1), prefix2(p2), suffix(s) {};
|
||||
|
||||
/** create subdirs in storage */
|
||||
bool Init(const char* chars, size_t cnt);
|
||||
const std::string & GetRoot() const { return root; }
|
||||
const std::string & GetName() const { return name; }
|
||||
/** set directory where to place storage directory */
|
||||
void SetPlace(const std::string & path);
|
||||
/** path to file with given ident */
|
||||
std::string Path(const std::string & ident) const;
|
||||
/** remove file by ident */
|
||||
void Remove(const std::string & ident);
|
||||
/** find all files in storage and store list in provided vector */
|
||||
void Traverse(std::vector<std::string> & files);
|
||||
};
|
||||
|
||||
/** @brief Returns current application name, default 'i2pd' */
|
||||
const std::string & GetAppName ();
|
||||
/** @brief Set applicaton name, affects autodetection of datadir */
|
||||
void SetAppName (const std::string& name);
|
||||
|
||||
/** @brief Returns datadir path */
|
||||
const std::string & GetDataDir();
|
||||
|
||||
/**
|
||||
* @brief Set datadir either from cmdline option or using autodetection
|
||||
* @param cmdline_param Value of cmdline parameter --datadir=<something>
|
||||
* @param isService Value of cmdline parameter --service
|
||||
*
|
||||
* Examples of autodetected paths:
|
||||
*
|
||||
* Windows < Vista: C:\Documents and Settings\Username\Application Data\i2pd\
|
||||
* Windows >= Vista: C:\Users\Username\AppData\Roaming\i2pd\
|
||||
* Mac: /Library/Application Support/i2pd/ or ~/Library/Application Support/i2pd/
|
||||
* Unix: /var/lib/i2pd/ (system=1) >> ~/.i2pd/ or /tmp/i2pd/
|
||||
*/
|
||||
void DetectDataDir(const std::string & cmdline_datadir, bool isService = false);
|
||||
|
||||
/**
|
||||
* @brief Create subdirectories inside datadir
|
||||
*/
|
||||
bool Init();
|
||||
|
||||
/**
|
||||
* @brief Get list of files in directory
|
||||
* @param path Path to directory
|
||||
* @param files Vector to store found files
|
||||
* @return true on success and false if directory not exists
|
||||
*/
|
||||
bool ReadDir(const std::string & path, std::vector<std::string> & files);
|
||||
|
||||
/**
|
||||
* @brief Remove file with given path
|
||||
* @param path Absolute path to file
|
||||
* @return true on success, false if file not exists, throws exception on error
|
||||
*/
|
||||
bool Remove(const std::string & path);
|
||||
|
||||
/**
|
||||
* @brief Check existence of file
|
||||
* @param path Absolute path to file
|
||||
* @return true if file exists, false otherwise
|
||||
*/
|
||||
bool Exists(const std::string & path);
|
||||
|
||||
bool CreateDirectory (const std::string& path);
|
||||
|
||||
template<typename T>
|
||||
void _ExpandPath(std::stringstream & path, T c) {
|
||||
path << i2p::fs::dirSep << c;
|
||||
}
|
||||
|
||||
template<typename T, typename ... Other>
|
||||
void _ExpandPath(std::stringstream & path, T c, Other ... other) {
|
||||
_ExpandPath(path, c);
|
||||
_ExpandPath(path, other ...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get path relative to datadir
|
||||
*
|
||||
* Examples (with datadir = "/tmp/i2pd"):
|
||||
*
|
||||
* i2p::fs::Path("test") -> '/tmp/i2pd/test'
|
||||
* i2p::fs::Path("test", "file.txt") -> '/tmp/i2pd/test/file.txt'
|
||||
*/
|
||||
template<typename ... Other>
|
||||
std::string DataDirPath(Other ... components) {
|
||||
std::stringstream s("");
|
||||
s << i2p::fs::GetDataDir();
|
||||
_ExpandPath(s, components ...);
|
||||
|
||||
return s.str();
|
||||
}
|
||||
|
||||
template<typename Storage, typename... Filename>
|
||||
std::string StorageRootPath (const Storage& storage, Filename... filenames)
|
||||
{
|
||||
std::stringstream s("");
|
||||
s << storage.GetRoot ();
|
||||
_ExpandPath(s, filenames...);
|
||||
|
||||
return s.str();
|
||||
}
|
||||
|
||||
} // fs
|
||||
} // i2p
|
||||
|
||||
#endif // /* FS_H__ */
|
||||
175
Family.cpp
Normal file
175
Family.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
#include <string.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include "Crypto.h"
|
||||
#include "FS.h"
|
||||
#include "Log.h"
|
||||
#include "Family.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
Families::Families ()
|
||||
{
|
||||
}
|
||||
|
||||
Families::~Families ()
|
||||
{
|
||||
}
|
||||
|
||||
void Families::LoadCertificate (const std::string& filename)
|
||||
{
|
||||
SSL_CTX * ctx = SSL_CTX_new (TLSv1_method ());
|
||||
int ret = SSL_CTX_use_certificate_file (ctx, filename.c_str (), SSL_FILETYPE_PEM);
|
||||
if (ret)
|
||||
{
|
||||
SSL * ssl = SSL_new (ctx);
|
||||
X509 * cert = SSL_get_certificate (ssl);
|
||||
if (cert)
|
||||
{
|
||||
std::shared_ptr<i2p::crypto::Verifier> verifier;
|
||||
// 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 * family = strstr (cn, ".family");
|
||||
if (family) family[0] = 0;
|
||||
}
|
||||
auto pkey = X509_get_pubkey (cert);
|
||||
int keyType = EVP_PKEY_type(pkey->type);
|
||||
switch (keyType)
|
||||
{
|
||||
case EVP_PKEY_DSA:
|
||||
// TODO:
|
||||
break;
|
||||
case EVP_PKEY_EC:
|
||||
{
|
||||
EC_KEY * ecKey = EVP_PKEY_get1_EC_KEY (pkey);
|
||||
if (ecKey)
|
||||
{
|
||||
auto group = EC_KEY_get0_group (ecKey);
|
||||
if (group)
|
||||
{
|
||||
int curve = EC_GROUP_get_curve_name (group);
|
||||
if (curve == NID_X9_62_prime256v1)
|
||||
{
|
||||
uint8_t signingKey[64];
|
||||
BIGNUM * x = BN_new(), * y = BN_new();
|
||||
EC_POINT_get_affine_coordinates_GFp (group,
|
||||
EC_KEY_get0_public_key (ecKey), x, y, NULL);
|
||||
i2p::crypto::bn2buf (x, signingKey, 32);
|
||||
i2p::crypto::bn2buf (y, signingKey + 32, 32);
|
||||
BN_free (x); BN_free (y);
|
||||
verifier = std::make_shared<i2p::crypto::ECDSAP256Verifier>(signingKey);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported");
|
||||
}
|
||||
EC_KEY_free (ecKey);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LogPrint (eLogWarning, "Family: Certificate key type ", keyType, " is not supported");
|
||||
}
|
||||
EVP_PKEY_free (pkey);
|
||||
if (verifier && cn)
|
||||
m_SigningKeys[cn] = verifier;
|
||||
}
|
||||
SSL_free (ssl);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Family: Can't open certificate file ", filename);
|
||||
SSL_CTX_free (ctx);
|
||||
}
|
||||
|
||||
void Families::LoadCertificates ()
|
||||
{
|
||||
std::string certDir = i2p::fs::DataDirPath("certificates", "family");
|
||||
std::vector<std::string> files;
|
||||
int numCertificates = 0;
|
||||
|
||||
if (!i2p::fs::ReadDir(certDir, files)) {
|
||||
LogPrint(eLogWarning, "Family: Can't load family certificates from ", certDir);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const std::string & file : files) {
|
||||
if (file.compare(file.size() - 4, 4, ".crt") != 0) {
|
||||
LogPrint(eLogWarning, "Family: ignoring file ", file);
|
||||
continue;
|
||||
}
|
||||
LoadCertificate (file);
|
||||
numCertificates++;
|
||||
}
|
||||
LogPrint (eLogInfo, "Family: ", numCertificates, " certificates loaded");
|
||||
}
|
||||
|
||||
bool Families::VerifyFamily (const std::string& family, const IdentHash& ident,
|
||||
const char * signature, const char * key)
|
||||
{
|
||||
uint8_t buf[50], signatureBuf[64];
|
||||
size_t len = family.length (), signatureLen = strlen (signature);
|
||||
memcpy (buf, family.c_str (), len);
|
||||
memcpy (buf + len, (const uint8_t *)ident, 32);
|
||||
len += 32;
|
||||
Base64ToByteStream (signature, signatureLen, signatureBuf, 64);
|
||||
auto it = m_SigningKeys.find (family);
|
||||
if (it != m_SigningKeys.end ())
|
||||
return it->second->Verify (buf, len, signatureBuf);
|
||||
// TODO: process key
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string CreateFamilySignature (const std::string& family, const IdentHash& ident)
|
||||
{
|
||||
auto filename = i2p::fs::DataDirPath("family", (family + ".key"));
|
||||
std::string sig;
|
||||
SSL_CTX * ctx = SSL_CTX_new (TLSv1_method ());
|
||||
int ret = SSL_CTX_use_PrivateKey_file (ctx, filename.c_str (), SSL_FILETYPE_PEM);
|
||||
if (ret)
|
||||
{
|
||||
SSL * ssl = SSL_new (ctx);
|
||||
EVP_PKEY * pkey = SSL_get_privatekey (ssl);
|
||||
EC_KEY * ecKey = EVP_PKEY_get1_EC_KEY (pkey);
|
||||
if (ecKey)
|
||||
{
|
||||
auto group = EC_KEY_get0_group (ecKey);
|
||||
if (group)
|
||||
{
|
||||
int curve = EC_GROUP_get_curve_name (group);
|
||||
if (curve == NID_X9_62_prime256v1)
|
||||
{
|
||||
uint8_t signingPrivateKey[32], buf[50], signature[64];
|
||||
i2p::crypto::bn2buf (EC_KEY_get0_private_key (ecKey), signingPrivateKey, 32);
|
||||
i2p::crypto::ECDSAP256Signer signer (signingPrivateKey);
|
||||
size_t len = family.length ();
|
||||
memcpy (buf, family.c_str (), len);
|
||||
memcpy (buf + len, (const uint8_t *)ident, 32);
|
||||
len += 32;
|
||||
signer.Sign (buf, len, signature);
|
||||
len = Base64EncodingBufferSize (64);
|
||||
char * b64 = new char[len+1];
|
||||
len = ByteStreamToBase64 (signature, 64, b64, len);
|
||||
b64[len] = 0;
|
||||
sig = b64;
|
||||
delete[] b64;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported");
|
||||
}
|
||||
}
|
||||
SSL_free (ssl);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Family: Can't open keys file: ", filename);
|
||||
SSL_CTX_free (ctx);
|
||||
return sig;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
38
Family.h
Normal file
38
Family.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef FAMILY_H__
|
||||
#define FAMILY_H__
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include "Signature.h"
|
||||
#include "Identity.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
class Families
|
||||
{
|
||||
public:
|
||||
|
||||
Families ();
|
||||
~Families ();
|
||||
void LoadCertificates ();
|
||||
bool VerifyFamily (const std::string& family, const IdentHash& ident,
|
||||
const char * signature, const char * key = nullptr);
|
||||
|
||||
private:
|
||||
|
||||
void LoadCertificate (const std::string& filename);
|
||||
|
||||
private:
|
||||
|
||||
std::map<std::string, std::shared_ptr<i2p::crypto::Verifier> > m_SigningKeys;
|
||||
};
|
||||
|
||||
std::string CreateFamilySignature (const std::string& family, const IdentHash& ident);
|
||||
// return base64 signature of empty string in case of failure
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
411
Garlic.cpp
411
Garlic.cpp
@@ -2,12 +2,14 @@
|
||||
#include "I2PEndian.h"
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include "Crypto.h"
|
||||
#include "RouterContext.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Tunnel.h"
|
||||
#include "TunnelPool.h"
|
||||
#include "Transports.h"
|
||||
#include "Timestamp.h"
|
||||
#include "Destination.h"
|
||||
#include "Log.h"
|
||||
#include "Garlic.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -15,17 +17,18 @@ namespace i2p
|
||||
namespace garlic
|
||||
{
|
||||
GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner,
|
||||
std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags):
|
||||
std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags, bool attachLeaseSet):
|
||||
m_Owner (owner), m_Destination (destination), m_NumTags (numTags),
|
||||
m_LeaseSetUpdated (numTags > 0)
|
||||
m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend),
|
||||
m_ElGamalEncryption (new i2p::crypto::ElGamalEncryption (destination->GetEncryptionPublicKey ()))
|
||||
{
|
||||
// create new session tags and session key
|
||||
m_Rnd.GenerateBlock (m_SessionKey, 32);
|
||||
RAND_bytes (m_SessionKey, 32);
|
||||
m_Encryption.SetKey (m_SessionKey);
|
||||
}
|
||||
|
||||
GarlicRoutingSession::GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag):
|
||||
m_Owner (nullptr), m_Destination (nullptr), m_NumTags (1), m_LeaseSetUpdated (false)
|
||||
m_Owner (nullptr), m_Destination (nullptr), m_NumTags (1), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend)
|
||||
{
|
||||
memcpy (m_SessionKey, sessionKey, 32);
|
||||
m_Encryption.SetKey (m_SessionKey);
|
||||
@@ -35,39 +38,81 @@ namespace garlic
|
||||
|
||||
GarlicRoutingSession::~GarlicRoutingSession ()
|
||||
{
|
||||
for (auto it: m_UnconfirmedTagsMsgs)
|
||||
delete it.second;
|
||||
m_UnconfirmedTagsMsgs.clear ();
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<GarlicRoutingPath> GarlicRoutingSession::GetSharedRoutingPath ()
|
||||
{
|
||||
if (!m_SharedRoutingPath) return nullptr;
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (m_SharedRoutingPath->numTimesUsed >= ROUTING_PATH_MAX_NUM_TIMES_USED ||
|
||||
!m_SharedRoutingPath->outboundTunnel->IsEstablished () ||
|
||||
ts*1000LL > m_SharedRoutingPath->remoteLease->endDate ||
|
||||
ts > m_SharedRoutingPath->updateTime + ROUTING_PATH_EXPIRATION_TIMEOUT)
|
||||
m_SharedRoutingPath = nullptr;
|
||||
if (m_SharedRoutingPath) m_SharedRoutingPath->numTimesUsed++;
|
||||
return m_SharedRoutingPath;
|
||||
}
|
||||
|
||||
void GarlicRoutingSession::SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path)
|
||||
{
|
||||
if (path && path->outboundTunnel && path->remoteLease)
|
||||
{
|
||||
path->updateTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
path->numTimesUsed = 0;
|
||||
}
|
||||
else
|
||||
path = nullptr;
|
||||
m_SharedRoutingPath = path;
|
||||
}
|
||||
|
||||
GarlicRoutingSession::UnconfirmedTags * GarlicRoutingSession::GenerateSessionTags ()
|
||||
{
|
||||
auto tags = new UnconfirmedTags (m_NumTags);
|
||||
tags->tagsCreationTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (int i = 0; i < m_NumTags; i++)
|
||||
{
|
||||
m_Rnd.GenerateBlock (tags->sessionTags[i], 32);
|
||||
RAND_bytes (tags->sessionTags[i], 32);
|
||||
tags->sessionTags[i].creationTime = tags->tagsCreationTime;
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
|
||||
void GarlicRoutingSession::MessageConfirmed (uint32_t msgID)
|
||||
{
|
||||
TagsConfirmed (msgID);
|
||||
if (msgID == m_LeaseSetUpdateMsgID)
|
||||
{
|
||||
m_LeaseSetUpdateStatus = eLeaseSetUpToDate;
|
||||
LogPrint (eLogInfo, "Garlic: LeaseSet update confirmed");
|
||||
}
|
||||
else
|
||||
CleanupExpiredTags ();
|
||||
}
|
||||
|
||||
void GarlicRoutingSession::TagsConfirmed (uint32_t msgID)
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
auto it = m_UnconfirmedTagsMsgs.find (msgID);
|
||||
if (it != m_UnconfirmedTagsMsgs.end ())
|
||||
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();)
|
||||
{
|
||||
UnconfirmedTags * tags = it->second;
|
||||
if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
|
||||
{
|
||||
for (int i = 0; i < tags->numTags; i++)
|
||||
m_SessionTags.push_back (tags->sessionTags[i]);
|
||||
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);
|
||||
}
|
||||
m_UnconfirmedTagsMsgs.erase (it);
|
||||
delete tags;
|
||||
else if (ts >= tags->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
|
||||
{
|
||||
if (m_Owner)
|
||||
m_Owner->RemoveDeliveryStatusSession (tags->msgID);
|
||||
it = m_UnconfirmedTagsMsgs.erase (it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
CleanupExpiredTags ();
|
||||
}
|
||||
|
||||
bool GarlicRoutingSession::CleanupExpiredTags ()
|
||||
@@ -78,27 +123,35 @@ namespace garlic
|
||||
if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
|
||||
it = m_SessionTags.erase (it);
|
||||
else
|
||||
it++;
|
||||
++it;
|
||||
}
|
||||
CleanupUnconfirmedTags ();
|
||||
return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty ();
|
||||
}
|
||||
|
||||
bool GarlicRoutingSession::CleanupUnconfirmedTags ()
|
||||
{
|
||||
bool ret = false;
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
// delete expired unconfirmed tags
|
||||
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();)
|
||||
{
|
||||
if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
|
||||
if (ts >= (*it)->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
|
||||
{
|
||||
if (m_Owner)
|
||||
m_Owner->RemoveCreatedSession (it->first);
|
||||
delete it->second;
|
||||
m_Owner->RemoveDeliveryStatusSession ((*it)->msgID);
|
||||
it = m_UnconfirmedTagsMsgs.erase (it);
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
it++;
|
||||
++it;
|
||||
}
|
||||
return !m_SessionTags.empty () || m_UnconfirmedTagsMsgs.empty ();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
I2NPMessage * GarlicRoutingSession::WrapSingleMessage (I2NPMessage * msg)
|
||||
std::shared_ptr<I2NPMessage> GarlicRoutingSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
|
||||
{
|
||||
I2NPMessage * m = NewI2NPMessage ();
|
||||
auto m = NewI2NPMessage ();
|
||||
m->Align (12); // in order to get buf aligned to 16 (12 + 4)
|
||||
size_t len = 0;
|
||||
uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length
|
||||
@@ -125,19 +178,19 @@ namespace garlic
|
||||
// create message
|
||||
if (!tagFound) // new session
|
||||
{
|
||||
LogPrint ("No garlic tags available. Use ElGamal");
|
||||
LogPrint (eLogWarning, "Garlic: No tags available, will use ElGamal");
|
||||
if (!m_Destination)
|
||||
{
|
||||
LogPrint ("Can't use ElGamal for unknown destination");
|
||||
LogPrint (eLogError, "Garlic: Can't use ElGamal for unknown destination");
|
||||
return nullptr;
|
||||
}
|
||||
// create ElGamal block
|
||||
ElGamalBlock elGamal;
|
||||
memcpy (elGamal.sessionKey, m_SessionKey, 32);
|
||||
m_Rnd.GenerateBlock (elGamal.preIV, 32); // Pre-IV
|
||||
RAND_bytes (elGamal.preIV, 32); // Pre-IV
|
||||
uint8_t iv[32]; // IV is first 16 bytes
|
||||
CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32);
|
||||
m_Destination->GetElGamalEncryption ()->Encrypt ((uint8_t *)&elGamal, sizeof(elGamal), buf, true);
|
||||
SHA256(elGamal.preIV, 32, iv);
|
||||
m_ElGamalEncryption->Encrypt ((uint8_t *)&elGamal, sizeof(elGamal), buf, true);
|
||||
m_Encryption.SetIV (iv);
|
||||
buf += 514;
|
||||
len += 514;
|
||||
@@ -147,7 +200,7 @@ namespace garlic
|
||||
// session tag
|
||||
memcpy (buf, tag, 32);
|
||||
uint8_t iv[32]; // IV is first 16 bytes
|
||||
CryptoPP::SHA256().CalculateDigest(iv, tag, 32);
|
||||
SHA256(tag, 32, iv);
|
||||
m_Encryption.SetIV (iv);
|
||||
buf += 32;
|
||||
len += 32;
|
||||
@@ -156,13 +209,11 @@ namespace garlic
|
||||
len += CreateAESBlock (buf, msg);
|
||||
htobe32buf (m->GetPayload (), len);
|
||||
m->len += len + 4;
|
||||
FillI2NPMessageHeader (m, eI2NPGarlic);
|
||||
if (msg)
|
||||
DeleteI2NPMessage (msg);
|
||||
m->FillI2NPMessageHeader (eI2NPGarlic);
|
||||
return m;
|
||||
}
|
||||
|
||||
size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, const I2NPMessage * msg)
|
||||
size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg)
|
||||
{
|
||||
size_t blockSize = 0;
|
||||
bool createNewTags = m_Owner && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags*2/3);
|
||||
@@ -185,7 +236,7 @@ namespace garlic
|
||||
blockSize++;
|
||||
size_t len = CreateGarlicPayload (buf + blockSize, msg, newTags);
|
||||
htobe32buf (payloadSize, len);
|
||||
CryptoPP::SHA256().CalculateDigest(payloadHash, buf + blockSize, len);
|
||||
SHA256(buf + blockSize, len, payloadHash);
|
||||
blockSize += len;
|
||||
size_t rem = blockSize % 16;
|
||||
if (rem)
|
||||
@@ -194,10 +245,11 @@ namespace garlic
|
||||
return blockSize;
|
||||
}
|
||||
|
||||
size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, const I2NPMessage * msg, UnconfirmedTags * newTags)
|
||||
size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags)
|
||||
{
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
|
||||
uint32_t msgID = m_Rnd.GenerateWord32 ();
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec
|
||||
uint32_t msgID;
|
||||
RAND_bytes ((uint8_t *)&msgID, 4);
|
||||
size_t size = 0;
|
||||
uint8_t * numCloves = payload + size;
|
||||
*numCloves = 0;
|
||||
@@ -205,26 +257,39 @@ namespace garlic
|
||||
|
||||
if (m_Owner)
|
||||
{
|
||||
if (newTags) // new session
|
||||
// resubmit non-confirmed LeaseSet
|
||||
if (m_LeaseSetUpdateStatus == eLeaseSetSubmitted &&
|
||||
i2p::util::GetMillisecondsSinceEpoch () > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT)
|
||||
m_LeaseSetUpdateStatus = eLeaseSetUpdated;
|
||||
|
||||
// attach DeviveryStatus if necessary
|
||||
if (newTags || m_LeaseSetUpdateStatus == eLeaseSetUpdated) // new tags created or leaseset updated
|
||||
{
|
||||
// clove is DeliveryStatus
|
||||
size += CreateDeliveryStatusClove (payload + size, msgID);
|
||||
if (size > 0) // successive?
|
||||
auto cloveSize = CreateDeliveryStatusClove (payload + size, msgID);
|
||||
if (cloveSize > 0) // successive?
|
||||
{
|
||||
size += cloveSize;
|
||||
(*numCloves)++;
|
||||
m_UnconfirmedTagsMsgs[msgID] = newTags;
|
||||
if (newTags) // new tags created
|
||||
{
|
||||
newTags->msgID = msgID;
|
||||
m_UnconfirmedTagsMsgs.emplace_back (newTags);
|
||||
}
|
||||
m_Owner->DeliveryStatusSent (shared_from_this (), msgID);
|
||||
}
|
||||
else
|
||||
LogPrint ("DeliveryStatus clove was not created");
|
||||
LogPrint (eLogWarning, "Garlic: DeliveryStatus clove was not created");
|
||||
}
|
||||
if (m_LeaseSetUpdated)
|
||||
// attach LeaseSet
|
||||
if (m_LeaseSetUpdateStatus == eLeaseSetUpdated)
|
||||
{
|
||||
m_LeaseSetUpdated = false;
|
||||
m_LeaseSetUpdateStatus = eLeaseSetSubmitted;
|
||||
m_LeaseSetUpdateMsgID = msgID;
|
||||
m_LeaseSetSubmissionTime = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
// clove if our leaseSet must be attached
|
||||
auto leaseSet = CreateDatabaseStoreMsg (m_Owner->GetLeaseSet ());
|
||||
size += CreateGarlicClove (payload + size, leaseSet, false);
|
||||
DeleteI2NPMessage (leaseSet);
|
||||
size += CreateGarlicClove (payload + size, leaseSet, false);
|
||||
(*numCloves)++;
|
||||
}
|
||||
}
|
||||
@@ -243,9 +308,9 @@ namespace garlic
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t GarlicRoutingSession::CreateGarlicClove (uint8_t * buf, const I2NPMessage * msg, bool isDestination)
|
||||
size_t GarlicRoutingSession::CreateGarlicClove (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination)
|
||||
{
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec
|
||||
size_t size = 0;
|
||||
if (isDestination && m_Destination)
|
||||
{
|
||||
@@ -262,7 +327,9 @@ namespace garlic
|
||||
|
||||
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
|
||||
size += msg->GetLength ();
|
||||
htobe32buf (buf + size, m_Rnd.GenerateWord32 ()); // CloveID
|
||||
uint32_t cloveID;
|
||||
RAND_bytes ((uint8_t *)&cloveID, 4);
|
||||
htobe32buf (buf + size, cloveID); // CloveID
|
||||
size += 4;
|
||||
htobe64buf (buf + size, ts); // Expiration of clove
|
||||
size += 8;
|
||||
@@ -276,35 +343,35 @@ namespace garlic
|
||||
size_t size = 0;
|
||||
if (m_Owner)
|
||||
{
|
||||
auto leases = m_Owner->GetLeaseSet ()->GetNonExpiredLeases ();
|
||||
if (!leases.empty ())
|
||||
auto inboundTunnel = m_Owner->GetTunnelPool ()->GetNextInboundTunnel ();
|
||||
if (inboundTunnel)
|
||||
{
|
||||
buf[size] = eGarlicDeliveryTypeTunnel << 5; // delivery instructions flag tunnel
|
||||
size++;
|
||||
uint32_t i = m_Rnd.GenerateWord32 (0, leases.size () - 1);
|
||||
// hash and tunnelID sequence is reversed for Garlic
|
||||
memcpy (buf + size, leases[i].tunnelGateway, 32); // To Hash
|
||||
memcpy (buf + size, inboundTunnel->GetNextIdentHash (), 32); // To Hash
|
||||
size += 32;
|
||||
htobe32buf (buf + size, leases[i].tunnelID); // tunnelID
|
||||
htobe32buf (buf + size, inboundTunnel->GetNextTunnelID ()); // tunnelID
|
||||
size += 4;
|
||||
// create msg
|
||||
I2NPMessage * msg = CreateDeliveryStatusMsg (msgID);
|
||||
auto msg = CreateDeliveryStatusMsg (msgID);
|
||||
if (m_Owner)
|
||||
{
|
||||
//encrypt
|
||||
uint8_t key[32], tag[32];
|
||||
m_Rnd.GenerateBlock (key, 32); // random session key
|
||||
m_Rnd.GenerateBlock (tag, 32); // random session tag
|
||||
RAND_bytes (key, 32); // random session key
|
||||
RAND_bytes (tag, 32); // random session tag
|
||||
m_Owner->SubmitSessionKey (key, tag);
|
||||
GarlicRoutingSession garlic (key, tag);
|
||||
msg = garlic.WrapSingleMessage (msg);
|
||||
}
|
||||
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
|
||||
size += msg->GetLength ();
|
||||
DeleteI2NPMessage (msg);
|
||||
// fill clove
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
|
||||
htobe32buf (buf + size, m_Rnd.GenerateWord32 ()); // CloveID
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec
|
||||
uint32_t cloveID;
|
||||
RAND_bytes ((uint8_t *)&cloveID, 4);
|
||||
htobe32buf (buf + size, cloveID); // CloveID
|
||||
size += 4;
|
||||
htobe64buf (buf + size, ts); // Expiration of clove
|
||||
size += 8;
|
||||
@@ -312,10 +379,10 @@ namespace garlic
|
||||
size += 3;
|
||||
}
|
||||
else
|
||||
LogPrint ("All tunnels of local LeaseSet expired");
|
||||
LogPrint (eLogError, "Garlic: No inbound tunnels in the pool for DeliveryStatus");
|
||||
}
|
||||
else
|
||||
LogPrint ("Missing local LeaseSet");
|
||||
LogPrint (eLogWarning, "Garlic: Missing local LeaseSet");
|
||||
|
||||
return size;
|
||||
}
|
||||
@@ -341,62 +408,49 @@ namespace garlic
|
||||
return true;
|
||||
}
|
||||
|
||||
void GarlicDestination::HandleGarlicMessage (I2NPMessage * msg)
|
||||
void GarlicDestination::HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
uint8_t * buf = msg->GetPayload ();
|
||||
uint32_t length = bufbe32toh (buf);
|
||||
if (length > msg->GetLength ())
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: message length ", length, " exceeds I2NP message length ", msg->GetLength ());
|
||||
return;
|
||||
}
|
||||
buf += 4; // length
|
||||
auto it = m_Tags.find (SessionTag(buf));
|
||||
if (it != m_Tags.end ())
|
||||
{
|
||||
// tag found. Use AES
|
||||
uint8_t iv[32]; // IV is first 16 bytes
|
||||
CryptoPP::SHA256().CalculateDigest(iv, buf, 32);
|
||||
it->second->SetIV (iv);
|
||||
it->second->Decrypt (buf + 32, length - 32, buf + 32);
|
||||
HandleAESBlock (buf + 32, length - 32, it->second, msg->from);
|
||||
m_Tags.erase (it); // tag might be used only once
|
||||
if (length >= 32)
|
||||
{
|
||||
uint8_t iv[32]; // IV is first 16 bytes
|
||||
SHA256(buf, 32, iv);
|
||||
it->second->SetIV (iv);
|
||||
it->second->Decrypt (buf + 32, length - 32, buf + 32);
|
||||
HandleAESBlock (buf + 32, length - 32, it->second, msg->from);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Garlic: message length ", length, " is less than 32 bytes");
|
||||
m_Tags.erase (it); // tag might be used only once
|
||||
}
|
||||
else
|
||||
{
|
||||
// tag not found. Use ElGamal
|
||||
ElGamalBlock elGamal;
|
||||
if (i2p::crypto::ElGamalDecrypt (GetEncryptionPrivateKey (), buf, (uint8_t *)&elGamal, true))
|
||||
if (length >= 514 && i2p::crypto::ElGamalDecrypt (GetEncryptionPrivateKey (), buf, (uint8_t *)&elGamal, true))
|
||||
{
|
||||
auto decryption = std::make_shared<i2p::crypto::CBCDecryption>();
|
||||
decryption->SetKey (elGamal.sessionKey);
|
||||
uint8_t iv[32]; // IV is first 16 bytes
|
||||
CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32);
|
||||
SHA256(elGamal.preIV, 32, iv);
|
||||
decryption->SetIV (iv);
|
||||
decryption->Decrypt(buf + 514, length - 514, buf + 514);
|
||||
HandleAESBlock (buf + 514, length - 514, decryption, msg->from);
|
||||
}
|
||||
else
|
||||
LogPrint ("Failed to decrypt garlic");
|
||||
LogPrint (eLogError, "Garlic: Failed to decrypt message");
|
||||
}
|
||||
DeleteI2NPMessage (msg);
|
||||
|
||||
// cleanup expired tags
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (ts > m_LastTagsCleanupTime + INCOMING_TAGS_EXPIRATION_TIMEOUT)
|
||||
{
|
||||
if (m_LastTagsCleanupTime)
|
||||
{
|
||||
int numExpiredTags = 0;
|
||||
for (auto it = m_Tags.begin (); it != m_Tags.end ();)
|
||||
{
|
||||
if (ts > it->first.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT)
|
||||
{
|
||||
numExpiredTags++;
|
||||
it = m_Tags.erase (it);
|
||||
}
|
||||
else
|
||||
it++;
|
||||
}
|
||||
LogPrint (numExpiredTags, " tags expired for ", GetIdentHash().ToBase64 ());
|
||||
}
|
||||
m_LastTagsCleanupTime = ts;
|
||||
}
|
||||
}
|
||||
|
||||
void GarlicDestination::HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<i2p::crypto::CBCDecryption> decryption,
|
||||
@@ -408,7 +462,7 @@ namespace garlic
|
||||
{
|
||||
if (tagCount*32 > len)
|
||||
{
|
||||
LogPrint (eLogError, "Tag count ", tagCount, " exceeds length ", len);
|
||||
LogPrint (eLogError, "Garlic: Tag count ", tagCount, " exceeds length ", len);
|
||||
return ;
|
||||
}
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
@@ -420,7 +474,7 @@ namespace garlic
|
||||
uint32_t payloadSize = bufbe32toh (buf);
|
||||
if (payloadSize > len)
|
||||
{
|
||||
LogPrint (eLogError, "Unexpected payload size ", payloadSize);
|
||||
LogPrint (eLogError, "Garlic: Unexpected payload size ", payloadSize);
|
||||
return;
|
||||
}
|
||||
buf += 4;
|
||||
@@ -431,9 +485,11 @@ namespace garlic
|
||||
buf++; // flag
|
||||
|
||||
// payload
|
||||
if (!CryptoPP::SHA256().VerifyDigest (payloadHash, buf, payloadSize)) // payload hash doesn't match
|
||||
uint8_t digest[32];
|
||||
SHA256 (buf, payloadSize, digest);
|
||||
if (memcmp (payloadHash, digest, 32)) // payload hash doesn't match
|
||||
{
|
||||
LogPrint ("Wrong payload hash");
|
||||
LogPrint (eLogError, "Garlic: wrong payload hash");
|
||||
return;
|
||||
}
|
||||
HandleGarlicPayload (buf, payloadSize, from);
|
||||
@@ -441,8 +497,9 @@ namespace garlic
|
||||
|
||||
void GarlicDestination::HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
|
||||
{
|
||||
const uint8_t * buf1 = buf;
|
||||
int numCloves = buf[0];
|
||||
LogPrint (numCloves," cloves");
|
||||
LogPrint (eLogDebug, "Garlic: ", numCloves," cloves");
|
||||
buf++;
|
||||
for (int i = 0; i < numCloves; i++)
|
||||
{
|
||||
@@ -452,139 +509,169 @@ namespace garlic
|
||||
if (flag & 0x80) // encrypted?
|
||||
{
|
||||
// TODO: implement
|
||||
LogPrint ("Clove encrypted");
|
||||
LogPrint (eLogWarning, "Garlic: clove encrypted");
|
||||
buf += 32;
|
||||
}
|
||||
GarlicDeliveryType deliveryType = (GarlicDeliveryType)((flag >> 5) & 0x03);
|
||||
switch (deliveryType)
|
||||
{
|
||||
case eGarlicDeliveryTypeLocal:
|
||||
LogPrint ("Garlic type local");
|
||||
LogPrint (eLogDebug, "Garlic: type local");
|
||||
HandleI2NPMessage (buf, len, from);
|
||||
break;
|
||||
case eGarlicDeliveryTypeDestination:
|
||||
LogPrint ("Garlic type destination");
|
||||
LogPrint (eLogDebug, "Garlic: type destination");
|
||||
buf += 32; // destination. check it later or for multiple destinations
|
||||
HandleI2NPMessage (buf, len, from);
|
||||
break;
|
||||
case eGarlicDeliveryTypeTunnel:
|
||||
{
|
||||
LogPrint ("Garlic type tunnel");
|
||||
LogPrint (eLogDebug, "Garlic: type tunnel");
|
||||
// gwHash and gwTunnel sequence is reverted
|
||||
uint8_t * gwHash = buf;
|
||||
buf += 32;
|
||||
uint32_t gwTunnel = bufbe32toh (buf);
|
||||
buf += 4;
|
||||
std::shared_ptr<i2p::tunnel::OutboundTunnel> tunnel;
|
||||
if (from && from->GetTunnelPool ())
|
||||
tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel ();
|
||||
if (tunnel) // we have send it through an outbound tunnel
|
||||
{
|
||||
I2NPMessage * msg = CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from);
|
||||
tunnel->SendTunnelDataMsg (gwHash, gwTunnel, msg);
|
||||
}
|
||||
else
|
||||
LogPrint ("No outbound tunnels available for garlic clove");
|
||||
auto msg = CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from);
|
||||
if (from) // received through an inbound tunnel
|
||||
{
|
||||
std::shared_ptr<i2p::tunnel::OutboundTunnel> tunnel;
|
||||
if (from->GetTunnelPool ())
|
||||
tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel ();
|
||||
else
|
||||
LogPrint (eLogError, "Garlic: Tunnel pool is not set for inbound tunnel");
|
||||
if (tunnel) // we have send it through an outbound tunnel
|
||||
tunnel->SendTunnelDataMsg (gwHash, gwTunnel, msg);
|
||||
else
|
||||
LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove");
|
||||
}
|
||||
else // received directly
|
||||
i2p::transport::transports.SendMessage (gwHash, i2p::CreateTunnelGatewayMsg (gwTunnel, msg)); // send directly
|
||||
break;
|
||||
}
|
||||
case eGarlicDeliveryTypeRouter:
|
||||
LogPrint ("Garlic type router not supported");
|
||||
{
|
||||
uint8_t * ident = buf;
|
||||
buf += 32;
|
||||
break;
|
||||
if (!from) // received directly
|
||||
i2p::transport::transports.SendMessage (ident,
|
||||
CreateI2NPMessage (buf, GetI2NPMessageLength (buf)));
|
||||
else
|
||||
LogPrint (eLogWarning, "Garlic: type router for inbound tunnels not supported");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LogPrint ("Unknow garlic delivery type ", (int)deliveryType);
|
||||
LogPrint (eLogWarning, "Garlic: unknown delivery type ", (int)deliveryType);
|
||||
}
|
||||
buf += GetI2NPMessageLength (buf); // I2NP
|
||||
buf += 4; // CloveID
|
||||
buf += 8; // Date
|
||||
buf += 3; // Certificate
|
||||
if (buf - buf1 > (int)len)
|
||||
{
|
||||
LogPrint (eLogError, "Garlic: clove is too long");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
I2NPMessage * GarlicDestination::WrapMessage (std::shared_ptr<const i2p::data::RoutingDestination> destination,
|
||||
I2NPMessage * msg, bool attachLeaseSet)
|
||||
std::shared_ptr<I2NPMessage> GarlicDestination::WrapMessage (std::shared_ptr<const i2p::data::RoutingDestination> destination,
|
||||
std::shared_ptr<I2NPMessage> msg, bool attachLeaseSet)
|
||||
{
|
||||
if (attachLeaseSet) // we should maintain this session
|
||||
{
|
||||
auto session = GetRoutingSession (destination, 32); // 32 tags by default
|
||||
return session->WrapSingleMessage (msg);
|
||||
}
|
||||
else // one time session
|
||||
{
|
||||
GarlicRoutingSession session (this, destination, 0); // don't use tag if no LeaseSet
|
||||
return session.WrapSingleMessage (msg);
|
||||
}
|
||||
auto session = GetRoutingSession (destination, attachLeaseSet);
|
||||
return session->WrapSingleMessage (msg);
|
||||
}
|
||||
|
||||
std::shared_ptr<GarlicRoutingSession> GarlicDestination::GetRoutingSession (
|
||||
std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags)
|
||||
std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet)
|
||||
{
|
||||
auto it = m_Sessions.find (destination->GetIdentHash ());
|
||||
std::shared_ptr<GarlicRoutingSession> session;
|
||||
if (it != m_Sessions.end ())
|
||||
session = it->second;
|
||||
GarlicRoutingSessionPtr session;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
auto it = m_Sessions.find (destination->GetIdentHash ());
|
||||
if (it != m_Sessions.end ())
|
||||
session = it->second;
|
||||
}
|
||||
if (!session)
|
||||
{
|
||||
session = std::make_shared<GarlicRoutingSession> (this, destination, numTags);
|
||||
session = std::make_shared<GarlicRoutingSession> (this, destination,
|
||||
attachLeaseSet ? m_NumTags : 4, attachLeaseSet); // specified num tags for connections and 4 for LS requests
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
m_Sessions[destination->GetIdentHash ()] = session;
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
void GarlicDestination::CleanupRoutingSessions ()
|
||||
void GarlicDestination::CleanupExpiredTags ()
|
||||
{
|
||||
// incoming
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
int numExpiredTags = 0;
|
||||
for (auto it = m_Tags.begin (); it != m_Tags.end ();)
|
||||
{
|
||||
if (ts > it->first.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT)
|
||||
{
|
||||
numExpiredTags++;
|
||||
it = m_Tags.erase (it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
if (numExpiredTags > 0)
|
||||
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 ())
|
||||
{
|
||||
LogPrint (eLogInfo, "Routing session to ", it->first.ToBase32 (), " deleted");
|
||||
it = m_Sessions.erase (it);
|
||||
}
|
||||
else
|
||||
it++;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void GarlicDestination::RemoveCreatedSession (uint32_t msgID)
|
||||
|
||||
void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID)
|
||||
{
|
||||
m_CreatedSessions.erase (msgID);
|
||||
m_DeliveryStatusSessions.erase (msgID);
|
||||
}
|
||||
|
||||
void GarlicDestination::DeliveryStatusSent (std::shared_ptr<GarlicRoutingSession> session, uint32_t msgID)
|
||||
void GarlicDestination::DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID)
|
||||
{
|
||||
m_CreatedSessions[msgID] = session;
|
||||
}
|
||||
m_DeliveryStatusSessions[msgID] = session;
|
||||
}
|
||||
|
||||
void GarlicDestination::HandleDeliveryStatusMessage (I2NPMessage * msg)
|
||||
void GarlicDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
uint32_t msgID = bufbe32toh (msg->GetPayload ());
|
||||
{
|
||||
auto it = m_CreatedSessions.find (msgID);
|
||||
if (it != m_CreatedSessions.end ())
|
||||
auto it = m_DeliveryStatusSessions.find (msgID);
|
||||
if (it != m_DeliveryStatusSessions.end ())
|
||||
{
|
||||
it->second->TagsConfirmed (msgID);
|
||||
m_CreatedSessions.erase (it);
|
||||
LogPrint (eLogInfo, "Garlic message ", msgID, " acknowledged");
|
||||
}
|
||||
it->second->MessageConfirmed (msgID);
|
||||
m_DeliveryStatusSessions.erase (it);
|
||||
LogPrint (eLogDebug, "Garlic: message ", msgID, " acknowledged");
|
||||
}
|
||||
}
|
||||
DeleteI2NPMessage (msg);
|
||||
}
|
||||
|
||||
void GarlicDestination::SetLeaseSetUpdated ()
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
for (auto it: m_Sessions)
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
for (auto& it: m_Sessions)
|
||||
it.second->SetLeaseSetUpdated ();
|
||||
}
|
||||
|
||||
void GarlicDestination::ProcessGarlicMessage (I2NPMessage * msg)
|
||||
void GarlicDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
HandleGarlicMessage (msg);
|
||||
}
|
||||
|
||||
void GarlicDestination::ProcessDeliveryStatusMessage (I2NPMessage * msg)
|
||||
void GarlicDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
HandleDeliveryStatusMessage (msg);
|
||||
}
|
||||
|
||||
118
Garlic.h
118
Garlic.h
@@ -8,15 +8,19 @@
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <cryptopp/osrng.h>
|
||||
#include "aes.h"
|
||||
#include "Crypto.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "LeaseSet.h"
|
||||
#include "Queue.h"
|
||||
#include "Identity.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
{
|
||||
namespace tunnel
|
||||
{
|
||||
class OutboundTunnel;
|
||||
}
|
||||
|
||||
namespace garlic
|
||||
{
|
||||
|
||||
@@ -28,17 +32,19 @@ namespace garlic
|
||||
eGarlicDeliveryTypeTunnel = 3
|
||||
};
|
||||
|
||||
#pragma pack(1)
|
||||
struct ElGamalBlock
|
||||
{
|
||||
uint8_t sessionKey[32];
|
||||
uint8_t preIV[32];
|
||||
uint8_t padding[158];
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
const int INCOMING_TAGS_EXPIRATION_TIMEOUT = 960; // 16 minutes
|
||||
const int OUTGOING_TAGS_EXPIRATION_TIMEOUT = 720; // 12 minutes
|
||||
const int OUTGOING_TAGS_CONFIRMATION_TIMEOUT = 10; // 10 seconds
|
||||
const int LEASET_CONFIRMATION_TIMEOUT = 4000; // in milliseconds
|
||||
const int ROUTING_PATH_EXPIRATION_TIMEOUT = 30; // 30 seconds
|
||||
const int ROUTING_PATH_MAX_NUM_TIMES_USED = 100; // how many times might be used
|
||||
|
||||
struct SessionTag: public i2p::data::Tag<32>
|
||||
{
|
||||
@@ -52,14 +58,32 @@ namespace garlic
|
||||
#endif
|
||||
uint32_t creationTime; // seconds since epoch
|
||||
};
|
||||
|
||||
|
||||
struct GarlicRoutingPath
|
||||
{
|
||||
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
|
||||
std::shared_ptr<const i2p::data::Lease> remoteLease;
|
||||
int rtt; // RTT
|
||||
uint32_t updateTime; // seconds since epoch
|
||||
int numTimesUsed;
|
||||
};
|
||||
|
||||
class GarlicDestination;
|
||||
class GarlicRoutingSession: public std::enable_shared_from_this<GarlicRoutingSession>
|
||||
{
|
||||
enum LeaseSetUpdateStatus
|
||||
{
|
||||
eLeaseSetUpToDate = 0,
|
||||
eLeaseSetUpdated,
|
||||
eLeaseSetSubmitted,
|
||||
eLeaseSetDoNotSend
|
||||
};
|
||||
|
||||
struct UnconfirmedTags
|
||||
{
|
||||
UnconfirmedTags (int n): numTags (n), tagsCreationTime (0) { sessionTags = new SessionTag[numTags]; };
|
||||
~UnconfirmedTags () { delete[] sessionTags; };
|
||||
uint32_t msgID;
|
||||
int numTags;
|
||||
SessionTag * sessionTags;
|
||||
uint32_t tagsCreationTime;
|
||||
@@ -67,22 +91,31 @@ namespace garlic
|
||||
|
||||
public:
|
||||
|
||||
GarlicRoutingSession (GarlicDestination * owner, std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags);
|
||||
GarlicRoutingSession (GarlicDestination * owner, std::shared_ptr<const i2p::data::RoutingDestination> destination,
|
||||
int numTags, bool attachLeaseSet);
|
||||
GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption
|
||||
~GarlicRoutingSession ();
|
||||
I2NPMessage * WrapSingleMessage (I2NPMessage * msg);
|
||||
void TagsConfirmed (uint32_t msgID);
|
||||
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
|
||||
void MessageConfirmed (uint32_t msgID);
|
||||
bool CleanupExpiredTags (); // returns true if something left
|
||||
bool CleanupUnconfirmedTags (); // returns true if something has been deleted
|
||||
|
||||
void SetLeaseSetUpdated () { m_LeaseSetUpdated = true; };
|
||||
void SetLeaseSetUpdated ()
|
||||
{
|
||||
if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated;
|
||||
};
|
||||
|
||||
std::shared_ptr<GarlicRoutingPath> GetSharedRoutingPath ();
|
||||
void SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path);
|
||||
|
||||
private:
|
||||
|
||||
size_t CreateAESBlock (uint8_t * buf, const I2NPMessage * msg);
|
||||
size_t CreateGarlicPayload (uint8_t * payload, const I2NPMessage * msg, UnconfirmedTags * newTags);
|
||||
size_t CreateGarlicClove (uint8_t * buf, const I2NPMessage * msg, bool isDestination);
|
||||
size_t CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg);
|
||||
size_t CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags);
|
||||
size_t CreateGarlicClove (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination);
|
||||
size_t CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID);
|
||||
|
||||
|
||||
void TagsConfirmed (uint32_t msgID);
|
||||
UnconfirmedTags * GenerateSessionTags ();
|
||||
|
||||
private:
|
||||
@@ -92,41 +125,54 @@ namespace garlic
|
||||
i2p::crypto::AESKey m_SessionKey;
|
||||
std::list<SessionTag> m_SessionTags;
|
||||
int m_NumTags;
|
||||
std::map<uint32_t, UnconfirmedTags *> m_UnconfirmedTagsMsgs;
|
||||
bool m_LeaseSetUpdated;
|
||||
|
||||
std::list<std::unique_ptr<UnconfirmedTags> > m_UnconfirmedTagsMsgs;
|
||||
|
||||
LeaseSetUpdateStatus m_LeaseSetUpdateStatus;
|
||||
uint32_t m_LeaseSetUpdateMsgID;
|
||||
uint64_t m_LeaseSetSubmissionTime; // in milliseconds
|
||||
|
||||
i2p::crypto::CBCEncryption m_Encryption;
|
||||
CryptoPP::AutoSeededRandomPool m_Rnd;
|
||||
std::unique_ptr<const i2p::crypto::ElGamalEncryption> m_ElGamalEncryption;
|
||||
|
||||
std::shared_ptr<GarlicRoutingPath> m_SharedRoutingPath;
|
||||
|
||||
public:
|
||||
// for HTTP only
|
||||
size_t GetNumOutgoingTags () const { return m_SessionTags.size (); };
|
||||
};
|
||||
//using GarlicRoutingSessionPtr = std::shared_ptr<GarlicRoutingSession>;
|
||||
typedef std::shared_ptr<GarlicRoutingSession> GarlicRoutingSessionPtr; // TODO: replace to using after switch to 4.8
|
||||
|
||||
class GarlicDestination: public i2p::data::LocalDestination
|
||||
{
|
||||
public:
|
||||
|
||||
GarlicDestination (): m_LastTagsCleanupTime (0) {};
|
||||
GarlicDestination (): m_NumTags (32) {}; // 32 tags by default
|
||||
~GarlicDestination ();
|
||||
|
||||
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags);
|
||||
void CleanupRoutingSessions ();
|
||||
void RemoveCreatedSession (uint32_t msgID);
|
||||
I2NPMessage * WrapMessage (std::shared_ptr<const i2p::data::RoutingDestination> destination,
|
||||
I2NPMessage * msg, bool attachLeaseSet = false);
|
||||
void SetNumTags (int numTags) { m_NumTags = numTags; };
|
||||
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);
|
||||
void CleanupExpiredTags ();
|
||||
void RemoveDeliveryStatusSession (uint32_t msgID);
|
||||
std::shared_ptr<I2NPMessage> WrapMessage (std::shared_ptr<const i2p::data::RoutingDestination> destination,
|
||||
std::shared_ptr<I2NPMessage> msg, bool attachLeaseSet = false);
|
||||
|
||||
void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag
|
||||
virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread
|
||||
void DeliveryStatusSent (std::shared_ptr<GarlicRoutingSession> session, uint32_t msgID);
|
||||
void DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID);
|
||||
|
||||
virtual void ProcessGarlicMessage (I2NPMessage * msg);
|
||||
virtual void ProcessDeliveryStatusMessage (I2NPMessage * msg);
|
||||
virtual void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
virtual void SetLeaseSetUpdated ();
|
||||
|
||||
virtual const i2p::data::LeaseSet * GetLeaseSet () = 0; // TODO
|
||||
virtual std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () = 0; // TODO
|
||||
virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const = 0;
|
||||
virtual void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from) = 0;
|
||||
|
||||
protected:
|
||||
|
||||
void HandleGarlicMessage (I2NPMessage * msg);
|
||||
void HandleDeliveryStatusMessage (I2NPMessage * msg);
|
||||
void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
|
||||
private:
|
||||
|
||||
@@ -137,13 +183,19 @@ namespace garlic
|
||||
private:
|
||||
|
||||
// outgoing sessions
|
||||
int m_NumTags;
|
||||
std::mutex m_SessionsMutex;
|
||||
std::map<i2p::data::IdentHash, std::shared_ptr<GarlicRoutingSession> > m_Sessions;
|
||||
std::map<i2p::data::IdentHash, GarlicRoutingSessionPtr> m_Sessions;
|
||||
// incoming
|
||||
std::map<SessionTag, std::shared_ptr<i2p::crypto::CBCDecryption>> m_Tags;
|
||||
uint32_t m_LastTagsCleanupTime;
|
||||
// DeliveryStatus
|
||||
std::map<uint32_t, std::shared_ptr<GarlicRoutingSession> > m_CreatedSessions; // msgID -> session
|
||||
std::map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session
|
||||
|
||||
public:
|
||||
|
||||
// for HTTP only
|
||||
size_t GetNumIncomingTags () const { return m_Tags.size (); }
|
||||
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
108
Gzip.cpp
Normal file
108
Gzip.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016, 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 <inttypes.h>
|
||||
#include <string.h> /* memset */
|
||||
#include <iostream>
|
||||
|
||||
#include "Gzip.h"
|
||||
|
||||
namespace i2p {
|
||||
namespace data {
|
||||
const size_t GZIP_CHUNK_SIZE = 16384;
|
||||
|
||||
GzipInflator::GzipInflator (): m_IsDirty (false)
|
||||
{
|
||||
memset (&m_Inflator, 0, sizeof (m_Inflator));
|
||||
inflateInit2 (&m_Inflator, MAX_WBITS + 16); // gzip
|
||||
}
|
||||
|
||||
GzipInflator::~GzipInflator ()
|
||||
{
|
||||
inflateEnd (&m_Inflator);
|
||||
}
|
||||
|
||||
size_t GzipInflator::Inflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen)
|
||||
{
|
||||
if (m_IsDirty) inflateReset (&m_Inflator);
|
||||
m_IsDirty = true;
|
||||
m_Inflator.next_in = const_cast<uint8_t *>(in);
|
||||
m_Inflator.avail_in = inLen;
|
||||
m_Inflator.next_out = out;
|
||||
m_Inflator.avail_out = outLen;
|
||||
int err;
|
||||
if ((err = inflate (&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END) {
|
||||
return outLen - m_Inflator.avail_out;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GzipInflator::Inflate (const uint8_t * in, size_t inLen, std::ostream& os)
|
||||
{
|
||||
m_IsDirty = true;
|
||||
uint8_t * out = new uint8_t[GZIP_CHUNK_SIZE];
|
||||
m_Inflator.next_in = const_cast<uint8_t *>(in);
|
||||
m_Inflator.avail_in = inLen;
|
||||
int ret;
|
||||
do {
|
||||
m_Inflator.next_out = out;
|
||||
m_Inflator.avail_out = GZIP_CHUNK_SIZE;
|
||||
ret = inflate (&m_Inflator, Z_NO_FLUSH);
|
||||
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
|
||||
delete[] out;
|
||||
}
|
||||
|
||||
void GzipInflator::Inflate (std::istream& in, std::ostream& out)
|
||||
{
|
||||
uint8_t * buf = new uint8_t[GZIP_CHUNK_SIZE];
|
||||
while (!in.eof ())
|
||||
{
|
||||
in.read ((char *) buf, GZIP_CHUNK_SIZE);
|
||||
Inflate (buf, in.gcount (), out);
|
||||
}
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
GzipDeflator::GzipDeflator (): m_IsDirty (false)
|
||||
{
|
||||
memset (&m_Deflator, 0, sizeof (m_Deflator));
|
||||
deflateInit2 (&m_Deflator, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); // 15 + 16 sets gzip
|
||||
}
|
||||
|
||||
GzipDeflator::~GzipDeflator ()
|
||||
{
|
||||
deflateEnd (&m_Deflator);
|
||||
}
|
||||
|
||||
void GzipDeflator::SetCompressionLevel (int level)
|
||||
{
|
||||
deflateParams (&m_Deflator, level, Z_DEFAULT_STRATEGY);
|
||||
}
|
||||
|
||||
size_t GzipDeflator::Deflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen)
|
||||
{
|
||||
if (m_IsDirty) deflateReset (&m_Deflator);
|
||||
m_IsDirty = true;
|
||||
m_Deflator.next_in = const_cast<uint8_t *>(in);
|
||||
m_Deflator.avail_in = inLen;
|
||||
m_Deflator.next_out = out;
|
||||
m_Deflator.avail_out = outLen;
|
||||
int err;
|
||||
if ((err = deflate (&m_Deflator, Z_FINISH)) == Z_STREAM_END) {
|
||||
return outLen - m_Deflator.avail_out;
|
||||
} /* else */
|
||||
return 0;
|
||||
}
|
||||
} // data
|
||||
} // i2p
|
||||
44
Gzip.h
Normal file
44
Gzip.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#ifndef GZIP_H__
|
||||
#define GZIP_H__
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
namespace i2p {
|
||||
namespace data {
|
||||
class GzipInflator
|
||||
{
|
||||
public:
|
||||
|
||||
GzipInflator ();
|
||||
~GzipInflator ();
|
||||
|
||||
size_t Inflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen);
|
||||
/** @note @a os failbit will be set in case of error */
|
||||
void Inflate (const uint8_t * in, size_t inLen, std::ostream& os);
|
||||
void Inflate (std::istream& in, std::ostream& out);
|
||||
|
||||
private:
|
||||
|
||||
z_stream m_Inflator;
|
||||
bool m_IsDirty;
|
||||
};
|
||||
|
||||
class GzipDeflator
|
||||
{
|
||||
public:
|
||||
|
||||
GzipDeflator ();
|
||||
~GzipDeflator ();
|
||||
|
||||
void SetCompressionLevel (int level);
|
||||
size_t Deflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen);
|
||||
|
||||
private:
|
||||
|
||||
z_stream m_Deflator;
|
||||
bool m_IsDirty;
|
||||
};
|
||||
} // data
|
||||
} // i2p
|
||||
|
||||
#endif /* GZIP_H__ */
|
||||
431
HTTP.cpp
Normal file
431
HTTP.cpp
Normal file
@@ -0,0 +1,431 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016, 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 "util.h"
|
||||
#include "HTTP.h"
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
|
||||
namespace i2p {
|
||||
namespace http {
|
||||
const std::vector<std::string> HTTP_METHODS = {
|
||||
"GET", "HEAD", "POST", "PUT", "PATCH",
|
||||
"DELETE", "OPTIONS", "CONNECT"
|
||||
};
|
||||
const std::vector<std::string> HTTP_VERSIONS = {
|
||||
"HTTP/1.0", "HTTP/1.1"
|
||||
};
|
||||
|
||||
inline bool is_http_version(const std::string & str) {
|
||||
return std::find(HTTP_VERSIONS.begin(), HTTP_VERSIONS.end(), str) != std::end(HTTP_VERSIONS);
|
||||
}
|
||||
|
||||
inline bool is_http_method(const std::string & str) {
|
||||
return std::find(HTTP_METHODS.begin(), HTTP_METHODS.end(), str) != std::end(HTTP_METHODS);
|
||||
}
|
||||
|
||||
void strsplit(const std::string & line, std::vector<std::string> &tokens, char delim, std::size_t limit = 0) {
|
||||
std::size_t count = 0;
|
||||
std::stringstream ss(line);
|
||||
std::string token;
|
||||
while (1) {
|
||||
count++;
|
||||
if (limit > 0 && count >= limit)
|
||||
delim = '\n'; /* reset delimiter */
|
||||
if (!std::getline(ss, token, delim))
|
||||
break;
|
||||
tokens.push_back(token);
|
||||
}
|
||||
}
|
||||
|
||||
bool parse_header_line(const std::string & line, std::map<std::string, std::string> & headers) {
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
void gen_rfc1123_date(std::string & out) {
|
||||
std::time_t now = std::time(nullptr);
|
||||
char buf[128];
|
||||
std::strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", std::gmtime(&now));
|
||||
out = buf;
|
||||
}
|
||||
|
||||
bool URL::parse(const char *str, std::size_t len) {
|
||||
std::string url(str, len ? len : strlen(str));
|
||||
return parse(url);
|
||||
}
|
||||
|
||||
bool URL::parse(const std::string& url) {
|
||||
std::size_t pos_p = 0; /* < current parse position */
|
||||
std::size_t pos_c = 0; /* < work position */
|
||||
if(url.at(0) != '/' || pos_p > 0) {
|
||||
std::size_t pos_s = 0;
|
||||
/* schema */
|
||||
pos_c = url.find("://");
|
||||
if (pos_c != std::string::npos) {
|
||||
schema = url.substr(0, pos_c);
|
||||
pos_p = pos_c + 3;
|
||||
}
|
||||
/* user[:pass] */
|
||||
pos_s = url.find('/', pos_p); /* find first slash */
|
||||
pos_c = url.find('@', pos_p); /* find end of 'user' or 'user:pass' part */
|
||||
if (pos_c != std::string::npos && (pos_s == std::string::npos || pos_s > pos_c)) {
|
||||
std::size_t delim = url.find(':', pos_p);
|
||||
if (delim != std::string::npos && delim < pos_c) {
|
||||
user = url.substr(pos_p, delim - pos_p);
|
||||
delim += 1;
|
||||
pass = url.substr(delim, pos_c - delim);
|
||||
} else {
|
||||
user = url.substr(pos_p, pos_c - pos_p);
|
||||
}
|
||||
pos_p = pos_c + 1;
|
||||
}
|
||||
/* hostname[:port][/path] */
|
||||
pos_c = url.find_first_of(":/", pos_p);
|
||||
if (pos_c == std::string::npos) {
|
||||
/* only hostname, without post and path */
|
||||
host = url.substr(pos_p, std::string::npos);
|
||||
return true;
|
||||
} else if (url.at(pos_c) == ':') {
|
||||
host = url.substr(pos_p, pos_c - pos_p);
|
||||
/* port[/path] */
|
||||
pos_p = pos_c + 1;
|
||||
pos_c = url.find('/', pos_p);
|
||||
std::string port_str = (pos_c == std::string::npos)
|
||||
? url.substr(pos_p, std::string::npos)
|
||||
: url.substr(pos_p, pos_c - pos_p);
|
||||
/* stoi throws exception on failure, we don't need it */
|
||||
for (char c : port_str) {
|
||||
if (c < '0' || c > '9')
|
||||
return false;
|
||||
port *= 10;
|
||||
port += c - '0';
|
||||
}
|
||||
if (pos_c == std::string::npos)
|
||||
return true; /* no path part */
|
||||
pos_p = pos_c;
|
||||
} else {
|
||||
/* start of path part found */
|
||||
host = url.substr(pos_p, pos_c - pos_p);
|
||||
pos_p = pos_c;
|
||||
}
|
||||
}
|
||||
|
||||
/* pos_p now at start of path part */
|
||||
pos_c = url.find_first_of("?#", pos_p);
|
||||
if (pos_c == std::string::npos) {
|
||||
/* only path, without fragment and query */
|
||||
path = url.substr(pos_p, std::string::npos);
|
||||
return true;
|
||||
} else if (url.at(pos_c) == '?') {
|
||||
/* found query part */
|
||||
path = url.substr(pos_p, pos_c - pos_p);
|
||||
pos_p = pos_c + 1;
|
||||
pos_c = url.find('#', pos_p);
|
||||
if (pos_c == std::string::npos) {
|
||||
/* no fragment */
|
||||
query = url.substr(pos_p, std::string::npos);
|
||||
return true;
|
||||
} else {
|
||||
query = url.substr(pos_p, pos_c - pos_p);
|
||||
pos_p = pos_c + 1;
|
||||
}
|
||||
} else {
|
||||
/* found fragment part */
|
||||
path = url.substr(pos_p, pos_c - pos_p);
|
||||
pos_p = pos_c + 1;
|
||||
}
|
||||
|
||||
/* pos_p now at start of fragment part */
|
||||
frag = url.substr(pos_p, std::string::npos);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool URL::parse_query(std::map<std::string, std::string> & params) {
|
||||
std::vector<std::string> tokens;
|
||||
strsplit(query, tokens, '&');
|
||||
|
||||
params.clear();
|
||||
for (const auto& it : tokens) {
|
||||
std::size_t eq = it.find ('=');
|
||||
if (eq != std::string::npos) {
|
||||
auto e = std::pair<std::string, std::string>(it.substr(0, eq), it.substr(eq + 1));
|
||||
params.insert(e);
|
||||
} else {
|
||||
auto e = std::pair<std::string, std::string>(it, "");
|
||||
params.insert(e);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string URL::to_string() {
|
||||
std::string out = "";
|
||||
if (schema != "") {
|
||||
out = schema + "://";
|
||||
if (user != "" && pass != "") {
|
||||
out += user + ":" + pass + "@";
|
||||
} else if (user != "") {
|
||||
out += user + "@";
|
||||
}
|
||||
if (port) {
|
||||
out += host + ":" + std::to_string(port);
|
||||
} else {
|
||||
out += host;
|
||||
}
|
||||
}
|
||||
out += path;
|
||||
if (query != "")
|
||||
out += "?" + query;
|
||||
if (frag != "")
|
||||
out += "#" + frag;
|
||||
return out;
|
||||
}
|
||||
|
||||
void HTTPMsg::add_header(const char *name, std::string & value, bool replace) {
|
||||
add_header(name, value.c_str(), replace);
|
||||
}
|
||||
|
||||
void HTTPMsg::add_header(const char *name, const char *value, bool replace) {
|
||||
std::size_t count = headers.count(name);
|
||||
if (count && !replace)
|
||||
return;
|
||||
if (count) {
|
||||
headers[name] = value;
|
||||
return;
|
||||
}
|
||||
headers.insert(std::pair<std::string, std::string>(name, value));
|
||||
}
|
||||
|
||||
void HTTPMsg::del_header(const char *name) {
|
||||
headers.erase(name);
|
||||
}
|
||||
|
||||
int HTTPReq::parse(const char *buf, size_t len) {
|
||||
std::string str(buf, len);
|
||||
return parse(str);
|
||||
}
|
||||
|
||||
int HTTPReq::parse(const std::string& str) {
|
||||
enum { REQ_LINE, HEADER_LINE } expect = REQ_LINE;
|
||||
std::size_t eoh = str.find(HTTP_EOH); /* request head size */
|
||||
std::size_t eol = 0, pos = 0;
|
||||
URL url;
|
||||
|
||||
if (eoh == std::string::npos)
|
||||
return 0; /* str not contains complete request */
|
||||
|
||||
while ((eol = str.find(CRLF, pos)) != std::string::npos) {
|
||||
if (expect == REQ_LINE) {
|
||||
std::string line = str.substr(pos, eol - pos);
|
||||
std::vector<std::string> tokens;
|
||||
strsplit(line, tokens, ' ');
|
||||
if (tokens.size() != 3)
|
||||
return -1;
|
||||
if (!is_http_method(tokens[0]))
|
||||
return -1;
|
||||
if (!is_http_version(tokens[2]))
|
||||
return -1;
|
||||
if (!url.parse(tokens[1]))
|
||||
return -1;
|
||||
/* all ok */
|
||||
method = tokens[0];
|
||||
uri = tokens[1];
|
||||
version = tokens[2];
|
||||
expect = HEADER_LINE;
|
||||
} else {
|
||||
std::string line = str.substr(pos, eol - pos);
|
||||
if (!parse_header_line(line, headers))
|
||||
return -1;
|
||||
}
|
||||
pos = eol + strlen(CRLF);
|
||||
if (pos >= eoh)
|
||||
break;
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
bool HTTPRes::is_chunked() {
|
||||
auto it = headers.find("Transfer-Encoding");
|
||||
if (it == headers.end())
|
||||
return false;
|
||||
if (it->second.find("chunked") == std::string::npos)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HTTPRes::is_gzipped() {
|
||||
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! */
|
||||
return false;
|
||||
}
|
||||
|
||||
long int HTTPMsg::content_length() {
|
||||
unsigned long int length = 0;
|
||||
auto it = headers.find("Content-Length");
|
||||
if (it == headers.end())
|
||||
return -1;
|
||||
errno = 0;
|
||||
length = std::strtoul(it->second.c_str(), (char **) NULL, 10);
|
||||
if (errno != 0)
|
||||
return -1;
|
||||
return length;
|
||||
}
|
||||
|
||||
int HTTPRes::parse(const char *buf, size_t len) {
|
||||
std::string str(buf, len);
|
||||
return parse(str);
|
||||
}
|
||||
|
||||
int HTTPRes::parse(const std::string& str) {
|
||||
enum { RES_LINE, HEADER_LINE } expect = RES_LINE;
|
||||
std::size_t eoh = str.find(HTTP_EOH); /* request head size */
|
||||
std::size_t eol = 0, pos = 0;
|
||||
|
||||
if (eoh == std::string::npos)
|
||||
return 0; /* str not contains complete request */
|
||||
|
||||
while ((eol = str.find(CRLF, pos)) != std::string::npos) {
|
||||
if (expect == RES_LINE) {
|
||||
std::string line = str.substr(pos, eol - pos);
|
||||
std::vector<std::string> tokens;
|
||||
strsplit(line, tokens, ' ', 3);
|
||||
if (tokens.size() != 3)
|
||||
return -1;
|
||||
if (!is_http_version(tokens[0]))
|
||||
return -1;
|
||||
code = atoi(tokens[1].c_str());
|
||||
if (code < 100 || code >= 600)
|
||||
return -1;
|
||||
/* all ok */
|
||||
version = tokens[0];
|
||||
status = tokens[2];
|
||||
expect = HEADER_LINE;
|
||||
} else {
|
||||
std::string line = str.substr(pos, eol - pos);
|
||||
if (!parse_header_line(line, headers))
|
||||
return -1;
|
||||
}
|
||||
pos = eol + strlen(CRLF);
|
||||
if (pos >= eoh)
|
||||
break;
|
||||
}
|
||||
|
||||
return eoh + strlen(HTTP_EOH);
|
||||
}
|
||||
|
||||
std::string HTTPRes::to_string() {
|
||||
if (version == "HTTP/1.1" && headers.count("Date") == 0) {
|
||||
std::string date;
|
||||
gen_rfc1123_date(date);
|
||||
add_header("Date", date.c_str());
|
||||
}
|
||||
if (status == "OK" && code != 200)
|
||||
status = HTTPCodeToStatus(code); // update
|
||||
if (body.length() > 0 && headers.count("Content-Length") == 0)
|
||||
add_header("Content-Length", std::to_string(body.length()).c_str());
|
||||
/* build response */
|
||||
std::stringstream ss;
|
||||
ss << version << " " << code << " " << status << CRLF;
|
||||
for (auto & h : headers) {
|
||||
ss << h.first << ": " << h.second << CRLF;
|
||||
}
|
||||
ss << CRLF;
|
||||
if (body.length() > 0)
|
||||
ss << body;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
const char * HTTPCodeToStatus(int code) {
|
||||
const char *ptr;
|
||||
switch (code) {
|
||||
case 105: ptr = "Name Not Resolved"; break;
|
||||
/* success */
|
||||
case 200: ptr = "OK"; break;
|
||||
case 206: ptr = "Partial Content"; break;
|
||||
/* redirect */
|
||||
case 301: ptr = "Moved Permanently"; break;
|
||||
case 302: ptr = "Found"; break;
|
||||
case 304: ptr = "Not Modified"; break;
|
||||
case 307: ptr = "Temporary Redirect"; break;
|
||||
/* client error */
|
||||
case 400: ptr = "Bad Request"; break;
|
||||
case 401: ptr = "Unauthorized"; break;
|
||||
case 403: ptr = "Forbidden"; break;
|
||||
case 404: ptr = "Not Found"; break;
|
||||
case 407: ptr = "Proxy Authentication Required"; break;
|
||||
case 408: ptr = "Request Timeout"; break;
|
||||
/* server error */
|
||||
case 500: ptr = "Internal Server Error"; break;
|
||||
case 502: ptr = "Bad Gateway"; break;
|
||||
case 503: ptr = "Not Implemented"; break;
|
||||
case 504: ptr = "Gateway Timeout"; break;
|
||||
default: ptr = "Unknown Status"; break;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
std::string UrlDecode(const std::string& data, bool allow_null) {
|
||||
std::string decoded(data);
|
||||
size_t pos = 0;
|
||||
while ((pos = decoded.find('%', pos)) != std::string::npos) {
|
||||
char c = strtol(decoded.substr(pos + 1, 2).c_str(), NULL, 16);
|
||||
if (c == '\0' && !allow_null) {
|
||||
pos += 3;
|
||||
continue;
|
||||
}
|
||||
decoded.replace(pos, 3, 1, c);
|
||||
pos++;
|
||||
}
|
||||
return decoded;
|
||||
}
|
||||
|
||||
bool MergeChunkedResponse (std::istream& in, std::ostream& out) {
|
||||
std::string hexLen;
|
||||
while (!in.eof ()) {
|
||||
std::getline (in, hexLen);
|
||||
errno = 0;
|
||||
long int len = strtoul(hexLen.c_str(), (char **) NULL, 16);
|
||||
if (errno != 0)
|
||||
return false; /* conversion error */
|
||||
if (len == 0)
|
||||
return true; /* end of stream */
|
||||
if (len < 0 || len > 10 * 1024 * 1024) /* < 10Mb */
|
||||
return false; /* too large chunk */
|
||||
char * buf = new char[len];
|
||||
in.read (buf, len);
|
||||
out.write (buf, len);
|
||||
delete[] buf;
|
||||
std::getline (in, hexLen); // read \r\n after chunk
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // http
|
||||
} // i2p
|
||||
151
HTTP.h
Normal file
151
HTTP.h
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016, 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
|
||||
*/
|
||||
|
||||
#ifndef HTTP_H__
|
||||
#define HTTP_H__
|
||||
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace i2p {
|
||||
namespace http {
|
||||
const char CRLF[] = "\r\n"; /**< HTTP line terminator */
|
||||
const char HTTP_EOH[] = "\r\n\r\n"; /**< HTTP end-of-headers mark */
|
||||
extern const std::vector<std::string> HTTP_METHODS; /**< list of valid HTTP methods */
|
||||
extern const std::vector<std::string> HTTP_VERSIONS; /**< list of valid HTTP versions */
|
||||
|
||||
struct URL {
|
||||
std::string schema;
|
||||
std::string user;
|
||||
std::string pass;
|
||||
std::string host;
|
||||
unsigned short int port;
|
||||
std::string path;
|
||||
std::string query;
|
||||
std::string frag;
|
||||
|
||||
URL(): schema(""), user(""), pass(""), host(""), port(0), path(""), query(""), frag("") {};
|
||||
|
||||
/**
|
||||
* @brief Tries to parse url from string
|
||||
* @return true on success, false on invalid url
|
||||
*/
|
||||
bool parse (const char *str, std::size_t len = 0);
|
||||
bool parse (const std::string& url);
|
||||
|
||||
/**
|
||||
* @brief Parse query part of url to key/value map
|
||||
* @note Honestly, this should be implemented with std::multimap
|
||||
*/
|
||||
bool parse_query(std::map<std::string, std::string> & params);
|
||||
|
||||
/**
|
||||
* @brief Serialize URL structure to url
|
||||
* @note Returns relative url if schema if empty, absolute url otherwise
|
||||
*/
|
||||
std::string to_string ();
|
||||
};
|
||||
|
||||
struct HTTPMsg {
|
||||
std::map<std::string, std::string> headers;
|
||||
|
||||
void add_header(const char *name, std::string & value, bool replace = false);
|
||||
void add_header(const char *name, const char *value, bool replace = false);
|
||||
void del_header(const char *name);
|
||||
|
||||
/** @brief Returns declared message length or -1 if unknown */
|
||||
long int content_length();
|
||||
};
|
||||
|
||||
struct HTTPReq : HTTPMsg {
|
||||
std::string version;
|
||||
std::string method;
|
||||
std::string uri;
|
||||
|
||||
HTTPReq (): version("HTTP/1.0"), method("GET"), uri("/") {};
|
||||
|
||||
/**
|
||||
* @brief Tries to parse HTTP request from string
|
||||
* @return -1 on error, 0 on incomplete query, >0 on success
|
||||
* @note Positive return value is a size of header
|
||||
*/
|
||||
int parse(const char *buf, size_t len);
|
||||
int parse(const std::string& buf);
|
||||
|
||||
/** @brief Serialize HTTP request to string */
|
||||
std::string to_string();
|
||||
};
|
||||
|
||||
struct HTTPRes : HTTPMsg {
|
||||
std::string version;
|
||||
std::string status;
|
||||
unsigned short int code;
|
||||
/**
|
||||
* @brief Simplifies response generation
|
||||
*
|
||||
* If this variable is set, on @a to_string() call:
|
||||
* * Content-Length header will be added if missing,
|
||||
* * contents of @a body will be included in generated response
|
||||
*/
|
||||
std::string body;
|
||||
|
||||
HTTPRes (): version("HTTP/1.1"), status("OK"), code(200) {}
|
||||
|
||||
/**
|
||||
* @brief Tries to parse HTTP response from string
|
||||
* @return -1 on error, 0 on incomplete query, >0 on success
|
||||
* @note Positive return value is a size of header
|
||||
*/
|
||||
int parse(const char *buf, size_t len);
|
||||
int parse(const std::string& buf);
|
||||
|
||||
/**
|
||||
* @brief Serialize HTTP response to string
|
||||
* @note If @a version is set to HTTP/1.1, and Date header is missing,
|
||||
* it will be generated based on current time and added to headers
|
||||
* @note If @a body is set and Content-Length header is missing,
|
||||
* this header will be added, based on body's length
|
||||
*/
|
||||
std::string to_string();
|
||||
|
||||
/** @brief Checks that response declared as chunked data */
|
||||
bool is_chunked();
|
||||
|
||||
/** @brief Checks that response contains compressed data */
|
||||
bool is_gzipped();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief returns HTTP status string by integer code
|
||||
* @param code HTTP code [100, 599]
|
||||
* @return Immutable string with status
|
||||
*/
|
||||
const char * HTTPCodeToStatus(int code);
|
||||
|
||||
/**
|
||||
* @brief Replaces %-encoded characters in string with their values
|
||||
* @param data Source string
|
||||
* @param null If set to true - decode also %00 sequence, otherwise - skip
|
||||
* @return Decoded string
|
||||
*/
|
||||
std::string UrlDecode(const std::string& data, bool null = false);
|
||||
|
||||
/**
|
||||
* @brief Merge HTTP response content with Transfer-Encoding: chunked
|
||||
* @param in Input stream
|
||||
* @param out Output stream
|
||||
* @return true on success, false otherwise
|
||||
*/
|
||||
bool MergeChunkedResponse (std::istream& in, std::ostream& out);
|
||||
} // http
|
||||
} // i2p
|
||||
|
||||
#endif /* HTTP_H__ */
|
||||
475
HTTPProxy.cpp
475
HTTPProxy.cpp
@@ -1,9 +1,14 @@
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <boost/asio.hpp>
|
||||
#include <mutex>
|
||||
|
||||
#include "I2PService.h"
|
||||
#include "Destination.h"
|
||||
#include "HTTPProxy.h"
|
||||
#include "util.h"
|
||||
#include "Identity.h"
|
||||
@@ -12,268 +17,320 @@
|
||||
#include "ClientContext.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "I2PTunnel.h"
|
||||
#include "Config.h"
|
||||
#include "HTTP.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace proxy
|
||||
{
|
||||
static const size_t http_buffer_size = 8192;
|
||||
class HTTPProxyHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this<HTTPProxyHandler> {
|
||||
namespace i2p {
|
||||
namespace proxy {
|
||||
std::map<std::string, std::string> jumpservices = {
|
||||
{ "inr.i2p", "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/search/?q=" },
|
||||
{ "stats.i2p", "http://7tbay5p4kzeekxvyvbf6v7eauazemsnnl2aoyqhg5jzpr5eke7tq.b32.i2p/cgi-bin/jump.cgi?a=" },
|
||||
};
|
||||
|
||||
static const char *pageHead =
|
||||
"<head>\r\n"
|
||||
" <title>I2P HTTP proxy: error</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"
|
||||
" </style>\r\n"
|
||||
"</head>\r\n"
|
||||
;
|
||||
|
||||
bool str_rmatch(std::string & str, const char *suffix) {
|
||||
auto pos = str.rfind (suffix);
|
||||
if (pos == std::string::npos)
|
||||
return false; /* not found */
|
||||
if (str.length() == (pos + std::strlen(suffix)))
|
||||
return true; /* match */
|
||||
return false;
|
||||
}
|
||||
|
||||
class HTTPReqHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this<HTTPReqHandler>
|
||||
{
|
||||
private:
|
||||
enum state {
|
||||
GET_METHOD,
|
||||
GET_HOSTNAME,
|
||||
GET_HTTPV,
|
||||
GET_HTTPVNL, //TODO: fallback to finding HOst: header if needed
|
||||
DONE
|
||||
};
|
||||
|
||||
void EnterState(state nstate);
|
||||
bool HandleData(uint8_t *http_buff, std::size_t len);
|
||||
bool HandleRequest();
|
||||
void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
|
||||
void Terminate();
|
||||
void AsyncSockRead();
|
||||
void HTTPRequestFailed(/*std::string message*/);
|
||||
void ExtractRequest();
|
||||
bool ValidateHTTPRequest();
|
||||
void HandleJumpServices();
|
||||
bool CreateHTTPRequest(uint8_t *http_buff, std::size_t len);
|
||||
bool ExtractAddressHelper(i2p::http::URL & url, std::string & b64);
|
||||
void SanitizeHTTPRequest(i2p::http::HTTPReq & req);
|
||||
void SentHTTPFailed(const boost::system::error_code & ecode);
|
||||
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
|
||||
/* error helpers */
|
||||
void GenericProxyError(const char *title, const char *description);
|
||||
void HostNotFound(std::string & host);
|
||||
void SendProxyError(std::string & content);
|
||||
|
||||
uint8_t m_http_buff[http_buffer_size];
|
||||
boost::asio::ip::tcp::socket * m_sock;
|
||||
std::string m_request; //Data left to be sent
|
||||
std::string m_url; //URL
|
||||
std::string m_method; //Method
|
||||
std::string m_version; //HTTP version
|
||||
std::string m_address; //Address
|
||||
std::string m_path; //Path
|
||||
int m_port; //Port
|
||||
state m_state;//Parsing state
|
||||
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;
|
||||
|
||||
public:
|
||||
HTTPProxyHandler(HTTPProxyServer * parent, boost::asio::ip::tcp::socket * sock) :
|
||||
I2PServiceHandler(parent), m_sock(sock)
|
||||
{ EnterState(GET_METHOD); }
|
||||
~HTTPProxyHandler() { Terminate(); }
|
||||
void Handle () { AsyncSockRead(); }
|
||||
|
||||
HTTPReqHandler(HTTPProxy * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock) :
|
||||
I2PServiceHandler(parent), m_sock(sock) {}
|
||||
~HTTPReqHandler() { Terminate(); }
|
||||
void Handle () { AsyncSockRead(); } /* overload */
|
||||
};
|
||||
|
||||
void HTTPProxyHandler::AsyncSockRead()
|
||||
void HTTPReqHandler::AsyncSockRead()
|
||||
{
|
||||
LogPrint(eLogDebug,"--- HTTP Proxy async sock read");
|
||||
if(m_sock) {
|
||||
m_sock->async_receive(boost::asio::buffer(m_http_buff, http_buffer_size),
|
||||
std::bind(&HTTPProxyHandler::HandleSockRecv, shared_from_this(),
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
} else {
|
||||
LogPrint(eLogError,"--- HTTP Proxy no socket for read");
|
||||
LogPrint(eLogDebug, "HTTPProxy: async sock read");
|
||||
if (!m_sock) {
|
||||
LogPrint(eLogError, "HTTPProxy: no socket for read");
|
||||
return;
|
||||
}
|
||||
m_sock->async_read_some(boost::asio::buffer(m_recv_chunk, sizeof(m_recv_chunk)),
|
||||
std::bind(&HTTPReqHandler::HandleSockRecv, shared_from_this(),
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void HTTPProxyHandler::Terminate() {
|
||||
void HTTPReqHandler::Terminate() {
|
||||
if (Kill()) return;
|
||||
if (m_sock) {
|
||||
LogPrint(eLogDebug,"--- HTTP Proxy close sock");
|
||||
if (m_sock)
|
||||
{
|
||||
LogPrint(eLogDebug, "HTTPProxy: close sock");
|
||||
m_sock->close();
|
||||
delete m_sock;
|
||||
m_sock = nullptr;
|
||||
}
|
||||
Done(shared_from_this());
|
||||
}
|
||||
|
||||
/* All hope is lost beyond this point */
|
||||
//TODO: handle this apropriately
|
||||
void HTTPProxyHandler::HTTPRequestFailed(/*HTTPProxyHandler::errTypes error*/)
|
||||
{
|
||||
std::string response = "HTTP/1.0 500 Internal Server Error\r\nContent-type: text/html\r\nContent-length: 0\r\n";
|
||||
boost::asio::async_write(*m_sock, boost::asio::buffer(response,response.size()),
|
||||
std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
|
||||
void HTTPReqHandler::GenericProxyError(const char *title, const char *description) {
|
||||
std::stringstream ss;
|
||||
ss << "<h1>Proxy error: " << title << "</h1>\r\n";
|
||||
ss << "<p>" << description << "</p>\r\n";
|
||||
std::string content = ss.str();
|
||||
SendProxyError(content);
|
||||
}
|
||||
|
||||
void HTTPProxyHandler::EnterState(HTTPProxyHandler::state nstate) {
|
||||
m_state = nstate;
|
||||
}
|
||||
|
||||
void HTTPProxyHandler::ExtractRequest()
|
||||
{
|
||||
LogPrint(eLogDebug,"--- HTTP Proxy method is: ", m_method, "\nRequest is: ", m_url);
|
||||
std::string server="";
|
||||
std::string port="80";
|
||||
boost::regex rHTTP("http://(.*?)(:(\\d+))?(/.*)");
|
||||
boost::smatch m;
|
||||
std::string path;
|
||||
if(boost::regex_search(m_url, m, rHTTP, boost::match_extra)) {
|
||||
server=m[1].str();
|
||||
if(m[2].str() != "") {
|
||||
port=m[3].str();
|
||||
}
|
||||
path=m[4].str();
|
||||
void HTTPReqHandler::HostNotFound(std::string & host) {
|
||||
std::stringstream ss;
|
||||
ss << "<h1>Proxy error: Host not found</h1>\r\n"
|
||||
<< "<p>Remote host not found in router's addressbook</p>\r\n"
|
||||
<< "<p>You may try to find this host on jumpservices below:</p>\r\n"
|
||||
<< "<ul>\r\n";
|
||||
for (const auto& js : jumpservices) {
|
||||
ss << " <li><a href=\"" << js.second << host << "\">" << js.first << "</a></li>\r\n";
|
||||
}
|
||||
LogPrint(eLogDebug,"--- HTTP Proxy server is: ",server, " port is: ", port, "\n path is: ",path);
|
||||
m_address = server;
|
||||
m_port = boost::lexical_cast<int>(port);
|
||||
m_path = path;
|
||||
ss << "</ul>\r\n";
|
||||
std::string content = ss.str();
|
||||
SendProxyError(content);
|
||||
}
|
||||
|
||||
bool HTTPProxyHandler::ValidateHTTPRequest() {
|
||||
if ( m_version != "HTTP/1.0" && m_version != "HTTP/1.1" ) {
|
||||
LogPrint(eLogError,"--- HTTP Proxy unsupported version: ", m_version);
|
||||
HTTPRequestFailed(); //TODO: send right stuff
|
||||
void HTTPReqHandler::SendProxyError(std::string & content)
|
||||
{
|
||||
i2p::http::HTTPRes res;
|
||||
res.code = 500;
|
||||
res.add_header("Content-Type", "text/html; charset=UTF-8");
|
||||
res.add_header("Connection", "close");
|
||||
std::stringstream ss;
|
||||
ss << "<html>\r\n" << pageHead
|
||||
<< "<body>" << content << "</body>\r\n"
|
||||
<< "</html>\r\n";
|
||||
res.body = ss.str();
|
||||
std::string response = res.to_string();
|
||||
boost::asio::async_write(*m_sock, boost::asio::buffer(response),
|
||||
std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
|
||||
}
|
||||
|
||||
bool HTTPReqHandler::ExtractAddressHelper(i2p::http::URL & url, std::string & b64)
|
||||
{
|
||||
const char *param = "i2paddresshelper=";
|
||||
std::size_t pos = url.query.find(param);
|
||||
std::size_t len = std::strlen(param);
|
||||
std::map<std::string, std::string> params;
|
||||
|
||||
if (pos == std::string::npos)
|
||||
return false; /* not found */
|
||||
if (!url.parse_query(params))
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string value = params["i2paddresshelper"];
|
||||
len += value.length();
|
||||
b64 = i2p::http::UrlDecode(value);
|
||||
url.query.replace(pos, len, "");
|
||||
return true;
|
||||
}
|
||||
|
||||
void HTTPProxyHandler::HandleJumpServices() {
|
||||
static const char * helpermark1 = "?i2paddresshelper=";
|
||||
static const char * helpermark2 = "&i2paddresshelper=";
|
||||
size_t addressHelperPos1 = m_path.rfind (helpermark1);
|
||||
size_t addressHelperPos2 = m_path.rfind (helpermark2);
|
||||
size_t addressHelperPos;
|
||||
if (addressHelperPos1 == std::string::npos)
|
||||
{
|
||||
if (addressHelperPos2 == std::string::npos)
|
||||
return; //Not a jump service
|
||||
else
|
||||
addressHelperPos = addressHelperPos2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (addressHelperPos2 == std::string::npos)
|
||||
addressHelperPos = addressHelperPos1;
|
||||
else if ( addressHelperPos1 > addressHelperPos2 )
|
||||
addressHelperPos = addressHelperPos1;
|
||||
else
|
||||
addressHelperPos = addressHelperPos2;
|
||||
}
|
||||
auto base64 = m_path.substr (addressHelperPos + strlen(helpermark1));
|
||||
base64 = i2p::util::http::urlDecode(base64); //Some of the symbols may be urlencoded
|
||||
LogPrint (eLogDebug,"Jump service for ", m_address, " found at ", base64, ". Inserting to address book");
|
||||
//TODO: this is very dangerous and broken. We should ask the user before doing anything see http://pastethis.i2p/raw/pn5fL4YNJL7OSWj3Sc6N/
|
||||
//TODO: we could redirect the user again to avoid dirtiness in the browser
|
||||
i2p::client::context.GetAddressBook ().InsertAddress (m_address, base64);
|
||||
m_path.erase(addressHelperPos);
|
||||
}
|
||||
|
||||
bool HTTPProxyHandler::CreateHTTPRequest(uint8_t *http_buff, std::size_t len) {
|
||||
ExtractRequest(); //TODO: parse earlier
|
||||
if (!ValidateHTTPRequest()) return false;
|
||||
HandleJumpServices();
|
||||
m_request = m_method;
|
||||
m_request.push_back(' ');
|
||||
m_request += m_path;
|
||||
m_request.push_back(' ');
|
||||
m_request += m_version;
|
||||
m_request.push_back('\r');
|
||||
m_request.push_back('\n');
|
||||
m_request.append("Connection: close\r\n");
|
||||
m_request.append(reinterpret_cast<const char *>(http_buff),len);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HTTPProxyHandler::HandleData(uint8_t *http_buff, std::size_t len)
|
||||
void HTTPReqHandler::SanitizeHTTPRequest(i2p::http::HTTPReq & req)
|
||||
{
|
||||
assert(len); // This should always be called with a least a byte left to parse
|
||||
while (len > 0) {
|
||||
//TODO: fallback to finding HOst: header if needed
|
||||
switch (m_state) {
|
||||
case GET_METHOD:
|
||||
switch (*http_buff) {
|
||||
case ' ': EnterState(GET_HOSTNAME); break;
|
||||
default: m_method.push_back(*http_buff); break;
|
||||
}
|
||||
break;
|
||||
case GET_HOSTNAME:
|
||||
switch (*http_buff) {
|
||||
case ' ': EnterState(GET_HTTPV); break;
|
||||
default: m_url.push_back(*http_buff); break;
|
||||
}
|
||||
break;
|
||||
case GET_HTTPV:
|
||||
switch (*http_buff) {
|
||||
case '\r': EnterState(GET_HTTPVNL); break;
|
||||
default: m_version.push_back(*http_buff); break;
|
||||
}
|
||||
break;
|
||||
case GET_HTTPVNL:
|
||||
switch (*http_buff) {
|
||||
case '\n': EnterState(DONE); break;
|
||||
default:
|
||||
LogPrint(eLogError,"--- HTTP Proxy rejected invalid request ending with: ", ((int)*http_buff));
|
||||
HTTPRequestFailed(); //TODO: add correct code
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LogPrint(eLogError,"--- HTTP Proxy invalid state: ", m_state);
|
||||
HTTPRequestFailed(); //TODO: add correct code 500
|
||||
return false;
|
||||
/* drop common headers */
|
||||
req.del_header("Referer");
|
||||
req.del_header("Via");
|
||||
req.del_header("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 */
|
||||
}
|
||||
http_buff++;
|
||||
len--;
|
||||
if (m_state == DONE)
|
||||
return CreateHTTPRequest(http_buff,len);
|
||||
}
|
||||
for (const auto& header : toErase) {
|
||||
req.headers.erase(header);
|
||||
}
|
||||
/* 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 */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Try to parse request from @a m_recv_buf
|
||||
* If parsing success, rebuild request and store to @a m_send_buf
|
||||
* with remaining data tail
|
||||
* @return true on processed request or false if more data needed
|
||||
*/
|
||||
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);
|
||||
|
||||
if (req_len == 0)
|
||||
return false; /* need more data */
|
||||
|
||||
if (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);
|
||||
|
||||
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();
|
||||
std::stringstream ss;
|
||||
ss << "Host " << url.host << " added to router's addressbook from helper. "
|
||||
<< "Click <a href=\"" << full_url << "\">here</a> to proceed.";
|
||||
GenericProxyError("Addresshelper found", ss.str().c_str());
|
||||
return true; /* request processed */
|
||||
}
|
||||
|
||||
SanitizeHTTPRequest(req);
|
||||
|
||||
std::string dest_host = url.host;
|
||||
uint16_t dest_port = url.port;
|
||||
/* always set port, even if missing in request */
|
||||
if (!dest_port) {
|
||||
dest_port = (url.schema == "https") ? 443 : 80;
|
||||
}
|
||||
/* detect dest_host, set proper 'Host' header in upstream request */
|
||||
auto h = req.headers.find("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;
|
||||
}
|
||||
|
||||
/* check dest_host really exists and inside I2P network */
|
||||
i2p::data::IdentHash identHash;
|
||||
if (str_rmatch(dest_host, ".i2p")) {
|
||||
if (!i2p::client::context.GetAddressBook ().GetIdentHash (dest_host, identHash)) {
|
||||
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());
|
||||
return true;
|
||||
}
|
||||
|
||||
/* make relative url */
|
||||
url.schema = "";
|
||||
url.host = "";
|
||||
req.uri = url.to_string();
|
||||
|
||||
/* drop original request from recv buffer */
|
||||
m_recv_buf.erase(0, req_len);
|
||||
/* build new buffer from modified request and data from original request */
|
||||
m_send_buf = req.to_string();
|
||||
m_send_buf.append(m_recv_buf);
|
||||
/* connect to destination */
|
||||
LogPrint(eLogDebug, "HTTPProxy: connecting to host ", dest_host, ":", dest_port);
|
||||
GetOwner()->CreateStream (std::bind (&HTTPReqHandler::HandleStreamRequestComplete,
|
||||
shared_from_this(), std::placeholders::_1), dest_host, dest_port);
|
||||
return true;
|
||||
}
|
||||
|
||||
void HTTPProxyHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len)
|
||||
/* will be called after some data received from client */
|
||||
void HTTPReqHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len)
|
||||
{
|
||||
LogPrint(eLogDebug,"--- HTTP Proxy sock recv: ", len);
|
||||
if(ecode) {
|
||||
LogPrint(eLogWarning," --- HTTP Proxy sock recv got error: ", ecode);
|
||||
Terminate();
|
||||
LogPrint(eLogDebug, "HTTPProxy: sock recv: ", len, " bytes, recv buf: ", m_recv_buf.length(), ", send buf: ", m_send_buf.length());
|
||||
if(ecode)
|
||||
{
|
||||
LogPrint(eLogWarning, "HTTPProxy: sock recv got error: ", ecode);
|
||||
Terminate();
|
||||
return;
|
||||
}
|
||||
|
||||
if (HandleData(m_http_buff, len)) {
|
||||
if (m_state == DONE) {
|
||||
LogPrint(eLogInfo,"--- HTTP Proxy requested: ", m_url);
|
||||
GetOwner()->CreateStream (std::bind (&HTTPProxyHandler::HandleStreamRequestComplete,
|
||||
shared_from_this(), std::placeholders::_1), m_address, m_port);
|
||||
} else {
|
||||
AsyncSockRead();
|
||||
}
|
||||
m_recv_buf.append(reinterpret_cast<const char *>(m_recv_chunk), len);
|
||||
if (HandleRequest()) {
|
||||
m_recv_buf.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
AsyncSockRead();
|
||||
}
|
||||
|
||||
void HTTPProxyHandler::SentHTTPFailed(const boost::system::error_code & ecode)
|
||||
void HTTPReqHandler::SentHTTPFailed(const boost::system::error_code & ecode)
|
||||
{
|
||||
if (!ecode) {
|
||||
Terminate();
|
||||
} else {
|
||||
LogPrint (eLogError,"--- HTTP Proxy Closing socket after sending failure because: ", ecode.message ());
|
||||
Terminate();
|
||||
}
|
||||
if (ecode)
|
||||
LogPrint (eLogError, "HTTPProxy: Closing socket after sending failure because: ", ecode.message ());
|
||||
Terminate();
|
||||
}
|
||||
|
||||
void HTTPProxyHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
|
||||
void HTTPReqHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
|
||||
{
|
||||
if (stream) {
|
||||
if (Kill()) return;
|
||||
LogPrint (eLogInfo,"--- HTTP Proxy New I2PTunnel connection");
|
||||
auto connection = std::make_shared<i2p::client::I2PTunnelConnection>(GetOwner(), m_sock, stream);
|
||||
GetOwner()->AddHandler (connection);
|
||||
connection->I2PConnect (reinterpret_cast<const uint8_t*>(m_request.data()), m_request.size());
|
||||
Done(shared_from_this());
|
||||
} else {
|
||||
LogPrint (eLogError,"--- HTTP Proxy Issue when creating the stream, check the previous warnings for more info.");
|
||||
HTTPRequestFailed(); // TODO: Send correct error message host unreachable
|
||||
if (!stream) {
|
||||
LogPrint (eLogError, "HTTPProxy: error when creating the stream, check the previous warnings for more info");
|
||||
GenericProxyError("Host is down", "Can't create connection to requested host, it may be down");
|
||||
return;
|
||||
}
|
||||
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);
|
||||
GetOwner()->AddHandler (connection);
|
||||
connection->I2PConnect (reinterpret_cast<const uint8_t*>(m_send_buf.data()), m_send_buf.length());
|
||||
Done (shared_from_this());
|
||||
}
|
||||
|
||||
HTTPProxyServer::HTTPProxyServer(int port):
|
||||
TCPIPAcceptor(port, i2p::client::context.GetSharedLocalDestination ())
|
||||
HTTPProxy::HTTPProxy(const std::string& address, int port, std::shared_ptr<i2p::client::ClientDestination> localDestination):
|
||||
TCPIPAcceptor(address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ())
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<i2p::client::I2PServiceHandler> HTTPProxyServer::CreateHandler(boost::asio::ip::tcp::socket * socket)
|
||||
std::shared_ptr<i2p::client::I2PServiceHandler> HTTPProxy::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
|
||||
{
|
||||
return std::make_shared<HTTPProxyHandler> (this, socket);
|
||||
return std::make_shared<HTTPReqHandler> (this, socket);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} // http
|
||||
} // i2p
|
||||
|
||||
33
HTTPProxy.h
33
HTTPProxy.h
@@ -1,30 +1,21 @@
|
||||
#ifndef HTTP_PROXY_H__
|
||||
#define HTTP_PROXY_H__
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <boost/asio.hpp>
|
||||
#include <mutex>
|
||||
#include "I2PService.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace proxy
|
||||
{
|
||||
class HTTPProxyServer: public i2p::client::TCPIPAcceptor
|
||||
namespace i2p {
|
||||
namespace proxy {
|
||||
class HTTPProxy: public i2p::client::TCPIPAcceptor
|
||||
{
|
||||
public:
|
||||
|
||||
HTTPProxy(const std::string& address, int port, std::shared_ptr<i2p::client::ClientDestination> localDestination = nullptr);
|
||||
~HTTPProxy() {};
|
||||
|
||||
protected:
|
||||
// Implements TCPIPAcceptor
|
||||
std::shared_ptr<i2p::client::I2PServiceHandler> CreateHandler(boost::asio::ip::tcp::socket * socket);
|
||||
std::shared_ptr<i2p::client::I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket);
|
||||
const char* GetName() { return "HTTP Proxy"; }
|
||||
|
||||
public:
|
||||
HTTPProxyServer(int port);
|
||||
~HTTPProxyServer() {}
|
||||
};
|
||||
} // http
|
||||
} // i2p
|
||||
|
||||
typedef HTTPProxyServer HTTPProxy;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
1669
HTTPServer.cpp
1669
HTTPServer.cpp
File diff suppressed because it is too large
Load Diff
137
HTTPServer.h
137
HTTPServer.h
@@ -1,114 +1,47 @@
|
||||
#ifndef HTTP_SERVER_H__
|
||||
#define HTTP_SERVER_H__
|
||||
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/array.hpp>
|
||||
#include "LeaseSet.h"
|
||||
#include "Streaming.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
namespace i2p {
|
||||
namespace http {
|
||||
extern const char *itoopieFavicon;
|
||||
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
|
||||
const int HTTP_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds
|
||||
class HTTPConnection
|
||||
|
||||
class HTTPConnection: public std::enable_shared_from_this<HTTPConnection>
|
||||
{
|
||||
protected:
|
||||
|
||||
struct header
|
||||
{
|
||||
std::string name;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
struct request
|
||||
{
|
||||
std::string method;
|
||||
std::string uri;
|
||||
std::string host;
|
||||
int port;
|
||||
int http_version_major;
|
||||
int http_version_minor;
|
||||
std::vector<header> headers;
|
||||
};
|
||||
|
||||
struct reply
|
||||
{
|
||||
std::vector<header> headers;
|
||||
std::string content;
|
||||
|
||||
std::vector<boost::asio::const_buffer> to_buffers (int status);
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
HTTPConnection (boost::asio::ip::tcp::socket * socket):
|
||||
m_Socket (socket), m_Timer (socket->get_io_service ()),
|
||||
m_Stream (nullptr), m_BufferLen (0) { Receive (); };
|
||||
virtual ~HTTPConnection() { delete m_Socket; }
|
||||
HTTPConnection (std::shared_ptr<boost::asio::ip::tcp::socket> socket);
|
||||
void Receive ();
|
||||
|
||||
private:
|
||||
|
||||
void HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void Terminate (const boost::system::error_code& ecode);
|
||||
|
||||
void RunRequest ();
|
||||
bool CheckAuth (const HTTPReq & req);
|
||||
void HandleRequest (const HTTPReq & req);
|
||||
void HandlePage (const HTTPReq & req, HTTPRes & res, std::stringstream& data);
|
||||
void HandleCommand (const HTTPReq & req, HTTPRes & res, std::stringstream& data);
|
||||
void SendReply (HTTPRes & res, std::string & content);
|
||||
|
||||
private:
|
||||
|
||||
void Terminate ();
|
||||
void Receive ();
|
||||
void HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void AsyncStreamReceive ();
|
||||
void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleWriteReply(const boost::system::error_code& ecode);
|
||||
void HandleWrite (const boost::system::error_code& ecode);
|
||||
void SendReply (const std::string& content, int status = 200);
|
||||
|
||||
void HandleRequest (const std::string& address);
|
||||
void HandleCommand (const std::string& command, std::stringstream& s);
|
||||
void ShowTransports (std::stringstream& s);
|
||||
void ShowTunnels (std::stringstream& s);
|
||||
void ShowTransitTunnels (std::stringstream& s);
|
||||
void ShowLocalDestinations (std::stringstream& s);
|
||||
void ShowLocalDestination (const std::string& b32, std::stringstream& s);
|
||||
void ShowSAMSessions (std::stringstream& s);
|
||||
void ShowSAMSession (const std::string& id, std::stringstream& s);
|
||||
void StartAcceptingTunnels (std::stringstream& s);
|
||||
void StopAcceptingTunnels (std::stringstream& s);
|
||||
void FillContent (std::stringstream& s);
|
||||
std::string ExtractAddress ();
|
||||
void ExtractParams (const std::string& str, std::map<std::string, std::string>& params);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
boost::asio::ip::tcp::socket * m_Socket;
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
|
||||
boost::asio::deadline_timer m_Timer;
|
||||
std::shared_ptr<i2p::stream::Stream> m_Stream;
|
||||
char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1], m_StreamBuffer[HTTP_CONNECTION_BUFFER_SIZE + 1];
|
||||
char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1];
|
||||
size_t m_BufferLen;
|
||||
request m_Request;
|
||||
reply m_Reply;
|
||||
|
||||
protected:
|
||||
|
||||
virtual void RunRequest ();
|
||||
void HandleDestinationRequest(const std::string& address, const std::string& uri);
|
||||
void SendToAddress (const std::string& address, int port, const char * buf, size_t len);
|
||||
void HandleDestinationRequestTimeout (const boost::system::error_code& ecode,
|
||||
i2p::data::IdentHash destination, int port, const char * buf, size_t len);
|
||||
void SendToDestination (std::shared_ptr<const i2p::data::LeaseSet> remote, int port, const char * buf, size_t len);
|
||||
|
||||
public:
|
||||
|
||||
static const std::string itoopieImage;
|
||||
static const std::string itoopieFavicon;
|
||||
bool needAuth;
|
||||
std::string user;
|
||||
std::string pass;
|
||||
};
|
||||
|
||||
class HTTPServer
|
||||
{
|
||||
public:
|
||||
|
||||
HTTPServer (int port);
|
||||
virtual ~HTTPServer ();
|
||||
HTTPServer (const std::string& address, int port);
|
||||
~HTTPServer ();
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
@@ -117,22 +50,18 @@ namespace util
|
||||
|
||||
void Run ();
|
||||
void Accept ();
|
||||
void HandleAccept(const boost::system::error_code& ecode);
|
||||
void HandleAccept(const boost::system::error_code& ecode,
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> newSocket);
|
||||
void CreateConnection(std::shared_ptr<boost::asio::ip::tcp::socket> newSocket);
|
||||
|
||||
private:
|
||||
|
||||
std::thread * m_Thread;
|
||||
std::unique_ptr<std::thread> m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_service::work m_Work;
|
||||
boost::asio::ip::tcp::acceptor m_Acceptor;
|
||||
boost::asio::ip::tcp::socket * m_NewSocket;
|
||||
|
||||
protected:
|
||||
virtual void CreateConnection(boost::asio::ip::tcp::socket * m_NewSocket);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // http
|
||||
} // i2p
|
||||
|
||||
#endif /* HTTP_SERVER_H__ */
|
||||
|
||||
726
I2CP.cpp
Normal file
726
I2CP.cpp
Normal file
@@ -0,0 +1,726 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016, 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 <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <openssl/rand.h>
|
||||
#include "I2PEndian.h"
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
#include "LeaseSet.h"
|
||||
#include "ClientContext.h"
|
||||
#include "Transports.h"
|
||||
#include "Signature.h"
|
||||
#include "I2CP.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
|
||||
I2CPDestination::I2CPDestination (std::shared_ptr<I2CPSession> owner, std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, const std::map<std::string, std::string>& params):
|
||||
LeaseSetDestination (isPublic, ¶ms), m_Owner (owner), m_Identity (identity)
|
||||
{
|
||||
}
|
||||
|
||||
void I2CPDestination::SetEncryptionPrivateKey (const uint8_t * key)
|
||||
{
|
||||
memcpy (m_EncryptionPrivateKey, key, 256);
|
||||
}
|
||||
|
||||
void I2CPDestination::HandleDataMessage (const uint8_t * buf, size_t len)
|
||||
{
|
||||
uint32_t length = bufbe32toh (buf);
|
||||
if (length > len - 4) length = len - 4;
|
||||
m_Owner->SendMessagePayloadMessage (buf + 4, length);
|
||||
}
|
||||
|
||||
void I2CPDestination::CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels)
|
||||
{
|
||||
i2p::data::LocalLeaseSet ls (m_Identity, m_EncryptionPrivateKey, tunnels); // we don't care about encryption key
|
||||
m_LeaseSetExpirationTime = ls.GetExpirationTime ();
|
||||
uint8_t * leases = ls.GetLeases ();
|
||||
leases[-1] = tunnels.size ();
|
||||
htobe16buf (leases - 3, m_Owner->GetSessionID ());
|
||||
size_t l = 2/*sessionID*/ + 1/*num leases*/ + i2p::data::LEASE_SIZE*tunnels.size ();
|
||||
m_Owner->SendI2CPMessage (I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE, leases - 3, l);
|
||||
}
|
||||
|
||||
void I2CPDestination::LeaseSetCreated (const uint8_t * buf, size_t len)
|
||||
{
|
||||
auto ls = new i2p::data::LocalLeaseSet (m_Identity, buf, len);
|
||||
ls->SetExpirationTime (m_LeaseSetExpirationTime);
|
||||
SetLeaseSet (ls);
|
||||
}
|
||||
|
||||
void I2CPDestination::SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce)
|
||||
{
|
||||
auto msg = NewI2NPMessage ();
|
||||
uint8_t * buf = msg->GetPayload ();
|
||||
htobe32buf (buf, len);
|
||||
memcpy (buf + 4, payload, len);
|
||||
msg->len += len + 4;
|
||||
msg->FillI2NPMessageHeader (eI2NPData);
|
||||
auto remote = FindLeaseSet (ident);
|
||||
if (remote)
|
||||
GetService ().post (std::bind (&I2CPDestination::SendMsg, GetSharedFromThis (), msg, remote));
|
||||
else
|
||||
{
|
||||
auto s = GetSharedFromThis ();
|
||||
RequestDestination (ident,
|
||||
[s, msg, nonce](std::shared_ptr<i2p::data::LeaseSet> ls)
|
||||
{
|
||||
if (ls)
|
||||
{
|
||||
bool sent = s->SendMsg (msg, ls);
|
||||
s->m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure);
|
||||
}
|
||||
else
|
||||
s->m_Owner->SendMessageStatusMessage (nonce, eI2CPMessageStatusNoLeaseSet);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool I2CPDestination::SendMsg (std::shared_ptr<I2NPMessage> msg, std::shared_ptr<const i2p::data::LeaseSet> remote)
|
||||
{
|
||||
auto remoteSession = GetRoutingSession (remote, true);
|
||||
if (!remoteSession)
|
||||
{
|
||||
LogPrint (eLogError, "I2CP: Failed to create remote session");
|
||||
return false;
|
||||
}
|
||||
auto path = remoteSession->GetSharedRoutingPath ();
|
||||
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
|
||||
std::shared_ptr<const i2p::data::Lease> remoteLease;
|
||||
if (path)
|
||||
{
|
||||
if (!remoteSession->CleanupUnconfirmedTags ()) // no stuck tags
|
||||
{
|
||||
outboundTunnel = path->outboundTunnel;
|
||||
remoteLease = path->remoteLease;
|
||||
}
|
||||
else
|
||||
remoteSession->SetSharedRoutingPath (nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto outboundTunnel = GetTunnelPool ()->GetNextOutboundTunnel ();
|
||||
auto leases = remote->GetNonExpiredLeases ();
|
||||
if (!leases.empty ())
|
||||
remoteLease = leases[rand () % leases.size ()];
|
||||
if (remoteLease && outboundTunnel)
|
||||
remoteSession->SetSharedRoutingPath (std::make_shared<i2p::garlic::GarlicRoutingPath> (
|
||||
i2p::garlic::GarlicRoutingPath{outboundTunnel, remoteLease, 10000, 0, 0})); // 10 secs RTT
|
||||
else
|
||||
remoteSession->SetSharedRoutingPath (nullptr);
|
||||
}
|
||||
if (remoteLease && outboundTunnel)
|
||||
{
|
||||
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
|
||||
auto garlic = remoteSession->WrapSingleMessage (msg);
|
||||
msgs.push_back (i2p::tunnel::TunnelMessageBlock
|
||||
{
|
||||
i2p::tunnel::eDeliveryTypeTunnel,
|
||||
remoteLease->tunnelGateway, remoteLease->tunnelID,
|
||||
garlic
|
||||
});
|
||||
outboundTunnel->SendTunnelDataMsg (msgs);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (outboundTunnel)
|
||||
LogPrint (eLogWarning, "I2CP: Failed to send message. All leases expired");
|
||||
else
|
||||
LogPrint (eLogWarning, "I2CP: Failed to send message. No outbound tunnels");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr<proto::socket> socket):
|
||||
m_Owner (owner), m_Socket (socket), m_Payload (nullptr),
|
||||
m_SessionID (0xFFFF), m_MessageID (0), m_IsSendAccepted (true)
|
||||
{
|
||||
}
|
||||
|
||||
I2CPSession::~I2CPSession ()
|
||||
{
|
||||
delete[] m_Payload;
|
||||
}
|
||||
|
||||
void I2CPSession::Start ()
|
||||
{
|
||||
ReadProtocolByte ();
|
||||
}
|
||||
|
||||
void I2CPSession::Stop ()
|
||||
{
|
||||
Terminate ();
|
||||
}
|
||||
|
||||
void I2CPSession::ReadProtocolByte ()
|
||||
{
|
||||
if (m_Socket)
|
||||
{
|
||||
auto s = shared_from_this ();
|
||||
m_Socket->async_read_some (boost::asio::buffer (m_Header, 1),
|
||||
[s](const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (!ecode && bytes_transferred > 0 && s->m_Header[0] == I2CP_PROTOCOL_BYTE)
|
||||
s->ReceiveHeader ();
|
||||
else
|
||||
s->Terminate ();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void I2CPSession::ReceiveHeader ()
|
||||
{
|
||||
boost::asio::async_read (*m_Socket, boost::asio::buffer (m_Header, I2CP_HEADER_SIZE),
|
||||
boost::asio::transfer_all (),
|
||||
std::bind (&I2CPSession::HandleReceivedHeader, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void I2CPSession::HandleReceivedHeader (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (ecode)
|
||||
Terminate ();
|
||||
else
|
||||
{
|
||||
m_PayloadLen = bufbe32toh (m_Header + I2CP_HEADER_LENGTH_OFFSET);
|
||||
if (m_PayloadLen > 0)
|
||||
{
|
||||
m_Payload = new uint8_t[m_PayloadLen];
|
||||
ReceivePayload ();
|
||||
}
|
||||
else // no following payload
|
||||
{
|
||||
HandleMessage ();
|
||||
ReceiveHeader (); // next message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void I2CPSession::ReceivePayload ()
|
||||
{
|
||||
boost::asio::async_read (*m_Socket, boost::asio::buffer (m_Payload, m_PayloadLen),
|
||||
boost::asio::transfer_all (),
|
||||
std::bind (&I2CPSession::HandleReceivedPayload, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void I2CPSession::HandleReceivedPayload (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (ecode)
|
||||
Terminate ();
|
||||
else
|
||||
{
|
||||
HandleMessage ();
|
||||
delete[] m_Payload;
|
||||
m_Payload = nullptr;
|
||||
m_PayloadLen = 0;
|
||||
ReceiveHeader (); // next message
|
||||
}
|
||||
}
|
||||
|
||||
void I2CPSession::HandleMessage ()
|
||||
{
|
||||
auto handler = m_Owner.GetMessagesHandlers ()[m_Header[I2CP_HEADER_TYPE_OFFSET]];
|
||||
if (handler)
|
||||
(this->*handler)(m_Payload, m_PayloadLen);
|
||||
else
|
||||
LogPrint (eLogError, "I2CP: Unknown I2CP messsage ", (int)m_Header[I2CP_HEADER_TYPE_OFFSET]);
|
||||
}
|
||||
|
||||
void I2CPSession::Terminate ()
|
||||
{
|
||||
if (m_Destination)
|
||||
{
|
||||
m_Destination->Stop ();
|
||||
m_Destination = nullptr;
|
||||
}
|
||||
if (m_Socket)
|
||||
{
|
||||
m_Socket->close ();
|
||||
m_Socket = nullptr;
|
||||
}
|
||||
m_Owner.RemoveSession (GetSessionID ());
|
||||
LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " terminated");
|
||||
}
|
||||
|
||||
void I2CPSession::SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len)
|
||||
{
|
||||
auto socket = m_Socket;
|
||||
if (socket)
|
||||
{
|
||||
auto l = len + I2CP_HEADER_SIZE;
|
||||
uint8_t * buf = new uint8_t[l];
|
||||
htobe32buf (buf + I2CP_HEADER_LENGTH_OFFSET, len);
|
||||
buf[I2CP_HEADER_TYPE_OFFSET] = type;
|
||||
memcpy (buf + I2CP_HEADER_SIZE, payload, len);
|
||||
boost::asio::async_write (*socket, boost::asio::buffer (buf, l), boost::asio::transfer_all (),
|
||||
std::bind(&I2CPSession::HandleI2CPMessageSent, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2, buf));
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "I2CP: Can't write to the socket");
|
||||
}
|
||||
|
||||
void I2CPSession::HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, const uint8_t * buf)
|
||||
{
|
||||
delete[] buf;
|
||||
if (ecode && ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
|
||||
std::string I2CPSession::ExtractString (const uint8_t * buf, size_t len)
|
||||
{
|
||||
uint8_t l = buf[0];
|
||||
if (l > len) l = len;
|
||||
return std::string ((const char *)(buf + 1), l);
|
||||
}
|
||||
|
||||
size_t I2CPSession::PutString (uint8_t * buf, size_t len, const std::string& str)
|
||||
{
|
||||
auto l = str.length ();
|
||||
if (l + 1 >= len) l = len - 1;
|
||||
if (l > 255) l = 255; // 1 byte max
|
||||
buf[0] = l;
|
||||
memcpy (buf + 1, str.c_str (), l);
|
||||
return l + 1;
|
||||
}
|
||||
|
||||
void I2CPSession::ExtractMapping (const uint8_t * buf, size_t len, std::map<std::string, std::string>& mapping)
|
||||
// TODO: move to Base.cpp
|
||||
{
|
||||
size_t offset = 0;
|
||||
while (offset < len)
|
||||
{
|
||||
std::string param = ExtractString (buf + offset, len - offset);
|
||||
offset += param.length () + 1;
|
||||
if (buf[offset] != '=')
|
||||
{
|
||||
LogPrint (eLogWarning, "I2CP: Unexpected character ", buf[offset], " instead '=' after ", param);
|
||||
break;
|
||||
}
|
||||
offset++;
|
||||
|
||||
std::string value = ExtractString (buf + offset, len - offset);
|
||||
offset += value.length () + 1;
|
||||
if (buf[offset] != ';')
|
||||
{
|
||||
LogPrint (eLogWarning, "I2CP: Unexpected character ", buf[offset], " instead ';' after ", value);
|
||||
break;
|
||||
}
|
||||
offset++;
|
||||
mapping.insert (std::make_pair (param, value));
|
||||
}
|
||||
}
|
||||
|
||||
void I2CPSession::GetDateMessageHandler (const uint8_t * buf, size_t len)
|
||||
{
|
||||
// get version
|
||||
auto version = ExtractString (buf, len);
|
||||
auto l = version.length () + 1 + 8;
|
||||
uint8_t * payload = new uint8_t[l];
|
||||
// set date
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
htobe64buf (payload, ts);
|
||||
// echo vesrion back
|
||||
PutString (payload + 8, l - 8, version);
|
||||
SendI2CPMessage (I2CP_SET_DATE_MESSAGE, payload, l);
|
||||
delete[] payload;
|
||||
}
|
||||
|
||||
void I2CPSession::CreateSessionMessageHandler (const uint8_t * buf, size_t len)
|
||||
{
|
||||
RAND_bytes ((uint8_t *)&m_SessionID, 2);
|
||||
auto identity = std::make_shared<i2p::data::IdentityEx>();
|
||||
size_t offset = identity->FromBuffer (buf, len);
|
||||
if (!offset)
|
||||
{
|
||||
LogPrint (eLogError, "I2CP: create session maformed identity");
|
||||
SendSessionStatusMessage (3); // invalid
|
||||
return;
|
||||
}
|
||||
uint16_t optionsSize = bufbe16toh (buf + offset);
|
||||
offset += 2;
|
||||
if (optionsSize > len - offset)
|
||||
{
|
||||
LogPrint (eLogError, "I2CP: options size ", optionsSize, "exceeds message size");
|
||||
SendSessionStatusMessage (3); // invalid
|
||||
return;
|
||||
}
|
||||
std::map<std::string, std::string> params;
|
||||
ExtractMapping (buf + offset, optionsSize, params);
|
||||
offset += optionsSize; // options
|
||||
if (params[I2CP_PARAM_MESSAGE_RELIABILITY] == "none") m_IsSendAccepted = false;
|
||||
|
||||
offset += 8; // date
|
||||
if (identity->Verify (buf, offset, buf + offset)) // signature
|
||||
{
|
||||
bool isPublic = true;
|
||||
if (params[I2CP_PARAM_DONT_PUBLISH_LEASESET] == "true") isPublic = false;
|
||||
if (!m_Destination)
|
||||
{
|
||||
m_Destination = std::make_shared<I2CPDestination>(shared_from_this (), identity, isPublic, params);
|
||||
SendSessionStatusMessage (1); // created
|
||||
LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " created");
|
||||
m_Destination->Start ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "I2CP: session already exists");
|
||||
SendSessionStatusMessage (4); // refused
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "I2CP: create session signature verification falied");
|
||||
SendSessionStatusMessage (3); // invalid
|
||||
}
|
||||
}
|
||||
|
||||
void I2CPSession::DestroySessionMessageHandler (const uint8_t * buf, size_t len)
|
||||
{
|
||||
SendSessionStatusMessage (0); // destroy
|
||||
LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " destroyed");
|
||||
if (m_Destination)
|
||||
{
|
||||
m_Destination->Stop ();
|
||||
m_Destination = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void I2CPSession::ReconfigureSessionMessageHandler (const uint8_t * buf, size_t len)
|
||||
{
|
||||
// TODO: implement actual reconfiguration
|
||||
SendSessionStatusMessage (2); // updated
|
||||
}
|
||||
|
||||
void I2CPSession::SendSessionStatusMessage (uint8_t status)
|
||||
{
|
||||
uint8_t buf[3];
|
||||
htobe16buf (buf, m_SessionID);
|
||||
buf[2] = status;
|
||||
SendI2CPMessage (I2CP_SESSION_STATUS_MESSAGE, buf, 3);
|
||||
}
|
||||
|
||||
void I2CPSession::SendMessageStatusMessage (uint32_t nonce, I2CPMessageStatus status)
|
||||
{
|
||||
if (!nonce) return; // don't send status with zero nonce
|
||||
uint8_t buf[15];
|
||||
htobe16buf (buf, m_SessionID);
|
||||
htobe32buf (buf + 2, m_MessageID++);
|
||||
buf[6] = (uint8_t)status;
|
||||
memset (buf + 7, 0, 4); // size
|
||||
htobe32buf (buf + 11, nonce);
|
||||
SendI2CPMessage (I2CP_MESSAGE_STATUS_MESSAGE, buf, 15);
|
||||
}
|
||||
|
||||
void I2CPSession::CreateLeaseSetMessageHandler (const uint8_t * buf, size_t len)
|
||||
{
|
||||
uint16_t sessionID = bufbe16toh (buf);
|
||||
if (sessionID == m_SessionID)
|
||||
{
|
||||
size_t offset = 2;
|
||||
if (m_Destination)
|
||||
{
|
||||
offset += i2p::crypto::DSA_PRIVATE_KEY_LENGTH; // skip signing private key
|
||||
// we always assume this field as 20 bytes (DSA) regardless actual size
|
||||
// instead of
|
||||
//offset += m_Destination->GetIdentity ()->GetSigningPrivateKeyLen ();
|
||||
m_Destination->SetEncryptionPrivateKey (buf + offset);
|
||||
offset += 256;
|
||||
m_Destination->LeaseSetCreated (buf + offset, len - offset);
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "I2CP: unexpected sessionID ", sessionID);
|
||||
}
|
||||
|
||||
void I2CPSession::SendMessageMessageHandler (const uint8_t * buf, size_t len)
|
||||
{
|
||||
uint16_t sessionID = bufbe16toh (buf);
|
||||
if (sessionID == m_SessionID)
|
||||
{
|
||||
size_t offset = 2;
|
||||
if (m_Destination)
|
||||
{
|
||||
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");
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "I2CP: unexpected sessionID ", sessionID);
|
||||
}
|
||||
|
||||
void I2CPSession::SendMessageExpiresMessageHandler (const uint8_t * buf, size_t len)
|
||||
{
|
||||
SendMessageMessageHandler (buf, len - 8); // ignore flags(2) and expiration(6)
|
||||
}
|
||||
|
||||
void I2CPSession::HostLookupMessageHandler (const uint8_t * buf, size_t len)
|
||||
{
|
||||
uint16_t sessionID = bufbe16toh (buf);
|
||||
if (sessionID == m_SessionID || sessionID == 0xFFFF) // -1 means without session
|
||||
{
|
||||
uint32_t requestID = bufbe32toh (buf + 2);
|
||||
//uint32_t timeout = bufbe32toh (buf + 6);
|
||||
i2p::data::IdentHash ident;
|
||||
switch (buf[10])
|
||||
{
|
||||
case 0: // hash
|
||||
ident = i2p::data::IdentHash (buf + 11);
|
||||
break;
|
||||
case 1: // address
|
||||
{
|
||||
auto name = ExtractString (buf + 11, len - 11);
|
||||
if (!i2p::client::context.GetAddressBook ().GetIdentHash (name, ident))
|
||||
{
|
||||
LogPrint (eLogError, "I2CP: address ", name, " not found");
|
||||
SendHostReplyMessage (requestID, nullptr);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LogPrint (eLogError, "I2CP: request type ", (int)buf[10], " is not supported");
|
||||
SendHostReplyMessage (requestID, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<LeaseSetDestination> destination = m_Destination;
|
||||
if(!destination) destination = i2p::client::context.GetSharedLocalDestination ();
|
||||
if (destination)
|
||||
{
|
||||
auto ls = destination->FindLeaseSet (ident);
|
||||
if (ls)
|
||||
SendHostReplyMessage (requestID, ls->GetIdentity ());
|
||||
else
|
||||
{
|
||||
auto s = shared_from_this ();
|
||||
destination->RequestDestination (ident,
|
||||
[s, requestID](std::shared_ptr<i2p::data::LeaseSet> leaseSet)
|
||||
{
|
||||
s->SendHostReplyMessage (requestID, leaseSet ? leaseSet->GetIdentity () : nullptr);
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
SendHostReplyMessage (requestID, nullptr);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "I2CP: unexpected sessionID ", sessionID);
|
||||
}
|
||||
|
||||
void I2CPSession::SendHostReplyMessage (uint32_t requestID, std::shared_ptr<const i2p::data::IdentityEx> identity)
|
||||
{
|
||||
if (identity)
|
||||
{
|
||||
size_t l = identity->GetFullLen () + 7;
|
||||
uint8_t * buf = new uint8_t[l];
|
||||
htobe16buf (buf, m_SessionID);
|
||||
htobe32buf (buf + 2, requestID);
|
||||
buf[6] = 0; // result code
|
||||
identity->ToBuffer (buf + 7, l - 7);
|
||||
SendI2CPMessage (I2CP_HOST_REPLY_MESSAGE, buf, l);
|
||||
delete[] buf;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t buf[7];
|
||||
htobe16buf (buf, m_SessionID);
|
||||
htobe32buf (buf + 2, requestID);
|
||||
buf[6] = 1; // result code
|
||||
SendI2CPMessage (I2CP_HOST_REPLY_MESSAGE, buf, 7);
|
||||
}
|
||||
}
|
||||
|
||||
void I2CPSession::DestLookupMessageHandler (const uint8_t * buf, size_t len)
|
||||
{
|
||||
if (m_Destination)
|
||||
{
|
||||
auto ls = m_Destination->FindLeaseSet (buf);
|
||||
if (ls)
|
||||
{
|
||||
auto l = ls->GetIdentity ()->GetFullLen ();
|
||||
uint8_t * identBuf = new uint8_t[l];
|
||||
ls->GetIdentity ()->ToBuffer (identBuf, l);
|
||||
SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, identBuf, l);
|
||||
delete[] identBuf;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto s = shared_from_this ();
|
||||
i2p::data::IdentHash ident (buf);
|
||||
m_Destination->RequestDestination (ident,
|
||||
[s, ident](std::shared_ptr<i2p::data::LeaseSet> leaseSet)
|
||||
{
|
||||
if (leaseSet) // found
|
||||
{
|
||||
auto l = leaseSet->GetIdentity ()->GetFullLen ();
|
||||
uint8_t * identBuf = new uint8_t[l];
|
||||
leaseSet->GetIdentity ()->ToBuffer (identBuf, l);
|
||||
s->SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, identBuf, l);
|
||||
delete[] identBuf;
|
||||
}
|
||||
else
|
||||
s->SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, ident, 32); // not found
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, buf, 32);
|
||||
}
|
||||
|
||||
void I2CPSession::GetBandwidthLimitsMessageHandler (const uint8_t * buf, size_t len)
|
||||
{
|
||||
uint8_t limits[64];
|
||||
memset (limits, 0, 64);
|
||||
htobe32buf (limits, i2p::transport::transports.GetInBandwidth ()); // inbound
|
||||
htobe32buf (limits + 4, i2p::transport::transports.GetOutBandwidth ()); // outbound
|
||||
SendI2CPMessage (I2CP_BANDWIDTH_LIMITS_MESSAGE, limits, 64);
|
||||
}
|
||||
|
||||
void I2CPSession::SendMessagePayloadMessage (const uint8_t * payload, size_t len)
|
||||
{
|
||||
// we don't use SendI2CPMessage to eliminate additional copy
|
||||
auto l = len + 10 + I2CP_HEADER_SIZE;
|
||||
uint8_t * buf = new uint8_t[l];
|
||||
htobe32buf (buf + I2CP_HEADER_LENGTH_OFFSET, len + 10);
|
||||
buf[I2CP_HEADER_TYPE_OFFSET] = I2CP_MESSAGE_PAYLOAD_MESSAGE;
|
||||
htobe16buf (buf + I2CP_HEADER_SIZE, m_SessionID);
|
||||
htobe32buf (buf + I2CP_HEADER_SIZE + 2, m_MessageID++);
|
||||
htobe32buf (buf + I2CP_HEADER_SIZE + 6, len);
|
||||
memcpy (buf + I2CP_HEADER_SIZE + 10, payload, len);
|
||||
boost::asio::async_write (*m_Socket, boost::asio::buffer (buf, l), boost::asio::transfer_all (),
|
||||
std::bind(&I2CPSession::HandleI2CPMessageSent, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2, buf));
|
||||
}
|
||||
|
||||
I2CPServer::I2CPServer (const std::string& interface, int port):
|
||||
m_IsRunning (false), m_Thread (nullptr),
|
||||
m_Acceptor (m_Service,
|
||||
#ifdef ANDROID
|
||||
I2CPSession::proto::endpoint(std::string (1, '\0') + interface)) // leading 0 for abstract address
|
||||
#else
|
||||
I2CPSession::proto::endpoint(boost::asio::ip::address::from_string(interface), port))
|
||||
#endif
|
||||
{
|
||||
memset (m_MessagesHandlers, 0, sizeof (m_MessagesHandlers));
|
||||
m_MessagesHandlers[I2CP_GET_DATE_MESSAGE] = &I2CPSession::GetDateMessageHandler;
|
||||
m_MessagesHandlers[I2CP_CREATE_SESSION_MESSAGE] = &I2CPSession::CreateSessionMessageHandler;
|
||||
m_MessagesHandlers[I2CP_DESTROY_SESSION_MESSAGE] = &I2CPSession::DestroySessionMessageHandler;
|
||||
m_MessagesHandlers[I2CP_RECONFIGURE_SESSION_MESSAGE] = &I2CPSession::ReconfigureSessionMessageHandler;
|
||||
m_MessagesHandlers[I2CP_CREATE_LEASESET_MESSAGE] = &I2CPSession::CreateLeaseSetMessageHandler;
|
||||
m_MessagesHandlers[I2CP_SEND_MESSAGE_MESSAGE] = &I2CPSession::SendMessageMessageHandler;
|
||||
m_MessagesHandlers[I2CP_SEND_MESSAGE_EXPIRES_MESSAGE] = &I2CPSession::SendMessageExpiresMessageHandler;
|
||||
m_MessagesHandlers[I2CP_HOST_LOOKUP_MESSAGE] = &I2CPSession::HostLookupMessageHandler;
|
||||
m_MessagesHandlers[I2CP_DEST_LOOKUP_MESSAGE] = &I2CPSession::DestLookupMessageHandler;
|
||||
m_MessagesHandlers[I2CP_GET_BANDWIDTH_LIMITS_MESSAGE] = &I2CPSession::GetBandwidthLimitsMessageHandler;
|
||||
}
|
||||
|
||||
I2CPServer::~I2CPServer ()
|
||||
{
|
||||
if (m_IsRunning)
|
||||
Stop ();
|
||||
}
|
||||
|
||||
void I2CPServer::Start ()
|
||||
{
|
||||
Accept ();
|
||||
m_IsRunning = true;
|
||||
m_Thread = new std::thread (std::bind (&I2CPServer::Run, this));
|
||||
}
|
||||
|
||||
void I2CPServer::Stop ()
|
||||
{
|
||||
m_IsRunning = false;
|
||||
m_Acceptor.cancel ();
|
||||
for (auto& it: m_Sessions)
|
||||
it.second->Stop ();
|
||||
m_Sessions.clear ();
|
||||
m_Service.stop ();
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void I2CPServer::Run ()
|
||||
{
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_Service.run ();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "I2CP: runtime exception: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void I2CPServer::Accept ()
|
||||
{
|
||||
auto newSocket = std::make_shared<I2CPSession::proto::socket> (m_Service);
|
||||
m_Acceptor.async_accept (*newSocket, std::bind (&I2CPServer::HandleAccept, this,
|
||||
std::placeholders::_1, newSocket));
|
||||
}
|
||||
|
||||
void I2CPServer::HandleAccept(const boost::system::error_code& ecode,
|
||||
std::shared_ptr<I2CPSession::proto::socket> socket)
|
||||
{
|
||||
if (!ecode && socket)
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
auto ep = socket->remote_endpoint (ec);
|
||||
if (!ec)
|
||||
{
|
||||
LogPrint (eLogDebug, "I2CP: new connection from ", ep);
|
||||
auto session = std::make_shared<I2CPSession>(*this, socket);
|
||||
m_Sessions[session->GetSessionID ()] = session;
|
||||
session->Start ();
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "I2CP: incoming connection error ", ec.message ());
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "I2CP: accept error: ", ecode.message ());
|
||||
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Accept ();
|
||||
}
|
||||
|
||||
void I2CPServer::RemoveSession (uint16_t sessionID)
|
||||
{
|
||||
m_Sessions.erase (sessionID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
204
I2CP.h
Normal file
204
I2CP.h
Normal file
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016, 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
|
||||
*/
|
||||
|
||||
#ifndef I2CP_H__
|
||||
#define I2CP_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <map>
|
||||
#include <boost/asio.hpp>
|
||||
#include "Destination.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
const uint8_t I2CP_PROTOCOL_BYTE = 0x2A;
|
||||
const size_t I2CP_SESSION_BUFFER_SIZE = 4096;
|
||||
|
||||
const size_t I2CP_HEADER_LENGTH_OFFSET = 0;
|
||||
const size_t I2CP_HEADER_TYPE_OFFSET = I2CP_HEADER_LENGTH_OFFSET + 4;
|
||||
const size_t I2CP_HEADER_SIZE = I2CP_HEADER_TYPE_OFFSET + 1;
|
||||
|
||||
const uint8_t I2CP_GET_DATE_MESSAGE = 32;
|
||||
const uint8_t I2CP_SET_DATE_MESSAGE = 33;
|
||||
const uint8_t I2CP_CREATE_SESSION_MESSAGE = 1;
|
||||
const uint8_t I2CP_RECONFIGURE_SESSION_MESSAGE = 2;
|
||||
const uint8_t I2CP_SESSION_STATUS_MESSAGE = 20;
|
||||
const uint8_t I2CP_DESTROY_SESSION_MESSAGE = 3;
|
||||
const uint8_t I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE = 37;
|
||||
const uint8_t I2CP_CREATE_LEASESET_MESSAGE = 4;
|
||||
const uint8_t I2CP_SEND_MESSAGE_MESSAGE = 5;
|
||||
const uint8_t I2CP_SEND_MESSAGE_EXPIRES_MESSAGE = 36;
|
||||
const uint8_t I2CP_MESSAGE_PAYLOAD_MESSAGE = 31;
|
||||
const uint8_t I2CP_MESSAGE_STATUS_MESSAGE = 22;
|
||||
const uint8_t I2CP_HOST_LOOKUP_MESSAGE = 38;
|
||||
const uint8_t I2CP_HOST_REPLY_MESSAGE = 39;
|
||||
const uint8_t I2CP_DEST_LOOKUP_MESSAGE = 34;
|
||||
const uint8_t I2CP_DEST_REPLY_MESSAGE = 35;
|
||||
const uint8_t I2CP_GET_BANDWIDTH_LIMITS_MESSAGE = 8;
|
||||
const uint8_t I2CP_BANDWIDTH_LIMITS_MESSAGE = 23;
|
||||
|
||||
enum I2CPMessageStatus
|
||||
{
|
||||
eI2CPMessageStatusAccepted = 1,
|
||||
eI2CPMessageStatusGuaranteedSuccess = 4,
|
||||
eI2CPMessageStatusGuaranteedFailure = 5,
|
||||
eI2CPMessageStatusNoLeaseSet = 21
|
||||
};
|
||||
|
||||
// params
|
||||
const char I2CP_PARAM_DONT_PUBLISH_LEASESET[] = "i2cp.dontPublishLeaseSet";
|
||||
const char I2CP_PARAM_MESSAGE_RELIABILITY[] = "i2cp.messageReliability";
|
||||
|
||||
class I2CPSession;
|
||||
class I2CPDestination: public LeaseSetDestination
|
||||
{
|
||||
public:
|
||||
|
||||
I2CPDestination (std::shared_ptr<I2CPSession> owner, std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, const std::map<std::string, std::string>& params);
|
||||
|
||||
void SetEncryptionPrivateKey (const uint8_t * key);
|
||||
void LeaseSetCreated (const uint8_t * buf, size_t len); // called from I2CPSession
|
||||
void SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce); // called from I2CPSession
|
||||
|
||||
// implements LocalDestination
|
||||
const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; };
|
||||
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Identity; };
|
||||
|
||||
protected:
|
||||
|
||||
// I2CP
|
||||
void HandleDataMessage (const uint8_t * buf, size_t len);
|
||||
void CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
|
||||
|
||||
private:
|
||||
|
||||
std::shared_ptr<I2CPDestination> GetSharedFromThis ()
|
||||
{ return std::static_pointer_cast<I2CPDestination>(shared_from_this ()); }
|
||||
bool SendMsg (std::shared_ptr<I2NPMessage> msg, std::shared_ptr<const i2p::data::LeaseSet> remote);
|
||||
|
||||
private:
|
||||
|
||||
std::shared_ptr<I2CPSession> m_Owner;
|
||||
std::shared_ptr<const i2p::data::IdentityEx> m_Identity;
|
||||
uint8_t m_EncryptionPrivateKey[256];
|
||||
uint64_t m_LeaseSetExpirationTime;
|
||||
};
|
||||
|
||||
class I2CPServer;
|
||||
class I2CPSession: public std::enable_shared_from_this<I2CPSession>
|
||||
{
|
||||
public:
|
||||
|
||||
#ifdef ANDROID
|
||||
typedef boost::asio::local::stream_protocol proto;
|
||||
#else
|
||||
typedef boost::asio::ip::tcp proto;
|
||||
#endif
|
||||
|
||||
I2CPSession (I2CPServer& owner, std::shared_ptr<proto::socket> socket);
|
||||
|
||||
~I2CPSession ();
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
uint16_t GetSessionID () const { return m_SessionID; };
|
||||
|
||||
// called from I2CPDestination
|
||||
void SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len);
|
||||
void SendMessagePayloadMessage (const uint8_t * payload, size_t len);
|
||||
void SendMessageStatusMessage (uint32_t nonce, I2CPMessageStatus status);
|
||||
|
||||
// message handlers
|
||||
void GetDateMessageHandler (const uint8_t * buf, size_t len);
|
||||
void CreateSessionMessageHandler (const uint8_t * buf, size_t len);
|
||||
void DestroySessionMessageHandler (const uint8_t * buf, size_t len);
|
||||
void ReconfigureSessionMessageHandler (const uint8_t * buf, size_t len);
|
||||
void CreateLeaseSetMessageHandler (const uint8_t * buf, size_t len);
|
||||
void SendMessageMessageHandler (const uint8_t * buf, size_t len);
|
||||
void SendMessageExpiresMessageHandler (const uint8_t * buf, size_t len);
|
||||
void HostLookupMessageHandler (const uint8_t * buf, size_t len);
|
||||
void DestLookupMessageHandler (const uint8_t * buf, size_t len);
|
||||
void GetBandwidthLimitsMessageHandler (const uint8_t * buf, size_t len);
|
||||
|
||||
private:
|
||||
|
||||
void ReadProtocolByte ();
|
||||
void ReceiveHeader ();
|
||||
void HandleReceivedHeader (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void ReceivePayload ();
|
||||
void HandleReceivedPayload (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleMessage ();
|
||||
void Terminate ();
|
||||
|
||||
void HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, const uint8_t * buf);
|
||||
std::string ExtractString (const uint8_t * buf, size_t len);
|
||||
size_t PutString (uint8_t * buf, size_t len, const std::string& str);
|
||||
void ExtractMapping (const uint8_t * buf, size_t len, std::map<std::string, std::string>& mapping);
|
||||
|
||||
void SendSessionStatusMessage (uint8_t status);
|
||||
void SendHostReplyMessage (uint32_t requestID, std::shared_ptr<const i2p::data::IdentityEx> identity);
|
||||
|
||||
private:
|
||||
|
||||
I2CPServer& m_Owner;
|
||||
std::shared_ptr<proto::socket> m_Socket;
|
||||
uint8_t m_Header[I2CP_HEADER_SIZE], * m_Payload;
|
||||
size_t m_PayloadLen;
|
||||
|
||||
std::shared_ptr<I2CPDestination> m_Destination;
|
||||
uint16_t m_SessionID;
|
||||
uint32_t m_MessageID;
|
||||
bool m_IsSendAccepted;
|
||||
};
|
||||
typedef void (I2CPSession::*I2CPMessageHandler)(const uint8_t * buf, size_t len);
|
||||
|
||||
class I2CPServer
|
||||
{
|
||||
public:
|
||||
|
||||
I2CPServer (const std::string& interface, int port);
|
||||
~I2CPServer ();
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
boost::asio::io_service& GetService () { return m_Service; };
|
||||
|
||||
void RemoveSession (uint16_t sessionID);
|
||||
|
||||
private:
|
||||
|
||||
void Run ();
|
||||
|
||||
void Accept ();
|
||||
|
||||
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<I2CPSession::proto::socket> socket);
|
||||
|
||||
private:
|
||||
|
||||
I2CPMessageHandler m_MessagesHandlers[256];
|
||||
std::map<uint16_t, std::shared_ptr<I2CPSession> > m_Sessions;
|
||||
|
||||
bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
I2CPSession::proto::acceptor m_Acceptor;
|
||||
|
||||
public:
|
||||
|
||||
const decltype(m_MessagesHandlers)& GetMessagesHandlers () const { return m_MessagesHandlers; };
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
357
I2NPProtocol.cpp
357
I2NPProtocol.cpp
@@ -1,88 +1,97 @@
|
||||
#include <string.h>
|
||||
#include <atomic>
|
||||
#include "Base.h"
|
||||
#include "Log.h"
|
||||
#include "Crypto.h"
|
||||
#include "I2PEndian.h"
|
||||
#include <cryptopp/gzip.h>
|
||||
#include "ElGamal.h"
|
||||
#include "Timestamp.h"
|
||||
#include "RouterContext.h"
|
||||
#include "NetDb.h"
|
||||
#include "Tunnel.h"
|
||||
#include "base64.h"
|
||||
#include "Transports.h"
|
||||
#include "Garlic.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "version.h"
|
||||
|
||||
using namespace i2p::transport;
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
I2NPMessage * NewI2NPMessage ()
|
||||
std::shared_ptr<I2NPMessage> NewI2NPMessage ()
|
||||
{
|
||||
return new I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE>();
|
||||
return std::make_shared<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> >();
|
||||
}
|
||||
|
||||
I2NPMessage * NewI2NPShortMessage ()
|
||||
std::shared_ptr<I2NPMessage> NewI2NPShortMessage ()
|
||||
{
|
||||
return new I2NPMessageBuffer<I2NP_MAX_SHORT_MESSAGE_SIZE>();
|
||||
return std::make_shared<I2NPMessageBuffer<I2NP_MAX_SHORT_MESSAGE_SIZE> >();
|
||||
}
|
||||
|
||||
I2NPMessage * NewI2NPMessage (size_t len)
|
||||
std::shared_ptr<I2NPMessage> NewI2NPMessage (size_t len)
|
||||
{
|
||||
return (len < I2NP_MAX_SHORT_MESSAGE_SIZE/2) ? NewI2NPShortMessage () : NewI2NPMessage ();
|
||||
}
|
||||
|
||||
void DeleteI2NPMessage (I2NPMessage * msg)
|
||||
{
|
||||
delete msg;
|
||||
}
|
||||
|
||||
static std::atomic<uint32_t> I2NPmsgID(0); // TODO: create class
|
||||
void FillI2NPMessageHeader (I2NPMessage * msg, I2NPMessageType msgType, uint32_t replyMsgID)
|
||||
void I2NPMessage::FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID)
|
||||
{
|
||||
msg->SetTypeID (msgType);
|
||||
if (replyMsgID) // for tunnel creation
|
||||
msg->SetMsgID (replyMsgID);
|
||||
else
|
||||
{
|
||||
msg->SetMsgID (I2NPmsgID);
|
||||
I2NPmsgID++;
|
||||
}
|
||||
msg->SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + 5000); // TODO: 5 secs is a magic number
|
||||
msg->UpdateSize ();
|
||||
msg->UpdateChks ();
|
||||
SetTypeID (msgType);
|
||||
if (!replyMsgID) RAND_bytes ((uint8_t *)&replyMsgID, 4);
|
||||
SetMsgID (replyMsgID);
|
||||
SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + I2NP_MESSAGE_EXPIRATION_TIMEOUT);
|
||||
UpdateSize ();
|
||||
UpdateChks ();
|
||||
}
|
||||
|
||||
void RenewI2NPMessageHeader (I2NPMessage * msg)
|
||||
void I2NPMessage::RenewI2NPMessageHeader ()
|
||||
{
|
||||
if (msg)
|
||||
{
|
||||
msg->SetMsgID (I2NPmsgID);
|
||||
I2NPmsgID++;
|
||||
msg->SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + 5000);
|
||||
}
|
||||
uint32_t msgID;
|
||||
RAND_bytes ((uint8_t *)&msgID, 4);
|
||||
SetMsgID (msgID);
|
||||
SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + I2NP_MESSAGE_EXPIRATION_TIMEOUT);
|
||||
}
|
||||
|
||||
I2NPMessage * CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, int len, uint32_t replyMsgID)
|
||||
bool I2NPMessage::IsExpired () const
|
||||
{
|
||||
I2NPMessage * msg = NewI2NPMessage (len);
|
||||
memcpy (msg->GetPayload (), buf, len);
|
||||
msg->len += len;
|
||||
FillI2NPMessageHeader (msg, msgType, replyMsgID);
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
auto exp = GetExpiration ();
|
||||
return (ts > exp + I2NP_MESSAGE_CLOCK_SKEW) || (ts < exp - 3*I2NP_MESSAGE_CLOCK_SKEW); // check if expired or too far in future
|
||||
}
|
||||
|
||||
std::shared_ptr<I2NPMessage> CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID)
|
||||
{
|
||||
auto msg = NewI2NPMessage (len);
|
||||
if (msg->Concat (buf, len) < len)
|
||||
LogPrint (eLogError, "I2NP: message length ", len, " exceeds max length ", msg->maxLen);
|
||||
msg->FillI2NPMessageHeader (msgType, replyMsgID);
|
||||
return msg;
|
||||
}
|
||||
|
||||
I2NPMessage * CreateI2NPMessage (const uint8_t * buf, int len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
|
||||
std::shared_ptr<I2NPMessage> CreateI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
|
||||
{
|
||||
I2NPMessage * msg = NewI2NPMessage ();
|
||||
memcpy (msg->GetBuffer (), buf, len);
|
||||
msg->len = msg->offset + len;
|
||||
msg->from = from;
|
||||
auto msg = NewI2NPMessage ();
|
||||
if (msg->offset + len < msg->maxLen)
|
||||
{
|
||||
memcpy (msg->GetBuffer (), buf, len);
|
||||
msg->len = msg->offset + len;
|
||||
msg->from = from;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "I2NP: message length ", len, " exceeds max length");
|
||||
return msg;
|
||||
}
|
||||
|
||||
I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID)
|
||||
|
||||
std::shared_ptr<I2NPMessage> CopyI2NPMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
I2NPMessage * m = NewI2NPShortMessage ();
|
||||
if (!msg) return nullptr;
|
||||
auto newMsg = NewI2NPMessage (msg->len);
|
||||
newMsg->offset = msg->offset;
|
||||
*newMsg = *msg;
|
||||
return newMsg;
|
||||
}
|
||||
|
||||
std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg (uint32_t msgID)
|
||||
{
|
||||
auto m = NewI2NPShortMessage ();
|
||||
uint8_t * buf = m->GetPayload ();
|
||||
if (msgID)
|
||||
{
|
||||
@@ -91,18 +100,19 @@ namespace i2p
|
||||
}
|
||||
else // for SSU establishment
|
||||
{
|
||||
htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, i2p::context.GetRandomNumberGenerator ().GenerateWord32 ());
|
||||
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, 2); // netID = 2
|
||||
RAND_bytes ((uint8_t *)&msgID, 4);
|
||||
htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID);
|
||||
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, I2PD_NET_ID);
|
||||
}
|
||||
m->len += DELIVERY_STATUS_SIZE;
|
||||
FillI2NPMessageHeader (m, eI2NPDeliveryStatus);
|
||||
m->FillI2NPMessageHeader (eI2NPDeliveryStatus);
|
||||
return m;
|
||||
}
|
||||
|
||||
I2NPMessage * CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
|
||||
std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
|
||||
uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers)
|
||||
{
|
||||
I2NPMessage * m = NewI2NPMessage ();
|
||||
auto m = excludedPeers ? NewI2NPMessage () : NewI2NPShortMessage ();
|
||||
uint8_t * buf = m->GetPayload ();
|
||||
memcpy (buf, key, 32); // key
|
||||
buf += 32;
|
||||
@@ -140,26 +150,27 @@ namespace i2p
|
||||
}
|
||||
|
||||
m->len += (buf - m->GetPayload ());
|
||||
FillI2NPMessageHeader (m, eI2NPDatabaseLookup);
|
||||
m->FillI2NPMessageHeader (eI2NPDatabaseLookup);
|
||||
return m;
|
||||
}
|
||||
|
||||
I2NPMessage * CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
|
||||
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
|
||||
const std::set<i2p::data::IdentHash>& excludedFloodfills,
|
||||
const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag)
|
||||
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag)
|
||||
{
|
||||
I2NPMessage * m = NewI2NPMessage ();
|
||||
int cnt = excludedFloodfills.size ();
|
||||
auto m = cnt > 0 ? NewI2NPMessage () : NewI2NPShortMessage ();
|
||||
uint8_t * buf = m->GetPayload ();
|
||||
memcpy (buf, dest, 32); // key
|
||||
buf += 32;
|
||||
memcpy (buf, replyTunnel->GetNextIdentHash (), 32); // reply tunnel GW
|
||||
buf += 32;
|
||||
*buf = DATABASE_LOOKUP_DELIVERY_FLAG | DATABASE_LOOKUP_ENCYPTION_FLAG | DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP; // flags
|
||||
htobe32buf (buf + 1, replyTunnel->GetNextTunnelID ()); // reply tunnel ID
|
||||
buf += 5;
|
||||
*buf = DATABASE_LOOKUP_DELIVERY_FLAG | DATABASE_LOOKUP_ENCRYPTION_FLAG | DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP; // flags
|
||||
buf ++;
|
||||
htobe32buf (buf, replyTunnel->GetNextTunnelID ()); // reply tunnel ID
|
||||
buf += 4;
|
||||
|
||||
// excluded
|
||||
int cnt = excludedFloodfills.size ();
|
||||
htobe16buf (buf, cnt);
|
||||
buf += 2;
|
||||
if (cnt > 0)
|
||||
@@ -172,28 +183,26 @@ namespace i2p
|
||||
}
|
||||
// encryption
|
||||
memcpy (buf, replyKey, 32);
|
||||
buf[32] = 1; // 1 tag
|
||||
buf[32] = uint8_t( 1 ); // 1 tag
|
||||
memcpy (buf + 33, replyTag, 32);
|
||||
buf += 65;
|
||||
|
||||
m->len += (buf - m->GetPayload ());
|
||||
FillI2NPMessageHeader (m, eI2NPDatabaseLookup);
|
||||
m->FillI2NPMessageHeader (eI2NPDatabaseLookup);
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident,
|
||||
std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident,
|
||||
std::vector<i2p::data::IdentHash> routers)
|
||||
{
|
||||
I2NPMessage * m = NewI2NPShortMessage ();
|
||||
auto m = NewI2NPShortMessage ();
|
||||
uint8_t * buf = m->GetPayload ();
|
||||
size_t len = 0;
|
||||
memcpy (buf, ident, 32);
|
||||
len += 32;
|
||||
buf[len] = routers.size ();
|
||||
len++;
|
||||
for (auto it: routers)
|
||||
for (const auto& it: routers)
|
||||
{
|
||||
memcpy (buf + len, it, 32);
|
||||
len += 32;
|
||||
@@ -201,16 +210,16 @@ namespace i2p
|
||||
memcpy (buf + len, i2p::context.GetRouterInfo ().GetIdentHash (), 32);
|
||||
len += 32;
|
||||
m->len += len;
|
||||
FillI2NPMessageHeader (m, eI2NPDatabaseSearchReply);
|
||||
m->FillI2NPMessageHeader (eI2NPDatabaseSearchReply);
|
||||
return m;
|
||||
}
|
||||
|
||||
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::RouterInfo * router, uint32_t replyToken)
|
||||
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router, uint32_t replyToken)
|
||||
{
|
||||
if (!router) // we send own RouterInfo
|
||||
router = &context.GetRouterInfo ();
|
||||
router = context.GetSharedRouterInfo ();
|
||||
|
||||
I2NPMessage * m = NewI2NPShortMessage ();
|
||||
auto m = NewI2NPShortMessage ();
|
||||
uint8_t * payload = m->GetPayload ();
|
||||
|
||||
memcpy (payload + DATABASE_STORE_KEY_OFFSET, router->GetIdentHash (), 32);
|
||||
@@ -225,38 +234,55 @@ namespace i2p
|
||||
buf += 32;
|
||||
}
|
||||
|
||||
CryptoPP::Gzip compressor;
|
||||
compressor.Put (router->GetBuffer (), router->GetBufferLen ());
|
||||
compressor.MessageEnd();
|
||||
auto size = compressor.MaxRetrievable ();
|
||||
htobe16buf (buf, size); // size
|
||||
uint8_t * sizePtr = buf;
|
||||
buf += 2;
|
||||
// TODO: check if size doesn't exceed buffer
|
||||
compressor.Get (buf, size);
|
||||
buf += size;
|
||||
m->len += (buf - payload); // payload size
|
||||
FillI2NPMessageHeader (m, eI2NPDatabaseStore);
|
||||
|
||||
i2p::data::GzipDeflator deflator;
|
||||
size_t size = deflator.Deflate (router->GetBuffer (), router->GetBufferLen (), buf, m->maxLen -m->len);
|
||||
if (size)
|
||||
{
|
||||
htobe16buf (sizePtr, size); // size
|
||||
m->len += size;
|
||||
}
|
||||
else
|
||||
m = nullptr;
|
||||
if (m)
|
||||
m->FillI2NPMessageHeader (eI2NPDatabaseStore);
|
||||
return m;
|
||||
}
|
||||
|
||||
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::LeaseSet * leaseSet, uint32_t replyToken)
|
||||
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
|
||||
{
|
||||
if (!leaseSet) return nullptr;
|
||||
I2NPMessage * m = NewI2NPShortMessage ();
|
||||
auto m = NewI2NPShortMessage ();
|
||||
uint8_t * payload = m->GetPayload ();
|
||||
memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetIdentHash (), 32);
|
||||
payload[DATABASE_STORE_TYPE_OFFSET] = 1; // LeaseSet
|
||||
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0);
|
||||
size_t size = DATABASE_STORE_HEADER_SIZE;
|
||||
memcpy (payload + size, leaseSet->GetBuffer (), leaseSet->GetBufferLen ());
|
||||
size += leaseSet->GetBufferLen ();
|
||||
m->len += size;
|
||||
m->FillI2NPMessageHeader (eI2NPDatabaseStore);
|
||||
return m;
|
||||
}
|
||||
|
||||
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LocalLeaseSet> leaseSet, uint32_t replyToken, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel)
|
||||
{
|
||||
if (!leaseSet) return nullptr;
|
||||
auto m = NewI2NPShortMessage ();
|
||||
uint8_t * payload = m->GetPayload ();
|
||||
memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetIdentHash (), 32);
|
||||
payload[DATABASE_STORE_TYPE_OFFSET] = 1; // LeaseSet
|
||||
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken);
|
||||
size_t size = DATABASE_STORE_HEADER_SIZE;
|
||||
if (replyToken)
|
||||
if (replyToken && replyTunnel)
|
||||
{
|
||||
auto leases = leaseSet->GetNonExpiredLeases ();
|
||||
if (leases.size () > 0)
|
||||
if (replyTunnel)
|
||||
{
|
||||
htobe32buf (payload + size, leases[0].tunnelID);
|
||||
htobe32buf (payload + size, replyTunnel->GetNextTunnelID ());
|
||||
size += 4; // reply tunnelID
|
||||
memcpy (payload + size, leases[0].tunnelGateway, 32);
|
||||
memcpy (payload + size, replyTunnel->GetNextIdentHash (), 32);
|
||||
size += 32; // reply tunnel gateway
|
||||
}
|
||||
else
|
||||
@@ -265,10 +291,26 @@ namespace i2p
|
||||
memcpy (payload + size, leaseSet->GetBuffer (), leaseSet->GetBufferLen ());
|
||||
size += leaseSet->GetBufferLen ();
|
||||
m->len += size;
|
||||
FillI2NPMessageHeader (m, eI2NPDatabaseStore);
|
||||
m->FillI2NPMessageHeader (eI2NPDatabaseStore);
|
||||
return m;
|
||||
}
|
||||
|
||||
bool IsRouterInfoMsg (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
if (!msg || msg->GetTypeID () != eI2NPDatabaseStore) return false;
|
||||
return !msg->GetPayload ()[DATABASE_STORE_TYPE_OFFSET]; // 0- RouterInfo
|
||||
}
|
||||
|
||||
static uint16_t g_MaxNumTransitTunnels = DEFAULT_MAX_NUM_TRANSIT_TUNNELS; // TODO:
|
||||
void SetMaxNumTransitTunnels (uint16_t maxNumTransitTunnels)
|
||||
{
|
||||
if (maxNumTransitTunnels > 0 && maxNumTransitTunnels <= 10000 && g_MaxNumTransitTunnels != maxNumTransitTunnels)
|
||||
{
|
||||
LogPrint (eLogDebug, "I2NP: Max number of transit tunnels set to ", maxNumTransitTunnels);
|
||||
g_MaxNumTransitTunnels = maxNumTransitTunnels;
|
||||
}
|
||||
}
|
||||
|
||||
bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText)
|
||||
{
|
||||
for (int i = 0; i < num; i++)
|
||||
@@ -276,14 +318,15 @@ namespace i2p
|
||||
uint8_t * record = records + i*TUNNEL_BUILD_RECORD_SIZE;
|
||||
if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
|
||||
{
|
||||
LogPrint ("Record ",i," is ours");
|
||||
LogPrint (eLogDebug, "I2NP: Build request record ", i, " is ours");
|
||||
|
||||
i2p::crypto::ElGamalDecrypt (i2p::context.GetEncryptionPrivateKey (), record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText);
|
||||
// replace record to reply
|
||||
if (i2p::context.AcceptsTunnels ())
|
||||
if (i2p::context.AcceptsTunnels () &&
|
||||
i2p::tunnel::tunnels.GetTransitTunnels ().size () <= g_MaxNumTransitTunnels &&
|
||||
!i2p::transport::transports.IsBandwidthExceeded ())
|
||||
{
|
||||
i2p::tunnel::TransitTunnel * transitTunnel =
|
||||
i2p::tunnel::CreateTransitTunnel (
|
||||
auto transitTunnel = i2p::tunnel::CreateTransitTunnel (
|
||||
bufbe32toh (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
|
||||
clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
||||
bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
|
||||
@@ -298,8 +341,8 @@ namespace i2p
|
||||
record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 30; // always reject with bandwidth reason (30)
|
||||
|
||||
//TODO: fill filler
|
||||
CryptoPP::SHA256().CalculateDigest(record + BUILD_RESPONSE_RECORD_HASH_OFFSET,
|
||||
record + BUILD_RESPONSE_RECORD_PADDING_OFFSET, BUILD_RESPONSE_RECORD_PADDING_SIZE + 1); // + 1 byte of ret
|
||||
SHA256 (record + BUILD_RESPONSE_RECORD_PADDING_OFFSET, BUILD_RESPONSE_RECORD_PADDING_SIZE + 1, // + 1 byte of ret
|
||||
record + BUILD_RESPONSE_RECORD_HASH_OFFSET);
|
||||
// encrypt reply
|
||||
i2p::crypto::CBCEncryption encryption;
|
||||
for (int j = 0; j < num; j++)
|
||||
@@ -318,22 +361,27 @@ namespace i2p
|
||||
void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
|
||||
{
|
||||
int num = buf[0];
|
||||
LogPrint ("VariableTunnelBuild ", num, " records");
|
||||
LogPrint (eLogDebug, "I2NP: VariableTunnelBuild ", num, " records");
|
||||
if (len < num*BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 1)
|
||||
{
|
||||
LogPrint (eLogError, "VaribleTunnelBuild message of ", num, " records is too short ", len);
|
||||
return;
|
||||
}
|
||||
|
||||
auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (replyMsgID);
|
||||
if (tunnel)
|
||||
{
|
||||
// endpoint of inbound tunnel
|
||||
LogPrint ("VariableTunnelBuild reply for tunnel ", tunnel->GetTunnelID ());
|
||||
LogPrint (eLogDebug, "I2NP: VariableTunnelBuild reply for tunnel ", tunnel->GetTunnelID ());
|
||||
if (tunnel->HandleTunnelBuildResponse (buf, len))
|
||||
{
|
||||
LogPrint ("Inbound tunnel ", tunnel->GetTunnelID (), " has been created");
|
||||
LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been created");
|
||||
tunnel->SetState (i2p::tunnel::eTunnelStateEstablished);
|
||||
i2p::tunnel::tunnels.AddInboundTunnel (tunnel);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("Inbound tunnel ", tunnel->GetTunnelID (), " has been declined");
|
||||
LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been declined");
|
||||
tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed);
|
||||
}
|
||||
}
|
||||
@@ -360,6 +408,11 @@ namespace i2p
|
||||
|
||||
void HandleTunnelBuildMsg (uint8_t * buf, size_t len)
|
||||
{
|
||||
if (len < NUM_TUNNEL_BUILD_RECORDS*BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE)
|
||||
{
|
||||
LogPrint (eLogError, "TunnelBuild message is too short ", len);
|
||||
return;
|
||||
}
|
||||
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
|
||||
if (HandleBuildRequestRecords (NUM_TUNNEL_BUILD_RECORDS, buf, clearText))
|
||||
{
|
||||
@@ -380,60 +433,74 @@ namespace i2p
|
||||
|
||||
void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
|
||||
{
|
||||
LogPrint ("VariableTunnelBuildReplyMsg replyMsgID=", replyMsgID);
|
||||
int num = buf[0];
|
||||
LogPrint (eLogDebug, "I2NP: VariableTunnelBuildReplyMsg of ", num, " records replyMsgID=", replyMsgID);
|
||||
if (len < num*BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 1)
|
||||
{
|
||||
LogPrint (eLogError, "VaribleTunnelBuildReply message of ", num, " records is too short ", len);
|
||||
return;
|
||||
}
|
||||
|
||||
auto tunnel = i2p::tunnel::tunnels.GetPendingOutboundTunnel (replyMsgID);
|
||||
if (tunnel)
|
||||
{
|
||||
// reply for outbound tunnel
|
||||
if (tunnel->HandleTunnelBuildResponse (buf, len))
|
||||
{
|
||||
LogPrint ("Outbound tunnel ", tunnel->GetTunnelID (), " has been created");
|
||||
LogPrint (eLogInfo, "I2NP: Outbound tunnel ", tunnel->GetTunnelID (), " has been created");
|
||||
tunnel->SetState (i2p::tunnel::eTunnelStateEstablished);
|
||||
i2p::tunnel::tunnels.AddOutboundTunnel (tunnel);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("Outbound tunnel ", tunnel->GetTunnelID (), " has been declined");
|
||||
LogPrint (eLogInfo, "I2NP: Outbound tunnel ", tunnel->GetTunnelID (), " has been declined");
|
||||
tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed);
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint ("Pending tunnel for message ", replyMsgID, " not found");
|
||||
LogPrint (eLogWarning, "I2NP: Pending tunnel for message ", replyMsgID, " not found");
|
||||
}
|
||||
|
||||
|
||||
I2NPMessage * CreateTunnelDataMsg (const uint8_t * buf)
|
||||
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf)
|
||||
{
|
||||
I2NPMessage * msg = NewI2NPMessage ();
|
||||
memcpy (msg->GetPayload (), buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE);
|
||||
msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE;
|
||||
FillI2NPMessageHeader (msg, eI2NPTunnelData);
|
||||
auto msg = NewI2NPShortMessage ();
|
||||
msg->Concat (buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE);
|
||||
msg->FillI2NPMessageHeader (eI2NPTunnelData);
|
||||
return msg;
|
||||
}
|
||||
|
||||
I2NPMessage * CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload)
|
||||
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload)
|
||||
{
|
||||
I2NPMessage * msg = NewI2NPMessage ();
|
||||
memcpy (msg->GetPayload () + 4, payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4);
|
||||
auto msg = NewI2NPShortMessage ();
|
||||
htobe32buf (msg->GetPayload (), tunnelID);
|
||||
msg->len += 4; // tunnelID
|
||||
msg->Concat (payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4);
|
||||
msg->FillI2NPMessageHeader (eI2NPTunnelData);
|
||||
return msg;
|
||||
}
|
||||
|
||||
std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg ()
|
||||
{
|
||||
auto msg = NewI2NPShortMessage ();
|
||||
msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE;
|
||||
FillI2NPMessageHeader (msg, eI2NPTunnelData);
|
||||
return msg;
|
||||
}
|
||||
|
||||
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len)
|
||||
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len)
|
||||
{
|
||||
I2NPMessage * msg = NewI2NPMessage (len);
|
||||
auto msg = NewI2NPMessage (len);
|
||||
uint8_t * payload = msg->GetPayload ();
|
||||
htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
|
||||
htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
|
||||
memcpy (payload + TUNNEL_GATEWAY_HEADER_SIZE, buf, len);
|
||||
msg->len += TUNNEL_GATEWAY_HEADER_SIZE + len;
|
||||
FillI2NPMessageHeader (msg, eI2NPTunnelGateway);
|
||||
msg->len += TUNNEL_GATEWAY_HEADER_SIZE;
|
||||
if (msg->Concat (buf, len) < len)
|
||||
LogPrint (eLogError, "I2NP: tunnel gateway buffer overflow ", msg->maxLen);
|
||||
msg->FillI2NPMessageHeader (eI2NPTunnelGateway);
|
||||
return msg;
|
||||
}
|
||||
|
||||
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessage * msg)
|
||||
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
if (msg->offset >= I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE)
|
||||
{
|
||||
@@ -444,33 +511,29 @@ namespace i2p
|
||||
htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
|
||||
msg->offset -= (I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE);
|
||||
msg->len = msg->offset + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE +len;
|
||||
FillI2NPMessageHeader (msg, eI2NPTunnelGateway);
|
||||
msg->FillI2NPMessageHeader (eI2NPTunnelGateway);
|
||||
return msg;
|
||||
}
|
||||
else
|
||||
{
|
||||
I2NPMessage * msg1 = CreateTunnelGatewayMsg (tunnelID, msg->GetBuffer (), msg->GetLength ());
|
||||
DeleteI2NPMessage (msg);
|
||||
return msg1;
|
||||
}
|
||||
return CreateTunnelGatewayMsg (tunnelID, msg->GetBuffer (), msg->GetLength ());
|
||||
}
|
||||
|
||||
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType,
|
||||
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType,
|
||||
const uint8_t * buf, size_t len, uint32_t replyMsgID)
|
||||
{
|
||||
I2NPMessage * msg = NewI2NPMessage (len);
|
||||
auto msg = NewI2NPMessage (len);
|
||||
size_t gatewayMsgOffset = I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE;
|
||||
msg->offset += gatewayMsgOffset;
|
||||
msg->len += gatewayMsgOffset;
|
||||
memcpy (msg->GetPayload (), buf, len);
|
||||
msg->len += len;
|
||||
FillI2NPMessageHeader (msg, msgType, replyMsgID); // create content message
|
||||
if (msg->Concat (buf, len) < len)
|
||||
LogPrint (eLogError, "I2NP: tunnel gateway buffer overflow ", msg->maxLen);
|
||||
msg->FillI2NPMessageHeader (msgType, replyMsgID); // create content message
|
||||
len = msg->GetLength ();
|
||||
msg->offset -= gatewayMsgOffset;
|
||||
uint8_t * payload = msg->GetPayload ();
|
||||
htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
|
||||
htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
|
||||
FillI2NPMessageHeader (msg, eI2NPTunnelGateway); // gateway message
|
||||
msg->FillI2NPMessageHeader (eI2NPTunnelGateway); // gateway message
|
||||
return msg;
|
||||
}
|
||||
|
||||
@@ -483,62 +546,56 @@ namespace i2p
|
||||
{
|
||||
uint8_t typeID = msg[I2NP_HEADER_TYPEID_OFFSET];
|
||||
uint32_t msgID = bufbe32toh (msg + I2NP_HEADER_MSGID_OFFSET);
|
||||
LogPrint ("I2NP msg received len=", len,", type=", (int)typeID, ", msgID=", (unsigned int)msgID);
|
||||
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)
|
||||
{
|
||||
case eI2NPVariableTunnelBuild:
|
||||
LogPrint ("VariableTunnelBuild");
|
||||
HandleVariableTunnelBuildMsg (msgID, buf, size);
|
||||
break;
|
||||
case eI2NPVariableTunnelBuildReply:
|
||||
LogPrint ("VariableTunnelBuildReply");
|
||||
HandleVariableTunnelBuildReplyMsg (msgID, buf, size);
|
||||
break;
|
||||
case eI2NPTunnelBuild:
|
||||
LogPrint ("TunnelBuild");
|
||||
HandleTunnelBuildMsg (buf, size);
|
||||
break;
|
||||
case eI2NPTunnelBuildReply:
|
||||
LogPrint ("TunnelBuildReply");
|
||||
// TODO:
|
||||
break;
|
||||
default:
|
||||
LogPrint ("Unexpected message ", (int)typeID);
|
||||
LogPrint (eLogWarning, "I2NP: Unexpected message ", (int)typeID);
|
||||
}
|
||||
}
|
||||
|
||||
void HandleI2NPMessage (I2NPMessage * msg)
|
||||
void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
if (msg)
|
||||
{
|
||||
switch (msg->GetTypeID ())
|
||||
uint8_t typeID = msg->GetTypeID ();
|
||||
LogPrint (eLogDebug, "I2NP: Handling message with type ", (int)typeID);
|
||||
switch (typeID)
|
||||
{
|
||||
case eI2NPTunnelData:
|
||||
LogPrint ("TunnelData");
|
||||
i2p::tunnel::tunnels.PostTunnelData (msg);
|
||||
break;
|
||||
case eI2NPTunnelGateway:
|
||||
LogPrint ("TunnelGateway");
|
||||
i2p::tunnel::tunnels.PostTunnelData (msg);
|
||||
break;
|
||||
case eI2NPGarlic:
|
||||
LogPrint ("Garlic");
|
||||
{
|
||||
if (msg->from)
|
||||
{
|
||||
if (msg->from->GetTunnelPool ())
|
||||
msg->from->GetTunnelPool ()->ProcessGarlicMessage (msg);
|
||||
else
|
||||
{
|
||||
LogPrint (eLogInfo, "Local destination for garlic doesn't exist anymore");
|
||||
DeleteI2NPMessage (msg);
|
||||
}
|
||||
LogPrint (eLogInfo, "I2NP: Local destination for garlic doesn't exist anymore");
|
||||
}
|
||||
else
|
||||
i2p::context.ProcessGarlicMessage (msg);
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case eI2NPDatabaseStore:
|
||||
case eI2NPDatabaseSearchReply:
|
||||
case eI2NPDatabaseLookup:
|
||||
@@ -546,12 +603,13 @@ namespace i2p
|
||||
i2p::data::netdb.PostI2NPMsg (msg);
|
||||
break;
|
||||
case eI2NPDeliveryStatus:
|
||||
LogPrint ("DeliveryStatus");
|
||||
{
|
||||
if (msg->from && msg->from->GetTunnelPool ())
|
||||
msg->from->GetTunnelPool ()->ProcessDeliveryStatus (msg);
|
||||
else
|
||||
i2p::context.ProcessDeliveryStatusMessage (msg);
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case eI2NPVariableTunnelBuild:
|
||||
case eI2NPVariableTunnelBuildReply:
|
||||
case eI2NPTunnelBuild:
|
||||
@@ -561,7 +619,6 @@ namespace i2p
|
||||
break;
|
||||
default:
|
||||
HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ());
|
||||
DeleteI2NPMessage (msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -571,7 +628,7 @@ namespace i2p
|
||||
Flush ();
|
||||
}
|
||||
|
||||
void I2NPMessagesHandler::PutNextMessage (I2NPMessage * msg)
|
||||
void I2NPMessagesHandler::PutNextMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
if (msg)
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <string.h>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include <cryptopp/sha.h>
|
||||
#include "Crypto.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "Identity.h"
|
||||
#include "RouterInfo.h"
|
||||
@@ -90,7 +90,7 @@ namespace i2p
|
||||
|
||||
// DatabaseLookup flags
|
||||
const uint8_t DATABASE_LOOKUP_DELIVERY_FLAG = 0x01;
|
||||
const uint8_t DATABASE_LOOKUP_ENCYPTION_FLAG = 0x02;
|
||||
const uint8_t DATABASE_LOOKUP_ENCRYPTION_FLAG = 0x02;
|
||||
const uint8_t DATABASE_LOOKUP_TYPE_FLAGS_MASK = 0x0C;
|
||||
const uint8_t DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP = 0;
|
||||
const uint8_t DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP = 0x04; // 0100
|
||||
@@ -104,7 +104,10 @@ namespace tunnel
|
||||
}
|
||||
|
||||
const size_t I2NP_MAX_MESSAGE_SIZE = 32768;
|
||||
const size_t I2NP_MAX_SHORT_MESSAGE_SIZE = 2400;
|
||||
const size_t I2NP_MAX_SHORT_MESSAGE_SIZE = 4096;
|
||||
const unsigned int I2NP_MESSAGE_EXPIRATION_TIMEOUT = 8000; // in milliseconds (as initial RTT)
|
||||
const unsigned int I2NP_MESSAGE_CLOCK_SKEW = 60*1000; // 1 minute in milliseconds
|
||||
|
||||
struct I2NPMessage
|
||||
{
|
||||
uint8_t * buf;
|
||||
@@ -130,12 +133,13 @@ namespace tunnel
|
||||
void UpdateChks ()
|
||||
{
|
||||
uint8_t hash[32];
|
||||
CryptoPP::SHA256().CalculateDigest(hash, GetPayload (), GetPayloadLength ());
|
||||
SHA256(GetPayload (), GetPayloadLength (), hash);
|
||||
GetHeader ()[I2NP_HEADER_CHKS_OFFSET] = hash[0];
|
||||
}
|
||||
|
||||
// payload
|
||||
uint8_t * GetPayload () { return GetBuffer () + I2NP_HEADER_SIZE; };
|
||||
const uint8_t * GetPayload () const { return GetBuffer () + I2NP_HEADER_SIZE; };
|
||||
uint8_t * GetBuffer () { return buf + offset; };
|
||||
const uint8_t * GetBuffer () const { return buf + offset; };
|
||||
size_t GetLength () const { return len - offset; };
|
||||
@@ -143,6 +147,7 @@ namespace tunnel
|
||||
|
||||
void Align (size_t alignment)
|
||||
{
|
||||
if (len + alignment > maxLen) return;
|
||||
size_t rem = ((size_t)GetBuffer ()) % alignment;
|
||||
if (rem)
|
||||
{
|
||||
@@ -151,6 +156,15 @@ namespace tunnel
|
||||
}
|
||||
}
|
||||
|
||||
size_t Concat (const uint8_t * buf1, size_t len1)
|
||||
{
|
||||
// make sure with don't write beyond maxLen
|
||||
if (len + len1 > maxLen) len1 = maxLen - len;
|
||||
memcpy (buf + len, buf1, len1);
|
||||
len += len1;
|
||||
return len1;
|
||||
}
|
||||
|
||||
I2NPMessage& operator=(const I2NPMessage& other)
|
||||
{
|
||||
memcpy (buf + offset, other.buf + other.offset, other.GetLength ());
|
||||
@@ -158,7 +172,7 @@ namespace tunnel
|
||||
from = other.from;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
// for SSU only
|
||||
uint8_t * GetSSUHeader () { return buf + offset + I2NP_HEADER_SIZE - I2NP_SHORT_HEADER_SIZE; };
|
||||
void FromSSU (uint32_t msgID) // we have received SSU message and convert it to regular
|
||||
@@ -180,64 +194,73 @@ namespace tunnel
|
||||
len = offset + I2NP_SHORT_HEADER_SIZE + bufbe16toh (header + I2NP_HEADER_SIZE_OFFSET);
|
||||
return bufbe32toh (header + I2NP_HEADER_MSGID_OFFSET);
|
||||
}
|
||||
|
||||
void FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID = 0);
|
||||
void RenewI2NPMessageHeader ();
|
||||
bool IsExpired () const;
|
||||
};
|
||||
|
||||
template<int sz>
|
||||
struct I2NPMessageBuffer: public I2NPMessage
|
||||
{
|
||||
I2NPMessageBuffer () { buf = m_Buffer; maxLen = sz; };
|
||||
uint8_t m_Buffer[sz];
|
||||
uint8_t m_Buffer[sz + 32]; // 16 alignment + 16 padding
|
||||
};
|
||||
|
||||
I2NPMessage * NewI2NPMessage ();
|
||||
I2NPMessage * NewI2NPShortMessage ();
|
||||
I2NPMessage * NewI2NPMessage (size_t len);
|
||||
void DeleteI2NPMessage (I2NPMessage * msg);
|
||||
void FillI2NPMessageHeader (I2NPMessage * msg, I2NPMessageType msgType, uint32_t replyMsgID = 0);
|
||||
void RenewI2NPMessageHeader (I2NPMessage * msg);
|
||||
I2NPMessage * CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, int len, uint32_t replyMsgID = 0);
|
||||
I2NPMessage * CreateI2NPMessage (const uint8_t * buf, int len, std::shared_ptr<i2p::tunnel::InboundTunnel> from = nullptr);
|
||||
std::shared_ptr<I2NPMessage> NewI2NPMessage ();
|
||||
std::shared_ptr<I2NPMessage> NewI2NPShortMessage ();
|
||||
std::shared_ptr<I2NPMessage> NewI2NPMessage (size_t len);
|
||||
|
||||
I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID);
|
||||
I2NPMessage * CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
|
||||
std::shared_ptr<I2NPMessage> CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID = 0);
|
||||
std::shared_ptr<I2NPMessage> CreateI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from = nullptr);
|
||||
std::shared_ptr<I2NPMessage> CopyI2NPMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
|
||||
std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg (uint32_t msgID);
|
||||
std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
|
||||
uint32_t replyTunnelID, bool exploratory = false, std::set<i2p::data::IdentHash> * excludedPeers = nullptr);
|
||||
I2NPMessage * CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
|
||||
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
|
||||
const std::set<i2p::data::IdentHash>& excludedFloodfills,
|
||||
const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag);
|
||||
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers);
|
||||
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag);
|
||||
std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers);
|
||||
|
||||
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::RouterInfo * router = nullptr, uint32_t replyToken = 0);
|
||||
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::LeaseSet * leaseSet, uint32_t replyToken = 0);
|
||||
|
||||
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, uint32_t replyToken = 0);
|
||||
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet); // for floodfill only
|
||||
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LocalLeaseSet> leaseSet, uint32_t replyToken = 0, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel = nullptr);
|
||||
bool IsRouterInfoMsg (std::shared_ptr<I2NPMessage> msg);
|
||||
|
||||
bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText);
|
||||
void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len);
|
||||
void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len);
|
||||
void HandleTunnelBuildMsg (uint8_t * buf, size_t len);
|
||||
|
||||
I2NPMessage * CreateTunnelDataMsg (const uint8_t * buf);
|
||||
I2NPMessage * CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload);
|
||||
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf);
|
||||
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload);
|
||||
std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg ();
|
||||
|
||||
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len);
|
||||
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType,
|
||||
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len);
|
||||
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType,
|
||||
const uint8_t * buf, size_t len, uint32_t replyMsgID = 0);
|
||||
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessage * msg);
|
||||
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg);
|
||||
|
||||
size_t GetI2NPMessageLength (const uint8_t * msg);
|
||||
void HandleI2NPMessage (uint8_t * msg, size_t len);
|
||||
void HandleI2NPMessage (I2NPMessage * msg);
|
||||
void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
|
||||
class I2NPMessagesHandler
|
||||
{
|
||||
public:
|
||||
|
||||
~I2NPMessagesHandler ();
|
||||
void PutNextMessage (I2NPMessage * msg);
|
||||
void PutNextMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void Flush ();
|
||||
|
||||
private:
|
||||
|
||||
std::vector<I2NPMessage *> m_TunnelMsgs, m_TunnelGatewayMsgs;
|
||||
std::vector<std::shared_ptr<I2NPMessage> > m_TunnelMsgs, m_TunnelGatewayMsgs;
|
||||
};
|
||||
|
||||
const uint16_t DEFAULT_MAX_NUM_TRANSIT_TUNNELS = 2500;
|
||||
void SetMaxNumTransitTunnels (uint16_t maxNumTransitTunnels);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
586
I2PControl.cpp
586
I2PControl.cpp
@@ -1,48 +1,93 @@
|
||||
// There is bug in boost 1.49 with gcc 4.7 coming with Debian Wheezy
|
||||
#define GCC47_BOOST149 ((BOOST_VERSION == 104900) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 7))
|
||||
|
||||
#include "I2PControl.h"
|
||||
#include <stdio.h>
|
||||
#include <sstream>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/date_time/local_time/local_time.hpp>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/ini_parser.hpp>
|
||||
|
||||
// There is bug in boost 1.49 with gcc 4.7 coming with Debian Wheezy
|
||||
#define GCC47_BOOST149 ((BOOST_VERSION == 104900) && (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7))
|
||||
#if !GCC47_BOOST149
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#endif
|
||||
|
||||
#include "Crypto.h"
|
||||
#include "FS.h"
|
||||
#include "Log.h"
|
||||
#include "HTTP.h"
|
||||
#include "Config.h"
|
||||
#include "NetDb.h"
|
||||
#include "RouterContext.h"
|
||||
#include "Daemon.h"
|
||||
#include "Tunnel.h"
|
||||
#include "Timestamp.h"
|
||||
#include "Transports.h"
|
||||
#include "version.h"
|
||||
#include "util.h"
|
||||
#include "I2PControl.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
I2PControlService::I2PControlService (int port):
|
||||
m_Password (I2P_CONTROL_DEFAULT_PASSWORD), m_IsRunning (false), m_Thread (nullptr),
|
||||
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
|
||||
I2PControlService::I2PControlService (const std::string& address, int port):
|
||||
m_IsRunning (false), m_Thread (nullptr),
|
||||
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)),
|
||||
m_SSLContext (m_Service, boost::asio::ssl::context::sslv23),
|
||||
m_ShutdownTimer (m_Service)
|
||||
{
|
||||
m_MethodHandlers[I2P_CONTROL_METHOD_AUTHENTICATE] = &I2PControlService::AuthenticateHandler;
|
||||
m_MethodHandlers[I2P_CONTROL_METHOD_ECHO] = &I2PControlService::EchoHandler;
|
||||
m_MethodHandlers[I2P_CONTROL_METHOD_I2PCONTROL] = &I2PControlService::I2PControlHandler;
|
||||
m_MethodHandlers[I2P_CONTROL_METHOD_ROUTER_INFO] = &I2PControlService::RouterInfoHandler;
|
||||
m_MethodHandlers[I2P_CONTROL_METHOD_ROUTER_MANAGER] = &I2PControlService::RouterManagerHandler;
|
||||
m_MethodHandlers[I2P_CONTROL_METHOD_NETWORK_SETTING] = &I2PControlService::NetworkSettingHandler;
|
||||
i2p::config::GetOption("i2pcontrol.password", m_Password);
|
||||
|
||||
// certificate / keys
|
||||
std::string i2pcp_crt; i2p::config::GetOption("i2pcontrol.cert", i2pcp_crt);
|
||||
std::string i2pcp_key; i2p::config::GetOption("i2pcontrol.key", i2pcp_key);
|
||||
|
||||
if (i2pcp_crt.at(0) != '/')
|
||||
i2pcp_crt = i2p::fs::DataDirPath(i2pcp_crt);
|
||||
if (i2pcp_key.at(0) != '/')
|
||||
i2pcp_key = i2p::fs::DataDirPath(i2pcp_key);
|
||||
if (!i2p::fs::Exists (i2pcp_crt) || !i2p::fs::Exists (i2pcp_key)) {
|
||||
LogPrint (eLogInfo, "I2PControl: creating new certificate for control connection");
|
||||
CreateCertificate (i2pcp_crt.c_str(), i2pcp_key.c_str());
|
||||
} else {
|
||||
LogPrint(eLogDebug, "I2PControl: using cert from ", i2pcp_crt);
|
||||
}
|
||||
m_SSLContext.set_options (boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use);
|
||||
m_SSLContext.use_certificate_file (i2pcp_crt, boost::asio::ssl::context::pem);
|
||||
m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem);
|
||||
|
||||
// handlers
|
||||
m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler;
|
||||
m_MethodHandlers["Echo"] = &I2PControlService::EchoHandler;
|
||||
m_MethodHandlers["I2PControl"] = &I2PControlService::I2PControlHandler;
|
||||
m_MethodHandlers["RouterInfo"] = &I2PControlService::RouterInfoHandler;
|
||||
m_MethodHandlers["RouterManager"] = &I2PControlService::RouterManagerHandler;
|
||||
m_MethodHandlers["NetworkSetting"] = &I2PControlService::NetworkSettingHandler;
|
||||
|
||||
// I2PControl
|
||||
m_I2PControlHandlers["i2pcontrol.password"] = &I2PControlService::PasswordHandler;
|
||||
|
||||
// RouterInfo
|
||||
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS] = &I2PControlService::NetDbKnownPeersHandler;
|
||||
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS] = &I2PControlService::NetDbActivePeersHandler;
|
||||
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING] = &I2PControlService::TunnelsParticipatingHandler;
|
||||
m_RouterInfoHandlers["i2p.router.uptime"] = &I2PControlService::UptimeHandler;
|
||||
m_RouterInfoHandlers["i2p.router.version"] = &I2PControlService::VersionHandler;
|
||||
m_RouterInfoHandlers["i2p.router.status"] = &I2PControlService::StatusHandler;
|
||||
m_RouterInfoHandlers["i2p.router.netdb.knownpeers"] = &I2PControlService::NetDbKnownPeersHandler;
|
||||
m_RouterInfoHandlers["i2p.router.netdb.activepeers"] = &I2PControlService::NetDbActivePeersHandler;
|
||||
m_RouterInfoHandlers["i2p.router.net.bw.inbound.1s"] = &I2PControlService::InboundBandwidth1S;
|
||||
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.total.received.bytes"] = &I2PControlService::NetTotalReceivedBytes;
|
||||
m_RouterInfoHandlers["i2p.router.net.total.sent.bytes"] = &I2PControlService::NetTotalSentBytes;
|
||||
|
||||
// RouterManager
|
||||
m_RouterManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN] = &I2PControlService::ShutdownHandler;
|
||||
m_RouterManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL] = &I2PControlService::ShutdownGracefulHandler;
|
||||
m_RouterManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_RESEED] = &I2PControlService::ReseedHandler;
|
||||
m_RouterManagerHandlers["Reseed"] = &I2PControlService::ReseedHandler;
|
||||
m_RouterManagerHandlers["Shutdown"] = &I2PControlService::ShutdownHandler;
|
||||
m_RouterManagerHandlers["ShutdownGraceful"] = &I2PControlService::ShutdownGracefulHandler;
|
||||
|
||||
// NetworkSetting
|
||||
m_NetworkSettingHandlers["i2p.router.net.bw.in"] = &I2PControlService::InboundBandwidthLimit;
|
||||
m_NetworkSettingHandlers["i2p.router.net.bw.out"] = &I2PControlService::OutboundBandwidthLimit;
|
||||
}
|
||||
|
||||
I2PControlService::~I2PControlService ()
|
||||
@@ -65,311 +110,454 @@ namespace client
|
||||
if (m_IsRunning)
|
||||
{
|
||||
m_IsRunning = false;
|
||||
m_Acceptor.cancel ();
|
||||
m_Acceptor.cancel ();
|
||||
m_Service.stop ();
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
{
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void I2PControlService::Run ()
|
||||
{
|
||||
void I2PControlService::Run ()
|
||||
{
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
try {
|
||||
m_Service.run ();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "I2PControl: ", ex.what ());
|
||||
} catch (std::exception& ex) {
|
||||
LogPrint (eLogError, "I2PControl: runtime exception: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void I2PControlService::Accept ()
|
||||
{
|
||||
auto newSocket = std::make_shared<boost::asio::ip::tcp::socket> (m_Service);
|
||||
m_Acceptor.async_accept (*newSocket, std::bind (&I2PControlService::HandleAccept, this,
|
||||
auto newSocket = std::make_shared<ssl_socket> (m_Service, m_SSLContext);
|
||||
m_Acceptor.async_accept (newSocket->lowest_layer(), std::bind (&I2PControlService::HandleAccept, this,
|
||||
std::placeholders::_1, newSocket));
|
||||
}
|
||||
|
||||
void I2PControlService::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket)
|
||||
void I2PControlService::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Accept ();
|
||||
|
||||
if (!ecode)
|
||||
{
|
||||
LogPrint (eLogInfo, "New I2PControl request from ", socket->remote_endpoint ());
|
||||
ReadRequest (socket);
|
||||
if (ecode) {
|
||||
LogPrint (eLogError, "I2PControl: accept error: ", ecode.message ());
|
||||
return;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "I2PControl accept error: ", ecode.message ());
|
||||
LogPrint (eLogDebug, "I2PControl: new request from ", socket->lowest_layer ().remote_endpoint ());
|
||||
Handshake (socket);
|
||||
}
|
||||
|
||||
void I2PControlService::ReadRequest (std::shared_ptr<boost::asio::ip::tcp::socket> socket)
|
||||
void I2PControlService::Handshake (std::shared_ptr<ssl_socket> socket)
|
||||
{
|
||||
socket->async_handshake(boost::asio::ssl::stream_base::server,
|
||||
std::bind( &I2PControlService::HandleHandshake, this, std::placeholders::_1, socket));
|
||||
}
|
||||
|
||||
void I2PControlService::HandleHandshake (const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket)
|
||||
{
|
||||
if (ecode) {
|
||||
LogPrint (eLogError, "I2PControl: handshake error: ", ecode.message ());
|
||||
return;
|
||||
}
|
||||
//std::this_thread::sleep_for (std::chrono::milliseconds(5));
|
||||
ReadRequest (socket);
|
||||
}
|
||||
|
||||
void I2PControlService::ReadRequest (std::shared_ptr<ssl_socket> socket)
|
||||
{
|
||||
auto request = std::make_shared<I2PControlBuffer>();
|
||||
socket->async_read_some (
|
||||
#if BOOST_VERSION >= 104900
|
||||
boost::asio::buffer (*request),
|
||||
#if defined(BOOST_ASIO_HAS_STD_ARRAY)
|
||||
boost::asio::buffer (*request),
|
||||
#else
|
||||
boost::asio::buffer (request->data (), request->size ()),
|
||||
#endif
|
||||
std::bind(&I2PControlService::HandleRequestReceived, this,
|
||||
boost::asio::buffer (request->data (), request->size ()),
|
||||
#endif
|
||||
std::bind(&I2PControlService::HandleRequestReceived, this,
|
||||
std::placeholders::_1, std::placeholders::_2, socket, request));
|
||||
}
|
||||
|
||||
void I2PControlService::HandleRequestReceived (const boost::system::error_code& ecode,
|
||||
size_t bytes_transferred, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
||||
size_t bytes_transferred, std::shared_ptr<ssl_socket> socket,
|
||||
std::shared_ptr<I2PControlBuffer> buf)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "I2PControl read error: ", ecode.message ());
|
||||
if (ecode) {
|
||||
LogPrint (eLogError, "I2PControl: read error: ", ecode.message ());
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
bool isHtml = !memcmp (buf->data (), "POST", 4);
|
||||
std::stringstream ss;
|
||||
ss.write (buf->data (), bytes_transferred);
|
||||
if (isHtml)
|
||||
{
|
||||
std::string header;
|
||||
while (!ss.eof () && header != "\r")
|
||||
std::getline(ss, header);
|
||||
if (ss.eof ())
|
||||
{
|
||||
LogPrint (eLogError, "Malformed I2PControl request. HTTP header expected");
|
||||
return; // TODO:
|
||||
}
|
||||
}
|
||||
/* 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());
|
||||
#if GCC47_BOOST149
|
||||
LogPrint (eLogError, "json_read is not supported due bug in boost 1.49 with gcc 4.7");
|
||||
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");
|
||||
#else
|
||||
boost::property_tree::ptree pt;
|
||||
boost::property_tree::read_json (ss, pt);
|
||||
/* now try to parse json itself */
|
||||
try {
|
||||
boost::property_tree::ptree pt;
|
||||
boost::property_tree::read_json (json, pt);
|
||||
|
||||
std::string method = pt.get<std::string>(I2P_CONTROL_PROPERTY_METHOD);
|
||||
auto it = m_MethodHandlers.find (method);
|
||||
if (it != m_MethodHandlers.end ())
|
||||
{
|
||||
std::map<std::string, std::string> params;
|
||||
for (auto& v: pt.get_child (I2P_CONTROL_PROPERTY_PARAMS))
|
||||
{
|
||||
LogPrint (eLogInfo, v.first);
|
||||
if (!v.first.empty())
|
||||
params[v.first] = v.second.data ();
|
||||
}
|
||||
std::map<std::string, std::string> results;
|
||||
(this->*(it->second))(params, results);
|
||||
SendResponse (socket, buf, pt.get<std::string>(I2P_CONTROL_PROPERTY_ID), results, isHtml);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Unknown I2PControl method ", method);
|
||||
#endif
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "I2PControl handle request: ", ex.what ());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LogPrint (eLogError, "I2PControl handle request unknown exception");
|
||||
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");
|
||||
}
|
||||
#endif
|
||||
SendResponse (socket, buf, response, isHTTP);
|
||||
}
|
||||
|
||||
void I2PControlService::SendResponse (std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
||||
std::shared_ptr<I2PControlBuffer> buf, const std::string& id,
|
||||
const std::map<std::string, std::string>& results, bool isHtml)
|
||||
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, int value) const
|
||||
{
|
||||
boost::property_tree::ptree ptr;
|
||||
for (auto& result: results)
|
||||
ptr.put (boost::property_tree::ptree::path_type (result.first, '/'), result.second);
|
||||
ss << "\"" << name << "\":" << value;
|
||||
}
|
||||
|
||||
boost::property_tree::ptree pt;
|
||||
pt.put (I2P_CONTROL_PROPERTY_ID, id);
|
||||
pt.put_child (I2P_CONTROL_PROPERTY_RESULT, ptr);
|
||||
pt.put ("jsonrpc", "2.0");
|
||||
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value) const
|
||||
{
|
||||
ss << "\"" << name << "\":";
|
||||
if (value.length () > 0)
|
||||
ss << "\"" << value << "\"";
|
||||
else
|
||||
ss << "null";
|
||||
}
|
||||
|
||||
std::ostringstream ss;
|
||||
#if GCC47_BOOST149
|
||||
LogPrint (eLogError, "json_write is not supported due bug in boost 1.49 with gcc 4.7");
|
||||
#else
|
||||
boost::property_tree::write_json (ss, pt, false);
|
||||
#endif
|
||||
size_t len = ss.str ().length (), offset = 0;
|
||||
if (isHtml)
|
||||
{
|
||||
std::ostringstream header;
|
||||
header << "HTTP/1.1 200 OK\r\n";
|
||||
header << "Connection: close\r\n";
|
||||
header << "Content-Length: " << boost::lexical_cast<std::string>(len) << "\r\n";
|
||||
header << "Content-Type: application/json\r\n";
|
||||
header << "Date: ";
|
||||
auto facet = new boost::local_time::local_time_facet ("%a, %d %b %Y %H:%M:%S GMT");
|
||||
header.imbue(std::locale (header.getloc(), facet));
|
||||
header << boost::posix_time::second_clock::local_time() << "\r\n";
|
||||
header << "\r\n";
|
||||
offset = header.str ().size ();
|
||||
memcpy (buf->data (), header.str ().c_str (), offset);
|
||||
}
|
||||
memcpy (buf->data () + offset, ss.str ().c_str (), len);
|
||||
boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), len),
|
||||
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, double value) const
|
||||
{
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
std::copy(content.begin(), content.end(), buf->begin());
|
||||
boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), content.length()),
|
||||
boost::asio::transfer_all (),
|
||||
std::bind(&I2PControlService::HandleResponseSent, this,
|
||||
std::bind(&I2PControlService::HandleResponseSent, this,
|
||||
std::placeholders::_1, std::placeholders::_2, socket, buf));
|
||||
}
|
||||
|
||||
void I2PControlService::HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred,
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> socket, std::shared_ptr<I2PControlBuffer> buf)
|
||||
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf)
|
||||
{
|
||||
if (ecode)
|
||||
LogPrint (eLogError, "I2PControl write error: ", ecode.message ());
|
||||
socket->close ();
|
||||
if (ecode) {
|
||||
LogPrint (eLogError, "I2PControl: write error: ", ecode.message ());
|
||||
}
|
||||
}
|
||||
|
||||
// handlers
|
||||
|
||||
void I2PControlService::AuthenticateHandler (const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results)
|
||||
void I2PControlService::AuthenticateHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
||||
{
|
||||
const std::string& api = params.at (I2P_CONTROL_PARAM_API);
|
||||
const std::string& password = params.at (I2P_CONTROL_PARAM_PASSWORD);
|
||||
LogPrint (eLogDebug, "I2PControl Authenticate API=", api, " Password=", password);
|
||||
if (password != m_Password)
|
||||
LogPrint (eLogError, "I2PControl Authenticate Invalid password ", password, " expected ", m_Password);
|
||||
results[I2P_CONTROL_PARAM_API] = api;
|
||||
results[I2P_CONTROL_PARAM_TOKEN] = boost::lexical_cast<std::string>(i2p::util::GetSecondsSinceEpoch ());
|
||||
int api = params.get<int> ("API");
|
||||
auto password = params.get<std::string> ("Password");
|
||||
LogPrint (eLogDebug, "I2PControl: Authenticate API=", api, " Password=", password);
|
||||
if (password != m_Password) {
|
||||
LogPrint (eLogError, "I2PControl: Authenticate - Invalid password: ", password);
|
||||
return;
|
||||
}
|
||||
InsertParam (results, "API", api);
|
||||
results << ",";
|
||||
std::string token = std::to_string(i2p::util::GetSecondsSinceEpoch ());
|
||||
m_Tokens.insert (token);
|
||||
InsertParam (results, "Token", token);
|
||||
}
|
||||
|
||||
void I2PControlService::EchoHandler (const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results)
|
||||
void I2PControlService::EchoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
||||
{
|
||||
const std::string& echo = params.at (I2P_CONTROL_PARAM_ECHO);
|
||||
auto echo = params.get<std::string> ("Echo");
|
||||
LogPrint (eLogDebug, "I2PControl Echo Echo=", echo);
|
||||
results[I2P_CONTROL_PARAM_RESULT] = echo;
|
||||
InsertParam (results, "Result", echo);
|
||||
}
|
||||
|
||||
|
||||
// I2PControl
|
||||
|
||||
void I2PControlService::I2PControlHandler (const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results)
|
||||
void I2PControlService::I2PControlHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
||||
{
|
||||
LogPrint (eLogDebug, "I2PControl I2PControl");
|
||||
for (auto& it: params)
|
||||
{
|
||||
LogPrint (eLogDebug, it.first);
|
||||
LogPrint (eLogDebug, "I2PControl: I2PControl request: ", it.first);
|
||||
auto it1 = m_I2PControlHandlers.find (it.first);
|
||||
if (it1 != m_I2PControlHandlers.end ())
|
||||
(this->*(it1->second))(it.second);
|
||||
{
|
||||
(this->*(it1->second))(it.second.data ());
|
||||
InsertParam (results, it.first, "");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "I2PControl NetworkSetting unknown request ", it.first);
|
||||
LogPrint (eLogError, "I2PControl: I2PControl unknown request: ", it.first);
|
||||
}
|
||||
}
|
||||
|
||||
void I2PControlService::PasswordHandler (const std::string& value)
|
||||
{
|
||||
LogPrint (eLogWarning, "I2PControl: new password=", value, ", to make it persistent you should update your config!");
|
||||
m_Password = value;
|
||||
m_Tokens.clear ();
|
||||
}
|
||||
|
||||
// RouterInfo
|
||||
|
||||
void I2PControlService::RouterInfoHandler (const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results)
|
||||
void I2PControlService::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
||||
{
|
||||
LogPrint (eLogDebug, "I2PControl RouterInfo");
|
||||
for (auto& it: params)
|
||||
for (auto it = params.begin (); it != params.end (); ++it)
|
||||
{
|
||||
LogPrint (eLogDebug, it.first);
|
||||
auto it1 = m_RouterInfoHandlers.find (it.first);
|
||||
LogPrint (eLogDebug, "I2PControl: RouterInfo request: ", it->first);
|
||||
auto it1 = m_RouterInfoHandlers.find (it->first);
|
||||
if (it1 != m_RouterInfoHandlers.end ())
|
||||
(this->*(it1->second))(results);
|
||||
{
|
||||
if (it != params.begin ()) results << ",";
|
||||
(this->*(it1->second))(results);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "I2PControl RouterInfo unknown request ", it.first);
|
||||
|
||||
LogPrint (eLogError, "I2PControl: RouterInfo unknown request ", it->first);
|
||||
}
|
||||
}
|
||||
|
||||
void I2PControlService::NetDbKnownPeersHandler (std::map<std::string, std::string>& results)
|
||||
void I2PControlService::UptimeHandler (std::ostringstream& results)
|
||||
{
|
||||
results[I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS] = boost::lexical_cast<std::string>(i2p::data::netdb.GetNumRouters ());
|
||||
InsertParam (results, "i2p.router.uptime", (int)i2p::context.GetUptime ()*1000);
|
||||
}
|
||||
|
||||
void I2PControlService::NetDbActivePeersHandler (std::map<std::string, std::string>& results)
|
||||
void I2PControlService::VersionHandler (std::ostringstream& results)
|
||||
{
|
||||
results[I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS] = boost::lexical_cast<std::string>(i2p::transport::transports.GetPeers ().size ());
|
||||
InsertParam (results, "i2p.router.version", VERSION);
|
||||
}
|
||||
|
||||
void I2PControlService::StatusHandler (std::ostringstream& results)
|
||||
{
|
||||
InsertParam (results, "i2p.router.status", "???"); // TODO:
|
||||
}
|
||||
|
||||
void I2PControlService::TunnelsParticipatingHandler (std::map<std::string, std::string>& results)
|
||||
void I2PControlService::NetDbKnownPeersHandler (std::ostringstream& results)
|
||||
{
|
||||
results[I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING] = boost::lexical_cast<std::string>(i2p::tunnel::tunnels.GetTransitTunnels ().size ());;
|
||||
InsertParam (results, "i2p.router.netdb.knownpeers", i2p::data::netdb.GetNumRouters ());
|
||||
}
|
||||
|
||||
void I2PControlService::NetDbActivePeersHandler (std::ostringstream& results)
|
||||
{
|
||||
InsertParam (results, "i2p.router.netdb.activepeers", (int)i2p::transport::transports.GetPeers ().size ());
|
||||
}
|
||||
|
||||
void I2PControlService::NetStatusHandler (std::ostringstream& results)
|
||||
{
|
||||
InsertParam (results, "i2p.router.net.status", (int)i2p::context.GetStatus ());
|
||||
}
|
||||
|
||||
void I2PControlService::TunnelsParticipatingHandler (std::ostringstream& results)
|
||||
{
|
||||
int transit = i2p::tunnel::tunnels.GetTransitTunnels ().size ();
|
||||
InsertParam (results, "i2p.router.net.tunnels.participating", transit);
|
||||
}
|
||||
|
||||
void I2PControlService::InboundBandwidth1S (std::ostringstream& results)
|
||||
{
|
||||
double bw = i2p::transport::transports.GetInBandwidth ();
|
||||
InsertParam (results, "i2p.router.net.bw.inbound.1s", bw);
|
||||
}
|
||||
|
||||
void I2PControlService::OutboundBandwidth1S (std::ostringstream& results)
|
||||
{
|
||||
double bw = i2p::transport::transports.GetOutBandwidth ();
|
||||
InsertParam (results, "i2p.router.net.bw.outbound.1s", bw);
|
||||
}
|
||||
|
||||
void I2PControlService::NetTotalReceivedBytes (std::ostringstream& results)
|
||||
{
|
||||
InsertParam (results, "i2p.router.net.total.received.bytes", (double)i2p::transport::transports.GetTotalReceivedBytes ());
|
||||
}
|
||||
|
||||
void I2PControlService::NetTotalSentBytes (std::ostringstream& results)
|
||||
{
|
||||
InsertParam (results, "i2p.router.net.total.sent.bytes", (double)i2p::transport::transports.GetTotalSentBytes ());
|
||||
}
|
||||
|
||||
// RouterManager
|
||||
|
||||
void I2PControlService::RouterManagerHandler (const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results)
|
||||
|
||||
void I2PControlService::RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
||||
{
|
||||
LogPrint (eLogDebug, "I2PControl RouterManager");
|
||||
for (auto& it: params)
|
||||
for (auto it = params.begin (); it != params.end (); ++it)
|
||||
{
|
||||
LogPrint (eLogDebug, it.first);
|
||||
auto it1 = m_RouterManagerHandlers.find (it.first);
|
||||
if (it1 != m_RouterManagerHandlers.end ())
|
||||
if (it != params.begin ()) results << ",";
|
||||
LogPrint (eLogDebug, "I2PControl: RouterManager request: ", it->first);
|
||||
auto it1 = m_RouterManagerHandlers.find (it->first);
|
||||
if (it1 != m_RouterManagerHandlers.end ()) {
|
||||
(this->*(it1->second))(results);
|
||||
else
|
||||
LogPrint (eLogError, "I2PControl RouterManager unknown request ", it.first);
|
||||
} else
|
||||
LogPrint (eLogError, "I2PControl: RouterManager unknown request: ", it->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void I2PControlService::ShutdownHandler (std::map<std::string, std::string>& results)
|
||||
void I2PControlService::ShutdownHandler (std::ostringstream& results)
|
||||
{
|
||||
LogPrint (eLogInfo, "Shutdown requested");
|
||||
results[I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN] = "";
|
||||
LogPrint (eLogInfo, "I2PControl: Shutdown requested");
|
||||
InsertParam (results, "Shutdown", "");
|
||||
m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(1)); // 1 second to make sure response has been sent
|
||||
m_ShutdownTimer.async_wait (
|
||||
[](const boost::system::error_code& ecode)
|
||||
{
|
||||
Daemon.running = 0;
|
||||
Daemon.running = 0;
|
||||
});
|
||||
}
|
||||
|
||||
void I2PControlService::ShutdownGracefulHandler (std::map<std::string, std::string>& results)
|
||||
void I2PControlService::ShutdownGracefulHandler (std::ostringstream& results)
|
||||
{
|
||||
i2p::context.SetAcceptsTunnels (false);
|
||||
int timeout = i2p::tunnel::tunnels.GetTransitTunnelsExpirationTimeout ();
|
||||
LogPrint (eLogInfo, "Graceful shutdown requested. Will shutdown after ", timeout, " seconds");
|
||||
results[I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL] = "";
|
||||
LogPrint (eLogInfo, "I2PControl: Graceful shutdown requested, ", timeout, " seconds remains");
|
||||
InsertParam (results, "ShutdownGraceful", "");
|
||||
m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(timeout + 1)); // + 1 second
|
||||
m_ShutdownTimer.async_wait (
|
||||
[](const boost::system::error_code& ecode)
|
||||
{
|
||||
Daemon.running = 0;
|
||||
Daemon.running = 0;
|
||||
});
|
||||
}
|
||||
|
||||
void I2PControlService::ReseedHandler (std::map<std::string, std::string>& results)
|
||||
void I2PControlService::ReseedHandler (std::ostringstream& results)
|
||||
{
|
||||
LogPrint (eLogInfo, "Reseed requested");
|
||||
results[I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN] = "";
|
||||
LogPrint (eLogInfo, "I2PControl: Reseed requested");
|
||||
InsertParam (results, "Reseed", "");
|
||||
i2p::data::netdb.Reseed ();
|
||||
}
|
||||
|
||||
// network setting
|
||||
void I2PControlService::NetworkSettingHandler (const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results)
|
||||
void I2PControlService::NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
||||
{
|
||||
LogPrint (eLogDebug, "I2PControl NetworkSetting");
|
||||
for (auto& it: params)
|
||||
for (auto it = params.begin (); it != params.end (); ++it)
|
||||
{
|
||||
LogPrint (eLogDebug, it.first);
|
||||
auto it1 = m_NetworkSettingHandlers.find (it.first);
|
||||
if (it1 != m_NetworkSettingHandlers.end ())
|
||||
(this->*(it1->second))(it.second, results);
|
||||
else
|
||||
LogPrint (eLogError, "I2PControl NetworkSetting unknown request ", it.first);
|
||||
if (it != params.begin ()) results << ",";
|
||||
LogPrint (eLogDebug, "I2PControl: NetworkSetting request: ", it->first);
|
||||
auto it1 = m_NetworkSettingHandlers.find (it->first);
|
||||
if (it1 != m_NetworkSettingHandlers.end ()) {
|
||||
(this->*(it1->second))(it->second.data (), results);
|
||||
} else
|
||||
LogPrint (eLogError, "I2PControl: NetworkSetting unknown request: ", it->first);
|
||||
}
|
||||
}
|
||||
|
||||
void I2PControlService::InboundBandwidthLimit (const std::string& value, std::ostringstream& results)
|
||||
{
|
||||
if (value != "null")
|
||||
i2p::context.SetBandwidth (std::atoi(value.c_str()));
|
||||
int bw = i2p::context.GetBandwidthLimit();
|
||||
InsertParam (results, "i2p.router.net.bw.in", bw);
|
||||
}
|
||||
|
||||
void I2PControlService::OutboundBandwidthLimit (const std::string& value, std::ostringstream& results)
|
||||
{
|
||||
if (value != "null")
|
||||
i2p::context.SetBandwidth (std::atoi(value.c_str()));
|
||||
int bw = i2p::context.GetBandwidthLimit();
|
||||
InsertParam (results, "i2p.router.net.bw.out", bw);
|
||||
}
|
||||
|
||||
// certificate
|
||||
void I2PControlService::CreateCertificate (const char *crt_path, const char *key_path)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
EVP_PKEY * pkey = EVP_PKEY_new ();
|
||||
RSA * rsa = RSA_new ();
|
||||
BIGNUM * e = BN_dup (i2p::crypto::GetRSAE ());
|
||||
RSA_generate_key_ex (rsa, 4096, e, NULL);
|
||||
BN_free (e);
|
||||
if (rsa)
|
||||
{
|
||||
EVP_PKEY_assign_RSA (pkey, rsa);
|
||||
X509 * x509 = X509_new ();
|
||||
ASN1_INTEGER_set (X509_get_serialNumber (x509), 1);
|
||||
X509_gmtime_adj (X509_get_notBefore (x509), 0);
|
||||
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, "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
|
||||
X509_sign (x509, pkey, EVP_sha1 ()); // sign
|
||||
|
||||
// save cert
|
||||
if ((f = fopen (crt_path, "wb")) != NULL) {
|
||||
LogPrint (eLogInfo, "I2PControl: saving new cert to ", crt_path);
|
||||
PEM_write_X509 (f, x509);
|
||||
fclose (f);
|
||||
} else {
|
||||
LogPrint (eLogError, "I2PControl: can't write cert: ", strerror(errno));
|
||||
}
|
||||
|
||||
// save key
|
||||
if ((f = fopen (key_path, "wb")) != NULL) {
|
||||
LogPrint (eLogInfo, "I2PControl: saving cert key to ", key_path);
|
||||
PEM_write_PrivateKey (f, pkey, NULL, NULL, 0, NULL, NULL);
|
||||
fclose (f);
|
||||
} else {
|
||||
LogPrint (eLogError, "I2PControl: can't write key: ", strerror(errno));
|
||||
}
|
||||
|
||||
X509_free (x509);
|
||||
} else {
|
||||
LogPrint (eLogError, "I2PControl: can't create RSA key for certificate");
|
||||
}
|
||||
EVP_PKEY_free (pkey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
123
I2PControl.h
123
I2PControl.h
@@ -6,58 +6,30 @@
|
||||
#include <memory>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
const size_t I2P_CONTROL_MAX_REQUEST_SIZE = 1024;
|
||||
typedef std::array<char, I2P_CONTROL_MAX_REQUEST_SIZE> I2PControlBuffer;
|
||||
typedef std::array<char, I2P_CONTROL_MAX_REQUEST_SIZE> I2PControlBuffer;
|
||||
|
||||
const char I2P_CONTROL_DEFAULT_PASSWORD[] = "itoopie";
|
||||
|
||||
const char I2P_CONTROL_PROPERTY_ID[] = "id";
|
||||
const char I2P_CONTROL_PROPERTY_METHOD[] = "method";
|
||||
const char I2P_CONTROL_PROPERTY_PARAMS[] = "params";
|
||||
const char I2P_CONTROL_PROPERTY_RESULT[] = "result";
|
||||
|
||||
// methods
|
||||
const char I2P_CONTROL_METHOD_AUTHENTICATE[] = "Authenticate";
|
||||
const char I2P_CONTROL_METHOD_ECHO[] = "Echo";
|
||||
const char I2P_CONTROL_METHOD_I2PCONTROL[] = "I2PControl";
|
||||
const char I2P_CONTROL_METHOD_ROUTER_INFO[] = "RouterInfo";
|
||||
const char I2P_CONTROL_METHOD_ROUTER_MANAGER[] = "RouterManager";
|
||||
const char I2P_CONTROL_METHOD_NETWORK_SETTING[] = "NetworkSetting";
|
||||
|
||||
// params
|
||||
const char I2P_CONTROL_PARAM_API[] = "API";
|
||||
const char I2P_CONTROL_PARAM_PASSWORD[] = "Password";
|
||||
const char I2P_CONTROL_PARAM_TOKEN[] = "Token";
|
||||
const char I2P_CONTROL_PARAM_ECHO[] = "Echo";
|
||||
const char I2P_CONTROL_PARAM_RESULT[] = "Result";
|
||||
|
||||
// I2PControl
|
||||
const char I2P_CONTROL_I2PCONTROL_ADDRESS[] = "i2pcontrol.address";
|
||||
const char I2P_CONTROL_I2PCONTROL_PASSWORD[] = "i2pcontrol.password";
|
||||
const char I2P_CONTROL_I2PCONTROL_PORT[] = "i2pcontrol.port";
|
||||
|
||||
// RouterInfo requests
|
||||
const char I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS[] = "i2p.router.netdb.knownpeers";
|
||||
const char I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS[] = "i2p.router.netdb.activepeers";
|
||||
const char I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING[] = "i2p.router.net.tunnels.participating";
|
||||
|
||||
// RouterManager requests
|
||||
const char I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN[] = "Shutdown";
|
||||
const char I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL[] = "ShutdownGraceful";
|
||||
const char I2P_CONTROL_ROUTER_MANAGER_RESEED[] = "Reseed";
|
||||
const long I2P_CONTROL_CERTIFICATE_VALIDITY = 365*10; // 10 years
|
||||
const char I2P_CONTROL_CERTIFICATE_COMMON_NAME[] = "i2pd.i2pcontrol";
|
||||
const char I2P_CONTROL_CERTIFICATE_ORGANIZATION[] = "Purple I2P";
|
||||
|
||||
class I2PControlService
|
||||
{
|
||||
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket;
|
||||
public:
|
||||
|
||||
I2PControlService (int port);
|
||||
I2PControlService (const std::string& address, int port);
|
||||
~I2PControlService ();
|
||||
|
||||
void Start ();
|
||||
@@ -67,56 +39,77 @@ namespace client
|
||||
|
||||
void Run ();
|
||||
void Accept ();
|
||||
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket);
|
||||
void ReadRequest (std::shared_ptr<boost::asio::ip::tcp::socket> socket);
|
||||
void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred,
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> socket, std::shared_ptr<I2PControlBuffer> buf);
|
||||
void SendResponse (std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
||||
std::shared_ptr<I2PControlBuffer> buf, const std::string& id,
|
||||
const std::map<std::string, std::string>& results, bool isHtml);
|
||||
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket);
|
||||
void Handshake (std::shared_ptr<ssl_socket> socket);
|
||||
void HandleHandshake (const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket);
|
||||
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);
|
||||
void HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred,
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> socket, std::shared_ptr<I2PControlBuffer> buf);
|
||||
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);
|
||||
|
||||
void CreateCertificate (const char *crt_path, const char *key_path);
|
||||
|
||||
private:
|
||||
|
||||
// methods
|
||||
typedef void (I2PControlService::*MethodHandler)(const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results);
|
||||
void InsertParam (std::ostringstream& ss, const std::string& name, int value) const;
|
||||
void InsertParam (std::ostringstream& ss, const std::string& name, double value) const;
|
||||
void InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value) const;
|
||||
|
||||
void AuthenticateHandler (const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results);
|
||||
void EchoHandler (const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results);
|
||||
void I2PControlHandler (const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results);
|
||||
void RouterInfoHandler (const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results);
|
||||
void RouterManagerHandler (const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results);
|
||||
void NetworkSettingHandler (const std::map<std::string, std::string>& params, std::map<std::string, std::string>& results);
|
||||
// methods
|
||||
typedef void (I2PControlService::*MethodHandler)(const boost::property_tree::ptree& params, std::ostringstream& results);
|
||||
|
||||
void AuthenticateHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
|
||||
void EchoHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
|
||||
void I2PControlHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
|
||||
void RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
|
||||
void RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
|
||||
void NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
|
||||
|
||||
// I2PControl
|
||||
typedef void (I2PControlService::*I2PControlRequestHandler)(const std::string& value);
|
||||
void PasswordHandler (const std::string& value);
|
||||
|
||||
// RouterInfo
|
||||
typedef void (I2PControlService::*RouterInfoRequestHandler)(std::map<std::string, std::string>& results);
|
||||
void NetDbKnownPeersHandler (std::map<std::string, std::string>& results);
|
||||
void NetDbActivePeersHandler (std::map<std::string, std::string>& results);
|
||||
void TunnelsParticipatingHandler (std::map<std::string, std::string>& results);
|
||||
typedef void (I2PControlService::*RouterInfoRequestHandler)(std::ostringstream& results);
|
||||
void UptimeHandler (std::ostringstream& results);
|
||||
void VersionHandler (std::ostringstream& results);
|
||||
void StatusHandler (std::ostringstream& results);
|
||||
void NetDbKnownPeersHandler (std::ostringstream& results);
|
||||
void NetDbActivePeersHandler (std::ostringstream& results);
|
||||
void NetStatusHandler (std::ostringstream& results);
|
||||
void TunnelsParticipatingHandler (std::ostringstream& results);
|
||||
void InboundBandwidth1S (std::ostringstream& results);
|
||||
void OutboundBandwidth1S (std::ostringstream& results);
|
||||
void NetTotalReceivedBytes (std::ostringstream& results);
|
||||
void NetTotalSentBytes (std::ostringstream& results);
|
||||
|
||||
// RouterManager
|
||||
typedef void (I2PControlService::*RouterManagerRequestHandler)(std::map<std::string, std::string>& results);
|
||||
void ShutdownHandler (std::map<std::string, std::string>& results);
|
||||
void ShutdownGracefulHandler (std::map<std::string, std::string>& results);
|
||||
void ReseedHandler (std::map<std::string, std::string>& results);
|
||||
typedef void (I2PControlService::*RouterManagerRequestHandler)(std::ostringstream& results);
|
||||
void ShutdownHandler (std::ostringstream& results);
|
||||
void ShutdownGracefulHandler (std::ostringstream& results);
|
||||
void ReseedHandler (std::ostringstream& results);
|
||||
|
||||
// NetworkSetting
|
||||
typedef void (I2PControlService::*NetworkSettingRequestHandler)(const std::string& value, std::map<std::string, std::string>& results);
|
||||
typedef void (I2PControlService::*NetworkSettingRequestHandler)(const std::string& value, std::ostringstream& results);
|
||||
void InboundBandwidthLimit (const std::string& value, std::ostringstream& results);
|
||||
void OutboundBandwidthLimit (const std::string& value, std::ostringstream& results);
|
||||
|
||||
private:
|
||||
|
||||
std::string m_Password;
|
||||
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::ssl::context m_SSLContext;
|
||||
boost::asio::deadline_timer m_ShutdownTimer;
|
||||
|
||||
std::set<std::string> m_Tokens;
|
||||
|
||||
std::map<std::string, MethodHandler> m_MethodHandlers;
|
||||
std::map<std::string, I2PControlRequestHandler> m_I2PControlHandlers;
|
||||
std::map<std::string, RouterInfoRequestHandler> m_RouterInfoHandlers;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD_kernel__)
|
||||
#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__)
|
||||
#include <endian.h>
|
||||
#elif __FreeBSD__
|
||||
#include <sys/endian.h>
|
||||
|
||||
155
I2PService.cpp
155
I2PService.cpp
@@ -3,14 +3,13 @@
|
||||
#include "ClientContext.h"
|
||||
#include "I2PService.h"
|
||||
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
static const i2p::data::SigningKeyType I2P_SERVICE_DEFAULT_KEY_TYPE = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256;
|
||||
|
||||
I2PService::I2PService (ClientDestination * localDestination):
|
||||
I2PService::I2PService (std::shared_ptr<ClientDestination> localDestination):
|
||||
m_LocalDestination (localDestination ? localDestination :
|
||||
i2p::client::context.CreateNewLocalDestination (false, I2P_SERVICE_DEFAULT_KEY_TYPE))
|
||||
{
|
||||
@@ -28,11 +27,143 @@ namespace client
|
||||
m_LocalDestination->CreateStream (streamRequestComplete, identHash, port);
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Remote destination ", dest, " not found");
|
||||
LogPrint (eLogWarning, "I2PService: Remote destination not found: ", dest);
|
||||
streamRequestComplete (nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
Terminate();
|
||||
}
|
||||
|
||||
void TCPIPPipe::Start()
|
||||
{
|
||||
AsyncReceiveUpstream();
|
||||
AsyncReceiveDownstream();
|
||||
}
|
||||
|
||||
void TCPIPPipe::Terminate()
|
||||
{
|
||||
if(Kill()) return;
|
||||
Done(shared_from_this());
|
||||
if (m_up) {
|
||||
if (m_up->is_open()) {
|
||||
m_up->close();
|
||||
}
|
||||
m_up = nullptr;
|
||||
}
|
||||
if (m_down) {
|
||||
if (m_down->is_open()) {
|
||||
m_down->close();
|
||||
}
|
||||
m_down = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void TCPIPPipe::AsyncReceiveUpstream()
|
||||
{
|
||||
if (m_up) {
|
||||
m_up->async_read_some(boost::asio::buffer(m_upstream_to_down_buf, TCP_IP_PIPE_BUFFER_SIZE),
|
||||
std::bind(&TCPIPPipe::HandleUpstreamReceived, shared_from_this(),
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
} else {
|
||||
LogPrint(eLogError, "TCPIPPipe: upstream receive: no socket");
|
||||
}
|
||||
}
|
||||
|
||||
void TCPIPPipe::AsyncReceiveDownstream()
|
||||
{
|
||||
if (m_down) {
|
||||
m_down->async_read_some(boost::asio::buffer(m_downstream_to_up_buf, TCP_IP_PIPE_BUFFER_SIZE),
|
||||
std::bind(&TCPIPPipe::HandleDownstreamReceived, shared_from_this(),
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
} else {
|
||||
LogPrint(eLogError, "TCPIPPipe: downstream receive: no socket");
|
||||
}
|
||||
}
|
||||
|
||||
void TCPIPPipe::UpstreamWrite(const uint8_t * buf, 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::transfer_all(),
|
||||
std::bind(&TCPIPPipe::HandleUpstreamWrite,
|
||||
shared_from_this(),
|
||||
std::placeholders::_1)
|
||||
);
|
||||
} else {
|
||||
LogPrint(eLogError, "TCPIPPipe: upstream write: no socket");
|
||||
}
|
||||
}
|
||||
|
||||
void TCPIPPipe::DownstreamWrite(const uint8_t * buf, 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::transfer_all(),
|
||||
std::bind(&TCPIPPipe::HandleDownstreamWrite,
|
||||
shared_from_this(),
|
||||
std::placeholders::_1)
|
||||
);
|
||||
} else {
|
||||
LogPrint(eLogError, "TCPIPPipe: downstream write: no socket");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TCPIPPipe::HandleDownstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered)
|
||||
{
|
||||
LogPrint(eLogDebug, "TCPIPPipe: downstream: ", (int) bytes_transfered, " bytes received");
|
||||
if (ecode) {
|
||||
LogPrint(eLogError, "TCPIPPipe: downstream read error:" , ecode.message());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate();
|
||||
} else {
|
||||
if (bytes_transfered > 0 ) {
|
||||
memcpy(m_upstream_buf, m_downstream_to_up_buf, bytes_transfered);
|
||||
UpstreamWrite(m_upstream_buf, bytes_transfered);
|
||||
}
|
||||
AsyncReceiveDownstream();
|
||||
}
|
||||
}
|
||||
|
||||
void TCPIPPipe::HandleDownstreamWrite(const boost::system::error_code & ecode) {
|
||||
if (ecode) {
|
||||
LogPrint(eLogError, "TCPIPPipe: downstream write error:" , ecode.message());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate();
|
||||
}
|
||||
}
|
||||
|
||||
void TCPIPPipe::HandleUpstreamWrite(const boost::system::error_code & ecode) {
|
||||
if (ecode) {
|
||||
LogPrint(eLogError, "TCPIPPipe: upstream write error:" , ecode.message());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate();
|
||||
}
|
||||
}
|
||||
|
||||
void TCPIPPipe::HandleUpstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered)
|
||||
{
|
||||
LogPrint(eLogDebug, "TCPIPPipe: upstream ", (int)bytes_transfered, " bytes received");
|
||||
if (ecode) {
|
||||
LogPrint(eLogError, "TCPIPPipe: upstream read error:" , ecode.message());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate();
|
||||
} else {
|
||||
if (bytes_transfered > 0 ) {
|
||||
memcpy(m_upstream_buf, m_upstream_to_down_buf, bytes_transfered);
|
||||
DownstreamWrite(m_upstream_buf, bytes_transfered);
|
||||
}
|
||||
AsyncReceiveUpstream();
|
||||
}
|
||||
}
|
||||
|
||||
void TCPIPAcceptor::Start ()
|
||||
{
|
||||
m_Acceptor.listen ();
|
||||
@@ -48,33 +179,31 @@ namespace client
|
||||
|
||||
void TCPIPAcceptor::Accept ()
|
||||
{
|
||||
auto newSocket = new boost::asio::ip::tcp::socket (GetService ());
|
||||
auto newSocket = std::make_shared<boost::asio::ip::tcp::socket> (GetService ());
|
||||
m_Acceptor.async_accept (*newSocket, std::bind (&TCPIPAcceptor::HandleAccept, this,
|
||||
std::placeholders::_1, newSocket));
|
||||
}
|
||||
|
||||
void TCPIPAcceptor::HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket)
|
||||
void TCPIPAcceptor::HandleAccept (const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket)
|
||||
{
|
||||
if (!ecode)
|
||||
{
|
||||
LogPrint(eLogDebug,"--- ",GetName()," accepted");
|
||||
LogPrint(eLogDebug, "I2PService: ", GetName(), " accepted");
|
||||
auto handler = CreateHandler(socket);
|
||||
if (handler) {
|
||||
if (handler)
|
||||
{
|
||||
AddHandler(handler);
|
||||
handler->Handle();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
socket->close();
|
||||
delete socket;
|
||||
}
|
||||
Accept();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
LogPrint (eLogError,"--- ",GetName()," Closing socket on accept because: ", ecode.message ());
|
||||
delete socket;
|
||||
LogPrint (eLogError, "I2PService: ", GetName(), " closing socket on accept because: ", ecode.message ());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
47
I2PService.h
47
I2PService.h
@@ -17,7 +17,7 @@ namespace client
|
||||
class I2PService
|
||||
{
|
||||
public:
|
||||
I2PService (ClientDestination * localDestination = nullptr);
|
||||
I2PService (std::shared_ptr<ClientDestination> localDestination = nullptr);
|
||||
I2PService (i2p::data::SigningKeyType kt);
|
||||
virtual ~I2PService () { ClearHandlers (); }
|
||||
|
||||
@@ -37,8 +37,8 @@ namespace client
|
||||
m_Handlers.clear();
|
||||
}
|
||||
|
||||
inline ClientDestination * GetLocalDestination () { return m_LocalDestination; }
|
||||
inline void SetLocalDestination (ClientDestination * dest) { m_LocalDestination = dest; }
|
||||
inline std::shared_ptr<ClientDestination> GetLocalDestination () { return m_LocalDestination; }
|
||||
inline void SetLocalDestination (std::shared_ptr<ClientDestination> dest) { m_LocalDestination = dest; }
|
||||
void CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port = 0);
|
||||
|
||||
inline boost::asio::io_service& GetService () { return m_LocalDestination->GetService (); }
|
||||
@@ -49,7 +49,7 @@ namespace client
|
||||
virtual const char* GetName() { return "Generic I2P Service"; }
|
||||
private:
|
||||
|
||||
ClientDestination * m_LocalDestination;
|
||||
std::shared_ptr<ClientDestination> m_LocalDestination;
|
||||
std::unordered_set<std::shared_ptr<I2PServiceHandler> > m_Handlers;
|
||||
std::mutex m_HandlersMutex;
|
||||
};
|
||||
@@ -76,30 +76,57 @@ namespace client
|
||||
std::atomic<bool> m_Dead; //To avoid cleaning up multiple times
|
||||
};
|
||||
|
||||
const size_t TCP_IP_PIPE_BUFFER_SIZE = 8192;
|
||||
|
||||
// bidirectional pipe for 2 tcp/ip sockets
|
||||
class TCPIPPipe: public I2PServiceHandler, public std::enable_shared_from_this<TCPIPPipe> {
|
||||
public:
|
||||
TCPIPPipe(I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> upstream, std::shared_ptr<boost::asio::ip::tcp::socket> downstream);
|
||||
~TCPIPPipe();
|
||||
void Start();
|
||||
protected:
|
||||
void Terminate();
|
||||
void AsyncReceiveUpstream();
|
||||
void AsyncReceiveDownstream();
|
||||
void HandleUpstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transferred);
|
||||
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);
|
||||
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];
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> m_up, m_down;
|
||||
};
|
||||
|
||||
/* TODO: support IPv6 too */
|
||||
//This is a service that listens for connections on the IP network and interacts with I2P
|
||||
class TCPIPAcceptor: public I2PService
|
||||
{
|
||||
public:
|
||||
TCPIPAcceptor (int port, ClientDestination * localDestination = nullptr) :
|
||||
TCPIPAcceptor (const std::string& address, int port, std::shared_ptr<ClientDestination> localDestination = nullptr) :
|
||||
I2PService(localDestination),
|
||||
m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
|
||||
m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string(address), port)),
|
||||
m_Timer (GetService ()) {}
|
||||
TCPIPAcceptor (int port, i2p::data::SigningKeyType kt) :
|
||||
TCPIPAcceptor (const std::string& address, int port, i2p::data::SigningKeyType kt) :
|
||||
I2PService(kt),
|
||||
m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
|
||||
m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string(address), port)),
|
||||
m_Timer (GetService ()) {}
|
||||
virtual ~TCPIPAcceptor () { TCPIPAcceptor::Stop(); }
|
||||
//If you override this make sure you call it from the children
|
||||
void Start ();
|
||||
//If you override this make sure you call it from the children
|
||||
void Stop ();
|
||||
|
||||
const boost::asio::ip::tcp::acceptor& GetAcceptor () const { return m_Acceptor; };
|
||||
|
||||
protected:
|
||||
virtual std::shared_ptr<I2PServiceHandler> CreateHandler(boost::asio::ip::tcp::socket * socket) = 0;
|
||||
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, boost::asio::ip::tcp::socket * socket);
|
||||
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket);
|
||||
boost::asio::ip::tcp::acceptor m_Acceptor;
|
||||
boost::asio::deadline_timer m_Timer;
|
||||
};
|
||||
|
||||
315
I2PTunnel.cpp
315
I2PTunnel.cpp
@@ -1,5 +1,5 @@
|
||||
#include <cassert>
|
||||
#include "base64.h"
|
||||
#include "Base.h"
|
||||
#include "Log.h"
|
||||
#include "Destination.h"
|
||||
#include "ClientContext.h"
|
||||
@@ -9,23 +9,23 @@ namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner,
|
||||
boost::asio::ip::tcp::socket * socket, std::shared_ptr<const i2p::data::LeaseSet> leaseSet):
|
||||
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
||||
std::shared_ptr<const i2p::data::LeaseSet> leaseSet, int port):
|
||||
I2PServiceHandler(owner), m_Socket (socket), m_RemoteEndpoint (socket->remote_endpoint ()),
|
||||
m_IsQuiet (true)
|
||||
{
|
||||
m_Stream = GetOwner()->GetLocalDestination ()->CreateStream (leaseSet);
|
||||
m_Stream = GetOwner()->GetLocalDestination ()->CreateStream (leaseSet, port);
|
||||
}
|
||||
|
||||
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner,
|
||||
boost::asio::ip::tcp::socket * socket, std::shared_ptr<i2p::stream::Stream> stream):
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> socket, std::shared_ptr<i2p::stream::Stream> stream):
|
||||
I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream),
|
||||
m_RemoteEndpoint (socket->remote_endpoint ()), m_IsQuiet (true)
|
||||
{
|
||||
}
|
||||
|
||||
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
|
||||
boost::asio::ip::tcp::socket * socket, const boost::asio::ip::tcp::endpoint& target, bool quiet):
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> socket, const boost::asio::ip::tcp::endpoint& target, bool quiet):
|
||||
I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream),
|
||||
m_RemoteEndpoint (target), m_IsQuiet (quiet)
|
||||
{
|
||||
@@ -33,7 +33,6 @@ namespace client
|
||||
|
||||
I2PTunnelConnection::~I2PTunnelConnection ()
|
||||
{
|
||||
delete m_Socket;
|
||||
}
|
||||
|
||||
void I2PTunnelConnection::I2PConnect (const uint8_t * msg, size_t len)
|
||||
@@ -79,15 +78,24 @@ namespace client
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("I2PTunnel read error: ", ecode.message ());
|
||||
LogPrint (eLogError, "I2PTunnel: read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Stream)
|
||||
m_Stream->Send (m_Buffer, bytes_transferred);
|
||||
Receive ();
|
||||
{
|
||||
auto s = shared_from_this ();
|
||||
m_Stream->AsyncSend (m_Buffer, bytes_transferred,
|
||||
[s](const boost::system::error_code& ecode)
|
||||
{
|
||||
if (!ecode)
|
||||
s->Receive ();
|
||||
else
|
||||
s->Terminate ();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +103,7 @@ namespace client
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("I2PTunnel write error: ", ecode.message ());
|
||||
LogPrint (eLogError, "I2PTunnel: write error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
@@ -106,43 +114,66 @@ namespace client
|
||||
void I2PTunnelConnection::StreamReceive ()
|
||||
{
|
||||
if (m_Stream)
|
||||
m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
|
||||
std::bind (&I2PTunnelConnection::HandleStreamReceive, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2),
|
||||
I2P_TUNNEL_CONNECTION_MAX_IDLE);
|
||||
{
|
||||
if (m_Stream->GetStatus () == i2p::stream::eStreamStatusNew ||
|
||||
m_Stream->GetStatus () == i2p::stream::eStreamStatusOpen) // regular
|
||||
{
|
||||
m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
|
||||
std::bind (&I2PTunnelConnection::HandleStreamReceive, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2),
|
||||
I2P_TUNNEL_CONNECTION_MAX_IDLE);
|
||||
}
|
||||
else // closed by peer
|
||||
{
|
||||
// get remaning data
|
||||
auto len = m_Stream->ReadSome (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE);
|
||||
if (len > 0) // still some data
|
||||
Write (m_StreamBuffer, len);
|
||||
else // no more data
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void I2PTunnelConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("I2PTunnel stream read error: ", ecode.message ());
|
||||
LogPrint (eLogError, "I2PTunnel: stream read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
{
|
||||
if (bytes_transferred > 0)
|
||||
Write (m_StreamBuffer, bytes_transferred); // postpone termination
|
||||
else
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::asio::async_write (*m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred),
|
||||
std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
Write (m_StreamBuffer, bytes_transferred);
|
||||
}
|
||||
|
||||
void I2PTunnelConnection::Write (const uint8_t * buf, size_t len)
|
||||
{
|
||||
boost::asio::async_write (*m_Socket, boost::asio::buffer (buf, len), boost::asio::transfer_all (),
|
||||
std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
|
||||
void I2PTunnelConnection::HandleConnect (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("I2PTunnel connect error: ", ecode.message ());
|
||||
LogPrint (eLogError, "I2PTunnel: connect error: ", ecode.message ());
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("I2PTunnel connected");
|
||||
LogPrint (eLogDebug, "I2PTunnel: connected");
|
||||
if (m_IsQuiet)
|
||||
StreamReceive ();
|
||||
else
|
||||
{
|
||||
// send destination first like received from I2P
|
||||
std::string dest = m_Stream->GetRemoteIdentity ().ToBase64 ();
|
||||
std::string dest = m_Stream->GetRemoteIdentity ()->ToBase64 ();
|
||||
dest += "\n";
|
||||
memcpy (m_StreamBuffer, dest.c_str (), dest.size ());
|
||||
HandleStreamReceive (boost::system::error_code (), dest.size ());
|
||||
@@ -151,25 +182,127 @@ namespace client
|
||||
}
|
||||
}
|
||||
|
||||
I2PTunnelConnectionHTTP::I2PTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
||||
const boost::asio::ip::tcp::endpoint& target, const std::string& host):
|
||||
I2PTunnelConnection (owner, stream, socket, target), m_Host (host), m_HeaderSent (false), m_From (stream->GetRemoteIdentity ())
|
||||
{
|
||||
}
|
||||
|
||||
void I2PTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len)
|
||||
{
|
||||
if (m_HeaderSent)
|
||||
I2PTunnelConnection::Write (buf, len);
|
||||
else
|
||||
{
|
||||
m_InHeader.clear ();
|
||||
m_InHeader.write ((const char *)buf, len);
|
||||
std::string line;
|
||||
bool endOfHeader = false;
|
||||
while (!endOfHeader)
|
||||
{
|
||||
std::getline(m_InHeader, line);
|
||||
if (!m_InHeader.fail ())
|
||||
{
|
||||
if (line == "\r") endOfHeader = true;
|
||||
else
|
||||
{
|
||||
if (line.find ("Host:") != std::string::npos)
|
||||
m_OutHeader << "Host: " << m_Host << "\r\n";
|
||||
else
|
||||
m_OutHeader << line << "\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
// add X-I2P fields
|
||||
if (m_From)
|
||||
{
|
||||
m_OutHeader << X_I2P_DEST_B32 << ": " << context.GetAddressBook ().ToAddress(m_From->GetIdentHash ()) << "\r\n";
|
||||
m_OutHeader << X_I2P_DEST_HASH << ": " << m_From->GetIdentHash ().ToBase64 () << "\r\n";
|
||||
m_OutHeader << X_I2P_DEST_B64 << ": " << m_From->ToBase64 () << "\r\n";
|
||||
}
|
||||
|
||||
if (endOfHeader)
|
||||
{
|
||||
m_OutHeader << "\r\n"; // end of header
|
||||
m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header
|
||||
m_HeaderSent = true;
|
||||
I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
I2PTunnelConnectionIRC::I2PTunnelConnectionIRC (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& webircpass):
|
||||
I2PTunnelConnection (owner, stream, socket, target), m_From (stream->GetRemoteIdentity ()),
|
||||
m_NeedsWebIrc (webircpass.length() ? true : false), m_WebircPass (webircpass)
|
||||
{
|
||||
}
|
||||
|
||||
void I2PTunnelConnectionIRC::Write (const uint8_t * buf, size_t len)
|
||||
{
|
||||
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 ());
|
||||
}
|
||||
|
||||
std::string line;
|
||||
m_OutPacket.str ("");
|
||||
m_InPacket.clear ();
|
||||
m_InPacket.write ((const char *)buf, len);
|
||||
|
||||
while (!m_InPacket.eof () && !m_InPacket.fail ())
|
||||
{
|
||||
std::getline (m_InPacket, line);
|
||||
if (line.length () == 0 && m_InPacket.eof ()) {
|
||||
m_InPacket.str ("");
|
||||
}
|
||||
auto pos = line.find ("USER");
|
||||
if (pos != std::string::npos && pos == 0)
|
||||
{
|
||||
pos = line.find (" ");
|
||||
pos++;
|
||||
pos = line.find (" ", pos);
|
||||
pos++;
|
||||
auto nextpos = line.find (" ", pos);
|
||||
m_OutPacket << line.substr (0, pos);
|
||||
m_OutPacket << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ());
|
||||
m_OutPacket << line.substr (nextpos) << '\n';
|
||||
} else {
|
||||
m_OutPacket << line << '\n';
|
||||
}
|
||||
}
|
||||
I2PTunnelConnection::Write ((uint8_t *)m_OutPacket.str ().c_str (), m_OutPacket.str ().length ());
|
||||
}
|
||||
|
||||
|
||||
/* This handler tries to stablish a connection with the desired server and dies if it fails to do so */
|
||||
class I2PClientTunnelHandler: public I2PServiceHandler, public std::enable_shared_from_this<I2PClientTunnelHandler>
|
||||
{
|
||||
public:
|
||||
I2PClientTunnelHandler (I2PClientTunnel * parent, i2p::data::IdentHash destination,
|
||||
boost::asio::ip::tcp::socket * socket):
|
||||
I2PServiceHandler(parent), m_DestinationIdentHash(destination), m_Socket(socket) {}
|
||||
int destinationPort, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
|
||||
I2PServiceHandler(parent), m_DestinationIdentHash(destination),
|
||||
m_DestinationPort (destinationPort), m_Socket(socket) {};
|
||||
void Handle();
|
||||
void Terminate();
|
||||
private:
|
||||
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
|
||||
i2p::data::IdentHash m_DestinationIdentHash;
|
||||
boost::asio::ip::tcp::socket * m_Socket;
|
||||
int m_DestinationPort;
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
|
||||
};
|
||||
|
||||
void I2PClientTunnelHandler::Handle()
|
||||
{
|
||||
GetOwner()->GetLocalDestination ()->CreateStream (std::bind (&I2PClientTunnelHandler::HandleStreamRequestComplete,
|
||||
shared_from_this(), std::placeholders::_1), m_DestinationIdentHash);
|
||||
GetOwner()->GetLocalDestination ()->CreateStream (
|
||||
std::bind (&I2PClientTunnelHandler::HandleStreamRequestComplete, shared_from_this(), std::placeholders::_1),
|
||||
m_DestinationIdentHash, m_DestinationPort);
|
||||
}
|
||||
|
||||
void I2PClientTunnelHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
|
||||
@@ -177,7 +310,7 @@ namespace client
|
||||
if (stream)
|
||||
{
|
||||
if (Kill()) return;
|
||||
LogPrint (eLogInfo,"New I2PTunnel connection");
|
||||
LogPrint (eLogDebug, "I2PTunnel: new connection");
|
||||
auto connection = std::make_shared<I2PTunnelConnection>(GetOwner(), m_Socket, stream);
|
||||
GetOwner()->AddHandler (connection);
|
||||
connection->I2PConnect ();
|
||||
@@ -185,7 +318,7 @@ namespace client
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError,"I2P Client Tunnel Issue when creating the stream, check the previous warnings for more info.");
|
||||
LogPrint (eLogError, "I2PTunnel: Client Tunnel Issue when creating the stream, check the previous warnings for more info.");
|
||||
Terminate();
|
||||
}
|
||||
}
|
||||
@@ -196,15 +329,17 @@ namespace client
|
||||
if (m_Socket)
|
||||
{
|
||||
m_Socket->close();
|
||||
delete m_Socket;
|
||||
m_Socket = nullptr;
|
||||
}
|
||||
Done(shared_from_this());
|
||||
}
|
||||
|
||||
I2PClientTunnel::I2PClientTunnel (const std::string& destination, int port, ClientDestination * localDestination):
|
||||
TCPIPAcceptor (port,localDestination), m_Destination (destination), m_DestinationIdentHash (nullptr)
|
||||
{}
|
||||
I2PClientTunnel::I2PClientTunnel (const std::string& name, const std::string& destination,
|
||||
const std::string& address, int port, std::shared_ptr<ClientDestination> localDestination, int destinationPort):
|
||||
TCPIPAcceptor (address, port, localDestination), m_Name (name), m_Destination (destination),
|
||||
m_DestinationIdentHash (nullptr), m_DestinationPort (destinationPort)
|
||||
{
|
||||
}
|
||||
|
||||
void I2PClientTunnel::Start ()
|
||||
{
|
||||
@@ -229,28 +364,44 @@ namespace client
|
||||
if (i2p::client::context.GetAddressBook ().GetIdentHash (m_Destination, identHash))
|
||||
m_DestinationIdentHash = new i2p::data::IdentHash (identHash);
|
||||
else
|
||||
LogPrint (eLogWarning,"Remote destination ", m_Destination, " not found");
|
||||
LogPrint (eLogWarning, "I2PTunnel: Remote destination ", m_Destination, " not found");
|
||||
}
|
||||
return m_DestinationIdentHash;
|
||||
}
|
||||
|
||||
std::shared_ptr<I2PServiceHandler> I2PClientTunnel::CreateHandler(boost::asio::ip::tcp::socket * socket)
|
||||
std::shared_ptr<I2PServiceHandler> I2PClientTunnel::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
|
||||
{
|
||||
const i2p::data::IdentHash *identHash = GetIdentHash();
|
||||
if (identHash)
|
||||
return std::make_shared<I2PClientTunnelHandler>(this, *identHash, socket);
|
||||
return std::make_shared<I2PClientTunnelHandler>(this, *identHash, m_DestinationPort, socket);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
I2PServerTunnel::I2PServerTunnel (const std::string& address, int port, ClientDestination * localDestination):
|
||||
I2PService (localDestination), m_Endpoint (boost::asio::ip::address::from_string (address), port)
|
||||
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)
|
||||
{
|
||||
m_PortDestination = localDestination->CreateStreamingDestination (inport > 0 ? inport : port, gzip);
|
||||
}
|
||||
|
||||
void I2PServerTunnel::Start ()
|
||||
{
|
||||
Accept ();
|
||||
m_Endpoint.port (m_Port);
|
||||
boost::system::error_code ec;
|
||||
auto addr = boost::asio::ip::address::from_string (m_Address, ec);
|
||||
if (!ec)
|
||||
{
|
||||
m_Endpoint.address (addr);
|
||||
Accept ();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto resolver = std::make_shared<boost::asio::ip::tcp::resolver>(GetService ());
|
||||
resolver->async_resolve (boost::asio::ip::tcp::resolver::query (m_Address, ""),
|
||||
std::bind (&I2PServerTunnel::HandleResolve, this,
|
||||
std::placeholders::_1, std::placeholders::_2, resolver));
|
||||
}
|
||||
}
|
||||
|
||||
void I2PServerTunnel::Stop ()
|
||||
@@ -258,23 +409,95 @@ namespace client
|
||||
ClearHandlers ();
|
||||
}
|
||||
|
||||
void I2PServerTunnel::HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
|
||||
std::shared_ptr<boost::asio::ip::tcp::resolver> resolver)
|
||||
{
|
||||
if (!ecode)
|
||||
{
|
||||
auto addr = (*it).endpoint ().address ();
|
||||
LogPrint (eLogInfo, "I2PTunnel: server tunnel ", (*it).host_name (), " has been resolved to ", addr);
|
||||
m_Endpoint.address (addr);
|
||||
Accept ();
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "I2PTunnel: Unable to resolve server tunnel address: ", ecode.message ());
|
||||
}
|
||||
|
||||
void I2PServerTunnel::SetAccessList (const std::set<i2p::data::IdentHash>& accessList)
|
||||
{
|
||||
m_AccessList = accessList;
|
||||
m_IsAccessList = true;
|
||||
}
|
||||
|
||||
void I2PServerTunnel::Accept ()
|
||||
{
|
||||
if (m_PortDestination)
|
||||
m_PortDestination->SetAcceptor (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1));
|
||||
|
||||
auto localDestination = GetLocalDestination ();
|
||||
if (localDestination)
|
||||
localDestination->AcceptStreams (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1));
|
||||
{
|
||||
if (!localDestination->IsAcceptingStreams ()) // set it as default if not set yet
|
||||
localDestination->AcceptStreams (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1));
|
||||
}
|
||||
else
|
||||
LogPrint ("Local destination not set for server tunnel");
|
||||
LogPrint (eLogError, "I2PTunnel: Local destination not set for server tunnel");
|
||||
}
|
||||
|
||||
void I2PServerTunnel::HandleAccept (std::shared_ptr<i2p::stream::Stream> stream)
|
||||
{
|
||||
if (stream)
|
||||
{
|
||||
auto conn = std::make_shared<I2PTunnelConnection> (this, stream, new boost::asio::ip::tcp::socket (GetService ()), m_Endpoint);
|
||||
AddHandler (conn);
|
||||
conn->Connect ();
|
||||
if (m_IsAccessList)
|
||||
{
|
||||
if (!m_AccessList.count (stream->GetRemoteIdentity ()->GetIdentHash ()))
|
||||
{
|
||||
LogPrint (eLogWarning, "I2PTunnel: Address ", stream->GetRemoteIdentity ()->GetIdentHash ().ToBase32 (), " is not in white list. Incoming connection dropped");
|
||||
stream->Close ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
CreateI2PConnection (stream);
|
||||
}
|
||||
}
|
||||
|
||||
void I2PServerTunnel::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
|
||||
{
|
||||
auto conn = std::make_shared<I2PTunnelConnection> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint ());
|
||||
AddHandler (conn);
|
||||
conn->Connect ();
|
||||
}
|
||||
|
||||
I2PServerTunnelHTTP::I2PServerTunnelHTTP (const std::string& 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)
|
||||
{
|
||||
}
|
||||
|
||||
void I2PServerTunnelHTTP::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
|
||||
{
|
||||
auto conn = std::make_shared<I2PTunnelConnectionHTTP> (this, stream,
|
||||
std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint (), m_Host);
|
||||
AddHandler (conn);
|
||||
conn->Connect ();
|
||||
}
|
||||
|
||||
I2PServerTunnelIRC::I2PServerTunnelIRC (const std::string& name, const std::string& address,
|
||||
int port, std::shared_ptr<ClientDestination> localDestination,
|
||||
const std::string& webircpass, int inport, bool gzip):
|
||||
I2PServerTunnel (name, address, port, localDestination, inport, gzip),
|
||||
m_WebircPass (webircpass)
|
||||
{
|
||||
}
|
||||
|
||||
void 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 ();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
133
I2PTunnel.h
133
I2PTunnel.h
@@ -5,6 +5,7 @@
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <boost/asio.hpp>
|
||||
#include "Identity.h"
|
||||
#include "Destination.h"
|
||||
@@ -18,28 +19,32 @@ namespace client
|
||||
const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 8192;
|
||||
const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds
|
||||
const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds
|
||||
|
||||
// for HTTP tunnels
|
||||
const char X_I2P_DEST_HASH[] = "X-I2P-DestHash"; // hash in base64
|
||||
const char X_I2P_DEST_B64[] = "X-I2P-DestB64"; // full address in base64
|
||||
const char X_I2P_DEST_B32[] = "X-I2P-DestB32"; // .b32.i2p address
|
||||
|
||||
class I2PTunnelConnection: public I2PServiceHandler, public std::enable_shared_from_this<I2PTunnelConnection>
|
||||
{
|
||||
public:
|
||||
|
||||
I2PTunnelConnection (I2PService * owner, boost::asio::ip::tcp::socket * socket,
|
||||
std::shared_ptr<const i2p::data::LeaseSet> leaseSet); // to I2P
|
||||
I2PTunnelConnection (I2PService * owner, boost::asio::ip::tcp::socket * socket,
|
||||
std::shared_ptr<i2p::stream::Stream> stream); // to I2P using simplified API :)
|
||||
I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream, boost::asio::ip::tcp::socket * socket,
|
||||
I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
||||
std::shared_ptr<const i2p::data::LeaseSet> leaseSet, int port = 0); // to I2P
|
||||
I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
||||
std::shared_ptr<i2p::stream::Stream> stream); // to I2P using simplified API
|
||||
I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
||||
const boost::asio::ip::tcp::endpoint& target, bool quiet = true); // from I2P
|
||||
~I2PTunnelConnection ();
|
||||
void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0);
|
||||
void Connect ();
|
||||
|
||||
private:
|
||||
protected:
|
||||
|
||||
void Terminate ();
|
||||
|
||||
void Receive ();
|
||||
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
virtual void Write (const uint8_t * buf, size_t len); // can be overloaded
|
||||
void HandleWrite (const boost::system::error_code& ecode);
|
||||
|
||||
void StreamReceive ();
|
||||
@@ -49,54 +54,154 @@ namespace client
|
||||
private:
|
||||
|
||||
uint8_t m_Buffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE], m_StreamBuffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE];
|
||||
boost::asio::ip::tcp::socket * m_Socket;
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
|
||||
std::shared_ptr<i2p::stream::Stream> m_Stream;
|
||||
boost::asio::ip::tcp::endpoint m_RemoteEndpoint;
|
||||
bool m_IsQuiet; // don't send destination
|
||||
};
|
||||
|
||||
class I2PTunnelConnectionHTTP: public I2PTunnelConnection
|
||||
{
|
||||
public:
|
||||
|
||||
I2PTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
||||
const boost::asio::ip::tcp::endpoint& target, const std::string& host);
|
||||
|
||||
protected:
|
||||
|
||||
void Write (const uint8_t * buf, size_t len);
|
||||
|
||||
private:
|
||||
|
||||
std::string m_Host;
|
||||
std::stringstream m_InHeader, m_OutHeader;
|
||||
bool m_HeaderSent;
|
||||
std::shared_ptr<const i2p::data::IdentityEx> m_From;
|
||||
};
|
||||
|
||||
class I2PTunnelConnectionIRC: public I2PTunnelConnection
|
||||
{
|
||||
public:
|
||||
|
||||
I2PTunnelConnectionIRC (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& m_WebircPass);
|
||||
|
||||
protected:
|
||||
|
||||
void Write (const uint8_t * buf, size_t len);
|
||||
|
||||
private:
|
||||
|
||||
std::shared_ptr<const i2p::data::IdentityEx> m_From;
|
||||
std::stringstream m_OutPacket, m_InPacket;
|
||||
bool m_NeedsWebIrc;
|
||||
std::string m_WebircPass;
|
||||
};
|
||||
|
||||
|
||||
class I2PClientTunnel: public TCPIPAcceptor
|
||||
{
|
||||
protected:
|
||||
|
||||
// Implements TCPIPAcceptor
|
||||
std::shared_ptr<I2PServiceHandler> CreateHandler(boost::asio::ip::tcp::socket * socket);
|
||||
const char* GetName() { return "I2P Client Tunnel"; }
|
||||
std::shared_ptr<I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket);
|
||||
|
||||
public:
|
||||
|
||||
I2PClientTunnel (const std::string& destination, int port, ClientDestination * localDestination = nullptr);
|
||||
I2PClientTunnel (const std::string& name, const std::string& destination,
|
||||
const std::string& address, int port, std::shared_ptr<ClientDestination> localDestination, int destinationPort = 0);
|
||||
~I2PClientTunnel () {}
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
const char* GetName() { return m_Name.c_str (); }
|
||||
|
||||
private:
|
||||
|
||||
const i2p::data::IdentHash * GetIdentHash ();
|
||||
|
||||
std::string m_Destination;
|
||||
private:
|
||||
|
||||
std::string m_Name, m_Destination;
|
||||
const i2p::data::IdentHash * m_DestinationIdentHash;
|
||||
int m_DestinationPort;
|
||||
};
|
||||
|
||||
class I2PServerTunnel: public I2PService
|
||||
{
|
||||
public:
|
||||
|
||||
I2PServerTunnel (const std::string& address, int port, ClientDestination * localDestination);
|
||||
I2PServerTunnel (const std::string& name, const std::string& address, int port,
|
||||
std::shared_ptr<ClientDestination> localDestination, int inport = 0, bool gzip = true);
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
void SetAccessList (const std::set<i2p::data::IdentHash>& accessList);
|
||||
|
||||
const std::string& GetAddress() const { return m_Address; }
|
||||
int GetPort () const { return m_Port; };
|
||||
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 HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
|
||||
std::shared_ptr<boost::asio::ip::tcp::resolver> resolver);
|
||||
|
||||
void Accept ();
|
||||
void HandleAccept (std::shared_ptr<i2p::stream::Stream> stream);
|
||||
virtual void CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
|
||||
|
||||
private:
|
||||
|
||||
boost::asio::ip::tcp::endpoint m_Endpoint;
|
||||
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;
|
||||
};
|
||||
|
||||
class I2PServerTunnelHTTP: public I2PServerTunnel
|
||||
{
|
||||
public:
|
||||
|
||||
I2PServerTunnelHTTP (const std::string& name, const std::string& address, int port,
|
||||
std::shared_ptr<ClientDestination> localDestination, const std::string& host,
|
||||
int inport = 0, bool gzip = true);
|
||||
|
||||
private:
|
||||
|
||||
void CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
|
||||
|
||||
private:
|
||||
|
||||
std::string m_Host;
|
||||
};
|
||||
|
||||
class I2PServerTunnelIRC: public I2PServerTunnel
|
||||
{
|
||||
public:
|
||||
|
||||
I2PServerTunnelIRC (const std::string& name, const std::string& address, int port,
|
||||
std::shared_ptr<ClientDestination> localDestination, const std::string& webircpass,
|
||||
int inport = 0, bool gzip = true);
|
||||
|
||||
private:
|
||||
|
||||
void CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
|
||||
|
||||
private:
|
||||
|
||||
std::string m_WebircPass;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
215
Identity.cpp
215
Identity.cpp
@@ -1,14 +1,9 @@
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <cryptopp/sha.h>
|
||||
#include <cryptopp/osrng.h>
|
||||
#include <cryptopp/dsa.h>
|
||||
#include "base64.h"
|
||||
#include "CryptoConst.h"
|
||||
#include "ElGamal.h"
|
||||
#include "RouterContext.h"
|
||||
#include "Identity.h"
|
||||
#include "Crypto.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "Log.h"
|
||||
#include "Identity.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -18,12 +13,16 @@ namespace data
|
||||
{
|
||||
// copy public and signing keys together
|
||||
memcpy (publicKey, keys.publicKey, sizeof (publicKey) + sizeof (signingKey));
|
||||
memset (&certificate, 0, sizeof (certificate));
|
||||
memset (certificate, 0, sizeof (certificate));
|
||||
return *this;
|
||||
}
|
||||
|
||||
size_t Identity::FromBuffer (const uint8_t * buf, size_t len)
|
||||
{
|
||||
if ( len < DEFAULT_IDENTITY_SIZE ) {
|
||||
// buffer too small, don't overflow
|
||||
return 0;
|
||||
}
|
||||
memcpy (publicKey, buf, DEFAULT_IDENTITY_SIZE);
|
||||
return DEFAULT_IDENTITY_SIZE;
|
||||
}
|
||||
@@ -31,12 +30,12 @@ namespace data
|
||||
IdentHash Identity::Hash () const
|
||||
{
|
||||
IdentHash hash;
|
||||
CryptoPP::SHA256().CalculateDigest(hash, publicKey, DEFAULT_IDENTITY_SIZE);
|
||||
SHA256(publicKey, DEFAULT_IDENTITY_SIZE, hash);
|
||||
return hash;
|
||||
}
|
||||
|
||||
IdentityEx::IdentityEx ():
|
||||
m_Verifier (nullptr), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -52,12 +51,14 @@ namespace data
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
|
||||
{
|
||||
size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64
|
||||
RAND_bytes (m_StandardIdentity.signingKey, padding);
|
||||
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP256_KEY_LENGTH);
|
||||
break;
|
||||
}
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
|
||||
{
|
||||
size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96
|
||||
RAND_bytes (m_StandardIdentity.signingKey, padding);
|
||||
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP384_KEY_LENGTH);
|
||||
break;
|
||||
}
|
||||
@@ -92,14 +93,21 @@ namespace data
|
||||
excessBuf = new uint8_t[excessLen];
|
||||
memcpy (excessBuf, signingKey + 128, excessLen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
|
||||
{
|
||||
size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32
|
||||
RAND_bytes (m_StandardIdentity.signingKey, padding);
|
||||
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LogPrint ("Signing key type ", (int)type, " is not supported");
|
||||
LogPrint (eLogError, "Identity: Signing key type ", (int)type, " is not supported");
|
||||
}
|
||||
m_ExtendedLen = 4 + excessLen; // 4 bytes extra + excess length
|
||||
// fill certificate
|
||||
m_StandardIdentity.certificate.type = CERTIFICATE_TYPE_KEY;
|
||||
m_StandardIdentity.certificate.length = htobe16 (m_ExtendedLen);
|
||||
m_StandardIdentity.certificate[0] = CERTIFICATE_TYPE_KEY;
|
||||
htobe16buf (m_StandardIdentity.certificate + 1, m_ExtendedLen);
|
||||
// fill extended buffer
|
||||
m_ExtendedBuffer = new uint8_t[m_ExtendedLen];
|
||||
htobe16buf (m_ExtendedBuffer, type);
|
||||
@@ -112,13 +120,13 @@ namespace data
|
||||
// calculate ident hash
|
||||
uint8_t * buf = new uint8_t[GetFullLen ()];
|
||||
ToBuffer (buf, GetFullLen ());
|
||||
CryptoPP::SHA256().CalculateDigest(m_IdentHash, buf, GetFullLen ());
|
||||
SHA256(buf, GetFullLen (), m_IdentHash);
|
||||
delete[] buf;
|
||||
}
|
||||
else // DSA-SHA1
|
||||
{
|
||||
memcpy (m_StandardIdentity.signingKey, signingKey, sizeof (m_StandardIdentity.signingKey));
|
||||
memset (&m_StandardIdentity.certificate, 0, sizeof (m_StandardIdentity.certificate));
|
||||
memset (m_StandardIdentity.certificate, 0, sizeof (m_StandardIdentity.certificate));
|
||||
m_IdentHash = m_StandardIdentity.Hash ();
|
||||
m_ExtendedLen = 0;
|
||||
m_ExtendedBuffer = nullptr;
|
||||
@@ -127,20 +135,25 @@ namespace data
|
||||
}
|
||||
|
||||
IdentityEx::IdentityEx (const uint8_t * buf, size_t len):
|
||||
m_Verifier (nullptr), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
{
|
||||
FromBuffer (buf, len);
|
||||
}
|
||||
|
||||
IdentityEx::IdentityEx (const IdentityEx& other):
|
||||
m_Verifier (nullptr), m_ExtendedBuffer (nullptr)
|
||||
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
IdentityEx::IdentityEx (const Identity& standard):
|
||||
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
{
|
||||
*this = standard;
|
||||
}
|
||||
|
||||
IdentityEx::~IdentityEx ()
|
||||
{
|
||||
delete m_Verifier;
|
||||
delete[] m_ExtendedBuffer;
|
||||
}
|
||||
|
||||
@@ -159,7 +172,6 @@ namespace data
|
||||
else
|
||||
m_ExtendedBuffer = nullptr;
|
||||
|
||||
delete m_Verifier;
|
||||
m_Verifier = nullptr;
|
||||
|
||||
return *this;
|
||||
@@ -174,7 +186,6 @@ namespace data
|
||||
m_ExtendedBuffer = nullptr;
|
||||
m_ExtendedLen = 0;
|
||||
|
||||
delete m_Verifier;
|
||||
m_Verifier = nullptr;
|
||||
|
||||
return *this;
|
||||
@@ -184,15 +195,15 @@ namespace data
|
||||
{
|
||||
if (len < DEFAULT_IDENTITY_SIZE)
|
||||
{
|
||||
LogPrint (eLogError, "Identity buffer length ", len, " is too small");
|
||||
LogPrint (eLogError, "Identity: buffer length ", len, " is too small");
|
||||
return 0;
|
||||
}
|
||||
memcpy (&m_StandardIdentity, buf, DEFAULT_IDENTITY_SIZE);
|
||||
|
||||
delete[] m_ExtendedBuffer;
|
||||
if (m_StandardIdentity.certificate.length)
|
||||
delete[] m_ExtendedBuffer; m_ExtendedBuffer = nullptr;
|
||||
m_ExtendedLen = bufbe16toh (m_StandardIdentity.certificate + 1);
|
||||
if (m_ExtendedLen)
|
||||
{
|
||||
m_ExtendedLen = be16toh (m_StandardIdentity.certificate.length);
|
||||
if (m_ExtendedLen + DEFAULT_IDENTITY_SIZE <= len)
|
||||
{
|
||||
m_ExtendedBuffer = new uint8_t[m_ExtendedLen];
|
||||
@@ -200,7 +211,8 @@ namespace data
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "Certificate length ", m_ExtendedLen, " exceeds buffer length ", len - DEFAULT_IDENTITY_SIZE);
|
||||
LogPrint (eLogError, "Identity: Certificate length ", m_ExtendedLen, " exceeds buffer length ", len - DEFAULT_IDENTITY_SIZE);
|
||||
m_ExtendedLen = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -209,37 +221,40 @@ namespace data
|
||||
m_ExtendedLen = 0;
|
||||
m_ExtendedBuffer = nullptr;
|
||||
}
|
||||
CryptoPP::SHA256().CalculateDigest(m_IdentHash, buf, GetFullLen ());
|
||||
SHA256(buf, GetFullLen (), m_IdentHash);
|
||||
|
||||
delete m_Verifier;
|
||||
m_Verifier = nullptr;
|
||||
|
||||
return GetFullLen ();
|
||||
}
|
||||
|
||||
size_t IdentityEx::ToBuffer (uint8_t * buf, size_t len) const
|
||||
{
|
||||
{
|
||||
const size_t fullLen = GetFullLen();
|
||||
if (fullLen > len) return 0; // buffer is too small and may overflow somewhere else
|
||||
memcpy (buf, &m_StandardIdentity, DEFAULT_IDENTITY_SIZE);
|
||||
if (m_ExtendedLen > 0 && m_ExtendedBuffer)
|
||||
memcpy (buf + DEFAULT_IDENTITY_SIZE, m_ExtendedBuffer, m_ExtendedLen);
|
||||
return GetFullLen ();
|
||||
return fullLen;
|
||||
}
|
||||
|
||||
size_t IdentityEx::FromBase64(const std::string& s)
|
||||
{
|
||||
uint8_t buf[1024];
|
||||
auto len = Base64ToByteStream (s.c_str(), s.length(), buf, 1024);
|
||||
return FromBuffer (buf, len);
|
||||
const size_t slen = s.length();
|
||||
std::vector<uint8_t> buf(slen); // binary data can't exceed base64
|
||||
const size_t len = Base64ToByteStream (s.c_str(), slen, buf.data(), slen);
|
||||
return FromBuffer (buf.data(), len);
|
||||
}
|
||||
|
||||
std::string IdentityEx::ToBase64 () const
|
||||
{
|
||||
uint8_t buf[1024];
|
||||
char str[1536];
|
||||
size_t l = ToBuffer (buf, 1024);
|
||||
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, str, 1536);
|
||||
str[l1] = 0;
|
||||
return std::string (str);
|
||||
const size_t bufLen = GetFullLen();
|
||||
const size_t strLen = Base64EncodingBufferSize(bufLen);
|
||||
std::vector<uint8_t> buf(bufLen);
|
||||
std::vector<char> str(strLen);
|
||||
size_t l = ToBuffer (buf.data(), bufLen);
|
||||
size_t l1 = i2p::data::ByteStreamToBase64 (buf.data(), l, str.data(), strLen);
|
||||
return std::string (str.data(), l1);
|
||||
}
|
||||
|
||||
size_t IdentityEx::GetSigningPublicKeyLen () const
|
||||
@@ -263,7 +278,7 @@ namespace data
|
||||
if (!m_Verifier) CreateVerifier ();
|
||||
if (m_Verifier)
|
||||
return m_Verifier->GetSignatureLen ();
|
||||
return 40;
|
||||
return i2p::crypto::DSA_SIGNATURE_LENGTH;
|
||||
}
|
||||
bool IdentityEx::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
|
||||
{
|
||||
@@ -275,14 +290,14 @@ namespace data
|
||||
|
||||
SigningKeyType IdentityEx::GetSigningKeyType () const
|
||||
{
|
||||
if (m_StandardIdentity.certificate.type == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer)
|
||||
if (m_StandardIdentity.certificate[0] == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer)
|
||||
return bufbe16toh (m_ExtendedBuffer); // signing key
|
||||
return SIGNING_KEY_TYPE_DSA_SHA1;
|
||||
}
|
||||
|
||||
CryptoKeyType IdentityEx::GetCryptoKeyType () const
|
||||
{
|
||||
if (m_StandardIdentity.certificate.type == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer)
|
||||
if (m_StandardIdentity.certificate[0] == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer)
|
||||
return bufbe16toh (m_ExtendedBuffer + 2); // crypto key
|
||||
return CRYPTO_KEY_TYPE_ELGAMAL;
|
||||
}
|
||||
@@ -293,18 +308,18 @@ namespace data
|
||||
switch (keyType)
|
||||
{
|
||||
case SIGNING_KEY_TYPE_DSA_SHA1:
|
||||
m_Verifier = new i2p::crypto::DSAVerifier (m_StandardIdentity.signingKey);
|
||||
UpdateVerifier (new i2p::crypto::DSAVerifier (m_StandardIdentity.signingKey));
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
|
||||
{
|
||||
size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64
|
||||
m_Verifier = new i2p::crypto::ECDSAP256Verifier (m_StandardIdentity.signingKey + padding);
|
||||
UpdateVerifier (new i2p::crypto::ECDSAP256Verifier (m_StandardIdentity.signingKey + padding));
|
||||
break;
|
||||
}
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
|
||||
{
|
||||
size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96
|
||||
m_Verifier = new i2p::crypto::ECDSAP384Verifier (m_StandardIdentity.signingKey + padding);
|
||||
UpdateVerifier (new i2p::crypto::ECDSAP384Verifier (m_StandardIdentity.signingKey + padding));
|
||||
break;
|
||||
}
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
|
||||
@@ -313,7 +328,7 @@ namespace data
|
||||
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
|
||||
size_t excessLen = i2p::crypto::ECDSAP521_KEY_LENGTH - 128; // 4 = 132- 128
|
||||
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
|
||||
m_Verifier = new i2p::crypto::ECDSAP521Verifier (signingKey);
|
||||
UpdateVerifier (new i2p::crypto::ECDSAP521Verifier (signingKey));
|
||||
break;
|
||||
}
|
||||
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
|
||||
@@ -322,7 +337,7 @@ namespace data
|
||||
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
|
||||
size_t excessLen = i2p::crypto::RSASHA2562048_KEY_LENGTH - 128; // 128 = 256- 128
|
||||
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
|
||||
m_Verifier = new i2p::crypto:: RSASHA2562048Verifier (signingKey);
|
||||
UpdateVerifier (new i2p::crypto:: RSASHA2562048Verifier (signingKey));
|
||||
break;
|
||||
}
|
||||
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
|
||||
@@ -331,7 +346,7 @@ namespace data
|
||||
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
|
||||
size_t excessLen = i2p::crypto::RSASHA3843072_KEY_LENGTH - 128; // 256 = 384- 128
|
||||
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
|
||||
m_Verifier = new i2p::crypto:: RSASHA3843072Verifier (signingKey);
|
||||
UpdateVerifier (new i2p::crypto:: RSASHA3843072Verifier (signingKey));
|
||||
break;
|
||||
}
|
||||
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
|
||||
@@ -340,27 +355,39 @@ namespace data
|
||||
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
|
||||
size_t excessLen = i2p::crypto::RSASHA5124096_KEY_LENGTH - 128; // 384 = 512- 128
|
||||
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
|
||||
m_Verifier = new i2p::crypto:: RSASHA5124096Verifier (signingKey);
|
||||
UpdateVerifier (new i2p::crypto:: RSASHA5124096Verifier (signingKey));
|
||||
break;
|
||||
}
|
||||
}
|
||||
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
|
||||
{
|
||||
size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32
|
||||
UpdateVerifier (new i2p::crypto::EDDSA25519Verifier (m_StandardIdentity.signingKey + padding));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LogPrint ("Signing key type ", (int)keyType, " is not supported");
|
||||
LogPrint (eLogError, "Identity: Signing key type ", (int)keyType, " is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
void IdentityEx::DropVerifier ()
|
||||
|
||||
void IdentityEx::UpdateVerifier (i2p::crypto::Verifier * verifier) const
|
||||
{
|
||||
auto verifier = m_Verifier;
|
||||
m_Verifier = nullptr; // TODO: make this atomic
|
||||
delete verifier;
|
||||
if (!m_Verifier || !verifier)
|
||||
m_Verifier.reset (verifier);
|
||||
else
|
||||
delete verifier;
|
||||
}
|
||||
|
||||
void IdentityEx::DropVerifier () const
|
||||
{
|
||||
// TODO: potential race condition with Verify
|
||||
m_Verifier = nullptr;
|
||||
}
|
||||
|
||||
PrivateKeys& PrivateKeys::operator=(const Keys& keys)
|
||||
{
|
||||
m_Public = Identity (keys);
|
||||
m_Public = std::make_shared<IdentityEx>(Identity (keys));
|
||||
memcpy (m_PrivateKey, keys.privateKey, 256); // 256
|
||||
memcpy (m_SigningPrivateKey, keys.signingPrivateKey, m_Public.GetSigningPrivateKeyLen ());
|
||||
delete m_Signer;
|
||||
memcpy (m_SigningPrivateKey, keys.signingPrivateKey, m_Public->GetSigningPrivateKeyLen ());
|
||||
m_Signer = nullptr;
|
||||
CreateSigner ();
|
||||
return *this;
|
||||
@@ -368,10 +395,9 @@ namespace data
|
||||
|
||||
PrivateKeys& PrivateKeys::operator=(const PrivateKeys& other)
|
||||
{
|
||||
m_Public = other.m_Public;
|
||||
m_Public = std::make_shared<IdentityEx>(*other.m_Public);
|
||||
memcpy (m_PrivateKey, other.m_PrivateKey, 256); // 256
|
||||
memcpy (m_SigningPrivateKey, other.m_SigningPrivateKey, m_Public.GetSigningPrivateKeyLen ());
|
||||
delete m_Signer;
|
||||
memcpy (m_SigningPrivateKey, other.m_SigningPrivateKey, m_Public->GetSigningPrivateKeyLen ());
|
||||
m_Signer = nullptr;
|
||||
CreateSigner ();
|
||||
return *this;
|
||||
@@ -379,13 +405,13 @@ namespace data
|
||||
|
||||
size_t PrivateKeys::FromBuffer (const uint8_t * buf, size_t len)
|
||||
{
|
||||
size_t ret = m_Public.FromBuffer (buf, len);
|
||||
m_Public = std::make_shared<IdentityEx>(buf, len);
|
||||
size_t ret = m_Public->GetFullLen ();
|
||||
memcpy (m_PrivateKey, buf + ret, 256); // private key always 256
|
||||
ret += 256;
|
||||
size_t signingPrivateKeySize = m_Public.GetSigningPrivateKeyLen ();
|
||||
size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
|
||||
memcpy (m_SigningPrivateKey, buf + ret, signingPrivateKeySize);
|
||||
ret += signingPrivateKeySize;
|
||||
delete m_Signer;
|
||||
m_Signer = nullptr;
|
||||
CreateSigner ();
|
||||
return ret;
|
||||
@@ -393,10 +419,10 @@ namespace data
|
||||
|
||||
size_t PrivateKeys::ToBuffer (uint8_t * buf, size_t len) const
|
||||
{
|
||||
size_t ret = m_Public.ToBuffer (buf, len);
|
||||
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 ();
|
||||
memcpy (buf + ret, m_SigningPrivateKey, signingPrivateKeySize);
|
||||
ret += signingPrivateKeySize;
|
||||
return ret;
|
||||
@@ -427,36 +453,39 @@ namespace data
|
||||
void PrivateKeys::Sign (const uint8_t * buf, int len, uint8_t * signature) const
|
||||
{
|
||||
if (m_Signer)
|
||||
m_Signer->Sign (i2p::context.GetRandomNumberGenerator (), buf, len, signature);
|
||||
m_Signer->Sign (buf, len, signature);
|
||||
}
|
||||
|
||||
void PrivateKeys::CreateSigner ()
|
||||
{
|
||||
switch (m_Public.GetSigningKeyType ())
|
||||
switch (m_Public->GetSigningKeyType ())
|
||||
{
|
||||
case SIGNING_KEY_TYPE_DSA_SHA1:
|
||||
m_Signer = new i2p::crypto::DSASigner (m_SigningPrivateKey);
|
||||
m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey));
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
|
||||
m_Signer = new i2p::crypto::ECDSAP256Signer (m_SigningPrivateKey);
|
||||
m_Signer.reset (new i2p::crypto::ECDSAP256Signer (m_SigningPrivateKey));
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
|
||||
m_Signer = new i2p::crypto::ECDSAP384Signer (m_SigningPrivateKey);
|
||||
m_Signer.reset (new i2p::crypto::ECDSAP384Signer (m_SigningPrivateKey));
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
|
||||
m_Signer = new i2p::crypto::ECDSAP521Signer (m_SigningPrivateKey);
|
||||
m_Signer.reset (new i2p::crypto::ECDSAP521Signer (m_SigningPrivateKey));
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
|
||||
m_Signer = new i2p::crypto::RSASHA2562048Signer (m_SigningPrivateKey);
|
||||
m_Signer.reset (new i2p::crypto::RSASHA2562048Signer (m_SigningPrivateKey));
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
|
||||
m_Signer = new i2p::crypto::RSASHA3843072Signer (m_SigningPrivateKey);
|
||||
m_Signer.reset (new i2p::crypto::RSASHA3843072Signer (m_SigningPrivateKey));
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
|
||||
m_Signer = new i2p::crypto::RSASHA5124096Signer (m_SigningPrivateKey);
|
||||
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));
|
||||
break;
|
||||
default:
|
||||
LogPrint ("Signing key type ", (int)m_Public.GetSigningKeyType (), " is not supported");
|
||||
LogPrint (eLogError, "Identity: Signing key type ", (int)m_Public->GetSigningKeyType (), " is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,39 +494,40 @@ namespace data
|
||||
if (type != SIGNING_KEY_TYPE_DSA_SHA1)
|
||||
{
|
||||
PrivateKeys keys;
|
||||
auto& rnd = i2p::context.GetRandomNumberGenerator ();
|
||||
// signature
|
||||
uint8_t signingPublicKey[512]; // signing public key is 512 bytes max
|
||||
switch (type)
|
||||
{
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
|
||||
i2p::crypto::CreateECDSAP256RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey);
|
||||
i2p::crypto::CreateECDSAP256RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
|
||||
i2p::crypto::CreateECDSAP384RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey);
|
||||
i2p::crypto::CreateECDSAP384RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
|
||||
i2p::crypto::CreateECDSAP521RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey);
|
||||
i2p::crypto::CreateECDSAP521RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
|
||||
i2p::crypto::CreateRSARandomKeys (rnd, i2p::crypto::RSASHA2562048_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
|
||||
i2p::crypto::CreateRSARandomKeys (i2p::crypto::RSASHA2562048_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
|
||||
i2p::crypto::CreateRSARandomKeys (rnd, i2p::crypto::RSASHA3843072_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
|
||||
i2p::crypto::CreateRSARandomKeys (i2p::crypto::RSASHA3843072_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
|
||||
i2p::crypto::CreateRSARandomKeys (rnd, i2p::crypto::RSASHA5124096_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
|
||||
i2p::crypto::CreateRSARandomKeys (i2p::crypto::RSASHA5124096_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
|
||||
i2p::crypto::CreateEDDSA25519RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
|
||||
break;
|
||||
default:
|
||||
LogPrint ("Signing key type ", (int)type, " is not supported. Create DSA-SHA1");
|
||||
LogPrint (eLogError, "Identity: Signing key type ", (int)type, " is not supported. Create DSA-SHA1");
|
||||
return PrivateKeys (i2p::data::CreateRandomKeys ()); // DSA-SHA1
|
||||
}
|
||||
// encryption
|
||||
uint8_t publicKey[256];
|
||||
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
|
||||
dh.GenerateKeyPair(rnd, keys.m_PrivateKey, publicKey);
|
||||
i2p::crypto::GenerateElGamalKeyPair (keys.m_PrivateKey, publicKey);
|
||||
// identity
|
||||
keys.m_Public = IdentityEx (publicKey, signingPublicKey, type);
|
||||
keys.m_Public = std::make_shared<IdentityEx> (publicKey, signingPublicKey, type);
|
||||
|
||||
keys.CreateSigner ();
|
||||
return keys;
|
||||
@@ -508,11 +538,10 @@ namespace data
|
||||
Keys CreateRandomKeys ()
|
||||
{
|
||||
Keys keys;
|
||||
auto& rnd = i2p::context.GetRandomNumberGenerator ();
|
||||
// encryption
|
||||
i2p::crypto::GenerateElGamalKeyPair(rnd, keys.privateKey, keys.publicKey);
|
||||
i2p::crypto::GenerateElGamalKeyPair(keys.privateKey, keys.publicKey);
|
||||
// signing
|
||||
i2p::crypto::CreateDSARandomKeys (rnd, keys.signingPrivateKey, keys.signingKey);
|
||||
i2p::crypto::CreateDSARandomKeys (keys.signingPrivateKey, keys.signingKey);
|
||||
return keys;
|
||||
}
|
||||
|
||||
@@ -530,7 +559,7 @@ namespace data
|
||||
sprintf((char *)(buf + 32), "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
|
||||
#endif
|
||||
IdentHash key;
|
||||
CryptoPP::SHA256().CalculateDigest((uint8_t *)key, buf, 40);
|
||||
SHA256(buf, 40, key);
|
||||
return key;
|
||||
}
|
||||
|
||||
|
||||
123
Identity.h
123
Identity.h
@@ -4,74 +4,20 @@
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include "base64.h"
|
||||
#include "ElGamal.h"
|
||||
#include <memory>
|
||||
#include "Base.h"
|
||||
#include "Signature.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
template<int sz>
|
||||
class Tag
|
||||
{
|
||||
public:
|
||||
|
||||
Tag (const uint8_t * buf) { memcpy (m_Buf, buf, sz); };
|
||||
Tag (const Tag<sz>& ) = default;
|
||||
#ifndef _WIN32 // FIXME!!! msvs 2013 can't compile it
|
||||
Tag (Tag<sz>&& ) = default;
|
||||
#endif
|
||||
Tag () = default;
|
||||
|
||||
Tag<sz>& operator= (const Tag<sz>& ) = default;
|
||||
#ifndef _WIN32
|
||||
Tag<sz>& operator= (Tag<sz>&& ) = default;
|
||||
#endif
|
||||
|
||||
uint8_t * operator()() { return m_Buf; };
|
||||
const uint8_t * operator()() const { return m_Buf; };
|
||||
|
||||
operator uint8_t * () { return m_Buf; };
|
||||
operator const uint8_t * () const { return m_Buf; };
|
||||
|
||||
const uint64_t * GetLL () const { return ll; };
|
||||
|
||||
bool operator== (const Tag<sz>& other) const { return !memcmp (m_Buf, other.m_Buf, sz); };
|
||||
bool operator< (const Tag<sz>& other) const { return memcmp (m_Buf, other.m_Buf, sz) < 0; };
|
||||
|
||||
std::string ToBase64 () const
|
||||
{
|
||||
char str[sz*2];
|
||||
int l = i2p::data::ByteStreamToBase64 (m_Buf, sz, str, sz*2);
|
||||
str[l] = 0;
|
||||
return std::string (str);
|
||||
}
|
||||
|
||||
std::string ToBase32 () const
|
||||
{
|
||||
char str[sz*2];
|
||||
int l = i2p::data::ByteStreamToBase32 (m_Buf, sz, str, sz*2);
|
||||
str[l] = 0;
|
||||
return std::string (str);
|
||||
}
|
||||
|
||||
void FromBase32 (const std::string& s)
|
||||
{
|
||||
i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
union // 8 bytes alignment
|
||||
{
|
||||
uint8_t m_Buf[sz];
|
||||
uint64_t ll[sz/8];
|
||||
};
|
||||
};
|
||||
typedef Tag<32> IdentHash;
|
||||
inline std::string GetIdentHashAbbreviation (const IdentHash& ident)
|
||||
{
|
||||
return ident.ToBase64 ().substr (0, 4);
|
||||
}
|
||||
|
||||
#pragma pack(1)
|
||||
struct Keys
|
||||
{
|
||||
uint8_t privateKey[256];
|
||||
@@ -91,11 +37,7 @@ namespace data
|
||||
{
|
||||
uint8_t publicKey[256];
|
||||
uint8_t signingKey[128];
|
||||
struct
|
||||
{
|
||||
uint8_t type;
|
||||
uint16_t length;
|
||||
} certificate;
|
||||
uint8_t certificate[3]; // byte 1 - type, bytes 2-3 - length
|
||||
|
||||
Identity () = default;
|
||||
Identity (const Keys& keys) { *this = keys; };
|
||||
@@ -103,7 +45,7 @@ namespace data
|
||||
size_t FromBuffer (const uint8_t * buf, size_t len);
|
||||
IdentHash Hash () const;
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
Keys CreateRandomKeys ();
|
||||
|
||||
const size_t DEFAULT_IDENTITY_SIZE = sizeof (Identity); // 387 bytes
|
||||
@@ -116,6 +58,7 @@ namespace data
|
||||
const uint16_t SIGNING_KEY_TYPE_RSA_SHA256_2048 = 4;
|
||||
const uint16_t SIGNING_KEY_TYPE_RSA_SHA384_3072 = 5;
|
||||
const uint16_t SIGNING_KEY_TYPE_RSA_SHA512_4096 = 6;
|
||||
const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519 = 7;
|
||||
typedef uint16_t SigningKeyType;
|
||||
typedef uint16_t CryptoKeyType;
|
||||
|
||||
@@ -128,6 +71,7 @@ namespace data
|
||||
SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1);
|
||||
IdentityEx (const uint8_t * buf, size_t len);
|
||||
IdentityEx (const IdentityEx& other);
|
||||
IdentityEx (const Identity& standard);
|
||||
~IdentityEx ();
|
||||
IdentityEx& operator=(const IdentityEx& other);
|
||||
IdentityEx& operator=(const Identity& standard);
|
||||
@@ -138,6 +82,7 @@ namespace data
|
||||
std::string ToBase64 () const;
|
||||
const Identity& GetStandardIdentity () const { return m_StandardIdentity; };
|
||||
const IdentHash& GetIdentHash () const { return m_IdentHash; };
|
||||
const uint8_t * GetEncryptionPublicKey () const { return m_StandardIdentity.publicKey; };
|
||||
size_t GetFullLen () const { return m_ExtendedLen + DEFAULT_IDENTITY_SIZE; };
|
||||
size_t GetSigningPublicKeyLen () const;
|
||||
size_t GetSigningPrivateKeyLen () const;
|
||||
@@ -145,17 +90,18 @@ namespace data
|
||||
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const;
|
||||
SigningKeyType GetSigningKeyType () const;
|
||||
CryptoKeyType GetCryptoKeyType () const;
|
||||
void DropVerifier (); // to save memory
|
||||
void DropVerifier () const; // to save memory
|
||||
|
||||
private:
|
||||
|
||||
void CreateVerifier () const;
|
||||
void UpdateVerifier (i2p::crypto::Verifier * verifier) const;
|
||||
|
||||
private:
|
||||
|
||||
Identity m_StandardIdentity;
|
||||
IdentHash m_IdentHash;
|
||||
mutable i2p::crypto::Verifier * m_Verifier;
|
||||
mutable std::unique_ptr<i2p::crypto::Verifier> m_Verifier;
|
||||
size_t m_ExtendedLen;
|
||||
uint8_t * m_ExtendedBuffer;
|
||||
};
|
||||
@@ -164,19 +110,19 @@ namespace data
|
||||
{
|
||||
public:
|
||||
|
||||
PrivateKeys (): m_Signer (nullptr) {};
|
||||
PrivateKeys (const PrivateKeys& other): m_Signer (nullptr) { *this = other; };
|
||||
PrivateKeys (const Keys& keys): m_Signer (nullptr) { *this = keys; };
|
||||
PrivateKeys () = default;
|
||||
PrivateKeys (const PrivateKeys& other) { *this = other; };
|
||||
PrivateKeys (const Keys& keys) { *this = keys; };
|
||||
PrivateKeys& operator=(const Keys& keys);
|
||||
PrivateKeys& operator=(const PrivateKeys& other);
|
||||
~PrivateKeys () { delete m_Signer; };
|
||||
~PrivateKeys () = default;
|
||||
|
||||
const IdentityEx& GetPublic () const { return m_Public; };
|
||||
std::shared_ptr<const IdentityEx> GetPublic () const { return m_Public; };
|
||||
const uint8_t * GetPrivateKey () const { return m_PrivateKey; };
|
||||
const uint8_t * GetSigningPrivateKey () const { return m_SigningPrivateKey; };
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const;
|
||||
|
||||
size_t GetFullLen () const { return m_Public.GetFullLen () + 256 + m_Public.GetSigningPrivateKeyLen (); };
|
||||
size_t GetFullLen () const { return m_Public->GetFullLen () + 256 + m_Public->GetSigningPrivateKeyLen (); };
|
||||
size_t FromBuffer (const uint8_t * buf, size_t len);
|
||||
size_t ToBuffer (uint8_t * buf, size_t len) const;
|
||||
|
||||
@@ -191,10 +137,10 @@ namespace data
|
||||
|
||||
private:
|
||||
|
||||
IdentityEx m_Public;
|
||||
std::shared_ptr<IdentityEx> m_Public;
|
||||
uint8_t m_PrivateKey[256];
|
||||
uint8_t m_SigningPrivateKey[1024]; // assume private key doesn't exceed 1024 bytes
|
||||
i2p::crypto::Signer * m_Signer;
|
||||
std::unique_ptr<i2p::crypto::Signer> m_Signer;
|
||||
};
|
||||
|
||||
// kademlia
|
||||
@@ -219,23 +165,12 @@ namespace data
|
||||
{
|
||||
public:
|
||||
|
||||
RoutingDestination (): m_ElGamalEncryption (nullptr) {};
|
||||
virtual ~RoutingDestination () { delete m_ElGamalEncryption; };
|
||||
RoutingDestination () {};
|
||||
virtual ~RoutingDestination () {};
|
||||
|
||||
virtual const IdentHash& GetIdentHash () const = 0;
|
||||
virtual const uint8_t * GetEncryptionPublicKey () const = 0;
|
||||
virtual bool IsDestination () const = 0; // for garlic
|
||||
|
||||
i2p::crypto::ElGamalEncryption * GetElGamalEncryption () const
|
||||
{
|
||||
if (!m_ElGamalEncryption)
|
||||
m_ElGamalEncryption = new i2p::crypto::ElGamalEncryption (GetEncryptionPublicKey ());
|
||||
return m_ElGamalEncryption;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
mutable i2p::crypto::ElGamalEncryption * m_ElGamalEncryption; // use lazy initialization
|
||||
};
|
||||
|
||||
class LocalDestination
|
||||
@@ -243,16 +178,10 @@ namespace data
|
||||
public:
|
||||
|
||||
virtual ~LocalDestination() {};
|
||||
virtual const PrivateKeys& GetPrivateKeys () const = 0;
|
||||
virtual const uint8_t * GetEncryptionPrivateKey () const = 0;
|
||||
virtual const uint8_t * GetEncryptionPublicKey () const = 0;
|
||||
virtual std::shared_ptr<const IdentityEx> GetIdentity () const = 0;
|
||||
|
||||
const IdentityEx& GetIdentity () const { return GetPrivateKeys ().GetPublic (); };
|
||||
const IdentHash& GetIdentHash () const { return GetIdentity ().GetIdentHash (); };
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
|
||||
{
|
||||
GetPrivateKeys ().Sign (buf, len, signature);
|
||||
};
|
||||
const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); };
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
274
LeaseSet.cpp
274
LeaseSet.cpp
@@ -1,11 +1,10 @@
|
||||
#include <string.h>
|
||||
#include "I2PEndian.h"
|
||||
#include <cryptopp/dsa.h>
|
||||
#include "CryptoConst.h"
|
||||
#include "Crypto.h"
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
#include "NetDb.h"
|
||||
#include "TunnelPool.h"
|
||||
#include "Tunnel.h"
|
||||
#include "LeaseSet.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -13,119 +12,238 @@ namespace i2p
|
||||
namespace data
|
||||
{
|
||||
|
||||
LeaseSet::LeaseSet (const uint8_t * buf, int len)
|
||||
LeaseSet::LeaseSet (const uint8_t * buf, size_t len, bool storeLeases):
|
||||
m_IsValid (true), m_StoreLeases (storeLeases), m_ExpirationTime (0)
|
||||
{
|
||||
m_Buffer = new uint8_t[len];
|
||||
memcpy (m_Buffer, buf, len);
|
||||
m_BufferLen = len;
|
||||
ReadFromBuffer ();
|
||||
}
|
||||
|
||||
LeaseSet::LeaseSet (const i2p::tunnel::TunnelPool& pool)
|
||||
void LeaseSet::Update (const uint8_t * buf, size_t len)
|
||||
{
|
||||
// header
|
||||
const i2p::data::LocalDestination * localDestination = pool.GetLocalDestination ();
|
||||
if (!localDestination)
|
||||
if (len > m_BufferLen)
|
||||
{
|
||||
m_BufferLen = 0;
|
||||
LogPrint (eLogError, "Destination for local LeaseSet doesn't exist");
|
||||
return;
|
||||
auto oldBuffer = m_Buffer;
|
||||
m_Buffer = new uint8_t[len];
|
||||
delete[] oldBuffer;
|
||||
}
|
||||
m_BufferLen = localDestination->GetIdentity ().ToBuffer (m_Buffer, MAX_LS_BUFFER_SIZE);
|
||||
memcpy (m_Buffer + m_BufferLen, localDestination->GetEncryptionPublicKey (), 256);
|
||||
m_BufferLen += 256;
|
||||
auto signingKeyLen = localDestination->GetIdentity ().GetSigningPublicKeyLen ();
|
||||
memset (m_Buffer + m_BufferLen, 0, signingKeyLen);
|
||||
m_BufferLen += signingKeyLen;
|
||||
auto tunnels = pool.GetInboundTunnels (5); // 5 tunnels maximum
|
||||
m_Buffer[m_BufferLen] = tunnels.size (); // num leases
|
||||
m_BufferLen++;
|
||||
// leases
|
||||
for (auto it: tunnels)
|
||||
{
|
||||
Lease lease;
|
||||
memcpy (lease.tunnelGateway, it->GetNextIdentHash (), 32);
|
||||
lease.tunnelID = htobe32 (it->GetNextTunnelID ());
|
||||
uint64_t ts = it->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - 60; // 1 minute before expiration
|
||||
ts *= 1000; // in milliseconds
|
||||
lease.endDate = htobe64 (ts);
|
||||
memcpy(m_Buffer + m_BufferLen, &lease, sizeof(Lease));
|
||||
m_BufferLen += sizeof (Lease);
|
||||
}
|
||||
// signature
|
||||
localDestination->Sign (m_Buffer, m_BufferLen, m_Buffer + m_BufferLen);
|
||||
m_BufferLen += localDestination->GetIdentity ().GetSignatureLen ();
|
||||
LogPrint ("Local LeaseSet of ", tunnels.size (), " leases created");
|
||||
|
||||
ReadFromBuffer ();
|
||||
}
|
||||
|
||||
void LeaseSet::Update (const uint8_t * buf, int len)
|
||||
{
|
||||
m_Leases.clear ();
|
||||
memcpy (m_Buffer, buf, len);
|
||||
m_BufferLen = len;
|
||||
ReadFromBuffer ();
|
||||
ReadFromBuffer (false);
|
||||
}
|
||||
|
||||
void LeaseSet::ReadFromBuffer ()
|
||||
|
||||
void LeaseSet::PopulateLeases ()
|
||||
{
|
||||
m_StoreLeases = true;
|
||||
ReadFromBuffer (false);
|
||||
}
|
||||
|
||||
void LeaseSet::ReadFromBuffer (bool readIdentity)
|
||||
{
|
||||
size_t size = m_Identity.FromBuffer (m_Buffer, m_BufferLen);
|
||||
if (readIdentity || !m_Identity)
|
||||
m_Identity = std::make_shared<IdentityEx>(m_Buffer, m_BufferLen);
|
||||
size_t size = m_Identity->GetFullLen ();
|
||||
if (size > m_BufferLen)
|
||||
{
|
||||
LogPrint (eLogError, "LeaseSet: identity length ", size, " exceeds buffer size ", m_BufferLen);
|
||||
m_IsValid = false;
|
||||
return;
|
||||
}
|
||||
memcpy (m_EncryptionKey, m_Buffer + size, 256);
|
||||
size += 256; // encryption key
|
||||
size += m_Identity.GetSigningPublicKeyLen (); // unused signing key
|
||||
size += m_Identity->GetSigningPublicKeyLen (); // unused signing key
|
||||
uint8_t num = m_Buffer[size];
|
||||
size++; // num
|
||||
LogPrint ("LeaseSet num=", (int)num);
|
||||
LogPrint (eLogDebug, "LeaseSet: read num=", (int)num);
|
||||
if (!num || num > MAX_NUM_LEASES)
|
||||
{
|
||||
LogPrint (eLogError, "LeaseSet: incorrect number of leases", (int)num);
|
||||
m_IsValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// reset existing leases
|
||||
if (m_StoreLeases)
|
||||
for (auto& it: m_Leases)
|
||||
it->isUpdated = false;
|
||||
else
|
||||
m_Leases.clear ();
|
||||
|
||||
// process leases
|
||||
m_ExpirationTime = 0;
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
const uint8_t * leases = m_Buffer + size;
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
Lease lease;
|
||||
memcpy (&lease, leases, sizeof(Lease));
|
||||
lease.tunnelID = be32toh (lease.tunnelID);
|
||||
lease.endDate = be64toh (lease.endDate);
|
||||
m_Leases.push_back (lease);
|
||||
leases += sizeof (Lease);
|
||||
|
||||
// check if lease's gateway is in our netDb
|
||||
if (!netdb.FindRouter (lease.tunnelGateway))
|
||||
{
|
||||
// if not found request it
|
||||
LogPrint ("Lease's tunnel gateway not found. Requested");
|
||||
netdb.RequestDestination (lease.tunnelGateway);
|
||||
}
|
||||
lease.tunnelGateway = leases;
|
||||
leases += 32; // gateway
|
||||
lease.tunnelID = bufbe32toh (leases);
|
||||
leases += 4; // tunnel ID
|
||||
lease.endDate = bufbe64toh (leases);
|
||||
leases += 8; // end date
|
||||
if (ts < lease.endDate + LEASE_ENDDATE_THRESHOLD)
|
||||
{
|
||||
if (lease.endDate > m_ExpirationTime)
|
||||
m_ExpirationTime = lease.endDate;
|
||||
if (m_StoreLeases)
|
||||
{
|
||||
auto ret = m_Leases.insert (std::make_shared<Lease>(lease));
|
||||
if (!ret.second) *(*ret.first) = lease; // update existing
|
||||
(*ret.first)->isUpdated = true;
|
||||
// check if lease's gateway is in our netDb
|
||||
if (!netdb.FindRouter (lease.tunnelGateway))
|
||||
{
|
||||
// if not found request it
|
||||
LogPrint (eLogInfo, "LeaseSet: Lease's tunnel gateway not found, requesting");
|
||||
netdb.RequestDestination (lease.tunnelGateway);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "LeaseSet: Lease is expired already ");
|
||||
}
|
||||
|
||||
if (!m_ExpirationTime)
|
||||
{
|
||||
LogPrint (eLogWarning, "LeaseSet: all leases are expired. Dropped");
|
||||
m_IsValid = false;
|
||||
return;
|
||||
}
|
||||
m_ExpirationTime += LEASE_ENDDATE_THRESHOLD;
|
||||
// delete old leases
|
||||
if (m_StoreLeases)
|
||||
{
|
||||
for (auto it = m_Leases.begin (); it != m_Leases.end ();)
|
||||
{
|
||||
if (!(*it)->isUpdated)
|
||||
{
|
||||
(*it)->endDate = 0; // somebody might still hold it
|
||||
m_Leases.erase (it++);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// verify
|
||||
if (!m_Identity.Verify (m_Buffer, leases - m_Buffer, leases))
|
||||
LogPrint ("LeaseSet verification failed");
|
||||
if (!m_Identity->Verify (m_Buffer, leases - m_Buffer, leases))
|
||||
{
|
||||
LogPrint (eLogWarning, "LeaseSet: verification failed");
|
||||
m_IsValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<Lease> LeaseSet::GetNonExpiredLeases () const
|
||||
|
||||
uint64_t LeaseSet::ExtractTimestamp (const uint8_t * buf, size_t len) const
|
||||
{
|
||||
if (!m_Identity) return 0;
|
||||
size_t size = m_Identity->GetFullLen ();
|
||||
if (size > len) return 0;
|
||||
size += 256; // encryption key
|
||||
size += m_Identity->GetSigningPublicKeyLen (); // unused signing key
|
||||
if (size > len) return 0;
|
||||
uint8_t num = buf[size];
|
||||
size++; // num
|
||||
if (size + num*LEASE_SIZE > len) return 0;
|
||||
uint64_t timestamp= 0 ;
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
size += 36; // gateway (32) + tunnelId(4)
|
||||
auto endDate = bufbe64toh (buf + size);
|
||||
size += 8; // end date
|
||||
if (!timestamp || endDate < timestamp)
|
||||
timestamp = endDate;
|
||||
}
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
bool LeaseSet::IsNewer (const uint8_t * buf, size_t len) const
|
||||
{
|
||||
return ExtractTimestamp (buf, len) > ExtractTimestamp (m_Buffer, m_BufferLen);
|
||||
}
|
||||
|
||||
const std::vector<std::shared_ptr<const Lease> > LeaseSet::GetNonExpiredLeases (bool withThreshold) const
|
||||
{
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
std::vector<Lease> leases;
|
||||
for (auto& it: m_Leases)
|
||||
if (ts < it.endDate)
|
||||
std::vector<std::shared_ptr<const Lease> > leases;
|
||||
for (const auto& it: m_Leases)
|
||||
{
|
||||
auto endDate = it->endDate;
|
||||
if (withThreshold)
|
||||
endDate += LEASE_ENDDATE_THRESHOLD;
|
||||
else
|
||||
endDate -= LEASE_ENDDATE_THRESHOLD;
|
||||
if (ts < endDate)
|
||||
leases.push_back (it);
|
||||
}
|
||||
return leases;
|
||||
}
|
||||
|
||||
bool LeaseSet::HasExpiredLeases () const
|
||||
{
|
||||
{
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
for (auto& it: m_Leases)
|
||||
if (ts >= it.endDate) return true;
|
||||
for (const auto& it: m_Leases)
|
||||
if (ts >= it->endDate) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool LeaseSet::HasNonExpiredLeases () const
|
||||
bool LeaseSet::IsExpired () const
|
||||
{
|
||||
if (m_StoreLeases && IsEmpty ()) return true;
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
return ts > m_ExpirationTime;
|
||||
}
|
||||
|
||||
LocalLeaseSet::LocalLeaseSet (std::shared_ptr<const IdentityEx> identity, const uint8_t * encryptionPublicKey, std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels):
|
||||
m_ExpirationTime (0), m_Identity (identity)
|
||||
{
|
||||
int num = tunnels.size ();
|
||||
if (num > MAX_NUM_LEASES) num = MAX_NUM_LEASES;
|
||||
// identity
|
||||
auto signingKeyLen = m_Identity->GetSigningPublicKeyLen ();
|
||||
m_BufferLen = m_Identity->GetFullLen () + 256 + signingKeyLen + 1 + num*LEASE_SIZE + m_Identity->GetSignatureLen ();
|
||||
m_Buffer = new uint8_t[m_BufferLen];
|
||||
auto offset = m_Identity->ToBuffer (m_Buffer, m_BufferLen);
|
||||
memcpy (m_Buffer + offset, encryptionPublicKey, 256);
|
||||
offset += 256;
|
||||
memset (m_Buffer + offset, 0, signingKeyLen);
|
||||
offset += signingKeyLen;
|
||||
// num leases
|
||||
m_Buffer[offset] = num;
|
||||
offset++;
|
||||
// leases
|
||||
m_Leases = m_Buffer + offset;
|
||||
auto currentTime = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
memcpy (m_Buffer + offset, tunnels[i]->GetNextIdentHash (), 32);
|
||||
offset += 32; // gateway id
|
||||
htobe32buf (m_Buffer + offset, tunnels[i]->GetNextTunnelID ());
|
||||
offset += 4; // tunnel id
|
||||
uint64_t ts = tunnels[i]->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // 1 minute before expiration
|
||||
ts *= 1000; // in milliseconds
|
||||
if (ts > m_ExpirationTime) m_ExpirationTime = ts;
|
||||
// make sure leaseset is newer than previous, but adding some time to expiration date
|
||||
ts += (currentTime - tunnels[i]->GetCreationTime ()*1000LL)*2/i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT; // up to 2 secs
|
||||
htobe64buf (m_Buffer + offset, ts);
|
||||
offset += 8; // end date
|
||||
}
|
||||
// we don't sign it yet. must be signed later on
|
||||
}
|
||||
|
||||
LocalLeaseSet::LocalLeaseSet (std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len):
|
||||
m_ExpirationTime (0), m_Identity (identity)
|
||||
{
|
||||
m_BufferLen = len;
|
||||
m_Buffer = new uint8_t[m_BufferLen];
|
||||
memcpy (m_Buffer, buf, len);
|
||||
}
|
||||
|
||||
bool LocalLeaseSet::IsExpired () const
|
||||
{
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
for (auto& it: m_Leases)
|
||||
if (ts < it.endDate) return true;
|
||||
return false;
|
||||
return ts > m_ExpirationTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
105
LeaseSet.h
105
LeaseSet.h
@@ -4,6 +4,8 @@
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include "Identity.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -11,67 +13,106 @@ namespace i2p
|
||||
|
||||
namespace tunnel
|
||||
{
|
||||
class TunnelPool;
|
||||
class InboundTunnel;
|
||||
}
|
||||
|
||||
namespace data
|
||||
{
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
const int LEASE_ENDDATE_THRESHOLD = 51000; // in milliseconds
|
||||
struct Lease
|
||||
{
|
||||
uint8_t tunnelGateway[32];
|
||||
IdentHash tunnelGateway;
|
||||
uint32_t tunnelID;
|
||||
uint64_t endDate;
|
||||
|
||||
bool operator< (const Lease& other) const
|
||||
{
|
||||
if (endDate != other.endDate)
|
||||
return endDate > other.endDate;
|
||||
else
|
||||
return tunnelID < other.tunnelID;
|
||||
}
|
||||
uint64_t endDate; // 0 means invalid
|
||||
bool isUpdated; // trasient
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
|
||||
const int MAX_LS_BUFFER_SIZE = 3072;
|
||||
struct LeaseCmp
|
||||
{
|
||||
bool operator() (std::shared_ptr<const Lease> l1, std::shared_ptr<const Lease> l2) const
|
||||
{
|
||||
if (l1->tunnelID != l2->tunnelID)
|
||||
return l1->tunnelID < l2->tunnelID;
|
||||
else
|
||||
return l1->tunnelGateway < l2->tunnelGateway;
|
||||
};
|
||||
};
|
||||
|
||||
const size_t MAX_LS_BUFFER_SIZE = 3072;
|
||||
const size_t LEASE_SIZE = 44; // 32 + 4 + 8
|
||||
const uint8_t MAX_NUM_LEASES = 16;
|
||||
class LeaseSet: public RoutingDestination
|
||||
{
|
||||
public:
|
||||
|
||||
LeaseSet (const uint8_t * buf, int len);
|
||||
LeaseSet (const LeaseSet& ) = default;
|
||||
LeaseSet (const i2p::tunnel::TunnelPool& pool);
|
||||
LeaseSet& operator=(const LeaseSet& ) = default;
|
||||
void Update (const uint8_t * buf, int len);
|
||||
const IdentityEx& GetIdentity () const { return m_Identity; };
|
||||
LeaseSet (const uint8_t * buf, size_t len, bool storeLeases = true);
|
||||
~LeaseSet () { delete[] m_Buffer; };
|
||||
void Update (const uint8_t * buf, size_t len);
|
||||
bool IsNewer (const uint8_t * buf, size_t len) const;
|
||||
void PopulateLeases (); // from buffer
|
||||
std::shared_ptr<const IdentityEx> GetIdentity () const { return m_Identity; };
|
||||
|
||||
const uint8_t * GetBuffer () const { return m_Buffer; };
|
||||
size_t GetBufferLen () const { return m_BufferLen; };
|
||||
bool IsValid () const { return m_IsValid; };
|
||||
const std::vector<std::shared_ptr<const Lease> > GetNonExpiredLeases (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 operator== (const LeaseSet& other) const
|
||||
{ return m_BufferLen == other.m_BufferLen && !memcmp (m_Buffer, other.m_Buffer, m_BufferLen); };
|
||||
|
||||
// implements RoutingDestination
|
||||
const IdentHash& GetIdentHash () const { return m_Identity.GetIdentHash (); };
|
||||
const std::vector<Lease>& GetLeases () const { return m_Leases; };
|
||||
const std::vector<Lease> GetNonExpiredLeases () const;
|
||||
bool HasExpiredLeases () const;
|
||||
bool HasNonExpiredLeases () const;
|
||||
const IdentHash& GetIdentHash () const { return m_Identity->GetIdentHash (); };
|
||||
const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionKey; };
|
||||
bool IsDestination () const { return true; };
|
||||
|
||||
private:
|
||||
|
||||
void ReadFromBuffer ();
|
||||
void ReadFromBuffer (bool readIdentity = true);
|
||||
uint64_t ExtractTimestamp (const uint8_t * buf, size_t len) const; // min expiration time
|
||||
|
||||
private:
|
||||
|
||||
std::vector<Lease> m_Leases;
|
||||
IdentityEx m_Identity;
|
||||
bool m_IsValid, m_StoreLeases; // we don't need to store leases for floodfill
|
||||
std::set<std::shared_ptr<Lease>, LeaseCmp> m_Leases;
|
||||
uint64_t m_ExpirationTime; // in milliseconds
|
||||
std::shared_ptr<const IdentityEx> m_Identity;
|
||||
uint8_t m_EncryptionKey[256];
|
||||
uint8_t m_Buffer[MAX_LS_BUFFER_SIZE];
|
||||
uint8_t * m_Buffer;
|
||||
size_t m_BufferLen;
|
||||
};
|
||||
|
||||
class LocalLeaseSet
|
||||
{
|
||||
public:
|
||||
|
||||
LocalLeaseSet (std::shared_ptr<const IdentityEx> identity, const uint8_t * encryptionPublicKey, std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
|
||||
LocalLeaseSet (std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len);
|
||||
~LocalLeaseSet () { delete[] m_Buffer; };
|
||||
|
||||
const uint8_t * GetBuffer () const { return m_Buffer; };
|
||||
uint8_t * GetSignature () { return m_Buffer + m_BufferLen - GetSignatureLen (); };
|
||||
size_t GetBufferLen () const { return m_BufferLen; };
|
||||
size_t GetSignatureLen () const { return m_Identity->GetSignatureLen (); };
|
||||
uint8_t * GetLeases () { return m_Leases; };
|
||||
|
||||
const IdentHash& GetIdentHash () const { return m_Identity->GetIdentHash (); };
|
||||
bool IsExpired () const;
|
||||
uint64_t GetExpirationTime () const { return m_ExpirationTime; };
|
||||
void SetExpirationTime (uint64_t expirationTime) { m_ExpirationTime = expirationTime; };
|
||||
bool operator== (const LeaseSet& other) const
|
||||
{ return m_BufferLen == other.GetBufferLen () && !memcmp (other.GetBuffer (), other.GetBuffer (), m_BufferLen); };
|
||||
|
||||
|
||||
private:
|
||||
|
||||
uint64_t m_ExpirationTime; // in milliseconds
|
||||
std::shared_ptr<const IdentityEx> m_Identity;
|
||||
uint8_t * m_Buffer, * m_Leases;
|
||||
size_t m_BufferLen;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
206
Log.cpp
206
Log.cpp
@@ -1,43 +1,171 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016, 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 "Log.h"
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
|
||||
Log * g_Log = nullptr;
|
||||
|
||||
static const char * g_LogLevelStr[eNumLogLevels] =
|
||||
{
|
||||
"error", // eLogError
|
||||
"warn", // eLogWarning
|
||||
"info", // eLogInfo
|
||||
"debug" // eLogDebug
|
||||
};
|
||||
|
||||
void LogMsg::Process()
|
||||
{
|
||||
output << boost::posix_time::second_clock::local_time().time_of_day () <<
|
||||
"/" << g_LogLevelStr[level] << " - ";
|
||||
output << s.str();
|
||||
}
|
||||
|
||||
void Log::Flush ()
|
||||
{
|
||||
if (m_LogStream)
|
||||
m_LogStream->flush();
|
||||
}
|
||||
|
||||
void Log::SetLogFile (const std::string& fullFilePath)
|
||||
{
|
||||
auto logFile = new std::ofstream (fullFilePath, std::ofstream::out | std::ofstream::binary | std::ofstream::trunc);
|
||||
if (logFile->is_open ())
|
||||
namespace i2p {
|
||||
namespace log {
|
||||
Log logger;
|
||||
/**
|
||||
* @brief Maps our loglevel to their symbolic name
|
||||
*/
|
||||
static const char * g_LogLevelStr[eNumLogLevels] =
|
||||
{
|
||||
SetLogStream (logFile);
|
||||
LogPrint("Logging to file ", fullFilePath, " enabled.");
|
||||
}
|
||||
else
|
||||
delete logFile;
|
||||
}
|
||||
"error", // eLogError
|
||||
"warn", // eLogWarn
|
||||
"info", // eLogInfo
|
||||
"debug" // eLogDebug
|
||||
};
|
||||
|
||||
void Log::SetLogStream (std::ostream * logStream)
|
||||
{
|
||||
if (m_LogStream) delete m_LogStream;
|
||||
m_LogStream = logStream;
|
||||
}
|
||||
#ifndef _WIN32
|
||||
/**
|
||||
* @brief Maps our log levels to syslog one
|
||||
* @return syslog priority LOG_*, as defined in syslog.h
|
||||
*/
|
||||
static inline int GetSyslogPrio (enum LogLevel l) {
|
||||
int priority = LOG_DEBUG;
|
||||
switch (l) {
|
||||
case eLogError : priority = LOG_ERR; break;
|
||||
case eLogWarning : priority = LOG_WARNING; break;
|
||||
case eLogInfo : priority = LOG_INFO; break;
|
||||
case eLogDebug : priority = LOG_DEBUG; break;
|
||||
default : priority = LOG_DEBUG; break;
|
||||
}
|
||||
return priority;
|
||||
}
|
||||
#endif
|
||||
|
||||
Log::Log():
|
||||
m_Destination(eLogStdout), m_MinLevel(eLogInfo),
|
||||
m_LogStream (nullptr), m_Logfile(""), m_IsReady(false)
|
||||
{
|
||||
}
|
||||
|
||||
Log::~Log ()
|
||||
{
|
||||
switch (m_Destination) {
|
||||
#ifndef _WIN32
|
||||
case eLogSyslog :
|
||||
closelog();
|
||||
break;
|
||||
#endif
|
||||
case eLogFile:
|
||||
case eLogStream:
|
||||
if (m_LogStream) m_LogStream->flush();
|
||||
break;
|
||||
default:
|
||||
/* do nothing */
|
||||
break;
|
||||
}
|
||||
Process();
|
||||
}
|
||||
|
||||
void Log::SetLogLevel (const std::string& level) {
|
||||
if (level == "error") { m_MinLevel = eLogError; }
|
||||
else if (level == "warn") { m_MinLevel = eLogWarning; }
|
||||
else if (level == "info") { m_MinLevel = eLogInfo; }
|
||||
else if (level == "debug") { m_MinLevel = eLogDebug; }
|
||||
else {
|
||||
LogPrint(eLogError, "Log: unknown loglevel: ", level);
|
||||
return;
|
||||
}
|
||||
LogPrint(eLogInfo, "Log: min messages level set to ", level);
|
||||
}
|
||||
|
||||
const char * Log::TimeAsString(std::time_t t) {
|
||||
if (t != m_LastTimestamp) {
|
||||
strftime(m_LastDateTime, sizeof(m_LastDateTime), "%H:%M:%S", localtime(&t));
|
||||
m_LastTimestamp = t;
|
||||
}
|
||||
return m_LastDateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @note This function better to be run in separate thread due to disk i/o.
|
||||
* 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);
|
||||
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) {
|
||||
#ifndef _WIN32
|
||||
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)
|
||||
<< "@" << short_tid
|
||||
<< "/" << g_LogLevelStr[msg->level]
|
||||
<< " - " << msg->text << std::endl;
|
||||
break;
|
||||
} // switch
|
||||
} // while
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (m_LogStream) m_LogStream = nullptr; // close previous
|
||||
auto flags = std::ofstream::out | std::ofstream::app;
|
||||
auto os = std::make_shared<std::ofstream> (path, flags);
|
||||
if (os->is_open ())
|
||||
{
|
||||
m_Logfile = path;
|
||||
m_Destination = eLogFile;
|
||||
m_LogStream = os;
|
||||
return;
|
||||
}
|
||||
LogPrint(eLogError, "Log: can't open file ", path);
|
||||
}
|
||||
|
||||
void Log::SendTo (std::shared_ptr<std::ostream> os) {
|
||||
m_Destination = eLogStream;
|
||||
m_LogStream = os;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
void Log::SendTo(const char *name, int facility) {
|
||||
m_Destination = eLogSyslog;
|
||||
m_LogStream = nullptr;
|
||||
openlog(name, LOG_CONS | LOG_PID, facility);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Log::Reopen() {
|
||||
if (m_Destination == eLogFile)
|
||||
SendTo(m_Logfile);
|
||||
}
|
||||
|
||||
Log & Logger() {
|
||||
return logger;
|
||||
}
|
||||
} // log
|
||||
} // i2p
|
||||
|
||||
232
Log.h
232
Log.h
@@ -1,13 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016, 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
|
||||
*/
|
||||
|
||||
#ifndef LOG_H__
|
||||
#define LOG_H__
|
||||
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include "Queue.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <syslog.h>
|
||||
#endif
|
||||
|
||||
enum LogLevel
|
||||
{
|
||||
eLogError = 0,
|
||||
@@ -17,101 +31,157 @@ enum LogLevel
|
||||
eNumLogLevels
|
||||
};
|
||||
|
||||
struct LogMsg
|
||||
{
|
||||
std::stringstream s;
|
||||
std::ostream& output;
|
||||
LogLevel level;
|
||||
|
||||
LogMsg (std::ostream& o = std::cout, LogLevel l = eLogInfo): output (o), level (l) {};
|
||||
|
||||
void Process();
|
||||
enum LogType {
|
||||
eLogStdout = 0,
|
||||
eLogStream,
|
||||
eLogFile,
|
||||
#ifndef _WIN32
|
||||
eLogSyslog,
|
||||
#endif
|
||||
};
|
||||
|
||||
class Log: public i2p::util::MsgQueue<LogMsg>
|
||||
{
|
||||
public:
|
||||
namespace i2p {
|
||||
namespace log {
|
||||
struct LogMsg; /* forward declaration */
|
||||
|
||||
Log (): m_LogStream (nullptr) { SetOnEmpty (std::bind (&Log::Flush, this)); };
|
||||
~Log () { delete m_LogStream; };
|
||||
|
||||
void SetLogFile (const std::string& fullFilePath);
|
||||
void SetLogStream (std::ostream * logStream);
|
||||
std::ostream * GetLogStream () const { return m_LogStream; };
|
||||
|
||||
private:
|
||||
|
||||
void Flush ();
|
||||
|
||||
private:
|
||||
|
||||
std::ostream * m_LogStream;
|
||||
};
|
||||
|
||||
extern Log * g_Log;
|
||||
|
||||
inline void StartLog (const std::string& fullFilePath)
|
||||
{
|
||||
if (!g_Log)
|
||||
{
|
||||
g_Log = new Log ();
|
||||
if (fullFilePath.length () > 0)
|
||||
g_Log->SetLogFile (fullFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
inline void StartLog (std::ostream * s)
|
||||
{
|
||||
if (!g_Log)
|
||||
{
|
||||
g_Log = new Log ();
|
||||
if (s)
|
||||
g_Log->SetLogStream (s);
|
||||
}
|
||||
}
|
||||
|
||||
inline void StopLog ()
|
||||
{
|
||||
if (g_Log)
|
||||
class Log
|
||||
{
|
||||
delete g_Log;
|
||||
g_Log = nullptr;
|
||||
}
|
||||
}
|
||||
private:
|
||||
|
||||
enum LogType m_Destination;
|
||||
enum LogLevel m_MinLevel;
|
||||
std::shared_ptr<std::ostream> m_LogStream;
|
||||
std::string m_Logfile;
|
||||
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;
|
||||
|
||||
private:
|
||||
|
||||
/** prevent making copies */
|
||||
Log (const Log &);
|
||||
const Log& operator=(const Log&);
|
||||
|
||||
/**
|
||||
* @brief process stored messages in queue
|
||||
*/
|
||||
void Process ();
|
||||
|
||||
/**
|
||||
* @brief Makes formatted string from unix timestamp
|
||||
* @param ts Second since epoch
|
||||
*
|
||||
* This function internally caches the result for last provided value
|
||||
*/
|
||||
const char * TimeAsString(std::time_t ts);
|
||||
|
||||
public:
|
||||
|
||||
Log ();
|
||||
~Log ();
|
||||
|
||||
LogType GetLogType () { return m_Destination; };
|
||||
LogLevel GetLogLevel () { return m_MinLevel; };
|
||||
|
||||
/**
|
||||
* @brief Sets minimal allowed level for log messages
|
||||
* @param level String with wanted minimal msg level
|
||||
*/
|
||||
void SetLogLevel (const std::string& level);
|
||||
|
||||
/**
|
||||
* @brief Sets log destination to logfile
|
||||
* @param path Path to logfile
|
||||
*/
|
||||
void SendTo (const std::string &path);
|
||||
|
||||
/**
|
||||
* @brief Sets log destination to given output stream
|
||||
* @param os Output stream
|
||||
*/
|
||||
void SendTo (std::shared_ptr<std::ostream> os);
|
||||
|
||||
#ifndef _WIN32
|
||||
/**
|
||||
* @brief Sets log destination to syslog
|
||||
* @param name Wanted program name
|
||||
* @param facility Wanted log category
|
||||
*/
|
||||
void SendTo (const char *name, int facility);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Format log message and write to output stream/syslog
|
||||
* @param msg Pointer to processed message
|
||||
*/
|
||||
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();
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct LogMsg
|
||||
* @brief Log message container
|
||||
*
|
||||
* We creating it somewhere with LogPrint(),
|
||||
* then put in MsgQueue for later processing.
|
||||
*/
|
||||
struct LogMsg {
|
||||
std::time_t timestamp;
|
||||
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)
|
||||
{
|
||||
s << 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)
|
||||
{
|
||||
LogPrint (s, arg);
|
||||
LogPrint (s, args...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create log message and send it to queue
|
||||
* @param level Message level (eLogError, eLogInfo, ...)
|
||||
* @param args Array of message parts
|
||||
*/
|
||||
template<typename... TArgs>
|
||||
void LogPrint (LogLevel level, TArgs... args)
|
||||
void LogPrint (LogLevel level, TArgs... args)
|
||||
{
|
||||
LogMsg * msg = (g_Log && g_Log->GetLogStream ()) ? new LogMsg (*g_Log->GetLogStream (), level) :
|
||||
new LogMsg (std::cout, level);
|
||||
LogPrint (msg->s, args...);
|
||||
msg->s << std::endl;
|
||||
if (g_Log)
|
||||
g_Log->Put (msg);
|
||||
else
|
||||
{
|
||||
msg->Process ();
|
||||
delete msg;
|
||||
}
|
||||
i2p::log::Log &log = i2p::log::Logger();
|
||||
if (level > log.GetLogLevel ())
|
||||
return;
|
||||
|
||||
// fold message to single string
|
||||
std::stringstream ss("");
|
||||
LogPrint (ss, 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);
|
||||
}
|
||||
|
||||
template<typename... TArgs>
|
||||
void LogPrint (TArgs... args)
|
||||
{
|
||||
LogPrint (eLogInfo, args...);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif // LOG_H__
|
||||
|
||||
65
Makefile
65
Makefile
@@ -1,6 +1,9 @@
|
||||
UNAME := $(shell uname -s)
|
||||
SHLIB := libi2pd.so
|
||||
I2PD := i2p
|
||||
ARLIB := libi2pd.a
|
||||
SHLIB_CLIENT := libi2pdclient.so
|
||||
ARLIB_CLIENT := libi2pdclient.a
|
||||
I2PD := i2pd
|
||||
GREP := fgrep
|
||||
DEPS := obj/make.dep
|
||||
|
||||
@@ -8,26 +11,39 @@ include filelist.mk
|
||||
|
||||
USE_AESNI := yes
|
||||
USE_STATIC := no
|
||||
USE_MESHNET := no
|
||||
USE_UPNP := no
|
||||
|
||||
ifeq ($(UNAME),Darwin)
|
||||
DAEMON_SRC += DaemonLinux.cpp
|
||||
include Makefile.osx
|
||||
ifeq ($(HOMEBREW),1)
|
||||
include Makefile.homebrew
|
||||
else
|
||||
include Makefile.osx
|
||||
endif
|
||||
else ifeq ($(shell echo $(UNAME) | $(GREP) -c FreeBSD),1)
|
||||
DAEMON_SRC += DaemonLinux.cpp
|
||||
include Makefile.bsd
|
||||
else ifeq ($(UNAME),Linux)
|
||||
DAEMON_SRC += DaemonLinux.cpp
|
||||
include Makefile.linux
|
||||
else # win32
|
||||
DAEMON_SRC += DaemonWin32.cpp
|
||||
else # win32 mingw
|
||||
DAEMON_SRC += DaemonWin32.cpp Win32/Win32Service.cpp Win32/Win32App.cpp
|
||||
include Makefile.mingw
|
||||
endif
|
||||
|
||||
all: mk_build_dir $(SHLIB) $(I2PD)
|
||||
ifeq ($(USE_MESHNET),yes)
|
||||
NEEDED_CXXFLAGS += -DMESHNET
|
||||
endif
|
||||
|
||||
mk_build_dir:
|
||||
mkdir -p obj
|
||||
all: mk_obj_dir $(ARLIB) $(ARLIB_CLIENT) $(I2PD)
|
||||
|
||||
api: $(SHLIB)
|
||||
mk_obj_dir:
|
||||
@mkdir -p obj
|
||||
@mkdir -p obj/Win32
|
||||
|
||||
api: mk_obj_dir $(SHLIB) $(ARLIB)
|
||||
api_client: mk_obj_dir $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
|
||||
|
||||
## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time
|
||||
## **without** overwriting the CXXFLAGS which we need in order to build.
|
||||
@@ -36,19 +52,18 @@ api: $(SHLIB)
|
||||
## -std=c++11. If you want to remove this variable please do so in a way that allows setting
|
||||
## custom FLAGS to work at build-time.
|
||||
|
||||
deps:
|
||||
@mkdir -p obj
|
||||
deps: mk_obj_dir
|
||||
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) -MM *.cpp > $(DEPS)
|
||||
@sed -i -e '/\.o:/ s/^/obj\//' $(DEPS)
|
||||
|
||||
obj/%.o : %.cpp
|
||||
@mkdir -p obj
|
||||
obj/%.o: %.cpp
|
||||
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(CPU_FLAGS) -c -o $@ $<
|
||||
|
||||
# '-' is 'ignore if missing' on first run
|
||||
-include $(DEPS)
|
||||
|
||||
$(I2PD): $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
|
||||
DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
|
||||
$(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT)
|
||||
$(CXX) -o $@ $^ $(LDLIBS) $(LDFLAGS)
|
||||
|
||||
$(SHLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
|
||||
@@ -56,18 +71,36 @@ ifneq ($(USE_STATIC),yes)
|
||||
$(CXX) $(LDFLAGS) $(LDLIBS) -shared -o $@ $^
|
||||
endif
|
||||
|
||||
$(SHLIB_CLIENT): $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
|
||||
$(CXX) $(LDFLAGS) $(LDLIBS) -shared -o $@ $^
|
||||
|
||||
$(ARLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
|
||||
ar -r $@ $^
|
||||
|
||||
$(ARLIB_CLIENT): $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
|
||||
ar -r $@ $^
|
||||
|
||||
clean:
|
||||
rm -rf obj
|
||||
$(RM) $(I2PD) $(SHLIB)
|
||||
rm -rf docs/generated
|
||||
$(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
|
||||
|
||||
LATEST_TAG=$(shell git describe --tags --abbrev=0 master)
|
||||
strip: $(I2PD) $(SHLIB_CLIENT) $(SHLIB)
|
||||
strip $^
|
||||
|
||||
LATEST_TAG=$(shell git describe --tags --abbrev=0 openssl)
|
||||
dist:
|
||||
git archive --format=tar.gz -9 --worktree-attributes \
|
||||
--prefix=i2pd_$(LATEST_TAG)/ $(LATEST_TAG) -o i2pd_$(LATEST_TAG).tar.gz
|
||||
|
||||
doxygen:
|
||||
doxygen -s docs/Doxyfile
|
||||
|
||||
.PHONY: all
|
||||
.PHONY: clean
|
||||
.PHONY: deps
|
||||
.PHONY: doxygen
|
||||
.PHONY: dist
|
||||
.PHONY: api
|
||||
.PHONY: mk_build_dir
|
||||
.PHONY: api_client
|
||||
.PHONY: mk_obj_dir
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
CXX = g++
|
||||
CXX = clang++
|
||||
CXXFLAGS = -O2
|
||||
## NOTE: NEEDED_CXXFLAGS is here so that custom CXXFLAGS can be specified at build time
|
||||
## **without** overwriting the CXXFLAGS which we need in order to build.
|
||||
@@ -6,7 +6,7 @@ CXXFLAGS = -O2
|
||||
## (e.g. -fstack-protector-strong -Wformat -Werror=format-security), we do not want to remove
|
||||
## -std=c++11. If you want to remove this variable please do so in a way that allows setting
|
||||
## custom FLAGS to work at build-time.
|
||||
NEEDED_CXXFLAGS = -std=c++11
|
||||
NEEDED_CXXFLAGS = -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1
|
||||
INCFLAGS = -I/usr/include/ -I/usr/local/include/
|
||||
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib
|
||||
LDLIBS = -lcryptopp -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
|
||||
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||
|
||||
29
Makefile.homebrew
Normal file
29
Makefile.homebrew
Normal file
@@ -0,0 +1,29 @@
|
||||
# root directory holding homebrew
|
||||
BREWROOT = /usr/local/
|
||||
BOOSTROOT = ${BREWROOT}/opt/boost
|
||||
SSLROOT = ${BREWROOT}/opt/libressl
|
||||
CXX = clang++
|
||||
CXXFLAGS = -g -Wall -std=c++11 -DMAC_OSX
|
||||
INCFLAGS = -I${SSLROOT}/include -I${BOOSTROOT}/include
|
||||
LDFLAGS = -L${SSLROOT}/lib -L${BOOSTROOT}/lib
|
||||
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||
|
||||
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
|
||||
# note from psi: 2009 macbook does not have aesni
|
||||
#ifeq ($(USE_AESNI),yes)
|
||||
# CXXFLAGS += -maes -DAESNI
|
||||
#endif
|
||||
|
||||
# Disabled, since it will be the default make rule. I think its better
|
||||
# to define the default rule in Makefile and not Makefile.<ostype> - torkel
|
||||
#install: all
|
||||
# test -d ${PREFIX} || mkdir -p ${PREFIX}/
|
||||
# cp -r i2p ${PREFIX}/
|
||||
@@ -1,5 +1,6 @@
|
||||
CXXFLAGS = -g -Wall
|
||||
INCFLAGS =
|
||||
# set defaults instead redefine
|
||||
CXXFLAGS ?= -g -Wall -Wextra -Wno-unused-parameter -pedantic
|
||||
INCFLAGS ?=
|
||||
|
||||
## NOTE: The NEEDED_CXXFLAGS are here so that custom CXXFLAGS can be specified at build time
|
||||
## **without** overwriting the CXXFLAGS which we need in order to build.
|
||||
@@ -8,15 +9,17 @@ INCFLAGS =
|
||||
## -std=c++11. If you want to remove this variable please do so in a way that allows setting
|
||||
## custom FLAGS to work at build-time.
|
||||
|
||||
# detect proper flag for c++11 support by gcc
|
||||
# detect proper flag for c++11 support by compilers
|
||||
CXXVER := $(shell $(CXX) -dumpversion)
|
||||
ifeq ($(shell expr match ${CXXVER} "4\.[0-9][0-9]"),4) # >= 4.10
|
||||
ifeq ($(shell expr match $(CXX) 'clang'),5)
|
||||
NEEDED_CXXFLAGS += -std=c++11
|
||||
else ifeq ($(shell expr match ${CXXVER} "4\.[0-9][0-9]"),4) # gcc >= 4.10
|
||||
NEEDED_CXXFLAGS += -std=c++11
|
||||
else ifeq ($(shell expr match ${CXXVER} "4\.[7-9]"),3) # >= 4.7
|
||||
NEEDED_CXXFLAGS += -std=c++11
|
||||
NEEDED_CXXFLAGS += -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1
|
||||
else ifeq ($(shell expr match ${CXXVER} "4\.6"),3) # = 4.6
|
||||
NEEDED_CXXFLAGS += -std=c++0x
|
||||
else ifeq ($(shell expr match $(CXX) 'clang'),5)
|
||||
else ifeq ($(shell expr match ${CXXVER} "[5-6]\.[0-9]"),3) # gcc >= 5.0
|
||||
NEEDED_CXXFLAGS += -std=c++11
|
||||
else # not supported
|
||||
$(error Compiler too old)
|
||||
@@ -29,18 +32,19 @@ ifeq ($(USE_STATIC),yes)
|
||||
LDLIBS = $(LIBDIR)/libboost_system.a
|
||||
LDLIBS += $(LIBDIR)/libboost_date_time.a
|
||||
LDLIBS += $(LIBDIR)/libboost_filesystem.a
|
||||
LDLIBS += $(LIBDIR)/libboost_regex.a
|
||||
LDLIBS += $(LIBDIR)/libboost_program_options.a
|
||||
LDLIBS += $(LIBDIR)/libcryptopp.a
|
||||
LDLIBS += -lpthread -static-libstdc++ -static-libgcc
|
||||
LDLIBS += $(LIBDIR)/libssl.a
|
||||
LDLIBS += $(LIBDIR)/libcrypto.a
|
||||
LDLIBS += $(LIBDIR)/libz.a
|
||||
LDLIBS += -lpthread -static-libstdc++ -static-libgcc -lrt
|
||||
USE_AESNI := no
|
||||
else
|
||||
LDLIBS = -lcryptopp -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
|
||||
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||
endif
|
||||
|
||||
# UPNP Support (miniupnpc 1.5 or 1.6)
|
||||
ifeq ($(USE_UPNP),1)
|
||||
LDFLAGS += -ldl
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
LDFLAGS += -lminiupnpc
|
||||
CXXFLAGS += -DUSE_UPNP
|
||||
endif
|
||||
|
||||
|
||||
47
Makefile.mingw
Normal file
47
Makefile.mingw
Normal file
@@ -0,0 +1,47 @@
|
||||
USE_WIN32_APP=yes
|
||||
CXX = g++
|
||||
WINDRES = windres
|
||||
CXXFLAGS = -Os -D_MT -DWIN32 -D_WINDOWS -DWIN32_LEAN_AND_MEAN
|
||||
NEEDED_CXXFLAGS = -std=c++11
|
||||
BOOST_SUFFIX = -mt
|
||||
INCFLAGS = -I/usr/include/ -I/usr/local/include/
|
||||
LDFLAGS = -Wl,-rpath,/usr/local/lib \
|
||||
-L/usr/local/lib
|
||||
|
||||
# UPNP Support
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
CXXFLAGS += -DUSE_UPNP -DMINIUPNP_STATICLIB
|
||||
LDLIBS = -Wl,-Bstatic -lminiupnpc
|
||||
endif
|
||||
|
||||
LDLIBS += \
|
||||
-Wl,-Bstatic -lboost_system$(BOOST_SUFFIX) \
|
||||
-Wl,-Bstatic -lboost_date_time$(BOOST_SUFFIX) \
|
||||
-Wl,-Bstatic -lboost_filesystem$(BOOST_SUFFIX) \
|
||||
-Wl,-Bstatic -lboost_program_options$(BOOST_SUFFIX) \
|
||||
-Wl,-Bstatic -lssl \
|
||||
-Wl,-Bstatic -lcrypto \
|
||||
-Wl,-Bstatic -lz \
|
||||
-Wl,-Bstatic -lwsock32 \
|
||||
-Wl,-Bstatic -lws2_32 \
|
||||
-Wl,-Bstatic -lgdi32 \
|
||||
-Wl,-Bstatic -liphlpapi \
|
||||
-static-libgcc -static-libstdc++ \
|
||||
-Wl,-Bstatic -lstdc++ \
|
||||
-Wl,-Bstatic -lpthread
|
||||
|
||||
ifeq ($(USE_WIN32_APP), yes)
|
||||
CXXFLAGS += -DWIN32_APP
|
||||
LDFLAGS += -mwindows -s
|
||||
DAEMON_RC += Win32/Resource.rc
|
||||
DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC))
|
||||
endif
|
||||
|
||||
ifeq ($(USE_AESNI),1)
|
||||
CPU_FLAGS = -maes -DAESNI
|
||||
else
|
||||
CPU_FLAGS = -msse
|
||||
endif
|
||||
|
||||
obj/%.o : %.rc
|
||||
$(WINDRES) -i $< -o $@
|
||||
12
Makefile.osx
12
Makefile.osx
@@ -1,11 +1,11 @@
|
||||
CXX = clang++
|
||||
CXXFLAGS = -g -Wall -std=c++11 -DCRYPTOPP_DISABLE_ASM -DMAC_OSX
|
||||
#CXXFLAGS = -g -O2 -Wall -std=c++11 -DCRYPTOPP_DISABLE_ASM
|
||||
INCFLAGS = -I/usr/local/include
|
||||
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib
|
||||
LDLIBS = -lcryptopp -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
|
||||
CXXFLAGS = -g -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
|
||||
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||
|
||||
ifeq ($(USE_UPNP),1)
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
LDFLAGS += -ldl
|
||||
CXXFLAGS += -DUSE_UPNP
|
||||
endif
|
||||
|
||||
418
NTCPSession.cpp
418
NTCPSession.cpp
@@ -1,11 +1,11 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "I2PEndian.h"
|
||||
#include <cryptopp/dh.h>
|
||||
#include "base64.h"
|
||||
#include "Base.h"
|
||||
#include "Crypto.h"
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
#include "CryptoConst.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "RouterContext.h"
|
||||
#include "Transports.h"
|
||||
@@ -19,12 +19,11 @@ namespace i2p
|
||||
namespace transport
|
||||
{
|
||||
NTCPSession::NTCPSession (NTCPServer& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter):
|
||||
TransportSession (in_RemoteRouter), m_Server (server), m_Socket (m_Server.GetService ()),
|
||||
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_ReceiveBufferOffset (0), m_NextMessage (nullptr), m_IsSending (false),
|
||||
m_NumSentBytes (0), m_NumReceivedBytes (0)
|
||||
m_ReceiveBufferOffset (0), m_NextMessage (nullptr), m_IsSending (false)
|
||||
{
|
||||
m_DHKeysPair = transports.GetNextDHKeysPair ();
|
||||
m_Establisher = new Establisher;
|
||||
}
|
||||
|
||||
@@ -35,15 +34,9 @@ namespace transport
|
||||
|
||||
void NTCPSession::CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key)
|
||||
{
|
||||
CryptoPP::DH dh (elgp, elgg);
|
||||
uint8_t sharedKey[256];
|
||||
if (!dh.Agree (sharedKey, m_DHKeysPair->privateKey, pubKey))
|
||||
{
|
||||
LogPrint (eLogError, "Couldn't create shared key");
|
||||
Terminate ();
|
||||
return;
|
||||
};
|
||||
|
||||
m_DHKeysPair->Agree (pubKey, sharedKey);
|
||||
|
||||
uint8_t * aesKey = key;
|
||||
if (sharedKey[0] & 0x80)
|
||||
{
|
||||
@@ -61,7 +54,7 @@ namespace transport
|
||||
nonZero++;
|
||||
if (nonZero - sharedKey > 32)
|
||||
{
|
||||
LogPrint (eLogWarning, "First 32 bytes of shared key is all zeros. Ignored");
|
||||
LogPrint (eLogWarning, "NTCP: First 32 bytes of shared key is all zeros, ignored");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -83,16 +76,10 @@ namespace transport
|
||||
m_Socket.close ();
|
||||
transports.PeerDisconnected (shared_from_this ());
|
||||
m_Server.RemoveNTCPSession (shared_from_this ());
|
||||
for (auto it: m_SendQueue)
|
||||
DeleteI2NPMessage (it);
|
||||
m_SendQueue.clear ();
|
||||
if (m_NextMessage)
|
||||
{
|
||||
i2p::DeleteI2NPMessage (m_NextMessage);
|
||||
m_NextMessage = nullptr;
|
||||
}
|
||||
m_NextMessage = nullptr;
|
||||
m_TerminationTimer.cancel ();
|
||||
LogPrint (eLogInfo, "NTCP session terminated");
|
||||
LogPrint (eLogDebug, "NTCP: session terminated");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,12 +90,9 @@ namespace transport
|
||||
delete m_Establisher;
|
||||
m_Establisher = nullptr;
|
||||
|
||||
delete m_DHKeysPair;
|
||||
m_DHKeysPair = nullptr;
|
||||
|
||||
SendTimeSyncMessage ();
|
||||
PostI2NPMessage (CreateDatabaseStoreMsg ()); // we tell immediately who we are
|
||||
|
||||
SendTimeSyncMessage ();
|
||||
transports.PeerConnected (shared_from_this ());
|
||||
}
|
||||
|
||||
@@ -117,10 +101,10 @@ namespace transport
|
||||
if (!m_DHKeysPair)
|
||||
m_DHKeysPair = transports.GetNextDHKeysPair ();
|
||||
// send Phase1
|
||||
const uint8_t * x = m_DHKeysPair->publicKey;
|
||||
const uint8_t * x = m_DHKeysPair->GetPublicKey ();
|
||||
memcpy (m_Establisher->phase1.pubKey, x, 256);
|
||||
CryptoPP::SHA256().CalculateDigest(m_Establisher->phase1.HXxorHI, x, 256);
|
||||
const uint8_t * ident = m_RemoteIdentity.GetIdentHash ();
|
||||
SHA256(x, 256, m_Establisher->phase1.HXxorHI);
|
||||
const uint8_t * ident = m_RemoteIdentity->GetIdentHash ();
|
||||
for (int i = 0; i < 32; i++)
|
||||
m_Establisher->phase1.HXxorHI[i] ^= ident[i];
|
||||
|
||||
@@ -146,9 +130,10 @@ namespace transport
|
||||
|
||||
void NTCPSession::HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
(void) bytes_transferred;
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "Couldn't send Phase 1 message: ", ecode.message ());
|
||||
LogPrint (eLogInfo, "NTCP: couldn't send Phase 1 message: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
@@ -162,9 +147,10 @@ namespace transport
|
||||
|
||||
void NTCPSession::HandlePhase1Received (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
(void) bytes_transferred;
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "Phase 1 read error: ", ecode.message ());
|
||||
LogPrint (eLogInfo, "NTCP: phase 1 read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
@@ -172,13 +158,13 @@ namespace transport
|
||||
{
|
||||
// verify ident
|
||||
uint8_t digest[32];
|
||||
CryptoPP::SHA256().CalculateDigest(digest, m_Establisher->phase1.pubKey, 256);
|
||||
const uint8_t * ident = i2p::context.GetRouterInfo ().GetIdentHash ();
|
||||
SHA256(m_Establisher->phase1.pubKey, 256, digest);
|
||||
const uint8_t * ident = i2p::context.GetIdentHash ();
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
if ((m_Establisher->phase1.HXxorHI[i] ^ ident[i]) != digest[i])
|
||||
{
|
||||
LogPrint (eLogError, "Wrong ident");
|
||||
LogPrint (eLogError, "NTCP: phase 1 error: ident mismatch");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
@@ -192,15 +178,15 @@ namespace transport
|
||||
{
|
||||
if (!m_DHKeysPair)
|
||||
m_DHKeysPair = transports.GetNextDHKeysPair ();
|
||||
const uint8_t * y = m_DHKeysPair->publicKey;
|
||||
const uint8_t * y = m_DHKeysPair->GetPublicKey ();
|
||||
memcpy (m_Establisher->phase2.pubKey, y, 256);
|
||||
uint8_t xy[512];
|
||||
memcpy (xy, m_Establisher->phase1.pubKey, 256);
|
||||
memcpy (xy + 256, y, 256);
|
||||
CryptoPP::SHA256().CalculateDigest(m_Establisher->phase2.encrypted.hxy, xy, 512);
|
||||
SHA256(xy, 512, m_Establisher->phase2.encrypted.hxy);
|
||||
uint32_t tsB = htobe32 (i2p::util::GetSecondsSinceEpoch ());
|
||||
m_Establisher->phase2.encrypted.timestamp = tsB;
|
||||
// TODO: fill filler
|
||||
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);
|
||||
@@ -217,9 +203,10 @@ namespace transport
|
||||
|
||||
void NTCPSession::HandlePhase2Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB)
|
||||
{
|
||||
(void) bytes_transferred;
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "Couldn't send Phase 2 message: ", ecode.message ());
|
||||
LogPrint (eLogInfo, "NTCP: Couldn't send Phase 2 message: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
@@ -233,13 +220,14 @@ namespace transport
|
||||
|
||||
void NTCPSession::HandlePhase2Received (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
(void) bytes_transferred;
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "Phase 2 read error: ", ecode.message (), ". Wrong ident assumed");
|
||||
LogPrint (eLogInfo, "NTCP: Phase 2 read error: ", ecode.message (), ". Wrong ident assumed");
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
// this RI is not valid
|
||||
i2p::data::netdb.SetUnreachable (GetRemoteIdentity ().GetIdentHash (), true);
|
||||
i2p::data::netdb.SetUnreachable (GetRemoteIdentity ()->GetIdentHash (), true);
|
||||
transports.ReuseDHKeysPair (m_DHKeysPair);
|
||||
m_DHKeysPair = nullptr;
|
||||
Terminate ();
|
||||
@@ -257,11 +245,13 @@ namespace transport
|
||||
m_Decryption.Decrypt((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted);
|
||||
// verify
|
||||
uint8_t xy[512];
|
||||
memcpy (xy, m_DHKeysPair->publicKey, 256);
|
||||
memcpy (xy, m_DHKeysPair->GetPublicKey (), 256);
|
||||
memcpy (xy + 256, m_Establisher->phase2.pubKey, 256);
|
||||
if (!CryptoPP::SHA256().VerifyDigest(m_Establisher->phase2.encrypted.hxy, xy, 512))
|
||||
uint8_t digest[32];
|
||||
SHA256 (xy, 512, digest);
|
||||
if (memcmp(m_Establisher->phase2.encrypted.hxy, digest, 32))
|
||||
{
|
||||
LogPrint (eLogError, "Incorrect hash");
|
||||
LogPrint (eLogError, "NTCP: Phase 2 process error: incorrect hash");
|
||||
transports.ReuseDHKeysPair (m_DHKeysPair);
|
||||
m_DHKeysPair = nullptr;
|
||||
Terminate ();
|
||||
@@ -275,19 +265,20 @@ namespace transport
|
||||
{
|
||||
auto keys = i2p::context.GetPrivateKeys ();
|
||||
uint8_t * buf = m_ReceiveBuffer;
|
||||
htobe16buf (buf, keys.GetPublic ().GetFullLen ());
|
||||
htobe16buf (buf, keys.GetPublic ()->GetFullLen ());
|
||||
buf += 2;
|
||||
buf += i2p::context.GetIdentity ().ToBuffer (buf, NTCP_BUFFER_SIZE);
|
||||
buf += i2p::context.GetIdentity ()->ToBuffer (buf, NTCP_BUFFER_SIZE);
|
||||
uint32_t tsA = htobe32 (i2p::util::GetSecondsSinceEpoch ());
|
||||
htobuf32(buf,tsA);
|
||||
buf += 4;
|
||||
size_t signatureLen = keys.GetPublic ().GetSignatureLen ();
|
||||
size_t signatureLen = keys.GetPublic ()->GetSignatureLen ();
|
||||
size_t len = (buf - m_ReceiveBuffer) + signatureLen;
|
||||
size_t paddingSize = len & 0x0F; // %16
|
||||
if (paddingSize > 0)
|
||||
{
|
||||
paddingSize = 16 - paddingSize;
|
||||
// TODO: fill padding with random data
|
||||
// fill padding with random data
|
||||
RAND_bytes(buf, paddingSize);
|
||||
buf += paddingSize;
|
||||
len += paddingSize;
|
||||
}
|
||||
@@ -295,9 +286,9 @@ namespace transport
|
||||
SignedData s;
|
||||
s.Insert (m_Establisher->phase1.pubKey, 256); // x
|
||||
s.Insert (m_Establisher->phase2.pubKey, 256); // y
|
||||
s.Insert (m_RemoteIdentity.GetIdentHash (), 32); // ident
|
||||
s.Insert (m_RemoteIdentity->GetIdentHash (), 32); // ident
|
||||
s.Insert (tsA); // tsA
|
||||
s.Insert (m_Establisher->phase2.encrypted.timestamp); // tsB
|
||||
s.Insert (m_Establisher->phase2.encrypted.timestamp, 4); // tsB
|
||||
s.Sign (keys, buf);
|
||||
|
||||
m_Encryption.Encrypt(m_ReceiveBuffer, len, m_ReceiveBuffer);
|
||||
@@ -307,16 +298,17 @@ namespace transport
|
||||
|
||||
void NTCPSession::HandlePhase3Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA)
|
||||
{
|
||||
(void) bytes_transferred;
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "Couldn't send Phase 3 message: ", ecode.message ());
|
||||
LogPrint (eLogInfo, "NTCP: Couldn't send Phase 3 message: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
// wait for phase4
|
||||
auto signatureLen = m_RemoteIdentity.GetSignatureLen ();
|
||||
auto signatureLen = m_RemoteIdentity->GetSignatureLen ();
|
||||
size_t paddingSize = signatureLen & 0x0F; // %16
|
||||
if (paddingSize > 0) signatureLen += (16 - paddingSize);
|
||||
boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer, signatureLen), boost::asio::transfer_all (),
|
||||
@@ -329,7 +321,7 @@ namespace transport
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "Phase 3 read error: ", ecode.message ());
|
||||
LogPrint (eLogInfo, "NTCP: Phase 3 read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
@@ -338,20 +330,20 @@ namespace transport
|
||||
m_Decryption.Decrypt (m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer);
|
||||
uint8_t * buf = m_ReceiveBuffer;
|
||||
uint16_t size = bufbe16toh (buf);
|
||||
m_RemoteIdentity.FromBuffer (buf + 2, size);
|
||||
if (m_Server.FindNTCPSession (m_RemoteIdentity.GetIdentHash ()))
|
||||
SetRemoteIdentity (std::make_shared<i2p::data::IdentityEx> (buf + 2, size));
|
||||
if (m_Server.FindNTCPSession (m_RemoteIdentity->GetIdentHash ()))
|
||||
{
|
||||
LogPrint (eLogError, "NTCP session already exists");
|
||||
LogPrint (eLogInfo, "NTCP: session already exists");
|
||||
Terminate ();
|
||||
}
|
||||
size_t expectedSize = size + 2/*size*/ + 4/*timestamp*/ + m_RemoteIdentity.GetSignatureLen ();
|
||||
size_t expectedSize = size + 2/*size*/ + 4/*timestamp*/ + m_RemoteIdentity->GetSignatureLen ();
|
||||
size_t paddingLen = expectedSize & 0x0F;
|
||||
if (paddingLen) paddingLen = (16 - paddingLen);
|
||||
if (expectedSize > NTCP_DEFAULT_PHASE3_SIZE)
|
||||
{
|
||||
// we need more bytes for Phase3
|
||||
expectedSize += paddingLen;
|
||||
boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer + NTCP_DEFAULT_PHASE3_SIZE, expectedSize), boost::asio::transfer_all (),
|
||||
boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer + NTCP_DEFAULT_PHASE3_SIZE, expectedSize - NTCP_DEFAULT_PHASE3_SIZE), boost::asio::transfer_all (),
|
||||
std::bind(&NTCPSession::HandlePhase3ExtraReceived, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2, tsB, paddingLen));
|
||||
}
|
||||
@@ -364,7 +356,7 @@ namespace transport
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "Phase 3 extra read error: ", ecode.message ());
|
||||
LogPrint (eLogInfo, "NTCP: Phase 3 extra read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
@@ -377,11 +369,22 @@ namespace transport
|
||||
|
||||
void NTCPSession::HandlePhase3 (uint32_t tsB, size_t paddingLen)
|
||||
{
|
||||
uint8_t * buf = m_ReceiveBuffer + m_RemoteIdentity.GetFullLen () + 2 /*size*/;
|
||||
uint8_t * buf = m_ReceiveBuffer + m_RemoteIdentity->GetFullLen () + 2 /*size*/;
|
||||
uint32_t tsA = buf32toh(buf);
|
||||
buf += 4;
|
||||
buf += paddingLen;
|
||||
|
||||
// check timestamp
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
uint32_t tsA1 = be32toh (tsA);
|
||||
if (tsA1 < ts - NTCP_CLOCK_SKEW || tsA1 > ts + NTCP_CLOCK_SKEW)
|
||||
{
|
||||
LogPrint (eLogError, "NTCP: Phase3 time difference ", ts - tsA1, " exceeds clock skew");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
|
||||
// check signature
|
||||
SignedData s;
|
||||
s.Insert (m_Establisher->phase1.pubKey, 256); // x
|
||||
s.Insert (m_Establisher->phase2.pubKey, 256); // y
|
||||
@@ -390,7 +393,7 @@ namespace transport
|
||||
s.Insert (tsB); // tsB
|
||||
if (!s.Verify (m_RemoteIdentity, buf))
|
||||
{
|
||||
LogPrint (eLogError, "signature verification failed");
|
||||
LogPrint (eLogError, "NTCP: signature verification failed");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
@@ -403,11 +406,11 @@ namespace transport
|
||||
SignedData s;
|
||||
s.Insert (m_Establisher->phase1.pubKey, 256); // x
|
||||
s.Insert (m_Establisher->phase2.pubKey, 256); // y
|
||||
s.Insert (m_RemoteIdentity.GetIdentHash (), 32); // ident
|
||||
s.Insert (m_RemoteIdentity->GetIdentHash (), 32); // ident
|
||||
s.Insert (tsA); // tsA
|
||||
s.Insert (tsB); // tsB
|
||||
auto keys = i2p::context.GetPrivateKeys ();
|
||||
auto signatureLen = keys.GetPublic ().GetSignatureLen ();
|
||||
auto signatureLen = keys.GetPublic ()->GetSignatureLen ();
|
||||
s.Sign (keys, m_ReceiveBuffer);
|
||||
size_t paddingSize = signatureLen & 0x0F; // %16
|
||||
if (paddingSize > 0) signatureLen += (16 - paddingSize);
|
||||
@@ -419,15 +422,16 @@ namespace transport
|
||||
|
||||
void NTCPSession::HandlePhase4Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
(void) bytes_transferred;
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogWarning, "Couldn't send Phase 4 message: ", ecode.message ());
|
||||
LogPrint (eLogWarning, "NTCP: Couldn't send Phase 4 message: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogInfo, "NTCP server session from ", m_Socket.remote_endpoint (), " connected");
|
||||
LogPrint (eLogDebug, "NTCP: Server session from ", m_Socket.remote_endpoint (), " connected");
|
||||
m_Server.AddNTCPSession (shared_from_this ());
|
||||
|
||||
Connected ();
|
||||
@@ -441,11 +445,11 @@ namespace transport
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "Phase 4 read error: ", ecode.message (), ". Check your clock");
|
||||
LogPrint (eLogError, "NTCP: Phase 4 read error: ", ecode.message (), ". Check your clock");
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
// this router doesn't like us
|
||||
i2p::data::netdb.SetUnreachable (GetRemoteIdentity ().GetIdentHash (), true);
|
||||
i2p::data::netdb.SetUnreachable (GetRemoteIdentity ()->GetIdentHash (), true);
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
@@ -453,21 +457,31 @@ namespace transport
|
||||
{
|
||||
m_Decryption.Decrypt(m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer);
|
||||
|
||||
// check timestamp
|
||||
uint32_t tsB = bufbe32toh (m_Establisher->phase2.encrypted.timestamp);
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (tsB < ts - NTCP_CLOCK_SKEW || tsB > ts + NTCP_CLOCK_SKEW)
|
||||
{
|
||||
LogPrint (eLogError, "NTCP: Phase4 time difference ", ts - tsB, " exceeds clock skew");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
|
||||
// verify signature
|
||||
SignedData s;
|
||||
s.Insert (m_Establisher->phase1.pubKey, 256); // x
|
||||
s.Insert (m_Establisher->phase2.pubKey, 256); // y
|
||||
s.Insert (i2p::context.GetRouterInfo ().GetIdentHash (), 32); // ident
|
||||
s.Insert (i2p::context.GetIdentHash (), 32); // ident
|
||||
s.Insert (tsA); // tsA
|
||||
s.Insert (m_Establisher->phase2.encrypted.timestamp); // tsB
|
||||
s.Insert (m_Establisher->phase2.encrypted.timestamp, 4); // tsB
|
||||
|
||||
if (!s.Verify (m_RemoteIdentity, m_ReceiveBuffer))
|
||||
{
|
||||
LogPrint (eLogError, "signature verification failed");
|
||||
LogPrint (eLogError, "NTCP: Phase 4 process error: signature verification failed");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
LogPrint (eLogInfo, "NTCP session to ", m_Socket.remote_endpoint (), " connected");
|
||||
LogPrint (eLogDebug, "NTCP: session to ", m_Socket.remote_endpoint (), " connected");
|
||||
Connected ();
|
||||
|
||||
m_ReceiveBufferOffset = 0;
|
||||
@@ -485,16 +499,18 @@ namespace transport
|
||||
|
||||
void NTCPSession::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "Read error: ", ecode.message ());
|
||||
if (!m_NumReceivedBytes) m_Server.Ban (m_ConnectedFrom);
|
||||
if (ecode) {
|
||||
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 ();
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_NumReceivedBytes += bytes_transferred;
|
||||
i2p::transport::transports.UpdateReceivedBytes (bytes_transferred);
|
||||
m_ReceiveBufferOffset += bytes_transferred;
|
||||
|
||||
if (m_ReceiveBufferOffset >= 16)
|
||||
@@ -528,7 +544,7 @@ namespace transport
|
||||
moreBytes = m_Socket.read_some (boost::asio::buffer (m_ReceiveBuffer + m_ReceiveBufferOffset, moreBytes));
|
||||
if (ec)
|
||||
{
|
||||
LogPrint (eLogError, "Read more bytes error: ", ec.message ());
|
||||
LogPrint (eLogInfo, "NTCP: Read more bytes error: ", ec.message ());
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
@@ -551,31 +567,29 @@ namespace transport
|
||||
{
|
||||
if (!m_NextMessage) // new message, header expected
|
||||
{
|
||||
m_NextMessage = i2p::NewI2NPMessage ();
|
||||
m_NextMessageOffset = 0;
|
||||
|
||||
m_Decryption.Decrypt (encrypted, m_NextMessage->buf);
|
||||
uint16_t dataSize = bufbe16toh (m_NextMessage->buf);
|
||||
// decrypt header and extract length
|
||||
uint8_t buf[16];
|
||||
m_Decryption.Decrypt (encrypted, buf);
|
||||
uint16_t dataSize = bufbe16toh (buf);
|
||||
if (dataSize)
|
||||
{
|
||||
// new message
|
||||
if (dataSize > NTCP_MAX_MESSAGE_SIZE)
|
||||
if (dataSize + 16U > NTCP_MAX_MESSAGE_SIZE - 2) // + 6 + padding
|
||||
{
|
||||
LogPrint (eLogError, "NTCP data size ", dataSize, " exceeds max size");
|
||||
i2p::DeleteI2NPMessage (m_NextMessage);
|
||||
m_NextMessage = nullptr;
|
||||
LogPrint (eLogError, "NTCP: data size ", dataSize, " exceeds max size");
|
||||
return false;
|
||||
}
|
||||
m_NextMessageOffset += 16;
|
||||
auto msg = (dataSize + 16U) <= I2NP_MAX_SHORT_MESSAGE_SIZE - 2 ? NewI2NPShortMessage () : NewI2NPMessage ();
|
||||
m_NextMessage = msg;
|
||||
memcpy (m_NextMessage->buf, buf, 16);
|
||||
m_NextMessageOffset = 16;
|
||||
m_NextMessage->offset = 2; // size field
|
||||
m_NextMessage->len = dataSize + 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// timestamp
|
||||
LogPrint ("Timestamp");
|
||||
i2p::DeleteI2NPMessage (m_NextMessage);
|
||||
m_NextMessage = nullptr;
|
||||
LogPrint (eLogDebug, "NTCP: Timestamp");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -588,20 +602,30 @@ namespace transport
|
||||
if (m_NextMessageOffset >= m_NextMessage->len + 4) // +checksum
|
||||
{
|
||||
// we have a complete I2NP message
|
||||
m_Handler.PutNextMessage (m_NextMessage);
|
||||
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))
|
||||
{
|
||||
if (!m_NextMessage->IsExpired ())
|
||||
m_Handler.PutNextMessage (m_NextMessage);
|
||||
else
|
||||
LogPrint (eLogInfo, "NTCP: message expired");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "NTCP: Incorrect adler checksum of message, dropped");
|
||||
m_NextMessage = nullptr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void NTCPSession::Send (i2p::I2NPMessage * msg)
|
||||
void NTCPSession::Send (std::shared_ptr<i2p::I2NPMessage> msg)
|
||||
{
|
||||
m_IsSending = true;
|
||||
boost::asio::async_write (m_Socket, CreateMsgBuffer (msg), boost::asio::transfer_all (),
|
||||
std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, std::vector<I2NPMessage *>{ msg }));
|
||||
std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, std::vector<std::shared_ptr<I2NPMessage> >{ msg }));
|
||||
}
|
||||
|
||||
boost::asio::const_buffers_1 NTCPSession::CreateMsgBuffer (I2NPMessage * msg)
|
||||
boost::asio::const_buffers_1 NTCPSession::CreateMsgBuffer (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
uint8_t * sendBuffer;
|
||||
int len;
|
||||
@@ -610,10 +634,7 @@ namespace transport
|
||||
{
|
||||
// regular I2NP
|
||||
if (msg->offset < 2)
|
||||
{
|
||||
LogPrint (eLogError, "Malformed I2NP message");
|
||||
i2p::DeleteI2NPMessage (msg);
|
||||
}
|
||||
LogPrint (eLogError, "NTCP: Malformed I2NP message"); // TODO:
|
||||
sendBuffer = msg->GetBuffer () - 2;
|
||||
len = msg->GetLength ();
|
||||
htobe16buf (sendBuffer, len);
|
||||
@@ -628,9 +649,12 @@ namespace transport
|
||||
}
|
||||
int rem = (len + 6) & 0x0F; // %16
|
||||
int padding = 0;
|
||||
if (rem > 0) padding = 16 - rem;
|
||||
// TODO: fill padding
|
||||
m_Adler.CalculateDigest (sendBuffer + len + 2 + padding, sendBuffer, len + 2+ padding);
|
||||
if (rem > 0) {
|
||||
padding = 16 - rem;
|
||||
// fill with random padding
|
||||
RAND_bytes(sendBuffer + len + 2, padding);
|
||||
}
|
||||
htobe32buf (sendBuffer + len + 2 + padding, adler32 (adler32 (0, Z_NULL, 0), sendBuffer, len + 2+ padding));
|
||||
|
||||
int l = len + padding + 6;
|
||||
m_Encryption.Encrypt(sendBuffer, l, sendBuffer);
|
||||
@@ -638,24 +662,23 @@ namespace transport
|
||||
}
|
||||
|
||||
|
||||
void NTCPSession::Send (const std::vector<I2NPMessage *>& msgs)
|
||||
void NTCPSession::Send (const std::vector<std::shared_ptr<I2NPMessage> >& msgs)
|
||||
{
|
||||
m_IsSending = true;
|
||||
std::vector<boost::asio::const_buffer> bufs;
|
||||
for (auto it: msgs)
|
||||
for (const auto& it: msgs)
|
||||
bufs.push_back (CreateMsgBuffer (it));
|
||||
boost::asio::async_write (m_Socket, bufs, boost::asio::transfer_all (),
|
||||
std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, msgs));
|
||||
}
|
||||
|
||||
void NTCPSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector<I2NPMessage *> msgs)
|
||||
void NTCPSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector<std::shared_ptr<I2NPMessage> > msgs)
|
||||
{
|
||||
(void) msgs;
|
||||
m_IsSending = false;
|
||||
for (auto it: msgs)
|
||||
if (it) i2p::DeleteI2NPMessage (it);
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogWarning, "Couldn't send msgs: ", ecode.message ());
|
||||
LogPrint (eLogWarning, "NTCP: Couldn't send msgs: ", ecode.message ());
|
||||
// we shouldn't call Terminate () here, because HandleReceive takes care
|
||||
// TODO: 'delete this' statement in Terminate () must be eliminated later
|
||||
// Terminate ();
|
||||
@@ -663,6 +686,7 @@ namespace transport
|
||||
else
|
||||
{
|
||||
m_NumSentBytes += bytes_transferred;
|
||||
i2p::transport::transports.UpdateSentBytes (bytes_transferred);
|
||||
if (!m_SendQueue.empty())
|
||||
{
|
||||
Send (m_SendQueue);
|
||||
@@ -679,44 +703,27 @@ namespace transport
|
||||
Send (nullptr);
|
||||
}
|
||||
|
||||
void NTCPSession::SendI2NPMessage (I2NPMessage * msg)
|
||||
{
|
||||
m_Server.GetService ().post (std::bind (&NTCPSession::PostI2NPMessage, shared_from_this (), msg));
|
||||
}
|
||||
|
||||
void NTCPSession::PostI2NPMessage (I2NPMessage * msg)
|
||||
{
|
||||
if (msg)
|
||||
{
|
||||
if (m_IsTerminated)
|
||||
{
|
||||
DeleteI2NPMessage (msg);
|
||||
return;
|
||||
}
|
||||
if (m_IsSending)
|
||||
m_SendQueue.push_back (msg);
|
||||
else
|
||||
Send (msg);
|
||||
}
|
||||
}
|
||||
|
||||
void NTCPSession::SendI2NPMessages (const std::vector<I2NPMessage *>& msgs)
|
||||
void NTCPSession::SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs)
|
||||
{
|
||||
m_Server.GetService ().post (std::bind (&NTCPSession::PostI2NPMessages, shared_from_this (), msgs));
|
||||
}
|
||||
|
||||
void NTCPSession::PostI2NPMessages (std::vector<I2NPMessage *> msgs)
|
||||
void NTCPSession::PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs)
|
||||
{
|
||||
if (m_IsTerminated)
|
||||
{
|
||||
for (auto it: msgs)
|
||||
DeleteI2NPMessage (it);
|
||||
return;
|
||||
}
|
||||
if (m_IsTerminated) return;
|
||||
if (m_IsSending)
|
||||
{
|
||||
for (auto it: msgs)
|
||||
m_SendQueue.push_back (it);
|
||||
if (m_SendQueue.size () < NTCP_MAX_OUTGOING_QUEUE_SIZE)
|
||||
{
|
||||
for (const auto& it: msgs)
|
||||
m_SendQueue.push_back (it);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP: outgoing messages queue size exceeds ", NTCP_MAX_OUTGOING_QUEUE_SIZE);
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
else
|
||||
Send (msgs);
|
||||
@@ -725,7 +732,7 @@ namespace transport
|
||||
void NTCPSession::ScheduleTermination ()
|
||||
{
|
||||
m_TerminationTimer.cancel ();
|
||||
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(NTCP_TERMINATION_TIMEOUT));
|
||||
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(GetTerminationTimeout ()));
|
||||
m_TerminationTimer.async_wait (std::bind (&NTCPSession::HandleTerminationTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
@@ -734,14 +741,14 @@ namespace transport
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint ("No activity fo ", NTCP_TERMINATION_TIMEOUT, " seconds");
|
||||
LogPrint (eLogDebug, "NTCP: No activity for ", GetTerminationTimeout (), " seconds");
|
||||
//Terminate ();
|
||||
m_Socket.close ();// invoke Terminate () from HandleReceive
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------
|
||||
NTCPServer::NTCPServer (int port):
|
||||
NTCPServer::NTCPServer ():
|
||||
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
|
||||
m_NTCPAcceptor (nullptr), m_NTCPV6Acceptor (nullptr)
|
||||
{
|
||||
@@ -759,33 +766,49 @@ namespace transport
|
||||
m_IsRunning = true;
|
||||
m_Thread = new std::thread (std::bind (&NTCPServer::Run, this));
|
||||
// create acceptors
|
||||
auto addresses = context.GetRouterInfo ().GetAddresses ();
|
||||
for (auto& address : addresses)
|
||||
auto& addresses = context.GetRouterInfo ().GetAddresses ();
|
||||
for (const auto& address: addresses)
|
||||
{
|
||||
if (address.transportStyle == i2p::data::RouterInfo::eTransportNTCP && address.host.is_v4 ())
|
||||
{
|
||||
m_NTCPAcceptor = new boost::asio::ip::tcp::acceptor (m_Service,
|
||||
boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address.port));
|
||||
|
||||
LogPrint (eLogInfo, "Start listening TCP port ", address.port);
|
||||
auto conn = std::make_shared<NTCPSession>(*this);
|
||||
m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this,
|
||||
conn, std::placeholders::_1));
|
||||
|
||||
if (context.SupportsV6 ())
|
||||
if (address->transportStyle == i2p::data::RouterInfo::eTransportNTCP)
|
||||
{
|
||||
if (address->host.is_v4())
|
||||
{
|
||||
m_NTCPV6Acceptor = new boost::asio::ip::tcp::acceptor (m_Service);
|
||||
m_NTCPV6Acceptor->open (boost::asio::ip::tcp::v6());
|
||||
m_NTCPV6Acceptor->set_option (boost::asio::ip::v6_only (true));
|
||||
m_NTCPV6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address.port));
|
||||
m_NTCPV6Acceptor->listen ();
|
||||
|
||||
LogPrint (eLogInfo, "Start listening V6 TCP port ", address.port);
|
||||
auto conn = std::make_shared<NTCPSession> (*this);
|
||||
m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6,
|
||||
this, conn, std::placeholders::_1));
|
||||
try
|
||||
{
|
||||
m_NTCPAcceptor = new boost::asio::ip::tcp::acceptor (m_Service,
|
||||
boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address->port));
|
||||
} catch ( std::exception & ex ) {
|
||||
/** fail to bind ip4 */
|
||||
LogPrint(eLogError, "NTCP: Failed to bind to ip4 port ",address->port, ex.what());
|
||||
continue;
|
||||
}
|
||||
|
||||
LogPrint (eLogInfo, "NTCP: Start listening TCP port ", address->port);
|
||||
auto conn = std::make_shared<NTCPSession>(*this);
|
||||
m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this,
|
||||
conn, std::placeholders::_1));
|
||||
}
|
||||
else if (address->host.is_v6() && context.SupportsV6 ())
|
||||
{
|
||||
m_NTCPV6Acceptor = new boost::asio::ip::tcp::acceptor (m_Service);
|
||||
try
|
||||
{
|
||||
m_NTCPV6Acceptor->open (boost::asio::ip::tcp::v6());
|
||||
m_NTCPV6Acceptor->set_option (boost::asio::ip::v6_only (true));
|
||||
|
||||
m_NTCPV6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port));
|
||||
m_NTCPV6Acceptor->listen ();
|
||||
|
||||
LogPrint (eLogInfo, "NTCP: Start listening V6 TCP port ", address->port);
|
||||
auto conn = std::make_shared<NTCPSession> (*this);
|
||||
m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6,
|
||||
this, conn, std::placeholders::_1));
|
||||
} catch ( std::exception & ex ) {
|
||||
LogPrint(eLogError, "NTCP: failed to bind to ip6 port ", address->port);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -797,9 +820,11 @@ namespace transport
|
||||
if (m_IsRunning)
|
||||
{
|
||||
m_IsRunning = false;
|
||||
delete m_NTCPAcceptor;
|
||||
if (m_NTCPAcceptor)
|
||||
delete m_NTCPAcceptor;
|
||||
m_NTCPAcceptor = nullptr;
|
||||
delete m_NTCPV6Acceptor;
|
||||
if (m_NTCPV6Acceptor)
|
||||
delete m_NTCPV6Acceptor;
|
||||
m_NTCPV6Acceptor = nullptr;
|
||||
|
||||
m_Service.stop ();
|
||||
@@ -823,32 +848,33 @@ namespace transport
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint ("NTCP server: ", ex.what ());
|
||||
LogPrint (eLogError, "NTCP: runtime exception: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NTCPServer::AddNTCPSession (std::shared_ptr<NTCPSession> session)
|
||||
bool NTCPServer::AddNTCPSession (std::shared_ptr<NTCPSession> session)
|
||||
{
|
||||
if (session)
|
||||
if (!session || !session->GetRemoteIdentity ()) return false;
|
||||
auto& ident = session->GetRemoteIdentity ()->GetIdentHash ();
|
||||
auto it = m_NTCPSessions.find (ident);
|
||||
if (it != m_NTCPSessions.end ())
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_NTCPSessionsMutex);
|
||||
m_NTCPSessions[session->GetRemoteIdentity ().GetIdentHash ()] = session;
|
||||
LogPrint (eLogWarning, "NTCP: session to ", ident.ToBase64 (), " already exists");
|
||||
return false;
|
||||
}
|
||||
m_NTCPSessions.insert (std::pair<i2p::data::IdentHash, std::shared_ptr<NTCPSession> >(ident, session));
|
||||
return true;
|
||||
}
|
||||
|
||||
void NTCPServer::RemoveNTCPSession (std::shared_ptr<NTCPSession> session)
|
||||
{
|
||||
if (session)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_NTCPSessionsMutex);
|
||||
m_NTCPSessions.erase (session->GetRemoteIdentity ().GetIdentHash ());
|
||||
}
|
||||
if (session && session->GetRemoteIdentity ())
|
||||
m_NTCPSessions.erase (session->GetRemoteIdentity ()->GetIdentHash ());
|
||||
}
|
||||
|
||||
std::shared_ptr<NTCPSession> NTCPServer::FindNTCPSession (const i2p::data::IdentHash& ident)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_NTCPSessionsMutex);
|
||||
auto it = m_NTCPSessions.find (ident);
|
||||
if (it != m_NTCPSessions.end ())
|
||||
return it->second;
|
||||
@@ -863,14 +889,14 @@ namespace transport
|
||||
auto ep = conn->GetSocket ().remote_endpoint(ec);
|
||||
if (!ec)
|
||||
{
|
||||
LogPrint (eLogInfo, "Connected from ", ep);
|
||||
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 (eLogInfo, ep.address (), " is banned for ", it->second - ts, " more seconds");
|
||||
LogPrint (eLogWarning, "NTCP: ", ep.address (), " is banned for ", it->second - ts, " more seconds");
|
||||
conn = nullptr;
|
||||
}
|
||||
else
|
||||
@@ -880,7 +906,7 @@ namespace transport
|
||||
conn->ServerLogin ();
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Connected from error ", ec.message ());
|
||||
LogPrint (eLogError, "NTCP: Connected from error ", ec.message ());
|
||||
}
|
||||
|
||||
|
||||
@@ -900,14 +926,14 @@ namespace transport
|
||||
auto ep = conn->GetSocket ().remote_endpoint(ec);
|
||||
if (!ec)
|
||||
{
|
||||
LogPrint (eLogInfo, "Connected from ", ep);
|
||||
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 (eLogInfo, ep.address (), " is banned for ", it->second - ts, " more seconds");
|
||||
LogPrint (eLogWarning, "NTCP: ", ep.address (), " is banned for ", it->second - ts, " more seconds");
|
||||
conn = nullptr;
|
||||
}
|
||||
else
|
||||
@@ -917,7 +943,7 @@ namespace transport
|
||||
conn->ServerLogin ();
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Connected from error ", ec.message ());
|
||||
LogPrint (eLogError, "NTCP: Connected from error ", ec.message ());
|
||||
}
|
||||
|
||||
if (error != boost::asio::error::operation_aborted)
|
||||
@@ -930,27 +956,27 @@ namespace transport
|
||||
|
||||
void NTCPServer::Connect (const boost::asio::ip::address& address, int port, std::shared_ptr<NTCPSession> conn)
|
||||
{
|
||||
LogPrint (eLogInfo, "Connecting to ", address ,":", port);
|
||||
m_Service.post([conn, this]()
|
||||
{
|
||||
this->AddNTCPSession (conn);
|
||||
});
|
||||
conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port),
|
||||
std::bind (&NTCPServer::HandleConnect, this, std::placeholders::_1, conn));
|
||||
LogPrint (eLogDebug, "NTCP: Connecting to ", address ,":", port);
|
||||
m_Service.post([=]()
|
||||
{
|
||||
if (this->AddNTCPSession (conn))
|
||||
conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port),
|
||||
std::bind (&NTCPServer::HandleConnect, this, std::placeholders::_1, conn));
|
||||
});
|
||||
}
|
||||
|
||||
void NTCPServer::HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "Connect error: ", ecode.message ());
|
||||
LogPrint (eLogError, "NTCP: Can't connect to ", conn->GetSocket ().remote_endpoint (), ": ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ().GetIdentHash (), true);
|
||||
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
|
||||
conn->Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogInfo, "Connected to ", conn->GetSocket ().remote_endpoint ());
|
||||
LogPrint (eLogDebug, "NTCP: Connected to ", conn->GetSocket ().remote_endpoint ());
|
||||
if (conn->GetSocket ().local_endpoint ().protocol () == boost::asio::ip::tcp::v6()) // ipv6
|
||||
context.UpdateNTCPV6Address (conn->GetSocket ().local_endpoint ().address ());
|
||||
conn->ClientLogin ();
|
||||
@@ -961,7 +987,7 @@ namespace transport
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
m_BanList[addr] = ts + NTCP_BAN_EXPIRATION_TIMEOUT;
|
||||
LogPrint (eLogInfo, addr, " has been banned for ", NTCP_BAN_EXPIRATION_TIMEOUT, " seconds");
|
||||
LogPrint (eLogWarning, "NTCP: ", addr, " has been banned for ", NTCP_BAN_EXPIRATION_TIMEOUT, " seconds");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,7 @@
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <boost/asio.hpp>
|
||||
#include <cryptopp/modes.h>
|
||||
#include <cryptopp/aes.h>
|
||||
#include <cryptopp/adler32.h>
|
||||
#include "aes.h"
|
||||
#include "Crypto.h"
|
||||
#include "Identity.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "I2NPProtocol.h"
|
||||
@@ -20,8 +17,6 @@ namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
|
||||
#pragma pack(1)
|
||||
struct NTCPPhase1
|
||||
{
|
||||
uint8_t pubKey[256];
|
||||
@@ -34,18 +29,18 @@ namespace transport
|
||||
struct
|
||||
{
|
||||
uint8_t hxy[32];
|
||||
uint32_t timestamp;
|
||||
uint8_t timestamp[4];
|
||||
uint8_t filler[12];
|
||||
} encrypted;
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
|
||||
const size_t NTCP_MAX_MESSAGE_SIZE = 16384;
|
||||
const size_t NTCP_BUFFER_SIZE = 4160; // fits 4 tunnel messages (4*1028)
|
||||
const int NTCP_TERMINATION_TIMEOUT = 120; // 2 minutes
|
||||
const size_t NTCP_DEFAULT_PHASE3_SIZE = 2/*size*/ + i2p::data::DEFAULT_IDENTITY_SIZE/*387*/ + 4/*ts*/ + 15/*padding*/ + 40/*signature*/; // 448
|
||||
const int NTCP_BAN_EXPIRATION_TIMEOUT = 70; // in second
|
||||
const int NTCP_CLOCK_SKEW = 60; // in seconds
|
||||
const int NTCP_MAX_OUTGOING_QUEUE_SIZE = 200; // how many messages we can queue up
|
||||
|
||||
class NTCPServer;
|
||||
class NTCPSession: public TransportSession, public std::enable_shared_from_this<NTCPSession>
|
||||
@@ -62,16 +57,11 @@ namespace transport
|
||||
|
||||
void ClientLogin ();
|
||||
void ServerLogin ();
|
||||
void SendI2NPMessage (I2NPMessage * msg);
|
||||
void SendI2NPMessages (const std::vector<I2NPMessage *>& msgs);
|
||||
|
||||
size_t GetNumSentBytes () const { return m_NumSentBytes; };
|
||||
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
|
||||
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
|
||||
|
||||
private:
|
||||
|
||||
void PostI2NPMessage (I2NPMessage * msg);
|
||||
void PostI2NPMessages (std::vector<I2NPMessage *> msgs);
|
||||
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
|
||||
void Connected ();
|
||||
void SendTimeSyncMessage ();
|
||||
void SetIsEstablished (bool isEstablished) { m_IsEstablished = isEstablished; }
|
||||
@@ -100,10 +90,10 @@ namespace transport
|
||||
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
bool DecryptNextBlock (const uint8_t * encrypted);
|
||||
|
||||
void Send (i2p::I2NPMessage * msg);
|
||||
boost::asio::const_buffers_1 CreateMsgBuffer (I2NPMessage * msg);
|
||||
void Send (const std::vector<I2NPMessage *>& msgs);
|
||||
void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector<I2NPMessage *> msgs);
|
||||
void Send (std::shared_ptr<i2p::I2NPMessage> msg);
|
||||
boost::asio::const_buffers_1 CreateMsgBuffer (std::shared_ptr<I2NPMessage> msg);
|
||||
void Send (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
|
||||
void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector<std::shared_ptr<I2NPMessage> > msgs);
|
||||
|
||||
|
||||
// timer
|
||||
@@ -119,7 +109,6 @@ namespace transport
|
||||
|
||||
i2p::crypto::CBCDecryption m_Decryption;
|
||||
i2p::crypto::CBCEncryption m_Encryption;
|
||||
CryptoPP::Adler32 m_Adler;
|
||||
|
||||
struct Establisher
|
||||
{
|
||||
@@ -131,14 +120,13 @@ namespace transport
|
||||
i2p::crypto::AESAlignedBuffer<16> m_TimeSyncBuffer;
|
||||
int m_ReceiveBufferOffset;
|
||||
|
||||
i2p::I2NPMessage * m_NextMessage;
|
||||
std::shared_ptr<I2NPMessage> m_NextMessage;
|
||||
size_t m_NextMessageOffset;
|
||||
i2p::I2NPMessagesHandler m_Handler;
|
||||
|
||||
bool m_IsSending;
|
||||
std::vector<I2NPMessage *> m_SendQueue;
|
||||
std::vector<std::shared_ptr<I2NPMessage> > m_SendQueue;
|
||||
|
||||
size_t m_NumSentBytes, m_NumReceivedBytes;
|
||||
boost::asio::ip::address m_ConnectedFrom; // for ban
|
||||
};
|
||||
|
||||
@@ -147,17 +135,20 @@ namespace transport
|
||||
{
|
||||
public:
|
||||
|
||||
NTCPServer (int port);
|
||||
NTCPServer ();
|
||||
~NTCPServer ();
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
void AddNTCPSession (std::shared_ptr<NTCPSession> session);
|
||||
bool AddNTCPSession (std::shared_ptr<NTCPSession> session);
|
||||
void RemoveNTCPSession (std::shared_ptr<NTCPSession> session);
|
||||
std::shared_ptr<NTCPSession> FindNTCPSession (const i2p::data::IdentHash& ident);
|
||||
void Connect (const boost::asio::ip::address& address, int port, std::shared_ptr<NTCPSession> conn);
|
||||
|
||||
|
||||
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);
|
||||
|
||||
@@ -176,8 +167,7 @@ namespace transport
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_service::work m_Work;
|
||||
boost::asio::ip::tcp::acceptor * m_NTCPAcceptor, * m_NTCPV6Acceptor;
|
||||
std::mutex m_NTCPSessionsMutex;
|
||||
std::map<i2p::data::IdentHash, std::shared_ptr<NTCPSession> > m_NTCPSessions;
|
||||
std::map<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:
|
||||
|
||||
106
NetDb.h
106
NetDb.h
@@ -8,7 +8,10 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include "Base.h"
|
||||
#include "Gzip.h"
|
||||
#include "FS.h"
|
||||
#include "Queue.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "RouterInfo.h"
|
||||
@@ -16,44 +19,22 @@
|
||||
#include "Tunnel.h"
|
||||
#include "TunnelPool.h"
|
||||
#include "Reseed.h"
|
||||
#include "NetDbRequests.h"
|
||||
#include "Family.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
class RequestedDestination
|
||||
{
|
||||
public:
|
||||
const int NETDB_MIN_ROUTERS = 90;
|
||||
const int NETDB_FLOODFILL_EXPIRATION_TIMEOUT = 60*60; // 1 hour, in seconds
|
||||
const int NETDB_INTRODUCEE_EXPIRATION_TIMEOUT = 65*60;
|
||||
const int NETDB_MIN_EXPIRATION_TIMEOUT = 90*60; // 1.5 hours
|
||||
const int NETDB_MAX_EXPIRATION_TIMEOUT = 27*60*60; // 27 hours
|
||||
const int NETDB_PUBLISH_INTERVAL = 60*40;
|
||||
|
||||
typedef std::function<void (std::shared_ptr<RouterInfo>)> RequestComplete;
|
||||
|
||||
RequestedDestination (const IdentHash& destination, bool isExploratory = false):
|
||||
m_Destination (destination), m_IsExploratory (isExploratory), m_CreationTime (0) {};
|
||||
~RequestedDestination () { if (m_RequestComplete) m_RequestComplete (nullptr); };
|
||||
|
||||
const IdentHash& GetDestination () const { return m_Destination; };
|
||||
int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); };
|
||||
const std::set<IdentHash>& GetExcludedPeers () { return m_ExcludedPeers; };
|
||||
void ClearExcludedPeers ();
|
||||
bool IsExploratory () const { return m_IsExploratory; };
|
||||
bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); };
|
||||
uint64_t GetCreationTime () const { return m_CreationTime; };
|
||||
I2NPMessage * CreateRequestMessage (std::shared_ptr<const RouterInfo>, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel);
|
||||
I2NPMessage * CreateRequestMessage (const IdentHash& floodfill);
|
||||
|
||||
void SetRequestComplete (const RequestComplete& requestComplete) { m_RequestComplete = requestComplete; };
|
||||
bool IsRequestComplete () const { return m_RequestComplete != nullptr; };
|
||||
void Success (std::shared_ptr<RouterInfo> r);
|
||||
void Fail ();
|
||||
|
||||
private:
|
||||
|
||||
IdentHash m_Destination;
|
||||
bool m_IsExploratory;
|
||||
std::set<IdentHash> m_ExcludedPeers;
|
||||
uint64_t m_CreationTime;
|
||||
RequestComplete m_RequestComplete;
|
||||
};
|
||||
/** function for visiting a leaseset stored in a floodfill */
|
||||
typedef std::function<void(const IdentHash, std::shared_ptr<LeaseSet>)> LeaseSetVisitor;
|
||||
|
||||
class NetDb
|
||||
{
|
||||
@@ -65,65 +46,88 @@ namespace data
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
void AddRouterInfo (const uint8_t * buf, int len);
|
||||
void AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len);
|
||||
void AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
||||
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);
|
||||
std::shared_ptr<RouterInfo> FindRouter (const IdentHash& ident) const;
|
||||
std::shared_ptr<LeaseSet> FindLeaseSet (const IdentHash& destination) const;
|
||||
std::shared_ptr<RouterProfile> FindRouterProfile (const IdentHash& ident) const;
|
||||
|
||||
void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr);
|
||||
|
||||
void HandleDatabaseStoreMsg (I2NPMessage * msg);
|
||||
void HandleDatabaseSearchReplyMsg (I2NPMessage * msg);
|
||||
void HandleDatabaseLookupMsg (I2NPMessage * msg);
|
||||
void HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> msg);
|
||||
void HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg);
|
||||
void HandleDatabaseLookupMsg (std::shared_ptr<const I2NPMessage> msg);
|
||||
|
||||
std::shared_ptr<const RouterInfo> GetRandomRouter () const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
|
||||
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
|
||||
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomPeerTestRouter () const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomIntroducer () const;
|
||||
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
|
||||
std::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num,
|
||||
std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
|
||||
std::shared_ptr<const RouterInfo> GetClosestNonFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomRouterInFamily(const std::string & fam) const;
|
||||
void SetUnreachable (const IdentHash& ident, bool unreachable);
|
||||
|
||||
void PostI2NPMsg (I2NPMessage * msg);
|
||||
void PostI2NPMsg (std::shared_ptr<const I2NPMessage> msg);
|
||||
|
||||
/** set hidden mode, aka don't publish our RI to netdb and don't explore */
|
||||
void SetHidden(bool hide);
|
||||
|
||||
void Reseed ();
|
||||
Families& GetFamilies () { return m_Families; };
|
||||
|
||||
// for web interface
|
||||
int GetNumRouters () const { return m_RouterInfos.size (); };
|
||||
int GetNumFloodfills () const { return m_Floodfills.size (); };
|
||||
int GetNumLeaseSets () const { return m_LeaseSets.size (); };
|
||||
|
||||
|
||||
/** visit all lease sets we currently store */
|
||||
void VisitLeaseSets(LeaseSetVisitor v);
|
||||
|
||||
private:
|
||||
|
||||
bool CreateNetDb(boost::filesystem::path directory);
|
||||
void Load (const char * directory);
|
||||
void SaveUpdated (const char * directory);
|
||||
void Load ();
|
||||
bool LoadRouterInfo (const std::string & path);
|
||||
void SaveUpdated ();
|
||||
void Run (); // exploratory thread
|
||||
void Explore (int numDestinations);
|
||||
void Publish ();
|
||||
void ManageLeaseSets ();
|
||||
void ManageRequests ();
|
||||
void ManageLookupResponses ();
|
||||
|
||||
template<typename Filter>
|
||||
std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const;
|
||||
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;
|
||||
std::map<IdentHash, std::shared_ptr<RouterInfo> > m_RouterInfos;
|
||||
mutable std::mutex m_FloodfillsMutex;
|
||||
std::list<std::shared_ptr<RouterInfo> > m_Floodfills;
|
||||
std::mutex m_RequestedDestinationsMutex;
|
||||
std::map<IdentHash, std::unique_ptr<RequestedDestination> > m_RequestedDestinations;
|
||||
|
||||
bool m_IsRunning;
|
||||
uint64_t m_LastLoad;
|
||||
std::thread * m_Thread;
|
||||
i2p::util::Queue<I2NPMessage> m_Queue; // of I2NPDatabaseStoreMsg
|
||||
i2p::util::Queue<std::shared_ptr<const I2NPMessage> > m_Queue; // of I2NPDatabaseStoreMsg
|
||||
|
||||
GzipInflator m_Inflator;
|
||||
Reseeder * m_Reseeder;
|
||||
Families m_Families;
|
||||
i2p::fs::HashedStorage m_Storage;
|
||||
|
||||
static const char m_NetDbPath[];
|
||||
friend class NetDbRequests;
|
||||
NetDbRequests m_Requests;
|
||||
|
||||
std::map<IdentHash, std::pair<std::vector<IdentHash>, uint64_t> > m_LookupResponses; // ident->(closest FFs, timestamp)
|
||||
|
||||
/** true if in hidden mode */
|
||||
bool m_HiddenMode;
|
||||
};
|
||||
|
||||
extern NetDb netdb;
|
||||
|
||||
149
NetDbRequests.cpp
Normal file
149
NetDbRequests.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
#include "Log.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Transports.h"
|
||||
#include "NetDb.h"
|
||||
#include "NetDbRequests.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (std::shared_ptr<const RouterInfo> router,
|
||||
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel)
|
||||
{
|
||||
auto msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination,
|
||||
replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory,
|
||||
&m_ExcludedPeers);
|
||||
m_ExcludedPeers.insert (router->GetIdentHash ());
|
||||
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
return msg;
|
||||
}
|
||||
|
||||
std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (const IdentHash& floodfill)
|
||||
{
|
||||
auto msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination,
|
||||
i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers);
|
||||
m_ExcludedPeers.insert (floodfill);
|
||||
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
return msg;
|
||||
}
|
||||
|
||||
void RequestedDestination::ClearExcludedPeers ()
|
||||
{
|
||||
m_ExcludedPeers.clear ();
|
||||
}
|
||||
|
||||
void RequestedDestination::Success (std::shared_ptr<RouterInfo> r)
|
||||
{
|
||||
if (m_RequestComplete)
|
||||
{
|
||||
m_RequestComplete (r);
|
||||
m_RequestComplete = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void RequestedDestination::Fail ()
|
||||
{
|
||||
if (m_RequestComplete)
|
||||
{
|
||||
m_RequestComplete (nullptr);
|
||||
m_RequestComplete = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void NetDbRequests::Start ()
|
||||
{
|
||||
}
|
||||
|
||||
void NetDbRequests::Stop ()
|
||||
{
|
||||
m_RequestedDestinations.clear ();
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<RequestedDestination> NetDbRequests::CreateRequest (const IdentHash& destination, bool isExploratory, RequestedDestination::RequestComplete requestComplete)
|
||||
{
|
||||
// request RouterInfo directly
|
||||
auto dest = std::make_shared<RequestedDestination> (destination, isExploratory);
|
||||
dest->SetRequestComplete (requestComplete);
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
|
||||
if (!m_RequestedDestinations.insert (std::make_pair (destination,
|
||||
std::shared_ptr<RequestedDestination> (dest))).second) // not inserted
|
||||
return nullptr;
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
void NetDbRequests::RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> r)
|
||||
{
|
||||
auto it = m_RequestedDestinations.find (ident);
|
||||
if (it != m_RequestedDestinations.end ())
|
||||
{
|
||||
if (r)
|
||||
it->second->Success (r);
|
||||
else
|
||||
it->second->Fail ();
|
||||
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
|
||||
m_RequestedDestinations.erase (it);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<RequestedDestination> NetDbRequests::FindRequest (const IdentHash& ident) const
|
||||
{
|
||||
auto it = m_RequestedDestinations.find (ident);
|
||||
if (it != m_RequestedDestinations.end ())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void NetDbRequests::ManageRequests ()
|
||||
{
|
||||
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
|
||||
for (auto it = m_RequestedDestinations.begin (); it != m_RequestedDestinations.end ();)
|
||||
{
|
||||
auto& dest = it->second;
|
||||
bool done = false;
|
||||
if (ts < dest->GetCreationTime () + 60) // request is worthless after 1 minute
|
||||
{
|
||||
if (ts > dest->GetCreationTime () + 5) // no response for 5 seconds
|
||||
{
|
||||
auto count = dest->GetExcludedPeers ().size ();
|
||||
if (!dest->IsExploratory () && count < 7)
|
||||
{
|
||||
auto pool = i2p::tunnel::tunnels.GetExploratoryPool ();
|
||||
auto outbound = pool->GetNextOutboundTunnel ();
|
||||
auto inbound = pool->GetNextInboundTunnel ();
|
||||
auto nextFloodfill = netdb.GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ());
|
||||
if (nextFloodfill && outbound && inbound)
|
||||
outbound->SendTunnelDataMsg (nextFloodfill->GetIdentHash (), 0,
|
||||
dest->CreateRequestMessage (nextFloodfill, inbound));
|
||||
else
|
||||
{
|
||||
done = true;
|
||||
if (!inbound) LogPrint (eLogWarning, "NetDbReq: No inbound tunnels");
|
||||
if (!outbound) LogPrint (eLogWarning, "NetDbReq: No outbound tunnels");
|
||||
if (!nextFloodfill) LogPrint (eLogWarning, "NetDbReq: No more floodfills");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!dest->IsExploratory ())
|
||||
LogPrint (eLogWarning, "NetDbReq: ", dest->GetDestination ().ToBase64 (), " not found after 7 attempts");
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else // delete obsolete request
|
||||
done = true;
|
||||
|
||||
if (done)
|
||||
it = m_RequestedDestinations.erase (it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
69
NetDbRequests.h
Normal file
69
NetDbRequests.h
Normal file
@@ -0,0 +1,69 @@
|
||||
#ifndef NETDB_REQUESTS_H__
|
||||
#define NETDB_REQUESTS_H__
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include "Identity.h"
|
||||
#include "RouterInfo.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
class RequestedDestination
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::function<void (std::shared_ptr<RouterInfo>)> RequestComplete;
|
||||
|
||||
RequestedDestination (const IdentHash& destination, bool isExploratory = false):
|
||||
m_Destination (destination), m_IsExploratory (isExploratory), m_CreationTime (0) {};
|
||||
~RequestedDestination () { if (m_RequestComplete) m_RequestComplete (nullptr); };
|
||||
|
||||
const IdentHash& GetDestination () const { return m_Destination; };
|
||||
int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); };
|
||||
const std::set<IdentHash>& GetExcludedPeers () { return m_ExcludedPeers; };
|
||||
void ClearExcludedPeers ();
|
||||
bool IsExploratory () const { return m_IsExploratory; };
|
||||
bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); };
|
||||
uint64_t GetCreationTime () const { return m_CreationTime; };
|
||||
std::shared_ptr<I2NPMessage> CreateRequestMessage (std::shared_ptr<const RouterInfo>, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel);
|
||||
std::shared_ptr<I2NPMessage> CreateRequestMessage (const IdentHash& floodfill);
|
||||
|
||||
void SetRequestComplete (const RequestComplete& requestComplete) { m_RequestComplete = requestComplete; };
|
||||
bool IsRequestComplete () const { return m_RequestComplete != nullptr; };
|
||||
void Success (std::shared_ptr<RouterInfo> r);
|
||||
void Fail ();
|
||||
|
||||
private:
|
||||
|
||||
IdentHash m_Destination;
|
||||
bool m_IsExploratory;
|
||||
std::set<IdentHash> m_ExcludedPeers;
|
||||
uint64_t m_CreationTime;
|
||||
RequestComplete m_RequestComplete;
|
||||
};
|
||||
|
||||
class NetDbRequests
|
||||
{
|
||||
public:
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
std::shared_ptr<RequestedDestination> CreateRequest (const IdentHash& destination, bool isExploratory, RequestedDestination::RequestComplete requestComplete = nullptr);
|
||||
void RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> r);
|
||||
std::shared_ptr<RequestedDestination> FindRequest (const IdentHash& ident) const;
|
||||
void ManageRequests ();
|
||||
|
||||
private:
|
||||
|
||||
std::mutex m_RequestedDestinationsMutex;
|
||||
std::map<IdentHash, std::shared_ptr<RequestedDestination> > m_RequestedDestinations;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
182
Profiling.cpp
Normal file
182
Profiling.cpp
Normal file
@@ -0,0 +1,182 @@
|
||||
#include <sys/stat.h>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/ini_parser.hpp>
|
||||
#include "Base.h"
|
||||
#include "FS.h"
|
||||
#include "Log.h"
|
||||
#include "Profiling.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
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()),
|
||||
m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), m_NumTunnelsNonReplied (0),
|
||||
m_NumTimesTaken (0), m_NumTimesRejected (0)
|
||||
{
|
||||
}
|
||||
|
||||
boost::posix_time::ptime RouterProfile::GetTime () const
|
||||
{
|
||||
return boost::posix_time::second_clock::local_time();
|
||||
}
|
||||
|
||||
void RouterProfile::UpdateTime ()
|
||||
{
|
||||
m_LastUpdateTime = GetTime ();
|
||||
}
|
||||
|
||||
void RouterProfile::Save ()
|
||||
{
|
||||
// fill sections
|
||||
boost::property_tree::ptree participation;
|
||||
participation.put (PEER_PROFILE_PARTICIPATION_AGREED, m_NumTunnelsAgreed);
|
||||
participation.put (PEER_PROFILE_PARTICIPATION_DECLINED, m_NumTunnelsDeclined);
|
||||
participation.put (PEER_PROFILE_PARTICIPATION_NON_REPLIED, m_NumTunnelsNonReplied);
|
||||
boost::property_tree::ptree usage;
|
||||
usage.put (PEER_PROFILE_USAGE_TAKEN, m_NumTimesTaken);
|
||||
usage.put (PEER_PROFILE_USAGE_REJECTED, m_NumTimesRejected);
|
||||
// fill property tree
|
||||
boost::property_tree::ptree pt;
|
||||
pt.put (PEER_PROFILE_LAST_UPDATE_TIME, boost::posix_time::to_simple_string (m_LastUpdateTime));
|
||||
pt.put_child (PEER_PROFILE_SECTION_PARTICIPATION, participation);
|
||||
pt.put_child (PEER_PROFILE_SECTION_USAGE, usage);
|
||||
|
||||
// save to file
|
||||
std::string ident = m_IdentHash.ToBase64 ();
|
||||
std::string path = m_ProfilesStorage.Path(ident);
|
||||
|
||||
try {
|
||||
boost::property_tree::write_ini (path, pt);
|
||||
} catch (std::exception& ex) {
|
||||
/* boost exception verbose enough */
|
||||
LogPrint (eLogError, "Profiling: ", ex.what ());
|
||||
}
|
||||
}
|
||||
|
||||
void RouterProfile::Load ()
|
||||
{
|
||||
std::string ident = m_IdentHash.ToBase64 ();
|
||||
std::string path = m_ProfilesStorage.Path(ident);
|
||||
boost::property_tree::ptree pt;
|
||||
|
||||
if (!i2p::fs::Exists(path)) {
|
||||
LogPrint(eLogWarning, "Profiling: no profile yet for ", ident);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
boost::property_tree::read_ini (path, pt);
|
||||
} catch (std::exception& ex) {
|
||||
/* boost exception verbose enough */
|
||||
LogPrint (eLogError, "Profiling: ", ex.what ());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
auto t = pt.get (PEER_PROFILE_LAST_UPDATE_TIME, "");
|
||||
if (t.length () > 0)
|
||||
m_LastUpdateTime = boost::posix_time::time_from_string (t);
|
||||
if ((GetTime () - m_LastUpdateTime).hours () < PEER_PROFILE_EXPIRATION_TIMEOUT) {
|
||||
try {
|
||||
// read participations
|
||||
auto participations = pt.get_child (PEER_PROFILE_SECTION_PARTICIPATION);
|
||||
m_NumTunnelsAgreed = participations.get (PEER_PROFILE_PARTICIPATION_AGREED, 0);
|
||||
m_NumTunnelsDeclined = participations.get (PEER_PROFILE_PARTICIPATION_DECLINED, 0);
|
||||
m_NumTunnelsNonReplied = participations.get (PEER_PROFILE_PARTICIPATION_NON_REPLIED, 0);
|
||||
} catch (boost::property_tree::ptree_bad_path& ex) {
|
||||
LogPrint (eLogWarning, "Profiling: Missing section ", PEER_PROFILE_SECTION_PARTICIPATION, " in profile for ", ident);
|
||||
}
|
||||
try {
|
||||
// read usage
|
||||
auto usage = pt.get_child (PEER_PROFILE_SECTION_USAGE);
|
||||
m_NumTimesTaken = usage.get (PEER_PROFILE_USAGE_TAKEN, 0);
|
||||
m_NumTimesRejected = usage.get (PEER_PROFILE_USAGE_REJECTED, 0);
|
||||
} catch (boost::property_tree::ptree_bad_path& ex) {
|
||||
LogPrint (eLogWarning, "Missing section ", PEER_PROFILE_SECTION_USAGE, " in profile for ", ident);
|
||||
}
|
||||
} else {
|
||||
*this = RouterProfile (m_IdentHash);
|
||||
}
|
||||
} catch (std::exception& ex) {
|
||||
LogPrint (eLogError, "Profiling: Can't read profile ", ident, " :", ex.what ());
|
||||
}
|
||||
}
|
||||
|
||||
void RouterProfile::TunnelBuildResponse (uint8_t ret)
|
||||
{
|
||||
UpdateTime ();
|
||||
if (ret > 0)
|
||||
m_NumTunnelsDeclined++;
|
||||
else
|
||||
m_NumTunnelsAgreed++;
|
||||
}
|
||||
|
||||
void RouterProfile::TunnelNonReplied ()
|
||||
{
|
||||
m_NumTunnelsNonReplied++;
|
||||
UpdateTime ();
|
||||
}
|
||||
|
||||
bool RouterProfile::IsLowPartcipationRate () const
|
||||
{
|
||||
return 4*m_NumTunnelsAgreed < m_NumTunnelsDeclined; // < 20% rate
|
||||
}
|
||||
|
||||
bool RouterProfile::IsLowReplyRate () const
|
||||
{
|
||||
auto total = m_NumTunnelsAgreed + m_NumTunnelsDeclined;
|
||||
return m_NumTunnelsNonReplied > 10*(total + 1);
|
||||
}
|
||||
|
||||
bool RouterProfile::IsBad ()
|
||||
{
|
||||
auto isBad = IsAlwaysDeclining () || IsLowPartcipationRate () /*|| IsLowReplyRate ()*/;
|
||||
if (isBad && m_NumTimesRejected > 10*(m_NumTimesTaken + 1))
|
||||
{
|
||||
// reset profile
|
||||
m_NumTunnelsAgreed = 0;
|
||||
m_NumTunnelsDeclined = 0;
|
||||
m_NumTunnelsNonReplied = 0;
|
||||
isBad = false;
|
||||
}
|
||||
if (isBad) m_NumTimesRejected++; else m_NumTimesTaken++;
|
||||
return isBad;
|
||||
}
|
||||
|
||||
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash)
|
||||
{
|
||||
auto profile = std::make_shared<RouterProfile> (identHash);
|
||||
profile->Load (); // if possible
|
||||
return profile;
|
||||
}
|
||||
|
||||
void InitProfilesStorage ()
|
||||
{
|
||||
m_ProfilesStorage.SetPlace(i2p::fs::GetDataDir());
|
||||
m_ProfilesStorage.Init(i2p::data::GetBase64SubstitutionTable(), 64);
|
||||
}
|
||||
|
||||
void DeleteObsoleteProfiles ()
|
||||
{
|
||||
struct stat st;
|
||||
std::time_t now = std::time(nullptr);
|
||||
|
||||
std::vector<std::string> files;
|
||||
m_ProfilesStorage.Traverse(files);
|
||||
for (const auto& path: files) {
|
||||
if (stat(path.c_str(), &st) != 0) {
|
||||
LogPrint(eLogWarning, "Profiling: Can't stat(): ", path);
|
||||
continue;
|
||||
}
|
||||
if (((now - st.st_mtime) / 3600) >= PEER_PROFILE_EXPIRATION_TIMEOUT) {
|
||||
LogPrint(eLogDebug, "Profiling: removing expired peer profile: ", path);
|
||||
i2p::fs::Remove(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
68
Profiling.h
Normal file
68
Profiling.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#ifndef PROFILING_H__
|
||||
#define PROFILING_H__
|
||||
|
||||
#include <memory>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include "Identity.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
// sections
|
||||
const char PEER_PROFILE_SECTION_PARTICIPATION[] = "participation";
|
||||
const char PEER_PROFILE_SECTION_USAGE[] = "usage";
|
||||
// params
|
||||
const char PEER_PROFILE_LAST_UPDATE_TIME[] = "lastupdatetime";
|
||||
const char PEER_PROFILE_PARTICIPATION_AGREED[] = "agreed";
|
||||
const char PEER_PROFILE_PARTICIPATION_DECLINED[] = "declined";
|
||||
const char PEER_PROFILE_PARTICIPATION_NON_REPLIED[] = "nonreplied";
|
||||
const char PEER_PROFILE_USAGE_TAKEN[] = "taken";
|
||||
const char PEER_PROFILE_USAGE_REJECTED[] = "rejected";
|
||||
|
||||
const int PEER_PROFILE_EXPIRATION_TIMEOUT = 72; // in hours (3 days)
|
||||
|
||||
class RouterProfile
|
||||
{
|
||||
public:
|
||||
|
||||
RouterProfile (const IdentHash& identHash);
|
||||
RouterProfile& operator= (const RouterProfile& ) = default;
|
||||
|
||||
void Save ();
|
||||
void Load ();
|
||||
|
||||
bool IsBad ();
|
||||
|
||||
void TunnelBuildResponse (uint8_t ret);
|
||||
void TunnelNonReplied ();
|
||||
|
||||
private:
|
||||
|
||||
boost::posix_time::ptime GetTime () const;
|
||||
void UpdateTime ();
|
||||
|
||||
bool IsAlwaysDeclining () const { return !m_NumTunnelsAgreed && m_NumTunnelsDeclined >= 5; };
|
||||
bool IsLowPartcipationRate () const;
|
||||
bool IsLowReplyRate () const;
|
||||
|
||||
private:
|
||||
|
||||
IdentHash m_IdentHash;
|
||||
boost::posix_time::ptime m_LastUpdateTime;
|
||||
// participation
|
||||
uint32_t m_NumTunnelsAgreed;
|
||||
uint32_t m_NumTunnelsDeclined;
|
||||
uint32_t m_NumTunnelsNonReplied;
|
||||
// usage
|
||||
uint32_t m_NumTimesTaken;
|
||||
uint32_t m_NumTimesRejected;
|
||||
};
|
||||
|
||||
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash);
|
||||
void InitProfilesStorage ();
|
||||
void DeleteObsoleteProfiles ();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
73
Queue.h
73
Queue.h
@@ -7,6 +7,7 @@
|
||||
#include <thread>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -17,28 +18,28 @@ namespace util
|
||||
{
|
||||
public:
|
||||
|
||||
void Put (Element * e)
|
||||
void Put (Element e)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_QueueMutex);
|
||||
m_Queue.push (e);
|
||||
m_Queue.push (std::move(e));
|
||||
m_NonEmpty.notify_one ();
|
||||
}
|
||||
|
||||
void Put (const std::vector<Element *>& vec)
|
||||
void Put (const std::vector<Element>& vec)
|
||||
{
|
||||
if (!vec.empty ())
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_QueueMutex);
|
||||
for (auto it: vec)
|
||||
for (const auto& it: vec)
|
||||
m_Queue.push (it);
|
||||
m_NonEmpty.notify_one ();
|
||||
}
|
||||
}
|
||||
|
||||
Element * GetNext ()
|
||||
Element GetNext ()
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_QueueMutex);
|
||||
Element * el = GetNonThreadSafe ();
|
||||
auto el = GetNonThreadSafe ();
|
||||
if (!el)
|
||||
{
|
||||
m_NonEmpty.wait (l);
|
||||
@@ -47,10 +48,10 @@ namespace util
|
||||
return el;
|
||||
}
|
||||
|
||||
Element * GetNextWithTimeout (int usec)
|
||||
Element GetNextWithTimeout (int usec)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_QueueMutex);
|
||||
Element * el = GetNonThreadSafe ();
|
||||
auto el = GetNonThreadSafe ();
|
||||
if (!el)
|
||||
{
|
||||
m_NonEmpty.wait_for (l, std::chrono::milliseconds (usec));
|
||||
@@ -85,13 +86,13 @@ namespace util
|
||||
|
||||
void WakeUp () { m_NonEmpty.notify_all (); };
|
||||
|
||||
Element * Get ()
|
||||
Element Get ()
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_QueueMutex);
|
||||
return GetNonThreadSafe ();
|
||||
}
|
||||
|
||||
Element * Peek ()
|
||||
Element Peek ()
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_QueueMutex);
|
||||
return GetNonThreadSafe (true);
|
||||
@@ -99,11 +100,11 @@ namespace util
|
||||
|
||||
private:
|
||||
|
||||
Element * GetNonThreadSafe (bool peek = false)
|
||||
Element GetNonThreadSafe (bool peek = false)
|
||||
{
|
||||
if (!m_Queue.empty ())
|
||||
{
|
||||
Element * el = m_Queue.front ();
|
||||
auto el = m_Queue.front ();
|
||||
if (!peek)
|
||||
m_Queue.pop ();
|
||||
return el;
|
||||
@@ -113,56 +114,10 @@ namespace util
|
||||
|
||||
private:
|
||||
|
||||
std::queue<Element *> m_Queue;
|
||||
std::queue<Element> m_Queue;
|
||||
std::mutex m_QueueMutex;
|
||||
std::condition_variable m_NonEmpty;
|
||||
};
|
||||
|
||||
template<class Msg>
|
||||
class MsgQueue: public Queue<Msg>
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::function<void()> OnEmpty;
|
||||
|
||||
MsgQueue (): m_IsRunning (true), m_Thread (std::bind (&MsgQueue<Msg>::Run, this)) {};
|
||||
~MsgQueue () { Stop (); };
|
||||
void Stop()
|
||||
{
|
||||
if (m_IsRunning)
|
||||
{
|
||||
m_IsRunning = false;
|
||||
Queue<Msg>::WakeUp ();
|
||||
m_Thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void SetOnEmpty (OnEmpty const & e) { m_OnEmpty = e; };
|
||||
|
||||
private:
|
||||
|
||||
void Run ()
|
||||
{
|
||||
while (m_IsRunning)
|
||||
{
|
||||
while (Msg * msg = Queue<Msg>::Get ())
|
||||
{
|
||||
msg->Process ();
|
||||
delete msg;
|
||||
}
|
||||
if (m_OnEmpty != nullptr)
|
||||
m_OnEmpty ();
|
||||
if (m_IsRunning)
|
||||
Queue<Msg>::Wait ();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
volatile bool m_IsRunning;
|
||||
OnEmpty m_OnEmpty;
|
||||
std::thread m_Thread;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
139
README.md
139
README.md
@@ -1,102 +1,55 @@
|
||||
i2pd
|
||||
====
|
||||
|
||||
I2P router written in C++
|
||||
i2pd is a full-featured C++ implementation of
|
||||
[I2P](https://geti2p.net/en/about/intro) client.
|
||||
|
||||
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.
|
||||
|
||||
We are building network which helps people 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)
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
The easiest way to install i2pd is by using
|
||||
[precompiled binaries](https://github.com/PurpleI2P/i2pd/releases/latest).
|
||||
See [documentation](https://i2pd.readthedocs.io/en/latest/) for how to build
|
||||
i2pd from source on your OS.
|
||||
|
||||
**Supported systems:**
|
||||
|
||||
* Linux x86/x64 - [](https://travis-ci.org/PurpleI2P/i2pd)
|
||||
* Windows - [](https://ci.appveyor.com/project/PurpleI2P/i2pd)
|
||||
* Mac OS X
|
||||
* FreeBSD
|
||||
* Android
|
||||
|
||||
Using i2pd
|
||||
----------
|
||||
|
||||
See [documentation](https://i2pd.readthedocs.io/en/latest/) and
|
||||
[example config file](https://github.com/PurpleI2P/i2pd/blob/openssl/docs/i2pd.conf).
|
||||
|
||||
Donations
|
||||
---------
|
||||
|
||||
BTC: 1K7Ds6KUeR8ya287UC4rYTjvC96vXyZbDY
|
||||
DASH: Xw8YUrQpYzP9tZBmbjqxS3M97Q7v3vJKUF
|
||||
LTC: LKQirrYrDeTuAPnpYq5y7LVKtywfkkHi59
|
||||
ANC: AQJYweYYUqM1nVfLqfoSMpUMfzxvS4Xd7z
|
||||
DOGE: DNXLQKziRPAsD9H3DFNjk4fLQrdaSX893Y
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
This project is licensed under the BSD 3-clause license, which can be found in the file
|
||||
LICENSE in the root of the project source code.
|
||||
|
||||
Requirements for Linux/FreeBSD/OSX
|
||||
----------------------------------
|
||||
|
||||
GCC 4.6 or newer, Boost 1.46 or newer, crypto++. Clang can be used instead of
|
||||
GCC.
|
||||
|
||||
Requirements for Windows
|
||||
------------------------
|
||||
|
||||
VS2013 (known to work with 12.0.21005.1 or newer), Boost 1.46 or newer,
|
||||
crypto++ 5.62. See Win32/README-Build.txt for instructions on how to build i2pd
|
||||
and its dependencies.
|
||||
|
||||
Downloads
|
||||
------------
|
||||
|
||||
Official binary releases could be found at:
|
||||
http://download.i2p.io/purplei2p/i2pd/releases/
|
||||
|
||||
|
||||
Build Statuses
|
||||
---------------
|
||||
|
||||
- Linux x64 - [](https://jenkins.nordcloud.no/job/i2pd-linux/)
|
||||
- Linux ARM - To be added
|
||||
- Mac OS X - Got it working, but not well tested. (Only works with clang, not GCC.)
|
||||
- Microsoft VC13 - To be added
|
||||
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
||||
First, build it.
|
||||
|
||||
On Ubuntu/Debian based
|
||||
* sudo apt-get install libboost-dev libboost-filesystem-dev libboost-program-options-dev libboost-regex-dev libcrypto++-dev libboost-date-time-dev
|
||||
* $ cd i2pd
|
||||
* $ make
|
||||
|
||||
Next, find out your public ip. (find it for example at http://www.whatismyip.com/)
|
||||
|
||||
Then, run it with:
|
||||
|
||||
$ ./i2p --host=YOUR_PUBLIC_IP
|
||||
|
||||
The client should now reseed by itself.
|
||||
|
||||
To visit an I2P page, you need to find the b32 address of your destination.
|
||||
After that, go to the webconsole and add it behind the url. (Remove http:// from the address)
|
||||
|
||||
This should resulting in for example:
|
||||
http://localhost:7070/4oes3rlgrpbkmzv4lqcfili23h3cvpwslqcfjlk6vvguxyggspwa.b32.i2p
|
||||
|
||||
|
||||
Cmdline options
|
||||
---------------
|
||||
|
||||
* --host= - The external IP
|
||||
* --port= - The port to listen on
|
||||
* --httpport= - The http port to listen on
|
||||
* --log= - Enable or disable logging to file. 1 for yes, 0 for no.
|
||||
* --daemon= - Enable or disable daemon mode. 1 for yes, 0 for no.
|
||||
* --service= - 1 if uses system folders (/var/run/i2pd.pid, /var/log/i2pd.log, /var/lib/i2pd).
|
||||
* --unreachable= - 1 if router is declared as unreachable and works through introducers.
|
||||
* --v6= - 1 if supports communication through ipv6, off by default
|
||||
* --floodfill= - 1 if router is floodfill, off by default
|
||||
* --httpproxyport= - The port to listen on (HTTP Proxy)
|
||||
* --socksproxyport= - The port to listen on (SOCKS Proxy)
|
||||
* --ircport= - The local port of IRC tunnel to listen on. 6668 by default
|
||||
* --ircdest= - I2P destination address of IRC server. For example irc.postman.i2p
|
||||
* --irckeys= - optional keys file for local destination
|
||||
* --eepkeys= - File name containing destination keys, for example privKeys.dat.
|
||||
The file will be created if it does not already exist (issue #110).
|
||||
* --eephost= - Address incoming trafic forward to. 127.0.0.1 by default
|
||||
* --eepport= - Port incoming trafic forward to. 80 by default
|
||||
* --samport= - Port of SAM bridge. Usually 7656. SAM is off if not specified
|
||||
* --bobport= - Port of BOB command channel. Usually 2827. BOB is off if not specified
|
||||
* --i2pcontrolport= - Port of I2P control service. Usually 7650. I2PControl is off if not specified
|
||||
* --conf= - Config file (default: ~/.i2pd/i2p.conf or /var/lib/i2pd/i2p.conf)
|
||||
This parameter will be silently ignored if the specified config file does not exist.
|
||||
Options specified on the command line take precedence over those in the config file.
|
||||
|
||||
Config file
|
||||
-----------
|
||||
|
||||
INI-like, syntax is the following : <key> = <value>.
|
||||
All command-line parameters are allowed as keys, for example:
|
||||
|
||||
log = 1
|
||||
v6 = 0
|
||||
ircdest = irc.postman.i2p
|
||||
|
||||
782
Reseed.cpp
782
Reseed.cpp
@@ -1,52 +1,47 @@
|
||||
#include <string.h>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <cryptopp/asn.h>
|
||||
#include <cryptopp/base64.h>
|
||||
#include <cryptopp/crc.h>
|
||||
#include <cryptopp/hmac.h>
|
||||
#include <cryptopp/zinflate.h>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "Crypto.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "Reseed.h"
|
||||
#include "FS.h"
|
||||
#include "Log.h"
|
||||
#include "Identity.h"
|
||||
#include "CryptoConst.h"
|
||||
#include "NetDb.h"
|
||||
#include "HTTP.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
|
||||
static std::vector<std::string> httpReseedHostList = {
|
||||
// "http://193.150.121.66/netDb/", // unstable
|
||||
// "http://us.reseed.i2p2.no/", // misconfigured, not serving reseed data
|
||||
// "http://jp.reseed.i2p2.no/", // Really outdated RIs
|
||||
"http://netdb.i2p2.no/", // only SU3 (v2) support
|
||||
"http://i2p.mooo.com/netDb/",
|
||||
"http://uk.reseed.i2p2.no/",
|
||||
"http://i2p-netdb.innovatio.no/"
|
||||
};
|
||||
|
||||
//TODO: Remember to add custom port support. Not all serves on 443
|
||||
static std::vector<std::string> httpsReseedHostList = {
|
||||
// "https://193.150.121.66/netDb/", // unstable
|
||||
// "https://i2p-netdb.innovatio.no/",// Vuln to POODLE
|
||||
"https://netdb.i2p2.no/", // Only SU3 (v2) support
|
||||
"https://reseed.i2p-projekt.de/", // Only HTTPS
|
||||
"https://cowpuncher.drollette.com/netdb/", // Only HTTPS and SU3 (v2) support -- will move to a new location
|
||||
// following hosts are fine but don't support AES256
|
||||
/*"https://i2p.mooo.com/netDb/",
|
||||
"https://link.mx24.eu/", // Only HTTPS and SU3 (v2) support
|
||||
"https://i2pseed.zarrenspry.info/", // Only HTTPS and SU3 (v2) support
|
||||
"https://ieb9oopo.mooo.com/" // Only HTTPS and SU3 (v2) support*/
|
||||
};
|
||||
|
||||
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()
|
||||
{
|
||||
}
|
||||
@@ -55,82 +50,25 @@ namespace data
|
||||
{
|
||||
}
|
||||
|
||||
bool Reseeder::reseedNow()
|
||||
{
|
||||
// This method is deprecated
|
||||
try
|
||||
{
|
||||
std::string reseedHost = httpReseedHostList[(rand() % httpReseedHostList.size())];
|
||||
LogPrint("Reseeding from ", reseedHost);
|
||||
std::string content = i2p::util::http::httpRequest(reseedHost);
|
||||
if (content == "")
|
||||
{
|
||||
LogPrint("Reseed failed");
|
||||
return false;
|
||||
}
|
||||
boost::regex e("<\\s*A\\s+[^>]*href\\s*=\\s*\"([^\"]*)\"", boost::regex::normal | boost::regbase::icase);
|
||||
boost::sregex_token_iterator i(content.begin(), content.end(), e, 1);
|
||||
boost::sregex_token_iterator j;
|
||||
//TODO: Ugly code, try to clean up.
|
||||
//TODO: Try to reduce N number of variables
|
||||
std::string name;
|
||||
std::string routerInfo;
|
||||
std::string tmpUrl;
|
||||
std::string filename;
|
||||
std::string ignoreFileSuffix = ".su3";
|
||||
boost::filesystem::path root = i2p::util::filesystem::GetDataDir();
|
||||
while (i != j)
|
||||
{
|
||||
name = *i++;
|
||||
if (name.find(ignoreFileSuffix)!=std::string::npos)
|
||||
continue;
|
||||
LogPrint("Downloading ", name);
|
||||
tmpUrl = reseedHost;
|
||||
tmpUrl.append(name);
|
||||
routerInfo = i2p::util::http::httpRequest(tmpUrl);
|
||||
if (routerInfo.size()==0)
|
||||
continue;
|
||||
filename = root.string();
|
||||
#ifndef _WIN32
|
||||
filename += "/netDb/r";
|
||||
#else
|
||||
filename += "\\netDb\\r";
|
||||
#endif
|
||||
filename += name.at(11); // first char in id
|
||||
#ifndef _WIN32
|
||||
filename.append("/");
|
||||
#else
|
||||
filename.append("\\");
|
||||
#endif
|
||||
filename.append(name.c_str());
|
||||
std::ofstream outfile (filename, std::ios::binary);
|
||||
outfile << routerInfo;
|
||||
outfile.close();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
//TODO: error reporting
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int Reseeder::ReseedNowSU3 ()
|
||||
{
|
||||
CryptoPP::AutoSeededRandomPool rnd;
|
||||
auto ind = rnd.GenerateWord32 (0, httpReseedHostList.size() - 1 + httpsReseedHostList.size () - 1);
|
||||
std::string reseedHost = (ind < httpReseedHostList.size()) ? httpReseedHostList[ind] :
|
||||
httpsReseedHostList[ind - httpReseedHostList.size()];
|
||||
return ReseedFromSU3 (reseedHost, ind >= httpReseedHostList.size());
|
||||
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);
|
||||
}
|
||||
|
||||
int Reseeder::ReseedFromSU3 (const std::string& host, bool https)
|
||||
int Reseeder::ReseedFromSU3 (const std::string& host)
|
||||
{
|
||||
std::string url = host + "i2pseeds.su3";
|
||||
LogPrint (eLogInfo, "Dowloading SU3 from ", host);
|
||||
std::string su3 = https ? HttpsRequest (url) : i2p::util::http::httpRequest (url);
|
||||
LogPrint (eLogInfo, "Reseed: Downloading SU3 from ", host);
|
||||
std::string su3 = HttpsRequest (url);
|
||||
if (su3.length () > 0)
|
||||
{
|
||||
std::stringstream s(su3);
|
||||
@@ -138,7 +76,7 @@ namespace data
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "SU3 download failed");
|
||||
LogPrint (eLogWarning, "Reseed: SU3 download failed");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -150,7 +88,7 @@ namespace data
|
||||
return ProcessSU3Stream (s);
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "Can't open file ", filename);
|
||||
LogPrint (eLogError, "Reseed: Can't open file ", filename);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -165,7 +103,7 @@ namespace data
|
||||
s.read (magicNumber, 7); // magic number and zero byte 6
|
||||
if (strcmp (magicNumber, SU3_MAGIC_NUMBER))
|
||||
{
|
||||
LogPrint (eLogError, "Unexpected SU3 magic number");
|
||||
LogPrint (eLogError, "Reseed: Unexpected SU3 magic number");
|
||||
return 0;
|
||||
}
|
||||
s.seekg (1, std::ios::cur); // su3 file format version
|
||||
@@ -189,7 +127,7 @@ namespace data
|
||||
s.read ((char *)&fileType, 1); // file type
|
||||
if (fileType != 0x00) // zip file
|
||||
{
|
||||
LogPrint (eLogError, "Can't handle file type ", (int)fileType);
|
||||
LogPrint (eLogError, "Reseed: Can't handle file type ", (int)fileType);
|
||||
return 0;
|
||||
}
|
||||
s.seekg (1, std::ios::cur); // unused
|
||||
@@ -197,7 +135,7 @@ namespace data
|
||||
s.read ((char *)&contentType, 1); // content type
|
||||
if (contentType != 0x03) // reseed data
|
||||
{
|
||||
LogPrint (eLogError, "Unexpected content type ", (int)contentType);
|
||||
LogPrint (eLogError, "Reseed: Unexpected content type ", (int)contentType);
|
||||
return 0;
|
||||
}
|
||||
s.seekg (12, std::ios::cur); // unused
|
||||
@@ -222,19 +160,36 @@ namespace data
|
||||
uint8_t * signature = new uint8_t[signatureLength];
|
||||
s.read ((char *)signature, signatureLength);
|
||||
// RSA-raw
|
||||
i2p::crypto::RSASHA5124096RawVerifier verifier(it->second);
|
||||
verifier.Update (tbs, tbsLen);
|
||||
if (!verifier.Verify (signature))
|
||||
LogPrint (eLogWarning, "SU3 signature verification failed");
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
||||
delete[] signature;
|
||||
delete[] tbs;
|
||||
s.seekg (pos, std::ios::beg);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Signature type ", signatureType, " is not supported");
|
||||
LogPrint (eLogWarning, "Reseed: Signature type ", signatureType, " is not supported");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Certificate for ", signerID, " not loaded");
|
||||
LogPrint (eLogWarning, "Reseed: Certificate for ", signerID, " not loaded");
|
||||
|
||||
// handle content
|
||||
int numFiles = 0;
|
||||
@@ -256,8 +211,9 @@ namespace data
|
||||
compressionMethod = le16toh (compressionMethod);
|
||||
s.seekg (4, std::ios::cur); // skip fields we don't care about
|
||||
uint32_t compressedSize, uncompressedSize;
|
||||
uint8_t crc32[4];
|
||||
s.read ((char *)crc32, 4);
|
||||
uint32_t crc_32;
|
||||
s.read ((char *)&crc_32, 4);
|
||||
crc_32 = le32toh (crc_32);
|
||||
s.read ((char *)&compressedSize, 4);
|
||||
compressedSize = le32toh (compressedSize);
|
||||
s.read ((char *)&uncompressedSize, 4);
|
||||
@@ -265,6 +221,11 @@ namespace data
|
||||
uint16_t fileNameLength, extraFieldLength;
|
||||
s.read ((char *)&fileNameLength, 2);
|
||||
fileNameLength = le16toh (fileNameLength);
|
||||
if ( fileNameLength > 255 ) {
|
||||
// too big
|
||||
LogPrint(eLogError, "Reseed: SU3 fileNameLength too large: ", fileNameLength);
|
||||
return numFiles;
|
||||
}
|
||||
s.read ((char *)&extraFieldLength, 2);
|
||||
extraFieldLength = le16toh (extraFieldLength);
|
||||
char localFileName[255];
|
||||
@@ -277,11 +238,11 @@ namespace data
|
||||
size_t pos = s.tellg ();
|
||||
if (!FindZipDataDescriptor (s))
|
||||
{
|
||||
LogPrint (eLogError, "SU3 archive data descriptor not found");
|
||||
LogPrint (eLogError, "Reseed: SU3 archive data descriptor not found");
|
||||
return numFiles;
|
||||
}
|
||||
|
||||
s.read ((char *)crc32, 4);
|
||||
}
|
||||
s.read ((char *)&crc_32, 4);
|
||||
crc_32 = le32toh (crc_32);
|
||||
s.read ((char *)&compressedSize, 4);
|
||||
compressedSize = le32toh (compressedSize) + 4; // ??? we must consider signature as part of compressed data
|
||||
s.read ((char *)&uncompressedSize, 4);
|
||||
@@ -291,10 +252,10 @@ namespace data
|
||||
s.seekg (pos, std::ios::beg); // back to compressed data
|
||||
}
|
||||
|
||||
LogPrint (eLogDebug, "Proccessing file ", localFileName, " ", compressedSize, " bytes");
|
||||
LogPrint (eLogDebug, "Reseed: Proccessing file ", localFileName, " ", compressedSize, " bytes");
|
||||
if (!compressedSize)
|
||||
{
|
||||
LogPrint (eLogWarning, "Unexpected size 0. Skipped");
|
||||
LogPrint (eLogWarning, "Reseed: Unexpected size 0. Skipped");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -302,25 +263,31 @@ namespace data
|
||||
s.read ((char *)compressed, compressedSize);
|
||||
if (compressionMethod) // we assume Deflate
|
||||
{
|
||||
CryptoPP::Inflator decompressor;
|
||||
decompressor.Put (compressed, compressedSize);
|
||||
decompressor.MessageEnd();
|
||||
if (decompressor.MaxRetrievable () <= uncompressedSize)
|
||||
{
|
||||
uint8_t * uncompressed = new uint8_t[uncompressedSize];
|
||||
decompressor.Get (uncompressed, uncompressedSize);
|
||||
if (CryptoPP::CRC32().VerifyDigest (crc32, uncompressed, uncompressedSize))
|
||||
z_stream inflator;
|
||||
memset (&inflator, 0, sizeof (inflator));
|
||||
inflateInit2 (&inflator, -MAX_WBITS); // no zlib header
|
||||
uint8_t * uncompressed = new uint8_t[uncompressedSize];
|
||||
inflator.next_in = compressed;
|
||||
inflator.avail_in = compressedSize;
|
||||
inflator.next_out = uncompressed;
|
||||
inflator.avail_out = uncompressedSize;
|
||||
int err;
|
||||
if ((err = inflate (&inflator, Z_SYNC_FLUSH)) >= 0)
|
||||
{
|
||||
uncompressedSize -= inflator.avail_out;
|
||||
if (crc32 (0, uncompressed, uncompressedSize) == crc_32)
|
||||
{
|
||||
i2p::data::netdb.AddRouterInfo (uncompressed, uncompressedSize);
|
||||
numFiles++;
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "CRC32 verification failed");
|
||||
delete[] uncompressed;
|
||||
}
|
||||
LogPrint (eLogError, "Reseed: CRC32 verification failed");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Actual uncompressed size ", decompressor.MaxRetrievable (), " exceed ", uncompressedSize, " from header");
|
||||
}
|
||||
LogPrint (eLogError, "Reseed: SU3 decompression error ", err);
|
||||
delete[] uncompressed;
|
||||
inflateEnd (&inflator);
|
||||
}
|
||||
else // no compression
|
||||
{
|
||||
i2p::data::netdb.AddRouterInfo (compressed, compressedSize);
|
||||
@@ -333,7 +300,7 @@ namespace data
|
||||
else
|
||||
{
|
||||
if (signature != ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE)
|
||||
LogPrint (eLogWarning, "Missing zip central directory header");
|
||||
LogPrint (eLogWarning, "Reseed: Missing zip central directory header");
|
||||
break; // no more files
|
||||
}
|
||||
size_t end = s.tellg ();
|
||||
@@ -363,462 +330,129 @@ namespace data
|
||||
return false;
|
||||
}
|
||||
|
||||
const char CERTIFICATE_HEADER[] = "-----BEGIN CERTIFICATE-----";
|
||||
const char CERTIFICATE_FOOTER[] = "-----END CERTIFICATE-----";
|
||||
void Reseeder::LoadCertificate (const std::string& filename)
|
||||
{
|
||||
std::ifstream s(filename, std::ifstream::binary);
|
||||
if (s.is_open ())
|
||||
{
|
||||
s.seekg (0, std::ios::end);
|
||||
size_t len = s.tellg ();
|
||||
s.seekg (0, std::ios::beg);
|
||||
char buf[2048];
|
||||
s.read (buf, len);
|
||||
std::string cert (buf, len);
|
||||
// assume file in pem format
|
||||
auto pos1 = cert.find (CERTIFICATE_HEADER);
|
||||
auto pos2 = cert.find (CERTIFICATE_FOOTER);
|
||||
if (pos1 == std::string::npos || pos2 == std::string::npos)
|
||||
{
|
||||
LogPrint (eLogError, "Malformed certificate file");
|
||||
return;
|
||||
SSL_CTX * ctx = SSL_CTX_new (TLSv1_method ());
|
||||
int ret = SSL_CTX_use_certificate_file (ctx, filename.c_str (), SSL_FILETYPE_PEM);
|
||||
if (ret)
|
||||
{
|
||||
SSL * ssl = SSL_new (ctx);
|
||||
X509 * cert = SSL_get_certificate (ssl);
|
||||
// verify
|
||||
if (cert)
|
||||
{
|
||||
// extract issuer name
|
||||
char name[100];
|
||||
X509_NAME_oneline (X509_get_issuer_name(cert), name, 100);
|
||||
// extract RSA key (we need n only, e = 65537)
|
||||
RSA * key = X509_get_pubkey (cert)->pkey.rsa;
|
||||
PublicKey value;
|
||||
i2p::crypto::bn2buf (key->n, value, 512);
|
||||
m_SigningKeys[name] = value;
|
||||
}
|
||||
pos1 += strlen (CERTIFICATE_HEADER);
|
||||
pos2 -= pos1;
|
||||
std::string base64 = cert.substr (pos1, pos2);
|
||||
|
||||
CryptoPP::ByteQueue queue;
|
||||
CryptoPP::Base64Decoder decoder; // regular base64 rather than I2P
|
||||
decoder.Attach (new CryptoPP::Redirector (queue));
|
||||
decoder.Put ((const uint8_t *)base64.data(), base64.length());
|
||||
decoder.MessageEnd ();
|
||||
|
||||
LoadCertificate (queue);
|
||||
}
|
||||
SSL_free (ssl);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Can't open certificate file ", filename);
|
||||
LogPrint (eLogError, "Reseed: Can't open certificate file ", filename);
|
||||
SSL_CTX_free (ctx);
|
||||
}
|
||||
|
||||
std::string Reseeder::LoadCertificate (CryptoPP::ByteQueue& queue)
|
||||
{
|
||||
// extract X.509
|
||||
CryptoPP::BERSequenceDecoder x509Cert (queue);
|
||||
CryptoPP::BERSequenceDecoder tbsCert (x509Cert);
|
||||
// version
|
||||
uint32_t ver;
|
||||
CryptoPP::BERGeneralDecoder context (tbsCert, CryptoPP::CONTEXT_SPECIFIC | CryptoPP::CONSTRUCTED);
|
||||
CryptoPP::BERDecodeUnsigned<uint32_t>(context, ver, CryptoPP::INTEGER);
|
||||
// serial
|
||||
CryptoPP::Integer serial;
|
||||
serial.BERDecode(tbsCert);
|
||||
// signature
|
||||
CryptoPP::BERSequenceDecoder signature (tbsCert);
|
||||
signature.SkipAll();
|
||||
|
||||
// issuer
|
||||
std::string name;
|
||||
CryptoPP::BERSequenceDecoder issuer (tbsCert);
|
||||
{
|
||||
CryptoPP::BERSetDecoder c (issuer); c.SkipAll();
|
||||
CryptoPP::BERSetDecoder st (issuer); st.SkipAll();
|
||||
CryptoPP::BERSetDecoder l (issuer); l.SkipAll();
|
||||
CryptoPP::BERSetDecoder o (issuer); o.SkipAll();
|
||||
CryptoPP::BERSetDecoder ou (issuer); ou.SkipAll();
|
||||
CryptoPP::BERSetDecoder cn (issuer);
|
||||
{
|
||||
CryptoPP::BERSequenceDecoder attributes (cn);
|
||||
{
|
||||
CryptoPP::BERGeneralDecoder ident(attributes, CryptoPP::OBJECT_IDENTIFIER);
|
||||
ident.SkipAll ();
|
||||
CryptoPP::BERDecodeTextString (attributes, name, CryptoPP::UTF8_STRING);
|
||||
}
|
||||
}
|
||||
}
|
||||
issuer.SkipAll();
|
||||
// validity
|
||||
CryptoPP::BERSequenceDecoder validity (tbsCert);
|
||||
validity.SkipAll();
|
||||
// subject
|
||||
CryptoPP::BERSequenceDecoder subject (tbsCert);
|
||||
subject.SkipAll();
|
||||
// public key
|
||||
CryptoPP::BERSequenceDecoder publicKey (tbsCert);
|
||||
{
|
||||
CryptoPP::BERSequenceDecoder ident (publicKey);
|
||||
ident.SkipAll ();
|
||||
CryptoPP::BERGeneralDecoder key (publicKey, CryptoPP::BIT_STRING);
|
||||
key.Skip (1); // FIXME: probably bug in crypto++
|
||||
CryptoPP::BERSequenceDecoder keyPair (key);
|
||||
CryptoPP::Integer n;
|
||||
n.BERDecode (keyPair);
|
||||
if (name.length () > 0)
|
||||
{
|
||||
PublicKey value;
|
||||
n.Encode (value, 512);
|
||||
m_SigningKeys[name] = value;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Unknown issuer. Skipped");
|
||||
}
|
||||
publicKey.SkipAll();
|
||||
|
||||
tbsCert.SkipAll();
|
||||
x509Cert.SkipAll();
|
||||
return name;
|
||||
}
|
||||
|
||||
void Reseeder::LoadCertificates ()
|
||||
{
|
||||
boost::filesystem::path reseedDir = i2p::util::filesystem::GetCertificatesDir() / "reseed";
|
||||
|
||||
if (!boost::filesystem::exists (reseedDir))
|
||||
{
|
||||
LogPrint (eLogWarning, "Reseed certificates not loaded. ", reseedDir, " doesn't exist");
|
||||
std::string certDir = i2p::fs::DataDirPath("certificates", "reseed");
|
||||
std::vector<std::string> files;
|
||||
int numCertificates = 0;
|
||||
|
||||
if (!i2p::fs::ReadDir(certDir, files)) {
|
||||
LogPrint(eLogWarning, "Reseed: Can't load reseed certificates from ", certDir);
|
||||
return;
|
||||
}
|
||||
|
||||
int numCertificates = 0;
|
||||
boost::filesystem::directory_iterator end; // empty
|
||||
for (boost::filesystem::directory_iterator it (reseedDir); it != end; ++it)
|
||||
{
|
||||
if (boost::filesystem::is_regular_file (it->status()) && it->path ().extension () == ".crt")
|
||||
{
|
||||
LoadCertificate (it->path ().string ());
|
||||
numCertificates++;
|
||||
}
|
||||
for (const std::string & file : files) {
|
||||
if (file.compare(file.size() - 4, 4, ".crt") != 0) {
|
||||
LogPrint(eLogWarning, "Reseed: ignoring file ", file);
|
||||
continue;
|
||||
}
|
||||
LoadCertificate (file);
|
||||
numCertificates++;
|
||||
}
|
||||
LogPrint (eLogInfo, numCertificates, " certificates loaded");
|
||||
LogPrint (eLogInfo, "Reseed: ", numCertificates, " certificates loaded");
|
||||
}
|
||||
|
||||
std::string Reseeder::HttpsRequest (const std::string& address)
|
||||
{
|
||||
i2p::util::http::url u(address);
|
||||
TlsSession session (u.host_, 443);
|
||||
|
||||
// send request
|
||||
std::stringstream ss;
|
||||
ss << "GET " << u.path_ << " HTTP/1.1\r\nHost: " << u.host_
|
||||
<< "\r\nAccept: */*\r\n" << "User-Agent: Wget/1.11.4\r\n" << "Connection: close\r\n\r\n";
|
||||
session.Send ((uint8_t *)ss.str ().c_str (), ss.str ().length ());
|
||||
i2p::http::URL url;
|
||||
if (!url.parse(address)) {
|
||||
LogPrint(eLogError, "Reseed: failed to parse url: ", address);
|
||||
return "";
|
||||
}
|
||||
url.schema = "https";
|
||||
if (!url.port)
|
||||
url.port = 443;
|
||||
|
||||
// read response
|
||||
std::stringstream rs;
|
||||
while (session.Receive (rs))
|
||||
;
|
||||
return i2p::util::http::GetHttpContent (rs);
|
||||
}
|
||||
|
||||
TlsSession::TlsSession (const std::string& host, int port):
|
||||
m_Seqn (0)
|
||||
{
|
||||
m_Site.connect(host, boost::lexical_cast<std::string>(port));
|
||||
if (m_Site.good ())
|
||||
boost::asio::io_service service;
|
||||
boost::system::error_code ecode;
|
||||
auto it = boost::asio::ip::tcp::resolver(service).resolve (
|
||||
boost::asio::ip::tcp::resolver::query (url.host, std::to_string(url.port)), ecode);
|
||||
if (!ecode)
|
||||
{
|
||||
Handshake ();
|
||||
boost::asio::ssl::context ctx(service, boost::asio::ssl::context::sslv23);
|
||||
ctx.set_verify_mode(boost::asio::ssl::context::verify_none);
|
||||
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> s(service, ctx);
|
||||
s.lowest_layer().connect (*it, ecode);
|
||||
if (!ecode)
|
||||
{
|
||||
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");
|
||||
s.write_some (boost::asio::buffer (req.to_string()));
|
||||
// read response
|
||||
std::stringstream rs;
|
||||
char recv_buf[1024]; size_t l = 0;
|
||||
do {
|
||||
l = s.read_some (boost::asio::buffer (recv_buf, sizeof(recv_buf)), ecode);
|
||||
if (l) rs.write (recv_buf, l);
|
||||
} while (!ecode && l);
|
||||
// process response
|
||||
std::string data = rs.str();
|
||||
i2p::http::HTTPRes res;
|
||||
int len = res.parse(data);
|
||||
if (len <= 0) {
|
||||
LogPrint(eLogWarning, "Reseed: incomplete/broken response from ", url.host);
|
||||
return "";
|
||||
}
|
||||
if (res.code != 200) {
|
||||
LogPrint(eLogError, "Reseed: failed to reseed from ", url.host, ", http code ", res.code);
|
||||
return "";
|
||||
}
|
||||
data.erase(0, len); /* drop http headers from response */
|
||||
LogPrint(eLogDebug, "Reseed: got ", data.length(), " bytes of data from ", url.host);
|
||||
if (res.is_chunked()) {
|
||||
std::stringstream in(data), out;
|
||||
if (!i2p::http::MergeChunkedResponse(in, out)) {
|
||||
LogPrint(eLogWarning, "Reseed: failed to merge chunked response from ", url.host);
|
||||
return "";
|
||||
}
|
||||
LogPrint(eLogDebug, "Reseed: got ", data.length(), "(", out.tellg(), ") bytes of data from ", url.host);
|
||||
data = out.str();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Reseed: SSL handshake failed: ", ecode.message ());
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Reseed: Couldn't connect to ", url.host, ": ", ecode.message ());
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Can't connect to ", host, ":", port);
|
||||
LogPrint (eLogError, "Reseed: Couldn't resolve address ", url.host, ": ", ecode.message ());
|
||||
return "";
|
||||
}
|
||||
|
||||
void TlsSession::Handshake ()
|
||||
{
|
||||
static uint8_t clientHello[] =
|
||||
{
|
||||
0x16, // handshake
|
||||
0x03, 0x03, // version (TLS 1.2)
|
||||
0x00, 0x2F, // length of handshake
|
||||
// handshake
|
||||
0x01, // handshake type (client hello)
|
||||
0x00, 0x00, 0x2B, // length of handshake payload
|
||||
// client hello
|
||||
0x03, 0x03, // highest version supported (TLS 1.2)
|
||||
0x45, 0xFA, 0x01, 0x19, 0x74, 0x55, 0x18, 0x36,
|
||||
0x42, 0x05, 0xC1, 0xDD, 0x4A, 0x21, 0x80, 0x80,
|
||||
0xEC, 0x37, 0x11, 0x93, 0x16, 0xF4, 0x66, 0x00,
|
||||
0x12, 0x67, 0xAB, 0xBA, 0xFF, 0x29, 0x13, 0x9E, // 32 random bytes
|
||||
0x00, // session id length
|
||||
0x00, 0x02, // chiper suites length
|
||||
0x00, 0x3D, // RSA_WITH_AES_256_CBC_SHA256
|
||||
0x01, // compression methods length
|
||||
0x00, // no compression
|
||||
0x00, 0x00 // extensions length
|
||||
};
|
||||
|
||||
static uint8_t changeCipherSpecs[] =
|
||||
{
|
||||
0x14, // change cipher specs
|
||||
0x03, 0x03, // version (TLS 1.2)
|
||||
0x00, 0x01, // length
|
||||
0x01 // type
|
||||
};
|
||||
|
||||
static uint8_t finished[] =
|
||||
{
|
||||
0x16, // handshake
|
||||
0x03, 0x03, // version (TLS 1.2)
|
||||
0x00, 0x50, // length of handshake (80 bytes)
|
||||
// handshake (encrypted)
|
||||
// unencrypted context
|
||||
// 0x14 handshake type (finished)
|
||||
// 0x00, 0x00, 0x0C length of handshake payload
|
||||
// 12 bytes of verified data
|
||||
};
|
||||
|
||||
// send ClientHello
|
||||
m_Site.write ((char *)clientHello, sizeof (clientHello));
|
||||
m_FinishedHash.Update (clientHello + 5, sizeof (clientHello) - 5);
|
||||
// read ServerHello
|
||||
uint8_t type;
|
||||
m_Site.read ((char *)&type, 1);
|
||||
uint16_t version;
|
||||
m_Site.read ((char *)&version, 2);
|
||||
uint16_t length;
|
||||
m_Site.read ((char *)&length, 2);
|
||||
length = be16toh (length);
|
||||
char * serverHello = new char[length];
|
||||
m_Site.read (serverHello, length);
|
||||
m_FinishedHash.Update ((uint8_t *)serverHello, length);
|
||||
uint8_t serverRandom[32];
|
||||
if (serverHello[0] == 0x02) // handshake type server hello
|
||||
memcpy (serverRandom, serverHello + 6, 32);
|
||||
else
|
||||
LogPrint (eLogError, "Unexpected handshake type ", (int)serverHello[0]);
|
||||
delete[] serverHello;
|
||||
// read Certificate
|
||||
m_Site.read ((char *)&type, 1);
|
||||
m_Site.read ((char *)&version, 2);
|
||||
m_Site.read ((char *)&length, 2);
|
||||
length = be16toh (length);
|
||||
char * certificate = new char[length];
|
||||
m_Site.read (certificate, length);
|
||||
m_FinishedHash.Update ((uint8_t *)certificate, length);
|
||||
CryptoPP::RSA::PublicKey publicKey;
|
||||
// 0 - handshake type
|
||||
// 1 - 3 - handshake payload length
|
||||
// 4 - 6 - length of array of certificates
|
||||
// 7 - 9 - length of certificate
|
||||
if (certificate[0] == 0x0B) // handshake type certificate
|
||||
publicKey = ExtractPublicKey ((uint8_t *)certificate + 10, length - 10);
|
||||
else
|
||||
LogPrint (eLogError, "Unexpected handshake type ", (int)certificate[0]);
|
||||
delete[] certificate;
|
||||
// read ServerHelloDone
|
||||
m_Site.read ((char *)&type, 1);
|
||||
m_Site.read ((char *)&version, 2);
|
||||
m_Site.read ((char *)&length, 2);
|
||||
length = be16toh (length);
|
||||
char * serverHelloDone = new char[length];
|
||||
m_Site.read (serverHelloDone, length);
|
||||
m_FinishedHash.Update ((uint8_t *)serverHelloDone, length);
|
||||
if (serverHelloDone[0] != 0x0E) // handshake type hello done
|
||||
LogPrint (eLogError, "Unexpected handshake type ", (int)serverHelloDone[0]);
|
||||
delete[] serverHelloDone;
|
||||
// our turn now
|
||||
// generate secret key
|
||||
uint8_t secret[48];
|
||||
secret[0] = 3; secret[1] = 3; // version
|
||||
m_Rnd.GenerateBlock (secret + 2, 46); // 46 random bytes
|
||||
// encrypt RSA
|
||||
CryptoPP::RSAES_PKCS1v15_Encryptor encryptor(publicKey);
|
||||
size_t encryptedLen = encryptor.CiphertextLength (48); // number of bytes for encrypted 48 bytes, usually 256 (2048 bits key)
|
||||
uint8_t * encrypted = new uint8_t[encryptedLen + 2]; // + 2 bytes for length
|
||||
htobe16buf (encrypted, encryptedLen); // first two bytes means length
|
||||
encryptor.Encrypt (m_Rnd, secret, 48, encrypted + 2);
|
||||
// send ClientKeyExchange
|
||||
// 0x10 - handshake type "client key exchange"
|
||||
SendHandshakeMsg (0x10, encrypted, encryptedLen + 2);
|
||||
delete[] encrypted;
|
||||
// send ChangeCipherSpecs
|
||||
m_Site.write ((char *)changeCipherSpecs, sizeof (changeCipherSpecs));
|
||||
// calculate master secret
|
||||
uint8_t masterSecret[48], random[64];
|
||||
memcpy (random, clientHello + 11, 32);
|
||||
memcpy (random + 32, serverRandom, 32);
|
||||
PRF (secret, "master secret", random, 64, 48, masterSecret);
|
||||
// expand master secret
|
||||
uint8_t keys[128]; // clientMACKey(32), serverMACKey(32), clientKey(32), serverKey(32)
|
||||
memcpy (random, serverRandom, 32);
|
||||
memcpy (random + 32, clientHello + 11, 32);
|
||||
PRF (masterSecret, "key expansion", random, 64, 128, keys);
|
||||
memcpy (m_MacKey, keys, 32);
|
||||
m_Encryption.SetKey (keys + 64);
|
||||
m_Decryption.SetKey (keys + 96);
|
||||
|
||||
// send finished
|
||||
uint8_t finishedHashDigest[32], finishedPayload[40], encryptedPayload[80];
|
||||
finishedPayload[0] = 0x14; // handshake type (finished)
|
||||
finishedPayload[1] = 0; finishedPayload[2] = 0; finishedPayload[3] = 0x0C; // 12 bytes
|
||||
m_FinishedHash.Final (finishedHashDigest);
|
||||
PRF (masterSecret, "client finished", finishedHashDigest, 32, 12, finishedPayload + 4);
|
||||
uint8_t mac[32];
|
||||
CalculateMAC (0x16, finishedPayload, 16, mac);
|
||||
Encrypt (finishedPayload, 16, mac, encryptedPayload);
|
||||
m_Site.write ((char *)finished, sizeof (finished));
|
||||
m_Site.write ((char *)encryptedPayload, 80);
|
||||
// read ChangeCipherSpecs
|
||||
uint8_t changeCipherSpecs1[6];
|
||||
m_Site.read ((char *)changeCipherSpecs1, 6);
|
||||
// read finished
|
||||
m_Site.read ((char *)&type, 1);
|
||||
m_Site.read ((char *)&version, 2);
|
||||
m_Site.read ((char *)&length, 2);
|
||||
length = be16toh (length);
|
||||
char * finished1 = new char[length];
|
||||
m_Site.read (finished1, length);
|
||||
delete[] finished1;
|
||||
}
|
||||
|
||||
void TlsSession::SendHandshakeMsg (uint8_t handshakeType, uint8_t * data, size_t len)
|
||||
{
|
||||
uint8_t handshakeHeader[9];
|
||||
handshakeHeader[0] = 0x16; // handshake
|
||||
handshakeHeader[1] = 0x03; handshakeHeader[2] = 0x03; // version is always TLS 1.2 (3,3)
|
||||
htobe16buf (handshakeHeader + 3, len + 4); // length of payload
|
||||
//payload starts
|
||||
handshakeHeader[5] = handshakeType; // handshake type
|
||||
handshakeHeader[6] = 0; // highest byte of payload length is always zero
|
||||
htobe16buf (handshakeHeader + 7, len); // length of data
|
||||
m_Site.write ((char *)handshakeHeader, 9);
|
||||
m_FinishedHash.Update (handshakeHeader + 5, 4); // only payload counts
|
||||
m_Site.write ((char *)data, len);
|
||||
m_FinishedHash.Update (data, len);
|
||||
}
|
||||
|
||||
void TlsSession::PRF (const uint8_t * secret, const char * label, const uint8_t * random, size_t randomLen,
|
||||
size_t len, uint8_t * buf)
|
||||
{
|
||||
// secret is assumed 48 bytes
|
||||
// random is not more than 64 bytes
|
||||
CryptoPP::HMAC<CryptoPP::SHA256> hmac (secret, 48);
|
||||
uint8_t seed[96]; size_t seedLen;
|
||||
seedLen = strlen (label);
|
||||
memcpy (seed, label, seedLen);
|
||||
memcpy (seed + seedLen, random, randomLen);
|
||||
seedLen += randomLen;
|
||||
|
||||
size_t offset = 0;
|
||||
uint8_t a[128];
|
||||
hmac.CalculateDigest (a, seed, seedLen);
|
||||
while (offset < len)
|
||||
{
|
||||
memcpy (a + 32, seed, seedLen);
|
||||
hmac.CalculateDigest (buf + offset, a, seedLen + 32);
|
||||
offset += 32;
|
||||
hmac.CalculateDigest (a, a, 32);
|
||||
}
|
||||
}
|
||||
|
||||
size_t TlsSession::Encrypt (const uint8_t * in, size_t len, const uint8_t * mac, uint8_t * out)
|
||||
{
|
||||
size_t size = 0;
|
||||
m_Rnd.GenerateBlock (out, 16); // iv
|
||||
size += 16;
|
||||
m_Encryption.SetIV (out);
|
||||
memcpy (out + size, in, len);
|
||||
size += len;
|
||||
memcpy (out + size, mac, 32);
|
||||
size += 32;
|
||||
uint8_t paddingSize = len + 1;
|
||||
paddingSize &= 0x0F; // %16
|
||||
if (paddingSize > 0) paddingSize = 16 - paddingSize;
|
||||
memset (out + size, paddingSize, paddingSize + 1); // paddind and last byte are equal to padding size
|
||||
size += paddingSize + 1;
|
||||
m_Encryption.Encrypt (out + 16, size - 16, out + 16);
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t TlsSession::Decrypt (uint8_t * buf, size_t len)
|
||||
{
|
||||
m_Decryption.SetIV (buf);
|
||||
m_Decryption.Decrypt (buf + 16, len - 16, buf + 16);
|
||||
return len - 48 - buf[len -1] - 1; // IV(16), mac(32) and padding
|
||||
}
|
||||
|
||||
void TlsSession::CalculateMAC (uint8_t type, const uint8_t * buf, size_t len, uint8_t * mac)
|
||||
{
|
||||
uint8_t header[13]; // seqn (8) + type (1) + version (2) + length (2)
|
||||
htobe64buf (header, m_Seqn);
|
||||
header[8] = type; header[9] = 3; header[10] = 3; // 3,3 means TLS 1.2
|
||||
htobe16buf (header + 11, len);
|
||||
CryptoPP::HMAC<CryptoPP::SHA256> hmac (m_MacKey, 32);
|
||||
hmac.Update (header, 13);
|
||||
hmac.Update (buf, len);
|
||||
hmac.Final (mac);
|
||||
m_Seqn++;
|
||||
}
|
||||
|
||||
CryptoPP::RSA::PublicKey TlsSession::ExtractPublicKey (const uint8_t * certificate, size_t len)
|
||||
{
|
||||
CryptoPP::ByteQueue queue;
|
||||
queue.Put (certificate, len);
|
||||
queue.MessageEnd ();
|
||||
// extract X.509
|
||||
CryptoPP::BERSequenceDecoder x509Cert (queue);
|
||||
CryptoPP::BERSequenceDecoder tbsCert (x509Cert);
|
||||
// version
|
||||
uint32_t ver;
|
||||
CryptoPP::BERGeneralDecoder context (tbsCert, CryptoPP::CONTEXT_SPECIFIC | CryptoPP::CONSTRUCTED);
|
||||
CryptoPP::BERDecodeUnsigned<uint32_t>(context, ver, CryptoPP::INTEGER);
|
||||
// serial
|
||||
CryptoPP::Integer serial;
|
||||
serial.BERDecode(tbsCert);
|
||||
// signature
|
||||
CryptoPP::BERSequenceDecoder signature (tbsCert);
|
||||
signature.SkipAll();
|
||||
// issuer
|
||||
CryptoPP::BERSequenceDecoder issuer (tbsCert);
|
||||
issuer.SkipAll();
|
||||
// validity
|
||||
CryptoPP::BERSequenceDecoder validity (tbsCert);
|
||||
validity.SkipAll();
|
||||
// subject
|
||||
CryptoPP::BERSequenceDecoder subject (tbsCert);
|
||||
subject.SkipAll();
|
||||
// public key
|
||||
CryptoPP::BERSequenceDecoder publicKey (tbsCert);
|
||||
CryptoPP::BERSequenceDecoder ident (publicKey);
|
||||
ident.SkipAll ();
|
||||
CryptoPP::BERGeneralDecoder key (publicKey, CryptoPP::BIT_STRING);
|
||||
key.Skip (1); // FIXME: probably bug in crypto++
|
||||
CryptoPP::BERSequenceDecoder keyPair (key);
|
||||
CryptoPP::Integer n, e;
|
||||
n.BERDecode (keyPair);
|
||||
e.BERDecode (keyPair);
|
||||
|
||||
CryptoPP::RSA::PublicKey ret;
|
||||
ret.Initialize (n, e);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void TlsSession::Send (const uint8_t * buf, size_t len)
|
||||
{
|
||||
uint8_t * out = new uint8_t[len + 64 + 5]; // 64 = 32 mac + 16 iv + upto 16 padding, 5 = header
|
||||
out[0] = 0x17; // application data
|
||||
out[1] = 0x03; out[2] = 0x03; // version
|
||||
uint8_t mac[32];
|
||||
CalculateMAC (0x17, buf, len, mac);
|
||||
size_t encryptedLen = Encrypt (buf, len, mac, out + 5);
|
||||
htobe16buf (out + 3, encryptedLen);
|
||||
m_Site.write ((char *)out, encryptedLen + 5);
|
||||
delete[] out;
|
||||
}
|
||||
|
||||
bool TlsSession::Receive (std::ostream& rs)
|
||||
{
|
||||
if (m_Site.eof ()) return false;
|
||||
uint8_t type; uint16_t version, length;
|
||||
m_Site.read ((char *)&type, 1);
|
||||
m_Site.read ((char *)&version, 2);
|
||||
m_Site.read ((char *)&length, 2);
|
||||
length = be16toh (length);
|
||||
uint8_t * buf = new uint8_t[length];
|
||||
m_Site.read ((char *)buf, length);
|
||||
size_t decryptedLen = Decrypt (buf, length);
|
||||
rs.write ((char *)buf + 16, decryptedLen);
|
||||
delete[] buf;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
41
Reseed.h
41
Reseed.h
@@ -5,11 +5,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <cryptopp/osrng.h>
|
||||
#include <cryptopp/rsa.h>
|
||||
#include <boost/asio.hpp>
|
||||
#include "Identity.h"
|
||||
#include "aes.h"
|
||||
#include "Crypto.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -24,7 +21,6 @@ namespace data
|
||||
|
||||
Reseeder();
|
||||
~Reseeder();
|
||||
bool reseedNow(); // depreacted
|
||||
int ReseedNowSU3 ();
|
||||
|
||||
void LoadCertificates ();
|
||||
@@ -32,9 +28,8 @@ namespace data
|
||||
private:
|
||||
|
||||
void LoadCertificate (const std::string& filename);
|
||||
std::string LoadCertificate (CryptoPP::ByteQueue& queue); // returns issuer's name
|
||||
|
||||
int ReseedFromSU3 (const std::string& host, bool https = false);
|
||||
|
||||
int ReseedFromSU3 (const std::string& host);
|
||||
int ProcessSU3File (const char * filename);
|
||||
int ProcessSU3Stream (std::istream& s);
|
||||
|
||||
@@ -46,36 +41,6 @@ namespace data
|
||||
|
||||
std::map<std::string, PublicKey> m_SigningKeys;
|
||||
};
|
||||
|
||||
class TlsSession
|
||||
{
|
||||
public:
|
||||
|
||||
TlsSession (const std::string& host, int port);
|
||||
void Send (const uint8_t * buf, size_t len);
|
||||
bool Receive (std::ostream& rs);
|
||||
|
||||
private:
|
||||
|
||||
void Handshake ();
|
||||
void SendHandshakeMsg (uint8_t handshakeType, uint8_t * data, size_t len);
|
||||
CryptoPP::RSA::PublicKey ExtractPublicKey (const uint8_t * certificate, size_t len);
|
||||
void PRF (const uint8_t * secret, const char * label, const uint8_t * random, size_t randomLen,
|
||||
size_t len, uint8_t * buf);
|
||||
void CalculateMAC (uint8_t type, const uint8_t * buf, size_t len, uint8_t * mac);
|
||||
size_t Encrypt (const uint8_t * in, size_t len, const uint8_t * mac, uint8_t * out);
|
||||
size_t Decrypt (uint8_t * buf, size_t len); // pyaload is buf + 16
|
||||
|
||||
private:
|
||||
|
||||
uint64_t m_Seqn;
|
||||
boost::asio::ip::tcp::iostream m_Site;
|
||||
CryptoPP::SHA256 m_FinishedHash;
|
||||
CryptoPP::AutoSeededRandomPool m_Rnd;
|
||||
i2p::crypto::CBCEncryption m_Encryption;
|
||||
i2p::crypto::CBCDecryption m_Decryption;
|
||||
uint8_t m_MacKey[32]; // client
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,31 @@
|
||||
#include <fstream>
|
||||
#include <cryptopp/dh.h>
|
||||
#include <cryptopp/dsa.h>
|
||||
#include "CryptoConst.h"
|
||||
#include "RouterContext.h"
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "Config.h"
|
||||
#include "Crypto.h"
|
||||
#include "Timestamp.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "NetDb.h"
|
||||
#include "FS.h"
|
||||
#include "util.h"
|
||||
#include "version.h"
|
||||
#include "Log.h"
|
||||
#include "Family.h"
|
||||
#include "RouterContext.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
RouterContext context;
|
||||
|
||||
RouterContext::RouterContext ():
|
||||
m_LastUpdateTime (0), m_IsUnreachable (false), m_AcceptsTunnels (true),
|
||||
m_IsFloodfill (false)
|
||||
m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false),
|
||||
m_StartupTime (0), m_Status (eRouterStatusOK )
|
||||
{
|
||||
}
|
||||
|
||||
void RouterContext::Init ()
|
||||
{
|
||||
srand (i2p::util::GetMillisecondsSinceEpoch () % 1000);
|
||||
m_StartupTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (!Load ())
|
||||
CreateNewRouter ();
|
||||
UpdateRouterInfo ();
|
||||
@@ -27,7 +33,11 @@ namespace i2p
|
||||
|
||||
void RouterContext::CreateNewRouter ()
|
||||
{
|
||||
m_Keys = i2p::data::CreateRandomKeys ();
|
||||
#if defined(__x86_64__) || defined(__i386__) || defined(_MSC_VER)
|
||||
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519);
|
||||
#else
|
||||
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_DSA_SHA1);
|
||||
#endif
|
||||
SaveKeys ();
|
||||
NewRouterInfo ();
|
||||
}
|
||||
@@ -36,36 +46,77 @@ namespace i2p
|
||||
{
|
||||
i2p::data::RouterInfo routerInfo;
|
||||
routerInfo.SetRouterIdentity (GetIdentity ());
|
||||
int port = i2p::util::config::GetArg("-port", 0);
|
||||
uint16_t port; i2p::config::GetOption("port", port);
|
||||
if (!port)
|
||||
port = m_Rnd.GenerateWord32 (9111, 30777); // I2P network ports range
|
||||
routerInfo.AddSSUAddress (i2p::util::config::GetCharArg("-host", "127.0.0.1"), port, routerInfo.GetIdentHash ());
|
||||
routerInfo.AddNTCPAddress (i2p::util::config::GetCharArg("-host", "127.0.0.1"), port);
|
||||
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
|
||||
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
|
||||
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
|
||||
bool nat; i2p::config::GetOption("nat", nat);
|
||||
std::string ifname; i2p::config::GetOption("ifname", ifname);
|
||||
if (ipv4)
|
||||
{
|
||||
std::string host = "127.0.0.1";
|
||||
if (!i2p::config::IsDefault("host"))
|
||||
i2p::config::GetOption("host", host);
|
||||
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
|
||||
routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ());
|
||||
routerInfo.AddNTCPAddress (host.c_str(), port);
|
||||
}
|
||||
if (ipv6)
|
||||
{
|
||||
std::string host = "::";
|
||||
if (!i2p::config::IsDefault("host") && !ipv4) // override if v6 only
|
||||
i2p::config::GetOption("host", host);
|
||||
else if (!ifname.empty())
|
||||
host = i2p::util::net::GetInterfaceAddress(ifname, true).to_string(); // v6
|
||||
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 ("coreVersion", I2P_VERSION);
|
||||
routerInfo.SetProperty ("netId", "2");
|
||||
routerInfo.SetProperty ("netId", std::to_string (I2PD_NET_ID));
|
||||
routerInfo.SetProperty ("router.version", I2P_VERSION);
|
||||
routerInfo.SetProperty ("stat_uptime", "90m");
|
||||
routerInfo.CreateBuffer (m_Keys);
|
||||
m_RouterInfo.SetRouterIdentity (GetIdentity ());
|
||||
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
|
||||
}
|
||||
|
||||
void RouterContext::UpdateRouterInfo ()
|
||||
{
|
||||
m_RouterInfo.CreateBuffer (m_Keys);
|
||||
m_RouterInfo.SaveToFile (i2p::util::filesystem::GetFullPath (ROUTER_INFO));
|
||||
m_RouterInfo.SaveToFile (i2p::fs::DataDirPath (ROUTER_INFO));
|
||||
m_LastUpdateTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
}
|
||||
|
||||
void RouterContext::SetStatus (RouterStatus status)
|
||||
{
|
||||
if (status != m_Status)
|
||||
{
|
||||
m_Status = status;
|
||||
switch (m_Status)
|
||||
{
|
||||
case eRouterStatusOK:
|
||||
SetReachable ();
|
||||
break;
|
||||
case eRouterStatusFirewalled:
|
||||
SetUnreachable ();
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RouterContext::UpdatePort (int port)
|
||||
{
|
||||
bool updated = false;
|
||||
for (auto& address : m_RouterInfo.GetAddresses ())
|
||||
{
|
||||
if (address.port != port)
|
||||
if (address->port != port)
|
||||
{
|
||||
address.port = port;
|
||||
address->port = port;
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
@@ -78,9 +129,9 @@ namespace i2p
|
||||
bool updated = false;
|
||||
for (auto& address : m_RouterInfo.GetAddresses ())
|
||||
{
|
||||
if (address.host != host && address.IsCompatible (host))
|
||||
if (address->host != host && address->IsCompatible (host))
|
||||
{
|
||||
address.host = host;
|
||||
address->host = host;
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
@@ -89,16 +140,11 @@ namespace i2p
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
bool RouterContext::AddIntroducer (const i2p::data::RouterInfo& routerInfo, uint32_t tag)
|
||||
bool RouterContext::AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer)
|
||||
{
|
||||
bool ret = false;
|
||||
auto address = routerInfo.GetSSUAddress ();
|
||||
if (address)
|
||||
{
|
||||
ret = m_RouterInfo.AddIntroducer (address, tag);
|
||||
if (ret)
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
bool ret = m_RouterInfo.AddIntroducer (introducer);
|
||||
if (ret)
|
||||
UpdateRouterInfo ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -114,33 +160,138 @@ namespace i2p
|
||||
if (floodfill)
|
||||
m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eFloodfill);
|
||||
else
|
||||
{
|
||||
m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () & ~i2p::data::RouterInfo::eFloodfill);
|
||||
// we don't publish number of routers and leaseset for non-floodfill
|
||||
m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_LEASESETS);
|
||||
m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_ROUTERS);
|
||||
}
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
std::string RouterContext::GetFamily () const
|
||||
{
|
||||
return m_RouterInfo.GetProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY);
|
||||
}
|
||||
|
||||
void RouterContext::SetFamily (const std::string& family)
|
||||
{
|
||||
std::string signature;
|
||||
if (family.length () > 0)
|
||||
signature = i2p::data::CreateFamilySignature (family, GetIdentHash ());
|
||||
if (signature.length () > 0)
|
||||
{
|
||||
m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY, family);
|
||||
m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY_SIG, signature);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY);
|
||||
m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY_SIG);
|
||||
}
|
||||
}
|
||||
|
||||
void RouterContext::SetBandwidth (char L) {
|
||||
uint16_t limit = 0;
|
||||
enum { low, high, extra } type = high;
|
||||
/* detect parameters */
|
||||
switch (L)
|
||||
{
|
||||
case i2p::data::CAPS_FLAG_LOW_BANDWIDTH1 : limit = 12; type = low; break;
|
||||
case i2p::data::CAPS_FLAG_LOW_BANDWIDTH2 : limit = 48; type = low; break;
|
||||
case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH1 : limit = 64; type = high; break;
|
||||
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;
|
||||
default:
|
||||
limit = 48; type = low;
|
||||
}
|
||||
/* update caps & flags in RI */
|
||||
auto caps = m_RouterInfo.GetCaps ();
|
||||
caps &= ~i2p::data::RouterInfo::eHighBandwidth;
|
||||
caps &= ~i2p::data::RouterInfo::eExtraBandwidth;
|
||||
switch (type)
|
||||
{
|
||||
case low : /* not set */; break;
|
||||
case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break;
|
||||
case extra : caps |= i2p::data::RouterInfo::eExtraBandwidth; break;
|
||||
}
|
||||
m_RouterInfo.SetCaps (caps);
|
||||
UpdateRouterInfo ();
|
||||
m_BandwidthLimit = limit;
|
||||
}
|
||||
|
||||
void RouterContext::SetBandwidth (int limit)
|
||||
{
|
||||
if (limit > 2000) { SetBandwidth('X'); }
|
||||
else if (limit > 256) { SetBandwidth('P'); }
|
||||
else if (limit > 128) { SetBandwidth('O'); }
|
||||
else if (limit > 64) { SetBandwidth('N'); }
|
||||
else if (limit > 48) { SetBandwidth('M'); }
|
||||
else if (limit > 12) { SetBandwidth('L'); }
|
||||
else { SetBandwidth('K'); }
|
||||
}
|
||||
|
||||
bool RouterContext::IsUnreachable () const
|
||||
{
|
||||
return m_RouterInfo.GetCaps () & i2p::data::RouterInfo::eUnreachable;
|
||||
}
|
||||
|
||||
void RouterContext::SetUnreachable ()
|
||||
{
|
||||
m_IsUnreachable = true;
|
||||
// set caps
|
||||
m_RouterInfo.SetCaps (i2p::data::RouterInfo::eUnreachable | i2p::data::RouterInfo::eSSUTesting); // LU, B
|
||||
// remove NTCP address
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (size_t i = 0; i < addresses.size (); i++)
|
||||
for (auto it = addresses.begin (); it != addresses.end (); ++it)
|
||||
{
|
||||
if (addresses[i].transportStyle == i2p::data::RouterInfo::eTransportNTCP)
|
||||
if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportNTCP &&
|
||||
(*it)->host.is_v4 ())
|
||||
{
|
||||
addresses.erase (addresses.begin () + i);
|
||||
addresses.erase (it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// delete previous introducers
|
||||
for (auto& addr : addresses)
|
||||
addr.introducers.clear ();
|
||||
|
||||
addr->introducers.clear ();
|
||||
|
||||
// update
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
void RouterContext::SetReachable ()
|
||||
{
|
||||
// update caps
|
||||
uint8_t caps = m_RouterInfo.GetCaps ();
|
||||
caps &= ~i2p::data::RouterInfo::eUnreachable;
|
||||
caps |= i2p::data::RouterInfo::eReachable;
|
||||
caps |= i2p::data::RouterInfo::eSSUIntroducer;
|
||||
if (m_IsFloodfill)
|
||||
caps |= i2p::data::RouterInfo::eFloodfill;
|
||||
m_RouterInfo.SetCaps (caps);
|
||||
|
||||
// insert NTCP back
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (const auto& addr : addresses)
|
||||
{
|
||||
if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU &&
|
||||
addr->host.is_v4 ())
|
||||
{
|
||||
// insert NTCP address with host/port from SSU
|
||||
m_RouterInfo.AddNTCPAddress (addr->host.to_string ().c_str (), addr->port);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// delete previous introducers
|
||||
for (auto& addr : addresses)
|
||||
addr->introducers.clear ();
|
||||
|
||||
// update
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
void RouterContext::SetSupportsV6 (bool supportsV6)
|
||||
{
|
||||
if (supportsV6)
|
||||
@@ -148,26 +299,36 @@ namespace i2p
|
||||
else
|
||||
m_RouterInfo.DisableV6 ();
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
}
|
||||
|
||||
void RouterContext::SetSupportsV4 (bool supportsV4)
|
||||
{
|
||||
if (supportsV4)
|
||||
m_RouterInfo.EnableV4 ();
|
||||
else
|
||||
m_RouterInfo.DisableV4 ();
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
|
||||
void RouterContext::UpdateNTCPV6Address (const boost::asio::ip::address& host)
|
||||
{
|
||||
bool updated = false, found = false;
|
||||
int port = 0;
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (auto& addr : addresses)
|
||||
for (auto& addr: addresses)
|
||||
{
|
||||
if (addr.host.is_v6 () && addr.transportStyle == i2p::data::RouterInfo::eTransportNTCP)
|
||||
if (addr->host.is_v6 () && addr->transportStyle == i2p::data::RouterInfo::eTransportNTCP)
|
||||
{
|
||||
if (addr.host != host)
|
||||
if (addr->host != host)
|
||||
{
|
||||
addr.host = host;
|
||||
addr->host = host;
|
||||
updated = true;
|
||||
}
|
||||
found = true;
|
||||
}
|
||||
else
|
||||
port = addr.port;
|
||||
port = addr->port;
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
@@ -176,8 +337,11 @@ namespace i2p
|
||||
auto mtu = i2p::util::net::GetMTU (host);
|
||||
if (mtu)
|
||||
{
|
||||
LogPrint ("Our v6 MTU=", mtu);
|
||||
if (mtu > 1472) mtu = 1472;
|
||||
LogPrint (eLogDebug, "Router: Our v6 MTU=", mtu);
|
||||
if (mtu > 1472) { // TODO: magic constant
|
||||
mtu = 1472;
|
||||
LogPrint(eLogWarning, "Router: MTU dropped to upper limit of 1472 bytes");
|
||||
}
|
||||
}
|
||||
m_RouterInfo.AddSSUAddress (host.to_string ().c_str (), port, GetIdentHash (), mtu ? mtu : 1472); // TODO
|
||||
updated = true;
|
||||
@@ -185,38 +349,105 @@ namespace i2p
|
||||
if (updated)
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
void RouterContext::UpdateStats ()
|
||||
{
|
||||
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 ()));
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
}
|
||||
|
||||
bool RouterContext::Load ()
|
||||
{
|
||||
std::ifstream fk (i2p::util::filesystem::GetFullPath (ROUTER_KEYS).c_str (), std::ifstream::binary | std::ofstream::in);
|
||||
std::ifstream fk (i2p::fs::DataDirPath (ROUTER_KEYS), std::ifstream::in | std::ifstream::binary);
|
||||
if (!fk.is_open ()) return false;
|
||||
|
||||
i2p::data::Keys keys;
|
||||
fk.read ((char *)&keys, sizeof (keys));
|
||||
m_Keys = keys;
|
||||
fk.seekg (0, std::ios::end);
|
||||
size_t len = fk.tellg();
|
||||
fk.seekg (0, std::ios::beg);
|
||||
|
||||
i2p::data::RouterInfo routerInfo(i2p::util::filesystem::GetFullPath (ROUTER_INFO)); // TODO
|
||||
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
|
||||
m_RouterInfo.SetProperty ("coreVersion", I2P_VERSION);
|
||||
m_RouterInfo.SetProperty ("router.version", I2P_VERSION);
|
||||
if (len == sizeof (i2p::data::Keys)) // old keys file format
|
||||
{
|
||||
i2p::data::Keys keys;
|
||||
fk.read ((char *)&keys, sizeof (keys));
|
||||
m_Keys = keys;
|
||||
}
|
||||
else // new keys file format
|
||||
{
|
||||
uint8_t * buf = new uint8_t[len];
|
||||
fk.read ((char *)buf, len);
|
||||
m_Keys.FromBuffer (buf, len);
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
m_RouterInfo.SetRouterIdentity (GetIdentity ());
|
||||
i2p::data::RouterInfo routerInfo(i2p::fs::DataDirPath (ROUTER_INFO));
|
||||
if (!routerInfo.IsUnreachable ()) // router.info looks good
|
||||
{
|
||||
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
|
||||
m_RouterInfo.SetProperty ("coreVersion", I2P_VERSION);
|
||||
m_RouterInfo.SetProperty ("router.version", I2P_VERSION);
|
||||
|
||||
// Migration to 0.9.24. TODO: remove later
|
||||
m_RouterInfo.DeleteProperty ("coreVersion");
|
||||
m_RouterInfo.DeleteProperty ("stat_uptime");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, ROUTER_INFO, " is malformed. Creating new");
|
||||
NewRouterInfo ();
|
||||
}
|
||||
|
||||
if (IsUnreachable ())
|
||||
SetReachable (); // we assume reachable until we discover firewall through peer tests
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RouterContext::SaveKeys ()
|
||||
{
|
||||
std::ofstream fk (i2p::util::filesystem::GetFullPath (ROUTER_KEYS).c_str (), std::ofstream::binary | std::ofstream::out);
|
||||
i2p::data::Keys keys;
|
||||
memcpy (keys.privateKey, m_Keys.GetPrivateKey (), sizeof (keys.privateKey));
|
||||
memcpy (keys.signingPrivateKey, m_Keys.GetSigningPrivateKey (), sizeof (keys.signingPrivateKey));
|
||||
auto& ident = GetIdentity ().GetStandardIdentity ();
|
||||
memcpy (keys.publicKey, ident.publicKey, sizeof (keys.publicKey));
|
||||
memcpy (keys.signingKey, ident.signingKey, sizeof (keys.signingKey));
|
||||
fk.write ((char *)&keys, sizeof (keys));
|
||||
// save in the same format as .dat files
|
||||
std::ofstream fk (i2p::fs::DataDirPath (ROUTER_KEYS), std::ofstream::binary | std::ofstream::out);
|
||||
size_t len = m_Keys.GetFullLen ();
|
||||
uint8_t * buf = new uint8_t[len];
|
||||
m_Keys.ToBuffer (buf, len);
|
||||
fk.write ((char *)buf, len);
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> RouterContext::GetTunnelPool () const
|
||||
{
|
||||
return i2p::tunnel::tunnels.GetExploratoryPool ();
|
||||
}
|
||||
|
||||
void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
|
||||
{
|
||||
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from));
|
||||
}
|
||||
|
||||
void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_GarlicMutex);
|
||||
i2p::garlic::GarlicDestination::ProcessGarlicMessage (msg);
|
||||
}
|
||||
|
||||
void RouterContext::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_GarlicMutex);
|
||||
i2p::garlic::GarlicDestination::ProcessDeliveryStatusMessage (msg);
|
||||
}
|
||||
|
||||
void RouterContext::CleanupDestination ()
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_GarlicMutex);
|
||||
i2p::garlic::GarlicDestination::CleanupExpiredTags ();
|
||||
}
|
||||
|
||||
uint32_t RouterContext::GetUptime () const
|
||||
{
|
||||
return i2p::util::GetSecondsSinceEpoch () - m_StartupTime;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <boost/asio.hpp>
|
||||
#include <cryptopp/dsa.h>
|
||||
#include <cryptopp/osrng.h>
|
||||
#include "Identity.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "Garlic.h"
|
||||
@@ -16,7 +15,14 @@ namespace i2p
|
||||
const char ROUTER_INFO[] = "router.info";
|
||||
const char ROUTER_KEYS[] = "router.keys";
|
||||
const int ROUTER_INFO_UPDATE_INTERVAL = 1800; // 30 minutes
|
||||
|
||||
|
||||
enum RouterStatus
|
||||
{
|
||||
eRouterStatusOK = 0,
|
||||
eRouterStatusTesting = 1,
|
||||
eRouterStatusFirewalled = 2
|
||||
};
|
||||
|
||||
class RouterContext: public i2p::garlic::GarlicDestination
|
||||
{
|
||||
public:
|
||||
@@ -24,37 +30,65 @@ namespace i2p
|
||||
RouterContext ();
|
||||
void Init ();
|
||||
|
||||
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
|
||||
i2p::data::RouterInfo& GetRouterInfo () { return m_RouterInfo; };
|
||||
std::shared_ptr<const i2p::data::RouterInfo> GetSharedRouterInfo () const
|
||||
{
|
||||
return std::shared_ptr<const i2p::data::RouterInfo> (&m_RouterInfo,
|
||||
[](const i2p::data::RouterInfo *) {});
|
||||
}
|
||||
CryptoPP::RandomNumberGenerator& GetRandomNumberGenerator () { return m_Rnd; };
|
||||
std::shared_ptr<i2p::garlic::GarlicDestination> GetSharedDestination ()
|
||||
{
|
||||
return std::shared_ptr<i2p::garlic::GarlicDestination> (this,
|
||||
[](i2p::garlic::GarlicDestination *) {});
|
||||
}
|
||||
|
||||
uint32_t GetUptime () const;
|
||||
uint32_t GetStartupTime () const { return m_StartupTime; };
|
||||
uint64_t GetLastUpdateTime () const { return m_LastUpdateTime; };
|
||||
uint64_t GetBandwidthLimit () const { return m_BandwidthLimit; };
|
||||
RouterStatus GetStatus () const { return m_Status; };
|
||||
void SetStatus (RouterStatus status);
|
||||
|
||||
void UpdatePort (int port); // called from Daemon
|
||||
void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon
|
||||
bool AddIntroducer (const i2p::data::RouterInfo& routerInfo, uint32_t tag);
|
||||
bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer);
|
||||
void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
|
||||
bool IsUnreachable () const { return m_IsUnreachable; };
|
||||
bool IsUnreachable () const;
|
||||
void SetUnreachable ();
|
||||
void SetReachable ();
|
||||
bool IsFloodfill () const { return m_IsFloodfill; };
|
||||
void SetFloodfill (bool floodfill);
|
||||
void SetFamily (const std::string& family);
|
||||
std::string GetFamily () const;
|
||||
void SetBandwidth (int limit); /* in kilobytes */
|
||||
void SetBandwidth (char L); /* by letter */
|
||||
bool AcceptsTunnels () const { return m_AcceptsTunnels; };
|
||||
void SetAcceptsTunnels (bool acceptsTunnels) { m_AcceptsTunnels = acceptsTunnels; };
|
||||
bool SupportsV6 () const { return m_RouterInfo.IsV6 (); };
|
||||
bool SupportsV4 () const { return m_RouterInfo.IsV4 (); };
|
||||
void SetSupportsV6 (bool supportsV6);
|
||||
void UpdateNTCPV6Address (const boost::asio::ip::address& host); // called from NTCP session
|
||||
void SetSupportsV4 (bool supportsV4);
|
||||
|
||||
void UpdateNTCPV6Address (const boost::asio::ip::address& host); // called from NTCP session
|
||||
void UpdateStats ();
|
||||
void CleanupDestination (); // garlic destination
|
||||
|
||||
// implements LocalDestination
|
||||
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
|
||||
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
|
||||
const uint8_t * GetEncryptionPrivateKey () const { return m_Keys.GetPrivateKey (); };
|
||||
const uint8_t * GetEncryptionPublicKey () const { return GetIdentity ().GetStandardIdentity ().publicKey; };
|
||||
const uint8_t * GetEncryptionPublicKey () const { return GetIdentity ()->GetStandardIdentity ().publicKey; };
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
|
||||
void SetLeaseSetUpdated () {};
|
||||
|
||||
// implements GarlicDestination
|
||||
const i2p::data::LeaseSet * GetLeaseSet () { return nullptr; };
|
||||
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () { return nullptr; };
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const;
|
||||
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
||||
|
||||
// override GarlicDestination
|
||||
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
|
||||
private:
|
||||
|
||||
@@ -68,9 +102,12 @@ namespace i2p
|
||||
|
||||
i2p::data::RouterInfo m_RouterInfo;
|
||||
i2p::data::PrivateKeys m_Keys;
|
||||
CryptoPP::AutoSeededRandomPool m_Rnd;
|
||||
uint64_t m_LastUpdateTime;
|
||||
bool m_IsUnreachable, m_AcceptsTunnels, m_IsFloodfill;
|
||||
bool m_AcceptsTunnels, m_IsFloodfill;
|
||||
uint64_t m_StartupTime; // in seconds since epoch
|
||||
uint32_t m_BandwidthLimit; // allowed bandwidth
|
||||
RouterStatus m_Status;
|
||||
std::mutex m_GarlicMutex;
|
||||
};
|
||||
|
||||
extern RouterContext context;
|
||||
|
||||
411
RouterInfo.cpp
411
RouterInfo.cpp
@@ -3,24 +3,28 @@
|
||||
#include "I2PEndian.h"
|
||||
#include <fstream>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <cryptopp/sha.h>
|
||||
#include <cryptopp/dsa.h>
|
||||
#include "CryptoConst.h"
|
||||
#include "base64.h"
|
||||
#include "version.h"
|
||||
#include "Crypto.h"
|
||||
#include "Base.h"
|
||||
#include "Timestamp.h"
|
||||
#include "Log.h"
|
||||
#include "NetDb.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "RouterContext.h"
|
||||
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
RouterInfo::RouterInfo (): m_Buffer (nullptr)
|
||||
{
|
||||
m_Addresses = std::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_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
|
||||
ReadFromFile ();
|
||||
}
|
||||
@@ -28,6 +32,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_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
|
||||
memcpy (m_Buffer, buf, len);
|
||||
m_BufferLen = len;
|
||||
@@ -36,26 +41,42 @@ namespace data
|
||||
|
||||
RouterInfo::~RouterInfo ()
|
||||
{
|
||||
delete m_Buffer;
|
||||
delete[] m_Buffer;
|
||||
}
|
||||
|
||||
void RouterInfo::Update (const uint8_t * buf, int len)
|
||||
{
|
||||
if (!m_Buffer)
|
||||
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
|
||||
m_IsUpdated = true;
|
||||
m_IsUnreachable = false;
|
||||
m_SupportedTransports = 0;
|
||||
m_Caps = 0;
|
||||
m_Addresses.clear ();
|
||||
m_Properties.clear ();
|
||||
memcpy (m_Buffer, buf, len);
|
||||
m_BufferLen = len;
|
||||
ReadFromBuffer (true);
|
||||
// don't delete buffer until save to file
|
||||
// verify signature since we have indentity already
|
||||
int l = len - m_RouterIdentity->GetSignatureLen ();
|
||||
if (m_RouterIdentity->Verify (buf, l, buf + l))
|
||||
{
|
||||
// clean up
|
||||
m_IsUpdated = true;
|
||||
m_IsUnreachable = false;
|
||||
m_SupportedTransports = 0;
|
||||
m_Caps = 0;
|
||||
// don't clean up m_Addresses, it will be replaced in ReadFromStream
|
||||
m_Properties.clear ();
|
||||
// copy buffer
|
||||
if (!m_Buffer)
|
||||
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
|
||||
memcpy (m_Buffer, buf, len);
|
||||
m_BufferLen = len;
|
||||
// skip identity
|
||||
size_t identityLen = m_RouterIdentity->GetFullLen ();
|
||||
// read new RI
|
||||
std::stringstream str (std::string ((char *)m_Buffer + identityLen, m_BufferLen - identityLen));
|
||||
ReadFromStream (str);
|
||||
// don't delete buffer until saved to the file
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "RouterInfo: signature verification failed");
|
||||
m_IsUnreachable = true;
|
||||
}
|
||||
}
|
||||
|
||||
void RouterInfo::SetRouterIdentity (const IdentityEx& identity)
|
||||
void RouterInfo::SetRouterIdentity (std::shared_ptr<const IdentityEx> identity)
|
||||
{
|
||||
m_RouterIdentity = identity;
|
||||
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
@@ -63,14 +84,14 @@ namespace data
|
||||
|
||||
bool RouterInfo::LoadFile ()
|
||||
{
|
||||
std::ifstream s(m_FullPath.c_str (), std::ifstream::binary);
|
||||
std::ifstream s(m_FullPath, std::ifstream::binary);
|
||||
if (s.is_open ())
|
||||
{
|
||||
s.seekg (0,std::ios::end);
|
||||
m_BufferLen = s.tellg ();
|
||||
if (m_BufferLen < 40)
|
||||
if (m_BufferLen < 40 || m_BufferLen > MAX_RI_BUFFER_SIZE)
|
||||
{
|
||||
LogPrint(eLogError, "File", m_FullPath, " is malformed");
|
||||
LogPrint(eLogError, "RouterInfo: File", m_FullPath, " is malformed");
|
||||
return false;
|
||||
}
|
||||
s.seekg(0, std::ios::beg);
|
||||
@@ -80,7 +101,7 @@ namespace data
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "Can't open file ", m_FullPath);
|
||||
LogPrint (eLogError, "RouterInfo: Can't open file ", m_FullPath);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -90,23 +111,38 @@ namespace data
|
||||
{
|
||||
if (LoadFile ())
|
||||
ReadFromBuffer (false);
|
||||
else
|
||||
m_IsUnreachable = true;
|
||||
}
|
||||
|
||||
void RouterInfo::ReadFromBuffer (bool verifySignature)
|
||||
{
|
||||
size_t identityLen = m_RouterIdentity.FromBuffer (m_Buffer, m_BufferLen);
|
||||
m_RouterIdentity = std::make_shared<IdentityEx>(m_Buffer, m_BufferLen);
|
||||
size_t identityLen = m_RouterIdentity->GetFullLen ();
|
||||
if (identityLen >= m_BufferLen)
|
||||
{
|
||||
LogPrint (eLogError, "RouterInfo: identity length ", identityLen, " exceeds buffer size ", m_BufferLen);
|
||||
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
|
||||
int l = m_BufferLen - m_RouterIdentity.GetSignatureLen ();
|
||||
if (!m_RouterIdentity.Verify ((uint8_t *)m_Buffer, l, (uint8_t *)m_Buffer + l))
|
||||
int l = m_BufferLen - m_RouterIdentity->GetSignatureLen ();
|
||||
if (l < 0 || !m_RouterIdentity->Verify ((uint8_t *)m_Buffer, l, (uint8_t *)m_Buffer + l))
|
||||
{
|
||||
LogPrint (eLogError, "signature verification failed");
|
||||
LogPrint (eLogError, "RouterInfo: signature verification failed");
|
||||
m_IsUnreachable = true;
|
||||
}
|
||||
m_RouterIdentity.DropVerifier ();
|
||||
m_RouterIdentity->DropVerifier ();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,11 +151,13 @@ namespace data
|
||||
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp));
|
||||
m_Timestamp = be64toh (m_Timestamp);
|
||||
// read addresses
|
||||
auto addresses = std::make_shared<Addresses>();
|
||||
uint8_t numAddresses;
|
||||
s.read ((char *)&numAddresses, sizeof (numAddresses));
|
||||
s.read ((char *)&numAddresses, sizeof (numAddresses)); if (!s) return;
|
||||
bool introducers = false;
|
||||
for (int i = 0; i < numAddresses; i++)
|
||||
{
|
||||
uint8_t supportedTransports = 0;
|
||||
bool isValidAddress = true;
|
||||
Address address;
|
||||
s.read ((char *)&address.cost, sizeof (address.cost));
|
||||
@@ -135,7 +173,7 @@ namespace data
|
||||
address.port = 0;
|
||||
address.mtu = 0;
|
||||
uint16_t size, r = 0;
|
||||
s.read ((char *)&size, sizeof (size));
|
||||
s.read ((char *)&size, sizeof (size)); if (!s) return;
|
||||
size = be16toh (size);
|
||||
while (r < size)
|
||||
{
|
||||
@@ -152,23 +190,22 @@ namespace data
|
||||
{
|
||||
if (address.transportStyle == eTransportNTCP)
|
||||
{
|
||||
m_SupportedTransports |= eNTCPV4; // TODO:
|
||||
supportedTransports |= eNTCPV4; // TODO:
|
||||
address.addressString = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: resolve address for SSU
|
||||
LogPrint (eLogWarning, "Unexpected SSU address ", value);
|
||||
isValidAddress = false;
|
||||
supportedTransports |= eSSUV4; // TODO:
|
||||
address.addressString = value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// add supported protocol
|
||||
if (address.host.is_v4 ())
|
||||
m_SupportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4;
|
||||
supportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4;
|
||||
else
|
||||
m_SupportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6;
|
||||
supportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6;
|
||||
}
|
||||
}
|
||||
else if (!strcmp (key, "port"))
|
||||
@@ -201,17 +238,22 @@ namespace data
|
||||
else if (!strcmp (key, "ikey"))
|
||||
Base64ToByteStream (value, strlen (value), introducer.iKey, 32);
|
||||
}
|
||||
if (!s) return;
|
||||
}
|
||||
if (isValidAddress)
|
||||
m_Addresses.push_back(address);
|
||||
{
|
||||
addresses->push_back(std::make_shared<Address>(address));
|
||||
m_SupportedTransports |= supportedTransports;
|
||||
}
|
||||
}
|
||||
m_Addresses = addresses;
|
||||
// read peers
|
||||
uint8_t numPeers;
|
||||
s.read ((char *)&numPeers, sizeof (numPeers));
|
||||
s.read ((char *)&numPeers, sizeof (numPeers)); if (!s) return;
|
||||
s.seekg (numPeers*32, std::ios_base::cur); // TODO: read peers
|
||||
// read properties
|
||||
uint16_t size, r = 0;
|
||||
s.read ((char *)&size, sizeof (size));
|
||||
s.read ((char *)&size, sizeof (size)); if (!s) return;
|
||||
size = be16toh (size);
|
||||
while (r < size)
|
||||
{
|
||||
@@ -231,11 +273,37 @@ namespace data
|
||||
// extract caps
|
||||
if (!strcmp (key, "caps"))
|
||||
ExtractCaps (value);
|
||||
// check netId
|
||||
else if (!strcmp (key, ROUTER_INFO_PROPERTY_NETID) && atoi (value) != I2PD_NET_ID)
|
||||
{
|
||||
LogPrint (eLogError, "Unexpected ", ROUTER_INFO_PROPERTY_NETID, "=", value);
|
||||
m_IsUnreachable = true;
|
||||
}
|
||||
// family
|
||||
else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY))
|
||||
{
|
||||
m_Family = value;
|
||||
boost::to_lower (m_Family);
|
||||
}
|
||||
else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY_SIG))
|
||||
{
|
||||
if (!netdb.GetFamilies ().VerifyFamily (m_Family, GetIdentHash (), value))
|
||||
{
|
||||
LogPrint (eLogWarning, "RouterInfo: family signature verification failed");
|
||||
m_Family.clear ();
|
||||
}
|
||||
}
|
||||
|
||||
if (!s) return;
|
||||
}
|
||||
|
||||
if (!m_SupportedTransports || !m_Addresses.size() || (UsesIntroducer () && !introducers))
|
||||
if (!m_SupportedTransports || !m_Addresses->size() || (UsesIntroducer () && !introducers))
|
||||
SetUnreachable (true);
|
||||
}
|
||||
}
|
||||
|
||||
bool RouterInfo::IsFamily(const std::string & fam) const {
|
||||
return m_Family == fam;
|
||||
}
|
||||
|
||||
void RouterInfo::ExtractCaps (const char * value)
|
||||
{
|
||||
@@ -252,6 +320,10 @@ namespace data
|
||||
case CAPS_FLAG_HIGH_BANDWIDTH3:
|
||||
m_Caps |= Caps::eHighBandwidth;
|
||||
break;
|
||||
case CAPS_FLAG_EXTRA_BANDWIDTH1:
|
||||
case CAPS_FLAG_EXTRA_BANDWIDTH2:
|
||||
m_Caps |= Caps::eExtraBandwidth;
|
||||
break;
|
||||
case CAPS_FLAG_HIDDEN:
|
||||
m_Caps |= Caps::eHidden;
|
||||
break;
|
||||
@@ -276,32 +348,40 @@ namespace data
|
||||
void RouterInfo::UpdateCapsProperty ()
|
||||
{
|
||||
std::string caps;
|
||||
if (m_Caps & eFloodfill)
|
||||
{
|
||||
caps += CAPS_FLAG_HIGH_BANDWIDTH3; // highest bandwidth
|
||||
if (m_Caps & eFloodfill) {
|
||||
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) {
|
||||
caps += CAPS_FLAG_HIGH_BANDWIDTH3; // 'O'
|
||||
} else {
|
||||
caps += CAPS_FLAG_LOW_BANDWIDTH2; // 'L'
|
||||
}
|
||||
}
|
||||
else
|
||||
caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH1 : CAPS_FLAG_LOW_BANDWIDTH2; // bandwidth
|
||||
if (m_Caps & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden
|
||||
if (m_Caps & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable
|
||||
if (m_Caps & eUnreachable) caps += CAPS_FLAG_UNREACHABLE; // unreachable
|
||||
|
||||
SetProperty ("caps", caps.c_str ());
|
||||
SetProperty ("caps", caps);
|
||||
}
|
||||
|
||||
void RouterInfo::WriteToStream (std::ostream& s)
|
||||
void RouterInfo::WriteToStream (std::ostream& s) const
|
||||
{
|
||||
uint64_t ts = htobe64 (m_Timestamp);
|
||||
s.write ((char *)&ts, sizeof (ts));
|
||||
s.write ((const char *)&ts, sizeof (ts));
|
||||
|
||||
// addresses
|
||||
uint8_t numAddresses = m_Addresses.size ();
|
||||
uint8_t numAddresses = m_Addresses->size ();
|
||||
s.write ((char *)&numAddresses, sizeof (numAddresses));
|
||||
for (auto& address : m_Addresses)
|
||||
for (const auto& addr_ptr : *m_Addresses)
|
||||
{
|
||||
s.write ((char *)&address.cost, sizeof (address.cost));
|
||||
s.write ((char *)&address.date, sizeof (address.date));
|
||||
const Address& address = *addr_ptr;
|
||||
s.write ((const char *)&address.cost, sizeof (address.cost));
|
||||
s.write ((const char *)&address.date, sizeof (address.date));
|
||||
std::stringstream properties;
|
||||
if (address.transportStyle == eTransportNTCP)
|
||||
WriteString ("NTCP", s);
|
||||
@@ -330,7 +410,7 @@ namespace data
|
||||
if (address.introducers.size () > 0)
|
||||
{
|
||||
int i = 0;
|
||||
for (auto introducer: address.introducers)
|
||||
for (const auto& introducer: address.introducers)
|
||||
{
|
||||
WriteString ("ihost" + boost::lexical_cast<std::string>(i), properties);
|
||||
properties << '=';
|
||||
@@ -339,7 +419,7 @@ namespace data
|
||||
i++;
|
||||
}
|
||||
i = 0;
|
||||
for (auto introducer: address.introducers)
|
||||
for (const auto& introducer: address.introducers)
|
||||
{
|
||||
WriteString ("ikey" + boost::lexical_cast<std::string>(i), properties);
|
||||
properties << '=';
|
||||
@@ -351,7 +431,7 @@ namespace data
|
||||
i++;
|
||||
}
|
||||
i = 0;
|
||||
for (auto introducer: address.introducers)
|
||||
for (const auto& introducer: address.introducers)
|
||||
{
|
||||
WriteString ("iport" + boost::lexical_cast<std::string>(i), properties);
|
||||
properties << '=';
|
||||
@@ -360,7 +440,7 @@ namespace data
|
||||
i++;
|
||||
}
|
||||
i = 0;
|
||||
for (auto introducer: address.introducers)
|
||||
for (const auto& introducer: address.introducers)
|
||||
{
|
||||
WriteString ("itag" + boost::lexical_cast<std::string>(i), properties);
|
||||
properties << '=';
|
||||
@@ -402,7 +482,7 @@ namespace data
|
||||
|
||||
// properties
|
||||
std::stringstream properties;
|
||||
for (auto& p : m_Properties)
|
||||
for (const auto& p : m_Properties)
|
||||
{
|
||||
WriteString (p.first, properties);
|
||||
properties << '=';
|
||||
@@ -414,12 +494,20 @@ namespace data
|
||||
s.write (properties.str ().c_str (), properties.str ().size ());
|
||||
}
|
||||
|
||||
bool RouterInfo::IsNewer (const uint8_t * buf, size_t len) const
|
||||
{
|
||||
if (!m_RouterIdentity) return false;
|
||||
size_t size = m_RouterIdentity->GetFullLen ();
|
||||
if (size + 8 > len) return false;
|
||||
return bufbe64toh (buf + size) > m_Timestamp;
|
||||
}
|
||||
|
||||
const uint8_t * RouterInfo::LoadBuffer ()
|
||||
{
|
||||
if (!m_Buffer)
|
||||
{
|
||||
if (LoadFile ())
|
||||
LogPrint ("Buffer for ", GetIdentHashAbbreviation (), " loaded from file");
|
||||
LogPrint (eLogDebug, "RouterInfo: Buffer for ", GetIdentHashAbbreviation (GetIdentHash ()), " loaded from file");
|
||||
}
|
||||
return m_Buffer;
|
||||
}
|
||||
@@ -429,7 +517,7 @@ namespace data
|
||||
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); // refresh timstamp
|
||||
std::stringstream s;
|
||||
uint8_t ident[1024];
|
||||
auto identLen = privateKeys.GetPublic ().ToBuffer (ident, 1024);
|
||||
auto identLen = privateKeys.GetPublic ()->ToBuffer (ident, 1024);
|
||||
s.write ((char *)ident, identLen);
|
||||
WriteToStream (s);
|
||||
m_BufferLen = s.str ().size ();
|
||||
@@ -438,19 +526,23 @@ namespace data
|
||||
memcpy (m_Buffer, s.str ().c_str (), m_BufferLen);
|
||||
// signature
|
||||
privateKeys.Sign ((uint8_t *)m_Buffer, m_BufferLen, (uint8_t *)m_Buffer + m_BufferLen);
|
||||
m_BufferLen += privateKeys.GetPublic ().GetSignatureLen ();
|
||||
m_BufferLen += privateKeys.GetPublic ()->GetSignatureLen ();
|
||||
}
|
||||
|
||||
void RouterInfo::SaveToFile (const std::string& fullPath)
|
||||
bool RouterInfo::SaveToFile (const std::string& fullPath)
|
||||
{
|
||||
m_FullPath = fullPath;
|
||||
if (m_Buffer)
|
||||
{
|
||||
std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out);
|
||||
f.write ((char *)m_Buffer, m_BufferLen);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Can't save to file");
|
||||
if (!m_Buffer) {
|
||||
LogPrint (eLogError, "RouterInfo: Can't save, m_Buffer == NULL");
|
||||
return false;
|
||||
}
|
||||
std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out);
|
||||
if (!f.is_open ()) {
|
||||
LogPrint(eLogError, "RouterInfo: Can't save to ", fullPath);
|
||||
return false;
|
||||
}
|
||||
f.write ((char *)m_Buffer, m_BufferLen);
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t RouterInfo::ReadString (char * str, std::istream& s)
|
||||
@@ -471,47 +563,47 @@ namespace data
|
||||
|
||||
void RouterInfo::AddNTCPAddress (const char * host, int port)
|
||||
{
|
||||
Address addr;
|
||||
addr.host = boost::asio::ip::address::from_string (host);
|
||||
addr.port = port;
|
||||
addr.transportStyle = eTransportNTCP;
|
||||
addr.cost = 2;
|
||||
addr.date = 0;
|
||||
addr.mtu = 0;
|
||||
m_Addresses.push_back(addr);
|
||||
m_SupportedTransports |= addr.host.is_v6 () ? eNTCPV6 : eNTCPV4;
|
||||
auto addr = std::make_shared<Address>();
|
||||
addr->host = boost::asio::ip::address::from_string (host);
|
||||
addr->port = port;
|
||||
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;
|
||||
m_Addresses->push_back(std::move(addr));
|
||||
}
|
||||
|
||||
void RouterInfo::AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu)
|
||||
{
|
||||
Address addr;
|
||||
addr.host = boost::asio::ip::address::from_string (host);
|
||||
addr.port = port;
|
||||
addr.transportStyle = eTransportSSU;
|
||||
addr.cost = 10; // NTCP should have priority over SSU
|
||||
addr.date = 0;
|
||||
addr.mtu = mtu;
|
||||
memcpy (addr.key, key, 32);
|
||||
m_Addresses.push_back(addr);
|
||||
m_SupportedTransports |= addr.host.is_v6 () ? eNTCPV6 : eSSUV4;
|
||||
m_Caps |= eSSUTesting;
|
||||
m_Caps |= eSSUIntroducer;
|
||||
auto addr = std::make_shared<Address>();
|
||||
addr->host = boost::asio::ip::address::from_string (host);
|
||||
addr->port = port;
|
||||
addr->transportStyle = eTransportSSU;
|
||||
addr->cost = 10; // NTCP should have priority over SSU
|
||||
addr->date = 0;
|
||||
addr->mtu = mtu;
|
||||
memcpy (addr->key, key, 32);
|
||||
for (const auto& it: *m_Addresses) // don't insert same address twice
|
||||
if (*it == *addr) return;
|
||||
m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4;
|
||||
m_Addresses->push_back(std::move(addr));
|
||||
|
||||
m_Caps |= eSSUTesting;
|
||||
m_Caps |= eSSUIntroducer;
|
||||
}
|
||||
|
||||
bool RouterInfo::AddIntroducer (const Address * address, uint32_t tag)
|
||||
bool RouterInfo::AddIntroducer (const Introducer& introducer)
|
||||
{
|
||||
for (auto& addr : m_Addresses)
|
||||
for (auto& addr : *m_Addresses)
|
||||
{
|
||||
if (addr.transportStyle == eTransportSSU && addr.host.is_v4 ())
|
||||
if (addr->transportStyle == eTransportSSU && addr->host.is_v4 ())
|
||||
{
|
||||
for (auto intro: addr.introducers)
|
||||
if (intro.iTag == tag) return false; // already presented
|
||||
Introducer x;
|
||||
x.iHost = address->host;
|
||||
x.iPort = address->port;
|
||||
x.iTag = tag;
|
||||
memcpy (x.iKey, address->key, 32); // TODO: replace to Tag<32>
|
||||
addr.introducers.push_back (x);
|
||||
for (auto& intro: addr->introducers)
|
||||
if (intro.iTag == introducer.iTag) return false; // already presented
|
||||
addr->introducers.push_back (introducer);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -520,16 +612,16 @@ namespace data
|
||||
|
||||
bool RouterInfo::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e)
|
||||
{
|
||||
for (auto& addr : m_Addresses)
|
||||
for (auto& addr: *m_Addresses)
|
||||
{
|
||||
if (addr.transportStyle == eTransportSSU && addr.host.is_v4 ())
|
||||
if (addr->transportStyle == eTransportSSU && addr->host.is_v4 ())
|
||||
{
|
||||
for (std::vector<Introducer>::iterator it = addr.introducers.begin (); it != addr.introducers.end (); it++)
|
||||
for (auto it = addr->introducers.begin (); it != addr->introducers.end (); ++it)
|
||||
if ( boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e)
|
||||
{
|
||||
addr.introducers.erase (it);
|
||||
addr->introducers.erase (it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -548,24 +640,24 @@ namespace data
|
||||
ExtractCaps (caps);
|
||||
}
|
||||
|
||||
void RouterInfo::SetProperty (const char * key, const char * value)
|
||||
void RouterInfo::SetProperty (const std::string& key, const std::string& value)
|
||||
{
|
||||
m_Properties[key] = value;
|
||||
}
|
||||
|
||||
const char * RouterInfo::GetProperty (const char * key) const
|
||||
void RouterInfo::DeleteProperty (const std::string& key)
|
||||
{
|
||||
m_Properties.erase (key);
|
||||
}
|
||||
|
||||
std::string RouterInfo::GetProperty (const std::string& key) const
|
||||
{
|
||||
auto it = m_Properties.find (key);
|
||||
if (it != m_Properties.end ())
|
||||
return it->second.c_str ();
|
||||
return 0;
|
||||
return it->second;
|
||||
return "";
|
||||
}
|
||||
|
||||
bool RouterInfo::IsFloodfill () const
|
||||
{
|
||||
return m_Caps & Caps::eFloodfill;
|
||||
}
|
||||
|
||||
|
||||
bool RouterInfo::IsNTCP (bool v4only) const
|
||||
{
|
||||
if (v4only)
|
||||
@@ -587,73 +679,96 @@ namespace data
|
||||
return m_SupportedTransports & (eNTCPV6 | eSSUV6);
|
||||
}
|
||||
|
||||
bool RouterInfo::IsV4 () const
|
||||
{
|
||||
return m_SupportedTransports & (eNTCPV4 | eSSUV4);
|
||||
}
|
||||
|
||||
void RouterInfo::EnableV6 ()
|
||||
{
|
||||
if (!IsV6 ())
|
||||
m_SupportedTransports |= eNTCPV6 | eSSUV6;
|
||||
}
|
||||
|
||||
|
||||
void RouterInfo::EnableV4 ()
|
||||
{
|
||||
if (!IsV4 ())
|
||||
m_SupportedTransports |= eNTCPV4 | eSSUV4;
|
||||
}
|
||||
|
||||
|
||||
void RouterInfo::DisableV6 ()
|
||||
{
|
||||
if (IsV6 ())
|
||||
{
|
||||
// NTCP
|
||||
m_SupportedTransports &= ~eNTCPV6;
|
||||
for (size_t i = 0; i < m_Addresses.size (); i++)
|
||||
m_SupportedTransports &= ~(eNTCPV6 | eSSUV6);
|
||||
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
|
||||
{
|
||||
if (m_Addresses[i].transportStyle == i2p::data::RouterInfo::eTransportNTCP &&
|
||||
m_Addresses[i].host.is_v6 ())
|
||||
{
|
||||
m_Addresses.erase (m_Addresses.begin () + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// SSU
|
||||
m_SupportedTransports &= ~eSSUV6;
|
||||
for (size_t i = 0; i < m_Addresses.size (); i++)
|
||||
{
|
||||
if (m_Addresses[i].transportStyle == i2p::data::RouterInfo::eTransportSSU &&
|
||||
m_Addresses[i].host.is_v6 ())
|
||||
{
|
||||
m_Addresses.erase (m_Addresses.begin () + i);
|
||||
break;
|
||||
}
|
||||
auto addr = *it;
|
||||
if (addr->host.is_v6 ())
|
||||
it = m_Addresses->erase (it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RouterInfo::DisableV4 ()
|
||||
{
|
||||
if (IsV4 ())
|
||||
{
|
||||
m_SupportedTransports &= ~(eNTCPV4 | eSSUV4);
|
||||
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
|
||||
{
|
||||
auto addr = *it;
|
||||
if (addr->host.is_v4 ())
|
||||
it = m_Addresses->erase (it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool RouterInfo::UsesIntroducer () const
|
||||
{
|
||||
return m_Caps & Caps::eUnreachable; // non-reachable
|
||||
}
|
||||
|
||||
const RouterInfo::Address * RouterInfo::GetNTCPAddress (bool v4only) const
|
||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetNTCPAddress (bool v4only) const
|
||||
{
|
||||
return GetAddress (eTransportNTCP, v4only);
|
||||
}
|
||||
|
||||
const RouterInfo::Address * RouterInfo::GetSSUAddress (bool v4only) const
|
||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSUAddress (bool v4only) const
|
||||
{
|
||||
return GetAddress (eTransportSSU, v4only);
|
||||
}
|
||||
|
||||
const RouterInfo::Address * RouterInfo::GetSSUV6Address () const
|
||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSUV6Address () const
|
||||
{
|
||||
return GetAddress (eTransportSSU, false, true);
|
||||
}
|
||||
|
||||
const RouterInfo::Address * RouterInfo::GetAddress (TransportStyle s, bool v4only, bool v6only) const
|
||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetAddress (TransportStyle s, bool v4only, bool v6only) const
|
||||
{
|
||||
for (auto& address : m_Addresses)
|
||||
auto addresses = m_Addresses;
|
||||
for (const auto& address : *addresses)
|
||||
{
|
||||
if (address.transportStyle == s)
|
||||
if (address->transportStyle == s)
|
||||
{
|
||||
if ((!v4only || address.host.is_v4 ()) && (!v6only || address.host.is_v6 ()))
|
||||
return &address;
|
||||
if ((!v4only || address->host.is_v4 ()) && (!v6only || address->host.is_v6 ()))
|
||||
return address;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<RouterProfile> RouterInfo::GetProfile () const
|
||||
{
|
||||
if (!m_Profile)
|
||||
m_Profile = GetRouterProfile (GetIdentHash ());
|
||||
return m_Profile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
120
RouterInfo.h
120
RouterInfo.h
@@ -5,24 +5,35 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iostream>
|
||||
#include <boost/asio.hpp>
|
||||
#include "Identity.h"
|
||||
#include "Profiling.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
const char ROUTER_INFO_PROPERTY_LEASESETS[] = "netdb.knownLeaseSets";
|
||||
const char ROUTER_INFO_PROPERTY_ROUTERS[] = "netdb.knownRouters";
|
||||
const char ROUTER_INFO_PROPERTY_NETID[] = "netId";
|
||||
const char ROUTER_INFO_PROPERTY_FAMILY[] = "family";
|
||||
const char ROUTER_INFO_PROPERTY_FAMILY_SIG[] = "family.sig";
|
||||
|
||||
const char CAPS_FLAG_FLOODFILL = 'f';
|
||||
const char CAPS_FLAG_HIDDEN = 'H';
|
||||
const char CAPS_FLAG_REACHABLE = 'R';
|
||||
const char CAPS_FLAG_UNREACHABLE = 'U';
|
||||
const char CAPS_FLAG_LOW_BANDWIDTH1 = 'K';
|
||||
const char CAPS_FLAG_LOW_BANDWIDTH2 = 'L';
|
||||
const char CAPS_FLAG_HIGH_BANDWIDTH1 = 'M';
|
||||
const char CAPS_FLAG_HIGH_BANDWIDTH2 = 'N';
|
||||
const char CAPS_FLAG_HIGH_BANDWIDTH3 = 'O';
|
||||
|
||||
/* bandwidth flags */
|
||||
const char CAPS_FLAG_LOW_BANDWIDTH1 = 'K'; /* < 12 KBps */
|
||||
const char CAPS_FLAG_LOW_BANDWIDTH2 = 'L'; /* 12-48 KBps */
|
||||
const char CAPS_FLAG_HIGH_BANDWIDTH1 = 'M'; /* 48-64 KBps */
|
||||
const char CAPS_FLAG_HIGH_BANDWIDTH2 = 'N'; /* 64-128 KBps */
|
||||
const char CAPS_FLAG_HIGH_BANDWIDTH3 = 'O'; /* 128-256 KBps */
|
||||
const char CAPS_FLAG_EXTRA_BANDWIDTH1 = 'P'; /* 256-2000 KBps */
|
||||
const char CAPS_FLAG_EXTRA_BANDWIDTH2 = 'X'; /* > 2000 KBps */
|
||||
|
||||
const char CAPS_FLAG_SSU_TESTING = 'B';
|
||||
const char CAPS_FLAG_SSU_INTRODUCER = 'C';
|
||||
|
||||
@@ -43,11 +54,12 @@ namespace data
|
||||
{
|
||||
eFloodfill = 0x01,
|
||||
eHighBandwidth = 0x02,
|
||||
eReachable = 0x04,
|
||||
eSSUTesting = 0x08,
|
||||
eSSUIntroducer = 0x10,
|
||||
eHidden = 0x20,
|
||||
eUnreachable = 0x40
|
||||
eExtraBandwidth = 0x04,
|
||||
eReachable = 0x08,
|
||||
eSSUTesting = 0x10,
|
||||
eSSUIntroducer = 0x20,
|
||||
eHidden = 0x40,
|
||||
eUnreachable = 0x80
|
||||
};
|
||||
|
||||
enum TransportStyle
|
||||
@@ -57,11 +69,12 @@ namespace data
|
||||
eTransportSSU
|
||||
};
|
||||
|
||||
typedef Tag<32> IntroKey; // should be castable to MacKey and AESKey
|
||||
struct Introducer
|
||||
{
|
||||
boost::asio::ip::address iHost;
|
||||
int iPort;
|
||||
Tag<32> iKey;
|
||||
IntroKey iKey;
|
||||
uint32_t iTag;
|
||||
};
|
||||
|
||||
@@ -74,7 +87,7 @@ namespace data
|
||||
uint64_t date;
|
||||
uint8_t cost;
|
||||
// SSU only
|
||||
Tag<32> key; // intro key for SSU
|
||||
IntroKey key; // intro key for SSU
|
||||
std::vector<Introducer> introducers;
|
||||
|
||||
bool IsCompatible (const boost::asio::ip::address& other) const
|
||||
@@ -82,43 +95,61 @@ namespace data
|
||||
return (host.is_v4 () && other.is_v4 ()) ||
|
||||
(host.is_v6 () && other.is_v6 ());
|
||||
}
|
||||
|
||||
bool operator==(const Address& other) const
|
||||
{
|
||||
return transportStyle == other.transportStyle && host == other.host && port == other.port;
|
||||
}
|
||||
|
||||
bool operator!=(const Address& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::list<std::shared_ptr<Address> > Addresses;
|
||||
|
||||
RouterInfo ();
|
||||
RouterInfo (const std::string& fullPath);
|
||||
RouterInfo (): m_Buffer (nullptr) { };
|
||||
RouterInfo (const RouterInfo& ) = default;
|
||||
RouterInfo& operator=(const RouterInfo& ) = default;
|
||||
RouterInfo (const uint8_t * buf, int len);
|
||||
~RouterInfo ();
|
||||
|
||||
const IdentityEx& GetRouterIdentity () const { return m_RouterIdentity; };
|
||||
void SetRouterIdentity (const IdentityEx& identity);
|
||||
std::shared_ptr<const IdentityEx> GetRouterIdentity () const { return m_RouterIdentity; };
|
||||
void SetRouterIdentity (std::shared_ptr<const IdentityEx> identity);
|
||||
std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); };
|
||||
std::string GetIdentHashAbbreviation () const { return GetIdentHash ().ToBase64 ().substr (0, 4); };
|
||||
uint64_t GetTimestamp () const { return m_Timestamp; };
|
||||
std::vector<Address>& GetAddresses () { return m_Addresses; };
|
||||
const Address * GetNTCPAddress (bool v4only = true) const;
|
||||
const Address * GetSSUAddress (bool v4only = true) const;
|
||||
const Address * GetSSUV6Address () const;
|
||||
Addresses& GetAddresses () { return *m_Addresses; }; // should be called for local RI only, otherwise must return shared_ptr
|
||||
std::shared_ptr<const Address> GetNTCPAddress (bool v4only = true) const;
|
||||
std::shared_ptr<const Address> GetSSUAddress (bool v4only = true) const;
|
||||
std::shared_ptr<const Address> GetSSUV6Address () const;
|
||||
|
||||
void AddNTCPAddress (const char * host, int port);
|
||||
void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0);
|
||||
bool AddIntroducer (const Address * address, uint32_t tag);
|
||||
bool AddIntroducer (const Introducer& introducer);
|
||||
bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
|
||||
void SetProperty (const char * key, const char * value);
|
||||
const char * GetProperty (const char * key) const;
|
||||
bool IsFloodfill () const;
|
||||
void SetProperty (const std::string& key, const std::string& value); // called from RouterContext only
|
||||
void DeleteProperty (const std::string& key); // called from RouterContext only
|
||||
std::string GetProperty (const std::string& key) const; // called from RouterContext only
|
||||
void ClearProperties () { m_Properties.clear (); };
|
||||
bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; };
|
||||
bool IsReachable () const { return m_Caps & Caps::eReachable; };
|
||||
bool IsNTCP (bool v4only = true) const;
|
||||
bool IsSSU (bool v4only = true) const;
|
||||
bool IsV6 () const;
|
||||
bool IsV4 () const;
|
||||
void EnableV6 ();
|
||||
void DisableV6 ();
|
||||
void EnableV4 ();
|
||||
void DisableV4 ();
|
||||
bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; };
|
||||
bool UsesIntroducer () const;
|
||||
bool IsIntroducer () const { return m_Caps & eSSUIntroducer; };
|
||||
bool IsPeerTesting () const { return m_Caps & eSSUTesting; };
|
||||
bool IsHidden () const { return m_Caps & eHidden; };
|
||||
|
||||
bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; };
|
||||
bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; };
|
||||
|
||||
uint8_t GetCaps () const { return m_Caps; };
|
||||
void SetCaps (uint8_t caps);
|
||||
void SetCaps (const char * caps);
|
||||
@@ -133,41 +164,48 @@ namespace data
|
||||
|
||||
bool IsUpdated () const { return m_IsUpdated; };
|
||||
void SetUpdated (bool updated) { m_IsUpdated = updated; };
|
||||
void SaveToFile (const std::string& fullPath);
|
||||
bool SaveToFile (const std::string& fullPath);
|
||||
|
||||
void Update (const uint8_t * buf, int len);
|
||||
void DeleteBuffer () { delete m_Buffer; m_Buffer = nullptr; };
|
||||
std::shared_ptr<RouterProfile> GetProfile () const;
|
||||
void SaveProfile () { if (m_Profile) m_Profile->Save (); };
|
||||
|
||||
void Update (const uint8_t * buf, int len);
|
||||
void DeleteBuffer () { delete[] m_Buffer; m_Buffer = nullptr; };
|
||||
bool IsNewer (const uint8_t * buf, size_t len) const;
|
||||
|
||||
/** return true if we are in a router family and the signature is valid */
|
||||
bool IsFamily(const std::string & fam) const;
|
||||
|
||||
// implements RoutingDestination
|
||||
const IdentHash& GetIdentHash () const { return m_RouterIdentity.GetIdentHash (); };
|
||||
const uint8_t * GetEncryptionPublicKey () const { return m_RouterIdentity.GetStandardIdentity ().publicKey; };
|
||||
const IdentHash& GetIdentHash () const { return m_RouterIdentity->GetIdentHash (); };
|
||||
const uint8_t * GetEncryptionPublicKey () const { return m_RouterIdentity->GetStandardIdentity ().publicKey; };
|
||||
bool IsDestination () const { return false; };
|
||||
|
||||
|
||||
private:
|
||||
|
||||
bool LoadFile ();
|
||||
void ReadFromFile ();
|
||||
void ReadFromStream (std::istream& s);
|
||||
void ReadFromBuffer (bool verifySignature);
|
||||
void WriteToStream (std::ostream& s);
|
||||
size_t ReadString (char * str, std::istream& s);
|
||||
void WriteString (const std::string& str, std::ostream& s);
|
||||
void WriteToStream (std::ostream& s) const;
|
||||
static size_t ReadString (char* str, std::istream& s);
|
||||
static void WriteString (const std::string& str, std::ostream& s);
|
||||
void ExtractCaps (const char * value);
|
||||
const Address * GetAddress (TransportStyle s, bool v4only, bool v6only = false) const;
|
||||
std::shared_ptr<const Address> GetAddress (TransportStyle s, bool v4only, bool v6only = false) const;
|
||||
void UpdateCapsProperty ();
|
||||
|
||||
private:
|
||||
|
||||
std::string m_FullPath;
|
||||
IdentityEx m_RouterIdentity;
|
||||
std::string m_FullPath, m_Family;
|
||||
std::shared_ptr<const IdentityEx> m_RouterIdentity;
|
||||
uint8_t * m_Buffer;
|
||||
int m_BufferLen;
|
||||
size_t m_BufferLen;
|
||||
uint64_t m_Timestamp;
|
||||
std::vector<Address> m_Addresses;
|
||||
std::shared_ptr<Addresses> m_Addresses;
|
||||
std::map<std::string, std::string> m_Properties;
|
||||
bool m_IsUpdated, m_IsUnreachable;
|
||||
uint8_t m_SupportedTransports, m_Caps;
|
||||
mutable std::shared_ptr<RouterProfile> m_Profile;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
350
SAM.cpp
350
SAM.cpp
@@ -4,7 +4,7 @@
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "base64.h"
|
||||
#include "Base.h"
|
||||
#include "Identity.h"
|
||||
#include "Log.h"
|
||||
#include "Destination.h"
|
||||
@@ -17,8 +17,8 @@ namespace client
|
||||
{
|
||||
SAMSocket::SAMSocket (SAMBridge& owner):
|
||||
m_Owner (owner), m_Socket (m_Owner.GetService ()), m_Timer (m_Owner.GetService ()),
|
||||
m_SocketType (eSAMSocketTypeUnknown), m_IsSilent (false), m_Stream (nullptr),
|
||||
m_Session (nullptr)
|
||||
m_BufferOffset (0), m_SocketType (eSAMSocketTypeUnknown), m_IsSilent (false),
|
||||
m_Stream (nullptr), m_Session (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -47,16 +47,17 @@ namespace client
|
||||
break;
|
||||
case eSAMSocketTypeStream:
|
||||
{
|
||||
if (m_Session)
|
||||
m_Session->sockets.remove (shared_from_this ());
|
||||
if (m_Session)
|
||||
m_Session->DelSocket (shared_from_this ());
|
||||
break;
|
||||
}
|
||||
case eSAMSocketTypeAcceptor:
|
||||
{
|
||||
if (m_Session)
|
||||
{
|
||||
m_Session->sockets.remove (shared_from_this ());
|
||||
m_Session->localDestination->StopAcceptingStreams ();
|
||||
{
|
||||
m_Session->DelSocket (shared_from_this ());
|
||||
if (m_Session->localDestination)
|
||||
m_Session->localDestination->StopAcceptingStreams ();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -64,7 +65,8 @@ namespace client
|
||||
;
|
||||
}
|
||||
m_SocketType = eSAMSocketTypeTerminated;
|
||||
m_Socket.close ();
|
||||
if (m_Socket.is_open()) m_Socket.close ();
|
||||
m_Session = nullptr;
|
||||
}
|
||||
|
||||
void SAMSocket::ReceiveHandshake ()
|
||||
@@ -78,14 +80,17 @@ namespace client
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("SAM handshake read error: ", ecode.message ());
|
||||
LogPrint (eLogError, "SAM: handshake read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Buffer[bytes_transferred] = 0;
|
||||
LogPrint ("SAM handshake ", m_Buffer);
|
||||
char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred);
|
||||
if (eol)
|
||||
*eol = 0;
|
||||
LogPrint (eLogDebug, "SAM: handshake ", m_Buffer);
|
||||
char * separator = strchr (m_Buffer, ' ');
|
||||
if (separator)
|
||||
{
|
||||
@@ -102,7 +107,7 @@ namespace client
|
||||
{
|
||||
separator++;
|
||||
std::map<std::string, std::string> params;
|
||||
ExtractParams (separator, bytes_transferred - (separator - m_Buffer), params);
|
||||
ExtractParams (separator, params);
|
||||
auto it = params.find (SAM_PARAM_MAX);
|
||||
// TODO: check MIN as well
|
||||
if (it != params.end ())
|
||||
@@ -124,7 +129,7 @@ namespace client
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("SAM handshake mismatch");
|
||||
LogPrint (eLogError, "SAM: handshake mismatch");
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
@@ -134,7 +139,7 @@ namespace client
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("SAM handshake reply send error: ", ecode.message ());
|
||||
LogPrint (eLogError, "SAM: handshake reply send error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
@@ -165,7 +170,7 @@ namespace client
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("SAM reply send error: ", ecode.message ());
|
||||
LogPrint (eLogError, "SAM: reply send error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
@@ -182,14 +187,18 @@ namespace client
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("SAM read error: ", ecode.message ());
|
||||
LogPrint (eLogError, "SAM: read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else if (m_SocketType == eSAMSocketTypeStream)
|
||||
HandleReceived (ecode, bytes_transferred);
|
||||
else
|
||||
{
|
||||
bytes_transferred += m_BufferOffset;
|
||||
m_BufferOffset = 0;
|
||||
m_Buffer[bytes_transferred] = 0;
|
||||
char * eol = strchr (m_Buffer, '\n');
|
||||
char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred);
|
||||
if (eol)
|
||||
{
|
||||
*eol = 0;
|
||||
@@ -212,31 +221,52 @@ namespace client
|
||||
ProcessDestGenerate ();
|
||||
else if (!strcmp (m_Buffer, SAM_NAMING_LOOKUP))
|
||||
ProcessNamingLookup (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
|
||||
else if (!strcmp (m_Buffer, SAM_DATAGRAM_SEND))
|
||||
{
|
||||
size_t len = bytes_transferred - (separator - m_Buffer) - 1;
|
||||
size_t processed = ProcessDatagramSend (separator + 1, len, eol + 1);
|
||||
if (processed < len)
|
||||
{
|
||||
m_BufferOffset = len - processed;
|
||||
if (processed > 0)
|
||||
memmove (m_Buffer, separator + 1 + processed, m_BufferOffset);
|
||||
else
|
||||
{
|
||||
// restore string back
|
||||
*separator = ' ';
|
||||
*eol = '\n';
|
||||
}
|
||||
}
|
||||
// since it's SAM v1 reply is not expected
|
||||
Receive ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("SAM unexpected message ", m_Buffer);
|
||||
LogPrint (eLogError, "SAM: unexpected message ", m_Buffer);
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("SAM malformed message ", m_Buffer);
|
||||
LogPrint (eLogError, "SAM: malformed message ", m_Buffer);
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("SAM malformed message ", m_Buffer);
|
||||
Terminate ();
|
||||
LogPrint (eLogWarning, "SAM: incomplete message ", bytes_transferred);
|
||||
m_BufferOffset = bytes_transferred;
|
||||
// try to receive remaining message
|
||||
Receive ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SAMSocket::ProcessSessionCreate (char * buf, size_t len)
|
||||
{
|
||||
LogPrint ("SAM session create: ", buf);
|
||||
LogPrint (eLogDebug, "SAM: session create: ", buf);
|
||||
std::map<std::string, std::string> params;
|
||||
ExtractParams (buf, len, params);
|
||||
ExtractParams (buf, params);
|
||||
std::string& style = params[SAM_PARAM_STYLE];
|
||||
std::string& id = params[SAM_PARAM_ID];
|
||||
std::string& destination = params[SAM_PARAM_DESTINATION];
|
||||
@@ -253,16 +283,15 @@ namespace client
|
||||
if (m_Session)
|
||||
{
|
||||
m_SocketType = eSAMSocketTypeSession;
|
||||
if (m_Session->localDestination->IsReady ())
|
||||
if (style == SAM_VALUE_DATAGRAM)
|
||||
{
|
||||
if (style == SAM_VALUE_DATAGRAM)
|
||||
{
|
||||
auto dest = m_Session->localDestination->CreateDatagramDestination ();
|
||||
dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
}
|
||||
SendSessionCreateReplyOk ();
|
||||
auto dest = m_Session->localDestination->CreateDatagramDestination ();
|
||||
dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
|
||||
}
|
||||
|
||||
if (m_Session->localDestination->IsReady ())
|
||||
SendSessionCreateReplyOk ();
|
||||
else
|
||||
{
|
||||
m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL));
|
||||
@@ -306,9 +335,9 @@ namespace client
|
||||
|
||||
void SAMSocket::ProcessStreamConnect (char * buf, size_t len)
|
||||
{
|
||||
LogPrint ("SAM stream connect: ", buf);
|
||||
LogPrint (eLogDebug, "SAM: stream connect: ", buf);
|
||||
std::map<std::string, std::string> params;
|
||||
ExtractParams (buf, len, params);
|
||||
ExtractParams (buf, params);
|
||||
std::string& id = params[SAM_PARAM_ID];
|
||||
std::string& destination = params[SAM_PARAM_DESTINATION];
|
||||
std::string& silent = params[SAM_PARAM_SILENT];
|
||||
@@ -317,52 +346,54 @@ namespace client
|
||||
m_Session = m_Owner.FindSession (id);
|
||||
if (m_Session)
|
||||
{
|
||||
i2p::data::IdentityEx dest;
|
||||
dest.FromBase64 (destination);
|
||||
context.GetAddressBook ().InsertAddress (dest);
|
||||
auto leaseSet = m_Session->localDestination->FindLeaseSet (dest.GetIdentHash ());
|
||||
if (leaseSet)
|
||||
Connect (leaseSet);
|
||||
else
|
||||
auto dest = std::make_shared<i2p::data::IdentityEx> ();
|
||||
size_t len = dest->FromBase64(destination);
|
||||
if (len > 0)
|
||||
{
|
||||
m_Session->localDestination->RequestDestination (dest.GetIdentHash (),
|
||||
std::bind (&SAMSocket::HandleConnectLeaseSetRequestComplete,
|
||||
shared_from_this (), std::placeholders::_1, dest.GetIdentHash ()));
|
||||
context.GetAddressBook().InsertAddress(dest);
|
||||
auto leaseSet = m_Session->localDestination->FindLeaseSet(dest->GetIdentHash());
|
||||
if (leaseSet)
|
||||
Connect(leaseSet);
|
||||
else
|
||||
{
|
||||
m_Session->localDestination->RequestDestination(dest->GetIdentHash(),
|
||||
std::bind(&SAMSocket::HandleConnectLeaseSetRequestComplete,
|
||||
shared_from_this(), std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
else
|
||||
SendMessageReply(SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), true);
|
||||
}
|
||||
else
|
||||
else
|
||||
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
|
||||
}
|
||||
|
||||
void SAMSocket::Connect (std::shared_ptr<const i2p::data::LeaseSet> remote)
|
||||
{
|
||||
m_SocketType = eSAMSocketTypeStream;
|
||||
m_Session->sockets.push_back (shared_from_this ());
|
||||
m_Session->AddSocket (shared_from_this ());
|
||||
m_Stream = m_Session->localDestination->CreateStream (remote);
|
||||
m_Stream->Send ((uint8_t *)m_Buffer, 0); // connect
|
||||
I2PReceive ();
|
||||
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
|
||||
}
|
||||
|
||||
void SAMSocket::HandleConnectLeaseSetRequestComplete (bool success, i2p::data::IdentHash ident)
|
||||
void SAMSocket::HandleConnectLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet)
|
||||
{
|
||||
std::shared_ptr<const i2p::data::LeaseSet> leaseSet;
|
||||
if (success)
|
||||
leaseSet = m_Session->localDestination->FindLeaseSet (ident);
|
||||
if (leaseSet)
|
||||
Connect (leaseSet);
|
||||
else
|
||||
{
|
||||
LogPrint ("SAM destination to connect not found");
|
||||
LogPrint (eLogError, "SAM: destination to connect not found");
|
||||
SendMessageReply (SAM_STREAM_STATUS_CANT_REACH_PEER, strlen(SAM_STREAM_STATUS_CANT_REACH_PEER), true);
|
||||
}
|
||||
}
|
||||
|
||||
void SAMSocket::ProcessStreamAccept (char * buf, size_t len)
|
||||
{
|
||||
LogPrint ("SAM stream accept: ", buf);
|
||||
LogPrint (eLogDebug, "SAM: stream accept: ", buf);
|
||||
std::map<std::string, std::string> params;
|
||||
ExtractParams (buf, len, params);
|
||||
ExtractParams (buf, params);
|
||||
std::string& id = params[SAM_PARAM_ID];
|
||||
std::string& silent = params[SAM_PARAM_SILENT];
|
||||
if (silent == SAM_VALUE_TRUE) m_IsSilent = true;
|
||||
@@ -373,7 +404,7 @@ namespace client
|
||||
if (!m_Session->localDestination->IsAcceptingStreams ())
|
||||
{
|
||||
m_SocketType = eSAMSocketTypeAcceptor;
|
||||
m_Session->sockets.push_back (shared_from_this ());
|
||||
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);
|
||||
}
|
||||
@@ -384,36 +415,62 @@ namespace client
|
||||
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
|
||||
}
|
||||
|
||||
size_t SAMSocket::ProcessDatagramSend (char * buf, size_t len, const char * data)
|
||||
{
|
||||
LogPrint (eLogDebug, "SAM: datagram send: ", buf, " ", len);
|
||||
std::map<std::string, std::string> params;
|
||||
ExtractParams (buf, params);
|
||||
size_t size = boost::lexical_cast<int>(params[SAM_PARAM_SIZE]), offset = data - buf;
|
||||
if (offset + size <= len)
|
||||
{
|
||||
if (m_Session)
|
||||
{
|
||||
auto d = m_Session->localDestination->GetDatagramDestination ();
|
||||
if (d)
|
||||
{
|
||||
i2p::data::IdentityEx dest;
|
||||
dest.FromBase64 (params[SAM_PARAM_DESTINATION]);
|
||||
d->SendDatagramTo ((const uint8_t *)data, size, dest.GetIdentHash ());
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "SAM: missing datagram destination");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "SAM: session is not created from DATAGRAM SEND");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "SAM: sent datagram size ", size, " exceeds buffer ", len - offset);
|
||||
return 0; // try to receive more
|
||||
}
|
||||
return offset + size;
|
||||
}
|
||||
|
||||
void SAMSocket::ProcessDestGenerate ()
|
||||
{
|
||||
LogPrint ("SAM dest generate");
|
||||
auto localDestination = i2p::client::context.CreateNewLocalDestination ();
|
||||
if (localDestination)
|
||||
{
|
||||
auto priv = localDestination->GetPrivateKeys ().ToBase64 ();
|
||||
auto pub = localDestination->GetIdentity ().ToBase64 ();
|
||||
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, pub.c_str (), priv.c_str ());
|
||||
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, pub.c_str (), priv.c_str ());
|
||||
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, true);
|
||||
}
|
||||
else
|
||||
SendMessageReply (SAM_DEST_REPLY_I2P_ERROR, strlen(SAM_DEST_REPLY_I2P_ERROR), true);
|
||||
SendMessageReply (m_Buffer, len, false);
|
||||
}
|
||||
|
||||
void SAMSocket::ProcessNamingLookup (char * buf, size_t len)
|
||||
{
|
||||
LogPrint ("SAM naming lookup: ", buf);
|
||||
LogPrint (eLogDebug, "SAM: naming lookup: ", buf);
|
||||
std::map<std::string, std::string> params;
|
||||
ExtractParams (buf, len, params);
|
||||
ExtractParams (buf, params);
|
||||
std::string& name = params[SAM_PARAM_NAME];
|
||||
i2p::data::IdentityEx identity;
|
||||
std::shared_ptr<const i2p::data::IdentityEx> identity;
|
||||
i2p::data::IdentHash ident;
|
||||
if (name == "ME")
|
||||
SendNamingLookupReply (m_Session->localDestination->GetIdentity ());
|
||||
else if (context.GetAddressBook ().GetAddress (name, identity))
|
||||
else if ((identity = context.GetAddressBook ().GetAddress (name)) != nullptr)
|
||||
SendNamingLookupReply (identity);
|
||||
else if (m_Session && m_Session->localDestination &&
|
||||
context.GetAddressBook ().GetIdentHash (name, ident))
|
||||
@@ -428,7 +485,7 @@ namespace client
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("SAM naming failed. Unknown address ", name);
|
||||
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
|
||||
@@ -438,11 +495,8 @@ namespace client
|
||||
}
|
||||
}
|
||||
|
||||
void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (bool success, i2p::data::IdentHash ident)
|
||||
void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, i2p::data::IdentHash ident)
|
||||
{
|
||||
std::shared_ptr<const i2p::data::LeaseSet> leaseSet;
|
||||
if (success)
|
||||
leaseSet = m_Session->localDestination->FindLeaseSet (ident);
|
||||
if (leaseSet)
|
||||
{
|
||||
context.GetAddressBook ().InsertAddress (leaseSet->GetIdentity ());
|
||||
@@ -450,7 +504,7 @@ namespace client
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogInfo, "SAM naming lookup failed. LeaseSet for ", ident.ToBase32 (), " not found");
|
||||
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,
|
||||
context.GetAddressBook ().ToAddress (ident).c_str());
|
||||
@@ -462,9 +516,9 @@ namespace client
|
||||
}
|
||||
}
|
||||
|
||||
void SAMSocket::SendNamingLookupReply (const i2p::data::IdentityEx& identity)
|
||||
void SAMSocket::SendNamingLookupReply (std::shared_ptr<const i2p::data::IdentityEx> identity)
|
||||
{
|
||||
auto base64 = identity.ToBase64 ();
|
||||
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
|
||||
@@ -473,7 +527,7 @@ namespace client
|
||||
SendMessageReply (m_Buffer, l, false);
|
||||
}
|
||||
|
||||
void SAMSocket::ExtractParams (char * buf, size_t len, std::map<std::string, std::string>& params)
|
||||
void SAMSocket::ExtractParams (char * buf, std::map<std::string, std::string>& params)
|
||||
{
|
||||
char * separator;
|
||||
do
|
||||
@@ -494,8 +548,14 @@ namespace client
|
||||
|
||||
void SAMSocket::Receive ()
|
||||
{
|
||||
m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE),
|
||||
std::bind((m_SocketType == eSAMSocketTypeSession) ? &SAMSocket::HandleMessage : &SAMSocket::HandleReceived,
|
||||
if (m_BufferOffset >= SAM_SOCKET_BUFFER_SIZE)
|
||||
{
|
||||
LogPrint (eLogError, "SAM: Buffer is full, terminate");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
m_Socket.async_read_some (boost::asio::buffer(m_Buffer + m_BufferOffset, SAM_SOCKET_BUFFER_SIZE - m_BufferOffset),
|
||||
std::bind((m_SocketType == eSAMSocketTypeStream) ? &SAMSocket::HandleReceived : &SAMSocket::HandleMessage,
|
||||
shared_from_this (), std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
@@ -503,15 +563,24 @@ namespace client
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("SAM read error: ", ecode.message ());
|
||||
LogPrint (eLogError, "SAM: read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Stream)
|
||||
m_Stream->Send ((uint8_t *)m_Buffer, bytes_transferred);
|
||||
Receive ();
|
||||
{
|
||||
auto s = shared_from_this ();
|
||||
m_Stream->AsyncSend ((uint8_t *)m_Buffer, bytes_transferred,
|
||||
[s](const boost::system::error_code& ecode)
|
||||
{
|
||||
if (!ecode)
|
||||
s->Receive ();
|
||||
else
|
||||
s->Terminate ();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -528,7 +597,7 @@ namespace client
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("SAM stream read error: ", ecode.message ());
|
||||
LogPrint (eLogError, "SAM: stream read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
@@ -543,7 +612,7 @@ namespace client
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("SAM socket write error: ", ecode.message ());
|
||||
LogPrint (eLogError, "SAM: socket write error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
@@ -555,18 +624,24 @@ namespace client
|
||||
{
|
||||
if (stream)
|
||||
{
|
||||
LogPrint ("SAM incoming I2P connection for session ", m_ID);
|
||||
LogPrint (eLogDebug, "SAM: incoming I2P connection for session ", m_ID);
|
||||
m_Stream = stream;
|
||||
context.GetAddressBook ().InsertAddress (stream->GetRemoteIdentity ());
|
||||
auto session = m_Owner.FindSession (m_ID);
|
||||
if (session)
|
||||
session->localDestination->StopAcceptingStreams ();
|
||||
m_SocketType = eSAMSocketTypeStream;
|
||||
if (!m_IsSilent)
|
||||
{
|
||||
// send remote peer address
|
||||
uint8_t ident[1024];
|
||||
size_t l = stream->GetRemoteIdentity ().ToBuffer (ident, 1024);
|
||||
size_t l1 = i2p::data::ByteStreamToBase64 (ident, l, (char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE);
|
||||
// get remote peer address
|
||||
auto ident_ptr = stream->GetRemoteIdentity();
|
||||
const size_t ident_len = ident_ptr->GetFullLen();
|
||||
uint8_t* ident = new uint8_t[ident_len];
|
||||
|
||||
// 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
|
||||
}
|
||||
@@ -574,12 +649,13 @@ namespace client
|
||||
I2PReceive ();
|
||||
}
|
||||
else
|
||||
LogPrint (eLogInfo, "SAM I2P acceptor has been reset");
|
||||
LogPrint (eLogWarning, "SAM: I2P acceptor has been reset");
|
||||
}
|
||||
|
||||
void SAMSocket::HandleI2PDatagramReceive (const i2p::data::IdentityEx& ident, const uint8_t * buf, size_t len)
|
||||
void SAMSocket::HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
|
||||
{
|
||||
auto base64 = ident.ToBase64 ();
|
||||
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
|
||||
@@ -592,35 +668,36 @@ namespace client
|
||||
std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Datagram size ", len," exceeds buffer");
|
||||
LogPrint (eLogWarning, "SAM: received datagram size ", len," exceeds buffer");
|
||||
}
|
||||
|
||||
SAMSession::SAMSession (ClientDestination * dest):
|
||||
SAMSession::SAMSession (std::shared_ptr<ClientDestination> dest):
|
||||
localDestination (dest)
|
||||
{
|
||||
}
|
||||
|
||||
SAMSession::~SAMSession ()
|
||||
{
|
||||
for (auto it: sockets)
|
||||
it->SetSocketType (eSAMSocketTypeTerminated);
|
||||
CloseStreams();
|
||||
i2p::client::context.DeleteLocalDestination (localDestination);
|
||||
}
|
||||
|
||||
void SAMSession::CloseStreams ()
|
||||
{
|
||||
for (auto it: sockets)
|
||||
{
|
||||
it->CloseStream ();
|
||||
it->SetSocketType (eSAMSocketTypeTerminated);
|
||||
}
|
||||
sockets.clear ();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_SocketsMutex);
|
||||
for (auto& sock : m_Sockets) {
|
||||
sock->CloseStream();
|
||||
}
|
||||
}
|
||||
// XXX: should this be done inside locked parts?
|
||||
m_Sockets.clear();
|
||||
}
|
||||
|
||||
SAMBridge::SAMBridge (int port):
|
||||
SAMBridge::SAMBridge (const std::string& address, int port):
|
||||
m_IsRunning (false), m_Thread (nullptr),
|
||||
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
|
||||
m_DatagramEndpoint (boost::asio::ip::udp::v4 (), port-1), m_DatagramSocket (m_Service, m_DatagramEndpoint)
|
||||
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)),
|
||||
m_DatagramEndpoint (boost::asio::ip::address::from_string(address), port-1), m_DatagramSocket (m_Service, m_DatagramEndpoint)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -642,8 +719,8 @@ namespace client
|
||||
{
|
||||
m_IsRunning = false;
|
||||
m_Acceptor.cancel ();
|
||||
for (auto it: m_Sessions)
|
||||
delete it.second;
|
||||
for (auto& it: m_Sessions)
|
||||
it.second->CloseStreams ();
|
||||
m_Sessions.clear ();
|
||||
m_Service.stop ();
|
||||
if (m_Thread)
|
||||
@@ -664,7 +741,7 @@ namespace client
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint ("SAM: ", ex.what ());
|
||||
LogPrint (eLogError, "SAM: runtime exception: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -684,23 +761,23 @@ namespace client
|
||||
auto ep = socket->GetSocket ().remote_endpoint (ec);
|
||||
if (!ec)
|
||||
{
|
||||
LogPrint ("New SAM connection from ", ep);
|
||||
LogPrint (eLogDebug, "SAM: new connection from ", ep);
|
||||
socket->ReceiveHandshake ();
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "SAM connection from error ", ec.message ());
|
||||
LogPrint (eLogError, "SAM: incoming connection error ", ec.message ());
|
||||
}
|
||||
else
|
||||
LogPrint ("SAM accept error: ", ecode.message ());
|
||||
LogPrint (eLogError, "SAM: accept error: ", ecode.message ());
|
||||
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Accept ();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
ClientDestination * localDestination = nullptr;
|
||||
std::shared_ptr<ClientDestination> localDestination = nullptr;
|
||||
if (destination != "")
|
||||
{
|
||||
i2p::data::PrivateKeys keys;
|
||||
@@ -722,10 +799,11 @@ namespace client
|
||||
}
|
||||
if (localDestination)
|
||||
{
|
||||
auto session = std::make_shared<SAMSession>(localDestination);
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
auto ret = m_Sessions.insert (std::pair<std::string, SAMSession *>(id, new SAMSession (localDestination)));
|
||||
auto ret = m_Sessions.insert (std::make_pair(id, session));
|
||||
if (!ret.second)
|
||||
LogPrint ("Session ", id, " already exists");
|
||||
LogPrint (eLogWarning, "SAM: Session ", id, " already exists");
|
||||
return ret.first->second;
|
||||
}
|
||||
return nullptr;
|
||||
@@ -733,19 +811,24 @@ namespace client
|
||||
|
||||
void SAMBridge::CloseSession (const std::string& id)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
auto it = m_Sessions.find (id);
|
||||
if (it != m_Sessions.end ())
|
||||
std::shared_ptr<SAMSession> session;
|
||||
{
|
||||
auto session = it->second;
|
||||
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 ();
|
||||
m_Sessions.erase (it);
|
||||
delete session;
|
||||
}
|
||||
}
|
||||
|
||||
SAMSession * SAMBridge::FindSession (const std::string& id) const
|
||||
std::shared_ptr<SAMSession> SAMBridge::FindSession (const std::string& id) const
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
auto it = m_Sessions.find (id);
|
||||
@@ -770,7 +853,7 @@ namespace client
|
||||
char * eol = strchr ((char *)m_DatagramReceiveBuffer, '\n');
|
||||
*eol = 0; eol++;
|
||||
size_t payloadLen = bytes_transferred - ((uint8_t *)eol - m_DatagramReceiveBuffer);
|
||||
LogPrint ("SAM datagram received ", m_DatagramReceiveBuffer," size=", payloadLen);
|
||||
LogPrint (eLogDebug, "SAM: datagram received ", m_DatagramReceiveBuffer," size=", payloadLen);
|
||||
char * sessionID = strchr ((char *)m_DatagramReceiveBuffer, ' ');
|
||||
if (sessionID)
|
||||
{
|
||||
@@ -784,28 +867,21 @@ namespace client
|
||||
{
|
||||
i2p::data::IdentityEx dest;
|
||||
dest.FromBase64 (destination);
|
||||
auto leaseSet = i2p::data::netdb.FindLeaseSet (dest.GetIdentHash ());
|
||||
if (leaseSet)
|
||||
session->localDestination->GetDatagramDestination ()->
|
||||
SendDatagramTo ((uint8_t *)eol, payloadLen, leaseSet);
|
||||
else
|
||||
{
|
||||
LogPrint ("SAM datagram destination not found");
|
||||
session->localDestination->RequestDestination (dest.GetIdentHash ());
|
||||
}
|
||||
session->localDestination->GetDatagramDestination ()->
|
||||
SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
|
||||
}
|
||||
else
|
||||
LogPrint ("Session ", sessionID, " not found");
|
||||
LogPrint (eLogError, "SAM: Session ", sessionID, " not found");
|
||||
}
|
||||
else
|
||||
LogPrint ("Missing destination key");
|
||||
LogPrint (eLogError, "SAM: Missing destination key");
|
||||
}
|
||||
else
|
||||
LogPrint ("Missing sessionID");
|
||||
LogPrint (eLogError, "SAM: Missing sessionID");
|
||||
ReceiveDatagram ();
|
||||
}
|
||||
else
|
||||
LogPrint ("SAM datagram receive error: ", ecode.message ());
|
||||
LogPrint (eLogError, "SAM: datagram receive error: ", ecode.message ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
60
SAM.h
60
SAM.h
@@ -18,7 +18,7 @@ namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
const size_t SAM_SOCKET_BUFFER_SIZE = 4096;
|
||||
const size_t SAM_SOCKET_BUFFER_SIZE = 8192;
|
||||
const int SAM_SOCKET_CONNECTION_MAX_IDLE = 3600; // in seconds
|
||||
const int SAM_SESSION_READINESS_CHECK_INTERVAL = 20; // in seconds
|
||||
const char SAM_HANDSHAKE[] = "HELLO VERSION";
|
||||
@@ -28,18 +28,20 @@ namespace client
|
||||
const char SAM_SESSION_CREATE_REPLY_OK[] = "SESSION STATUS RESULT=OK DESTINATION=%s\n";
|
||||
const char SAM_SESSION_CREATE_DUPLICATED_ID[] = "SESSION STATUS RESULT=DUPLICATED_ID\n";
|
||||
const char SAM_SESSION_CREATE_DUPLICATED_DEST[] = "SESSION STATUS RESULT=DUPLICATED_DEST\n";
|
||||
const char SAM_SESSION_STATUS_INVALID_KEY[] = "SESSION STATUS RESULT=INVALID_KEY\n";
|
||||
const char SAM_STREAM_CONNECT[] = "STREAM CONNECT";
|
||||
const char SAM_STREAM_STATUS_OK[] = "STREAM STATUS RESULT=OK\n";
|
||||
const char SAM_STREAM_STATUS_INVALID_ID[] = "STREAM STATUS RESULT=INVALID_ID\n";
|
||||
const char SAM_STREAM_STATUS_CANT_REACH_PEER[] = "STREAM STATUS RESULT=CANT_REACH_PEER\n";
|
||||
const char SAM_STREAM_STATUS_I2P_ERROR[] = "STREAM STATUS RESULT=I2P_ERROR\n";
|
||||
const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT";
|
||||
const char SAM_DATAGRAM_SEND[] = "DATAGRAM SEND";
|
||||
const char SAM_DEST_GENERATE[] = "DEST GENERATE";
|
||||
const char SAM_DEST_REPLY[] = "DEST REPLY PUB=%s PRIV=%s\n";
|
||||
const char SAM_DEST_REPLY_I2P_ERROR[] = "DEST REPLY RESULT=I2P_ERROR\n";
|
||||
const char SAM_NAMING_LOOKUP[] = "NAMING LOOKUP";
|
||||
const char SAM_NAMING_REPLY[] = "NAMING REPLY RESULT=OK NAME=ME VALUE=%s\n";
|
||||
const char SAM_DATAGRAM_RECEIVED[] = "DATAGRAM_RECEIVED DESTINATION=%s SIZE=%lu\n";
|
||||
const char SAM_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";
|
||||
@@ -49,7 +51,8 @@ namespace client
|
||||
const char SAM_PARAM_SILENT[] = "SILENT";
|
||||
const char SAM_PARAM_DESTINATION[] = "DESTINATION";
|
||||
const char SAM_PARAM_NAME[] = "NAME";
|
||||
const char SAM_PARAM_SIGNATURE_TYPE[] = "SIGNATURE_TYPE";
|
||||
const char SAM_PARAM_SIGNATURE_TYPE[] = "SIGNATURE_TYPE";
|
||||
const char SAM_PARAM_SIZE[] = "SIZE";
|
||||
const char SAM_VALUE_TRANSIENT[] = "TRANSIENT";
|
||||
const char SAM_VALUE_STREAM[] = "STREAM";
|
||||
const char SAM_VALUE_DATAGRAM[] = "DATAGRAM";
|
||||
@@ -96,19 +99,20 @@ namespace client
|
||||
void HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleI2PAccept (std::shared_ptr<i2p::stream::Stream> stream);
|
||||
void HandleWriteI2PData (const boost::system::error_code& ecode);
|
||||
void HandleI2PDatagramReceive (const i2p::data::IdentityEx& ident, const uint8_t * buf, size_t len);
|
||||
void HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
||||
|
||||
void ProcessSessionCreate (char * buf, size_t len);
|
||||
void ProcessStreamConnect (char * buf, size_t len);
|
||||
void ProcessStreamAccept (char * buf, size_t len);
|
||||
void ProcessDestGenerate ();
|
||||
void ProcessNamingLookup (char * buf, size_t len);
|
||||
void ExtractParams (char * buf, size_t len, std::map<std::string, std::string>& params);
|
||||
size_t ProcessDatagramSend (char * buf, size_t len, const char * data); // from SAM 1.0
|
||||
void ExtractParams (char * buf, std::map<std::string, std::string>& params);
|
||||
|
||||
void Connect (std::shared_ptr<const i2p::data::LeaseSet> remote);
|
||||
void HandleConnectLeaseSetRequestComplete (bool success, i2p::data::IdentHash ident);
|
||||
void SendNamingLookupReply (const i2p::data::IdentityEx& identity);
|
||||
void HandleNamingLookupLeaseSetRequestComplete (bool success, i2p::data::IdentHash ident);
|
||||
void HandleConnectLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet);
|
||||
void SendNamingLookupReply (std::shared_ptr<const i2p::data::IdentityEx> identity);
|
||||
void HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, i2p::data::IdentHash ident);
|
||||
void HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode);
|
||||
void SendSessionCreateReplyOk ();
|
||||
|
||||
@@ -118,20 +122,44 @@ namespace client
|
||||
boost::asio::ip::tcp::socket m_Socket;
|
||||
boost::asio::deadline_timer m_Timer;
|
||||
char m_Buffer[SAM_SOCKET_BUFFER_SIZE + 1];
|
||||
size_t m_BufferOffset;
|
||||
uint8_t m_StreamBuffer[SAM_SOCKET_BUFFER_SIZE];
|
||||
SAMSocketType m_SocketType;
|
||||
std::string m_ID; // nickname
|
||||
bool m_IsSilent;
|
||||
std::shared_ptr<i2p::stream::Stream> m_Stream;
|
||||
SAMSession * m_Session;
|
||||
std::shared_ptr<SAMSession> m_Session;
|
||||
};
|
||||
|
||||
struct SAMSession
|
||||
{
|
||||
ClientDestination * localDestination;
|
||||
std::list<std::shared_ptr<SAMSocket> > sockets;
|
||||
std::shared_ptr<ClientDestination> localDestination;
|
||||
std::list<std::shared_ptr<SAMSocket> > m_Sockets;
|
||||
std::mutex m_SocketsMutex;
|
||||
|
||||
/** safely add a socket to this session */
|
||||
void AddSocket(std::shared_ptr<SAMSocket> sock) {
|
||||
std::lock_guard<std::mutex> lock(m_SocketsMutex);
|
||||
m_Sockets.push_back(sock);
|
||||
}
|
||||
|
||||
/** safely remove a socket from this session */
|
||||
void DelSocket(std::shared_ptr<SAMSocket> sock) {
|
||||
std::lock_guard<std::mutex> lock(m_SocketsMutex);
|
||||
m_Sockets.remove(sock);
|
||||
}
|
||||
|
||||
/** get a list holding a copy of all sam sockets from this session */
|
||||
std::list<std::shared_ptr<SAMSocket> > ListSockets() {
|
||||
std::list<std::shared_ptr<SAMSocket> > l;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_SocketsMutex);
|
||||
for(const auto& sock : m_Sockets ) l.push_back(sock);
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
SAMSession (ClientDestination * localDestination);
|
||||
SAMSession (std::shared_ptr<ClientDestination> dest);
|
||||
~SAMSession ();
|
||||
|
||||
void CloseStreams ();
|
||||
@@ -141,17 +169,17 @@ namespace client
|
||||
{
|
||||
public:
|
||||
|
||||
SAMBridge (int port);
|
||||
SAMBridge (const std::string& address, int port);
|
||||
~SAMBridge ();
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
boost::asio::io_service& GetService () { return m_Service; };
|
||||
SAMSession * CreateSession (const std::string& id, const std::string& destination, // empty string means transient
|
||||
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);
|
||||
SAMSession * FindSession (const std::string& id) const;
|
||||
std::shared_ptr<SAMSession> FindSession (const std::string& id) const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -172,7 +200,7 @@ namespace client
|
||||
boost::asio::ip::udp::endpoint m_DatagramEndpoint, m_SenderEndpoint;
|
||||
boost::asio::ip::udp::socket m_DatagramSocket;
|
||||
mutable std::mutex m_SessionsMutex;
|
||||
std::map<std::string, SAMSession *> m_Sessions;
|
||||
std::map<std::string, std::shared_ptr<SAMSession> > m_Sessions;
|
||||
uint8_t m_DatagramReceiveBuffer[i2p::datagram::MAX_DATAGRAM_SIZE+1];
|
||||
|
||||
public:
|
||||
|
||||
549
SOCKS.cpp
549
SOCKS.cpp
@@ -9,6 +9,7 @@
|
||||
#include "ClientContext.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "I2PTunnel.h"
|
||||
#include "I2PService.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -17,10 +18,16 @@ namespace proxy
|
||||
static const size_t socks_buffer_size = 8192;
|
||||
static const size_t max_socks_hostname_size = 255; // Limit for socks5 and bad idea to traverse
|
||||
|
||||
struct SOCKSDnsAddress {
|
||||
static const size_t SOCKS_FORWARDER_BUFFER_SIZE = 8192;
|
||||
|
||||
static const size_t SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE = 8;
|
||||
|
||||
struct SOCKSDnsAddress
|
||||
{
|
||||
uint8_t size;
|
||||
char value[max_socks_hostname_size];
|
||||
void FromString (std::string str) {
|
||||
void FromString (const std::string& str)
|
||||
{
|
||||
size = str.length();
|
||||
if (str.length() > max_socks_hostname_size) size = max_socks_hostname_size;
|
||||
memcpy(value,str.c_str(),size);
|
||||
@@ -30,9 +37,11 @@ namespace proxy
|
||||
};
|
||||
|
||||
class SOCKSServer;
|
||||
class SOCKSHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this<SOCKSHandler> {
|
||||
class SOCKSHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this<SOCKSHandler>
|
||||
{
|
||||
private:
|
||||
enum state {
|
||||
enum state
|
||||
{
|
||||
GET_SOCKSV,
|
||||
GET_COMMAND,
|
||||
GET_PORT,
|
||||
@@ -47,20 +56,26 @@ namespace proxy
|
||||
GET5_IPV6,
|
||||
GET5_HOST_SIZE,
|
||||
GET5_HOST,
|
||||
DONE
|
||||
READY,
|
||||
UPSTREAM_RESOLVE,
|
||||
UPSTREAM_CONNECT,
|
||||
UPSTREAM_HANDSHAKE
|
||||
};
|
||||
enum authMethods {
|
||||
enum authMethods
|
||||
{
|
||||
AUTH_NONE = 0, //No authentication, skip to next step
|
||||
AUTH_GSSAPI = 1, //GSSAPI authentication
|
||||
AUTH_USERPASSWD = 2, //Username and password
|
||||
AUTH_UNACCEPTABLE = 0xff //No acceptable method found
|
||||
};
|
||||
enum addrTypes {
|
||||
enum addrTypes
|
||||
{
|
||||
ADDR_IPV4 = 1, //IPv4 address (4 octets)
|
||||
ADDR_DNS = 3, // DNS name (up to 255 octets)
|
||||
ADDR_IPV6 = 4 //IPV6 address (16 octets)
|
||||
};
|
||||
enum errTypes {
|
||||
enum errTypes
|
||||
{
|
||||
SOCKS5_OK = 0, // No error for SOCKS5
|
||||
SOCKS5_GEN_FAIL = 1, // General server failure
|
||||
SOCKS5_RULE_DENIED = 2, // Connection disallowed by ruleset
|
||||
@@ -75,16 +90,19 @@ namespace proxy
|
||||
SOCKS4_IDENTD_MISSING = 92, // Couldn't connect to the identd server
|
||||
SOCKS4_IDENTD_DIFFER = 93 // The ID reported by the application and by identd differ
|
||||
};
|
||||
enum cmdTypes {
|
||||
enum cmdTypes
|
||||
{
|
||||
CMD_CONNECT = 1, // TCP Connect
|
||||
CMD_BIND = 2, // TCP Bind
|
||||
CMD_UDP = 3 // UDP associate
|
||||
};
|
||||
enum socksVersions {
|
||||
enum socksVersions
|
||||
{
|
||||
SOCKS4 = 4, // SOCKS4
|
||||
SOCKS5 = 5 // SOCKS5
|
||||
};
|
||||
union address {
|
||||
union address
|
||||
{
|
||||
uint32_t ip;
|
||||
SOCKSDnsAddress dns;
|
||||
uint8_t ipv6[16];
|
||||
@@ -99,6 +117,7 @@ namespace proxy
|
||||
boost::asio::const_buffers_1 GenerateSOCKS5SelectAuth(authMethods method);
|
||||
boost::asio::const_buffers_1 GenerateSOCKS4Response(errTypes error, uint32_t ip, uint16_t port);
|
||||
boost::asio::const_buffers_1 GenerateSOCKS5Response(errTypes error, addrTypes type, const address &addr, uint16_t port);
|
||||
boost::asio::const_buffers_1 GenerateUpstreamRequest();
|
||||
bool Socks5ChooseAuth();
|
||||
void SocksRequestFailed(errTypes error);
|
||||
void SocksRequestSuccess();
|
||||
@@ -106,12 +125,29 @@ namespace proxy
|
||||
void SentSocksDone(const boost::system::error_code & ecode);
|
||||
void SentSocksResponse(const boost::system::error_code & ecode);
|
||||
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
|
||||
void ForwardSOCKS();
|
||||
|
||||
uint8_t m_sock_buff[socks_buffer_size];
|
||||
boost::asio::ip::tcp::socket * m_sock;
|
||||
void SocksUpstreamSuccess();
|
||||
void AsyncUpstreamSockRead();
|
||||
void SendUpstreamRequest();
|
||||
void HandleUpstreamData(uint8_t * buff, std::size_t len);
|
||||
void HandleUpstreamSockSend(const boost::system::error_code & ecode, std::size_t bytes_transfered);
|
||||
void HandleUpstreamSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
|
||||
void HandleUpstreamConnected(const boost::system::error_code & ecode,
|
||||
boost::asio::ip::tcp::resolver::iterator itr);
|
||||
void HandleUpstreamResolved(const boost::system::error_code & ecode,
|
||||
boost::asio::ip::tcp::resolver::iterator itr);
|
||||
|
||||
boost::asio::ip::tcp::resolver m_proxy_resolver;
|
||||
uint8_t m_sock_buff[socks_buffer_size];
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> m_sock, m_upstreamSock;
|
||||
std::shared_ptr<i2p::stream::Stream> m_stream;
|
||||
uint8_t *m_remaining_data; //Data left to be sent
|
||||
uint8_t *m_remaining_upstream_data; //upstream data left to be forwarded
|
||||
uint8_t m_response[7+max_socks_hostname_size];
|
||||
uint8_t m_upstream_response[SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE];
|
||||
uint8_t m_upstream_request[14+max_socks_hostname_size];
|
||||
std::size_t m_upstream_response_len;
|
||||
address m_address; //Address
|
||||
std::size_t m_remaining_data_len; //Size of the data left to be sent
|
||||
uint32_t m_4aip; //Used in 4a requests
|
||||
@@ -123,38 +159,55 @@ namespace proxy
|
||||
socksVersions m_socksv; //Socks version
|
||||
cmdTypes m_cmd; // Command requested
|
||||
state m_state;
|
||||
|
||||
const bool m_UseUpstreamProxy; // do we want to use the upstream proxy for non i2p addresses?
|
||||
const std::string m_UpstreamProxyAddress;
|
||||
const uint16_t m_UpstreamProxyPort;
|
||||
|
||||
public:
|
||||
SOCKSHandler(SOCKSServer * parent, boost::asio::ip::tcp::socket * sock) :
|
||||
I2PServiceHandler(parent), m_sock(sock), m_stream(nullptr),
|
||||
m_authchosen(AUTH_UNACCEPTABLE), m_addrtype(ADDR_IPV4)
|
||||
SOCKSHandler(SOCKSServer * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock, const std::string & upstreamAddr, const uint16_t upstreamPort, const bool useUpstream) :
|
||||
I2PServiceHandler(parent),
|
||||
m_proxy_resolver(parent->GetService()),
|
||||
m_sock(sock), m_stream(nullptr),
|
||||
m_authchosen(AUTH_UNACCEPTABLE), m_addrtype(ADDR_IPV4),
|
||||
m_UseUpstreamProxy(useUpstream),
|
||||
m_UpstreamProxyAddress(upstreamAddr),
|
||||
m_UpstreamProxyPort(upstreamPort)
|
||||
{ m_address.ip = 0; EnterState(GET_SOCKSV); }
|
||||
|
||||
~SOCKSHandler() { Terminate(); }
|
||||
void Handle() { AsyncSockRead(); }
|
||||
};
|
||||
|
||||
|
||||
void SOCKSHandler::AsyncSockRead()
|
||||
{
|
||||
LogPrint(eLogDebug,"--- SOCKS async sock read");
|
||||
if(m_sock) {
|
||||
LogPrint(eLogDebug, "SOCKS: async sock read");
|
||||
if (m_sock) {
|
||||
m_sock->async_receive(boost::asio::buffer(m_sock_buff, socks_buffer_size),
|
||||
std::bind(&SOCKSHandler::HandleSockRecv, shared_from_this(),
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
} else {
|
||||
LogPrint(eLogError,"--- SOCKS no socket for read");
|
||||
LogPrint(eLogError,"SOCKS: no socket for read");
|
||||
}
|
||||
}
|
||||
|
||||
void SOCKSHandler::Terminate() {
|
||||
void SOCKSHandler::Terminate()
|
||||
{
|
||||
if (Kill()) return;
|
||||
if (m_sock) {
|
||||
LogPrint(eLogDebug,"--- SOCKS close sock");
|
||||
if (m_sock)
|
||||
{
|
||||
LogPrint(eLogDebug, "SOCKS: closing socket");
|
||||
m_sock->close();
|
||||
delete m_sock;
|
||||
m_sock = nullptr;
|
||||
}
|
||||
if (m_stream) {
|
||||
LogPrint(eLogDebug,"--- SOCKS close stream");
|
||||
if (m_upstreamSock)
|
||||
{
|
||||
LogPrint(eLogDebug, "SOCKS: closing upstream socket");
|
||||
m_upstreamSock->close();
|
||||
m_upstreamSock = nullptr;
|
||||
}
|
||||
if (m_stream)
|
||||
{
|
||||
LogPrint(eLogDebug, "SOCKS: closing stream");
|
||||
m_stream.reset ();
|
||||
}
|
||||
Done(shared_from_this());
|
||||
@@ -170,8 +223,7 @@ namespace proxy
|
||||
return boost::asio::const_buffers_1(m_response,8);
|
||||
}
|
||||
|
||||
boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS5Response(SOCKSHandler::errTypes error, SOCKSHandler::addrTypes type,
|
||||
const SOCKSHandler::address &addr, uint16_t port)
|
||||
boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS5Response(SOCKSHandler::errTypes error, SOCKSHandler::addrTypes type, const SOCKSHandler::address &addr, uint16_t port)
|
||||
{
|
||||
size_t size = 6;
|
||||
assert(error <= SOCKS5_ADDR_UNSUP);
|
||||
@@ -179,7 +231,8 @@ namespace proxy
|
||||
m_response[1] = error; //Response code
|
||||
m_response[2] = '\x00'; //RSV
|
||||
m_response[3] = type; //Address type
|
||||
switch (type) {
|
||||
switch (type)
|
||||
{
|
||||
case ADDR_IPV4:
|
||||
size = 10;
|
||||
htobe32buf(m_response+4,addr.ip);
|
||||
@@ -198,20 +251,54 @@ namespace proxy
|
||||
return boost::asio::const_buffers_1(m_response,size);
|
||||
}
|
||||
|
||||
boost::asio::const_buffers_1 SOCKSHandler::GenerateUpstreamRequest()
|
||||
{
|
||||
size_t upstreamRequestSize = 0;
|
||||
// TODO: negotiate with upstream
|
||||
// SOCKS 4a
|
||||
m_upstream_request[0] = '\x04'; //version
|
||||
m_upstream_request[1] = m_cmd;
|
||||
htobe16buf(m_upstream_request+2, m_port);
|
||||
m_upstream_request[4] = 0;
|
||||
m_upstream_request[5] = 0;
|
||||
m_upstream_request[6] = 0;
|
||||
m_upstream_request[7] = 1;
|
||||
// user id
|
||||
m_upstream_request[8] = 'i';
|
||||
m_upstream_request[9] = '2';
|
||||
m_upstream_request[10] = 'p';
|
||||
m_upstream_request[11] = 'd';
|
||||
m_upstream_request[12] = 0;
|
||||
upstreamRequestSize += 13;
|
||||
if (m_address.dns.size <= max_socks_hostname_size - ( upstreamRequestSize + 1) ) {
|
||||
// bounds check okay
|
||||
memcpy(m_upstream_request + upstreamRequestSize, m_address.dns.value, m_address.dns.size);
|
||||
upstreamRequestSize += m_address.dns.size;
|
||||
// null terminate
|
||||
m_upstream_request[++upstreamRequestSize] = 0;
|
||||
} else {
|
||||
LogPrint(eLogError, "SOCKS: BUG!!! m_addr.dns.sizs > max_socks_hostname - ( upstreamRequestSize + 1 ) )");
|
||||
}
|
||||
return boost::asio::const_buffers_1(m_upstream_request, upstreamRequestSize);
|
||||
}
|
||||
|
||||
bool SOCKSHandler::Socks5ChooseAuth()
|
||||
{
|
||||
m_response[0] = '\x05'; //Version
|
||||
m_response[1] = m_authchosen; //Response code
|
||||
boost::asio::const_buffers_1 response(m_response,2);
|
||||
if (m_authchosen == AUTH_UNACCEPTABLE) {
|
||||
LogPrint(eLogWarning,"--- SOCKS5 authentication negotiation failed");
|
||||
if (m_authchosen == AUTH_UNACCEPTABLE)
|
||||
{
|
||||
LogPrint(eLogWarning, "SOCKS: v5 authentication negotiation failed");
|
||||
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed,
|
||||
shared_from_this(), std::placeholders::_1));
|
||||
shared_from_this(), std::placeholders::_1));
|
||||
return false;
|
||||
} else {
|
||||
LogPrint(eLogDebug,"--- SOCKS5 choosing authentication method: ", m_authchosen);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint(eLogDebug, "SOCKS: v5 choosing authentication method: ", m_authchosen);
|
||||
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksResponse,
|
||||
shared_from_this(), std::placeholders::_1));
|
||||
shared_from_this(), std::placeholders::_1));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -221,44 +308,47 @@ namespace proxy
|
||||
{
|
||||
boost::asio::const_buffers_1 response(nullptr,0);
|
||||
assert(error != SOCKS4_OK && error != SOCKS5_OK);
|
||||
switch (m_socksv) {
|
||||
switch (m_socksv)
|
||||
{
|
||||
case SOCKS4:
|
||||
LogPrint(eLogWarning,"--- SOCKS4 failed: ", error);
|
||||
LogPrint(eLogWarning, "SOCKS: v4 request failed: ", error);
|
||||
if (error < SOCKS4_OK) error = SOCKS4_FAIL; //Transparently map SOCKS5 errors
|
||||
response = GenerateSOCKS4Response(error, m_4aip, m_port);
|
||||
break;
|
||||
break;
|
||||
case SOCKS5:
|
||||
LogPrint(eLogWarning,"--- SOCKS5 failed: ", error);
|
||||
LogPrint(eLogWarning, "SOCKS: v5 request failed: ", error);
|
||||
response = GenerateSOCKS5Response(error, m_addrtype, m_address, m_port);
|
||||
break;
|
||||
break;
|
||||
}
|
||||
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed,
|
||||
shared_from_this(), std::placeholders::_1));
|
||||
shared_from_this(), std::placeholders::_1));
|
||||
}
|
||||
|
||||
void SOCKSHandler::SocksRequestSuccess()
|
||||
{
|
||||
boost::asio::const_buffers_1 response(nullptr,0);
|
||||
//TODO: this should depend on things like the command type and callbacks may change
|
||||
switch (m_socksv) {
|
||||
switch (m_socksv)
|
||||
{
|
||||
case SOCKS4:
|
||||
LogPrint(eLogInfo,"--- SOCKS4 connection success");
|
||||
LogPrint(eLogInfo, "SOCKS: v4 connection success");
|
||||
response = GenerateSOCKS4Response(SOCKS4_OK, m_4aip, m_port);
|
||||
break;
|
||||
break;
|
||||
case SOCKS5:
|
||||
LogPrint(eLogInfo,"--- SOCKS5 connection success");
|
||||
LogPrint(eLogInfo, "SOCKS: v5 connection success");
|
||||
auto s = i2p::client::context.GetAddressBook().ToAddress(GetOwner()->GetLocalDestination()->GetIdentHash());
|
||||
address ad; ad.dns.FromString(s);
|
||||
//HACK only 16 bits passed in port as SOCKS5 doesn't allow for more
|
||||
response = GenerateSOCKS5Response(SOCKS5_OK, ADDR_DNS, ad, m_stream->GetRecvStreamID());
|
||||
break;
|
||||
break;
|
||||
}
|
||||
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksDone,
|
||||
shared_from_this(), std::placeholders::_1));
|
||||
shared_from_this(), std::placeholders::_1));
|
||||
}
|
||||
|
||||
void SOCKSHandler::EnterState(SOCKSHandler::state nstate, uint8_t parseleft) {
|
||||
switch (nstate) {
|
||||
switch (nstate)
|
||||
{
|
||||
case GET_PORT: parseleft = 2; break;
|
||||
case GET_IPV4: m_addrtype = ADDR_IPV4; m_address.ip = 0; parseleft = 4; break;
|
||||
case GET4_IDENT: m_4aip = m_address.ip; break;
|
||||
@@ -271,174 +361,188 @@ namespace proxy
|
||||
m_state = nstate;
|
||||
}
|
||||
|
||||
bool SOCKSHandler::ValidateSOCKSRequest() {
|
||||
if ( m_cmd != CMD_CONNECT ) {
|
||||
bool SOCKSHandler::ValidateSOCKSRequest()
|
||||
{
|
||||
if ( m_cmd != CMD_CONNECT )
|
||||
{
|
||||
//TODO: we need to support binds and other shit!
|
||||
LogPrint(eLogError,"--- SOCKS unsupported command: ", m_cmd);
|
||||
LogPrint(eLogError, "SOCKS: unsupported command: ", m_cmd);
|
||||
SocksRequestFailed(SOCKS5_CMD_UNSUP);
|
||||
return false;
|
||||
}
|
||||
//TODO: we may want to support other address types!
|
||||
if ( m_addrtype != ADDR_DNS ) {
|
||||
switch (m_socksv) {
|
||||
if ( m_addrtype != ADDR_DNS )
|
||||
{
|
||||
switch (m_socksv)
|
||||
{
|
||||
case SOCKS5:
|
||||
LogPrint(eLogError,"--- SOCKS5 unsupported address type: ", m_addrtype);
|
||||
break;
|
||||
LogPrint(eLogError, "SOCKS: v5 unsupported address type: ", m_addrtype);
|
||||
break;
|
||||
case SOCKS4:
|
||||
LogPrint(eLogError,"--- SOCKS4a rejected because it's actually SOCKS4");
|
||||
break;
|
||||
LogPrint(eLogError, "SOCKS: request with v4a rejected because it's actually SOCKS4");
|
||||
break;
|
||||
}
|
||||
SocksRequestFailed(SOCKS5_ADDR_UNSUP);
|
||||
return false;
|
||||
}
|
||||
//TODO: we may want to support other domains
|
||||
if(m_addrtype == ADDR_DNS && m_address.dns.ToString().find(".i2p") == std::string::npos) {
|
||||
LogPrint(eLogError,"--- SOCKS invalid hostname: ", m_address.dns.ToString());
|
||||
SocksRequestFailed(SOCKS5_ADDR_UNSUP);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SOCKSHandler::HandleData(uint8_t *sock_buff, std::size_t len)
|
||||
{
|
||||
assert(len); // This should always be called with a least a byte left to parse
|
||||
while (len > 0) {
|
||||
switch (m_state) {
|
||||
while (len > 0)
|
||||
{
|
||||
switch (m_state)
|
||||
{
|
||||
case GET_SOCKSV:
|
||||
m_socksv = (SOCKSHandler::socksVersions) *sock_buff;
|
||||
switch (*sock_buff) {
|
||||
switch (*sock_buff)
|
||||
{
|
||||
case SOCKS4:
|
||||
EnterState(GET_COMMAND); //Initialize the parser at the right position
|
||||
break;
|
||||
break;
|
||||
case SOCKS5:
|
||||
EnterState(GET5_AUTHNUM); //Initialize the parser at the right position
|
||||
break;
|
||||
break;
|
||||
default:
|
||||
LogPrint(eLogError,"--- SOCKS rejected invalid version: ", ((int)*sock_buff));
|
||||
LogPrint(eLogError, "SOCKS: rejected invalid version: ", ((int)*sock_buff));
|
||||
Terminate();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case GET5_AUTHNUM:
|
||||
EnterState(GET5_AUTH, *sock_buff);
|
||||
break;
|
||||
break;
|
||||
case GET5_AUTH:
|
||||
m_parseleft --;
|
||||
if (*sock_buff == AUTH_NONE)
|
||||
m_authchosen = AUTH_NONE;
|
||||
if ( m_parseleft == 0 ) {
|
||||
if ( m_parseleft == 0 )
|
||||
{
|
||||
if (!Socks5ChooseAuth()) return false;
|
||||
EnterState(GET5_REQUESTV);
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case GET_COMMAND:
|
||||
switch (*sock_buff) {
|
||||
switch (*sock_buff)
|
||||
{
|
||||
case CMD_CONNECT:
|
||||
case CMD_BIND:
|
||||
break;
|
||||
break;
|
||||
case CMD_UDP:
|
||||
if (m_socksv == SOCKS5) break;
|
||||
default:
|
||||
LogPrint(eLogError,"--- SOCKS invalid command: ", ((int)*sock_buff));
|
||||
LogPrint(eLogError, "SOCKS: invalid command: ", ((int)*sock_buff));
|
||||
SocksRequestFailed(SOCKS5_GEN_FAIL);
|
||||
return false;
|
||||
}
|
||||
m_cmd = (SOCKSHandler::cmdTypes)*sock_buff;
|
||||
switch (m_socksv) {
|
||||
switch (m_socksv)
|
||||
{
|
||||
case SOCKS5: EnterState(GET5_GETRSV); break;
|
||||
case SOCKS4: EnterState(GET_PORT); break;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case GET_PORT:
|
||||
m_port = (m_port << 8)|((uint16_t)*sock_buff);
|
||||
m_parseleft--;
|
||||
if (m_parseleft == 0) {
|
||||
switch (m_socksv) {
|
||||
case SOCKS5: EnterState(DONE); break;
|
||||
if (m_parseleft == 0)
|
||||
{
|
||||
switch (m_socksv)
|
||||
{
|
||||
case SOCKS5: EnterState(READY); break;
|
||||
case SOCKS4: EnterState(GET_IPV4); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case GET_IPV4:
|
||||
m_address.ip = (m_address.ip << 8)|((uint32_t)*sock_buff);
|
||||
m_parseleft--;
|
||||
if (m_parseleft == 0) {
|
||||
switch (m_socksv) {
|
||||
if (m_parseleft == 0)
|
||||
{
|
||||
switch (m_socksv)
|
||||
{
|
||||
case SOCKS5: EnterState(GET_PORT); break;
|
||||
case SOCKS4: EnterState(GET4_IDENT); m_4aip = m_address.ip; break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case GET4_IDENT:
|
||||
if (!*sock_buff) {
|
||||
if( m_4aip == 0 || m_4aip > 255 ) {
|
||||
EnterState(DONE);
|
||||
} else {
|
||||
if (!*sock_buff)
|
||||
{
|
||||
if( m_4aip == 0 || m_4aip > 255 )
|
||||
EnterState(READY);
|
||||
else
|
||||
EnterState(GET4A_HOST);
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case GET4A_HOST:
|
||||
if (!*sock_buff) {
|
||||
EnterState(DONE);
|
||||
if (!*sock_buff)
|
||||
{
|
||||
EnterState(READY);
|
||||
break;
|
||||
}
|
||||
if (m_address.dns.size >= max_socks_hostname_size) {
|
||||
LogPrint(eLogError,"--- SOCKS4a destination is too large");
|
||||
if (m_address.dns.size >= max_socks_hostname_size)
|
||||
{
|
||||
LogPrint(eLogError, "SOCKS: v4a req failed: destination is too large");
|
||||
SocksRequestFailed(SOCKS4_FAIL);
|
||||
return false;
|
||||
}
|
||||
m_address.dns.push_back(*sock_buff);
|
||||
break;
|
||||
break;
|
||||
case GET5_REQUESTV:
|
||||
if (*sock_buff != SOCKS5) {
|
||||
LogPrint(eLogError,"--- SOCKS5 rejected unknown request version: ", ((int)*sock_buff));
|
||||
if (*sock_buff != SOCKS5)
|
||||
{
|
||||
LogPrint(eLogError,"SOCKS: v5 rejected unknown request version: ", ((int)*sock_buff));
|
||||
SocksRequestFailed(SOCKS5_GEN_FAIL);
|
||||
return false;
|
||||
}
|
||||
EnterState(GET_COMMAND);
|
||||
break;
|
||||
break;
|
||||
case GET5_GETRSV:
|
||||
if ( *sock_buff != 0 ) {
|
||||
LogPrint(eLogError,"--- SOCKS5 unknown reserved field: ", ((int)*sock_buff));
|
||||
if ( *sock_buff != 0 )
|
||||
{
|
||||
LogPrint(eLogError, "SOCKS: v5 unknown reserved field: ", ((int)*sock_buff));
|
||||
SocksRequestFailed(SOCKS5_GEN_FAIL);
|
||||
return false;
|
||||
}
|
||||
EnterState(GET5_GETADDRTYPE);
|
||||
break;
|
||||
break;
|
||||
case GET5_GETADDRTYPE:
|
||||
switch (*sock_buff) {
|
||||
switch (*sock_buff)
|
||||
{
|
||||
case ADDR_IPV4: EnterState(GET_IPV4); break;
|
||||
case ADDR_IPV6: EnterState(GET5_IPV6); break;
|
||||
case ADDR_DNS : EnterState(GET5_HOST_SIZE); break;
|
||||
default:
|
||||
LogPrint(eLogError,"--- SOCKS5 unknown address type: ", ((int)*sock_buff));
|
||||
LogPrint(eLogError, "SOCKS: v5 unknown address type: ", ((int)*sock_buff));
|
||||
SocksRequestFailed(SOCKS5_GEN_FAIL);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case GET5_IPV6:
|
||||
m_address.ipv6[16-m_parseleft] = *sock_buff;
|
||||
m_parseleft--;
|
||||
if (m_parseleft == 0) EnterState(GET_PORT);
|
||||
break;
|
||||
break;
|
||||
case GET5_HOST_SIZE:
|
||||
EnterState(GET5_HOST, *sock_buff);
|
||||
break;
|
||||
break;
|
||||
case GET5_HOST:
|
||||
m_address.dns.push_back(*sock_buff);
|
||||
m_parseleft--;
|
||||
if (m_parseleft == 0) EnterState(GET_PORT);
|
||||
break;
|
||||
break;
|
||||
default:
|
||||
LogPrint(eLogError,"--- SOCKS parse state?? ", m_state);
|
||||
LogPrint(eLogError, "SOCKS: parse state?? ", m_state);
|
||||
Terminate();
|
||||
return false;
|
||||
}
|
||||
sock_buff++;
|
||||
len--;
|
||||
if (m_state == DONE) {
|
||||
if (m_state == READY)
|
||||
{
|
||||
m_remaining_data_len = len;
|
||||
m_remaining_data = sock_buff;
|
||||
return ValidateSOCKSRequest();
|
||||
@@ -449,40 +553,52 @@ namespace proxy
|
||||
|
||||
void SOCKSHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len)
|
||||
{
|
||||
LogPrint(eLogDebug,"--- SOCKS sock recv: ", len);
|
||||
if(ecode) {
|
||||
LogPrint(eLogWarning," --- SOCKS sock recv got error: ", ecode);
|
||||
Terminate();
|
||||
LogPrint(eLogDebug, "SOCKS: recieved ", len, " bytes");
|
||||
if(ecode)
|
||||
{
|
||||
LogPrint(eLogWarning, "SOCKS: recv got error: ", ecode);
|
||||
Terminate();
|
||||
return;
|
||||
}
|
||||
|
||||
if (HandleData(m_sock_buff, len)) {
|
||||
if (m_state == DONE) {
|
||||
LogPrint(eLogInfo,"--- SOCKS requested ", m_address.dns.ToString(), ":" , m_port);
|
||||
GetOwner()->CreateStream ( std::bind (&SOCKSHandler::HandleStreamRequestComplete,
|
||||
shared_from_this(), std::placeholders::_1), m_address.dns.ToString(), m_port);
|
||||
} else {
|
||||
if (HandleData(m_sock_buff, len))
|
||||
{
|
||||
if (m_state == READY)
|
||||
{
|
||||
const std::string addr = m_address.dns.ToString();
|
||||
LogPrint(eLogInfo, "SOCKS: requested ", addr, ":" , m_port);
|
||||
const size_t addrlen = addr.size();
|
||||
// does it end with .i2p?
|
||||
if ( addr.rfind(".i2p") == addrlen - 4) {
|
||||
// yes it does, make an i2p session
|
||||
GetOwner()->CreateStream ( std::bind (&SOCKSHandler::HandleStreamRequestComplete,
|
||||
shared_from_this(), std::placeholders::_1), m_address.dns.ToString(), m_port);
|
||||
} else if (m_UseUpstreamProxy) {
|
||||
// forward it to upstream proxy
|
||||
ForwardSOCKS();
|
||||
} else {
|
||||
// no upstream proxy
|
||||
SocksRequestFailed(SOCKS5_ADDR_UNSUP);
|
||||
}
|
||||
}
|
||||
else
|
||||
AsyncSockRead();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SOCKSHandler::SentSocksFailed(const boost::system::error_code & ecode)
|
||||
{
|
||||
if (!ecode) {
|
||||
Terminate();
|
||||
} else {
|
||||
LogPrint (eLogError,"--- SOCKS Closing socket after sending failure because: ", ecode.message ());
|
||||
Terminate();
|
||||
}
|
||||
if (ecode)
|
||||
LogPrint (eLogError, "SOCKS: closing socket after sending failure because: ", ecode.message ());
|
||||
Terminate();
|
||||
}
|
||||
|
||||
void SOCKSHandler::SentSocksDone(const boost::system::error_code & ecode)
|
||||
{
|
||||
if (!ecode) {
|
||||
if (Kill()) return;
|
||||
LogPrint (eLogInfo,"--- SOCKS New I2PTunnel connection");
|
||||
if (!ecode)
|
||||
{
|
||||
if (Kill()) return;
|
||||
LogPrint (eLogInfo, "SOCKS: new I2PTunnel connection");
|
||||
auto connection = std::make_shared<i2p::client::I2PTunnelConnection>(GetOwner(), m_sock, m_stream);
|
||||
GetOwner()->AddHandler (connection);
|
||||
connection->I2PConnect (m_remaining_data,m_remaining_data_len);
|
||||
@@ -490,39 +606,186 @@ namespace proxy
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError,"--- SOCKS Closing socket after completion reply because: ", ecode.message ());
|
||||
LogPrint (eLogError, "SOCKS: closing socket after completion reply because: ", ecode.message ());
|
||||
Terminate();
|
||||
}
|
||||
}
|
||||
|
||||
void SOCKSHandler::SentSocksResponse(const boost::system::error_code & ecode)
|
||||
{
|
||||
if (ecode) {
|
||||
LogPrint (eLogError,"--- SOCKS Closing socket after sending reply because: ", ecode.message ());
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "SOCKS: closing socket after sending reply because: ", ecode.message ());
|
||||
Terminate();
|
||||
}
|
||||
}
|
||||
|
||||
void SOCKSHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
|
||||
{
|
||||
if (stream) {
|
||||
if (stream)
|
||||
{
|
||||
m_stream = stream;
|
||||
SocksRequestSuccess();
|
||||
} else {
|
||||
LogPrint (eLogError,"--- SOCKS Issue when creating the stream, check the previous warnings for more info.");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "SOCKS: error when creating the stream, check the previous warnings for more info");
|
||||
SocksRequestFailed(SOCKS5_HOST_UNREACH);
|
||||
}
|
||||
}
|
||||
|
||||
SOCKSServer::SOCKSServer(int port) :
|
||||
TCPIPAcceptor (port, i2p::client::context.GetSharedLocalDestination ())
|
||||
|
||||
void SOCKSHandler::ForwardSOCKS()
|
||||
{
|
||||
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) );
|
||||
m_proxy_resolver.async_resolve(q, std::bind(&SOCKSHandler::HandleUpstreamResolved, shared_from_this(),
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void SOCKSHandler::AsyncUpstreamSockRead()
|
||||
{
|
||||
LogPrint(eLogDebug, "SOCKS: async upstream sock read");
|
||||
if (m_upstreamSock) {
|
||||
m_upstreamSock->async_read_some(boost::asio::buffer(m_upstream_response, SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE),
|
||||
std::bind(&SOCKSHandler::HandleUpstreamSockRecv, shared_from_this(),
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
} else {
|
||||
LogPrint(eLogError, "SOCKS: no upstream socket for read");
|
||||
SocksRequestFailed(SOCKS5_GEN_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<i2p::client::I2PServiceHandler> SOCKSServer::CreateHandler(boost::asio::ip::tcp::socket * socket)
|
||||
void SOCKSHandler::HandleUpstreamSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered)
|
||||
{
|
||||
return std::make_shared<SOCKSHandler> (this, socket);
|
||||
if (ecode) {
|
||||
if (m_state == UPSTREAM_HANDSHAKE ) {
|
||||
// we are trying to handshake but it failed
|
||||
SocksRequestFailed(SOCKS5_NET_UNREACH);
|
||||
} else {
|
||||
LogPrint(eLogError, "SOCKS: bad state when reading from upstream: ", (int) m_state);
|
||||
}
|
||||
return;
|
||||
}
|
||||
HandleUpstreamData(m_upstream_response, bytes_transfered);
|
||||
}
|
||||
|
||||
void SOCKSHandler::SocksUpstreamSuccess()
|
||||
{
|
||||
LogPrint(eLogInfo, "SOCKS: upstream success");
|
||||
boost::asio::const_buffers_1 response(nullptr, 0);
|
||||
switch (m_socksv)
|
||||
{
|
||||
case SOCKS4:
|
||||
LogPrint(eLogInfo, "SOCKS: v4 connection success");
|
||||
response = GenerateSOCKS4Response(SOCKS4_OK, m_4aip, m_port);
|
||||
break;
|
||||
case SOCKS5:
|
||||
LogPrint(eLogInfo, "SOCKS: v5 connection success");
|
||||
//HACK only 16 bits passed in port as SOCKS5 doesn't allow for more
|
||||
response = GenerateSOCKS5Response(SOCKS5_OK, ADDR_DNS, m_address, m_port);
|
||||
break;
|
||||
}
|
||||
m_sock->send(response);
|
||||
auto forwarder = std::make_shared<i2p::client::TCPIPPipe>(GetOwner(), m_sock, m_upstreamSock);
|
||||
m_upstreamSock = nullptr;
|
||||
m_sock = nullptr;
|
||||
GetOwner()->AddHandler(forwarder);
|
||||
forwarder->Start();
|
||||
Terminate();
|
||||
|
||||
}
|
||||
|
||||
void SOCKSHandler::HandleUpstreamData(uint8_t * dataptr, std::size_t len)
|
||||
{
|
||||
if (m_state == UPSTREAM_HANDSHAKE) {
|
||||
m_upstream_response_len += len;
|
||||
// handle handshake data
|
||||
if (m_upstream_response_len < SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE) {
|
||||
// too small, continue reading
|
||||
AsyncUpstreamSockRead();
|
||||
} else if (len == SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE) {
|
||||
// just right
|
||||
uint8_t resp = m_upstream_response[1];
|
||||
if (resp == SOCKS4_OK) {
|
||||
// we have connected !
|
||||
SocksUpstreamSuccess();
|
||||
} else {
|
||||
// upstream failure
|
||||
LogPrint(eLogError, "SOCKS: upstream proxy failure: ", (int) resp);
|
||||
// TODO: runtime error?
|
||||
SocksRequestFailed(SOCKS5_GEN_FAIL);
|
||||
}
|
||||
} else {
|
||||
// too big
|
||||
SocksRequestFailed(SOCKS5_GEN_FAIL);
|
||||
}
|
||||
} else {
|
||||
// invalid state
|
||||
LogPrint(eLogError, "SOCKS: invalid state reading from upstream: ", (int) m_state);
|
||||
}
|
||||
}
|
||||
|
||||
void SOCKSHandler::SendUpstreamRequest()
|
||||
{
|
||||
LogPrint(eLogInfo, "SOCKS: negotiating with upstream proxy");
|
||||
EnterState(UPSTREAM_HANDSHAKE);
|
||||
if (m_upstreamSock) {
|
||||
boost::asio::write(*m_upstreamSock,
|
||||
GenerateUpstreamRequest());
|
||||
AsyncUpstreamSockRead();
|
||||
} else {
|
||||
LogPrint(eLogError, "SOCKS: no upstream socket to send handshake to");
|
||||
}
|
||||
}
|
||||
|
||||
void SOCKSHandler::HandleUpstreamConnected(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr)
|
||||
{
|
||||
if (ecode) {
|
||||
LogPrint(eLogWarning, "SOCKS: could not connect to upstream proxy: ", ecode.message());
|
||||
SocksRequestFailed(SOCKS5_NET_UNREACH);
|
||||
return;
|
||||
}
|
||||
LogPrint(eLogInfo, "SOCKS: connected to upstream proxy");
|
||||
SendUpstreamRequest();
|
||||
}
|
||||
|
||||
void SOCKSHandler::HandleUpstreamResolved(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr)
|
||||
{
|
||||
if (ecode) {
|
||||
// error resolving
|
||||
LogPrint(eLogWarning, "SOCKS: upstream proxy", m_UpstreamProxyAddress, " not resolved: ", ecode.message());
|
||||
SocksRequestFailed(SOCKS5_NET_UNREACH);
|
||||
return;
|
||||
}
|
||||
LogPrint(eLogInfo, "SOCKS: upstream proxy resolved");
|
||||
EnterState(UPSTREAM_CONNECT);
|
||||
auto & service = GetOwner()->GetService();
|
||||
m_upstreamSock = std::make_shared<boost::asio::ip::tcp::socket>(service);
|
||||
boost::asio::async_connect(*m_upstreamSock, itr,
|
||||
std::bind(&SOCKSHandler::HandleUpstreamConnected,
|
||||
shared_from_this(), std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
SOCKSServer::SOCKSServer(const std::string& address, int port, const std::string& outAddress, uint16_t outPort,
|
||||
std::shared_ptr<i2p::client::ClientDestination> localDestination) :
|
||||
TCPIPAcceptor (address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ())
|
||||
{
|
||||
m_UseUpstreamProxy = false;
|
||||
if (outAddress.length() > 0)
|
||||
SetUpstreamProxy(outAddress, outPort);
|
||||
}
|
||||
|
||||
std::shared_ptr<i2p::client::I2PServiceHandler> SOCKSServer::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
|
||||
{
|
||||
return std::make_shared<SOCKSHandler> (this, socket, m_UpstreamProxyAddress, m_UpstreamProxyPort, m_UseUpstreamProxy);
|
||||
}
|
||||
|
||||
void SOCKSServer::SetUpstreamProxy(const std::string & addr, const uint16_t port)
|
||||
{
|
||||
m_UpstreamProxyAddress = addr;
|
||||
m_UpstreamProxyPort = port;
|
||||
m_UseUpstreamProxy = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
17
SOCKS.h
17
SOCKS.h
@@ -13,14 +13,23 @@ namespace proxy
|
||||
{
|
||||
class SOCKSServer: public i2p::client::TCPIPAcceptor
|
||||
{
|
||||
public:
|
||||
|
||||
SOCKSServer(const std::string& address, int port, const std::string& outAddress, uint16_t outPort,
|
||||
std::shared_ptr<i2p::client::ClientDestination> localDestination = nullptr);
|
||||
~SOCKSServer() {};
|
||||
|
||||
void SetUpstreamProxy(const std::string & addr, const uint16_t port);
|
||||
|
||||
protected:
|
||||
// Implements TCPIPAcceptor
|
||||
std::shared_ptr<i2p::client::I2PServiceHandler> CreateHandler(boost::asio::ip::tcp::socket * socket);
|
||||
std::shared_ptr<i2p::client::I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket);
|
||||
const char* GetName() { return "SOCKS"; }
|
||||
|
||||
public:
|
||||
SOCKSServer(int port);
|
||||
~SOCKSServer() {}
|
||||
private:
|
||||
std::string m_UpstreamProxyAddress;
|
||||
uint16_t m_UpstreamProxyPort;
|
||||
bool m_UseUpstreamProxy;
|
||||
};
|
||||
|
||||
typedef SOCKSServer SOCKSProxy;
|
||||
|
||||
434
SSU.cpp
434
SSU.cpp
@@ -3,18 +3,38 @@
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
#include "RouterContext.h"
|
||||
#include "NetDb.h"
|
||||
#include "SSU.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
SSUServer::SSUServer (int port): m_Thread (nullptr), m_ThreadV6 (nullptr), m_ReceiversThread (nullptr),
|
||||
|
||||
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_SocketV6.open (boost::asio::ip::udp::v6());
|
||||
m_SocketV6.set_option (boost::asio::ip::v6_only (true));
|
||||
m_SocketV6.set_option (boost::asio::socket_base::receive_buffer_size (65535));
|
||||
m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (65535));
|
||||
m_SocketV6.bind (m_EndpointV6);
|
||||
}
|
||||
|
||||
SSUServer::SSUServer (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_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_IntroducersUpdateTimer (m_Service), m_PeerTestsCleanupTimer (m_Service)
|
||||
{
|
||||
|
||||
m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (65535));
|
||||
m_Socket.set_option (boost::asio::socket_base::send_buffer_size (65535));
|
||||
if (context.SupportsV6 ())
|
||||
@@ -34,16 +54,19 @@ namespace transport
|
||||
void SSUServer::Start ()
|
||||
{
|
||||
m_IsRunning = true;
|
||||
m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this));
|
||||
m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
|
||||
m_ReceiversService.post (std::bind (&SSUServer::Receive, this));
|
||||
m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this));
|
||||
if (!m_OnlyV6)
|
||||
{
|
||||
m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
|
||||
m_ReceiversService.post (std::bind (&SSUServer::Receive, this));
|
||||
}
|
||||
if (context.SupportsV6 ())
|
||||
{
|
||||
m_ThreadV6 = new std::thread (std::bind (&SSUServer::RunV6, this));
|
||||
m_ReceiversService.post (std::bind (&SSUServer::ReceiveV6, this));
|
||||
}
|
||||
if (i2p::context.IsUnreachable ())
|
||||
ScheduleIntroducersUpdateTimer ();
|
||||
m_ReceiversService.post (std::bind (&SSUServer::ReceiveV6, this));
|
||||
}
|
||||
SchedulePeerTestsCleanupTimer ();
|
||||
ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers
|
||||
}
|
||||
|
||||
void SSUServer::Stop ()
|
||||
@@ -85,7 +108,7 @@ namespace transport
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "SSU server: ", ex.what ());
|
||||
LogPrint (eLogError, "SSU: server runtime exception: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,7 +123,7 @@ namespace transport
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "SSU V6 server: ", ex.what ());
|
||||
LogPrint (eLogError, "SSU: v6 server runtime exception: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,7 +138,7 @@ namespace transport
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "SSU receivers: ", ex.what ());
|
||||
LogPrint (eLogError, "SSU: receivers runtime exception: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,12 +196,12 @@ namespace transport
|
||||
moreBytes = m_Socket.available();
|
||||
}
|
||||
|
||||
m_Service.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets));
|
||||
m_Service.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets, &m_Sessions));
|
||||
Receive ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("SSU receive error: ", ecode.message ());
|
||||
LogPrint (eLogError, "SSU: receive error: ", ecode.message ());
|
||||
delete packet;
|
||||
}
|
||||
}
|
||||
@@ -200,40 +223,46 @@ namespace transport
|
||||
moreBytes = m_SocketV6.available();
|
||||
}
|
||||
|
||||
m_ServiceV6.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets));
|
||||
m_ServiceV6.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets, &m_SessionsV6));
|
||||
ReceiveV6 ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("SSU V6 receive error: ", ecode.message ());
|
||||
LogPrint (eLogError, "SSU: v6 receive error: ", ecode.message ());
|
||||
delete packet;
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::HandleReceivedPackets (std::vector<SSUPacket *> packets)
|
||||
void SSUServer::HandleReceivedPackets (std::vector<SSUPacket *> packets,
|
||||
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > * sessions)
|
||||
{
|
||||
std::shared_ptr<SSUSession> session;
|
||||
for (auto it1: packets)
|
||||
for (auto& packet: packets)
|
||||
{
|
||||
auto packet = it1;
|
||||
if (!session || session->GetRemoteEndpoint () != packet->from) // we received packet for other session than previous
|
||||
{
|
||||
if (session) session->FlushData ();
|
||||
auto it = m_Sessions.find (packet->from);
|
||||
if (it != m_Sessions.end ())
|
||||
session = it->second;
|
||||
if (!session)
|
||||
try
|
||||
{
|
||||
if (!session || session->GetRemoteEndpoint () != packet->from) // we received packet for other session than previous
|
||||
{
|
||||
session = std::make_shared<SSUSession> (*this, packet->from);
|
||||
session->WaitForConnect ();
|
||||
if (session) session->FlushData ();
|
||||
auto it = sessions->find (packet->from);
|
||||
if (it != sessions->end ())
|
||||
session = it->second;
|
||||
if (!session)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
m_Sessions[packet->from] = session;
|
||||
}
|
||||
LogPrint ("New SSU session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created");
|
||||
session = std::make_shared<SSUSession> (*this, packet->from);
|
||||
session->WaitForConnect ();
|
||||
(*sessions)[packet->from] = session;
|
||||
LogPrint (eLogDebug, "SSU: new session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created");
|
||||
}
|
||||
}
|
||||
}
|
||||
session->ProcessNextMessage (packet->buf, packet->len, packet->from);
|
||||
session->ProcessNextMessage (packet->buf, packet->len, packet->from);
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: HandleReceivedPackets ", ex.what ());
|
||||
if (session) session->FlushData ();
|
||||
session = nullptr;
|
||||
}
|
||||
delete packet;
|
||||
}
|
||||
if (session) session->FlushData ();
|
||||
@@ -255,96 +284,135 @@ namespace transport
|
||||
|
||||
std::shared_ptr<SSUSession> SSUServer::FindSession (const boost::asio::ip::udp::endpoint& e) const
|
||||
{
|
||||
auto it = m_Sessions.find (e);
|
||||
if (it != m_Sessions.end ())
|
||||
auto& sessions = e.address ().is_v6 () ? m_SessionsV6 : m_Sessions;
|
||||
auto it = sessions.find (e);
|
||||
if (it != sessions.end ())
|
||||
return it->second;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<SSUSession> SSUServer::GetSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest)
|
||||
|
||||
void SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest)
|
||||
{
|
||||
auto address = router->GetSSUAddress (!context.SupportsV6 ());
|
||||
if (address)
|
||||
CreateSession (router, address->host, address->port, peerTest);
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU: Router ", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), " doesn't have SSU address");
|
||||
}
|
||||
|
||||
void SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||
const boost::asio::ip::address& addr, int port, bool peerTest)
|
||||
{
|
||||
std::shared_ptr<SSUSession> session;
|
||||
if (router)
|
||||
{
|
||||
auto address = router->GetSSUAddress (!context.SupportsV6 ());
|
||||
if (router->UsesIntroducer ())
|
||||
m_Service.post (std::bind (&SSUServer::CreateSessionThroughIntroducer, this, router, peerTest)); // always V4 thread
|
||||
else
|
||||
{
|
||||
boost::asio::ip::udp::endpoint remoteEndpoint (addr, port);
|
||||
auto& s = addr.is_v6 () ? m_ServiceV6 : m_Service;
|
||||
s.post (std::bind (&SSUServer::CreateDirectSession, this, router, remoteEndpoint, peerTest));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::CreateDirectSession (std::shared_ptr<const i2p::data::RouterInfo> router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest)
|
||||
{
|
||||
auto& sessions = remoteEndpoint.address ().is_v6 () ? m_SessionsV6 : m_Sessions;
|
||||
auto it = sessions.find (remoteEndpoint);
|
||||
if (it != sessions.end ())
|
||||
{
|
||||
auto session = it->second;
|
||||
if (peerTest && session->GetState () == eSessionStateEstablished)
|
||||
session->SendPeerTest ();
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise create new session
|
||||
auto session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
|
||||
sessions[remoteEndpoint] = session;
|
||||
// connect
|
||||
LogPrint (eLogDebug, "SSU: Creating new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), "] ",
|
||||
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ());
|
||||
session->Connect ();
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest)
|
||||
{
|
||||
if (router && router->UsesIntroducer ())
|
||||
{
|
||||
auto address = router->GetSSUAddress (true); // v4 only for now
|
||||
if (address)
|
||||
{
|
||||
boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
|
||||
auto it = m_Sessions.find (remoteEndpoint);
|
||||
// check if session if presented alredy
|
||||
if (it != m_Sessions.end ())
|
||||
session = it->second;
|
||||
else
|
||||
{
|
||||
auto session = it->second;
|
||||
if (peerTest && session->GetState () == eSessionStateEstablished)
|
||||
session->SendPeerTest ();
|
||||
return;
|
||||
}
|
||||
// create new session
|
||||
int numIntroducers = address->introducers.size ();
|
||||
if (numIntroducers > 0)
|
||||
{
|
||||
// otherwise create new session
|
||||
session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
|
||||
std::shared_ptr<SSUSession> introducerSession;
|
||||
const i2p::data::RouterInfo::Introducer * introducer = nullptr;
|
||||
// we might have a session to introducer already
|
||||
for (int i = 0; i < numIntroducers; i++)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
m_Sessions[remoteEndpoint] = session;
|
||||
}
|
||||
if (!router->UsesIntroducer ())
|
||||
{
|
||||
// connect directly
|
||||
LogPrint ("Creating new SSU session to [", router->GetIdentHashAbbreviation (), "] ",
|
||||
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ());
|
||||
session->Connect ();
|
||||
}
|
||||
else
|
||||
{
|
||||
// connect through introducer
|
||||
int numIntroducers = address->introducers.size ();
|
||||
if (numIntroducers > 0)
|
||||
{
|
||||
std::shared_ptr<SSUSession> introducerSession;
|
||||
const i2p::data::RouterInfo::Introducer * introducer = nullptr;
|
||||
// we might have a session to introducer already
|
||||
for (int i = 0; i < numIntroducers; i++)
|
||||
{
|
||||
introducer = &(address->introducers[i]);
|
||||
it = m_Sessions.find (boost::asio::ip::udp::endpoint (introducer->iHost, introducer->iPort));
|
||||
if (it != m_Sessions.end ())
|
||||
{
|
||||
introducerSession = it->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (introducerSession) // session found
|
||||
LogPrint ("Session to introducer already exists");
|
||||
else // create new
|
||||
{
|
||||
LogPrint ("Creating new session to introducer");
|
||||
introducer = &(address->introducers[0]); // TODO:
|
||||
boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort);
|
||||
introducerSession = std::make_shared<SSUSession> (*this, introducerEndpoint, router);
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
m_Sessions[introducerEndpoint] = introducerSession;
|
||||
}
|
||||
// introduce
|
||||
LogPrint ("Introduce new SSU session to [", router->GetIdentHashAbbreviation (),
|
||||
"] through introducer ", introducer->iHost, ":", introducer->iPort);
|
||||
session->WaitForIntroduction ();
|
||||
if (i2p::context.GetRouterInfo ().UsesIntroducer ()) // if we are unreachable
|
||||
{
|
||||
uint8_t buf[1];
|
||||
Send (buf, 0, remoteEndpoint); // send HolePunch
|
||||
}
|
||||
introducerSession->Introduce (introducer->iTag, introducer->iKey);
|
||||
}
|
||||
else
|
||||
auto intr = &(address->introducers[i]);
|
||||
boost::asio::ip::udp::endpoint ep (intr->iHost, intr->iPort);
|
||||
if (ep.address ().is_v4 ()) // ipv4 only
|
||||
{
|
||||
LogPrint (eLogWarning, "Can't connect to unreachable router. No introducers presented");
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
m_Sessions.erase (remoteEndpoint);
|
||||
session.reset ();
|
||||
}
|
||||
if (!introducer) introducer = intr; // we pick first one for now
|
||||
it = m_Sessions.find (ep);
|
||||
if (it != m_Sessions.end ())
|
||||
{
|
||||
introducerSession = it->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!introducer)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no ipv4 introducers present");
|
||||
return;
|
||||
}
|
||||
|
||||
if (introducerSession) // session found
|
||||
LogPrint (eLogWarning, "SSU: Session to introducer already exists");
|
||||
else // create new
|
||||
{
|
||||
LogPrint (eLogDebug, "SSU: Creating new session to introducer ", introducer->iHost);
|
||||
boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort);
|
||||
introducerSession = std::make_shared<SSUSession> (*this, introducerEndpoint, router);
|
||||
m_Sessions[introducerEndpoint] = introducerSession;
|
||||
}
|
||||
// create session
|
||||
auto session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
|
||||
m_Sessions[remoteEndpoint] = session;
|
||||
// introduce
|
||||
LogPrint (eLogInfo, "SSU: Introduce new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()),
|
||||
"] through introducer ", introducer->iHost, ":", introducer->iPort);
|
||||
session->WaitForIntroduction ();
|
||||
if (i2p::context.GetRouterInfo ().UsesIntroducer ()) // if we are unreachable
|
||||
{
|
||||
uint8_t buf[1];
|
||||
Send (buf, 0, remoteEndpoint); // send HolePunch
|
||||
}
|
||||
introducerSession->Introduce (*introducer, router);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no introducers present");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Router ", router->GetIdentHashAbbreviation (), " doesn't have SSU address");
|
||||
LogPrint (eLogWarning, "SSU: Router ", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), " doesn't have SSU address");
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
void SSUServer::DeleteSession (std::shared_ptr<SSUSession> session)
|
||||
@@ -352,40 +420,69 @@ namespace transport
|
||||
if (session)
|
||||
{
|
||||
session->Close ();
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
m_Sessions.erase (session->GetRemoteEndpoint ());
|
||||
auto& ep = session->GetRemoteEndpoint ();
|
||||
if (ep.address ().is_v6 ())
|
||||
m_SessionsV6.erase (ep);
|
||||
else
|
||||
m_Sessions.erase (ep);
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::DeleteAllSessions ()
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
for (auto it: m_Sessions)
|
||||
for (auto& it: m_Sessions)
|
||||
it.second->Close ();
|
||||
m_Sessions.clear ();
|
||||
|
||||
for (auto& it: m_SessionsV6)
|
||||
it.second->Close ();
|
||||
m_SessionsV6.clear ();
|
||||
}
|
||||
|
||||
template<typename Filter>
|
||||
std::shared_ptr<SSUSession> SSUServer::GetRandomSession (Filter filter)
|
||||
std::shared_ptr<SSUSession> SSUServer::GetRandomV4Session (Filter filter) // v4 only
|
||||
{
|
||||
std::vector<std::shared_ptr<SSUSession> > filteredSessions;
|
||||
for (auto s :m_Sessions)
|
||||
for (const auto& s :m_Sessions)
|
||||
if (filter (s.second)) filteredSessions.push_back (s.second);
|
||||
if (filteredSessions.size () > 0)
|
||||
{
|
||||
auto ind = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (0, filteredSessions.size ()-1);
|
||||
auto ind = rand () % filteredSessions.size ();
|
||||
return filteredSessions[ind];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<SSUSession> SSUServer::GetRandomEstablishedSession (std::shared_ptr<const SSUSession> excluded)
|
||||
std::shared_ptr<SSUSession> SSUServer::GetRandomEstablishedV4Session (std::shared_ptr<const SSUSession> excluded) // v4 only
|
||||
{
|
||||
return GetRandomSession (
|
||||
return GetRandomV4Session (
|
||||
[excluded](std::shared_ptr<SSUSession> session)->bool
|
||||
{
|
||||
return session->GetState () == eSessionStateEstablished &&
|
||||
session != excluded;
|
||||
return session->GetState () == eSessionStateEstablished && session != excluded;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
template<typename Filter>
|
||||
std::shared_ptr<SSUSession> SSUServer::GetRandomV6Session (Filter filter) // v6 only
|
||||
{
|
||||
std::vector<std::shared_ptr<SSUSession> > filteredSessions;
|
||||
for (const auto& s :m_SessionsV6)
|
||||
if (filter (s.second)) filteredSessions.push_back (s.second);
|
||||
if (filteredSessions.size () > 0)
|
||||
{
|
||||
auto ind = rand () % filteredSessions.size ();
|
||||
return filteredSessions[ind];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<SSUSession> SSUServer::GetRandomEstablishedV6Session (std::shared_ptr<const SSUSession> excluded) // v6 only
|
||||
{
|
||||
return GetRandomV6Session (
|
||||
[excluded](std::shared_ptr<SSUSession> session)->bool
|
||||
{
|
||||
return session->GetState () == eSessionStateEstablished && session != excluded;
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -396,7 +493,7 @@ namespace transport
|
||||
std::set<SSUSession *> ret;
|
||||
for (int i = 0; i < maxNumIntroducers; i++)
|
||||
{
|
||||
auto session = GetRandomSession (
|
||||
auto session = GetRandomV4Session (
|
||||
[&ret, ts](std::shared_ptr<SSUSession> session)->bool
|
||||
{
|
||||
return session->GetRelayTag () && !ret.count (session.get ()) &&
|
||||
@@ -422,13 +519,22 @@ namespace transport
|
||||
|
||||
void SSUServer::HandleIntroducersUpdateTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (!ecode)
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
// timeout expired
|
||||
if (i2p::context.GetStatus () == eRouterStatusTesting)
|
||||
{
|
||||
// we still don't know if we need introducers
|
||||
ScheduleIntroducersUpdateTimer ();
|
||||
return;
|
||||
}
|
||||
if (i2p::context.GetStatus () == eRouterStatusOK) return; // we don't need introducers anymore
|
||||
// we are firewalled
|
||||
if (!i2p::context.IsUnreachable ()) i2p::context.SetUnreachable ();
|
||||
std::list<boost::asio::ip::udp::endpoint> newList;
|
||||
size_t numIntroducers = 0;
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto it :m_Introducers)
|
||||
for (const auto& it : m_Introducers)
|
||||
{
|
||||
auto session = FindSession (it);
|
||||
if (session && ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION)
|
||||
@@ -445,23 +551,95 @@ namespace transport
|
||||
{
|
||||
// create new
|
||||
auto introducers = FindIntroducers (SSU_MAX_NUM_INTRODUCERS);
|
||||
if (introducers.size () > 0)
|
||||
for (const auto& it1: introducers)
|
||||
{
|
||||
for (auto it1: introducers)
|
||||
const auto& ep = it1->GetRemoteEndpoint ();
|
||||
i2p::data::RouterInfo::Introducer introducer;
|
||||
introducer.iHost = ep.address ();
|
||||
introducer.iPort = ep.port ();
|
||||
introducer.iTag = it1->GetRelayTag ();
|
||||
introducer.iKey = it1->GetIntroKey ();
|
||||
if (i2p::context.AddIntroducer (introducer))
|
||||
{
|
||||
auto router = it1->GetRemoteRouter ();
|
||||
if (router && i2p::context.AddIntroducer (*router, it1->GetRelayTag ()))
|
||||
{
|
||||
newList.push_back (it1->GetRemoteEndpoint ());
|
||||
if (newList.size () >= SSU_MAX_NUM_INTRODUCERS) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
newList.push_back (ep);
|
||||
if (newList.size () >= SSU_MAX_NUM_INTRODUCERS) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_Introducers = newList;
|
||||
if (m_Introducers.size () < SSU_MAX_NUM_INTRODUCERS)
|
||||
{
|
||||
auto introducer = i2p::data::netdb.GetRandomIntroducer ();
|
||||
if (introducer)
|
||||
CreateSession (introducer);
|
||||
}
|
||||
ScheduleIntroducersUpdateTimer ();
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr<SSUSession> session)
|
||||
{
|
||||
m_PeerTests[nonce] = { i2p::util::GetMillisecondsSinceEpoch (), role, session };
|
||||
}
|
||||
|
||||
PeerTestParticipant SSUServer::GetPeerTestParticipant (uint32_t nonce)
|
||||
{
|
||||
auto it = m_PeerTests.find (nonce);
|
||||
if (it != m_PeerTests.end ())
|
||||
return it->second.role;
|
||||
else
|
||||
return ePeerTestParticipantUnknown;
|
||||
}
|
||||
|
||||
std::shared_ptr<SSUSession> SSUServer::GetPeerTestSession (uint32_t nonce)
|
||||
{
|
||||
auto it = m_PeerTests.find (nonce);
|
||||
if (it != m_PeerTests.end ())
|
||||
return it->second.session;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SSUServer::UpdatePeerTest (uint32_t nonce, PeerTestParticipant role)
|
||||
{
|
||||
auto it = m_PeerTests.find (nonce);
|
||||
if (it != m_PeerTests.end ())
|
||||
it->second.role = role;
|
||||
}
|
||||
|
||||
void SSUServer::RemovePeerTest (uint32_t nonce)
|
||||
{
|
||||
m_PeerTests.erase (nonce);
|
||||
}
|
||||
|
||||
void SSUServer::SchedulePeerTestsCleanupTimer ()
|
||||
{
|
||||
m_PeerTestsCleanupTimer.expires_from_now (boost::posix_time::seconds(SSU_PEER_TEST_TIMEOUT));
|
||||
m_PeerTestsCleanupTimer.async_wait (std::bind (&SSUServer::HandlePeerTestsCleanupTimer,
|
||||
this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void SSUServer::HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
int numDeleted = 0;
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
for (auto it = m_PeerTests.begin (); it != m_PeerTests.end ();)
|
||||
{
|
||||
if (ts > it->second.creationTime + SSU_PEER_TEST_TIMEOUT*1000LL)
|
||||
{
|
||||
numDeleted++;
|
||||
it = m_PeerTests.erase (it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
if (numDeleted > 0)
|
||||
LogPrint (eLogDebug, "SSU: ", numDeleted, " peer tests have been expired");
|
||||
SchedulePeerTestsCleanupTimer ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
48
SSU.h
48
SSU.h
@@ -9,7 +9,7 @@
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <boost/asio.hpp>
|
||||
#include "aes.h"
|
||||
#include "Crypto.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "Identity.h"
|
||||
#include "RouterInfo.h"
|
||||
@@ -21,6 +21,7 @@ namespace i2p
|
||||
namespace transport
|
||||
{
|
||||
const int SSU_KEEP_ALIVE_INTERVAL = 30; // 30 seconds
|
||||
const int SSU_PEER_TEST_TIMEOUT = 60; // 60 seconds
|
||||
const int SSU_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour
|
||||
const size_t SSU_MAX_NUM_INTRODUCERS = 3;
|
||||
|
||||
@@ -36,13 +37,18 @@ namespace transport
|
||||
public:
|
||||
|
||||
SSUServer (int port);
|
||||
SSUServer (const boost::asio::ip::address & addr, int port); // ipv6 only constructor
|
||||
~SSUServer ();
|
||||
void Start ();
|
||||
void Stop ();
|
||||
std::shared_ptr<SSUSession> GetSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false);
|
||||
void CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = 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);
|
||||
std::shared_ptr<SSUSession> FindSession (std::shared_ptr<const i2p::data::RouterInfo> router) const;
|
||||
std::shared_ptr<SSUSession> FindSession (const boost::asio::ip::udp::endpoint& e) const;
|
||||
std::shared_ptr<SSUSession> GetRandomEstablishedSession (std::shared_ptr<const SSUSession> excluded);
|
||||
std::shared_ptr<SSUSession> GetRandomEstablishedV4Session (std::shared_ptr<const SSUSession> excluded);
|
||||
std::shared_ptr<SSUSession> GetRandomEstablishedV6Session (std::shared_ptr<const SSUSession> excluded);
|
||||
void DeleteSession (std::shared_ptr<SSUSession> session);
|
||||
void DeleteAllSessions ();
|
||||
|
||||
@@ -53,6 +59,12 @@ namespace transport
|
||||
void AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay);
|
||||
std::shared_ptr<SSUSession> FindRelaySession (uint32_t tag);
|
||||
|
||||
void NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr<SSUSession> session = nullptr);
|
||||
PeerTestParticipant GetPeerTestParticipant (uint32_t nonce);
|
||||
std::shared_ptr<SSUSession> GetPeerTestSession (uint32_t nonce);
|
||||
void UpdatePeerTest (uint32_t nonce, PeerTestParticipant role);
|
||||
void RemovePeerTest (uint32_t nonce);
|
||||
|
||||
private:
|
||||
|
||||
void Run ();
|
||||
@@ -62,32 +74,48 @@ namespace transport
|
||||
void ReceiveV6 ();
|
||||
void HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet);
|
||||
void HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet);
|
||||
void HandleReceivedPackets (std::vector<SSUPacket *> packets);
|
||||
void HandleReceivedPackets (std::vector<SSUPacket *> packets,
|
||||
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> >* sessions);
|
||||
|
||||
void CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false);
|
||||
template<typename Filter>
|
||||
std::shared_ptr<SSUSession> GetRandomSession (Filter filter);
|
||||
|
||||
std::shared_ptr<SSUSession> GetRandomV4Session (Filter filter);
|
||||
template<typename Filter>
|
||||
std::shared_ptr<SSUSession> GetRandomV6Session (Filter filter);
|
||||
|
||||
std::set<SSUSession *> FindIntroducers (int maxNumIntroducers);
|
||||
void ScheduleIntroducersUpdateTimer ();
|
||||
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode);
|
||||
|
||||
|
||||
void SchedulePeerTestsCleanupTimer ();
|
||||
void HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode);
|
||||
|
||||
private:
|
||||
|
||||
struct PeerTest
|
||||
{
|
||||
uint64_t creationTime;
|
||||
PeerTestParticipant role;
|
||||
std::shared_ptr<SSUSession> session; // for Bob to Alice
|
||||
};
|
||||
|
||||
bool m_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;
|
||||
boost::asio::ip::udp::endpoint m_Endpoint, m_EndpointV6;
|
||||
boost::asio::ip::udp::socket m_Socket, m_SocketV6;
|
||||
boost::asio::deadline_timer m_IntroducersUpdateTimer;
|
||||
boost::asio::deadline_timer m_IntroducersUpdateTimer, m_PeerTestsCleanupTimer;
|
||||
std::list<boost::asio::ip::udp::endpoint> m_Introducers; // introducers we are connected to
|
||||
std::mutex m_SessionsMutex;
|
||||
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > m_Sessions;
|
||||
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, PeerTest> m_PeerTests; // nonce -> creation time in milliseconds
|
||||
|
||||
public:
|
||||
// for HTTP only
|
||||
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
|
||||
const decltype(m_SessionsV6)& GetSessionsV6 () const { return m_SessionsV6; };
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
154
SSUData.cpp
154
SSUData.cpp
@@ -10,15 +10,26 @@ namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
SSUData::SSUData (SSUSession& session):
|
||||
m_Session (session), m_ResendTimer (session.GetService ()), m_DecayTimer (session.GetService ()),
|
||||
m_IncompleteMessagesCleanupTimer (session.GetService ())
|
||||
void IncompleteMessage::AttachNextFragment (const uint8_t * fragment, size_t fragmentSize)
|
||||
{
|
||||
if (msg->len + fragmentSize > msg->maxLen)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: I2NP message size ", msg->maxLen, " is not enough");
|
||||
auto newMsg = NewI2NPMessage ();
|
||||
*newMsg = *msg;
|
||||
msg = newMsg;
|
||||
}
|
||||
if (msg->Concat (fragment, fragmentSize) < fragmentSize)
|
||||
LogPrint (eLogError, "SSU: I2NP buffer overflow ", msg->maxLen);
|
||||
nextFragmentNum++;
|
||||
}
|
||||
|
||||
SSUData::SSUData (SSUSession& session):
|
||||
m_Session (session), m_ResendTimer (session.GetService ()),
|
||||
m_IncompleteMessagesCleanupTimer (session.GetService ()),
|
||||
m_MaxPacketSize (session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE),
|
||||
m_PacketSize (m_MaxPacketSize), m_LastMessageReceivedTime (0)
|
||||
{
|
||||
m_MaxPacketSize = session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE;
|
||||
m_PacketSize = m_MaxPacketSize;
|
||||
auto remoteRouter = session.GetRemoteRouter ();
|
||||
if (remoteRouter)
|
||||
AdjustPacketSize (*remoteRouter);
|
||||
}
|
||||
|
||||
SSUData::~SSUData ()
|
||||
@@ -33,13 +44,13 @@ namespace transport
|
||||
void SSUData::Stop ()
|
||||
{
|
||||
m_ResendTimer.cancel ();
|
||||
m_DecayTimer.cancel ();
|
||||
m_IncompleteMessagesCleanupTimer.cancel ();
|
||||
}
|
||||
|
||||
void SSUData::AdjustPacketSize (const i2p::data::RouterInfo& remoteRouter)
|
||||
void SSUData::AdjustPacketSize (std::shared_ptr<const i2p::data::RouterInfo> remoteRouter)
|
||||
{
|
||||
auto ssuAddress = remoteRouter.GetSSUAddress ();
|
||||
if (remoteRouter) return;
|
||||
auto ssuAddress = remoteRouter->GetSSUAddress ();
|
||||
if (ssuAddress && ssuAddress->mtu)
|
||||
{
|
||||
if (m_Session.IsV6 ())
|
||||
@@ -52,11 +63,11 @@ namespace transport
|
||||
m_PacketSize >>= 4;
|
||||
m_PacketSize <<= 4;
|
||||
if (m_PacketSize > m_MaxPacketSize) m_PacketSize = m_MaxPacketSize;
|
||||
LogPrint ("MTU=", ssuAddress->mtu, " packet size=", m_PacketSize);
|
||||
LogPrint (eLogDebug, "SSU: MTU=", ssuAddress->mtu, " packet size=", m_PacketSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Unexpected MTU ", ssuAddress->mtu);
|
||||
LogPrint (eLogWarning, "SSU: Unexpected MTU ", ssuAddress->mtu);
|
||||
m_PacketSize = m_MaxPacketSize;
|
||||
}
|
||||
}
|
||||
@@ -66,7 +77,7 @@ namespace transport
|
||||
{
|
||||
auto routerInfo = i2p::data::netdb.FindRouter (remoteIdent);
|
||||
if (routerInfo)
|
||||
AdjustPacketSize (*routerInfo);
|
||||
AdjustPacketSize (routerInfo);
|
||||
}
|
||||
|
||||
void SSUData::ProcessSentMessageAck (uint32_t msgID)
|
||||
@@ -145,25 +156,21 @@ namespace transport
|
||||
memcpy (frag + 1, buf, 3);
|
||||
buf += 3;
|
||||
uint32_t fragmentInfo = bufbe32toh (frag); // fragment info
|
||||
uint16_t fragmentSize = fragmentInfo & 0x1FFF; // bits 0 - 13
|
||||
uint16_t fragmentSize = fragmentInfo & 0x3FFF; // bits 0 - 13
|
||||
bool isLast = fragmentInfo & 0x010000; // bit 16
|
||||
uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17
|
||||
if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE)
|
||||
{
|
||||
LogPrint (eLogError, "Fragment size ", fragmentSize, "exceeds max SSU packet size");
|
||||
LogPrint (eLogError, "SSU: Fragment size ", fragmentSize, " exceeds max SSU packet size");
|
||||
return;
|
||||
}
|
||||
|
||||
// find message with msgID
|
||||
I2NPMessage * msg = nullptr;
|
||||
auto it = m_IncompleteMessages.find (msgID);
|
||||
if (it != m_IncompleteMessages.end ())
|
||||
// message exists
|
||||
msg = it->second->msg;
|
||||
else
|
||||
if (it == m_IncompleteMessages.end ())
|
||||
{
|
||||
// create new message
|
||||
msg = NewI2NPMessage ();
|
||||
auto msg = NewI2NPShortMessage ();
|
||||
msg->len -= I2NP_SHORT_HEADER_SIZE;
|
||||
it = m_IncompleteMessages.insert (std::make_pair (msgID,
|
||||
std::unique_ptr<IncompleteMessage>(new IncompleteMessage (msg)))).first;
|
||||
@@ -174,9 +181,7 @@ namespace transport
|
||||
if (fragmentNum == incompleteMessage->nextFragmentNum)
|
||||
{
|
||||
// expected fragment
|
||||
memcpy (msg->buf + msg->len, buf, fragmentSize);
|
||||
msg->len += fragmentSize;
|
||||
incompleteMessage->nextFragmentNum++;
|
||||
incompleteMessage->AttachNextFragment (buf, fragmentSize);
|
||||
if (!isLast && !incompleteMessage->savedFragments.empty ())
|
||||
{
|
||||
// try saved fragments
|
||||
@@ -185,33 +190,31 @@ namespace transport
|
||||
auto& savedFragment = *it1;
|
||||
if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum)
|
||||
{
|
||||
memcpy (msg->buf + msg->len, savedFragment->buf, savedFragment->len);
|
||||
msg->len += savedFragment->len;
|
||||
incompleteMessage->AttachNextFragment (savedFragment->buf, savedFragment->len);
|
||||
isLast = savedFragment->isLast;
|
||||
incompleteMessage->nextFragmentNum++;
|
||||
incompleteMessage->savedFragments.erase (it1++);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (isLast)
|
||||
LogPrint (eLogDebug, "Message ", msgID, " complete");
|
||||
LogPrint (eLogDebug, "SSU: Message ", msgID, " complete");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fragmentNum < incompleteMessage->nextFragmentNum)
|
||||
// duplicate fragment
|
||||
LogPrint (eLogWarning, "Duplicate fragment ", (int)fragmentNum, " of message ", msgID, ". Ignored");
|
||||
LogPrint (eLogWarning, "SSU: Duplicate fragment ", (int)fragmentNum, " of message ", msgID, ", ignored");
|
||||
else
|
||||
{
|
||||
// missing fragment
|
||||
LogPrint (eLogWarning, "Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID);
|
||||
LogPrint (eLogWarning, "SSU: Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID);
|
||||
auto savedFragment = new Fragment (fragmentNum, buf, fragmentSize, isLast);
|
||||
if (incompleteMessage->savedFragments.insert (std::unique_ptr<Fragment>(savedFragment)).second)
|
||||
incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
else
|
||||
LogPrint (eLogWarning, "Fragment ", (int)fragmentNum, " of message ", msgID, " already saved");
|
||||
LogPrint (eLogWarning, "SSU: Fragment ", (int)fragmentNum, " of message ", msgID, " already saved");
|
||||
}
|
||||
isLast = false;
|
||||
}
|
||||
@@ -219,6 +222,7 @@ namespace transport
|
||||
if (isLast)
|
||||
{
|
||||
// delete incomplete message
|
||||
auto msg = incompleteMessage->msg;
|
||||
incompleteMessage->msg = nullptr;
|
||||
m_IncompleteMessages.erase (msgID);
|
||||
// process message
|
||||
@@ -228,30 +232,26 @@ namespace transport
|
||||
{
|
||||
if (!m_ReceivedMessages.count (msgID))
|
||||
{
|
||||
if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES)
|
||||
m_ReceivedMessages.clear ();
|
||||
else
|
||||
ScheduleDecay ();
|
||||
m_ReceivedMessages.insert (msgID);
|
||||
m_Handler.PutNextMessage (msg);
|
||||
m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (!msg->IsExpired ())
|
||||
m_Handler.PutNextMessage (msg);
|
||||
else
|
||||
LogPrint (eLogDebug, "SSU: message expired");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU message ", msgID, " already received");
|
||||
i2p::DeleteI2NPMessage (msg);
|
||||
}
|
||||
LogPrint (eLogWarning, "SSU: Message ", msgID, " already received");
|
||||
}
|
||||
else
|
||||
{
|
||||
// we expect DeliveryStatus
|
||||
if (msg->GetTypeID () == eI2NPDeliveryStatus)
|
||||
{
|
||||
LogPrint ("SSU session established");
|
||||
LogPrint (eLogDebug, "SSU: session established");
|
||||
m_Session.Established ();
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "SSU unexpected message ", (int)msg->GetTypeID ());
|
||||
DeleteI2NPMessage (msg);
|
||||
LogPrint (eLogError, "SSU: unexpected message ", (int)msg->GetTypeID ());
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -270,7 +270,7 @@ namespace transport
|
||||
//uint8_t * start = buf;
|
||||
uint8_t flag = *buf;
|
||||
buf++;
|
||||
LogPrint (eLogDebug, "Process SSU data flags=", (int)flag, " len=", len);
|
||||
LogPrint (eLogDebug, "SSU: Process data, flags=", (int)flag, ", len=", len);
|
||||
// process acks if presented
|
||||
if (flag & (DATA_FLAG_ACK_BITFIELDS_INCLUDED | DATA_FLAG_EXPLICIT_ACKS_INCLUDED))
|
||||
ProcessAcks (buf, flag);
|
||||
@@ -279,20 +279,19 @@ namespace transport
|
||||
{
|
||||
uint8_t extendedDataSize = *buf;
|
||||
buf++; // size
|
||||
LogPrint (eLogDebug, "SSU extended data of ", extendedDataSize, " bytes presented");
|
||||
LogPrint (eLogDebug, "SSU: extended data of ", extendedDataSize, " bytes present");
|
||||
buf += extendedDataSize;
|
||||
}
|
||||
// process data
|
||||
ProcessFragments (buf);
|
||||
}
|
||||
|
||||
void SSUData::Send (i2p::I2NPMessage * msg)
|
||||
void SSUData::Send (std::shared_ptr<i2p::I2NPMessage> msg)
|
||||
{
|
||||
uint32_t msgID = msg->ToSSU ();
|
||||
if (m_SentMessages.count (msgID) > 0)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU message ", msgID, " already sent");
|
||||
DeleteI2NPMessage (msg);
|
||||
LogPrint (eLogWarning, "SSU: message ", msgID, " already sent");
|
||||
return;
|
||||
}
|
||||
if (m_SentMessages.empty ()) // schedule resend at first message only
|
||||
@@ -349,7 +348,7 @@ namespace transport
|
||||
}
|
||||
catch (boost::system::system_error& ec)
|
||||
{
|
||||
LogPrint (eLogError, "Can't send SSU fragment ", ec.what ());
|
||||
LogPrint (eLogWarning, "SSU: Can't send data fragment ", ec.what ());
|
||||
}
|
||||
if (!isLast)
|
||||
{
|
||||
@@ -360,7 +359,6 @@ namespace transport
|
||||
len = 0;
|
||||
fragmentNum++;
|
||||
}
|
||||
DeleteI2NPMessage (msg);
|
||||
}
|
||||
|
||||
void SSUData::SendMsgAck (uint32_t msgID)
|
||||
@@ -371,7 +369,7 @@ namespace transport
|
||||
payload++;
|
||||
*payload = 1; // number of ACKs
|
||||
payload++;
|
||||
*(uint32_t *)(payload) = htobe32 (msgID); // msgID
|
||||
htobe32buf (payload, msgID); // msgID
|
||||
payload += 4;
|
||||
*payload = 0; // number of fragments
|
||||
|
||||
@@ -384,7 +382,7 @@ namespace transport
|
||||
{
|
||||
if (fragmentNum > 64)
|
||||
{
|
||||
LogPrint (eLogWarning, "Fragment number ", fragmentNum, " exceeds 64");
|
||||
LogPrint (eLogWarning, "SSU: Fragment number ", fragmentNum, " exceeds 64");
|
||||
return;
|
||||
}
|
||||
uint8_t buf[64 + 18];
|
||||
@@ -423,57 +421,50 @@ namespace transport
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
int numResent = 0;
|
||||
for (auto it = m_SentMessages.begin (); it != m_SentMessages.end ();)
|
||||
{
|
||||
if (ts >= it->second->nextResendTime)
|
||||
{
|
||||
if (it->second->numResends < MAX_NUM_RESENDS)
|
||||
{
|
||||
{
|
||||
for (auto& f: it->second->fragments)
|
||||
if (f)
|
||||
if (f)
|
||||
{
|
||||
try
|
||||
{
|
||||
{
|
||||
m_Session.Send (f->buf, f->len); // resend
|
||||
numResent++;
|
||||
}
|
||||
catch (boost::system::system_error& ec)
|
||||
{
|
||||
LogPrint (eLogError, "Can't resend SSU fragment ", ec.what ());
|
||||
LogPrint (eLogWarning, "SSU: Can't resend data fragment ", ec.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it->second->numResends++;
|
||||
it->second->nextResendTime += it->second->numResends*RESEND_INTERVAL;
|
||||
it++;
|
||||
++it;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "SSU message has not been ACKed after ", MAX_NUM_RESENDS, " attempts. Deleted");
|
||||
LogPrint (eLogInfo, "SSU: message has not been ACKed after ", MAX_NUM_RESENDS, " attempts, deleted");
|
||||
it = m_SentMessages.erase (it);
|
||||
}
|
||||
}
|
||||
else
|
||||
it++;
|
||||
++it;
|
||||
}
|
||||
ScheduleResend ();
|
||||
if (numResent < MAX_OUTGOING_WINDOW_SIZE)
|
||||
ScheduleResend ();
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "SSU: resend window exceeds max size. Session terminated");
|
||||
m_Session.Close ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUData::ScheduleDecay ()
|
||||
{
|
||||
m_DecayTimer.cancel ();
|
||||
m_DecayTimer.expires_from_now (boost::posix_time::seconds(DECAY_INTERVAL));
|
||||
auto s = m_Session.shared_from_this();
|
||||
m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode)
|
||||
{ s->m_Data.HandleDecayTimer (ecode); });
|
||||
}
|
||||
|
||||
void SSUData::HandleDecayTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
m_ReceivedMessages.clear ();
|
||||
}
|
||||
|
||||
void SSUData::ScheduleIncompleteMessagesCleanup ()
|
||||
{
|
||||
m_IncompleteMessagesCleanupTimer.cancel ();
|
||||
@@ -492,12 +483,17 @@ namespace transport
|
||||
{
|
||||
if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT)
|
||||
{
|
||||
LogPrint (eLogError, "SSU message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds. Deleted");
|
||||
LogPrint (eLogWarning, "SSU: message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted");
|
||||
it = m_IncompleteMessages.erase (it);
|
||||
}
|
||||
else
|
||||
it++;
|
||||
++it;
|
||||
}
|
||||
// decay
|
||||
if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES ||
|
||||
i2p::util::GetSecondsSinceEpoch () > m_LastMessageReceivedTime + DECAY_INTERVAL)
|
||||
m_ReceivedMessages.clear ();
|
||||
|
||||
ScheduleIncompleteMessagesCleanup ();
|
||||
}
|
||||
}
|
||||
|
||||
29
SSUData.h
29
SSUData.h
@@ -5,7 +5,7 @@
|
||||
#include <string.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
#include <memory>
|
||||
#include <boost/asio.hpp>
|
||||
#include "I2NPProtocol.h"
|
||||
@@ -18,7 +18,11 @@ namespace transport
|
||||
{
|
||||
|
||||
const size_t SSU_MTU_V4 = 1484;
|
||||
#ifdef MESHNET
|
||||
const size_t SSU_MTU_V6 = 1286;
|
||||
#else
|
||||
const size_t SSU_MTU_V6 = 1472;
|
||||
#endif
|
||||
const size_t IPV4_HEADER_SIZE = 20;
|
||||
const size_t IPV6_HEADER_SIZE = 40;
|
||||
const size_t UDP_HEADER_SIZE = 8;
|
||||
@@ -27,8 +31,9 @@ namespace transport
|
||||
const int RESEND_INTERVAL = 3; // in seconds
|
||||
const int MAX_NUM_RESENDS = 5;
|
||||
const int DECAY_INTERVAL = 20; // in seconds
|
||||
const int MAX_NUM_RECEIVED_MESSAGES = 1000; // how many msgID we store for duplicates check
|
||||
const int INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds
|
||||
const unsigned int MAX_NUM_RECEIVED_MESSAGES = 1000; // how many msgID we store for duplicates check
|
||||
const int MAX_OUTGOING_WINDOW_SIZE = 200; // how many unacked message we can store
|
||||
// data flags
|
||||
const uint8_t DATA_FLAG_EXTENDED_DATA_INCLUDED = 0x02;
|
||||
const uint8_t DATA_FLAG_WANT_REPLY = 0x04;
|
||||
@@ -59,13 +64,13 @@ namespace transport
|
||||
|
||||
struct IncompleteMessage
|
||||
{
|
||||
I2NPMessage * msg;
|
||||
std::shared_ptr<I2NPMessage> msg;
|
||||
int nextFragmentNum;
|
||||
uint32_t lastFragmentInsertTime; // in seconds
|
||||
std::set<std::unique_ptr<Fragment>, FragmentCmp> savedFragments;
|
||||
|
||||
IncompleteMessage (I2NPMessage * m): msg (m), nextFragmentNum (0), lastFragmentInsertTime (0) {};
|
||||
~IncompleteMessage () { if (msg) DeleteI2NPMessage (msg); };
|
||||
IncompleteMessage (std::shared_ptr<I2NPMessage> m): msg (m), nextFragmentNum (0), lastFragmentInsertTime (0) {};
|
||||
void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize);
|
||||
};
|
||||
|
||||
struct SentMessage
|
||||
@@ -80,7 +85,7 @@ namespace transport
|
||||
{
|
||||
public:
|
||||
|
||||
SSUData (SSUSession& session);
|
||||
SSUData (SSUSession& session);
|
||||
~SSUData ();
|
||||
|
||||
void Start ();
|
||||
@@ -88,8 +93,9 @@ namespace transport
|
||||
|
||||
void ProcessMessage (uint8_t * buf, size_t len);
|
||||
void FlushReceivedMessage ();
|
||||
void Send (i2p::I2NPMessage * msg);
|
||||
void Send (std::shared_ptr<i2p::I2NPMessage> msg);
|
||||
|
||||
void AdjustPacketSize (std::shared_ptr<const i2p::data::RouterInfo> remoteRouter);
|
||||
void UpdatePacketSize (const i2p::data::IdentHash& remoteIdent);
|
||||
|
||||
private:
|
||||
@@ -103,23 +109,20 @@ namespace transport
|
||||
void ScheduleResend ();
|
||||
void HandleResendTimer (const boost::system::error_code& ecode);
|
||||
|
||||
void ScheduleDecay ();
|
||||
void HandleDecayTimer (const boost::system::error_code& ecode);
|
||||
|
||||
void ScheduleIncompleteMessagesCleanup ();
|
||||
void HandleIncompleteMessagesCleanupTimer (const boost::system::error_code& ecode);
|
||||
|
||||
void AdjustPacketSize (const i2p::data::RouterInfo& remoteRouter);
|
||||
|
||||
private:
|
||||
|
||||
SSUSession& m_Session;
|
||||
std::map<uint32_t, std::unique_ptr<IncompleteMessage> > m_IncompleteMessages;
|
||||
std::map<uint32_t, std::unique_ptr<SentMessage> > m_SentMessages;
|
||||
std::set<uint32_t> m_ReceivedMessages;
|
||||
boost::asio::deadline_timer m_ResendTimer, m_DecayTimer, m_IncompleteMessagesCleanupTimer;
|
||||
std::unordered_set<uint32_t> m_ReceivedMessages;
|
||||
boost::asio::deadline_timer m_ResendTimer, m_IncompleteMessagesCleanupTimer;
|
||||
int m_MaxPacketSize, m_PacketSize;
|
||||
i2p::I2NPMessagesHandler m_Handler;
|
||||
uint32_t m_LastMessageReceivedTime; // in second
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
695
SSUSession.cpp
695
SSUSession.cpp
File diff suppressed because it is too large
Load Diff
68
SSUSession.h
68
SSUSession.h
@@ -4,8 +4,7 @@
|
||||
#include <inttypes.h>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include "aes.h"
|
||||
#include "hmac.h"
|
||||
#include "Crypto.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "TransportSession.h"
|
||||
#include "SSUData.h"
|
||||
@@ -14,17 +13,17 @@ namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
#pragma pack(1)
|
||||
const uint8_t SSU_HEADER_EXTENDED_OPTIONS_INCLUDED = 0x04;
|
||||
struct SSUHeader
|
||||
{
|
||||
uint8_t mac[16];
|
||||
uint8_t iv[16];
|
||||
uint8_t flag;
|
||||
uint32_t time;
|
||||
uint8_t time[4];
|
||||
|
||||
uint8_t GetPayloadType () const { return flag >> 4; };
|
||||
bool IsExtendedOptions () const { return flag & SSU_HEADER_EXTENDED_OPTIONS_INCLUDED; };
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds
|
||||
const int SSU_TERMINATION_TIMEOUT = 330; // 5.5 minutes
|
||||
@@ -40,6 +39,9 @@ namespace transport
|
||||
const uint8_t PAYLOAD_TYPE_PEER_TEST = 7;
|
||||
const uint8_t PAYLOAD_TYPE_SESSION_DESTROYED = 8;
|
||||
|
||||
// extended options
|
||||
const uint16_t EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG = 0x0001;
|
||||
|
||||
enum SessionState
|
||||
{
|
||||
eSessionStateUnknown,
|
||||
@@ -49,6 +51,15 @@ namespace transport
|
||||
eSessionStateFailed
|
||||
};
|
||||
|
||||
enum PeerTestParticipant
|
||||
{
|
||||
ePeerTestParticipantUnknown = 0,
|
||||
ePeerTestParticipantAlice1,
|
||||
ePeerTestParticipantAlice2,
|
||||
ePeerTestParticipantBob,
|
||||
ePeerTestParticipantCharlie
|
||||
};
|
||||
|
||||
class SSUServer;
|
||||
class SSUSession: public TransportSession, public std::enable_shared_from_this<SSUSession>
|
||||
{
|
||||
@@ -61,14 +72,14 @@ namespace transport
|
||||
|
||||
void Connect ();
|
||||
void WaitForConnect ();
|
||||
void Introduce (uint32_t iTag, const uint8_t * iKey);
|
||||
void Introduce (const i2p::data::RouterInfo::Introducer& introducer,
|
||||
std::shared_ptr<const i2p::data::RouterInfo> to); // Alice to Charlie
|
||||
void WaitForIntroduction ();
|
||||
void Close ();
|
||||
void Done ();
|
||||
boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
|
||||
bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); };
|
||||
void SendI2NPMessage (I2NPMessage * msg);
|
||||
void SendI2NPMessages (const std::vector<I2NPMessage *>& msgs);
|
||||
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
|
||||
void SendPeerTest (); // Alice
|
||||
|
||||
SessionState GetState () const { return m_State; };
|
||||
@@ -77,6 +88,7 @@ namespace transport
|
||||
|
||||
void SendKeepAlive ();
|
||||
uint32_t GetRelayTag () const { return m_RelayTag; };
|
||||
const i2p::data::RouterInfo::IntroKey& GetIntroKey () const { return m_IntroKey; };
|
||||
uint32_t GetCreationTime () const { return m_CreationTime; };
|
||||
|
||||
void FlushData ();
|
||||
@@ -85,40 +97,39 @@ namespace transport
|
||||
|
||||
boost::asio::io_service& GetService ();
|
||||
void CreateAESandMacKey (const uint8_t * pubKey);
|
||||
|
||||
void PostI2NPMessage (I2NPMessage * msg);
|
||||
void PostI2NPMessages (std::vector<I2NPMessage *> msgs);
|
||||
size_t GetSSUHeaderSize (const uint8_t * buf) const;
|
||||
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
|
||||
void ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for established session
|
||||
void ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
|
||||
void ProcessSessionRequest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
|
||||
void SendSessionRequest ();
|
||||
void SendRelayRequest (uint32_t iTag, const uint8_t * iKey);
|
||||
void SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer, uint32_t nonce);
|
||||
void ProcessSessionCreated (uint8_t * buf, size_t len);
|
||||
void SendSessionCreated (const uint8_t * x);
|
||||
void ProcessSessionConfirmed (uint8_t * buf, size_t len);
|
||||
void SendSessionCreated (const uint8_t * x, bool sendRelayTag = true);
|
||||
void ProcessSessionConfirmed (const uint8_t * buf, size_t len);
|
||||
void SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, size_t ourAddressLen);
|
||||
void ProcessRelayRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from);
|
||||
void ProcessRelayRequest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from);
|
||||
void SendRelayResponse (uint32_t nonce, const boost::asio::ip::udp::endpoint& from,
|
||||
const uint8_t * introKey, const boost::asio::ip::udp::endpoint& to);
|
||||
void SendRelayIntro (SSUSession * session, const boost::asio::ip::udp::endpoint& from);
|
||||
void ProcessRelayResponse (uint8_t * buf, size_t len);
|
||||
void ProcessRelayIntro (uint8_t * buf, size_t len);
|
||||
void SendRelayIntro (std::shared_ptr<SSUSession> session, const boost::asio::ip::udp::endpoint& from);
|
||||
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 (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
|
||||
void SendPeerTest (uint32_t nonce, uint32_t address, uint16_t port, const uint8_t * introKey, bool toAddress = true);
|
||||
void ProcessPeerTest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
|
||||
void SendPeerTest (uint32_t nonce, const boost::asio::ip::address& address, uint16_t port, const uint8_t * introKey, bool toAddress = true, bool sendAddress = true);
|
||||
void ProcessData (uint8_t * buf, size_t len);
|
||||
void SendSesionDestroyed ();
|
||||
void Send (uint8_t type, const uint8_t * payload, size_t len); // with session key
|
||||
void Send (const uint8_t * buf, size_t size);
|
||||
|
||||
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const uint8_t * aesKey, const uint8_t * iv, const uint8_t * macKey);
|
||||
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey,
|
||||
const uint8_t * iv, const i2p::crypto::MACKey& macKey, uint8_t flag = 0);
|
||||
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len); // with session key
|
||||
void Decrypt (uint8_t * buf, size_t len, const uint8_t * aesKey);
|
||||
void Decrypt (uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey);
|
||||
void DecryptSessionKey (uint8_t * buf, size_t len);
|
||||
bool Validate (uint8_t * buf, size_t len, const uint8_t * macKey);
|
||||
const uint8_t * GetIntroKey () const;
|
||||
bool Validate (uint8_t * buf, size_t len, const i2p::crypto::MACKey& macKey);
|
||||
|
||||
void ScheduleTermination ();
|
||||
void HandleTerminationTimer (const boost::system::error_code& ecode);
|
||||
@@ -129,19 +140,20 @@ namespace transport
|
||||
SSUServer& m_Server;
|
||||
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
|
||||
boost::asio::deadline_timer m_Timer;
|
||||
bool m_PeerTest;
|
||||
bool m_IsPeerTest;
|
||||
SessionState m_State;
|
||||
bool m_IsSessionKey;
|
||||
uint32_t m_RelayTag;
|
||||
std::set<uint32_t> m_PeerTestNonces;
|
||||
i2p::crypto::CBCEncryption m_SessionKeyEncryption;
|
||||
i2p::crypto::CBCDecryption m_SessionKeyDecryption;
|
||||
i2p::crypto::AESKey m_SessionKey;
|
||||
i2p::crypto::MACKey m_MacKey;
|
||||
size_t m_NumSentBytes, m_NumReceivedBytes;
|
||||
i2p::data::RouterInfo::IntroKey m_IntroKey;
|
||||
uint32_t m_CreationTime; // seconds since epoch
|
||||
SSUData m_Data;
|
||||
bool m_IsDataReceived;
|
||||
std::unique_ptr<SignedData> m_SignedData; // we need it for SessionConfirmed only
|
||||
std::map<uint32_t, std::shared_ptr<const i2p::data::RouterInfo> > m_RelayRequests; // nonce->Charlie
|
||||
};
|
||||
|
||||
|
||||
|
||||
491
Signature.cpp
Normal file
491
Signature.cpp
Normal file
@@ -0,0 +1,491 @@
|
||||
#include <memory>
|
||||
#include "Log.h"
|
||||
#include "Signature.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace crypto
|
||||
{
|
||||
class Ed25519
|
||||
{
|
||||
public:
|
||||
|
||||
Ed25519 ()
|
||||
{
|
||||
BN_CTX * ctx = BN_CTX_new ();
|
||||
BIGNUM * tmp = BN_new ();
|
||||
|
||||
q = BN_new ();
|
||||
// 2^255-19
|
||||
BN_set_bit (q, 255); // 2^255
|
||||
BN_sub_word (q, 19);
|
||||
|
||||
l = BN_new ();
|
||||
// 2^252 + 27742317777372353535851937790883648493
|
||||
BN_set_bit (l, 252);
|
||||
two_252_2 = BN_dup (l);
|
||||
BN_dec2bn (&tmp, "27742317777372353535851937790883648493");
|
||||
BN_add (l, l, tmp);
|
||||
BN_sub_word (two_252_2, 2); // 2^252 - 2
|
||||
|
||||
// -121665*inv(121666)
|
||||
d = BN_new ();
|
||||
BN_set_word (tmp, 121666);
|
||||
BN_mod_inverse (tmp, tmp, q, ctx);
|
||||
BN_set_word (d, 121665);
|
||||
BN_set_negative (d, 1);
|
||||
BN_mul (d, d, tmp, ctx);
|
||||
|
||||
// 2^((q-1)/4)
|
||||
I = BN_new ();
|
||||
BN_free (tmp);
|
||||
tmp = BN_dup (q);
|
||||
BN_sub_word (tmp, 1);
|
||||
BN_div_word (tmp, 4);
|
||||
BN_set_word (I, 2);
|
||||
BN_mod_exp (I, I, tmp, q, ctx);
|
||||
BN_free (tmp);
|
||||
|
||||
// 4*inv(5)
|
||||
BIGNUM * By = BN_new ();
|
||||
BN_set_word (By, 5);
|
||||
BN_mod_inverse (By, By, q, ctx);
|
||||
BN_mul_word (By, 4);
|
||||
BIGNUM * Bx = RecoverX (By, ctx);
|
||||
BN_mod (Bx, Bx, q, ctx); // % q
|
||||
BN_mod (By, By, q, ctx); // % q
|
||||
|
||||
// precalculate Bi256 table
|
||||
Bi256Carry = { Bx, By }; // B
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
Bi256[i][0] = Bi256Carry; // first point
|
||||
for (int j = 1; j < 128; j++)
|
||||
Bi256[i][j] = Sum (Bi256[i][j-1], Bi256[i][0], ctx); // (256+j+1)^i*B
|
||||
Bi256Carry = Bi256[i][127];
|
||||
for (int j = 0; j < 128; j++) // add first point 128 more times
|
||||
Bi256Carry = Sum (Bi256Carry, Bi256[i][0], ctx);
|
||||
}
|
||||
|
||||
BN_CTX_free (ctx);
|
||||
}
|
||||
|
||||
Ed25519 (const Ed25519& other): q (BN_dup (other.q)), l (BN_dup (other.l)),
|
||||
d (BN_dup (other.d)), I (BN_dup (other.I)), two_252_2 (BN_dup (other.two_252_2)),
|
||||
Bi256Carry (other.Bi256Carry)
|
||||
{
|
||||
for (int i = 0; i < 32; i++)
|
||||
for (int j = 0; j < 128; j++)
|
||||
Bi256[i][j] = other.Bi256[i][j];
|
||||
}
|
||||
|
||||
~Ed25519 ()
|
||||
{
|
||||
BN_free (q);
|
||||
BN_free (l);
|
||||
BN_free (d);
|
||||
BN_free (I);
|
||||
BN_free (two_252_2);
|
||||
}
|
||||
|
||||
|
||||
EDDSAPoint GeneratePublicKey (const uint8_t * expandedPrivateKey, BN_CTX * ctx) const
|
||||
{
|
||||
return MulB (expandedPrivateKey, ctx); // left half of expanded key, considered as Little Endian
|
||||
}
|
||||
|
||||
EDDSAPoint DecodePublicKey (const uint8_t * buf, BN_CTX * ctx) const
|
||||
{
|
||||
return DecodePoint (buf, ctx);
|
||||
}
|
||||
|
||||
void EncodePublicKey (const EDDSAPoint& publicKey, uint8_t * buf, BN_CTX * ctx) const
|
||||
{
|
||||
EncodePoint (Normalize (publicKey, ctx), buf);
|
||||
}
|
||||
|
||||
bool Verify (const EDDSAPoint& publicKey, const uint8_t * digest, const uint8_t * signature) const
|
||||
{
|
||||
BN_CTX * ctx = BN_CTX_new ();
|
||||
BIGNUM * h = DecodeBN<64> (digest);
|
||||
// signature 0..31 - R, 32..63 - S
|
||||
// B*S = R + PK*h => R = B*S - PK*h
|
||||
// we don't decode R, but encode (B*S - PK*h)
|
||||
auto Bs = MulB (signature + EDDSA25519_SIGNATURE_LENGTH/2, ctx); // B*S;
|
||||
BN_mod (h, h, l, ctx); // public key is multiple of B, but B%l = 0
|
||||
auto PKh = Mul (publicKey, h, ctx); // PK*h
|
||||
uint8_t diff[32];
|
||||
EncodePoint (Normalize (Sum (Bs, -PKh, ctx), ctx), diff); // Bs - PKh encoded
|
||||
bool passed = !memcmp (signature, diff, 32); // R
|
||||
BN_free (h);
|
||||
BN_CTX_free (ctx);
|
||||
if (!passed)
|
||||
LogPrint (eLogError, "25519 signature verification failed");
|
||||
return passed;
|
||||
}
|
||||
|
||||
void Sign (const uint8_t * expandedPrivateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len,
|
||||
uint8_t * signature) const
|
||||
{
|
||||
BN_CTX * bnCtx = BN_CTX_new ();
|
||||
// calculate r
|
||||
SHA512_CTX ctx;
|
||||
SHA512_Init (&ctx);
|
||||
SHA512_Update (&ctx, expandedPrivateKey + EDDSA25519_PRIVATE_KEY_LENGTH, EDDSA25519_PRIVATE_KEY_LENGTH); // right half of expanded key
|
||||
SHA512_Update (&ctx, buf, len); // data
|
||||
uint8_t digest[64];
|
||||
SHA512_Final (digest, &ctx);
|
||||
BIGNUM * r = DecodeBN<32> (digest); // DecodeBN<64> (digest); // for test vectors
|
||||
// calculate R
|
||||
uint8_t R[EDDSA25519_SIGNATURE_LENGTH/2]; // we must use separate buffer because signature might be inside buf
|
||||
EncodePoint (Normalize (MulB (digest, bnCtx), bnCtx), R); // EncodePoint (Mul (B, r, bnCtx), R); // for test vectors
|
||||
// calculate S
|
||||
SHA512_Init (&ctx);
|
||||
SHA512_Update (&ctx, R, EDDSA25519_SIGNATURE_LENGTH/2); // R
|
||||
SHA512_Update (&ctx, publicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
|
||||
SHA512_Update (&ctx, buf, len); // data
|
||||
SHA512_Final (digest, &ctx);
|
||||
BIGNUM * h = DecodeBN<64> (digest);
|
||||
// S = (r + h*a) % l
|
||||
BIGNUM * a = DecodeBN<EDDSA25519_PRIVATE_KEY_LENGTH> (expandedPrivateKey); // left half of expanded key
|
||||
BN_mod_mul (h, h, a, l, bnCtx); // %l
|
||||
BN_mod_add (h, h, r, l, bnCtx); // %l
|
||||
memcpy (signature, R, EDDSA25519_SIGNATURE_LENGTH/2);
|
||||
EncodeBN (h, signature + EDDSA25519_SIGNATURE_LENGTH/2, EDDSA25519_SIGNATURE_LENGTH/2); // S
|
||||
BN_free (r); BN_free (h); BN_free (a);
|
||||
BN_CTX_free (bnCtx);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
EDDSAPoint Sum (const EDDSAPoint& p1, const EDDSAPoint& p2, BN_CTX * ctx) const
|
||||
{
|
||||
// x3 = (x1*y2+y1*x2)*(z1*z2-d*t1*t2)
|
||||
// y3 = (y1*y2+x1*x2)*(z1*z2+d*t1*t2)
|
||||
// z3 = (z1*z2-d*t1*t2)*(z1*z2+d*t1*t2)
|
||||
// t3 = (y1*y2+x1*x2)*(x1*y2+y1*x2)
|
||||
BIGNUM * x3 = BN_new (), * y3 = BN_new (), * z3 = BN_new (), * t3 = BN_new ();
|
||||
|
||||
BN_mul (x3, p1.x, p2.x, ctx); // A = x1*x2
|
||||
BN_mul (y3, p1.y, p2.y, ctx); // B = y1*y2
|
||||
|
||||
BIGNUM * t1 = p1.t, * t2 = p2.t;
|
||||
if (!t1) { t1 = BN_new (); BN_mul (t1, p1.x, p1.y, ctx); }
|
||||
if (!t2) { t2 = BN_new (); BN_mul (t2, p2.x, p2.y, ctx); }
|
||||
BN_mul (t3, t1, t2, ctx);
|
||||
BN_mul (t3, t3, d, ctx); // C = d*t1*t2
|
||||
if (!p1.t) BN_free (t1);
|
||||
if (!p2.t) BN_free (t2);
|
||||
|
||||
if (p1.z)
|
||||
{
|
||||
if (p2.z)
|
||||
BN_mul (z3, p1.z, p2.z, ctx); // D = z1*z2
|
||||
else
|
||||
BN_copy (z3, p1.z); // D = z1
|
||||
}
|
||||
else
|
||||
{
|
||||
if (p2.z)
|
||||
BN_copy (z3, p2.z); // D = z2
|
||||
else
|
||||
BN_one (z3); // D = 1
|
||||
}
|
||||
|
||||
BIGNUM * E = BN_new (), * F = BN_new (), * G = BN_new (), * H = BN_new ();
|
||||
BN_add (E, p1.x, p1.y);
|
||||
BN_add (F, p2.x, p2.y);
|
||||
BN_mul (E, E, F, ctx); // (x1 + y1)*(x2 + y2)
|
||||
BN_sub (E, E, x3);
|
||||
BN_sub (E, E, y3); // E = (x1 + y1)*(x2 + y2) - A - B
|
||||
BN_sub (F, z3, t3); // F = D - C
|
||||
BN_add (G, z3, t3); // G = D + C
|
||||
BN_add (H, y3, x3); // H = B + A
|
||||
|
||||
BN_mod_mul (x3, E, F, q, ctx); // x3 = E*F
|
||||
BN_mod_mul (y3, G, H, q, ctx); // y3 = G*H
|
||||
BN_mod_mul (z3, F, G, q, ctx); // z3 = F*G
|
||||
BN_mod_mul (t3, E, H, q, ctx); // t3 = E*H
|
||||
|
||||
BN_free (E); BN_free (F); BN_free (G); BN_free (H);
|
||||
|
||||
return EDDSAPoint {x3, y3, z3, t3};
|
||||
}
|
||||
|
||||
EDDSAPoint Double (const EDDSAPoint& p, BN_CTX * ctx) const
|
||||
{
|
||||
BIGNUM * x2 = BN_new (), * y2 = BN_new (), * z2 = BN_new (), * t2 = BN_new ();
|
||||
|
||||
BN_sqr (x2, p.x, ctx); // x2 = A = x^2
|
||||
BN_sqr (y2, p.y, ctx); // y2 = B = y^2
|
||||
if (p.t)
|
||||
BN_sqr (t2, p.t, ctx); // t2 = t^2
|
||||
else
|
||||
{
|
||||
BN_mul (t2, p.x, p.y, ctx); // t = x*y
|
||||
BN_sqr (t2, t2, ctx); // t2 = t^2
|
||||
}
|
||||
BN_mul (t2, t2, d, ctx); // t2 = C = d*t^2
|
||||
if (p.z)
|
||||
BN_sqr (z2, p.z, ctx); // z2 = D = z^2
|
||||
else
|
||||
BN_one (z2); // z2 = 1
|
||||
|
||||
BIGNUM * E = BN_new (), * F = BN_new (), * G = BN_new (), * H = BN_new ();
|
||||
// E = (x+y)*(x+y)-A-B = x^2+y^2+2xy-A-B = 2xy
|
||||
BN_mul (E, p.x, p.y, ctx);
|
||||
BN_lshift1 (E, E); // E =2*x*y
|
||||
BN_sub (F, z2, t2); // F = D - C
|
||||
BN_add (G, z2, t2); // G = D + C
|
||||
BN_add (H, y2, x2); // H = B + A
|
||||
|
||||
BN_mod_mul (x2, E, F, q, ctx); // x2 = E*F
|
||||
BN_mod_mul (y2, G, H, q, ctx); // y2 = G*H
|
||||
BN_mod_mul (z2, F, G, q, ctx); // z2 = F*G
|
||||
BN_mod_mul (t2, E, H, q, ctx); // t2 = E*H
|
||||
|
||||
BN_free (E); BN_free (F); BN_free (G); BN_free (H);
|
||||
|
||||
return EDDSAPoint {x2, y2, z2, t2};
|
||||
}
|
||||
|
||||
EDDSAPoint Mul (const EDDSAPoint& p, const BIGNUM * e, BN_CTX * ctx) const
|
||||
{
|
||||
BIGNUM * zero = BN_new (), * one = BN_new ();
|
||||
BN_zero (zero); BN_one (one);
|
||||
EDDSAPoint res {zero, one};
|
||||
if (!BN_is_zero (e))
|
||||
{
|
||||
int bitCount = BN_num_bits (e);
|
||||
for (int i = bitCount - 1; i >= 0; i--)
|
||||
{
|
||||
res = Double (res, ctx);
|
||||
if (BN_is_bit_set (e, i)) res = Sum (res, p, ctx);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
EDDSAPoint MulB (const uint8_t * e, BN_CTX * ctx) const // B*e, e is 32 bytes Little Endian
|
||||
{
|
||||
BIGNUM * zero = BN_new (), * one = BN_new ();
|
||||
BN_zero (zero); BN_one (one);
|
||||
EDDSAPoint res {zero, one};
|
||||
bool carry = false;
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
uint8_t x = e[i];
|
||||
if (carry)
|
||||
{
|
||||
if (x < 255)
|
||||
{
|
||||
x++;
|
||||
carry = false;
|
||||
}
|
||||
else
|
||||
x = 0;
|
||||
}
|
||||
if (x > 0)
|
||||
{
|
||||
if (x <= 128)
|
||||
res = Sum (res, Bi256[i][x-1], ctx);
|
||||
else
|
||||
{
|
||||
res = Sum (res, -Bi256[i][255-x], ctx); // -Bi[256-x]
|
||||
carry = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (carry) res = Sum (res, Bi256Carry, ctx);
|
||||
return res;
|
||||
}
|
||||
|
||||
EDDSAPoint Normalize (const EDDSAPoint& p, BN_CTX * ctx) const
|
||||
{
|
||||
if (p.z)
|
||||
{
|
||||
BIGNUM * x = BN_new (), * y = BN_new ();
|
||||
BN_mod_inverse (y, p.z, q, ctx);
|
||||
BN_mod_mul (x, p.x, y, q, ctx); // x = x/z
|
||||
BN_mod_mul (y, p.y, y, q, ctx); // y = y/z
|
||||
return EDDSAPoint{x, y};
|
||||
}
|
||||
else
|
||||
return EDDSAPoint{BN_dup (p.x), BN_dup (p.y)};
|
||||
}
|
||||
|
||||
bool IsOnCurve (const EDDSAPoint& p, BN_CTX * ctx) const
|
||||
{
|
||||
BIGNUM * x2 = BN_new ();
|
||||
BN_sqr (x2, p.x, ctx); // x^2
|
||||
BIGNUM * y2 = BN_new ();
|
||||
BN_sqr (y2, p.y, ctx); // y^2
|
||||
// y^2 - x^2 - 1 - d*x^2*y^2
|
||||
BIGNUM * tmp = BN_new ();
|
||||
BN_mul (tmp, d, x2, ctx);
|
||||
BN_mul (tmp, tmp, y2, ctx);
|
||||
BN_sub (tmp, y2, tmp);
|
||||
BN_sub (tmp, tmp, x2);
|
||||
BN_sub_word (tmp, 1);
|
||||
BN_mod (tmp, tmp, q, ctx); // % q
|
||||
bool ret = BN_is_zero (tmp);
|
||||
BN_free (x2);
|
||||
BN_free (y2);
|
||||
BN_free (tmp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
BIGNUM * RecoverX (const BIGNUM * y, BN_CTX * ctx) const
|
||||
{
|
||||
BIGNUM * y2 = BN_new ();
|
||||
BN_sqr (y2, y, ctx); // y^2
|
||||
// xx = (y^2 -1)*inv(d*y^2 +1)
|
||||
BIGNUM * xx = BN_new ();
|
||||
BN_mul (xx, d, y2, ctx);
|
||||
BN_add_word (xx, 1);
|
||||
BN_mod_inverse (xx, xx, q, ctx);
|
||||
BN_sub_word (y2, 1);
|
||||
BN_mul (xx, y2, xx, ctx);
|
||||
// x = srqt(xx) = xx^(2^252-2)
|
||||
BIGNUM * x = BN_new ();
|
||||
BN_mod_exp (x, xx, two_252_2, q, ctx);
|
||||
// check (x^2 -xx) % q
|
||||
BN_sqr (y2, x, ctx);
|
||||
BN_mod_sub (y2, y2, xx, q, ctx);
|
||||
if (!BN_is_zero (y2))
|
||||
BN_mod_mul (x, x, I, q, ctx);
|
||||
if (BN_is_odd (x))
|
||||
BN_sub (x, q, x);
|
||||
BN_free (y2);
|
||||
BN_free (xx);
|
||||
return x;
|
||||
}
|
||||
|
||||
EDDSAPoint DecodePoint (const uint8_t * buf, BN_CTX * ctx) const
|
||||
{
|
||||
// buf is 32 bytes Little Endian, convert it to Big Endian
|
||||
uint8_t buf1[EDDSA25519_PUBLIC_KEY_LENGTH];
|
||||
for (size_t i = 0; i < EDDSA25519_PUBLIC_KEY_LENGTH/2; i++) // invert bytes
|
||||
{
|
||||
buf1[i] = buf[EDDSA25519_PUBLIC_KEY_LENGTH -1 - i];
|
||||
buf1[EDDSA25519_PUBLIC_KEY_LENGTH -1 - i] = buf[i];
|
||||
}
|
||||
bool isHighestBitSet = buf1[0] & 0x80;
|
||||
if (isHighestBitSet)
|
||||
buf1[0] &= 0x7f; // clear highest bit
|
||||
BIGNUM * y = BN_new ();
|
||||
BN_bin2bn (buf1, EDDSA25519_PUBLIC_KEY_LENGTH, y);
|
||||
auto x = RecoverX (y, ctx);
|
||||
if (BN_is_bit_set (x, 0) != isHighestBitSet)
|
||||
BN_sub (x, q, x); // x = q - x
|
||||
BIGNUM * z = BN_new (), * t = BN_new ();
|
||||
BN_one (z); BN_mod_mul (t, x, y, q, ctx); // pre-calculate t
|
||||
EDDSAPoint p {x, y, z, t};
|
||||
if (!IsOnCurve (p, ctx))
|
||||
LogPrint (eLogError, "Decoded point is not on 25519");
|
||||
return p;
|
||||
}
|
||||
|
||||
void EncodePoint (const EDDSAPoint& p, uint8_t * buf) const
|
||||
{
|
||||
EncodeBN (p.y, buf,EDDSA25519_PUBLIC_KEY_LENGTH);
|
||||
if (BN_is_bit_set (p.x, 0)) // highest bit
|
||||
buf[EDDSA25519_PUBLIC_KEY_LENGTH - 1] |= 0x80; // set highest bit
|
||||
}
|
||||
|
||||
template<int len>
|
||||
BIGNUM * DecodeBN (const uint8_t * buf) const
|
||||
{
|
||||
// buf is Little Endian convert it to Big Endian
|
||||
uint8_t buf1[len];
|
||||
for (size_t i = 0; i < len/2; i++) // invert bytes
|
||||
{
|
||||
buf1[i] = buf[len -1 - i];
|
||||
buf1[len -1 - i] = buf[i];
|
||||
}
|
||||
BIGNUM * res = BN_new ();
|
||||
BN_bin2bn (buf1, len, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
void EncodeBN (const BIGNUM * bn, uint8_t * buf, size_t len) const
|
||||
{
|
||||
bn2buf (bn, buf, len);
|
||||
// To Little Endian
|
||||
for (size_t i = 0; i < len/2; i++) // invert bytes
|
||||
{
|
||||
uint8_t tmp = buf[i];
|
||||
buf[i] = buf[len -1 - i];
|
||||
buf[len -1 - i] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
BIGNUM * q, * l, * d, * I;
|
||||
// transient values
|
||||
BIGNUM * two_252_2; // 2^252-2
|
||||
EDDSAPoint Bi256[32][128]; // per byte, Bi256[i][j] = (256+j+1)^i*B, we don't store zeroes
|
||||
// if j > 128 we use 256 - j and carry 1 to next byte
|
||||
// Bi256[0][0] = B, base point
|
||||
EDDSAPoint Bi256Carry; // Bi256[32][0]
|
||||
};
|
||||
|
||||
static std::unique_ptr<Ed25519> g_Ed25519;
|
||||
std::unique_ptr<Ed25519>& GetEd25519 ()
|
||||
{
|
||||
if (!g_Ed25519)
|
||||
{
|
||||
auto c = new Ed25519();
|
||||
if (!g_Ed25519) // make sure it was not created already
|
||||
g_Ed25519.reset (c);
|
||||
else
|
||||
delete c;
|
||||
}
|
||||
return g_Ed25519;
|
||||
}
|
||||
|
||||
|
||||
EDDSA25519Verifier::EDDSA25519Verifier (const uint8_t * signingKey)
|
||||
{
|
||||
memcpy (m_PublicKeyEncoded, signingKey, EDDSA25519_PUBLIC_KEY_LENGTH);
|
||||
BN_CTX * ctx = BN_CTX_new ();
|
||||
m_PublicKey = GetEd25519 ()->DecodePublicKey (m_PublicKeyEncoded, ctx);
|
||||
BN_CTX_free (ctx);
|
||||
}
|
||||
|
||||
bool EDDSA25519Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
|
||||
{
|
||||
uint8_t digest[64];
|
||||
SHA512_CTX ctx;
|
||||
SHA512_Init (&ctx);
|
||||
SHA512_Update (&ctx, signature, EDDSA25519_SIGNATURE_LENGTH/2); // R
|
||||
SHA512_Update (&ctx, m_PublicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
|
||||
SHA512_Update (&ctx, buf, len); // data
|
||||
SHA512_Final (digest, &ctx);
|
||||
|
||||
return GetEd25519 ()->Verify (m_PublicKey, digest, signature);
|
||||
}
|
||||
|
||||
EDDSA25519Signer::EDDSA25519Signer (const uint8_t * signingPrivateKey)
|
||||
{
|
||||
// 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] |= 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);
|
||||
BN_CTX_free (ctx);
|
||||
}
|
||||
|
||||
void EDDSA25519Signer::Sign (const uint8_t * buf, int len, uint8_t * signature) const
|
||||
{
|
||||
GetEd25519 ()->Sign (m_ExpandedPrivateKey, m_PublicKeyEncoded, buf, len, signature);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
505
Signature.h
505
Signature.h
@@ -2,13 +2,13 @@
|
||||
#define SIGNATURE_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cryptopp/dsa.h>
|
||||
#include <cryptopp/rsa.h>
|
||||
#include <cryptopp/asn.h>
|
||||
#include <cryptopp/oids.h>
|
||||
#include <cryptopp/osrng.h>
|
||||
#include <cryptopp/eccrypto.h>
|
||||
#include "CryptoConst.h"
|
||||
#include <string.h>
|
||||
#include <openssl/dsa.h>
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/ecdsa.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/evp.h>
|
||||
#include "Crypto.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -30,7 +30,7 @@ namespace crypto
|
||||
public:
|
||||
|
||||
virtual ~Signer () {};
|
||||
virtual void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const = 0;
|
||||
virtual void Sign (const uint8_t * buf, int len, uint8_t * signature) const = 0;
|
||||
};
|
||||
|
||||
const size_t DSA_PUBLIC_KEY_LENGTH = 128;
|
||||
@@ -42,13 +42,28 @@ namespace crypto
|
||||
|
||||
DSAVerifier (const uint8_t * signingKey)
|
||||
{
|
||||
m_PublicKey.Initialize (dsap, dsaq, dsag, CryptoPP::Integer (signingKey, DSA_PUBLIC_KEY_LENGTH));
|
||||
m_PublicKey = CreateDSA ();
|
||||
m_PublicKey->pub_key = BN_bin2bn (signingKey, DSA_PUBLIC_KEY_LENGTH, NULL);
|
||||
}
|
||||
|
||||
|
||||
~DSAVerifier ()
|
||||
{
|
||||
DSA_free (m_PublicKey);
|
||||
}
|
||||
|
||||
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
|
||||
{
|
||||
CryptoPP::DSA::Verifier verifier (m_PublicKey);
|
||||
return verifier.VerifyMessage (buf, len, signature, DSA_SIGNATURE_LENGTH);
|
||||
// calculate SHA1 digest
|
||||
uint8_t digest[20];
|
||||
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 verification
|
||||
int ret = DSA_do_verify (digest, 20, sig, m_PublicKey);
|
||||
DSA_SIG_free(sig);
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t GetPublicKeyLen () const { return DSA_PUBLIC_KEY_LENGTH; };
|
||||
@@ -56,7 +71,7 @@ namespace crypto
|
||||
|
||||
private:
|
||||
|
||||
CryptoPP::DSA::PublicKey m_PublicKey;
|
||||
DSA * m_PublicKey;
|
||||
};
|
||||
|
||||
class DSASigner: public Signer
|
||||
@@ -65,189 +80,210 @@ namespace crypto
|
||||
|
||||
DSASigner (const uint8_t * signingPrivateKey)
|
||||
{
|
||||
m_PrivateKey.Initialize (dsap, dsaq, dsag, CryptoPP::Integer (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH));
|
||||
m_PrivateKey = CreateDSA ();
|
||||
m_PrivateKey->priv_key = BN_bin2bn (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL);
|
||||
}
|
||||
|
||||
void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const
|
||||
~DSASigner ()
|
||||
{
|
||||
CryptoPP::DSA::Signer signer (m_PrivateKey);
|
||||
signer.SignMessage (rnd, buf, len, signature);
|
||||
DSA_free (m_PrivateKey);
|
||||
}
|
||||
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
|
||||
{
|
||||
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);
|
||||
DSA_SIG_free(sig);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
CryptoPP::DSA::PrivateKey m_PrivateKey;
|
||||
DSA * m_PrivateKey;
|
||||
};
|
||||
|
||||
inline void CreateDSARandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
||||
inline void CreateDSARandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
||||
{
|
||||
CryptoPP::DSA::PrivateKey privateKey;
|
||||
CryptoPP::DSA::PublicKey publicKey;
|
||||
privateKey.Initialize (rnd, dsap, dsaq, dsag);
|
||||
privateKey.MakePublicKey (publicKey);
|
||||
privateKey.GetPrivateExponent ().Encode (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH);
|
||||
publicKey.GetPublicElement ().Encode (signingPublicKey, DSA_PUBLIC_KEY_LENGTH);
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
template<typename Hash, size_t keyLen>
|
||||
struct SHA256Hash
|
||||
{
|
||||
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest)
|
||||
{
|
||||
SHA256 (buf, len, digest);
|
||||
}
|
||||
|
||||
enum { hashLen = 32 };
|
||||
};
|
||||
|
||||
struct SHA384Hash
|
||||
{
|
||||
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest)
|
||||
{
|
||||
SHA384 (buf, len, digest);
|
||||
}
|
||||
|
||||
enum { hashLen = 48 };
|
||||
};
|
||||
|
||||
struct SHA512Hash
|
||||
{
|
||||
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest)
|
||||
{
|
||||
SHA512 (buf, len, digest);
|
||||
}
|
||||
|
||||
enum { hashLen = 64 };
|
||||
};
|
||||
|
||||
template<typename Hash, int curve, size_t keyLen>
|
||||
class ECDSAVerifier: public Verifier
|
||||
{
|
||||
public:
|
||||
|
||||
template<typename Curve>
|
||||
ECDSAVerifier (Curve curve, const uint8_t * signingKey)
|
||||
ECDSAVerifier (const uint8_t * signingKey)
|
||||
{
|
||||
m_PublicKey.Initialize (curve,
|
||||
CryptoPP::ECP::Point (CryptoPP::Integer (signingKey, keyLen/2),
|
||||
CryptoPP::Integer (signingKey + keyLen/2, keyLen/2)));
|
||||
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));
|
||||
}
|
||||
|
||||
~ECDSAVerifier ()
|
||||
{
|
||||
EC_KEY_free (m_PublicKey);
|
||||
}
|
||||
|
||||
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
|
||||
{
|
||||
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::Verifier verifier (m_PublicKey);
|
||||
return verifier.VerifyMessage (buf, len, signature, keyLen); // signature length
|
||||
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);
|
||||
// ECDSA verification
|
||||
int ret = ECDSA_do_verify (digest, Hash::hashLen, sig, m_PublicKey);
|
||||
ECDSA_SIG_free(sig);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
size_t GetPublicKeyLen () const { return keyLen; };
|
||||
size_t GetSignatureLen () const { return keyLen; }; // signature length = key length
|
||||
|
||||
|
||||
private:
|
||||
|
||||
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::PublicKey m_PublicKey;
|
||||
EC_KEY * m_PublicKey;
|
||||
};
|
||||
|
||||
template<typename Hash>
|
||||
template<typename Hash, int curve, size_t keyLen>
|
||||
class ECDSASigner: public Signer
|
||||
{
|
||||
public:
|
||||
|
||||
template<typename Curve>
|
||||
ECDSASigner (Curve curve, const uint8_t * signingPrivateKey, size_t keyLen)
|
||||
ECDSASigner (const uint8_t * signingPrivateKey)
|
||||
{
|
||||
m_PrivateKey.Initialize (curve, CryptoPP::Integer (signingPrivateKey, keyLen/2)); // private key length
|
||||
m_PrivateKey = EC_KEY_new_by_curve_name (curve);
|
||||
EC_KEY_set_private_key (m_PrivateKey, BN_bin2bn (signingPrivateKey, keyLen/2, NULL));
|
||||
}
|
||||
|
||||
void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const
|
||||
~ECDSASigner ()
|
||||
{
|
||||
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::Signer signer (m_PrivateKey);
|
||||
signer.SignMessage (rnd, buf, len, signature);
|
||||
EC_KEY_free (m_PrivateKey);
|
||||
}
|
||||
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
|
||||
{
|
||||
uint8_t digest[Hash::hashLen];
|
||||
Hash::CalculateHash (buf, len, digest);
|
||||
ECDSA_SIG * sig = ECDSA_do_sign (digest, Hash::hashLen, m_PrivateKey);
|
||||
// signatureLen = keyLen
|
||||
bn2buf (sig->r, signature, keyLen/2);
|
||||
bn2buf (sig->s, signature + keyLen/2, keyLen/2);
|
||||
ECDSA_SIG_free(sig);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::PrivateKey m_PrivateKey;
|
||||
EC_KEY * m_PrivateKey;
|
||||
};
|
||||
|
||||
template<typename Hash, typename Curve>
|
||||
inline void CreateECDSARandomKeys (CryptoPP::RandomNumberGenerator& rnd, Curve curve,
|
||||
size_t keyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
||||
inline void CreateECDSARandomKeys (int curve, size_t keyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
||||
{
|
||||
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::PrivateKey privateKey;
|
||||
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::PublicKey publicKey;
|
||||
privateKey.Initialize (rnd, curve);
|
||||
privateKey.MakePublicKey (publicKey);
|
||||
privateKey.GetPrivateExponent ().Encode (signingPrivateKey, keyLen/2);
|
||||
auto q = publicKey.GetPublicElement ();
|
||||
q.x.Encode (signingPublicKey, keyLen/2);
|
||||
q.y.Encode (signingPublicKey + keyLen/2, keyLen/2);
|
||||
}
|
||||
EC_KEY * signingKey = EC_KEY_new_by_curve_name (curve);
|
||||
EC_KEY_generate_key (signingKey);
|
||||
bn2buf (EC_KEY_get0_private_key (signingKey), signingPrivateKey, keyLen/2);
|
||||
BIGNUM * x = BN_new(), * y = BN_new();
|
||||
EC_POINT_get_affine_coordinates_GFp (EC_KEY_get0_group(signingKey),
|
||||
EC_KEY_get0_public_key (signingKey), x, y, NULL);
|
||||
bn2buf (x, signingPublicKey, keyLen/2);
|
||||
bn2buf (y, signingPublicKey + keyLen/2, keyLen/2);
|
||||
BN_free (x); BN_free (y);
|
||||
EC_KEY_free (signingKey);
|
||||
}
|
||||
|
||||
// ECDSA_SHA256_P256
|
||||
const size_t ECDSAP256_KEY_LENGTH = 64;
|
||||
class ECDSAP256Verifier: public ECDSAVerifier<CryptoPP::SHA256, ECDSAP256_KEY_LENGTH>
|
||||
typedef ECDSAVerifier<SHA256Hash, NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH> ECDSAP256Verifier;
|
||||
typedef ECDSASigner<SHA256Hash, NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH> ECDSAP256Signer;
|
||||
|
||||
inline void CreateECDSAP256RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
||||
{
|
||||
public:
|
||||
|
||||
ECDSAP256Verifier (const uint8_t * signingKey):
|
||||
ECDSAVerifier (CryptoPP::ASN1::secp256r1(), signingKey)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class ECDSAP256Signer: public ECDSASigner<CryptoPP::SHA256>
|
||||
{
|
||||
public:
|
||||
|
||||
ECDSAP256Signer (const uint8_t * signingPrivateKey):
|
||||
ECDSASigner (CryptoPP::ASN1::secp256r1(), signingPrivateKey, ECDSAP256_KEY_LENGTH)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
inline void CreateECDSAP256RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
||||
{
|
||||
CreateECDSARandomKeys<CryptoPP::SHA256> (rnd, CryptoPP::ASN1::secp256r1(), ECDSAP256_KEY_LENGTH, signingPrivateKey, signingPublicKey);
|
||||
CreateECDSARandomKeys (NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH, signingPrivateKey, signingPublicKey);
|
||||
}
|
||||
|
||||
// ECDSA_SHA384_P384
|
||||
const size_t ECDSAP384_KEY_LENGTH = 96;
|
||||
class ECDSAP384Verifier: public ECDSAVerifier<CryptoPP::SHA384, ECDSAP384_KEY_LENGTH>
|
||||
typedef ECDSAVerifier<SHA384Hash, NID_secp384r1, ECDSAP384_KEY_LENGTH> ECDSAP384Verifier;
|
||||
typedef ECDSASigner<SHA384Hash, NID_secp384r1, ECDSAP384_KEY_LENGTH> ECDSAP384Signer;
|
||||
|
||||
inline void CreateECDSAP384RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
||||
{
|
||||
public:
|
||||
|
||||
ECDSAP384Verifier (const uint8_t * signingKey):
|
||||
ECDSAVerifier (CryptoPP::ASN1::secp384r1(), signingKey)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class ECDSAP384Signer: public ECDSASigner<CryptoPP::SHA384>
|
||||
{
|
||||
public:
|
||||
|
||||
ECDSAP384Signer (const uint8_t * signingPrivateKey):
|
||||
ECDSASigner (CryptoPP::ASN1::secp384r1(), signingPrivateKey, ECDSAP384_KEY_LENGTH)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
inline void CreateECDSAP384RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
||||
{
|
||||
CreateECDSARandomKeys<CryptoPP::SHA384> (rnd, CryptoPP::ASN1::secp384r1(), ECDSAP384_KEY_LENGTH, signingPrivateKey, signingPublicKey);
|
||||
CreateECDSARandomKeys (NID_secp384r1, ECDSAP384_KEY_LENGTH, signingPrivateKey, signingPublicKey);
|
||||
}
|
||||
|
||||
// ECDSA_SHA512_P521
|
||||
const size_t ECDSAP521_KEY_LENGTH = 132;
|
||||
class ECDSAP521Verifier: public ECDSAVerifier<CryptoPP::SHA512, ECDSAP521_KEY_LENGTH>
|
||||
typedef ECDSAVerifier<SHA512Hash, NID_secp521r1, ECDSAP521_KEY_LENGTH> ECDSAP521Verifier;
|
||||
typedef ECDSASigner<SHA512Hash, NID_secp521r1, ECDSAP521_KEY_LENGTH> ECDSAP521Signer;
|
||||
|
||||
inline void CreateECDSAP521RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
||||
{
|
||||
public:
|
||||
|
||||
ECDSAP521Verifier (const uint8_t * signingKey):
|
||||
ECDSAVerifier (CryptoPP::ASN1::secp521r1(), signingKey)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class ECDSAP521Signer: public ECDSASigner<CryptoPP::SHA512>
|
||||
{
|
||||
public:
|
||||
|
||||
ECDSAP521Signer (const uint8_t * signingPrivateKey):
|
||||
ECDSASigner (CryptoPP::ASN1::secp521r1(), signingPrivateKey, ECDSAP521_KEY_LENGTH)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
inline void CreateECDSAP521RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
||||
{
|
||||
CreateECDSARandomKeys<CryptoPP::SHA512> (rnd, CryptoPP::ASN1::secp521r1(), ECDSAP521_KEY_LENGTH, signingPrivateKey, signingPublicKey);
|
||||
CreateECDSARandomKeys (NID_secp521r1, ECDSAP521_KEY_LENGTH, signingPrivateKey, signingPublicKey);
|
||||
}
|
||||
|
||||
// RSA
|
||||
template<typename Hash, size_t keyLen>
|
||||
template<typename Hash, int type, size_t keyLen>
|
||||
class RSAVerifier: public Verifier
|
||||
{
|
||||
public:
|
||||
|
||||
RSAVerifier (const uint8_t * signingKey)
|
||||
{
|
||||
m_PublicKey.Initialize (CryptoPP::Integer (signingKey, keyLen), CryptoPP::Integer (rsae));
|
||||
m_PublicKey = RSA_new ();
|
||||
memset (m_PublicKey, 0, sizeof (RSA));
|
||||
m_PublicKey->e = BN_dup (GetRSAE ());
|
||||
m_PublicKey->n = BN_bin2bn (signingKey, keyLen, NULL);
|
||||
}
|
||||
|
||||
~RSAVerifier ()
|
||||
{
|
||||
RSA_free (m_PublicKey);
|
||||
}
|
||||
|
||||
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
|
||||
{
|
||||
typename CryptoPP::RSASS<CryptoPP::PKCS1v15, Hash>::Verifier verifier (m_PublicKey);
|
||||
return verifier.VerifyMessage (buf, len, signature, keyLen); // signature length
|
||||
uint8_t digest[Hash::hashLen];
|
||||
Hash::CalculateHash (buf, len, digest);
|
||||
return RSA_verify (type, digest, Hash::hashLen, signature, GetSignatureLen (), m_PublicKey);
|
||||
}
|
||||
size_t GetPublicKeyLen () const { return keyLen; }
|
||||
size_t GetSignatureLen () const { return keyLen; }
|
||||
@@ -255,161 +291,150 @@ namespace crypto
|
||||
|
||||
private:
|
||||
|
||||
CryptoPP::RSA::PublicKey m_PublicKey;
|
||||
RSA * m_PublicKey;
|
||||
};
|
||||
|
||||
|
||||
template<typename Hash>
|
||||
template<typename Hash, int type, size_t keyLen>
|
||||
class RSASigner: public Signer
|
||||
{
|
||||
public:
|
||||
|
||||
RSASigner (const uint8_t * signingPrivateKey, size_t keyLen)
|
||||
RSASigner (const uint8_t * signingPrivateKey)
|
||||
{
|
||||
m_PrivateKey.Initialize (CryptoPP::Integer (signingPrivateKey, keyLen/2),
|
||||
rsae,
|
||||
CryptoPP::Integer (signingPrivateKey + keyLen/2, keyLen/2));
|
||||
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);
|
||||
}
|
||||
|
||||
void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const
|
||||
~RSASigner ()
|
||||
{
|
||||
typename CryptoPP::RSASS<CryptoPP::PKCS1v15, Hash>::Signer signer (m_PrivateKey);
|
||||
signer.SignMessage (rnd, buf, len, signature);
|
||||
RSA_free (m_PrivateKey);
|
||||
}
|
||||
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
|
||||
{
|
||||
uint8_t digest[Hash::hashLen];
|
||||
Hash::CalculateHash (buf, len, digest);
|
||||
unsigned int signatureLen = keyLen;
|
||||
RSA_sign (type, digest, Hash::hashLen, signature, &signatureLen, m_PrivateKey);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
CryptoPP::RSA::PrivateKey m_PrivateKey;
|
||||
RSA * m_PrivateKey;
|
||||
};
|
||||
|
||||
inline void CreateRSARandomKeys (CryptoPP::RandomNumberGenerator& rnd,
|
||||
size_t publicKeyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
||||
inline void CreateRSARandomKeys (size_t publicKeyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
||||
{
|
||||
CryptoPP::RSA::PrivateKey privateKey;
|
||||
privateKey.Initialize (rnd, publicKeyLen*8, rsae);
|
||||
privateKey.GetModulus ().Encode (signingPrivateKey, publicKeyLen);
|
||||
privateKey.GetPrivateExponent ().Encode (signingPrivateKey + publicKeyLen, publicKeyLen);
|
||||
privateKey.GetModulus ().Encode (signingPublicKey, publicKeyLen);
|
||||
RSA * 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);
|
||||
BN_free (e); // this e is not assigned to rsa->e
|
||||
RSA_free (rsa);
|
||||
}
|
||||
|
||||
|
||||
// RSA_SHA256_2048
|
||||
const size_t RSASHA2562048_KEY_LENGTH = 256;
|
||||
class RSASHA2562048Verifier: public RSAVerifier<CryptoPP::SHA256, RSASHA2562048_KEY_LENGTH>
|
||||
{
|
||||
public:
|
||||
|
||||
RSASHA2562048Verifier (const uint8_t * signingKey): RSAVerifier (signingKey)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class RSASHA2562048Signer: public RSASigner<CryptoPP::SHA256>
|
||||
{
|
||||
public:
|
||||
|
||||
RSASHA2562048Signer (const uint8_t * signingPrivateKey):
|
||||
RSASigner (signingPrivateKey, RSASHA2562048_KEY_LENGTH*2)
|
||||
{
|
||||
}
|
||||
};
|
||||
typedef RSAVerifier<SHA256Hash, NID_sha256, RSASHA2562048_KEY_LENGTH> RSASHA2562048Verifier;
|
||||
typedef RSASigner<SHA256Hash, NID_sha256, RSASHA2562048_KEY_LENGTH> RSASHA2562048Signer;
|
||||
|
||||
// RSA_SHA384_3072
|
||||
const size_t RSASHA3843072_KEY_LENGTH = 384;
|
||||
class RSASHA3843072Verifier: public RSAVerifier<CryptoPP::SHA384, RSASHA3843072_KEY_LENGTH>
|
||||
{
|
||||
public:
|
||||
|
||||
RSASHA3843072Verifier (const uint8_t * signingKey): RSAVerifier (signingKey)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class RSASHA3843072Signer: public RSASigner<CryptoPP::SHA384>
|
||||
{
|
||||
public:
|
||||
|
||||
RSASHA3843072Signer (const uint8_t * signingPrivateKey):
|
||||
RSASigner (signingPrivateKey, RSASHA3843072_KEY_LENGTH*2)
|
||||
{
|
||||
}
|
||||
};
|
||||
typedef RSAVerifier<SHA384Hash, NID_sha384, RSASHA3843072_KEY_LENGTH> RSASHA3843072Verifier;
|
||||
typedef RSASigner<SHA384Hash, NID_sha384, RSASHA3843072_KEY_LENGTH> RSASHA3843072Signer;
|
||||
|
||||
// RSA_SHA512_4096
|
||||
const size_t RSASHA5124096_KEY_LENGTH = 512;
|
||||
class RSASHA5124096Verifier: public RSAVerifier<CryptoPP::SHA512, RSASHA5124096_KEY_LENGTH>
|
||||
typedef RSAVerifier<SHA512Hash, NID_sha512, RSASHA5124096_KEY_LENGTH> RSASHA5124096Verifier;
|
||||
typedef RSASigner<SHA512Hash, NID_sha512, RSASHA5124096_KEY_LENGTH> RSASHA5124096Signer;
|
||||
|
||||
// EdDSA
|
||||
struct EDDSAPoint
|
||||
{
|
||||
BIGNUM * x, * y;
|
||||
BIGNUM * z, * t; // projective coordinates
|
||||
EDDSAPoint (): x(nullptr), y(nullptr), z(nullptr), t(nullptr) {};
|
||||
EDDSAPoint (const EDDSAPoint& other): x(nullptr), y(nullptr), z(nullptr), t(nullptr)
|
||||
{ *this = other; };
|
||||
EDDSAPoint (EDDSAPoint&& other): x(nullptr), y(nullptr), z(nullptr), t(nullptr)
|
||||
{ *this = std::move (other); };
|
||||
EDDSAPoint (BIGNUM * x1, BIGNUM * y1, BIGNUM * z1 = nullptr, BIGNUM * t1 = nullptr): x(x1), y(y1), z(z1), t(t1) {};
|
||||
~EDDSAPoint () { BN_free (x); BN_free (y); BN_free(z); BN_free(t); };
|
||||
|
||||
EDDSAPoint& operator=(EDDSAPoint&& other)
|
||||
{
|
||||
if (x) BN_free (x); x = other.x; other.x = nullptr;
|
||||
if (y) BN_free (y); y = other.y; other.y = nullptr;
|
||||
if (z) BN_free (z); z = other.z; other.z = nullptr;
|
||||
if (t) BN_free (t); t = other.t; other.t = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
EDDSAPoint& operator=(const EDDSAPoint& other)
|
||||
{
|
||||
if (x) BN_free (x); x = other.x ? BN_dup (other.x) : nullptr;
|
||||
if (y) BN_free (y); y = other.y ? BN_dup (other.y) : nullptr;
|
||||
if (z) BN_free (z); z = other.z ? BN_dup (other.z) : nullptr;
|
||||
if (t) BN_free (t); t = other.t ? BN_dup (other.t) : nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
EDDSAPoint operator-() const
|
||||
{
|
||||
BIGNUM * x1 = NULL, * y1 = NULL, * z1 = NULL, * t1 = NULL;
|
||||
if (x) { x1 = BN_dup (x); BN_set_negative (x1, !BN_is_negative (x)); };
|
||||
if (y) y1 = BN_dup (y);
|
||||
if (z) z1 = BN_dup (z);
|
||||
if (t) { t1 = BN_dup (t); BN_set_negative (t1, !BN_is_negative (t)); };
|
||||
return EDDSAPoint {x1, y1, z1, t1};
|
||||
}
|
||||
};
|
||||
|
||||
const size_t EDDSA25519_PUBLIC_KEY_LENGTH = 32;
|
||||
const size_t EDDSA25519_SIGNATURE_LENGTH = 64;
|
||||
const size_t EDDSA25519_PRIVATE_KEY_LENGTH = 32;
|
||||
class EDDSA25519Verifier: public Verifier
|
||||
{
|
||||
public:
|
||||
|
||||
RSASHA5124096Verifier (const uint8_t * signingKey): RSAVerifier (signingKey)
|
||||
{
|
||||
}
|
||||
};
|
||||
EDDSA25519Verifier (const uint8_t * signingKey);
|
||||
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const;
|
||||
|
||||
class RSASHA5124096Signer: public RSASigner<CryptoPP::SHA512>
|
||||
{
|
||||
public:
|
||||
|
||||
RSASHA5124096Signer (const uint8_t * signingPrivateKey):
|
||||
RSASigner (signingPrivateKey, RSASHA5124096_KEY_LENGTH*2)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Raw verifiers
|
||||
class RawVerifier
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~RawVerifier () {};
|
||||
virtual void Update (const uint8_t * buf, size_t len) = 0;
|
||||
virtual bool Verify (const uint8_t * signature) = 0;
|
||||
};
|
||||
|
||||
template<typename Hash, size_t keyLen>
|
||||
class RSARawVerifier: public RawVerifier
|
||||
{
|
||||
public:
|
||||
|
||||
RSARawVerifier (const uint8_t * signingKey):
|
||||
n (signingKey, keyLen)
|
||||
{
|
||||
}
|
||||
|
||||
void Update (const uint8_t * buf, size_t len)
|
||||
{
|
||||
m_Hash.Update (buf, len);
|
||||
}
|
||||
|
||||
bool Verify (const uint8_t * signature)
|
||||
{
|
||||
// RSA encryption first
|
||||
CryptoPP::Integer enSig (a_exp_b_mod_c (CryptoPP::Integer (signature, keyLen),
|
||||
CryptoPP::Integer (i2p::crypto::rsae), n)); // s^e mod n
|
||||
uint8_t enSigBuf[keyLen];
|
||||
enSig.Encode (enSigBuf, keyLen);
|
||||
|
||||
uint8_t digest[Hash::DIGESTSIZE];
|
||||
m_Hash.Final (digest);
|
||||
if ((int)keyLen < Hash::DIGESTSIZE) return false; // can't verify digest longer than key
|
||||
// we assume digest is right aligned, at least for PKCS#1 v1.5 padding
|
||||
return !memcmp (enSigBuf + (keyLen - Hash::DIGESTSIZE), digest, Hash::DIGESTSIZE);
|
||||
}
|
||||
size_t GetPublicKeyLen () const { return EDDSA25519_PUBLIC_KEY_LENGTH; };
|
||||
size_t GetSignatureLen () const { return EDDSA25519_SIGNATURE_LENGTH; };
|
||||
|
||||
private:
|
||||
|
||||
CryptoPP::Integer n; // RSA modulus
|
||||
Hash m_Hash;
|
||||
};
|
||||
EDDSAPoint m_PublicKey;
|
||||
uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH];
|
||||
};
|
||||
|
||||
class RSASHA5124096RawVerifier: public RSARawVerifier<CryptoPP::SHA512, RSASHA5124096_KEY_LENGTH>
|
||||
class EDDSA25519Signer: public Signer
|
||||
{
|
||||
public:
|
||||
|
||||
RSASHA5124096RawVerifier (const uint8_t * signingKey): RSARawVerifier (signingKey)
|
||||
{
|
||||
}
|
||||
EDDSA25519Signer (const uint8_t * signingPrivateKey);
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const;
|
||||
const uint8_t * GetPublicKey () const { return m_PublicKeyEncoded; };
|
||||
|
||||
private:
|
||||
|
||||
uint8_t m_ExpandedPrivateKey[64];
|
||||
uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH];
|
||||
};
|
||||
|
||||
inline void CreateEDDSA25519RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
||||
{
|
||||
RAND_bytes (signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH);
|
||||
EDDSA25519Signer signer (signingPrivateKey);
|
||||
memcpy (signingPublicKey, signer.GetPublicKey (), EDDSA25519_PUBLIC_KEY_LENGTH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
650
Streaming.cpp
650
Streaming.cpp
File diff suppressed because it is too large
Load Diff
121
Streaming.h
121
Streaming.h
@@ -11,6 +11,7 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <boost/asio.hpp>
|
||||
#include "Base.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "Identity.h"
|
||||
#include "LeaseSet.h"
|
||||
@@ -41,22 +42,23 @@ namespace stream
|
||||
const size_t STREAMING_MTU = 1730;
|
||||
const size_t MAX_PACKET_SIZE = 4096;
|
||||
const size_t COMPRESSION_THRESHOLD_SIZE = 66;
|
||||
const int RESEND_TIMEOUT = 10; // in seconds
|
||||
const int ACK_SEND_TIMEOUT = 200; // in milliseconds
|
||||
const int MAX_NUM_RESEND_ATTEMPTS = 5;
|
||||
const int MAX_NUM_RESEND_ATTEMPTS = 6;
|
||||
const int WINDOW_SIZE = 6; // in messages
|
||||
const int MIN_WINDOW_SIZE = 1;
|
||||
const int MAX_WINDOW_SIZE = 128;
|
||||
const int INITIAL_RTT = 8000; // in milliseconds
|
||||
const int INITIAL_RTO = 9000; // in milliseconds
|
||||
const size_t MAX_PENDING_INCOMING_BACKLOG = 128;
|
||||
const int PENDING_INCOMING_TIMEOUT = 10; // in seconds
|
||||
|
||||
struct Packet
|
||||
{
|
||||
size_t len, offset;
|
||||
uint8_t buf[MAX_PACKET_SIZE];
|
||||
int numResendAttempts;
|
||||
uint64_t sendTime;
|
||||
|
||||
Packet (): len (0), offset (0), numResendAttempts (0), sendTime (0) {};
|
||||
Packet (): len (0), offset (0), sendTime (0) {};
|
||||
uint8_t * GetBuffer () { return buf + offset; };
|
||||
size_t GetLength () const { return len - offset; };
|
||||
|
||||
@@ -83,12 +85,23 @@ namespace stream
|
||||
return p1->GetSeqn () < p2->GetSeqn ();
|
||||
};
|
||||
};
|
||||
|
||||
enum StreamStatus
|
||||
{
|
||||
eStreamStatusNew = 0,
|
||||
eStreamStatusOpen,
|
||||
eStreamStatusReset,
|
||||
eStreamStatusClosing,
|
||||
eStreamStatusClosed
|
||||
};
|
||||
|
||||
class StreamingDestination;
|
||||
class Stream: public std::enable_shared_from_this<Stream>
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::function<void (const boost::system::error_code& ecode)> SendHandler;
|
||||
|
||||
Stream (boost::asio::io_service& service, StreamingDestination& local,
|
||||
std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0); // outgoing
|
||||
Stream (boost::asio::io_service& service, StreamingDestination& local); // incoming
|
||||
@@ -97,13 +110,15 @@ namespace stream
|
||||
uint32_t GetSendStreamID () const { return m_SendStreamID; };
|
||||
uint32_t GetRecvStreamID () const { return m_RecvStreamID; };
|
||||
std::shared_ptr<const i2p::data::LeaseSet> GetRemoteLeaseSet () const { return m_RemoteLeaseSet; };
|
||||
const i2p::data::IdentityEx& GetRemoteIdentity () const { return m_RemoteIdentity; };
|
||||
bool IsOpen () const { return m_IsOpen; };
|
||||
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity () const { return m_RemoteIdentity; };
|
||||
bool IsOpen () const { return m_Status == eStreamStatusOpen; };
|
||||
bool IsEstablished () const { return m_SendStreamID; };
|
||||
StreamStatus GetStatus () const { return m_Status; };
|
||||
StreamingDestination& GetLocalDestination () { return m_LocalDestination; };
|
||||
|
||||
void HandleNextPacket (Packet * packet);
|
||||
size_t Send (const uint8_t * buf, size_t len);
|
||||
void AsyncSend (const uint8_t * buf, size_t len, SendHandler handler);
|
||||
|
||||
template<typename Buffer, typename ReceiveHandler>
|
||||
void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0);
|
||||
@@ -122,8 +137,11 @@ namespace stream
|
||||
|
||||
private:
|
||||
|
||||
void Terminate ();
|
||||
|
||||
void SendBuffer ();
|
||||
void SendQuickAck ();
|
||||
void SendClose ();
|
||||
bool SendPacket (Packet * packet);
|
||||
void SendPackets (const std::vector<Packet *>& packets);
|
||||
|
||||
@@ -132,28 +150,27 @@ namespace stream
|
||||
void ProcessAck (Packet * packet);
|
||||
size_t ConcatenatePackets (uint8_t * buf, size_t len);
|
||||
|
||||
void UpdateCurrentRemoteLease ();
|
||||
void UpdateCurrentRemoteLease (bool expired = false);
|
||||
|
||||
template<typename Buffer, typename ReceiveHandler>
|
||||
void HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler);
|
||||
|
||||
|
||||
void ScheduleResend ();
|
||||
void HandleResendTimer (const boost::system::error_code& ecode);
|
||||
void HandleAckSendTimer (const boost::system::error_code& ecode);
|
||||
|
||||
I2NPMessage * CreateDataMessage (const uint8_t * payload, size_t len);
|
||||
|
||||
private:
|
||||
|
||||
boost::asio::io_service& m_Service;
|
||||
uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber;
|
||||
int32_t m_LastReceivedSequenceNumber;
|
||||
bool m_IsOpen, m_IsReset, m_IsAckSendScheduled;
|
||||
StreamStatus m_Status;
|
||||
bool m_IsAckSendScheduled;
|
||||
StreamingDestination& m_LocalDestination;
|
||||
i2p::data::IdentityEx m_RemoteIdentity;
|
||||
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
|
||||
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
|
||||
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
|
||||
i2p::data::Lease m_CurrentRemoteLease;
|
||||
std::shared_ptr<const i2p::data::Lease> m_CurrentRemoteLease;
|
||||
std::shared_ptr<i2p::tunnel::OutboundTunnel> m_CurrentOutboundTunnel;
|
||||
std::queue<Packet *> m_ReceiveQueue;
|
||||
std::set<Packet *, PacketCmp> m_SavedPackets;
|
||||
@@ -164,45 +181,58 @@ namespace stream
|
||||
|
||||
std::mutex m_SendBufferMutex;
|
||||
std::stringstream m_SendBuffer;
|
||||
int m_WindowSize, m_RTT;
|
||||
int m_WindowSize, m_RTT, m_RTO;
|
||||
uint64_t m_LastWindowSizeIncreaseTime;
|
||||
int m_NumResendAttempts;
|
||||
SendHandler m_SendHandler;
|
||||
};
|
||||
|
||||
class StreamingDestination
|
||||
class StreamingDestination: public std::enable_shared_from_this<StreamingDestination>
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::function<void (std::shared_ptr<Stream>)> Acceptor;
|
||||
|
||||
StreamingDestination (i2p::client::ClientDestination& owner): m_Owner (owner) {};
|
||||
~StreamingDestination () {};
|
||||
StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort = 0, bool gzip = true);
|
||||
~StreamingDestination ();
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
std::shared_ptr<Stream> CreateNewOutgoingStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
|
||||
void DeleteStream (std::shared_ptr<Stream> stream);
|
||||
void SetAcceptor (const Acceptor& acceptor) { m_Acceptor = acceptor; };
|
||||
void ResetAcceptor () { if (m_Acceptor) m_Acceptor (nullptr); m_Acceptor = nullptr; };
|
||||
void SetAcceptor (const Acceptor& acceptor);
|
||||
void ResetAcceptor ();
|
||||
bool IsAcceptorSet () const { return m_Acceptor != nullptr; };
|
||||
i2p::client::ClientDestination& GetOwner () { return m_Owner; };
|
||||
std::shared_ptr<i2p::client::ClientDestination> GetOwner () const { return m_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);
|
||||
|
||||
private:
|
||||
|
||||
void HandleNextPacket (Packet * packet);
|
||||
std::shared_ptr<Stream> CreateNewIncomingStream ();
|
||||
void HandlePendingIncomingTimer (const boost::system::error_code& ecode);
|
||||
|
||||
private:
|
||||
|
||||
i2p::client::ClientDestination& m_Owner;
|
||||
std::shared_ptr<i2p::client::ClientDestination> m_Owner;
|
||||
uint16_t m_LocalPort;
|
||||
bool m_Gzip; // gzip compression of data messages
|
||||
std::mutex m_StreamsMutex;
|
||||
std::map<uint32_t, std::shared_ptr<Stream> > m_Streams;
|
||||
std::map<uint32_t, std::shared_ptr<Stream> > m_Streams; // sendStreamID->stream
|
||||
Acceptor m_Acceptor;
|
||||
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
|
||||
|
||||
public:
|
||||
|
||||
i2p::data::GzipInflator m_Inflator;
|
||||
i2p::data::GzipDeflator m_Deflator;
|
||||
|
||||
// for HTTP only
|
||||
const decltype(m_Streams)& GetStreams () const { return m_Streams; };
|
||||
};
|
||||
@@ -212,46 +242,33 @@ namespace stream
|
||||
template<typename Buffer, typename ReceiveHandler>
|
||||
void Stream::AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout)
|
||||
{
|
||||
if (!m_ReceiveQueue.empty ())
|
||||
auto s = shared_from_this();
|
||||
m_Service.post ([=](void)
|
||||
{
|
||||
auto s = shared_from_this();
|
||||
m_Service.post ([=](void) { s->HandleReceiveTimer (
|
||||
boost::asio::error::make_error_code (boost::asio::error::operation_aborted),
|
||||
buffer, handler); });
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(timeout));
|
||||
auto s = shared_from_this();
|
||||
m_ReceiveTimer.async_wait ([=](const boost::system::error_code& ecode)
|
||||
{ s->HandleReceiveTimer (ecode, buffer, handler); });
|
||||
}
|
||||
if (!m_ReceiveQueue.empty () || m_Status == eStreamStatusReset)
|
||||
s->HandleReceiveTimer (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), buffer, handler);
|
||||
else
|
||||
{
|
||||
s->m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(timeout));
|
||||
s->m_ReceiveTimer.async_wait ([=](const boost::system::error_code& ecode)
|
||||
{ s->HandleReceiveTimer (ecode, buffer, handler); });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<typename Buffer, typename ReceiveHandler>
|
||||
void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler)
|
||||
{
|
||||
size_t received = ConcatenatePackets (boost::asio::buffer_cast<uint8_t *>(buffer), boost::asio::buffer_size(buffer));
|
||||
if (ecode == boost::asio::error::operation_aborted)
|
||||
if (received > 0)
|
||||
handler (boost::system::error_code (), received);
|
||||
else if (ecode == boost::asio::error::operation_aborted)
|
||||
{
|
||||
// timeout not expired
|
||||
if (m_IsOpen)
|
||||
// no error
|
||||
handler (boost::system::error_code (), received);
|
||||
if (m_Status == eStreamStatusReset)
|
||||
handler (boost::asio::error::make_error_code (boost::asio::error::connection_reset), 0);
|
||||
else
|
||||
{
|
||||
// stream closed
|
||||
if (m_IsReset)
|
||||
{
|
||||
// stream closed by peer
|
||||
handler (received > 0 ? boost::system::error_code () : // we still have some data
|
||||
boost::asio::error::make_error_code (boost::asio::error::connection_reset), // no more data
|
||||
received);
|
||||
|
||||
}
|
||||
else // stream closed by us
|
||||
handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), received);
|
||||
}
|
||||
handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), 0);
|
||||
}
|
||||
else
|
||||
// timeout expired
|
||||
|
||||
90
Tag.h
Normal file
90
Tag.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016, 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
|
||||
*/
|
||||
|
||||
#ifndef TAG_H__
|
||||
#define TAG_H__
|
||||
|
||||
#include <string.h> /* memcpy */
|
||||
|
||||
#include "Base.h"
|
||||
|
||||
namespace i2p {
|
||||
namespace data {
|
||||
template<int sz>
|
||||
class Tag
|
||||
{
|
||||
public:
|
||||
|
||||
Tag (const uint8_t * buf) { memcpy (m_Buf, buf, sz); };
|
||||
Tag (const Tag<sz>& ) = default;
|
||||
#ifndef _WIN32 // FIXME!!! msvs 2013 can't compile it
|
||||
Tag (Tag<sz>&& ) = default;
|
||||
#endif
|
||||
Tag () = default;
|
||||
|
||||
Tag<sz>& operator= (const Tag<sz>& ) = default;
|
||||
#ifndef _WIN32
|
||||
Tag<sz>& operator= (Tag<sz>&& ) = default;
|
||||
#endif
|
||||
|
||||
uint8_t * operator()() { return m_Buf; };
|
||||
const uint8_t * operator()() const { return m_Buf; };
|
||||
|
||||
operator uint8_t * () { return m_Buf; };
|
||||
operator const uint8_t * () const { return m_Buf; };
|
||||
|
||||
const uint64_t * GetLL () const { return ll; };
|
||||
|
||||
bool operator== (const Tag<sz>& other) const { return !memcmp (m_Buf, other.m_Buf, sz); };
|
||||
bool operator< (const Tag<sz>& other) const { return memcmp (m_Buf, other.m_Buf, sz) < 0; };
|
||||
|
||||
bool IsZero () const
|
||||
{
|
||||
for (int i = 0; i < sz/8; i++)
|
||||
if (ll[i]) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string ToBase64 () const
|
||||
{
|
||||
char str[sz*2];
|
||||
int l = i2p::data::ByteStreamToBase64 (m_Buf, sz, str, sz*2);
|
||||
str[l] = 0;
|
||||
return std::string (str);
|
||||
}
|
||||
|
||||
std::string ToBase32 () const
|
||||
{
|
||||
char str[sz*2];
|
||||
int l = i2p::data::ByteStreamToBase32 (m_Buf, sz, str, sz*2);
|
||||
str[l] = 0;
|
||||
return std::string (str);
|
||||
}
|
||||
|
||||
void FromBase32 (const std::string& s)
|
||||
{
|
||||
i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz);
|
||||
}
|
||||
|
||||
void FromBase64 (const std::string& s)
|
||||
{
|
||||
i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
union // 8 bytes alignment
|
||||
{
|
||||
uint8_t m_Buf[sz];
|
||||
uint64_t ll[sz/8];
|
||||
};
|
||||
};
|
||||
} // data
|
||||
} // i2p
|
||||
|
||||
#endif /* TAG_H__ */
|
||||
@@ -14,56 +14,54 @@ namespace tunnel
|
||||
TransitTunnel::TransitTunnel (uint32_t receiveTunnelID,
|
||||
const uint8_t * nextIdent, uint32_t nextTunnelID,
|
||||
const uint8_t * layerKey,const uint8_t * ivKey):
|
||||
m_TunnelID (receiveTunnelID), m_NextTunnelID (nextTunnelID),
|
||||
m_NextIdent (nextIdent)
|
||||
TunnelBase (receiveTunnelID, nextTunnelID, nextIdent)
|
||||
{
|
||||
m_Encryption.SetKeys (layerKey, ivKey);
|
||||
}
|
||||
|
||||
void TransitTunnel::EncryptTunnelMsg (I2NPMessage * tunnelMsg)
|
||||
void TransitTunnel::EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out)
|
||||
{
|
||||
m_Encryption.Encrypt (tunnelMsg->GetPayload () + 4);
|
||||
m_Encryption.Encrypt (in->GetPayload () + 4, out->GetPayload () + 4);
|
||||
}
|
||||
|
||||
TransitTunnelParticipant::~TransitTunnelParticipant ()
|
||||
{
|
||||
for (auto it: m_TunnelDataMsgs)
|
||||
i2p::DeleteI2NPMessage (it);
|
||||
}
|
||||
|
||||
void TransitTunnelParticipant::HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg)
|
||||
void TransitTunnelParticipant::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg)
|
||||
{
|
||||
EncryptTunnelMsg (tunnelMsg);
|
||||
auto newMsg = CreateEmptyTunnelDataMsg ();
|
||||
EncryptTunnelMsg (tunnelMsg, newMsg);
|
||||
|
||||
m_NumTransmittedBytes += tunnelMsg->GetLength ();
|
||||
htobe32buf (tunnelMsg->GetPayload (), GetNextTunnelID ());
|
||||
FillI2NPMessageHeader (tunnelMsg, eI2NPTunnelData);
|
||||
m_TunnelDataMsgs.push_back (tunnelMsg);
|
||||
htobe32buf (newMsg->GetPayload (), GetNextTunnelID ());
|
||||
newMsg->FillI2NPMessageHeader (eI2NPTunnelData);
|
||||
m_TunnelDataMsgs.push_back (newMsg);
|
||||
}
|
||||
|
||||
void TransitTunnelParticipant::FlushTunnelDataMsgs ()
|
||||
{
|
||||
if (!m_TunnelDataMsgs.empty ())
|
||||
{
|
||||
LogPrint (eLogDebug, "TransitTunnel: ",GetTunnelID (),"->", GetNextTunnelID (), " ", m_TunnelDataMsgs.size ());
|
||||
auto num = m_TunnelDataMsgs.size ();
|
||||
if (num > 1)
|
||||
LogPrint (eLogDebug, "TransitTunnel: ", GetTunnelID (), "->", GetNextTunnelID (), " ", num);
|
||||
i2p::transport::transports.SendMessages (GetNextIdentHash (), m_TunnelDataMsgs);
|
||||
m_TunnelDataMsgs.clear ();
|
||||
}
|
||||
}
|
||||
|
||||
void TransitTunnel::SendTunnelDataMsg (i2p::I2NPMessage * msg)
|
||||
void TransitTunnel::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg)
|
||||
{
|
||||
LogPrint (eLogError, "We are not a gateway for transit tunnel ", m_TunnelID);
|
||||
i2p::DeleteI2NPMessage (msg);
|
||||
LogPrint (eLogError, "TransitTunnel: We are not a gateway for ", GetTunnelID ());
|
||||
}
|
||||
|
||||
void TransitTunnel::HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg)
|
||||
void TransitTunnel::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg)
|
||||
{
|
||||
LogPrint (eLogError, "Incoming tunnel message is not supported ", m_TunnelID);
|
||||
DeleteI2NPMessage (tunnelMsg);
|
||||
LogPrint (eLogError, "TransitTunnel: Incoming tunnel message is not supported ", GetTunnelID ());
|
||||
}
|
||||
|
||||
void TransitTunnelGateway::SendTunnelDataMsg (i2p::I2NPMessage * msg)
|
||||
void TransitTunnelGateway::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg)
|
||||
{
|
||||
TunnelMessageBlock block;
|
||||
block.deliveryType = eDeliveryTypeLocal;
|
||||
@@ -78,33 +76,34 @@ namespace tunnel
|
||||
m_Gateway.SendBuffer ();
|
||||
}
|
||||
|
||||
void TransitTunnelEndpoint::HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg)
|
||||
void TransitTunnelEndpoint::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg)
|
||||
{
|
||||
EncryptTunnelMsg (tunnelMsg);
|
||||
auto newMsg = CreateEmptyTunnelDataMsg ();
|
||||
EncryptTunnelMsg (tunnelMsg, newMsg);
|
||||
|
||||
LogPrint (eLogDebug, "TransitTunnel endpoint for ", GetTunnelID ());
|
||||
m_Endpoint.HandleDecryptedTunnelDataMsg (tunnelMsg);
|
||||
LogPrint (eLogDebug, "TransitTunnel: handle msg for endpoint ", GetTunnelID ());
|
||||
m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg);
|
||||
}
|
||||
|
||||
TransitTunnel * CreateTransitTunnel (uint32_t receiveTunnelID,
|
||||
std::shared_ptr<TransitTunnel> CreateTransitTunnel (uint32_t receiveTunnelID,
|
||||
const uint8_t * nextIdent, uint32_t nextTunnelID,
|
||||
const uint8_t * layerKey,const uint8_t * ivKey,
|
||||
bool isGateway, bool isEndpoint)
|
||||
{
|
||||
if (isEndpoint)
|
||||
{
|
||||
LogPrint (eLogInfo, "TransitTunnel endpoint: ", receiveTunnelID, " created");
|
||||
return new TransitTunnelEndpoint (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
|
||||
LogPrint (eLogDebug, "TransitTunnel: endpoint ", receiveTunnelID, " created");
|
||||
return std::make_shared<TransitTunnelEndpoint> (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
|
||||
}
|
||||
else if (isGateway)
|
||||
{
|
||||
LogPrint (eLogInfo, "TransitTunnel gateway: ", receiveTunnelID, " created");
|
||||
return new TransitTunnelGateway (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
|
||||
LogPrint (eLogInfo, "TransitTunnel: gateway ", receiveTunnelID, " created");
|
||||
return std::make_shared<TransitTunnelGateway> (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogInfo, "TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created");
|
||||
return new TransitTunnelParticipant (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
|
||||
LogPrint (eLogDebug, "TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created");
|
||||
return std::make_shared<TransitTunnelParticipant> (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
#include <inttypes.h>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include "aes.h"
|
||||
#include <memory>
|
||||
#include "Crypto.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "TunnelEndpoint.h"
|
||||
#include "TunnelGateway.h"
|
||||
@@ -23,20 +24,12 @@ namespace tunnel
|
||||
const uint8_t * layerKey,const uint8_t * ivKey);
|
||||
|
||||
virtual size_t GetNumTransmittedBytes () const { return 0; };
|
||||
|
||||
uint32_t GetTunnelID () const { return m_TunnelID; };
|
||||
|
||||
// implements TunnelBase
|
||||
void SendTunnelDataMsg (i2p::I2NPMessage * msg);
|
||||
void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg);
|
||||
void EncryptTunnelMsg (I2NPMessage * tunnelMsg);
|
||||
uint32_t GetNextTunnelID () const { return m_NextTunnelID; };
|
||||
const i2p::data::IdentHash& GetNextIdentHash () const { return m_NextIdent; };
|
||||
|
||||
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
|
||||
void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg);
|
||||
void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out);
|
||||
private:
|
||||
|
||||
uint32_t m_TunnelID, m_NextTunnelID;
|
||||
i2p::data::IdentHash m_NextIdent;
|
||||
|
||||
i2p::crypto::TunnelEncryption m_Encryption;
|
||||
};
|
||||
@@ -53,13 +46,13 @@ namespace tunnel
|
||||
~TransitTunnelParticipant ();
|
||||
|
||||
size_t GetNumTransmittedBytes () const { return m_NumTransmittedBytes; };
|
||||
void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg);
|
||||
void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg);
|
||||
void FlushTunnelDataMsgs ();
|
||||
|
||||
private:
|
||||
|
||||
size_t m_NumTransmittedBytes;
|
||||
std::vector<i2p::I2NPMessage *> m_TunnelDataMsgs;
|
||||
std::vector<std::shared_ptr<i2p::I2NPMessage> > m_TunnelDataMsgs;
|
||||
};
|
||||
|
||||
class TransitTunnelGateway: public TransitTunnel
|
||||
@@ -72,7 +65,7 @@ namespace tunnel
|
||||
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID,
|
||||
layerKey, ivKey), m_Gateway(this) {};
|
||||
|
||||
void SendTunnelDataMsg (i2p::I2NPMessage * msg);
|
||||
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
|
||||
void FlushTunnelDataMsgs ();
|
||||
size_t GetNumTransmittedBytes () const { return m_Gateway.GetNumSentBytes (); };
|
||||
|
||||
@@ -92,7 +85,7 @@ namespace tunnel
|
||||
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey),
|
||||
m_Endpoint (false) {}; // transit endpoint is always outbound
|
||||
|
||||
void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg);
|
||||
void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg);
|
||||
size_t GetNumTransmittedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }
|
||||
|
||||
private:
|
||||
@@ -100,7 +93,7 @@ namespace tunnel
|
||||
TunnelEndpoint m_Endpoint;
|
||||
};
|
||||
|
||||
TransitTunnel * CreateTransitTunnel (uint32_t receiveTunnelID,
|
||||
std::shared_ptr<TransitTunnel> CreateTransitTunnel (uint32_t receiveTunnelID,
|
||||
const uint8_t * nextIdent, uint32_t nextTunnelID,
|
||||
const uint8_t * layerKey,const uint8_t * ivKey,
|
||||
bool isGateway, bool isEndpoint);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "Identity.h"
|
||||
#include "Crypto.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "I2NPProtocol.h"
|
||||
|
||||
@@ -13,17 +14,15 @@ namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
struct DHKeysPair // transient keys for transport sessions
|
||||
{
|
||||
uint8_t publicKey[256];
|
||||
uint8_t privateKey[256];
|
||||
};
|
||||
|
||||
class SignedData
|
||||
{
|
||||
public:
|
||||
|
||||
SignedData () {};
|
||||
SignedData () {}
|
||||
SignedData (const SignedData& other)
|
||||
{
|
||||
m_Stream << other.m_Stream.rdbuf ();
|
||||
}
|
||||
void Insert (const uint8_t * buf, size_t len)
|
||||
{
|
||||
m_Stream.write ((char *)buf, len);
|
||||
@@ -35,9 +34,9 @@ namespace transport
|
||||
m_Stream.write ((char *)&t, sizeof (T));
|
||||
}
|
||||
|
||||
bool Verify (const i2p::data::IdentityEx& ident, const uint8_t * signature) const
|
||||
bool Verify (std::shared_ptr<const i2p::data::IdentityEx> ident, const uint8_t * signature) const
|
||||
{
|
||||
return ident.Verify ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature);
|
||||
return ident->Verify ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature);
|
||||
}
|
||||
|
||||
void Sign (const i2p::data::PrivateKeys& keys, uint8_t * signature) const
|
||||
@@ -54,27 +53,35 @@ namespace transport
|
||||
{
|
||||
public:
|
||||
|
||||
TransportSession (std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter):
|
||||
m_RemoteRouter (in_RemoteRouter), m_DHKeysPair (nullptr)
|
||||
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)
|
||||
{
|
||||
if (m_RemoteRouter)
|
||||
m_RemoteIdentity = m_RemoteRouter->GetRouterIdentity ();
|
||||
if (router)
|
||||
m_RemoteIdentity = router->GetRouterIdentity ();
|
||||
}
|
||||
|
||||
virtual ~TransportSession () { delete m_DHKeysPair; };
|
||||
virtual ~TransportSession () {};
|
||||
virtual void Done () = 0;
|
||||
|
||||
std::shared_ptr<const i2p::data::RouterInfo> GetRemoteRouter () { return m_RemoteRouter; };
|
||||
const i2p::data::IdentityEx& GetRemoteIdentity () { return m_RemoteIdentity; };
|
||||
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity () { return m_RemoteIdentity; };
|
||||
void SetRemoteIdentity (std::shared_ptr<const i2p::data::IdentityEx> ident) { m_RemoteIdentity = ident; };
|
||||
|
||||
size_t GetNumSentBytes () const { return m_NumSentBytes; };
|
||||
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
|
||||
bool IsOutgoing () const { return m_IsOutgoing; };
|
||||
|
||||
int GetTerminationTimeout () const { return m_TerminationTimeout; };
|
||||
void SetTerminationTimeout (int terminationTimeout) { m_TerminationTimeout = terminationTimeout; };
|
||||
|
||||
virtual void SendI2NPMessage (I2NPMessage * msg) = 0;
|
||||
virtual void SendI2NPMessages (const std::vector<I2NPMessage *>& msgs) = 0;
|
||||
virtual void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) = 0;
|
||||
|
||||
protected:
|
||||
|
||||
std::shared_ptr<const i2p::data::RouterInfo> m_RemoteRouter;
|
||||
i2p::data::IdentityEx m_RemoteIdentity;
|
||||
DHKeysPair * m_DHKeysPair; // X - for client and Y - for server
|
||||
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
|
||||
std::shared_ptr<i2p::crypto::DHKeys> m_DHKeysPair; // X - for client and Y - for server
|
||||
size_t m_NumSentBytes, m_NumReceivedBytes;
|
||||
bool m_IsOutgoing;
|
||||
int m_TerminationTimeout;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
498
Transports.cpp
498
Transports.cpp
@@ -1,6 +1,5 @@
|
||||
#include <cryptopp/dh.h>
|
||||
#include "Log.h"
|
||||
#include "CryptoConst.h"
|
||||
#include "Crypto.h"
|
||||
#include "RouterContext.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "NetDb.h"
|
||||
@@ -47,7 +46,7 @@ namespace transport
|
||||
int num;
|
||||
while ((num = m_QueueSize - m_Queue.size ()) > 0)
|
||||
CreateDHKeysPairs (num);
|
||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
||||
m_Acquired.wait (l); // wait for element gets aquired
|
||||
}
|
||||
}
|
||||
@@ -56,48 +55,48 @@ namespace transport
|
||||
{
|
||||
if (num > 0)
|
||||
{
|
||||
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
|
||||
i2p::crypto::DHKeys dh;
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
i2p::transport::DHKeysPair * pair = new i2p::transport::DHKeysPair ();
|
||||
dh.GenerateKeyPair(m_Rnd, pair->privateKey, pair->publicKey);
|
||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
||||
auto pair = std::make_shared<i2p::crypto::DHKeys> ();
|
||||
pair->GenerateKeys ();
|
||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
||||
m_Queue.push (pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DHKeysPair * DHKeysPairSupplier::Acquire ()
|
||||
std::shared_ptr<i2p::crypto::DHKeys> DHKeysPairSupplier::Acquire ()
|
||||
{
|
||||
if (!m_Queue.empty ())
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
||||
auto pair = m_Queue.front ();
|
||||
m_Queue.pop ();
|
||||
m_Acquired.notify_one ();
|
||||
return pair;
|
||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
||||
if (!m_Queue.empty ())
|
||||
{
|
||||
auto pair = m_Queue.front ();
|
||||
m_Queue.pop ();
|
||||
m_Acquired.notify_one ();
|
||||
return pair;
|
||||
}
|
||||
}
|
||||
else // queue is empty, create new
|
||||
{
|
||||
DHKeysPair * pair = new DHKeysPair ();
|
||||
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
|
||||
dh.GenerateKeyPair(m_Rnd, pair->privateKey, pair->publicKey);
|
||||
return pair;
|
||||
}
|
||||
// queue is empty, create new
|
||||
auto pair = std::make_shared<i2p::crypto::DHKeys> ();
|
||||
pair->GenerateKeys ();
|
||||
return pair;
|
||||
}
|
||||
|
||||
void DHKeysPairSupplier::Return (DHKeysPair * pair)
|
||||
void DHKeysPairSupplier::Return (std::shared_ptr<i2p::crypto::DHKeys> pair)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
||||
m_Queue.push (pair);
|
||||
}
|
||||
|
||||
Transports transports;
|
||||
|
||||
Transports::Transports ():
|
||||
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_PeerCleanupTimer (m_Service),
|
||||
m_NTCPServer (nullptr), m_SSUServer (nullptr),
|
||||
m_DHKeysPairSupplier (5) // 5 pre-generated keys
|
||||
m_IsOnline (true), m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_PeerCleanupTimer (m_Service),
|
||||
m_NTCPServer (nullptr), m_SSUServer (nullptr), m_DHKeysPairSupplier (5), // 5 pre-generated keys
|
||||
m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_InBandwidth (0), m_OutBandwidth (0),
|
||||
m_LastInBandwidthUpdateBytes (0), m_LastOutBandwidthUpdateBytes (0), m_LastBandwidthUpdateTime (0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -106,32 +105,49 @@ namespace transport
|
||||
Stop ();
|
||||
}
|
||||
|
||||
void Transports::Start ()
|
||||
void Transports::Start (bool enableNTCP, bool enableSSU)
|
||||
{
|
||||
m_DHKeysPairSupplier.Start ();
|
||||
m_IsRunning = true;
|
||||
m_Thread = new std::thread (std::bind (&Transports::Run, this));
|
||||
// create acceptors
|
||||
auto addresses = context.GetRouterInfo ().GetAddresses ();
|
||||
for (auto& address : addresses)
|
||||
auto& addresses = context.GetRouterInfo ().GetAddresses ();
|
||||
for (const auto& address : addresses)
|
||||
{
|
||||
if (!m_NTCPServer)
|
||||
{
|
||||
m_NTCPServer = new NTCPServer (address.port);
|
||||
if (m_NTCPServer == nullptr && enableNTCP)
|
||||
{
|
||||
m_NTCPServer = new NTCPServer ();
|
||||
m_NTCPServer->Start ();
|
||||
if (!(m_NTCPServer->IsBoundV6() || m_NTCPServer->IsBoundV4())) {
|
||||
/** failed to bind to NTCP */
|
||||
LogPrint(eLogError, "Transports: failed to bind to TCP");
|
||||
m_NTCPServer->Stop();
|
||||
delete m_NTCPServer;
|
||||
m_NTCPServer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (address.transportStyle == RouterInfo::eTransportSSU && address.host.is_v4 ())
|
||||
if (address->transportStyle == RouterInfo::eTransportSSU)
|
||||
{
|
||||
if (!m_SSUServer)
|
||||
{
|
||||
m_SSUServer = new SSUServer (address.port);
|
||||
LogPrint ("Start listening UDP port ", address.port);
|
||||
m_SSUServer->Start ();
|
||||
if (m_SSUServer == nullptr && enableSSU)
|
||||
{
|
||||
if (address->host.is_v4())
|
||||
m_SSUServer = new SSUServer (address->port);
|
||||
else
|
||||
m_SSUServer = new SSUServer (address->host, address->port);
|
||||
LogPrint (eLogInfo, "Transports: Start listening UDP port ", address->port);
|
||||
try {
|
||||
m_SSUServer->Start ();
|
||||
} catch ( std::exception & ex ) {
|
||||
LogPrint(eLogError, "Transports: Failed to bind to UDP port", address->port);
|
||||
delete m_SSUServer;
|
||||
m_SSUServer = nullptr;
|
||||
continue;
|
||||
}
|
||||
DetectExternalIP ();
|
||||
}
|
||||
else
|
||||
LogPrint ("SSU server already exists");
|
||||
LogPrint (eLogError, "Transports: SSU server already exists");
|
||||
}
|
||||
}
|
||||
m_PeerCleanupTimer.expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
|
||||
@@ -176,77 +192,89 @@ namespace transport
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint ("Transports: ", ex.what ());
|
||||
LogPrint (eLogError, "Transports: runtime exception: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Transports::SendMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg)
|
||||
void Transports::UpdateBandwidth ()
|
||||
{
|
||||
m_Service.post (std::bind (&Transports::PostMessage, this, ident, msg));
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
if (m_LastBandwidthUpdateTime > 0)
|
||||
{
|
||||
auto delta = ts - m_LastBandwidthUpdateTime;
|
||||
if (delta > 0)
|
||||
{
|
||||
m_InBandwidth = (m_TotalReceivedBytes - m_LastInBandwidthUpdateBytes)*1000/delta; // per second
|
||||
m_OutBandwidth = (m_TotalSentBytes - m_LastOutBandwidthUpdateBytes)*1000/delta; // per second
|
||||
}
|
||||
}
|
||||
m_LastBandwidthUpdateTime = ts;
|
||||
m_LastInBandwidthUpdateBytes = m_TotalReceivedBytes;
|
||||
m_LastOutBandwidthUpdateBytes = m_TotalSentBytes;
|
||||
}
|
||||
|
||||
bool Transports::IsBandwidthExceeded () const
|
||||
{
|
||||
auto limit = i2p::context.GetBandwidthLimit() * 1024; // convert to bytes
|
||||
auto bw = std::max (m_InBandwidth, m_OutBandwidth);
|
||||
return bw > limit;
|
||||
}
|
||||
|
||||
void Transports::SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg)
|
||||
{
|
||||
SendMessages (ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > {msg });
|
||||
}
|
||||
|
||||
void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector<i2p::I2NPMessage *>& msgs)
|
||||
void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs)
|
||||
{
|
||||
m_Service.post (std::bind (&Transports::PostMessages, this, ident, msgs));
|
||||
}
|
||||
|
||||
void Transports::PostMessage (i2p::data::IdentHash ident, i2p::I2NPMessage * msg)
|
||||
|
||||
void Transports::PostMessages (i2p::data::IdentHash ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > msgs)
|
||||
{
|
||||
if (ident == i2p::context.GetRouterInfo ().GetIdentHash ())
|
||||
{
|
||||
// we send it to ourself
|
||||
i2p::HandleI2NPMessage (msg);
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = m_Peers.find (ident);
|
||||
if (it == m_Peers.end ())
|
||||
{
|
||||
auto r = netdb.FindRouter (ident);
|
||||
it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, { 0, r, nullptr,
|
||||
i2p::util::GetSecondsSinceEpoch () })).first;
|
||||
if (!ConnectToPeer (ident, it->second))
|
||||
{
|
||||
DeleteI2NPMessage (msg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (it->second.session)
|
||||
it->second.session->SendI2NPMessage (msg);
|
||||
else
|
||||
it->second.delayedMessages.push_back (msg);
|
||||
}
|
||||
|
||||
void Transports::PostMessages (i2p::data::IdentHash ident, std::vector<i2p::I2NPMessage *> msgs)
|
||||
{
|
||||
if (ident == i2p::context.GetRouterInfo ().GetIdentHash ())
|
||||
{
|
||||
// we send it to ourself
|
||||
for (auto it: msgs)
|
||||
for (auto& it: msgs)
|
||||
i2p::HandleI2NPMessage (it);
|
||||
return;
|
||||
}
|
||||
auto it = m_Peers.find (ident);
|
||||
if (it == m_Peers.end ())
|
||||
{
|
||||
auto r = netdb.FindRouter (ident);
|
||||
it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, { 0, r, nullptr,
|
||||
i2p::util::GetSecondsSinceEpoch () })).first;
|
||||
if (!ConnectToPeer (ident, it->second))
|
||||
bool connected = false;
|
||||
try
|
||||
{
|
||||
for (auto it1: msgs)
|
||||
DeleteI2NPMessage (it1);
|
||||
return;
|
||||
}
|
||||
auto r = netdb.FindRouter (ident);
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, { 0, r, {},
|
||||
i2p::util::GetSecondsSinceEpoch (), {} })).first;
|
||||
}
|
||||
connected = ConnectToPeer (ident, it->second);
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "Transports: PostMessages exception:", ex.what ());
|
||||
}
|
||||
if (!connected) return;
|
||||
}
|
||||
if (it->second.session)
|
||||
it->second.session->SendI2NPMessages (msgs);
|
||||
if (!it->second.sessions.empty ())
|
||||
it->second.sessions.front ()->SendI2NPMessages (msgs);
|
||||
else
|
||||
{
|
||||
for (auto it1: msgs)
|
||||
it->second.delayedMessages.push_back (it1);
|
||||
if (it->second.delayedMessages.size () < MAX_NUM_DELAYED_MESSAGES)
|
||||
{
|
||||
for (auto& it1: msgs)
|
||||
it->second.delayedMessages.push_back (it1);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Transports: delayed messages queue size exceeds ", MAX_NUM_DELAYED_MESSAGES);
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
m_Peers.erase (it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +286,7 @@ namespace transport
|
||||
{
|
||||
peer.numAttempts++;
|
||||
auto address = peer.router->GetNTCPAddress (!context.SupportsV6 ());
|
||||
if (address)
|
||||
if (address && m_NTCPServer)
|
||||
{
|
||||
#if BOOST_VERSION >= 104900
|
||||
if (!address->host.is_unspecified ()) // we have address now
|
||||
@@ -279,30 +307,52 @@ namespace transport
|
||||
{
|
||||
if (address->addressString.length () > 0) // trying to resolve
|
||||
{
|
||||
LogPrint (eLogInfo, "Resolving ", address->addressString);
|
||||
LogPrint (eLogDebug, "Transports: Resolving NTCP ", address->addressString);
|
||||
NTCPResolve (address->addressString, ident);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogDebug, "Transports: NTCP address is not present for ", i2p::data::GetIdentHashAbbreviation (ident), ", trying SSU");
|
||||
}
|
||||
else if (peer.numAttempts == 1)// SSU
|
||||
if (peer.numAttempts == 1)// SSU
|
||||
{
|
||||
peer.numAttempts++;
|
||||
if (m_SSUServer)
|
||||
{
|
||||
if (m_SSUServer->GetSession (peer.router))
|
||||
if (m_SSUServer && peer.router->IsSSU (!context.SupportsV6 ()))
|
||||
{
|
||||
auto address = peer.router->GetSSUAddress (!context.SupportsV6 ());
|
||||
#if BOOST_VERSION >= 104900
|
||||
if (!address->host.is_unspecified ()) // we have address now
|
||||
#else
|
||||
boost::system::error_code ecode;
|
||||
address->host.to_string (ecode);
|
||||
if (!ecode)
|
||||
#endif
|
||||
{
|
||||
m_SSUServer->CreateSession (peer.router, address->host, address->port);
|
||||
return true;
|
||||
}
|
||||
else // we don't have address
|
||||
{
|
||||
if (address->addressString.length () > 0) // trying to resolve
|
||||
{
|
||||
LogPrint (eLogDebug, "Transports: Resolving SSU ", address->addressString);
|
||||
SSUResolve (address->addressString, ident);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LogPrint (eLogError, "No NTCP and SSU addresses available");
|
||||
if (peer.session) peer.session->Done ();
|
||||
LogPrint (eLogError, "Transports: No NTCP or SSU addresses available");
|
||||
peer.Done ();
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
m_Peers.erase (ident);
|
||||
return false;
|
||||
}
|
||||
else // otherwise request RI
|
||||
{
|
||||
LogPrint ("Router not found. Requested");
|
||||
LogPrint (eLogInfo, "Transports: RouterInfo for ", ident.ToBase64 (), " not found, requested");
|
||||
i2p::data::netdb.RequestDestination (ident, std::bind (
|
||||
&Transports::RequestComplete, this, std::placeholders::_1, ident));
|
||||
}
|
||||
@@ -314,20 +364,21 @@ namespace transport
|
||||
m_Service.post (std::bind (&Transports::HandleRequestComplete, this, r, ident));
|
||||
}
|
||||
|
||||
void Transports::HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident)
|
||||
void Transports::HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, i2p::data::IdentHash ident)
|
||||
{
|
||||
auto it = m_Peers.find (ident);
|
||||
if (it != m_Peers.end ())
|
||||
{
|
||||
if (r)
|
||||
{
|
||||
LogPrint ("Router found. Trying to connect");
|
||||
LogPrint (eLogDebug, "Transports: RouterInfo for ", ident.ToBase64 (), " found, Trying to connect");
|
||||
it->second.router = r;
|
||||
ConnectToPeer (ident, it->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("Router not found. Failed to send messages");
|
||||
LogPrint (eLogError, "Transports: RouterInfo not found, Failed to send messages");
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
m_Peers.erase (it);
|
||||
}
|
||||
}
|
||||
@@ -350,17 +401,70 @@ namespace transport
|
||||
auto& peer = it1->second;
|
||||
if (!ecode && peer.router)
|
||||
{
|
||||
auto address = (*it).endpoint ().address ();
|
||||
LogPrint (eLogInfo, (*it).host_name (), " has been resolved to ", address);
|
||||
auto addr = peer.router->GetNTCPAddress ();
|
||||
if (addr)
|
||||
{
|
||||
auto s = std::make_shared<NTCPSession> (*m_NTCPServer, peer.router);
|
||||
m_NTCPServer->Connect (address, addr->port, s);
|
||||
return;
|
||||
while (it != boost::asio::ip::tcp::resolver::iterator())
|
||||
{
|
||||
auto address = (*it).endpoint ().address ();
|
||||
LogPrint (eLogDebug, "Transports: ", (*it).host_name (), " has been resolved to ", address);
|
||||
if (address.is_v4 () || context.SupportsV6 ())
|
||||
{
|
||||
auto addr = peer.router->GetNTCPAddress (); // TODO: take one we requested
|
||||
if (addr)
|
||||
{
|
||||
auto s = std::make_shared<NTCPSession> (*m_NTCPServer, peer.router);
|
||||
m_NTCPServer->Connect (address, addr->port, s);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogInfo, "Transports: NTCP ", address, " is not supported");
|
||||
it++;
|
||||
}
|
||||
}
|
||||
LogPrint (eLogError, "Unable to resolve NTCP address: ", ecode.message ());
|
||||
LogPrint (eLogError, "Transports: Unable to resolve NTCP address: ", ecode.message ());
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
m_Peers.erase (it1);
|
||||
}
|
||||
}
|
||||
|
||||
void Transports::SSUResolve (const std::string& addr, const i2p::data::IdentHash& ident)
|
||||
{
|
||||
auto resolver = std::make_shared<boost::asio::ip::tcp::resolver>(m_Service);
|
||||
resolver->async_resolve (boost::asio::ip::tcp::resolver::query (addr, ""),
|
||||
std::bind (&Transports::HandleSSUResolve, this,
|
||||
std::placeholders::_1, std::placeholders::_2, ident, resolver));
|
||||
}
|
||||
|
||||
void Transports::HandleSSUResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
|
||||
i2p::data::IdentHash ident, std::shared_ptr<boost::asio::ip::tcp::resolver> resolver)
|
||||
{
|
||||
auto it1 = m_Peers.find (ident);
|
||||
if (it1 != m_Peers.end ())
|
||||
{
|
||||
auto& peer = it1->second;
|
||||
if (!ecode && peer.router)
|
||||
{
|
||||
while (it != boost::asio::ip::tcp::resolver::iterator())
|
||||
{
|
||||
auto address = (*it).endpoint ().address ();
|
||||
LogPrint (eLogDebug, "Transports: ", (*it).host_name (), " has been resolved to ", address);
|
||||
if (address.is_v4 () || context.SupportsV6 ())
|
||||
{
|
||||
auto addr = peer.router->GetSSUAddress (); // TODO: take one we requested
|
||||
if (addr)
|
||||
{
|
||||
m_SSUServer->CreateSession (peer.router, address, addr->port);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogInfo, "Transports: SSU ", address, " is not supported");
|
||||
it++;
|
||||
}
|
||||
}
|
||||
LogPrint (eLogError, "Transports: Unable to resolve SSU address: ", ecode.message ());
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
m_Peers.erase (it1);
|
||||
}
|
||||
}
|
||||
@@ -368,7 +472,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)
|
||||
@@ -377,27 +481,69 @@ namespace transport
|
||||
if (ssuSession) // try SSU first
|
||||
{
|
||||
m_SSUServer->DeleteSession (ssuSession);
|
||||
LogPrint ("SSU session closed");
|
||||
}
|
||||
// TODO: delete NTCP
|
||||
LogPrint (eLogDebug, "Transports: SSU session closed");
|
||||
}
|
||||
auto ntcpSession = m_NTCPServer ? m_NTCPServer->FindNTCPSession(router->GetIdentHash()) : nullptr;
|
||||
if (ntcpSession) // try deleting ntcp session too
|
||||
{
|
||||
ntcpSession->Terminate ();
|
||||
LogPrint(eLogDebug, "Transports: NTCP session closed");
|
||||
}
|
||||
}
|
||||
|
||||
void Transports::DetectExternalIP ()
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
if (m_SSUServer)
|
||||
{
|
||||
auto router = i2p::data::netdb.GetRandomRouter ();
|
||||
if (router && router->IsSSU () && m_SSUServer)
|
||||
m_SSUServer->GetSession (router, true); // peer test
|
||||
}
|
||||
#ifndef MESHNET
|
||||
i2p::context.SetStatus (eRouterStatusTesting);
|
||||
#endif
|
||||
|
||||
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
|
||||
else
|
||||
{
|
||||
// if not peer test capable routers found pick any
|
||||
router = i2p::data::netdb.GetRandomRouter ();
|
||||
if (router && router->IsSSU ())
|
||||
m_SSUServer->CreateSession (router); // no peer test
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Transports: Can't detect external IP. SSU is not available");
|
||||
}
|
||||
|
||||
DHKeysPair * Transports::GetNextDHKeysPair ()
|
||||
|
||||
void Transports::PeerTest ()
|
||||
{
|
||||
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 ()))
|
||||
{
|
||||
if (!statusChanged)
|
||||
{
|
||||
statusChanged = true;
|
||||
i2p::context.SetStatus (eRouterStatusTesting); // first time only
|
||||
}
|
||||
m_SSUServer->CreateSession (router, true); // peer test
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<i2p::crypto::DHKeys> Transports::GetNextDHKeysPair ()
|
||||
{
|
||||
return m_DHKeysPairSupplier.Acquire ();
|
||||
}
|
||||
|
||||
void Transports::ReuseDHKeysPair (DHKeysPair * pair)
|
||||
void Transports::ReuseDHKeysPair (std::shared_ptr<i2p::crypto::DHKeys> pair)
|
||||
{
|
||||
m_DHKeysPairSupplier.Return (pair);
|
||||
}
|
||||
@@ -405,44 +551,71 @@ namespace transport
|
||||
void Transports::PeerConnected (std::shared_ptr<TransportSession> session)
|
||||
{
|
||||
m_Service.post([session, this]()
|
||||
{
|
||||
auto ident = session->GetRemoteIdentity ().GetIdentHash ();
|
||||
{
|
||||
auto remoteIdentity = session->GetRemoteIdentity ();
|
||||
if (!remoteIdentity) return;
|
||||
auto ident = remoteIdentity->GetIdentHash ();
|
||||
auto it = m_Peers.find (ident);
|
||||
if (it != m_Peers.end ())
|
||||
{
|
||||
if (!it->second.session)
|
||||
bool sendDatabaseStore = true;
|
||||
if (it->second.delayedMessages.size () > 0)
|
||||
{
|
||||
it->second.session = session;
|
||||
session->SendI2NPMessages (it->second.delayedMessages);
|
||||
it->second.delayedMessages.clear ();
|
||||
}
|
||||
// check if first message is our DatabaseStore (publishing)
|
||||
auto firstMsg = it->second.delayedMessages[0];
|
||||
if (firstMsg && firstMsg->GetTypeID () == eI2NPDatabaseStore &&
|
||||
i2p::data::IdentHash(firstMsg->GetPayload () + DATABASE_STORE_KEY_OFFSET) == i2p::context.GetIdentHash ())
|
||||
sendDatabaseStore = false; // we have it in the list already
|
||||
}
|
||||
if (sendDatabaseStore)
|
||||
session->SendI2NPMessages ({ CreateDatabaseStoreMsg () });
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "Session for ", ident.ToBase64 ().substr (0, 4), " already exists");
|
||||
session->Done ();
|
||||
}
|
||||
session->SetTerminationTimeout (10); // most likely it's publishing, no follow-up messages expected, set timeout to 10 seconds
|
||||
it->second.sessions.push_back (session);
|
||||
session->SendI2NPMessages (it->second.delayedMessages);
|
||||
it->second.delayedMessages.clear ();
|
||||
}
|
||||
else // incoming connection
|
||||
m_Peers.insert (std::make_pair (ident, Peer{ 0, nullptr, session, i2p::util::GetSecondsSinceEpoch () }));
|
||||
{
|
||||
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]()
|
||||
{
|
||||
auto ident = session->GetRemoteIdentity ().GetIdentHash ();
|
||||
{
|
||||
auto remoteIdentity = session->GetRemoteIdentity ();
|
||||
if (!remoteIdentity) return;
|
||||
auto ident = remoteIdentity->GetIdentHash ();
|
||||
auto it = m_Peers.find (ident);
|
||||
if (it != m_Peers.end () && (!it->second.session || it->second.session == session))
|
||||
if (it != m_Peers.end ())
|
||||
{
|
||||
if (it->second.delayedMessages.size () > 0)
|
||||
ConnectToPeer (ident, it->second);
|
||||
else
|
||||
m_Peers.erase (it);
|
||||
it->second.sessions.remove (session);
|
||||
if (it->second.sessions.empty ()) // TODO: why?
|
||||
{
|
||||
if (it->second.delayedMessages.size () > 0)
|
||||
ConnectToPeer (ident, it->second);
|
||||
else
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
m_Peers.erase (it);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool Transports::IsConnected (const i2p::data::IdentHash& ident) const
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
auto it = m_Peers.find (ident);
|
||||
return it != m_Peers.end ();
|
||||
}
|
||||
|
||||
void Transports::HandlePeerCleanupTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
@@ -450,18 +623,61 @@ namespace transport
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto it = m_Peers.begin (); it != m_Peers.end (); )
|
||||
{
|
||||
if (!it->second.session && ts > it->second.creationTime + SESSION_CREATION_TIMEOUT)
|
||||
if (it->second.sessions.empty () && ts > it->second.creationTime + SESSION_CREATION_TIMEOUT)
|
||||
{
|
||||
LogPrint (eLogError, "Session to peer ", it->first.ToBase64 (), " has not been created in ", SESSION_CREATION_TIMEOUT, " seconds");
|
||||
LogPrint (eLogWarning, "Transports: Session to peer ", it->first.ToBase64 (), " has not been created in ", SESSION_CREATION_TIMEOUT, " seconds");
|
||||
auto profile = i2p::data::GetRouterProfile(it->first);
|
||||
if (profile)
|
||||
{
|
||||
profile->TunnelNonReplied();
|
||||
profile->Save();
|
||||
}
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
it = m_Peers.erase (it);
|
||||
}
|
||||
else
|
||||
it++;
|
||||
++it;
|
||||
}
|
||||
UpdateBandwidth (); // TODO: use separate timer(s) for it
|
||||
if (i2p::context.GetStatus () == eRouterStatusTesting) // if still testing, repeat peer test
|
||||
DetectExternalIP ();
|
||||
m_PeerCleanupTimer.expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
|
||||
m_PeerCleanupTimer.async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRandomPeer () const
|
||||
{
|
||||
if (m_Peers.empty ()) return nullptr;
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
auto it = m_Peers.begin ();
|
||||
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);
|
||||
}
|
||||
|
||||
bool Transports::RoutesRestricted() const {
|
||||
std::lock_guard<std::mutex> lock(m_FamilyMutex);
|
||||
return m_TrustedFamilies.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
81
Transports.h
81
Transports.h
@@ -10,7 +10,7 @@
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <cryptopp/osrng.h>
|
||||
#include <atomic>
|
||||
#include <boost/asio.hpp>
|
||||
#include "TransportSession.h"
|
||||
#include "NTCPSession.h"
|
||||
@@ -31,8 +31,8 @@ namespace transport
|
||||
~DHKeysPairSupplier ();
|
||||
void Start ();
|
||||
void Stop ();
|
||||
DHKeysPair * Acquire ();
|
||||
void Return (DHKeysPair * pair);
|
||||
std::shared_ptr<i2p::crypto::DHKeys> Acquire ();
|
||||
void Return (std::shared_ptr<i2p::crypto::DHKeys> pair);
|
||||
|
||||
private:
|
||||
|
||||
@@ -42,31 +42,31 @@ namespace transport
|
||||
private:
|
||||
|
||||
const int m_QueueSize;
|
||||
std::queue<DHKeysPair *> m_Queue;
|
||||
std::queue<std::shared_ptr<i2p::crypto::DHKeys> > m_Queue;
|
||||
|
||||
bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
std::condition_variable m_Acquired;
|
||||
std::mutex m_AcquiredMutex;
|
||||
CryptoPP::AutoSeededRandomPool m_Rnd;
|
||||
};
|
||||
|
||||
struct Peer
|
||||
{
|
||||
int numAttempts;
|
||||
std::shared_ptr<const i2p::data::RouterInfo> router;
|
||||
std::shared_ptr<TransportSession> session;
|
||||
std::list<std::shared_ptr<TransportSession> > sessions;
|
||||
uint64_t creationTime;
|
||||
std::vector<i2p::I2NPMessage *> delayedMessages;
|
||||
std::vector<std::shared_ptr<i2p::I2NPMessage> > delayedMessages;
|
||||
|
||||
~Peer ()
|
||||
void Done ()
|
||||
{
|
||||
for (auto it :delayedMessages)
|
||||
i2p::DeleteI2NPMessage (it);
|
||||
for (auto& it: sessions)
|
||||
it->Done ();
|
||||
}
|
||||
};
|
||||
|
||||
const size_t SESSION_CREATION_TIMEOUT = 10; // in seconds
|
||||
const int MAX_NUM_DELAYED_MESSAGES = 50;
|
||||
class Transports
|
||||
{
|
||||
public:
|
||||
@@ -74,27 +74,52 @@ namespace transport
|
||||
Transports ();
|
||||
~Transports ();
|
||||
|
||||
void Start ();
|
||||
void Start (bool enableNTCP=true, bool enableSSU=true);
|
||||
void Stop ();
|
||||
|
||||
boost::asio::io_service& GetService () { return m_Service; };
|
||||
i2p::transport::DHKeysPair * GetNextDHKeysPair ();
|
||||
void ReuseDHKeysPair (DHKeysPair * pair);
|
||||
|
||||
void SendMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg);
|
||||
void SendMessages (const i2p::data::IdentHash& ident, const std::vector<i2p::I2NPMessage *>& msgs);
|
||||
bool IsBoundNTCP() const { return m_NTCPServer != nullptr; }
|
||||
bool IsBoundSSU() const { return m_SSUServer != nullptr; }
|
||||
|
||||
bool IsOnline() const { return m_IsOnline; };
|
||||
void SetOnline (bool online) { m_IsOnline = online; };
|
||||
|
||||
boost::asio::io_service& GetService () { return m_Service; };
|
||||
std::shared_ptr<i2p::crypto::DHKeys> GetNextDHKeysPair ();
|
||||
void ReuseDHKeysPair (std::shared_ptr<i2p::crypto::DHKeys> pair);
|
||||
|
||||
void SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg);
|
||||
void SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs);
|
||||
void CloseSession (std::shared_ptr<const i2p::data::RouterInfo> router);
|
||||
|
||||
void PeerConnected (std::shared_ptr<TransportSession> session);
|
||||
void PeerDisconnected (std::shared_ptr<TransportSession> session);
|
||||
bool IsConnected (const i2p::data::IdentHash& ident) const;
|
||||
|
||||
void UpdateSentBytes (uint64_t numBytes) { m_TotalSentBytes += numBytes; };
|
||||
void UpdateReceivedBytes (uint64_t numBytes) { m_TotalReceivedBytes += numBytes; };
|
||||
uint64_t GetTotalSentBytes () const { return m_TotalSentBytes; };
|
||||
uint64_t GetTotalReceivedBytes () const { return m_TotalReceivedBytes; };
|
||||
uint32_t GetInBandwidth () const { return m_InBandwidth; };
|
||||
uint32_t GetOutBandwidth () const { return m_OutBandwidth; };
|
||||
bool IsBandwidthExceeded () const;
|
||||
size_t GetNumPeers () const { return m_Peers.size (); };
|
||||
std::shared_ptr<const i2p::data::RouterInfo> GetRandomPeer () const;
|
||||
|
||||
/** get a trusted first hop for restricted routes */
|
||||
std::shared_ptr<const i2p::data::RouterInfo> GetRestrictedPeer() const;
|
||||
/** 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 PeerTest ();
|
||||
|
||||
private:
|
||||
|
||||
void Run ();
|
||||
void RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident);
|
||||
void HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident);
|
||||
void PostMessage (i2p::data::IdentHash ident, i2p::I2NPMessage * msg);
|
||||
void PostMessages (i2p::data::IdentHash ident, std::vector<i2p::I2NPMessage *> msgs);
|
||||
void HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, i2p::data::IdentHash ident);
|
||||
void PostMessages (i2p::data::IdentHash ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > msgs);
|
||||
void PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router);
|
||||
bool ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer);
|
||||
void HandlePeerCleanupTimer (const boost::system::error_code& ecode);
|
||||
@@ -102,12 +127,16 @@ namespace transport
|
||||
void NTCPResolve (const std::string& addr, const i2p::data::IdentHash& ident);
|
||||
void HandleNTCPResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
|
||||
i2p::data::IdentHash ident, std::shared_ptr<boost::asio::ip::tcp::resolver> resolver);
|
||||
void SSUResolve (const std::string& addr, const i2p::data::IdentHash& ident);
|
||||
void HandleSSUResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
|
||||
i2p::data::IdentHash ident, std::shared_ptr<boost::asio::ip::tcp::resolver> resolver);
|
||||
|
||||
void UpdateBandwidth ();
|
||||
void DetectExternalIP ();
|
||||
|
||||
private:
|
||||
|
||||
bool m_IsRunning;
|
||||
bool m_IsOnline, m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_service::work m_Work;
|
||||
@@ -115,10 +144,20 @@ namespace transport
|
||||
|
||||
NTCPServer * m_NTCPServer;
|
||||
SSUServer * m_SSUServer;
|
||||
mutable std::mutex m_PeersMutex;
|
||||
std::map<i2p::data::IdentHash, Peer> m_Peers;
|
||||
|
||||
DHKeysPairSupplier m_DHKeysPairSupplier;
|
||||
|
||||
std::atomic<uint64_t> m_TotalSentBytes, m_TotalReceivedBytes;
|
||||
uint32_t m_InBandwidth, m_OutBandwidth; // bytes per second
|
||||
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;
|
||||
|
||||
public:
|
||||
|
||||
// for HTTP only
|
||||
|
||||
746
Tunnel.cpp
746
Tunnel.cpp
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user