mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-03-07 06:09:42 +00:00
Compare commits
780 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 | ||
|
|
f843d34234 | ||
|
|
23c7340afe | ||
|
|
bf3615fb32 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -5,6 +5,10 @@ router.keys
|
||||
i2p
|
||||
libi2pd.so
|
||||
netDb
|
||||
/i2pd
|
||||
/libi2pd.a
|
||||
/libi2pdclient.a
|
||||
|
||||
|
||||
# Autotools
|
||||
autom4te.cache
|
||||
@@ -233,3 +237,4 @@ pip-log.txt
|
||||
|
||||
# Sphinx
|
||||
docs/_build
|
||||
/androidIdea/
|
||||
|
||||
@@ -16,13 +16,17 @@ addons:
|
||||
- libboost-date-time-dev
|
||||
- libboost-filesystem-dev
|
||||
- libboost-program-options-dev
|
||||
- libboost-regex-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
|
||||
|
||||
663
AddressBook.cpp
663
AddressBook.cpp
@@ -5,12 +5,13 @@
|
||||
#include <fstream>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <openssl/rand.h>
|
||||
#include "Base.h"
|
||||
#include "util.h"
|
||||
#include "Identity.h"
|
||||
#include "FS.h"
|
||||
#include "Log.h"
|
||||
#include "HTTP.h"
|
||||
#include "NetDb.h"
|
||||
#include "ClientContext.h"
|
||||
#include "AddressBook.h"
|
||||
@@ -24,7 +25,7 @@ namespace client
|
||||
{
|
||||
private:
|
||||
i2p::fs::HashedStorage storage;
|
||||
std::string indexPath;
|
||||
std::string etagsPath, indexPath, localPath;
|
||||
|
||||
public:
|
||||
AddressBookFilesystemStorage (): storage("addressbook", "b", "", "b32") {};
|
||||
@@ -34,14 +35,34 @@ namespace client
|
||||
|
||||
bool Init ();
|
||||
int Load (std::map<std::string, i2p::data::IdentHash>& addresses);
|
||||
int LoadLocal (std::map<std::string, i2p::data::IdentHash>& addresses);
|
||||
int Save (const std::map<std::string, i2p::data::IdentHash>& addresses);
|
||||
|
||||
void SaveEtag (const i2p::data::IdentHash& subsciption, const std::string& etag, const std::string& lastModified);
|
||||
bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified);
|
||||
|
||||
private:
|
||||
|
||||
int LoadFromFile (const std::string& filename, std::map<std::string, i2p::data::IdentHash>& addresses); // returns -1 if can't open file, otherwise number of records
|
||||
|
||||
};
|
||||
|
||||
bool AddressBookFilesystemStorage::Init()
|
||||
{
|
||||
{
|
||||
storage.SetPlace(i2p::fs::GetDataDir());
|
||||
indexPath = storage.GetRoot() + i2p::fs::dirSep + "addresses.csv";
|
||||
return storage.Init(i2p::data::GetBase32SubstitutionTable(), 32);
|
||||
// init storage
|
||||
if (storage.Init(i2p::data::GetBase32SubstitutionTable(), 32))
|
||||
{
|
||||
// init ETags
|
||||
etagsPath = i2p::fs::StorageRootPath (storage, "etags");
|
||||
if (!i2p::fs::Exists (etagsPath))
|
||||
i2p::fs::CreateDirectory (etagsPath);
|
||||
// init address files
|
||||
indexPath = i2p::fs::StorageRootPath (storage, "addresses.csv");
|
||||
localPath = i2p::fs::StorageRootPath (storage, "local.csv");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<const i2p::data::IdentityEx> AddressBookFilesystemStorage::GetAddress (const i2p::data::IdentHash& ident) const
|
||||
@@ -56,7 +77,7 @@ namespace client
|
||||
f.seekg (0,std::ios::end);
|
||||
size_t len = f.tellg ();
|
||||
if (len < i2p::data::DEFAULT_IDENTITY_SIZE) {
|
||||
LogPrint (eLogError, "Addresbook: File ", filename, " is too short: ", len);
|
||||
LogPrint (eLogError, "Addressbook: File ", filename, " is too short: ", len);
|
||||
return nullptr;
|
||||
}
|
||||
f.seekg(0, std::ios::beg);
|
||||
@@ -72,7 +93,7 @@ namespace client
|
||||
std::string path = storage.Path( address->GetIdentHash().ToBase32() );
|
||||
std::ofstream f (path, std::ofstream::binary | std::ofstream::out);
|
||||
if (!f.is_open ()) {
|
||||
LogPrint (eLogError, "Addresbook: can't open file ", path);
|
||||
LogPrint (eLogError, "Addressbook: can't open file ", path);
|
||||
return;
|
||||
}
|
||||
size_t len = address->GetFullLen ();
|
||||
@@ -87,24 +108,18 @@ namespace client
|
||||
storage.Remove( ident.ToBase32() );
|
||||
}
|
||||
|
||||
int AddressBookFilesystemStorage::Load (std::map<std::string, i2p::data::IdentHash>& addresses)
|
||||
int AddressBookFilesystemStorage::LoadFromFile (const std::string& filename, std::map<std::string, i2p::data::IdentHash>& addresses)
|
||||
{
|
||||
int num = 0;
|
||||
std::string s;
|
||||
std::ifstream f (indexPath, std::ifstream::in); // in text mode
|
||||
|
||||
if (f.is_open ()) {
|
||||
LogPrint(eLogInfo, "Addressbook: using index file ", indexPath);
|
||||
} else {
|
||||
LogPrint(eLogWarning, "Addressbook: Can't open ", indexPath);
|
||||
return 0;
|
||||
}
|
||||
std::ifstream f (filename, std::ifstream::in); // in text mode
|
||||
if (!f) return -1;
|
||||
|
||||
addresses.clear ();
|
||||
while (!f.eof ()) {
|
||||
while (!f.eof ())
|
||||
{
|
||||
std::string s;
|
||||
getline(f, s);
|
||||
if (!s.length())
|
||||
continue; // skip empty line
|
||||
if (!s.length()) continue; // skip empty line
|
||||
|
||||
std::size_t pos = s.find(',');
|
||||
if (pos != std::string::npos)
|
||||
@@ -118,14 +133,34 @@ namespace client
|
||||
num++;
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
int AddressBookFilesystemStorage::Load (std::map<std::string, i2p::data::IdentHash>& addresses)
|
||||
{
|
||||
int num = LoadFromFile (indexPath, addresses);
|
||||
if (num < 0)
|
||||
{
|
||||
LogPrint(eLogWarning, "Addressbook: Can't open ", indexPath);
|
||||
return 0;
|
||||
}
|
||||
LogPrint(eLogInfo, "Addressbook: using index file ", indexPath);
|
||||
LogPrint (eLogInfo, "Addressbook: ", num, " addresses loaded from storage");
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
int AddressBookFilesystemStorage::LoadLocal (std::map<std::string, i2p::data::IdentHash>& addresses)
|
||||
{
|
||||
int num = LoadFromFile (localPath, addresses);
|
||||
if (num < 0) return 0;
|
||||
LogPrint (eLogInfo, "Addressbook: ", num, " local addresses loaded");
|
||||
return num;
|
||||
}
|
||||
|
||||
int AddressBookFilesystemStorage::Save (const std::map<std::string, i2p::data::IdentHash>& addresses)
|
||||
{
|
||||
if (addresses.size() == 0) {
|
||||
if (addresses.empty()) {
|
||||
LogPrint(eLogWarning, "Addressbook: not saving empty addressbook");
|
||||
return 0;
|
||||
}
|
||||
@@ -138,7 +173,7 @@ namespace client
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (auto it: addresses) {
|
||||
for (const auto& it: addresses) {
|
||||
f << it.first << "," << it.second.ToBase32 () << std::endl;
|
||||
num++;
|
||||
}
|
||||
@@ -146,8 +181,30 @@ namespace client
|
||||
return num;
|
||||
}
|
||||
|
||||
void AddressBookFilesystemStorage::SaveEtag (const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified)
|
||||
{
|
||||
std::string fname = etagsPath + i2p::fs::dirSep + subscription.ToBase32 () + ".txt";
|
||||
std::ofstream f (fname, std::ofstream::out | std::ofstream::trunc);
|
||||
if (f)
|
||||
{
|
||||
f << etag << std::endl;
|
||||
f<< lastModified << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool AddressBookFilesystemStorage::GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified)
|
||||
{
|
||||
std::string fname = etagsPath + i2p::fs::dirSep + subscription.ToBase32 () + ".txt";
|
||||
std::ifstream f (fname, std::ofstream::in);
|
||||
if (!f || f.eof ()) return false;
|
||||
std::getline (f, etag);
|
||||
if (f.eof ()) return false;
|
||||
std::getline (f, lastModified);
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
AddressBook::AddressBook (): m_Storage(new AddressBookFilesystemStorage), m_IsLoaded (false), m_IsDownloading (false),
|
||||
AddressBook::AddressBook (): m_Storage(nullptr), m_IsLoaded (false), m_IsDownloading (false),
|
||||
m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr)
|
||||
{
|
||||
}
|
||||
@@ -159,13 +216,22 @@ namespace client
|
||||
|
||||
void AddressBook::Start ()
|
||||
{
|
||||
if (!m_Storage)
|
||||
m_Storage = new AddressBookFilesystemStorage;
|
||||
m_Storage->Init();
|
||||
LoadHosts (); /* try storage, then hosts.txt, then download */
|
||||
StartSubscriptions ();
|
||||
StartLookups ();
|
||||
}
|
||||
|
||||
void AddressBook::StartResolvers ()
|
||||
{
|
||||
LoadLocal ();
|
||||
}
|
||||
|
||||
void AddressBook::Stop ()
|
||||
{
|
||||
StopLookups ();
|
||||
StopSubscriptions ();
|
||||
if (m_SubscriptionsUpdateTimer)
|
||||
{
|
||||
@@ -174,19 +240,19 @@ namespace client
|
||||
}
|
||||
if (m_IsDownloading)
|
||||
{
|
||||
LogPrint (eLogInfo, "Addresbook: subscriptions is downloading, abort");
|
||||
LogPrint (eLogInfo, "Addressbook: subscriptions is downloading, abort");
|
||||
for (int i = 0; i < 30; i++)
|
||||
{
|
||||
if (!m_IsDownloading)
|
||||
{
|
||||
LogPrint (eLogInfo, "Addresbook: subscriptions download complete");
|
||||
LogPrint (eLogInfo, "Addressbook: subscriptions download complete");
|
||||
break;
|
||||
}
|
||||
std::this_thread::sleep_for (std::chrono::seconds (1)); // wait for 1 seconds
|
||||
}
|
||||
LogPrint (eLogError, "Addresbook: subscription download timeout");
|
||||
LogPrint (eLogError, "Addressbook: subscription download timeout");
|
||||
m_IsDownloading = false;
|
||||
}
|
||||
}
|
||||
if (m_Storage)
|
||||
{
|
||||
m_Storage->Save (m_Addresses);
|
||||
@@ -194,8 +260,6 @@ namespace client
|
||||
m_Storage = nullptr;
|
||||
}
|
||||
m_DefaultSubscription = nullptr;
|
||||
for (auto it: m_Subscriptions)
|
||||
delete it;
|
||||
m_Subscriptions.clear ();
|
||||
}
|
||||
|
||||
@@ -219,7 +283,10 @@ namespace client
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LookupAddress (address); // TODO:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if not .b32 we assume full base64 address
|
||||
@@ -271,15 +338,16 @@ namespace client
|
||||
std::ifstream f (i2p::fs::DataDirPath("hosts.txt"), std::ifstream::in); // in text mode
|
||||
if (f.is_open ())
|
||||
{
|
||||
LoadHostsFromStream (f);
|
||||
LoadHostsFromStream (f, false);
|
||||
m_IsLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AddressBook::LoadHostsFromStream (std::istream& f)
|
||||
bool AddressBook::LoadHostsFromStream (std::istream& f, bool is_update)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_AddressBookMutex);
|
||||
int numAddresses = 0;
|
||||
bool incomplete = false;
|
||||
std::string s;
|
||||
while (!f.eof ())
|
||||
{
|
||||
@@ -296,22 +364,29 @@ namespace client
|
||||
std::string addr = s.substr(pos);
|
||||
|
||||
auto ident = std::make_shared<i2p::data::IdentityEx> ();
|
||||
if (ident->FromBase64(addr))
|
||||
{
|
||||
m_Addresses[name] = ident->GetIdentHash ();
|
||||
m_Storage->AddAddress (ident);
|
||||
numAddresses++;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Addresbook: malformed address ", addr, " for ", name);
|
||||
}
|
||||
if (!ident->FromBase64(addr)) {
|
||||
LogPrint (eLogError, "Addressbook: malformed address ", addr, " for ", name);
|
||||
incomplete = f.eof ();
|
||||
continue;
|
||||
}
|
||||
numAddresses++;
|
||||
if (m_Addresses.count(name) > 0)
|
||||
continue; /* already exists */
|
||||
m_Addresses[name] = ident->GetIdentHash ();
|
||||
m_Storage->AddAddress (ident);
|
||||
if (is_update)
|
||||
LogPrint(eLogInfo, "Addressbook: added new host: ", name);
|
||||
}
|
||||
else
|
||||
incomplete = f.eof ();
|
||||
}
|
||||
LogPrint (eLogInfo, "Addresbook: ", numAddresses, " addresses processed");
|
||||
LogPrint (eLogInfo, "Addressbook: ", numAddresses, " addresses processed");
|
||||
if (numAddresses > 0)
|
||||
{
|
||||
m_IsLoaded = true;
|
||||
if (!incomplete) m_IsLoaded = true;
|
||||
m_Storage->Save (m_Addresses);
|
||||
}
|
||||
return !incomplete;
|
||||
}
|
||||
|
||||
void AddressBook::LoadSubscriptions ()
|
||||
@@ -326,29 +401,75 @@ namespace client
|
||||
{
|
||||
getline(f, s);
|
||||
if (!s.length()) continue; // skip empty line
|
||||
m_Subscriptions.push_back (new AddressBookSubscription (*this, s));
|
||||
m_Subscriptions.push_back (std::make_shared<AddressBookSubscription> (*this, s));
|
||||
}
|
||||
LogPrint (eLogInfo, "Addressbook: ", m_Subscriptions.size (), " subscriptions urls loaded");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Addresbook: subscriptions.txt not found in datadir");
|
||||
LogPrint (eLogWarning, "Addressbook: subscriptions.txt not found in datadir");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Addressbook: subscriptions already loaded");
|
||||
}
|
||||
|
||||
void AddressBook::DownloadComplete (bool success)
|
||||
void AddressBook::LoadLocal ()
|
||||
{
|
||||
std::map<std::string, i2p::data::IdentHash> localAddresses;
|
||||
m_Storage->LoadLocal (localAddresses);
|
||||
for (const auto& it: localAddresses)
|
||||
{
|
||||
auto dot = it.first.find ('.');
|
||||
if (dot != std::string::npos)
|
||||
{
|
||||
auto domain = it.first.substr (dot + 1);
|
||||
auto it1 = m_Addresses.find (domain); // find domain in our addressbook
|
||||
if (it1 != m_Addresses.end ())
|
||||
{
|
||||
auto dest = context.FindLocalDestination (it1->second);
|
||||
if (dest)
|
||||
{
|
||||
// address is ours
|
||||
std::shared_ptr<AddressResolver> resolver;
|
||||
auto it2 = m_Resolvers.find (it1->second);
|
||||
if (it2 != m_Resolvers.end ())
|
||||
resolver = it2->second; // resolver exists
|
||||
else
|
||||
{
|
||||
// create new resolver
|
||||
resolver = std::make_shared<AddressResolver>(dest);
|
||||
m_Resolvers.insert (std::make_pair(it1->second, resolver));
|
||||
}
|
||||
resolver->AddAddress (it.first, it.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AddressBook::GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified)
|
||||
{
|
||||
if (m_Storage)
|
||||
return m_Storage->GetEtag (subscription, etag, lastModified);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void AddressBook::DownloadComplete (bool success, const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified)
|
||||
{
|
||||
m_IsDownloading = false;
|
||||
if (success && m_DefaultSubscription)
|
||||
int nextUpdateTimeout = CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT;
|
||||
if (success)
|
||||
{
|
||||
m_DefaultSubscription.reset (nullptr);
|
||||
m_IsLoaded = true;
|
||||
if (m_DefaultSubscription) m_DefaultSubscription = nullptr;
|
||||
if (m_IsLoaded)
|
||||
nextUpdateTimeout = CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT;
|
||||
else
|
||||
m_IsLoaded = true;
|
||||
if (m_Storage) m_Storage->SaveEtag (subscription, etag, lastModified);
|
||||
}
|
||||
if (m_SubscriptionsUpdateTimer)
|
||||
{
|
||||
m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(
|
||||
success ? CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT : CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT));
|
||||
m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(nextUpdateTimeout));
|
||||
m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer,
|
||||
this, std::placeholders::_1));
|
||||
}
|
||||
@@ -368,7 +489,7 @@ namespace client
|
||||
this, std::placeholders::_1));
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Addresbook: can't start subscriptions: missing shared local destination");
|
||||
LogPrint (eLogError, "Addressbook: can't start subscriptions: missing shared local destination");
|
||||
}
|
||||
|
||||
void AddressBook::StopSubscriptions ()
|
||||
@@ -393,16 +514,18 @@ namespace client
|
||||
// download it from http://i2p-projekt.i2p/hosts.txt
|
||||
LogPrint (eLogInfo, "Addressbook: trying to download it from default subscription.");
|
||||
if (!m_DefaultSubscription)
|
||||
m_DefaultSubscription.reset (new AddressBookSubscription (*this, DEFAULT_SUBSCRIPTION_ADDRESS));
|
||||
m_DefaultSubscription = std::make_shared<AddressBookSubscription>(*this, DEFAULT_SUBSCRIPTION_ADDRESS);
|
||||
m_IsDownloading = true;
|
||||
m_DefaultSubscription->CheckSubscription ();
|
||||
std::thread load_hosts(std::bind (&AddressBookSubscription::CheckUpdates, m_DefaultSubscription));
|
||||
load_hosts.detach(); // TODO: use join
|
||||
}
|
||||
else if (!m_Subscriptions.empty ())
|
||||
{
|
||||
// pick random subscription
|
||||
auto ind = rand () % m_Subscriptions.size();
|
||||
m_IsDownloading = true;
|
||||
m_Subscriptions[ind]->CheckSubscription ();
|
||||
std::thread load_hosts(std::bind (&AddressBookSubscription::CheckUpdates, m_Subscriptions[ind]));
|
||||
load_hosts.detach(); // TODO: use join
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -415,168 +538,304 @@ namespace client
|
||||
}
|
||||
}
|
||||
|
||||
void AddressBook::StartLookups ()
|
||||
{
|
||||
auto dest = i2p::client::context.GetSharedLocalDestination ();
|
||||
if (dest)
|
||||
{
|
||||
auto datagram = dest->GetDatagramDestination ();
|
||||
if (!datagram)
|
||||
datagram = dest->CreateDatagramDestination ();
|
||||
datagram->SetReceiver (std::bind (&AddressBook::HandleLookupResponse, this,
|
||||
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5),
|
||||
ADDRESS_RESPONSE_DATAGRAM_PORT);
|
||||
}
|
||||
}
|
||||
|
||||
void AddressBook::StopLookups ()
|
||||
{
|
||||
auto dest = i2p::client::context.GetSharedLocalDestination ();
|
||||
if (dest)
|
||||
{
|
||||
auto datagram = dest->GetDatagramDestination ();
|
||||
if (datagram)
|
||||
datagram->ResetReceiver (ADDRESS_RESPONSE_DATAGRAM_PORT);
|
||||
}
|
||||
}
|
||||
|
||||
void AddressBook::LookupAddress (const std::string& address)
|
||||
{
|
||||
const i2p::data::IdentHash * ident = nullptr;
|
||||
auto dot = address.find ('.');
|
||||
if (dot != std::string::npos)
|
||||
ident = FindAddress (address.substr (dot + 1));
|
||||
if (!ident)
|
||||
{
|
||||
LogPrint (eLogError, "Addressbook: Can't find domain for ", address);
|
||||
return;
|
||||
}
|
||||
|
||||
auto dest = i2p::client::context.GetSharedLocalDestination ();
|
||||
if (dest)
|
||||
{
|
||||
auto datagram = dest->GetDatagramDestination ();
|
||||
if (datagram)
|
||||
{
|
||||
uint32_t nonce;
|
||||
RAND_bytes ((uint8_t *)&nonce, 4);
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_LookupsMutex);
|
||||
m_Lookups[nonce] = address;
|
||||
}
|
||||
LogPrint (eLogDebug, "Addressbook: Lookup of ", address, " to ", ident->ToBase32 (), " nonce=", nonce);
|
||||
size_t len = address.length () + 9;
|
||||
uint8_t * buf = new uint8_t[len];
|
||||
memset (buf, 0, 4);
|
||||
htobe32buf (buf + 4, nonce);
|
||||
buf[8] = address.length ();
|
||||
memcpy (buf + 9, address.c_str (), address.length ());
|
||||
datagram->SendDatagramTo (buf, len, *ident, ADDRESS_RESPONSE_DATAGRAM_PORT, ADDRESS_RESOLVER_DATAGRAM_PORT);
|
||||
delete[] buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddressBook::HandleLookupResponse (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
|
||||
{
|
||||
if (len < 44)
|
||||
{
|
||||
LogPrint (eLogError, "Addressbook: Lookup response is too short ", len);
|
||||
return;
|
||||
}
|
||||
uint32_t nonce = bufbe32toh (buf + 4);
|
||||
LogPrint (eLogDebug, "Addressbook: Lookup response received from ", from.GetIdentHash ().ToBase32 (), " nonce=", nonce);
|
||||
std::string address;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_LookupsMutex);
|
||||
auto it = m_Lookups.find (nonce);
|
||||
if (it != m_Lookups.end ())
|
||||
{
|
||||
address = it->second;
|
||||
m_Lookups.erase (it);
|
||||
}
|
||||
}
|
||||
if (address.length () > 0)
|
||||
{
|
||||
// TODO: verify from
|
||||
m_Addresses[address] = buf + 8;
|
||||
}
|
||||
}
|
||||
|
||||
AddressBookSubscription::AddressBookSubscription (AddressBook& book, const std::string& link):
|
||||
m_Book (book), m_Link (link)
|
||||
{
|
||||
}
|
||||
|
||||
void AddressBookSubscription::CheckSubscription ()
|
||||
void AddressBookSubscription::CheckUpdates ()
|
||||
{
|
||||
std::thread load_hosts(&AddressBookSubscription::Request, this);
|
||||
load_hosts.detach(); // TODO: use join
|
||||
bool result = MakeRequest ();
|
||||
m_Book.DownloadComplete (result, m_Ident, m_Etag, m_LastModified);
|
||||
}
|
||||
|
||||
void AddressBookSubscription::Request ()
|
||||
bool AddressBookSubscription::MakeRequest ()
|
||||
{
|
||||
i2p::http::URL url;
|
||||
// must be run in separate thread
|
||||
LogPrint (eLogInfo, "Addresbook: Downloading hosts database from ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified);
|
||||
bool success = false;
|
||||
i2p::util::http::url u (m_Link);
|
||||
i2p::data::IdentHash ident;
|
||||
if (m_Book.GetIdentHash (u.host_, ident))
|
||||
{
|
||||
std::condition_variable newDataReceived;
|
||||
std::mutex newDataReceivedMutex;
|
||||
auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (ident);
|
||||
if (!leaseSet)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(newDataReceivedMutex);
|
||||
i2p::client::context.GetSharedLocalDestination ()->RequestDestination (ident,
|
||||
[&newDataReceived, &leaseSet](std::shared_ptr<i2p::data::LeaseSet> ls)
|
||||
{
|
||||
leaseSet = ls;
|
||||
newDataReceived.notify_all ();
|
||||
});
|
||||
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout)
|
||||
{
|
||||
LogPrint (eLogError, "Addressbook: Subscription LeaseSet request timeout expired");
|
||||
i2p::client::context.GetSharedLocalDestination ()->CancelDestinationRequest (ident);
|
||||
}
|
||||
}
|
||||
if (leaseSet)
|
||||
{
|
||||
std::stringstream request, response;
|
||||
// standard header
|
||||
request << "GET " << u.path_ << " HTTP/1.1\r\n"
|
||||
<< "Host: " << u.host_ << "\r\n"
|
||||
<< "Accept: */*\r\n"
|
||||
<< "User-Agent: Wget/1.11.4\r\n"
|
||||
//<< "Accept-Encoding: gzip\r\n"
|
||||
<< "X-Accept-Encoding: x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0\r\n"
|
||||
<< "Connection: close\r\n";
|
||||
if (m_Etag.length () > 0) // etag
|
||||
request << i2p::util::http::IF_NONE_MATCH << ": \"" << m_Etag << "\"\r\n";
|
||||
if (m_LastModified.length () > 0) // if-modfief-since
|
||||
request << i2p::util::http::IF_MODIFIED_SINCE << ": " << m_LastModified << "\r\n";
|
||||
request << "\r\n"; // end of header
|
||||
auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (leaseSet, u.port_);
|
||||
stream->Send ((uint8_t *)request.str ().c_str (), request.str ().length ());
|
||||
|
||||
uint8_t buf[4096];
|
||||
bool end = false;
|
||||
while (!end)
|
||||
{
|
||||
stream->AsyncReceive (boost::asio::buffer (buf, 4096),
|
||||
[&](const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (bytes_transferred)
|
||||
response.write ((char *)buf, bytes_transferred);
|
||||
if (ecode == boost::asio::error::timed_out || !stream->IsOpen ())
|
||||
end = true;
|
||||
newDataReceived.notify_all ();
|
||||
},
|
||||
30); // wait for 30 seconds
|
||||
std::unique_lock<std::mutex> l(newDataReceivedMutex);
|
||||
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout)
|
||||
LogPrint (eLogError, "Addresbook: subscriptions request timeout expired");
|
||||
}
|
||||
// process remaining buffer
|
||||
while (size_t len = stream->ReadSome (buf, 4096))
|
||||
response.write ((char *)buf, len);
|
||||
|
||||
// parse response
|
||||
std::string version;
|
||||
response >> version; // HTTP version
|
||||
int status = 0;
|
||||
response >> status; // status
|
||||
if (status == 200) // OK
|
||||
{
|
||||
bool isChunked = false, isGzip = false;
|
||||
std::string header, statusMessage;
|
||||
std::getline (response, statusMessage);
|
||||
// read until new line meaning end of header
|
||||
while (!response.eof () && header != "\r")
|
||||
{
|
||||
std::getline (response, header);
|
||||
auto colon = header.find (':');
|
||||
if (colon != std::string::npos)
|
||||
{
|
||||
std::string field = header.substr (0, colon);
|
||||
boost::to_lower (field); // field are not case-sensitive
|
||||
colon++;
|
||||
header.resize (header.length () - 1); // delete \r
|
||||
if (field == i2p::util::http::ETAG)
|
||||
m_Etag = header.substr (colon + 1);
|
||||
else if (field == i2p::util::http::LAST_MODIFIED)
|
||||
m_LastModified = header.substr (colon + 1);
|
||||
else if (field == i2p::util::http::TRANSFER_ENCODING)
|
||||
isChunked = !header.compare (colon + 1, std::string::npos, "chunked");
|
||||
else if (field == i2p::util::http::CONTENT_ENCODING)
|
||||
isGzip = !header.compare (colon + 1, std::string::npos, "gzip") ||
|
||||
!header.compare (colon + 1, std::string::npos, "x-i2p-gzip");
|
||||
}
|
||||
}
|
||||
LogPrint (eLogInfo, "Addressbook: ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified);
|
||||
if (!response.eof ())
|
||||
{
|
||||
success = true;
|
||||
if (!isChunked)
|
||||
success = ProcessResponse (response, isGzip);
|
||||
else
|
||||
{
|
||||
// merge chunks
|
||||
std::stringstream merged;
|
||||
i2p::util::http::MergeChunkedResponse (response, merged);
|
||||
success = ProcessResponse (merged, isGzip);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (status == 304)
|
||||
{
|
||||
success = true;
|
||||
LogPrint (eLogInfo, "Addressbook: no updates from ", m_Link);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Adressbook: HTTP response ", status);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Addressbook: address ", u.host_, " not found");
|
||||
LogPrint (eLogInfo, "Addressbook: Downloading hosts database from ", m_Link);
|
||||
if (!url.parse(m_Link)) {
|
||||
LogPrint(eLogError, "Addressbook: failed to parse url: ", m_Link);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Addressbook: Can't resolve ", u.host_);
|
||||
|
||||
if (!success)
|
||||
LogPrint (eLogError, "Addressbook: download hosts.txt from ", m_Link, " failed");
|
||||
|
||||
m_Book.DownloadComplete (success);
|
||||
}
|
||||
|
||||
bool AddressBookSubscription::ProcessResponse (std::stringstream& s, bool isGzip)
|
||||
{
|
||||
if (isGzip)
|
||||
if (!m_Book.GetIdentHash (url.host, m_Ident)) {
|
||||
LogPrint (eLogError, "Addressbook: Can't resolve ", url.host);
|
||||
return false;
|
||||
}
|
||||
/* this code block still needs some love */
|
||||
std::condition_variable newDataReceived;
|
||||
std::mutex newDataReceivedMutex;
|
||||
auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (m_Ident);
|
||||
if (!leaseSet)
|
||||
{
|
||||
std::stringstream uncompressed;
|
||||
i2p::data::GzipInflator inflator;
|
||||
inflator.Inflate (s, uncompressed);
|
||||
if (!uncompressed.fail ())
|
||||
m_Book.LoadHostsFromStream (uncompressed);
|
||||
else
|
||||
std::unique_lock<std::mutex> l(newDataReceivedMutex);
|
||||
i2p::client::context.GetSharedLocalDestination ()->RequestDestination (m_Ident,
|
||||
[&newDataReceived, &leaseSet, &newDataReceivedMutex](std::shared_ptr<i2p::data::LeaseSet> ls)
|
||||
{
|
||||
leaseSet = ls;
|
||||
std::unique_lock<std::mutex> l1(newDataReceivedMutex);
|
||||
newDataReceived.notify_all ();
|
||||
});
|
||||
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout)
|
||||
{
|
||||
LogPrint (eLogError, "Addressbook: Subscription LeaseSet request timeout expired");
|
||||
i2p::client::context.GetSharedLocalDestination ()->CancelDestinationRequest (m_Ident, false); // don't notify, because we know it already
|
||||
return false;
|
||||
}
|
||||
else
|
||||
m_Book.LoadHostsFromStream (s);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!leaseSet) {
|
||||
/* still no leaseset found */
|
||||
LogPrint (eLogError, "Addressbook: LeaseSet for address ", url.host, " not found");
|
||||
return false;
|
||||
}
|
||||
if (m_Etag.empty() && m_LastModified.empty()) {
|
||||
m_Book.GetEtag (m_Ident, m_Etag, m_LastModified);
|
||||
LogPrint (eLogDebug, "Addressbook: loaded for ", url.host, ": ETag: ", m_Etag, ", Last-Modified: ", m_LastModified);
|
||||
}
|
||||
/* save url parts for later use */
|
||||
std::string dest_host = url.host;
|
||||
int dest_port = url.port ? url.port : 80;
|
||||
/* create http request & send it */
|
||||
i2p::http::HTTPReq req;
|
||||
req.add_header("Host", dest_host);
|
||||
req.add_header("User-Agent", "Wget/1.11.4");
|
||||
req.add_header("Connection", "close");
|
||||
if (!m_Etag.empty())
|
||||
req.add_header("If-None-Match", m_Etag);
|
||||
if (!m_LastModified.empty())
|
||||
req.add_header("If-Modified-Since", m_LastModified);
|
||||
/* convert url to relative */
|
||||
url.schema = "";
|
||||
url.host = "";
|
||||
req.uri = url.to_string();
|
||||
auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (leaseSet, dest_port);
|
||||
std::string request = req.to_string();
|
||||
stream->Send ((const uint8_t *) request.data(), request.length());
|
||||
/* read response */
|
||||
std::string response;
|
||||
uint8_t recv_buf[4096];
|
||||
bool end = false;
|
||||
while (!end) {
|
||||
stream->AsyncReceive (boost::asio::buffer (recv_buf, 4096),
|
||||
[&](const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (bytes_transferred)
|
||||
response.append ((char *)recv_buf, bytes_transferred);
|
||||
if (ecode == boost::asio::error::timed_out || !stream->IsOpen ())
|
||||
end = true;
|
||||
newDataReceived.notify_all ();
|
||||
},
|
||||
30); // wait for 30 seconds
|
||||
std::unique_lock<std::mutex> l(newDataReceivedMutex);
|
||||
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout)
|
||||
LogPrint (eLogError, "Addressbook: subscriptions request timeout expired");
|
||||
}
|
||||
// process remaining buffer
|
||||
while (size_t len = stream->ReadSome (recv_buf, sizeof(recv_buf))) {
|
||||
response.append ((char *)recv_buf, len);
|
||||
}
|
||||
/* parse response */
|
||||
i2p::http::HTTPRes res;
|
||||
int res_head_len = res.parse(response);
|
||||
if (res_head_len < 0) {
|
||||
LogPrint(eLogError, "Addressbook: can't parse http response from ", dest_host);
|
||||
return false;
|
||||
}
|
||||
if (res_head_len == 0) {
|
||||
LogPrint(eLogError, "Addressbook: incomplete http response from ", dest_host, ", interrupted by timeout");
|
||||
return false;
|
||||
}
|
||||
/* assert: res_head_len > 0 */
|
||||
response.erase(0, res_head_len);
|
||||
if (res.code == 304) {
|
||||
LogPrint (eLogInfo, "Addressbook: no updates from ", dest_host, ", code 304");
|
||||
return false;
|
||||
}
|
||||
if (res.code != 200) {
|
||||
LogPrint (eLogWarning, "Adressbook: can't get updates from ", dest_host, ", response code ", res.code);
|
||||
return false;
|
||||
}
|
||||
int len = res.content_length();
|
||||
if (response.empty()) {
|
||||
LogPrint(eLogError, "Addressbook: empty response from ", dest_host, ", expected ", len, " bytes");
|
||||
return false;
|
||||
}
|
||||
if (len > 0 && len != (int) response.length()) {
|
||||
LogPrint(eLogError, "Addressbook: response size mismatch, expected: ", response.length(), ", got: ", len, "bytes");
|
||||
return false;
|
||||
}
|
||||
/* assert: res.code == 200 */
|
||||
auto it = res.headers.find("ETag");
|
||||
if (it != res.headers.end()) {
|
||||
m_Etag = it->second;
|
||||
}
|
||||
it = res.headers.find("If-Modified-Since");
|
||||
if (it != res.headers.end()) {
|
||||
m_LastModified = it->second;
|
||||
}
|
||||
if (res.is_chunked()) {
|
||||
std::stringstream in(response), out;
|
||||
i2p::http::MergeChunkedResponse (in, out);
|
||||
response = out.str();
|
||||
} else if (res.is_gzipped()) {
|
||||
std::stringstream out;
|
||||
i2p::data::GzipInflator inflator;
|
||||
inflator.Inflate ((const uint8_t *) response.data(), response.length(), out);
|
||||
if (out.fail()) {
|
||||
LogPrint(eLogError, "Addressbook: can't gunzip http response");
|
||||
return false;
|
||||
}
|
||||
response = out.str();
|
||||
}
|
||||
std::stringstream ss(response);
|
||||
LogPrint (eLogInfo, "Addressbook: got update from ", dest_host);
|
||||
m_Book.LoadHostsFromStream (ss, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
AddressResolver::AddressResolver (std::shared_ptr<ClientDestination> destination):
|
||||
m_LocalDestination (destination)
|
||||
{
|
||||
if (m_LocalDestination)
|
||||
{
|
||||
auto datagram = m_LocalDestination->GetDatagramDestination ();
|
||||
if (!datagram)
|
||||
datagram = m_LocalDestination->CreateDatagramDestination ();
|
||||
datagram->SetReceiver (std::bind (&AddressResolver::HandleRequest, this,
|
||||
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5),
|
||||
ADDRESS_RESOLVER_DATAGRAM_PORT);
|
||||
}
|
||||
}
|
||||
|
||||
AddressResolver::~AddressResolver ()
|
||||
{
|
||||
if (m_LocalDestination)
|
||||
{
|
||||
auto datagram = m_LocalDestination->GetDatagramDestination ();
|
||||
if (datagram)
|
||||
datagram->ResetReceiver (ADDRESS_RESOLVER_DATAGRAM_PORT);
|
||||
}
|
||||
}
|
||||
|
||||
void AddressResolver::HandleRequest (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
|
||||
{
|
||||
if (len < 9 || len < buf[8] + 9U)
|
||||
{
|
||||
LogPrint (eLogError, "Addressbook: Address request is too short ", len);
|
||||
return;
|
||||
}
|
||||
// read requested address
|
||||
uint8_t l = buf[8];
|
||||
char address[255];
|
||||
memcpy (address, buf + 9, l);
|
||||
address[l] = 0;
|
||||
LogPrint (eLogDebug, "Addressbook: Address request ", address);
|
||||
// send response
|
||||
uint8_t response[44];
|
||||
memset (response, 0, 4); // reserved
|
||||
memcpy (response + 4, buf + 4, 4); // nonce
|
||||
auto it = m_LocalAddresses.find (address); // address lookup
|
||||
if (it != m_LocalAddresses.end ())
|
||||
memcpy (response + 8, it->second, 32); // ident
|
||||
else
|
||||
memset (response + 8, 0, 32); // not found
|
||||
memset (response + 40, 0, 4); // set expiration time to zero
|
||||
m_LocalDestination->GetDatagramDestination ()->SendDatagramTo (response, 44, from.GetIdentHash (), toPort, fromPort);
|
||||
}
|
||||
|
||||
void AddressResolver::AddAddress (const std::string& name, const i2p::data::IdentHash& ident)
|
||||
{
|
||||
m_LocalAddresses[name] = ident;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,17 +12,25 @@
|
||||
#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"); }
|
||||
|
||||
@@ -37,10 +45,15 @@ namespace client
|
||||
|
||||
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:
|
||||
@@ -48,18 +61,23 @@ namespace client
|
||||
AddressBook ();
|
||||
~AddressBook ();
|
||||
void Start ();
|
||||
void StartResolvers ();
|
||||
void Stop ();
|
||||
bool GetIdentHash (const std::string& address, i2p::data::IdentHash& ident);
|
||||
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 (std::shared_ptr<const i2p::data::IdentityEx> address);
|
||||
|
||||
void LoadHostsFromStream (std::istream& f);
|
||||
void DownloadComplete (bool success);
|
||||
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 ();
|
||||
@@ -67,17 +85,25 @@ namespace client
|
||||
|
||||
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;
|
||||
std::unique_ptr<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;
|
||||
};
|
||||
|
||||
@@ -86,17 +112,36 @@ namespace client
|
||||
public:
|
||||
|
||||
AddressBookSubscription (AddressBook& book, const std::string& link);
|
||||
void CheckSubscription ();
|
||||
void CheckUpdates ();
|
||||
|
||||
private:
|
||||
|
||||
void Request ();
|
||||
bool ProcessResponse (std::stringstream& s, bool isGzip = false);
|
||||
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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
108
BOB.cpp
108
BOB.cpp
@@ -70,7 +70,7 @@ 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;
|
||||
@@ -202,9 +202,9 @@ namespace client
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -354,6 +354,11 @@ 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));
|
||||
@@ -364,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)
|
||||
@@ -384,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 ());
|
||||
}
|
||||
|
||||
@@ -396,12 +409,15 @@ 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)
|
||||
@@ -441,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)
|
||||
@@ -455,14 +474,27 @@ 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)
|
||||
@@ -497,14 +529,15 @@ namespace client
|
||||
{
|
||||
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");
|
||||
}
|
||||
@@ -515,14 +548,46 @@ 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 (const std::string& address, int port):
|
||||
m_IsRunning (false), m_Thread (nullptr),
|
||||
@@ -548,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;
|
||||
}
|
||||
|
||||
@@ -567,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 ();
|
||||
|
||||
4
BOB.h
4
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";
|
||||
@@ -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;
|
||||
|
||||
108
Base.cpp
108
Base.cpp
@@ -1,5 +1,6 @@
|
||||
#include <stdlib.h>
|
||||
#include "Log.h"
|
||||
#include <string.h>
|
||||
|
||||
#include "Base.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -8,8 +9,8 @@ namespace data
|
||||
{
|
||||
static const char T32[32] = {
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
|
||||
'i', 'k', 'k', 'l', 'm', 'n', 'o', 'p',
|
||||
'q', 'r', 't', 't', 'u', 'v', 'w', 'x',
|
||||
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
|
||||
'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
|
||||
'y', 'z', '2', '3', '4', '5', '6', '7',
|
||||
};
|
||||
|
||||
@@ -284,107 +285,6 @@ namespace data
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "Decompression error ", err);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool GzipInflator::Inflate (const uint8_t * in, size_t inLen, std::ostream& s)
|
||||
{
|
||||
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)
|
||||
{
|
||||
LogPrint (eLogError, "Decompression error ", ret);
|
||||
inflateEnd (&m_Inflator);
|
||||
s.setstate(std::ios_base::failbit);
|
||||
break;
|
||||
}
|
||||
else
|
||||
s.write ((char *)out, GZIP_CHUNK_SIZE - m_Inflator.avail_out);
|
||||
}
|
||||
while (!m_Inflator.avail_out); // more data to read
|
||||
delete[] out;
|
||||
return ret == Z_STREAM_END || ret < 0;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
LogPrint (eLogError, "Compression error ", err);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
117
Base.h
117
Base.h
@@ -2,15 +2,11 @@
|
||||
#define BASE_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <zlib.h>
|
||||
#include <iostream>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
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 ();
|
||||
@@ -23,112 +19,7 @@ namespace data
|
||||
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);
|
||||
|
||||
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];
|
||||
};
|
||||
};
|
||||
|
||||
const size_t GZIP_CHUNK_SIZE = 16384;
|
||||
class GzipInflator
|
||||
{
|
||||
public:
|
||||
|
||||
GzipInflator ();
|
||||
~GzipInflator ();
|
||||
|
||||
size_t Inflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen);
|
||||
bool Inflate (const uint8_t * in, size_t inLen, std::ostream& s);
|
||||
// return true when finshed or error, s failbit will be set in case of error
|
||||
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
|
||||
|
||||
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
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "FS.h"
|
||||
#include "Log.h"
|
||||
#include "Identity.h"
|
||||
#include "util.h"
|
||||
#include "ClientContext.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -16,7 +17,7 @@ namespace client
|
||||
|
||||
ClientContext::ClientContext (): m_SharedLocalDestination (nullptr),
|
||||
m_HttpProxy (nullptr), m_SocksProxy (nullptr), m_SamBridge (nullptr),
|
||||
m_BOBCommandChannel (nullptr)
|
||||
m_BOBCommandChannel (nullptr), m_I2CPServer (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -26,6 +27,7 @@ namespace client
|
||||
delete m_SocksProxy;
|
||||
delete m_SamBridge;
|
||||
delete m_BOBCommandChannel;
|
||||
delete m_I2CPServer;
|
||||
}
|
||||
|
||||
void ClientContext::Start ()
|
||||
@@ -52,8 +54,12 @@ namespace client
|
||||
LoadPrivateKeys (keys, httpProxyKeys);
|
||||
localDestination = CreateNewLocalDestination (keys, false);
|
||||
}
|
||||
m_HttpProxy = new i2p::proxy::HTTPProxy(httpProxyAddr, httpProxyPort, localDestination);
|
||||
m_HttpProxy->Start();
|
||||
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);
|
||||
@@ -70,8 +76,12 @@ namespace client
|
||||
LoadPrivateKeys (keys, socksProxyKeys);
|
||||
localDestination = CreateNewLocalDestination (keys, false);
|
||||
}
|
||||
m_SocksProxy = new i2p::proxy::SOCKSProxy(socksProxyAddr, socksProxyPort, socksOutProxyAddr, socksOutProxyPort, localDestination);
|
||||
m_SocksProxy->Start();
|
||||
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
|
||||
@@ -83,8 +93,12 @@ namespace client
|
||||
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);
|
||||
m_SamBridge = new SAMBridge (samAddr, samPort);
|
||||
m_SamBridge->Start ();
|
||||
try {
|
||||
m_SamBridge = new SAMBridge (samAddr, samPort);
|
||||
m_SamBridge->Start ();
|
||||
} catch (std::exception& e) {
|
||||
LogPrint(eLogError, "Clients: Exception in SAM bridge: ", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
// BOB
|
||||
@@ -93,9 +107,33 @@ namespace client
|
||||
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);
|
||||
m_BOBCommandChannel = new BOBCommandChannel (bobAddr, bobPort);
|
||||
m_BOBCommandChannel->Start ();
|
||||
try {
|
||||
m_BOBCommandChannel = new BOBCommandChannel (bobAddr, bobPort);
|
||||
m_BOBCommandChannel->Start ();
|
||||
} catch (std::exception& e) {
|
||||
LogPrint(eLogError, "Clients: Exception in BOB bridge: ", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
// I2CP
|
||||
bool i2cp; i2p::config::GetOption("i2cp.enabled", i2cp);
|
||||
if (i2cp)
|
||||
{
|
||||
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 ()
|
||||
@@ -146,13 +184,26 @@ namespace client
|
||||
m_BOBCommandChannel = nullptr;
|
||||
}
|
||||
|
||||
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)
|
||||
for (auto& it: m_Destinations)
|
||||
it.second->Stop ();
|
||||
m_Destinations.clear ();
|
||||
m_SharedLocalDestination = nullptr;
|
||||
}
|
||||
|
||||
void ClientContext::ReloadConfig ()
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -242,7 +293,7 @@ namespace client
|
||||
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));
|
||||
return section.second.get (boost::property_tree::ptree::path_type (name, '/'), std::to_string (value));
|
||||
}
|
||||
|
||||
template<typename Section>
|
||||
@@ -259,8 +310,15 @@ namespace client
|
||||
{
|
||||
boost::property_tree::ptree pt;
|
||||
std::string tunConf; i2p::config::GetOption("tunconf", tunConf);
|
||||
if (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
|
||||
{
|
||||
@@ -303,11 +361,14 @@ namespace client
|
||||
localDestination = CreateNewLocalDestination (k, false, &options);
|
||||
}
|
||||
auto clientTunnel = new I2PClientTunnel (name, dest, address, port, localDestination, destinationPort);
|
||||
if (m_ClientTunnels.insert (std::make_pair (port, std::unique_ptr<I2PClientTunnel>(clientTunnel))).second)
|
||||
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 with port ", port, " already exists");
|
||||
numClientTunnels++;
|
||||
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)
|
||||
{
|
||||
@@ -357,12 +418,15 @@ namespace client
|
||||
serverTunnel->SetAccessList (idents);
|
||||
}
|
||||
if (m_ServerTunnels.insert (std::make_pair (
|
||||
std::make_tuple (localDestination->GetIdentHash (), inPort),
|
||||
std::make_pair (localDestination->GetIdentHash (), inPort),
|
||||
std::unique_ptr<I2PServerTunnel>(serverTunnel))).second)
|
||||
{
|
||||
serverTunnel->Start ();
|
||||
numServerTunnels++;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Clients: I2P server tunnel for destination ", m_AddressBook.ToAddress(localDestination->GetIdentHash ()), " already exists");
|
||||
numServerTunnels++;
|
||||
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);
|
||||
|
||||
@@ -2,15 +2,17 @@
|
||||
#define CLIENT_CONTEXT_H__
|
||||
|
||||
#include <map>
|
||||
#include <tuple>
|
||||
#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"
|
||||
|
||||
namespace i2p
|
||||
@@ -48,6 +50,8 @@ namespace client
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
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
|
||||
@@ -78,10 +82,11 @@ namespace client
|
||||
|
||||
i2p::proxy::HTTPProxy * m_HttpProxy;
|
||||
i2p::proxy::SOCKSProxy * m_SocksProxy;
|
||||
std::map<int, std::unique_ptr<I2PClientTunnel> > m_ClientTunnels; // port->tunnel
|
||||
std::map<std::tuple<i2p::data::IdentHash, int>, std::unique_ptr<I2PServerTunnel> > m_ServerTunnels; // <destination,port>->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;
|
||||
I2CPServer * m_I2CPServer;
|
||||
|
||||
public:
|
||||
// for HTTP
|
||||
|
||||
152
Config.cpp
152
Config.cpp
@@ -26,113 +26,56 @@ namespace config {
|
||||
options_description m_OptionsDesc;
|
||||
variables_map m_Options;
|
||||
|
||||
/* list of renamed options */
|
||||
std::map<std::string, std::string> remapped_options = {
|
||||
{ "tunnelscfg", "tunconf" },
|
||||
{ "v6", "ipv6" },
|
||||
{ "httpaddress", "http.address" },
|
||||
{ "httpport", "http.port" },
|
||||
{ "httpproxyaddress", "httpproxy.address" },
|
||||
{ "httpproxyport", "httpproxy.port" },
|
||||
{ "socksproxyaddress", "socksproxy.address" },
|
||||
{ "socksproxyport", "socksproxy.port" },
|
||||
{ "samaddress", "sam.address" },
|
||||
{ "samport", "sam.port" },
|
||||
{ "bobaddress", "bob.address" },
|
||||
{ "bobport", "bob.port" },
|
||||
{ "i2pcontroladdress", "i2pcontrol.address" },
|
||||
{ "i2pcontroladdress", "i2pcontrol.port" },
|
||||
{ "proxykeys", "httpproxy.keys" },
|
||||
};
|
||||
/* list of options, that loose their argument and become simple switch */
|
||||
std::set<std::string> boolean_options = {
|
||||
"daemon", "floodfill", "notransit", "service", "ipv6"
|
||||
};
|
||||
|
||||
/* this function is a solid piece of shit, remove it after 2.6.0 */
|
||||
std::pair<std::string, std::string> old_syntax_parser(const std::string& s) {
|
||||
std::string name = "";
|
||||
std::string value = "";
|
||||
std::size_t pos = 0;
|
||||
/* shortcuts -- -h */
|
||||
if (s.length() == 2 && s.at(0) == '-' && s.at(1) != '-')
|
||||
return make_pair(s.substr(1), "");
|
||||
/* old-style -- -log, /log, etc */
|
||||
if (s.at(0) == '/' || (s.at(0) == '-' && s.at(1) != '-')) {
|
||||
if ((pos = s.find_first_of("= ")) != std::string::npos) {
|
||||
name = s.substr(1, pos - 1);
|
||||
value = s.substr(pos + 1);
|
||||
} else {
|
||||
name = s.substr(1, pos);
|
||||
value = "";
|
||||
}
|
||||
if (boolean_options.count(name) > 0 && value != "")
|
||||
std::cerr << "args: don't give an argument to switch option: " << s << std::endl;
|
||||
if (m_OptionsDesc.find_nothrow(name, false)) {
|
||||
std::cerr << "args: option " << s << " style is DEPRECATED, use --" << name << " instead" << std::endl;
|
||||
return std::make_pair(name, value);
|
||||
}
|
||||
if (remapped_options.count(name) > 0) {
|
||||
name = remapped_options[name];
|
||||
std::cerr << "args: option " << s << " is DEPRECATED, use --" << name << " instead" << std::endl;
|
||||
return std::make_pair(name, value);
|
||||
} /* else */
|
||||
}
|
||||
/* long options -- --help */
|
||||
if (s.substr(0, 2) == "--") {
|
||||
if ((pos = s.find_first_of("= ")) != std::string::npos) {
|
||||
name = s.substr(2, pos - 2);
|
||||
value = s.substr(pos + 1);
|
||||
} else {
|
||||
name = s.substr(2, pos);
|
||||
value = "";
|
||||
}
|
||||
if (boolean_options.count(name) > 0 && value != "") {
|
||||
std::cerr << "args: don't give an argument to switch option: " << s << std::endl;
|
||||
value = "";
|
||||
}
|
||||
if (m_OptionsDesc.find_nothrow(name, false))
|
||||
return std::make_pair(name, value);
|
||||
if (remapped_options.count(name) > 0) {
|
||||
name = remapped_options[name];
|
||||
std::cerr << "args: option " << s << " is DEPRECATED, use --" << name << " instead" << std::endl;
|
||||
return std::make_pair(name, value);
|
||||
} /* else */
|
||||
}
|
||||
std::cerr << "args: unknown option -- " << s << std::endl;
|
||||
return std::make_pair("", "");
|
||||
}
|
||||
|
||||
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/i2p.conf or /var/lib/i2pd/i2p.conf)")
|
||||
("tunconf", value<std::string>()->default_value(""), "Path to config with tunnels list and options (default: try ~/.i2pd/tunnels.cfg or /var/lib/i2pd/tunnels.cfg)")
|
||||
("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 (stdout if not set, file - otherwise, for compatibility)")
|
||||
("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<char>()->default_value('-'), "Bandwidth limiting: L - 32kbps, O - 256Kbps, P - unlimited")
|
||||
("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");
|
||||
@@ -167,6 +110,13 @@ namespace config {
|
||||
("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")
|
||||
@@ -177,14 +127,52 @@ namespace config {
|
||||
("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)
|
||||
;
|
||||
}
|
||||
|
||||
@@ -193,7 +181,7 @@ namespace config {
|
||||
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, old_syntax_parser), m_Options);
|
||||
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);
|
||||
|
||||
4
Config.h
4
Config.h
@@ -68,7 +68,7 @@ namespace config {
|
||||
* @param value Variable where to store option
|
||||
* @return this function returns false if parameter not found
|
||||
*
|
||||
* @example uint16_t port; GetOption("sam.port", port);
|
||||
* Example: uint16_t port; GetOption("sam.port", port);
|
||||
*/
|
||||
template<typename T>
|
||||
bool GetOption(const char *name, T& value) {
|
||||
@@ -84,7 +84,7 @@ namespace config {
|
||||
* @param value New parameter value
|
||||
* @return true if value set up successful, false otherwise
|
||||
*
|
||||
* @example uint16_t port = 2827; SetOption("bob.port", port);
|
||||
* Example: uint16_t port = 2827; SetOption("bob.port", port);
|
||||
*/
|
||||
template<typename T>
|
||||
bool SetOption(const char *name, const T& value) {
|
||||
|
||||
153
Crypto.cpp
153
Crypto.cpp
@@ -3,10 +3,8 @@
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/dh.h>
|
||||
#include <openssl/md5.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/crypto.h>
|
||||
#include "TunnelBase.h"
|
||||
#include <openssl/ssl.h>
|
||||
@@ -146,9 +144,87 @@ namespace crypto
|
||||
}
|
||||
|
||||
// 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)
|
||||
@@ -169,7 +245,23 @@ namespace crypto
|
||||
{
|
||||
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; };
|
||||
DH_generate_key (m_DH);
|
||||
#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;
|
||||
@@ -200,11 +292,20 @@ namespace crypto
|
||||
ctx = BN_CTX_new ();
|
||||
// select random k
|
||||
BIGNUM * k = BN_new ();
|
||||
BN_rand_range (k, elgp);
|
||||
if (BN_is_zero (k)) BN_one (k);
|
||||
// caulculate a
|
||||
a = BN_new ();
|
||||
BN_mod_exp (a, elgg, k, elgp, ctx);
|
||||
#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
|
||||
@@ -279,6 +380,14 @@ namespace crypto
|
||||
{
|
||||
#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);
|
||||
@@ -286,11 +395,6 @@ namespace crypto
|
||||
bn2buf (p, pub, 256);
|
||||
BN_free (p);
|
||||
BN_CTX_free (ctx);
|
||||
#else
|
||||
DHKeys dh;
|
||||
dh.GenerateKeys (priv, pub);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
// HMAC
|
||||
@@ -695,17 +799,38 @@ namespace crypto
|
||||
}
|
||||
}*/
|
||||
|
||||
void InitCrypto ()
|
||||
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 ();*/
|
||||
}
|
||||
|
||||
6
Crypto.h
6
Crypto.h
@@ -7,7 +7,11 @@
|
||||
#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
|
||||
{
|
||||
@@ -273,7 +277,7 @@ namespace crypto
|
||||
#endif
|
||||
};
|
||||
|
||||
void InitCrypto ();
|
||||
void InitCrypto (bool precomputation);
|
||||
void TerminateCrypto ();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
234
Daemon.cpp
234
Daemon.cpp
@@ -13,6 +13,7 @@
|
||||
#include "RouterInfo.h"
|
||||
#include "RouterContext.h"
|
||||
#include "Tunnel.h"
|
||||
#include "HTTP.h"
|
||||
#include "NetDb.h"
|
||||
#include "Garlic.h"
|
||||
#include "Streaming.h"
|
||||
@@ -21,10 +22,8 @@
|
||||
#include "I2PControl.h"
|
||||
#include "ClientContext.h"
|
||||
#include "Crypto.h"
|
||||
|
||||
#ifdef USE_UPNP
|
||||
#include "UPnP.h"
|
||||
#endif
|
||||
#include "util.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -36,18 +35,15 @@ namespace i2p
|
||||
Daemon_Singleton_Private() {};
|
||||
~Daemon_Singleton_Private() {};
|
||||
|
||||
std::unique_ptr<i2p::util::HTTPServer> httpServer;
|
||||
std::unique_ptr<i2p::http::HTTPServer> httpServer;
|
||||
std::unique_ptr<i2p::client::I2PControlService> m_I2PControlService;
|
||||
|
||||
#ifdef USE_UPNP
|
||||
i2p::transport::UPnP m_UPnP;
|
||||
#endif
|
||||
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
|
||||
{
|
||||
@@ -69,123 +65,185 @@ namespace i2p
|
||||
i2p::fs::Init();
|
||||
|
||||
datadir = i2p::fs::GetDataDir();
|
||||
if (config == "")
|
||||
// TODO: drop old name detection in v2.8.0
|
||||
if (config == "")
|
||||
{
|
||||
config = i2p::fs::DataDirPath("i2p.conf");
|
||||
// use i2p.cong only if exists
|
||||
if (!i2p::fs::Exists (config)) config = ""; /* reset */
|
||||
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::crypto::InitCrypto ();
|
||||
i2p::context.Init ();
|
||||
|
||||
i2p::config::GetOption("daemon", isDaemon);
|
||||
|
||||
// TODO: move log init here
|
||||
std::string logs = ""; i2p::config::GetOption("log", logs);
|
||||
std::string logfile = ""; i2p::config::GetOption("logfile", logfile);
|
||||
std::string loglevel = ""; i2p::config::GetOption("loglevel", loglevel);
|
||||
|
||||
LogPrint(eLogInfo, "i2pd v", VERSION, " starting");
|
||||
/* 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 ();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
std::string host; i2p::config::GetOption("host", host);
|
||||
if (!i2p::config::IsDefault("host"))
|
||||
{
|
||||
LogPrint(eLogInfo, "Daemon: setting address for incoming connections to ", host);
|
||||
i2p::context.UpdateAddress (boost::asio::ip::address::from_string (host));
|
||||
}
|
||||
|
||||
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
|
||||
i2p::context.SetSupportsV6 (ipv6);
|
||||
i2p::context.SetSupportsV4 (ipv4);
|
||||
|
||||
bool transit; i2p::config::GetOption("notransit", transit);
|
||||
i2p::context.SetSupportsV6 (ipv6);
|
||||
i2p::context.SetAcceptsTunnels (!transit);
|
||||
uint16_t transitTunnels; i2p::config::GetOption("limits.transittunnels", transitTunnels);
|
||||
SetMaxNumTransitTunnels (transitTunnels);
|
||||
|
||||
bool isFloodfill; i2p::config::GetOption("floodfill", isFloodfill);
|
||||
char bandwidth; i2p::config::GetOption("bandwidth", bandwidth);
|
||||
|
||||
if (isFloodfill)
|
||||
{
|
||||
if (isFloodfill) {
|
||||
LogPrint(eLogInfo, "Daemon: router will be floodfill");
|
||||
i2p::context.SetFloodfill (true);
|
||||
}
|
||||
else
|
||||
} else {
|
||||
i2p::context.SetFloodfill (false);
|
||||
if (bandwidth != '-')
|
||||
{
|
||||
LogPrint(eLogInfo, "Daemon: bandwidth set to ", bandwidth);
|
||||
if (bandwidth > 'O')
|
||||
i2p::context.SetExtraBandwidth ();
|
||||
else if (bandwidth > 'L')
|
||||
i2p::context.SetHighBandwidth ();
|
||||
else
|
||||
i2p::context.SetLowBandwidth ();
|
||||
}
|
||||
|
||||
/* 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.SetExtraBandwidth ();
|
||||
i2p::context.SetBandwidth (i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH1);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint(eLogInfo, "Daemon: bandwidth set to 'low'");
|
||||
i2p::context.SetLowBandwidth ();
|
||||
}
|
||||
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);
|
||||
|
||||
return true;
|
||||
|
||||
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()
|
||||
{
|
||||
std::string logs = ""; i2p::config::GetOption("log", logs);
|
||||
std::string logfile = ""; i2p::config::GetOption("logfile", logfile);
|
||||
std::string loglevel = ""; i2p::config::GetOption("loglevel", loglevel);
|
||||
|
||||
if (isDaemon && (logs == "" || logs == "stdout"))
|
||||
logs = "file";
|
||||
|
||||
if (logs == "file") {
|
||||
if (logfile == "")
|
||||
logfile = i2p::fs::DataDirPath("i2pd.log");
|
||||
StartLog (logfile);
|
||||
} else {
|
||||
// use stdout
|
||||
StartLog ("");
|
||||
}
|
||||
SetLogLevel(loglevel);
|
||||
|
||||
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::util::HTTPServer>(new i2p::util::HTTPServer(httpAddr, httpPort));
|
||||
d.httpServer->Start();
|
||||
}
|
||||
|
||||
LogPrint(eLogInfo, "Daemon: starting NetDB");
|
||||
i2p::data::netdb.Start();
|
||||
|
||||
#ifdef USE_UPNP
|
||||
LogPrint(eLogInfo, "Daemon: starting UPnP");
|
||||
d.m_UPnP.Start ();
|
||||
#endif
|
||||
LogPrint(eLogInfo, "Daemon: starting Transports");
|
||||
i2p::transport::transports.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 ();
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@@ -201,6 +259,7 @@ namespace i2p
|
||||
d.m_I2PControlService = std::unique_ptr<i2p::client::I2PControlService>(new i2p::client::I2PControlService (i2pcpAddr, i2pcpPort));
|
||||
d.m_I2PControlService->Start ();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -211,10 +270,12 @@ namespace i2p
|
||||
i2p::client::context.Stop();
|
||||
LogPrint(eLogInfo, "Daemon: stopping Tunnels");
|
||||
i2p::tunnel::tunnels.Stop();
|
||||
#ifdef USE_UPNP
|
||||
LogPrint(eLogInfo, "Daemon: stopping UPnP");
|
||||
d.m_UPnP.Stop ();
|
||||
#endif
|
||||
|
||||
if (d.UPnP) {
|
||||
d.UPnP->Stop ();
|
||||
d.UPnP = nullptr;
|
||||
}
|
||||
|
||||
LogPrint(eLogInfo, "Daemon: stopping Transports");
|
||||
i2p::transport::transports.Stop();
|
||||
LogPrint(eLogInfo, "Daemon: stopping NetDB");
|
||||
@@ -231,9 +292,8 @@ namespace i2p
|
||||
d.m_I2PControlService = nullptr;
|
||||
}
|
||||
i2p::crypto::TerminateCrypto ();
|
||||
StopLog ();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
74
Daemon.h
74
Daemon.h
@@ -1,14 +1,9 @@
|
||||
#ifndef DAEMON_H__
|
||||
#define DAEMON_H__
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define Daemon i2p::util::DaemonWin32::Instance()
|
||||
#else
|
||||
#define Daemon i2p::util::DaemonLinux::Instance()
|
||||
#endif
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace util
|
||||
@@ -22,9 +17,7 @@ namespace i2p
|
||||
virtual bool stop();
|
||||
virtual void run () {};
|
||||
|
||||
bool isLogging;
|
||||
bool isDaemon;
|
||||
|
||||
bool running;
|
||||
|
||||
protected:
|
||||
@@ -38,7 +31,36 @@ namespace i2p
|
||||
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:
|
||||
@@ -50,26 +72,32 @@ namespace i2p
|
||||
|
||||
bool init(int argc, char* argv[]);
|
||||
bool start();
|
||||
bool stop();
|
||||
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;
|
||||
}
|
||||
|
||||
bool start();
|
||||
bool stop();
|
||||
; void run ();
|
||||
bool start();
|
||||
bool stop();
|
||||
void run ();
|
||||
|
||||
private:
|
||||
std::string pidfile;
|
||||
int pidFH;
|
||||
private:
|
||||
|
||||
std::string pidfile;
|
||||
int pidFH;
|
||||
|
||||
public:
|
||||
|
||||
int gracefullShutdownInterval; // in seconds
|
||||
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -12,19 +12,31 @@
|
||||
#include "Config.h"
|
||||
#include "FS.h"
|
||||
#include "Log.h"
|
||||
#include "RouterContext.h"
|
||||
#include "ClientContext.h"
|
||||
|
||||
void handle_signal(int sig)
|
||||
{
|
||||
switch (sig)
|
||||
{
|
||||
case SIGHUP:
|
||||
LogPrint(eLogInfo, "Daemon: Got SIGHUP, reopening log...");
|
||||
ReopenLogFile ();
|
||||
break;
|
||||
case SIGABRT:
|
||||
case SIGTERM:
|
||||
case SIGINT:
|
||||
Daemon.running = 0; // Exit loop
|
||||
case SIGHUP:
|
||||
LogPrint(eLogInfo, "Daemon: Got SIGHUP, reopening log...");
|
||||
i2p::log::Logger().Reopen ();
|
||||
i2p::client::context.ReloadConfig();
|
||||
break;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -35,7 +47,7 @@ namespace i2p
|
||||
{
|
||||
bool DaemonLinux::start()
|
||||
{
|
||||
if (isDaemon == 1)
|
||||
if (isDaemon)
|
||||
{
|
||||
pid_t pid;
|
||||
pid = fork();
|
||||
@@ -63,13 +75,10 @@ namespace i2p
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -99,6 +108,7 @@ namespace i2p
|
||||
return false;
|
||||
}
|
||||
}
|
||||
gracefullShutdownInterval = 0; // not specified
|
||||
|
||||
// Signal handler
|
||||
struct sigaction sa;
|
||||
@@ -125,6 +135,15 @@ namespace i2p
|
||||
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,3 +1,5 @@
|
||||
#include <thread>
|
||||
#include <clocale>
|
||||
#include "Config.h"
|
||||
#include "Daemon.h"
|
||||
#include "util.h"
|
||||
@@ -5,7 +7,10 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "Win32/Win32App.h"
|
||||
#include "Win32/Win32Service.h"
|
||||
#ifdef WIN32_APP
|
||||
#include "Win32/Win32App.h"
|
||||
#endif
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -16,9 +21,48 @@ namespace i2p
|
||||
setlocale(LC_CTYPE, "");
|
||||
SetConsoleCP(1251);
|
||||
SetConsoleOutputCP(1251);
|
||||
setlocale(LC_ALL, "Russian");
|
||||
return Daemon_Singleton::init(argc, argv);
|
||||
}
|
||||
setlocale(LC_ALL, "Russian");
|
||||
|
||||
if (!Daemon_Singleton::init(argc, argv))
|
||||
return false;
|
||||
|
||||
std::string serviceControl; i2p::config::GetOption("svcctl", serviceControl);
|
||||
if (serviceControl == "install")
|
||||
{
|
||||
LogPrint(eLogInfo, "WinSVC: installing ", SERVICE_NAME, " as service");
|
||||
InstallService(
|
||||
SERVICE_NAME, // Name of service
|
||||
SERVICE_DISPLAY_NAME, // Name to display
|
||||
SERVICE_START_TYPE, // Service start type
|
||||
SERVICE_DEPENDENCIES, // Dependencies
|
||||
SERVICE_ACCOUNT, // Service running account
|
||||
SERVICE_PASSWORD // Password of the account
|
||||
);
|
||||
return false;
|
||||
}
|
||||
else if (serviceControl == "remove")
|
||||
{
|
||||
LogPrint(eLogInfo, "WinSVC: uninstalling ", SERVICE_NAME, " service");
|
||||
UninstallService(SERVICE_NAME);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isDaemon)
|
||||
{
|
||||
LogPrint(eLogDebug, "Daemon: running as service");
|
||||
I2PService service(SERVICE_NAME);
|
||||
if (!I2PService::Run(service))
|
||||
{
|
||||
LogPrint(eLogError, "Daemon: Service failed to run w/err 0x%08lx\n", GetLastError());
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
LogPrint(eLogDebug, "Daemon: running as user");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DaemonWin32::start()
|
||||
{
|
||||
@@ -26,29 +70,44 @@ namespace i2p
|
||||
SetConsoleCP(1251);
|
||||
SetConsoleOutputCP(1251);
|
||||
setlocale(LC_ALL, "Russian");
|
||||
if (!i2p::win32::StartWin32App ()) return false;
|
||||
|
||||
// override log
|
||||
#ifdef WIN32_APP
|
||||
if (!i2p::win32::StartWin32App ()) return false;
|
||||
|
||||
// override log
|
||||
i2p::config::SetOption("log", std::string ("file"));
|
||||
#endif
|
||||
bool ret = Daemon_Singleton::start();
|
||||
if (ret && IsLogToFile ())
|
||||
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 ()
|
||||
{
|
||||
i2p::win32::RunWin32App ();
|
||||
}
|
||||
|
||||
void DaemonWin32::run ()
|
||||
{
|
||||
#ifdef WIN32_APP
|
||||
i2p::win32::RunWin32App ();
|
||||
#else
|
||||
while (running)
|
||||
{
|
||||
std::this_thread::sleep_for (std::chrono::seconds(1));
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/rand.h>
|
||||
#include "Crypto.h"
|
||||
#include "Log.h"
|
||||
#include "TunnelBase.h"
|
||||
#include "RouterContext.h"
|
||||
|
||||
572
Destination.cpp
572
Destination.cpp
@@ -1,30 +1,23 @@
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#include "Crypto.h"
|
||||
#include "Log.h"
|
||||
#include "FS.h"
|
||||
#include "Crypto.h"
|
||||
#include "Timestamp.h"
|
||||
#include "NetDb.h"
|
||||
#include "Destination.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic,
|
||||
const std::map<std::string, std::string> * params):
|
||||
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
|
||||
m_Keys (keys), m_IsPublic (isPublic), m_PublishReplyToken (0),
|
||||
m_DatagramDestination (nullptr), m_PublishConfirmationTimer (m_Service),
|
||||
LeaseSetDestination::LeaseSetDestination (bool isPublic, const std::map<std::string, std::string> * params):
|
||||
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_IsPublic (isPublic),
|
||||
m_PublishReplyToken (0), m_PublishConfirmationTimer (m_Service),
|
||||
m_PublishVerificationTimer (m_Service), m_CleanupTimer (m_Service)
|
||||
{
|
||||
if (m_IsPublic)
|
||||
PersistTemporaryKeys ();
|
||||
else
|
||||
i2p::crypto::GenerateElGamalKeyPair(m_EncryptionPrivateKey, m_EncryptionPublicKey);
|
||||
int inboundTunnelLen = DEFAULT_INBOUND_TUNNEL_LENGTH;
|
||||
int outboundTunnelLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH;
|
||||
int inboundTunnelsQuantity = DEFAULT_INBOUND_TUNNELS_QUANTITY;
|
||||
@@ -35,28 +28,30 @@ namespace client
|
||||
{
|
||||
auto it = params->find (I2CP_PARAM_INBOUND_TUNNEL_LENGTH);
|
||||
if (it != params->end ())
|
||||
{
|
||||
int len = boost::lexical_cast<int>(it->second);
|
||||
if (len > 0)
|
||||
{
|
||||
|
||||
int len = i2p::util::lexical_cast<int>(it->second, inboundTunnelLen);
|
||||
if (len >= 0)
|
||||
{
|
||||
inboundTunnelLen = len;
|
||||
LogPrint (eLogInfo, "Destination: Inbound tunnel length set to ", len);
|
||||
inboundTunnelLen = len;
|
||||
}
|
||||
LogPrint (eLogInfo, "Destination: Inbound tunnel length set to ", inboundTunnelLen);
|
||||
}
|
||||
it = params->find (I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH);
|
||||
if (it != params->end ())
|
||||
{
|
||||
int len = boost::lexical_cast<int>(it->second);
|
||||
if (len > 0)
|
||||
|
||||
int len = i2p::util::lexical_cast<int>(it->second, outboundTunnelLen);
|
||||
if (len >= 0)
|
||||
{
|
||||
outboundTunnelLen = len;
|
||||
LogPrint (eLogInfo, "Destination: Outbound tunnel length set to ", len);
|
||||
outboundTunnelLen = len;
|
||||
}
|
||||
LogPrint (eLogInfo, "Destination: Outbound tunnel length set to ", outboundTunnelLen);
|
||||
}
|
||||
it = params->find (I2CP_PARAM_INBOUND_TUNNELS_QUANTITY);
|
||||
if (it != params->end ())
|
||||
{
|
||||
int quantity = boost::lexical_cast<int>(it->second);
|
||||
int quantity = i2p::util::lexical_cast<int>(it->second, inboundTunnelsQuantity);
|
||||
if (quantity > 0)
|
||||
{
|
||||
inboundTunnelsQuantity = quantity;
|
||||
@@ -66,7 +61,7 @@ namespace client
|
||||
it = params->find (I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY);
|
||||
if (it != params->end ())
|
||||
{
|
||||
int quantity = boost::lexical_cast<int>(it->second);
|
||||
int quantity = i2p::util::lexical_cast<int>(it->second, outboundTunnelsQuantity);
|
||||
if (quantity > 0)
|
||||
{
|
||||
outboundTunnelsQuantity = quantity;
|
||||
@@ -76,11 +71,11 @@ namespace client
|
||||
it = params->find (I2CP_PARAM_TAGS_TO_SEND);
|
||||
if (it != params->end ())
|
||||
{
|
||||
int tagsToSend = boost::lexical_cast<int>(it->second);
|
||||
int tagsToSend = i2p::util::lexical_cast<int>(it->second, numTags);
|
||||
if (tagsToSend > 0)
|
||||
{
|
||||
numTags = tagsToSend;
|
||||
LogPrint (eLogInfo, "Destination: Tags to send set to ", tagsToSend);
|
||||
LogPrint (eLogInfo, "Destination: Tags to send set to ", tagsToSend);
|
||||
}
|
||||
}
|
||||
it = params->find (I2CP_PARAM_EXPLICIT_PEERS);
|
||||
@@ -102,24 +97,20 @@ namespace client
|
||||
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inboundTunnelLen, outboundTunnelLen, inboundTunnelsQuantity, outboundTunnelsQuantity);
|
||||
if (explicitPeers)
|
||||
m_Pool->SetExplicitPeers (explicitPeers);
|
||||
if (m_IsPublic)
|
||||
LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created");
|
||||
}
|
||||
|
||||
ClientDestination::~ClientDestination ()
|
||||
LeaseSetDestination::~LeaseSetDestination ()
|
||||
{
|
||||
if (m_IsRunning)
|
||||
Stop ();
|
||||
for (auto it: m_LeaseSetRequests)
|
||||
for (auto& it: m_LeaseSetRequests)
|
||||
if (it.second->requestComplete) it.second->requestComplete (nullptr);
|
||||
m_LeaseSetRequests.clear ();
|
||||
if (m_Pool)
|
||||
i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool);
|
||||
if (m_DatagramDestination)
|
||||
delete m_DatagramDestination;
|
||||
}
|
||||
|
||||
void ClientDestination::Run ()
|
||||
void LeaseSetDestination::Run ()
|
||||
{
|
||||
while (m_IsRunning)
|
||||
{
|
||||
@@ -134,26 +125,25 @@ namespace client
|
||||
}
|
||||
}
|
||||
|
||||
void ClientDestination::Start ()
|
||||
bool LeaseSetDestination::Start ()
|
||||
{
|
||||
if (!m_IsRunning)
|
||||
{
|
||||
m_IsRunning = true;
|
||||
m_Pool->SetLocalDestination (shared_from_this ());
|
||||
m_Pool->SetActive (true);
|
||||
m_Thread = new std::thread (std::bind (&ClientDestination::Run, shared_from_this ()));
|
||||
m_StreamingDestination = std::make_shared<i2p::stream::StreamingDestination> (shared_from_this ()); // TODO:
|
||||
m_StreamingDestination->Start ();
|
||||
for (auto it: m_StreamingDestinationsByPorts)
|
||||
it.second->Start ();
|
||||
m_Thread = new std::thread (std::bind (&LeaseSetDestination::Run, shared_from_this ()));
|
||||
|
||||
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
|
||||
m_CleanupTimer.async_wait (std::bind (&ClientDestination::HandleCleanupTimer,
|
||||
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void ClientDestination::Stop ()
|
||||
bool LeaseSetDestination::Stop ()
|
||||
{
|
||||
if (m_IsRunning)
|
||||
{
|
||||
@@ -161,16 +151,6 @@ namespace client
|
||||
m_PublishConfirmationTimer.cancel ();
|
||||
m_PublishVerificationTimer.cancel ();
|
||||
m_IsRunning = false;
|
||||
m_StreamingDestination->Stop ();
|
||||
m_StreamingDestination = nullptr;
|
||||
for (auto it: m_StreamingDestinationsByPorts)
|
||||
it.second->Stop ();
|
||||
if (m_DatagramDestination)
|
||||
{
|
||||
auto d = m_DatagramDestination;
|
||||
m_DatagramDestination = nullptr;
|
||||
delete d;
|
||||
}
|
||||
if (m_Pool)
|
||||
{
|
||||
m_Pool->SetLocalDestination (nullptr);
|
||||
@@ -183,10 +163,13 @@ namespace client
|
||||
delete m_Thread;
|
||||
m_Thread = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<const i2p::data::LeaseSet> ClientDestination::FindLeaseSet (const i2p::data::IdentHash& ident)
|
||||
std::shared_ptr<const i2p::data::LeaseSet> LeaseSetDestination::FindLeaseSet (const i2p::data::IdentHash& ident)
|
||||
{
|
||||
auto it = m_RemoteLeaseSets.find (ident);
|
||||
if (it != m_RemoteLeaseSets.end ())
|
||||
@@ -209,7 +192,7 @@ namespace client
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<const i2p::data::LeaseSet> ClientDestination::GetLeaseSet ()
|
||||
std::shared_ptr<const i2p::data::LocalLeaseSet> LeaseSetDestination::GetLeaseSet ()
|
||||
{
|
||||
if (!m_Pool) return nullptr;
|
||||
if (!m_LeaseSet)
|
||||
@@ -217,12 +200,24 @@ namespace client
|
||||
return m_LeaseSet;
|
||||
}
|
||||
|
||||
void ClientDestination::UpdateLeaseSet ()
|
||||
void LeaseSetDestination::SetLeaseSet (i2p::data::LocalLeaseSet * newLeaseSet)
|
||||
{
|
||||
m_LeaseSet.reset (new i2p::data::LeaseSet (m_Pool));
|
||||
m_LeaseSet.reset (newLeaseSet);
|
||||
if (m_IsPublic)
|
||||
{
|
||||
m_PublishVerificationTimer.cancel ();
|
||||
Publish ();
|
||||
}
|
||||
}
|
||||
|
||||
void LeaseSetDestination::UpdateLeaseSet ()
|
||||
{
|
||||
int numTunnels = m_Pool->GetNumInboundTunnels () + 2; // 2 backup tunnels
|
||||
if (numTunnels > i2p::data::MAX_NUM_LEASES) numTunnels = i2p::data::MAX_NUM_LEASES; // 16 tunnels maximum
|
||||
CreateNewLeaseSet (m_Pool->GetInboundTunnels (numTunnels));
|
||||
}
|
||||
|
||||
bool ClientDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag)
|
||||
bool LeaseSetDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag)
|
||||
{
|
||||
struct
|
||||
{
|
||||
@@ -238,17 +233,17 @@ namespace client
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClientDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
void LeaseSetDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
m_Service.post (std::bind (&ClientDestination::HandleGarlicMessage, shared_from_this (), msg));
|
||||
m_Service.post (std::bind (&LeaseSetDestination::HandleGarlicMessage, shared_from_this (), msg));
|
||||
}
|
||||
|
||||
void ClientDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
void LeaseSetDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
m_Service.post (std::bind (&ClientDestination::HandleDeliveryStatusMessage, shared_from_this (), msg));
|
||||
m_Service.post (std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msg));
|
||||
}
|
||||
|
||||
void ClientDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
|
||||
void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
|
||||
{
|
||||
uint8_t typeID = buf[I2NP_HEADER_TYPEID_OFFSET];
|
||||
switch (typeID)
|
||||
@@ -271,7 +266,7 @@ namespace client
|
||||
}
|
||||
}
|
||||
|
||||
void ClientDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len)
|
||||
void LeaseSetDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len)
|
||||
{
|
||||
uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET);
|
||||
size_t offset = DATABASE_STORE_HEADER_SIZE;
|
||||
@@ -335,7 +330,7 @@ namespace client
|
||||
}
|
||||
}
|
||||
|
||||
void ClientDestination::HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len)
|
||||
void LeaseSetDestination::HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len)
|
||||
{
|
||||
i2p::data::IdentHash key (buf);
|
||||
int num = buf[32]; // num
|
||||
@@ -378,35 +373,30 @@ namespace client
|
||||
LogPrint (eLogWarning, "Destination: Request for ", key.ToBase64 (), " not found");
|
||||
}
|
||||
|
||||
void ClientDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
void LeaseSetDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
|
||||
if (msgID == m_PublishReplyToken)
|
||||
{
|
||||
LogPrint (eLogDebug, "Destination: Publishing LeaseSet confirmed");
|
||||
LogPrint (eLogDebug, "Destination: Publishing LeaseSet confirmed for ", GetIdentHash().ToBase32());
|
||||
m_ExcludedFloodfills.clear ();
|
||||
m_PublishReplyToken = 0;
|
||||
// schedule verification
|
||||
m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT));
|
||||
m_PublishVerificationTimer.async_wait (std::bind (&ClientDestination::HandlePublishVerificationTimer,
|
||||
m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
else
|
||||
i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msg);
|
||||
}
|
||||
|
||||
void ClientDestination::SetLeaseSetUpdated ()
|
||||
void LeaseSetDestination::SetLeaseSetUpdated ()
|
||||
{
|
||||
i2p::garlic::GarlicDestination::SetLeaseSetUpdated ();
|
||||
UpdateLeaseSet ();
|
||||
if (m_IsPublic)
|
||||
{
|
||||
m_PublishVerificationTimer.cancel ();
|
||||
Publish ();
|
||||
}
|
||||
}
|
||||
|
||||
void ClientDestination::Publish ()
|
||||
void LeaseSetDestination::Publish ()
|
||||
{
|
||||
if (!m_LeaseSet || !m_Pool)
|
||||
{
|
||||
@@ -424,6 +414,12 @@ namespace client
|
||||
LogPrint (eLogError, "Destination: Can't publish LeaseSet. No outbound tunnels");
|
||||
return;
|
||||
}
|
||||
auto inbound = m_Pool->GetNextInboundTunnel ();
|
||||
if (!inbound)
|
||||
{
|
||||
LogPrint (eLogError, "Destination: Can't publish LeaseSet. No inbound tunnels");
|
||||
return;
|
||||
}
|
||||
auto floodfill = i2p::data::netdb.GetClosestFloodfill (m_LeaseSet->GetIdentHash (), m_ExcludedFloodfills);
|
||||
if (!floodfill)
|
||||
{
|
||||
@@ -434,14 +430,14 @@ namespace client
|
||||
m_ExcludedFloodfills.insert (floodfill->GetIdentHash ());
|
||||
LogPrint (eLogDebug, "Destination: Publish LeaseSet of ", GetIdentHash ().ToBase32 ());
|
||||
RAND_bytes ((uint8_t *)&m_PublishReplyToken, 4);
|
||||
auto msg = WrapMessage (floodfill, i2p::CreateDatabaseStoreMsg (m_LeaseSet, m_PublishReplyToken));
|
||||
auto msg = WrapMessage (floodfill, i2p::CreateDatabaseStoreMsg (m_LeaseSet, m_PublishReplyToken, inbound));
|
||||
m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT));
|
||||
m_PublishConfirmationTimer.async_wait (std::bind (&ClientDestination::HandlePublishConfirmationTimer,
|
||||
m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg);
|
||||
}
|
||||
|
||||
void ClientDestination::HandlePublishConfirmationTimer (const boost::system::error_code& ecode)
|
||||
void LeaseSetDestination::HandlePublishConfirmationTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
@@ -454,7 +450,7 @@ namespace client
|
||||
}
|
||||
}
|
||||
|
||||
void ClientDestination::HandlePublishVerificationTimer (const boost::system::error_code& ecode)
|
||||
void LeaseSetDestination::HandlePublishVerificationTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
@@ -463,25 +459,239 @@ namespace client
|
||||
// "this" added due to bug in gcc 4.7-4.8
|
||||
[s,this](std::shared_ptr<i2p::data::LeaseSet> leaseSet)
|
||||
{
|
||||
if (leaseSet)
|
||||
if (leaseSet && s->m_LeaseSet)
|
||||
{
|
||||
if (s->m_LeaseSet && *s->m_LeaseSet == *leaseSet)
|
||||
{
|
||||
// we got latest LeasetSet
|
||||
LogPrint (eLogDebug, "Destination: published LeaseSet verified");
|
||||
s->m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_REGULAR_VERIFICATION_INTERNAL));
|
||||
s->m_PublishVerificationTimer.async_wait (std::bind (&ClientDestination::HandlePublishVerificationTimer, s, std::placeholders::_1));
|
||||
return;
|
||||
}
|
||||
// we got latest LeasetSet
|
||||
LogPrint (eLogDebug, "Destination: published LeaseSet verified for ", GetIdentHash().ToBase32());
|
||||
s->m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_REGULAR_VERIFICATION_INTERNAL));
|
||||
s->m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, s, std::placeholders::_1));
|
||||
return;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Destination: couldn't find published LeaseSet");
|
||||
LogPrint (eLogWarning, "Destination: couldn't find published LeaseSet for ", GetIdentHash().ToBase32());
|
||||
// we have to publish again
|
||||
s->Publish ();
|
||||
s->Publish ();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool LeaseSetDestination::RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
|
||||
{
|
||||
if (!m_Pool || !IsReady ())
|
||||
{
|
||||
if (requestComplete)
|
||||
m_Service.post ([requestComplete](void){requestComplete (nullptr);});
|
||||
return false;
|
||||
}
|
||||
m_Service.post (std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete));
|
||||
return true;
|
||||
}
|
||||
|
||||
void LeaseSetDestination::CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify)
|
||||
{
|
||||
auto s = shared_from_this ();
|
||||
m_Service.post ([dest, notify, s](void)
|
||||
{
|
||||
auto it = s->m_LeaseSetRequests.find (dest);
|
||||
if (it != s->m_LeaseSetRequests.end ())
|
||||
{
|
||||
auto requestComplete = it->second->requestComplete;
|
||||
s->m_LeaseSetRequests.erase (it);
|
||||
if (notify && requestComplete) requestComplete (nullptr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void LeaseSetDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
|
||||
{
|
||||
std::set<i2p::data::IdentHash> excluded;
|
||||
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded);
|
||||
if (floodfill)
|
||||
{
|
||||
auto request = std::make_shared<LeaseSetRequest> (m_Service);
|
||||
request->requestComplete = requestComplete;
|
||||
auto ret = m_LeaseSetRequests.insert (std::pair<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> >(dest,request));
|
||||
if (ret.second) // inserted
|
||||
{
|
||||
if (!SendLeaseSetRequest (dest, floodfill, request))
|
||||
{
|
||||
// request failed
|
||||
m_LeaseSetRequests.erase (dest);
|
||||
if (request->requestComplete) request->requestComplete (nullptr);
|
||||
}
|
||||
}
|
||||
else // duplicate
|
||||
{
|
||||
LogPrint (eLogWarning, "Destination: Request of LeaseSet ", dest.ToBase64 (), " is pending already");
|
||||
// TODO: queue up requests
|
||||
if (request->requestComplete) request->requestComplete (nullptr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "Destination: Can't request LeaseSet, no floodfills found");
|
||||
if (requestComplete) requestComplete (nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
bool LeaseSetDestination::SendLeaseSetRequest (const i2p::data::IdentHash& dest,
|
||||
std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request)
|
||||
{
|
||||
if (!request->replyTunnel || !request->replyTunnel->IsEstablished ())
|
||||
request->replyTunnel = m_Pool->GetNextInboundTunnel ();
|
||||
if (!request->replyTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no inbound tunnels found");
|
||||
if (!request->outboundTunnel || !request->outboundTunnel->IsEstablished ())
|
||||
request->outboundTunnel = m_Pool->GetNextOutboundTunnel ();
|
||||
if (!request->outboundTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no outbound tunnels found");
|
||||
|
||||
if (request->replyTunnel && request->outboundTunnel)
|
||||
{
|
||||
request->excluded.insert (nextFloodfill->GetIdentHash ());
|
||||
request->requestTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
request->requestTimeoutTimer.cancel ();
|
||||
|
||||
uint8_t replyKey[32], replyTag[32];
|
||||
RAND_bytes (replyKey, 32); // random session key
|
||||
RAND_bytes (replyTag, 32); // random session tag
|
||||
AddSessionKey (replyKey, replyTag);
|
||||
|
||||
auto msg = WrapMessage (nextFloodfill,
|
||||
CreateLeaseSetDatabaseLookupMsg (dest, request->excluded,
|
||||
request->replyTunnel, replyKey, replyTag));
|
||||
request->outboundTunnel->SendTunnelDataMsg (
|
||||
{
|
||||
i2p::tunnel::TunnelMessageBlock
|
||||
{
|
||||
i2p::tunnel::eDeliveryTypeRouter,
|
||||
nextFloodfill->GetIdentHash (), 0, msg
|
||||
}
|
||||
});
|
||||
request->requestTimeoutTimer.expires_from_now (boost::posix_time::seconds(LEASESET_REQUEST_TIMEOUT));
|
||||
request->requestTimeoutTimer.async_wait (std::bind (&LeaseSetDestination::HandleRequestTimoutTimer,
|
||||
shared_from_this (), std::placeholders::_1, dest));
|
||||
}
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void LeaseSetDestination::HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
auto it = m_LeaseSetRequests.find (dest);
|
||||
if (it != m_LeaseSetRequests.end ())
|
||||
{
|
||||
bool done = false;
|
||||
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (ts < it->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT)
|
||||
{
|
||||
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, it->second->excluded);
|
||||
if (floodfill)
|
||||
{
|
||||
// reset tunnels, because one them might fail
|
||||
it->second->outboundTunnel = nullptr;
|
||||
it->second->replyTunnel = nullptr;
|
||||
done = !SendLeaseSetRequest (dest, floodfill, it->second);
|
||||
}
|
||||
else
|
||||
done = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Destination: ", dest.ToBase64 (), " was not found within ", MAX_LEASESET_REQUEST_TIMEOUT, " seconds");
|
||||
done = true;
|
||||
}
|
||||
|
||||
if (done)
|
||||
{
|
||||
auto requestComplete = it->second->requestComplete;
|
||||
m_LeaseSetRequests.erase (it);
|
||||
if (requestComplete) requestComplete (nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LeaseSetDestination::HandleCleanupTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
CleanupExpiredTags ();
|
||||
CleanupRemoteLeaseSets ();
|
||||
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
|
||||
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
|
||||
void LeaseSetDestination::CleanupRemoteLeaseSets ()
|
||||
{
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
for (auto it = m_RemoteLeaseSets.begin (); it != m_RemoteLeaseSets.end ();)
|
||||
{
|
||||
if (it->second->IsEmpty () || ts > it->second->GetExpirationTime ()) // leaseset expired
|
||||
{
|
||||
LogPrint (eLogWarning, "Destination: Remote LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired");
|
||||
it = m_RemoteLeaseSets.erase (it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params):
|
||||
LeaseSetDestination (isPublic, params),
|
||||
m_Keys (keys), m_DatagramDestination (nullptr)
|
||||
{
|
||||
if (isPublic)
|
||||
PersistTemporaryKeys ();
|
||||
else
|
||||
i2p::crypto::GenerateElGamalKeyPair(m_EncryptionPrivateKey, m_EncryptionPublicKey);
|
||||
if (isPublic)
|
||||
LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created");
|
||||
}
|
||||
|
||||
ClientDestination::~ClientDestination ()
|
||||
{
|
||||
if (m_DatagramDestination)
|
||||
delete m_DatagramDestination;
|
||||
}
|
||||
|
||||
bool ClientDestination::Start ()
|
||||
{
|
||||
if (LeaseSetDestination::Start ())
|
||||
{
|
||||
m_StreamingDestination = std::make_shared<i2p::stream::StreamingDestination> (GetSharedFromThis ()); // TODO:
|
||||
m_StreamingDestination->Start ();
|
||||
for (auto& it: m_StreamingDestinationsByPorts)
|
||||
it.second->Start ();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ClientDestination::Stop ()
|
||||
{
|
||||
if (LeaseSetDestination::Stop ())
|
||||
{
|
||||
m_StreamingDestination->Stop ();
|
||||
m_StreamingDestination = nullptr;
|
||||
for (auto& it: m_StreamingDestinationsByPorts)
|
||||
it.second->Stop ();
|
||||
if (m_DatagramDestination)
|
||||
{
|
||||
auto d = m_DatagramDestination;
|
||||
m_DatagramDestination = nullptr;
|
||||
delete d;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len)
|
||||
{
|
||||
uint32_t length = bufbe32toh (buf);
|
||||
@@ -511,8 +721,8 @@ namespace client
|
||||
default:
|
||||
LogPrint (eLogError, "Destination: Data: unexpected protocol ", buf[9]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port)
|
||||
{
|
||||
if (!streamRequestComplete)
|
||||
@@ -525,7 +735,7 @@ namespace client
|
||||
streamRequestComplete(CreateStream (leaseSet, port));
|
||||
else
|
||||
{
|
||||
auto s = shared_from_this ();
|
||||
auto s = GetSharedFromThis ();
|
||||
RequestDestination (dest,
|
||||
[s, streamRequestComplete, port](std::shared_ptr<i2p::data::LeaseSet> ls)
|
||||
{
|
||||
@@ -578,7 +788,7 @@ namespace client
|
||||
|
||||
std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::CreateStreamingDestination (int port, bool gzip)
|
||||
{
|
||||
auto dest = std::make_shared<i2p::stream::StreamingDestination> (shared_from_this (), port, gzip);
|
||||
auto dest = std::make_shared<i2p::stream::StreamingDestination> (GetSharedFromThis (), port, gzip);
|
||||
if (port)
|
||||
m_StreamingDestinationsByPorts[port] = dest;
|
||||
else // update default
|
||||
@@ -589,173 +799,23 @@ namespace client
|
||||
i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination ()
|
||||
{
|
||||
if (!m_DatagramDestination)
|
||||
m_DatagramDestination = new i2p::datagram::DatagramDestination (shared_from_this ());
|
||||
m_DatagramDestination = new i2p::datagram::DatagramDestination (GetSharedFromThis ());
|
||||
return m_DatagramDestination;
|
||||
}
|
||||
|
||||
bool ClientDestination::RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
|
||||
std::vector<std::shared_ptr<const i2p::stream::Stream> > ClientDestination::GetAllStreams () const
|
||||
{
|
||||
if (!m_Pool || !IsReady ())
|
||||
{
|
||||
if (requestComplete) requestComplete (nullptr);
|
||||
return false;
|
||||
}
|
||||
m_Service.post (std::bind (&ClientDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete));
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClientDestination::CancelDestinationRequest (const i2p::data::IdentHash& dest)
|
||||
{
|
||||
auto s = shared_from_this ();
|
||||
m_Service.post ([dest, s](void)
|
||||
{
|
||||
auto it = s->m_LeaseSetRequests.find (dest);
|
||||
if (it != s->m_LeaseSetRequests.end ())
|
||||
{
|
||||
auto requestComplete = it->second->requestComplete;
|
||||
s->m_LeaseSetRequests.erase (it);
|
||||
if (requestComplete) requestComplete (nullptr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ClientDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
|
||||
{
|
||||
std::set<i2p::data::IdentHash> excluded;
|
||||
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded);
|
||||
if (floodfill)
|
||||
std::vector<std::shared_ptr<const i2p::stream::Stream> > ret;
|
||||
if (m_StreamingDestination)
|
||||
{
|
||||
auto request = std::make_shared<LeaseSetRequest> (m_Service);
|
||||
request->requestComplete = requestComplete;
|
||||
auto ret = m_LeaseSetRequests.insert (std::pair<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> >(dest,request));
|
||||
if (ret.second) // inserted
|
||||
{
|
||||
if (!SendLeaseSetRequest (dest, floodfill, request))
|
||||
{
|
||||
// request failed
|
||||
m_LeaseSetRequests.erase (dest);
|
||||
if (request->requestComplete) request->requestComplete (nullptr);
|
||||
}
|
||||
}
|
||||
else // duplicate
|
||||
{
|
||||
LogPrint (eLogWarning, "Destination: Request of LeaseSet ", dest.ToBase64 (), " is pending already");
|
||||
// TODO: queue up requests
|
||||
if (request->requestComplete) request->requestComplete (nullptr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "Destination: Can't request LeaseSet, no floodfills found");
|
||||
if (requestComplete) requestComplete (nullptr);
|
||||
for (auto& it: m_StreamingDestination->GetStreams ())
|
||||
ret.push_back (it.second);
|
||||
}
|
||||
for (auto& it: m_StreamingDestinationsByPorts)
|
||||
for (auto& it1: it.second->GetStreams ())
|
||||
ret.push_back (it1.second);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ClientDestination::SendLeaseSetRequest (const i2p::data::IdentHash& dest,
|
||||
std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request)
|
||||
{
|
||||
if (!request->replyTunnel || !request->replyTunnel->IsEstablished ())
|
||||
request->replyTunnel = m_Pool->GetNextInboundTunnel ();
|
||||
if (!request->replyTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no inbound tunnels found");
|
||||
if (!request->outboundTunnel || !request->outboundTunnel->IsEstablished ())
|
||||
request->outboundTunnel = m_Pool->GetNextOutboundTunnel ();
|
||||
if (!request->outboundTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no outbound tunnels found");
|
||||
|
||||
if (request->replyTunnel && request->outboundTunnel)
|
||||
{
|
||||
request->excluded.insert (nextFloodfill->GetIdentHash ());
|
||||
request->requestTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
request->requestTimeoutTimer.cancel ();
|
||||
|
||||
uint8_t replyKey[32], replyTag[32];
|
||||
RAND_bytes (replyKey, 32); // random session key
|
||||
RAND_bytes (replyTag, 32); // random session tag
|
||||
AddSessionKey (replyKey, replyTag);
|
||||
|
||||
auto msg = WrapMessage (nextFloodfill,
|
||||
CreateLeaseSetDatabaseLookupMsg (dest, request->excluded,
|
||||
request->replyTunnel, replyKey, replyTag));
|
||||
request->outboundTunnel->SendTunnelDataMsg (
|
||||
{
|
||||
i2p::tunnel::TunnelMessageBlock
|
||||
{
|
||||
i2p::tunnel::eDeliveryTypeRouter,
|
||||
nextFloodfill->GetIdentHash (), 0, msg
|
||||
}
|
||||
});
|
||||
request->requestTimeoutTimer.expires_from_now (boost::posix_time::seconds(LEASESET_REQUEST_TIMEOUT));
|
||||
request->requestTimeoutTimer.async_wait (std::bind (&ClientDestination::HandleRequestTimoutTimer,
|
||||
shared_from_this (), std::placeholders::_1, dest));
|
||||
}
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClientDestination::HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
auto it = m_LeaseSetRequests.find (dest);
|
||||
if (it != m_LeaseSetRequests.end ())
|
||||
{
|
||||
bool done = false;
|
||||
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (ts < it->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT)
|
||||
{
|
||||
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, it->second->excluded);
|
||||
if (floodfill)
|
||||
{
|
||||
// reset tunnels, because one them might fail
|
||||
it->second->outboundTunnel = nullptr;
|
||||
it->second->replyTunnel = nullptr;
|
||||
done = !SendLeaseSetRequest (dest, floodfill, it->second);
|
||||
}
|
||||
else
|
||||
done = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Destination: ", dest.ToBase64 (), " was not found within ", MAX_LEASESET_REQUEST_TIMEOUT, " seconds");
|
||||
done = true;
|
||||
}
|
||||
|
||||
if (done)
|
||||
{
|
||||
auto requestComplete = it->second->requestComplete;
|
||||
m_LeaseSetRequests.erase (it);
|
||||
if (requestComplete) requestComplete (nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClientDestination::HandleCleanupTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
CleanupExpiredTags ();
|
||||
CleanupRemoteLeaseSets ();
|
||||
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
|
||||
m_CleanupTimer.async_wait (std::bind (&ClientDestination::HandleCleanupTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
|
||||
void ClientDestination::CleanupRemoteLeaseSets ()
|
||||
{
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
for (auto it = m_RemoteLeaseSets.begin (); it != m_RemoteLeaseSets.end ();)
|
||||
{
|
||||
if (it->second->IsEmpty () || ts > it->second->GetExpirationTime ()) // leaseset expired
|
||||
{
|
||||
LogPrint (eLogWarning, "Destination: Remote LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired");
|
||||
it = m_RemoteLeaseSets.erase (it);
|
||||
}
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
void ClientDestination::PersistTemporaryKeys ()
|
||||
{
|
||||
@@ -779,6 +839,14 @@ namespace client
|
||||
return;
|
||||
}
|
||||
LogPrint(eLogError, "Destinations: Can't save keys to ", path);
|
||||
}
|
||||
}
|
||||
|
||||
void ClientDestination::CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels)
|
||||
{
|
||||
auto leaseSet = new i2p::data::LocalLeaseSet (GetIdentity (), m_EncryptionPublicKey, tunnels);
|
||||
// sign
|
||||
Sign (leaseSet->GetBuffer (), leaseSet->GetBufferLen () - leaseSet->GetSignatureLen (), leaseSet->GetSignature ()); // TODO
|
||||
SetLeaseSet (leaseSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
114
Destination.h
114
Destination.h
@@ -49,8 +49,8 @@ namespace client
|
||||
|
||||
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
|
||||
|
||||
class ClientDestination: public i2p::garlic::GarlicDestination,
|
||||
public std::enable_shared_from_this<ClientDestination>
|
||||
class LeaseSetDestination: public i2p::garlic::GarlicDestination,
|
||||
public std::enable_shared_from_this<LeaseSetDestination>
|
||||
{
|
||||
typedef std::function<void (std::shared_ptr<i2p::data::LeaseSet> leaseSet)> RequestComplete;
|
||||
// leaseSet = nullptr means not found
|
||||
@@ -68,40 +68,21 @@ namespace client
|
||||
|
||||
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->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);
|
||||
|
||||
// streaming
|
||||
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);
|
||||
void StopAcceptingStreams ();
|
||||
bool IsAcceptingStreams () const;
|
||||
|
||||
// datagram
|
||||
i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; };
|
||||
i2p::datagram::DatagramDestination * CreateDatagramDestination ();
|
||||
void CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify = true);
|
||||
|
||||
// implements LocalDestination
|
||||
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
|
||||
const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; };
|
||||
const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionPublicKey; };
|
||||
|
||||
// implements GarlicDestination
|
||||
std::shared_ptr<const i2p::data::LeaseSet> GetLeaseSet ();
|
||||
std::shared_ptr<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);
|
||||
|
||||
@@ -111,9 +92,13 @@ namespace client
|
||||
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void SetLeaseSetUpdated ();
|
||||
|
||||
// I2CP
|
||||
void HandleDataMessage (const uint8_t * buf, size_t len);
|
||||
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 ();
|
||||
@@ -129,29 +114,22 @@ namespace client
|
||||
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 ();
|
||||
void PersistTemporaryKeys ();
|
||||
|
||||
void CleanupRemoteLeaseSets ();
|
||||
|
||||
private:
|
||||
|
||||
volatile bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_service::work m_Work;
|
||||
i2p::data::PrivateKeys m_Keys;
|
||||
uint8_t m_EncryptionPublicKey[256], m_EncryptionPrivateKey[256];
|
||||
std::map<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets;
|
||||
std::map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests;
|
||||
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> m_Pool;
|
||||
std::shared_ptr<i2p::data::LeaseSet> m_LeaseSet;
|
||||
std::shared_ptr<i2p::data::LocalLeaseSet> m_LeaseSet;
|
||||
bool m_IsPublic;
|
||||
uint32_t m_PublishReplyToken;
|
||||
std::set<i2p::data::IdentHash> m_ExcludedFloodfills; // for publishing
|
||||
|
||||
std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default
|
||||
std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > m_StreamingDestinationsByPorts;
|
||||
i2p::datagram::DatagramDestination * m_DatagramDestination;
|
||||
|
||||
boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer, m_CleanupTimer;
|
||||
|
||||
@@ -160,6 +138,64 @@ namespace client
|
||||
// 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
|
||||
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);
|
||||
void StopAcceptingStreams ();
|
||||
bool IsAcceptingStreams () const;
|
||||
|
||||
// datagram
|
||||
i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; };
|
||||
i2p::datagram::DatagramDestination * CreateDatagramDestination ();
|
||||
|
||||
// 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);
|
||||
void CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
|
||||
|
||||
private:
|
||||
|
||||
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::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;
|
||||
|
||||
public:
|
||||
|
||||
// for HTTP only
|
||||
std::vector<std::shared_ptr<const i2p::stream::Stream> > GetAllStreams () const;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
19
FS.cpp
19
FS.cpp
@@ -9,7 +9,7 @@
|
||||
#include <algorithm>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#ifdef WIN32
|
||||
#ifdef _WIN32
|
||||
#include <shlobj.h>
|
||||
#endif
|
||||
|
||||
@@ -55,6 +55,14 @@ namespace fs {
|
||||
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;
|
||||
@@ -102,13 +110,20 @@ namespace fs {
|
||||
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_directory(root);
|
||||
boost::filesystem::create_directories(root);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
|
||||
17
FS.h
17
FS.h
@@ -48,8 +48,8 @@ namespace fs {
|
||||
|
||||
/** create subdirs in storage */
|
||||
bool Init(const char* chars, size_t cnt);
|
||||
const std::string & GetRoot() const { return this->root; }
|
||||
const std::string & GetName() const { return this->name; }
|
||||
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 */
|
||||
@@ -108,6 +108,8 @@ namespace fs {
|
||||
* @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) {
|
||||
@@ -136,6 +138,17 @@ namespace fs {
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#include <string.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include "Crypto.h"
|
||||
#include "FS.h"
|
||||
#include "Log.h"
|
||||
#include "Crypto.h"
|
||||
#include "Family.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -94,7 +94,7 @@ namespace data
|
||||
int numCertificates = 0;
|
||||
|
||||
if (!i2p::fs::ReadDir(certDir, files)) {
|
||||
LogPrint(eLogWarning, "Reseed: Can't load reseed certificates from ", certDir);
|
||||
LogPrint(eLogWarning, "Family: Can't load family certificates from ", certDir);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
115
Garlic.cpp
115
Garlic.cpp
@@ -2,12 +2,12 @@
|
||||
#include "I2PEndian.h"
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/sha.h>
|
||||
#include "Crypto.h"
|
||||
#include "RouterContext.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Tunnel.h"
|
||||
#include "TunnelPool.h"
|
||||
#include "Transports.h"
|
||||
#include "Timestamp.h"
|
||||
#include "Log.h"
|
||||
#include "Garlic.h"
|
||||
@@ -38,9 +38,6 @@ namespace garlic
|
||||
|
||||
GarlicRoutingSession::~GarlicRoutingSession ()
|
||||
{
|
||||
for (auto it: m_UnconfirmedTagsMsgs)
|
||||
delete it.second;
|
||||
m_UnconfirmedTagsMsgs.clear ();
|
||||
}
|
||||
|
||||
std::shared_ptr<GarlicRoutingPath> GarlicRoutingSession::GetSharedRoutingPath ()
|
||||
@@ -94,18 +91,27 @@ namespace garlic
|
||||
|
||||
void GarlicRoutingSession::TagsConfirmed (uint32_t msgID)
|
||||
{
|
||||
auto it = m_UnconfirmedTagsMsgs.find (msgID);
|
||||
if (it != m_UnconfirmedTagsMsgs.end ())
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();)
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
UnconfirmedTags * tags = it->second;
|
||||
if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
|
||||
{
|
||||
for (int i = 0; i < tags->numTags; i++)
|
||||
m_SessionTags.push_back (tags->sessionTags[i]);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,23 +123,31 @@ 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_CONFIRMATION_TIMEOUT)
|
||||
if (ts >= (*it)->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
|
||||
{
|
||||
if (m_Owner)
|
||||
m_Owner->RemoveDeliveryStatusSession (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;
|
||||
}
|
||||
|
||||
std::shared_ptr<I2NPMessage> GarlicRoutingSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
|
||||
{
|
||||
@@ -258,7 +272,10 @@ namespace garlic
|
||||
size += cloveSize;
|
||||
(*numCloves)++;
|
||||
if (newTags) // new tags created
|
||||
m_UnconfirmedTagsMsgs[msgID] = newTags;
|
||||
{
|
||||
newTags->msgID = msgID;
|
||||
m_UnconfirmedTagsMsgs.emplace_back (newTags);
|
||||
}
|
||||
m_Owner->DeliveryStatusSent (shared_from_this (), msgID);
|
||||
}
|
||||
else
|
||||
@@ -515,22 +532,34 @@ namespace garlic
|
||||
buf += 32;
|
||||
uint32_t gwTunnel = bufbe32toh (buf);
|
||||
buf += 4;
|
||||
std::shared_ptr<i2p::tunnel::OutboundTunnel> tunnel;
|
||||
if (from && from->GetTunnelPool ())
|
||||
tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel ();
|
||||
if (tunnel) // we have send it through an outbound tunnel
|
||||
{
|
||||
auto msg = CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from);
|
||||
tunnel->SendTunnelDataMsg (gwHash, gwTunnel, msg);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Garlic: 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 (eLogWarning, "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 (eLogWarning, "Garlic: unknown delivery type ", (int)deliveryType);
|
||||
}
|
||||
@@ -586,7 +615,7 @@ namespace garlic
|
||||
it = m_Tags.erase (it);
|
||||
}
|
||||
else
|
||||
it++;
|
||||
++it;
|
||||
}
|
||||
if (numExpiredTags > 0)
|
||||
LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " tags expired for ", GetIdentHash().ToBase64 ());
|
||||
@@ -602,10 +631,10 @@ namespace garlic
|
||||
it = m_Sessions.erase (it);
|
||||
}
|
||||
else
|
||||
it++;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID)
|
||||
{
|
||||
m_DeliveryStatusSessions.erase (msgID);
|
||||
@@ -614,26 +643,26 @@ namespace garlic
|
||||
void GarlicDestination::DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID)
|
||||
{
|
||||
m_DeliveryStatusSessions[msgID] = session;
|
||||
}
|
||||
}
|
||||
|
||||
void GarlicDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
uint32_t msgID = bufbe32toh (msg->GetPayload ());
|
||||
{
|
||||
auto it = m_DeliveryStatusSessions.find (msgID);
|
||||
if (it != m_DeliveryStatusSessions.end ())
|
||||
if (it != m_DeliveryStatusSessions.end ())
|
||||
{
|
||||
it->second->MessageConfirmed (msgID);
|
||||
m_DeliveryStatusSessions.erase (it);
|
||||
LogPrint (eLogDebug, "Garlic: message ", msgID, " acknowledged");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 ();
|
||||
}
|
||||
|
||||
|
||||
6
Garlic.h
6
Garlic.h
@@ -83,6 +83,7 @@ namespace garlic
|
||||
{
|
||||
UnconfirmedTags (int n): numTags (n), tagsCreationTime (0) { sessionTags = new SessionTag[numTags]; };
|
||||
~UnconfirmedTags () { delete[] sessionTags; };
|
||||
uint32_t msgID;
|
||||
int numTags;
|
||||
SessionTag * sessionTags;
|
||||
uint32_t tagsCreationTime;
|
||||
@@ -97,6 +98,7 @@ namespace garlic
|
||||
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 ()
|
||||
{
|
||||
@@ -123,7 +125,7 @@ namespace garlic
|
||||
i2p::crypto::AESKey m_SessionKey;
|
||||
std::list<SessionTag> m_SessionTags;
|
||||
int m_NumTags;
|
||||
std::map<uint32_t, UnconfirmedTags *> m_UnconfirmedTagsMsgs;
|
||||
std::list<std::unique_ptr<UnconfirmedTags> > m_UnconfirmedTagsMsgs;
|
||||
|
||||
LeaseSetUpdateStatus m_LeaseSetUpdateStatus;
|
||||
uint32_t m_LeaseSetUpdateMsgID;
|
||||
@@ -163,7 +165,7 @@ namespace garlic
|
||||
virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
virtual void SetLeaseSetUpdated ();
|
||||
|
||||
virtual std::shared_ptr<const i2p::data::LeaseSet> GetLeaseSet () = 0; // TODO
|
||||
virtual std::shared_ptr<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;
|
||||
|
||||
|
||||
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__ */
|
||||
481
HTTPProxy.cpp
481
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,70 +17,78 @@
|
||||
#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];
|
||||
uint8_t m_recv_chunk[8192];
|
||||
std::string m_recv_buf; // from client
|
||||
std::string m_send_buf; // to upstream
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> m_sock;
|
||||
std::string m_request; //Data left to be sent
|
||||
std::string m_url; //URL
|
||||
std::string m_method; //Method
|
||||
std::string m_version; //HTTP version
|
||||
std::string m_address; //Address
|
||||
std::string m_path; //Path
|
||||
int m_port; //Port
|
||||
state m_state;//Parsing state
|
||||
|
||||
public:
|
||||
|
||||
HTTPProxyHandler(HTTPProxyServer * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock) :
|
||||
I2PServiceHandler(parent), m_sock(sock)
|
||||
{ EnterState(GET_METHOD); }
|
||||
~HTTPProxyHandler() { Terminate(); }
|
||||
void Handle () { AsyncSockRead(); }
|
||||
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, "HTTPProxy: 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 {
|
||||
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)
|
||||
{
|
||||
@@ -86,181 +99,192 @@ namespace proxy
|
||||
Done(shared_from_this());
|
||||
}
|
||||
|
||||
/* All hope is lost beyond this point */
|
||||
//TODO: handle this apropriately
|
||||
void HTTPProxyHandler::HTTPRequestFailed(/*HTTPProxyHandler::errTypes error*/)
|
||||
{
|
||||
static std::string response = "HTTP/1.0 500 Internal Server Error\r\nContent-type: text/html\r\nContent-length: 0\r\n";
|
||||
boost::asio::async_write(*m_sock, boost::asio::buffer(response,response.size()),
|
||||
std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
|
||||
void 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, "HTTPProxy: request: ", m_method, " ", 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, "HTTPProxy: server: ", server, ", port: ", port, ", path: ", 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()
|
||||
void HTTPReqHandler::SendProxyError(std::string & content)
|
||||
{
|
||||
if ( m_version != "HTTP/1.0" && m_version != "HTTP/1.1" )
|
||||
{
|
||||
LogPrint(eLogError, "HTTPProxy: unsupported version: ", m_version);
|
||||
HTTPRequestFailed(); //TODO: send right stuff
|
||||
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()
|
||||
void HTTPReqHandler::SanitizeHTTPRequest(i2p::http::HTTPReq & req)
|
||||
{
|
||||
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 (eLogInfo, "HTTPProxy: jump service for ", m_address, ", 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");
|
||||
// TODO: temporary shortcut. Must be implemented properly
|
||||
uint8_t * eol = nullptr;
|
||||
bool isEndOfHeader = false;
|
||||
while (!isEndOfHeader && len && (eol = (uint8_t *)memchr (http_buff, '\r', len)))
|
||||
{
|
||||
if (eol)
|
||||
{
|
||||
*eol = 0; eol++;
|
||||
if (strncmp ((const char *)http_buff, "Referer", 7)) // strip out referer
|
||||
{
|
||||
if (!strncmp ((const char *)http_buff, "User-Agent", 10)) // replace UserAgent
|
||||
m_request.append("User-Agent: MYOB/6.66 (AN/ON)");
|
||||
else
|
||||
m_request.append ((const char *)http_buff);
|
||||
m_request.append ("\r\n");
|
||||
}
|
||||
isEndOfHeader = !http_buff[0];
|
||||
auto l = eol - http_buff;
|
||||
http_buff = eol;
|
||||
len -= l;
|
||||
if (len > 0) // \r
|
||||
{
|
||||
http_buff++;
|
||||
len--;
|
||||
}
|
||||
/* 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 */
|
||||
}
|
||||
}
|
||||
m_request.append(reinterpret_cast<const char *>(http_buff),len);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HTTPProxyHandler::HandleData(uint8_t *http_buff, std::size_t len)
|
||||
{
|
||||
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, "HTTPProxy: rejected invalid request ending with: ", ((int)*http_buff));
|
||||
HTTPRequestFailed(); //TODO: add correct code
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LogPrint(eLogError, "HTTPProxy: invalid state: ", m_state);
|
||||
HTTPRequestFailed(); //TODO: add correct code 500
|
||||
return false;
|
||||
}
|
||||
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, "HTTPProxy: sock recv: ", len, " bytes");
|
||||
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);
|
||||
@@ -268,54 +292,45 @@ namespace proxy
|
||||
return;
|
||||
}
|
||||
|
||||
if (HandleData(m_http_buff, len))
|
||||
{
|
||||
if (m_state == DONE)
|
||||
{
|
||||
LogPrint(eLogDebug, "HTTPProxy: 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)
|
||||
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, "HTTPProxy: 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
|
||||
{
|
||||
if (!stream) {
|
||||
LogPrint (eLogError, "HTTPProxy: error when creating the stream, check the previous warnings for more info");
|
||||
HTTPRequestFailed(); // TODO: Send correct error message host unreachable
|
||||
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(const std::string& address, int port, std::shared_ptr<i2p::client::ClientDestination> localDestination):
|
||||
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(std::shared_ptr<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
|
||||
|
||||
25
HTTPProxy.h
25
HTTPProxy.h
@@ -1,32 +1,21 @@
|
||||
#ifndef HTTP_PROXY_H__
|
||||
#define HTTP_PROXY_H__
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <boost/asio.hpp>
|
||||
#include <mutex>
|
||||
#include "I2PService.h"
|
||||
#include "Destination.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace proxy
|
||||
{
|
||||
class HTTPProxyServer: public i2p::client::TCPIPAcceptor
|
||||
namespace i2p {
|
||||
namespace proxy {
|
||||
class HTTPProxy: public i2p::client::TCPIPAcceptor
|
||||
{
|
||||
public:
|
||||
|
||||
HTTPProxyServer(const std::string& address, int port, std::shared_ptr<i2p::client::ClientDestination> localDestination = nullptr);
|
||||
~HTTPProxyServer() {};
|
||||
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(std::shared_ptr<boost::asio::ip::tcp::socket> socket);
|
||||
const char* GetName() { return "HTTP Proxy"; }
|
||||
};
|
||||
|
||||
typedef HTTPProxyServer HTTPProxy;
|
||||
}
|
||||
}
|
||||
} // http
|
||||
} // i2p
|
||||
|
||||
#endif
|
||||
|
||||
1223
HTTPServer.cpp
1223
HTTPServer.cpp
File diff suppressed because it is too large
Load Diff
117
HTTPServer.h
117
HTTPServer.h
@@ -1,106 +1,39 @@
|
||||
#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: 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 status_string, content;
|
||||
std::vector<boost::asio::const_buffer> to_buffers (int status);
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
HTTPConnection (std::shared_ptr<boost::asio::ip::tcp::socket> socket):
|
||||
m_Socket (socket), m_Timer (socket->get_io_service ()),
|
||||
m_Stream (nullptr), m_BufferLen (0) {};
|
||||
HTTPConnection (std::shared_ptr<boost::asio::ip::tcp::socket> socket);
|
||||
void Receive ();
|
||||
|
||||
private:
|
||||
|
||||
void Terminate ();
|
||||
void HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void AsyncStreamReceive ();
|
||||
void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleWriteReply(const boost::system::error_code& ecode);
|
||||
void HandleWrite (const boost::system::error_code& ecode);
|
||||
void SendReply (const std::string& content, int status = 200);
|
||||
void Terminate (const boost::system::error_code& ecode);
|
||||
|
||||
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 ShowI2PTunnels (std::stringstream& s);
|
||||
void StartAcceptingTunnels (std::stringstream& s);
|
||||
void StopAcceptingTunnels (std::stringstream& s);
|
||||
void RunPeerTest (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:
|
||||
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:
|
||||
|
||||
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
|
||||
@@ -108,7 +41,7 @@ namespace util
|
||||
public:
|
||||
|
||||
HTTPServer (const std::string& address, int port);
|
||||
virtual ~HTTPServer ();
|
||||
~HTTPServer ();
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
@@ -119,6 +52,7 @@ namespace util
|
||||
void Accept ();
|
||||
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:
|
||||
|
||||
@@ -126,13 +60,8 @@ namespace util
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_service::work m_Work;
|
||||
boost::asio::ip::tcp::acceptor m_Acceptor;
|
||||
|
||||
protected:
|
||||
virtual void CreateConnection(std::shared_ptr<boost::asio::ip::tcp::socket> 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
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#include <string.h>
|
||||
#include <atomic>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/sha.h>
|
||||
#include "Base.h"
|
||||
#include "Log.h"
|
||||
#include "Crypto.h"
|
||||
@@ -13,6 +11,7 @@
|
||||
#include "Transports.h"
|
||||
#include "Garlic.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "version.h"
|
||||
|
||||
using namespace i2p::transport;
|
||||
|
||||
@@ -103,7 +102,7 @@ namespace i2p
|
||||
{
|
||||
RAND_bytes ((uint8_t *)&msgID, 4);
|
||||
htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID);
|
||||
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, 2); // netID = 2
|
||||
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, I2PD_NET_ID);
|
||||
}
|
||||
m->len += DELIVERY_STATUS_SIZE;
|
||||
m->FillI2NPMessageHeader (eI2NPDeliveryStatus);
|
||||
@@ -166,9 +165,10 @@ namespace i2p
|
||||
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
|
||||
htobe16buf (buf, cnt);
|
||||
@@ -183,7 +183,7 @@ 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;
|
||||
|
||||
@@ -202,7 +202,7 @@ namespace i2p
|
||||
len += 32;
|
||||
buf[len] = routers.size ();
|
||||
len++;
|
||||
for (auto it: routers)
|
||||
for (const auto& it: routers)
|
||||
{
|
||||
memcpy (buf + len, it, 32);
|
||||
len += 32;
|
||||
@@ -251,7 +251,23 @@ namespace i2p
|
||||
return m;
|
||||
}
|
||||
|
||||
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<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;
|
||||
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 ();
|
||||
@@ -260,14 +276,13 @@ namespace i2p
|
||||
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
|
||||
@@ -286,6 +301,16 @@ namespace i2p
|
||||
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++)
|
||||
@@ -298,7 +323,7 @@ namespace i2p
|
||||
i2p::crypto::ElGamalDecrypt (i2p::context.GetEncryptionPrivateKey (), record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText);
|
||||
// replace record to reply
|
||||
if (i2p::context.AcceptsTunnels () &&
|
||||
i2p::tunnel::tunnels.GetTransitTunnels ().size () <= MAX_NUM_TRANSIT_TUNNELS &&
|
||||
i2p::tunnel::tunnels.GetTransitTunnels ().size () <= g_MaxNumTransitTunnels &&
|
||||
!i2p::transport::transports.IsBandwidthExceeded ())
|
||||
{
|
||||
auto transitTunnel = i2p::tunnel::CreateTransitTunnel (
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <string.h>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include <openssl/sha.h>
|
||||
#include "Crypto.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "Identity.h"
|
||||
#include "RouterInfo.h"
|
||||
@@ -90,15 +90,13 @@ 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
|
||||
const uint8_t DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP = 0x08; // 1000
|
||||
const uint8_t DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP = 0x0C; // 1100
|
||||
|
||||
const unsigned int MAX_NUM_TRANSIT_TUNNELS = 2500;
|
||||
|
||||
namespace tunnel
|
||||
{
|
||||
class InboundTunnel;
|
||||
@@ -226,7 +224,8 @@ namespace tunnel
|
||||
std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers);
|
||||
|
||||
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, 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);
|
||||
@@ -259,6 +258,9 @@ namespace tunnel
|
||||
|
||||
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
|
||||
|
||||
191
I2PControl.cpp
191
I2PControl.cpp
@@ -2,8 +2,6 @@
|
||||
#include <sstream>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/date_time/local_time/local_time.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/property_tree/ini_parser.hpp>
|
||||
|
||||
@@ -13,8 +11,10 @@
|
||||
#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"
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "Timestamp.h"
|
||||
#include "Transports.h"
|
||||
#include "version.h"
|
||||
#include "util.h"
|
||||
#include "I2PControl.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -83,6 +84,10 @@ namespace client
|
||||
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 ()
|
||||
@@ -184,71 +189,64 @@ namespace client
|
||||
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;
|
||||
size_t contentLength = 0;
|
||||
while (!ss.eof () && header != "\r")
|
||||
{
|
||||
std::getline(ss, header);
|
||||
auto colon = header.find (':');
|
||||
if (colon != std::string::npos && header.substr (0, colon) == "Content-Length")
|
||||
contentLength = std::stoi (header.substr (colon + 1));
|
||||
}
|
||||
if (ss.eof ())
|
||||
{
|
||||
LogPrint (eLogError, "I2PControl: malformed request, HTTP header expected");
|
||||
return; // TODO:
|
||||
}
|
||||
std::streamoff rem = contentLength + ss.tellg () - bytes_transferred; // more bytes to read
|
||||
if (rem > 0)
|
||||
{
|
||||
bytes_transferred = boost::asio::read (*socket, boost::asio::buffer (buf->data (), rem));
|
||||
ss.write (buf->data (), bytes_transferred);
|
||||
}
|
||||
}
|
||||
std::ostringstream response;
|
||||
#if GCC47_BOOST149
|
||||
LogPrint (eLogError, "I2PControl: json_read is not supported due bug in boost 1.49 with gcc 4.7");
|
||||
response << "{\"id\":null,\"error\":";
|
||||
response << "{\"code\":-32603,\"message\":\"JSON requests is not supported with this version of boost\"},";
|
||||
response << "\"jsonrpc\":\"2.0\"}";
|
||||
#else
|
||||
boost::property_tree::ptree pt;
|
||||
boost::property_tree::read_json (ss, pt);
|
||||
|
||||
std::string id = pt.get<std::string>("id");
|
||||
std::string method = pt.get<std::string>("method");
|
||||
auto it = m_MethodHandlers.find (method);
|
||||
if (it != m_MethodHandlers.end ())
|
||||
{
|
||||
response << "{\"id\":" << id << ",\"result\":{";
|
||||
(this->*(it->second))(pt.get_child ("params"), response);
|
||||
response << "},\"jsonrpc\":\"2.0\"}";
|
||||
} else {
|
||||
LogPrint (eLogWarning, "I2PControl: unknown method ", method);
|
||||
response << "{\"id\":null,\"error\":";
|
||||
response << "{\"code\":-32601,\"message\":\"Method not found\"},";
|
||||
response << "\"jsonrpc\":\"2.0\"}";
|
||||
}
|
||||
#endif
|
||||
SendResponse (socket, buf, response, isHtml);
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "I2PControl: exception when handle request: ", ex.what ());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LogPrint (eLogError, "I2PControl: handle request unknown exception");
|
||||
}
|
||||
}
|
||||
/* 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, "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
|
||||
/* now try to parse json itself */
|
||||
try {
|
||||
boost::property_tree::ptree pt;
|
||||
boost::property_tree::read_json (json, pt);
|
||||
|
||||
std::string id = pt.get<std::string>("id");
|
||||
std::string method = pt.get<std::string>("method");
|
||||
auto it = m_MethodHandlers.find (method);
|
||||
if (it != m_MethodHandlers.end ()) {
|
||||
std::ostringstream ss;
|
||||
ss << "{\"id\":" << id << ",\"result\":{";
|
||||
(this->*(it->second))(pt.get_child ("params"), ss);
|
||||
ss << "},\"jsonrpc\":\"2.0\"}";
|
||||
response = ss.str();
|
||||
} else {
|
||||
LogPrint (eLogWarning, "I2PControl: unknown method ", method);
|
||||
BuildErrorResponse(response, 32601, "Method not found");
|
||||
}
|
||||
} catch (std::exception& ex) {
|
||||
LogPrint (eLogError, "I2PControl: exception when handle request: ", ex.what ());
|
||||
BuildErrorResponse(response, 32603, ex.what());
|
||||
} catch (...) {
|
||||
LogPrint (eLogError, "I2PControl: handle request unknown exception");
|
||||
}
|
||||
#endif
|
||||
SendResponse (socket, buf, response, isHTTP);
|
||||
}
|
||||
|
||||
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, int value) const
|
||||
@@ -270,27 +268,28 @@ namespace client
|
||||
ss << "\"" << name << "\":" << std::fixed << std::setprecision(2) << value;
|
||||
}
|
||||
|
||||
void I2PControlService::BuildErrorResponse (std::string & content, int code, const char *message) {
|
||||
std::stringstream ss;
|
||||
ss << "{\"id\":null,\"error\":";
|
||||
ss << "{\"code\":" << -code << ",\"message\":\"" << message << "\"},";
|
||||
ss << "\"jsonrpc\":\"2.0\"}";
|
||||
content = ss.str();
|
||||
}
|
||||
|
||||
void I2PControlService::SendResponse (std::shared_ptr<ssl_socket> socket,
|
||||
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml)
|
||||
std::shared_ptr<I2PControlBuffer> buf, std::string& content, bool isHTTP)
|
||||
{
|
||||
size_t len = response.str ().length (), offset = 0;
|
||||
if (isHtml)
|
||||
{
|
||||
std::ostringstream header;
|
||||
header << "HTTP/1.1 200 OK\r\n";
|
||||
header << "Connection: close\r\n";
|
||||
header << "Content-Length: " << boost::lexical_cast<std::string>(len) << "\r\n";
|
||||
header << "Content-Type: application/json\r\n";
|
||||
header << "Date: ";
|
||||
auto facet = new boost::local_time::local_time_facet ("%a, %d %b %Y %H:%M:%S GMT");
|
||||
header.imbue(std::locale (header.getloc(), facet));
|
||||
header << boost::posix_time::second_clock::local_time() << "\r\n";
|
||||
header << "\r\n";
|
||||
offset = header.str ().size ();
|
||||
memcpy (buf->data (), header.str ().c_str (), offset);
|
||||
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;
|
||||
}
|
||||
memcpy (buf->data () + offset, response.str ().c_str (), len);
|
||||
boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), offset + len),
|
||||
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::placeholders::_1, std::placeholders::_2, socket, buf));
|
||||
@@ -317,7 +316,7 @@ namespace client
|
||||
}
|
||||
InsertParam (results, "API", api);
|
||||
results << ",";
|
||||
std::string token = boost::lexical_cast<std::string>(i2p::util::GetSecondsSinceEpoch ());
|
||||
std::string token = std::to_string(i2p::util::GetSecondsSinceEpoch ());
|
||||
m_Tokens.insert (token);
|
||||
InsertParam (results, "Token", token);
|
||||
}
|
||||
@@ -359,7 +358,7 @@ namespace client
|
||||
|
||||
void I2PControlService::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
||||
{
|
||||
for (auto it = params.begin (); it != params.end (); it++)
|
||||
for (auto it = params.begin (); it != params.end (); ++it)
|
||||
{
|
||||
LogPrint (eLogDebug, "I2PControl: RouterInfo request: ", it->first);
|
||||
auto it1 = m_RouterInfoHandlers.find (it->first);
|
||||
@@ -435,7 +434,7 @@ namespace client
|
||||
|
||||
void I2PControlService::RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
||||
{
|
||||
for (auto it = params.begin (); it != params.end (); it++)
|
||||
for (auto it = params.begin (); it != params.end (); ++it)
|
||||
{
|
||||
if (it != params.begin ()) results << ",";
|
||||
LogPrint (eLogDebug, "I2PControl: RouterManager request: ", it->first);
|
||||
@@ -484,7 +483,7 @@ namespace client
|
||||
// network setting
|
||||
void I2PControlService::NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
||||
{
|
||||
for (auto it = params.begin (); it != params.end (); it++)
|
||||
for (auto it = params.begin (); it != params.end (); ++it)
|
||||
{
|
||||
if (it != params.begin ()) results << ",";
|
||||
LogPrint (eLogDebug, "I2PControl: NetworkSetting request: ", it->first);
|
||||
@@ -496,6 +495,22 @@ namespace client
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
|
||||
namespace i2p
|
||||
@@ -45,8 +45,9 @@ namespace client
|
||||
void ReadRequest (std::shared_ptr<ssl_socket> socket);
|
||||
void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred,
|
||||
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);
|
||||
void BuildErrorResponse (std::string & content, int code, const char *message);
|
||||
void SendResponse (std::shared_ptr<ssl_socket> socket,
|
||||
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml);
|
||||
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<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);
|
||||
|
||||
@@ -94,6 +95,8 @@ namespace client
|
||||
|
||||
// NetworkSetting
|
||||
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:
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "ClientContext.h"
|
||||
#include "I2PService.h"
|
||||
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace client
|
||||
@@ -28,7 +27,7 @@ namespace client
|
||||
m_LocalDestination->CreateStream (streamRequestComplete, identHash, port);
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "I2PService: Remote destination ", dest, " not found");
|
||||
LogPrint (eLogWarning, "I2PService: Remote destination not found: ", dest);
|
||||
streamRequestComplete (nullptr);
|
||||
}
|
||||
}
|
||||
@@ -71,7 +70,7 @@ namespace client
|
||||
std::bind(&TCPIPPipe::HandleUpstreamReceived, shared_from_this(),
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
} else {
|
||||
LogPrint(eLogError, "TCPIPPipe: no upstream socket for read");
|
||||
LogPrint(eLogError, "TCPIPPipe: upstream receive: no socket");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,14 +81,14 @@ namespace client
|
||||
std::bind(&TCPIPPipe::HandleDownstreamReceived, shared_from_this(),
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
} else {
|
||||
LogPrint(eLogError, "TCPIPPipe: no downstream socket for read");
|
||||
LogPrint(eLogError, "TCPIPPipe: downstream receive: no socket");
|
||||
}
|
||||
}
|
||||
|
||||
void TCPIPPipe::UpstreamWrite(const uint8_t * buf, size_t len)
|
||||
{
|
||||
if (m_up) {
|
||||
LogPrint(eLogDebug, "TCPIPPipe: write upstream ", (int)len);
|
||||
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,
|
||||
@@ -97,14 +96,14 @@ namespace client
|
||||
std::placeholders::_1)
|
||||
);
|
||||
} else {
|
||||
LogPrint(eLogError, "tcpip pipe upstream socket null");
|
||||
LogPrint(eLogError, "TCPIPPipe: upstream write: no socket");
|
||||
}
|
||||
}
|
||||
|
||||
void TCPIPPipe::DownstreamWrite(const uint8_t * buf, size_t len)
|
||||
{
|
||||
if (m_down) {
|
||||
LogPrint(eLogDebug, "TCPIPPipe: write downstream ", (int)len);
|
||||
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,
|
||||
@@ -112,16 +111,16 @@ namespace client
|
||||
std::placeholders::_1)
|
||||
);
|
||||
} else {
|
||||
LogPrint(eLogError, "tcpip pipe downstream socket null");
|
||||
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 got ", (int) bytes_transfered);
|
||||
LogPrint(eLogDebug, "TCPIPPipe: downstream: ", (int) bytes_transfered, " bytes received");
|
||||
if (ecode) {
|
||||
LogPrint(eLogError, "TCPIPPipe Downstream read error:" , ecode.message());
|
||||
LogPrint(eLogError, "TCPIPPipe: downstream read error:" , ecode.message());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate();
|
||||
} else {
|
||||
@@ -135,7 +134,7 @@ namespace client
|
||||
|
||||
void TCPIPPipe::HandleDownstreamWrite(const boost::system::error_code & ecode) {
|
||||
if (ecode) {
|
||||
LogPrint(eLogError, "TCPIPPipe Downstream write error:" , ecode.message());
|
||||
LogPrint(eLogError, "TCPIPPipe: downstream write error:" , ecode.message());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate();
|
||||
}
|
||||
@@ -143,7 +142,7 @@ namespace client
|
||||
|
||||
void TCPIPPipe::HandleUpstreamWrite(const boost::system::error_code & ecode) {
|
||||
if (ecode) {
|
||||
LogPrint(eLogError, "TCPIPPipe Upstream write error:" , ecode.message());
|
||||
LogPrint(eLogError, "TCPIPPipe: upstream write error:" , ecode.message());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate();
|
||||
}
|
||||
@@ -151,9 +150,9 @@ namespace client
|
||||
|
||||
void TCPIPPipe::HandleUpstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered)
|
||||
{
|
||||
LogPrint(eLogDebug, "TCPIPPipe upstream got ", (int) bytes_transfered);
|
||||
LogPrint(eLogDebug, "TCPIPPipe: upstream ", (int)bytes_transfered, " bytes received");
|
||||
if (ecode) {
|
||||
LogPrint(eLogError, "TCPIPPipe Upstream read error:" , ecode.message());
|
||||
LogPrint(eLogError, "TCPIPPipe: upstream read error:" , ecode.message());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate();
|
||||
} else {
|
||||
@@ -206,6 +205,5 @@ namespace client
|
||||
LogPrint (eLogError, "I2PService: ", GetName(), " closing socket on accept because: ", ecode.message ());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +118,9 @@ namespace client
|
||||
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(std::shared_ptr<boost::asio::ip::tcp::socket> socket) = 0;
|
||||
virtual const char* GetName() { return "Generic TCP/IP accepting daemon"; }
|
||||
|
||||
46
Identity.cpp
46
Identity.cpp
@@ -1,8 +1,5 @@
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/dh.h>
|
||||
#include <openssl/rand.h>
|
||||
#include "Crypto.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "Log.h"
|
||||
@@ -244,21 +241,20 @@ namespace data
|
||||
size_t IdentityEx::FromBase64(const std::string& s)
|
||||
{
|
||||
const size_t slen = s.length();
|
||||
uint8_t buf[slen]; // binary data can't exceed base64
|
||||
const size_t len = Base64ToByteStream (s.c_str(), slen, buf, slen);
|
||||
return FromBuffer (buf, len);
|
||||
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
|
||||
{
|
||||
const size_t bufLen = GetFullLen();
|
||||
const size_t strLen = Base64EncodingBufferSize(bufLen);
|
||||
uint8_t buf[bufLen];
|
||||
char str[strLen];
|
||||
size_t l = ToBuffer (buf, bufLen);
|
||||
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, str, strLen);
|
||||
str[l1] = 0;
|
||||
return std::string (str);
|
||||
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
|
||||
@@ -312,18 +308,18 @@ namespace data
|
||||
switch (keyType)
|
||||
{
|
||||
case SIGNING_KEY_TYPE_DSA_SHA1:
|
||||
m_Verifier.reset (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.reset (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.reset (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:
|
||||
@@ -332,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.reset (new i2p::crypto::ECDSAP521Verifier (signingKey));
|
||||
UpdateVerifier (new i2p::crypto::ECDSAP521Verifier (signingKey));
|
||||
break;
|
||||
}
|
||||
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
|
||||
@@ -341,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.reset (new i2p::crypto:: RSASHA2562048Verifier (signingKey));
|
||||
UpdateVerifier (new i2p::crypto:: RSASHA2562048Verifier (signingKey));
|
||||
break;
|
||||
}
|
||||
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
|
||||
@@ -350,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.reset (new i2p::crypto:: RSASHA3843072Verifier (signingKey));
|
||||
UpdateVerifier (new i2p::crypto:: RSASHA3843072Verifier (signingKey));
|
||||
break;
|
||||
}
|
||||
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
|
||||
@@ -359,20 +355,28 @@ 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.reset (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
|
||||
m_Verifier.reset (new i2p::crypto::EDDSA25519Verifier (m_StandardIdentity.signingKey + padding));
|
||||
UpdateVerifier (new i2p::crypto::EDDSA25519Verifier (m_StandardIdentity.signingKey + padding));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LogPrint (eLogError, "Identity: Signing key type ", (int)keyType, " is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void IdentityEx::UpdateVerifier (i2p::crypto::Verifier * verifier) const
|
||||
{
|
||||
if (!m_Verifier || !verifier)
|
||||
m_Verifier.reset (verifier);
|
||||
else
|
||||
delete verifier;
|
||||
}
|
||||
|
||||
void IdentityEx::DropVerifier () const
|
||||
{
|
||||
// TODO: potential race condition with Verify
|
||||
|
||||
@@ -95,6 +95,7 @@ namespace data
|
||||
private:
|
||||
|
||||
void CreateVerifier () const;
|
||||
void UpdateVerifier (i2p::crypto::Verifier * verifier) const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -177,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;
|
||||
|
||||
std::shared_ptr<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);
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
119
LeaseSet.cpp
119
LeaseSet.cpp
@@ -4,7 +4,7 @@
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
#include "NetDb.h"
|
||||
#include "TunnelPool.h"
|
||||
#include "Tunnel.h"
|
||||
#include "LeaseSet.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -21,56 +21,6 @@ namespace data
|
||||
ReadFromBuffer ();
|
||||
}
|
||||
|
||||
LeaseSet::LeaseSet (std::shared_ptr<const i2p::tunnel::TunnelPool> pool):
|
||||
m_IsValid (true), m_StoreLeases (true), m_ExpirationTime (0)
|
||||
{
|
||||
if (!pool) return;
|
||||
// header
|
||||
auto localDestination = pool->GetLocalDestination ();
|
||||
if (!localDestination)
|
||||
{
|
||||
m_Buffer = nullptr;
|
||||
m_BufferLen = 0;
|
||||
m_IsValid = false;
|
||||
LogPrint (eLogError, "LeaseSet: Destination for local LeaseSet doesn't exist");
|
||||
return;
|
||||
}
|
||||
m_Buffer = new uint8_t[MAX_LS_BUFFER_SIZE];
|
||||
m_BufferLen = localDestination->GetIdentity ()->ToBuffer (m_Buffer, MAX_LS_BUFFER_SIZE);
|
||||
memcpy (m_Buffer + m_BufferLen, localDestination->GetEncryptionPublicKey (), 256);
|
||||
m_BufferLen += 256;
|
||||
auto signingKeyLen = localDestination->GetIdentity ()->GetSigningPublicKeyLen ();
|
||||
memset (m_Buffer + m_BufferLen, 0, signingKeyLen);
|
||||
m_BufferLen += signingKeyLen;
|
||||
int numTunnels = pool->GetNumInboundTunnels () + 2; // 2 backup tunnels
|
||||
if (numTunnels > 16) numTunnels = 16; // 16 tunnels maximum
|
||||
auto tunnels = pool->GetInboundTunnels (numTunnels);
|
||||
m_Buffer[m_BufferLen] = tunnels.size (); // num leases
|
||||
m_BufferLen++;
|
||||
// leases
|
||||
auto currentTime = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
for (auto it: tunnels)
|
||||
{
|
||||
memcpy (m_Buffer + m_BufferLen, it->GetNextIdentHash (), 32);
|
||||
m_BufferLen += 32; // gateway id
|
||||
htobe32buf (m_Buffer + m_BufferLen, it->GetNextTunnelID ());
|
||||
m_BufferLen += 4; // tunnel id
|
||||
uint64_t ts = it->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // 1 minute before expiration
|
||||
ts *= 1000; // in milliseconds
|
||||
if (ts > m_ExpirationTime) m_ExpirationTime = ts;
|
||||
// make sure leaseset is newer than previous, but adding some time to expiration date
|
||||
ts += (currentTime - it->GetCreationTime ()*1000LL)*2/i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT; // up to 2 secs
|
||||
htobe64buf (m_Buffer + m_BufferLen, ts);
|
||||
m_BufferLen += 8; // end date
|
||||
}
|
||||
// signature
|
||||
localDestination->Sign (m_Buffer, m_BufferLen, m_Buffer + m_BufferLen);
|
||||
m_BufferLen += localDestination->GetIdentity ()->GetSignatureLen ();
|
||||
LogPrint (eLogDebug, "LeaseSet: Local LeaseSet of ", tunnels.size (), " leases created");
|
||||
|
||||
ReadFromBuffer ();
|
||||
}
|
||||
|
||||
void LeaseSet::Update (const uint8_t * buf, size_t len)
|
||||
{
|
||||
if (len > m_BufferLen)
|
||||
@@ -114,10 +64,10 @@ namespace data
|
||||
return;
|
||||
}
|
||||
|
||||
// reset existing leases
|
||||
// reset existing leases
|
||||
if (m_StoreLeases)
|
||||
for (auto it: m_Leases)
|
||||
it->isUpdated = false;
|
||||
for (auto& it: m_Leases)
|
||||
it->isUpdated = false;
|
||||
else
|
||||
m_Leases.clear ();
|
||||
|
||||
@@ -173,7 +123,7 @@ namespace data
|
||||
m_Leases.erase (it++);
|
||||
}
|
||||
else
|
||||
it++;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +145,7 @@ namespace data
|
||||
if (size > len) return 0;
|
||||
uint8_t num = buf[size];
|
||||
size++; // num
|
||||
if (size + num*44 > len) return 0;
|
||||
if (size + num*LEASE_SIZE > len) return 0;
|
||||
uint64_t timestamp= 0 ;
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
@@ -217,7 +167,7 @@ namespace data
|
||||
{
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
std::vector<std::shared_ptr<const Lease> > leases;
|
||||
for (auto it: m_Leases)
|
||||
for (const auto& it: m_Leases)
|
||||
{
|
||||
auto endDate = it->endDate;
|
||||
if (withThreshold)
|
||||
@@ -233,14 +183,65 @@ namespace data
|
||||
bool LeaseSet::HasExpiredLeases () const
|
||||
{
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
for (auto it: m_Leases)
|
||||
for (const auto& it: m_Leases)
|
||||
if (ts >= it->endDate) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LeaseSet::IsExpired () const
|
||||
{
|
||||
if (IsEmpty ()) return true;
|
||||
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 ();
|
||||
return ts > m_ExpirationTime;
|
||||
}
|
||||
|
||||
37
LeaseSet.h
37
LeaseSet.h
@@ -4,6 +4,7 @@
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include "Identity.h"
|
||||
|
||||
@@ -12,7 +13,7 @@ namespace i2p
|
||||
|
||||
namespace tunnel
|
||||
{
|
||||
class TunnelPool;
|
||||
class InboundTunnel;
|
||||
}
|
||||
|
||||
namespace data
|
||||
@@ -37,14 +38,14 @@ namespace data
|
||||
};
|
||||
};
|
||||
|
||||
const int MAX_LS_BUFFER_SIZE = 3072;
|
||||
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, size_t len, bool storeLeases = true);
|
||||
LeaseSet (std::shared_ptr<const i2p::tunnel::TunnelPool> pool);
|
||||
~LeaseSet () { delete[] m_Buffer; };
|
||||
void Update (const uint8_t * buf, size_t len);
|
||||
bool IsNewer (const uint8_t * buf, size_t len) const;
|
||||
@@ -82,6 +83,36 @@ namespace data
|
||||
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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
241
Log.cpp
241
Log.cpp
@@ -1,86 +1,171 @@
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
/*
|
||||
* 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"
|
||||
|
||||
Log * g_Log = nullptr;
|
||||
|
||||
static const char * g_LogLevelStr[eNumLogLevels] =
|
||||
{
|
||||
"error", // eLogError
|
||||
"warn", // eLogWarning
|
||||
"info", // eLogInfo
|
||||
"debug" // eLogDebug
|
||||
};
|
||||
|
||||
void LogMsg::Process()
|
||||
{
|
||||
auto stream = log ? log->GetLogStream () : nullptr;
|
||||
auto& output = stream ? *stream : std::cout;
|
||||
if (log)
|
||||
output << log->GetTimestamp ();
|
||||
else
|
||||
output << boost::posix_time::second_clock::local_time().time_of_day ();
|
||||
output << "/" << g_LogLevelStr[level] << " - ";
|
||||
output << s.str();
|
||||
}
|
||||
|
||||
const std::string& Log::GetTimestamp ()
|
||||
{
|
||||
#if (__GNUC__ == 4) && (__GNUC_MINOR__ <= 6) && !defined(__clang__)
|
||||
auto ts = std::chrono::monotonic_clock::now ();
|
||||
#else
|
||||
auto ts = std::chrono::steady_clock::now ();
|
||||
#endif
|
||||
if (ts > m_LastTimestampUpdate + std::chrono::milliseconds (500)) // 0.5 second
|
||||
namespace i2p {
|
||||
namespace log {
|
||||
Log logger;
|
||||
/**
|
||||
* @brief Maps our loglevel to their symbolic name
|
||||
*/
|
||||
static const char * g_LogLevelStr[eNumLogLevels] =
|
||||
{
|
||||
m_LastTimestampUpdate = ts;
|
||||
m_Timestamp = boost::posix_time::to_simple_string (boost::posix_time::second_clock::local_time().time_of_day ());
|
||||
}
|
||||
return m_Timestamp;
|
||||
}
|
||||
"error", // eLogError
|
||||
"warn", // eLogWarn
|
||||
"info", // eLogInfo
|
||||
"debug" // eLogDebug
|
||||
};
|
||||
|
||||
void Log::Flush ()
|
||||
{
|
||||
if (m_LogStream)
|
||||
m_LogStream->flush();
|
||||
}
|
||||
|
||||
void Log::SetLogFile (const std::string& fullFilePath, bool truncate)
|
||||
{
|
||||
m_FullFilePath = fullFilePath;
|
||||
auto mode = std::ofstream::out | std::ofstream::binary;
|
||||
mode |= truncate ? std::ofstream::trunc : std::ofstream::app;
|
||||
auto logFile = std::make_shared<std::ofstream> (fullFilePath, mode);
|
||||
if (logFile->is_open ())
|
||||
{
|
||||
SetLogStream (logFile);
|
||||
LogPrint(eLogInfo, "Log: will send messages to ", fullFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
void Log::ReopenLogFile ()
|
||||
{
|
||||
if (m_FullFilePath.length () > 0)
|
||||
{
|
||||
SetLogFile (m_FullFilePath, false); // don't truncate
|
||||
LogPrint(eLogInfo, "Log: file ", m_FullFilePath, " reopen");
|
||||
#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)
|
||||
{
|
||||
}
|
||||
|
||||
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 msg level set to ", level);
|
||||
}
|
||||
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::SetLogStream (std::shared_ptr<std::ostream> logStream)
|
||||
{
|
||||
m_LogStream = logStream;
|
||||
}
|
||||
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
|
||||
|
||||
251
Log.h
251
Log.h
@@ -1,15 +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,
|
||||
@@ -19,130 +31,157 @@ enum LogLevel
|
||||
eNumLogLevels
|
||||
};
|
||||
|
||||
class Log;
|
||||
struct LogMsg
|
||||
{
|
||||
std::stringstream s;
|
||||
Log * log;
|
||||
LogLevel level;
|
||||
|
||||
LogMsg (Log * l = nullptr, LogLevel lv = eLogInfo): log (l), level (lv) {};
|
||||
|
||||
void Process();
|
||||
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 () { SetOnEmpty (std::bind (&Log::Flush, this)); };
|
||||
~Log () {};
|
||||
|
||||
void SetLogFile (const std::string& fullFilePath, bool truncate = true);
|
||||
void ReopenLogFile ();
|
||||
void SetLogLevel (const std::string& level);
|
||||
void SetLogStream (std::shared_ptr<std::ostream> logStream);
|
||||
std::shared_ptr<std::ostream> GetLogStream () const { return m_LogStream; };
|
||||
const std::string& GetTimestamp ();
|
||||
LogLevel GetLogLevel () { return m_MinLevel; };
|
||||
const std::string& GetFullFilePath () const { return m_FullFilePath; };
|
||||
|
||||
private:
|
||||
|
||||
void Flush ();
|
||||
|
||||
private:
|
||||
|
||||
std::string m_FullFilePath; // empty if stream
|
||||
std::shared_ptr<std::ostream> m_LogStream;
|
||||
enum LogLevel m_MinLevel;
|
||||
std::string m_Timestamp;
|
||||
#if (__GNUC__ == 4) && (__GNUC_MINOR__ <= 6) && !defined(__clang__) // gcc 4.6
|
||||
std::chrono::monotonic_clock::time_point m_LastTimestampUpdate;
|
||||
#else
|
||||
std::chrono::steady_clock::time_point m_LastTimestampUpdate;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern Log * g_Log;
|
||||
|
||||
inline void StartLog (const std::string& fullFilePath)
|
||||
{
|
||||
if (!g_Log)
|
||||
{
|
||||
auto log = new Log ();
|
||||
if (fullFilePath.length () > 0)
|
||||
log->SetLogFile (fullFilePath);
|
||||
g_Log = log;
|
||||
}
|
||||
}
|
||||
|
||||
inline void StartLog (std::shared_ptr<std::ostream> s)
|
||||
{
|
||||
if (!g_Log)
|
||||
{
|
||||
auto log = new Log ();
|
||||
if (s)
|
||||
log->SetLogStream (s);
|
||||
g_Log = log;
|
||||
}
|
||||
}
|
||||
|
||||
inline void StopLog ()
|
||||
{
|
||||
if (g_Log)
|
||||
class Log
|
||||
{
|
||||
auto log = g_Log;
|
||||
g_Log = nullptr;
|
||||
log->Stop ();
|
||||
delete log;
|
||||
}
|
||||
}
|
||||
private:
|
||||
|
||||
inline void SetLogLevel (const std::string& level)
|
||||
{
|
||||
if (g_Log)
|
||||
g_Log->SetLogLevel(level);
|
||||
}
|
||||
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;
|
||||
|
||||
inline void ReopenLogFile ()
|
||||
{
|
||||
if (g_Log)
|
||||
g_Log->ReopenLogFile ();
|
||||
}
|
||||
private:
|
||||
|
||||
inline bool IsLogToFile ()
|
||||
{
|
||||
return g_Log ? !g_Log->GetFullFilePath ().empty () : false;
|
||||
}
|
||||
/** 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)
|
||||
{
|
||||
if (g_Log && level > g_Log->GetLogLevel ())
|
||||
i2p::log::Log &log = i2p::log::Logger();
|
||||
if (level > log.GetLogLevel ())
|
||||
return;
|
||||
LogMsg * msg = new LogMsg (g_Log, level);
|
||||
LogPrint (msg->s, args...);
|
||||
msg->s << std::endl;
|
||||
if (g_Log) {
|
||||
g_Log->Put (msg);
|
||||
} else {
|
||||
msg->Process ();
|
||||
delete msg;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif // LOG_H__
|
||||
|
||||
197
Makefile
197
Makefile
@@ -1,91 +1,106 @@
|
||||
UNAME := $(shell uname -s)
|
||||
SHLIB := libi2pd.so
|
||||
ARLIB := libi2pd.a
|
||||
SHLIB_CLIENT := libi2pdclient.so
|
||||
ARLIB_CLIENT := libi2pdclient.a
|
||||
I2PD := i2pd
|
||||
GREP := fgrep
|
||||
DEPS := obj/make.dep
|
||||
|
||||
include filelist.mk
|
||||
|
||||
USE_AESNI := yes
|
||||
USE_STATIC := no
|
||||
|
||||
ifeq ($(UNAME),Darwin)
|
||||
DAEMON_SRC += DaemonLinux.cpp
|
||||
include Makefile.osx
|
||||
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 mingw
|
||||
DAEMON_SRC += DaemonWin32.cpp Win32/Win32App.cpp
|
||||
include Makefile.mingw
|
||||
endif
|
||||
|
||||
all: mk_obj_dir $(ARLIB) $(ARLIB_CLIENT) $(I2PD)
|
||||
|
||||
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.
|
||||
## For example, when adding 'hardening flags' to the build
|
||||
## (e.g. -fstack-protector-strong -Wformat -Werror=format-security), we do not want to remove
|
||||
## -std=c++11. If you want to remove this variable please do so in a way that allows setting
|
||||
## custom FLAGS to work at build-time.
|
||||
|
||||
deps: mk_obj_dir
|
||||
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) -MM *.cpp > $(DEPS)
|
||||
@sed -i -e '/\.o:/ s/^/obj\//' $(DEPS)
|
||||
|
||||
obj/%.o: %.cpp
|
||||
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(CPU_FLAGS) -c -o $@ $<
|
||||
|
||||
# '-' is 'ignore if missing' on first run
|
||||
-include $(DEPS)
|
||||
|
||||
DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
|
||||
$(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT)
|
||||
$(CXX) -o $@ $^ $(LDLIBS) $(LDFLAGS)
|
||||
|
||||
$(SHLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
|
||||
ifneq ($(USE_STATIC),yes)
|
||||
$(CXX) $(LDFLAGS) $(LDLIBS) -shared -o $@ $^
|
||||
endif
|
||||
|
||||
$(SHLIB_CLIENT): $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
|
||||
$(CXX) $(LDFLAGS) $(LDLIBS) -shared -o $@ $^
|
||||
|
||||
$(ARLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
|
||||
ar -r $@ $^
|
||||
|
||||
$(ARLIB_CLIENT): $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
|
||||
ar -r $@ $^
|
||||
|
||||
clean:
|
||||
rm -rf obj
|
||||
$(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
|
||||
|
||||
strip: $(I2PD) $(SHLIB_CLIENT) $(SHLIB)
|
||||
strip $^
|
||||
|
||||
LATEST_TAG=$(shell git describe --tags --abbrev=0 master)
|
||||
dist:
|
||||
git archive --format=tar.gz -9 --worktree-attributes \
|
||||
--prefix=i2pd_$(LATEST_TAG)/ $(LATEST_TAG) -o i2pd_$(LATEST_TAG).tar.gz
|
||||
|
||||
.PHONY: all
|
||||
.PHONY: clean
|
||||
.PHONY: deps
|
||||
.PHONY: dist
|
||||
.PHONY: api
|
||||
.PHONY: api_client
|
||||
.PHONY: mk_obj_dir
|
||||
UNAME := $(shell uname -s)
|
||||
SHLIB := libi2pd.so
|
||||
ARLIB := libi2pd.a
|
||||
SHLIB_CLIENT := libi2pdclient.so
|
||||
ARLIB_CLIENT := libi2pdclient.a
|
||||
I2PD := i2pd
|
||||
GREP := fgrep
|
||||
DEPS := obj/make.dep
|
||||
|
||||
include filelist.mk
|
||||
|
||||
USE_AESNI := yes
|
||||
USE_STATIC := no
|
||||
USE_MESHNET := no
|
||||
USE_UPNP := no
|
||||
|
||||
ifeq ($(UNAME),Darwin)
|
||||
DAEMON_SRC += DaemonLinux.cpp
|
||||
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 mingw
|
||||
DAEMON_SRC += DaemonWin32.cpp Win32/Win32Service.cpp Win32/Win32App.cpp
|
||||
include Makefile.mingw
|
||||
endif
|
||||
|
||||
ifeq ($(USE_MESHNET),yes)
|
||||
NEEDED_CXXFLAGS += -DMESHNET
|
||||
endif
|
||||
|
||||
all: mk_obj_dir $(ARLIB) $(ARLIB_CLIENT) $(I2PD)
|
||||
|
||||
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.
|
||||
## For example, when adding 'hardening flags' to the build
|
||||
## (e.g. -fstack-protector-strong -Wformat -Werror=format-security), we do not want to remove
|
||||
## -std=c++11. If you want to remove this variable please do so in a way that allows setting
|
||||
## custom FLAGS to work at build-time.
|
||||
|
||||
deps: mk_obj_dir
|
||||
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) -MM *.cpp > $(DEPS)
|
||||
@sed -i -e '/\.o:/ s/^/obj\//' $(DEPS)
|
||||
|
||||
obj/%.o: %.cpp
|
||||
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(CPU_FLAGS) -c -o $@ $<
|
||||
|
||||
# '-' is 'ignore if missing' on first run
|
||||
-include $(DEPS)
|
||||
|
||||
DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
|
||||
$(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT)
|
||||
$(CXX) -o $@ $^ $(LDLIBS) $(LDFLAGS)
|
||||
|
||||
$(SHLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
|
||||
ifneq ($(USE_STATIC),yes)
|
||||
$(CXX) $(LDFLAGS) $(LDLIBS) -shared -o $@ $^
|
||||
endif
|
||||
|
||||
$(SHLIB_CLIENT): $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
|
||||
$(CXX) $(LDFLAGS) $(LDLIBS) -shared -o $@ $^
|
||||
|
||||
$(ARLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
|
||||
ar -r $@ $^
|
||||
|
||||
$(ARLIB_CLIENT): $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
|
||||
ar -r $@ $^
|
||||
|
||||
clean:
|
||||
rm -rf obj
|
||||
rm -rf docs/generated
|
||||
$(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
|
||||
|
||||
strip: $(I2PD) $(SHLIB_CLIENT) $(SHLIB)
|
||||
strip $^
|
||||
|
||||
LATEST_TAG=$(shell git describe --tags --abbrev=0 openssl)
|
||||
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: api_client
|
||||
.PHONY: mk_obj_dir
|
||||
|
||||
@@ -9,4 +9,4 @@ CXXFLAGS = -O2
|
||||
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 = -lcrypto -lssl -lz -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,5 @@
|
||||
# set defaults instead redefine
|
||||
CXXFLAGS ?= -g -Wall
|
||||
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
|
||||
@@ -19,7 +19,7 @@ else ifeq ($(shell expr match ${CXXVER} "4\.[7-9]"),3) # >= 4.7
|
||||
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 ${CXXVER} "5\.[0-9]"),3) # gcc >= 5.0
|
||||
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)
|
||||
@@ -32,20 +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)/libcrypto.a
|
||||
LDLIBS += $(LIBDIR)/libssl.a
|
||||
LDLIBS += $(LIBDIR)/libcrypto.a
|
||||
LDLIBS += $(LIBDIR)/libz.a
|
||||
LDLIBS += -lpthread -static-libstdc++ -static-libgcc
|
||||
LDLIBS += -lpthread -static-libstdc++ -static-libgcc -lrt
|
||||
USE_AESNI := no
|
||||
else
|
||||
LDLIBS = -lcrypto -lssl -lz -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
|
||||
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
USE_WIN32_APP=yes
|
||||
CXX = g++
|
||||
WINDRES = windres
|
||||
CXXFLAGS = -D_MT -DWIN32 -D_WINDOWS -DWIN32_LEAN_AND_MEAN
|
||||
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 = -mwindows -Wl,-rpath,/usr/local/lib \
|
||||
-L/usr/local/lib \
|
||||
-L/c/dev/openssl \
|
||||
-L/c/dev/boost/lib
|
||||
LDLIBS = \
|
||||
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_regex$(BOOST_SUFFIX) \
|
||||
-Wl,-Bstatic -lboost_program_options$(BOOST_SUFFIX) \
|
||||
-Wl,-Bstatic -lssl \
|
||||
-Wl,-Bstatic -lcrypto \
|
||||
@@ -24,8 +29,13 @@ LDLIBS = \
|
||||
-static-libgcc -static-libstdc++ \
|
||||
-Wl,-Bstatic -lstdc++ \
|
||||
-Wl,-Bstatic -lpthread
|
||||
DAEMON_RC += Win32/Resource.rc
|
||||
DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC))
|
||||
|
||||
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
|
||||
@@ -35,4 +45,3 @@ endif
|
||||
|
||||
obj/%.o : %.rc
|
||||
$(WINDRES) -i $< -o $@
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ 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_regex -lboost_program_options -lpthread
|
||||
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
|
||||
|
||||
105
NTCPSession.cpp
105
NTCPSession.cpp
@@ -1,13 +1,11 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <openssl/dh.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "I2PEndian.h"
|
||||
#include "Base.h"
|
||||
#include "Crypto.h"
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
#include "Crypto.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "RouterContext.h"
|
||||
#include "Transports.h"
|
||||
@@ -21,7 +19,8 @@ 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)
|
||||
{
|
||||
@@ -93,9 +92,7 @@ namespace transport
|
||||
|
||||
m_DHKeysPair = nullptr;
|
||||
|
||||
SendTimeSyncMessage ();
|
||||
m_SendQueue.push_back (CreateDatabaseStoreMsg ()); // we tell immediately who we are
|
||||
|
||||
SendTimeSyncMessage ();
|
||||
transports.PeerConnected (shared_from_this ());
|
||||
}
|
||||
|
||||
@@ -434,7 +431,7 @@ namespace transport
|
||||
}
|
||||
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 ();
|
||||
@@ -669,7 +666,7 @@ namespace transport
|
||||
{
|
||||
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));
|
||||
@@ -717,8 +714,16 @@ namespace transport
|
||||
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);
|
||||
@@ -727,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));
|
||||
}
|
||||
@@ -736,7 +741,7 @@ namespace transport
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint (eLogDebug, "NTCP: No activity for ", NTCP_TERMINATION_TIMEOUT, " seconds");
|
||||
LogPrint (eLogDebug, "NTCP: No activity for ", GetTerminationTimeout (), " seconds");
|
||||
//Terminate ();
|
||||
m_Socket.close ();// invoke Terminate () from HandleReceive
|
||||
}
|
||||
@@ -761,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, "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));
|
||||
|
||||
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, "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));
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -799,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 ();
|
||||
@@ -946,7 +969,7 @@ namespace transport
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "NTCP: 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);
|
||||
conn->Terminate ();
|
||||
|
||||
@@ -40,6 +40,7 @@ namespace transport
|
||||
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>
|
||||
@@ -144,7 +145,10 @@ namespace transport
|
||||
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);
|
||||
|
||||
|
||||
195
NetDb.cpp
195
NetDb.cpp
@@ -2,10 +2,10 @@
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <boost/asio.hpp>
|
||||
#include <openssl/rand.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "I2PEndian.h"
|
||||
#include "Base.h"
|
||||
#include "Crypto.h"
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
#include "I2NPProtocol.h"
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "RouterContext.h"
|
||||
#include "Garlic.h"
|
||||
#include "NetDb.h"
|
||||
#include "util.h"
|
||||
|
||||
using namespace i2p::transport;
|
||||
|
||||
@@ -24,7 +23,7 @@ namespace data
|
||||
{
|
||||
NetDb netdb;
|
||||
|
||||
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr), m_Storage("netDb", "r", "routerInfo-", "dat")
|
||||
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr), m_Storage("netDb", "r", "routerInfo-", "dat"), m_HiddenMode(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -52,7 +51,7 @@ namespace data
|
||||
{
|
||||
if (m_IsRunning)
|
||||
{
|
||||
for (auto it: m_RouterInfos)
|
||||
for (auto& it: m_RouterInfos)
|
||||
it.second->SaveProfile ();
|
||||
DeleteObsoleteProfiles ();
|
||||
m_RouterInfos.clear ();
|
||||
@@ -67,12 +66,12 @@ namespace data
|
||||
}
|
||||
m_LeaseSets.clear();
|
||||
m_Requests.Stop ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetDb::Run ()
|
||||
{
|
||||
uint32_t lastSave = 0, lastPublish = 0, lastExploratory = 0, lastManageRequest = 0;
|
||||
uint32_t lastSave = 0, lastPublish = 0, lastExploratory = 0, lastManageRequest = 0, lastDestinationCleanup = 0;
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
@@ -118,10 +117,19 @@ namespace data
|
||||
{
|
||||
SaveUpdated ();
|
||||
ManageLeaseSets ();
|
||||
ManageLookupResponses ();
|
||||
}
|
||||
lastSave = ts;
|
||||
}
|
||||
if (ts - lastPublish >= 2400) // publish every 40 minutes
|
||||
}
|
||||
if (ts - lastDestinationCleanup >= i2p::garlic::INCOMING_TAGS_EXPIRATION_TIMEOUT)
|
||||
{
|
||||
i2p::context.CleanupDestination ();
|
||||
lastDestinationCleanup = ts;
|
||||
}
|
||||
// if we're in hidden mode don't publish or explore
|
||||
// if (m_HiddenMode) continue;
|
||||
|
||||
if (ts - lastPublish >= NETDB_PUBLISH_INTERVAL) // publish
|
||||
{
|
||||
Publish ();
|
||||
lastPublish = ts;
|
||||
@@ -160,6 +168,11 @@ namespace data
|
||||
return false;
|
||||
}
|
||||
|
||||
void NetDb::SetHidden(bool hide) {
|
||||
// TODO: remove reachable addresses from router info
|
||||
m_HiddenMode = hide;
|
||||
}
|
||||
|
||||
bool NetDb::AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len)
|
||||
{
|
||||
bool updated = true;
|
||||
@@ -173,10 +186,8 @@ namespace data
|
||||
// TODO: check if floodfill has been changed
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "NetDb: RouterInfo is older: ", ident.ToBase64());
|
||||
updated = false;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -205,6 +216,7 @@ namespace data
|
||||
bool NetDb::AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len,
|
||||
std::shared_ptr<i2p::tunnel::InboundTunnel> from)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_LeaseSetsMutex);
|
||||
bool updated = false;
|
||||
if (!from) // unsolicited LS must be received directly
|
||||
{
|
||||
@@ -216,29 +228,29 @@ namespace data
|
||||
it->second->Update (buf, len);
|
||||
if (it->second->IsValid ())
|
||||
{
|
||||
LogPrint (eLogInfo, "NetDb: LeaseSet updated: ", ident.ToBase64());
|
||||
LogPrint (eLogInfo, "NetDb: LeaseSet updated: ", ident.ToBase32());
|
||||
updated = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "NetDb: LeaseSet update failed: ", ident.ToBase64());
|
||||
LogPrint (eLogWarning, "NetDb: LeaseSet update failed: ", ident.ToBase32());
|
||||
m_LeaseSets.erase (it);
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogDebug, "NetDb: LeaseSet is older: ", ident.ToBase64());
|
||||
LogPrint (eLogDebug, "NetDb: LeaseSet is older: ", ident.ToBase32());
|
||||
}
|
||||
else
|
||||
{
|
||||
auto leaseSet = std::make_shared<LeaseSet> (buf, len, false); // we don't need leases in netdb
|
||||
if (leaseSet->IsValid ())
|
||||
{
|
||||
LogPrint (eLogInfo, "NetDb: LeaseSet added: ", ident.ToBase64());
|
||||
LogPrint (eLogInfo, "NetDb: LeaseSet added: ", ident.ToBase32());
|
||||
m_LeaseSets[ident] = leaseSet;
|
||||
updated = true;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "NetDb: new LeaseSet validation failed: ", ident.ToBase64());
|
||||
LogPrint (eLogError, "NetDb: new LeaseSet validation failed: ", ident.ToBase32());
|
||||
}
|
||||
}
|
||||
return updated;
|
||||
@@ -256,6 +268,7 @@ namespace data
|
||||
|
||||
std::shared_ptr<LeaseSet> NetDb::FindLeaseSet (const IdentHash& destination) const
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_LeaseSetsMutex);
|
||||
auto it = m_LeaseSets.find (destination);
|
||||
if (it != m_LeaseSets.end ())
|
||||
return it->second;
|
||||
@@ -310,6 +323,13 @@ namespace data
|
||||
return true;
|
||||
}
|
||||
|
||||
void NetDb::VisitLeaseSets(LeaseSetVisitor v)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_LeaseSetsMutex);
|
||||
for ( auto & entry : m_LeaseSets)
|
||||
v(entry.first, entry.second);
|
||||
}
|
||||
|
||||
void NetDb::Load ()
|
||||
{
|
||||
// make sure we cleanup netDb from previous attempts
|
||||
@@ -319,7 +339,7 @@ namespace data
|
||||
m_LastLoad = i2p::util::GetSecondsSinceEpoch();
|
||||
std::vector<std::string> files;
|
||||
m_Storage.Traverse(files);
|
||||
for (auto path : files)
|
||||
for (const auto& path : files)
|
||||
LoadRouterInfo(path);
|
||||
|
||||
LogPrint (eLogInfo, "NetDb: ", m_RouterInfos.size(), " routers loaded (", m_Floodfills.size (), " floodfils)");
|
||||
@@ -337,7 +357,7 @@ namespace data
|
||||
expirationTimeout = i2p::context.IsFloodfill () ? NETDB_FLOODFILL_EXPIRATION_TIMEOUT*1000LL :
|
||||
NETDB_MIN_EXPIRATION_TIMEOUT*1000LL + (NETDB_MAX_EXPIRATION_TIMEOUT - NETDB_MIN_EXPIRATION_TIMEOUT)*1000LL*NETDB_MIN_ROUTERS/total;
|
||||
|
||||
for (auto it: m_RouterInfos)
|
||||
for (auto& it: m_RouterInfos)
|
||||
{
|
||||
std::string ident = it.second->GetIdentHashBase64();
|
||||
std::string path = m_Storage.Path(ident);
|
||||
@@ -385,7 +405,7 @@ namespace data
|
||||
it = m_RouterInfos.erase (it);
|
||||
continue;
|
||||
}
|
||||
it++;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
// clean up expired floodfiils
|
||||
@@ -395,7 +415,7 @@ namespace data
|
||||
if ((*it)->IsUnreachable ())
|
||||
it = m_Floodfills.erase (it);
|
||||
else
|
||||
it++;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -449,12 +469,18 @@ namespace data
|
||||
}
|
||||
offset += 32;
|
||||
}
|
||||
// we must send reply back before this check
|
||||
if (ident == i2p::context.GetIdentHash ())
|
||||
{
|
||||
LogPrint (eLogError, "NetDb: database store with own RouterInfo received, dropped");
|
||||
return;
|
||||
}
|
||||
size_t payloadOffset = offset;
|
||||
|
||||
bool updated = false;
|
||||
if (buf[DATABASE_STORE_TYPE_OFFSET]) // type
|
||||
{
|
||||
LogPrint (eLogDebug, "NetDb: store request: LeaseSet");
|
||||
LogPrint (eLogDebug, "NetDb: store request: LeaseSet for ", ident.ToBase32());
|
||||
updated = AddLeaseSet (ident, buf + offset, len - offset, m->from);
|
||||
}
|
||||
else
|
||||
@@ -480,25 +506,32 @@ namespace data
|
||||
uint8_t * payload = floodMsg->GetPayload ();
|
||||
memcpy (payload, buf, 33); // key + type
|
||||
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0); // zero reply token
|
||||
auto msgLen = len - payloadOffset;
|
||||
size_t msgLen = len - payloadOffset;
|
||||
floodMsg->len += DATABASE_STORE_HEADER_SIZE + msgLen;
|
||||
if (floodMsg->len < floodMsg->maxLen)
|
||||
{
|
||||
memcpy (payload + DATABASE_STORE_HEADER_SIZE, buf + payloadOffset, msgLen);
|
||||
floodMsg->FillI2NPMessageHeader (eI2NPDatabaseStore);
|
||||
std::set<IdentHash> excluded;
|
||||
for (int i = 0; i < 3; i++)
|
||||
excluded.insert (i2p::context.GetIdentHash ()); // don't flood to itself
|
||||
excluded.insert (ident); // don't flood back
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
auto floodfill = GetClosestFloodfill (ident, excluded);
|
||||
if (floodfill)
|
||||
transports.SendMessage (floodfill->GetIdentHash (), floodMsg);
|
||||
{
|
||||
auto h = floodfill->GetIdentHash();
|
||||
LogPrint(eLogDebug, "NetDb: Flood lease set for ", ident.ToBase32(), " to ", h.ToBase64());
|
||||
transports.SendMessage (h, CopyI2NPMessage(floodMsg));
|
||||
excluded.insert (h);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Database store message is too long ", floodMsg->len);
|
||||
}
|
||||
LogPrint (eLogError, "NetDb: Database store message is too long ", floodMsg->len);
|
||||
}
|
||||
}
|
||||
|
||||
void NetDb::HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg)
|
||||
@@ -602,14 +635,18 @@ namespace data
|
||||
char key[48];
|
||||
int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48);
|
||||
key[l] = 0;
|
||||
|
||||
IdentHash replyIdent(buf + 32);
|
||||
uint8_t flag = buf[64];
|
||||
|
||||
|
||||
LogPrint (eLogDebug, "NetDb: DatabaseLookup for ", key, " recieved flags=", (int)flag);
|
||||
uint8_t lookupType = flag & DATABASE_LOOKUP_TYPE_FLAGS_MASK;
|
||||
const uint8_t * excluded = buf + 65;
|
||||
uint32_t replyTunnelID = 0;
|
||||
if (flag & DATABASE_LOOKUP_DELIVERY_FLAG) //reply to tunnel
|
||||
{
|
||||
replyTunnelID = bufbe32toh (buf + 64);
|
||||
replyTunnelID = bufbe32toh (excluded);
|
||||
excluded += 4;
|
||||
}
|
||||
uint16_t numExcluded = bufbe16toh (excluded);
|
||||
@@ -617,7 +654,7 @@ namespace data
|
||||
if (numExcluded > 512)
|
||||
{
|
||||
LogPrint (eLogWarning, "NetDb: number of excluded peers", numExcluded, " exceeds 512");
|
||||
numExcluded = 0; // TODO:
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<I2NPMessage> replyMsg;
|
||||
@@ -661,7 +698,12 @@ namespace data
|
||||
lookupType == DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP))
|
||||
{
|
||||
auto leaseSet = FindLeaseSet (ident);
|
||||
if (leaseSet && !leaseSet->IsExpired ()) // we don't send back our LeaseSets
|
||||
if (!leaseSet)
|
||||
{
|
||||
// no lease set found
|
||||
LogPrint(eLogDebug, "NetDb: requested LeaseSet not found for ", ident.ToBase32());
|
||||
}
|
||||
else if (!leaseSet->IsExpired ()) // we don't send back our LeaseSets
|
||||
{
|
||||
LogPrint (eLogDebug, "NetDb: requested LeaseSet ", key, " found");
|
||||
replyMsg = CreateDatabaseStoreMsg (leaseSet);
|
||||
@@ -670,42 +712,64 @@ namespace data
|
||||
|
||||
if (!replyMsg)
|
||||
{
|
||||
LogPrint (eLogWarning, "NetDb: Requested ", key, " not found. ", numExcluded, " excluded");
|
||||
std::set<IdentHash> excludedRouters;
|
||||
for (int i = 0; i < numExcluded; i++)
|
||||
{
|
||||
excludedRouters.insert (excluded);
|
||||
excluded += 32;
|
||||
LogPrint (eLogWarning, "NetDb: Requested ", key, " not found, ", numExcluded, " peers excluded");
|
||||
// find or cleate response
|
||||
std::vector<IdentHash> closestFloodfills;
|
||||
bool found = false;
|
||||
if (!numExcluded)
|
||||
{
|
||||
auto it = m_LookupResponses.find (ident);
|
||||
if (it != m_LookupResponses.end ())
|
||||
{
|
||||
closestFloodfills = it->second.first;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
std::set<IdentHash> excludedRouters;
|
||||
const uint8_t * exclude_ident = excluded;
|
||||
for (int i = 0; i < numExcluded; i++)
|
||||
{
|
||||
excludedRouters.insert (exclude_ident);
|
||||
exclude_ident += 32;
|
||||
}
|
||||
closestFloodfills = GetClosestFloodfills (ident, 3, excludedRouters, true);
|
||||
if (!numExcluded) // save if no excluded
|
||||
m_LookupResponses[ident] = std::make_pair(closestFloodfills, i2p::util::GetSecondsSinceEpoch ());
|
||||
}
|
||||
replyMsg = CreateDatabaseSearchReply (ident, GetClosestFloodfills (ident, 3, excludedRouters, true));
|
||||
}
|
||||
replyMsg = CreateDatabaseSearchReply (ident, closestFloodfills);
|
||||
}
|
||||
}
|
||||
|
||||
excluded += numExcluded * 32;
|
||||
if (replyMsg)
|
||||
{
|
||||
if (replyTunnelID)
|
||||
{
|
||||
// encryption might be used though tunnel only
|
||||
if (flag & DATABASE_LOOKUP_ENCYPTION_FLAG) // encrypted reply requested
|
||||
if (flag & DATABASE_LOOKUP_ENCRYPTION_FLAG) // encrypted reply requested
|
||||
{
|
||||
const uint8_t * sessionKey = excluded;
|
||||
uint8_t numTags = sessionKey[32];
|
||||
if (numTags > 0)
|
||||
const uint8_t numTags = excluded[32];
|
||||
if (numTags)
|
||||
{
|
||||
const uint8_t * sessionTag = sessionKey + 33; // take first tag
|
||||
const i2p::garlic::SessionTag sessionTag(excluded + 33); // take first tag
|
||||
i2p::garlic::GarlicRoutingSession garlic (sessionKey, sessionTag);
|
||||
replyMsg = garlic.WrapSingleMessage (replyMsg);
|
||||
if(replyMsg == nullptr) LogPrint(eLogError, "NetDb: failed to wrap message");
|
||||
}
|
||||
else
|
||||
LogPrint(eLogWarning, "NetDb: encrypted reply requested but no tags provided");
|
||||
}
|
||||
auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool ();
|
||||
auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr;
|
||||
if (outbound)
|
||||
outbound->SendTunnelDataMsg (buf+32, replyTunnelID, replyMsg);
|
||||
outbound->SendTunnelDataMsg (replyIdent, replyTunnelID, replyMsg);
|
||||
else
|
||||
transports.SendMessage (buf+32, i2p::CreateTunnelGatewayMsg (replyTunnelID, replyMsg));
|
||||
transports.SendMessage (replyIdent, i2p::CreateTunnelGatewayMsg (replyTunnelID, replyMsg));
|
||||
}
|
||||
else
|
||||
transports.SendMessage (buf+32, replyMsg);
|
||||
transports.SendMessage (replyIdent, replyMsg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -830,13 +894,14 @@ namespace data
|
||||
template<typename Filter>
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter (Filter filter) const
|
||||
{
|
||||
if (!m_RouterInfos.size ()) return 0;
|
||||
uint32_t ind = rand () % m_RouterInfos.size ();
|
||||
if (m_RouterInfos.empty())
|
||||
return 0;
|
||||
uint32_t ind = rand () % m_RouterInfos.size ();
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
uint32_t i = 0;
|
||||
std::unique_lock<std::mutex> l(m_RouterInfosMutex);
|
||||
for (auto it: m_RouterInfos)
|
||||
for (const auto& it: m_RouterInfos)
|
||||
{
|
||||
if (i >= ind)
|
||||
{
|
||||
@@ -868,7 +933,7 @@ namespace data
|
||||
else
|
||||
minMetric.SetMax ();
|
||||
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
|
||||
for (auto it: m_Floodfills)
|
||||
for (const auto& it: m_Floodfills)
|
||||
{
|
||||
if (!it->IsUnreachable ())
|
||||
{
|
||||
@@ -899,7 +964,7 @@ namespace data
|
||||
if (closeThanUsOnly) ourMetric = destKey ^ i2p::context.GetIdentHash ();
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
|
||||
for (auto it: m_Floodfills)
|
||||
for (const auto& it: m_Floodfills)
|
||||
{
|
||||
if (!it->IsUnreachable ())
|
||||
{
|
||||
@@ -918,11 +983,11 @@ namespace data
|
||||
|
||||
std::vector<IdentHash> res;
|
||||
size_t i = 0;
|
||||
for (auto it: sorted)
|
||||
for (const auto& it: sorted)
|
||||
{
|
||||
if (i < num)
|
||||
{
|
||||
auto& ident = it.r->GetIdentHash ();
|
||||
const auto& ident = it.r->GetIdentHash ();
|
||||
if (!excluded.count (ident))
|
||||
{
|
||||
res.push_back (ident);
|
||||
@@ -935,6 +1000,14 @@ namespace data
|
||||
return res;
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouterInFamily(const std::string & fam) const {
|
||||
return GetRandomRouter(
|
||||
[fam](std::shared_ptr<const RouterInfo> router)->bool
|
||||
{
|
||||
return router->IsFamily(fam);
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetClosestNonFloodfill (const IdentHash& destination,
|
||||
const std::set<IdentHash>& excluded) const
|
||||
{
|
||||
@@ -943,7 +1016,7 @@ namespace data
|
||||
IdentHash destKey = CreateRoutingKey (destination);
|
||||
minMetric.SetMax ();
|
||||
// must be called from NetDb thread only
|
||||
for (auto it: m_RouterInfos)
|
||||
for (const auto& it: m_RouterInfos)
|
||||
{
|
||||
if (!it.second->IsFloodfill ())
|
||||
{
|
||||
@@ -969,7 +1042,19 @@ namespace data
|
||||
it = m_LeaseSets.erase (it);
|
||||
}
|
||||
else
|
||||
it++;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void NetDb::ManageLookupResponses ()
|
||||
{
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto it = m_LookupResponses.begin (); it != m_LookupResponses.end ();)
|
||||
{
|
||||
if (ts > it->second.second + 180) // 3 minutes
|
||||
it = m_LookupResponses.erase (it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
26
NetDb.h
26
NetDb.h
@@ -8,7 +8,9 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
#include "Base.h"
|
||||
#include "Gzip.h"
|
||||
#include "FS.h"
|
||||
#include "Queue.h"
|
||||
#include "I2NPProtocol.h"
|
||||
@@ -29,6 +31,10 @@ namespace data
|
||||
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;
|
||||
|
||||
/** function for visiting a leaseset stored in a floodfill */
|
||||
typedef std::function<void(const IdentHash, std::shared_ptr<LeaseSet>)> LeaseSetVisitor;
|
||||
|
||||
class NetDb
|
||||
{
|
||||
@@ -62,10 +68,14 @@ namespace data
|
||||
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 (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; };
|
||||
|
||||
@@ -73,7 +83,10 @@ namespace data
|
||||
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:
|
||||
|
||||
void Load ();
|
||||
@@ -84,12 +97,14 @@ namespace data
|
||||
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;
|
||||
@@ -108,6 +123,11 @@ namespace data
|
||||
|
||||
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;
|
||||
|
||||
@@ -141,7 +141,7 @@ namespace data
|
||||
if (done)
|
||||
it = m_RequestedDestinations.erase (it);
|
||||
else
|
||||
it++;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ namespace data
|
||||
|
||||
std::vector<std::string> files;
|
||||
m_ProfilesStorage.Traverse(files);
|
||||
for (auto path: files) {
|
||||
for (const auto& path: files) {
|
||||
if (stat(path.c_str(), &st) != 0) {
|
||||
LogPrint(eLogWarning, "Profiling: Can't stat(): ", path);
|
||||
continue;
|
||||
|
||||
51
Queue.h
51
Queue.h
@@ -7,6 +7,7 @@
|
||||
#include <thread>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -20,7 +21,7 @@ namespace util
|
||||
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 ();
|
||||
}
|
||||
|
||||
@@ -29,7 +30,7 @@ namespace util
|
||||
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 ();
|
||||
}
|
||||
@@ -117,52 +118,6 @@ namespace util
|
||||
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 (auto 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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
78
README.md
78
README.md
@@ -1,41 +1,55 @@
|
||||
i2pd
|
||||
====
|
||||
|
||||
Independent C++ implementation of I2P router
|
||||
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.
|
||||
|
||||
Donations
|
||||
---------
|
||||
|
||||
BTC: 1K7Ds6KUeR8ya287UC4rYTjvC96vXyZbDY
|
||||
LTC: LKQirrYrDeTuAPnpYq5y7LVKtywfkkHi59
|
||||
ANC: AQJYweYYUqM1nVfLqfoSMpUMfzxvS4Xd7z
|
||||
|
||||
Downloads
|
||||
------------
|
||||
|
||||
Official binary releases could be found at:
|
||||
http://i2pd.website/releases/
|
||||
older releases
|
||||
http://download.i2p.io/purplei2p/i2pd/releases/
|
||||
|
||||
Supported OS
|
||||
------------
|
||||
|
||||
* Linux x86/x64 - [](https://travis-ci.org/PurpleI2P/i2pd)
|
||||
* Windows - [](https://ci.appveyor.com/project/PurpleI2P/i2pd)
|
||||
* Mac OS X
|
||||
* FreeBSD
|
||||
|
||||
More documentation
|
||||
------------------
|
||||
|
||||
* [Building from source / unix](docs/build_notes_unix.md)
|
||||
* [Building from source / windows](docs/build_notes_windows.md)
|
||||
* [Configuring your i2pd](docs/configuration.md)
|
||||
* [Github wiki](https://github.com/PurpleI2P/i2pd/wiki/)
|
||||
|
||||
109
Reseed.cpp
109
Reseed.cpp
@@ -1,43 +1,47 @@
|
||||
#include <string.h>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <openssl/bn.h>
|
||||
#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 "Crypto.h"
|
||||
#include "NetDb.h"
|
||||
#include "HTTP.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
static std::vector<std::string> httpsReseedHostList =
|
||||
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://i2pseed.zarrenspry.info/", // Only HTTPS and SU3 (v3) support
|
||||
"https://i2p.mooo.com/netDb/",
|
||||
"https://netdb.i2p2.no/", // Only SU3 (v3) support, SNI required
|
||||
"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://www.torontocrypto.org: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
|
||||
"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()
|
||||
{
|
||||
}
|
||||
@@ -48,6 +52,13 @@ namespace data
|
||||
|
||||
int Reseeder::ReseedNowSU3 ()
|
||||
{
|
||||
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);
|
||||
@@ -370,13 +381,19 @@ namespace data
|
||||
|
||||
std::string Reseeder::HttpsRequest (const std::string& address)
|
||||
{
|
||||
i2p::util::http::url u(address);
|
||||
if (u.port_ == 80) u.port_ = 443;
|
||||
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;
|
||||
|
||||
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 (u.host_, std::to_string (u.port_)), 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)
|
||||
{
|
||||
boost::asio::ssl::context ctx(service, boost::asio::ssl::context::sslv23);
|
||||
@@ -388,32 +405,52 @@ namespace data
|
||||
s.handshake (boost::asio::ssl::stream_base::client, ecode);
|
||||
if (!ecode)
|
||||
{
|
||||
LogPrint (eLogInfo, "Reseed: Connected to ", u.host_, ":", u.port_);
|
||||
// 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";
|
||||
s.write_some (boost::asio::buffer (ss.str ()));
|
||||
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 response[1024]; size_t l = 0;
|
||||
do
|
||||
{
|
||||
l = s.read_some (boost::asio::buffer (response, 1024), ecode);
|
||||
if (l) rs.write (response, l);
|
||||
}
|
||||
while (!ecode && l);
|
||||
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
|
||||
return i2p::util::http::GetHttpContent (rs);
|
||||
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 ", u.host_, ": ", ecode.message ());
|
||||
LogPrint (eLogError, "Reseed: Couldn't connect to ", url.host, ": ", ecode.message ());
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Reseed: Couldn't resolve address ", u.host_, ": ", ecode.message ());
|
||||
LogPrint (eLogError, "Reseed: Couldn't resolve address ", url.host, ": ", ecode.message ());
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,14 +49,34 @@ namespace i2p
|
||||
uint16_t port; i2p::config::GetOption("port", port);
|
||||
if (!port)
|
||||
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
|
||||
std::string host; i2p::config::GetOption("host", host);
|
||||
if (i2p::config::IsDefault("host"))
|
||||
host = "127.0.0.1"; // replace default address with safe value
|
||||
routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ());
|
||||
routerInfo.AddNTCPAddress (host.c_str(), port);
|
||||
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 ("netId", std::to_string (I2PD_NET_ID));
|
||||
routerInfo.SetProperty ("netId", std::to_string (I2PD_NET_ID));
|
||||
routerInfo.SetProperty ("router.version", I2P_VERSION);
|
||||
routerInfo.CreateBuffer (m_Keys);
|
||||
m_RouterInfo.SetRouterIdentity (GetIdentity ());
|
||||
@@ -94,9 +114,9 @@ namespace i2p
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -109,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;
|
||||
}
|
||||
}
|
||||
@@ -149,6 +169,11 @@ namespace i2p
|
||||
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;
|
||||
@@ -165,34 +190,49 @@ namespace i2p
|
||||
m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY_SIG);
|
||||
}
|
||||
}
|
||||
|
||||
void RouterContext::SetHighBandwidth ()
|
||||
{
|
||||
if (!m_RouterInfo.IsHighBandwidth () || m_RouterInfo.IsExtraBandwidth ())
|
||||
|
||||
void RouterContext::SetBandwidth (char L) {
|
||||
uint16_t limit = 0;
|
||||
enum { low, high, extra } type = high;
|
||||
/* detect parameters */
|
||||
switch (L)
|
||||
{
|
||||
m_RouterInfo.SetCaps ((m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eHighBandwidth) & ~i2p::data::RouterInfo::eExtraBandwidth);
|
||||
UpdateRouterInfo ();
|
||||
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::SetLowBandwidth ()
|
||||
void RouterContext::SetBandwidth (int limit)
|
||||
{
|
||||
if (m_RouterInfo.IsHighBandwidth () || m_RouterInfo.IsExtraBandwidth ())
|
||||
{
|
||||
m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () & ~i2p::data::RouterInfo::eHighBandwidth & ~i2p::data::RouterInfo::eExtraBandwidth);
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
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'); }
|
||||
}
|
||||
|
||||
void RouterContext::SetExtraBandwidth ()
|
||||
{
|
||||
if (!m_RouterInfo.IsExtraBandwidth () || !m_RouterInfo.IsHighBandwidth ())
|
||||
{
|
||||
m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eExtraBandwidth | i2p::data::RouterInfo::eHighBandwidth);
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
}
|
||||
|
||||
bool RouterContext::IsUnreachable () const
|
||||
{
|
||||
return m_RouterInfo.GetCaps () & i2p::data::RouterInfo::eUnreachable;
|
||||
@@ -204,17 +244,18 @@ namespace i2p
|
||||
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 ();
|
||||
@@ -233,18 +274,19 @@ namespace i2p
|
||||
|
||||
// insert NTCP back
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (size_t i = 0; i < addresses.size (); i++)
|
||||
for (const auto& addr : addresses)
|
||||
{
|
||||
if (addresses[i].transportStyle == i2p::data::RouterInfo::eTransportSSU)
|
||||
if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU &&
|
||||
addr->host.is_v4 ())
|
||||
{
|
||||
// insert NTCP address with host/port from SSU
|
||||
m_RouterInfo.AddNTCPAddress (addresses[i].host.to_string ().c_str (), addresses[i].port);
|
||||
m_RouterInfo.AddNTCPAddress (addr->host.to_string ().c_str (), addr->port);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// delete previous introducers
|
||||
for (auto& addr : addresses)
|
||||
addr.introducers.clear ();
|
||||
addr->introducers.clear ();
|
||||
|
||||
// update
|
||||
UpdateRouterInfo ();
|
||||
@@ -257,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)
|
||||
{
|
||||
@@ -331,16 +383,24 @@ namespace i2p
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
i2p::data::RouterInfo routerInfo(i2p::fs::DataDirPath (ROUTER_INFO)); // TODO
|
||||
m_RouterInfo.SetRouterIdentity (GetIdentity ());
|
||||
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
|
||||
m_RouterInfo.SetProperty ("coreVersion", I2P_VERSION);
|
||||
m_RouterInfo.SetProperty ("router.version", I2P_VERSION);
|
||||
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 ();
|
||||
}
|
||||
|
||||
// Migration to 0.9.24. TODO: remove later
|
||||
m_RouterInfo.DeleteProperty ("coreVersion");
|
||||
m_RouterInfo.DeleteProperty ("stat_uptime");
|
||||
|
||||
if (IsUnreachable ())
|
||||
SetReachable (); // we assume reachable until we discover firewall through peer tests
|
||||
|
||||
@@ -379,6 +439,12 @@ namespace i2p
|
||||
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
|
||||
{
|
||||
|
||||
@@ -30,6 +30,7 @@ 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
|
||||
{
|
||||
@@ -45,6 +46,7 @@ namespace i2p
|
||||
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);
|
||||
|
||||
@@ -58,24 +60,29 @@ namespace i2p
|
||||
bool IsFloodfill () const { return m_IsFloodfill; };
|
||||
void SetFloodfill (bool floodfill);
|
||||
void SetFamily (const std::string& family);
|
||||
void SetHighBandwidth ();
|
||||
void SetLowBandwidth ();
|
||||
void SetExtraBandwidth ();
|
||||
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 SetSupportsV4 (bool supportsV4);
|
||||
|
||||
void UpdateNTCPV6Address (const boost::asio::ip::address& host); // called from NTCP session
|
||||
void UpdateStats ();
|
||||
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; };
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
|
||||
void SetLeaseSetUpdated () {};
|
||||
|
||||
// implements GarlicDestination
|
||||
std::shared_ptr<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);
|
||||
|
||||
@@ -98,6 +105,7 @@ namespace i2p
|
||||
uint64_t m_LastUpdateTime;
|
||||
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;
|
||||
};
|
||||
|
||||
247
RouterInfo.cpp
247
RouterInfo.cpp
@@ -15,10 +15,16 @@ 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 ();
|
||||
}
|
||||
@@ -26,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;
|
||||
@@ -48,7 +55,7 @@ namespace data
|
||||
m_IsUnreachable = false;
|
||||
m_SupportedTransports = 0;
|
||||
m_Caps = 0;
|
||||
m_Addresses.clear ();
|
||||
// don't clean up m_Addresses, it will be replaced in ReadFromStream
|
||||
m_Properties.clear ();
|
||||
// copy buffer
|
||||
if (!m_Buffer)
|
||||
@@ -77,7 +84,7 @@ 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);
|
||||
@@ -104,6 +111,8 @@ namespace data
|
||||
{
|
||||
if (LoadFile ())
|
||||
ReadFromBuffer (false);
|
||||
else
|
||||
m_IsUnreachable = true;
|
||||
}
|
||||
|
||||
void RouterInfo::ReadFromBuffer (bool verifySignature)
|
||||
@@ -142,6 +151,7 @@ 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)); if (!s) return;
|
||||
bool introducers = false;
|
||||
@@ -232,10 +242,11 @@ namespace data
|
||||
}
|
||||
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)); if (!s) return;
|
||||
@@ -286,9 +297,13 @@ namespace data
|
||||
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)
|
||||
{
|
||||
@@ -333,36 +348,40 @@ namespace data
|
||||
void RouterInfo::UpdateCapsProperty ()
|
||||
{
|
||||
std::string caps;
|
||||
if (m_Caps & eFloodfill)
|
||||
{
|
||||
if (m_Caps & eExtraBandwidth) caps += CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P'
|
||||
caps += CAPS_FLAG_HIGH_BANDWIDTH3; // 'O'
|
||||
if (m_Caps & eFloodfill) {
|
||||
caps += CAPS_FLAG_FLOODFILL; // floodfill
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Caps & eExtraBandwidth) caps += CAPS_FLAG_EXTRA_BANDWIDTH1;
|
||||
caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH3 : CAPS_FLAG_LOW_BANDWIDTH2; // bandwidth
|
||||
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'
|
||||
}
|
||||
}
|
||||
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);
|
||||
@@ -391,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 << '=';
|
||||
@@ -400,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 << '=';
|
||||
@@ -412,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 << '=';
|
||||
@@ -421,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 << '=';
|
||||
@@ -463,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 << '=';
|
||||
@@ -510,19 +529,20 @@ namespace data
|
||||
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);
|
||||
if (f.is_open ())
|
||||
f.write ((char *)m_Buffer, m_BufferLen);
|
||||
else
|
||||
LogPrint(eLogError, "RouterInfo: Can't save to ", fullPath);
|
||||
}
|
||||
else
|
||||
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)
|
||||
@@ -543,46 +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;
|
||||
for (auto it: m_Addresses) // don't insert same address twice
|
||||
if (it == addr) return;
|
||||
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);
|
||||
for (auto it: m_Addresses) // don't insert same address twice
|
||||
if (it == addr) return;
|
||||
m_Addresses.push_back(addr);
|
||||
m_SupportedTransports |= addr.host.is_v6 () ? eSSUV6 : 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 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)
|
||||
for (auto& intro: addr->introducers)
|
||||
if (intro.iTag == introducer.iTag) return false; // already presented
|
||||
addr.introducers.push_back (introducer);
|
||||
addr->introducers.push_back (introducer);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -591,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;
|
||||
@@ -629,6 +650,14 @@ namespace data
|
||||
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;
|
||||
return "";
|
||||
}
|
||||
|
||||
bool RouterInfo::IsNTCP (bool v4only) const
|
||||
{
|
||||
if (v4only)
|
||||
@@ -650,70 +679,86 @@ 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;
|
||||
|
||||
49
RouterInfo.h
49
RouterInfo.h
@@ -5,6 +5,7 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iostream>
|
||||
#include <boost/asio.hpp>
|
||||
#include "Identity.h"
|
||||
@@ -24,13 +25,14 @@ namespace data
|
||||
const char CAPS_FLAG_HIDDEN = 'H';
|
||||
const char CAPS_FLAG_REACHABLE = 'R';
|
||||
const char CAPS_FLAG_UNREACHABLE = 'U';
|
||||
const char CAPS_FLAG_LOW_BANDWIDTH1 = 'K';
|
||||
const char CAPS_FLAG_LOW_BANDWIDTH2 = 'L';
|
||||
const char CAPS_FLAG_HIGH_BANDWIDTH1 = 'M';
|
||||
const char CAPS_FLAG_HIGH_BANDWIDTH2 = 'N';
|
||||
const char CAPS_FLAG_HIGH_BANDWIDTH3 = 'O';
|
||||
const char CAPS_FLAG_EXTRA_BANDWIDTH1 = 'P';
|
||||
const char CAPS_FLAG_EXTRA_BANDWIDTH2 = 'X';
|
||||
/* 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';
|
||||
@@ -104,9 +106,10 @@ namespace data
|
||||
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);
|
||||
@@ -116,10 +119,10 @@ namespace data
|
||||
void SetRouterIdentity (std::shared_ptr<const IdentityEx> identity);
|
||||
std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); };
|
||||
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);
|
||||
@@ -127,14 +130,18 @@ namespace data
|
||||
bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
|
||||
void SetProperty (const std::string& key, const std::string& value); // called from RouterContext only
|
||||
void DeleteProperty (const std::string& key); // called from RouterContext only
|
||||
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; };
|
||||
@@ -157,7 +164,7 @@ 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);
|
||||
|
||||
std::shared_ptr<RouterProfile> GetProfile () const;
|
||||
void SaveProfile () { if (m_Profile) m_Profile->Save (); };
|
||||
@@ -166,23 +173,25 @@ namespace data
|
||||
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; };
|
||||
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:
|
||||
@@ -192,7 +201,7 @@ namespace data
|
||||
uint8_t * m_Buffer;
|
||||
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;
|
||||
|
||||
63
SAM.cpp
63
SAM.cpp
@@ -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 ()
|
||||
@@ -369,7 +371,7 @@ namespace client
|
||||
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 ();
|
||||
@@ -402,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);
|
||||
}
|
||||
@@ -676,19 +678,20 @@ namespace client
|
||||
|
||||
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 (const std::string& address, int port):
|
||||
@@ -716,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)
|
||||
@@ -771,7 +774,7 @@ namespace client
|
||||
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)
|
||||
{
|
||||
std::shared_ptr<ClientDestination> localDestination = nullptr;
|
||||
@@ -796,8 +799,9 @@ 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 (eLogWarning, "SAM: Session ", id, " already exists");
|
||||
return ret.first->second;
|
||||
@@ -807,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);
|
||||
|
||||
33
SAM.h
33
SAM.h
@@ -128,13 +128,36 @@ namespace client
|
||||
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
|
||||
{
|
||||
std::shared_ptr<ClientDestination> localDestination;
|
||||
std::list<std::shared_ptr<SAMSocket> > sockets;
|
||||
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 (std::shared_ptr<ClientDestination> dest);
|
||||
~SAMSession ();
|
||||
@@ -153,10 +176,10 @@ namespace client
|
||||
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:
|
||||
|
||||
@@ -177,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:
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace proxy
|
||||
{
|
||||
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;
|
||||
|
||||
107
SSU.cpp
107
SSU.cpp
@@ -10,12 +10,31 @@ 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_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 ())
|
||||
@@ -35,13 +54,16 @@ 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));
|
||||
m_ReceiversService.post (std::bind (&SSUServer::ReceiveV6, this));
|
||||
}
|
||||
SchedulePeerTestsCleanupTimer ();
|
||||
ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers
|
||||
@@ -215,9 +237,8 @@ namespace transport
|
||||
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;
|
||||
try
|
||||
{
|
||||
if (!session || session->GetRemoteEndpoint () != packet->from) // we received packet for other session than previous
|
||||
@@ -231,7 +252,7 @@ namespace transport
|
||||
session = std::make_shared<SSUSession> (*this, packet->from);
|
||||
session->WaitForConnect ();
|
||||
(*sessions)[packet->from] = session;
|
||||
LogPrint (eLogInfo, "SSU: new session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created");
|
||||
LogPrint (eLogDebug, "SSU: new session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created");
|
||||
}
|
||||
}
|
||||
session->ProcessNextMessage (packet->buf, packet->len, packet->from);
|
||||
@@ -312,7 +333,7 @@ namespace transport
|
||||
auto session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
|
||||
sessions[remoteEndpoint] = session;
|
||||
// connect
|
||||
LogPrint (eLogInfo, "SSU: Creating new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), "] ",
|
||||
LogPrint (eLogDebug, "SSU: Creating new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), "] ",
|
||||
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ());
|
||||
session->Connect ();
|
||||
}
|
||||
@@ -364,10 +385,10 @@ namespace transport
|
||||
}
|
||||
|
||||
if (introducerSession) // session found
|
||||
LogPrint (eLogInfo, "SSU: Session to introducer already exists");
|
||||
LogPrint (eLogWarning, "SSU: Session to introducer already exists");
|
||||
else // create new
|
||||
{
|
||||
LogPrint (eLogInfo, "SSU: Creating new session to introducer");
|
||||
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;
|
||||
@@ -409,11 +430,11 @@ namespace transport
|
||||
|
||||
void SSUServer::DeleteAllSessions ()
|
||||
{
|
||||
for (auto it: m_Sessions)
|
||||
for (auto& it: m_Sessions)
|
||||
it.second->Close ();
|
||||
m_Sessions.clear ();
|
||||
|
||||
for (auto it: m_SessionsV6)
|
||||
for (auto& it: m_SessionsV6)
|
||||
it.second->Close ();
|
||||
m_SessionsV6.clear ();
|
||||
}
|
||||
@@ -422,7 +443,7 @@ namespace transport
|
||||
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)
|
||||
{
|
||||
@@ -437,8 +458,31 @@ namespace transport
|
||||
return GetRandomV4Session (
|
||||
[excluded](std::shared_ptr<SSUSession> session)->bool
|
||||
{
|
||||
return session->GetState () == eSessionStateEstablished && !session->IsV6 () &&
|
||||
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;
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -490,7 +534,7 @@ namespace transport
|
||||
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)
|
||||
@@ -507,23 +551,20 @@ 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& 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))
|
||||
{
|
||||
newList.push_back (ep);
|
||||
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)
|
||||
@@ -592,7 +633,7 @@ namespace transport
|
||||
it = m_PeerTests.erase (it);
|
||||
}
|
||||
else
|
||||
it++;
|
||||
++it;
|
||||
}
|
||||
if (numDeleted > 0)
|
||||
LogPrint (eLogDebug, "SSU: ", numDeleted, " peer tests have been expired");
|
||||
|
||||
11
SSU.h
11
SSU.h
@@ -37,6 +37,7 @@ namespace transport
|
||||
public:
|
||||
|
||||
SSUServer (int port);
|
||||
SSUServer (const boost::asio::ip::address & addr, int port); // ipv6 only constructor
|
||||
~SSUServer ();
|
||||
void Start ();
|
||||
void Stop ();
|
||||
@@ -47,6 +48,7 @@ namespace transport
|
||||
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> 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 ();
|
||||
|
||||
@@ -62,7 +64,7 @@ namespace transport
|
||||
std::shared_ptr<SSUSession> GetPeerTestSession (uint32_t nonce);
|
||||
void UpdatePeerTest (uint32_t nonce, PeerTestParticipant role);
|
||||
void RemovePeerTest (uint32_t nonce);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
void Run ();
|
||||
@@ -78,7 +80,9 @@ namespace transport
|
||||
void CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false);
|
||||
template<typename 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);
|
||||
@@ -93,8 +97,9 @@ namespace transport
|
||||
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;
|
||||
|
||||
56
SSUData.cpp
56
SSUData.cpp
@@ -25,10 +25,10 @@ namespace transport
|
||||
}
|
||||
|
||||
SSUData::SSUData (SSUSession& session):
|
||||
m_Session (session), m_ResendTimer (session.GetService ()), m_DecayTimer (session.GetService ()),
|
||||
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_PacketSize (m_MaxPacketSize), m_LastMessageReceivedTime (0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -44,7 +44,6 @@ namespace transport
|
||||
void SSUData::Stop ()
|
||||
{
|
||||
m_ResendTimer.cancel ();
|
||||
m_DecayTimer.cancel ();
|
||||
m_IncompleteMessagesCleanupTimer.cancel ();
|
||||
}
|
||||
|
||||
@@ -233,15 +232,12 @@ 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_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (!msg->IsExpired ())
|
||||
m_Handler.PutNextMessage (msg);
|
||||
else
|
||||
LogPrint (eLogInfo, "SSU: message expired");
|
||||
LogPrint (eLogDebug, "SSU: message expired");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "SSU: Message ", msgID, " already received");
|
||||
@@ -425,28 +421,30 @@ 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 (eLogWarning, "SSU: Can't resend data fragment ", ec.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it->second->numResends++;
|
||||
it->second->nextResendTime += it->second->numResends*RESEND_INTERVAL;
|
||||
it++;
|
||||
++it;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -455,27 +453,18 @@ namespace transport
|
||||
}
|
||||
}
|
||||
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 ();
|
||||
@@ -498,8 +487,13 @@ namespace transport
|
||||
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 ();
|
||||
}
|
||||
}
|
||||
|
||||
15
SSUData.h
15
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;
|
||||
@@ -29,6 +33,7 @@ namespace transport
|
||||
const int DECAY_INTERVAL = 20; // in seconds
|
||||
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;
|
||||
@@ -104,9 +109,6 @@ 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);
|
||||
|
||||
@@ -116,10 +118,11 @@ namespace transport
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
114
SSUSession.cpp
114
SSUSession.cpp
@@ -1,7 +1,5 @@
|
||||
#include <boost/bind.hpp>
|
||||
#include <openssl/dh.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/rand.h>
|
||||
#include "Crypto.h"
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
#include "RouterContext.h"
|
||||
@@ -14,7 +12,8 @@ namespace i2p
|
||||
namespace transport
|
||||
{
|
||||
SSUSession::SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint,
|
||||
std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest ): TransportSession (router),
|
||||
std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest ):
|
||||
TransportSession (router, SSU_TERMINATION_TIMEOUT),
|
||||
m_Server (server), m_RemoteEndpoint (remoteEndpoint), m_Timer (GetService ()),
|
||||
m_IsPeerTest (peerTest),m_State (eSessionStateUnknown), m_IsSessionKey (false),
|
||||
m_RelayTag (0),m_Data (*this), m_IsDataReceived (false)
|
||||
@@ -22,14 +21,14 @@ namespace transport
|
||||
if (router)
|
||||
{
|
||||
// we are client
|
||||
auto address = router->GetSSUAddress ();
|
||||
auto address = router->GetSSUAddress (false);
|
||||
if (address) m_IntroKey = address->key;
|
||||
m_Data.AdjustPacketSize (router); // mtu
|
||||
}
|
||||
else
|
||||
{
|
||||
// we are server
|
||||
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
|
||||
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false);
|
||||
if (address) m_IntroKey = address->key;
|
||||
}
|
||||
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
@@ -110,7 +109,7 @@ namespace transport
|
||||
else
|
||||
{
|
||||
// try own intro key
|
||||
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
|
||||
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false);
|
||||
if (!address)
|
||||
{
|
||||
LogPrint (eLogInfo, "SSU is not supported");
|
||||
@@ -265,8 +264,6 @@ namespace transport
|
||||
uint16_t ourPort = bufbe16toh (payload);
|
||||
s.Insert (payload, 2); // our port
|
||||
payload += 2; // port
|
||||
LogPrint (eLogInfo, "SSU: Our external address is ", ourIP.to_string (), ":", ourPort);
|
||||
i2p::context.UpdateAddress (ourIP);
|
||||
if (m_RemoteEndpoint.address ().is_v4 ())
|
||||
s.Insert (m_RemoteEndpoint.address ().to_v4 ().to_bytes ().data (), 4); // remote IP v4
|
||||
else
|
||||
@@ -283,11 +280,18 @@ namespace transport
|
||||
//TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved
|
||||
m_SessionKeyDecryption.SetIV (((SSUHeader *)buf)->iv);
|
||||
m_SessionKeyDecryption.Decrypt (payload, signatureLen, payload); // TODO: non-const payload
|
||||
// verify
|
||||
if (!s.Verify (m_RemoteIdentity, payload))
|
||||
// verify signature
|
||||
if (s.Verify (m_RemoteIdentity, payload))
|
||||
{
|
||||
LogPrint (eLogInfo, "SSU: Our external address is ", ourIP.to_string (), ":", ourPort);
|
||||
i2p::context.UpdateAddress (ourIP);
|
||||
SendSessionConfirmed (y, ourAddress, addressSize + 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "SSU: message 'created' signature verification failed");
|
||||
|
||||
SendSessionConfirmed (y, ourAddress, addressSize + 2);
|
||||
Failed ();
|
||||
}
|
||||
}
|
||||
|
||||
void SSUSession::ProcessSessionConfirmed (const uint8_t * buf, size_t len)
|
||||
@@ -313,11 +317,17 @@ namespace transport
|
||||
paddingSize &= 0x0F; // %16
|
||||
if (paddingSize > 0) paddingSize = 16 - paddingSize;
|
||||
payload += paddingSize;
|
||||
// verify
|
||||
if (m_SignedData && !m_SignedData->Verify (m_RemoteIdentity, payload))
|
||||
// verify signature
|
||||
if (m_SignedData && m_SignedData->Verify (m_RemoteIdentity, payload))
|
||||
{
|
||||
m_Data.Send (CreateDeliveryStatusMsg (0));
|
||||
Established ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "SSU message 'confirmed' signature verification failed");
|
||||
m_Data.Send (CreateDeliveryStatusMsg (0));
|
||||
Established ();
|
||||
Failed ();
|
||||
}
|
||||
}
|
||||
|
||||
void SSUSession::SendSessionRequest ()
|
||||
@@ -357,7 +367,7 @@ namespace transport
|
||||
|
||||
void SSUSession::SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer, uint32_t nonce)
|
||||
{
|
||||
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
|
||||
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false);
|
||||
if (!address)
|
||||
{
|
||||
LogPrint (eLogInfo, "SSU is not supported");
|
||||
@@ -805,7 +815,7 @@ namespace transport
|
||||
if (!ecode)
|
||||
{
|
||||
// timeout expired
|
||||
LogPrint (eLogWarning, "SSU: session was not established after ", SSU_CONNECT_TIMEOUT, " seconds");
|
||||
LogPrint (eLogWarning, "SSU: session with ", m_RemoteEndpoint, " was not established after ", SSU_CONNECT_TIMEOUT, " seconds");
|
||||
Failed ();
|
||||
}
|
||||
}
|
||||
@@ -855,7 +865,6 @@ namespace transport
|
||||
m_DHKeysPair = nullptr;
|
||||
m_SignedData = nullptr;
|
||||
m_Data.Start ();
|
||||
m_Data.Send (CreateDatabaseStoreMsg ());
|
||||
transports.PeerConnected (shared_from_this ());
|
||||
if (m_IsPeerTest)
|
||||
SendPeerTest ();
|
||||
@@ -874,7 +883,7 @@ namespace transport
|
||||
void SSUSession::ScheduleTermination ()
|
||||
{
|
||||
m_Timer.cancel ();
|
||||
m_Timer.expires_from_now (boost::posix_time::seconds(SSU_TERMINATION_TIMEOUT));
|
||||
m_Timer.expires_from_now (boost::posix_time::seconds(GetTerminationTimeout ()));
|
||||
m_Timer.async_wait (std::bind (&SSUSession::HandleTerminationTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
@@ -883,7 +892,7 @@ namespace transport
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: no activity for ", SSU_TERMINATION_TIMEOUT, " seconds");
|
||||
LogPrint (eLogWarning, "SSU: no activity with ", m_RemoteEndpoint, " for ", GetTerminationTimeout (), " seconds");
|
||||
Failed ();
|
||||
}
|
||||
}
|
||||
@@ -898,7 +907,7 @@ namespace transport
|
||||
{
|
||||
if (m_State == eSessionStateEstablished)
|
||||
{
|
||||
for (auto it: msgs)
|
||||
for (const auto& it: msgs)
|
||||
if (it) m_Data.Send (it);
|
||||
}
|
||||
}
|
||||
@@ -922,10 +931,10 @@ namespace transport
|
||||
{
|
||||
uint32_t nonce = bufbe32toh (buf); // 4 bytes
|
||||
uint8_t size = buf[4]; // 1 byte
|
||||
uint32_t address = (size == 4) ? buf32toh(buf + 5) : 0; // big endian, size bytes
|
||||
const uint8_t * address = buf + 5; // big endian, size bytes
|
||||
uint16_t port = buf16toh(buf + size + 5); // big endian, 2 bytes
|
||||
const uint8_t * introKey = buf + size + 7;
|
||||
if (port && !address)
|
||||
if (port && (size != 4) && (size != 16))
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: Address of ", size, " bytes not supported");
|
||||
return;
|
||||
@@ -946,8 +955,7 @@ namespace transport
|
||||
LogPrint (eLogDebug, "SSU: first peer test from Charlie. We are Alice");
|
||||
i2p::context.SetStatus (eRouterStatusOK);
|
||||
m_Server.UpdatePeerTest (nonce, ePeerTestParticipantAlice2);
|
||||
SendPeerTest (nonce, senderEndpoint.address ().to_v4 ().to_ulong (),
|
||||
senderEndpoint.port (), introKey, true, false); // to Charlie
|
||||
SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey, true, false); // to Charlie
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -976,8 +984,7 @@ namespace transport
|
||||
case ePeerTestParticipantCharlie:
|
||||
{
|
||||
LogPrint (eLogDebug, "SSU: peer test from Alice. We are Charlie");
|
||||
SendPeerTest (nonce, senderEndpoint.address ().to_v4 ().to_ulong (),
|
||||
senderEndpoint.port (), introKey); // to Alice with her actual address
|
||||
SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey); // to Alice with her actual address
|
||||
m_Server.RemovePeerTest (nonce); // nonce has been used
|
||||
break;
|
||||
}
|
||||
@@ -992,17 +999,29 @@ namespace transport
|
||||
LogPrint (eLogDebug, "SSU: peer test from Bob. We are Charlie");
|
||||
m_Server.NewPeerTest (nonce, ePeerTestParticipantCharlie);
|
||||
Send (PAYLOAD_TYPE_PEER_TEST, buf, len); // back to Bob
|
||||
SendPeerTest (nonce, be32toh (address), be16toh (port), introKey); // to Alice with her address received from Bob
|
||||
boost::asio::ip::address addr; // Alice's address
|
||||
if (size == 4) // v4
|
||||
{
|
||||
boost::asio::ip::address_v4::bytes_type bytes;
|
||||
memcpy (bytes.data (), address, 4);
|
||||
addr = boost::asio::ip::address_v4 (bytes);
|
||||
}
|
||||
else // v6
|
||||
{
|
||||
boost::asio::ip::address_v6::bytes_type bytes;
|
||||
memcpy (bytes.data (), address, 6);
|
||||
addr = boost::asio::ip::address_v6 (bytes);
|
||||
}
|
||||
SendPeerTest (nonce, addr, be16toh (port), introKey); // to Alice with her address received from Bob
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "SSU: peer test from Alice. We are Bob");
|
||||
auto session = m_Server.GetRandomEstablishedV4Session (shared_from_this ()); // Charlie, TODO: implement v6 support
|
||||
auto session = senderEndpoint.address ().is_v4 () ? m_Server.GetRandomEstablishedV4Session (shared_from_this ()) : m_Server.GetRandomEstablishedV6Session (shared_from_this ()); // Charlie
|
||||
if (session)
|
||||
{
|
||||
m_Server.NewPeerTest (nonce, ePeerTestParticipantBob, shared_from_this ());
|
||||
session->SendPeerTest (nonce, senderEndpoint.address ().to_v4 ().to_ulong (),
|
||||
senderEndpoint.port (), introKey, false); // to Charlie with Alice's actual address
|
||||
session->SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey, false); // to Charlie with Alice's actual address
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1012,7 +1031,7 @@ namespace transport
|
||||
}
|
||||
}
|
||||
|
||||
void SSUSession::SendPeerTest (uint32_t nonce, uint32_t address, uint16_t port,
|
||||
void SSUSession::SendPeerTest (uint32_t nonce, const boost::asio::ip::address& address, uint16_t port,
|
||||
const uint8_t * introKey, bool toAddress, bool sendAddress)
|
||||
// toAddress is true for Alice<->Chalie communications only
|
||||
// sendAddress is false if message comes from Alice
|
||||
@@ -1023,12 +1042,21 @@ namespace transport
|
||||
htobe32buf (payload, nonce);
|
||||
payload += 4; // nonce
|
||||
// address and port
|
||||
if (sendAddress && address)
|
||||
{
|
||||
*payload = 4;
|
||||
payload++; // size
|
||||
htobe32buf (payload, address);
|
||||
payload += 4; // address
|
||||
if (sendAddress)
|
||||
{
|
||||
if (address.is_v4 ())
|
||||
{
|
||||
*payload = 4;
|
||||
memcpy (payload + 1, address.to_v4 ().to_bytes ().data (), 4); // our IP V4
|
||||
}
|
||||
else if (address.is_v6 ())
|
||||
{
|
||||
*payload = 6;
|
||||
memcpy (payload + 1, address.to_v6 ().to_bytes ().data (), 16); // our IP V6
|
||||
}
|
||||
else
|
||||
*payload = 0;
|
||||
payload += (payload[0] + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1056,7 +1084,7 @@ namespace transport
|
||||
{
|
||||
// encrypt message with specified intro key
|
||||
FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80, introKey, iv, introKey);
|
||||
boost::asio::ip::udp::endpoint e (boost::asio::ip::address_v4 (address), port);
|
||||
boost::asio::ip::udp::endpoint e (address, port);
|
||||
m_Server.Send (buf, 80, e);
|
||||
}
|
||||
else
|
||||
@@ -1071,7 +1099,7 @@ namespace transport
|
||||
{
|
||||
// we are Alice
|
||||
LogPrint (eLogDebug, "SSU: sending peer test");
|
||||
auto address = i2p::context.GetRouterInfo ().GetSSUAddress ();
|
||||
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false);
|
||||
if (!address)
|
||||
{
|
||||
LogPrint (eLogInfo, "SSU is not supported. Can't send peer test");
|
||||
@@ -1082,7 +1110,7 @@ namespace transport
|
||||
if (!nonce) nonce = 1;
|
||||
m_IsPeerTest = false;
|
||||
m_Server.NewPeerTest (nonce, ePeerTestParticipantAlice1);
|
||||
SendPeerTest (nonce, 0, 0, address->key, false, false); // address and port always zero for Alice
|
||||
SendPeerTest (nonce, boost::asio::ip::address(), 0, address->key, false, false); // address and port always zero for Alice
|
||||
}
|
||||
|
||||
void SSUSession::SendKeepAlive ()
|
||||
|
||||
@@ -118,7 +118,7 @@ namespace transport
|
||||
void ScheduleConnectTimer ();
|
||||
void HandleConnectTimer (const boost::system::error_code& ecode);
|
||||
void ProcessPeerTest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
|
||||
void SendPeerTest (uint32_t nonce, uint32_t address, uint16_t port, const uint8_t * introKey, bool toAddress = true, bool sendAddress = true);
|
||||
void 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
|
||||
|
||||
@@ -435,8 +435,13 @@ namespace crypto
|
||||
std::unique_ptr<Ed25519>& GetEd25519 ()
|
||||
{
|
||||
if (!g_Ed25519)
|
||||
g_Ed25519.reset (new 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,12 +3,10 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/dsa.h>
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/ecdsa.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/evp.h>
|
||||
#include "Crypto.h"
|
||||
|
||||
|
||||
119
Streaming.cpp
119
Streaming.cpp
@@ -1,4 +1,4 @@
|
||||
#include <openssl/rand.h>
|
||||
#include "Crypto.h"
|
||||
#include "Log.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "RouterContext.h"
|
||||
@@ -36,7 +36,6 @@ namespace stream
|
||||
|
||||
Stream::~Stream ()
|
||||
{
|
||||
Terminate ();
|
||||
while (!m_ReceiveQueue.empty ())
|
||||
{
|
||||
auto packet = m_ReceiveQueue.front ();
|
||||
@@ -66,6 +65,7 @@ namespace stream
|
||||
m_SendHandler = nullptr;
|
||||
handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted));
|
||||
}
|
||||
m_LocalDestination.DeleteStream (shared_from_this ());
|
||||
}
|
||||
|
||||
void Stream::HandleNextPacket (Packet * packet)
|
||||
@@ -87,7 +87,7 @@ namespace stream
|
||||
return;
|
||||
}
|
||||
|
||||
LogPrint (eLogDebug, "Streaming: Received seqn=", receivedSeqn);
|
||||
LogPrint (eLogDebug, "Streaming: Received seqn=", receivedSeqn, " on sSID=", m_SendStreamID);
|
||||
if (receivedSeqn == m_LastReceivedSequenceNumber + 1)
|
||||
{
|
||||
// we have received next in sequence message
|
||||
@@ -129,13 +129,13 @@ namespace stream
|
||||
if (receivedSeqn <= m_LastReceivedSequenceNumber)
|
||||
{
|
||||
// we have received duplicate
|
||||
LogPrint (eLogWarning, "Streaming: Duplicate message ", receivedSeqn, " received");
|
||||
LogPrint (eLogWarning, "Streaming: Duplicate message ", receivedSeqn, " on sSID=", m_SendStreamID);
|
||||
SendQuickAck (); // resend ack for previous message again
|
||||
delete packet; // packet dropped
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Streaming: Missing messages from ", m_LastReceivedSequenceNumber + 1, " to ", receivedSeqn - 1);
|
||||
LogPrint (eLogWarning, "Streaming: Missing messages on sSID=", m_SendStreamID, ": from ", m_LastReceivedSequenceNumber + 1, " to ", receivedSeqn - 1);
|
||||
// save message and wait for missing message again
|
||||
SavePacket (packet);
|
||||
if (m_LastReceivedSequenceNumber >= 0)
|
||||
@@ -162,7 +162,8 @@ namespace stream
|
||||
|
||||
void Stream::SavePacket (Packet * packet)
|
||||
{
|
||||
m_SavedPackets.insert (packet);
|
||||
if (!m_SavedPackets.insert (packet).second)
|
||||
delete packet;
|
||||
}
|
||||
|
||||
void Stream::ProcessPacket (Packet * packet)
|
||||
@@ -182,7 +183,7 @@ namespace stream
|
||||
m_RemoteIdentity = std::make_shared<i2p::data::IdentityEx>(optionData, packet->GetOptionSize ());
|
||||
optionData += m_RemoteIdentity->GetFullLen ();
|
||||
if (!m_RemoteLeaseSet)
|
||||
LogPrint (eLogDebug, "Streaming: Incoming stream from ", m_RemoteIdentity->GetIdentHash ().ToBase64 ());
|
||||
LogPrint (eLogDebug, "Streaming: Incoming stream from ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), ", sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID);
|
||||
}
|
||||
|
||||
if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED)
|
||||
@@ -200,7 +201,7 @@ namespace stream
|
||||
memset (const_cast<uint8_t *>(optionData), 0, signatureLen);
|
||||
if (!m_RemoteIdentity->Verify (packet->GetBuffer (), packet->GetLength (), signature))
|
||||
{
|
||||
LogPrint (eLogError, "Streaming: Signature verification failed");
|
||||
LogPrint (eLogError, "Streaming: Signature verification failed, sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID);
|
||||
Close ();
|
||||
flags |= PACKET_FLAG_CLOSE;
|
||||
}
|
||||
@@ -219,11 +220,19 @@ namespace stream
|
||||
|
||||
m_LastReceivedSequenceNumber = receivedSeqn;
|
||||
|
||||
if (flags & (PACKET_FLAG_CLOSE | PACKET_FLAG_RESET))
|
||||
if (flags & PACKET_FLAG_RESET)
|
||||
{
|
||||
LogPrint (eLogDebug, "Streaming: closing stream sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID, ": reset flag received in packet #", receivedSeqn);
|
||||
m_Status = eStreamStatusReset;
|
||||
Close ();
|
||||
}
|
||||
else if (flags & PACKET_FLAG_CLOSE)
|
||||
{
|
||||
if (m_Status != eStreamStatusClosed)
|
||||
SendClose ();
|
||||
m_Status = eStreamStatusClosed;
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
|
||||
void Stream::ProcessAck (Packet * packet)
|
||||
@@ -231,9 +240,14 @@ namespace stream
|
||||
bool acknowledged = false;
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
uint32_t ackThrough = packet->GetAckThrough ();
|
||||
if (ackThrough > m_SequenceNumber)
|
||||
{
|
||||
LogPrint (eLogError, "Streaming: Unexpected ackThrough=", ackThrough, " > seqn=", m_SequenceNumber);
|
||||
return;
|
||||
}
|
||||
int nackCount = packet->GetNACKCount ();
|
||||
for (auto it = m_SentPackets.begin (); it != m_SentPackets.end ();)
|
||||
{
|
||||
{
|
||||
auto seqn = (*it)->GetSeqn ();
|
||||
if (seqn <= ackThrough)
|
||||
{
|
||||
@@ -249,7 +263,7 @@ namespace stream
|
||||
if (nacked)
|
||||
{
|
||||
LogPrint (eLogDebug, "Streaming: Packet ", seqn, " NACK");
|
||||
it++;
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -257,7 +271,7 @@ namespace stream
|
||||
uint64_t rtt = ts - sentPacket->sendTime;
|
||||
m_RTT = (m_RTT*seqn + rtt)/(seqn + 1);
|
||||
m_RTO = m_RTT*1.5; // TODO: implement it better
|
||||
LogPrint (eLogDebug, "Packet ", seqn, " acknowledged rtt=", rtt);
|
||||
LogPrint (eLogDebug, "Streaming: Packet ", seqn, " acknowledged rtt=", rtt);
|
||||
m_SentPackets.erase (it++);
|
||||
delete sentPacket;
|
||||
acknowledged = true;
|
||||
@@ -288,8 +302,10 @@ namespace stream
|
||||
m_NumResendAttempts = 0;
|
||||
SendBuffer ();
|
||||
}
|
||||
if (m_Status == eStreamStatusClosing)
|
||||
Close (); // all outgoing messages have been sent
|
||||
if (m_Status == eStreamStatusClosed)
|
||||
Terminate ();
|
||||
else if (m_Status == eStreamStatusClosing)
|
||||
Close (); // check is all outgoing messages have been sent and we can send close
|
||||
}
|
||||
|
||||
size_t Stream::Send (const uint8_t * buf, size_t len)
|
||||
@@ -335,9 +351,9 @@ namespace stream
|
||||
htobe32buf (packet + size, m_SequenceNumber++);
|
||||
size += 4; // sequenceNum
|
||||
if (isNoAck)
|
||||
htobe32buf (packet + size, m_LastReceivedSequenceNumber);
|
||||
else
|
||||
htobuf32 (packet + size, 0);
|
||||
else
|
||||
htobe32buf (packet + size, m_LastReceivedSequenceNumber);
|
||||
size += 4; // ack Through
|
||||
packet[size] = 0;
|
||||
size++; // NACK count
|
||||
@@ -389,11 +405,14 @@ namespace stream
|
||||
}
|
||||
if (packets.size () > 0)
|
||||
{
|
||||
m_IsAckSendScheduled = false;
|
||||
m_AckSendTimer.cancel ();
|
||||
if (m_SavedPackets.empty ()) // no NACKS
|
||||
{
|
||||
m_IsAckSendScheduled = false;
|
||||
m_AckSendTimer.cancel ();
|
||||
}
|
||||
bool isEmpty = m_SentPackets.empty ();
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
for (auto it: packets)
|
||||
for (auto& it: packets)
|
||||
{
|
||||
it->sendTime = ts;
|
||||
m_SentPackets.insert (it);
|
||||
@@ -442,7 +461,7 @@ namespace stream
|
||||
auto seqn = it->GetSeqn ();
|
||||
if (numNacks + (seqn - nextSeqn) >= 256)
|
||||
{
|
||||
LogPrint (eLogError, "Number of NACKs exceeds 256. seqn=", seqn, " nextSeqn=", nextSeqn);
|
||||
LogPrint (eLogError, "Streaming: Number of NACKs exceeds 256. seqn=", seqn, " nextSeqn=", nextSeqn);
|
||||
htobe32buf (packet + 12, nextSeqn); // change ack Through
|
||||
break;
|
||||
}
|
||||
@@ -477,35 +496,32 @@ namespace stream
|
||||
|
||||
void Stream::Close ()
|
||||
{
|
||||
LogPrint(eLogDebug, "Streaming: closing stream with sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID, ", status=", m_Status);
|
||||
switch (m_Status)
|
||||
{
|
||||
case eStreamStatusOpen:
|
||||
m_Status = eStreamStatusClosing;
|
||||
Close (); // recursion
|
||||
if (m_Status == eStreamStatusClosing) //still closing
|
||||
LogPrint (eLogInfo, "Streaming: Trying to send stream data before closing");
|
||||
LogPrint (eLogDebug, "Streaming: Trying to send stream data before closing, sSID=", m_SendStreamID);
|
||||
break;
|
||||
case eStreamStatusReset:
|
||||
SendClose ();
|
||||
Terminate ();
|
||||
m_LocalDestination.DeleteStream (shared_from_this ());
|
||||
// TODO: send reset
|
||||
Terminate ();
|
||||
break;
|
||||
case eStreamStatusClosing:
|
||||
if (m_SentPackets.empty () && m_SendBuffer.eof ()) // nothing to send
|
||||
{
|
||||
m_Status = eStreamStatusClosed;
|
||||
SendClose ();
|
||||
Terminate ();
|
||||
m_LocalDestination.DeleteStream (shared_from_this ());
|
||||
}
|
||||
break;
|
||||
case eStreamStatusClosed:
|
||||
// already closed
|
||||
Terminate ();
|
||||
m_LocalDestination.DeleteStream (shared_from_this ());
|
||||
break;
|
||||
default:
|
||||
LogPrint (eLogWarning, "Streaming: Unexpected stream status ", (int)m_Status);
|
||||
LogPrint (eLogWarning, "Streaming: Unexpected stream status ", (int)m_Status, "sSID=", m_SendStreamID);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -520,7 +536,7 @@ namespace stream
|
||||
size += 4; // receiveStreamID
|
||||
htobe32buf (packet + size, m_SequenceNumber++);
|
||||
size += 4; // sequenceNum
|
||||
htobe32buf (packet + size, m_LastReceivedSequenceNumber);
|
||||
htobe32buf (packet + size, m_LastReceivedSequenceNumber >= 0 ? m_LastReceivedSequenceNumber : 0);
|
||||
size += 4; // ack Through
|
||||
packet[size] = 0;
|
||||
size++; // NACK count
|
||||
@@ -537,7 +553,7 @@ namespace stream
|
||||
|
||||
p->len = size;
|
||||
m_Service.post (std::bind (&Stream::SendPacket, shared_from_this (), p));
|
||||
LogPrint (eLogDebug, "Streaming: FIN sent");
|
||||
LogPrint (eLogDebug, "Streaming: FIN sent, sSID=", m_SendStreamID);
|
||||
}
|
||||
|
||||
size_t Stream::ConcatenatePackets (uint8_t * buf, size_t len)
|
||||
@@ -569,15 +585,10 @@ namespace stream
|
||||
m_AckSendTimer.cancel ();
|
||||
}
|
||||
SendPackets (std::vector<Packet *> { packet });
|
||||
if (m_Status == eStreamStatusOpen)
|
||||
{
|
||||
bool isEmpty = m_SentPackets.empty ();
|
||||
m_SentPackets.insert (packet);
|
||||
if (isEmpty)
|
||||
ScheduleResend ();
|
||||
}
|
||||
else
|
||||
delete packet;
|
||||
bool isEmpty = m_SentPackets.empty ();
|
||||
m_SentPackets.insert (packet);
|
||||
if (isEmpty)
|
||||
ScheduleResend ();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@@ -591,7 +602,7 @@ namespace stream
|
||||
UpdateCurrentRemoteLease ();
|
||||
if (!m_RemoteLeaseSet)
|
||||
{
|
||||
LogPrint (eLogError, "Streaming: Can't send packets, missing remote LeaseSet");
|
||||
LogPrint (eLogError, "Streaming: Can't send packets, missing remote LeaseSet, sSID=", m_SendStreamID);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -616,7 +627,7 @@ namespace stream
|
||||
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNewOutboundTunnel (m_CurrentOutboundTunnel);
|
||||
if (!m_CurrentOutboundTunnel)
|
||||
{
|
||||
LogPrint (eLogError, "Streaming: No outbound tunnels in the pool");
|
||||
LogPrint (eLogError, "Streaming: No outbound tunnels in the pool, sSID=", m_SendStreamID);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -640,7 +651,7 @@ namespace stream
|
||||
m_CurrentOutboundTunnel->SendTunnelDataMsg (msgs);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Streaming: All leases are expired");
|
||||
LogPrint (eLogWarning, "Streaming: All leases are expired, sSID=", m_SendStreamID);
|
||||
}
|
||||
|
||||
|
||||
@@ -659,7 +670,7 @@ namespace stream
|
||||
// check for resend attempts
|
||||
if (m_NumResendAttempts >= MAX_NUM_RESEND_ATTEMPTS)
|
||||
{
|
||||
LogPrint (eLogWarning, "Streaming: packet was not ACKed after ", MAX_NUM_RESEND_ATTEMPTS, " attempts, terminate");
|
||||
LogPrint (eLogWarning, "Streaming: packet was not ACKed after ", MAX_NUM_RESEND_ATTEMPTS, " attempts, terminate, rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID);
|
||||
m_Status = eStreamStatusReset;
|
||||
Close ();
|
||||
return;
|
||||
@@ -694,13 +705,13 @@ namespace stream
|
||||
case 4:
|
||||
if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr);
|
||||
UpdateCurrentRemoteLease (); // pick another lease
|
||||
LogPrint (eLogWarning, "Streaming: Another remote lease has been selected for stream");
|
||||
LogPrint (eLogWarning, "Streaming: Another remote lease has been selected for stream with rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID);
|
||||
break;
|
||||
case 3:
|
||||
// pick another outbound tunnel
|
||||
if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr);
|
||||
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel);
|
||||
LogPrint (eLogWarning, "Streaming: Another outbound tunnel has been selected for stream");
|
||||
LogPrint (eLogWarning, "Streaming: Another outbound tunnel has been selected for stream with sSID=", m_SendStreamID);
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
@@ -716,7 +727,7 @@ namespace stream
|
||||
{
|
||||
if (m_LastReceivedSequenceNumber < 0)
|
||||
{
|
||||
LogPrint (eLogWarning, "Streaming: SYN has not been recived after ", ACK_SEND_TIMEOUT, " milliseconds after follow on, terminate");
|
||||
LogPrint (eLogWarning, "Streaming: SYN has not been received after ", ACK_SEND_TIMEOUT, " milliseconds after follow on, terminate rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID);
|
||||
m_Status = eStreamStatusReset;
|
||||
Close ();
|
||||
return;
|
||||
@@ -751,7 +762,7 @@ namespace stream
|
||||
bool updated = false;
|
||||
if (expired && m_CurrentRemoteLease)
|
||||
{
|
||||
for (auto it: leases)
|
||||
for (const auto& it: leases)
|
||||
if ((it->tunnelGateway == m_CurrentRemoteLease->tunnelGateway) && (it->tunnelID != m_CurrentRemoteLease->tunnelID))
|
||||
{
|
||||
m_CurrentRemoteLease = it;
|
||||
@@ -787,7 +798,7 @@ namespace stream
|
||||
|
||||
StreamingDestination::~StreamingDestination ()
|
||||
{
|
||||
for (auto it: m_SavedPackets)
|
||||
for (auto& it: m_SavedPackets)
|
||||
{
|
||||
for (auto it1: it.second) delete it1;
|
||||
it.second.clear ();
|
||||
@@ -819,7 +830,7 @@ namespace stream
|
||||
it->second->HandleNextPacket (packet);
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "Streaming: Unknown stream sendStreamID=", sendStreamID);
|
||||
LogPrint (eLogError, "Streaming: Unknown stream sSID=", sendStreamID);
|
||||
delete packet;
|
||||
}
|
||||
}
|
||||
@@ -835,7 +846,7 @@ namespace stream
|
||||
auto it = m_SavedPackets.find (receiveStreamID);
|
||||
if (it != m_SavedPackets.end ())
|
||||
{
|
||||
LogPrint (eLogDebug, "Streaming: Processing ", it->second.size (), " saved packets for receiveStreamID=", receiveStreamID);
|
||||
LogPrint (eLogDebug, "Streaming: Processing ", it->second.size (), " saved packets for rSID=", receiveStreamID);
|
||||
for (auto it1: it->second)
|
||||
incomingStream->HandleNextPacket (it1);
|
||||
m_SavedPackets.erase (it);
|
||||
@@ -854,7 +865,7 @@ namespace stream
|
||||
m_PendingIncomingTimer.expires_from_now (boost::posix_time::seconds(PENDING_INCOMING_TIMEOUT));
|
||||
m_PendingIncomingTimer.async_wait (std::bind (&StreamingDestination::HandlePendingIncomingTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
LogPrint (eLogDebug, "Streaming: Pending incoming stream added");
|
||||
LogPrint (eLogDebug, "Streaming: Pending incoming stream added, rSID=", receiveStreamID);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -866,7 +877,7 @@ namespace stream
|
||||
else // follow on packet without SYN
|
||||
{
|
||||
uint32_t receiveStreamID = packet->GetReceiveStreamID ();
|
||||
for (auto it: m_Streams)
|
||||
for (auto& it: m_Streams)
|
||||
if (it.second->GetSendStreamID () == receiveStreamID)
|
||||
{
|
||||
// found
|
||||
@@ -933,7 +944,7 @@ namespace stream
|
||||
m_Owner->GetService ().post([acceptor, this](void)
|
||||
{
|
||||
m_Acceptor = acceptor;
|
||||
for (auto it: m_PendingIncomingStreams)
|
||||
for (auto& it: m_PendingIncomingStreams)
|
||||
if (it->GetStatus () == eStreamStatusOpen) // still open?
|
||||
m_Acceptor (it);
|
||||
m_PendingIncomingStreams.clear ();
|
||||
@@ -952,7 +963,7 @@ namespace stream
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint (eLogWarning, "Streaming: Pending incoming timeout expired");
|
||||
for (auto it: m_PendingIncomingStreams)
|
||||
for (auto& it: m_PendingIncomingStreams)
|
||||
it->Close ();
|
||||
m_PendingIncomingStreams.clear ();
|
||||
}
|
||||
|
||||
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__ */
|
||||
@@ -92,7 +92,7 @@ namespace tunnel
|
||||
{
|
||||
if (isEndpoint)
|
||||
{
|
||||
LogPrint (eLogInfo, "TransitTunnel: endpoint ", receiveTunnelID, " created");
|
||||
LogPrint (eLogDebug, "TransitTunnel: endpoint ", receiveTunnelID, " created");
|
||||
return std::make_shared<TransitTunnelEndpoint> (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
|
||||
}
|
||||
else if (isGateway)
|
||||
@@ -102,7 +102,7 @@ namespace tunnel
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogInfo, "TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created");
|
||||
LogPrint (eLogDebug, "TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created");
|
||||
return std::make_shared<TransitTunnelParticipant> (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,8 +53,8 @@ namespace transport
|
||||
{
|
||||
public:
|
||||
|
||||
TransportSession (std::shared_ptr<const i2p::data::RouterInfo> router):
|
||||
m_DHKeysPair (nullptr), m_NumSentBytes (0), m_NumReceivedBytes (0), m_IsOutgoing (router)
|
||||
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 (router)
|
||||
m_RemoteIdentity = router->GetRouterIdentity ();
|
||||
@@ -70,6 +70,9 @@ namespace transport
|
||||
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 SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) = 0;
|
||||
|
||||
protected:
|
||||
@@ -78,6 +81,7 @@ namespace transport
|
||||
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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
217
Transports.cpp
217
Transports.cpp
@@ -1,4 +1,3 @@
|
||||
#include <openssl/dh.h>
|
||||
#include "Log.h"
|
||||
#include "Crypto.h"
|
||||
#include "RouterContext.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
|
||||
}
|
||||
}
|
||||
@@ -61,7 +60,7 @@ namespace transport
|
||||
{
|
||||
auto pair = std::make_shared<i2p::crypto::DHKeys> ();
|
||||
pair->GenerateKeys ();
|
||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
||||
m_Queue.push (pair);
|
||||
}
|
||||
}
|
||||
@@ -70,7 +69,7 @@ namespace transport
|
||||
std::shared_ptr<i2p::crypto::DHKeys> DHKeysPairSupplier::Acquire ()
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
||||
if (!m_Queue.empty ())
|
||||
{
|
||||
auto pair = m_Queue.front ();
|
||||
@@ -87,14 +86,14 @@ namespace transport
|
||||
|
||||
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_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,28 +105,45 @@ 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)
|
||||
{
|
||||
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 (eLogInfo, "Transports: 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
|
||||
@@ -200,14 +216,14 @@ namespace transport
|
||||
|
||||
bool Transports::IsBandwidthExceeded () const
|
||||
{
|
||||
if (i2p::context.GetRouterInfo ().IsExtraBandwidth ()) return false;
|
||||
auto limit = i2p::context.GetBandwidthLimit() * 1024; // convert to bytes
|
||||
auto bw = std::max (m_InBandwidth, m_OutBandwidth);
|
||||
return bw > (i2p::context.GetRouterInfo ().IsHighBandwidth () ? HIGH_BANDWIDTH_LIMIT : LOW_BANDWIDTH_LIMIT);
|
||||
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 });
|
||||
SendMessages (ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > {msg });
|
||||
}
|
||||
|
||||
void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs)
|
||||
@@ -220,7 +236,7 @@ namespace transport
|
||||
if (ident == i2p::context.GetRouterInfo ().GetIdentHash ())
|
||||
{
|
||||
// we send it to ourself
|
||||
for (auto it: msgs)
|
||||
for (auto& it: msgs)
|
||||
i2p::HandleI2NPMessage (it);
|
||||
return;
|
||||
}
|
||||
@@ -232,7 +248,7 @@ namespace transport
|
||||
{
|
||||
auto r = netdb.FindRouter (ident);
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
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;
|
||||
}
|
||||
@@ -248,8 +264,17 @@ namespace transport
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,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
|
||||
@@ -289,7 +314,7 @@ namespace transport
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Transports: NTCP address is not present for ", i2p::data::GetIdentHashAbbreviation (ident), ", trying SSU");
|
||||
LogPrint (eLogDebug, "Transports: NTCP address is not present for ", i2p::data::GetIdentHashAbbreviation (ident), ", trying SSU");
|
||||
}
|
||||
if (peer.numAttempts == 1)// SSU
|
||||
{
|
||||
@@ -321,7 +346,7 @@ namespace transport
|
||||
}
|
||||
LogPrint (eLogError, "Transports: No NTCP or SSU addresses available");
|
||||
peer.Done ();
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
m_Peers.erase (ident);
|
||||
return false;
|
||||
}
|
||||
@@ -353,7 +378,7 @@ namespace transport
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "Transports: RouterInfo not found, Failed to send messages");
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
m_Peers.erase (it);
|
||||
}
|
||||
}
|
||||
@@ -376,18 +401,28 @@ namespace transport
|
||||
auto& peer = it1->second;
|
||||
if (!ecode && peer.router)
|
||||
{
|
||||
auto address = (*it).endpoint ().address ();
|
||||
LogPrint (eLogDebug, "Transports: ", (*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, "Transports: Unable to resolve NTCP address: ", ecode.message ());
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
m_Peers.erase (it1);
|
||||
}
|
||||
}
|
||||
@@ -409,17 +444,27 @@ namespace transport
|
||||
auto& peer = it1->second;
|
||||
if (!ecode && peer.router)
|
||||
{
|
||||
auto address = (*it).endpoint ().address ();
|
||||
LogPrint (eLogDebug, "Transports: ", (*it).host_name (), " has been resolved to ", address);
|
||||
auto addr = peer.router->GetSSUAddress (!context.SupportsV6 ());;
|
||||
if (addr)
|
||||
{
|
||||
m_SSUServer->CreateSession (peer.router, address, addr->port);
|
||||
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->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);
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
m_Peers.erase (it1);
|
||||
}
|
||||
}
|
||||
@@ -427,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)
|
||||
@@ -437,26 +482,34 @@ namespace transport
|
||||
{
|
||||
m_SSUServer->DeleteSession (ssuSession);
|
||||
LogPrint (eLogDebug, "Transports: SSU session closed");
|
||||
}
|
||||
// TODO: delete NTCP
|
||||
}
|
||||
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 ()
|
||||
{
|
||||
if (m_SSUServer)
|
||||
{
|
||||
#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
|
||||
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
|
||||
m_SSUServer->CreateSession (router); // no peer test
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -479,7 +532,7 @@ namespace transport
|
||||
statusChanged = true;
|
||||
i2p::context.SetStatus (eRouterStatusTesting); // first time only
|
||||
}
|
||||
m_SSUServer->CreateSession (router, true); // peer test
|
||||
m_SSUServer->CreateSession (router, true); // peer test
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -498,20 +551,34 @@ namespace transport
|
||||
void Transports::PeerConnected (std::shared_ptr<TransportSession> session)
|
||||
{
|
||||
m_Service.post([session, this]()
|
||||
{
|
||||
{
|
||||
auto remoteIdentity = session->GetRemoteIdentity ();
|
||||
if (!remoteIdentity) return;
|
||||
auto ident = remoteIdentity->GetIdentHash ();
|
||||
auto it = m_Peers.find (ident);
|
||||
if (it != m_Peers.end ())
|
||||
{
|
||||
bool sendDatabaseStore = true;
|
||||
if (it->second.delayedMessages.size () > 0)
|
||||
{
|
||||
// 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
|
||||
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
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
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 (), {} }));
|
||||
}
|
||||
});
|
||||
@@ -520,7 +587,7 @@ namespace transport
|
||||
void Transports::PeerDisconnected (std::shared_ptr<TransportSession> session)
|
||||
{
|
||||
m_Service.post([session, this]()
|
||||
{
|
||||
{
|
||||
auto remoteIdentity = session->GetRemoteIdentity ();
|
||||
if (!remoteIdentity) return;
|
||||
auto ident = remoteIdentity->GetIdentHash ();
|
||||
@@ -534,7 +601,7 @@ namespace transport
|
||||
ConnectToPeer (ident, it->second);
|
||||
else
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
m_Peers.erase (it);
|
||||
}
|
||||
}
|
||||
@@ -559,14 +626,20 @@ namespace transport
|
||||
if (it->second.sessions.empty () && ts > it->second.creationTime + SESSION_CREATION_TIMEOUT)
|
||||
{
|
||||
LogPrint (eLogWarning, "Transports: Session to peer ", it->first.ToBase64 (), " has not been created in ", SESSION_CREATION_TIMEOUT, " seconds");
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
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
|
||||
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));
|
||||
@@ -575,12 +648,36 @@ namespace transport
|
||||
|
||||
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRandomPeer () const
|
||||
{
|
||||
if (!m_Peers.size ()) return nullptr;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
32
Transports.h
32
Transports.h
@@ -60,14 +60,13 @@ namespace transport
|
||||
|
||||
void Done ()
|
||||
{
|
||||
for (auto it: sessions)
|
||||
for (auto& it: sessions)
|
||||
it->Done ();
|
||||
}
|
||||
};
|
||||
|
||||
const size_t SESSION_CREATION_TIMEOUT = 10; // in seconds
|
||||
const uint32_t LOW_BANDWIDTH_LIMIT = 32*1024; // 32KBs
|
||||
const uint32_t HIGH_BANDWIDTH_LIMIT = 256*1024; // 256KBs
|
||||
const int MAX_NUM_DELAYED_MESSAGES = 50;
|
||||
class Transports
|
||||
{
|
||||
public:
|
||||
@@ -75,9 +74,15 @@ namespace transport
|
||||
Transports ();
|
||||
~Transports ();
|
||||
|
||||
void Start ();
|
||||
void Start (bool enableNTCP=true, bool enableSSU=true);
|
||||
void Stop ();
|
||||
|
||||
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);
|
||||
@@ -94,12 +99,19 @@ namespace transport
|
||||
void UpdateReceivedBytes (uint64_t numBytes) { m_TotalReceivedBytes += numBytes; };
|
||||
uint64_t GetTotalSentBytes () const { return m_TotalSentBytes; };
|
||||
uint64_t GetTotalReceivedBytes () const { return m_TotalReceivedBytes; };
|
||||
uint32_t GetInBandwidth () const { return m_InBandwidth; }; // bytes per second
|
||||
uint32_t GetOutBandwidth () const { return m_OutBandwidth; }; // bytes per second
|
||||
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:
|
||||
@@ -124,7 +136,7 @@ namespace transport
|
||||
|
||||
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;
|
||||
@@ -138,10 +150,14 @@ namespace transport
|
||||
DHKeysPairSupplier m_DHKeysPairSupplier;
|
||||
|
||||
std::atomic<uint64_t> m_TotalSentBytes, m_TotalReceivedBytes;
|
||||
uint32_t m_InBandwidth, m_OutBandwidth;
|
||||
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
|
||||
|
||||
370
Tunnel.cpp
370
Tunnel.cpp
@@ -3,7 +3,7 @@
|
||||
#include <thread>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <openssl/rand.h>
|
||||
#include "Crypto.h"
|
||||
#include "RouterContext.h"
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
@@ -13,19 +13,19 @@
|
||||
#include "Tunnel.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
{
|
||||
namespace tunnel
|
||||
{
|
||||
|
||||
{
|
||||
|
||||
Tunnel::Tunnel (std::shared_ptr<const TunnelConfig> config):
|
||||
TunnelBase (config->GetTunnelID (), config->GetNextTunnelID (), config->GetNextIdentHash ()),
|
||||
m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending), m_IsRecreated (false)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
Tunnel::~Tunnel ()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> outboundTunnel)
|
||||
{
|
||||
@@ -33,7 +33,7 @@ namespace tunnel
|
||||
int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : numHops;
|
||||
auto msg = NewI2NPShortMessage ();
|
||||
*msg->GetPayload () = numRecords;
|
||||
msg->len += numRecords*TUNNEL_BUILD_RECORD_SIZE + 1;
|
||||
msg->len += numRecords*TUNNEL_BUILD_RECORD_SIZE + 1;
|
||||
|
||||
// shuffle records
|
||||
std::vector<int> recordIndicies;
|
||||
@@ -56,13 +56,13 @@ namespace tunnel
|
||||
hop->recordIndex = idx;
|
||||
i++;
|
||||
hop = hop->next;
|
||||
}
|
||||
// fill up fake records with random data
|
||||
}
|
||||
// fill up fake records with random data
|
||||
for (int i = numHops; i < numRecords; i++)
|
||||
{
|
||||
int idx = recordIndicies[i];
|
||||
RAND_bytes (records + idx*TUNNEL_BUILD_RECORD_SIZE, TUNNEL_BUILD_RECORD_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
// decrypt real records
|
||||
i2p::crypto::CBCDecryption decryption;
|
||||
@@ -73,31 +73,31 @@ namespace tunnel
|
||||
// decrypt records after current hop
|
||||
TunnelHopConfig * hop1 = hop->next;
|
||||
while (hop1)
|
||||
{
|
||||
{
|
||||
decryption.SetIV (hop->replyIV);
|
||||
uint8_t * record = records + hop1->recordIndex*TUNNEL_BUILD_RECORD_SIZE;
|
||||
decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record);
|
||||
hop1 = hop1->next;
|
||||
}
|
||||
}
|
||||
hop = hop->prev;
|
||||
}
|
||||
}
|
||||
msg->FillI2NPMessageHeader (eI2NPVariableTunnelBuild);
|
||||
|
||||
// send message
|
||||
if (outboundTunnel)
|
||||
outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, msg);
|
||||
outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, msg);
|
||||
else
|
||||
i2p::transport::transports.SendMessage (GetNextIdentHash (), msg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "Tunnel: TunnelBuildResponse ", (int)msg[0], " records.");
|
||||
|
||||
|
||||
i2p::crypto::CBCDecryption decryption;
|
||||
TunnelHopConfig * hop = m_Config->GetLastHop ();
|
||||
while (hop)
|
||||
{
|
||||
{
|
||||
decryption.SetKey (hop->replyKey);
|
||||
// decrypt records before and including current hop
|
||||
TunnelHopConfig * hop1 = hop;
|
||||
@@ -105,22 +105,22 @@ namespace tunnel
|
||||
{
|
||||
auto idx = hop1->recordIndex;
|
||||
if (idx >= 0 && idx < msg[0])
|
||||
{
|
||||
{
|
||||
uint8_t * record = msg + 1 + idx*TUNNEL_BUILD_RECORD_SIZE;
|
||||
decryption.SetIV (hop->replyIV);
|
||||
decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record);
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Tunnel: hop index ", idx, " is out of range");
|
||||
hop1 = hop1->prev;
|
||||
}
|
||||
}
|
||||
hop = hop->prev;
|
||||
}
|
||||
|
||||
bool established = true;
|
||||
hop = m_Config->GetFirstHop ();
|
||||
while (hop)
|
||||
{
|
||||
{
|
||||
const uint8_t * record = msg + 1 + hop->recordIndex*TUNNEL_BUILD_RECORD_SIZE;
|
||||
uint8_t ret = record[BUILD_RESPONSE_RECORD_RET_OFFSET];
|
||||
LogPrint (eLogDebug, "Tunnel: Build response ret code=", (int)ret);
|
||||
@@ -143,12 +143,12 @@ namespace tunnel
|
||||
tunnelHop->decryption.SetKeys (hop->layerKey, hop->ivKey);
|
||||
m_Hops.push_back (std::unique_ptr<TunnelHop>(tunnelHop));
|
||||
hop = hop->prev;
|
||||
}
|
||||
}
|
||||
m_Config = nullptr;
|
||||
}
|
||||
}
|
||||
if (established) m_State = eTunnelStateEstablished;
|
||||
return established;
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnel::EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out)
|
||||
{
|
||||
@@ -158,20 +158,20 @@ namespace tunnel
|
||||
{
|
||||
it->decryption.Decrypt (inPayload, outPayload);
|
||||
inPayload = outPayload;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnel::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg)
|
||||
{
|
||||
LogPrint (eLogWarning, "Tunnel: Can't send I2NP messages without delivery instructions");
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > Tunnel::GetPeers () const
|
||||
{
|
||||
auto peers = GetInvertedPeers ();
|
||||
std::reverse (peers.begin (), peers.end ());
|
||||
std::reverse (peers.begin (), peers.end ());
|
||||
return peers;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > Tunnel::GetInvertedPeers () const
|
||||
{
|
||||
@@ -179,53 +179,54 @@ namespace tunnel
|
||||
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > ret;
|
||||
for (auto& it: m_Hops)
|
||||
ret.push_back (it->ident);
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Tunnel::PrintHops (std::stringstream& s) const
|
||||
{
|
||||
for (auto& it: m_Hops)
|
||||
{
|
||||
s << " ⇒ ";
|
||||
{
|
||||
s << " ⇒ ";
|
||||
s << i2p::data::GetIdentHashAbbreviation (it->ident->GetIdentHash ());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr<const I2NPMessage> msg)
|
||||
{
|
||||
if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive
|
||||
if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive
|
||||
auto newMsg = CreateEmptyTunnelDataMsg ();
|
||||
EncryptTunnelMsg (msg, newMsg);
|
||||
newMsg->from = shared_from_this ();
|
||||
m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg);
|
||||
}
|
||||
m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg);
|
||||
}
|
||||
|
||||
void InboundTunnel::Print (std::stringstream& s) const
|
||||
{
|
||||
PrintHops (s);
|
||||
s << " ⇒ " << GetTunnelID () << ":me";
|
||||
}
|
||||
s << " ⇒ " << GetTunnelID () << ":me";
|
||||
}
|
||||
|
||||
ZeroHopsInboundTunnel::ZeroHopsInboundTunnel ():
|
||||
InboundTunnel (std::make_shared<ZeroHopsTunnelConfig> ()),
|
||||
m_NumReceivedBytes (0)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ZeroHopsInboundTunnel::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg)
|
||||
{
|
||||
if (msg)
|
||||
{
|
||||
{
|
||||
m_NumReceivedBytes += msg->GetLength ();
|
||||
msg->from = shared_from_this ();
|
||||
HandleI2NPMessage (msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ZeroHopsInboundTunnel::Print (std::stringstream& s) const
|
||||
{
|
||||
s << " ⇒ " << GetTunnelID () << ":me";
|
||||
}
|
||||
|
||||
s << " ⇒ " << GetTunnelID () << ":me";
|
||||
}
|
||||
|
||||
void OutboundTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr<i2p::I2NPMessage> msg)
|
||||
{
|
||||
TunnelMessageBlock block;
|
||||
@@ -233,28 +234,28 @@ namespace tunnel
|
||||
{
|
||||
block.hash = gwHash;
|
||||
if (gwTunnel)
|
||||
{
|
||||
{
|
||||
block.deliveryType = eDeliveryTypeTunnel;
|
||||
block.tunnelID = gwTunnel;
|
||||
}
|
||||
}
|
||||
else
|
||||
block.deliveryType = eDeliveryTypeRouter;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
block.deliveryType = eDeliveryTypeLocal;
|
||||
block.data = msg;
|
||||
|
||||
|
||||
SendTunnelDataMsg ({block});
|
||||
}
|
||||
|
||||
|
||||
void OutboundTunnel::SendTunnelDataMsg (const std::vector<TunnelMessageBlock>& msgs)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_SendMutex);
|
||||
for (auto& it : msgs)
|
||||
m_Gateway.PutTunnelDataMsg (it);
|
||||
m_Gateway.SendBuffer ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void OutboundTunnel::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg)
|
||||
{
|
||||
LogPrint (eLogError, "Tunnel: incoming message for outbound tunnel ", GetTunnelID ());
|
||||
@@ -264,9 +265,9 @@ namespace tunnel
|
||||
{
|
||||
s << GetTunnelID () << ":me";
|
||||
PrintHops (s);
|
||||
s << " ⇒ ";
|
||||
}
|
||||
|
||||
s << " ⇒ ";
|
||||
}
|
||||
|
||||
ZeroHopsOutboundTunnel::ZeroHopsOutboundTunnel ():
|
||||
OutboundTunnel (std::make_shared<ZeroHopsTunnelConfig> ()),
|
||||
m_NumSentBytes (0)
|
||||
@@ -285,30 +286,30 @@ namespace tunnel
|
||||
case eDeliveryTypeTunnel:
|
||||
i2p::transport::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data));
|
||||
break;
|
||||
case eDeliveryTypeRouter:
|
||||
case eDeliveryTypeRouter:
|
||||
i2p::transport::transports.SendMessage (msg.hash, msg.data);
|
||||
break;
|
||||
default:
|
||||
LogPrint (eLogError, "Tunnel: Unknown delivery type ", (int)msg.deliveryType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ZeroHopsOutboundTunnel::Print (std::stringstream& s) const
|
||||
{
|
||||
s << GetTunnelID () << ":me ⇒ ";
|
||||
s << GetTunnelID () << ":me ⇒ ";
|
||||
}
|
||||
|
||||
Tunnels tunnels;
|
||||
|
||||
|
||||
Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr),
|
||||
m_NumSuccesiveTunnelCreations (0), m_NumFailedTunnelCreations (0)
|
||||
{
|
||||
}
|
||||
|
||||
Tunnels::~Tunnels ()
|
||||
|
||||
Tunnels::~Tunnels ()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<TunnelBase> Tunnels::GetTunnel (uint32_t tunnelID)
|
||||
{
|
||||
@@ -316,35 +317,35 @@ namespace tunnel
|
||||
if (it != m_Tunnels.end ())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::shared_ptr<InboundTunnel> Tunnels::GetPendingInboundTunnel (uint32_t replyMsgID)
|
||||
{
|
||||
return GetPendingTunnel (replyMsgID, m_PendingInboundTunnels);
|
||||
return GetPendingTunnel (replyMsgID, m_PendingInboundTunnels);
|
||||
}
|
||||
|
||||
std::shared_ptr<OutboundTunnel> Tunnels::GetPendingOutboundTunnel (uint32_t replyMsgID)
|
||||
{
|
||||
return GetPendingTunnel (replyMsgID, m_PendingOutboundTunnels);
|
||||
}
|
||||
return GetPendingTunnel (replyMsgID, m_PendingOutboundTunnels);
|
||||
}
|
||||
|
||||
template<class TTunnel>
|
||||
template<class TTunnel>
|
||||
std::shared_ptr<TTunnel> Tunnels::GetPendingTunnel (uint32_t replyMsgID, const std::map<uint32_t, std::shared_ptr<TTunnel> >& pendingTunnels)
|
||||
{
|
||||
auto it = pendingTunnels.find(replyMsgID);
|
||||
if (it != pendingTunnels.end () && it->second->GetState () == eTunnelStatePending)
|
||||
{
|
||||
it->second->SetState (eTunnelStateBuildReplyReceived);
|
||||
{
|
||||
it->second->SetState (eTunnelStateBuildReplyReceived);
|
||||
return it->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<InboundTunnel> Tunnels::GetNextInboundTunnel ()
|
||||
{
|
||||
std::shared_ptr<InboundTunnel> tunnel;
|
||||
size_t minReceived = 0;
|
||||
for (auto it : m_InboundTunnels)
|
||||
for (const auto& it : m_InboundTunnels)
|
||||
{
|
||||
if (!it->IsEstablished ()) continue;
|
||||
if (!tunnel || it->GetNumReceivedBytes () < minReceived)
|
||||
@@ -352,26 +353,26 @@ namespace tunnel
|
||||
tunnel = it;
|
||||
minReceived = it->GetNumReceivedBytes ();
|
||||
}
|
||||
}
|
||||
}
|
||||
return tunnel;
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<OutboundTunnel> Tunnels::GetNextOutboundTunnel ()
|
||||
{
|
||||
if (!m_OutboundTunnels.size ()) return nullptr;
|
||||
if (m_OutboundTunnels.empty ()) return nullptr;
|
||||
uint32_t ind = rand () % m_OutboundTunnels.size (), i = 0;
|
||||
std::shared_ptr<OutboundTunnel> tunnel;
|
||||
for (auto it: m_OutboundTunnels)
|
||||
{
|
||||
for (const auto& it: m_OutboundTunnels)
|
||||
{
|
||||
if (it->IsEstablished ())
|
||||
{
|
||||
tunnel = it;
|
||||
i++;
|
||||
}
|
||||
if (i > ind && tunnel) break;
|
||||
}
|
||||
}
|
||||
return tunnel;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<TunnelPool> Tunnels::CreateTunnelPool (int numInboundHops,
|
||||
int numOutboundHops, int numInboundTunnels, int numOutboundTunnels)
|
||||
@@ -380,19 +381,19 @@ namespace tunnel
|
||||
std::unique_lock<std::mutex> l(m_PoolsMutex);
|
||||
m_Pools.push_back (pool);
|
||||
return pool;
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnels::DeleteTunnelPool (std::shared_ptr<TunnelPool> pool)
|
||||
{
|
||||
if (pool)
|
||||
{
|
||||
{
|
||||
StopTunnelPool (pool);
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_PoolsMutex);
|
||||
m_Pools.remove (pool);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnels::StopTunnelPool (std::shared_ptr<TunnelPool> pool)
|
||||
{
|
||||
@@ -400,64 +401,64 @@ namespace tunnel
|
||||
{
|
||||
pool->SetActive (false);
|
||||
pool->DetachTunnels ();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnels::AddTransitTunnel (std::shared_ptr<TransitTunnel> tunnel)
|
||||
{
|
||||
if (m_Tunnels.emplace (tunnel->GetTunnelID (), tunnel).second)
|
||||
m_TransitTunnels.push_back (tunnel);
|
||||
else
|
||||
LogPrint (eLogError, "Tunnel: tunnel with id ", tunnel->GetTunnelID (), " already exists");
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnels::Start ()
|
||||
{
|
||||
m_IsRunning = true;
|
||||
m_Thread = new std::thread (std::bind (&Tunnels::Run, this));
|
||||
}
|
||||
|
||||
|
||||
void Tunnels::Stop ()
|
||||
{
|
||||
m_IsRunning = false;
|
||||
m_Queue.WakeUp ();
|
||||
if (m_Thread)
|
||||
{
|
||||
{
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnels::Run ()
|
||||
{
|
||||
std::this_thread::sleep_for (std::chrono::seconds(1)); // wait for other parts are ready
|
||||
|
||||
|
||||
uint64_t lastTs = 0;
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
{
|
||||
auto msg = m_Queue.GetNextWithTimeout (1000); // 1 sec
|
||||
if (msg)
|
||||
{
|
||||
{
|
||||
uint32_t prevTunnelID = 0, tunnelID = 0;
|
||||
std::shared_ptr<TunnelBase> prevTunnel;
|
||||
std::shared_ptr<TunnelBase> prevTunnel;
|
||||
do
|
||||
{
|
||||
std::shared_ptr<TunnelBase> tunnel;
|
||||
uint8_t typeID = msg->GetTypeID ();
|
||||
switch (typeID)
|
||||
{
|
||||
{
|
||||
case eI2NPTunnelData:
|
||||
case eI2NPTunnelGateway:
|
||||
{
|
||||
{
|
||||
tunnelID = bufbe32toh (msg->GetPayload ());
|
||||
if (tunnelID == prevTunnelID)
|
||||
tunnel = prevTunnel;
|
||||
else if (prevTunnel)
|
||||
prevTunnel->FlushTunnelDataMsgs ();
|
||||
|
||||
|
||||
if (!tunnel)
|
||||
tunnel = GetTunnel (tunnelID);
|
||||
if (tunnel)
|
||||
@@ -467,20 +468,21 @@ namespace tunnel
|
||||
else // tunnel gateway assumed
|
||||
HandleTunnelGatewayMsg (tunnel, msg);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Tunnel: tunnel with id ", tunnelID, " not found");
|
||||
else
|
||||
LogPrint (eLogWarning, "Tunnel: tunnel not found, tunnelID=", tunnelID, " previousTunnelID=", prevTunnelID, " type=", (int)typeID);
|
||||
|
||||
break;
|
||||
}
|
||||
case eI2NPVariableTunnelBuild:
|
||||
}
|
||||
case eI2NPVariableTunnelBuild:
|
||||
case eI2NPVariableTunnelBuildReply:
|
||||
case eI2NPTunnelBuild:
|
||||
case eI2NPTunnelBuildReply:
|
||||
case eI2NPTunnelBuildReply:
|
||||
HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ());
|
||||
break;
|
||||
break;
|
||||
default:
|
||||
LogPrint (eLogWarning, "Tunnel: unexpected messsage type ", (int) typeID);
|
||||
}
|
||||
|
||||
|
||||
msg = m_Queue.Get ();
|
||||
if (msg)
|
||||
{
|
||||
@@ -491,8 +493,8 @@ namespace tunnel
|
||||
tunnel->FlushTunnelDataMsgs ();
|
||||
}
|
||||
while (msg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (ts - lastTs >= 15) // manage tunnels every 15 seconds
|
||||
{
|
||||
@@ -503,9 +505,9 @@ namespace tunnel
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "Tunnel: runtime exception: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnels::HandleTunnelGatewayMsg (std::shared_ptr<TunnelBase> tunnel, std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
@@ -526,7 +528,7 @@ namespace tunnel
|
||||
msg->len = msg->offset + len;
|
||||
auto typeID = msg->GetTypeID ();
|
||||
LogPrint (eLogDebug, "Tunnel: gateway of ", (int) len, " bytes for tunnel ", tunnel->GetTunnelID (), ", msg type ", (int)typeID);
|
||||
|
||||
|
||||
if (IsRouterInfoMsg (msg) || typeID == eI2NPDatabaseSearchReply)
|
||||
// transit DatabaseStore my contain new/updated RI
|
||||
// or DatabaseSearchReply with new routers
|
||||
@@ -541,7 +543,7 @@ namespace tunnel
|
||||
ManageOutboundTunnels ();
|
||||
ManageTransitTunnels ();
|
||||
ManageTunnelPools ();
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnels::ManagePendingTunnels ()
|
||||
{
|
||||
@@ -555,14 +557,14 @@ namespace tunnel
|
||||
// check pending tunnel. delete failed or timeout
|
||||
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto it = pendingTunnels.begin (); it != pendingTunnels.end ();)
|
||||
{
|
||||
{
|
||||
auto tunnel = it->second;
|
||||
switch (tunnel->GetState ())
|
||||
{
|
||||
case eTunnelStatePending:
|
||||
if (ts > tunnel->GetCreationTime () + TUNNEL_CREATION_TIMEOUT)
|
||||
{
|
||||
LogPrint (eLogWarning, "Tunnel: pending build request ", it->first, " timeout, deleted");
|
||||
LogPrint (eLogDebug, "Tunnel: pending build request ", it->first, " timeout, deleted");
|
||||
// update stats
|
||||
auto config = tunnel->GetTunnelConfig ();
|
||||
if (config)
|
||||
@@ -577,30 +579,30 @@ namespace tunnel
|
||||
profile->TunnelNonReplied ();
|
||||
}
|
||||
hop = hop->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// delete
|
||||
it = pendingTunnels.erase (it);
|
||||
m_NumFailedTunnelCreations++;
|
||||
}
|
||||
else
|
||||
it++;
|
||||
++it;
|
||||
break;
|
||||
case eTunnelStateBuildFailed:
|
||||
LogPrint (eLogWarning, "Tunnel: pending build request ", it->first, " failed, deleted");
|
||||
LogPrint (eLogDebug, "Tunnel: pending build request ", it->first, " failed, deleted");
|
||||
it = pendingTunnels.erase (it);
|
||||
m_NumFailedTunnelCreations++;
|
||||
break;
|
||||
case eTunnelStateBuildReplyReceived:
|
||||
// intermediate state, will be either established of build failed
|
||||
it++;
|
||||
break;
|
||||
++it;
|
||||
break;
|
||||
default:
|
||||
// success
|
||||
it = pendingTunnels.erase (it);
|
||||
m_NumSuccesiveTunnelCreations++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnels::ManageOutboundTunnels ()
|
||||
@@ -618,26 +620,26 @@ namespace tunnel
|
||||
pool->TunnelExpired (tunnel);
|
||||
// we don't have outbound tunnels in m_Tunnels
|
||||
it = m_OutboundTunnels.erase (it);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tunnel->IsEstablished ())
|
||||
{
|
||||
{
|
||||
if (!tunnel->IsRecreated () && ts + TUNNEL_RECREATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
|
||||
{
|
||||
tunnel->SetIsRecreated ();
|
||||
tunnel->SetIsRecreated ();
|
||||
auto pool = tunnel->GetTunnelPool ();
|
||||
if (pool)
|
||||
pool->RecreateOutboundTunnel (tunnel);
|
||||
}
|
||||
if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
|
||||
tunnel->SetState (eTunnelStateExpiring);
|
||||
}
|
||||
it++;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (m_OutboundTunnels.size () < 5)
|
||||
{
|
||||
// trying to create one more oubound tunnel
|
||||
@@ -646,12 +648,12 @@ namespace tunnel
|
||||
if (!inboundTunnel || !router) return;
|
||||
LogPrint (eLogDebug, "Tunnel: creating one hop outbound tunnel");
|
||||
CreateTunnel<OutboundTunnel> (
|
||||
std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > { router->GetRouterIdentity () },
|
||||
inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ())
|
||||
);
|
||||
std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > { router->GetRouterIdentity () },
|
||||
inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Tunnels::ManageInboundTunnels ()
|
||||
{
|
||||
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
@@ -667,26 +669,26 @@ namespace tunnel
|
||||
pool->TunnelExpired (tunnel);
|
||||
m_Tunnels.erase (tunnel->GetTunnelID ());
|
||||
it = m_InboundTunnels.erase (it);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tunnel->IsEstablished ())
|
||||
{
|
||||
{
|
||||
if (!tunnel->IsRecreated () && ts + TUNNEL_RECREATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
|
||||
{
|
||||
tunnel->SetIsRecreated ();
|
||||
tunnel->SetIsRecreated ();
|
||||
auto pool = tunnel->GetTunnelPool ();
|
||||
if (pool)
|
||||
pool->RecreateInboundTunnel (tunnel);
|
||||
}
|
||||
|
||||
|
||||
if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
|
||||
tunnel->SetState (eTunnelStateExpiring);
|
||||
}
|
||||
}
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_InboundTunnels.empty ())
|
||||
{
|
||||
@@ -700,10 +702,10 @@ namespace tunnel
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (m_OutboundTunnels.empty () || m_InboundTunnels.size () < 5)
|
||||
{
|
||||
// trying to create one more inbound tunnel
|
||||
// trying to create one more inbound tunnel
|
||||
auto router = i2p::data::netdb.GetRandomRouter ();
|
||||
if (!router) {
|
||||
LogPrint (eLogWarning, "Tunnel: can't find any router, skip creating tunnel");
|
||||
@@ -712,9 +714,9 @@ namespace tunnel
|
||||
LogPrint (eLogDebug, "Tunnel: creating one hop inbound tunnel");
|
||||
CreateTunnel<InboundTunnel> (
|
||||
std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > { router->GetRouterIdentity () })
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnels::ManageTransitTunnels ()
|
||||
{
|
||||
@@ -727,36 +729,35 @@ namespace tunnel
|
||||
LogPrint (eLogDebug, "Tunnel: Transit tunnel with id ", tunnel->GetTunnelID (), " expired");
|
||||
m_Tunnels.erase (tunnel->GetTunnelID ());
|
||||
it = m_TransitTunnels.erase (it);
|
||||
}
|
||||
}
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnels::ManageTunnelPools ()
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_PoolsMutex);
|
||||
for (auto it: m_Pools)
|
||||
{
|
||||
auto pool = it;
|
||||
for (auto& pool : m_Pools)
|
||||
{
|
||||
if (pool && pool->IsActive ())
|
||||
{
|
||||
{
|
||||
pool->CreateTunnels ();
|
||||
pool->TestTunnels ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Tunnels::PostTunnelData (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
if (msg) m_Queue.Put (msg);
|
||||
}
|
||||
if (msg) m_Queue.Put (msg);
|
||||
}
|
||||
|
||||
void Tunnels::PostTunnelData (const std::vector<std::shared_ptr<I2NPMessage> >& msgs)
|
||||
{
|
||||
m_Queue.Put (msgs);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<class TTunnel>
|
||||
std::shared_ptr<TTunnel> Tunnels::CreateTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel)
|
||||
{
|
||||
@@ -766,7 +767,23 @@ namespace tunnel
|
||||
AddPendingTunnel (replyMsgID, newTunnel);
|
||||
newTunnel->Build (replyMsgID, outboundTunnel);
|
||||
return newTunnel;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<InboundTunnel> Tunnels::CreateInboundTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel)
|
||||
{
|
||||
if (config)
|
||||
return CreateTunnel<InboundTunnel>(config, outboundTunnel);
|
||||
else
|
||||
return CreateZeroHopsInboundTunnel ();
|
||||
}
|
||||
|
||||
std::shared_ptr<OutboundTunnel> Tunnels::CreateOutboundTunnel (std::shared_ptr<TunnelConfig> config)
|
||||
{
|
||||
if (config)
|
||||
return CreateTunnel<OutboundTunnel>(config);
|
||||
else
|
||||
return CreateZeroHopsOutboundTunnel ();
|
||||
}
|
||||
|
||||
void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel)
|
||||
{
|
||||
@@ -787,20 +804,20 @@ namespace tunnel
|
||||
pool->TunnelCreated (newTunnel);
|
||||
else
|
||||
newTunnel->SetTunnelPool (nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnels::AddInboundTunnel (std::shared_ptr<InboundTunnel> newTunnel)
|
||||
{
|
||||
if (m_Tunnels.emplace (newTunnel->GetTunnelID (), newTunnel).second)
|
||||
{
|
||||
{
|
||||
m_InboundTunnels.push_back (newTunnel);
|
||||
auto pool = newTunnel->GetTunnelPool ();
|
||||
if (!pool)
|
||||
{
|
||||
{
|
||||
// build symmetric outbound tunnel
|
||||
CreateTunnel<OutboundTunnel> (std::make_shared<TunnelConfig>(newTunnel->GetInvertedPeers (),
|
||||
newTunnel->GetNextTunnelID (), newTunnel->GetNextIdentHash ()),
|
||||
GetNextOutboundTunnel ());
|
||||
GetNextOutboundTunnel ());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -812,23 +829,25 @@ namespace tunnel
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Tunnel: tunnel with id ", newTunnel->GetTunnelID (), " already exists");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Tunnels::CreateZeroHopsInboundTunnel ()
|
||||
|
||||
std::shared_ptr<ZeroHopsInboundTunnel> Tunnels::CreateZeroHopsInboundTunnel ()
|
||||
{
|
||||
auto inboundTunnel = std::make_shared<ZeroHopsInboundTunnel> ();
|
||||
inboundTunnel->SetState (eTunnelStateEstablished);
|
||||
m_InboundTunnels.push_back (inboundTunnel);
|
||||
m_Tunnels[inboundTunnel->GetTunnelID ()] = inboundTunnel;
|
||||
}
|
||||
return inboundTunnel;
|
||||
}
|
||||
|
||||
void Tunnels::CreateZeroHopsOutboundTunnel ()
|
||||
std::shared_ptr<ZeroHopsOutboundTunnel> Tunnels::CreateZeroHopsOutboundTunnel ()
|
||||
{
|
||||
auto outboundTunnel = std::make_shared<ZeroHopsOutboundTunnel> ();
|
||||
outboundTunnel->SetState (eTunnelStateEstablished);
|
||||
m_OutboundTunnels.push_back (outboundTunnel);
|
||||
// we don't insert into m_Tunnels
|
||||
return outboundTunnel;
|
||||
}
|
||||
|
||||
int Tunnels::GetTransitTunnelsExpirationTimeout ()
|
||||
@@ -836,11 +855,11 @@ namespace tunnel
|
||||
int timeout = 0;
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
// TODO: possible race condition with I2PControl
|
||||
for (auto it: m_TransitTunnels)
|
||||
for (const auto& it : m_TransitTunnels)
|
||||
{
|
||||
int t = it->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT - ts;
|
||||
if (t > timeout) timeout = t;
|
||||
}
|
||||
}
|
||||
return timeout;
|
||||
}
|
||||
|
||||
@@ -863,4 +882,3 @@ namespace tunnel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
13
Tunnel.h
13
Tunnel.h
@@ -73,7 +73,7 @@ namespace tunnel
|
||||
|
||||
bool HandleTunnelBuildResponse (uint8_t * msg, size_t len);
|
||||
|
||||
virtual void Print (std::stringstream& s) const {};
|
||||
virtual void Print (std::stringstream&) const {};
|
||||
|
||||
// implements TunnelBase
|
||||
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
|
||||
@@ -176,10 +176,10 @@ namespace tunnel
|
||||
void AddTransitTunnel (std::shared_ptr<TransitTunnel> tunnel);
|
||||
void AddOutboundTunnel (std::shared_ptr<OutboundTunnel> newTunnel);
|
||||
void AddInboundTunnel (std::shared_ptr<InboundTunnel> newTunnel);
|
||||
std::shared_ptr<InboundTunnel> CreateInboundTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel);
|
||||
std::shared_ptr<OutboundTunnel> CreateOutboundTunnel (std::shared_ptr<TunnelConfig> config);
|
||||
void PostTunnelData (std::shared_ptr<I2NPMessage> msg);
|
||||
void PostTunnelData (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
|
||||
template<class TTunnel>
|
||||
std::shared_ptr<TTunnel> CreateTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel = nullptr);
|
||||
void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel);
|
||||
void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> tunnel);
|
||||
std::shared_ptr<TunnelPool> CreateTunnelPool (int numInboundHops,
|
||||
@@ -189,6 +189,9 @@ namespace tunnel
|
||||
|
||||
private:
|
||||
|
||||
template<class TTunnel>
|
||||
std::shared_ptr<TTunnel> CreateTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel = nullptr);
|
||||
|
||||
template<class TTunnel>
|
||||
std::shared_ptr<TTunnel> GetPendingTunnel (uint32_t replyMsgID, const std::map<uint32_t, std::shared_ptr<TTunnel> >& pendingTunnels);
|
||||
|
||||
@@ -204,8 +207,8 @@ namespace tunnel
|
||||
void ManagePendingTunnels (PendingTunnels& pendingTunnels);
|
||||
void ManageTunnelPools ();
|
||||
|
||||
void CreateZeroHopsInboundTunnel ();
|
||||
void CreateZeroHopsOutboundTunnel ();
|
||||
std::shared_ptr<ZeroHopsInboundTunnel> CreateZeroHopsInboundTunnel ();
|
||||
std::shared_ptr<ZeroHopsOutboundTunnel> CreateZeroHopsOutboundTunnel ();
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <openssl/rand.h>
|
||||
#include "Crypto.h"
|
||||
#include "Identity.h"
|
||||
#include "RouterContext.h"
|
||||
@@ -160,6 +159,11 @@ namespace tunnel
|
||||
return num;
|
||||
}
|
||||
|
||||
bool IsEmpty () const
|
||||
{
|
||||
return !m_FirstHop;
|
||||
}
|
||||
|
||||
virtual bool IsInbound () const { return m_FirstHop->isGateway; }
|
||||
|
||||
virtual uint32_t GetTunnelID () const
|
||||
@@ -209,7 +213,7 @@ namespace tunnel
|
||||
void CreatePeers (const Peers& peers)
|
||||
{
|
||||
TunnelHopConfig * prev = nullptr;
|
||||
for (auto it: peers)
|
||||
for (const auto& it: peers)
|
||||
{
|
||||
auto hop = new TunnelHopConfig (it);
|
||||
if (prev)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "I2PEndian.h"
|
||||
#include <string.h>
|
||||
#include <openssl/sha.h>
|
||||
#include "Crypto.h"
|
||||
#include "Log.h"
|
||||
#include "NetDb.h"
|
||||
#include "I2NPProtocol.h"
|
||||
@@ -119,7 +119,7 @@ namespace tunnel
|
||||
if (ret.second)
|
||||
HandleOutOfSequenceFragment (msgID, ret.first->second);
|
||||
else
|
||||
LogPrint (eLogError, "TunnelMessage: Incomplete message ", msgID, "already exists");
|
||||
LogPrint (eLogError, "TunnelMessage: Incomplete message ", msgID, " already exists");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -205,7 +205,7 @@ namespace tunnel
|
||||
if (it->second.fragmentNum == msg.nextFragmentNum)
|
||||
{
|
||||
LogPrint (eLogWarning, "TunnelMessage: Out-of-sequence fragment ", (int)it->second.fragmentNum, " of message ", msgID, " found");
|
||||
auto size = it->second.data->GetLength ();
|
||||
size_t size = it->second.data->GetLength ();
|
||||
if (msg.data->len + size > msg.data->maxLen)
|
||||
{
|
||||
LogPrint (eLogWarning, "TunnelMessage: Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough");
|
||||
@@ -235,7 +235,7 @@ namespace tunnel
|
||||
LogPrint (eLogInfo, "TunnelMessage: message expired");
|
||||
return;
|
||||
}
|
||||
auto typeID = msg.data->GetTypeID ();
|
||||
uint8_t typeID = msg.data->GetTypeID ();
|
||||
LogPrint (eLogDebug, "TunnelMessage: handle fragment of ", msg.data->GetLength (), " bytes, msg type ", (int)typeID);
|
||||
// catch RI or reply with new list of routers
|
||||
if ((IsRouterInfoMsg (msg.data) || typeID == eI2NPDatabaseSearchReply) &&
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include <string.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/sha.h>
|
||||
#include "Crypto.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "Log.h"
|
||||
#include "RouterContext.h"
|
||||
@@ -50,7 +49,7 @@ namespace tunnel
|
||||
|
||||
// create fragments
|
||||
std::shared_ptr<I2NPMessage> msg = block.data;
|
||||
auto fullMsgLen = diLen + msg->GetLength () + 2; // delivery instructions + payload + 2 bytes length
|
||||
size_t fullMsgLen = diLen + msg->GetLength () + 2; // delivery instructions + payload + 2 bytes length
|
||||
if (fullMsgLen <= m_RemainingSize)
|
||||
{
|
||||
// message fits. First and last fragment
|
||||
@@ -67,10 +66,10 @@ namespace tunnel
|
||||
{
|
||||
if (!messageCreated) // check if we should complete previous message
|
||||
{
|
||||
auto numFollowOnFragments = fullMsgLen / TUNNEL_DATA_MAX_PAYLOAD_SIZE;
|
||||
size_t numFollowOnFragments = fullMsgLen / TUNNEL_DATA_MAX_PAYLOAD_SIZE;
|
||||
// length of bytes don't fit full tunnel message
|
||||
// every follow-on fragment adds 7 bytes
|
||||
auto nonFit = (fullMsgLen + numFollowOnFragments*7) % TUNNEL_DATA_MAX_PAYLOAD_SIZE;
|
||||
size_t nonFit = (fullMsgLen + numFollowOnFragments*7) % TUNNEL_DATA_MAX_PAYLOAD_SIZE;
|
||||
if (!nonFit || nonFit > m_RemainingSize)
|
||||
{
|
||||
CompleteCurrentTunnelDataMessage ();
|
||||
@@ -198,7 +197,7 @@ namespace tunnel
|
||||
{
|
||||
m_Buffer.CompleteCurrentTunnelDataMessage ();
|
||||
auto tunnelMsgs = m_Buffer.GetTunnelDataMsgs ();
|
||||
for (auto tunnelMsg : tunnelMsgs)
|
||||
for (auto& tunnelMsg : tunnelMsgs)
|
||||
{
|
||||
m_Tunnel->EncryptTunnelMsg (tunnelMsg, tunnelMsg);
|
||||
tunnelMsg->FillI2NPMessageHeader (eI2NPTunnelData);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include <algorithm>
|
||||
#include <openssl/rand.h>
|
||||
#include "I2PEndian.h"
|
||||
#include "Crypto.h"
|
||||
#include "Tunnel.h"
|
||||
@@ -50,13 +49,13 @@ namespace tunnel
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
||||
for (auto it: m_InboundTunnels)
|
||||
for (auto& it: m_InboundTunnels)
|
||||
it->SetTunnelPool (nullptr);
|
||||
m_InboundTunnels.clear ();
|
||||
}
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
||||
for (auto it: m_OutboundTunnels)
|
||||
for (auto& it: m_OutboundTunnels)
|
||||
it->SetTunnelPool (nullptr);
|
||||
m_OutboundTunnels.clear ();
|
||||
}
|
||||
@@ -79,7 +78,7 @@ namespace tunnel
|
||||
if (expiredTunnel)
|
||||
{
|
||||
expiredTunnel->SetTunnelPool (nullptr);
|
||||
for (auto it: m_Tests)
|
||||
for (auto& it: m_Tests)
|
||||
if (it.second.second == expiredTunnel) it.second.second = nullptr;
|
||||
|
||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
||||
@@ -102,7 +101,7 @@ namespace tunnel
|
||||
if (expiredTunnel)
|
||||
{
|
||||
expiredTunnel->SetTunnelPool (nullptr);
|
||||
for (auto it: m_Tests)
|
||||
for (auto& it: m_Tests)
|
||||
if (it.second.first == expiredTunnel) it.second.first = nullptr;
|
||||
|
||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
||||
@@ -115,7 +114,7 @@ namespace tunnel
|
||||
std::vector<std::shared_ptr<InboundTunnel> > v;
|
||||
int i = 0;
|
||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
||||
for (auto it : m_InboundTunnels)
|
||||
for (const auto& it : m_InboundTunnels)
|
||||
{
|
||||
if (i >= num) break;
|
||||
if (it->IsEstablished ())
|
||||
@@ -145,7 +144,7 @@ namespace tunnel
|
||||
if (tunnels.empty ()) return nullptr;
|
||||
uint32_t ind = rand () % (tunnels.size ()/2 + 1), i = 0;
|
||||
typename TTunnels::value_type tunnel = nullptr;
|
||||
for (auto it: tunnels)
|
||||
for (const auto& it: tunnels)
|
||||
{
|
||||
if (it->IsEstablished () && it != excluded)
|
||||
{
|
||||
@@ -165,7 +164,7 @@ namespace tunnel
|
||||
if (old)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
||||
for (auto it: m_OutboundTunnels)
|
||||
for (const auto& it: m_OutboundTunnels)
|
||||
if (it->IsEstablished () && old->GetEndpointIdentHash () == it->GetEndpointIdentHash ())
|
||||
{
|
||||
tunnel = it;
|
||||
@@ -183,7 +182,7 @@ namespace tunnel
|
||||
int num = 0;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
||||
for (auto it : m_InboundTunnels)
|
||||
for (const auto& it : m_InboundTunnels)
|
||||
if (it->IsEstablished ()) num++;
|
||||
}
|
||||
for (int i = num; i < m_NumInboundTunnels; i++)
|
||||
@@ -192,7 +191,7 @@ namespace tunnel
|
||||
num = 0;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
||||
for (auto it : m_OutboundTunnels)
|
||||
for (const auto& it : m_OutboundTunnels)
|
||||
if (it->IsEstablished ()) num++;
|
||||
}
|
||||
for (int i = num; i < m_NumOutboundTunnels; i++)
|
||||
@@ -204,11 +203,10 @@ namespace tunnel
|
||||
decltype(m_Tests) tests;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_TestsMutex);
|
||||
tests = m_Tests;
|
||||
m_Tests.clear ();
|
||||
tests.swap(m_Tests);
|
||||
}
|
||||
|
||||
for (auto it: tests)
|
||||
for (auto& it: tests)
|
||||
{
|
||||
LogPrint (eLogWarning, "Tunnels: test of tunnel ", it.first, " failed");
|
||||
// if test failed again with another tunnel we consider it failed
|
||||
@@ -249,12 +247,12 @@ namespace tunnel
|
||||
if ((*it1)->IsFailed ())
|
||||
{
|
||||
failed = true;
|
||||
it1++;
|
||||
++it1;
|
||||
}
|
||||
if ((*it2)->IsFailed ())
|
||||
{
|
||||
failed = true;
|
||||
it2++;
|
||||
++it2;
|
||||
}
|
||||
if (!failed)
|
||||
{
|
||||
@@ -266,7 +264,7 @@ namespace tunnel
|
||||
}
|
||||
(*it1)->SendTunnelDataMsg ((*it2)->GetNextIdentHash (), (*it2)->GetNextTunnelID (),
|
||||
CreateDeliveryStatusMsg (msgID));
|
||||
it1++; it2++;
|
||||
++it1; ++it2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -323,16 +321,25 @@ namespace tunnel
|
||||
i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop);
|
||||
|
||||
if (!hop || hop->GetProfile ()->IsBad ())
|
||||
hop = i2p::data::netdb.GetRandomRouter ();
|
||||
hop = i2p::data::netdb.GetRandomRouter (prevHop);
|
||||
return hop;
|
||||
}
|
||||
|
||||
bool TunnelPool::SelectPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers, bool isInbound)
|
||||
{
|
||||
if (m_ExplicitPeers) return SelectExplicitPeers (peers, isInbound);
|
||||
auto prevHop = i2p::context.GetSharedRouterInfo ();
|
||||
int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops;
|
||||
if (i2p::transport::transports.GetNumPeers () > 25)
|
||||
if (numHops <= 0) return true; // peers is empty
|
||||
auto prevHop = i2p::context.GetSharedRouterInfo ();
|
||||
if(i2p::transport::transports.RoutesRestricted())
|
||||
{
|
||||
/** if routes are restricted prepend trusted first hop */
|
||||
auto hop = i2p::transport::transports.GetRestrictedPeer();
|
||||
if(!hop) return false;
|
||||
peers.push_back(hop->GetRouterIdentity());
|
||||
prevHop = hop;
|
||||
}
|
||||
else if (i2p::transport::transports.GetNumPeers () > 25)
|
||||
{
|
||||
auto r = i2p::transport::transports.GetRandomPeer ();
|
||||
if (r && !r->GetProfile ()->IsBad ())
|
||||
@@ -343,7 +350,7 @@ namespace tunnel
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < numHops; i++)
|
||||
for(int i = 0; i < numHops; i++ )
|
||||
{
|
||||
auto hop = SelectNextHop (prevHop);
|
||||
if (!hop)
|
||||
@@ -353,7 +360,7 @@ namespace tunnel
|
||||
}
|
||||
prevHop = hop;
|
||||
peers.push_back (hop->GetRouterIdentity ());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -390,9 +397,16 @@ namespace tunnel
|
||||
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers;
|
||||
if (SelectPeers (peers, true))
|
||||
{
|
||||
std::reverse (peers.begin (), peers.end ());
|
||||
auto tunnel = tunnels.CreateTunnel<InboundTunnel> (std::make_shared<TunnelConfig> (peers), outboundTunnel);
|
||||
std::shared_ptr<TunnelConfig> config;
|
||||
if (m_NumInboundHops > 0)
|
||||
{
|
||||
std::reverse (peers.begin (), peers.end ());
|
||||
config = std::make_shared<TunnelConfig> (peers);
|
||||
}
|
||||
auto tunnel = tunnels.CreateInboundTunnel (config, outboundTunnel);
|
||||
tunnel->SetTunnelPool (shared_from_this ());
|
||||
if (tunnel->IsEstablished ()) // zero hops
|
||||
TunnelCreated (tunnel);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Tunnels: Can't create inbound tunnel, no peers available");
|
||||
@@ -404,8 +418,12 @@ namespace tunnel
|
||||
if (!outboundTunnel)
|
||||
outboundTunnel = tunnels.GetNextOutboundTunnel ();
|
||||
LogPrint (eLogDebug, "Tunnels: Re-creating destination inbound tunnel...");
|
||||
auto newTunnel = tunnels.CreateTunnel<InboundTunnel> (std::make_shared<TunnelConfig>(tunnel->GetPeers ()), outboundTunnel);
|
||||
std::shared_ptr<TunnelConfig> config;
|
||||
if (m_NumInboundHops > 0) config = std::make_shared<TunnelConfig>(tunnel->GetPeers ());
|
||||
auto newTunnel = tunnels.CreateInboundTunnel (config, outboundTunnel);
|
||||
newTunnel->SetTunnelPool (shared_from_this());
|
||||
if (newTunnel->IsEstablished ()) // zero hops
|
||||
TunnelCreated (newTunnel);
|
||||
}
|
||||
|
||||
void TunnelPool::CreateOutboundTunnel ()
|
||||
@@ -419,9 +437,13 @@ namespace tunnel
|
||||
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers;
|
||||
if (SelectPeers (peers, false))
|
||||
{
|
||||
auto tunnel = tunnels.CreateTunnel<OutboundTunnel> (
|
||||
std::make_shared<TunnelConfig> (peers, inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ()));
|
||||
std::shared_ptr<TunnelConfig> config;
|
||||
if (m_NumOutboundHops > 0)
|
||||
config = std::make_shared<TunnelConfig>(peers, inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ());
|
||||
auto tunnel = tunnels.CreateOutboundTunnel (config);
|
||||
tunnel->SetTunnelPool (shared_from_this ());
|
||||
if (tunnel->IsEstablished ()) // zero hops
|
||||
TunnelCreated (tunnel);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Tunnels: Can't create outbound tunnel, no peers available");
|
||||
@@ -438,10 +460,13 @@ namespace tunnel
|
||||
if (inboundTunnel)
|
||||
{
|
||||
LogPrint (eLogDebug, "Tunnels: Re-creating destination outbound tunnel...");
|
||||
auto newTunnel = tunnels.CreateTunnel<OutboundTunnel> (
|
||||
std::make_shared<TunnelConfig> (tunnel->GetPeers (),
|
||||
inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ()));
|
||||
std::shared_ptr<TunnelConfig> config;
|
||||
if (m_NumOutboundHops > 0)
|
||||
config = std::make_shared<TunnelConfig>(tunnel->GetPeers (), inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ());
|
||||
auto newTunnel = tunnels.CreateOutboundTunnel (config);
|
||||
newTunnel->SetTunnelPool (shared_from_this ());
|
||||
if (newTunnel->IsEstablished ()) // zero hops
|
||||
TunnelCreated (newTunnel);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogDebug, "Tunnels: Can't re-create outbound tunnel, no inbound tunnels found");
|
||||
@@ -450,7 +475,7 @@ namespace tunnel
|
||||
void TunnelPool::CreatePairedInboundTunnel (std::shared_ptr<OutboundTunnel> outboundTunnel)
|
||||
{
|
||||
LogPrint (eLogDebug, "Tunnels: Creating paired inbound tunnel...");
|
||||
auto tunnel = tunnels.CreateTunnel<InboundTunnel> (std::make_shared<TunnelConfig>(outboundTunnel->GetInvertedPeers ()), outboundTunnel);
|
||||
auto tunnel = tunnels.CreateInboundTunnel (std::make_shared<TunnelConfig>(outboundTunnel->GetInvertedPeers ()), outboundTunnel);
|
||||
tunnel->SetTunnelPool (shared_from_this ());
|
||||
}
|
||||
}
|
||||
|
||||
291
UPnP.cpp
291
UPnP.cpp
@@ -6,242 +6,197 @@
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#define dlsym GetProcAddress
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#include "Log.h"
|
||||
|
||||
#include "RouterContext.h"
|
||||
#include "UPnP.h"
|
||||
#include "NetDb.h"
|
||||
#include "util.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "Config.h"
|
||||
|
||||
#include <miniupnpc/miniupnpc.h>
|
||||
#include <miniupnpc/upnpcommands.h>
|
||||
|
||||
// These are per-process and are safe to reuse for all threads
|
||||
#ifndef UPNPDISCOVER_SUCCESS
|
||||
/* miniupnpc 1.5 */
|
||||
UPNPDev* (*upnpDiscoverFunc) (int, const char *, const char *, int);
|
||||
int (*UPNP_AddPortMappingFunc) (const char *, const char *, const char *, const char *,
|
||||
const char *, const char *, const char *, const char *);
|
||||
#else
|
||||
/* miniupnpc 1.6 */
|
||||
UPNPDev* (*upnpDiscoverFunc) (int, const char *, const char *, int, int, int *);
|
||||
int (*UPNP_AddPortMappingFunc) (const char *, const char *, const char *, const char *,
|
||||
const char *, const char *, const char *, const char *, const char *);
|
||||
#endif
|
||||
int (*UPNP_GetValidIGDFunc) (struct UPNPDev *, struct UPNPUrls *, struct IGDdatas *, char *, int);
|
||||
int (*UPNP_GetExternalIPAddressFunc) (const char *, const char *, char *);
|
||||
int (*UPNP_DeletePortMappingFunc) (const char *, const char *, const char *, const char *, const char *);
|
||||
void (*freeUPNPDevlistFunc) (struct UPNPDev *);
|
||||
void (*FreeUPNPUrlsFunc) (struct UPNPUrls *);
|
||||
|
||||
// Nice approach http://stackoverflow.com/a/21517513/673826
|
||||
template<class M, typename F>
|
||||
F GetKnownProcAddressImpl(M hmod, const char *name, F) {
|
||||
auto proc = reinterpret_cast<F>(dlsym(hmod, name));
|
||||
if (!proc) {
|
||||
LogPrint(eLogError, "UPnP: Error resolving ", name, " from library, version mismatch?");
|
||||
}
|
||||
return proc;
|
||||
}
|
||||
#define GetKnownProcAddress(hmod, func) GetKnownProcAddressImpl(hmod, #func, func##Func);
|
||||
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
UPnP::UPnP () : m_Thread (nullptr) , m_IsModuleLoaded (false)
|
||||
UPnP::UPnP () : m_IsRunning(false), m_Thread (nullptr), m_Timer (m_Service)
|
||||
{
|
||||
}
|
||||
|
||||
void UPnP::Stop ()
|
||||
{
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = nullptr;
|
||||
}
|
||||
if (m_IsRunning)
|
||||
{
|
||||
LogPrint(eLogInfo, "UPnP: stopping");
|
||||
m_IsRunning = false;
|
||||
m_Timer.cancel ();
|
||||
m_Service.stop ();
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
m_Thread.reset (nullptr);
|
||||
}
|
||||
CloseMapping ();
|
||||
Close ();
|
||||
}
|
||||
}
|
||||
|
||||
void UPnP::Start()
|
||||
{
|
||||
if (!m_IsModuleLoaded) {
|
||||
#ifdef MAC_OSX
|
||||
m_Module = dlopen ("libminiupnpc.dylib", RTLD_LAZY);
|
||||
#elif _WIN32
|
||||
m_Module = LoadLibrary ("miniupnpc.dll"); // official prebuilt binary, e.g., in upnpc-exe-win32-20140422.zip
|
||||
#else
|
||||
m_Module = dlopen ("libminiupnpc.so", RTLD_LAZY);
|
||||
#endif
|
||||
if (m_Module == NULL)
|
||||
{
|
||||
LogPrint (eLogError, "UPnP: Error loading UPNP library, version mismatch?");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
upnpDiscoverFunc = GetKnownProcAddress (m_Module, upnpDiscover);
|
||||
UPNP_GetValidIGDFunc = GetKnownProcAddress (m_Module, UPNP_GetValidIGD);
|
||||
UPNP_GetExternalIPAddressFunc = GetKnownProcAddress (m_Module, UPNP_GetExternalIPAddress);
|
||||
UPNP_AddPortMappingFunc = GetKnownProcAddress (m_Module, UPNP_AddPortMapping);
|
||||
UPNP_DeletePortMappingFunc = GetKnownProcAddress (m_Module, UPNP_DeletePortMapping);
|
||||
freeUPNPDevlistFunc = GetKnownProcAddress (m_Module, freeUPNPDevlist);
|
||||
FreeUPNPUrlsFunc = GetKnownProcAddress (m_Module, FreeUPNPUrls);
|
||||
if (upnpDiscoverFunc && UPNP_GetValidIGDFunc && UPNP_GetExternalIPAddressFunc && UPNP_AddPortMappingFunc &&
|
||||
UPNP_DeletePortMappingFunc && freeUPNPDevlistFunc && FreeUPNPUrlsFunc)
|
||||
m_IsModuleLoaded = true;
|
||||
}
|
||||
}
|
||||
m_Thread = new std::thread (std::bind (&UPnP::Run, this));
|
||||
m_IsRunning = true;
|
||||
LogPrint(eLogInfo, "UPnP: starting");
|
||||
m_Service.post (std::bind (&UPnP::Discover, this));
|
||||
std::unique_lock<std::mutex> l(m_StartedMutex);
|
||||
m_Thread.reset (new std::thread (std::bind (&UPnP::Run, this)));
|
||||
m_Started.wait_for (l, std::chrono::seconds (5)); // 5 seconds maximum
|
||||
}
|
||||
|
||||
UPnP::~UPnP ()
|
||||
{
|
||||
Stop ();
|
||||
}
|
||||
|
||||
void UPnP::Run ()
|
||||
{
|
||||
for (auto& address : context.GetRouterInfo ().GetAddresses ())
|
||||
{
|
||||
if (!address.host.is_v6 ())
|
||||
{
|
||||
Discover ();
|
||||
if (address.transportStyle == data::RouterInfo::eTransportSSU )
|
||||
{
|
||||
TryPortMapping (I2P_UPNP_UDP, address.port);
|
||||
}
|
||||
else if (address.transportStyle == data::RouterInfo::eTransportNTCP )
|
||||
{
|
||||
TryPortMapping (I2P_UPNP_TCP, address.port);
|
||||
}
|
||||
}
|
||||
}
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_Service.run ();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "UPnP: runtime exception: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UPnP::Discover ()
|
||||
{
|
||||
#ifndef UPNPDISCOVER_SUCCESS
|
||||
/* miniupnpc 1.5 */
|
||||
m_Devlist = upnpDiscoverFunc (2000, m_MulticastIf, m_Minissdpdpath, 0);
|
||||
#else
|
||||
/* miniupnpc 1.6 */
|
||||
int nerror = 0;
|
||||
m_Devlist = upnpDiscoverFunc (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, &nerror);
|
||||
#if MINIUPNPC_API_VERSION >= 14
|
||||
m_Devlist = upnpDiscover (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, 2, &nerror);
|
||||
#else
|
||||
m_Devlist = upnpDiscover (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, &nerror);
|
||||
#endif
|
||||
|
||||
{
|
||||
// notify satrting thread
|
||||
std::unique_lock<std::mutex> l(m_StartedMutex);
|
||||
m_Started.notify_all ();
|
||||
}
|
||||
|
||||
int r;
|
||||
r = UPNP_GetValidIGDFunc (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr));
|
||||
r = UPNP_GetValidIGD (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr));
|
||||
if (r == 1)
|
||||
{
|
||||
r = UPNP_GetExternalIPAddressFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
|
||||
r = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
|
||||
if(r != UPNPCOMMAND_SUCCESS)
|
||||
{
|
||||
LogPrint (eLogError, "UPnP: UPNP_GetExternalIPAddress () returned ", r);
|
||||
LogPrint (eLogError, "UPnP: UPNP_GetExternalIPAddress() returned ", r);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_externalIPAddress[0])
|
||||
if (!m_externalIPAddress[0])
|
||||
{
|
||||
LogPrint (eLogInfo, "UPnP: ExternalIPAddress = ", m_externalIPAddress);
|
||||
i2p::context.UpdateAddress (boost::asio::ip::address::from_string (m_externalIPAddress));
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "UPnP: GetExternalIPAddress failed.");
|
||||
LogPrint (eLogError, "UPnP: GetExternalIPAddress() failed.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "UPnP: GetValidIGD() failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
// UPnP discovered
|
||||
LogPrint (eLogDebug, "UPnP: ExternalIPAddress is ", m_externalIPAddress);
|
||||
i2p::context.UpdateAddress (boost::asio::ip::address::from_string (m_externalIPAddress));
|
||||
// port mapping
|
||||
PortMapping ();
|
||||
}
|
||||
|
||||
void UPnP::TryPortMapping (int type, int port)
|
||||
{
|
||||
std::string strType, strPort (std::to_string (port));
|
||||
switch (type)
|
||||
void UPnP::PortMapping ()
|
||||
{
|
||||
const auto& a = context.GetRouterInfo().GetAddresses();
|
||||
for (const auto& address : a)
|
||||
{
|
||||
case I2P_UPNP_TCP:
|
||||
strType = "TCP";
|
||||
break;
|
||||
case I2P_UPNP_UDP:
|
||||
default:
|
||||
strType = "UDP";
|
||||
if (!address->host.is_v6 ())
|
||||
TryPortMapping (address);
|
||||
}
|
||||
m_Timer.expires_from_now (boost::posix_time::minutes(20)); // every 20 minutes
|
||||
m_Timer.async_wait ([this](const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
PortMapping ();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void UPnP::CloseMapping ()
|
||||
{
|
||||
const auto& a = context.GetRouterInfo().GetAddresses();
|
||||
for (const auto& address : a)
|
||||
{
|
||||
if (!address->host.is_v6 ())
|
||||
CloseMapping (address);
|
||||
}
|
||||
}
|
||||
|
||||
void UPnP::TryPortMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address)
|
||||
{
|
||||
std::string strType (GetProto (address)), strPort (std::to_string (address->port));
|
||||
int r;
|
||||
std::string strDesc = "I2Pd";
|
||||
try {
|
||||
for (;;) {
|
||||
#ifndef UPNPDISCOVER_SUCCESS
|
||||
/* miniupnpc 1.5 */
|
||||
r = UPNP_AddPortMappingFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0);
|
||||
#else
|
||||
/* miniupnpc 1.6 */
|
||||
r = UPNP_AddPortMappingFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0, "0");
|
||||
#endif
|
||||
if (r!=UPNPCOMMAND_SUCCESS)
|
||||
{
|
||||
LogPrint (eLogError, "UPnP: AddPortMapping (", strPort.c_str () ,", ", strPort.c_str () ,", ", m_NetworkAddr, ") failed with code ", r);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "UPnP: Port Mapping successful. (", m_NetworkAddr ,":", strPort.c_str(), " type ", strType.c_str () ," -> ", m_externalIPAddress ,":", strPort.c_str() ,")");
|
||||
return;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::minutes(20)); // c++11
|
||||
//boost::this_thread::sleep_for(); // pre c++11
|
||||
//sleep(20*60); // non-portable
|
||||
}
|
||||
}
|
||||
catch (boost::thread_interrupted)
|
||||
std::string strDesc; i2p::config::GetOption("upnp.name", strDesc);
|
||||
r = UPNP_AddPortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0, "0");
|
||||
if (r!=UPNPCOMMAND_SUCCESS)
|
||||
{
|
||||
CloseMapping(type, port);
|
||||
Close();
|
||||
throw;
|
||||
LogPrint (eLogError, "UPnP: AddPortMapping (", m_NetworkAddr, ":", strPort, ") failed with code ", r);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "UPnP: Port Mapping successful. (", m_NetworkAddr ,":", strPort, " type ", strType, " -> ", m_externalIPAddress ,":", strPort ,")");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void UPnP::CloseMapping (int type, int port)
|
||||
void UPnP::CloseMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address)
|
||||
{
|
||||
std::string strType, strPort (std::to_string (port));
|
||||
switch (type)
|
||||
{
|
||||
case I2P_UPNP_TCP:
|
||||
strType = "TCP";
|
||||
break;
|
||||
case I2P_UPNP_UDP:
|
||||
default:
|
||||
strType = "UDP";
|
||||
}
|
||||
std::string strType (GetProto (address)), strPort (std::to_string (address->port));
|
||||
int r = 0;
|
||||
r = UPNP_DeletePortMappingFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), 0);
|
||||
LogPrint (eLogError, "UPnP: DeletePortMapping() returned : ", r, "\n");
|
||||
r = UPNP_DeletePortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), 0);
|
||||
LogPrint (eLogError, "UPnP: DeletePortMapping() returned : ", r);
|
||||
}
|
||||
|
||||
void UPnP::Close ()
|
||||
{
|
||||
freeUPNPDevlistFunc (m_Devlist);
|
||||
freeUPNPDevlist (m_Devlist);
|
||||
m_Devlist = 0;
|
||||
FreeUPNPUrlsFunc (&m_upnpUrls);
|
||||
#ifndef _WIN32
|
||||
dlclose (m_Module);
|
||||
#else
|
||||
FreeLibrary (m_Module);
|
||||
#endif
|
||||
FreeUPNPUrls (&m_upnpUrls);
|
||||
}
|
||||
|
||||
std::string UPnP::GetProto (std::shared_ptr<i2p::data::RouterInfo::Address> address)
|
||||
{
|
||||
switch (address->transportStyle)
|
||||
{
|
||||
case i2p::data::RouterInfo::eTransportNTCP:
|
||||
return "TCP";
|
||||
break;
|
||||
case i2p::data::RouterInfo::eTransportSSU:
|
||||
default:
|
||||
return "UDP";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#else /* USE_UPNP */
|
||||
namespace i2p {
|
||||
namespace transport {
|
||||
}
|
||||
}
|
||||
#endif /* USE_UPNP */
|
||||
|
||||
54
UPnP.h
54
UPnP.h
@@ -4,6 +4,9 @@
|
||||
#ifdef USE_UPNP
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
|
||||
#include <miniupnpc/miniwget.h>
|
||||
#include <miniupnpc/miniupnpc.h>
|
||||
@@ -12,11 +15,6 @@
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#define I2P_UPNP_TCP 1
|
||||
#define I2P_UPNP_UDP 2
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
@@ -32,13 +30,25 @@ namespace transport
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
void Discover ();
|
||||
void TryPortMapping (int type, int port);
|
||||
void CloseMapping (int type, int port);
|
||||
private:
|
||||
void Run ();
|
||||
|
||||
std::thread * m_Thread;
|
||||
void Discover ();
|
||||
void PortMapping ();
|
||||
void TryPortMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address);
|
||||
void CloseMapping ();
|
||||
void CloseMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address);
|
||||
|
||||
void Run ();
|
||||
std::string GetProto (std::shared_ptr<i2p::data::RouterInfo::Address> address);
|
||||
|
||||
private:
|
||||
|
||||
bool m_IsRunning;
|
||||
std::unique_ptr<std::thread> m_Thread;
|
||||
std::condition_variable m_Started;
|
||||
std::mutex m_StartedMutex;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::deadline_timer m_Timer;
|
||||
struct UPNPUrls m_upnpUrls;
|
||||
struct IGDdatas m_upnpData;
|
||||
|
||||
@@ -48,16 +58,22 @@ namespace transport
|
||||
struct UPNPDev * m_Devlist = 0;
|
||||
char m_NetworkAddr[64];
|
||||
char m_externalIPAddress[40];
|
||||
bool m_IsModuleLoaded;
|
||||
#ifndef _WIN32
|
||||
void *m_Module;
|
||||
#else
|
||||
HINSTANCE m_Module;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#else // USE_UPNP
|
||||
namespace i2p {
|
||||
namespace transport {
|
||||
/* class stub */
|
||||
class UPnP {
|
||||
public:
|
||||
UPnP () {};
|
||||
~UPnP () {};
|
||||
void Start () { LogPrint(eLogWarning, "UPnP: this module was disabled at compile-time"); }
|
||||
void Stop () {};
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif // USE_UPNP
|
||||
#endif // __UPNP_H__
|
||||
|
||||
@@ -53,7 +53,8 @@ END
|
||||
// Icon with lowest ID value placed first to ensure application icon
|
||||
// remains consistent on all systems.
|
||||
MAINICON ICON "ictoopie.ico"
|
||||
IDI_ICON1 ICON "ictoopie_16.ico"
|
||||
//MAINICON ICON "anke.ico"
|
||||
|
||||
#endif // English (United States) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -1,16 +1,28 @@
|
||||
#include <string.h>
|
||||
#include <windows.h>
|
||||
#include <shellapi.h>
|
||||
//#include "../Daemon.h"
|
||||
#include "../Config.h"
|
||||
#include "../RouterContext.h"
|
||||
#include "../version.h"
|
||||
#include "resource.h"
|
||||
#include "Win32App.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
#define ID_ABOUT 2000
|
||||
#define ID_EXIT 2001
|
||||
#define ID_CONSOLE 2002
|
||||
#define ID_APP 2003
|
||||
#define ID_GRACEFUL_SHUTDOWN 2004
|
||||
|
||||
#define ID_TRAY_ICON 2050
|
||||
#define WM_TRAYICON (WM_USER + 1)
|
||||
|
||||
#define IDT_GRACEFUL_SHUTDOWN_TIMER 2100
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace win32
|
||||
@@ -18,10 +30,13 @@ namespace win32
|
||||
static void ShowPopupMenu (HWND hWnd, POINT *curpos, int wDefaultItem)
|
||||
{
|
||||
HMENU hPopup = CreatePopupMenu();
|
||||
InsertMenu (hPopup, 0, MF_BYPOSITION | MF_STRING, ID_ABOUT, "About...");
|
||||
InsertMenu (hPopup, 1, MF_BYPOSITION | MF_STRING, ID_EXIT , "Exit");
|
||||
SetMenuDefaultItem (hPopup, ID_ABOUT, FALSE);
|
||||
SetFocus (hWnd);
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_CONSOLE, "Open &console");
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_APP, "Show app");
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_ABOUT, "&About...");
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_SEPARATOR, NULL, NULL);
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown");
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_EXIT, "E&xit");
|
||||
SetMenuDefaultItem (hPopup, ID_CONSOLE, FALSE);
|
||||
SendMessage (hWnd, WM_INITMENUPOPUP, (WPARAM)hPopup, 0);
|
||||
|
||||
POINT p;
|
||||
@@ -44,10 +59,11 @@ namespace win32
|
||||
nid.cbSize = sizeof(nid);
|
||||
nid.hWnd = hWnd;
|
||||
nid.uID = ID_TRAY_ICON;
|
||||
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
|
||||
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO;
|
||||
nid.uCallbackMessage = WM_TRAYICON;
|
||||
nid.hIcon = LoadIcon (GetModuleHandle(NULL), MAKEINTRESOURCE (IDI_ICON1));
|
||||
nid.hIcon = LoadIcon (GetModuleHandle(NULL), MAKEINTRESOURCE (MAINICON));
|
||||
strcpy (nid.szTip, "i2pd");
|
||||
strcpy (nid.szInfo, "i2pd is running");
|
||||
Shell_NotifyIcon(NIM_ADD, &nid );
|
||||
}
|
||||
|
||||
@@ -71,6 +87,7 @@ namespace win32
|
||||
case WM_CLOSE:
|
||||
{
|
||||
RemoveTrayIcon (hWnd);
|
||||
KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER);
|
||||
PostQuitMessage (0);
|
||||
break;
|
||||
}
|
||||
@@ -80,7 +97,9 @@ namespace win32
|
||||
{
|
||||
case ID_ABOUT:
|
||||
{
|
||||
MessageBox( hWnd, TEXT("i2pd"), TEXT("About"), MB_ICONINFORMATION | MB_OK );
|
||||
std::stringstream text;
|
||||
text << "Version: " << I2PD_VERSION << " " << CODENAME;
|
||||
MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK );
|
||||
return 0;
|
||||
}
|
||||
case ID_EXIT:
|
||||
@@ -88,14 +107,68 @@ namespace win32
|
||||
PostMessage (hWnd, WM_CLOSE, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
case ID_GRACEFUL_SHUTDOWN:
|
||||
{
|
||||
i2p::context.SetAcceptsTunnels (false);
|
||||
SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes
|
||||
return 0;
|
||||
}
|
||||
case ID_CONSOLE:
|
||||
{
|
||||
char buf[30];
|
||||
std::string httpAddr; i2p::config::GetOption("http.address", httpAddr);
|
||||
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
|
||||
snprintf(buf, 30, "http://%s:%d", httpAddr.c_str(), httpPort);
|
||||
ShellExecute(NULL, "open", buf, NULL, NULL, SW_SHOWNORMAL);
|
||||
return 0;
|
||||
}
|
||||
case ID_APP:
|
||||
{
|
||||
ShowWindow(hWnd, SW_SHOW);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_SYSCOMMAND:
|
||||
{
|
||||
switch (wParam)
|
||||
{
|
||||
case SC_MINIMIZE:
|
||||
{
|
||||
ShowWindow(hWnd, SW_HIDE);
|
||||
return 0;
|
||||
}
|
||||
case SC_CLOSE:
|
||||
{
|
||||
std::string close; i2p::config::GetOption("close", close);
|
||||
if (0 == close.compare("ask"))
|
||||
switch(::MessageBox(hWnd, "Would you like to minimize instead of exiting?"
|
||||
" You can add 'close' configuration option. Valid values are: ask, minimize, exit.",
|
||||
"Minimize instead of exiting?", MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON1))
|
||||
{
|
||||
case IDYES: close = "minimize"; break;
|
||||
case IDNO: close = "exit"; break;
|
||||
default: return 0;
|
||||
}
|
||||
if (0 == close.compare("minimize"))
|
||||
{
|
||||
ShowWindow(hWnd, SW_HIDE);
|
||||
return 0;
|
||||
}
|
||||
if (0 != close.compare("exit"))
|
||||
{
|
||||
::MessageBox(hWnd, close.c_str(), "Unknown close action in config", MB_OK | MB_ICONWARNING);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case WM_TRAYICON:
|
||||
{
|
||||
SetForegroundWindow (hWnd);
|
||||
switch (lParam)
|
||||
{
|
||||
case WM_LBUTTONUP:
|
||||
case WM_RBUTTONUP:
|
||||
{
|
||||
SetForegroundWindow (hWnd);
|
||||
@@ -106,6 +179,15 @@ namespace win32
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_TIMER:
|
||||
{
|
||||
if (wParam == IDT_GRACEFUL_SHUTDOWN_TIMER)
|
||||
{
|
||||
PostMessage (hWnd, WM_CLOSE, 0, 0); // exit
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return DefWindowProc( hWnd, uMsg, wParam, lParam);
|
||||
}
|
||||
@@ -127,15 +209,14 @@ namespace win32
|
||||
wclx.cbClsExtra = 0;
|
||||
wclx.cbWndExtra = 0;
|
||||
wclx.hInstance = hInst;
|
||||
wclx.hIcon = LoadIcon (hInst, IDI_APPLICATION);
|
||||
wclx.hIconSm = LoadIcon (hInst, IDI_APPLICATION);
|
||||
wclx.hIcon = LoadIcon (hInst, MAKEINTRESOURCE(MAINICON));
|
||||
wclx.hCursor = LoadCursor (NULL, IDC_ARROW);
|
||||
wclx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
|
||||
wclx.lpszMenuName = NULL;
|
||||
wclx.lpszClassName = I2PD_WIN32_CLASSNAME;
|
||||
RegisterClassEx (&wclx);
|
||||
// create new window
|
||||
if (!CreateWindow(I2PD_WIN32_CLASSNAME, TEXT("i2pd"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 250, 150, NULL, NULL, hInst, NULL))
|
||||
if (!CreateWindow(I2PD_WIN32_CLASSNAME, TEXT("i2pd"), WS_OVERLAPPEDWINDOW, 100, 100, 549, 738, NULL, NULL, hInst, NULL))
|
||||
{
|
||||
MessageBox(NULL, "Failed to create main window", TEXT("Warning!"), MB_ICONERROR | MB_OK | MB_TOPMOST);
|
||||
return false;
|
||||
@@ -158,5 +239,13 @@ namespace win32
|
||||
{
|
||||
UnregisterClass (I2PD_WIN32_CLASSNAME, GetModuleHandle(NULL));
|
||||
}
|
||||
|
||||
bool GracefulShutdown ()
|
||||
{
|
||||
HWND hWnd = FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd"));
|
||||
if (hWnd)
|
||||
PostMessage (hWnd, WM_COMMAND, MAKEWPARAM(ID_GRACEFUL_SHUTDOWN, 0), 0);
|
||||
return hWnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace win32
|
||||
bool StartWin32App ();
|
||||
void StopWin32App ();
|
||||
int RunWin32App ();
|
||||
bool GracefulShutdown ();
|
||||
}
|
||||
}
|
||||
#endif // WIN32APP_H__
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -2,7 +2,7 @@
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by Resource.rc
|
||||
//
|
||||
#define IDI_ICON1 101
|
||||
#define MAINICON 101
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user