mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-03-07 06:09:42 +00:00
Compare commits
3463 Commits
2.10.2
...
static-bui
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7a882582f | ||
|
|
250696a7b5 | ||
|
|
af7905744e | ||
|
|
db2364e9aa | ||
|
|
067fb45a25 | ||
|
|
ac287a896c | ||
|
|
8baf62eb2c | ||
|
|
e1ec79daf2 | ||
|
|
d4426118c5 | ||
|
|
64fe56aa07 | ||
|
|
47eb49c34e | ||
|
|
cd6d86c8c3 | ||
|
|
84d4e074ce | ||
|
|
a0e71c4173 | ||
|
|
533c8a8a55 | ||
|
|
a57ae4dc56 | ||
|
|
88dfe3ca4e | ||
|
|
d68c7f8ea7 | ||
|
|
e8ace998ba | ||
|
|
e8be39af17 | ||
|
|
7196db09d6 | ||
|
|
b290ee1aa0 | ||
|
|
d105ab11af | ||
|
|
bc888167a7 | ||
|
|
6ca6591c43 | ||
|
|
36cb707e47 | ||
|
|
013d5ff74f | ||
|
|
9af5a90757 | ||
|
|
d8b6f5438c | ||
|
|
10030a4e0d | ||
|
|
993dc72ce6 | ||
|
|
324ace103b | ||
|
|
d530269e4f | ||
|
|
7146a4dbae | ||
|
|
f79900653b | ||
|
|
f172f44f32 | ||
|
|
f34abe60fa | ||
|
|
a3c305032a | ||
|
|
2921eaa055 | ||
|
|
1cc68ea402 | ||
|
|
c18e8f6c78 | ||
|
|
e59ca8420e | ||
|
|
a6f9a56e40 | ||
|
|
4011502f5f | ||
|
|
acd6af709e | ||
|
|
55704ece3a | ||
|
|
0d3ede56cb | ||
|
|
06fae976b3 | ||
|
|
e95035c143 | ||
|
|
55be5c74f0 | ||
|
|
a1c16e129d | ||
|
|
321c7cb7cf | ||
|
|
5f8d45154d | ||
|
|
503f522cc3 | ||
|
|
22179400c7 | ||
|
|
66f82cb43f | ||
|
|
1df67bd43c | ||
|
|
d5b03f214b | ||
|
|
cfb773351d | ||
|
|
6942c20879 | ||
|
|
7b341d5d30 | ||
|
|
e93718456f | ||
|
|
af838196be | ||
|
|
f8ba5b8c63 | ||
|
|
446bbf6b93 | ||
|
|
2c19da9aa7 | ||
|
|
01fc21ffb9 | ||
|
|
3e3e2c41bd | ||
|
|
cb139226df | ||
|
|
84d6028454 | ||
|
|
126dc0ebe0 | ||
|
|
099d9d977f | ||
|
|
5a167316cb | ||
|
|
85e31f84ec | ||
|
|
edb7a0e23c | ||
|
|
f401ccf5dd | ||
|
|
9f9e8bfa14 | ||
|
|
61168d5c9d | ||
|
|
8500aaa26f | ||
|
|
445cff0025 | ||
|
|
99356fd24d | ||
|
|
5ef5f5a170 | ||
|
|
54b7d4ef32 | ||
|
|
6376328c98 | ||
|
|
b6f83dfe9f | ||
|
|
3f728149ab | ||
|
|
36501fe31e | ||
|
|
e4ddc883d2 | ||
|
|
5ac01ddce8 | ||
|
|
d3656fcb3f | ||
|
|
d6c101d261 | ||
|
|
eeea02d834 | ||
|
|
0e0cd555eb | ||
|
|
5dfe483152 | ||
|
|
c210553a39 | ||
|
|
a315e4ce62 | ||
|
|
96cfd9acc2 | ||
|
|
d869bb25ed | ||
|
|
476e6aae35 | ||
|
|
d30ee99cf1 | ||
|
|
84d9c8f1b8 | ||
|
|
df737a65b2 | ||
|
|
c5230ca44b | ||
|
|
3471e6fe16 | ||
|
|
f1437feede | ||
|
|
0d523bd2a6 | ||
|
|
8943200ffa | ||
|
|
a902d68669 | ||
|
|
f6ca7c19af | ||
|
|
3458665df8 | ||
|
|
8320987124 | ||
|
|
648b09d45f | ||
|
|
857df5c734 | ||
|
|
737603e81b | ||
|
|
53ca5dc67a | ||
|
|
8ad5696e50 | ||
|
|
ef9d27e424 | ||
|
|
2bb5ff7184 | ||
|
|
753c7efde8 | ||
|
|
d0d0cd8445 | ||
|
|
410d2c2fa9 | ||
|
|
6a743f66e8 | ||
|
|
709c451400 | ||
|
|
cb73c7c72e | ||
|
|
50abeea82a | ||
|
|
8db352b4d0 | ||
|
|
6589bdf6b5 | ||
|
|
1ac171152a | ||
|
|
629c718527 | ||
|
|
519c0fe81d | ||
|
|
eb0ef80a17 | ||
|
|
9763499dbe | ||
|
|
949c38f5f0 | ||
|
|
2a6f906177 | ||
|
|
b4c226f4b3 | ||
|
|
64c3282aae | ||
|
|
f5d511ae0f | ||
|
|
aa9a9ef18d | ||
|
|
aa5e6400e4 | ||
|
|
61bcfebcc8 | ||
|
|
73b9c0302b | ||
|
|
aead9db971 | ||
|
|
d8230644b2 | ||
|
|
97ef908b0c | ||
|
|
fb8be32c28 | ||
|
|
a298588943 | ||
|
|
ccfeca728e | ||
|
|
7705423c42 | ||
|
|
379075c594 | ||
|
|
6a23153c0b | ||
|
|
9e02c99db5 | ||
|
|
ba3cee1cf1 | ||
|
|
9f59ff2df4 | ||
|
|
8df4082d6f | ||
|
|
cf005821d7 | ||
|
|
39b3996596 | ||
|
|
78357baca4 | ||
|
|
85b78dfb9b | ||
|
|
9fd60b52f1 | ||
|
|
851be41d0d | ||
|
|
c6a6a4e0e8 | ||
|
|
28aeebd4c7 | ||
|
|
c88638afe4 | ||
|
|
7f98a8b972 | ||
|
|
e1e4924592 | ||
|
|
8299f80ea5 | ||
|
|
b8ce0b0838 | ||
|
|
e13f151474 | ||
|
|
4ed4e8708e | ||
|
|
1738d118f7 | ||
|
|
f1f66d7b8f | ||
|
|
4ed5e44de7 | ||
|
|
3e3f92c616 | ||
|
|
5fb1247b87 | ||
|
|
016222463d | ||
|
|
0e477bf938 | ||
|
|
eb75eb0e55 | ||
|
|
2a703e0844 | ||
|
|
2b6d9eaa8b | ||
|
|
f9b0bb0383 | ||
|
|
c6e8873d57 | ||
|
|
b2767304e9 | ||
|
|
3d4d3ce80d | ||
|
|
01ea1854bc | ||
|
|
f3aada9e1a | ||
|
|
08fd32b3bf | ||
|
|
39a86ce5c9 | ||
|
|
fe25260ee2 | ||
|
|
63fd05c7d3 | ||
|
|
6c2aec8854 | ||
|
|
e5553f7528 | ||
|
|
6e3cec653d | ||
|
|
55976fd9dc | ||
|
|
bcbd5201e9 | ||
|
|
c2f91ea63b | ||
|
|
1d9d89b115 | ||
|
|
798dd8b27b | ||
|
|
3544f77e90 | ||
|
|
96c4463d39 | ||
|
|
650b7abef6 | ||
|
|
714b3856a2 | ||
|
|
6b939eba59 | ||
|
|
e82662b389 | ||
|
|
8f9dae8556 | ||
|
|
69ca3bc75d | ||
|
|
3945f34e96 | ||
|
|
549dcbee32 | ||
|
|
0a0c2350f2 | ||
|
|
cef2263a7f | ||
|
|
e338ce7da9 | ||
|
|
638c376e5b | ||
|
|
8eade86624 | ||
|
|
24ae8d5443 | ||
|
|
030af11d86 | ||
|
|
857a2bc399 | ||
|
|
09e6e2940f | ||
|
|
23e18a34d4 | ||
|
|
3bdef5f58d | ||
|
|
cf27581c76 | ||
|
|
cf41df82e2 | ||
|
|
4634bff9f0 | ||
|
|
1a9c658836 | ||
|
|
1a32c55ca3 | ||
|
|
f4e230f1ad | ||
|
|
9abc4cf359 | ||
|
|
c54fc7ee44 | ||
|
|
9df757a3fd | ||
|
|
9b5a885b3b | ||
|
|
f32b288785 | ||
|
|
f378119889 | ||
|
|
8fd466c5a9 | ||
|
|
36eddd48c3 | ||
|
|
2470ba76f0 | ||
|
|
d32475440a | ||
|
|
b4d73683d1 | ||
|
|
95f19a5fb2 | ||
|
|
f98780b1d7 | ||
|
|
150c89e48a | ||
|
|
c85bf82749 | ||
|
|
63227ab2f1 | ||
|
|
5b19237a85 | ||
|
|
150b8f8cbd | ||
|
|
79b97ef2f7 | ||
|
|
e45d68ad3a | ||
|
|
b40f1b67b9 | ||
|
|
4fa7e43162 | ||
|
|
66fcbcae96 | ||
|
|
7f0845dfd3 | ||
|
|
f875823357 | ||
|
|
75611866eb | ||
|
|
c3dd7ed73a | ||
|
|
3ae885d120 | ||
|
|
81f53d313c | ||
|
|
d10c86b849 | ||
|
|
9d123fa5ad | ||
|
|
f4d6a08d57 | ||
|
|
e9e641afbe | ||
|
|
8f5768f85b | ||
|
|
3dd78a2589 | ||
|
|
df92a85159 | ||
|
|
ab606a1121 | ||
|
|
457b3cf168 | ||
|
|
c6f898b8ca | ||
|
|
b9970e1908 | ||
|
|
8bb9a57908 | ||
|
|
53934a470b | ||
|
|
a94ae7d77d | ||
|
|
f43e860998 | ||
|
|
3e40852999 | ||
|
|
df073bb306 | ||
|
|
771c4a0d02 | ||
|
|
cb959ab14c | ||
|
|
34b75dac02 | ||
|
|
fbb590d9a9 | ||
|
|
ed5c533982 | ||
|
|
98d2ce5845 | ||
|
|
9d9d5e3e5d | ||
|
|
eba4626589 | ||
|
|
ff5fa1d137 | ||
|
|
71766ecd16 | ||
|
|
fc63ca6982 | ||
|
|
0e6d888ed3 | ||
|
|
9afe3b5f39 | ||
|
|
3bd40fc8b3 | ||
|
|
01fe642beb | ||
|
|
e70d57dcb4 | ||
|
|
fd41fba069 | ||
|
|
8a6fe0f321 | ||
|
|
ae73e8a305 | ||
|
|
a344c09d0d | ||
|
|
991e37d0bf | ||
|
|
fdeb884fe5 | ||
|
|
4b1f5c9c9b | ||
|
|
6b513a0f95 | ||
|
|
b574aaf99c | ||
|
|
bc0cdaa669 | ||
|
|
f9106b77bb | ||
|
|
a0419e4f34 | ||
|
|
46a549c875 | ||
|
|
f8a609f692 | ||
|
|
987497bb10 | ||
|
|
e537878b8a | ||
|
|
617f45bc59 | ||
|
|
fe744f8f81 | ||
|
|
93d879b297 | ||
|
|
dbb9295063 | ||
|
|
09aa96e486 | ||
|
|
4d0047ae7c | ||
|
|
b860a4799d | ||
|
|
6ff64352d3 | ||
|
|
3683ec6a95 | ||
|
|
454fa9ee9b | ||
|
|
d33aeb4bb2 | ||
|
|
5f9f23eb3f | ||
|
|
5dbc7a8ca4 | ||
|
|
33a5968eb7 | ||
|
|
5ff34b93c0 | ||
|
|
098fdf0596 | ||
|
|
2eb929fe05 | ||
|
|
ea0ed9e844 | ||
|
|
4a3e481a83 | ||
|
|
2197cd8620 | ||
|
|
cf0d3b5f61 | ||
|
|
6f7ab49346 | ||
|
|
000e0358a7 | ||
|
|
a3e19931f0 | ||
|
|
9fec1a86cf | ||
|
|
ffab29890b | ||
|
|
206c068d8e | ||
|
|
dc30cd1112 | ||
|
|
412a245e88 | ||
|
|
16290bf66f | ||
|
|
4f8b0e6484 | ||
|
|
5026dbc1b3 | ||
|
|
014e4b0e1d | ||
|
|
14a6947b02 | ||
|
|
665a914dc3 | ||
|
|
8feac310af | ||
|
|
3394bb4b8d | ||
|
|
1dd2bd0013 | ||
|
|
5c62726992 | ||
|
|
90981f628e | ||
|
|
0c34189d94 | ||
|
|
f1d3d6a7b5 | ||
|
|
b0d962b49a | ||
|
|
c50e453af6 | ||
|
|
efbaf02016 | ||
|
|
3cf809e99d | ||
|
|
8b649aaaf8 | ||
|
|
fdebbc4498 | ||
|
|
3ff3417ff2 | ||
|
|
bb6227281a | ||
|
|
2f44d99a74 | ||
|
|
ca4414d15a | ||
|
|
fbb961b43c | ||
|
|
fa9c174264 | ||
|
|
83f43ab166 | ||
|
|
f7e9e6a1c4 | ||
|
|
aa21748e9a | ||
|
|
a2f4e08b00 | ||
|
|
66bc29d075 | ||
|
|
e3eebe537b | ||
|
|
3ed625f949 | ||
|
|
a1e414c3b7 | ||
|
|
a5a35b1fa6 | ||
|
|
2a24584d45 | ||
|
|
6039cdceb0 | ||
|
|
473159be0f | ||
|
|
0e6ad548b2 | ||
|
|
6143515ac6 | ||
|
|
50419f200d | ||
|
|
455390f121 | ||
|
|
d375299fa9 | ||
|
|
28db337166 | ||
|
|
6ca9a599ff | ||
|
|
83bd3b6f0b | ||
|
|
a68765e021 | ||
|
|
f5ed9129cd | ||
|
|
5e3115a614 | ||
|
|
624c46f925 | ||
|
|
52d1ee161f | ||
|
|
d3bc9eb110 | ||
|
|
72b61a29c2 | ||
|
|
a99fcfe54f | ||
|
|
b5d139f7b2 | ||
|
|
463ed12ce8 | ||
|
|
baf74cb582 | ||
|
|
63d7cffefe | ||
|
|
d7d74666b2 | ||
|
|
078d76c6f3 | ||
|
|
3539ee9be6 | ||
|
|
437282b148 | ||
|
|
5394b747a1 | ||
|
|
dd1dd3b7cf | ||
|
|
ae77d4ad22 | ||
|
|
821987fed7 | ||
|
|
18ddba4332 | ||
|
|
aaad6dece6 | ||
|
|
ed04747b9d | ||
|
|
827a88d772 | ||
|
|
24e325db62 | ||
|
|
38e43bc9c8 | ||
|
|
c3c5c7ae63 | ||
|
|
578a15bbe5 | ||
|
|
6b3d7372ae | ||
|
|
55f7529167 | ||
|
|
bceae244c1 | ||
|
|
5de224d6bf | ||
|
|
694b936f30 | ||
|
|
dda25d431c | ||
|
|
22f9abc2f1 | ||
|
|
c6c3de9164 | ||
|
|
58186f0283 | ||
|
|
0253e2d3f6 | ||
|
|
ee20d5b804 | ||
|
|
3a5295dbb9 | ||
|
|
39f14fd952 | ||
|
|
4d59df9f59 | ||
|
|
b8bc114502 | ||
|
|
74d29770e1 | ||
|
|
e4d5788cdc | ||
|
|
2a5cf3e4a8 | ||
|
|
c348736058 | ||
|
|
5bb20cb039 | ||
|
|
dd602a27b5 | ||
|
|
2067de162a | ||
|
|
2cc106b43e | ||
|
|
b15bfd99b3 | ||
|
|
67252b90b3 | ||
|
|
079f7e515c | ||
|
|
e8c58270c4 | ||
|
|
0c64f278d7 | ||
|
|
03518ec94f | ||
|
|
93b5dc2dff | ||
|
|
3bef6383d9 | ||
|
|
605ccf3e02 | ||
|
|
17892238a9 | ||
|
|
b678c989e2 | ||
|
|
b72d1237d2 | ||
|
|
f7b6db5dad | ||
|
|
b744a0cc38 | ||
|
|
b918499f14 | ||
|
|
2cfd054f2c | ||
|
|
dddc7ab039 | ||
|
|
2e4d8cdc8b | ||
|
|
0640bec026 | ||
|
|
cbcee5fb45 | ||
|
|
47460d86b2 | ||
|
|
3cd74f0d4f | ||
|
|
690c9f7c6f | ||
|
|
e2718e5a12 | ||
|
|
d9fefe757e | ||
|
|
55e4bf6b65 | ||
|
|
0176e5cf18 | ||
|
|
4670b12d49 | ||
|
|
321ec8ae4d | ||
|
|
1ccbb8d10b | ||
|
|
86c0accdce | ||
|
|
38d6c29ce9 | ||
|
|
0cf9478cd4 | ||
|
|
a04abd304a | ||
|
|
84aec9fe31 | ||
|
|
593b9bb6c5 | ||
|
|
d3a9cc8fde | ||
|
|
87a434c377 | ||
|
|
56022c9442 | ||
|
|
593d6bf466 | ||
|
|
29a4366dcf | ||
|
|
0a42f414bf | ||
|
|
9b2ac4349e | ||
|
|
2d4c7729ad | ||
|
|
6ecab66b0e | ||
|
|
1dded57a1c | ||
|
|
1d6104ecf3 | ||
|
|
14da941ff4 | ||
|
|
06b87311ea | ||
|
|
3b31773117 | ||
|
|
9c87fe79ea | ||
|
|
bd00112562 | ||
|
|
1c9160c37d | ||
|
|
e2ef88229f | ||
|
|
fd7b889a0f | ||
|
|
a7aa056ec1 | ||
|
|
4f74acb2d3 | ||
|
|
22ef1be82b | ||
|
|
9ddbf255ba | ||
|
|
dfb171d32a | ||
|
|
6b4ffcff5a | ||
|
|
d31cd2e5d6 | ||
|
|
396c74e6c6 | ||
|
|
609c658a9b | ||
|
|
ee6bb40736 | ||
|
|
f8c5ea2b42 | ||
|
|
923eb9fdb3 | ||
|
|
2cd3ebbdb3 | ||
|
|
5e25e30330 | ||
|
|
5aa2a8f60f | ||
|
|
0a1e302e8a | ||
|
|
bb705a77cf | ||
|
|
cb6155b946 | ||
|
|
714d1cc993 | ||
|
|
bc8e4494c4 | ||
|
|
c3a064f980 | ||
|
|
eb3feb7dbd | ||
|
|
da3f3ccac9 | ||
|
|
1a1871e8cd | ||
|
|
c22ab7e1fc | ||
|
|
436992b069 | ||
|
|
18cb3912e5 | ||
|
|
a818b0ba02 | ||
|
|
3716b6f988 | ||
|
|
c9e4e78f41 | ||
|
|
9b4e8bf64b | ||
|
|
5aebefe73f | ||
|
|
8f2124beab | ||
|
|
8b8b43df28 | ||
|
|
c42b991bc9 | ||
|
|
ec08333bf9 | ||
|
|
9e5b4e14c9 | ||
|
|
1f5ed89a88 | ||
|
|
2304a2bc2e | ||
|
|
dc82105226 | ||
|
|
5221f3ddc9 | ||
|
|
e970deb92b | ||
|
|
9db7ec6bb0 | ||
|
|
2e691b6655 | ||
|
|
f22e10537b | ||
|
|
6e532c494c | ||
|
|
f9ed0d4aa2 | ||
|
|
78b1afcc8c | ||
|
|
40340cf9c2 | ||
|
|
eb6437050f | ||
|
|
45ebfe378b | ||
|
|
1326597226 | ||
|
|
751da92c13 | ||
|
|
e10ca637da | ||
|
|
c5d9d71a8a | ||
|
|
3e0f5d231d | ||
|
|
6990f177ba | ||
|
|
98e713166b | ||
|
|
4c91ae0085 | ||
|
|
43f74d4d5a | ||
|
|
8c3e716c3f | ||
|
|
05946125b5 | ||
|
|
1e2a0a4549 | ||
|
|
f9f5084dd7 | ||
|
|
b7e7c6db7b | ||
|
|
f9d67b28ec | ||
|
|
46b77cc280 | ||
|
|
2f10decf56 | ||
|
|
678a1ae0fb | ||
|
|
51cbffd097 | ||
|
|
207b13dcab | ||
|
|
3052dbd1e8 | ||
|
|
5891b1ceb2 | ||
|
|
07e14ddda8 | ||
|
|
db5e90787c | ||
|
|
67e501f5c7 | ||
|
|
2160001167 | ||
|
|
f5f4150d17 | ||
|
|
887f292612 | ||
|
|
f5f282af97 | ||
|
|
82f9585b7a | ||
|
|
eb561bb0c2 | ||
|
|
81207999eb | ||
|
|
2fef595b83 | ||
|
|
2024e790ca | ||
|
|
f9925c7374 | ||
|
|
dd774b8dfd | ||
|
|
064b8042a5 | ||
|
|
7923ed9567 | ||
|
|
30b83414ef | ||
|
|
990906c57f | ||
|
|
4c323a666a | ||
|
|
a3f165d374 | ||
|
|
4977f9e6b4 | ||
|
|
7d5f51e357 | ||
|
|
371a339b18 | ||
|
|
7e7aee27b6 | ||
|
|
53148fe58f | ||
|
|
56b6de6962 | ||
|
|
44735681af | ||
|
|
ee1c4f4fdc | ||
|
|
fb6ecdde1e | ||
|
|
861166d8a9 | ||
|
|
70dca81c40 | ||
|
|
2774d72888 | ||
|
|
2440ffbfc9 | ||
|
|
77c5dde320 | ||
|
|
aa49cad279 | ||
|
|
f56ae240ab | ||
|
|
e871a30a78 | ||
|
|
30e6984889 | ||
|
|
324932c758 | ||
|
|
421800bc8f | ||
|
|
86fb47b2b4 | ||
|
|
715f83bf84 | ||
|
|
87bf5c2418 | ||
|
|
5c9af1c613 | ||
|
|
765e0e5c6b | ||
|
|
cc296e16dc | ||
|
|
ab9901525b | ||
|
|
3643a46a0c | ||
|
|
d467e6869d | ||
|
|
db36018849 | ||
|
|
3c5c375f71 | ||
|
|
7473d8c9aa | ||
|
|
33645d7f09 | ||
|
|
9f1106b14a | ||
|
|
3dd952b49b | ||
|
|
6b85bd2cb8 | ||
|
|
60b164c853 | ||
|
|
40c8a1bc1d | ||
|
|
22de695f12 | ||
|
|
e91f588cd7 | ||
|
|
7b72d91549 | ||
|
|
b3c2e86436 | ||
|
|
908bdc7624 | ||
|
|
21c1ec9c8c | ||
|
|
6d7d71bb16 | ||
|
|
6eba061c2a | ||
|
|
f184f550b9 | ||
|
|
bb7c0fef20 | ||
|
|
5c15a12116 | ||
|
|
68d015763e | ||
|
|
7faa732f38 | ||
|
|
11f9eeabf1 | ||
|
|
a152f36894 | ||
|
|
d4ede6ff01 | ||
|
|
35542d803c | ||
|
|
f6ba776c12 | ||
|
|
1511dcb309 | ||
|
|
35afa98112 | ||
|
|
df62b40ca7 | ||
|
|
9f1a125ed9 | ||
|
|
b7e20b9b86 | ||
|
|
a5d6972913 | ||
|
|
e4cb42c599 | ||
|
|
0a34f1f3ad | ||
|
|
7bdeaa9611 | ||
|
|
ab2577ce0a | ||
|
|
34544be423 | ||
|
|
6bf0fdd344 | ||
|
|
6a177cdd1c | ||
|
|
a51ef0cfc6 | ||
|
|
48374d97df | ||
|
|
bf3d7e74f5 | ||
|
|
ab3f3890e4 | ||
|
|
dceb0fb8c5 | ||
|
|
05c1856389 | ||
|
|
dc5cba60d1 | ||
|
|
70409dcdcc | ||
|
|
a92c29e04c | ||
|
|
c4b4dc79cf | ||
|
|
510fe43ec4 | ||
|
|
73e572b66b | ||
|
|
a272a2cb7e | ||
|
|
43b990afe6 | ||
|
|
90130b5492 | ||
|
|
f22faaefeb | ||
|
|
ac25649425 | ||
|
|
04388325a8 | ||
|
|
61ec873842 | ||
|
|
ea1f2d4e26 | ||
|
|
4211c733a2 | ||
|
|
450266818a | ||
|
|
1e019157bb | ||
|
|
c9a1066f02 | ||
|
|
0062f7d764 | ||
|
|
95e994e171 | ||
|
|
31242401e5 | ||
|
|
19cc1c3b3f | ||
|
|
03bcdceb9b | ||
|
|
33ca836ad0 | ||
|
|
53f19e4050 | ||
|
|
54b7d46f5a | ||
|
|
40e6d675c5 | ||
|
|
73b77c83b8 | ||
|
|
632d41e50c | ||
|
|
17acadbfb9 | ||
|
|
2ab5924ec9 | ||
|
|
ac09a4cf0f | ||
|
|
c10ee59de3 | ||
|
|
afad405ed9 | ||
|
|
5a35de8dc9 | ||
|
|
58cf26c304 | ||
|
|
a2de5564ac | ||
|
|
338b17ccf1 | ||
|
|
843a968959 | ||
|
|
dc45c13eef | ||
|
|
0d6e801595 | ||
|
|
2cd50ebaee | ||
|
|
cb6f6a6596 | ||
|
|
e4ab0acc92 | ||
|
|
10237c41d3 | ||
|
|
ac2c6c6010 | ||
|
|
c6b2ce93c4 | ||
|
|
401b7fe883 | ||
|
|
f567417bb3 | ||
|
|
ae5cb3bbe7 | ||
|
|
0eb8e15796 | ||
|
|
1c95c7856f | ||
|
|
daf7551e59 | ||
|
|
5b63d3692e | ||
|
|
5f9972af78 | ||
|
|
1be4cce074 | ||
|
|
935e93eb36 | ||
|
|
5d924cd35a | ||
|
|
27116b9f30 | ||
|
|
8ac2ee49a8 | ||
|
|
8a8e328fcf | ||
|
|
c4207e7672 | ||
|
|
73642703bd | ||
|
|
b3bd175e64 | ||
|
|
742032907a | ||
|
|
edc0162163 | ||
|
|
94661f697b | ||
|
|
4ecf36fab6 | ||
|
|
96cdb3bca3 | ||
|
|
6c57ba36f7 | ||
|
|
cb61897236 | ||
|
|
8b931dd40b | ||
|
|
6b81478bd2 | ||
|
|
9d94eb83c1 | ||
|
|
13374f8b7b | ||
|
|
7bf7aae3d1 | ||
|
|
e5f39d0caf | ||
|
|
174983548d | ||
|
|
a9ec4d916b | ||
|
|
a55c346af5 | ||
|
|
8e0d8c96bb | ||
|
|
9abd383014 | ||
|
|
8ee9c437e1 | ||
|
|
4052b1ea6d | ||
|
|
fdde197c58 | ||
|
|
49883dc3ac | ||
|
|
d798faa1ca | ||
|
|
3f63f15b16 | ||
|
|
f8c390cdd3 | ||
|
|
8f0978cfd6 | ||
|
|
1a8a32a773 | ||
|
|
c0400bfd07 | ||
|
|
c6e4758187 | ||
|
|
56ec8fe95b | ||
|
|
67863cfcf9 | ||
|
|
4c5ec68ff1 | ||
|
|
58b7b7d731 | ||
|
|
1de1c79d4f | ||
|
|
7073a6bf38 | ||
|
|
26db88d89b | ||
|
|
876e98d91e | ||
|
|
8566f6c127 | ||
|
|
bb8dc67942 | ||
|
|
9965d72990 | ||
|
|
921ec9ec12 | ||
|
|
f1990bc2ab | ||
|
|
cdc8e463b7 | ||
|
|
0a62a962d7 | ||
|
|
b0f043ec86 | ||
|
|
ae0cf2e831 | ||
|
|
4ce7e192d6 | ||
|
|
04ca916aac | ||
|
|
a348e10620 | ||
|
|
af794f901f | ||
|
|
8a58572b34 | ||
|
|
0c25e8f1eb | ||
|
|
ff3d2db85e | ||
|
|
efd84a2404 | ||
|
|
278fd2d8d5 | ||
|
|
197882a4c9 | ||
|
|
d310efcb5c | ||
|
|
1af9117b80 | ||
|
|
44e01b41f8 | ||
|
|
7def2fa6a3 | ||
|
|
48131f4597 | ||
|
|
49e8cf89d8 | ||
|
|
e6bcd04a36 | ||
|
|
af133f4968 | ||
|
|
d723faaaa3 | ||
|
|
cc75efcbca | ||
|
|
2eded7cdd7 | ||
|
|
b10e5ce358 | ||
|
|
1bb1d89fab | ||
|
|
b9dd4aee8d | ||
|
|
518e53a61c | ||
|
|
18b6ba80f2 | ||
|
|
8debdc264c | ||
|
|
31bdce1f1f | ||
|
|
317d8cdc48 | ||
|
|
5b2b9e00a2 | ||
|
|
3dd9e81296 | ||
|
|
d2faec70be | ||
|
|
e5c773a3eb | ||
|
|
ec86c4611d | ||
|
|
247b6a0ed2 | ||
|
|
f7f36568ef | ||
|
|
e054c6e82c | ||
|
|
5e2e1a1e3d | ||
|
|
ad036de69d | ||
|
|
20652f7995 | ||
|
|
2f88a75325 | ||
|
|
292fe94352 | ||
|
|
76dca1b46b | ||
|
|
a54b5c18c6 | ||
|
|
c763472914 | ||
|
|
3a77e7ba2d | ||
|
|
41d6c117ee | ||
|
|
e8f4c42bfb | ||
|
|
bce8469e59 | ||
|
|
3f46ca41ca | ||
|
|
6b1ef6e1b9 | ||
|
|
349022ae42 | ||
|
|
bb518d3d51 | ||
|
|
c45e202fab | ||
|
|
541464b705 | ||
|
|
c762acd780 | ||
|
|
ec98ff297c | ||
|
|
af2c6c5575 | ||
|
|
7d220fb2eb | ||
|
|
f0c49b58fb | ||
|
|
24eeadea76 | ||
|
|
455c71ff25 | ||
|
|
96850da31e | ||
|
|
6ba992dabd | ||
|
|
2bdfcedd0e | ||
|
|
c93ab8f829 | ||
|
|
8abd08bd1b | ||
|
|
33355c0abe | ||
|
|
b830babcf4 | ||
|
|
97765ef895 | ||
|
|
8943d212ee | ||
|
|
86e118f2b7 | ||
|
|
8c3823fc92 | ||
|
|
b0874410f1 | ||
|
|
797f5eb714 | ||
|
|
fc29911ffd | ||
|
|
1e17ef2f21 | ||
|
|
b3e7b1b5ac | ||
|
|
38a2d45a3c | ||
|
|
49b3ac7f77 | ||
|
|
d124d4cace | ||
|
|
ba369d9b30 | ||
|
|
bef8587d8f | ||
|
|
fcbc16f2fd | ||
|
|
a3b172bbcb | ||
|
|
2f945a4fce | ||
|
|
dc9e5dc2f1 | ||
|
|
9396827379 | ||
|
|
28a055bd78 | ||
|
|
37f1a55147 | ||
|
|
64ec7dd559 | ||
|
|
367df4d0db | ||
|
|
da7e41c188 | ||
|
|
d88fe203e1 | ||
|
|
1e01c30e63 | ||
|
|
b16b753ed2 | ||
|
|
7a55d1fc38 | ||
|
|
f8623b6121 | ||
|
|
9a3c22f47d | ||
|
|
e68cff8bba | ||
|
|
513493fa78 | ||
|
|
a6937c792f | ||
|
|
99c7d5c23a | ||
|
|
cd8e8970de | ||
|
|
26d5ced2ef | ||
|
|
c7234f705a | ||
|
|
c153471c49 | ||
|
|
28369faa00 | ||
|
|
445c5f47ae | ||
|
|
7078ca53c3 | ||
|
|
d6ce5f9fa1 | ||
|
|
f28024cfe8 | ||
|
|
911ab9813e | ||
|
|
cfbf5862f9 | ||
|
|
5cb1f5986d | ||
|
|
0b14c810fb | ||
|
|
c2334db8f8 | ||
|
|
4807092df6 | ||
|
|
bdc1107c96 | ||
|
|
db9223b0d5 | ||
|
|
6ecfe0789f | ||
|
|
a37cf058cd | ||
|
|
f4902e6642 | ||
|
|
5d022c25ba | ||
|
|
0cd9f1b002 | ||
|
|
cd0751d3f1 | ||
|
|
a1d1a5df74 | ||
|
|
197f13f9c0 | ||
|
|
41bfc7899d | ||
|
|
2c129b6d39 | ||
|
|
dbe427d5eb | ||
|
|
3e281d4790 | ||
|
|
15c3d46492 | ||
|
|
6a467a09bd | ||
|
|
ba1b8c7c2b | ||
|
|
1e9eb30aa3 | ||
|
|
d47bf1bada | ||
|
|
59dd60f5cb | ||
|
|
c02a0c4da9 | ||
|
|
84f6024cc9 | ||
|
|
d73b42b726 | ||
|
|
ed0c2e68a5 | ||
|
|
847225c6bf | ||
|
|
a6294df9e8 | ||
|
|
431265a86a | ||
|
|
4255c4901d | ||
|
|
9000b3df4e | ||
|
|
a717542733 | ||
|
|
aace644815 | ||
|
|
0ae170531e | ||
|
|
5d01ee9581 | ||
|
|
8b35ce3320 | ||
|
|
ff0e23d2c4 | ||
|
|
d62d2ed269 | ||
|
|
abee29719d | ||
|
|
5781335814 | ||
|
|
f036b8df2d | ||
|
|
25f63ac22a | ||
|
|
12d6f03dc9 | ||
|
|
6d2c9e367b | ||
|
|
66422d6d83 | ||
|
|
b9476791f4 | ||
|
|
9fb8e8a582 | ||
|
|
377a50fa13 | ||
|
|
da20cae25c | ||
|
|
d0c5732e16 | ||
|
|
f9d9aa0306 | ||
|
|
f5db34b98b | ||
|
|
3c07665479 | ||
|
|
f7f50d049b | ||
|
|
7d51b4c6ed | ||
|
|
35ba16ff3b | ||
|
|
6971b1e9da | ||
|
|
84d987810f | ||
|
|
fed04c1a19 | ||
|
|
f5e7d87f5b | ||
|
|
6ca28adcbb | ||
|
|
8e5d2e1b73 | ||
|
|
e8ad7b4f79 | ||
|
|
d3a49e513c | ||
|
|
5bfab0a796 | ||
|
|
739d1aa9e9 | ||
|
|
e575c6e94d | ||
|
|
7bc2e74683 | ||
|
|
2185019b59 | ||
|
|
5d097651c1 | ||
|
|
81c83f0d54 | ||
|
|
5013ce5649 | ||
|
|
5e11a03f0a | ||
|
|
e14d358420 | ||
|
|
82bb3a9b25 | ||
|
|
669720d8f5 | ||
|
|
45ef6cba9d | ||
|
|
3330d2bb0c | ||
|
|
a97d2bbb63 | ||
|
|
f56f75bb3f | ||
|
|
08a82a0bcd | ||
|
|
3dc19bfd31 | ||
|
|
970f47ce33 | ||
|
|
2ee7ed8dda | ||
|
|
d058b9a595 | ||
|
|
1dda832e39 | ||
|
|
a6af4908d5 | ||
|
|
2c7fff077b | ||
|
|
71df1fc4d6 | ||
|
|
064ecdb5ec | ||
|
|
8ec4783249 | ||
|
|
f9d378f1ce | ||
|
|
f07241bff7 | ||
|
|
a6be32392d | ||
|
|
ac594dbd26 | ||
|
|
954711e980 | ||
|
|
2ba3f4758a | ||
|
|
6be4d508f3 | ||
|
|
dc75868bd3 | ||
|
|
c06a560946 | ||
|
|
0bacd4df5f | ||
|
|
b91eaf5487 | ||
|
|
eebea7b342 | ||
|
|
29c1173e14 | ||
|
|
b962a330ad | ||
|
|
1d973bc3ac | ||
|
|
631c8c9870 | ||
|
|
bce6685d0c | ||
|
|
e412b17f70 | ||
|
|
a92b93192d | ||
|
|
8708a0076f | ||
|
|
83fd289e46 | ||
|
|
ed53cbb7b7 | ||
|
|
3b051dbba3 | ||
|
|
8e4781b0f7 | ||
|
|
d599502b1a | ||
|
|
8571830485 | ||
|
|
48d9a03aa8 | ||
|
|
5fb426b336 | ||
|
|
d752a83eb5 | ||
|
|
e740d5fc4f | ||
|
|
8e3e35a36d | ||
|
|
5ce9c0f1e2 | ||
|
|
ef8c4389e1 | ||
|
|
0547d590e1 | ||
|
|
be31640010 | ||
|
|
39319853ab | ||
|
|
ed42948051 | ||
|
|
a0e545a6f1 | ||
|
|
e77e383efa | ||
|
|
8ce5ceef59 | ||
|
|
3a53e049bd | ||
|
|
5011ecaaa6 | ||
|
|
35b1842a72 | ||
|
|
0292227a6b | ||
|
|
ebce1e34d8 | ||
|
|
cc1244126c | ||
|
|
bdf63cf82c | ||
|
|
0275f7f574 | ||
|
|
779f2fa451 | ||
|
|
1a4250d8cc | ||
|
|
08fafe267a | ||
|
|
d06924b339 | ||
|
|
585116a51f | ||
|
|
b676d7034f | ||
|
|
69a0fe3040 | ||
|
|
5207dd4c9e | ||
|
|
919bf4e144 | ||
|
|
7ed440ba75 | ||
|
|
2db035d23c | ||
|
|
a4b84517dc | ||
|
|
e687773b41 | ||
|
|
df66c2d2dc | ||
|
|
f321eb66c0 | ||
|
|
80b44fc9a9 | ||
|
|
0e68fe4a57 | ||
|
|
59b471b9a2 | ||
|
|
f22eaa6db5 | ||
|
|
e37244fa0d | ||
|
|
c359c6e634 | ||
|
|
d299cbaabd | ||
|
|
2b22bfadbc | ||
|
|
baec22610e | ||
|
|
43b587636b | ||
|
|
c6cdb26f47 | ||
|
|
1285e30b3e | ||
|
|
a8e1cd9a13 | ||
|
|
d6f5640685 | ||
|
|
79dbf2a43e | ||
|
|
5ad4c2a65e | ||
|
|
fffa550bb0 | ||
|
|
0b9cb4e75b | ||
|
|
7f143a7f23 | ||
|
|
d8d8a68814 | ||
|
|
4018cf9d76 | ||
|
|
bd33ac202f | ||
|
|
e091eba831 | ||
|
|
4a0dbec4fb | ||
|
|
90dee900f0 | ||
|
|
94555b9c43 | ||
|
|
db93a7315f | ||
|
|
7a19533380 | ||
|
|
9d79b26506 | ||
|
|
b43a9cc80d | ||
|
|
b5618af308 | ||
|
|
9c8c3b9174 | ||
|
|
01e591b261 | ||
|
|
060e30d283 | ||
|
|
ad019da553 | ||
|
|
69afd3a1da | ||
|
|
7978adc577 | ||
|
|
ca77ca6ef0 | ||
|
|
d5b61ed544 | ||
|
|
5edb256990 | ||
|
|
74d0c04314 | ||
|
|
39d4464be0 | ||
|
|
be48dc6e87 | ||
|
|
2783337284 | ||
|
|
727743979c | ||
|
|
4543e14c57 | ||
|
|
83fc1b0b8e | ||
|
|
df858d9143 | ||
|
|
ac47c9c673 | ||
|
|
b9a2d5df02 | ||
|
|
3e873f88c9 | ||
|
|
277cef5ec4 | ||
|
|
5c9b478e46 | ||
|
|
ff89edf127 | ||
|
|
2cc9791bf2 | ||
|
|
67b32005f6 | ||
|
|
0f166973ca | ||
|
|
4f3333c841 | ||
|
|
bea384abea | ||
|
|
43033695f6 | ||
|
|
51ef7ef61c | ||
|
|
823b499a02 | ||
|
|
bb5ed0b40c | ||
|
|
94ca2514af | ||
|
|
5412352dec | ||
|
|
c94e8c7df4 | ||
|
|
094541caa6 | ||
|
|
8c59977e34 | ||
|
|
881bca6ae3 | ||
|
|
22865f8ee4 | ||
|
|
f3b728d828 | ||
|
|
bd7328345f | ||
|
|
25eae3c116 | ||
|
|
5cca5472e6 | ||
|
|
8462d382f4 | ||
|
|
2b0d18a6d7 | ||
|
|
edf3b7e2fc | ||
|
|
167d3a0e3c | ||
|
|
86415bc61f | ||
|
|
a6ea37a21e | ||
|
|
3695aa924b | ||
|
|
9e050d1a23 | ||
|
|
34eee2fc26 | ||
|
|
ac10f3055d | ||
|
|
991b74f036 | ||
|
|
589049ef0f | ||
|
|
6b0c7c2313 | ||
|
|
a9c7d0d598 | ||
|
|
ef1dfb153c | ||
|
|
ff9ee5873f | ||
|
|
a7b56bbbb7 | ||
|
|
820a365474 | ||
|
|
1d5d06f731 | ||
|
|
43d458cf72 | ||
|
|
436a3e7f54 | ||
|
|
7015bad905 | ||
|
|
cf8665748b | ||
|
|
1b8da90cbb | ||
|
|
6012585067 | ||
|
|
f162876600 | ||
|
|
6555ae5b0a | ||
|
|
f5af059ef4 | ||
|
|
cb8651ec68 | ||
|
|
7c0b0a4e3e | ||
|
|
880d1a7ccd | ||
|
|
744b25190a | ||
|
|
3792bb4928 | ||
|
|
9049902ced | ||
|
|
5f93dc72fd | ||
|
|
09dadd7e01 | ||
|
|
60b92f98db | ||
|
|
97f315d488 | ||
|
|
f3676d7f18 | ||
|
|
742dbdb68a | ||
|
|
2d59c968ca | ||
|
|
ad22247c9e | ||
|
|
f38920c338 | ||
|
|
8f90b21a5d | ||
|
|
ff0e6813c6 | ||
|
|
fa5e4d57fd | ||
|
|
876973f071 | ||
|
|
b994af9209 | ||
|
|
1f6cde652e | ||
|
|
3bf6db1c08 | ||
|
|
e70ffc9d7c | ||
|
|
065cfe3b9d | ||
|
|
def9873a70 | ||
|
|
618aa26454 | ||
|
|
924a7bc533 | ||
|
|
ef85277a1b | ||
|
|
876375f2c3 | ||
|
|
f70ee480ba | ||
|
|
6d88c3ab05 | ||
|
|
57c969b0ed | ||
|
|
ae58a7007b | ||
|
|
11c924bbe7 | ||
|
|
8bab4f60ef | ||
|
|
bef9a54f4a | ||
|
|
288b19c3f7 | ||
|
|
40f7e9d33e | ||
|
|
fab53dda66 | ||
|
|
a4e8bf9857 | ||
|
|
2cdf84cdab | ||
|
|
fbe83f729d | ||
|
|
4371a084ec | ||
|
|
d13f58088a | ||
|
|
f75bef7c03 | ||
|
|
3d7e93a688 | ||
|
|
a4dda304d2 | ||
|
|
124c3ef2d7 | ||
|
|
c3a2fca76a | ||
|
|
b60ebfe1c6 | ||
|
|
1d7639b3f4 | ||
|
|
2d972752ff | ||
|
|
616f0b2a21 | ||
|
|
94659ba890 | ||
|
|
d65bc068de | ||
|
|
1ca0354cf2 | ||
|
|
b1fcd4d27b | ||
|
|
74aa07eba8 | ||
|
|
d1a98212ee | ||
|
|
75a31c79ae | ||
|
|
da0b36cb91 | ||
|
|
aa206d034d | ||
|
|
765ab60753 | ||
|
|
44e4ec573d | ||
|
|
0ed793d6d0 | ||
|
|
272e25ff07 | ||
|
|
63127ab181 | ||
|
|
004f3532a0 | ||
|
|
abe1af7b4f | ||
|
|
01df1647bc | ||
|
|
9d8eaf0ccb | ||
|
|
7e4c33d27e | ||
|
|
c164601acf | ||
|
|
3b32da4f5c | ||
|
|
1bc3de8df4 | ||
|
|
374e0cbbc3 | ||
|
|
313921da56 | ||
|
|
2d0e219197 | ||
|
|
dc64d1738a | ||
|
|
89e8d99294 | ||
|
|
66a238045f | ||
|
|
33b82b5669 | ||
|
|
f59d509b15 | ||
|
|
6966539b86 | ||
|
|
0e5dc15005 | ||
|
|
a74f685a5d | ||
|
|
05c7aacfa5 | ||
|
|
ace80c29e7 | ||
|
|
bfb1380dd2 | ||
|
|
ea19802d3f | ||
|
|
fef4f13b8f | ||
|
|
c4fc0f4ecf | ||
|
|
ba3acdac75 | ||
|
|
aad2d68edb | ||
|
|
9e5935aea5 | ||
|
|
129b4a2135 | ||
|
|
82649ab2a7 | ||
|
|
1ba5d25819 | ||
|
|
daa3f8699b | ||
|
|
df7fda9e0c | ||
|
|
484f69f16b | ||
|
|
7c8280934a | ||
|
|
85902b358a | ||
|
|
5931cb59ab | ||
|
|
fd73aab7d0 | ||
|
|
d13fbe5549 | ||
|
|
ed4c00e4f4 | ||
|
|
07282ec39f | ||
|
|
2d998aba43 | ||
|
|
2e0019c8c8 | ||
|
|
96e9608036 | ||
|
|
9d5bb1b2b6 | ||
|
|
67dab9b6d2 | ||
|
|
6fc5f88a3b | ||
|
|
e0cec79ad6 | ||
|
|
1a9e11d86d | ||
|
|
1235d18d67 | ||
|
|
8f25b66760 | ||
|
|
2bc0850b0f | ||
|
|
29176dd9bf | ||
|
|
aedcd1bcc0 | ||
|
|
b1262d54de | ||
|
|
bc4a97774f | ||
|
|
ee3cd44f97 | ||
|
|
726bd0d63b | ||
|
|
ce9640773c | ||
|
|
7ce92118e4 | ||
|
|
e12c5fe007 | ||
|
|
86ff0d86db | ||
|
|
b4236b04c6 | ||
|
|
d34dc397e8 | ||
|
|
f2e4d5f06c | ||
|
|
da7e2f2580 | ||
|
|
e07a20a771 | ||
|
|
ae1b1da342 | ||
|
|
a61d7fe115 | ||
|
|
b4d1e89696 | ||
|
|
a0d90717c3 | ||
|
|
5c2f1f36e8 | ||
|
|
0b084956e6 | ||
|
|
8c61e7d227 | ||
|
|
d7342586a6 | ||
|
|
242e3d007c | ||
|
|
d4b6485102 | ||
|
|
370ab6307a | ||
|
|
83b10fba62 | ||
|
|
1921bce4c7 | ||
|
|
669fb62a54 | ||
|
|
1a5920ee47 | ||
|
|
9c6e3ff1d7 | ||
|
|
ca78601ada | ||
|
|
2edce12759 | ||
|
|
ccc604c0f4 | ||
|
|
d3bf8c2417 | ||
|
|
dc774f0f94 | ||
|
|
f2059947bf | ||
|
|
eccd5b6ff0 | ||
|
|
776dc7ec52 | ||
|
|
082c4f1104 | ||
|
|
06a7e181cd | ||
|
|
bf91e16b5d | ||
|
|
65945b3462 | ||
|
|
31f0c35077 | ||
|
|
fc2dc9a019 | ||
|
|
04645aacc4 | ||
|
|
c91a8711e3 | ||
|
|
7373dae026 | ||
|
|
ca3b819151 | ||
|
|
ba79b94e06 | ||
|
|
bfc3acb834 | ||
|
|
ac67cd7f9a | ||
|
|
9a2c6a7619 | ||
|
|
3100d4f902 | ||
|
|
aace200899 | ||
|
|
a843165cb4 | ||
|
|
36473e3889 | ||
|
|
e2fcab34b7 | ||
|
|
abdf92c084 | ||
|
|
32fc6482cc | ||
|
|
ce14ea6fe5 | ||
|
|
2f57013e02 | ||
|
|
ad84944d20 | ||
|
|
0ab95b1b87 | ||
|
|
58153c3579 | ||
|
|
746f53ba07 | ||
|
|
ff971563db | ||
|
|
242fb7db14 | ||
|
|
ad36738f57 | ||
|
|
c833b16544 | ||
|
|
1c5b350c2b | ||
|
|
9301e39af7 | ||
|
|
86e3b977e4 | ||
|
|
bc330ff0ea | ||
|
|
771480e368 | ||
|
|
c875ff923a | ||
|
|
3dfb44de31 | ||
|
|
2266c3877c | ||
|
|
f4486bc075 | ||
|
|
0436a65baa | ||
|
|
30d6bd144b | ||
|
|
d8381e9486 | ||
|
|
feaecbe177 | ||
|
|
85d796f906 | ||
|
|
0a3af12ee9 | ||
|
|
3925540517 | ||
|
|
3b630fe546 | ||
|
|
c69c4ae8a0 | ||
|
|
b4369470cb | ||
|
|
4a44b18b97 | ||
|
|
2bd6daeb8d | ||
|
|
1ae98b7fe1 | ||
|
|
44ca315c75 | ||
|
|
af20b13c7a | ||
|
|
1f6be38145 | ||
|
|
8b3a7486c7 | ||
|
|
62cd9fffa3 | ||
|
|
7e874eaa7c | ||
|
|
1c7780a423 | ||
|
|
07b77443dd | ||
|
|
4ba1be2dc0 | ||
|
|
6362a7bba5 | ||
|
|
1740715c00 | ||
|
|
21501cbf81 | ||
|
|
d5f3d6111e | ||
|
|
bd04f92087 | ||
|
|
942b2b05e7 | ||
|
|
b8064b9b4b | ||
|
|
f94d03465a | ||
|
|
4e7aafeec1 | ||
|
|
d820b8036e | ||
|
|
3907c17cf5 | ||
|
|
b12fa97a38 | ||
|
|
b9b431e82d | ||
|
|
9f2a2e44a3 | ||
|
|
aaf6c1ea8b | ||
|
|
b2f0278180 | ||
|
|
530eba1b91 | ||
|
|
812d312a9e | ||
|
|
5d256e1d80 | ||
|
|
d02a0c9b3a | ||
|
|
bdbd060229 | ||
|
|
bf04962994 | ||
|
|
33f2ddb696 | ||
|
|
e444519889 | ||
|
|
a47aa8c282 | ||
|
|
0c29aeb9be | ||
|
|
2b4a91cc80 | ||
|
|
9ffc4155dd | ||
|
|
979282a0d4 | ||
|
|
c63818f355 | ||
|
|
c400372a79 | ||
|
|
56f3bdd746 | ||
|
|
cc0367b079 | ||
|
|
e41bbcb2bb | ||
|
|
b35f43d79e | ||
|
|
e9f11e204e | ||
|
|
1b63c9f6ad | ||
|
|
21d99e355c | ||
|
|
f0adbcd5e1 | ||
|
|
bfcf3cfbf1 | ||
|
|
ef5495bfb2 | ||
|
|
c93ee0d65d | ||
|
|
db3e48a81a | ||
|
|
d9b87e877d | ||
|
|
b6175132eb | ||
|
|
57d6c7a3b3 | ||
|
|
d65a282e9d | ||
|
|
801ecaa41c | ||
|
|
49bf735c22 | ||
|
|
cb55944ff6 | ||
|
|
9c225f8d77 | ||
|
|
365fce922c | ||
|
|
fbfc5ecda3 | ||
|
|
4001f48a28 | ||
|
|
22124c25d1 | ||
|
|
17f5bcbd1c | ||
|
|
b7ebb3ea3d | ||
|
|
387830e07a | ||
|
|
da94d40738 | ||
|
|
417b5ed6cc | ||
|
|
005581ef62 | ||
|
|
050390c5c4 | ||
|
|
2648f1ba89 | ||
|
|
d9d31521f9 | ||
|
|
8e24d1b909 | ||
|
|
36fc0daa12 | ||
|
|
44d3854a13 | ||
|
|
1dbc35f13d | ||
|
|
11691fb44a | ||
|
|
acc5592f59 | ||
|
|
614921276e | ||
|
|
3f45a11f12 | ||
|
|
e3464add50 | ||
|
|
18bb4a71c2 | ||
|
|
85e9da82b0 | ||
|
|
99d046ca11 | ||
|
|
0b372a344c | ||
|
|
ffa0f0afd9 | ||
|
|
e21dac21c8 | ||
|
|
0108745065 | ||
|
|
e2a1cd12c3 | ||
|
|
f6ff232106 | ||
|
|
b0c690d836 | ||
|
|
7246624983 | ||
|
|
471c46ad8e | ||
|
|
59032d515b | ||
|
|
d218c9a983 | ||
|
|
243f6e755b | ||
|
|
67b76809ea | ||
|
|
77231bfc6c | ||
|
|
e614226926 | ||
|
|
65e15d74fc | ||
|
|
7ceb81cc83 | ||
|
|
d3f7eea0a3 | ||
|
|
c2f13a1496 | ||
|
|
faae2709d9 | ||
|
|
d595006d1f | ||
|
|
a8d23b5439 | ||
|
|
cfda807057 | ||
|
|
c601a2986f | ||
|
|
8483464aab | ||
|
|
dca69e9b6e | ||
|
|
9450dc84da | ||
|
|
3a2724ec58 | ||
|
|
ee84291997 | ||
|
|
fd9229c467 | ||
|
|
ac5a4fe70f | ||
|
|
873b4f3178 | ||
|
|
beb5d26e6d | ||
|
|
221b7cbf76 | ||
|
|
7d34f1e883 | ||
|
|
208707c00b | ||
|
|
cb41c04551 | ||
|
|
730c6aff11 | ||
|
|
3d40c7603d | ||
|
|
dec7a9a01c | ||
|
|
5f42888b61 | ||
|
|
489c38ec5b | ||
|
|
949fc47f31 | ||
|
|
4d85079372 | ||
|
|
335f9394a5 | ||
|
|
f939a7b349 | ||
|
|
09fdb068d2 | ||
|
|
024c29b180 | ||
|
|
2b0d1a2190 | ||
|
|
d0d71c93af | ||
|
|
da1e52357f | ||
|
|
a05a54b38e | ||
|
|
a0685d804d | ||
|
|
954781262c | ||
|
|
0777bad2c3 | ||
|
|
3159b06988 | ||
|
|
e7ff6fbffc | ||
|
|
6fec92c012 | ||
|
|
e50abbb250 | ||
|
|
8e25226574 | ||
|
|
9636d82b37 | ||
|
|
c3aa6b9cda | ||
|
|
3ef8b3dcbb | ||
|
|
c41554109b | ||
|
|
67b94d3533 | ||
|
|
d52c0633c8 | ||
|
|
4e4c117023 | ||
|
|
cb0d30cf42 | ||
|
|
2d65402ced | ||
|
|
e15b2cc5d6 | ||
|
|
c024905d56 | ||
|
|
6f17624742 | ||
|
|
6f2e6ed887 | ||
|
|
5f1e66d64b | ||
|
|
1f31fdc257 | ||
|
|
df9965e129 | ||
|
|
61e9c31f0d | ||
|
|
a0b35ebd3e | ||
|
|
951ec567c7 | ||
|
|
31494267e5 | ||
|
|
70e4cbc023 | ||
|
|
8d903a09e2 | ||
|
|
63451fb781 | ||
|
|
1e609acb03 | ||
|
|
69194118df | ||
|
|
0f309377ec | ||
|
|
49a19a52c8 | ||
|
|
1a39f7e5c6 | ||
|
|
a5fed64f38 | ||
|
|
79858d4372 | ||
|
|
61897ae16c | ||
|
|
5e0a8ed232 | ||
|
|
5993cc857a | ||
|
|
6a0174293e | ||
|
|
44bb8f6f16 | ||
|
|
a33cad4b70 | ||
|
|
0639cce784 | ||
|
|
a8f227f759 | ||
|
|
f077836bf5 | ||
|
|
6d7847f2df | ||
|
|
221c14cf0e | ||
|
|
6735b2686b | ||
|
|
55ff6beb7d | ||
|
|
4ae41513ac | ||
|
|
438a225487 | ||
|
|
e135696530 | ||
|
|
9135772f89 | ||
|
|
0dc212d97c | ||
|
|
45e8d5c50e | ||
|
|
37ec90c436 | ||
|
|
010541197c | ||
|
|
bdb918cdb3 | ||
|
|
64c986ebbb | ||
|
|
a4c4bf4b58 | ||
|
|
60b1b2ca4a | ||
|
|
8e0f1de25a | ||
|
|
dba6d68108 | ||
|
|
d226834eef | ||
|
|
2facf14443 | ||
|
|
6bd44f0e4b | ||
|
|
1dcb878796 | ||
|
|
0e0169d22b | ||
|
|
50c8a84037 | ||
|
|
1975adc48f | ||
|
|
71564f0d10 | ||
|
|
5a32082624 | ||
|
|
45aa78d953 | ||
|
|
86e8614934 | ||
|
|
ead89c767a | ||
|
|
8bae4975fb | ||
|
|
7a5146ea74 | ||
|
|
9633c247f0 | ||
|
|
78640532e1 | ||
|
|
46ee427ee3 | ||
|
|
0c2b0081b5 | ||
|
|
f133a7f9fd | ||
|
|
a6c9ee446a | ||
|
|
153aaa6d21 | ||
|
|
e5901dad91 | ||
|
|
9318388007 | ||
|
|
bdd75e1171 | ||
|
|
7d22ddd710 | ||
|
|
7e82c8e279 | ||
|
|
db6a0e6ad9 | ||
|
|
648d035a0f | ||
|
|
7ebf2f010c | ||
|
|
3db4421aa7 | ||
|
|
9fb59e128b | ||
|
|
c7c6e5917a | ||
|
|
7b418b3adf | ||
|
|
1c8d662e30 | ||
|
|
d4bfeab36c | ||
|
|
e1b1032df9 | ||
|
|
329439d0ae | ||
|
|
0b1cfb2102 | ||
|
|
1e4d2fd053 | ||
|
|
716378bd6b | ||
|
|
de48d3aaec | ||
|
|
b5b195e628 | ||
|
|
23be4c01df | ||
|
|
86782f3479 | ||
|
|
a96c205830 | ||
|
|
9274881c18 | ||
|
|
24c5f07153 | ||
|
|
789ff702ac | ||
|
|
9b6facf3b0 | ||
|
|
d503190647 | ||
|
|
c4d9c03930 | ||
|
|
d7d70b707f | ||
|
|
dbe1e3f577 | ||
|
|
bb7f03857c | ||
|
|
53b43353eb | ||
|
|
b197556447 | ||
|
|
42d4781a96 | ||
|
|
d991cc3b96 | ||
|
|
4d48d35ad7 | ||
|
|
b7ba8f8e93 | ||
|
|
dff510c181 | ||
|
|
1eead0e885 | ||
|
|
e301387896 | ||
|
|
c49e544781 | ||
|
|
d48db501e0 | ||
|
|
ec4e17f75c | ||
|
|
17e69e67b1 | ||
|
|
c4f9f7da06 | ||
|
|
c367476036 | ||
|
|
f5712c4198 | ||
|
|
1aa0da3382 | ||
|
|
27d69894d4 | ||
|
|
7133a07f38 | ||
|
|
627d8cfe69 | ||
|
|
16b992d705 | ||
|
|
3d9c844dca | ||
|
|
c0de9455bb | ||
|
|
65e1871cd7 | ||
|
|
0a431594f8 | ||
|
|
7b22ef4270 | ||
|
|
f77a58b2dc | ||
|
|
142a138cfc | ||
|
|
e6fdf5ad8d | ||
|
|
5700e18257 | ||
|
|
50a77fedca | ||
|
|
51e3d5f7bc | ||
|
|
7f859978dd | ||
|
|
d8134e8a21 | ||
|
|
614d91e0b1 | ||
|
|
30067fc7d7 | ||
|
|
ec29597dbd | ||
|
|
ad211a63f3 | ||
|
|
6b596bd05f | ||
|
|
510b85fd23 | ||
|
|
e3c8f3fd6f | ||
|
|
f9175db28e | ||
|
|
f4798d05e7 | ||
|
|
a2a0f62135 | ||
|
|
d923f0e01b | ||
|
|
2fc16ee13d | ||
|
|
152e579f7e | ||
|
|
90914bb2de | ||
|
|
95fa835191 | ||
|
|
4e37df26a3 | ||
|
|
5e606573b1 | ||
|
|
b3974cb52a | ||
|
|
b7c206c44b | ||
|
|
49c1e47736 | ||
|
|
4e1319d874 | ||
|
|
a9436aa9af | ||
|
|
d503f07564 | ||
|
|
aa7750bfd3 | ||
|
|
8872d1f389 | ||
|
|
f21af4068f | ||
|
|
f4ca6bbb52 | ||
|
|
869d0156ce | ||
|
|
744e893dce | ||
|
|
fe9ac10f02 | ||
|
|
6fb80f226a | ||
|
|
ff19bab800 | ||
|
|
962c2160c7 | ||
|
|
168da33d8b | ||
|
|
b6b25dc9f3 | ||
|
|
3ca17fdc03 | ||
|
|
2249708097 | ||
|
|
2fcaa7d260 | ||
|
|
f3b0e57a54 | ||
|
|
5da92437a1 | ||
|
|
b5bc05ac2b | ||
|
|
45145fa50a | ||
|
|
dd8200e8b0 | ||
|
|
2f56547d5f | ||
|
|
82bdcfbbcb | ||
|
|
0e38e43315 | ||
|
|
63746be4d5 | ||
|
|
ee73ee365f | ||
|
|
3c53479864 | ||
|
|
4adc741de3 | ||
|
|
64da62dbe6 | ||
|
|
dd9b5faa5c | ||
|
|
51d018acc6 | ||
|
|
5eec580727 | ||
|
|
1e9a53da3f | ||
|
|
8dae044600 | ||
|
|
2d3fad2cdb | ||
|
|
a59a8f62ca | ||
|
|
35cfa7d9fb | ||
|
|
c6ccb373a2 | ||
|
|
b6368170ed | ||
|
|
35e8424293 | ||
|
|
e969d58689 | ||
|
|
ae20e3aa95 | ||
|
|
de4cb74173 | ||
|
|
4f0da87a7a | ||
|
|
97f0347715 | ||
|
|
2ffe62ba41 | ||
|
|
fe1724e7e6 | ||
|
|
2ac2da41cf | ||
|
|
ed574f9d79 | ||
|
|
e0cb26bd9e | ||
|
|
1893127e84 | ||
|
|
b02c9fb118 | ||
|
|
bca0809918 | ||
|
|
00db527377 | ||
|
|
2c6e041ae2 | ||
|
|
a0d6c654cc | ||
|
|
5115c27e72 | ||
|
|
d09c3ccb2d | ||
|
|
5c308026ac | ||
|
|
91919c6d64 | ||
|
|
7168738835 | ||
|
|
9c9b723cf5 | ||
|
|
50450923df | ||
|
|
f392edd66c | ||
|
|
24b48e5d50 | ||
|
|
47f384a0e0 | ||
|
|
88594887f9 | ||
|
|
32e2f0b1fa | ||
|
|
09ed57ad42 | ||
|
|
53a6162b0c | ||
|
|
694d851cdb | ||
|
|
8e53c30a00 | ||
|
|
63e807b0b4 | ||
|
|
012f22cc47 | ||
|
|
9d891ab5dd | ||
|
|
d0e78be867 | ||
|
|
cbedebc9dd | ||
|
|
969f9aa436 | ||
|
|
b982be5ff5 | ||
|
|
2d154ee640 | ||
|
|
49810eb153 | ||
|
|
85b88b8749 | ||
|
|
239c8b5172 | ||
|
|
8c800dc178 | ||
|
|
cdd068d99a | ||
|
|
48fa10b080 | ||
|
|
a1dbec0fcb | ||
|
|
abe668f1c3 | ||
|
|
77440c235d | ||
|
|
fd1ee48dbe | ||
|
|
205e807b66 | ||
|
|
34295adb05 | ||
|
|
7c212bef63 | ||
|
|
76f95644b7 | ||
|
|
928b90d5bc | ||
|
|
09c6c2a4f3 | ||
|
|
2b2bd733e9 | ||
|
|
0d2d7e5e71 | ||
|
|
6142e93252 | ||
|
|
ccec3376ba | ||
|
|
f497a74ec4 | ||
|
|
0e666e7d6a | ||
|
|
f498fabd27 | ||
|
|
8b49a55442 | ||
|
|
a26eb942a9 | ||
|
|
eabcafa516 | ||
|
|
6cc388c1bc | ||
|
|
62e39ddfbd | ||
|
|
80373623cd | ||
|
|
451c3945f0 | ||
|
|
00cb15d9b4 | ||
|
|
67dd59125e | ||
|
|
b6800dd125 | ||
|
|
dc9da69509 | ||
|
|
d7d964bf57 | ||
|
|
bcfe44db54 | ||
|
|
376bf6ba72 | ||
|
|
f651baab25 | ||
|
|
61752e2aab | ||
|
|
b7d3fd959e | ||
|
|
7ac05f8487 | ||
|
|
5cfc574f9a | ||
|
|
4f70822b13 | ||
|
|
0007f304d0 | ||
|
|
4afef91359 | ||
|
|
815b6db0bf | ||
|
|
433d3bf582 | ||
|
|
a335841509 | ||
|
|
26ad793d82 | ||
|
|
5337aa10f7 | ||
|
|
9f79bdae9b | ||
|
|
db84be2488 | ||
|
|
599ec62bb0 | ||
|
|
19a88300c6 | ||
|
|
b5d55e1ffb | ||
|
|
521fb83e38 | ||
|
|
553d59c32b | ||
|
|
9ed58e5186 | ||
|
|
36eaaa748c | ||
|
|
4d7b86ca26 | ||
|
|
5faf84c732 | ||
|
|
d7b819267f | ||
|
|
7417867d0f | ||
|
|
8d74905257 | ||
|
|
c38298c06e | ||
|
|
3100d587d1 | ||
|
|
ba849d0300 | ||
|
|
95df3e4b39 | ||
|
|
72492e33a0 | ||
|
|
934f1269f5 | ||
|
|
e6956d9bb0 | ||
|
|
2877900233 | ||
|
|
df1aa52e08 | ||
|
|
5fa2485a7d | ||
|
|
29f0e10411 | ||
|
|
39300a5bbf | ||
|
|
35d6268675 | ||
|
|
0abb871f3f | ||
|
|
704fca969f | ||
|
|
95debf8c80 | ||
|
|
dd94b77b2a | ||
|
|
6cfe4fa580 | ||
|
|
515c086099 | ||
|
|
34ce06ac17 | ||
|
|
a104c9881e | ||
|
|
c3e3c091cc | ||
|
|
651240113c | ||
|
|
77189bf8e9 | ||
|
|
60fd3a4542 | ||
|
|
c66f9c8d6d | ||
|
|
569088eaca | ||
|
|
a7e8dd04fe | ||
|
|
dfdd76a1bb | ||
|
|
28aac6f93b | ||
|
|
c2f47119ce | ||
|
|
d6b1d0d4fb | ||
|
|
03a861745b | ||
|
|
9a7aed20e9 | ||
|
|
b7f17d4cb1 | ||
|
|
2497c3d187 | ||
|
|
f7a084969a | ||
|
|
2900bc26a5 | ||
|
|
2334c56a96 | ||
|
|
90a5d02bf6 | ||
|
|
81d9626da9 | ||
|
|
44a2549b81 | ||
|
|
a2b8d468bc | ||
|
|
d523f0cadd | ||
|
|
99116ff097 | ||
|
|
3939ca9eb4 | ||
|
|
b5aa67b491 | ||
|
|
e42efec220 | ||
|
|
9d06aa2f6a | ||
|
|
80765a797b | ||
|
|
0b5509a1ed | ||
|
|
478d7b4a83 | ||
|
|
9d3b38141a | ||
|
|
ab3a4d902e | ||
|
|
5eab5f2437 | ||
|
|
80f632c19a | ||
|
|
6e4f18543d | ||
|
|
54586c9076 | ||
|
|
351c899807 | ||
|
|
fe45d431d7 | ||
|
|
488c2f6d05 | ||
|
|
75ab0909b3 | ||
|
|
8f82d563c1 | ||
|
|
9bbce5dba6 | ||
|
|
099adab9ed | ||
|
|
c8cbf425ac | ||
|
|
ad9c11cd92 | ||
|
|
3872c2a3f5 | ||
|
|
e6a09b49c9 | ||
|
|
db107602bd | ||
|
|
a6558a61a7 | ||
|
|
254d2b82b3 | ||
|
|
2c9fa2f738 | ||
|
|
97d9795fc9 | ||
|
|
54071b0e5d | ||
|
|
925e8316c7 | ||
|
|
99e1b74023 | ||
|
|
7d68ccca53 | ||
|
|
a090114066 | ||
|
|
a204841abb | ||
|
|
cc451809cc | ||
|
|
a605e4bab6 | ||
|
|
3f0534134d | ||
|
|
3acfb129cd | ||
|
|
6ccef66920 | ||
|
|
e9fa4e94a6 | ||
|
|
fecc0c4640 | ||
|
|
b759294975 | ||
|
|
a23e845c03 | ||
|
|
cb8373e487 | ||
|
|
8e919ddc8e | ||
|
|
832a9ab6b5 | ||
|
|
13732ac333 | ||
|
|
3e932a55f4 | ||
|
|
74e8610ec9 | ||
|
|
089a60ded6 | ||
|
|
c8eeefe194 | ||
|
|
85eeba14c1 | ||
|
|
f6f45eab39 | ||
|
|
a74065f775 | ||
|
|
48d02f7e09 | ||
|
|
e60549f8df | ||
|
|
41f4f4713e | ||
|
|
213a292fd5 | ||
|
|
79630e844b | ||
|
|
1c9e46dbb3 | ||
|
|
0a299284f8 | ||
|
|
347a5f7346 | ||
|
|
c6a903572c | ||
|
|
14f0d6d26b | ||
|
|
485f105555 | ||
|
|
686c0b776f | ||
|
|
828862ea49 | ||
|
|
c4dffa4dc8 | ||
|
|
3c1906e3d4 | ||
|
|
7147a3694c | ||
|
|
64707dbb22 | ||
|
|
554e8eeef3 | ||
|
|
5e10549543 | ||
|
|
685f45bd76 | ||
|
|
61d84dd4c1 | ||
|
|
4d10593bb1 | ||
|
|
fbb8903774 | ||
|
|
e8cac91bb7 | ||
|
|
7328ffa036 | ||
|
|
a03e828317 | ||
|
|
93d4dc70cf | ||
|
|
8e3d16e9fb | ||
|
|
07405e57b9 | ||
|
|
354c9187db | ||
|
|
af33df3004 | ||
|
|
78bfde237f | ||
|
|
7b9033d678 | ||
|
|
f784cfad46 | ||
|
|
e40c139ff1 | ||
|
|
edf4f7695d | ||
|
|
60ec03237e | ||
|
|
a91641e427 | ||
|
|
5c3992018f | ||
|
|
f5b682619f | ||
|
|
743fa745b7 | ||
|
|
39400fd381 | ||
|
|
5299ac35a6 | ||
|
|
ef76ed394c | ||
|
|
1472637de7 | ||
|
|
3b8baa85a3 | ||
|
|
73921b1024 | ||
|
|
ece140f18c | ||
|
|
5e42947fbd | ||
|
|
1bfb9b02f5 | ||
|
|
16a14c2b76 | ||
|
|
f6199c6c17 | ||
|
|
d7e7f06e88 | ||
|
|
4c4e856a1a | ||
|
|
07bbbbaf61 | ||
|
|
3236827781 | ||
|
|
0be664cc3d | ||
|
|
6cc6849ccc | ||
|
|
5d5cd71714 | ||
|
|
d248343517 | ||
|
|
64d800427f | ||
|
|
c4c896a833 | ||
|
|
b6b5bb3f75 | ||
|
|
5d69bb7383 | ||
|
|
76e222079a | ||
|
|
73abb9278d | ||
|
|
8fd843e7ce | ||
|
|
6a497a23d9 | ||
|
|
3ac74e1091 | ||
|
|
ef0fb48f1f | ||
|
|
414ef2bc3d | ||
|
|
ea791309ad | ||
|
|
706da6e431 | ||
|
|
ed116e7cea | ||
|
|
5b56f4007b | ||
|
|
e2071542bf | ||
|
|
cdb217b774 | ||
|
|
079798940b | ||
|
|
f1c24689bf | ||
|
|
1f9cf6ed7c | ||
|
|
43f218410f | ||
|
|
3fd9d5f641 | ||
|
|
f5ab8f2062 | ||
|
|
8774a8fbc2 | ||
|
|
6f4f0f03d2 | ||
|
|
00b5fdce03 | ||
|
|
baee6a0d91 | ||
|
|
ff44bcc489 | ||
|
|
c797ac4268 | ||
|
|
d22a76d4d1 | ||
|
|
a6642e0ebc | ||
|
|
3d4d260a34 | ||
|
|
8e4b9da97d | ||
|
|
2be80ba30f | ||
|
|
2e44c88d6c | ||
|
|
21eb1ce6c9 | ||
|
|
cdfd411df7 | ||
|
|
a6149ca90c | ||
|
|
642435486c | ||
|
|
fc84d6c4b7 | ||
|
|
aa4bddd6ec | ||
|
|
8ec12a1b65 | ||
|
|
0fbf552e95 | ||
|
|
09b1b120d7 | ||
|
|
557244bc3f | ||
|
|
24c5ed1cff | ||
|
|
32e55ebd0c | ||
|
|
ea3070d02b | ||
|
|
9aaba49a9f | ||
|
|
9b64be07a9 | ||
|
|
42c3c28ea7 | ||
|
|
9e9236badb | ||
|
|
560ebcec8d | ||
|
|
9b1fe4338b | ||
|
|
9188e3ad3f | ||
|
|
af65af5be9 | ||
|
|
2f0115c300 | ||
|
|
0646461342 | ||
|
|
ec30ec0996 | ||
|
|
cdecb7a43c | ||
|
|
aa9c1b66a0 | ||
|
|
846eac29dc | ||
|
|
0f9e3c5b33 | ||
|
|
aa27746982 | ||
|
|
d8a4954bf1 | ||
|
|
d40a029dae | ||
|
|
96d961c393 | ||
|
|
7b6814e32d | ||
|
|
6fee2d3536 | ||
|
|
636fc633d4 | ||
|
|
72a239838e | ||
|
|
a463dbc5fb | ||
|
|
016ae3b9e9 | ||
|
|
7d0d421724 | ||
|
|
83b5856a19 | ||
|
|
f617b27110 | ||
|
|
a91a0263cf | ||
|
|
80ffe13f3e | ||
|
|
1eb726c9bb | ||
|
|
1fa3ba8b42 | ||
|
|
b6bfd66a49 | ||
|
|
1be0e7ddaa | ||
|
|
2cac9b03ff | ||
|
|
f5f4190803 | ||
|
|
a14d554947 | ||
|
|
6d9e5147b5 | ||
|
|
841452cb9e | ||
|
|
9c76368dbc | ||
|
|
bd5122c6ea | ||
|
|
6643258618 | ||
|
|
bc3f02cb6b | ||
|
|
d848ae332a | ||
|
|
08ddc98303 | ||
|
|
a3344c4290 | ||
|
|
22c1ce3ea5 | ||
|
|
afb14e6782 | ||
|
|
e177363377 | ||
|
|
ce213934c9 | ||
|
|
af286ec52e | ||
|
|
f7f2b7607b | ||
|
|
60a282826c | ||
|
|
3eba599aec | ||
|
|
74d876f145 | ||
|
|
d7609f119c | ||
|
|
65c2c7d80b | ||
|
|
468a32a819 | ||
|
|
b89cf73ae2 | ||
|
|
9cf43dea1a | ||
|
|
670ffe2078 | ||
|
|
884cf756ed | ||
|
|
e44ba54857 | ||
|
|
3712749a94 | ||
|
|
6569c4aa03 | ||
|
|
d6b2b3c996 | ||
|
|
06c7900ece | ||
|
|
52a6a12a9a | ||
|
|
e647603dce | ||
|
|
dadf6174ba | ||
|
|
84de7675c4 | ||
|
|
6311a80d0e | ||
|
|
9504e69598 | ||
|
|
5398b651f7 | ||
|
|
b5596c4596 | ||
|
|
fdcea5537c | ||
|
|
8ca8bc810d | ||
|
|
8f909b051f | ||
|
|
90f2b2d249 | ||
|
|
f74b27c58c | ||
|
|
3f091f4748 | ||
|
|
d84c9ad611 | ||
|
|
e55e15693d | ||
|
|
c54e6bafdb | ||
|
|
2e56c4895d | ||
|
|
bce4224d6e | ||
|
|
812e2814bc | ||
|
|
7cd17f8e1f | ||
|
|
6193b06708 | ||
|
|
12af68bdb5 | ||
|
|
881f7e9062 | ||
|
|
1db4076bbd | ||
|
|
1933e44719 | ||
|
|
25441cb650 | ||
|
|
bc755ac32f | ||
|
|
1fa34be52a | ||
|
|
f7a6d57855 | ||
|
|
8a987af244 | ||
|
|
65cbb06080 | ||
|
|
979ea9c252 | ||
|
|
aa1f4ee72a | ||
|
|
74ce485b73 | ||
|
|
165e6508f8 | ||
|
|
c7af2889fa | ||
|
|
5ab3390434 | ||
|
|
67f60f1889 | ||
|
|
985a468d0f | ||
|
|
34dc6fbdc1 | ||
|
|
b57152cc25 | ||
|
|
dc9562e430 | ||
|
|
05689fe183 | ||
|
|
8f6f95211e | ||
|
|
f30b6c9e6e | ||
|
|
12ac7d6a00 | ||
|
|
10251a6447 | ||
|
|
089cbbc20a | ||
|
|
95ab68acd1 | ||
|
|
abc4f6c70b | ||
|
|
8fc3a1f9c9 | ||
|
|
5c3d0fc02c | ||
|
|
7efb47fed4 | ||
|
|
7692332f0e | ||
|
|
ef6db64e9f | ||
|
|
e68f1dbc99 | ||
|
|
0c9ebc36d4 | ||
|
|
fcd6eb7801 | ||
|
|
328c2182c2 | ||
|
|
08706f5dfb | ||
|
|
d49f165f0d | ||
|
|
cf0fc3a4a9 | ||
|
|
72c8fd257c | ||
|
|
fa620e41a4 | ||
|
|
b07f851ce7 | ||
|
|
16b3108719 | ||
|
|
f385c624c7 | ||
|
|
f7e9975192 | ||
|
|
cde989b59d | ||
|
|
c0e263abd3 | ||
|
|
79c0c11e80 | ||
|
|
ca671551c8 | ||
|
|
42ed312384 | ||
|
|
0e9074aaba | ||
|
|
7c1961d4ef | ||
|
|
71e57717c2 | ||
|
|
8a549b83a2 | ||
|
|
d7081c5f23 | ||
|
|
588d64a30b | ||
|
|
8335bdf3d4 | ||
|
|
85394f2438 | ||
|
|
42b556574f | ||
|
|
f34e65ad9e | ||
|
|
51352a6819 | ||
|
|
d9887ec370 | ||
|
|
c994950aaf | ||
|
|
a26ed6fe6c | ||
|
|
a12a7e73f9 | ||
|
|
779228857e | ||
|
|
8d0b696d33 | ||
|
|
23ae220aa7 | ||
|
|
b7940e0002 | ||
|
|
b3fd8bd0ae | ||
|
|
bffeb237de | ||
|
|
23e3602ea1 | ||
|
|
34cfd205f6 | ||
|
|
df3da8be7a | ||
|
|
940243f45e | ||
|
|
75d6599143 | ||
|
|
929a27a5ac | ||
|
|
82ddee2104 | ||
|
|
a141678119 | ||
|
|
96d109af81 | ||
|
|
a309eb9f3c | ||
|
|
d034dab265 | ||
|
|
883a035e5c | ||
|
|
08603091c5 | ||
|
|
a2e84e5a1e | ||
|
|
d148898ad7 | ||
|
|
9439621849 | ||
|
|
36cf622979 | ||
|
|
15ded89618 | ||
|
|
b84f74c167 | ||
|
|
a97300f8be | ||
|
|
9e12cff317 | ||
|
|
ecdf1f4ddc | ||
|
|
2fa7a48163 | ||
|
|
5e31e533e2 | ||
|
|
8adf76dcc9 | ||
|
|
15899c10b2 | ||
|
|
05ff05ea4b | ||
|
|
bd62df48c2 | ||
|
|
2366cbc833 | ||
|
|
25fb609544 | ||
|
|
af793395f0 | ||
|
|
8f41776858 | ||
|
|
139b13b8d1 | ||
|
|
4c611a5be1 | ||
|
|
5e7a21e177 | ||
|
|
5f7dda5ba8 | ||
|
|
2dfa1ca0f2 | ||
|
|
358cdcf4c4 | ||
|
|
c8f4ace5c4 | ||
|
|
5cac6ca8bb | ||
|
|
fccad71df1 | ||
|
|
97ae2674dc | ||
|
|
7c70affd7f | ||
|
|
52ff568d86 | ||
|
|
b917aeaa0b | ||
|
|
8de443ec4c | ||
|
|
7d9893c614 | ||
|
|
3540712517 | ||
|
|
a8b1a86bd7 | ||
|
|
1babd3a5a2 | ||
|
|
5ecd04dd4f | ||
|
|
50399e5194 | ||
|
|
b734acf1b1 | ||
|
|
33aa8e2471 | ||
|
|
2c58fe736b | ||
|
|
6fe1de5d86 | ||
|
|
064460b95f | ||
|
|
2c3b19a539 | ||
|
|
dc30a4c1ae | ||
|
|
86e9901bf2 | ||
|
|
6519e0835a | ||
|
|
a52344fc01 | ||
|
|
b67424643d | ||
|
|
575a4c01c9 | ||
|
|
f0d4ee6618 | ||
|
|
8753186a0d | ||
|
|
ff8fb8000d | ||
|
|
9dd38b99d6 | ||
|
|
dfe08c1ec9 | ||
|
|
fb26e78ecc | ||
|
|
4c687036c4 | ||
|
|
062d8d0f4f | ||
|
|
73b6338f62 | ||
|
|
c0d1e2c07a | ||
|
|
e70feceafe | ||
|
|
71ac0286b1 | ||
|
|
022f4d2c11 | ||
|
|
a83a839cff | ||
|
|
b259ee89aa | ||
|
|
65cf14bfce | ||
|
|
d9476fb5ca | ||
|
|
9882365ab4 | ||
|
|
2d758ce963 | ||
|
|
1dd003d26a | ||
|
|
0df5b77595 | ||
|
|
e190a005db | ||
|
|
45596a0342 | ||
|
|
405429a300 | ||
|
|
d009a29426 | ||
|
|
f1fb42460a | ||
|
|
5e110e9f7b | ||
|
|
77a409935d | ||
|
|
863baeb68b | ||
|
|
11142690a0 | ||
|
|
02e8c5faca | ||
|
|
c41081d35c | ||
|
|
db4c26a400 | ||
|
|
331a23fc20 | ||
|
|
db5a40d743 | ||
|
|
e4ab51329d | ||
|
|
8490e7ca7c | ||
|
|
86782aeb1b | ||
|
|
49a44fc92e | ||
|
|
cd39a52c25 | ||
|
|
634101ceb5 | ||
|
|
55555c8787 | ||
|
|
d36d825ac1 | ||
|
|
9bb01cd67c | ||
|
|
29b91075d2 | ||
|
|
6d46fc9f9f | ||
|
|
a2c41c9e36 | ||
|
|
ee700ac861 | ||
|
|
9884a4336f | ||
|
|
5b83d4bef8 | ||
|
|
d320a89590 | ||
|
|
f7e4afc282 | ||
|
|
88e87d589b | ||
|
|
d8c6dede7e | ||
|
|
5cc84133e3 | ||
|
|
f7728aa1f6 | ||
|
|
2b61f9a731 | ||
|
|
f407022fe6 | ||
|
|
41b9f19b01 | ||
|
|
09c6faf923 | ||
|
|
26d0177c01 | ||
|
|
f7415c8a8f | ||
|
|
4cf79088f9 | ||
|
|
50cd321818 | ||
|
|
83bbe6a9d9 | ||
|
|
0a33c18e36 | ||
|
|
6cf158ac63 | ||
|
|
f96bfa6afa | ||
|
|
2b64cf9126 | ||
|
|
a8dcfc44f5 | ||
|
|
0ff9c9da27 | ||
|
|
07e7c2d852 | ||
|
|
10e4b5b2a3 | ||
|
|
998653ea9d | ||
|
|
1a38e925bf | ||
|
|
c8f51380e6 | ||
|
|
2406d57d51 | ||
|
|
cb1e47eb71 | ||
|
|
c0a650f28b | ||
|
|
460cf6fd20 | ||
|
|
5bedfc1c84 | ||
|
|
5001592fb4 | ||
|
|
f6495e59c5 | ||
|
|
66bf431481 | ||
|
|
d9685e991e | ||
|
|
e0790700cd | ||
|
|
910a9600bd | ||
|
|
fc52b2b940 | ||
|
|
b99f828583 | ||
|
|
f38891cace | ||
|
|
8c5111e11a | ||
|
|
5575b981c8 | ||
|
|
0b36732911 | ||
|
|
52f3081a40 | ||
|
|
00c71dc26a | ||
|
|
5218c8584f | ||
|
|
6054bd6621 | ||
|
|
55af4ed385 | ||
|
|
64aee9c8ae | ||
|
|
5233e72205 | ||
|
|
db5b45222a | ||
|
|
fc4787da4e | ||
|
|
4ffbb46cf9 | ||
|
|
c3c2550f17 | ||
|
|
41e8ab5383 | ||
|
|
a802940616 | ||
|
|
dec848f072 | ||
|
|
fb229d4064 | ||
|
|
fc16e76af1 | ||
|
|
0dff636dbe | ||
|
|
00df3f8d4e | ||
|
|
34c45f2694 | ||
|
|
a188de2e5c | ||
|
|
27fbf67352 | ||
|
|
b226e22d2f | ||
|
|
5bc157eb19 | ||
|
|
f4122abbad | ||
|
|
f0b32e3f54 | ||
|
|
fe00999b2c | ||
|
|
39eed0f6fb | ||
|
|
510d29b381 | ||
|
|
0aa618b938 | ||
|
|
5884852612 | ||
|
|
5b29592174 | ||
|
|
96411cc93e | ||
|
|
7d862d8eba | ||
|
|
dd392941d0 | ||
|
|
3cec5235c9 | ||
|
|
b5682012d3 | ||
|
|
4351a2736c | ||
|
|
9c7cadb191 | ||
|
|
4d9143734f | ||
|
|
3cec923294 | ||
|
|
58c92b8405 | ||
|
|
985b618932 | ||
|
|
a027a42c46 | ||
|
|
bdc7acffbe | ||
|
|
6bd73cdea2 | ||
|
|
59954c1d7c | ||
|
|
a59cdcc9e0 | ||
|
|
e1bfa786fc | ||
|
|
d5214099c5 | ||
|
|
e05110ff44 | ||
|
|
706b976a28 | ||
|
|
2bd7a92d20 | ||
|
|
6b37a41e00 | ||
|
|
5447259e1a | ||
|
|
ee0ae0b74b | ||
|
|
966256ac32 | ||
|
|
6b9061515f | ||
|
|
df60e78766 | ||
|
|
bf1e1ad457 | ||
|
|
7fa5b06359 | ||
|
|
3b46e9f351 | ||
|
|
046a80cfe4 | ||
|
|
a8278fc78b | ||
|
|
7f3127ac89 | ||
|
|
7cdb021a1f | ||
|
|
74c0b729c2 | ||
|
|
5cb81f8532 | ||
|
|
4f23d7b7df | ||
|
|
a70d0edf2e | ||
|
|
8c9eaccc11 | ||
|
|
86c1984982 | ||
|
|
cd0f75106a | ||
|
|
b5291b5151 | ||
|
|
46283dc0ea | ||
|
|
56e76ec59f | ||
|
|
4cedaa9e80 | ||
|
|
516f140bef | ||
|
|
5d86c1c9a6 | ||
|
|
d289aa71eb | ||
|
|
ed2818eaa2 | ||
|
|
f8fe124428 | ||
|
|
5ec11c53e9 | ||
|
|
42d118d9a2 | ||
|
|
d8b4765f23 | ||
|
|
be69280d0d | ||
|
|
53a1a097a6 | ||
|
|
a22e9a2ca7 | ||
|
|
db03595473 | ||
|
|
8fadac0fdc | ||
|
|
a63bc1cdca | ||
|
|
6265d452e9 | ||
|
|
b095399770 | ||
|
|
db8a546b8f | ||
|
|
6e95318cba | ||
|
|
08a8ab9892 | ||
|
|
c7b796ff31 | ||
|
|
ad23ccb219 | ||
|
|
be7a84fdf3 | ||
|
|
2fbbbf298b | ||
|
|
0df68872ab | ||
|
|
0ced38cdcb | ||
|
|
b046c45a9e | ||
|
|
2ce1ab1634 | ||
|
|
7225231814 | ||
|
|
11dca2b352 | ||
|
|
97127e86dc | ||
|
|
cb81195959 | ||
|
|
adaff9f354 | ||
|
|
66de7ad049 | ||
|
|
1e1e4da144 | ||
|
|
623433099b | ||
|
|
73b3fbc2da | ||
|
|
5f525d0e43 | ||
|
|
60463fdafa | ||
|
|
b7a67b4b03 | ||
|
|
4643c92d33 | ||
|
|
396cba7339 | ||
|
|
a2b3ee53e0 | ||
|
|
2c67d2055c | ||
|
|
c8de7aa23c | ||
|
|
fa154cc4d6 | ||
|
|
d9b8731ddc | ||
|
|
6cebc1a2a2 | ||
|
|
faac35cd1e | ||
|
|
6916147dda | ||
|
|
e2da16e9c3 | ||
|
|
0c661e7373 | ||
|
|
413f8e8462 | ||
|
|
eefbbd4efe | ||
|
|
83932a6f02 | ||
|
|
c175dc30f8 | ||
|
|
17aa91803a | ||
|
|
48099a367e | ||
|
|
a9b64893d8 | ||
|
|
387e030d83 | ||
|
|
855cc9ed83 | ||
|
|
82534eef12 | ||
|
|
ff4e254618 | ||
|
|
571a13f0a7 | ||
|
|
2cb6283d00 | ||
|
|
f4056e57bb | ||
|
|
e80da3cbeb | ||
|
|
c0436297c2 | ||
|
|
0d05b4f095 | ||
|
|
f06c8710be | ||
|
|
f11266972e | ||
|
|
479edaf80d | ||
|
|
ff5c26adf2 | ||
|
|
5361e11395 | ||
|
|
b041bcdc65 | ||
|
|
b7c350202d | ||
|
|
b1a6c5ddf7 | ||
|
|
ac943b5712 | ||
|
|
ce8d701ecb | ||
|
|
182ffe4495 | ||
|
|
c13983d395 | ||
|
|
066f8863fd | ||
|
|
e58aaa3f32 | ||
|
|
ca1fa11cb1 | ||
|
|
64ed485cdf | ||
|
|
b0781668e2 | ||
|
|
f9fc744949 | ||
|
|
2661db23f6 | ||
|
|
7d78f60d29 | ||
|
|
1d934bd543 | ||
|
|
190435acd9 | ||
|
|
656236cb4d | ||
|
|
6d15be9a32 | ||
|
|
18d3c81018 | ||
|
|
12292afdec | ||
|
|
aef0f4d7b8 | ||
|
|
21545ab7da | ||
|
|
5a2b795440 | ||
|
|
1303dd478c | ||
|
|
7b4fc19fca | ||
|
|
008a064764 | ||
|
|
82a4630061 | ||
|
|
0f77b4810d | ||
|
|
2f7cfddfc4 | ||
|
|
84608c16b3 | ||
|
|
157411dcc6 | ||
|
|
59672d23cc | ||
|
|
ce30f89c60 | ||
|
|
ce9c9411b1 | ||
|
|
cf0d5b616d | ||
|
|
29e861d1e6 | ||
|
|
c7accd4a5c | ||
|
|
b469080cd7 | ||
|
|
547a0057e6 | ||
|
|
b980ca4a9e | ||
|
|
098b2e968e | ||
|
|
cd59ca8376 | ||
|
|
f2e6fad104 | ||
|
|
8d7fde0287 | ||
|
|
91fdb038d9 | ||
|
|
a0188765c5 | ||
|
|
b970a005de | ||
|
|
b64878f4fa | ||
|
|
c8936c79bf | ||
|
|
f876cc9079 | ||
|
|
a5cc2f3b5d | ||
|
|
9c93d6f931 | ||
|
|
a077d7671f | ||
|
|
6485ebe9a7 | ||
|
|
ecb6bb220a | ||
|
|
e3dc400d74 | ||
|
|
3bb4151074 | ||
|
|
1de4c2e8c6 | ||
|
|
fbcc4f28e7 | ||
|
|
30fb0f5a94 | ||
|
|
b02464990b | ||
|
|
4988a32d33 | ||
|
|
b3e5874631 | ||
|
|
f5349dcef9 | ||
|
|
486a4cfdd6 | ||
|
|
2277dcb069 | ||
|
|
a618a01b1e | ||
|
|
7e60069968 | ||
|
|
91e45d9a4a | ||
|
|
dea6fbf285 | ||
|
|
48cc0f4289 | ||
|
|
cdc5fce583 | ||
|
|
b41a17d548 | ||
|
|
606cbaa519 | ||
|
|
aaf8f527ef | ||
|
|
b7596b7f70 | ||
|
|
0309b574e8 | ||
|
|
ca057177c7 | ||
|
|
5d9bf18267 | ||
|
|
f1b8742782 | ||
|
|
7786c97330 | ||
|
|
f2a14047eb | ||
|
|
124a9cb030 | ||
|
|
3ec000d0f8 | ||
|
|
aac1141ca6 | ||
|
|
33cb96126a | ||
|
|
441db9ad7f | ||
|
|
5225e1d7d1 | ||
|
|
de849b3f6a | ||
|
|
fb4387c41f | ||
|
|
a9061a8f58 | ||
|
|
0c099dc52b | ||
|
|
713e92c28f | ||
|
|
d111025012 | ||
|
|
5f2e6b1262 | ||
|
|
b6d838731f | ||
|
|
56db8b40b2 | ||
|
|
f488c97a09 | ||
|
|
31df49a884 | ||
|
|
e5fdced4ac | ||
|
|
71546367cf | ||
|
|
857817dae8 | ||
|
|
ae3fca15c7 | ||
|
|
6bb7382dbd | ||
|
|
badb837b46 | ||
|
|
74f5b70a5d | ||
|
|
ac495da5fe | ||
|
|
56f6e57118 | ||
|
|
33735b343d | ||
|
|
1b56d66fc8 | ||
|
|
0994211a48 | ||
|
|
62d9a47c3d | ||
|
|
e77037c2b8 | ||
|
|
030a6ebb71 | ||
|
|
5a657cff89 | ||
|
|
f3488be7af | ||
|
|
4af0caa506 | ||
|
|
0728991821 | ||
|
|
21c35f770b | ||
|
|
f039af6eda | ||
|
|
eb3f703b46 | ||
|
|
b88b82a85c | ||
|
|
1d0791dbf5 | ||
|
|
87f2eefd35 | ||
|
|
b8a2c9f955 | ||
|
|
319d748639 | ||
|
|
4f84d687e4 | ||
|
|
fbb9991128 | ||
|
|
62bac24246 | ||
|
|
4aa8461bea | ||
|
|
ce57a130fc | ||
|
|
80567312ed | ||
|
|
180730f9cf | ||
|
|
fca2693488 | ||
|
|
b6e75e9c5a | ||
|
|
4901434209 | ||
|
|
13d174c09c | ||
|
|
5363c063d1 | ||
|
|
32d300248e | ||
|
|
3426906a4f | ||
|
|
3aaa942c94 | ||
|
|
95d8887ab0 | ||
|
|
6272e15b47 | ||
|
|
20b4f6b24d | ||
|
|
6ee279d83e | ||
|
|
b00ff43be7 | ||
|
|
dfbefee477 | ||
|
|
8c2de4973c | ||
|
|
e1527dc137 | ||
|
|
0957f6b143 | ||
|
|
7db2e9dc4a | ||
|
|
b1c701085b | ||
|
|
e8d6c803cd | ||
|
|
f4a2dda94e | ||
|
|
c4216379ed | ||
|
|
52195bf296 | ||
|
|
10fe75ed87 | ||
|
|
1c659d6ef6 | ||
|
|
3ac86db038 | ||
|
|
4a77a03033 | ||
|
|
3820b51960 | ||
|
|
e070ce4e34 | ||
|
|
0bb0adbf3e | ||
|
|
ddd25f0945 | ||
|
|
162bd592f8 | ||
|
|
85fa728d41 | ||
|
|
c7db9010ad | ||
|
|
be16545063 | ||
|
|
c730839989 | ||
|
|
4ee364640d | ||
|
|
56dd0db001 | ||
|
|
626ed720a6 | ||
|
|
b8fd9ba83f | ||
|
|
316a4457af | ||
|
|
347a2c2150 | ||
|
|
42d3770b14 | ||
|
|
39ca07bcc6 | ||
|
|
df304fb38b | ||
|
|
914566ece0 | ||
|
|
f537e7b2c6 | ||
|
|
06020b8f54 | ||
|
|
b486d1cd27 | ||
|
|
b3b38015c2 | ||
|
|
4c6988e3bc | ||
|
|
0bd4db4cc7 | ||
|
|
bc72800fef | ||
|
|
951f8972c7 | ||
|
|
38b694a055 | ||
|
|
44a9c3ca0c | ||
|
|
6bf823fb15 | ||
|
|
43a751ee0b | ||
|
|
207212557e | ||
|
|
fd1aeeac92 | ||
|
|
50ba52756f | ||
|
|
e630b8f8a8 | ||
|
|
cf5081d300 | ||
|
|
8864cbf80a | ||
|
|
81d7a832c0 | ||
|
|
d41fabbc9f | ||
|
|
46f62e1af9 | ||
|
|
b91efaa973 | ||
|
|
e3238ff75c | ||
|
|
9cc4e8d03a | ||
|
|
68b1afa2df | ||
|
|
34c98e03c1 | ||
|
|
41e40bbc0d | ||
|
|
80149342f2 | ||
|
|
1967dee50c | ||
|
|
ab80def94b | ||
|
|
254bf313a2 | ||
|
|
938d5d901a | ||
|
|
7b00d828b2 | ||
|
|
ca49944c85 | ||
|
|
d5e9fc7677 | ||
|
|
6db7c5733d | ||
|
|
418f86ecbd | ||
|
|
c68c5af856 | ||
|
|
950dffbe06 | ||
|
|
5d557003b6 | ||
|
|
3b8c3c1346 | ||
|
|
1853263f6c | ||
|
|
b0f6d81f57 | ||
|
|
9ba0329432 | ||
|
|
614101c4b8 | ||
|
|
50e4fb138a | ||
|
|
6dba0c6e0e | ||
|
|
0f2d2156e6 | ||
|
|
13b17c5a93 | ||
|
|
511499d950 | ||
|
|
6632b71273 | ||
|
|
60ef70cee4 | ||
|
|
b3ba0a7241 | ||
|
|
fc73dabc0b | ||
|
|
1121d45eb6 | ||
|
|
18b6353803 | ||
|
|
c0c0642bd1 | ||
|
|
3cf26a84dc | ||
|
|
44d6d4405e | ||
|
|
cafa027f0b | ||
|
|
1c970b0714 | ||
|
|
6636e432d7 | ||
|
|
158889b85c | ||
|
|
92bebb7ecc | ||
|
|
fff34e77f5 | ||
|
|
df18692af9 | ||
|
|
276a78cb2e | ||
|
|
a1e820182c | ||
|
|
272090fc8f | ||
|
|
ab6bc52a0f | ||
|
|
c69c369502 | ||
|
|
a5b1b24fee | ||
|
|
40cfbc5d61 | ||
|
|
ffad1ecd6d | ||
|
|
e1b5803902 | ||
|
|
492d71a924 | ||
|
|
6d01a3a7d1 | ||
|
|
b71e20dfa3 | ||
|
|
474158dd18 | ||
|
|
914db816c2 | ||
|
|
4485d6fdf4 | ||
|
|
2c394661a6 | ||
|
|
611c1a7502 | ||
|
|
4e8858a764 | ||
|
|
fb46de5ca6 | ||
|
|
65db96e663 | ||
|
|
5109d40d8e | ||
|
|
1ba1fa37f9 | ||
|
|
9c97ee6407 | ||
|
|
7477d2c219 | ||
|
|
a6fb3b602e | ||
|
|
d9b9457b56 | ||
|
|
cfb6ddbfc6 | ||
|
|
7de21c1f93 | ||
|
|
100f3380c4 | ||
|
|
20e484bb8b | ||
|
|
94fc1a1cee | ||
|
|
ae28df5276 | ||
|
|
3a4f1382f3 | ||
|
|
01a7e08585 | ||
|
|
847fd15af2 | ||
|
|
a21fb17d73 | ||
|
|
0c34bd440b | ||
|
|
1008510750 | ||
|
|
34d6eb52d0 | ||
|
|
5820425b6c | ||
|
|
a4b39a3648 | ||
|
|
3dc5542a28 | ||
|
|
dde4643e77 | ||
|
|
7a857e08c1 | ||
|
|
80a3bd6a3b | ||
|
|
7fb8ee60b4 | ||
|
|
dca4cf2edb | ||
|
|
2bc33f22df | ||
|
|
d14c6e2829 | ||
|
|
a4ce224cd1 | ||
|
|
ab1cd3f5cf | ||
|
|
1e75de9bb8 | ||
|
|
19a03c42a5 | ||
|
|
9e5d1bf0fc | ||
|
|
c5f784719d | ||
|
|
60aa459dfc | ||
|
|
53d71d29ff | ||
|
|
81658d2ff9 | ||
|
|
9fa67b0e0a | ||
|
|
88ba494701 | ||
|
|
efacfced45 | ||
|
|
b3c836f298 | ||
|
|
3330bf4f2f | ||
|
|
e634c89995 | ||
|
|
5aa53eee43 | ||
|
|
42483b6f32 | ||
|
|
00bbb81375 | ||
|
|
5271cdacf2 | ||
|
|
6d01726961 | ||
|
|
12feac1f50 | ||
|
|
39c1c3567b | ||
|
|
63ae6850d3 | ||
|
|
bec24e052c | ||
|
|
91eb2b2c4a | ||
|
|
0bae2a3397 | ||
|
|
42ec6db746 | ||
|
|
7a9dc0eec0 | ||
|
|
6441c9d5d8 | ||
|
|
2930d39ce7 | ||
|
|
1500e805dd | ||
|
|
b14d1801f0 | ||
|
|
bc11181d5e | ||
|
|
9739e677aa | ||
|
|
056f076ae8 | ||
|
|
7dfb6f4a13 | ||
|
|
b347b719f3 | ||
|
|
7b537a4e94 | ||
|
|
291f28fcce | ||
|
|
fa9c39732d | ||
|
|
bfdf006bd2 | ||
|
|
057d6ca05b | ||
|
|
7d7f5ff4e2 | ||
|
|
6e32f4bc85 | ||
|
|
8460a8f4ef | ||
|
|
8c09a7429c | ||
|
|
346bf14b7b | ||
|
|
8e3c9410dc | ||
|
|
cb0552e20d | ||
|
|
8c8127dda6 | ||
|
|
1d8a481d59 | ||
|
|
dd4f066e95 | ||
|
|
5e0d4163a2 | ||
|
|
7fb2d13a8b | ||
|
|
acde10b46e | ||
|
|
c0bcab8bc5 | ||
|
|
fd6d0922ab | ||
|
|
8179e7dbf8 | ||
|
|
eabeeaccfe | ||
|
|
94bba69dee | ||
|
|
4d23de96d5 | ||
|
|
681810ea38 | ||
|
|
d500fe66fd | ||
|
|
05c2adeefd | ||
|
|
d46e0fb474 | ||
|
|
330fab2efa | ||
|
|
d59d36f93c | ||
|
|
fd6827fdca | ||
|
|
dca94f17d7 | ||
|
|
d4e16881ff | ||
|
|
cd3f274763 | ||
|
|
1947be4957 | ||
|
|
21de4709ea | ||
|
|
ec76381a0b | ||
|
|
66661417d7 | ||
|
|
81b79e6e53 | ||
|
|
5ae93d852e | ||
|
|
96cb663fa8 | ||
|
|
1efc2a9b5d | ||
|
|
9441c1cffe | ||
|
|
ef30d2d3b6 | ||
|
|
1673966e36 | ||
|
|
1d8f913364 | ||
|
|
a549ebc25f | ||
|
|
ce853786b5 | ||
|
|
7e0ab6d0b1 | ||
|
|
d6f907a05b | ||
|
|
b2d1962b81 | ||
|
|
b0a6c9fa53 | ||
|
|
7a0337f3db | ||
|
|
c1dbd3ffd0 | ||
|
|
1ea6d2016d | ||
|
|
416589cc93 | ||
|
|
41ce9d47e5 | ||
|
|
d7e4deab4e | ||
|
|
27782ceddd | ||
|
|
a6f62a99b9 | ||
|
|
4e4def4fb9 | ||
|
|
7af3b751d4 | ||
|
|
897cfad399 | ||
|
|
f87a51034e | ||
|
|
3f409d0e28 | ||
|
|
543566840c | ||
|
|
1c3174a277 | ||
|
|
fc2ae6f887 | ||
|
|
63e175d389 | ||
|
|
9bfbba6fea | ||
|
|
69d245c4bd | ||
|
|
7738eae4b0 | ||
|
|
3d5fb07ca8 | ||
|
|
0f0fb266c7 | ||
|
|
5c3d6298b0 | ||
|
|
028f0bdb8d | ||
|
|
44bcdc6866 | ||
|
|
b9f6f92bad | ||
|
|
1607535416 | ||
|
|
f6ced9279b | ||
|
|
95af716a96 | ||
|
|
07fe51fa25 | ||
|
|
822995cbaf | ||
|
|
db0e02c05d | ||
|
|
856dda68db | ||
|
|
163cbcb89d | ||
|
|
a79f614e12 | ||
|
|
7d3a818565 | ||
|
|
978bb47b92 | ||
|
|
b791a6a348 | ||
|
|
c21c1f5225 | ||
|
|
d6253b1dee | ||
|
|
390bb07cca | ||
|
|
309822d933 | ||
|
|
8cb612c10c | ||
|
|
7e244455c4 | ||
|
|
907fe3d8d9 | ||
|
|
6420e33fb8 | ||
|
|
0b560fdd27 | ||
|
|
04297eda80 | ||
|
|
c13fd2261e | ||
|
|
034bff5b2f | ||
|
|
987ad214ff | ||
|
|
d11ac64b95 | ||
|
|
4c8c3b6947 | ||
|
|
d47d8d22a3 | ||
|
|
0ec9defc6e | ||
|
|
4776f11b6a | ||
|
|
3118d7bede | ||
|
|
a83be187f3 | ||
|
|
deb5e435e5 | ||
|
|
9f824f3aa9 | ||
|
|
032e68da05 | ||
|
|
486661d6c6 | ||
|
|
aa86ab97f0 | ||
|
|
a17f07495a | ||
|
|
16d3440a4c | ||
|
|
d7b412c1eb | ||
|
|
e5751334d6 | ||
|
|
1fb1d7e4e9 | ||
|
|
adc20e78da | ||
|
|
d499e250e0 | ||
|
|
68166c22b3 | ||
|
|
06b2b26e39 | ||
|
|
641ca3d49d | ||
|
|
6d259e00a3 | ||
|
|
2bc5b97662 | ||
|
|
676c61aa99 | ||
|
|
eade8003ef | ||
|
|
817bbefac6 | ||
|
|
cded6206dc | ||
|
|
c287fb58bd | ||
|
|
1b97f9b6c9 | ||
|
|
14ca3fc2f3 | ||
|
|
4bc1143418 | ||
|
|
4267063dba | ||
|
|
8f8b4536b6 | ||
|
|
8121ab5163 | ||
|
|
76fab1fea8 | ||
|
|
143b235a22 | ||
|
|
3a89f2c32f | ||
|
|
7bab92042a | ||
|
|
7379b4ddd2 | ||
|
|
298181999d | ||
|
|
61e1e7fe8f | ||
|
|
b3050af1a7 | ||
|
|
275da075e0 | ||
|
|
9925e2732a | ||
|
|
59b3daabc5 | ||
|
|
f2b0f64138 | ||
|
|
5df77eb474 | ||
|
|
f202fb9af6 | ||
|
|
5b769869d0 | ||
|
|
8e266058ae | ||
|
|
7c21712e80 | ||
|
|
193fc343fe | ||
|
|
a1e9c3d270 | ||
|
|
629261c4be | ||
|
|
f6d3a6239c | ||
|
|
83c5131b67 | ||
|
|
36afef3498 | ||
|
|
52c0485b0c | ||
|
|
8c23a091da | ||
|
|
ca121f80ee | ||
|
|
b48846506f | ||
|
|
a1c72be2a9 | ||
|
|
2098368417 | ||
|
|
4014d86a57 | ||
|
|
e84e8748bd | ||
|
|
bd8166e630 | ||
|
|
03d1519b39 | ||
|
|
36c4719570 | ||
|
|
7c970771c5 | ||
|
|
3f64c042bd | ||
|
|
e336cbfb2d | ||
|
|
24eec76428 | ||
|
|
71c9b15ff1 | ||
|
|
2940f0d67c | ||
|
|
cbb1d2d3b5 | ||
|
|
36dd11a899 | ||
|
|
be88969b79 | ||
|
|
d91ad54ed9 | ||
|
|
1330228080 | ||
|
|
3ea1eca350 | ||
|
|
a4e6d8120b | ||
|
|
3219de235c | ||
|
|
4e5c2ff620 | ||
|
|
63e25f0ff9 | ||
|
|
840225b580 | ||
|
|
bd221d60d6 | ||
|
|
8a3bb50143 | ||
|
|
e4cd1a465c | ||
|
|
2173a9f246 | ||
|
|
973a838e2a | ||
|
|
d95ee55497 | ||
|
|
124e2e759c | ||
|
|
ac918e3618 | ||
|
|
009a720c32 | ||
|
|
0dbfa43dad | ||
|
|
e0b4d36a74 | ||
|
|
a441474d75 | ||
|
|
cfd3c3628e | ||
|
|
474d52f805 | ||
|
|
7ee8bdf2f3 | ||
|
|
8a9757111f | ||
|
|
65dda4a70b | ||
|
|
1ed39dbbed | ||
|
|
8162c2e4e4 | ||
|
|
a7d74f3f98 | ||
|
|
ad83ae1e7a | ||
|
|
066374906e | ||
|
|
ec79a4a6f6 | ||
|
|
9fae215db4 | ||
|
|
92b40c9485 | ||
|
|
19fc59739f | ||
|
|
7e0ae4c601 | ||
|
|
81c2f4b30b | ||
|
|
e238f7ed37 | ||
|
|
2756f3332c | ||
|
|
14b3eefbaf | ||
|
|
dc946582a4 | ||
|
|
dfa14a73a8 | ||
|
|
112aa845f4 | ||
|
|
150a309175 | ||
|
|
55c14819a3 | ||
|
|
598897caa6 | ||
|
|
cf3f8a796a | ||
|
|
bffc294b13 | ||
|
|
4cc3b7f9fb | ||
|
|
b3161dde93 | ||
|
|
5550eabac1 | ||
|
|
b2b320174b | ||
|
|
dd79348b35 | ||
|
|
bd6ce7d4da | ||
|
|
7a67670e1a | ||
|
|
539bf482b9 | ||
|
|
ed67ce7f33 | ||
|
|
d91c7e5e79 | ||
|
|
4f1dfe2ef7 | ||
|
|
36ea6c13df | ||
|
|
3acb0aac98 | ||
|
|
fdf4b3878f | ||
|
|
2fe71782a7 | ||
|
|
89dfe2b763 | ||
|
|
9b62f238ed | ||
|
|
987688f196 | ||
|
|
46cb95f16c | ||
|
|
4e1fcbb706 | ||
|
|
e4c038762b | ||
|
|
86dfa200a6 | ||
|
|
165cf980d2 | ||
|
|
13ccb16a4a | ||
|
|
f4b5426865 | ||
|
|
c2f62ba52a | ||
|
|
b2d2c56a09 | ||
|
|
abf0f5ac87 | ||
|
|
fa1965deb4 | ||
|
|
1f76dc78d8 | ||
|
|
4448884a3e | ||
|
|
e3fc23bae8 | ||
|
|
29ceed74a2 | ||
|
|
382308c3fd | ||
|
|
3d1b6e29c6 | ||
|
|
3a9a5ec669 | ||
|
|
8c37c491a9 | ||
|
|
fdf11e6038 | ||
|
|
8e558f0826 | ||
|
|
69804c23f1 | ||
|
|
9aa9a62ed4 | ||
|
|
d9b79f47c8 | ||
|
|
249bc42667 | ||
|
|
644c184f7c | ||
|
|
66cfae7b3b | ||
|
|
bd2c2acd5f | ||
|
|
13aab750dd | ||
|
|
7a51abc2f9 | ||
|
|
44a3e08095 | ||
|
|
2aa8cf7104 | ||
|
|
1b1cfe1b92 | ||
|
|
199c2cdb66 | ||
|
|
726828a487 | ||
|
|
fcbf81a3d4 | ||
|
|
7637b51ba5 | ||
|
|
3afed3b316 | ||
|
|
3d6e334007 | ||
|
|
6c848a57b6 | ||
|
|
eb12d43800 | ||
|
|
465366e644 | ||
|
|
289e9c809f | ||
|
|
8b40354786 | ||
|
|
8de8de1b1e | ||
|
|
4b76c76712 | ||
|
|
6b9a270506 | ||
|
|
da2c49ab66 | ||
|
|
af2a3f3a65 | ||
|
|
6369a900da | ||
|
|
e877247032 | ||
|
|
5bcc5ff873 | ||
|
|
a52064463e | ||
|
|
6ed7f19673 | ||
|
|
9aba0ba5a8 | ||
|
|
5803a84bd7 | ||
|
|
ce0bf0f4b4 | ||
|
|
65ed57aff4 | ||
|
|
1317b80fca | ||
|
|
f0d6145fa6 | ||
|
|
c0c157ecef | ||
|
|
4bb607f180 | ||
|
|
2eec205e31 | ||
|
|
bd8cdd345a | ||
|
|
7caf3ea7d0 | ||
|
|
ba89c60b6d | ||
|
|
084e48d6dd | ||
|
|
1bed3f3936 | ||
|
|
cd860bfbf8 | ||
|
|
439c2d445c | ||
|
|
7f71d5dbd8 | ||
|
|
831c835106 | ||
|
|
5dfb7cb938 | ||
|
|
044d6a2207 | ||
|
|
955b46534d | ||
|
|
0e8d80e055 | ||
|
|
92fc736cfa | ||
|
|
60ed43c11b | ||
|
|
319f72ae2a | ||
|
|
04dc34260f | ||
|
|
a8196d1f33 | ||
|
|
1ce6ad5ccc | ||
|
|
145e36925f | ||
|
|
c07928144c | ||
|
|
d8c30f6cbb | ||
|
|
e968c6a2a4 | ||
|
|
ffc3a31d09 | ||
|
|
d6e037dd28 | ||
|
|
83b9b3bf4a | ||
|
|
1cb89ce20d | ||
|
|
d75b916153 | ||
|
|
192b484a8c | ||
|
|
85e2137d0e | ||
|
|
c1042c8f20 | ||
|
|
c91b05bd4b | ||
|
|
f8a09df5c0 | ||
|
|
9363db816c | ||
|
|
22af4da4d4 | ||
|
|
16fa10b056 | ||
|
|
f044851abb | ||
|
|
217e99a0e2 | ||
|
|
1bc4aea217 | ||
|
|
4997934bfe | ||
|
|
4905dded87 | ||
|
|
ff6447ae2b | ||
|
|
7f51857fa5 | ||
|
|
78c3babc37 | ||
|
|
83300044dd | ||
|
|
55f891e2aa | ||
|
|
7ae40d89c1 | ||
|
|
29cc1cf390 | ||
|
|
960d9a8534 | ||
|
|
bcc8529bfc | ||
|
|
d773647a20 | ||
|
|
3a5a0837c7 | ||
|
|
44cfe6af1c | ||
|
|
cf6d445080 | ||
|
|
422f8b3660 | ||
|
|
b097938f47 | ||
|
|
c231eff4b1 | ||
|
|
1ddc96f965 | ||
|
|
13111c4b42 | ||
|
|
7c70dbce65 | ||
|
|
25559f1772 | ||
|
|
c010c83654 | ||
|
|
2057531e8c | ||
|
|
277d4d9333 | ||
|
|
051e642c0c | ||
|
|
a8778e358d | ||
|
|
d2edbfd6fa | ||
|
|
d96dbe9365 | ||
|
|
35b5dcdb22 | ||
|
|
66f3bd186f | ||
|
|
7ae38a71cc | ||
|
|
2ed356be65 | ||
|
|
99436c1334 | ||
|
|
9e57a4ea28 | ||
|
|
19e5b8cc50 | ||
|
|
33310732a6 | ||
|
|
a03bf89190 | ||
|
|
1b089ca5e6 | ||
|
|
21e23d5511 | ||
|
|
8a2c4ab3de | ||
|
|
040585bf3d | ||
|
|
9030b3e04c | ||
|
|
0b46495afd | ||
|
|
ace16d473f | ||
|
|
925c51420d | ||
|
|
764b8ab7a5 | ||
|
|
cb6a1bfb1d | ||
|
|
775b9f30f0 | ||
|
|
76fd1c5c58 | ||
|
|
3e2605490f | ||
|
|
7094588c53 | ||
|
|
3523047243 | ||
|
|
bdcbaa031d | ||
|
|
f722b3e9cb | ||
|
|
2d46cb072e | ||
|
|
28cf450bfa | ||
|
|
4aa48fb4b6 | ||
|
|
aa86593702 | ||
|
|
faa368cc07 | ||
|
|
a840ed06b7 | ||
|
|
7196bfd157 | ||
|
|
a6785e9143 | ||
|
|
4d2f26b1cd | ||
|
|
188987a8ff | ||
|
|
14d74d3230 | ||
|
|
bcd6bd6b04 | ||
|
|
8e4bd7fe4a | ||
|
|
8ab552793a | ||
|
|
29944f6bf2 | ||
|
|
162b60a05b | ||
|
|
da50d92d1e | ||
|
|
a746f5657f | ||
|
|
65ccc5bfce | ||
|
|
34939f9381 | ||
|
|
26e7821aaa | ||
|
|
298c5f0de2 | ||
|
|
a6c2b25f6f | ||
|
|
3a8c90c0d4 | ||
|
|
a25ce2296a | ||
|
|
280407a553 | ||
|
|
32c98e2161 | ||
|
|
2cbdb0bc17 | ||
|
|
4317694c64 | ||
|
|
e0879fbccb | ||
|
|
9cb8e194b0 | ||
|
|
dc914b1806 | ||
|
|
c70817b21a | ||
|
|
77918fd412 | ||
|
|
90d02234c7 | ||
|
|
b0b1c5af71 | ||
|
|
a8bd87938d | ||
|
|
10d2f0a565 | ||
|
|
c68aca4ada | ||
|
|
f46d96c4c6 | ||
|
|
e7b1ded486 | ||
|
|
719de94821 | ||
|
|
7ea0249e6e | ||
|
|
feab95ce4b | ||
|
|
ca6f755634 | ||
|
|
70b30f7849 | ||
|
|
01ab027615 | ||
|
|
11f5db871f | ||
|
|
d83fc3181b | ||
|
|
b4657a0d05 | ||
|
|
a5d6820453 | ||
|
|
7b16aa6050 | ||
|
|
c5d3c0c6f8 | ||
|
|
43c1a87c48 | ||
|
|
3755002381 | ||
|
|
dba38408c9 | ||
|
|
5b2bc23d03 | ||
|
|
a4cfdcb5c4 | ||
|
|
b6097160f1 | ||
|
|
fde1c08945 | ||
|
|
417eb56a9b | ||
|
|
0b28812f7e | ||
|
|
5ad25376bb | ||
|
|
b3ab85f3b5 | ||
|
|
11231abe8a | ||
|
|
c577706415 | ||
|
|
f1eea6a0bf | ||
|
|
8ce55f90d3 | ||
|
|
723f35ec5a | ||
|
|
025d9d3276 | ||
|
|
4f0c1d11eb | ||
|
|
1aae921ce7 | ||
|
|
2e1c508bc4 | ||
|
|
cea6ea4344 | ||
|
|
57310fdbd6 | ||
|
|
62ca6212ce | ||
|
|
d4f5871e74 | ||
|
|
a739580d3f | ||
|
|
5203565175 | ||
|
|
c91f6db68a | ||
|
|
b776b85fc3 | ||
|
|
b35e5f1582 | ||
|
|
7d5a929b5e | ||
|
|
c2e7bc13a6 | ||
|
|
97818c6f32 | ||
|
|
a8973f5463 | ||
|
|
75d790137d | ||
|
|
7ef6c72fc0 | ||
|
|
c5f8e2249e | ||
|
|
585a6c29d4 | ||
|
|
6b6df15dd9 | ||
|
|
f4de68cb22 | ||
|
|
86d5cbc355 | ||
|
|
88f9b69e2a | ||
|
|
d77c782f69 | ||
|
|
c115131ed2 | ||
|
|
178dedf78c | ||
|
|
b0c64afc6e | ||
|
|
be0c1c0912 | ||
|
|
2e8fa88fcb | ||
|
|
b1b5904852 | ||
|
|
08f029850f | ||
|
|
f3d4077142 | ||
|
|
59dd479a6d | ||
|
|
76d9f1ea37 | ||
|
|
858b497199 | ||
|
|
cee9f1df95 | ||
|
|
5bc2001ce3 | ||
|
|
652226dbf0 | ||
|
|
4688e6d534 | ||
|
|
1b0fc180c4 | ||
|
|
2524972807 | ||
|
|
8f51dc2c22 | ||
|
|
b363b50320 | ||
|
|
88a48a5c79 | ||
|
|
7be951b962 | ||
|
|
3dcc4e6bc1 | ||
|
|
573ee0b584 | ||
|
|
213629ef52 | ||
|
|
27e1579e4c | ||
|
|
f2c401b6c0 | ||
|
|
442c63d7a4 | ||
|
|
5babfb0f1e | ||
|
|
0ad3078524 | ||
|
|
f765c25020 | ||
|
|
4145251afd | ||
|
|
88c3532162 | ||
|
|
84b3ad3221 | ||
|
|
e699d3d02d | ||
|
|
9da984b866 | ||
|
|
fc08d15a79 | ||
|
|
ffaabe8674 | ||
|
|
0233ab4deb | ||
|
|
c9dc010c0b | ||
|
|
557696b1d8 | ||
|
|
9fefbb0c4a | ||
|
|
eb9ea97e21 | ||
|
|
673b7a95b7 | ||
|
|
d5f27ecb0e | ||
|
|
8f8b928cc4 | ||
|
|
965896b932 | ||
|
|
042adb5e34 | ||
|
|
67927bd8f4 | ||
|
|
259a63e612 | ||
|
|
adcf2158bf | ||
|
|
05c914156a | ||
|
|
f69884d573 | ||
|
|
d097554f7d | ||
|
|
1e2fd57c4c | ||
|
|
8b8007695c | ||
|
|
68f3c877ee | ||
|
|
ae442ee015 | ||
|
|
99b5f1b7b8 | ||
|
|
8071df0e68 | ||
|
|
88d1aab7a3 | ||
|
|
08001ba373 | ||
|
|
ebc24cee55 | ||
|
|
ae3bb30d8a | ||
|
|
63d6b23344 | ||
|
|
c009e6bd04 | ||
|
|
38d85a49e7 | ||
|
|
0edc149ecc | ||
|
|
10d6cd9896 | ||
|
|
6913da7efa | ||
|
|
34df1b1646 | ||
|
|
992603496e | ||
|
|
b9552c42f1 | ||
|
|
37e4dfc5d5 | ||
|
|
15b7284a8f | ||
|
|
b57a62fece | ||
|
|
9c7de5ad03 | ||
|
|
c065fae422 | ||
|
|
cfde1f8c27 | ||
|
|
c45f72a63e | ||
|
|
e1d9eca7bd | ||
|
|
573e5eb5bd | ||
|
|
d9090486e3 | ||
|
|
b4e7a91645 | ||
|
|
92dd68fca1 | ||
|
|
82e955ec02 | ||
|
|
2e66c4c9f5 | ||
|
|
0c6ee5e139 | ||
|
|
9a19b5994b | ||
|
|
920586f56c | ||
|
|
919aa2895a | ||
|
|
75690598e3 | ||
|
|
ac2caf2787 | ||
|
|
5640c96fd5 | ||
|
|
0396c4a4de | ||
|
|
f061fe581a | ||
|
|
5405876d84 | ||
|
|
4b9de0777b | ||
|
|
a59e073536 | ||
|
|
67492bf024 | ||
|
|
77c83c4f42 | ||
|
|
259baa0e84 | ||
|
|
dca48c7eec | ||
|
|
0d83a34cfd | ||
|
|
7386b0a523 | ||
|
|
eda13f9023 | ||
|
|
d0e9fe1e3e | ||
|
|
2b7bab04dd | ||
|
|
ad5f890a1e | ||
|
|
fa191e2928 | ||
|
|
6d8a23ec16 | ||
|
|
12371650f9 | ||
|
|
79e1d54e4c | ||
|
|
447f5f69c9 | ||
|
|
e08a26d015 | ||
|
|
975265b0af | ||
|
|
4d5e9c52b2 | ||
|
|
d1b154c285 | ||
|
|
381f6b184e | ||
|
|
59681398cb | ||
|
|
adf887a06b | ||
|
|
42f70cd55d | ||
|
|
3704a4ff47 | ||
|
|
5b8d637f6a | ||
|
|
0ea5fbfe0a | ||
|
|
f1acd122bc | ||
|
|
8704234669 | ||
|
|
2756cb8b8f | ||
|
|
bc0aed186e | ||
|
|
9ba961fa72 | ||
|
|
c166bc9b18 | ||
|
|
f36a9c4409 | ||
|
|
01da9e3ca2 | ||
|
|
f168e4586c | ||
|
|
03ff390685 | ||
|
|
2a77486567 | ||
|
|
32a5950aad | ||
|
|
f1370189b6 | ||
|
|
3125e05b49 | ||
|
|
59f292333f | ||
|
|
b7a2c11e81 | ||
|
|
3d07ddfba5 | ||
|
|
9286e4794b | ||
|
|
81276cb7f5 | ||
|
|
e270f90f8d | ||
|
|
b1fdfec18c | ||
|
|
fb59d80897 | ||
|
|
e384ec32b8 | ||
|
|
d93361939c | ||
|
|
644c0e3d33 | ||
|
|
7419f992e7 | ||
|
|
4a4292a0dc | ||
|
|
bc92586323 | ||
|
|
40456ebaae | ||
|
|
7c34c45983 | ||
|
|
31f6d13cd8 | ||
|
|
502e6b0ce5 | ||
|
|
516380f979 | ||
|
|
63edc60753 |
32
.editorconfig
Normal file
32
.editorconfig
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# editorconfig.org
|
||||||
|
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
# Unix style files
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[Makefile,Makefile.*]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.cmd]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = crlf
|
||||||
|
|
||||||
|
[*.{h,cpp}]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.rc]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.{md,markdown}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = false
|
||||||
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/build/build_mingw.cmd eol=crlf
|
||||||
32
.github/workflows/build-deb.yml
vendored
Normal file
32
.github/workflows/build-deb.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
name: Build Debian packages
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: ${{ matrix.dist }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
dist: ['buster', 'bullseye', 'bookworm']
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: change debian changelog
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install devscripts
|
||||||
|
debchange -v "`git describe --tags`-${{ matrix.dist }}" -b -M --distribution ${{ matrix.dist }} "trunk build"
|
||||||
|
- uses: jtdor/build-deb-action@v1
|
||||||
|
with:
|
||||||
|
docker-image: debian:${{ matrix.dist }}-slim
|
||||||
|
buildpackage-opts: --build=binary --no-sign
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: i2pd_${{ matrix.dist }}
|
||||||
|
path: debian/artifacts/i2pd_*.deb
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: i2pd-dbgsym_${{ matrix.dist }}
|
||||||
|
path: debian/artifacts/i2pd-dbgsym_*.deb
|
||||||
28
.github/workflows/build-freebsd.yml
vendored
Normal file
28
.github/workflows/build-freebsd.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
name: Build on FreeBSD
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: macos-12
|
||||||
|
name: with UPnP
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Test in FreeBSD
|
||||||
|
id: test
|
||||||
|
uses: vmactions/freebsd-vm@v0.3.0
|
||||||
|
with:
|
||||||
|
usesh: true
|
||||||
|
mem: 2048
|
||||||
|
sync: rsync
|
||||||
|
copyback: true
|
||||||
|
prepare: pkg install -y devel/cmake devel/gmake devel/boost-libs security/openssl net/miniupnpc
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
cmake -DWITH_UPNP=ON -DCMAKE_BUILD_TYPE=Release .
|
||||||
|
gmake -j2
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: i2pd-freebsd
|
||||||
|
path: build/i2pd
|
||||||
21
.github/workflows/build-osx.yml
vendored
Normal file
21
.github/workflows/build-osx.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
name: Build on OSX
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: With USE_UPNP=${{ matrix.with_upnp }}
|
||||||
|
runs-on: macOS-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: true
|
||||||
|
matrix:
|
||||||
|
with_upnp: ['yes', 'no']
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: install packages
|
||||||
|
run: |
|
||||||
|
find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete
|
||||||
|
brew update
|
||||||
|
brew install boost miniupnpc openssl@1.1
|
||||||
|
- name: build application
|
||||||
|
run: make HOMEBREW=1 USE_UPNP=${{ matrix.with_upnp }} PREFIX=$GITHUB_WORKSPACE/output -j3
|
||||||
75
.github/workflows/build-windows.yml
vendored
Normal file
75
.github/workflows/build-windows.yml
vendored
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
name: Build on Windows
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: msys2 {0}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Building using ${{ matrix.arch }} toolchain
|
||||||
|
runs-on: windows-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: true
|
||||||
|
matrix:
|
||||||
|
include: [
|
||||||
|
{ msystem: UCRT64, arch: ucrt-x86_64, arch_short: x64-ucrt },
|
||||||
|
{ msystem: MINGW64, arch: x86_64, arch_short: x64 },
|
||||||
|
{ msystem: MINGW32, arch: i686, arch_short: x86 }
|
||||||
|
]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Setup MSYS2
|
||||||
|
uses: msys2/setup-msys2@v2
|
||||||
|
with:
|
||||||
|
msystem: ${{ matrix.msystem }}
|
||||||
|
install: base-devel mingw-w64-${{ matrix.arch }}-gcc mingw-w64-${{ matrix.arch }}-boost mingw-w64-${{ matrix.arch }}-openssl mingw-w64-${{ matrix.arch }}-miniupnpc
|
||||||
|
update: true
|
||||||
|
- name: Build application
|
||||||
|
run: |
|
||||||
|
mkdir -p obj/Win32 obj/libi2pd obj/libi2pd_client obj/daemon
|
||||||
|
make USE_UPNP=yes DEBUG=no USE_GIT_VERSION=yes -j3
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: i2pd-${{ matrix.arch_short }}.exe
|
||||||
|
path: i2pd.exe
|
||||||
|
build-xp:
|
||||||
|
name: Building for Windows XP
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Setup MSYS2
|
||||||
|
uses: msys2/setup-msys2@v2
|
||||||
|
with:
|
||||||
|
msystem: MINGW32
|
||||||
|
install: base-devel git mingw-w64-i686-gcc mingw-w64-i686-boost mingw-w64-i686-openssl mingw-w64-i686-miniupnpc
|
||||||
|
update: true
|
||||||
|
- name: Build WinXP-capable CRT packages
|
||||||
|
run: |
|
||||||
|
git clone https://github.com/msys2/MINGW-packages
|
||||||
|
pushd MINGW-packages
|
||||||
|
pushd mingw-w64-headers-git
|
||||||
|
sed -i 's/0x601/0x501/' PKGBUILD
|
||||||
|
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm
|
||||||
|
pacman --noconfirm -U mingw-w64-i686-headers-git-*-any.pkg.tar.zst
|
||||||
|
popd
|
||||||
|
pushd mingw-w64-crt-git
|
||||||
|
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm
|
||||||
|
pacman --noconfirm -U mingw-w64-i686-crt-git-*-any.pkg.tar.zst
|
||||||
|
popd
|
||||||
|
pushd mingw-w64-winpthreads-git
|
||||||
|
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm
|
||||||
|
pacman --noconfirm -U mingw-w64-i686-libwinpthread-git-*-any.pkg.tar.zst mingw-w64-i686-winpthreads-git-*-any.pkg.tar.zst
|
||||||
|
popd
|
||||||
|
popd
|
||||||
|
- name: Build application
|
||||||
|
run: |
|
||||||
|
mkdir -p obj/Win32 obj/libi2pd obj/libi2pd_client obj/daemon
|
||||||
|
make USE_UPNP=yes DEBUG=no USE_GIT_VERSION=yes USE_WINXP_FLAGS=yes -j3
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: i2pd-xp.exe
|
||||||
|
path: i2pd.exe
|
||||||
54
.github/workflows/build.yml
vendored
Normal file
54
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
name: Build on Ubuntu
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-make:
|
||||||
|
name: Make with USE_UPNP=${{ matrix.with_upnp }}
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
strategy:
|
||||||
|
fail-fast: true
|
||||||
|
matrix:
|
||||||
|
with_upnp: ['yes', 'no']
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: install packages
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install build-essential libboost-all-dev libminiupnpc-dev libssl-dev zlib1g-dev
|
||||||
|
- name: build application
|
||||||
|
run: make USE_UPNP=${{ matrix.with_upnp }} -j3
|
||||||
|
build-cmake:
|
||||||
|
name: CMake with -DWITH_UPNP=${{ matrix.with_upnp }}
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
strategy:
|
||||||
|
fail-fast: true
|
||||||
|
matrix:
|
||||||
|
with_upnp: ['ON', 'OFF']
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: install packages
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install build-essential cmake libboost-all-dev libminiupnpc-dev libssl-dev zlib1g-dev
|
||||||
|
- name: build application
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
cmake -DWITH_UPNP=${{ matrix.with_upnp }} .
|
||||||
|
make -j3
|
||||||
|
build-static:
|
||||||
|
name: Static build with UPnP
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: install packages
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install build-essential libboost-all-dev libminiupnpc-dev libssl-dev zlib1g-dev
|
||||||
|
- name: build application
|
||||||
|
run: make DEBUG=no USE_STATIC=yes USE_UPNP=yes -j`nproc`
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: i2pd-amd64-static
|
||||||
|
path: i2pd
|
||||||
140
.github/workflows/docker.yml
vendored
Normal file
140
.github/workflows/docker.yml
vendored
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
name: Build containers
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- openssl
|
||||||
|
- docker
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
packages: write
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include: [
|
||||||
|
{ platform: 'linux/amd64', archname: 'amd64' },
|
||||||
|
{ platform: 'linux/386', archname: 'i386' },
|
||||||
|
{ platform: 'linux/arm64', archname: 'arm64' },
|
||||||
|
{ platform: 'linux/arm/v7', archname: 'armv7' },
|
||||||
|
]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to GitHub Container registry
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build container for ${{ matrix.archname }}
|
||||||
|
uses: docker/build-push-action@v3
|
||||||
|
with:
|
||||||
|
context: ./contrib/docker
|
||||||
|
file: ./contrib/docker/Dockerfile
|
||||||
|
platforms: ${{ matrix.platform }}
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
purplei2p/i2pd:latest-${{ matrix.archname }}
|
||||||
|
ghcr.io/purplei2p/i2pd:latest-${{ matrix.archname }}
|
||||||
|
|
||||||
|
push:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
packages: write
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
needs: build
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to GitHub Container registry
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Create and push latest manifest image to Docker Hub
|
||||||
|
uses: Noelware/docker-manifest-action@master
|
||||||
|
with:
|
||||||
|
base-image: purplei2p/i2pd:latest
|
||||||
|
extra-images: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7
|
||||||
|
push: true
|
||||||
|
|
||||||
|
- name: Create and push latest manifest image to GHCR
|
||||||
|
uses: Noelware/docker-manifest-action@master
|
||||||
|
with:
|
||||||
|
base-image: ghcr.io/purplei2p/i2pd:latest
|
||||||
|
extra-images: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7
|
||||||
|
push: true
|
||||||
|
|
||||||
|
- name: Store release version to env
|
||||||
|
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||||
|
run: echo "RELEASE_VERSION=${GITHUB_REF:10}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Create and push release manifest image to Docker Hub
|
||||||
|
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||||
|
uses: Noelware/docker-manifest-action@master
|
||||||
|
with:
|
||||||
|
base-image: purplei2p/i2pd:latest-release
|
||||||
|
extra-images: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7
|
||||||
|
push: true
|
||||||
|
|
||||||
|
- name: Create and push release manifest image to GHCR
|
||||||
|
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||||
|
uses: Noelware/docker-manifest-action@master
|
||||||
|
with:
|
||||||
|
base-image: ghcr.io/purplei2p/i2pd:latest-release
|
||||||
|
extra-images: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7
|
||||||
|
push: true
|
||||||
|
|
||||||
|
- name: Create and push versioned manifest image to Docker Hub
|
||||||
|
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||||
|
uses: Noelware/docker-manifest-action@master
|
||||||
|
with:
|
||||||
|
base-image: purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
||||||
|
extra-images: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7
|
||||||
|
push: true
|
||||||
|
|
||||||
|
- name: Create and push versioned manifest image to GHCR
|
||||||
|
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||||
|
uses: Noelware/docker-manifest-action@master
|
||||||
|
with:
|
||||||
|
base-image: ghcr.io/purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
||||||
|
extra-images: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7
|
||||||
|
push: true
|
||||||
37
.gitignore
vendored
37
.gitignore
vendored
@@ -1,13 +1,20 @@
|
|||||||
# i2pd
|
# i2pd
|
||||||
obj/*.o
|
*.o
|
||||||
router.info
|
router.info
|
||||||
router.keys
|
router.keys
|
||||||
i2p
|
i2p
|
||||||
libi2pd.so
|
|
||||||
netDb
|
netDb
|
||||||
/i2pd
|
/i2pd
|
||||||
/libi2pd.a
|
/libi2pd.a
|
||||||
/libi2pdclient.a
|
/libi2pdclient.a
|
||||||
|
/libi2pdlang.a
|
||||||
|
/libi2pd.so
|
||||||
|
/libi2pdclient.so
|
||||||
|
/libi2pdlang.so
|
||||||
|
/libi2pd.dll
|
||||||
|
/libi2pdclient.dll
|
||||||
|
/libi2pdlang.dll
|
||||||
|
*.exe
|
||||||
|
|
||||||
|
|
||||||
# Autotools
|
# Autotools
|
||||||
@@ -239,10 +246,34 @@ pip-log.txt
|
|||||||
docs/_build
|
docs/_build
|
||||||
/androidIdea/
|
/androidIdea/
|
||||||
|
|
||||||
|
# Doxygen
|
||||||
|
docs/generated
|
||||||
|
|
||||||
# emacs files
|
# emacs files
|
||||||
*~
|
*~
|
||||||
*\#*
|
*\#*
|
||||||
|
|
||||||
# gdb files
|
# gdb files
|
||||||
.gdb_history
|
.gdb_history
|
||||||
|
|
||||||
|
# cmake makefile
|
||||||
|
build/Makefile
|
||||||
|
|
||||||
|
# debian stuff
|
||||||
|
debian/i2pd.1.gz
|
||||||
|
.pc/
|
||||||
|
|
||||||
|
# qt
|
||||||
|
|
||||||
|
qt/i2pd_qt/*.autosave
|
||||||
|
qt/i2pd_qt/*.ui.bk*
|
||||||
|
qt/i2pd_qt/*.ui_*
|
||||||
|
|
||||||
|
#unknown android stuff
|
||||||
|
android/libs/
|
||||||
|
|
||||||
|
#various logs
|
||||||
|
*LOGS/
|
||||||
|
|
||||||
|
qt/build-*.sh*
|
||||||
|
|
||||||
|
|||||||
35
.travis.yml
35
.travis.yml
@@ -1,35 +0,0 @@
|
|||||||
language: cpp
|
|
||||||
cache:
|
|
||||||
apt: true
|
|
||||||
os:
|
|
||||||
- linux
|
|
||||||
sudo: required
|
|
||||||
dist: trusty
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
packages:
|
|
||||||
- build-essential
|
|
||||||
- cmake
|
|
||||||
- g++
|
|
||||||
- clang
|
|
||||||
- libboost-chrono-dev
|
|
||||||
- libboost-date-time-dev
|
|
||||||
- libboost-filesystem-dev
|
|
||||||
- libboost-program-options-dev
|
|
||||||
- libboost-system-dev
|
|
||||||
- libboost-thread-dev
|
|
||||||
- libminiupnpc-dev
|
|
||||||
- libssl-dev
|
|
||||||
compiler:
|
|
||||||
- gcc
|
|
||||||
- clang
|
|
||||||
before_install:
|
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi
|
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install openssl miniupnpc ; fi
|
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew unlink boost openssl && brew link boost openssl -f ; fi
|
|
||||||
env:
|
|
||||||
matrix:
|
|
||||||
- BUILD_TYPE=Release UPNP=ON
|
|
||||||
- BUILD_TYPE=Release UPNP=OFF
|
|
||||||
script:
|
|
||||||
- cd build && cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DWITH_UPNP=${UPNP} && make
|
|
||||||
717
BOB.cpp
717
BOB.cpp
@@ -1,717 +0,0 @@
|
|||||||
#include <string.h>
|
|
||||||
#include "Log.h"
|
|
||||||
#include "ClientContext.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include "BOB.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
BOBI2PInboundTunnel::BOBI2PInboundTunnel (int port, std::shared_ptr<ClientDestination> localDestination):
|
|
||||||
BOBI2PTunnel (localDestination),
|
|
||||||
m_Acceptor (localDestination->GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
BOBI2PInboundTunnel::~BOBI2PInboundTunnel ()
|
|
||||||
{
|
|
||||||
Stop ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBI2PInboundTunnel::Start ()
|
|
||||||
{
|
|
||||||
m_Acceptor.listen ();
|
|
||||||
Accept ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBI2PInboundTunnel::Stop ()
|
|
||||||
{
|
|
||||||
m_Acceptor.close();
|
|
||||||
ClearHandlers ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBI2PInboundTunnel::Accept ()
|
|
||||||
{
|
|
||||||
auto receiver = std::make_shared<AddressReceiver> ();
|
|
||||||
receiver->socket = std::make_shared<boost::asio::ip::tcp::socket> (GetService ());
|
|
||||||
m_Acceptor.async_accept (*receiver->socket, std::bind (&BOBI2PInboundTunnel::HandleAccept, this,
|
|
||||||
std::placeholders::_1, receiver));
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBI2PInboundTunnel::HandleAccept (const boost::system::error_code& ecode, std::shared_ptr<AddressReceiver> receiver)
|
|
||||||
{
|
|
||||||
if (!ecode)
|
|
||||||
{
|
|
||||||
Accept ();
|
|
||||||
ReceiveAddress (receiver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBI2PInboundTunnel::ReceiveAddress (std::shared_ptr<AddressReceiver> receiver)
|
|
||||||
{
|
|
||||||
receiver->socket->async_read_some (boost::asio::buffer(
|
|
||||||
receiver->buffer + receiver->bufferOffset,
|
|
||||||
BOB_COMMAND_BUFFER_SIZE - receiver->bufferOffset),
|
|
||||||
std::bind(&BOBI2PInboundTunnel::HandleReceivedAddress, this,
|
|
||||||
std::placeholders::_1, std::placeholders::_2, receiver));
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBI2PInboundTunnel::HandleReceivedAddress (const boost::system::error_code& ecode, std::size_t bytes_transferred,
|
|
||||||
std::shared_ptr<AddressReceiver> receiver)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
LogPrint (eLogError, "BOB: inbound tunnel read error: ", ecode.message ());
|
|
||||||
else
|
|
||||||
{
|
|
||||||
receiver->bufferOffset += bytes_transferred;
|
|
||||||
receiver->buffer[receiver->bufferOffset] = 0;
|
|
||||||
char * eol = strchr (receiver->buffer, '\n');
|
|
||||||
if (eol)
|
|
||||||
{
|
|
||||||
*eol = 0;
|
|
||||||
if (eol != receiver->buffer && eol[-1] == '\r') eol[-1] = 0; // workaround for Transmission, it sends '\r\n' terminated address
|
|
||||||
receiver->data = (uint8_t *)eol + 1;
|
|
||||||
receiver->dataLen = receiver->bufferOffset - (eol - receiver->buffer + 1);
|
|
||||||
i2p::data::IdentHash ident;
|
|
||||||
if (!context.GetAddressBook ().GetIdentHash (receiver->buffer, ident))
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "BOB: address ", receiver->buffer, " not found");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto leaseSet = GetLocalDestination ()->FindLeaseSet (ident);
|
|
||||||
if (leaseSet)
|
|
||||||
CreateConnection (receiver, leaseSet);
|
|
||||||
else
|
|
||||||
GetLocalDestination ()->RequestDestination (ident,
|
|
||||||
std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestComplete,
|
|
||||||
this, std::placeholders::_1, receiver));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (receiver->bufferOffset < BOB_COMMAND_BUFFER_SIZE)
|
|
||||||
ReceiveAddress (receiver);
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "BOB: missing inbound address");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBI2PInboundTunnel::HandleDestinationRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::shared_ptr<AddressReceiver> receiver)
|
|
||||||
{
|
|
||||||
if (leaseSet)
|
|
||||||
CreateConnection (receiver, leaseSet);
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "BOB: LeaseSet for inbound destination not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBI2PInboundTunnel::CreateConnection (std::shared_ptr<AddressReceiver> receiver, std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: New inbound connection");
|
|
||||||
auto connection = std::make_shared<I2PTunnelConnection>(this, receiver->socket, leaseSet);
|
|
||||||
AddHandler (connection);
|
|
||||||
connection->I2PConnect (receiver->data, receiver->dataLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
BOBI2POutboundTunnel::BOBI2POutboundTunnel (const std::string& address, int port,
|
|
||||||
std::shared_ptr<ClientDestination> localDestination, bool quiet): BOBI2PTunnel (localDestination),
|
|
||||||
m_Endpoint (boost::asio::ip::address::from_string (address), port), m_IsQuiet (quiet)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBI2POutboundTunnel::Start ()
|
|
||||||
{
|
|
||||||
Accept ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBI2POutboundTunnel::Stop ()
|
|
||||||
{
|
|
||||||
ClearHandlers ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBI2POutboundTunnel::Accept ()
|
|
||||||
{
|
|
||||||
auto localDestination = GetLocalDestination ();
|
|
||||||
if (localDestination)
|
|
||||||
localDestination->AcceptStreams (std::bind (&BOBI2POutboundTunnel::HandleAccept, this, std::placeholders::_1));
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "BOB: Local destination not set for server tunnel");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBI2POutboundTunnel::HandleAccept (std::shared_ptr<i2p::stream::Stream> stream)
|
|
||||||
{
|
|
||||||
if (stream)
|
|
||||||
{
|
|
||||||
auto conn = std::make_shared<I2PTunnelConnection> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), m_Endpoint, m_IsQuiet);
|
|
||||||
AddHandler (conn);
|
|
||||||
conn->Connect ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BOBDestination::BOBDestination (std::shared_ptr<ClientDestination> localDestination):
|
|
||||||
m_LocalDestination (localDestination),
|
|
||||||
m_OutboundTunnel (nullptr), m_InboundTunnel (nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
BOBDestination::~BOBDestination ()
|
|
||||||
{
|
|
||||||
delete m_OutboundTunnel;
|
|
||||||
delete m_InboundTunnel;
|
|
||||||
i2p::client::context.DeleteLocalDestination (m_LocalDestination);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBDestination::Start ()
|
|
||||||
{
|
|
||||||
if (m_OutboundTunnel) m_OutboundTunnel->Start ();
|
|
||||||
if (m_InboundTunnel) m_InboundTunnel->Start ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBDestination::Stop ()
|
|
||||||
{
|
|
||||||
StopTunnels ();
|
|
||||||
m_LocalDestination->Stop ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBDestination::StopTunnels ()
|
|
||||||
{
|
|
||||||
if (m_OutboundTunnel)
|
|
||||||
{
|
|
||||||
m_OutboundTunnel->Stop ();
|
|
||||||
delete m_OutboundTunnel;
|
|
||||||
m_OutboundTunnel = nullptr;
|
|
||||||
}
|
|
||||||
if (m_InboundTunnel)
|
|
||||||
{
|
|
||||||
m_InboundTunnel->Stop ();
|
|
||||||
delete m_InboundTunnel;
|
|
||||||
m_InboundTunnel = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBDestination::CreateInboundTunnel (int port)
|
|
||||||
{
|
|
||||||
if (!m_InboundTunnel)
|
|
||||||
m_InboundTunnel = new BOBI2PInboundTunnel (port, m_LocalDestination);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBDestination::CreateOutboundTunnel (const std::string& address, int port, bool quiet)
|
|
||||||
{
|
|
||||||
if (!m_OutboundTunnel)
|
|
||||||
m_OutboundTunnel = new BOBI2POutboundTunnel (address, port, m_LocalDestination, quiet);
|
|
||||||
}
|
|
||||||
|
|
||||||
BOBCommandSession::BOBCommandSession (BOBCommandChannel& owner):
|
|
||||||
m_Owner (owner), m_Socket (m_Owner.GetService ()),
|
|
||||||
m_ReceiveBufferOffset (0), m_IsOpen (true), m_IsQuiet (false), m_IsActive (false),
|
|
||||||
m_InPort (0), m_OutPort (0), m_CurrentDestination (nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
BOBCommandSession::~BOBCommandSession ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::Terminate ()
|
|
||||||
{
|
|
||||||
m_Socket.close ();
|
|
||||||
m_IsOpen = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::Receive ()
|
|
||||||
{
|
|
||||||
m_Socket.async_read_some (boost::asio::buffer(m_ReceiveBuffer + m_ReceiveBufferOffset, BOB_COMMAND_BUFFER_SIZE - m_ReceiveBufferOffset),
|
|
||||||
std::bind(&BOBCommandSession::HandleReceived, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2));
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "BOB: command channel read error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t size = m_ReceiveBufferOffset + bytes_transferred;
|
|
||||||
m_ReceiveBuffer[size] = 0;
|
|
||||||
char * eol = strchr (m_ReceiveBuffer, '\n');
|
|
||||||
if (eol)
|
|
||||||
{
|
|
||||||
*eol = 0;
|
|
||||||
char * operand = strchr (m_ReceiveBuffer, ' ');
|
|
||||||
if (operand)
|
|
||||||
{
|
|
||||||
*operand = 0;
|
|
||||||
operand++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
operand = eol;
|
|
||||||
// process command
|
|
||||||
auto& handlers = m_Owner.GetCommandHandlers ();
|
|
||||||
auto it = handlers.find (m_ReceiveBuffer);
|
|
||||||
if (it != handlers.end ())
|
|
||||||
(this->*(it->second))(operand, eol - operand);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "BOB: unknown command ", m_ReceiveBuffer);
|
|
||||||
SendReplyError ("unknown command");
|
|
||||||
}
|
|
||||||
|
|
||||||
m_ReceiveBufferOffset = size - (eol - m_ReceiveBuffer) - 1;
|
|
||||||
memmove (m_ReceiveBuffer, eol + 1, m_ReceiveBufferOffset);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (size < BOB_COMMAND_BUFFER_SIZE)
|
|
||||||
m_ReceiveBufferOffset = size;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "BOB: Malformed input of the command channel");
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::Send (size_t len)
|
|
||||||
{
|
|
||||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_SendBuffer, len),
|
|
||||||
boost::asio::transfer_all (),
|
|
||||||
std::bind(&BOBCommandSession::HandleSent, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2));
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "BOB: command channel send error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (m_IsOpen)
|
|
||||||
Receive ();
|
|
||||||
else
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::SendReplyOK (const char * msg)
|
|
||||||
{
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t len = sprintf_s (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_REPLY_OK, msg);
|
|
||||||
#else
|
|
||||||
size_t len = snprintf (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_REPLY_OK, msg);
|
|
||||||
#endif
|
|
||||||
Send (len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::SendReplyError (const char * msg)
|
|
||||||
{
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t len = sprintf_s (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_REPLY_ERROR, msg);
|
|
||||||
#else
|
|
||||||
size_t len = snprintf (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_REPLY_ERROR, msg);
|
|
||||||
#endif
|
|
||||||
Send (len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::SendVersion ()
|
|
||||||
{
|
|
||||||
size_t len = strlen (BOB_VERSION);
|
|
||||||
memcpy (m_SendBuffer, BOB_VERSION, len);
|
|
||||||
Send (len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::SendData (const char * nickname)
|
|
||||||
{
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t len = sprintf_s (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_DATA, nickname);
|
|
||||||
#else
|
|
||||||
size_t len = snprintf (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_DATA, nickname);
|
|
||||||
#endif
|
|
||||||
Send (len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::ZapCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: zap");
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::QuitCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: quit");
|
|
||||||
m_IsOpen = false;
|
|
||||||
SendReplyOK ("Bye!");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::StartCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: start ", m_Nickname);
|
|
||||||
if (m_IsActive)
|
|
||||||
{
|
|
||||||
SendReplyError ("tunnel is active");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!m_CurrentDestination)
|
|
||||||
{
|
|
||||||
m_CurrentDestination = new BOBDestination (i2p::client::context.CreateNewLocalDestination (m_Keys, true, &m_Options));
|
|
||||||
m_Owner.AddDestination (m_Nickname, m_CurrentDestination);
|
|
||||||
}
|
|
||||||
if (m_InPort)
|
|
||||||
m_CurrentDestination->CreateInboundTunnel (m_InPort);
|
|
||||||
if (m_OutPort && !m_Address.empty ())
|
|
||||||
m_CurrentDestination->CreateOutboundTunnel (m_Address, m_OutPort, m_IsQuiet);
|
|
||||||
m_CurrentDestination->Start ();
|
|
||||||
SendReplyOK ("Tunnel starting");
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SendReplyError ("tunnel not found");
|
|
||||||
m_IsActive = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::SetNickCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: setnick ", operand);
|
|
||||||
m_Nickname = operand;
|
|
||||||
std::string msg ("Nickname set to ");
|
|
||||||
msg += m_Nickname;
|
|
||||||
SendReplyOK (msg.c_str ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::GetNickCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: getnick ", operand);
|
|
||||||
m_CurrentDestination = m_Owner.FindDestination (operand);
|
|
||||||
if (m_CurrentDestination)
|
|
||||||
{
|
|
||||||
m_Keys = m_CurrentDestination->GetKeys ();
|
|
||||||
m_Nickname = operand;
|
|
||||||
}
|
|
||||||
if (m_Nickname == operand)
|
|
||||||
{
|
|
||||||
std::string msg ("Nickname set to ");
|
|
||||||
msg += m_Nickname;
|
|
||||||
SendReplyOK (msg.c_str ());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SendReplyError ("no nickname has been set");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::NewkeysCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: newkeys");
|
|
||||||
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys ();
|
|
||||||
SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::SetkeysCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: setkeys ", operand);
|
|
||||||
m_Keys.FromBase64 (operand);
|
|
||||||
SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::GetkeysCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: getkeys");
|
|
||||||
if (m_Keys.GetPublic ()) // keys are set ?
|
|
||||||
SendReplyOK (m_Keys.ToBase64 ().c_str ());
|
|
||||||
else
|
|
||||||
SendReplyError ("keys are not set");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::GetdestCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: getdest");
|
|
||||||
SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::OuthostCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: outhost ", operand);
|
|
||||||
m_Address = operand;
|
|
||||||
SendReplyOK ("outhost set");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::OutportCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: outport ", operand);
|
|
||||||
m_OutPort = std::stoi(operand);
|
|
||||||
if (m_OutPort >= 0)
|
|
||||||
SendReplyOK ("outbound port set");
|
|
||||||
else
|
|
||||||
SendReplyError ("port out of range");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::InhostCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: inhost ", operand);
|
|
||||||
m_Address = operand;
|
|
||||||
SendReplyOK ("inhost set");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::InportCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: inport ", operand);
|
|
||||||
m_InPort = std::stoi(operand);
|
|
||||||
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");
|
|
||||||
if (m_Nickname.length () > 0)
|
|
||||||
{
|
|
||||||
if (!m_IsActive)
|
|
||||||
{
|
|
||||||
m_IsQuiet = true;
|
|
||||||
SendReplyOK ("Quiet set");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SendReplyError ("tunnel is active");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SendReplyError ("no nickname has been set");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::LookupCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: lookup ", operand);
|
|
||||||
i2p::data::IdentHash ident;
|
|
||||||
if (!context.GetAddressBook ().GetIdentHash (operand, ident))
|
|
||||||
{
|
|
||||||
SendReplyError ("Address Not found");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto localDestination = m_CurrentDestination ? m_CurrentDestination->GetLocalDestination () : i2p::client::context.GetSharedLocalDestination ();
|
|
||||||
auto leaseSet = localDestination->FindLeaseSet (ident);
|
|
||||||
if (leaseSet)
|
|
||||||
SendReplyOK (leaseSet->GetIdentity ()->ToBase64 ().c_str ());
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto s = shared_from_this ();
|
|
||||||
localDestination->RequestDestination (ident,
|
|
||||||
[s](std::shared_ptr<i2p::data::LeaseSet> ls)
|
|
||||||
{
|
|
||||||
if (ls)
|
|
||||||
s->SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ());
|
|
||||||
else
|
|
||||||
s->SendReplyError ("LeaseSet Not found");
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::ClearCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: clear");
|
|
||||||
m_Owner.DeleteDestination (m_Nickname);
|
|
||||||
m_Nickname = "";
|
|
||||||
SendReplyOK ("cleared");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::ListCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: list");
|
|
||||||
const auto& destinations = m_Owner.GetDestinations ();
|
|
||||||
for (const auto& it: destinations)
|
|
||||||
SendData (it.first.c_str ());
|
|
||||||
SendReplyOK ("Listing done");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::OptionCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: option ", operand);
|
|
||||||
const char * value = strchr (operand, '=');
|
|
||||||
if (value)
|
|
||||||
{
|
|
||||||
std::string msg ("option ");
|
|
||||||
*(const_cast<char *>(value)) = 0;
|
|
||||||
m_Options[operand] = value + 1;
|
|
||||||
msg += operand;
|
|
||||||
*(const_cast<char *>(value)) = '=';
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
if (m_CurrentDestination->GetLocalDestination ()->IsReady ())
|
|
||||||
s << " STARTING: false RUNNING: true STOPPING: false";
|
|
||||||
else
|
|
||||||
s << " STARTING: true RUNNING: false STOPPING: false";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
s << " STARTING: false 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),
|
|
||||||
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port))
|
|
||||||
{
|
|
||||||
// command -> handler
|
|
||||||
m_CommandHandlers[BOB_COMMAND_ZAP] = &BOBCommandSession::ZapCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_QUIT] = &BOBCommandSession::QuitCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_START] = &BOBCommandSession::StartCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_STOP] = &BOBCommandSession::StopCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_SETNICK] = &BOBCommandSession::SetNickCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_GETNICK] = &BOBCommandSession::GetNickCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_NEWKEYS] = &BOBCommandSession::NewkeysCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_GETKEYS] = &BOBCommandSession::GetkeysCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_SETKEYS] = &BOBCommandSession::SetkeysCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_GETDEST] = &BOBCommandSession::GetdestCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_OUTHOST] = &BOBCommandSession::OuthostCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_OUTPORT] = &BOBCommandSession::OutportCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_INHOST] = &BOBCommandSession::InhostCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_INPORT] = &BOBCommandSession::InportCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_QUIET] = &BOBCommandSession::QuietCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_LOOKUP] = &BOBCommandSession::LookupCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_CLEAR] = &BOBCommandSession::ClearCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_LIST] = &BOBCommandSession::ListCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_OPTION] = &BOBCommandSession::OptionCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_STATUS] = &BOBCommandSession::StatusCommandHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOBCommandChannel::~BOBCommandChannel ()
|
|
||||||
{
|
|
||||||
Stop ();
|
|
||||||
for (const auto& it: m_Destinations)
|
|
||||||
delete it.second;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandChannel::Start ()
|
|
||||||
{
|
|
||||||
Accept ();
|
|
||||||
m_IsRunning = true;
|
|
||||||
m_Thread = new std::thread (std::bind (&BOBCommandChannel::Run, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandChannel::Stop ()
|
|
||||||
{
|
|
||||||
m_IsRunning = false;
|
|
||||||
for (auto& it: m_Destinations)
|
|
||||||
it.second->Stop ();
|
|
||||||
m_Acceptor.cancel ();
|
|
||||||
m_Service.stop ();
|
|
||||||
if (m_Thread)
|
|
||||||
{
|
|
||||||
m_Thread->join ();
|
|
||||||
delete m_Thread;
|
|
||||||
m_Thread = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandChannel::Run ()
|
|
||||||
{
|
|
||||||
while (m_IsRunning)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_Service.run ();
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "BOB: runtime exception: ", ex.what ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandChannel::AddDestination (const std::string& name, BOBDestination * dest)
|
|
||||||
{
|
|
||||||
m_Destinations[name] = dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandChannel::DeleteDestination (const std::string& name)
|
|
||||||
{
|
|
||||||
auto it = m_Destinations.find (name);
|
|
||||||
if (it != m_Destinations.end ())
|
|
||||||
{
|
|
||||||
it->second->Stop ();
|
|
||||||
delete it->second;
|
|
||||||
m_Destinations.erase (it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BOBDestination * BOBCommandChannel::FindDestination (const std::string& name)
|
|
||||||
{
|
|
||||||
auto it = m_Destinations.find (name);
|
|
||||||
if (it != m_Destinations.end ())
|
|
||||||
return it->second;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandChannel::Accept ()
|
|
||||||
{
|
|
||||||
auto newSession = std::make_shared<BOBCommandSession> (*this);
|
|
||||||
m_Acceptor.async_accept (newSession->GetSocket (), std::bind (&BOBCommandChannel::HandleAccept, this,
|
|
||||||
std::placeholders::_1, newSession));
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandChannel::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<BOBCommandSession> session)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Accept ();
|
|
||||||
|
|
||||||
if (!ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "BOB: New command connection from ", session->GetSocket ().remote_endpoint ());
|
|
||||||
session->SendVersion ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "BOB: accept error: ", ecode.message ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
290
Base.cpp
290
Base.cpp
@@ -1,290 +0,0 @@
|
|||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "Base.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace data
|
|
||||||
{
|
|
||||||
static const char T32[32] = {
|
|
||||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
|
|
||||||
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
|
|
||||||
'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
|
|
||||||
'y', 'z', '2', '3', '4', '5', '6', '7',
|
|
||||||
};
|
|
||||||
|
|
||||||
const char * GetBase32SubstitutionTable ()
|
|
||||||
{
|
|
||||||
return T32;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void iT64Build(void);
|
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* BASE64 Substitution Table
|
|
||||||
* -------------------------
|
|
||||||
*
|
|
||||||
* Direct Substitution Table
|
|
||||||
*/
|
|
||||||
|
|
||||||
static const char T64[64] = {
|
|
||||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
|
||||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
|
||||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
|
||||||
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
|
||||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
|
||||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
|
||||||
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
|
||||||
'4', '5', '6', '7', '8', '9', '-', '~'
|
|
||||||
};
|
|
||||||
|
|
||||||
const char * GetBase64SubstitutionTable ()
|
|
||||||
{
|
|
||||||
return T64;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Reverse Substitution Table (built in run time)
|
|
||||||
*/
|
|
||||||
|
|
||||||
static char iT64[256];
|
|
||||||
static int isFirstTime = 1;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Padding
|
|
||||||
*/
|
|
||||||
|
|
||||||
static char P64 = '=';
|
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* ByteStreamToBase64
|
|
||||||
* ------------------
|
|
||||||
*
|
|
||||||
* Converts binary encoded data to BASE64 format.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
size_t /* Number of bytes in the encoded buffer */
|
|
||||||
ByteStreamToBase64 (
|
|
||||||
const uint8_t * InBuffer, /* Input buffer, binary data */
|
|
||||||
size_t InCount, /* Number of bytes in the input buffer */
|
|
||||||
char * OutBuffer, /* output buffer */
|
|
||||||
size_t len /* length of output buffer */
|
|
||||||
)
|
|
||||||
|
|
||||||
{
|
|
||||||
unsigned char * ps;
|
|
||||||
unsigned char * pd;
|
|
||||||
unsigned char acc_1;
|
|
||||||
unsigned char acc_2;
|
|
||||||
int i;
|
|
||||||
int n;
|
|
||||||
int m;
|
|
||||||
size_t outCount;
|
|
||||||
|
|
||||||
ps = (unsigned char *)InBuffer;
|
|
||||||
n = InCount/3;
|
|
||||||
m = InCount%3;
|
|
||||||
if (!m)
|
|
||||||
outCount = 4*n;
|
|
||||||
else
|
|
||||||
outCount = 4*(n+1);
|
|
||||||
if (outCount > len) return 0;
|
|
||||||
pd = (unsigned char *)OutBuffer;
|
|
||||||
for ( i = 0; i<n; i++ ){
|
|
||||||
acc_1 = *ps++;
|
|
||||||
acc_2 = (acc_1<<4)&0x30;
|
|
||||||
acc_1 >>= 2; /* base64 digit #1 */
|
|
||||||
*pd++ = T64[acc_1];
|
|
||||||
acc_1 = *ps++;
|
|
||||||
acc_2 |= acc_1 >> 4; /* base64 digit #2 */
|
|
||||||
*pd++ = T64[acc_2];
|
|
||||||
acc_1 &= 0x0f;
|
|
||||||
acc_1 <<=2;
|
|
||||||
acc_2 = *ps++;
|
|
||||||
acc_1 |= acc_2>>6; /* base64 digit #3 */
|
|
||||||
*pd++ = T64[acc_1];
|
|
||||||
acc_2 &= 0x3f; /* base64 digit #4 */
|
|
||||||
*pd++ = T64[acc_2];
|
|
||||||
}
|
|
||||||
if ( m == 1 ){
|
|
||||||
acc_1 = *ps++;
|
|
||||||
acc_2 = (acc_1<<4)&0x3f; /* base64 digit #2 */
|
|
||||||
acc_1 >>= 2; /* base64 digit #1 */
|
|
||||||
*pd++ = T64[acc_1];
|
|
||||||
*pd++ = T64[acc_2];
|
|
||||||
*pd++ = P64;
|
|
||||||
*pd++ = P64;
|
|
||||||
|
|
||||||
}
|
|
||||||
else if ( m == 2 ){
|
|
||||||
acc_1 = *ps++;
|
|
||||||
acc_2 = (acc_1<<4)&0x3f;
|
|
||||||
acc_1 >>= 2; /* base64 digit #1 */
|
|
||||||
*pd++ = T64[acc_1];
|
|
||||||
acc_1 = *ps++;
|
|
||||||
acc_2 |= acc_1 >> 4; /* base64 digit #2 */
|
|
||||||
*pd++ = T64[acc_2];
|
|
||||||
acc_1 &= 0x0f;
|
|
||||||
acc_1 <<=2; /* base64 digit #3 */
|
|
||||||
*pd++ = T64[acc_1];
|
|
||||||
*pd++ = P64;
|
|
||||||
}
|
|
||||||
|
|
||||||
return outCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Base64ToByteStream
|
|
||||||
* ------------------
|
|
||||||
*
|
|
||||||
* Converts BASE64 encoded data to binary format. If input buffer is
|
|
||||||
* not properly padded, buffer of negative length is returned
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
size_t /* Number of output bytes */
|
|
||||||
Base64ToByteStream (
|
|
||||||
const char * InBuffer, /* BASE64 encoded buffer */
|
|
||||||
size_t InCount, /* Number of input bytes */
|
|
||||||
uint8_t * OutBuffer, /* output buffer length */
|
|
||||||
size_t len /* length of output buffer */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
unsigned char * ps;
|
|
||||||
unsigned char * pd;
|
|
||||||
unsigned char acc_1;
|
|
||||||
unsigned char acc_2;
|
|
||||||
int i;
|
|
||||||
int n;
|
|
||||||
int m;
|
|
||||||
size_t outCount;
|
|
||||||
|
|
||||||
if (isFirstTime) iT64Build();
|
|
||||||
n = InCount/4;
|
|
||||||
m = InCount%4;
|
|
||||||
if (InCount && !m)
|
|
||||||
outCount = 3*n;
|
|
||||||
else {
|
|
||||||
outCount = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ps = (unsigned char *)(InBuffer + InCount - 1);
|
|
||||||
while ( *ps-- == P64 ) outCount--;
|
|
||||||
ps = (unsigned char *)InBuffer;
|
|
||||||
|
|
||||||
if (outCount > len) return -1;
|
|
||||||
pd = OutBuffer;
|
|
||||||
auto endOfOutBuffer = OutBuffer + outCount;
|
|
||||||
for ( i = 0; i < n; i++ ){
|
|
||||||
acc_1 = iT64[*ps++];
|
|
||||||
acc_2 = iT64[*ps++];
|
|
||||||
acc_1 <<= 2;
|
|
||||||
acc_1 |= acc_2>>4;
|
|
||||||
*pd++ = acc_1;
|
|
||||||
if (pd >= endOfOutBuffer) break;
|
|
||||||
|
|
||||||
acc_2 <<= 4;
|
|
||||||
acc_1 = iT64[*ps++];
|
|
||||||
acc_2 |= acc_1 >> 2;
|
|
||||||
*pd++ = acc_2;
|
|
||||||
if (pd >= endOfOutBuffer) break;
|
|
||||||
|
|
||||||
acc_2 = iT64[*ps++];
|
|
||||||
acc_2 |= acc_1 << 6;
|
|
||||||
*pd++ = acc_2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return outCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Base64EncodingBufferSize (const size_t input_size)
|
|
||||||
{
|
|
||||||
auto d = div (input_size, 3);
|
|
||||||
if (d.rem) d.quot++;
|
|
||||||
return 4*d.quot;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* iT64
|
|
||||||
* ----
|
|
||||||
* Reverse table builder. P64 character is replaced with 0
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void iT64Build()
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
isFirstTime = 0;
|
|
||||||
for ( i=0; i<256; i++ ) iT64[i] = -1;
|
|
||||||
for ( i=0; i<64; i++ ) iT64[(int)T64[i]] = i;
|
|
||||||
iT64[(int)P64] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen)
|
|
||||||
{
|
|
||||||
int tmp = 0, bits = 0;
|
|
||||||
size_t ret = 0;
|
|
||||||
for (size_t i = 0; i < len; i++)
|
|
||||||
{
|
|
||||||
char ch = inBuf[i];
|
|
||||||
if (ch >= '2' && ch <= '7') // digit
|
|
||||||
ch = (ch - '2') + 26; // 26 means a-z
|
|
||||||
else if (ch >= 'a' && ch <= 'z')
|
|
||||||
ch = ch - 'a'; // a = 0
|
|
||||||
else
|
|
||||||
return 0; // unexpected character
|
|
||||||
|
|
||||||
tmp |= ch;
|
|
||||||
bits += 5;
|
|
||||||
if (bits >= 8)
|
|
||||||
{
|
|
||||||
if (ret >= outLen) return ret;
|
|
||||||
outBuf[ret] = tmp >> (bits - 8);
|
|
||||||
bits -= 8;
|
|
||||||
ret++;
|
|
||||||
}
|
|
||||||
tmp <<= 5;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t ByteStreamToBase32 (const uint8_t * inBuf, size_t len, char * outBuf, size_t outLen)
|
|
||||||
{
|
|
||||||
size_t ret = 0, pos = 1;
|
|
||||||
int bits = 8, tmp = inBuf[0];
|
|
||||||
while (ret < outLen && (bits > 0 || pos < len))
|
|
||||||
{
|
|
||||||
if (bits < 5)
|
|
||||||
{
|
|
||||||
if (pos < len)
|
|
||||||
{
|
|
||||||
tmp <<= 8;
|
|
||||||
tmp |= inBuf[pos] & 0xFF;
|
|
||||||
pos++;
|
|
||||||
bits += 8;
|
|
||||||
}
|
|
||||||
else // last byte
|
|
||||||
{
|
|
||||||
tmp <<= (5 - bits);
|
|
||||||
bits = 5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bits -= 5;
|
|
||||||
int ind = (tmp >> bits) & 0x1F;
|
|
||||||
outBuf[ret] = (ind < 26) ? (ind + 'a') : ((ind - 26) + '2');
|
|
||||||
ret++;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
788
ChangeLog
788
ChangeLog
@@ -1,6 +1,786 @@
|
|||||||
# for this file format description,
|
# for this file format description,
|
||||||
# see https://github.com/olivierlacan/keep-a-changelog
|
# see https://github.com/olivierlacan/keep-a-changelog
|
||||||
|
|
||||||
|
## [2.45.1] - 2023-01-11
|
||||||
|
### Added
|
||||||
|
- Full Cone NAT status error
|
||||||
|
### Changed
|
||||||
|
- Drop duplicated I2NP messages in SSU2
|
||||||
|
- Set rejection code 30 if tunnel with id already exists
|
||||||
|
- Network status is always OK if peer test msg 5 received
|
||||||
|
### Fixed
|
||||||
|
- UPnP crash if SSU2 or NTCP2 is disabled
|
||||||
|
- Crash on termination for some platforms
|
||||||
|
|
||||||
|
## [2.45.0] - 2023-01-03
|
||||||
|
### Added
|
||||||
|
- Test for Symmetric NAT with peer test msgs 6 and 7
|
||||||
|
- Webconsole "No Descriptors" router error state
|
||||||
|
- 1 and 15 seconds bandwidth calculation for i2pcontrol
|
||||||
|
- Show non-zero send queue size for transports in web console
|
||||||
|
- Compressible padding for I2P addresses
|
||||||
|
- Localization to Czech
|
||||||
|
- Don't accept incoming session from invalid/reserved addresses for NTCP2 and SSU2
|
||||||
|
- Limit simultaneous tunnel build requests by 4 per pool
|
||||||
|
### Changed
|
||||||
|
- Removed SSU support
|
||||||
|
- Reduced bandwidth calculation interval from 60 to 15 seconds
|
||||||
|
- Increased default max transit tunnels number from 2500 to 5000 or 10000 for floodfill
|
||||||
|
- Transit tunnels limit is doubled if floodfill mode is enabled
|
||||||
|
- NTCP2 and SSU2 timestamps are rounded to seconds
|
||||||
|
- Drop RouterInfos and LeaseSets with timestamp from future
|
||||||
|
- Don't delete unreachable routers if tunnel creation success rate is too low
|
||||||
|
- Refuse duplicated incoming pending NTCP2 session from same IP
|
||||||
|
- Don't send SSU2 termination again if termination received block received
|
||||||
|
- Handle standard network error for SSU2 without throwing an exception
|
||||||
|
- Don't select overloaded peer for next tunnel
|
||||||
|
- Remove "X-Requested-With" in HTTP Proxy for non-AJAX requests
|
||||||
|
### Fixed
|
||||||
|
- File descriptors leak
|
||||||
|
- Random crash on AddressBook update
|
||||||
|
- Crash if incorrect LeaseSet size
|
||||||
|
- Spamming to log if no descriptors
|
||||||
|
- ::1 address in RouterInfo
|
||||||
|
- SSU2 network error handling (especially for Windows)
|
||||||
|
- Race condition with pending outgoing SSU2 sessions
|
||||||
|
- RTT self-reduction for long-live streams
|
||||||
|
|
||||||
|
## [2.44.0] - 2022-11-20
|
||||||
|
### Added
|
||||||
|
- SSL connection for server I2P tunnels
|
||||||
|
- Localization to Italian and Spanish
|
||||||
|
- SSU2 through SOCKS5 UDP proxy
|
||||||
|
- Reload tunnels through web console
|
||||||
|
- SSU2 send immediate ack request flag
|
||||||
|
- SSU2 send and verify path challenge
|
||||||
|
- Configurable ssu2.mtu4 and ssu2.mtu6
|
||||||
|
### Changed
|
||||||
|
- SSU2 is enabled and SSU is disabled by default
|
||||||
|
- Separate network status and error
|
||||||
|
- Random selection between NTCP2 and SSU2 priority
|
||||||
|
- Added notbob.i2p to jump services
|
||||||
|
- Remove DoNotTrack flag from HTTP Request header
|
||||||
|
- Skip addresshelper page if destination was not changed
|
||||||
|
- SSU2 allow different ports from RelayReponse and HolePunch
|
||||||
|
- SSU2 resend PeerTest msg 1 and msg 2
|
||||||
|
- SSU2 Send Retry instead SessionCreated if clock skew detected
|
||||||
|
### Fixed
|
||||||
|
- Long HTTP headers for HTTP proxy and HTTP server tunnel
|
||||||
|
- SSU2 resends and resend limits
|
||||||
|
- Crash at startup if addressbook is disabled
|
||||||
|
- NTCP2 ipv6 connection through SOCKS5 proxy
|
||||||
|
- SSU2 SessionRequest with zero token
|
||||||
|
- SSU2 MTU less than 1280
|
||||||
|
- SSU2 port=1
|
||||||
|
- Incorrect addresses from network interfaces
|
||||||
|
- Definitions for Darwin PPC; do not use pthread_setname_np
|
||||||
|
|
||||||
|
## [2.43.0] - 2022-08-22
|
||||||
|
### Added
|
||||||
|
- Complete SSU2 implementation
|
||||||
|
- Localization to Chinese
|
||||||
|
- Send RouterInfo update for long live sessions
|
||||||
|
- Explicit ipv6 ranges of known tunnel brokers for MTU detection
|
||||||
|
- Always send "Connection: close" and strip out Keep-Alive for server HTTP tunnel
|
||||||
|
- Show ports for all transports in web console
|
||||||
|
- Translation of webconsole site title
|
||||||
|
- Support for Windows ProgramData path when running as service
|
||||||
|
- Ability to turn off address book
|
||||||
|
- Handle signals TSTP and CONT to stop and resume network
|
||||||
|
### Changed
|
||||||
|
- Case insensitive headers for server HTTP tunnel
|
||||||
|
- Do not show 'Address registration' line if LeaseSet is encrypted
|
||||||
|
- SSU2 transports have higher priority than SSU
|
||||||
|
- Disable ElGamal precalculated table if no SSU
|
||||||
|
- Deprecate limits.ntcpsoft, limits.ntcphard and limits.ntcpthreads config options
|
||||||
|
- SSU2 is enabled and SSU is disabled by default for new installations
|
||||||
|
### Fixed
|
||||||
|
- Typo with Referer header name in HTTP proxy
|
||||||
|
- Can't handle garlic message from an exploratory tunnel
|
||||||
|
- Incorrect encryption key for exploratory lookup reply
|
||||||
|
- Bound checks issues in LeaseSets code
|
||||||
|
- MTU detection on Windows
|
||||||
|
- Crash on stop of active server tunnel
|
||||||
|
- Send datagram to wrong destination in SAM
|
||||||
|
- Incorrect static key in RouterInfo if the keys were regenerated
|
||||||
|
- Duplicated sessions in BOB
|
||||||
|
|
||||||
|
## [2.42.1] - 2022-05-24
|
||||||
|
### Fixed
|
||||||
|
- Incorrect jump link in HTTP Proxy
|
||||||
|
|
||||||
|
## [2.42.0] - 2022-05-22
|
||||||
|
### Added
|
||||||
|
- Preliminary SSU2 implementation
|
||||||
|
- Tunnel length variance
|
||||||
|
- Localization to French
|
||||||
|
- Daily cleanup of obsolete peer profiles
|
||||||
|
- Ordered jump services list in HTTP proxy
|
||||||
|
- Win32 service
|
||||||
|
- Show port for local non-published SSU addresses in web console
|
||||||
|
### Changed
|
||||||
|
- Maximum RouterInfo length increased to 3K
|
||||||
|
- Skip unknown addresses in RouterInfo
|
||||||
|
- Don't pick own router for peer test
|
||||||
|
- Reseeds list
|
||||||
|
- Internal numeric id for families
|
||||||
|
- Use ipv6 preference only when netinet headers not used
|
||||||
|
- Close stream if delete requested
|
||||||
|
- Remove version from title in web console
|
||||||
|
- Drop MESHNET build option
|
||||||
|
- Set data path before initialization
|
||||||
|
- Don't show registration block in web console if token is not provided
|
||||||
|
### Fixed
|
||||||
|
- Encrypted LeaseSet for EdDSA signature
|
||||||
|
- Clients tunnels are not built if clock is not synced on start
|
||||||
|
- Incorrect processing of i2cp.dontPublishLeaseSet param
|
||||||
|
- UDP tunnels reload
|
||||||
|
- Build for LibreSSL 3.5.2
|
||||||
|
- Race condition in short tunnel build message
|
||||||
|
- Race condition in local RouterInfo buffer allocation
|
||||||
|
|
||||||
|
## [2.41.0] - 2022-02-20
|
||||||
|
### Added
|
||||||
|
- Clock syncronization through SSU
|
||||||
|
- Drop routers older than 6 months on start
|
||||||
|
- Localization to German
|
||||||
|
- Don't send streaming ack too frequently
|
||||||
|
- Select compatible outbound tunnel for I2CP messages
|
||||||
|
- Restart webconsole's acceptor in case of exception
|
||||||
|
### Changed
|
||||||
|
- Use builtin bitswap for endian on windows
|
||||||
|
- Send SessionCreated before connection close if clock skew
|
||||||
|
- Try another floodfill for publishing if no compatible tunnels found
|
||||||
|
- Reduce memory usage for RouterInfo structures
|
||||||
|
- Avoid duplicated addresses in RouterInfo. Check presence of netId and version
|
||||||
|
- Use TCP/IP sockets for I2CP on Android instead local sockets
|
||||||
|
- Return uptime as integer in I2PControl
|
||||||
|
- Reseed servers list/cerificates
|
||||||
|
- Webconsole's dark style colors
|
||||||
|
### Fixed
|
||||||
|
- Attempt to use Yggdrasil on start on Android
|
||||||
|
- Attempts to send peer tests to itself
|
||||||
|
- Severe packets drop in SSU
|
||||||
|
- Crash on tunnel tests
|
||||||
|
- Loading addressbook subscriptions from config
|
||||||
|
- Multiple I2CP session to the same destination
|
||||||
|
- Build on Apple Silicon
|
||||||
|
|
||||||
|
## [2.40.0] - 2021-11-29
|
||||||
|
### Added
|
||||||
|
- Keep alive parameter for client tunnels
|
||||||
|
- Support openssl 3.0.0
|
||||||
|
- Localization to Armenian
|
||||||
|
- Show git commit info in version
|
||||||
|
- Windows menu item for opening datadir
|
||||||
|
- Reseed if too few floodfills
|
||||||
|
- Don't publish old and replacing tunnel in LeaseSet
|
||||||
|
- Webconsole light/dark theme depending on system settings (via CSS)
|
||||||
|
### Changed
|
||||||
|
- Set gzip compression to false by default
|
||||||
|
- Build tunnel through ECIES routers only
|
||||||
|
- Removed ElGamal support for tunnels
|
||||||
|
- Moved webconsole resources to separate file
|
||||||
|
- Pick tunnels with compatible transport with another tunnel of floodfill
|
||||||
|
- Use common cleanup timer for all SSU sessions
|
||||||
|
- Reduced memory usage
|
||||||
|
- Reseed servers list
|
||||||
|
- i18n code called from ClientContext
|
||||||
|
### Fixed
|
||||||
|
- Tunnels reload
|
||||||
|
- Some typos in log messages
|
||||||
|
- Cleanup relay requests table
|
||||||
|
- Server tunnel is not published
|
||||||
|
- Build on GNU/Hurd. Disable pthread_setname_np
|
||||||
|
- Crash when incorrect sigtype used with blinding
|
||||||
|
|
||||||
|
## [2.39.0] - 2021-08-23
|
||||||
|
### Added
|
||||||
|
- Short tunnel build messages
|
||||||
|
- Localization. To: Russian, Ukrainian, Turkmen, Uzbek and Afrikaans
|
||||||
|
- Custom CSS styles for webconsole
|
||||||
|
- Avoid slow tunnels with more than 250 ms per hop
|
||||||
|
- Process DELAY_REQUESTED streaming option
|
||||||
|
- "certsdir" options for certificates location
|
||||||
|
- Keep own RouterInfo in NetBb
|
||||||
|
- Pick ECIES routers only for tunnels on non-x64
|
||||||
|
- NTP sync through ipv6
|
||||||
|
- Allow ipv6 addresses for UDP server tunnels
|
||||||
|
### Changed
|
||||||
|
- Rekey of all routers to ECIES
|
||||||
|
- Better distribution for random tunnel's peer selection
|
||||||
|
- Yggdrasil reseed for v0.4, added two more
|
||||||
|
- Encryption type 0,4 by default for server tunnels
|
||||||
|
- Handle i2cp.dontPublishLeaseSet param for all destinations
|
||||||
|
- reg.i2p for subscriptions
|
||||||
|
- LeaseSet type 3 by default
|
||||||
|
- Don't allocate payload buffer for every single ECIESx25519 message
|
||||||
|
- Prefer public ipv6 instead rfc4941
|
||||||
|
- Optimal padding for one-time ECIESx25519 message
|
||||||
|
- Don't send datetime block for one-time ECIESx25519 message with one-time key
|
||||||
|
- Router with expired introducer is still valid
|
||||||
|
- Don't disable floodfill if still reachable by ipv6
|
||||||
|
- Set minimal version for floodfill to 0.9.38
|
||||||
|
- Eliminate extra lookups for sequential fragments on tunnel endpoint
|
||||||
|
- Consistent path for explicit peers
|
||||||
|
- Always create new tunnel from exploratory pool
|
||||||
|
- Don't try to connect to a router not reachable from us
|
||||||
|
- Mark additional ipv6 addresses/nets as reserved (#1679)
|
||||||
|
### Fixed
|
||||||
|
- Zero-hop tunnels
|
||||||
|
- Crash upon SAM session termination
|
||||||
|
- Build with boost < 1.55.0
|
||||||
|
- Address type for NTCP2 acceptors
|
||||||
|
- Check of ipv4/ipv6 address
|
||||||
|
- Request router to send to if not in NetDb
|
||||||
|
- Count outbound traffic for zero-hop tunnels
|
||||||
|
- URLdecode domain for registration string generator in webconsole
|
||||||
|
|
||||||
|
## [2.38.0] - 2021-05-17
|
||||||
|
### Added
|
||||||
|
- Publish ipv6 introducers
|
||||||
|
- Bind ipv6 or yggdrasil NTCP2 acceptor to specified address
|
||||||
|
- Support .b32.i2p addresses and hostnames for SAM STREAM CREATE
|
||||||
|
- ipv6 peer tests
|
||||||
|
- Publish iexp param for introducers
|
||||||
|
- Show ipv6 network status on the webconsole
|
||||||
|
- EdDSA signing keys can also be blinded
|
||||||
|
- Show router version on the webconsole
|
||||||
|
### Changed
|
||||||
|
- Rekey of all routers but floodfills to ECIES
|
||||||
|
- Increased number of precalculated x25519 keys to 15
|
||||||
|
- Don't publish LeaseSet without inbound tunnels
|
||||||
|
- Reseed from compatible address(ipv4 or ipv6)
|
||||||
|
- Recongnize v4 and v6 SSU addresses without host
|
||||||
|
- Inbound tunnel gateway must be ipv4 compatible
|
||||||
|
- Don't select next introducers from existing sessions
|
||||||
|
- Set X bandwidth for floodfill by default
|
||||||
|
### Fixed
|
||||||
|
- Incoming ECIES-x25519 session doesn't send updated LeaseSet
|
||||||
|
- Unique local address for server tunnels
|
||||||
|
- Race condition for LeaseSet creation in I2CP
|
||||||
|
- Relay tag for ipv6 introducer
|
||||||
|
- Already expired introducers
|
||||||
|
- Find connected router for first peer in tunnel
|
||||||
|
- Failed outgoing ECIES-x25519 session's tagset stays forever
|
||||||
|
- Yggdrasil address disappears if router becomes unreachable through ipv6
|
||||||
|
- Ignore SSU address/introducers if port is not specified
|
||||||
|
- Check identity and signature length for SSU SessionConfirmed
|
||||||
|
|
||||||
|
## [2.37.0] - 2021-03-15
|
||||||
|
### Added
|
||||||
|
- Address registration line for reg.i2p and stats.i2p through the web console
|
||||||
|
- "4" and "6" caps for addresses without published IP address
|
||||||
|
- Mesh and Proxy network statuses
|
||||||
|
- Symmetric NAT network status error
|
||||||
|
- Bind server tunnel connection to specified address
|
||||||
|
- lookuplocal BOB extended command
|
||||||
|
- address4 and address6 parameters to bind outgoing connections to
|
||||||
|
- Rekey of low-bandwidth routers to ECIES
|
||||||
|
- Popup notification windows when unable to parse config for Windows
|
||||||
|
### Changed
|
||||||
|
- Floodfills with "U" cap are not ignored anymore
|
||||||
|
- Check transports reachability between tunnel peers and between router and floodfill
|
||||||
|
- NTCP2 and reseed HTTP proxy support authorization now
|
||||||
|
- Show actual IP addresses for proxy connections
|
||||||
|
- Publish and handle SSU addreses without host
|
||||||
|
- Outbound tunnel endpoint must be ipv4 compatible
|
||||||
|
- Logging optimization
|
||||||
|
- Removed Windows service
|
||||||
|
### Fixed
|
||||||
|
- Incoming SSU session terminates after 5 seconds
|
||||||
|
- Outgoing NTCP2 ipv4 session even if ipv4 is disabled
|
||||||
|
- No incoming Yggdrasil connection if connected through NTCP2 proxy
|
||||||
|
- Race condition between tunnel build and floodfill requests decryption for ECIES routers
|
||||||
|
- Numeric bandwidth limitation
|
||||||
|
- Yggdrasil for Android
|
||||||
|
|
||||||
|
## [2.36.0] - 2021-02-15
|
||||||
|
### Added
|
||||||
|
- Encrypted lookup and publications to ECIES-x25519 floodfiils
|
||||||
|
- Yggdrasil transports and reseeds
|
||||||
|
- Dump addressbook in hosts.txt format
|
||||||
|
- Request RouterInfo through exploratory tunnels if direct connection to fllodfill is not possible
|
||||||
|
- Threads naming
|
||||||
|
- Check if public x25519 key is valid
|
||||||
|
- ECIES-X25519-AEAD-Ratchet for shared local destination
|
||||||
|
- LeaseSet creation timeout for I2CP session
|
||||||
|
- Resend RouterInfo after some interval for longer NTCP2 sessions
|
||||||
|
- Select reachable router of inbound tunnel gateway
|
||||||
|
- Reseed if no compatible routers in netdb
|
||||||
|
- Refresh on swipe in Android webconsole
|
||||||
|
### Changed
|
||||||
|
- reg.i2p for default addressbook instead inr.i2p
|
||||||
|
- ECIES-x25519 (crypto type 4) for new routers
|
||||||
|
- Try to connect to all compatible addresses from peer's RouterInfo
|
||||||
|
- Replace LeaseSet completely if store type changes
|
||||||
|
- Try ECIES-X25519-AEAD-Ratchet tag before ElGamal
|
||||||
|
- Don't detach ECIES-X25519-AEAD-Ratchet session from destination immediately
|
||||||
|
- Viewport and styles on error in HTTP proxy
|
||||||
|
- Don't create notification when Windows taskbar restarted
|
||||||
|
- Cumulative SSU ACK bitfields
|
||||||
|
- limit tunnel length to 8 hops
|
||||||
|
- Limit tunnels quantity to 16
|
||||||
|
### Fixed
|
||||||
|
- Handling chunked HTTP response in addressbook
|
||||||
|
- Missing ECIES-X25519-AEAD-Ratchet tags for multiple streams with the same destination
|
||||||
|
- Correct NAME for NAMING REPLY in SAM
|
||||||
|
- SSU crash on termination
|
||||||
|
- Offline signature length for stream close packet
|
||||||
|
- Don't send updated LeaseSet through a terminated session
|
||||||
|
- Decryption of follow-on ECIES-X25519-AEAD-Ratchet NSR messages
|
||||||
|
- Non-confirmed LeaseSet is resent too late for ECIES-X25519-AEAD-Ratchet session
|
||||||
|
|
||||||
|
## [2.35.0] - 2020-11-30
|
||||||
|
### Added
|
||||||
|
- ECIES-x25519 routers
|
||||||
|
- Random intro keys for SSU
|
||||||
|
- Graceful shutdown timer for windows
|
||||||
|
- Send queue for I2CP messages
|
||||||
|
- Update DSA router keys to EdDSA
|
||||||
|
- TCP_QUICKACK for NTCP2 sockets on Linux
|
||||||
|
### Changed
|
||||||
|
- Exclude floodfills with DSA signatures and < 0.9.28
|
||||||
|
- Random intervals between tunnel tests and manage for tunnel pools
|
||||||
|
- Don't replace an addressbook record by one with DSA signature
|
||||||
|
- Publish RouterInfo after update
|
||||||
|
- Create paired inbound tunnels if no inbound tunnels yet
|
||||||
|
- Reseed servers list
|
||||||
|
### Fixed
|
||||||
|
- Transient signature length, if different from identity
|
||||||
|
- Terminate I2CP session if destroyed
|
||||||
|
- RouterInfo publishing confirmation
|
||||||
|
- Check if ECIES-X25519-AEAD-Ratchet session expired before generating more tags
|
||||||
|
- Correct block size for delivery type local for ECIES-X25519-AEAD-Ratchet
|
||||||
|
|
||||||
|
## [2.34.0] - 2020-10-27
|
||||||
|
### Added
|
||||||
|
- Ping responses for streaming
|
||||||
|
- STREAM FORWARD for SAM
|
||||||
|
- Tunnels through ECIES-x25519 routers
|
||||||
|
- Single thread for I2CP
|
||||||
|
- Shared transient destination between proxies
|
||||||
|
- Database lookups from ECIES destinations with ratchets response
|
||||||
|
- Handle WebDAV HTTP methods
|
||||||
|
- Don't try to connect or build tunnels if offline
|
||||||
|
- Validate IP when trying connect to remote peer
|
||||||
|
- Handle ICMP responses and WinAPI errors for SSU
|
||||||
|
### Changed
|
||||||
|
- Removed NTCP
|
||||||
|
- Dropped gcc 4.7 support
|
||||||
|
- Encyption type 0,4 by default for client tunnels
|
||||||
|
- Stripped out some HTTP header for HTTP server response
|
||||||
|
- HTTP 1.1 addressbook requests
|
||||||
|
- Set LeaseSet type to 3 for ratchets if not specified
|
||||||
|
- Handle SSU v4 and v6 messages in one thread
|
||||||
|
- Eliminate DH keys thread
|
||||||
|
### Fixed
|
||||||
|
- Random crashes on I2CP session disconnect
|
||||||
|
- Stream through racthets hangs if first SYN was not acked
|
||||||
|
- Check "Last-Modified" instead "If-Modified-Since" for addressbook reponse
|
||||||
|
- Trim behind ECIESx25519 tags
|
||||||
|
- Few bugs with Android main activity
|
||||||
|
- QT visual and layout issues
|
||||||
|
|
||||||
|
## [2.33.0] - 2020-08-24
|
||||||
|
### Added
|
||||||
|
- Shared transient addresses
|
||||||
|
- crypto.ratchet.inboundTags paramater
|
||||||
|
- Multiple encryption keys through I2CP
|
||||||
|
- Pre-calculated x25519 ephemeral keys
|
||||||
|
- Change datagram routing path if nothing comes back in 10 seconds
|
||||||
|
- Shared routing path for datagram session
|
||||||
|
### Changed
|
||||||
|
- UDP tunnels send mix of repliable and raw datagrams in bulk
|
||||||
|
- Encrypt SSU packet again upon resend
|
||||||
|
- Start new tunnel message if remaining buffer is too small
|
||||||
|
- Use LeaseSet2 for ECIES-X25519-AEAD-Ratchet automatically
|
||||||
|
- Save new ECIES-X25519-AEAD-Ratchet session with NSR tagset
|
||||||
|
- Generate random padding lengths for ECIES-X25519-AEAD-Ratchet in bulk
|
||||||
|
- Webconsole layout
|
||||||
|
- Reseed servers list
|
||||||
|
### Fixed
|
||||||
|
- Don't connect through terminated SAM destination
|
||||||
|
- Differentiate UDP server sessions by port
|
||||||
|
- ECIES-X25519-AEAD-Ratchet through I2CP
|
||||||
|
- Don't save invalid address to AddressBook
|
||||||
|
- ECDSA signatures names in SAM
|
||||||
|
- AppArmor profile
|
||||||
|
|
||||||
|
## [2.32.1] - 2020-06-02
|
||||||
|
### Added
|
||||||
|
- Read explicit peers in tunnels config
|
||||||
|
### Fixed
|
||||||
|
- Generation of tags for detached sessions
|
||||||
|
- Non-updating LeaseSet1
|
||||||
|
- Start when deprecated websocket options present in i2pd.conf
|
||||||
|
|
||||||
|
## [2.32.0] - 2020-05-25
|
||||||
|
### Added
|
||||||
|
- Multiple encryption types for local destinations
|
||||||
|
- Next key and tagset for ECIES-X25519-AEAD-Ratchet
|
||||||
|
- NTCP2 through SOCKS proxy
|
||||||
|
- Throw error message if any port to bind is occupied
|
||||||
|
- gzip parameter for UDP tunnels
|
||||||
|
- Show ECIES-X25519-AEAD-Ratchet sessions and tags on the web console
|
||||||
|
- Simplified implementation of gzip for no compression mode
|
||||||
|
- Allow ECIES-X25519-AEAD-Ratchet session restart after 2 minutes
|
||||||
|
- Added logrotate config for rpm package
|
||||||
|
### Changed
|
||||||
|
- Select peers for client tunnels among routers >= 0.9.36
|
||||||
|
- Check ECIES flag for encrypted lookup reply
|
||||||
|
- Streaming MTU size 1812 for ECIES-X25519-AEAD-Ratchet
|
||||||
|
- Don't calculate checksum for Data message send through ECIES-X25519-AEAD-Ratchet
|
||||||
|
- Catch network connectivity status for Windows
|
||||||
|
- Stop as soon as no more transit tunnels during graceful shutdown for Android
|
||||||
|
- RouterInfo gzip compression level depends on size
|
||||||
|
- Send response to received datagram from ECIES-X25519-AEAD-Ratchet session
|
||||||
|
- Update webconsole functional
|
||||||
|
- Increased max transit tunnels limit
|
||||||
|
- Reseeds list
|
||||||
|
- Dropped windows support in cmake
|
||||||
|
### Fixed
|
||||||
|
- Correct timestamp check for LeaseSet2
|
||||||
|
- Encrypted leaseset without authentication
|
||||||
|
- Change SOCKS proxy connection response for clients without socks5h support (#1336)
|
||||||
|
|
||||||
|
## [2.31.0] - 2020-04-10
|
||||||
|
### Added
|
||||||
|
- NTCP2 through HTTP proxy
|
||||||
|
- Publish LeaseSet2 for I2CP destinations
|
||||||
|
- Show status page on main activity for android
|
||||||
|
- Handle ECIESFlag in DatabaseLookup at floodfill
|
||||||
|
- C++17 features for eligible compilers
|
||||||
|
### Changed
|
||||||
|
- Droped Websockets and Lua support
|
||||||
|
- Send DeliveryStatusMsg for LeaseSet for ECIES-X25519-AEAD-Ratchet
|
||||||
|
- Keep sending new session reply until established for ECIES-X25519-AEAD-Ratchet
|
||||||
|
- Updated SSU log messages
|
||||||
|
- Reopen SSU socket on exception
|
||||||
|
- Security hardening headers in web console
|
||||||
|
- Various web console changes
|
||||||
|
- Various QT changes
|
||||||
|
### Fixed
|
||||||
|
- NTCP2 socket descriptors leak
|
||||||
|
- Race condition with router's identity in transport sessions
|
||||||
|
- Not terminated streams remain forever
|
||||||
|
|
||||||
|
## [2.30.0] - 2020-02-25
|
||||||
|
### Added
|
||||||
|
- Single threaded SAM
|
||||||
|
- Experimental support of ECIES-X25519-AEAD-Ratchet crypto type
|
||||||
|
### Changed
|
||||||
|
- Minimal MTU size is 1280 for ipv6
|
||||||
|
- Use unordered_map instead map for destination's sessions and tags list
|
||||||
|
- Use std::shuffle instead std::random_shuffle
|
||||||
|
- SAM is single threaded by default
|
||||||
|
- Reseeds list
|
||||||
|
### Fixed
|
||||||
|
- Correct termination of streaming destination
|
||||||
|
- Extra ',' in RouterInfo response in I2PControl
|
||||||
|
- SAM crash on session termination
|
||||||
|
- Storage for Android 10
|
||||||
|
|
||||||
|
## [2.29.0] - 2019-10-21
|
||||||
|
### Added
|
||||||
|
- Client auth flag for b33 address
|
||||||
|
### Changed
|
||||||
|
- Remove incoming NTCP2 session from pending list when established
|
||||||
|
- Handle errors for NTCP2 SessionConfrimed send
|
||||||
|
### Fixed
|
||||||
|
- Failure to start on Windows XP
|
||||||
|
- SAM crash if invalid lookup address
|
||||||
|
- Possible crash when UPnP enabled on shutdown
|
||||||
|
|
||||||
|
## [2.28.0] - 2019-08-27
|
||||||
|
### Added
|
||||||
|
- RAW datagrams in SAM
|
||||||
|
- Publishing encrypted LeaseSet2 with DH or PSH authentication
|
||||||
|
- Ability to disable battery optimization for Android
|
||||||
|
- Transport Network ID Check
|
||||||
|
### Changed
|
||||||
|
- Set and handle published encrypted flag for LeaseSet2
|
||||||
|
### Fixed
|
||||||
|
- ReceiveID changes in the same stream
|
||||||
|
- "\r\n" command terminator in SAM
|
||||||
|
- Addressbook lines with signatures
|
||||||
|
|
||||||
|
## [2.27.0] - 2019-07-03
|
||||||
|
### Added
|
||||||
|
- Support of PSK and DH authentication for encrypted LeaseSet2
|
||||||
|
### Changed
|
||||||
|
- Uptime is based on monotonic timer
|
||||||
|
### Fixed
|
||||||
|
- BOB status command response
|
||||||
|
- Correct NTCP2 port if NTCP is disabled
|
||||||
|
- Flood encrypted LeaseSet2 with store hash
|
||||||
|
|
||||||
|
## [2.26.0] - 2019-06-07
|
||||||
|
### Added
|
||||||
|
- HTTP method "PROPFIND"
|
||||||
|
- Detection of external ipv6 address through the SSU
|
||||||
|
- NTCP2 publishing depends on network status
|
||||||
|
### Changed
|
||||||
|
- ntcp is disabled by default, ntcp2 is published by default
|
||||||
|
- Response to BOB's "list" command
|
||||||
|
- ipv6 address is not longer NTCP's local endpoint's address
|
||||||
|
- Reseeds list
|
||||||
|
- HTTP_REFERER stripping in httpproxy (#823)
|
||||||
|
### Fixed
|
||||||
|
- Check and handle incorrect BOB input
|
||||||
|
- Ignore introducers for NTCP or NTCP2 addresses
|
||||||
|
- RouterInfo check from NTCP2
|
||||||
|
|
||||||
|
## [2.25.0] - 2019-05-09
|
||||||
|
### Added
|
||||||
|
- Create, publish and handle encrypted LeaseSet2
|
||||||
|
- Support of b33 addresses
|
||||||
|
- RedDSA key blinding
|
||||||
|
- .b32.i2p addresses in jump links
|
||||||
|
- ntcp2.addressv6 parameter
|
||||||
|
### Changed
|
||||||
|
- Allow HTTP headers without value
|
||||||
|
- Set data directory from external storage path for Android
|
||||||
|
- addresshelper support is configurable per tunnel
|
||||||
|
- gradlew script for android build
|
||||||
|
### Fixed
|
||||||
|
- Deletion of expired encrypted LeaseSet2 on floodfills
|
||||||
|
- ipv6 fallback address
|
||||||
|
- SSU incoming packets routing
|
||||||
|
|
||||||
|
## [2.24.0] - 2019-03-21
|
||||||
|
### Added
|
||||||
|
- Support of transient keys for LeaseSet2
|
||||||
|
- Support of encrypted LeaseSet2
|
||||||
|
- Recognize signature type 11 (RedDSA)
|
||||||
|
- Support websocket connections over HTTP proxy
|
||||||
|
- Ability to disable full addressbook persist
|
||||||
|
### Changed
|
||||||
|
- Don't load peer profiles if non-persistant
|
||||||
|
- REUSE_ADDR for ipv6 acceptors
|
||||||
|
- Reset eTags if addressbook can't be loaded
|
||||||
|
### Fixed
|
||||||
|
- Build with boost 1.70
|
||||||
|
- Filter out unspecified addresses from RouterInfo
|
||||||
|
- Check floodfill status change
|
||||||
|
- Correct SAM response for invalid key
|
||||||
|
- SAM crash on termination for Windows
|
||||||
|
- Race condition for publishing
|
||||||
|
|
||||||
|
## [2.23.0] - 2019-01-21
|
||||||
|
### Added
|
||||||
|
- Standard LeaseSet2 support
|
||||||
|
- Ability to adjust timestamps through the NTP
|
||||||
|
- Ability to disable peer profile persist
|
||||||
|
- Request permission for android >= 6
|
||||||
|
- Initial addressbook to android assets
|
||||||
|
- Cancel graceful shutdown for android
|
||||||
|
- Russian translation for android
|
||||||
|
### Changed
|
||||||
|
- Chacha20 and Poly1305 implementation
|
||||||
|
- Eliminate extra copy of NTCP2 send buffers
|
||||||
|
- Extract content of tunnel.d from assets on android
|
||||||
|
- Removed name resolvers from transports
|
||||||
|
- Update reseed certificates
|
||||||
|
### Fixed
|
||||||
|
- LeaseSet published content verification
|
||||||
|
- Exclude invalid LeaseSets from the list on a floodfill
|
||||||
|
- Build for OpenWrt with openssl 1.1.1
|
||||||
|
|
||||||
|
## [2.22.0] - 2018-11-09
|
||||||
|
### Added
|
||||||
|
- Multiple tunnel config files from tunnels.d folder
|
||||||
|
### Changed
|
||||||
|
- Fetch own RouterInfo upon SessionRequest for NTCP2
|
||||||
|
- Faster XOR between AES blocks for non AVX capable CPUs
|
||||||
|
### Fixed
|
||||||
|
- Fixed NTCP2 termination send
|
||||||
|
|
||||||
|
## [2.21.1] - 2018-10-22
|
||||||
|
### Changed
|
||||||
|
- cost=13 for unpublished NTCP2 address
|
||||||
|
### Fixed
|
||||||
|
- Handle I2NP messages longer than 32K
|
||||||
|
|
||||||
|
## [2.21.0] - 2018-10-04
|
||||||
|
### Added
|
||||||
|
- EdDSA, x25519 and SipHash from openssl 1.1.1
|
||||||
|
- NTCP2 ipv6 incoming connections
|
||||||
|
- Show total number of destination's outgoing tags in the web console
|
||||||
|
### Changed
|
||||||
|
- Android build with openssl 1.1.1/boost 1.64
|
||||||
|
- Bandwidth classes 'P' and 'X' don't add 'O' anymore
|
||||||
|
### Fixed
|
||||||
|
- Update own RouterInfo if no SSU
|
||||||
|
- Recognize 'P' and 'X' routers as high bandwidth without 'O'
|
||||||
|
- NTCP address doesn't disappear if NTCP2 enabled
|
||||||
|
- Android with api 26+
|
||||||
|
|
||||||
|
## [2.20.0] - 2018-08-23
|
||||||
|
### Added
|
||||||
|
- Full implementation of NTCP2
|
||||||
|
- Assets for android
|
||||||
|
### Changed
|
||||||
|
- armeabi-v7a and x86 in one apk for android
|
||||||
|
- NTCP2 is enabled by default
|
||||||
|
- Show lease's expiration time in readable format in the web console
|
||||||
|
### Fixed
|
||||||
|
- Correct names for transports in the web console
|
||||||
|
|
||||||
|
## [2.19.0] - 2018-06-26
|
||||||
|
### Added
|
||||||
|
- ECIES support for RouterInfo
|
||||||
|
- HTTP outproxy authorization
|
||||||
|
- AVX/AESNI runtime detection
|
||||||
|
- Initial implementation of NTCP2
|
||||||
|
- I2CP session reconfigure
|
||||||
|
- I2CP method ClientServicesInfo
|
||||||
|
- Datagrams to websocks
|
||||||
|
### Changed
|
||||||
|
- RouterInfo uses EdDSA signature by default
|
||||||
|
- Remove stream bans
|
||||||
|
- Android build system changed to gradle
|
||||||
|
- Multiple changes in QT GUI
|
||||||
|
- Dockerfile
|
||||||
|
### Fixed
|
||||||
|
- zero tunnelID issue
|
||||||
|
- tunnels reload
|
||||||
|
- headers in webconsole
|
||||||
|
- XSS in webconsole from SAM session name
|
||||||
|
- build for gcc 8
|
||||||
|
- cmake build scripts
|
||||||
|
- systemd service files
|
||||||
|
- some netbsd issues
|
||||||
|
|
||||||
|
## [2.18.0] - 2018-01-30
|
||||||
|
### Added
|
||||||
|
- Show tunnel nicknames for I2CP destination in WebUI
|
||||||
|
- Re-create HTTP and SOCKS proxy by tunnel reload
|
||||||
|
- Graceful shutdown as soon as no more transit tunnels
|
||||||
|
### Changed
|
||||||
|
- Regenerate shared local destination by tunnel reload
|
||||||
|
- Use transient local destination by default if not specified
|
||||||
|
- Return correct code if pid file can't be created
|
||||||
|
- Timing and number of attempts for adressbook requests
|
||||||
|
- Certificates list
|
||||||
|
### Fixed
|
||||||
|
- Malformed addressbook subsctiption request
|
||||||
|
- Build with boost 1.66
|
||||||
|
- Few race conditions for SAM
|
||||||
|
- Check LeaseSet's signature before update
|
||||||
|
|
||||||
|
## [2.17.0] - 2017-12-04
|
||||||
|
### Added
|
||||||
|
- Reseed through HTTP and SOCKS proxy
|
||||||
|
- Show status of client services through web console
|
||||||
|
- Change log level through web connsole
|
||||||
|
- transient keys for tunnels
|
||||||
|
- i2p.streaming.initialAckDelay parameter
|
||||||
|
- CRYPTO_TYPE for SAM destination
|
||||||
|
- signature and crypto type for newkeys BOB command
|
||||||
|
### Changed
|
||||||
|
- Correct publication of ECIES destinations
|
||||||
|
- Disable RSA signatures completely
|
||||||
|
### Fixed
|
||||||
|
- CVE-2017-17066
|
||||||
|
- Possible buffer overflow for RSA-4096
|
||||||
|
- Shutdown from web console for Windows
|
||||||
|
- Web console page layout
|
||||||
|
## [2.16.0] - 2017-11-13
|
||||||
|
### Added
|
||||||
|
- https and "Connect" method for HTTP proxy
|
||||||
|
- outproxy for HTTP proxy
|
||||||
|
- initial support of ECIES crypto
|
||||||
|
- NTCP soft and hard descriptors limits
|
||||||
|
- Support full timestamps in logs
|
||||||
|
### Changed
|
||||||
|
- Faster implementation of GOST R 34.11 hash
|
||||||
|
- Reject routers with RSA signtures
|
||||||
|
- Reload config and shudown from Windows GUI
|
||||||
|
- Update tunnels address(destination) without restart
|
||||||
|
### Fixed
|
||||||
|
- BOB crashes if destination is not set
|
||||||
|
- Correct SAM tunnel name
|
||||||
|
- QT GUI issues
|
||||||
|
|
||||||
|
## [2.15.0] - 2017-08-17
|
||||||
|
### Added
|
||||||
|
- QT GUI
|
||||||
|
- Ability to add and remove I2P tunnels without restart
|
||||||
|
- Ability to disable SOCKS outproxy option
|
||||||
|
### Changed
|
||||||
|
- Strip-out Accept-* hedaers in HTTP proxy
|
||||||
|
- Don't run peer test if nat=false
|
||||||
|
- Separate output of NTCP and SSU sessions in Transports tab
|
||||||
|
### Fixed
|
||||||
|
- Handle lines with comments in hosts.txt file for address book
|
||||||
|
- Run router with empty netdb for testnet
|
||||||
|
- Skip expired introducers by iexp
|
||||||
|
|
||||||
|
## [2.14.0] - 2017-06-01
|
||||||
|
### Added
|
||||||
|
- Transit traffic bandwidth limitation
|
||||||
|
- NTCP connections through HTTP and SOCKS proxies
|
||||||
|
- Ability to disable address helper for HTTP proxy
|
||||||
|
### Changed
|
||||||
|
- Reseed servers list
|
||||||
|
- Minimal required version is 4.0 for Android
|
||||||
|
### Fixed
|
||||||
|
- Ignore comments in addressbook feed
|
||||||
|
|
||||||
|
## [2.13.0] - 2017-04-06
|
||||||
|
### Added
|
||||||
|
- Persist local destination's tags
|
||||||
|
- GOST signature types 9 and 10
|
||||||
|
- Exploratory tunnels configuration
|
||||||
|
### Changed
|
||||||
|
- Reseed servers list
|
||||||
|
- Inactive NTCP sockets get closed faster
|
||||||
|
- Some EdDSA speed up
|
||||||
|
### Fixed
|
||||||
|
- Multiple acceptors for SAM
|
||||||
|
- Follow on data after STREAM CREATE for SAM
|
||||||
|
- Memory leaks
|
||||||
|
|
||||||
|
## [2.12.0] - 2017-02-14
|
||||||
|
### Added
|
||||||
|
- Additional HTTP and SOCKS proxy tunnels
|
||||||
|
- Reseed from ZIP archive
|
||||||
|
- Some stats in a main window for Windows version
|
||||||
|
### Changed
|
||||||
|
- Reseed servers list
|
||||||
|
- MTU of 1488 for ipv6
|
||||||
|
- Android and Mac OS X versions use OpenSSL 1.1
|
||||||
|
- New logo for Android
|
||||||
|
### Fixed
|
||||||
|
- Multiple memory leaks
|
||||||
|
- Incomptibility of some EdDSA private keys with Java
|
||||||
|
- Clock skew for Windows XP
|
||||||
|
- Occasional crashes with I2PSnark
|
||||||
|
|
||||||
|
## [2.11.0] - 2016-12-18
|
||||||
|
### Added
|
||||||
|
- Websockets support
|
||||||
|
- Reseed through a floodfill
|
||||||
|
- Tunnel configuration for HTTP and SOCKS proxy
|
||||||
|
- Zero-hops tunnels for destinations
|
||||||
|
- Multiple acceptors for SAM
|
||||||
|
### Changed
|
||||||
|
- Reseed servers list
|
||||||
|
- DHT uses AVX if applicable
|
||||||
|
- New logo
|
||||||
|
- LeaseSet lookups
|
||||||
|
### Fixed
|
||||||
|
- HTTP Proxy connection reset for Windows
|
||||||
|
- Crash upon SAM session termination
|
||||||
|
- Can't connect to a destination for a longer time after restart
|
||||||
|
- Mass packet loss for UDP tunnels
|
||||||
|
|
||||||
|
## [2.10.2] - 2016-12-04
|
||||||
|
### Fixed
|
||||||
|
- Fixes UPnP discovery bug, producing excessive CPU usage
|
||||||
|
- Fixes sudden SSU thread stop for Windows.
|
||||||
|
|
||||||
## [2.10.1] - 2016-11-07
|
## [2.10.1] - 2016-11-07
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fixed some performance issues for Windows and Android
|
- Fixed some performance issues for Windows and Android
|
||||||
@@ -9,12 +789,12 @@
|
|||||||
### Added
|
### Added
|
||||||
- Datagram i2p tunnels
|
- Datagram i2p tunnels
|
||||||
- Unique local addresses for server tunnels
|
- Unique local addresses for server tunnels
|
||||||
- Configurable list of reseed servers and initial addressbook
|
- Configurable list of reseed servers and initial addressbook
|
||||||
- Configurable netid
|
- Configurable netid
|
||||||
- Initial iOS support
|
- Initial iOS support
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Reduced file descriptiors usage
|
- Reduced file descriptors usage
|
||||||
- Strict reseed checks enabled by default
|
- Strict reseed checks enabled by default
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
@@ -50,12 +830,12 @@
|
|||||||
- Configurable limit of transit tunnels
|
- Configurable limit of transit tunnels
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Speed-up of assymetric crypto for non-x64 platforms
|
- Speed-up of asymmetric crypto for non-x64 platforms
|
||||||
- Refactoring of web-console
|
- Refactoring of web-console
|
||||||
|
|
||||||
## [2.6.0] - 2016-03-31
|
## [2.6.0] - 2016-03-31
|
||||||
### Added
|
### Added
|
||||||
- Gracefull shutdown on SIGINT
|
- Graceful shutdown on SIGINT
|
||||||
- Numeric bandwidth limits (was: by router class)
|
- Numeric bandwidth limits (was: by router class)
|
||||||
- Jumpservices in web-console
|
- Jumpservices in web-console
|
||||||
- Logging to syslog
|
- Logging to syslog
|
||||||
|
|||||||
@@ -1,602 +0,0 @@
|
|||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <boost/property_tree/ptree.hpp>
|
|
||||||
#include <boost/property_tree/ini_parser.hpp>
|
|
||||||
#include "Config.h"
|
|
||||||
#include "FS.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include "ClientContext.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
ClientContext context;
|
|
||||||
|
|
||||||
ClientContext::ClientContext (): m_SharedLocalDestination (nullptr),
|
|
||||||
m_HttpProxy (nullptr), m_SocksProxy (nullptr), m_SamBridge (nullptr),
|
|
||||||
m_BOBCommandChannel (nullptr), m_I2CPServer (nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientContext::~ClientContext ()
|
|
||||||
{
|
|
||||||
delete m_HttpProxy;
|
|
||||||
delete m_SocksProxy;
|
|
||||||
delete m_SamBridge;
|
|
||||||
delete m_BOBCommandChannel;
|
|
||||||
delete m_I2CPServer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientContext::Start ()
|
|
||||||
{
|
|
||||||
if (!m_SharedLocalDestination)
|
|
||||||
{
|
|
||||||
m_SharedLocalDestination = CreateNewLocalDestination (); // non-public, DSA
|
|
||||||
m_Destinations[m_SharedLocalDestination->GetIdentity ()->GetIdentHash ()] = m_SharedLocalDestination;
|
|
||||||
m_SharedLocalDestination->Start ();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
m_AddressBook.Start ();
|
|
||||||
|
|
||||||
std::shared_ptr<ClientDestination> localDestination;
|
|
||||||
bool httproxy; i2p::config::GetOption("httpproxy.enabled", httproxy);
|
|
||||||
if (httproxy) {
|
|
||||||
std::string httpProxyKeys; i2p::config::GetOption("httpproxy.keys", httpProxyKeys);
|
|
||||||
std::string httpProxyAddr; i2p::config::GetOption("httpproxy.address", httpProxyAddr);
|
|
||||||
uint16_t httpProxyPort; i2p::config::GetOption("httpproxy.port", httpProxyPort);
|
|
||||||
LogPrint(eLogInfo, "Clients: starting HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort);
|
|
||||||
if (httpProxyKeys.length () > 0)
|
|
||||||
{
|
|
||||||
i2p::data::PrivateKeys keys;
|
|
||||||
if(LoadPrivateKeys (keys, httpProxyKeys, i2p::data::SIGNING_KEY_TYPE_DSA_SHA1))
|
|
||||||
{
|
|
||||||
std::map<std::string, std::string> params;
|
|
||||||
ReadI2CPOptionsFromConfig ("httpproxy.", params);
|
|
||||||
localDestination = CreateNewLocalDestination (keys, false, ¶ms);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint(eLogError, "Clients: failed to load HTTP Proxy key");
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
m_HttpProxy = new i2p::proxy::HTTPProxy(httpProxyAddr, httpProxyPort, localDestination);
|
|
||||||
m_HttpProxy->Start();
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
LogPrint(eLogError, "Clients: Exception in HTTP Proxy: ", e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
localDestination = nullptr;
|
|
||||||
bool socksproxy; i2p::config::GetOption("socksproxy.enabled", socksproxy);
|
|
||||||
if (socksproxy)
|
|
||||||
{
|
|
||||||
std::string socksProxyKeys; i2p::config::GetOption("socksproxy.keys", socksProxyKeys);
|
|
||||||
std::string socksProxyAddr; i2p::config::GetOption("socksproxy.address", socksProxyAddr);
|
|
||||||
uint16_t socksProxyPort; i2p::config::GetOption("socksproxy.port", socksProxyPort);
|
|
||||||
std::string socksOutProxyAddr; i2p::config::GetOption("socksproxy.outproxy", socksOutProxyAddr);
|
|
||||||
uint16_t socksOutProxyPort; i2p::config::GetOption("socksproxy.outproxyport", socksOutProxyPort);
|
|
||||||
LogPrint(eLogInfo, "Clients: starting SOCKS Proxy at ", socksProxyAddr, ":", socksProxyPort);
|
|
||||||
if (socksProxyKeys.length () > 0)
|
|
||||||
{
|
|
||||||
i2p::data::PrivateKeys keys;
|
|
||||||
if (LoadPrivateKeys (keys, socksProxyKeys, i2p::data::SIGNING_KEY_TYPE_DSA_SHA1))
|
|
||||||
{
|
|
||||||
std::map<std::string, std::string> params;
|
|
||||||
ReadI2CPOptionsFromConfig ("socksproxy.", params);
|
|
||||||
localDestination = CreateNewLocalDestination (keys, false, ¶ms);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint(eLogError, "Clients: failed to load SOCKS Proxy key");
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
m_SocksProxy = new i2p::proxy::SOCKSProxy(socksProxyAddr, socksProxyPort, socksOutProxyAddr, socksOutProxyPort, localDestination);
|
|
||||||
m_SocksProxy->Start();
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
LogPrint(eLogError, "Clients: Exception in SOCKS Proxy: ", e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// I2P tunnels
|
|
||||||
ReadTunnels ();
|
|
||||||
|
|
||||||
// SAM
|
|
||||||
bool sam; i2p::config::GetOption("sam.enabled", sam);
|
|
||||||
if (sam) {
|
|
||||||
std::string samAddr; i2p::config::GetOption("sam.address", samAddr);
|
|
||||||
uint16_t samPort; i2p::config::GetOption("sam.port", samPort);
|
|
||||||
LogPrint(eLogInfo, "Clients: starting SAM bridge at ", samAddr, ":", samPort);
|
|
||||||
try {
|
|
||||||
m_SamBridge = new SAMBridge (samAddr, samPort);
|
|
||||||
m_SamBridge->Start ();
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
LogPrint(eLogError, "Clients: Exception in SAM bridge: ", e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BOB
|
|
||||||
bool bob; i2p::config::GetOption("bob.enabled", bob);
|
|
||||||
if (bob) {
|
|
||||||
std::string bobAddr; i2p::config::GetOption("bob.address", bobAddr);
|
|
||||||
uint16_t bobPort; i2p::config::GetOption("bob.port", bobPort);
|
|
||||||
LogPrint(eLogInfo, "Clients: starting BOB command channel at ", bobAddr, ":", bobPort);
|
|
||||||
try {
|
|
||||||
m_BOBCommandChannel = new BOBCommandChannel (bobAddr, bobPort);
|
|
||||||
m_BOBCommandChannel->Start ();
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
LogPrint(eLogError, "Clients: Exception in BOB bridge: ", e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 ();
|
|
||||||
|
|
||||||
// start UDP cleanup
|
|
||||||
if (!m_ServerForwards.empty ())
|
|
||||||
{
|
|
||||||
m_CleanupUDPTimer.reset (new boost::asio::deadline_timer(m_SharedLocalDestination->GetService ()));
|
|
||||||
ScheduleCleanupUDP();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientContext::Stop ()
|
|
||||||
{
|
|
||||||
if (m_HttpProxy)
|
|
||||||
{
|
|
||||||
LogPrint(eLogInfo, "Clients: stopping HTTP Proxy");
|
|
||||||
m_HttpProxy->Stop();
|
|
||||||
delete m_HttpProxy;
|
|
||||||
m_HttpProxy = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_SocksProxy)
|
|
||||||
{
|
|
||||||
LogPrint(eLogInfo, "Clients: stopping SOCKS Proxy");
|
|
||||||
m_SocksProxy->Stop();
|
|
||||||
delete m_SocksProxy;
|
|
||||||
m_SocksProxy = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& it: m_ClientTunnels)
|
|
||||||
{
|
|
||||||
LogPrint(eLogInfo, "Clients: stopping I2P client tunnel on port ", it.first);
|
|
||||||
it.second->Stop ();
|
|
||||||
}
|
|
||||||
m_ClientTunnels.clear ();
|
|
||||||
|
|
||||||
for (auto& it: m_ServerTunnels)
|
|
||||||
{
|
|
||||||
LogPrint(eLogInfo, "Clients: stopping I2P server tunnel");
|
|
||||||
it.second->Stop ();
|
|
||||||
}
|
|
||||||
m_ServerTunnels.clear ();
|
|
||||||
|
|
||||||
if (m_SamBridge)
|
|
||||||
{
|
|
||||||
LogPrint(eLogInfo, "Clients: stopping SAM bridge");
|
|
||||||
m_SamBridge->Stop ();
|
|
||||||
delete m_SamBridge;
|
|
||||||
m_SamBridge = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_BOBCommandChannel)
|
|
||||||
{
|
|
||||||
LogPrint(eLogInfo, "Clients: stopping BOB command channel");
|
|
||||||
m_BOBCommandChannel->Stop ();
|
|
||||||
delete m_BOBCommandChannel;
|
|
||||||
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 ();
|
|
||||||
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_ForwardsMutex);
|
|
||||||
m_ServerForwards.clear();
|
|
||||||
m_ClientForwards.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_CleanupUDPTimer)
|
|
||||||
{
|
|
||||||
m_CleanupUDPTimer->cancel ();
|
|
||||||
m_CleanupUDPTimer = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& it: m_Destinations)
|
|
||||||
it.second->Stop ();
|
|
||||||
m_Destinations.clear ();
|
|
||||||
m_SharedLocalDestination = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientContext::ReloadConfig ()
|
|
||||||
{
|
|
||||||
std::string config; i2p::config::GetOption("conf", config);
|
|
||||||
i2p::config::ParseConfig(config);
|
|
||||||
Stop();
|
|
||||||
Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClientContext::LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, i2p::data::SigningKeyType sigType)
|
|
||||||
{
|
|
||||||
bool success = true;
|
|
||||||
std::string fullPath = i2p::fs::DataDirPath (filename);
|
|
||||||
std::ifstream s(fullPath, std::ifstream::binary);
|
|
||||||
if (s.is_open ())
|
|
||||||
{
|
|
||||||
s.seekg (0, std::ios::end);
|
|
||||||
size_t len = s.tellg();
|
|
||||||
s.seekg (0, std::ios::beg);
|
|
||||||
uint8_t * buf = new uint8_t[len];
|
|
||||||
s.read ((char *)buf, len);
|
|
||||||
if(!keys.FromBuffer (buf, len))
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Clients: failed to load keyfile ", filename);
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogInfo, "Clients: Local address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " loaded");
|
|
||||||
delete[] buf;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Clients: can't open file ", fullPath, " Creating new one with signature type ", sigType);
|
|
||||||
keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType);
|
|
||||||
std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out);
|
|
||||||
size_t len = keys.GetFullLen ();
|
|
||||||
uint8_t * buf = new uint8_t[len];
|
|
||||||
len = keys.ToBuffer (buf, len);
|
|
||||||
f.write ((char *)buf, len);
|
|
||||||
delete[] buf;
|
|
||||||
|
|
||||||
LogPrint (eLogInfo, "Clients: New private keys file ", fullPath, " for ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " created");
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<DatagramSessionInfo> > ClientContext::GetForwardInfosFor(const i2p::data::IdentHash & destination)
|
|
||||||
{
|
|
||||||
std::vector<std::shared_ptr<DatagramSessionInfo> > infos;
|
|
||||||
std::lock_guard<std::mutex> lock(m_ForwardsMutex);
|
|
||||||
for(const auto & c : m_ClientForwards)
|
|
||||||
{
|
|
||||||
if (c.second->IsLocalDestination(destination))
|
|
||||||
{
|
|
||||||
for (auto & i : c.second->GetSessions()) infos.push_back(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(const auto & s : m_ServerForwards)
|
|
||||||
{
|
|
||||||
if(std::get<0>(s.first) == destination)
|
|
||||||
{
|
|
||||||
for( auto & i : s.second->GetSessions()) infos.push_back(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return infos;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType,
|
|
||||||
const std::map<std::string, std::string> * params)
|
|
||||||
{
|
|
||||||
i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType);
|
|
||||||
auto localDestination = std::make_shared<ClientDestination> (keys, isPublic, params);
|
|
||||||
std::unique_lock<std::mutex> l(m_DestinationsMutex);
|
|
||||||
m_Destinations[localDestination->GetIdentHash ()] = localDestination;
|
|
||||||
localDestination->Start ();
|
|
||||||
return localDestination;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientContext::DeleteLocalDestination (std::shared_ptr<ClientDestination> destination)
|
|
||||||
{
|
|
||||||
if (!destination) return;
|
|
||||||
auto it = m_Destinations.find (destination->GetIdentHash ());
|
|
||||||
if (it != m_Destinations.end ())
|
|
||||||
{
|
|
||||||
auto d = it->second;
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_DestinationsMutex);
|
|
||||||
m_Destinations.erase (it);
|
|
||||||
}
|
|
||||||
d->Stop ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic,
|
|
||||||
const std::map<std::string, std::string> * params)
|
|
||||||
{
|
|
||||||
auto it = m_Destinations.find (keys.GetPublic ()->GetIdentHash ());
|
|
||||||
if (it != m_Destinations.end ())
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Clients: Local destination ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " exists");
|
|
||||||
if (!it->second->IsRunning ())
|
|
||||||
{
|
|
||||||
it->second->Start ();
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
auto localDestination = std::make_shared<ClientDestination> (keys, isPublic, params);
|
|
||||||
std::unique_lock<std::mutex> l(m_DestinationsMutex);
|
|
||||||
m_Destinations[keys.GetPublic ()->GetIdentHash ()] = localDestination;
|
|
||||||
localDestination->Start ();
|
|
||||||
return localDestination;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<ClientDestination> ClientContext::FindLocalDestination (const i2p::data::IdentHash& destination) const
|
|
||||||
{
|
|
||||||
auto it = m_Destinations.find (destination);
|
|
||||||
if (it != m_Destinations.end ())
|
|
||||||
return it->second;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Section, typename Type>
|
|
||||||
std::string ClientContext::GetI2CPOption (const Section& section, const std::string& name, const Type& value) const
|
|
||||||
{
|
|
||||||
return section.second.get (boost::property_tree::ptree::path_type (name, '/'), std::to_string (value));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Section>
|
|
||||||
void ClientContext::ReadI2CPOptions (const Section& section, std::map<std::string, std::string>& options) const
|
|
||||||
{
|
|
||||||
options[I2CP_PARAM_INBOUND_TUNNEL_LENGTH] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNEL_LENGTH, DEFAULT_INBOUND_TUNNEL_LENGTH);
|
|
||||||
options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, DEFAULT_OUTBOUND_TUNNEL_LENGTH);
|
|
||||||
options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, DEFAULT_INBOUND_TUNNELS_QUANTITY);
|
|
||||||
options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, DEFAULT_OUTBOUND_TUNNELS_QUANTITY);
|
|
||||||
options[I2CP_PARAM_TAGS_TO_SEND] = GetI2CPOption (section, I2CP_PARAM_TAGS_TO_SEND, DEFAULT_TAGS_TO_SEND);
|
|
||||||
options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MIN_TUNNEL_LATENCY, DEFAULT_MIN_TUNNEL_LATENCY);
|
|
||||||
options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MAX_TUNNEL_LATENCY, DEFAULT_MAX_TUNNEL_LATENCY);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientContext::ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const
|
|
||||||
{
|
|
||||||
std::string value;
|
|
||||||
if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNEL_LENGTH, value))
|
|
||||||
options[I2CP_PARAM_INBOUND_TUNNEL_LENGTH] = value;
|
|
||||||
if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, value))
|
|
||||||
options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = value;
|
|
||||||
if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, value))
|
|
||||||
options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = value;
|
|
||||||
if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, value))
|
|
||||||
options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = value;
|
|
||||||
if (i2p::config::GetOption(prefix + I2CP_PARAM_MIN_TUNNEL_LATENCY, value))
|
|
||||||
options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = value;
|
|
||||||
if (i2p::config::GetOption(prefix + I2CP_PARAM_MAX_TUNNEL_LATENCY, value))
|
|
||||||
options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientContext::ReadTunnels ()
|
|
||||||
{
|
|
||||||
boost::property_tree::ptree pt;
|
|
||||||
std::string tunConf; i2p::config::GetOption("tunconf", tunConf);
|
|
||||||
if (tunConf == "") {
|
|
||||||
// TODO: cleanup this in 2.8.0
|
|
||||||
tunConf = i2p::fs::DataDirPath ("tunnels.cfg");
|
|
||||||
if (i2p::fs::Exists(tunConf)) {
|
|
||||||
LogPrint(eLogWarning, "FS: please rename tunnels.cfg -> tunnels.conf here: ", tunConf);
|
|
||||||
} else {
|
|
||||||
tunConf = i2p::fs::DataDirPath ("tunnels.conf");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LogPrint(eLogDebug, "FS: tunnels config file: ", tunConf);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
boost::property_tree::read_ini (tunConf, pt);
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Clients: Can't read ", tunConf, ": ", ex.what ());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int numClientTunnels = 0, numServerTunnels = 0;
|
|
||||||
for (auto& section: pt)
|
|
||||||
{
|
|
||||||
std::string name = section.first;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
std::string type = section.second.get<std::string> (I2P_TUNNELS_SECTION_TYPE);
|
|
||||||
if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT || type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT)
|
|
||||||
{
|
|
||||||
// mandatory params
|
|
||||||
std::string dest = section.second.get<std::string> (I2P_CLIENT_TUNNEL_DESTINATION);
|
|
||||||
int port = section.second.get<int> (I2P_CLIENT_TUNNEL_PORT);
|
|
||||||
// optional params
|
|
||||||
std::string keys = section.second.get (I2P_CLIENT_TUNNEL_KEYS, "");
|
|
||||||
std::string address = section.second.get (I2P_CLIENT_TUNNEL_ADDRESS, "127.0.0.1");
|
|
||||||
int destinationPort = section.second.get (I2P_CLIENT_TUNNEL_DESTINATION_PORT, 0);
|
|
||||||
i2p::data::SigningKeyType sigType = section.second.get (I2P_CLIENT_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
|
|
||||||
// I2CP
|
|
||||||
std::map<std::string, std::string> options;
|
|
||||||
ReadI2CPOptions (section, options);
|
|
||||||
|
|
||||||
std::shared_ptr<ClientDestination> localDestination = nullptr;
|
|
||||||
if (keys.length () > 0)
|
|
||||||
{
|
|
||||||
i2p::data::PrivateKeys k;
|
|
||||||
if(LoadPrivateKeys (k, keys, sigType))
|
|
||||||
{
|
|
||||||
localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ());
|
|
||||||
if (!localDestination)
|
|
||||||
localDestination = CreateNewLocalDestination (k, type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT, &options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) {
|
|
||||||
// udp client
|
|
||||||
// TODO: hostnames
|
|
||||||
boost::asio::ip::udp::endpoint end(boost::asio::ip::address::from_string(address), port);
|
|
||||||
if (!localDestination)
|
|
||||||
{
|
|
||||||
localDestination = m_SharedLocalDestination;
|
|
||||||
}
|
|
||||||
auto clientTunnel = new I2PUDPClientTunnel(name, dest, end, localDestination, destinationPort);
|
|
||||||
if(m_ClientForwards.insert(std::make_pair(end, std::unique_ptr<I2PUDPClientTunnel>(clientTunnel))).second)
|
|
||||||
{
|
|
||||||
clientTunnel->Start();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint(eLogError, "Clients: I2P Client forward for endpoint ", end, " already exists");
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// tcp client
|
|
||||||
auto clientTunnel = new I2PClientTunnel (name, dest, address, port, localDestination, destinationPort);
|
|
||||||
if (m_ClientTunnels.insert (std::make_pair (clientTunnel->GetAcceptor ().local_endpoint (),
|
|
||||||
std::unique_ptr<I2PClientTunnel>(clientTunnel))).second)
|
|
||||||
{
|
|
||||||
clientTunnel->Start ();
|
|
||||||
numClientTunnels++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Clients: I2P client tunnel for endpoint ", clientTunnel->GetAcceptor ().local_endpoint (), " already exists");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER || type == I2P_TUNNELS_SECTION_TYPE_HTTP || type == I2P_TUNNELS_SECTION_TYPE_IRC || type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER)
|
|
||||||
{
|
|
||||||
// mandatory params
|
|
||||||
std::string host = section.second.get<std::string> (I2P_SERVER_TUNNEL_HOST);
|
|
||||||
int port = section.second.get<int> (I2P_SERVER_TUNNEL_PORT);
|
|
||||||
std::string keys = section.second.get<std::string> (I2P_SERVER_TUNNEL_KEYS);
|
|
||||||
// optional params
|
|
||||||
int inPort = section.second.get (I2P_SERVER_TUNNEL_INPORT, 0);
|
|
||||||
std::string accessList = section.second.get (I2P_SERVER_TUNNEL_ACCESS_LIST, "");
|
|
||||||
std::string hostOverride = section.second.get (I2P_SERVER_TUNNEL_HOST_OVERRIDE, "");
|
|
||||||
std::string webircpass = section.second.get<std::string> (I2P_SERVER_TUNNEL_WEBIRC_PASSWORD, "");
|
|
||||||
bool gzip = section.second.get (I2P_SERVER_TUNNEL_GZIP, true);
|
|
||||||
i2p::data::SigningKeyType sigType = section.second.get (I2P_SERVER_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
|
|
||||||
uint32_t maxConns = section.second.get(i2p::stream::I2CP_PARAM_STREAMING_MAX_CONNS_PER_MIN, i2p::stream::DEFAULT_MAX_CONNS_PER_MIN);
|
|
||||||
std::string address = section.second.get<std::string> (I2P_SERVER_TUNNEL_ADDRESS, "127.0.0.1");
|
|
||||||
|
|
||||||
// I2CP
|
|
||||||
std::map<std::string, std::string> options;
|
|
||||||
ReadI2CPOptions (section, options);
|
|
||||||
|
|
||||||
std::shared_ptr<ClientDestination> localDestination = nullptr;
|
|
||||||
i2p::data::PrivateKeys k;
|
|
||||||
if(!LoadPrivateKeys (k, keys, sigType))
|
|
||||||
continue;
|
|
||||||
localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ());
|
|
||||||
if (!localDestination)
|
|
||||||
localDestination = CreateNewLocalDestination (k, true, &options);
|
|
||||||
if (type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER)
|
|
||||||
{
|
|
||||||
// udp server tunnel
|
|
||||||
// TODO: hostnames
|
|
||||||
auto localAddress = boost::asio::ip::address::from_string(address);
|
|
||||||
boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(host), port);
|
|
||||||
I2PUDPServerTunnel * serverTunnel = new I2PUDPServerTunnel(name, localDestination, localAddress, endpoint, port);
|
|
||||||
std::lock_guard<std::mutex> lock(m_ForwardsMutex);
|
|
||||||
if(m_ServerForwards.insert(
|
|
||||||
std::make_pair(
|
|
||||||
std::make_pair(
|
|
||||||
localDestination->GetIdentHash(), port),
|
|
||||||
std::unique_ptr<I2PUDPServerTunnel>(serverTunnel))).second)
|
|
||||||
{
|
|
||||||
serverTunnel->Start();
|
|
||||||
LogPrint(eLogInfo, "Clients: I2P Server Forward created for UDP Endpoint ", host, ":", port, " bound on ", address, " for ",localDestination->GetIdentHash().ToBase32());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint(eLogError, "Clients: I2P Server Forward for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash()), "/", port, "already exists");
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
I2PServerTunnel * serverTunnel;
|
|
||||||
if (type == I2P_TUNNELS_SECTION_TYPE_HTTP)
|
|
||||||
serverTunnel = new I2PServerTunnelHTTP (name, host, port, localDestination, hostOverride, inPort, gzip);
|
|
||||||
else if (type == I2P_TUNNELS_SECTION_TYPE_IRC)
|
|
||||||
serverTunnel = new I2PServerTunnelIRC (name, host, port, localDestination, webircpass, inPort, gzip);
|
|
||||||
else // regular server tunnel by default
|
|
||||||
serverTunnel = new I2PServerTunnel (name, host, port, localDestination, inPort, gzip);
|
|
||||||
|
|
||||||
LogPrint(eLogInfo, "Clients: Set Max Conns To ", maxConns);
|
|
||||||
serverTunnel->SetMaxConnsPerMinute(maxConns);
|
|
||||||
|
|
||||||
|
|
||||||
if (accessList.length () > 0)
|
|
||||||
{
|
|
||||||
std::set<i2p::data::IdentHash> idents;
|
|
||||||
size_t pos = 0, comma;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
comma = accessList.find (',', pos);
|
|
||||||
i2p::data::IdentHash ident;
|
|
||||||
ident.FromBase32 (accessList.substr (pos, comma != std::string::npos ? comma - pos : std::string::npos));
|
|
||||||
idents.insert (ident);
|
|
||||||
pos = comma + 1;
|
|
||||||
}
|
|
||||||
while (comma != std::string::npos);
|
|
||||||
serverTunnel->SetAccessList (idents);
|
|
||||||
}
|
|
||||||
if (m_ServerTunnels.insert (std::make_pair (
|
|
||||||
std::make_pair (localDestination->GetIdentHash (), inPort),
|
|
||||||
std::unique_ptr<I2PServerTunnel>(serverTunnel))).second)
|
|
||||||
{
|
|
||||||
serverTunnel->Start ();
|
|
||||||
numServerTunnels++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Clients: I2P server tunnel for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash ()), "/", inPort, " already exists");
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "Clients: Unknown section type=", type, " of ", name, " in ", tunConf);
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Clients: Can't read tunnel ", name, " params: ", ex.what ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LogPrint (eLogInfo, "Clients: ", numClientTunnels, " I2P client tunnels created");
|
|
||||||
LogPrint (eLogInfo, "Clients: ", numServerTunnels, " I2P server tunnels created");
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientContext::ScheduleCleanupUDP()
|
|
||||||
{
|
|
||||||
if (m_CleanupUDPTimer)
|
|
||||||
{
|
|
||||||
// schedule cleanup in 17 seconds
|
|
||||||
m_CleanupUDPTimer->expires_from_now (boost::posix_time::seconds (17));
|
|
||||||
m_CleanupUDPTimer->async_wait(std::bind(&ClientContext::CleanupUDP, this, std::placeholders::_1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientContext::CleanupUDP(const boost::system::error_code & ecode)
|
|
||||||
{
|
|
||||||
if(!ecode)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_ForwardsMutex);
|
|
||||||
for (auto & s : m_ServerForwards ) s.second->ExpireStale();
|
|
||||||
ScheduleCleanupUDP();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
122
ClientContext.h
122
ClientContext.h
@@ -1,122 +0,0 @@
|
|||||||
#ifndef CLIENT_CONTEXT_H__
|
|
||||||
#define CLIENT_CONTEXT_H__
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <mutex>
|
|
||||||
#include <memory>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include "Destination.h"
|
|
||||||
#include "I2PService.h"
|
|
||||||
#include "HTTPProxy.h"
|
|
||||||
#include "SOCKS.h"
|
|
||||||
#include "I2PTunnel.h"
|
|
||||||
#include "SAM.h"
|
|
||||||
#include "BOB.h"
|
|
||||||
#include "I2CP.h"
|
|
||||||
#include "AddressBook.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
const char I2P_TUNNELS_SECTION_TYPE[] = "type";
|
|
||||||
const char I2P_TUNNELS_SECTION_TYPE_CLIENT[] = "client";
|
|
||||||
const char I2P_TUNNELS_SECTION_TYPE_SERVER[] = "server";
|
|
||||||
const char I2P_TUNNELS_SECTION_TYPE_HTTP[] = "http";
|
|
||||||
const char I2P_TUNNELS_SECTION_TYPE_IRC[] = "irc";
|
|
||||||
const char I2P_TUNNELS_SECTION_TYPE_UDPCLIENT[] = "udpclient";
|
|
||||||
const char I2P_TUNNELS_SECTION_TYPE_UDPSERVER[] = "udpserver";
|
|
||||||
const char I2P_CLIENT_TUNNEL_PORT[] = "port";
|
|
||||||
const char I2P_CLIENT_TUNNEL_ADDRESS[] = "address";
|
|
||||||
const char I2P_CLIENT_TUNNEL_DESTINATION[] = "destination";
|
|
||||||
const char I2P_CLIENT_TUNNEL_KEYS[] = "keys";
|
|
||||||
const char I2P_CLIENT_TUNNEL_SIGNATURE_TYPE[] = "signaturetype";
|
|
||||||
const char I2P_CLIENT_TUNNEL_DESTINATION_PORT[] = "destinationport";
|
|
||||||
const char I2P_SERVER_TUNNEL_HOST[] = "host";
|
|
||||||
const char I2P_SERVER_TUNNEL_HOST_OVERRIDE[] = "hostoverride";
|
|
||||||
const char I2P_SERVER_TUNNEL_PORT[] = "port";
|
|
||||||
const char I2P_SERVER_TUNNEL_KEYS[] = "keys";
|
|
||||||
const char I2P_SERVER_TUNNEL_SIGNATURE_TYPE[] = "signaturetype";
|
|
||||||
const char I2P_SERVER_TUNNEL_INPORT[] = "inport";
|
|
||||||
const char I2P_SERVER_TUNNEL_ACCESS_LIST[] = "accesslist";
|
|
||||||
const char I2P_SERVER_TUNNEL_GZIP[] = "gzip";
|
|
||||||
const char I2P_SERVER_TUNNEL_WEBIRC_PASSWORD[] = "webircpassword";
|
|
||||||
const char I2P_SERVER_TUNNEL_ADDRESS[] = "address";
|
|
||||||
|
|
||||||
class ClientContext
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
ClientContext ();
|
|
||||||
~ClientContext ();
|
|
||||||
|
|
||||||
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
|
|
||||||
std::shared_ptr<ClientDestination> CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true,
|
|
||||||
const std::map<std::string, std::string> * params = nullptr);
|
|
||||||
void DeleteLocalDestination (std::shared_ptr<ClientDestination> destination);
|
|
||||||
std::shared_ptr<ClientDestination> FindLocalDestination (const i2p::data::IdentHash& destination) const;
|
|
||||||
bool LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
|
|
||||||
|
|
||||||
AddressBook& GetAddressBook () { return m_AddressBook; };
|
|
||||||
const SAMBridge * GetSAMBridge () const { return m_SamBridge; };
|
|
||||||
const I2CPServer * GetI2CPServer () const { return m_I2CPServer; };
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<DatagramSessionInfo> > GetForwardInfosFor(const i2p::data::IdentHash & destination);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void ReadTunnels ();
|
|
||||||
template<typename Section, typename Type>
|
|
||||||
std::string GetI2CPOption (const Section& section, const std::string& name, const Type& value) const;
|
|
||||||
template<typename Section>
|
|
||||||
void ReadI2CPOptions (const Section& section, std::map<std::string, std::string>& options) const;
|
|
||||||
void ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const;
|
|
||||||
|
|
||||||
void CleanupUDP(const boost::system::error_code & ecode);
|
|
||||||
void ScheduleCleanupUDP();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::mutex m_DestinationsMutex;
|
|
||||||
std::map<i2p::data::IdentHash, std::shared_ptr<ClientDestination> > m_Destinations;
|
|
||||||
std::shared_ptr<ClientDestination> m_SharedLocalDestination;
|
|
||||||
|
|
||||||
AddressBook m_AddressBook;
|
|
||||||
|
|
||||||
i2p::proxy::HTTPProxy * m_HttpProxy;
|
|
||||||
i2p::proxy::SOCKSProxy * m_SocksProxy;
|
|
||||||
std::map<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
|
|
||||||
|
|
||||||
std::mutex m_ForwardsMutex;
|
|
||||||
std::map<boost::asio::ip::udp::endpoint, std::unique_ptr<I2PUDPClientTunnel> > m_ClientForwards; // local endpoint -> udp tunnel
|
|
||||||
std::map<std::pair<i2p::data::IdentHash, int>, std::unique_ptr<I2PUDPServerTunnel> > m_ServerForwards; // <destination,port> -> udp tunnel
|
|
||||||
|
|
||||||
SAMBridge * m_SamBridge;
|
|
||||||
BOBCommandChannel * m_BOBCommandChannel;
|
|
||||||
I2CPServer * m_I2CPServer;
|
|
||||||
|
|
||||||
std::unique_ptr<boost::asio::deadline_timer> m_CleanupUDPTimer;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// for HTTP
|
|
||||||
const decltype(m_Destinations)& GetDestinations () const { return m_Destinations; };
|
|
||||||
const decltype(m_ClientTunnels)& GetClientTunnels () const { return m_ClientTunnels; };
|
|
||||||
const decltype(m_ServerTunnels)& GetServerTunnels () const { return m_ServerTunnels; };
|
|
||||||
const decltype(m_ClientForwards)& GetClientForwards () const { return m_ClientForwards; }
|
|
||||||
const decltype(m_ServerForwards)& GetServerForwards () const { return m_ServerForwards; }
|
|
||||||
const i2p::proxy::HTTPProxy * GetHttpProxy () const { return m_HttpProxy; }
|
|
||||||
};
|
|
||||||
|
|
||||||
extern ClientContext context;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
276
Config.cpp
276
Config.cpp
@@ -1,276 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2013-2016, The PurpleI2P Project
|
|
||||||
*
|
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
|
||||||
*
|
|
||||||
* See full license text in LICENSE file at top of project tree
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include <boost/program_options/cmdline.hpp>
|
|
||||||
#include <boost/program_options/options_description.hpp>
|
|
||||||
#include <boost/program_options/parsers.hpp>
|
|
||||||
#include <boost/program_options/variables_map.hpp>
|
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "version.h"
|
|
||||||
|
|
||||||
using namespace boost::program_options;
|
|
||||||
|
|
||||||
namespace i2p {
|
|
||||||
namespace config {
|
|
||||||
options_description m_OptionsDesc;
|
|
||||||
variables_map m_Options;
|
|
||||||
|
|
||||||
void Init() {
|
|
||||||
|
|
||||||
options_description general("General options");
|
|
||||||
general.add_options()
|
|
||||||
("help", "Show this message")
|
|
||||||
("conf", value<std::string>()->default_value(""), "Path to main i2pd config file (default: try ~/.i2pd/i2pd.conf or /var/lib/i2pd/i2pd.conf)")
|
|
||||||
("tunconf", value<std::string>()->default_value(""), "Path to config with tunnels list and options (default: try ~/.i2pd/tunnels.conf or /var/lib/i2pd/tunnels.conf)")
|
|
||||||
("pidfile", value<std::string>()->default_value(""), "Path to pidfile (default: ~/i2pd/i2pd.pid or /var/lib/i2pd/i2pd.pid)")
|
|
||||||
("log", value<std::string>()->default_value(""), "Logs destination: stdout, file, syslog (stdout if not set)")
|
|
||||||
("logfile", value<std::string>()->default_value(""), "Path to logfile (stdout if not set, autodetect if daemon)")
|
|
||||||
("loglevel", value<std::string>()->default_value("info"), "Set the minimal level of log messages (debug, info, warn, error)")
|
|
||||||
("family", value<std::string>()->default_value(""), "Specify a family, router belongs to")
|
|
||||||
("datadir", value<std::string>()->default_value(""), "Path to storage of i2pd data (RI, keys, peer profiles, ...)")
|
|
||||||
("host", value<std::string>()->default_value("0.0.0.0"), "External IP")
|
|
||||||
("ifname", value<std::string>()->default_value(""), "Network interface to bind to")
|
|
||||||
("ifname4", value<std::string>()->default_value(""), "Network interface to bind to for ipv4")
|
|
||||||
("ifname6", value<std::string>()->default_value(""), "Network interface to bind to for ipv6")
|
|
||||||
("nat", value<bool>()->zero_tokens()->default_value(true), "Should we assume we are behind NAT?")
|
|
||||||
("port", value<uint16_t>()->default_value(0), "Port to listen for incoming connections (default: auto)")
|
|
||||||
("ipv4", value<bool>()->zero_tokens()->default_value(true), "Enable communication through ipv4")
|
|
||||||
("ipv6", value<bool>()->zero_tokens()->default_value(false), "Enable communication through ipv6")
|
|
||||||
("netid", value<int>()->default_value(I2PD_NET_ID), "Specify NetID. Main I2P is 2")
|
|
||||||
("daemon", value<bool>()->zero_tokens()->default_value(false), "Router will go to background after start")
|
|
||||||
("service", value<bool>()->zero_tokens()->default_value(false), "Router will use system folders like '/var/lib/i2pd'")
|
|
||||||
("notransit", value<bool>()->zero_tokens()->default_value(false), "Router will not accept transit tunnels at startup")
|
|
||||||
("floodfill", value<bool>()->zero_tokens()->default_value(false), "Router will be floodfill")
|
|
||||||
("bandwidth", value<std::string>()->default_value(""), "Bandwidth limit: integer in kbps or letters: L (32), O (256), P (2048), X (>9000)")
|
|
||||||
("ntcp", value<bool>()->zero_tokens()->default_value(true), "Enable NTCP transport")
|
|
||||||
("ssu", value<bool>()->zero_tokens()->default_value(true), "Enable SSU transport")
|
|
||||||
#ifdef _WIN32
|
|
||||||
("svcctl", value<std::string>()->default_value(""), "Windows service management ('install' or 'remove')")
|
|
||||||
("insomnia", value<bool>()->zero_tokens()->default_value(false), "Prevent system from sleeping")
|
|
||||||
("close", value<std::string>()->default_value("ask"), "Action on close: minimize, exit, ask") // TODO: add custom validator or something
|
|
||||||
#endif
|
|
||||||
;
|
|
||||||
|
|
||||||
options_description limits("Limits options");
|
|
||||||
limits.add_options()
|
|
||||||
("limits.coresize", value<uint32_t>()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)")
|
|
||||||
("limits.openfiles", value<uint16_t>()->default_value(0), "Maximum number of open files (0 - use system default)")
|
|
||||||
("limits.transittunnels", value<uint16_t>()->default_value(2500), "Maximum active transit sessions (default:2500)")
|
|
||||||
;
|
|
||||||
|
|
||||||
options_description httpserver("HTTP Server options");
|
|
||||||
httpserver.add_options()
|
|
||||||
("http.enabled", value<bool>()->default_value(true), "Enable or disable webconsole")
|
|
||||||
("http.address", value<std::string>()->default_value("127.0.0.1"), "Webconsole listen address")
|
|
||||||
("http.port", value<uint16_t>()->default_value(7070), "Webconsole listen port")
|
|
||||||
("http.auth", value<bool>()->default_value(false), "Enable Basic HTTP auth for webconsole")
|
|
||||||
("http.user", value<std::string>()->default_value("i2pd"), "Username for basic auth")
|
|
||||||
("http.pass", value<std::string>()->default_value(""), "Password for basic auth (default: random, see logs)")
|
|
||||||
;
|
|
||||||
|
|
||||||
options_description httpproxy("HTTP Proxy options");
|
|
||||||
httpproxy.add_options()
|
|
||||||
("httpproxy.enabled", value<bool>()->default_value(true), "Enable or disable HTTP Proxy")
|
|
||||||
("httpproxy.address", value<std::string>()->default_value("127.0.0.1"), "HTTP Proxy listen address")
|
|
||||||
("httpproxy.port", value<uint16_t>()->default_value(4444), "HTTP Proxy listen port")
|
|
||||||
("httpproxy.keys", value<std::string>()->default_value(""), "File to persist HTTP Proxy keys")
|
|
||||||
("httpproxy.inbound.length", value<std::string>()->default_value("3"), "HTTP proxy inbound tunnel length")
|
|
||||||
("httpproxy.outbound.length", value<std::string>()->default_value("3"), "HTTP proxy outbound tunnel length")
|
|
||||||
("httpproxy.inbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy inbound tunnels quantity")
|
|
||||||
("httpproxy.outbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy outbound tunnels quantity")
|
|
||||||
("httpproxy.latency.min", value<std::string>()->default_value("0"), "HTTP proxy min latency for tunnels")
|
|
||||||
("httpproxy.latency.max", value<std::string>()->default_value("0"), "HTTP proxy max latency for tunnels")
|
|
||||||
;
|
|
||||||
|
|
||||||
options_description socksproxy("SOCKS Proxy options");
|
|
||||||
socksproxy.add_options()
|
|
||||||
("socksproxy.enabled", value<bool>()->default_value(true), "Enable or disable SOCKS Proxy")
|
|
||||||
("socksproxy.address", value<std::string>()->default_value("127.0.0.1"), "SOCKS Proxy listen address")
|
|
||||||
("socksproxy.port", value<uint16_t>()->default_value(4447), "SOCKS Proxy listen port")
|
|
||||||
("socksproxy.keys", value<std::string>()->default_value(""), "File to persist SOCKS Proxy keys")
|
|
||||||
("socksproxy.inbound.length", value<std::string>()->default_value("3"), "SOCKS proxy inbound tunnel length")
|
|
||||||
("socksproxy.outbound.length", value<std::string>()->default_value("3"), "SOCKS proxy outbound tunnel length")
|
|
||||||
("socksproxy.inbound.quantity", value<std::string>()->default_value("5"), "SOCKS proxy inbound tunnels quantity")
|
|
||||||
("socksproxy.outbound.quantity", value<std::string>()->default_value("5"), "SOCKS proxy outbound tunnels quantity")
|
|
||||||
("socksproxy.latency.min", value<std::string>()->default_value("0"), "SOCKS proxy min latency for tunnels")
|
|
||||||
("socksproxy.latency.max", value<std::string>()->default_value("0"), "SOCKS proxy max latency for tunnels")
|
|
||||||
("socksproxy.outproxy", value<std::string>()->default_value("127.0.0.1"), "Upstream outproxy address for SOCKS Proxy")
|
|
||||||
("socksproxy.outproxyport", value<uint16_t>()->default_value(9050), "Upstream outproxy port for SOCKS Proxy")
|
|
||||||
;
|
|
||||||
|
|
||||||
options_description sam("SAM bridge options");
|
|
||||||
sam.add_options()
|
|
||||||
("sam.enabled", value<bool>()->default_value(false), "Enable or disable SAM Application bridge")
|
|
||||||
("sam.address", value<std::string>()->default_value("127.0.0.1"), "SAM listen address")
|
|
||||||
("sam.port", value<uint16_t>()->default_value(7656), "SAM listen port")
|
|
||||||
;
|
|
||||||
|
|
||||||
options_description bob("BOB options");
|
|
||||||
bob.add_options()
|
|
||||||
("bob.enabled", value<bool>()->default_value(false), "Enable or disable BOB command channel")
|
|
||||||
("bob.address", value<std::string>()->default_value("127.0.0.1"), "BOB listen address")
|
|
||||||
("bob.port", value<uint16_t>()->default_value(2827), "BOB listen port")
|
|
||||||
;
|
|
||||||
|
|
||||||
options_description i2cp("I2CP options");
|
|
||||||
i2cp.add_options()
|
|
||||||
("i2cp.enabled", value<bool>()->default_value(false), "Enable or disable I2CP")
|
|
||||||
("i2cp.address", value<std::string>()->default_value("127.0.0.1"), "I2CP listen address")
|
|
||||||
("i2cp.port", value<uint16_t>()->default_value(7654), "I2CP listen port")
|
|
||||||
;
|
|
||||||
|
|
||||||
options_description i2pcontrol("I2PControl options");
|
|
||||||
i2pcontrol.add_options()
|
|
||||||
("i2pcontrol.enabled", value<bool>()->default_value(false), "Enable or disable I2P Control Protocol")
|
|
||||||
("i2pcontrol.address", value<std::string>()->default_value("127.0.0.1"), "I2PCP listen address")
|
|
||||||
("i2pcontrol.port", value<uint16_t>()->default_value(7650), "I2PCP listen port")
|
|
||||||
("i2pcontrol.password", value<std::string>()->default_value("itoopie"), "I2PCP access password")
|
|
||||||
("i2pcontrol.cert", value<std::string>()->default_value("i2pcontrol.crt.pem"), "I2PCP connection cerificate")
|
|
||||||
("i2pcontrol.key", value<std::string>()->default_value("i2pcontrol.key.pem"), "I2PCP connection cerificate key")
|
|
||||||
;
|
|
||||||
|
|
||||||
bool upnp_default = false;
|
|
||||||
#if (defined(USE_UPNP) && (defined(WIN32_APP) || defined(ANDROID)))
|
|
||||||
upnp_default = true; // enable UPNP for windows GUI and android by default
|
|
||||||
#endif
|
|
||||||
options_description upnp("UPnP options");
|
|
||||||
upnp.add_options()
|
|
||||||
("upnp.enabled", value<bool>()->default_value(upnp_default), "Enable or disable UPnP: automatic port forwarding")
|
|
||||||
("upnp.name", value<std::string>()->default_value("I2Pd"), "Name i2pd appears in UPnP forwardings list")
|
|
||||||
;
|
|
||||||
|
|
||||||
options_description precomputation("Precomputation options");
|
|
||||||
precomputation.add_options()
|
|
||||||
("precomputation.elgamal",
|
|
||||||
#if defined(__x86_64__)
|
|
||||||
value<bool>()->default_value(false),
|
|
||||||
#else
|
|
||||||
value<bool>()->default_value(true),
|
|
||||||
#endif
|
|
||||||
"Enable or disable elgamal precomputation table")
|
|
||||||
;
|
|
||||||
|
|
||||||
options_description reseed("Reseed options");
|
|
||||||
reseed.add_options()
|
|
||||||
("reseed.verify", value<bool>()->default_value(false), "Verify .su3 signature")
|
|
||||||
("reseed.floodfill", value<std::string>()->default_value(""), "Path to router info of floodfill to reseed from")
|
|
||||||
("reseed.file", value<std::string>()->default_value(""), "Path to local .su3 file or HTTPS URL to reseed from")
|
|
||||||
("reseed.urls", value<std::string>()->default_value(
|
|
||||||
"https://reseed.i2p-projekt.de/,"
|
|
||||||
"https://i2p.mooo.com/netDb/,"
|
|
||||||
"https://netdb.i2p2.no/,"
|
|
||||||
"https://us.reseed.i2p2.no:444/,"
|
|
||||||
"https://uk.reseed.i2p2.no:444/,"
|
|
||||||
"https://i2p-0.manas.ca:8443/,"
|
|
||||||
"https://reseed.i2p.vzaws.com:8443/,"
|
|
||||||
"https://download.xxlspeed.com/,"
|
|
||||||
"https://reseed-ru.lngserv.ru/,"
|
|
||||||
"https://reseed.atomike.ninja/"
|
|
||||||
), "Reseed URLs, separated by comma")
|
|
||||||
;
|
|
||||||
|
|
||||||
options_description addressbook("AddressBook options");
|
|
||||||
addressbook.add_options()
|
|
||||||
("addressbook.defaulturl", value<std::string>()->default_value(
|
|
||||||
"http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt"
|
|
||||||
), "AddressBook subscription URL for initial setup")
|
|
||||||
("addressbook.subscriptions", value<std::string>()->default_value(""),
|
|
||||||
"AddressBook subscriptions URLs, separated by comma");
|
|
||||||
|
|
||||||
options_description trust("Trust options");
|
|
||||||
trust.add_options()
|
|
||||||
("trust.enabled", value<bool>()->default_value(false), "Enable explicit trust options")
|
|
||||||
("trust.family", value<std::string>()->default_value(""), "Router Familiy to trust for first hops")
|
|
||||||
("trust.routers", value<std::string>()->default_value(""), "Only Connect to these routers")
|
|
||||||
("trust.hidden", value<bool>()->default_value(false), "Should we hide our router from other routers?");
|
|
||||||
|
|
||||||
options_description websocket("Websocket Options");
|
|
||||||
websocket.add_options()
|
|
||||||
("websockets.enabled", value<bool>()->default_value(false), "enable websocket server")
|
|
||||||
("websockets.address", value<std::string>()->default_value("127.0.0.1"), "address to bind websocket server on")
|
|
||||||
("websockets.port", value<uint16_t>()->default_value(7666), "port to bind websocket server on");
|
|
||||||
|
|
||||||
m_OptionsDesc
|
|
||||||
.add(general)
|
|
||||||
.add(limits)
|
|
||||||
.add(httpserver)
|
|
||||||
.add(httpproxy)
|
|
||||||
.add(socksproxy)
|
|
||||||
.add(sam)
|
|
||||||
.add(bob)
|
|
||||||
.add(i2cp)
|
|
||||||
.add(i2pcontrol)
|
|
||||||
.add(upnp)
|
|
||||||
.add(precomputation)
|
|
||||||
.add(reseed)
|
|
||||||
.add(addressbook)
|
|
||||||
.add(trust)
|
|
||||||
.add(websocket)
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ParseCmdline(int argc, char* argv[]) {
|
|
||||||
try {
|
|
||||||
auto style = boost::program_options::command_line_style::unix_style
|
|
||||||
| boost::program_options::command_line_style::allow_long_disguise;
|
|
||||||
style &= ~ boost::program_options::command_line_style::allow_guessing;
|
|
||||||
store(parse_command_line(argc, argv, m_OptionsDesc, style), m_Options);
|
|
||||||
} catch (boost::program_options::error& e) {
|
|
||||||
std::cerr << "args: " << e.what() << std::endl;
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_Options.count("help") || m_Options.count("h")) {
|
|
||||||
std::cout << "i2pd version " << I2PD_VERSION << " (" << I2P_VERSION << ")" << std::endl;
|
|
||||||
std::cout << m_OptionsDesc;
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ParseConfig(const std::string& path) {
|
|
||||||
if (path == "") return;
|
|
||||||
|
|
||||||
std::ifstream config(path, std::ios::in);
|
|
||||||
|
|
||||||
if (!config.is_open())
|
|
||||||
{
|
|
||||||
std::cerr << "missing/unreadable config file: " << path << std::endl;
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
store(boost::program_options::parse_config_file(config, m_OptionsDesc), m_Options);
|
|
||||||
}
|
|
||||||
catch (boost::program_options::error& e)
|
|
||||||
{
|
|
||||||
std::cerr << e.what() << std::endl;
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void Finalize() {
|
|
||||||
notify(m_Options);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsDefault(const char *name) {
|
|
||||||
if (!m_Options.count(name))
|
|
||||||
throw "try to check non-existent option";
|
|
||||||
|
|
||||||
if (m_Options[name].defaulted())
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} // namespace config
|
|
||||||
} // namespace i2p
|
|
||||||
113
Config.h
113
Config.h
@@ -1,113 +0,0 @@
|
|||||||
#ifndef CONFIG_H
|
|
||||||
#define CONFIG_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <boost/program_options/options_description.hpp>
|
|
||||||
#include <boost/program_options/variables_map.hpp>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Functions to parse and store i2pd parameters
|
|
||||||
*
|
|
||||||
* General usage flow:
|
|
||||||
* Init() -- early as possible
|
|
||||||
* ParseCmdline() -- somewhere close to main()
|
|
||||||
* ParseConfig() -- after detecting path to config
|
|
||||||
* Finalize() -- right after all Parse*() functions called
|
|
||||||
* GetOption() -- may be called after Finalize()
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace i2p {
|
|
||||||
namespace config {
|
|
||||||
extern boost::program_options::variables_map m_Options;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initialize list of acceptable parameters
|
|
||||||
*
|
|
||||||
* Should be called before any Parse* functions.
|
|
||||||
*/
|
|
||||||
void Init();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Parse cmdline parameters, and show help if requested
|
|
||||||
* @param argc Cmdline arguments count, should be passed from main().
|
|
||||||
* @param argv Cmdline parameters array, should be passed from main()
|
|
||||||
*
|
|
||||||
* If --help is given in parameters, shows it's list with description
|
|
||||||
* terminates the program with exitcode 0.
|
|
||||||
*
|
|
||||||
* In case of parameter misuse boost throws an exception.
|
|
||||||
* We internally handle type boost::program_options::unknown_option,
|
|
||||||
* and then terminate program with exitcode 1.
|
|
||||||
*
|
|
||||||
* Other exceptions will be passed to higher level.
|
|
||||||
*/
|
|
||||||
void ParseCmdline(int argc, char* argv[]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Load and parse given config file
|
|
||||||
* @param path Path to config file
|
|
||||||
*
|
|
||||||
* If error occured when opening file path is points to,
|
|
||||||
* we show the error message and terminate program.
|
|
||||||
*
|
|
||||||
* In case of parameter misuse boost throws an exception.
|
|
||||||
* We internally handle type boost::program_options::unknown_option,
|
|
||||||
* and then terminate program with exitcode 1.
|
|
||||||
*
|
|
||||||
* Other exceptions will be passed to higher level.
|
|
||||||
*/
|
|
||||||
void ParseConfig(const std::string& path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Used to combine options from cmdline, config and default values
|
|
||||||
*/
|
|
||||||
void Finalize();
|
|
||||||
|
|
||||||
/* @brief Accessor to parameters by name
|
|
||||||
* @param name Name of the requested parameter
|
|
||||||
* @param value Variable where to store option
|
|
||||||
* @return this function returns false if parameter not found
|
|
||||||
*
|
|
||||||
* Example: uint16_t port; GetOption("sam.port", port);
|
|
||||||
*/
|
|
||||||
template<typename T>
|
|
||||||
bool GetOption(const char *name, T& value) {
|
|
||||||
if (!m_Options.count(name))
|
|
||||||
return false;
|
|
||||||
value = m_Options[name].as<T>();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
bool GetOption(const std::string& name, T& value)
|
|
||||||
{
|
|
||||||
return GetOption (name.c_str (), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set value of given parameter
|
|
||||||
* @param name Name of settable parameter
|
|
||||||
* @param value New parameter value
|
|
||||||
* @return true if value set up successful, false otherwise
|
|
||||||
*
|
|
||||||
* Example: uint16_t port = 2827; SetOption("bob.port", port);
|
|
||||||
*/
|
|
||||||
template<typename T>
|
|
||||||
bool SetOption(const char *name, const T& value) {
|
|
||||||
if (!m_Options.count(name))
|
|
||||||
return false;
|
|
||||||
m_Options.at(name).value() = value;
|
|
||||||
notify(m_Options);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check is value explicitly given or default
|
|
||||||
* @param name Name of checked parameter
|
|
||||||
* @return true if value set to default, false othervise
|
|
||||||
*/
|
|
||||||
bool IsDefault(const char *name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // CONFIG_H
|
|
||||||
825
Crypto.cpp
825
Crypto.cpp
@@ -1,825 +0,0 @@
|
|||||||
#include <string.h>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <mutex>
|
|
||||||
#include <memory>
|
|
||||||
#include <openssl/dh.h>
|
|
||||||
#include <openssl/md5.h>
|
|
||||||
#include <openssl/crypto.h>
|
|
||||||
#include "TunnelBase.h"
|
|
||||||
#include <openssl/ssl.h>
|
|
||||||
#include "Log.h"
|
|
||||||
#include "Crypto.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace crypto
|
|
||||||
{
|
|
||||||
const uint8_t elgp_[256]=
|
|
||||||
{
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
|
|
||||||
0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
|
|
||||||
0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
|
|
||||||
0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
|
|
||||||
0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
|
|
||||||
0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
|
|
||||||
0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
|
|
||||||
0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
|
|
||||||
0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
|
|
||||||
0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
|
|
||||||
0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
|
|
||||||
0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
|
|
||||||
0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
|
|
||||||
0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
|
|
||||||
0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
|
|
||||||
0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
|
||||||
};
|
|
||||||
|
|
||||||
const int elgg_ = 2;
|
|
||||||
|
|
||||||
const uint8_t dsap_[128]=
|
|
||||||
{
|
|
||||||
0x9c, 0x05, 0xb2, 0xaa, 0x96, 0x0d, 0x9b, 0x97, 0xb8, 0x93, 0x19, 0x63, 0xc9, 0xcc, 0x9e, 0x8c,
|
|
||||||
0x30, 0x26, 0xe9, 0xb8, 0xed, 0x92, 0xfa, 0xd0, 0xa6, 0x9c, 0xc8, 0x86, 0xd5, 0xbf, 0x80, 0x15,
|
|
||||||
0xfc, 0xad, 0xae, 0x31, 0xa0, 0xad, 0x18, 0xfa, 0xb3, 0xf0, 0x1b, 0x00, 0xa3, 0x58, 0xde, 0x23,
|
|
||||||
0x76, 0x55, 0xc4, 0x96, 0x4a, 0xfa, 0xa2, 0xb3, 0x37, 0xe9, 0x6a, 0xd3, 0x16, 0xb9, 0xfb, 0x1c,
|
|
||||||
0xc5, 0x64, 0xb5, 0xae, 0xc5, 0xb6, 0x9a, 0x9f, 0xf6, 0xc3, 0xe4, 0x54, 0x87, 0x07, 0xfe, 0xf8,
|
|
||||||
0x50, 0x3d, 0x91, 0xdd, 0x86, 0x02, 0xe8, 0x67, 0xe6, 0xd3, 0x5d, 0x22, 0x35, 0xc1, 0x86, 0x9c,
|
|
||||||
0xe2, 0x47, 0x9c, 0x3b, 0x9d, 0x54, 0x01, 0xde, 0x04, 0xe0, 0x72, 0x7f, 0xb3, 0x3d, 0x65, 0x11,
|
|
||||||
0x28, 0x5d, 0x4c, 0xf2, 0x95, 0x38, 0xd9, 0xe3, 0xb6, 0x05, 0x1f, 0x5b, 0x22, 0xcc, 0x1c, 0x93
|
|
||||||
};
|
|
||||||
|
|
||||||
const uint8_t dsaq_[20]=
|
|
||||||
{
|
|
||||||
0xa5, 0xdf, 0xc2, 0x8f, 0xef, 0x4c, 0xa1, 0xe2, 0x86, 0x74, 0x4c, 0xd8, 0xee, 0xd9, 0xd2, 0x9d,
|
|
||||||
0x68, 0x40, 0x46, 0xb7
|
|
||||||
};
|
|
||||||
|
|
||||||
const uint8_t dsag_[128]=
|
|
||||||
{
|
|
||||||
0x0c, 0x1f, 0x4d, 0x27, 0xd4, 0x00, 0x93, 0xb4, 0x29, 0xe9, 0x62, 0xd7, 0x22, 0x38, 0x24, 0xe0,
|
|
||||||
0xbb, 0xc4, 0x7e, 0x7c, 0x83, 0x2a, 0x39, 0x23, 0x6f, 0xc6, 0x83, 0xaf, 0x84, 0x88, 0x95, 0x81,
|
|
||||||
0x07, 0x5f, 0xf9, 0x08, 0x2e, 0xd3, 0x23, 0x53, 0xd4, 0x37, 0x4d, 0x73, 0x01, 0xcd, 0xa1, 0xd2,
|
|
||||||
0x3c, 0x43, 0x1f, 0x46, 0x98, 0x59, 0x9d, 0xda, 0x02, 0x45, 0x18, 0x24, 0xff, 0x36, 0x97, 0x52,
|
|
||||||
0x59, 0x36, 0x47, 0xcc, 0x3d, 0xdc, 0x19, 0x7d, 0xe9, 0x85, 0xe4, 0x3d, 0x13, 0x6c, 0xdc, 0xfc,
|
|
||||||
0x6b, 0xd5, 0x40, 0x9c, 0xd2, 0xf4, 0x50, 0x82, 0x11, 0x42, 0xa5, 0xe6, 0xf8, 0xeb, 0x1c, 0x3a,
|
|
||||||
0xb5, 0xd0, 0x48, 0x4b, 0x81, 0x29, 0xfc, 0xf1, 0x7b, 0xce, 0x4f, 0x7f, 0x33, 0x32, 0x1c, 0x3c,
|
|
||||||
0xb3, 0xdb, 0xb1, 0x4a, 0x90, 0x5e, 0x7b, 0x2b, 0x3e, 0x93, 0xbe, 0x47, 0x08, 0xcb, 0xcc, 0x82
|
|
||||||
};
|
|
||||||
|
|
||||||
const int rsae_ = 65537;
|
|
||||||
|
|
||||||
struct CryptoConstants
|
|
||||||
{
|
|
||||||
// DH/ElGamal
|
|
||||||
BIGNUM * elgp;
|
|
||||||
BIGNUM * elgg;
|
|
||||||
|
|
||||||
// DSA
|
|
||||||
BIGNUM * dsap;
|
|
||||||
BIGNUM * dsaq;
|
|
||||||
BIGNUM * dsag;
|
|
||||||
|
|
||||||
// RSA
|
|
||||||
BIGNUM * rsae;
|
|
||||||
|
|
||||||
CryptoConstants (const uint8_t * elgp_, int elgg_, const uint8_t * dsap_,
|
|
||||||
const uint8_t * dsaq_, const uint8_t * dsag_, int rsae_)
|
|
||||||
{
|
|
||||||
elgp = BN_new ();
|
|
||||||
BN_bin2bn (elgp_, 256, elgp);
|
|
||||||
elgg = BN_new ();
|
|
||||||
BN_set_word (elgg, elgg_);
|
|
||||||
dsap = BN_new ();
|
|
||||||
BN_bin2bn (dsap_, 128, dsap);
|
|
||||||
dsaq = BN_new ();
|
|
||||||
BN_bin2bn (dsaq_, 20, dsaq);
|
|
||||||
dsag = BN_new ();
|
|
||||||
BN_bin2bn (dsag_, 128, dsag);
|
|
||||||
rsae = BN_new ();
|
|
||||||
BN_set_word (rsae, rsae_);
|
|
||||||
}
|
|
||||||
|
|
||||||
~CryptoConstants ()
|
|
||||||
{
|
|
||||||
BN_free (elgp); BN_free (elgg); BN_free (dsap); BN_free (dsaq); BN_free (dsag); BN_free (rsae);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static const CryptoConstants& GetCryptoConstants ()
|
|
||||||
{
|
|
||||||
static CryptoConstants cryptoConstants (elgp_, elgg_, dsap_, dsaq_, dsag_, rsae_);
|
|
||||||
return cryptoConstants;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool bn2buf (const BIGNUM * bn, uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
int offset = len - BN_num_bytes (bn);
|
|
||||||
if (offset < 0) return false;
|
|
||||||
BN_bn2bin (bn, buf + offset);
|
|
||||||
memset (buf, 0, offset);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// RSA
|
|
||||||
#define rsae GetCryptoConstants ().rsae
|
|
||||||
const BIGNUM * GetRSAE ()
|
|
||||||
{
|
|
||||||
return rsae;
|
|
||||||
}
|
|
||||||
|
|
||||||
// DSA
|
|
||||||
#define dsap GetCryptoConstants ().dsap
|
|
||||||
#define dsaq GetCryptoConstants ().dsaq
|
|
||||||
#define dsag GetCryptoConstants ().dsag
|
|
||||||
DSA * CreateDSA ()
|
|
||||||
{
|
|
||||||
DSA * dsa = DSA_new ();
|
|
||||||
DSA_set0_pqg (dsa, BN_dup (dsap), BN_dup (dsaq), BN_dup (dsag));
|
|
||||||
DSA_set0_key (dsa, NULL, NULL);
|
|
||||||
return dsa;
|
|
||||||
}
|
|
||||||
|
|
||||||
// DH/ElGamal
|
|
||||||
|
|
||||||
const int ELGAMAL_SHORT_EXPONENT_NUM_BITS = 226;
|
|
||||||
const int ELGAMAL_SHORT_EXPONENT_NUM_BYTES = ELGAMAL_SHORT_EXPONENT_NUM_BITS/8+1;
|
|
||||||
const int ELGAMAL_FULL_EXPONENT_NUM_BITS = 2048;
|
|
||||||
const int ELGAMAL_FULL_EXPONENT_NUM_BYTES = ELGAMAL_FULL_EXPONENT_NUM_BITS/8;
|
|
||||||
|
|
||||||
#define elgp GetCryptoConstants ().elgp
|
|
||||||
#define elgg GetCryptoConstants ().elgg
|
|
||||||
|
|
||||||
static BN_MONT_CTX * g_MontCtx = nullptr;
|
|
||||||
static void PrecalculateElggTable (BIGNUM * table[][255], int len) // table is len's array of array of 255 bignums
|
|
||||||
{
|
|
||||||
if (len <= 0) return;
|
|
||||||
BN_CTX * ctx = BN_CTX_new ();
|
|
||||||
g_MontCtx = BN_MONT_CTX_new ();
|
|
||||||
BN_MONT_CTX_set (g_MontCtx, elgp, ctx);
|
|
||||||
auto montCtx = BN_MONT_CTX_new ();
|
|
||||||
BN_MONT_CTX_copy (montCtx, g_MontCtx);
|
|
||||||
for (int i = 0; i < len; i++)
|
|
||||||
{
|
|
||||||
table[i][0] = BN_new ();
|
|
||||||
if (!i)
|
|
||||||
BN_to_montgomery (table[0][0], elgg, montCtx, ctx);
|
|
||||||
else
|
|
||||||
BN_mod_mul_montgomery (table[i][0], table[i-1][254], table[i-1][0], montCtx, ctx);
|
|
||||||
for (int j = 1; j < 255; j++)
|
|
||||||
{
|
|
||||||
table[i][j] = BN_new ();
|
|
||||||
BN_mod_mul_montgomery (table[i][j], table[i][j-1], table[i][0], montCtx, ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BN_MONT_CTX_free (montCtx);
|
|
||||||
BN_CTX_free (ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void DestroyElggTable (BIGNUM * table[][255], int len)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < len; i++)
|
|
||||||
for (int j = 0; j < 255; j++)
|
|
||||||
{
|
|
||||||
BN_free (table[i][j]);
|
|
||||||
table[i][j] = nullptr;
|
|
||||||
}
|
|
||||||
BN_MONT_CTX_free (g_MontCtx);
|
|
||||||
}
|
|
||||||
|
|
||||||
static BIGNUM * ElggPow (const uint8_t * exp, int len, BIGNUM * table[][255], BN_CTX * ctx)
|
|
||||||
// exp is in Big Endian
|
|
||||||
{
|
|
||||||
if (len <= 0) return nullptr;
|
|
||||||
auto montCtx = BN_MONT_CTX_new ();
|
|
||||||
BN_MONT_CTX_copy (montCtx, g_MontCtx);
|
|
||||||
BIGNUM * res = nullptr;
|
|
||||||
for (int i = 0; i < len; i++)
|
|
||||||
{
|
|
||||||
if (res)
|
|
||||||
{
|
|
||||||
if (exp[i])
|
|
||||||
BN_mod_mul_montgomery (res, res, table[len-1-i][exp[i]-1], montCtx, ctx);
|
|
||||||
}
|
|
||||||
else if (exp[i])
|
|
||||||
res = BN_dup (table[len-i-1][exp[i]-1]);
|
|
||||||
}
|
|
||||||
if (res)
|
|
||||||
BN_from_montgomery (res, res, montCtx, ctx);
|
|
||||||
BN_MONT_CTX_free (montCtx);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BIGNUM * ElggPow (const BIGNUM * exp, BIGNUM * table[][255], BN_CTX * ctx)
|
|
||||||
{
|
|
||||||
auto len = BN_num_bytes (exp);
|
|
||||||
uint8_t * buf = new uint8_t[len];
|
|
||||||
BN_bn2bin (exp, buf);
|
|
||||||
auto ret = ElggPow (buf, len, table, ctx);
|
|
||||||
delete[] buf;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BIGNUM * (* g_ElggTable)[255] = nullptr;
|
|
||||||
|
|
||||||
// DH
|
|
||||||
|
|
||||||
DHKeys::DHKeys ()
|
|
||||||
{
|
|
||||||
m_DH = DH_new ();
|
|
||||||
DH_set0_pqg (m_DH, BN_dup (elgp), NULL, BN_dup (elgg));
|
|
||||||
DH_set0_key (m_DH, NULL, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
DHKeys::~DHKeys ()
|
|
||||||
{
|
|
||||||
DH_free (m_DH);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DHKeys::GenerateKeys ()
|
|
||||||
{
|
|
||||||
BIGNUM * priv_key = NULL, * pub_key = NULL;
|
|
||||||
#if !defined(__x86_64__) // use short exponent for non x64
|
|
||||||
priv_key = BN_new ();
|
|
||||||
BN_rand (priv_key, ELGAMAL_SHORT_EXPONENT_NUM_BITS, 0, 1);
|
|
||||||
#endif
|
|
||||||
if (g_ElggTable)
|
|
||||||
{
|
|
||||||
#if defined(__x86_64__)
|
|
||||||
priv_key = BN_new ();
|
|
||||||
BN_rand (priv_key, ELGAMAL_FULL_EXPONENT_NUM_BITS, 0, 1);
|
|
||||||
#endif
|
|
||||||
auto ctx = BN_CTX_new ();
|
|
||||||
pub_key = ElggPow (priv_key, g_ElggTable, ctx);
|
|
||||||
DH_set0_key (m_DH, pub_key, priv_key);
|
|
||||||
BN_CTX_free (ctx);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DH_set0_key (m_DH, NULL, priv_key);
|
|
||||||
DH_generate_key (m_DH);
|
|
||||||
DH_get0_key (m_DH, (const BIGNUM **)&pub_key, (const BIGNUM **)&priv_key);
|
|
||||||
}
|
|
||||||
|
|
||||||
bn2buf (pub_key, m_PublicKey, 256);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DHKeys::Agree (const uint8_t * pub, uint8_t * shared)
|
|
||||||
{
|
|
||||||
BIGNUM * pk = BN_bin2bn (pub, 256, NULL);
|
|
||||||
DH_compute_key (shared, pk, m_DH);
|
|
||||||
BN_free (pk);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ElGamal
|
|
||||||
|
|
||||||
ElGamalEncryption::ElGamalEncryption (const uint8_t * key)
|
|
||||||
{
|
|
||||||
ctx = BN_CTX_new ();
|
|
||||||
// select random k
|
|
||||||
BIGNUM * k = BN_new ();
|
|
||||||
#if defined(__x86_64__)
|
|
||||||
BN_rand (k, ELGAMAL_FULL_EXPONENT_NUM_BITS, -1, 1); // full exponent for x64
|
|
||||||
#else
|
|
||||||
BN_rand (k, ELGAMAL_SHORT_EXPONENT_NUM_BITS, -1, 1); // short exponent of 226 bits
|
|
||||||
#endif
|
|
||||||
// calculate a
|
|
||||||
if (g_ElggTable)
|
|
||||||
a = ElggPow (k, g_ElggTable, ctx);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
a = BN_new ();
|
|
||||||
BN_mod_exp (a, elgg, k, elgp, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
BIGNUM * y = BN_new ();
|
|
||||||
BN_bin2bn (key, 256, y);
|
|
||||||
// calculate b1
|
|
||||||
b1 = BN_new ();
|
|
||||||
BN_mod_exp (b1, y, k, elgp, ctx);
|
|
||||||
BN_free (y);
|
|
||||||
BN_free (k);
|
|
||||||
}
|
|
||||||
|
|
||||||
ElGamalEncryption::~ElGamalEncryption ()
|
|
||||||
{
|
|
||||||
BN_CTX_free (ctx);
|
|
||||||
BN_free (a);
|
|
||||||
BN_free (b1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ElGamalEncryption::Encrypt (const uint8_t * data, uint8_t * encrypted, bool zeroPadding) const
|
|
||||||
{
|
|
||||||
// create m
|
|
||||||
uint8_t m[255];
|
|
||||||
m[0] = 0xFF;
|
|
||||||
memcpy (m+33, data, 222);
|
|
||||||
SHA256 (m+33, 222, m+1);
|
|
||||||
// calculate b = b1*m mod p
|
|
||||||
BIGNUM * b = BN_new ();
|
|
||||||
BN_bin2bn (m, 255, b);
|
|
||||||
BN_mod_mul (b, b1, b, elgp, ctx);
|
|
||||||
// copy a and b
|
|
||||||
if (zeroPadding)
|
|
||||||
{
|
|
||||||
encrypted[0] = 0;
|
|
||||||
bn2buf (a, encrypted + 1, 256);
|
|
||||||
encrypted[257] = 0;
|
|
||||||
bn2buf (b, encrypted + 258, 256);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bn2buf (a, encrypted, 256);
|
|
||||||
bn2buf (b, encrypted + 256, 256);
|
|
||||||
}
|
|
||||||
BN_free (b);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted,
|
|
||||||
uint8_t * data, bool zeroPadding)
|
|
||||||
{
|
|
||||||
BN_CTX * ctx = BN_CTX_new ();
|
|
||||||
BIGNUM * x = BN_new (), * a = BN_new (), * b = BN_new ();
|
|
||||||
BN_bin2bn (key, 256, x);
|
|
||||||
BN_sub (x, elgp, x); BN_sub_word (x, 1); // x = elgp - x- 1
|
|
||||||
BN_bin2bn (zeroPadding ? encrypted + 1 : encrypted, 256, a);
|
|
||||||
BN_bin2bn (zeroPadding ? encrypted + 258 : encrypted + 256, 256, b);
|
|
||||||
// m = b*(a^x mod p) mod p
|
|
||||||
BN_mod_exp (x, a, x, elgp, ctx);
|
|
||||||
BN_mod_mul (b, b, x, elgp, ctx);
|
|
||||||
uint8_t m[255];
|
|
||||||
bn2buf (b, m, 255);
|
|
||||||
BN_free (x); BN_free (a); BN_free (b);
|
|
||||||
BN_CTX_free (ctx);
|
|
||||||
uint8_t hash[32];
|
|
||||||
SHA256 (m + 33, 222, hash);
|
|
||||||
if (memcmp (m + 1, hash, 32))
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "ElGamal decrypt hash doesn't match");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
memcpy (data, m + 33, 222);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GenerateElGamalKeyPair (uint8_t * priv, uint8_t * pub)
|
|
||||||
{
|
|
||||||
#if defined(__x86_64__) || defined(__i386__) || defined(_MSC_VER)
|
|
||||||
RAND_bytes (priv, 256);
|
|
||||||
#else
|
|
||||||
// lower 226 bits (28 bytes and 2 bits) only. short exponent
|
|
||||||
auto numBytes = (ELGAMAL_SHORT_EXPONENT_NUM_BITS)/8 + 1; // 29
|
|
||||||
auto numZeroBytes = 256 - numBytes;
|
|
||||||
RAND_bytes (priv + numZeroBytes, numBytes);
|
|
||||||
memset (priv, 0, numZeroBytes);
|
|
||||||
priv[numZeroBytes] &= 0x03;
|
|
||||||
#endif
|
|
||||||
BN_CTX * ctx = BN_CTX_new ();
|
|
||||||
BIGNUM * p = BN_new ();
|
|
||||||
BN_bin2bn (priv, 256, p);
|
|
||||||
BN_mod_exp (p, elgg, p, elgp, ctx);
|
|
||||||
bn2buf (p, pub, 256);
|
|
||||||
BN_free (p);
|
|
||||||
BN_CTX_free (ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
// HMAC
|
|
||||||
const uint64_t IPAD = 0x3636363636363636;
|
|
||||||
const uint64_t OPAD = 0x5C5C5C5C5C5C5C5C;
|
|
||||||
|
|
||||||
void HMACMD5Digest (uint8_t * msg, size_t len, const MACKey& key, uint8_t * digest)
|
|
||||||
// key is 32 bytes
|
|
||||||
// digest is 16 bytes
|
|
||||||
// block size is 64 bytes
|
|
||||||
{
|
|
||||||
uint64_t buf[256];
|
|
||||||
// ikeypad
|
|
||||||
buf[0] = key.GetLL ()[0] ^ IPAD;
|
|
||||||
buf[1] = key.GetLL ()[1] ^ IPAD;
|
|
||||||
buf[2] = key.GetLL ()[2] ^ IPAD;
|
|
||||||
buf[3] = key.GetLL ()[3] ^ IPAD;
|
|
||||||
buf[4] = IPAD;
|
|
||||||
buf[5] = IPAD;
|
|
||||||
buf[6] = IPAD;
|
|
||||||
buf[7] = IPAD;
|
|
||||||
// concatenate with msg
|
|
||||||
memcpy (buf + 8, msg, len);
|
|
||||||
// calculate first hash
|
|
||||||
uint8_t hash[16]; // MD5
|
|
||||||
MD5((uint8_t *)buf, len + 64, hash);
|
|
||||||
|
|
||||||
// okeypad
|
|
||||||
buf[0] = key.GetLL ()[0] ^ OPAD;
|
|
||||||
buf[1] = key.GetLL ()[1] ^ OPAD;
|
|
||||||
buf[2] = key.GetLL ()[2] ^ OPAD;
|
|
||||||
buf[3] = key.GetLL ()[3] ^ OPAD;
|
|
||||||
buf[4] = OPAD;
|
|
||||||
buf[5] = OPAD;
|
|
||||||
buf[6] = OPAD;
|
|
||||||
buf[7] = OPAD;
|
|
||||||
// copy first hash after okeypad
|
|
||||||
memcpy (buf + 8, hash, 16);
|
|
||||||
// fill next 16 bytes with zeros (first hash size assumed 32 bytes in I2P)
|
|
||||||
memset (buf + 10, 0, 16);
|
|
||||||
|
|
||||||
// calculate digest
|
|
||||||
MD5((uint8_t *)buf, 96, digest);
|
|
||||||
}
|
|
||||||
|
|
||||||
// AES
|
|
||||||
#ifdef AESNI
|
|
||||||
|
|
||||||
#define KeyExpansion256(round0,round1) \
|
|
||||||
"pshufd $0xff, %%xmm2, %%xmm2 \n" \
|
|
||||||
"movaps %%xmm1, %%xmm4 \n" \
|
|
||||||
"pslldq $4, %%xmm4 \n" \
|
|
||||||
"pxor %%xmm4, %%xmm1 \n" \
|
|
||||||
"pslldq $4, %%xmm4 \n" \
|
|
||||||
"pxor %%xmm4, %%xmm1 \n" \
|
|
||||||
"pslldq $4, %%xmm4 \n" \
|
|
||||||
"pxor %%xmm4, %%xmm1 \n" \
|
|
||||||
"pxor %%xmm2, %%xmm1 \n" \
|
|
||||||
"movaps %%xmm1, "#round0"(%[sched]) \n" \
|
|
||||||
"aeskeygenassist $0, %%xmm1, %%xmm4 \n" \
|
|
||||||
"pshufd $0xaa, %%xmm4, %%xmm2 \n" \
|
|
||||||
"movaps %%xmm3, %%xmm4 \n" \
|
|
||||||
"pslldq $4, %%xmm4 \n" \
|
|
||||||
"pxor %%xmm4, %%xmm3 \n" \
|
|
||||||
"pslldq $4, %%xmm4 \n" \
|
|
||||||
"pxor %%xmm4, %%xmm3 \n" \
|
|
||||||
"pslldq $4, %%xmm4 \n" \
|
|
||||||
"pxor %%xmm4, %%xmm3 \n" \
|
|
||||||
"pxor %%xmm2, %%xmm3 \n" \
|
|
||||||
"movaps %%xmm3, "#round1"(%[sched]) \n"
|
|
||||||
|
|
||||||
void ECBCryptoAESNI::ExpandKey (const AESKey& key)
|
|
||||||
{
|
|
||||||
__asm__
|
|
||||||
(
|
|
||||||
"movups (%[key]), %%xmm1 \n"
|
|
||||||
"movups 16(%[key]), %%xmm3 \n"
|
|
||||||
"movaps %%xmm1, (%[sched]) \n"
|
|
||||||
"movaps %%xmm3, 16(%[sched]) \n"
|
|
||||||
"aeskeygenassist $1, %%xmm3, %%xmm2 \n"
|
|
||||||
KeyExpansion256(32,48)
|
|
||||||
"aeskeygenassist $2, %%xmm3, %%xmm2 \n"
|
|
||||||
KeyExpansion256(64,80)
|
|
||||||
"aeskeygenassist $4, %%xmm3, %%xmm2 \n"
|
|
||||||
KeyExpansion256(96,112)
|
|
||||||
"aeskeygenassist $8, %%xmm3, %%xmm2 \n"
|
|
||||||
KeyExpansion256(128,144)
|
|
||||||
"aeskeygenassist $16, %%xmm3, %%xmm2 \n"
|
|
||||||
KeyExpansion256(160,176)
|
|
||||||
"aeskeygenassist $32, %%xmm3, %%xmm2 \n"
|
|
||||||
KeyExpansion256(192,208)
|
|
||||||
"aeskeygenassist $64, %%xmm3, %%xmm2 \n"
|
|
||||||
// key expansion final
|
|
||||||
"pshufd $0xff, %%xmm2, %%xmm2 \n"
|
|
||||||
"movaps %%xmm1, %%xmm4 \n"
|
|
||||||
"pslldq $4, %%xmm4 \n"
|
|
||||||
"pxor %%xmm4, %%xmm1 \n"
|
|
||||||
"pslldq $4, %%xmm4 \n"
|
|
||||||
"pxor %%xmm4, %%xmm1 \n"
|
|
||||||
"pslldq $4, %%xmm4 \n"
|
|
||||||
"pxor %%xmm4, %%xmm1 \n"
|
|
||||||
"pxor %%xmm2, %%xmm1 \n"
|
|
||||||
"movups %%xmm1, 224(%[sched]) \n"
|
|
||||||
: // output
|
|
||||||
: [key]"r"((const uint8_t *)key), [sched]"r"(GetKeySchedule ()) // input
|
|
||||||
: "%xmm1", "%xmm2", "%xmm3", "%xmm4", "memory" // clogged
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define EncryptAES256(sched) \
|
|
||||||
"pxor (%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesenc 16(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesenc 32(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesenc 48(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesenc 64(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesenc 80(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesenc 96(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesenc 112(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesenc 128(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesenc 144(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesenc 160(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesenc 176(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesenc 192(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesenc 208(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesenclast 224(%["#sched"]), %%xmm0 \n"
|
|
||||||
|
|
||||||
void ECBEncryptionAESNI::Encrypt (const ChipherBlock * in, ChipherBlock * out)
|
|
||||||
{
|
|
||||||
__asm__
|
|
||||||
(
|
|
||||||
"movups (%[in]), %%xmm0 \n"
|
|
||||||
EncryptAES256(sched)
|
|
||||||
"movups %%xmm0, (%[out]) \n"
|
|
||||||
: : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define DecryptAES256(sched) \
|
|
||||||
"pxor 224(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesdec 208(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesdec 192(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesdec 176(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesdec 160(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesdec 144(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesdec 128(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesdec 112(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesdec 96(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesdec 80(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesdec 64(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesdec 48(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesdec 32(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesdec 16(%["#sched"]), %%xmm0 \n" \
|
|
||||||
"aesdeclast (%["#sched"]), %%xmm0 \n"
|
|
||||||
|
|
||||||
void ECBDecryptionAESNI::Decrypt (const ChipherBlock * in, ChipherBlock * out)
|
|
||||||
{
|
|
||||||
__asm__
|
|
||||||
(
|
|
||||||
"movups (%[in]), %%xmm0 \n"
|
|
||||||
DecryptAES256(sched)
|
|
||||||
"movups %%xmm0, (%[out]) \n"
|
|
||||||
: : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CallAESIMC(offset) \
|
|
||||||
"movaps "#offset"(%[shed]), %%xmm0 \n" \
|
|
||||||
"aesimc %%xmm0, %%xmm0 \n" \
|
|
||||||
"movaps %%xmm0, "#offset"(%[shed]) \n"
|
|
||||||
|
|
||||||
void ECBDecryptionAESNI::SetKey (const AESKey& key)
|
|
||||||
{
|
|
||||||
ExpandKey (key); // expand encryption key first
|
|
||||||
// then invert it using aesimc
|
|
||||||
__asm__
|
|
||||||
(
|
|
||||||
CallAESIMC(16)
|
|
||||||
CallAESIMC(32)
|
|
||||||
CallAESIMC(48)
|
|
||||||
CallAESIMC(64)
|
|
||||||
CallAESIMC(80)
|
|
||||||
CallAESIMC(96)
|
|
||||||
CallAESIMC(112)
|
|
||||||
CallAESIMC(128)
|
|
||||||
CallAESIMC(144)
|
|
||||||
CallAESIMC(160)
|
|
||||||
CallAESIMC(176)
|
|
||||||
CallAESIMC(192)
|
|
||||||
CallAESIMC(208)
|
|
||||||
: : [shed]"r"(GetKeySchedule ()) : "%xmm0", "memory"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
void CBCEncryption::Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out)
|
|
||||||
{
|
|
||||||
#ifdef AESNI
|
|
||||||
__asm__
|
|
||||||
(
|
|
||||||
"movups (%[iv]), %%xmm1 \n"
|
|
||||||
"1: \n"
|
|
||||||
"movups (%[in]), %%xmm0 \n"
|
|
||||||
"pxor %%xmm1, %%xmm0 \n"
|
|
||||||
EncryptAES256(sched)
|
|
||||||
"movaps %%xmm0, %%xmm1 \n"
|
|
||||||
"movups %%xmm0, (%[out]) \n"
|
|
||||||
"add $16, %[in] \n"
|
|
||||||
"add $16, %[out] \n"
|
|
||||||
"dec %[num] \n"
|
|
||||||
"jnz 1b \n"
|
|
||||||
"movups %%xmm1, (%[iv]) \n"
|
|
||||||
:
|
|
||||||
: [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()),
|
|
||||||
[in]"r"(in), [out]"r"(out), [num]"r"(numBlocks)
|
|
||||||
: "%xmm0", "%xmm1", "cc", "memory"
|
|
||||||
);
|
|
||||||
#else
|
|
||||||
for (int i = 0; i < numBlocks; i++)
|
|
||||||
{
|
|
||||||
*m_LastBlock.GetChipherBlock () ^= in[i];
|
|
||||||
m_ECBEncryption.Encrypt (m_LastBlock.GetChipherBlock (), m_LastBlock.GetChipherBlock ());
|
|
||||||
out[i] = *m_LastBlock.GetChipherBlock ();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBCEncryption::Encrypt (const uint8_t * in, std::size_t len, uint8_t * out)
|
|
||||||
{
|
|
||||||
// len/16
|
|
||||||
int numBlocks = len >> 4;
|
|
||||||
if (numBlocks > 0)
|
|
||||||
Encrypt (numBlocks, (const ChipherBlock *)in, (ChipherBlock *)out);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBCEncryption::Encrypt (const uint8_t * in, uint8_t * out)
|
|
||||||
{
|
|
||||||
#ifdef AESNI
|
|
||||||
__asm__
|
|
||||||
(
|
|
||||||
"movups (%[iv]), %%xmm1 \n"
|
|
||||||
"movups (%[in]), %%xmm0 \n"
|
|
||||||
"pxor %%xmm1, %%xmm0 \n"
|
|
||||||
EncryptAES256(sched)
|
|
||||||
"movups %%xmm0, (%[out]) \n"
|
|
||||||
"movups %%xmm0, (%[iv]) \n"
|
|
||||||
:
|
|
||||||
: [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()),
|
|
||||||
[in]"r"(in), [out]"r"(out)
|
|
||||||
: "%xmm0", "%xmm1", "memory"
|
|
||||||
);
|
|
||||||
#else
|
|
||||||
Encrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBCDecryption::Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out)
|
|
||||||
{
|
|
||||||
#ifdef AESNI
|
|
||||||
__asm__
|
|
||||||
(
|
|
||||||
"movups (%[iv]), %%xmm1 \n"
|
|
||||||
"1: \n"
|
|
||||||
"movups (%[in]), %%xmm0 \n"
|
|
||||||
"movaps %%xmm0, %%xmm2 \n"
|
|
||||||
DecryptAES256(sched)
|
|
||||||
"pxor %%xmm1, %%xmm0 \n"
|
|
||||||
"movups %%xmm0, (%[out]) \n"
|
|
||||||
"movaps %%xmm2, %%xmm1 \n"
|
|
||||||
"add $16, %[in] \n"
|
|
||||||
"add $16, %[out] \n"
|
|
||||||
"dec %[num] \n"
|
|
||||||
"jnz 1b \n"
|
|
||||||
"movups %%xmm1, (%[iv]) \n"
|
|
||||||
:
|
|
||||||
: [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
|
|
||||||
[in]"r"(in), [out]"r"(out), [num]"r"(numBlocks)
|
|
||||||
: "%xmm0", "%xmm1", "%xmm2", "cc", "memory"
|
|
||||||
);
|
|
||||||
#else
|
|
||||||
for (int i = 0; i < numBlocks; i++)
|
|
||||||
{
|
|
||||||
ChipherBlock tmp = in[i];
|
|
||||||
m_ECBDecryption.Decrypt (in + i, out + i);
|
|
||||||
out[i] ^= *m_IV.GetChipherBlock ();
|
|
||||||
*m_IV.GetChipherBlock () = tmp;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBCDecryption::Decrypt (const uint8_t * in, std::size_t len, uint8_t * out)
|
|
||||||
{
|
|
||||||
int numBlocks = len >> 4;
|
|
||||||
if (numBlocks > 0)
|
|
||||||
Decrypt (numBlocks, (const ChipherBlock *)in, (ChipherBlock *)out);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBCDecryption::Decrypt (const uint8_t * in, uint8_t * out)
|
|
||||||
{
|
|
||||||
#ifdef AESNI
|
|
||||||
__asm__
|
|
||||||
(
|
|
||||||
"movups (%[iv]), %%xmm1 \n"
|
|
||||||
"movups (%[in]), %%xmm0 \n"
|
|
||||||
"movups %%xmm0, (%[iv]) \n"
|
|
||||||
DecryptAES256(sched)
|
|
||||||
"pxor %%xmm1, %%xmm0 \n"
|
|
||||||
"movups %%xmm0, (%[out]) \n"
|
|
||||||
:
|
|
||||||
: [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
|
|
||||||
[in]"r"(in), [out]"r"(out)
|
|
||||||
: "%xmm0", "%xmm1", "memory"
|
|
||||||
);
|
|
||||||
#else
|
|
||||||
Decrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelEncryption::Encrypt (const uint8_t * in, uint8_t * out)
|
|
||||||
{
|
|
||||||
#ifdef AESNI
|
|
||||||
__asm__
|
|
||||||
(
|
|
||||||
// encrypt IV
|
|
||||||
"movups (%[in]), %%xmm0 \n"
|
|
||||||
EncryptAES256(sched_iv)
|
|
||||||
"movaps %%xmm0, %%xmm1 \n"
|
|
||||||
// double IV encryption
|
|
||||||
EncryptAES256(sched_iv)
|
|
||||||
"movups %%xmm0, (%[out]) \n"
|
|
||||||
// encrypt data, IV is xmm1
|
|
||||||
"1: \n"
|
|
||||||
"add $16, %[in] \n"
|
|
||||||
"add $16, %[out] \n"
|
|
||||||
"movups (%[in]), %%xmm0 \n"
|
|
||||||
"pxor %%xmm1, %%xmm0 \n"
|
|
||||||
EncryptAES256(sched_l)
|
|
||||||
"movaps %%xmm0, %%xmm1 \n"
|
|
||||||
"movups %%xmm0, (%[out]) \n"
|
|
||||||
"dec %[num] \n"
|
|
||||||
"jnz 1b \n"
|
|
||||||
:
|
|
||||||
: [sched_iv]"r"(m_IVEncryption.GetKeySchedule ()), [sched_l]"r"(m_LayerEncryption.GetKeySchedule ()),
|
|
||||||
[in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes
|
|
||||||
: "%xmm0", "%xmm1", "cc", "memory"
|
|
||||||
);
|
|
||||||
#else
|
|
||||||
m_IVEncryption.Encrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv
|
|
||||||
m_LayerEncryption.SetIV (out);
|
|
||||||
m_LayerEncryption.Encrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data
|
|
||||||
m_IVEncryption.Encrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelDecryption::Decrypt (const uint8_t * in, uint8_t * out)
|
|
||||||
{
|
|
||||||
#ifdef AESNI
|
|
||||||
__asm__
|
|
||||||
(
|
|
||||||
// decrypt IV
|
|
||||||
"movups (%[in]), %%xmm0 \n"
|
|
||||||
DecryptAES256(sched_iv)
|
|
||||||
"movaps %%xmm0, %%xmm1 \n"
|
|
||||||
// double IV encryption
|
|
||||||
DecryptAES256(sched_iv)
|
|
||||||
"movups %%xmm0, (%[out]) \n"
|
|
||||||
// decrypt data, IV is xmm1
|
|
||||||
"1: \n"
|
|
||||||
"add $16, %[in] \n"
|
|
||||||
"add $16, %[out] \n"
|
|
||||||
"movups (%[in]), %%xmm0 \n"
|
|
||||||
"movaps %%xmm0, %%xmm2 \n"
|
|
||||||
DecryptAES256(sched_l)
|
|
||||||
"pxor %%xmm1, %%xmm0 \n"
|
|
||||||
"movups %%xmm0, (%[out]) \n"
|
|
||||||
"movaps %%xmm2, %%xmm1 \n"
|
|
||||||
"dec %[num] \n"
|
|
||||||
"jnz 1b \n"
|
|
||||||
:
|
|
||||||
: [sched_iv]"r"(m_IVDecryption.GetKeySchedule ()), [sched_l]"r"(m_LayerDecryption.GetKeySchedule ()),
|
|
||||||
[in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes
|
|
||||||
: "%xmm0", "%xmm1", "%xmm2", "cc", "memory"
|
|
||||||
);
|
|
||||||
#else
|
|
||||||
m_IVDecryption.Decrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv
|
|
||||||
m_LayerDecryption.SetIV (out);
|
|
||||||
m_LayerDecryption.Decrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data
|
|
||||||
m_IVDecryption.Decrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/* std::vector <std::unique_ptr<std::mutex> > m_OpenSSLMutexes;
|
|
||||||
static void OpensslLockingCallback(int mode, int type, const char * file, int line)
|
|
||||||
{
|
|
||||||
if (type > 0 && (size_t)type < m_OpenSSLMutexes.size ())
|
|
||||||
{
|
|
||||||
if (mode & CRYPTO_LOCK)
|
|
||||||
m_OpenSSLMutexes[type]->lock ();
|
|
||||||
else
|
|
||||||
m_OpenSSLMutexes[type]->unlock ();
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
void InitCrypto (bool precomputation)
|
|
||||||
{
|
|
||||||
SSL_library_init ();
|
|
||||||
/* auto numLocks = CRYPTO_num_locks();
|
|
||||||
for (int i = 0; i < numLocks; i++)
|
|
||||||
m_OpenSSLMutexes.emplace_back (new std::mutex);
|
|
||||||
CRYPTO_set_locking_callback (OpensslLockingCallback);*/
|
|
||||||
if (precomputation)
|
|
||||||
{
|
|
||||||
#if defined(__x86_64__)
|
|
||||||
g_ElggTable = new BIGNUM * [ELGAMAL_FULL_EXPONENT_NUM_BYTES][255];
|
|
||||||
PrecalculateElggTable (g_ElggTable, ELGAMAL_FULL_EXPONENT_NUM_BYTES);
|
|
||||||
#else
|
|
||||||
g_ElggTable = new BIGNUM * [ELGAMAL_SHORT_EXPONENT_NUM_BYTES][255];
|
|
||||||
PrecalculateElggTable (g_ElggTable, ELGAMAL_SHORT_EXPONENT_NUM_BYTES);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TerminateCrypto ()
|
|
||||||
{
|
|
||||||
if (g_ElggTable)
|
|
||||||
{
|
|
||||||
DestroyElggTable (g_ElggTable,
|
|
||||||
#if defined(__x86_64__)
|
|
||||||
ELGAMAL_FULL_EXPONENT_NUM_BYTES
|
|
||||||
#else
|
|
||||||
ELGAMAL_SHORT_EXPONENT_NUM_BYTES
|
|
||||||
#endif
|
|
||||||
);
|
|
||||||
delete[] g_ElggTable; g_ElggTable = nullptr;
|
|
||||||
}
|
|
||||||
/* CRYPTO_set_locking_callback (nullptr);
|
|
||||||
m_OpenSSLMutexes.clear ();*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
333
Crypto.h
333
Crypto.h
@@ -1,333 +0,0 @@
|
|||||||
#ifndef CRYPTO_H__
|
|
||||||
#define CRYPTO_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <string>
|
|
||||||
#include <openssl/bn.h>
|
|
||||||
#include <openssl/dh.h>
|
|
||||||
#include <openssl/aes.h>
|
|
||||||
#include <openssl/dsa.h>
|
|
||||||
#include <openssl/ecdsa.h>
|
|
||||||
#include <openssl/rsa.h>
|
|
||||||
#include <openssl/sha.h>
|
|
||||||
#include <openssl/evp.h>
|
|
||||||
#include <openssl/rand.h>
|
|
||||||
|
|
||||||
#include "Base.h"
|
|
||||||
#include "Tag.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace crypto
|
|
||||||
{
|
|
||||||
bool bn2buf (const BIGNUM * bn, uint8_t * buf, size_t len);
|
|
||||||
|
|
||||||
// DSA
|
|
||||||
DSA * CreateDSA ();
|
|
||||||
|
|
||||||
// RSA
|
|
||||||
const BIGNUM * GetRSAE ();
|
|
||||||
|
|
||||||
// DH
|
|
||||||
class DHKeys
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
DHKeys ();
|
|
||||||
~DHKeys ();
|
|
||||||
|
|
||||||
void GenerateKeys ();
|
|
||||||
const uint8_t * GetPublicKey () const { return m_PublicKey; };
|
|
||||||
void Agree (const uint8_t * pub, uint8_t * shared);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
DH * m_DH;
|
|
||||||
uint8_t m_PublicKey[256];
|
|
||||||
};
|
|
||||||
|
|
||||||
// ElGamal
|
|
||||||
class ElGamalEncryption
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
ElGamalEncryption (const uint8_t * key);
|
|
||||||
~ElGamalEncryption ();
|
|
||||||
|
|
||||||
void Encrypt (const uint8_t * data, uint8_t * encrypted, bool zeroPadding = false) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
BN_CTX * ctx;
|
|
||||||
BIGNUM * a, * b1;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, uint8_t * data, bool zeroPadding = false);
|
|
||||||
void GenerateElGamalKeyPair (uint8_t * priv, uint8_t * pub);
|
|
||||||
|
|
||||||
// HMAC
|
|
||||||
typedef i2p::data::Tag<32> MACKey;
|
|
||||||
void HMACMD5Digest (uint8_t * msg, size_t len, const MACKey& key, uint8_t * digest);
|
|
||||||
|
|
||||||
// AES
|
|
||||||
struct ChipherBlock
|
|
||||||
{
|
|
||||||
uint8_t buf[16];
|
|
||||||
|
|
||||||
void operator^=(const ChipherBlock& other) // XOR
|
|
||||||
{
|
|
||||||
#if defined(__x86_64__) || defined(__SSE__) // for Intel x84 or with SSE
|
|
||||||
__asm__
|
|
||||||
(
|
|
||||||
"movups (%[buf]), %%xmm0 \n"
|
|
||||||
"movups (%[other]), %%xmm1 \n"
|
|
||||||
"pxor %%xmm1, %%xmm0 \n"
|
|
||||||
"movups %%xmm0, (%[buf]) \n"
|
|
||||||
:
|
|
||||||
: [buf]"r"(buf), [other]"r"(other.buf)
|
|
||||||
: "%xmm0", "%xmm1", "memory"
|
|
||||||
);
|
|
||||||
#else
|
|
||||||
// TODO: implement it better
|
|
||||||
for (int i = 0; i < 16; i++)
|
|
||||||
buf[i] ^= other.buf[i];
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef i2p::data::Tag<32> AESKey;
|
|
||||||
|
|
||||||
template<size_t sz>
|
|
||||||
class AESAlignedBuffer // 16 bytes alignment
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
AESAlignedBuffer ()
|
|
||||||
{
|
|
||||||
m_Buf = m_UnalignedBuffer;
|
|
||||||
uint8_t rem = ((size_t)m_Buf) & 0x0f;
|
|
||||||
if (rem)
|
|
||||||
m_Buf += (16 - rem);
|
|
||||||
}
|
|
||||||
|
|
||||||
operator uint8_t * () { return m_Buf; };
|
|
||||||
operator const uint8_t * () const { return m_Buf; };
|
|
||||||
ChipherBlock * GetChipherBlock () { return (ChipherBlock *)m_Buf; };
|
|
||||||
const ChipherBlock * GetChipherBlock () const { return (const ChipherBlock *)m_Buf; };
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
uint8_t m_UnalignedBuffer[sz + 15]; // up to 15 bytes alignment
|
|
||||||
uint8_t * m_Buf;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef AESNI
|
|
||||||
class ECBCryptoAESNI
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
uint8_t * GetKeySchedule () { return m_KeySchedule; };
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
void ExpandKey (const AESKey& key);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
AESAlignedBuffer<240> m_KeySchedule; // 14 rounds for AES-256, 240 bytes
|
|
||||||
};
|
|
||||||
|
|
||||||
class ECBEncryptionAESNI: public ECBCryptoAESNI
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
void SetKey (const AESKey& key) { ExpandKey (key); };
|
|
||||||
void Encrypt (const ChipherBlock * in, ChipherBlock * out);
|
|
||||||
};
|
|
||||||
|
|
||||||
class ECBDecryptionAESNI: public ECBCryptoAESNI
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
void SetKey (const AESKey& key);
|
|
||||||
void Decrypt (const ChipherBlock * in, ChipherBlock * out);
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef ECBEncryptionAESNI ECBEncryption;
|
|
||||||
typedef ECBDecryptionAESNI ECBDecryption;
|
|
||||||
|
|
||||||
#else // use openssl
|
|
||||||
|
|
||||||
class ECBEncryption
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
void SetKey (const AESKey& key)
|
|
||||||
{
|
|
||||||
AES_set_encrypt_key (key, 256, &m_Key);
|
|
||||||
}
|
|
||||||
void Encrypt (const ChipherBlock * in, ChipherBlock * out)
|
|
||||||
{
|
|
||||||
AES_encrypt (in->buf, out->buf, &m_Key);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
AES_KEY m_Key;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ECBDecryption
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
void SetKey (const AESKey& key)
|
|
||||||
{
|
|
||||||
AES_set_decrypt_key (key, 256, &m_Key);
|
|
||||||
}
|
|
||||||
void Decrypt (const ChipherBlock * in, ChipherBlock * out)
|
|
||||||
{
|
|
||||||
AES_decrypt (in->buf, out->buf, &m_Key);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
AES_KEY m_Key;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class CBCEncryption
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
CBCEncryption () { memset ((uint8_t *)m_LastBlock, 0, 16); };
|
|
||||||
|
|
||||||
void SetKey (const AESKey& key) { m_ECBEncryption.SetKey (key); }; // 32 bytes
|
|
||||||
void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_LastBlock, iv, 16); }; // 16 bytes
|
|
||||||
|
|
||||||
void Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out);
|
|
||||||
void Encrypt (const uint8_t * in, std::size_t len, uint8_t * out);
|
|
||||||
void Encrypt (const uint8_t * in, uint8_t * out); // one block
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
AESAlignedBuffer<16> m_LastBlock;
|
|
||||||
|
|
||||||
ECBEncryption m_ECBEncryption;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CBCDecryption
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
CBCDecryption () { memset ((uint8_t *)m_IV, 0, 16); };
|
|
||||||
|
|
||||||
void SetKey (const AESKey& key) { m_ECBDecryption.SetKey (key); }; // 32 bytes
|
|
||||||
void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_IV, iv, 16); }; // 16 bytes
|
|
||||||
|
|
||||||
void Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out);
|
|
||||||
void Decrypt (const uint8_t * in, std::size_t len, uint8_t * out);
|
|
||||||
void Decrypt (const uint8_t * in, uint8_t * out); // one block
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
AESAlignedBuffer<16> m_IV;
|
|
||||||
ECBDecryption m_ECBDecryption;
|
|
||||||
};
|
|
||||||
|
|
||||||
class TunnelEncryption // with double IV encryption
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
void SetKeys (const AESKey& layerKey, const AESKey& ivKey)
|
|
||||||
{
|
|
||||||
m_LayerEncryption.SetKey (layerKey);
|
|
||||||
m_IVEncryption.SetKey (ivKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Encrypt (const uint8_t * in, uint8_t * out); // 1024 bytes (16 IV + 1008 data)
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
ECBEncryption m_IVEncryption;
|
|
||||||
#ifdef AESNI
|
|
||||||
ECBEncryption m_LayerEncryption;
|
|
||||||
#else
|
|
||||||
CBCEncryption m_LayerEncryption;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
class TunnelDecryption // with double IV encryption
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
void SetKeys (const AESKey& layerKey, const AESKey& ivKey)
|
|
||||||
{
|
|
||||||
m_LayerDecryption.SetKey (layerKey);
|
|
||||||
m_IVDecryption.SetKey (ivKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Decrypt (const uint8_t * in, uint8_t * out); // 1024 bytes (16 IV + 1008 data)
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
ECBDecryption m_IVDecryption;
|
|
||||||
#ifdef AESNI
|
|
||||||
ECBDecryption m_LayerDecryption;
|
|
||||||
#else
|
|
||||||
CBCDecryption m_LayerDecryption;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
void InitCrypto (bool precomputation);
|
|
||||||
void TerminateCrypto ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// take care about openssl version
|
|
||||||
#include <openssl/opensslv.h>
|
|
||||||
#if (OPENSSL_VERSION_NUMBER < 0x010100000) || defined(LIBRESSL_VERSION_NUMBER) // 1.1.0 or LibreSSL
|
|
||||||
// define getters and setters introduced in 1.1.0
|
|
||||||
inline int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g)
|
|
||||||
{ d->p = p; d->q = q; d->g = g; return 1; }
|
|
||||||
inline int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key)
|
|
||||||
{ d->pub_key = pub_key; d->priv_key = priv_key; return 1; }
|
|
||||||
inline void DSA_get0_key(const DSA *d, const BIGNUM **pub_key, const BIGNUM **priv_key)
|
|
||||||
{ *pub_key = d->pub_key; *priv_key = d->priv_key; }
|
|
||||||
inline int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s)
|
|
||||||
{ sig->r = r; sig->s = s; return 1; }
|
|
||||||
inline void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
|
|
||||||
{ *pr = sig->r; *ps = sig->s; }
|
|
||||||
|
|
||||||
inline int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
|
|
||||||
{
|
|
||||||
if (sig->r) BN_free (sig->r);
|
|
||||||
if (sig->s) BN_free (sig->s);
|
|
||||||
sig->r = r; sig->s = s; return 1;
|
|
||||||
}
|
|
||||||
inline void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
|
|
||||||
{ *pr = sig->r; *ps = sig->s; }
|
|
||||||
|
|
||||||
inline int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
|
|
||||||
{ r->n = n; r->e = e; r->d = d; return 1; }
|
|
||||||
inline void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
|
|
||||||
{ *n = r->n; *e = r->e; *d = r->d; }
|
|
||||||
|
|
||||||
inline int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
|
|
||||||
{ dh->p = p; dh->q = q; dh->g = g; return 1; }
|
|
||||||
inline int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key)
|
|
||||||
{
|
|
||||||
if (dh->pub_key) BN_free (dh->pub_key);
|
|
||||||
if (dh->priv_key) BN_free (dh->priv_key);
|
|
||||||
dh->pub_key = pub_key; dh->priv_key = priv_key; return 1;
|
|
||||||
}
|
|
||||||
inline void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key)
|
|
||||||
{ *pub_key = dh->pub_key; *priv_key = dh->priv_key; }
|
|
||||||
|
|
||||||
inline RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey)
|
|
||||||
{ return pkey->pkey.rsa; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
359
Daemon.cpp
359
Daemon.cpp
@@ -1,359 +0,0 @@
|
|||||||
#include <thread>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "Daemon.h"
|
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "FS.h"
|
|
||||||
#include "Base.h"
|
|
||||||
#include "version.h"
|
|
||||||
#include "Transports.h"
|
|
||||||
#include "NTCPSession.h"
|
|
||||||
#include "RouterInfo.h"
|
|
||||||
#include "RouterContext.h"
|
|
||||||
#include "Tunnel.h"
|
|
||||||
#include "HTTP.h"
|
|
||||||
#include "NetDb.h"
|
|
||||||
#include "Garlic.h"
|
|
||||||
#include "Streaming.h"
|
|
||||||
#include "Destination.h"
|
|
||||||
#include "HTTPServer.h"
|
|
||||||
#include "I2PControl.h"
|
|
||||||
#include "ClientContext.h"
|
|
||||||
#include "Crypto.h"
|
|
||||||
#include "UPnP.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
#include "Event.h"
|
|
||||||
#include "Websocket.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace util
|
|
||||||
{
|
|
||||||
class Daemon_Singleton::Daemon_Singleton_Private
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Daemon_Singleton_Private() {};
|
|
||||||
~Daemon_Singleton_Private() {};
|
|
||||||
|
|
||||||
std::unique_ptr<i2p::http::HTTPServer> httpServer;
|
|
||||||
std::unique_ptr<i2p::client::I2PControlService> m_I2PControlService;
|
|
||||||
std::unique_ptr<i2p::transport::UPnP> UPnP;
|
|
||||||
#ifdef WITH_EVENTS
|
|
||||||
std::unique_ptr<i2p::event::WebsocketServer> m_WebsocketServer;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
Daemon_Singleton::Daemon_Singleton() : isDaemon(false), running(true), d(*new Daemon_Singleton_Private()) {}
|
|
||||||
Daemon_Singleton::~Daemon_Singleton() {
|
|
||||||
delete &d;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Daemon_Singleton::IsService () const
|
|
||||||
{
|
|
||||||
bool service = false;
|
|
||||||
#ifndef _WIN32
|
|
||||||
i2p::config::GetOption("service", service);
|
|
||||||
#endif
|
|
||||||
return service;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Daemon_Singleton::init(int argc, char* argv[])
|
|
||||||
{
|
|
||||||
i2p::config::Init();
|
|
||||||
i2p::config::ParseCmdline(argc, argv);
|
|
||||||
|
|
||||||
std::string config; i2p::config::GetOption("conf", config);
|
|
||||||
std::string datadir; i2p::config::GetOption("datadir", datadir);
|
|
||||||
i2p::fs::DetectDataDir(datadir, IsService());
|
|
||||||
i2p::fs::Init();
|
|
||||||
|
|
||||||
datadir = i2p::fs::GetDataDir();
|
|
||||||
// TODO: drop old name detection in v2.8.0
|
|
||||||
if (config == "")
|
|
||||||
{
|
|
||||||
config = i2p::fs::DataDirPath("i2p.conf");
|
|
||||||
if (i2p::fs::Exists (config)) {
|
|
||||||
LogPrint(eLogWarning, "Daemon: please rename i2p.conf to i2pd.conf here: ", config);
|
|
||||||
} else {
|
|
||||||
config = i2p::fs::DataDirPath("i2pd.conf");
|
|
||||||
if (!i2p::fs::Exists (config)) {
|
|
||||||
// use i2pd.conf only if exists
|
|
||||||
config = ""; /* reset */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
i2p::config::ParseConfig(config);
|
|
||||||
i2p::config::Finalize();
|
|
||||||
|
|
||||||
i2p::config::GetOption("daemon", isDaemon);
|
|
||||||
|
|
||||||
std::string logs = ""; i2p::config::GetOption("log", logs);
|
|
||||||
std::string logfile = ""; i2p::config::GetOption("logfile", logfile);
|
|
||||||
std::string loglevel = ""; i2p::config::GetOption("loglevel", loglevel);
|
|
||||||
|
|
||||||
/* setup logging */
|
|
||||||
if (isDaemon && (logs == "" || logs == "stdout"))
|
|
||||||
logs = "file";
|
|
||||||
|
|
||||||
i2p::log::Logger().SetLogLevel(loglevel);
|
|
||||||
if (logs == "file") {
|
|
||||||
if (logfile == "")
|
|
||||||
logfile = i2p::fs::DataDirPath("i2pd.log");
|
|
||||||
LogPrint(eLogInfo, "Log: will send messages to ", logfile);
|
|
||||||
i2p::log::Logger().SendTo (logfile);
|
|
||||||
#ifndef _WIN32
|
|
||||||
} else if (logs == "syslog") {
|
|
||||||
LogPrint(eLogInfo, "Log: will send messages to syslog");
|
|
||||||
i2p::log::Logger().SendTo("i2pd", LOG_DAEMON);
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
// use stdout -- default
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
int netID; i2p::config::GetOption("netid", netID);
|
|
||||||
i2p::context.SetNetID (netID);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
i2p::context.SetSupportsV6 (ipv6);
|
|
||||||
i2p::context.SetSupportsV4 (ipv4);
|
|
||||||
|
|
||||||
bool transit; i2p::config::GetOption("notransit", transit);
|
|
||||||
i2p::context.SetAcceptsTunnels (!transit);
|
|
||||||
uint16_t transitTunnels; i2p::config::GetOption("limits.transittunnels", transitTunnels);
|
|
||||||
SetMaxNumTransitTunnels (transitTunnels);
|
|
||||||
|
|
||||||
bool isFloodfill; i2p::config::GetOption("floodfill", isFloodfill);
|
|
||||||
if (isFloodfill) {
|
|
||||||
LogPrint(eLogInfo, "Daemon: router will be floodfill");
|
|
||||||
i2p::context.SetFloodfill (true);
|
|
||||||
} else {
|
|
||||||
i2p::context.SetFloodfill (false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* this section also honors 'floodfill' flag, if set above */
|
|
||||||
std::string bandwidth; i2p::config::GetOption("bandwidth", bandwidth);
|
|
||||||
if (bandwidth.length () > 0)
|
|
||||||
{
|
|
||||||
if (bandwidth[0] >= 'K' && bandwidth[0] <= 'X')
|
|
||||||
{
|
|
||||||
i2p::context.SetBandwidth (bandwidth[0]);
|
|
||||||
LogPrint(eLogInfo, "Daemon: bandwidth set to ", i2p::context.GetBandwidthLimit (), "KBps");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto value = std::atoi(bandwidth.c_str());
|
|
||||||
if (value > 0)
|
|
||||||
{
|
|
||||||
i2p::context.SetBandwidth (value);
|
|
||||||
LogPrint(eLogInfo, "Daemon: bandwidth set to ", i2p::context.GetBandwidthLimit (), " KBps");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint(eLogInfo, "Daemon: unexpected bandwidth ", bandwidth, ". Set to 'low'");
|
|
||||||
i2p::context.SetBandwidth (i2p::data::CAPS_FLAG_LOW_BANDWIDTH2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (isFloodfill)
|
|
||||||
{
|
|
||||||
LogPrint(eLogInfo, "Daemon: floodfill bandwidth set to 'extra'");
|
|
||||||
i2p::context.SetBandwidth (i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint(eLogInfo, "Daemon: bandwidth set to 'low'");
|
|
||||||
i2p::context.SetBandwidth (i2p::data::CAPS_FLAG_LOW_BANDWIDTH2);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string family; i2p::config::GetOption("family", family);
|
|
||||||
i2p::context.SetFamily (family);
|
|
||||||
if (family.length () > 0)
|
|
||||||
LogPrint(eLogInfo, "Daemon: family set to ", family);
|
|
||||||
|
|
||||||
bool trust; i2p::config::GetOption("trust.enabled", trust);
|
|
||||||
if (trust)
|
|
||||||
{
|
|
||||||
LogPrint(eLogInfo, "Daemon: explicit trust enabled");
|
|
||||||
std::string fam; i2p::config::GetOption("trust.family", fam);
|
|
||||||
std::string routers; i2p::config::GetOption("trust.routers", routers);
|
|
||||||
bool restricted = false;
|
|
||||||
if (fam.length() > 0)
|
|
||||||
{
|
|
||||||
std::set<std::string> fams;
|
|
||||||
size_t pos = 0, comma;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
comma = fam.find (',', pos);
|
|
||||||
fams.insert (fam.substr (pos, comma != std::string::npos ? comma - pos : std::string::npos));
|
|
||||||
pos = comma + 1;
|
|
||||||
}
|
|
||||||
while (comma != std::string::npos);
|
|
||||||
i2p::transport::transports.RestrictRoutesToFamilies(fams);
|
|
||||||
restricted = fams.size() > 0;
|
|
||||||
}
|
|
||||||
if (routers.length() > 0) {
|
|
||||||
std::set<i2p::data::IdentHash> idents;
|
|
||||||
size_t pos = 0, comma;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
comma = routers.find (',', pos);
|
|
||||||
i2p::data::IdentHash ident;
|
|
||||||
ident.FromBase64 (routers.substr (pos, comma != std::string::npos ? comma - pos : std::string::npos));
|
|
||||||
idents.insert (ident);
|
|
||||||
pos = comma + 1;
|
|
||||||
}
|
|
||||||
while (comma != std::string::npos);
|
|
||||||
LogPrint(eLogInfo, "Daemon: setting restricted routes to use ", idents.size(), " trusted routesrs");
|
|
||||||
i2p::transport::transports.RestrictRoutesToRouters(idents);
|
|
||||||
restricted = idents.size() > 0;
|
|
||||||
}
|
|
||||||
if(!restricted)
|
|
||||||
LogPrint(eLogError, "Daemon: no trusted routers of families specififed");
|
|
||||||
}
|
|
||||||
bool hidden; i2p::config::GetOption("trust.hidden", hidden);
|
|
||||||
if (hidden)
|
|
||||||
{
|
|
||||||
LogPrint(eLogInfo, "Daemon: using hidden mode");
|
|
||||||
i2p::data::netdb.SetHidden(true);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Daemon_Singleton::start()
|
|
||||||
{
|
|
||||||
i2p::log::Logger().Start();
|
|
||||||
LogPrint(eLogInfo, "Daemon: starting NetDB");
|
|
||||||
i2p::data::netdb.Start();
|
|
||||||
|
|
||||||
bool upnp; i2p::config::GetOption("upnp.enabled", upnp);
|
|
||||||
if (upnp) {
|
|
||||||
d.UPnP = std::unique_ptr<i2p::transport::UPnP>(new i2p::transport::UPnP);
|
|
||||||
d.UPnP->Start ();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ntcp; i2p::config::GetOption("ntcp", ntcp);
|
|
||||||
bool ssu; i2p::config::GetOption("ssu", ssu);
|
|
||||||
LogPrint(eLogInfo, "Daemon: starting Transports");
|
|
||||||
if(!ssu) LogPrint(eLogInfo, "Daemon: ssu disabled");
|
|
||||||
if(!ntcp) LogPrint(eLogInfo, "Daemon: ntcp disabled");
|
|
||||||
i2p::transport::transports.Start(ntcp, ssu);
|
|
||||||
if (i2p::transport::transports.IsBoundNTCP() || i2p::transport::transports.IsBoundSSU()) {
|
|
||||||
LogPrint(eLogInfo, "Daemon: Transports started");
|
|
||||||
} else {
|
|
||||||
LogPrint(eLogError, "Daemon: failed to start Transports");
|
|
||||||
/** shut down netdb right away */
|
|
||||||
i2p::transport::transports.Stop();
|
|
||||||
i2p::data::netdb.Stop();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool http; i2p::config::GetOption("http.enabled", http);
|
|
||||||
if (http) {
|
|
||||||
std::string httpAddr; i2p::config::GetOption("http.address", httpAddr);
|
|
||||||
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
|
|
||||||
LogPrint(eLogInfo, "Daemon: starting HTTP Server at ", httpAddr, ":", httpPort);
|
|
||||||
d.httpServer = std::unique_ptr<i2p::http::HTTPServer>(new i2p::http::HTTPServer(httpAddr, httpPort));
|
|
||||||
d.httpServer->Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
LogPrint(eLogInfo, "Daemon: starting Tunnels");
|
|
||||||
i2p::tunnel::tunnels.Start();
|
|
||||||
|
|
||||||
LogPrint(eLogInfo, "Daemon: starting Client");
|
|
||||||
i2p::client::context.Start ();
|
|
||||||
|
|
||||||
// I2P Control Protocol
|
|
||||||
bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol);
|
|
||||||
if (i2pcontrol) {
|
|
||||||
std::string i2pcpAddr; i2p::config::GetOption("i2pcontrol.address", i2pcpAddr);
|
|
||||||
uint16_t i2pcpPort; i2p::config::GetOption("i2pcontrol.port", i2pcpPort);
|
|
||||||
LogPrint(eLogInfo, "Daemon: starting I2PControl at ", i2pcpAddr, ":", i2pcpPort);
|
|
||||||
d.m_I2PControlService = std::unique_ptr<i2p::client::I2PControlService>(new i2p::client::I2PControlService (i2pcpAddr, i2pcpPort));
|
|
||||||
d.m_I2PControlService->Start ();
|
|
||||||
}
|
|
||||||
#ifdef WITH_EVENTS
|
|
||||||
|
|
||||||
bool websocket; i2p::config::GetOption("websockets.enabled", websocket);
|
|
||||||
if(websocket) {
|
|
||||||
std::string websocketAddr; i2p::config::GetOption("websockets.address", websocketAddr);
|
|
||||||
uint16_t websocketPort; i2p::config::GetOption("websockets.port", websocketPort);
|
|
||||||
LogPrint(eLogInfo, "Daemon: starting Websocket server at ", websocketAddr, ":", websocketPort);
|
|
||||||
d.m_WebsocketServer = std::unique_ptr<i2p::event::WebsocketServer>(new i2p::event::WebsocketServer (websocketAddr, websocketPort));
|
|
||||||
d.m_WebsocketServer->Start();
|
|
||||||
i2p::event::core.SetListener(d.m_WebsocketServer->ToListener());
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Daemon_Singleton::stop()
|
|
||||||
{
|
|
||||||
#ifdef WITH_EVENTS
|
|
||||||
i2p::event::core.SetListener(nullptr);
|
|
||||||
#endif
|
|
||||||
LogPrint(eLogInfo, "Daemon: shutting down");
|
|
||||||
LogPrint(eLogInfo, "Daemon: stopping Client");
|
|
||||||
i2p::client::context.Stop();
|
|
||||||
LogPrint(eLogInfo, "Daemon: stopping Tunnels");
|
|
||||||
i2p::tunnel::tunnels.Stop();
|
|
||||||
|
|
||||||
if (d.UPnP) {
|
|
||||||
d.UPnP->Stop ();
|
|
||||||
d.UPnP = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
LogPrint(eLogInfo, "Daemon: stopping Transports");
|
|
||||||
i2p::transport::transports.Stop();
|
|
||||||
LogPrint(eLogInfo, "Daemon: stopping NetDB");
|
|
||||||
i2p::data::netdb.Stop();
|
|
||||||
if (d.httpServer) {
|
|
||||||
LogPrint(eLogInfo, "Daemon: stopping HTTP Server");
|
|
||||||
d.httpServer->Stop();
|
|
||||||
d.httpServer = nullptr;
|
|
||||||
}
|
|
||||||
if (d.m_I2PControlService)
|
|
||||||
{
|
|
||||||
LogPrint(eLogInfo, "Daemon: stopping I2PControl");
|
|
||||||
d.m_I2PControlService->Stop ();
|
|
||||||
d.m_I2PControlService = nullptr;
|
|
||||||
}
|
|
||||||
#ifdef WITH_EVENTS
|
|
||||||
if (d.m_WebsocketServer) {
|
|
||||||
LogPrint(eLogInfo, "Daemon: stopping Websocket server");
|
|
||||||
d.m_WebsocketServer->Stop();
|
|
||||||
d.m_WebsocketServer = nullptr;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
i2p::crypto::TerminateCrypto ();
|
|
||||||
i2p::log::Logger().Stop();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
107
Daemon.h
107
Daemon.h
@@ -1,107 +0,0 @@
|
|||||||
#ifndef DAEMON_H__
|
|
||||||
#define DAEMON_H__
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace util
|
|
||||||
{
|
|
||||||
class Daemon_Singleton_Private;
|
|
||||||
class Daemon_Singleton
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual bool init(int argc, char* argv[]);
|
|
||||||
virtual bool start();
|
|
||||||
virtual bool stop();
|
|
||||||
virtual void run () {};
|
|
||||||
|
|
||||||
bool isDaemon;
|
|
||||||
bool running;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
Daemon_Singleton();
|
|
||||||
virtual ~Daemon_Singleton();
|
|
||||||
|
|
||||||
bool IsService () const;
|
|
||||||
|
|
||||||
// d-pointer for httpServer, httpProxy, etc.
|
|
||||||
class Daemon_Singleton_Private;
|
|
||||||
Daemon_Singleton_Private &d;
|
|
||||||
};
|
|
||||||
|
|
||||||
#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:
|
|
||||||
static DaemonWin32& Instance()
|
|
||||||
{
|
|
||||||
static DaemonWin32 instance;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool init(int argc, char* argv[]);
|
|
||||||
bool start();
|
|
||||||
bool stop();
|
|
||||||
void run ();
|
|
||||||
};
|
|
||||||
#else
|
|
||||||
#define Daemon i2p::util::DaemonLinux::Instance()
|
|
||||||
class DaemonLinux : public Daemon_Singleton
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static DaemonLinux& Instance()
|
|
||||||
{
|
|
||||||
static DaemonLinux instance;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool start();
|
|
||||||
bool stop();
|
|
||||||
void run ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::string pidfile;
|
|
||||||
int pidFH;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
int gracefulShutdownInterval; // in seconds
|
|
||||||
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // DAEMON_H__
|
|
||||||
115
DaemonWin32.cpp
115
DaemonWin32.cpp
@@ -1,115 +0,0 @@
|
|||||||
#include <thread>
|
|
||||||
#include <clocale>
|
|
||||||
#include "Config.h"
|
|
||||||
#include "Daemon.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include "Log.h"
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
|
|
||||||
#include "Win32/Win32Service.h"
|
|
||||||
#ifdef WIN32_APP
|
|
||||||
#include "Win32/Win32App.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace util
|
|
||||||
{
|
|
||||||
bool DaemonWin32::init(int argc, char* argv[])
|
|
||||||
{
|
|
||||||
setlocale(LC_CTYPE, "");
|
|
||||||
SetConsoleCP(1251);
|
|
||||||
SetConsoleOutputCP(1251);
|
|
||||||
setlocale(LC_ALL, "Russian");
|
|
||||||
|
|
||||||
if (!Daemon_Singleton::init(argc, argv))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::string serviceControl; i2p::config::GetOption("svcctl", serviceControl);
|
|
||||||
if (serviceControl == "install")
|
|
||||||
{
|
|
||||||
LogPrint(eLogInfo, "WinSVC: installing ", SERVICE_NAME, " as service");
|
|
||||||
InstallService(
|
|
||||||
SERVICE_NAME, // Name of service
|
|
||||||
SERVICE_DISPLAY_NAME, // Name to display
|
|
||||||
SERVICE_START_TYPE, // Service start type
|
|
||||||
SERVICE_DEPENDENCIES, // Dependencies
|
|
||||||
SERVICE_ACCOUNT, // Service running account
|
|
||||||
SERVICE_PASSWORD // Password of the account
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (serviceControl == "remove")
|
|
||||||
{
|
|
||||||
LogPrint(eLogInfo, "WinSVC: uninstalling ", SERVICE_NAME, " service");
|
|
||||||
UninstallService(SERVICE_NAME);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isDaemon)
|
|
||||||
{
|
|
||||||
LogPrint(eLogDebug, "Daemon: running as service");
|
|
||||||
I2PService service(SERVICE_NAME);
|
|
||||||
if (!I2PService::Run(service))
|
|
||||||
{
|
|
||||||
LogPrint(eLogError, "Daemon: Service failed to run w/err 0x%08lx\n", GetLastError());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint(eLogDebug, "Daemon: running as user");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DaemonWin32::start()
|
|
||||||
{
|
|
||||||
setlocale(LC_CTYPE, "");
|
|
||||||
SetConsoleCP(1251);
|
|
||||||
SetConsoleOutputCP(1251);
|
|
||||||
setlocale(LC_ALL, "Russian");
|
|
||||||
#ifdef WIN32_APP
|
|
||||||
if (!i2p::win32::StartWin32App ()) return false;
|
|
||||||
|
|
||||||
// override log
|
|
||||||
i2p::config::SetOption("log", std::string ("file"));
|
|
||||||
#endif
|
|
||||||
bool ret = Daemon_Singleton::start();
|
|
||||||
if (ret && i2p::log::Logger().GetLogType() == eLogFile)
|
|
||||||
{
|
|
||||||
// TODO: find out where this garbage to console comes from
|
|
||||||
SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE);
|
|
||||||
SetStdHandle(STD_ERROR_HANDLE, INVALID_HANDLE_VALUE);
|
|
||||||
}
|
|
||||||
bool insomnia; i2p::config::GetOption("insomnia", insomnia);
|
|
||||||
if (insomnia)
|
|
||||||
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DaemonWin32::stop()
|
|
||||||
{
|
|
||||||
#ifdef WIN32_APP
|
|
||||||
i2p::win32::StopWin32App ();
|
|
||||||
#endif
|
|
||||||
return Daemon_Singleton::stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DaemonWin32::run ()
|
|
||||||
{
|
|
||||||
#ifdef WIN32_APP
|
|
||||||
i2p::win32::RunWin32App ();
|
|
||||||
#else
|
|
||||||
while (running)
|
|
||||||
{
|
|
||||||
std::this_thread::sleep_for (std::chrono::seconds(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
446
Datagram.cpp
446
Datagram.cpp
@@ -1,446 +0,0 @@
|
|||||||
#include <string.h>
|
|
||||||
#include <vector>
|
|
||||||
#include "Crypto.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "TunnelBase.h"
|
|
||||||
#include "RouterContext.h"
|
|
||||||
#include "Destination.h"
|
|
||||||
#include "Datagram.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace datagram
|
|
||||||
{
|
|
||||||
DatagramDestination::DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner):
|
|
||||||
m_Owner (owner.get()),
|
|
||||||
m_Receiver (nullptr)
|
|
||||||
{
|
|
||||||
m_Identity.FromBase64 (owner->GetIdentity()->ToBase64());
|
|
||||||
}
|
|
||||||
|
|
||||||
DatagramDestination::~DatagramDestination ()
|
|
||||||
{
|
|
||||||
m_Sessions.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DatagramDestination::SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort, uint16_t toPort)
|
|
||||||
{
|
|
||||||
auto owner = m_Owner;
|
|
||||||
std::vector<uint8_t> v(MAX_DATAGRAM_SIZE);
|
|
||||||
uint8_t * buf = v.data();
|
|
||||||
auto identityLen = m_Identity.ToBuffer (buf, MAX_DATAGRAM_SIZE);
|
|
||||||
uint8_t * signature = buf + identityLen;
|
|
||||||
auto signatureLen = m_Identity.GetSignatureLen ();
|
|
||||||
uint8_t * buf1 = signature + signatureLen;
|
|
||||||
size_t headerLen = identityLen + signatureLen;
|
|
||||||
|
|
||||||
memcpy (buf1, payload, len);
|
|
||||||
if (m_Identity.GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
|
|
||||||
{
|
|
||||||
uint8_t hash[32];
|
|
||||||
SHA256(buf1, len, hash);
|
|
||||||
owner->Sign (hash, 32, signature);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
owner->Sign (buf1, len, signature);
|
|
||||||
|
|
||||||
auto msg = CreateDataMessage (buf, len + headerLen, fromPort, toPort);
|
|
||||||
auto session = ObtainSession(ident);
|
|
||||||
session->SendMsg(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort,uint8_t * const &buf, size_t len)
|
|
||||||
{
|
|
||||||
i2p::data::IdentityEx identity;
|
|
||||||
size_t identityLen = identity.FromBuffer (buf, len);
|
|
||||||
const uint8_t * signature = buf + identityLen;
|
|
||||||
size_t headerLen = identityLen + identity.GetSignatureLen ();
|
|
||||||
|
|
||||||
bool verified = false;
|
|
||||||
if (identity.GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
|
|
||||||
{
|
|
||||||
uint8_t hash[32];
|
|
||||||
SHA256(buf + headerLen, len - headerLen, hash);
|
|
||||||
verified = identity.Verify (hash, 32, signature);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
verified = identity.Verify (buf + headerLen, len - headerLen, signature);
|
|
||||||
|
|
||||||
if (verified)
|
|
||||||
{
|
|
||||||
auto r = FindReceiver(toPort);
|
|
||||||
if(r)
|
|
||||||
r(identity, fromPort, toPort, buf + headerLen, len -headerLen);
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "DatagramDestination: no receiver for port ", toPort);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "Datagram signature verification failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
DatagramDestination::Receiver DatagramDestination::FindReceiver(uint16_t port)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_ReceiversMutex);
|
|
||||||
Receiver r = m_Receiver;
|
|
||||||
auto itr = m_ReceiversByPorts.find(port);
|
|
||||||
if (itr != m_ReceiversByPorts.end())
|
|
||||||
r = itr->second;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
// unzip it
|
|
||||||
uint8_t uncompressed[MAX_DATAGRAM_SIZE];
|
|
||||||
size_t uncompressedLen = m_Inflator.Inflate (buf, len, uncompressed, MAX_DATAGRAM_SIZE);
|
|
||||||
if (uncompressedLen)
|
|
||||||
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort)
|
|
||||||
{
|
|
||||||
auto msg = NewI2NPMessage ();
|
|
||||||
uint8_t * buf = msg->GetPayload ();
|
|
||||||
buf += 4; // reserve for length
|
|
||||||
size_t size = m_Deflator.Deflate (payload, len, buf, msg->maxLen - msg->len);
|
|
||||||
if (size)
|
|
||||||
{
|
|
||||||
htobe32buf (msg->GetPayload (), size); // length
|
|
||||||
htobe16buf (buf + 4, fromPort); // source port
|
|
||||||
htobe16buf (buf + 6, toPort); // destination port
|
|
||||||
buf[9] = i2p::client::PROTOCOL_TYPE_DATAGRAM; // datagram protocol
|
|
||||||
msg->len += size + 4;
|
|
||||||
msg->FillI2NPMessageHeader (eI2NPData);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
msg = nullptr;
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DatagramDestination::CleanUp ()
|
|
||||||
{
|
|
||||||
if (m_Sessions.empty ()) return;
|
|
||||||
auto now = i2p::util::GetMillisecondsSinceEpoch();
|
|
||||||
LogPrint(eLogDebug, "DatagramDestination: clean up sessions");
|
|
||||||
std::unique_lock<std::mutex> lock(m_SessionsMutex);
|
|
||||||
// for each session ...
|
|
||||||
for (auto it = m_Sessions.begin (); it != m_Sessions.end (); )
|
|
||||||
{
|
|
||||||
// check if expired
|
|
||||||
if (now - it->second->LastActivity() >= DATAGRAM_SESSION_MAX_IDLE)
|
|
||||||
{
|
|
||||||
LogPrint(eLogInfo, "DatagramDestination: expiring idle session with ", it->first.ToBase32());
|
|
||||||
it = m_Sessions.erase (it); // we are expired
|
|
||||||
}
|
|
||||||
else
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<DatagramSession> DatagramDestination::ObtainSession(const i2p::data::IdentHash & ident)
|
|
||||||
{
|
|
||||||
std::shared_ptr<DatagramSession> session = nullptr;
|
|
||||||
std::lock_guard<std::mutex> lock(m_SessionsMutex);
|
|
||||||
auto itr = m_Sessions.find(ident);
|
|
||||||
if (itr == m_Sessions.end()) {
|
|
||||||
// not found, create new session
|
|
||||||
session = std::make_shared<DatagramSession>(m_Owner, ident);
|
|
||||||
m_Sessions[ident] = session;
|
|
||||||
} else {
|
|
||||||
session = itr->second;
|
|
||||||
}
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<DatagramSession::Info> DatagramDestination::GetInfoForRemote(const i2p::data::IdentHash & remote)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_SessionsMutex);
|
|
||||||
for ( auto & item : m_Sessions)
|
|
||||||
{
|
|
||||||
if(item.first == remote) return std::make_shared<DatagramSession::Info>(item.second->GetSessionInfo());
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
DatagramSession::DatagramSession(i2p::client::ClientDestination * localDestination,
|
|
||||||
const i2p::data::IdentHash & remoteIdent) :
|
|
||||||
m_LocalDestination(localDestination),
|
|
||||||
m_RemoteIdentity(remoteIdent),
|
|
||||||
m_LastUse(i2p::util::GetMillisecondsSinceEpoch ()),
|
|
||||||
m_LastPathChange(0),
|
|
||||||
m_LastSuccess(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void DatagramSession::SendMsg(std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
// we used this session
|
|
||||||
m_LastUse = i2p::util::GetMillisecondsSinceEpoch();
|
|
||||||
// schedule send
|
|
||||||
m_LocalDestination->GetService().post(std::bind(&DatagramSession::HandleSend, this, msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
DatagramSession::Info DatagramSession::GetSessionInfo() const
|
|
||||||
{
|
|
||||||
if(!m_RoutingSession)
|
|
||||||
return DatagramSession::Info(nullptr, nullptr, m_LastUse, m_LastSuccess);
|
|
||||||
|
|
||||||
auto routingPath = m_RoutingSession->GetSharedRoutingPath();
|
|
||||||
if (!routingPath)
|
|
||||||
return DatagramSession::Info(nullptr, nullptr, m_LastUse, m_LastSuccess);
|
|
||||||
auto lease = routingPath->remoteLease;
|
|
||||||
auto tunnel = routingPath->outboundTunnel;
|
|
||||||
if(lease)
|
|
||||||
{
|
|
||||||
if(tunnel)
|
|
||||||
return DatagramSession::Info(lease->tunnelGateway, tunnel->GetEndpointIdentHash(), m_LastUse, m_LastSuccess);
|
|
||||||
else
|
|
||||||
return DatagramSession::Info(lease->tunnelGateway, nullptr, m_LastUse, m_LastSuccess);
|
|
||||||
}
|
|
||||||
else if(tunnel)
|
|
||||||
return DatagramSession::Info(nullptr, tunnel->GetEndpointIdentHash(), m_LastUse, m_LastSuccess);
|
|
||||||
else
|
|
||||||
return DatagramSession::Info(nullptr, nullptr, m_LastUse, m_LastSuccess);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DatagramSession::HandleSend(std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
if(!m_RoutingSession)
|
|
||||||
{
|
|
||||||
// try to get one
|
|
||||||
if(m_RemoteLeaseSet) m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
UpdateLeaseSet(msg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// do we have a routing session?
|
|
||||||
if(m_RoutingSession)
|
|
||||||
{
|
|
||||||
// should we switch paths?
|
|
||||||
if(ShouldUpdateRoutingPath ())
|
|
||||||
{
|
|
||||||
LogPrint(eLogDebug, "DatagramSession: try getting new routing path");
|
|
||||||
// try switching paths
|
|
||||||
auto path = GetNextRoutingPath();
|
|
||||||
if(path)
|
|
||||||
UpdateRoutingPath (path);
|
|
||||||
else
|
|
||||||
ResetRoutingPath();
|
|
||||||
}
|
|
||||||
auto routingPath = m_RoutingSession->GetSharedRoutingPath ();
|
|
||||||
// make sure we have a routing path
|
|
||||||
if (routingPath)
|
|
||||||
{
|
|
||||||
auto outboundTunnel = routingPath->outboundTunnel;
|
|
||||||
if (outboundTunnel)
|
|
||||||
{
|
|
||||||
if(outboundTunnel->IsEstablished())
|
|
||||||
{
|
|
||||||
m_LastSuccess = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
// we have a routing path and routing session and the outbound tunnel we are using is good
|
|
||||||
// wrap message with routing session and send down routing path's outbound tunnel wrapped for the IBGW
|
|
||||||
auto m = m_RoutingSession->WrapSingleMessage(msg);
|
|
||||||
routingPath->outboundTunnel->SendTunnelDataMsg({i2p::tunnel::TunnelMessageBlock{
|
|
||||||
i2p::tunnel::eDeliveryTypeTunnel,
|
|
||||||
routingPath->remoteLease->tunnelGateway, routingPath->remoteLease->tunnelID,
|
|
||||||
m
|
|
||||||
}});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto now = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
// if this path looks dead reset the routing path since we didn't seem to be able to get a path in time
|
|
||||||
if (m_LastPathChange && now - m_LastPathChange >= DATAGRAM_SESSION_PATH_TIMEOUT ) ResetRoutingPath();
|
|
||||||
UpdateLeaseSet(msg);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void DatagramSession::UpdateRoutingPath(const std::shared_ptr<i2p::garlic::GarlicRoutingPath> & path)
|
|
||||||
{
|
|
||||||
if(m_RoutingSession == nullptr && m_RemoteLeaseSet)
|
|
||||||
m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true);
|
|
||||||
if(!m_RoutingSession) return;
|
|
||||||
// set routing path and update time we last updated the routing path
|
|
||||||
m_RoutingSession->SetSharedRoutingPath (path);
|
|
||||||
m_LastPathChange = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DatagramSession::ShouldUpdateRoutingPath() const
|
|
||||||
{
|
|
||||||
bool dead = m_RoutingSession == nullptr || m_RoutingSession->GetSharedRoutingPath () == nullptr;
|
|
||||||
auto now = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
// we need to rotate paths becuase the routing path is too old
|
|
||||||
// if (now - m_LastPathChange >= DATAGRAM_SESSION_PATH_SWITCH_INTERVAL) return true;
|
|
||||||
// too fast switching paths
|
|
||||||
if (now - m_LastPathChange < DATAGRAM_SESSION_PATH_MIN_LIFETIME ) return false;
|
|
||||||
// our path looks dead so we need to rotate paths
|
|
||||||
if (now - m_LastSuccess >= DATAGRAM_SESSION_PATH_TIMEOUT) return !dead;
|
|
||||||
// if we have a routing session and routing path we don't need to switch paths
|
|
||||||
return dead;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool DatagramSession::ShouldSwitchLease() const
|
|
||||||
{
|
|
||||||
std::shared_ptr<i2p::garlic::GarlicRoutingPath> routingPath = nullptr;
|
|
||||||
std::shared_ptr<const i2p::data::Lease> currentLease = nullptr;
|
|
||||||
if(m_RoutingSession)
|
|
||||||
routingPath = m_RoutingSession->GetSharedRoutingPath ();
|
|
||||||
if(routingPath)
|
|
||||||
currentLease = routingPath->remoteLease;
|
|
||||||
if(currentLease) // if we have a lease return true if it's about to expire otherwise return false
|
|
||||||
return currentLease->ExpiresWithin( DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW, DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE );
|
|
||||||
// we have no current lease, we should switch
|
|
||||||
return currentLease == nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<i2p::garlic::GarlicRoutingPath> DatagramSession::GetNextRoutingPath()
|
|
||||||
{
|
|
||||||
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel = nullptr;
|
|
||||||
std::shared_ptr<i2p::garlic::GarlicRoutingPath> routingPath = nullptr;
|
|
||||||
// get existing routing path if we have one
|
|
||||||
if(m_RoutingSession)
|
|
||||||
routingPath = m_RoutingSession->GetSharedRoutingPath();
|
|
||||||
// do we have an existing outbound tunnel and routing path?
|
|
||||||
if(routingPath && routingPath->outboundTunnel)
|
|
||||||
{
|
|
||||||
// is the outbound tunnel we are using good?
|
|
||||||
if (routingPath->outboundTunnel->IsEstablished())
|
|
||||||
{
|
|
||||||
// ya so let's stick with it
|
|
||||||
outboundTunnel = routingPath->outboundTunnel;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(routingPath->outboundTunnel); // no so we'll switch outbound tunnels
|
|
||||||
}
|
|
||||||
// do we have an outbound tunnel that works already ?
|
|
||||||
if(!outboundTunnel)
|
|
||||||
outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(); // no, let's get a new outbound tunnel as we probably just started
|
|
||||||
|
|
||||||
if(outboundTunnel)
|
|
||||||
{
|
|
||||||
std::shared_ptr<const i2p::data::Lease> lease = nullptr;
|
|
||||||
// should we switch leases ?
|
|
||||||
if (ShouldSwitchLease ())
|
|
||||||
{
|
|
||||||
// yes, get next available lease
|
|
||||||
lease = GetNextLease();
|
|
||||||
}
|
|
||||||
else if (routingPath)
|
|
||||||
{
|
|
||||||
if(routingPath->remoteLease)
|
|
||||||
{
|
|
||||||
if(routingPath->remoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW, DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE))
|
|
||||||
lease = GetNextLease();
|
|
||||||
else
|
|
||||||
lease = routingPath->remoteLease;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
lease = GetNextLease();
|
|
||||||
if(lease)
|
|
||||||
{
|
|
||||||
// we have a valid lease to use and an outbound tunnel
|
|
||||||
// create new routing path
|
|
||||||
uint32_t now = i2p::util::GetSecondsSinceEpoch();
|
|
||||||
routingPath = std::make_shared<i2p::garlic::GarlicRoutingPath>(i2p::garlic::GarlicRoutingPath{
|
|
||||||
outboundTunnel,
|
|
||||||
lease,
|
|
||||||
0,
|
|
||||||
now,
|
|
||||||
0
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else // we don't have a new routing path to give
|
|
||||||
routingPath = nullptr;
|
|
||||||
}
|
|
||||||
return routingPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DatagramSession::ResetRoutingPath()
|
|
||||||
{
|
|
||||||
if(m_RoutingSession)
|
|
||||||
{
|
|
||||||
auto routingPath = m_RoutingSession->GetSharedRoutingPath();
|
|
||||||
if(routingPath && routingPath->remoteLease) // we have a remote lease already specified and a routing path
|
|
||||||
{
|
|
||||||
// get outbound tunnel on this path
|
|
||||||
auto outboundTunnel = routingPath->outboundTunnel;
|
|
||||||
// is this outbound tunnel there and established
|
|
||||||
if (outboundTunnel && outboundTunnel->IsEstablished())
|
|
||||||
m_InvalidIBGW.push_back(routingPath->remoteLease->tunnelGateway); // yes, let's mark remote lease as dead because the outbound tunnel seems fine
|
|
||||||
}
|
|
||||||
// reset the routing path
|
|
||||||
UpdateRoutingPath(nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<const i2p::data::Lease> DatagramSession::GetNextLease()
|
|
||||||
{
|
|
||||||
auto now = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
std::shared_ptr<const i2p::data::Lease> next = nullptr;
|
|
||||||
if(m_RemoteLeaseSet)
|
|
||||||
{
|
|
||||||
std::vector<i2p::data::IdentHash> exclude;
|
|
||||||
for(const auto & ident : m_InvalidIBGW)
|
|
||||||
exclude.push_back(ident);
|
|
||||||
// find get all leases that are not in our ban list and are not going to expire within our lease set handover window + fudge
|
|
||||||
auto leases = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding( [&exclude, now] (const i2p::data::Lease & l) -> bool {
|
|
||||||
if(exclude.size())
|
|
||||||
{
|
|
||||||
auto end = std::end(exclude);
|
|
||||||
return std::find_if(exclude.begin(), end, [l, now] ( const i2p::data::IdentHash & ident) -> bool {
|
|
||||||
return ident == l.tunnelGateway;
|
|
||||||
}) != end;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
if(leases.size())
|
|
||||||
{
|
|
||||||
// pick random valid next lease
|
|
||||||
uint32_t idx = rand() % leases.size();
|
|
||||||
next = leases[idx];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint(eLogWarning, "DatagramDestination: no leases to use");
|
|
||||||
}
|
|
||||||
return next;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DatagramSession::UpdateLeaseSet(std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
LogPrint(eLogInfo, "DatagramSession: updating lease set");
|
|
||||||
m_LocalDestination->RequestDestination(m_RemoteIdentity, std::bind(&DatagramSession::HandleGotLeaseSet, this, std::placeholders::_1, msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
void DatagramSession::HandleGotLeaseSet(std::shared_ptr<const i2p::data::LeaseSet> remoteIdent, std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
if(remoteIdent)
|
|
||||||
{
|
|
||||||
// update routing session
|
|
||||||
if(m_RoutingSession)
|
|
||||||
m_RoutingSession = nullptr;
|
|
||||||
m_RoutingSession = m_LocalDestination->GetRoutingSession(remoteIdent, true);
|
|
||||||
// clear invalid IBGW as we have a new lease set
|
|
||||||
m_InvalidIBGW.clear();
|
|
||||||
m_RemoteLeaseSet = remoteIdent;
|
|
||||||
// update routing path
|
|
||||||
auto path = GetNextRoutingPath();
|
|
||||||
if (path)
|
|
||||||
UpdateRoutingPath(path);
|
|
||||||
else
|
|
||||||
ResetRoutingPath();
|
|
||||||
// send the message that was queued if it was provided
|
|
||||||
if(msg)
|
|
||||||
HandleSend(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
158
Datagram.h
158
Datagram.h
@@ -1,158 +0,0 @@
|
|||||||
#ifndef DATAGRAM_H__
|
|
||||||
#define DATAGRAM_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <memory>
|
|
||||||
#include <functional>
|
|
||||||
#include <map>
|
|
||||||
#include "Base.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "LeaseSet.h"
|
|
||||||
#include "I2NPProtocol.h"
|
|
||||||
#include "Garlic.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
class ClientDestination;
|
|
||||||
}
|
|
||||||
namespace datagram
|
|
||||||
{
|
|
||||||
// milliseconds for max session idle time
|
|
||||||
const uint64_t DATAGRAM_SESSION_MAX_IDLE = 10 * 60 * 1000;
|
|
||||||
// milliseconds for how long we try sticking to a dead routing path before trying to switch
|
|
||||||
const uint64_t DATAGRAM_SESSION_PATH_TIMEOUT = 10 * 1000;
|
|
||||||
// milliseconds interval a routing path is used before switching
|
|
||||||
const uint64_t DATAGRAM_SESSION_PATH_SWITCH_INTERVAL = 20 * 60 * 1000;
|
|
||||||
// milliseconds before lease expire should we try switching leases
|
|
||||||
const uint64_t DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW = 10 * 1000;
|
|
||||||
// milliseconds fudge factor for leases handover
|
|
||||||
const uint64_t DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE = 1000;
|
|
||||||
// milliseconds minimum time between path switches
|
|
||||||
const uint64_t DATAGRAM_SESSION_PATH_MIN_LIFETIME = 5 * 1000;
|
|
||||||
|
|
||||||
class DatagramSession
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DatagramSession(i2p::client::ClientDestination * localDestination,
|
|
||||||
const i2p::data::IdentHash & remoteIdent);
|
|
||||||
|
|
||||||
/** send an i2np message to remote endpoint for this session */
|
|
||||||
void SendMsg(std::shared_ptr<I2NPMessage> msg);
|
|
||||||
/** get the last time in milliseconds for when we used this datagram session */
|
|
||||||
uint64_t LastActivity() const { return m_LastUse; }
|
|
||||||
/** get the last time in milliseconds when we successfully sent data */
|
|
||||||
uint64_t LastSuccess() const { return m_LastSuccess; }
|
|
||||||
struct Info
|
|
||||||
{
|
|
||||||
std::shared_ptr<const i2p::data::IdentHash> IBGW;
|
|
||||||
std::shared_ptr<const i2p::data::IdentHash> OBEP;
|
|
||||||
const uint64_t activity;
|
|
||||||
const uint64_t success;
|
|
||||||
Info() : IBGW(nullptr), OBEP(nullptr), activity(0), success(0) {}
|
|
||||||
Info(const uint8_t * ibgw, const uint8_t * obep, const uint64_t a, const uint64_t s) :
|
|
||||||
activity(a),
|
|
||||||
success(s) {
|
|
||||||
if(ibgw) IBGW = std::make_shared<i2p::data::IdentHash>(ibgw);
|
|
||||||
else IBGW = nullptr;
|
|
||||||
if(obep) OBEP = std::make_shared<i2p::data::IdentHash>(obep);
|
|
||||||
else OBEP = nullptr;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Info GetSessionInfo() const;
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
/** update our routing path we are using, mark that we have changed paths */
|
|
||||||
void UpdateRoutingPath(const std::shared_ptr<i2p::garlic::GarlicRoutingPath> & path);
|
|
||||||
|
|
||||||
/** return true if we should switch routing paths because of path lifetime or timeout otherwise false */
|
|
||||||
bool ShouldUpdateRoutingPath() const;
|
|
||||||
|
|
||||||
/** return true if we should switch the lease for out routing path otherwise return false */
|
|
||||||
bool ShouldSwitchLease() const;
|
|
||||||
|
|
||||||
/** get next usable routing path, try reusing outbound tunnels */
|
|
||||||
std::shared_ptr<i2p::garlic::GarlicRoutingPath> GetNextRoutingPath();
|
|
||||||
/**
|
|
||||||
* mark current routing path as invalid and clear it
|
|
||||||
* if the outbound tunnel we were using was okay don't use the IBGW in the routing path's lease next time
|
|
||||||
*/
|
|
||||||
void ResetRoutingPath();
|
|
||||||
|
|
||||||
/** get next usable lease, does not fetch or update if expired or have no lease set */
|
|
||||||
std::shared_ptr<const i2p::data::Lease> GetNextLease();
|
|
||||||
|
|
||||||
void HandleSend(std::shared_ptr<I2NPMessage> msg);
|
|
||||||
void HandleGotLeaseSet(std::shared_ptr<const i2p::data::LeaseSet> remoteIdent,
|
|
||||||
std::shared_ptr<I2NPMessage> msg);
|
|
||||||
void UpdateLeaseSet(std::shared_ptr<I2NPMessage> msg=nullptr);
|
|
||||||
|
|
||||||
private:
|
|
||||||
i2p::client::ClientDestination * m_LocalDestination;
|
|
||||||
i2p::data::IdentHash m_RemoteIdentity;
|
|
||||||
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
|
|
||||||
// Ident hash of IBGW that are invalid
|
|
||||||
std::vector<i2p::data::IdentHash> m_InvalidIBGW;
|
|
||||||
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
|
|
||||||
uint64_t m_LastUse;
|
|
||||||
uint64_t m_LastPathChange;
|
|
||||||
uint64_t m_LastSuccess;
|
|
||||||
};
|
|
||||||
|
|
||||||
const size_t MAX_DATAGRAM_SIZE = 32768;
|
|
||||||
class DatagramDestination
|
|
||||||
{
|
|
||||||
typedef std::function<void (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)> Receiver;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
|
|
||||||
DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner);
|
|
||||||
~DatagramDestination ();
|
|
||||||
|
|
||||||
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort = 0, uint16_t toPort = 0);
|
|
||||||
void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
|
||||||
|
|
||||||
void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; };
|
|
||||||
void ResetReceiver () { m_Receiver = nullptr; };
|
|
||||||
|
|
||||||
void SetReceiver (const Receiver& receiver, uint16_t port) { std::lock_guard<std::mutex> lock(m_ReceiversMutex); m_ReceiversByPorts[port] = receiver; };
|
|
||||||
void ResetReceiver (uint16_t port) { std::lock_guard<std::mutex> lock(m_ReceiversMutex); m_ReceiversByPorts.erase (port); };
|
|
||||||
|
|
||||||
std::shared_ptr<DatagramSession::Info> GetInfoForRemote(const i2p::data::IdentHash & remote);
|
|
||||||
|
|
||||||
// clean up stale sessions
|
|
||||||
void CleanUp ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::shared_ptr<DatagramSession> ObtainSession(const i2p::data::IdentHash & ident);
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort);
|
|
||||||
|
|
||||||
void HandleDatagram (uint16_t fromPort, uint16_t toPort, uint8_t *const& buf, size_t len);
|
|
||||||
|
|
||||||
/** find a receiver by port, if none by port is found try default receiever, otherwise returns nullptr */
|
|
||||||
Receiver FindReceiver(uint16_t port);
|
|
||||||
|
|
||||||
private:
|
|
||||||
i2p::client::ClientDestination * m_Owner;
|
|
||||||
i2p::data::IdentityEx m_Identity;
|
|
||||||
Receiver m_Receiver; // default
|
|
||||||
std::mutex m_SessionsMutex;
|
|
||||||
std::map<i2p::data::IdentHash, std::shared_ptr<DatagramSession> > m_Sessions;
|
|
||||||
std::mutex m_ReceiversMutex;
|
|
||||||
std::map<uint16_t, Receiver> m_ReceiversByPorts;
|
|
||||||
|
|
||||||
i2p::data::GzipInflator m_Inflator;
|
|
||||||
i2p::data::GzipDeflator m_Deflator;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
913
Destination.cpp
913
Destination.cpp
@@ -1,913 +0,0 @@
|
|||||||
#include <algorithm>
|
|
||||||
#include <cassert>
|
|
||||||
#include "Crypto.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "FS.h"
|
|
||||||
#include "Timestamp.h"
|
|
||||||
#include "NetDb.h"
|
|
||||||
#include "Destination.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
LeaseSetDestination::LeaseSetDestination (bool isPublic, const std::map<std::string, std::string> * params):
|
|
||||||
m_IsRunning (false), m_Thread (nullptr), m_IsPublic (isPublic),
|
|
||||||
m_PublishReplyToken (0), m_PublishConfirmationTimer (m_Service),
|
|
||||||
m_PublishVerificationTimer (m_Service), m_CleanupTimer (m_Service)
|
|
||||||
{
|
|
||||||
int inLen = DEFAULT_INBOUND_TUNNEL_LENGTH;
|
|
||||||
int inQty = DEFAULT_INBOUND_TUNNELS_QUANTITY;
|
|
||||||
int outLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH;
|
|
||||||
int outQty = DEFAULT_OUTBOUND_TUNNELS_QUANTITY;
|
|
||||||
int numTags = DEFAULT_TAGS_TO_SEND;
|
|
||||||
std::shared_ptr<std::vector<i2p::data::IdentHash> > explicitPeers;
|
|
||||||
try {
|
|
||||||
if (params) {
|
|
||||||
auto it = params->find (I2CP_PARAM_INBOUND_TUNNEL_LENGTH);
|
|
||||||
if (it != params->end ())
|
|
||||||
inLen = std::stoi(it->second);
|
|
||||||
it = params->find (I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH);
|
|
||||||
if (it != params->end ())
|
|
||||||
outLen = std::stoi(it->second);
|
|
||||||
it = params->find (I2CP_PARAM_INBOUND_TUNNELS_QUANTITY);
|
|
||||||
if (it != params->end ())
|
|
||||||
inQty = std::stoi(it->second);
|
|
||||||
it = params->find (I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY);
|
|
||||||
if (it != params->end ())
|
|
||||||
outQty = std::stoi(it->second);
|
|
||||||
it = params->find (I2CP_PARAM_TAGS_TO_SEND);
|
|
||||||
if (it != params->end ())
|
|
||||||
numTags = std::stoi(it->second);
|
|
||||||
LogPrint (eLogInfo, "Destination: parameters for tunnel set to: ", inQty, " inbound (", inLen, " hops), ", outQty, " outbound (", outLen, " hops), ", numTags, " tags");
|
|
||||||
it = params->find (I2CP_PARAM_EXPLICIT_PEERS);
|
|
||||||
if (it != params->end ())
|
|
||||||
{
|
|
||||||
explicitPeers = std::make_shared<std::vector<i2p::data::IdentHash> >();
|
|
||||||
std::stringstream ss(it->second);
|
|
||||||
std::string b64;
|
|
||||||
while (std::getline (ss, b64, ','))
|
|
||||||
{
|
|
||||||
i2p::data::IdentHash ident;
|
|
||||||
ident.FromBase64 (b64);
|
|
||||||
explicitPeers->push_back (ident);
|
|
||||||
LogPrint (eLogInfo, "Destination: Added to explicit peers list: ", b64);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (std::exception & ex) {
|
|
||||||
LogPrint(eLogError, "Destination: unable to parse parameters for destination: ", ex.what());
|
|
||||||
}
|
|
||||||
SetNumTags (numTags);
|
|
||||||
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inLen, outLen, inQty, outQty);
|
|
||||||
if (explicitPeers)
|
|
||||||
m_Pool->SetExplicitPeers (explicitPeers);
|
|
||||||
if(params)
|
|
||||||
{
|
|
||||||
auto itr = params->find(I2CP_PARAM_MAX_TUNNEL_LATENCY);
|
|
||||||
if (itr != params->end()) {
|
|
||||||
auto maxlatency = std::stoi(itr->second);
|
|
||||||
itr = params->find(I2CP_PARAM_MIN_TUNNEL_LATENCY);
|
|
||||||
if (itr != params->end()) {
|
|
||||||
auto minlatency = std::stoi(itr->second);
|
|
||||||
if ( minlatency > 0 && maxlatency > 0 ) {
|
|
||||||
// set tunnel pool latency
|
|
||||||
LogPrint(eLogInfo, "Destination: requiring tunnel latency [", minlatency, "ms, ", maxlatency, "ms]");
|
|
||||||
m_Pool->RequireLatency(minlatency, maxlatency);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LeaseSetDestination::~LeaseSetDestination ()
|
|
||||||
{
|
|
||||||
if (m_IsRunning)
|
|
||||||
Stop ();
|
|
||||||
if (m_Pool)
|
|
||||||
i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool);
|
|
||||||
for (auto& it: m_LeaseSetRequests)
|
|
||||||
it.second->Complete (nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LeaseSetDestination::Run ()
|
|
||||||
{
|
|
||||||
while (m_IsRunning)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_Service.run ();
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Destination: runtime exception: ", ex.what ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LeaseSetDestination::Start ()
|
|
||||||
{
|
|
||||||
if (!m_IsRunning)
|
|
||||||
{
|
|
||||||
m_IsRunning = true;
|
|
||||||
m_Pool->SetLocalDestination (shared_from_this ());
|
|
||||||
m_Pool->SetActive (true);
|
|
||||||
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
|
|
||||||
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
|
|
||||||
shared_from_this (), std::placeholders::_1));
|
|
||||||
m_Thread = new std::thread (std::bind (&LeaseSetDestination::Run, shared_from_this ()));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LeaseSetDestination::Stop ()
|
|
||||||
{
|
|
||||||
if (m_IsRunning)
|
|
||||||
{
|
|
||||||
m_CleanupTimer.cancel ();
|
|
||||||
m_PublishConfirmationTimer.cancel ();
|
|
||||||
m_PublishVerificationTimer.cancel ();
|
|
||||||
|
|
||||||
m_IsRunning = false;
|
|
||||||
if (m_Pool)
|
|
||||||
{
|
|
||||||
m_Pool->SetLocalDestination (nullptr);
|
|
||||||
i2p::tunnel::tunnels.StopTunnelPool (m_Pool);
|
|
||||||
}
|
|
||||||
m_Service.stop ();
|
|
||||||
if (m_Thread)
|
|
||||||
{
|
|
||||||
m_Thread->join ();
|
|
||||||
delete m_Thread;
|
|
||||||
m_Thread = 0;
|
|
||||||
}
|
|
||||||
CleanUp (); // GarlicDestination
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<const i2p::data::LeaseSet> LeaseSetDestination::FindLeaseSet (const i2p::data::IdentHash& ident)
|
|
||||||
{
|
|
||||||
std::shared_ptr<i2p::data::LeaseSet> remoteLS;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
|
|
||||||
auto it = m_RemoteLeaseSets.find (ident);
|
|
||||||
if (it != m_RemoteLeaseSets.end ())
|
|
||||||
remoteLS = it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remoteLS)
|
|
||||||
{
|
|
||||||
if (!remoteLS->IsExpired ())
|
|
||||||
{
|
|
||||||
if (remoteLS->ExpiresSoon())
|
|
||||||
{
|
|
||||||
LogPrint(eLogDebug, "Destination: Lease Set expires soon, updating before expire");
|
|
||||||
// update now before expiration for smooth handover
|
|
||||||
auto s = shared_from_this ();
|
|
||||||
RequestDestination(ident, [s, ident] (std::shared_ptr<i2p::data::LeaseSet> ls) {
|
|
||||||
if(ls && !ls->IsExpired())
|
|
||||||
{
|
|
||||||
ls->PopulateLeases();
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> _lock(s->m_RemoteLeaseSetsMutex);
|
|
||||||
s->m_RemoteLeaseSets[ident] = ls;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return remoteLS;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Destination: remote LeaseSet expired");
|
|
||||||
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
|
|
||||||
m_RemoteLeaseSets.erase (ident);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto ls = i2p::data::netdb.FindLeaseSet (ident);
|
|
||||||
if (ls && !ls->IsExpired ())
|
|
||||||
{
|
|
||||||
ls->PopulateLeases (); // since we don't store them in netdb
|
|
||||||
std::lock_guard<std::mutex> _lock(m_RemoteLeaseSetsMutex);
|
|
||||||
m_RemoteLeaseSets[ident] = ls;
|
|
||||||
return ls;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<const i2p::data::LocalLeaseSet> LeaseSetDestination::GetLeaseSet ()
|
|
||||||
{
|
|
||||||
if (!m_Pool) return nullptr;
|
|
||||||
if (!m_LeaseSet)
|
|
||||||
UpdateLeaseSet ();
|
|
||||||
std::lock_guard<std::mutex> l(m_LeaseSetMutex);
|
|
||||||
return m_LeaseSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LeaseSetDestination::SetLeaseSet (i2p::data::LocalLeaseSet * newLeaseSet)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> l(m_LeaseSetMutex);
|
|
||||||
m_LeaseSet.reset (newLeaseSet);
|
|
||||||
}
|
|
||||||
i2p::garlic::GarlicDestination::SetLeaseSetUpdated ();
|
|
||||||
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 LeaseSetDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag)
|
|
||||||
{
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
uint8_t k[32], t[32];
|
|
||||||
} data;
|
|
||||||
memcpy (data.k, key, 32);
|
|
||||||
memcpy (data.t, tag, 32);
|
|
||||||
auto s = shared_from_this ();
|
|
||||||
m_Service.post ([s,data](void)
|
|
||||||
{
|
|
||||||
s->AddSessionKey (data.k, data.t);
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LeaseSetDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
m_Service.post (std::bind (&LeaseSetDestination::HandleGarlicMessage, shared_from_this (), msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
void LeaseSetDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
m_Service.post (std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
case eI2NPData:
|
|
||||||
HandleDataMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET));
|
|
||||||
break;
|
|
||||||
case eI2NPDeliveryStatus:
|
|
||||||
// we assume tunnel tests non-encrypted
|
|
||||||
HandleDeliveryStatusMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from));
|
|
||||||
break;
|
|
||||||
case eI2NPDatabaseStore:
|
|
||||||
HandleDatabaseStoreMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET));
|
|
||||||
break;
|
|
||||||
case eI2NPDatabaseSearchReply:
|
|
||||||
HandleDatabaseSearchReplyMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void 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;
|
|
||||||
if (replyToken)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Destination: Reply token is ignored for DatabaseStore");
|
|
||||||
offset += 36;
|
|
||||||
}
|
|
||||||
i2p::data::IdentHash key (buf + DATABASE_STORE_KEY_OFFSET);
|
|
||||||
std::shared_ptr<i2p::data::LeaseSet> leaseSet;
|
|
||||||
if (buf[DATABASE_STORE_TYPE_OFFSET] == 1) // LeaseSet
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "Destination: Remote LeaseSet");
|
|
||||||
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
|
|
||||||
auto it = m_RemoteLeaseSets.find (key);
|
|
||||||
if (it != m_RemoteLeaseSets.end ())
|
|
||||||
{
|
|
||||||
leaseSet = it->second;
|
|
||||||
if (leaseSet->IsNewer (buf + offset, len - offset))
|
|
||||||
{
|
|
||||||
leaseSet->Update (buf + offset, len - offset);
|
|
||||||
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key)
|
|
||||||
LogPrint (eLogDebug, "Destination: Remote LeaseSet updated");
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "Destination: Remote LeaseSet update failed");
|
|
||||||
m_RemoteLeaseSets.erase (it);
|
|
||||||
leaseSet = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogDebug, "Destination: Remote LeaseSet is older. Not updated");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
leaseSet = std::make_shared<i2p::data::LeaseSet> (buf + offset, len - offset);
|
|
||||||
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key)
|
|
||||||
{
|
|
||||||
if (leaseSet->GetIdentHash () != GetIdentHash ())
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "Destination: New remote LeaseSet added");
|
|
||||||
m_RemoteLeaseSets[key] = leaseSet;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogDebug, "Destination: Own remote LeaseSet dropped");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Destination: New remote LeaseSet failed");
|
|
||||||
leaseSet = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Destination: Unexpected client's DatabaseStore type ", buf[DATABASE_STORE_TYPE_OFFSET], ", dropped");
|
|
||||||
|
|
||||||
auto it1 = m_LeaseSetRequests.find (key);
|
|
||||||
if (it1 != m_LeaseSetRequests.end ())
|
|
||||||
{
|
|
||||||
it1->second->requestTimeoutTimer.cancel ();
|
|
||||||
if (it1->second) it1->second->Complete (leaseSet);
|
|
||||||
m_LeaseSetRequests.erase (it1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LeaseSetDestination::HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
i2p::data::IdentHash key (buf);
|
|
||||||
int num = buf[32]; // num
|
|
||||||
LogPrint (eLogDebug, "Destination: DatabaseSearchReply for ", key.ToBase64 (), " num=", num);
|
|
||||||
auto it = m_LeaseSetRequests.find (key);
|
|
||||||
if (it != m_LeaseSetRequests.end ())
|
|
||||||
{
|
|
||||||
auto request = it->second;
|
|
||||||
bool found = false;
|
|
||||||
if (request->excluded.size () < MAX_NUM_FLOODFILLS_PER_REQUEST)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < num; i++)
|
|
||||||
{
|
|
||||||
i2p::data::IdentHash peerHash (buf + 33 + i*32);
|
|
||||||
if (!request->excluded.count (peerHash) && !i2p::data::netdb.FindRouter (peerHash))
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Destination: Found new floodfill, request it"); // TODO: recheck this message
|
|
||||||
i2p::data::netdb.RequestDestination (peerHash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto floodfill = i2p::data::netdb.GetClosestFloodfill (key, request->excluded);
|
|
||||||
if (floodfill)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Destination: Requesting ", key.ToBase64 (), " at ", floodfill->GetIdentHash ().ToBase64 ());
|
|
||||||
if (SendLeaseSetRequest (key, floodfill, request))
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Destination: ", key.ToBase64 (), " was not found on ", MAX_NUM_FLOODFILLS_PER_REQUEST, " floodfills");
|
|
||||||
request->Complete (nullptr);
|
|
||||||
m_LeaseSetRequests.erase (key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "Destination: Request for ", key.ToBase64 (), " not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
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 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 (&LeaseSetDestination::HandlePublishVerificationTimer,
|
|
||||||
shared_from_this (), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LeaseSetDestination::SetLeaseSetUpdated ()
|
|
||||||
{
|
|
||||||
UpdateLeaseSet ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void LeaseSetDestination::Publish ()
|
|
||||||
{
|
|
||||||
if (!m_LeaseSet || !m_Pool)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Destination: Can't publish non-existing LeaseSet");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (m_PublishReplyToken)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "Destination: Publishing LeaseSet is pending");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto outbound = m_Pool->GetNextOutboundTunnel ();
|
|
||||||
if (!outbound)
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Destination: Can't publish LeaseSet, no more floodfills found");
|
|
||||||
m_ExcludedFloodfills.clear ();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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, inbound));
|
|
||||||
m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT));
|
|
||||||
m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer,
|
|
||||||
shared_from_this (), std::placeholders::_1));
|
|
||||||
outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LeaseSetDestination::HandlePublishConfirmationTimer (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
if (m_PublishReplyToken)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds, will try again");
|
|
||||||
m_PublishReplyToken = 0;
|
|
||||||
Publish ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LeaseSetDestination::HandlePublishVerificationTimer (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
auto s = shared_from_this ();
|
|
||||||
RequestLeaseSet (GetIdentHash (),
|
|
||||||
// "this" added due to bug in gcc 4.7-4.8
|
|
||||||
[s,this](std::shared_ptr<i2p::data::LeaseSet> leaseSet)
|
|
||||||
{
|
|
||||||
if (leaseSet && s->m_LeaseSet)
|
|
||||||
{
|
|
||||||
// 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 for ", GetIdentHash().ToBase32());
|
|
||||||
// we have to publish again
|
|
||||||
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;
|
|
||||||
s->m_LeaseSetRequests.erase (it);
|
|
||||||
if (notify && requestComplete) requestComplete->Complete (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);
|
|
||||||
if (requestComplete)
|
|
||||||
request->requestComplete.push_back (requestComplete);
|
|
||||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
auto ret = m_LeaseSetRequests.insert (std::pair<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> >(dest,request));
|
|
||||||
if (ret.second) // inserted
|
|
||||||
{
|
|
||||||
request->requestTime = ts;
|
|
||||||
if (!SendLeaseSetRequest (dest, floodfill, request))
|
|
||||||
{
|
|
||||||
// request failed
|
|
||||||
m_LeaseSetRequests.erase (ret.first);
|
|
||||||
if (requestComplete) requestComplete (nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // duplicate
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Destination: Request of LeaseSet ", dest.ToBase64 (), " is pending already");
|
|
||||||
// TODO: implement it properly
|
|
||||||
//ret.first->second->requestComplete.push_back (requestComplete);
|
|
||||||
if (ts > ret.first->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT)
|
|
||||||
m_LeaseSetRequests.erase (ret.first);
|
|
||||||
if (requestComplete) 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->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;
|
|
||||||
m_LeaseSetRequests.erase (it);
|
|
||||||
if (requestComplete) requestComplete->Complete (nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LeaseSetDestination::HandleCleanupTimer (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
CleanupExpiredTags ();
|
|
||||||
CleanupRemoteLeaseSets ();
|
|
||||||
CleanupDestination ();
|
|
||||||
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
|
|
||||||
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
|
|
||||||
shared_from_this (), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LeaseSetDestination::CleanupRemoteLeaseSets ()
|
|
||||||
{
|
|
||||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
|
|
||||||
for (auto it = m_RemoteLeaseSets.begin (); it != m_RemoteLeaseSets.end ();)
|
|
||||||
{
|
|
||||||
if (it->second->IsEmpty () || ts > it->second->GetExpirationTime ()) // leaseset expired
|
|
||||||
{
|
|
||||||
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),
|
|
||||||
m_ReadyChecker(GetService())
|
|
||||||
{
|
|
||||||
if (isPublic)
|
|
||||||
PersistTemporaryKeys ();
|
|
||||||
else
|
|
||||||
i2p::crypto::GenerateElGamalKeyPair(m_EncryptionPrivateKey, m_EncryptionPublicKey);
|
|
||||||
if (isPublic)
|
|
||||||
LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created");
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientDestination::~ClientDestination ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
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_ReadyChecker.cancel();
|
|
||||||
m_StreamingDestination->Stop ();
|
|
||||||
//m_StreamingDestination->SetOwner (nullptr);
|
|
||||||
m_StreamingDestination = nullptr;
|
|
||||||
for (auto& it: m_StreamingDestinationsByPorts)
|
|
||||||
{
|
|
||||||
it.second->Stop ();
|
|
||||||
//it.second->SetOwner (nullptr);
|
|
||||||
}
|
|
||||||
m_StreamingDestinationsByPorts.clear ();
|
|
||||||
if (m_DatagramDestination)
|
|
||||||
{
|
|
||||||
delete m_DatagramDestination;
|
|
||||||
m_DatagramDestination = nullptr;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef I2LUA
|
|
||||||
void ClientDestination::Ready(ReadyPromise & p)
|
|
||||||
{
|
|
||||||
ScheduleCheckForReady(&p);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::ScheduleCheckForReady(ReadyPromise * p)
|
|
||||||
{
|
|
||||||
// tick every 100ms
|
|
||||||
m_ReadyChecker.expires_from_now(boost::posix_time::milliseconds(100));
|
|
||||||
m_ReadyChecker.async_wait([&, p] (const boost::system::error_code & ecode) {
|
|
||||||
HandleCheckForReady(ecode, p);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::HandleCheckForReady(const boost::system::error_code & ecode, ReadyPromise * p)
|
|
||||||
{
|
|
||||||
if(ecode) // error happened
|
|
||||||
p->set_value(nullptr);
|
|
||||||
else if(IsReady()) // we are ready
|
|
||||||
p->set_value(std::shared_ptr<ClientDestination>(this));
|
|
||||||
else // we are not ready
|
|
||||||
ScheduleCheckForReady(p);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
uint32_t length = bufbe32toh (buf);
|
|
||||||
buf += 4;
|
|
||||||
// we assume I2CP payload
|
|
||||||
uint16_t fromPort = bufbe16toh (buf + 4), // source
|
|
||||||
toPort = bufbe16toh (buf + 6); // destination
|
|
||||||
switch (buf[9])
|
|
||||||
{
|
|
||||||
case PROTOCOL_TYPE_STREAMING:
|
|
||||||
{
|
|
||||||
// streaming protocol
|
|
||||||
auto dest = GetStreamingDestination (toPort);
|
|
||||||
if (dest)
|
|
||||||
dest->HandleDataMessagePayload (buf, length);
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Destination: Missing streaming destination");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case PROTOCOL_TYPE_DATAGRAM:
|
|
||||||
// datagram protocol
|
|
||||||
if (m_DatagramDestination)
|
|
||||||
m_DatagramDestination->HandleDataMessagePayload (fromPort, toPort, buf, length);
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Destination: Missing datagram destination");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LogPrint (eLogError, "Destination: Data: unexpected protocol ", buf[9]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port)
|
|
||||||
{
|
|
||||||
if (!streamRequestComplete)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Destination: request callback is not specified in CreateStream");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto leaseSet = FindLeaseSet (dest);
|
|
||||||
if (leaseSet)
|
|
||||||
streamRequestComplete(CreateStream (leaseSet, port));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto s = GetSharedFromThis ();
|
|
||||||
RequestDestination (dest,
|
|
||||||
[s, streamRequestComplete, port](std::shared_ptr<i2p::data::LeaseSet> ls)
|
|
||||||
{
|
|
||||||
if (ls)
|
|
||||||
streamRequestComplete(s->CreateStream (ls, port));
|
|
||||||
else
|
|
||||||
streamRequestComplete (nullptr);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port)
|
|
||||||
{
|
|
||||||
if (m_StreamingDestination)
|
|
||||||
return m_StreamingDestination->CreateNewOutgoingStream (remote, port);
|
|
||||||
else
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::GetStreamingDestination (int port) const
|
|
||||||
{
|
|
||||||
if (port)
|
|
||||||
{
|
|
||||||
auto it = m_StreamingDestinationsByPorts.find (port);
|
|
||||||
if (it != m_StreamingDestinationsByPorts.end ())
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
// if port is zero or not found, use default destination
|
|
||||||
return m_StreamingDestination;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor)
|
|
||||||
{
|
|
||||||
if (m_StreamingDestination)
|
|
||||||
m_StreamingDestination->SetAcceptor (acceptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::StopAcceptingStreams ()
|
|
||||||
{
|
|
||||||
if (m_StreamingDestination)
|
|
||||||
m_StreamingDestination->ResetAcceptor ();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClientDestination::IsAcceptingStreams () const
|
|
||||||
{
|
|
||||||
if (m_StreamingDestination)
|
|
||||||
return m_StreamingDestination->IsAcceptorSet ();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::CreateStreamingDestination (int port, bool gzip)
|
|
||||||
{
|
|
||||||
auto dest = std::make_shared<i2p::stream::StreamingDestination> (GetSharedFromThis (), port, gzip);
|
|
||||||
if (port)
|
|
||||||
m_StreamingDestinationsByPorts[port] = dest;
|
|
||||||
else // update default
|
|
||||||
m_StreamingDestination = dest;
|
|
||||||
return dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination ()
|
|
||||||
{
|
|
||||||
if (m_DatagramDestination == nullptr)
|
|
||||||
m_DatagramDestination = new i2p::datagram::DatagramDestination (GetSharedFromThis ());
|
|
||||||
return m_DatagramDestination;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<const i2p::stream::Stream> > ClientDestination::GetAllStreams () const
|
|
||||||
{
|
|
||||||
std::vector<std::shared_ptr<const i2p::stream::Stream> > ret;
|
|
||||||
if (m_StreamingDestination)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::PersistTemporaryKeys ()
|
|
||||||
{
|
|
||||||
std::string ident = GetIdentHash().ToBase32();
|
|
||||||
std::string path = i2p::fs::DataDirPath("destinations", (ident + ".dat"));
|
|
||||||
std::ifstream f(path, std::ifstream::binary);
|
|
||||||
|
|
||||||
if (f) {
|
|
||||||
f.read ((char *)m_EncryptionPublicKey, 256);
|
|
||||||
f.read ((char *)m_EncryptionPrivateKey, 256);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LogPrint (eLogInfo, "Destination: Creating new temporary keys for address ", ident, ".b32.i2p");
|
|
||||||
i2p::crypto::GenerateElGamalKeyPair(m_EncryptionPrivateKey, m_EncryptionPublicKey);
|
|
||||||
|
|
||||||
std::ofstream f1 (path, std::ofstream::binary | std::ofstream::out);
|
|
||||||
if (f1) {
|
|
||||||
f1.write ((char *)m_EncryptionPublicKey, 256);
|
|
||||||
f1.write ((char *)m_EncryptionPrivateKey, 256);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::CleanupDestination ()
|
|
||||||
{
|
|
||||||
if (m_DatagramDestination) m_DatagramDestination->CleanUp ();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
233
Destination.h
233
Destination.h
@@ -1,233 +0,0 @@
|
|||||||
#ifndef DESTINATION_H__
|
|
||||||
#define DESTINATION_H__
|
|
||||||
|
|
||||||
#include <thread>
|
|
||||||
#include <mutex>
|
|
||||||
#include <memory>
|
|
||||||
#include <map>
|
|
||||||
#include <set>
|
|
||||||
#include <string>
|
|
||||||
#include <functional>
|
|
||||||
#ifdef I2LUA
|
|
||||||
#include <future>
|
|
||||||
#endif
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "TunnelPool.h"
|
|
||||||
#include "Crypto.h"
|
|
||||||
#include "LeaseSet.h"
|
|
||||||
#include "Garlic.h"
|
|
||||||
#include "NetDb.h"
|
|
||||||
#include "Streaming.h"
|
|
||||||
#include "Datagram.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
const uint8_t PROTOCOL_TYPE_STREAMING = 6;
|
|
||||||
const uint8_t PROTOCOL_TYPE_DATAGRAM = 17;
|
|
||||||
const uint8_t PROTOCOL_TYPE_RAW = 18;
|
|
||||||
const int PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds
|
|
||||||
const int PUBLISH_VERIFICATION_TIMEOUT = 10; // in seconds after successfull publish
|
|
||||||
const int PUBLISH_REGULAR_VERIFICATION_INTERNAL = 100; // in seconds periodically
|
|
||||||
const int LEASESET_REQUEST_TIMEOUT = 5; // in seconds
|
|
||||||
const int MAX_LEASESET_REQUEST_TIMEOUT = 40; // in seconds
|
|
||||||
const int DESTINATION_CLEANUP_TIMEOUT = 3; // in minutes
|
|
||||||
const unsigned int MAX_NUM_FLOODFILLS_PER_REQUEST = 7;
|
|
||||||
|
|
||||||
// I2CP
|
|
||||||
const char I2CP_PARAM_INBOUND_TUNNEL_LENGTH[] = "inbound.length";
|
|
||||||
const int DEFAULT_INBOUND_TUNNEL_LENGTH = 3;
|
|
||||||
const char I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH[] = "outbound.length";
|
|
||||||
const int DEFAULT_OUTBOUND_TUNNEL_LENGTH = 3;
|
|
||||||
const char I2CP_PARAM_INBOUND_TUNNELS_QUANTITY[] = "inbound.quantity";
|
|
||||||
const int DEFAULT_INBOUND_TUNNELS_QUANTITY = 5;
|
|
||||||
const char I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY[] = "outbound.quantity";
|
|
||||||
const int DEFAULT_OUTBOUND_TUNNELS_QUANTITY = 5;
|
|
||||||
const char I2CP_PARAM_EXPLICIT_PEERS[] = "explicitPeers";
|
|
||||||
const int STREAM_REQUEST_TIMEOUT = 60; //in seconds
|
|
||||||
const char I2CP_PARAM_TAGS_TO_SEND[] = "crypto.tagsToSend";
|
|
||||||
const int DEFAULT_TAGS_TO_SEND = 40;
|
|
||||||
|
|
||||||
// latency
|
|
||||||
const char I2CP_PARAM_MIN_TUNNEL_LATENCY[] = "latency.min";
|
|
||||||
const int DEFAULT_MIN_TUNNEL_LATENCY = 0;
|
|
||||||
const char I2CP_PARAM_MAX_TUNNEL_LATENCY[] = "latency.max";
|
|
||||||
const int DEFAULT_MAX_TUNNEL_LATENCY = 0;
|
|
||||||
|
|
||||||
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
|
|
||||||
|
|
||||||
class LeaseSetDestination: public i2p::garlic::GarlicDestination,
|
|
||||||
public std::enable_shared_from_this<LeaseSetDestination>
|
|
||||||
{
|
|
||||||
typedef std::function<void (std::shared_ptr<i2p::data::LeaseSet> leaseSet)> RequestComplete;
|
|
||||||
// leaseSet = nullptr means not found
|
|
||||||
struct LeaseSetRequest
|
|
||||||
{
|
|
||||||
LeaseSetRequest (boost::asio::io_service& service): requestTime (0), requestTimeoutTimer (service) {};
|
|
||||||
std::set<i2p::data::IdentHash> excluded;
|
|
||||||
uint64_t requestTime;
|
|
||||||
boost::asio::deadline_timer requestTimeoutTimer;
|
|
||||||
std::list<RequestComplete> requestComplete;
|
|
||||||
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
|
|
||||||
std::shared_ptr<i2p::tunnel::InboundTunnel> replyTunnel;
|
|
||||||
|
|
||||||
void Complete (std::shared_ptr<i2p::data::LeaseSet> ls)
|
|
||||||
{
|
|
||||||
for (auto& it: requestComplete) it (ls);
|
|
||||||
requestComplete.clear ();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
LeaseSetDestination (bool isPublic, const std::map<std::string, std::string> * params = nullptr);
|
|
||||||
~LeaseSetDestination ();
|
|
||||||
|
|
||||||
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, bool notify = true);
|
|
||||||
|
|
||||||
// implements GarlicDestination
|
|
||||||
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet ();
|
|
||||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const { return m_Pool; }
|
|
||||||
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
|
||||||
|
|
||||||
// override GarlicDestination
|
|
||||||
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag);
|
|
||||||
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
void SetLeaseSetUpdated ();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
void SetLeaseSet (i2p::data::LocalLeaseSet * newLeaseSet);
|
|
||||||
virtual void CleanupDestination () {}; // additional clean up in derived classes
|
|
||||||
// I2CP
|
|
||||||
virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0;
|
|
||||||
virtual void CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels) = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void Run ();
|
|
||||||
void UpdateLeaseSet ();
|
|
||||||
void Publish ();
|
|
||||||
void HandlePublishConfirmationTimer (const boost::system::error_code& ecode);
|
|
||||||
void HandlePublishVerificationTimer (const boost::system::error_code& ecode);
|
|
||||||
void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len);
|
|
||||||
void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len);
|
|
||||||
void HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
|
|
||||||
void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete);
|
|
||||||
bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request);
|
|
||||||
void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest);
|
|
||||||
void HandleCleanupTimer (const boost::system::error_code& ecode);
|
|
||||||
void CleanupRemoteLeaseSets ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
volatile bool m_IsRunning;
|
|
||||||
std::thread * m_Thread;
|
|
||||||
boost::asio::io_service m_Service;
|
|
||||||
mutable std::mutex m_RemoteLeaseSetsMutex;
|
|
||||||
std::map<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets;
|
|
||||||
std::map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests;
|
|
||||||
|
|
||||||
std::shared_ptr<i2p::tunnel::TunnelPool> m_Pool;
|
|
||||||
std::mutex m_LeaseSetMutex;
|
|
||||||
std::shared_ptr<i2p::data::LocalLeaseSet> m_LeaseSet;
|
|
||||||
bool m_IsPublic;
|
|
||||||
uint32_t m_PublishReplyToken;
|
|
||||||
std::set<i2p::data::IdentHash> m_ExcludedFloodfills; // for publishing
|
|
||||||
|
|
||||||
boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer, m_CleanupTimer;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// for HTTP only
|
|
||||||
int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); };
|
|
||||||
const decltype(m_RemoteLeaseSets)& GetLeaseSets () const { return m_RemoteLeaseSets; };
|
|
||||||
};
|
|
||||||
|
|
||||||
class ClientDestination: public LeaseSetDestination
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
#ifdef I2LUA
|
|
||||||
// type for informing that a client destination is ready
|
|
||||||
typedef std::promise<std::shared_ptr<ClientDestination> > ReadyPromise;
|
|
||||||
// informs promise with shared_from_this() when this destination is ready to use
|
|
||||||
// if cancelled before ready, informs promise with nullptr
|
|
||||||
void Ready(ReadyPromise & p);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
|
|
||||||
~ClientDestination ();
|
|
||||||
|
|
||||||
bool Start ();
|
|
||||||
bool Stop ();
|
|
||||||
|
|
||||||
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
|
|
||||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
|
|
||||||
|
|
||||||
// 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:
|
|
||||||
|
|
||||||
void CleanupDestination ();
|
|
||||||
// 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 ();
|
|
||||||
#ifdef I2LUA
|
|
||||||
void ScheduleCheckForReady(ReadyPromise * p);
|
|
||||||
void HandleCheckForReady(const boost::system::error_code & ecode, ReadyPromise * p);
|
|
||||||
#endif
|
|
||||||
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;
|
|
||||||
|
|
||||||
boost::asio::deadline_timer m_ReadyChecker;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// for HTTP only
|
|
||||||
std::vector<std::shared_ptr<const i2p::stream::Stream> > GetAllStreams () const;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
32
Event.cpp
32
Event.cpp
@@ -1,32 +0,0 @@
|
|||||||
#include "Event.h"
|
|
||||||
#include "Log.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace event
|
|
||||||
{
|
|
||||||
#ifdef WITH_EVENTS
|
|
||||||
EventCore core;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void EventCore::SetListener(EventListener * l)
|
|
||||||
{
|
|
||||||
m_listener = l;
|
|
||||||
LogPrint(eLogInfo, "Event: listener set");
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventCore::QueueEvent(const EventType & ev)
|
|
||||||
{
|
|
||||||
if(m_listener)
|
|
||||||
m_listener->HandleEvent(ev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmitEvent(const EventType & e)
|
|
||||||
{
|
|
||||||
#ifdef WITH_EVENTS
|
|
||||||
i2p::event::core.QueueEvent(e);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
37
Event.h
37
Event.h
@@ -1,37 +0,0 @@
|
|||||||
#ifndef EVENT_H__
|
|
||||||
#define EVENT_H__
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
|
|
||||||
typedef std::map<std::string, std::string> EventType;
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace event
|
|
||||||
{
|
|
||||||
class EventListener {
|
|
||||||
public:
|
|
||||||
virtual ~EventListener() {};
|
|
||||||
virtual void HandleEvent(const EventType & ev) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class EventCore
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void QueueEvent(const EventType & ev);
|
|
||||||
void SetListener(EventListener * l);
|
|
||||||
|
|
||||||
private:
|
|
||||||
EventListener * m_listener = nullptr;
|
|
||||||
};
|
|
||||||
#ifdef WITH_EVENTS
|
|
||||||
extern EventCore core;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void EmitEvent(const EventType & ev);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
192
FS.cpp
192
FS.cpp
@@ -1,192 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2013-2016, The PurpleI2P Project
|
|
||||||
*
|
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
|
||||||
*
|
|
||||||
* See full license text in LICENSE file at top of project tree
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <shlobj.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "Base.h"
|
|
||||||
#include "FS.h"
|
|
||||||
#include "Log.h"
|
|
||||||
|
|
||||||
namespace i2p {
|
|
||||||
namespace fs {
|
|
||||||
std::string appName = "i2pd";
|
|
||||||
std::string dataDir = "";
|
|
||||||
#ifdef _WIN32
|
|
||||||
std::string dirSep = "\\";
|
|
||||||
#else
|
|
||||||
std::string dirSep = "/";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const std::string & GetAppName () {
|
|
||||||
return appName;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetAppName (const std::string& name) {
|
|
||||||
appName = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string & GetDataDir () {
|
|
||||||
return dataDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DetectDataDir(const std::string & cmdline_param, bool isService) {
|
|
||||||
if (cmdline_param != "") {
|
|
||||||
dataDir = cmdline_param;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#if defined(WIN32) || defined(_WIN32)
|
|
||||||
char localAppData[MAX_PATH];
|
|
||||||
// check executable directory first
|
|
||||||
GetModuleFileName (NULL, localAppData, MAX_PATH);
|
|
||||||
auto execPath = boost::filesystem::path(localAppData).parent_path();
|
|
||||||
// if config file exists in .exe's folder use it
|
|
||||||
if(boost::filesystem::exists(execPath/"i2pd.conf")) // TODO: magic string
|
|
||||||
dataDir = execPath.string ();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// otherwise %appdata%
|
|
||||||
SHGetFolderPath(NULL, CSIDL_APPDATA, 0, NULL, localAppData);
|
|
||||||
dataDir = std::string(localAppData) + "\\" + appName;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
#elif defined(MAC_OSX)
|
|
||||||
char *home = getenv("HOME");
|
|
||||||
dataDir = (home != NULL && strlen(home) > 0) ? home : "";
|
|
||||||
dataDir += "/Library/Application Support/" + appName;
|
|
||||||
return;
|
|
||||||
#else /* other unix */
|
|
||||||
#if defined(ANDROID)
|
|
||||||
const char * ext = getenv("EXTERNAL_STORAGE");
|
|
||||||
if (!ext) ext = "/sdcard";
|
|
||||||
if (boost::filesystem::exists(ext))
|
|
||||||
{
|
|
||||||
dataDir = std::string (ext) + "/" + appName;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// otherwise use /data/files
|
|
||||||
#endif
|
|
||||||
char *home = getenv("HOME");
|
|
||||||
if (isService) {
|
|
||||||
dataDir = "/var/lib/" + appName;
|
|
||||||
} else if (home != NULL && strlen(home) > 0) {
|
|
||||||
dataDir = std::string(home) + "/." + appName;
|
|
||||||
} else {
|
|
||||||
dataDir = "/tmp/" + appName;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Init() {
|
|
||||||
if (!boost::filesystem::exists(dataDir))
|
|
||||||
boost::filesystem::create_directory(dataDir);
|
|
||||||
std::string destinations = DataDirPath("destinations");
|
|
||||||
if (!boost::filesystem::exists(destinations))
|
|
||||||
boost::filesystem::create_directory(destinations);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ReadDir(const std::string & path, std::vector<std::string> & files) {
|
|
||||||
if (!boost::filesystem::exists(path))
|
|
||||||
return false;
|
|
||||||
boost::filesystem::directory_iterator it(path);
|
|
||||||
boost::filesystem::directory_iterator end;
|
|
||||||
|
|
||||||
for ( ; it != end; it++) {
|
|
||||||
if (!boost::filesystem::is_regular_file(it->status()))
|
|
||||||
continue;
|
|
||||||
files.push_back(it->path().string());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Exists(const std::string & path) {
|
|
||||||
return boost::filesystem::exists(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Remove(const std::string & path) {
|
|
||||||
if (!boost::filesystem::exists(path))
|
|
||||||
return false;
|
|
||||||
return boost::filesystem::remove(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CreateDirectory (const std::string& path)
|
|
||||||
{
|
|
||||||
if (boost::filesystem::exists(path) &&
|
|
||||||
boost::filesystem::is_directory (boost::filesystem::status (path))) return true;
|
|
||||||
return boost::filesystem::create_directory(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HashedStorage::SetPlace(const std::string &path) {
|
|
||||||
root = path + i2p::fs::dirSep + name;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HashedStorage::Init(const char * chars, size_t count) {
|
|
||||||
if (!boost::filesystem::exists(root)) {
|
|
||||||
boost::filesystem::create_directories(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < count; i++) {
|
|
||||||
auto p = root + i2p::fs::dirSep + prefix1 + chars[i];
|
|
||||||
if (boost::filesystem::exists(p))
|
|
||||||
continue;
|
|
||||||
if (boost::filesystem::create_directory(p))
|
|
||||||
continue; /* ^ throws exception on failure */
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string HashedStorage::Path(const std::string & ident) const {
|
|
||||||
std::string safe_ident = ident;
|
|
||||||
std::replace(safe_ident.begin(), safe_ident.end(), '/', '-');
|
|
||||||
std::replace(safe_ident.begin(), safe_ident.end(), '\\', '-');
|
|
||||||
|
|
||||||
std::stringstream t("");
|
|
||||||
t << this->root << i2p::fs::dirSep;
|
|
||||||
t << prefix1 << safe_ident[0] << i2p::fs::dirSep;
|
|
||||||
t << prefix2 << safe_ident << "." << suffix;
|
|
||||||
|
|
||||||
return t.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
void HashedStorage::Remove(const std::string & ident) {
|
|
||||||
std::string path = Path(ident);
|
|
||||||
if (!boost::filesystem::exists(path))
|
|
||||||
return;
|
|
||||||
boost::filesystem::remove(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HashedStorage::Traverse(std::vector<std::string> & files) {
|
|
||||||
Iterate([&files] (const std::string & fname) {
|
|
||||||
files.push_back(fname);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void HashedStorage::Iterate(FilenameVisitor v)
|
|
||||||
{
|
|
||||||
boost::filesystem::path p(root);
|
|
||||||
boost::filesystem::recursive_directory_iterator it(p);
|
|
||||||
boost::filesystem::recursive_directory_iterator end;
|
|
||||||
|
|
||||||
for ( ; it != end; it++) {
|
|
||||||
if (!boost::filesystem::is_regular_file( it->status() ))
|
|
||||||
continue;
|
|
||||||
const std::string & t = it->path().string();
|
|
||||||
v(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // fs
|
|
||||||
} // i2p
|
|
||||||
159
FS.h
159
FS.h
@@ -1,159 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2013-2016, The PurpleI2P Project
|
|
||||||
*
|
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
|
||||||
*
|
|
||||||
* See full license text in LICENSE file at top of project tree
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FS_H__
|
|
||||||
#define FS_H__
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
namespace i2p {
|
|
||||||
namespace fs {
|
|
||||||
extern std::string dirSep;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Class to work with NetDb & Router profiles
|
|
||||||
*
|
|
||||||
* Usage:
|
|
||||||
*
|
|
||||||
* const char alphabet[8] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};
|
|
||||||
* auto h = HashedStorage("name", "y", "z-", ".txt");
|
|
||||||
* h.SetPlace("/tmp/hs-test");
|
|
||||||
* h.GetName() -> gives "name"
|
|
||||||
* h.GetRoot() -> gives "/tmp/hs-test/name"
|
|
||||||
* h.Init(alphabet, 8); <- creates needed dirs, 8 is size of alphabet
|
|
||||||
* h.Path("abcd"); <- returns /tmp/hs-test/name/ya/z-abcd.txt
|
|
||||||
* h.Remove("abcd"); <- removes /tmp/hs-test/name/ya/z-abcd.txt, if it exists
|
|
||||||
* std::vector<std::string> files;
|
|
||||||
* h.Traverse(files); <- finds all files in storage and saves in given vector
|
|
||||||
*/
|
|
||||||
class HashedStorage {
|
|
||||||
protected:
|
|
||||||
std::string root; /**< path to storage with it's name included */
|
|
||||||
std::string name; /**< name of the storage */
|
|
||||||
std::string prefix1; /**< hashed directory prefix */
|
|
||||||
std::string prefix2; /**< prefix of file in storage */
|
|
||||||
std::string suffix; /**< suffix of file in storage (extension) */
|
|
||||||
|
|
||||||
public:
|
|
||||||
typedef std::function<void(const std::string &)> FilenameVisitor;
|
|
||||||
HashedStorage(const char *n, const char *p1, const char *p2, const char *s):
|
|
||||||
name(n), prefix1(p1), prefix2(p2), suffix(s) {};
|
|
||||||
|
|
||||||
/** create subdirs in storage */
|
|
||||||
bool Init(const char* chars, size_t cnt);
|
|
||||||
const std::string & GetRoot() const { return root; }
|
|
||||||
const std::string & GetName() const { return name; }
|
|
||||||
/** set directory where to place storage directory */
|
|
||||||
void SetPlace(const std::string & path);
|
|
||||||
/** path to file with given ident */
|
|
||||||
std::string Path(const std::string & ident) const;
|
|
||||||
/** remove file by ident */
|
|
||||||
void Remove(const std::string & ident);
|
|
||||||
/** find all files in storage and store list in provided vector */
|
|
||||||
void Traverse(std::vector<std::string> & files);
|
|
||||||
/** visit every file in this storage with a visitor */
|
|
||||||
void Iterate(FilenameVisitor v);
|
|
||||||
};
|
|
||||||
|
|
||||||
/** @brief Returns current application name, default 'i2pd' */
|
|
||||||
const std::string & GetAppName ();
|
|
||||||
/** @brief Set applicaton name, affects autodetection of datadir */
|
|
||||||
void SetAppName (const std::string& name);
|
|
||||||
|
|
||||||
/** @brief Returns datadir path */
|
|
||||||
const std::string & GetDataDir();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set datadir either from cmdline option or using autodetection
|
|
||||||
* @param cmdline_param Value of cmdline parameter --datadir=<something>
|
|
||||||
* @param isService Value of cmdline parameter --service
|
|
||||||
*
|
|
||||||
* Examples of autodetected paths:
|
|
||||||
*
|
|
||||||
* Windows < Vista: C:\Documents and Settings\Username\Application Data\i2pd\
|
|
||||||
* Windows >= Vista: C:\Users\Username\AppData\Roaming\i2pd\
|
|
||||||
* Mac: /Library/Application Support/i2pd/ or ~/Library/Application Support/i2pd/
|
|
||||||
* Unix: /var/lib/i2pd/ (system=1) >> ~/.i2pd/ or /tmp/i2pd/
|
|
||||||
*/
|
|
||||||
void DetectDataDir(const std::string & cmdline_datadir, bool isService = false);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Create subdirectories inside datadir
|
|
||||||
*/
|
|
||||||
bool Init();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get list of files in directory
|
|
||||||
* @param path Path to directory
|
|
||||||
* @param files Vector to store found files
|
|
||||||
* @return true on success and false if directory not exists
|
|
||||||
*/
|
|
||||||
bool ReadDir(const std::string & path, std::vector<std::string> & files);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Remove file with given path
|
|
||||||
* @param path Absolute path to file
|
|
||||||
* @return true on success, false if file not exists, throws exception on error
|
|
||||||
*/
|
|
||||||
bool Remove(const std::string & path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check existence of file
|
|
||||||
* @param path Absolute path to file
|
|
||||||
* @return true if file exists, false otherwise
|
|
||||||
*/
|
|
||||||
bool Exists(const std::string & path);
|
|
||||||
|
|
||||||
bool CreateDirectory (const std::string& path);
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void _ExpandPath(std::stringstream & path, T c) {
|
|
||||||
path << i2p::fs::dirSep << c;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, typename ... Other>
|
|
||||||
void _ExpandPath(std::stringstream & path, T c, Other ... other) {
|
|
||||||
_ExpandPath(path, c);
|
|
||||||
_ExpandPath(path, other ...);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get path relative to datadir
|
|
||||||
*
|
|
||||||
* Examples (with datadir = "/tmp/i2pd"):
|
|
||||||
*
|
|
||||||
* i2p::fs::Path("test") -> '/tmp/i2pd/test'
|
|
||||||
* i2p::fs::Path("test", "file.txt") -> '/tmp/i2pd/test/file.txt'
|
|
||||||
*/
|
|
||||||
template<typename ... Other>
|
|
||||||
std::string DataDirPath(Other ... components) {
|
|
||||||
std::stringstream s("");
|
|
||||||
s << i2p::fs::GetDataDir();
|
|
||||||
_ExpandPath(s, components ...);
|
|
||||||
|
|
||||||
return s.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Storage, typename... Filename>
|
|
||||||
std::string StorageRootPath (const Storage& storage, Filename... filenames)
|
|
||||||
{
|
|
||||||
std::stringstream s("");
|
|
||||||
s << storage.GetRoot ();
|
|
||||||
_ExpandPath(s, filenames...);
|
|
||||||
|
|
||||||
return s.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // fs
|
|
||||||
} // i2p
|
|
||||||
|
|
||||||
#endif // /* FS_H__ */
|
|
||||||
711
Garlic.cpp
711
Garlic.cpp
@@ -1,711 +0,0 @@
|
|||||||
#include <inttypes.h>
|
|
||||||
#include "I2PEndian.h"
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include "Crypto.h"
|
|
||||||
#include "RouterContext.h"
|
|
||||||
#include "I2NPProtocol.h"
|
|
||||||
#include "Tunnel.h"
|
|
||||||
#include "TunnelPool.h"
|
|
||||||
#include "Transports.h"
|
|
||||||
#include "Timestamp.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "Garlic.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace garlic
|
|
||||||
{
|
|
||||||
GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner,
|
|
||||||
std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags, bool attachLeaseSet):
|
|
||||||
m_Owner (owner), m_Destination (destination), m_NumTags (numTags),
|
|
||||||
m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend),
|
|
||||||
m_LeaseSetUpdateMsgID (0),
|
|
||||||
m_ElGamalEncryption (new i2p::crypto::ElGamalEncryption (destination->GetEncryptionPublicKey ()))
|
|
||||||
{
|
|
||||||
// create new session tags and session key
|
|
||||||
RAND_bytes (m_SessionKey, 32);
|
|
||||||
m_Encryption.SetKey (m_SessionKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
GarlicRoutingSession::GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag):
|
|
||||||
m_Owner (nullptr), m_Destination (nullptr), m_NumTags (1), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend), m_LeaseSetUpdateMsgID (0)
|
|
||||||
{
|
|
||||||
memcpy (m_SessionKey, sessionKey, 32);
|
|
||||||
m_Encryption.SetKey (m_SessionKey);
|
|
||||||
m_SessionTags.push_back (sessionTag);
|
|
||||||
m_SessionTags.back ().creationTime = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
}
|
|
||||||
|
|
||||||
GarlicRoutingSession::~GarlicRoutingSession ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<GarlicRoutingPath> GarlicRoutingSession::GetSharedRoutingPath ()
|
|
||||||
{
|
|
||||||
if (!m_SharedRoutingPath) return nullptr;
|
|
||||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
if (m_SharedRoutingPath->numTimesUsed >= ROUTING_PATH_MAX_NUM_TIMES_USED ||
|
|
||||||
!m_SharedRoutingPath->outboundTunnel->IsEstablished () ||
|
|
||||||
ts*1000LL > m_SharedRoutingPath->remoteLease->endDate ||
|
|
||||||
ts > m_SharedRoutingPath->updateTime + ROUTING_PATH_EXPIRATION_TIMEOUT)
|
|
||||||
m_SharedRoutingPath = nullptr;
|
|
||||||
if (m_SharedRoutingPath) m_SharedRoutingPath->numTimesUsed++;
|
|
||||||
return m_SharedRoutingPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GarlicRoutingSession::SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path)
|
|
||||||
{
|
|
||||||
if (path && path->outboundTunnel && path->remoteLease)
|
|
||||||
{
|
|
||||||
path->updateTime = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
path->numTimesUsed = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
path = nullptr;
|
|
||||||
m_SharedRoutingPath = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
GarlicRoutingSession::UnconfirmedTags * GarlicRoutingSession::GenerateSessionTags ()
|
|
||||||
{
|
|
||||||
auto tags = new UnconfirmedTags (m_NumTags);
|
|
||||||
tags->tagsCreationTime = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
for (int i = 0; i < m_NumTags; i++)
|
|
||||||
{
|
|
||||||
RAND_bytes (tags->sessionTags[i], 32);
|
|
||||||
tags->sessionTags[i].creationTime = tags->tagsCreationTime;
|
|
||||||
}
|
|
||||||
return tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GarlicRoutingSession::MessageConfirmed (uint32_t msgID)
|
|
||||||
{
|
|
||||||
TagsConfirmed (msgID);
|
|
||||||
if (msgID == m_LeaseSetUpdateMsgID)
|
|
||||||
{
|
|
||||||
m_LeaseSetUpdateStatus = eLeaseSetUpToDate;
|
|
||||||
m_LeaseSetUpdateMsgID = 0;
|
|
||||||
LogPrint (eLogInfo, "Garlic: LeaseSet update confirmed");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
CleanupExpiredTags ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GarlicRoutingSession::TagsConfirmed (uint32_t msgID)
|
|
||||||
{
|
|
||||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
auto it = m_UnconfirmedTagsMsgs.find (msgID);
|
|
||||||
if (it != m_UnconfirmedTagsMsgs.end ())
|
|
||||||
{
|
|
||||||
auto& tags = it->second;
|
|
||||||
if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < tags->numTags; i++)
|
|
||||||
m_SessionTags.push_back (tags->sessionTags[i]);
|
|
||||||
}
|
|
||||||
m_UnconfirmedTagsMsgs.erase (it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GarlicRoutingSession::CleanupExpiredTags ()
|
|
||||||
{
|
|
||||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
for (auto it = m_SessionTags.begin (); it != m_SessionTags.end ();)
|
|
||||||
{
|
|
||||||
if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
|
|
||||||
it = m_SessionTags.erase (it);
|
|
||||||
else
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
CleanupUnconfirmedTags ();
|
|
||||||
if (m_LeaseSetUpdateMsgID && ts*1000LL > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT)
|
|
||||||
{
|
|
||||||
if (m_Owner)
|
|
||||||
m_Owner->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID);
|
|
||||||
m_LeaseSetUpdateMsgID = 0;
|
|
||||||
}
|
|
||||||
return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty ();
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (m_Owner)
|
|
||||||
m_Owner->RemoveDeliveryStatusSession (it->first);
|
|
||||||
it = m_UnconfirmedTagsMsgs.erase (it);
|
|
||||||
ret = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> GarlicRoutingSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
auto m = NewI2NPMessage ();
|
|
||||||
m->Align (12); // in order to get buf aligned to 16 (12 + 4)
|
|
||||||
size_t len = 0;
|
|
||||||
uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length
|
|
||||||
|
|
||||||
// find non-expired tag
|
|
||||||
bool tagFound = false;
|
|
||||||
SessionTag tag;
|
|
||||||
if (m_NumTags > 0)
|
|
||||||
{
|
|
||||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
while (!m_SessionTags.empty ())
|
|
||||||
{
|
|
||||||
if (ts < m_SessionTags.front ().creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
|
|
||||||
{
|
|
||||||
tag = m_SessionTags.front ();
|
|
||||||
m_SessionTags.pop_front (); // use same tag only once
|
|
||||||
tagFound = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_SessionTags.pop_front (); // remove expired tag
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// create message
|
|
||||||
if (!tagFound) // new session
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Garlic: No tags available, will use ElGamal");
|
|
||||||
if (!m_Destination)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Garlic: Can't use ElGamal for unknown destination");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
// create ElGamal block
|
|
||||||
ElGamalBlock elGamal;
|
|
||||||
memcpy (elGamal.sessionKey, m_SessionKey, 32);
|
|
||||||
RAND_bytes (elGamal.preIV, 32); // Pre-IV
|
|
||||||
uint8_t iv[32]; // IV is first 16 bytes
|
|
||||||
SHA256(elGamal.preIV, 32, iv);
|
|
||||||
m_ElGamalEncryption->Encrypt ((uint8_t *)&elGamal, buf, true);
|
|
||||||
m_Encryption.SetIV (iv);
|
|
||||||
buf += 514;
|
|
||||||
len += 514;
|
|
||||||
}
|
|
||||||
else // existing session
|
|
||||||
{
|
|
||||||
// session tag
|
|
||||||
memcpy (buf, tag, 32);
|
|
||||||
uint8_t iv[32]; // IV is first 16 bytes
|
|
||||||
SHA256(tag, 32, iv);
|
|
||||||
m_Encryption.SetIV (iv);
|
|
||||||
buf += 32;
|
|
||||||
len += 32;
|
|
||||||
}
|
|
||||||
// AES block
|
|
||||||
len += CreateAESBlock (buf, msg);
|
|
||||||
htobe32buf (m->GetPayload (), len);
|
|
||||||
m->len += len + 4;
|
|
||||||
m->FillI2NPMessageHeader (eI2NPGarlic);
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
size_t blockSize = 0;
|
|
||||||
bool createNewTags = m_Owner && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags*2/3);
|
|
||||||
UnconfirmedTags * newTags = createNewTags ? GenerateSessionTags () : nullptr;
|
|
||||||
htobuf16 (buf, newTags ? htobe16 (newTags->numTags) : 0); // tag count
|
|
||||||
blockSize += 2;
|
|
||||||
if (newTags) // session tags recreated
|
|
||||||
{
|
|
||||||
for (int i = 0; i < newTags->numTags; i++)
|
|
||||||
{
|
|
||||||
memcpy (buf + blockSize, newTags->sessionTags[i], 32); // tags
|
|
||||||
blockSize += 32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
uint32_t * payloadSize = (uint32_t *)(buf + blockSize);
|
|
||||||
blockSize += 4;
|
|
||||||
uint8_t * payloadHash = buf + blockSize;
|
|
||||||
blockSize += 32;
|
|
||||||
buf[blockSize] = 0; // flag
|
|
||||||
blockSize++;
|
|
||||||
size_t len = CreateGarlicPayload (buf + blockSize, msg, newTags);
|
|
||||||
htobe32buf (payloadSize, len);
|
|
||||||
SHA256(buf + blockSize, len, payloadHash);
|
|
||||||
blockSize += len;
|
|
||||||
size_t rem = blockSize % 16;
|
|
||||||
if (rem)
|
|
||||||
blockSize += (16-rem); //padding
|
|
||||||
m_Encryption.Encrypt(buf, blockSize, buf);
|
|
||||||
return blockSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags)
|
|
||||||
{
|
|
||||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
uint32_t msgID;
|
|
||||||
RAND_bytes ((uint8_t *)&msgID, 4);
|
|
||||||
size_t size = 0;
|
|
||||||
uint8_t * numCloves = payload + size;
|
|
||||||
*numCloves = 0;
|
|
||||||
size++;
|
|
||||||
|
|
||||||
if (m_Owner)
|
|
||||||
{
|
|
||||||
// resubmit non-confirmed LeaseSet
|
|
||||||
if (m_LeaseSetUpdateStatus == eLeaseSetSubmitted && ts > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT)
|
|
||||||
{
|
|
||||||
m_LeaseSetUpdateStatus = eLeaseSetUpdated;
|
|
||||||
SetSharedRoutingPath (nullptr); // invalidate path since leaseset was not confirmed
|
|
||||||
}
|
|
||||||
|
|
||||||
// attach DeviveryStatus if necessary
|
|
||||||
if (newTags || m_LeaseSetUpdateStatus == eLeaseSetUpdated) // new tags created or leaseset updated
|
|
||||||
{
|
|
||||||
// clove is DeliveryStatus
|
|
||||||
auto cloveSize = CreateDeliveryStatusClove (payload + size, msgID);
|
|
||||||
if (cloveSize > 0) // successive?
|
|
||||||
{
|
|
||||||
size += cloveSize;
|
|
||||||
(*numCloves)++;
|
|
||||||
if (newTags) // new tags created
|
|
||||||
{
|
|
||||||
newTags->msgID = msgID;
|
|
||||||
m_UnconfirmedTagsMsgs.insert (std::make_pair(msgID, std::unique_ptr<UnconfirmedTags>(newTags)));
|
|
||||||
newTags = nullptr; // got acquired
|
|
||||||
}
|
|
||||||
m_Owner->DeliveryStatusSent (shared_from_this (), msgID);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "Garlic: DeliveryStatus clove was not created");
|
|
||||||
}
|
|
||||||
// attach LeaseSet
|
|
||||||
if (m_LeaseSetUpdateStatus == eLeaseSetUpdated)
|
|
||||||
{
|
|
||||||
if (m_LeaseSetUpdateMsgID) m_Owner->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID); // remove previous
|
|
||||||
m_LeaseSetUpdateStatus = eLeaseSetSubmitted;
|
|
||||||
m_LeaseSetUpdateMsgID = msgID;
|
|
||||||
m_LeaseSetSubmissionTime = ts;
|
|
||||||
// clove if our leaseSet must be attached
|
|
||||||
auto leaseSet = CreateDatabaseStoreMsg (m_Owner->GetLeaseSet ());
|
|
||||||
size += CreateGarlicClove (payload + size, leaseSet, false);
|
|
||||||
(*numCloves)++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (msg) // clove message ifself if presented
|
|
||||||
{
|
|
||||||
size += CreateGarlicClove (payload + size, msg, m_Destination ? m_Destination->IsDestination () : false);
|
|
||||||
(*numCloves)++;
|
|
||||||
}
|
|
||||||
memset (payload + size, 0, 3); // certificate of message
|
|
||||||
size += 3;
|
|
||||||
htobe32buf (payload + size, msgID); // MessageID
|
|
||||||
size += 4;
|
|
||||||
htobe64buf (payload + size, ts + 8000); // Expiration of message, 8 sec
|
|
||||||
size += 8;
|
|
||||||
|
|
||||||
if (newTags) delete newTags; // not acquired, delete
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GarlicRoutingSession::CreateGarlicClove (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination)
|
|
||||||
{
|
|
||||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec
|
|
||||||
size_t size = 0;
|
|
||||||
if (isDestination && m_Destination)
|
|
||||||
{
|
|
||||||
buf[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination
|
|
||||||
size++;
|
|
||||||
memcpy (buf + size, m_Destination->GetIdentHash (), 32);
|
|
||||||
size += 32;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
buf[size] = 0;// delivery instructions flag local
|
|
||||||
size++;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
|
|
||||||
size += msg->GetLength ();
|
|
||||||
uint32_t cloveID;
|
|
||||||
RAND_bytes ((uint8_t *)&cloveID, 4);
|
|
||||||
htobe32buf (buf + size, cloveID); // CloveID
|
|
||||||
size += 4;
|
|
||||||
htobe64buf (buf + size, ts); // Expiration of clove
|
|
||||||
size += 8;
|
|
||||||
memset (buf + size, 0, 3); // certificate of clove
|
|
||||||
size += 3;
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GarlicRoutingSession::CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID)
|
|
||||||
{
|
|
||||||
size_t size = 0;
|
|
||||||
if (m_Owner)
|
|
||||||
{
|
|
||||||
auto inboundTunnel = m_Owner->GetTunnelPool ()->GetNextInboundTunnel ();
|
|
||||||
if (inboundTunnel)
|
|
||||||
{
|
|
||||||
buf[size] = eGarlicDeliveryTypeTunnel << 5; // delivery instructions flag tunnel
|
|
||||||
size++;
|
|
||||||
// hash and tunnelID sequence is reversed for Garlic
|
|
||||||
memcpy (buf + size, inboundTunnel->GetNextIdentHash (), 32); // To Hash
|
|
||||||
size += 32;
|
|
||||||
htobe32buf (buf + size, inboundTunnel->GetNextTunnelID ()); // tunnelID
|
|
||||||
size += 4;
|
|
||||||
// create msg
|
|
||||||
auto msg = CreateDeliveryStatusMsg (msgID);
|
|
||||||
if (m_Owner)
|
|
||||||
{
|
|
||||||
//encrypt
|
|
||||||
uint8_t key[32], tag[32];
|
|
||||||
RAND_bytes (key, 32); // random session key
|
|
||||||
RAND_bytes (tag, 32); // random session tag
|
|
||||||
m_Owner->SubmitSessionKey (key, tag);
|
|
||||||
GarlicRoutingSession garlic (key, tag);
|
|
||||||
msg = garlic.WrapSingleMessage (msg);
|
|
||||||
}
|
|
||||||
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
|
|
||||||
size += msg->GetLength ();
|
|
||||||
// fill clove
|
|
||||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec
|
|
||||||
uint32_t cloveID;
|
|
||||||
RAND_bytes ((uint8_t *)&cloveID, 4);
|
|
||||||
htobe32buf (buf + size, cloveID); // CloveID
|
|
||||||
size += 4;
|
|
||||||
htobe64buf (buf + size, ts); // Expiration of clove
|
|
||||||
size += 8;
|
|
||||||
memset (buf + size, 0, 3); // certificate of clove
|
|
||||||
size += 3;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Garlic: No inbound tunnels in the pool for DeliveryStatus");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "Garlic: Missing local LeaseSet");
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
GarlicDestination::~GarlicDestination ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void GarlicDestination::CleanUp ()
|
|
||||||
{
|
|
||||||
m_Sessions.clear ();
|
|
||||||
m_DeliveryStatusSessions.clear ();
|
|
||||||
m_Tags.clear ();
|
|
||||||
}
|
|
||||||
void GarlicDestination::AddSessionKey (const uint8_t * key, const uint8_t * tag)
|
|
||||||
{
|
|
||||||
if (key)
|
|
||||||
{
|
|
||||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
auto decryption = std::make_shared<i2p::crypto::CBCDecryption>();
|
|
||||||
decryption->SetKey (key);
|
|
||||||
m_Tags[SessionTag(tag, ts)] = decryption;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GarlicDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag)
|
|
||||||
{
|
|
||||||
AddSessionKey (key, tag);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GarlicDestination::HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
uint8_t * buf = msg->GetPayload ();
|
|
||||||
uint32_t length = bufbe32toh (buf);
|
|
||||||
if (length > msg->GetLength ())
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Garlic: message length ", length, " exceeds I2NP message length ", msg->GetLength ());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
buf += 4; // length
|
|
||||||
auto it = m_Tags.find (SessionTag(buf));
|
|
||||||
if (it != m_Tags.end ())
|
|
||||||
{
|
|
||||||
// tag found. Use AES
|
|
||||||
if (length >= 32)
|
|
||||||
{
|
|
||||||
uint8_t iv[32]; // IV is first 16 bytes
|
|
||||||
SHA256(buf, 32, iv);
|
|
||||||
it->second->SetIV (iv);
|
|
||||||
it->second->Decrypt (buf + 32, length - 32, buf + 32);
|
|
||||||
HandleAESBlock (buf + 32, length - 32, it->second, msg->from);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "Garlic: message length ", length, " is less than 32 bytes");
|
|
||||||
m_Tags.erase (it); // tag might be used only once
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// tag not found. Use ElGamal
|
|
||||||
ElGamalBlock elGamal;
|
|
||||||
if (length >= 514 && i2p::crypto::ElGamalDecrypt (GetEncryptionPrivateKey (), buf, (uint8_t *)&elGamal, true))
|
|
||||||
{
|
|
||||||
auto decryption = std::make_shared<i2p::crypto::CBCDecryption>();
|
|
||||||
decryption->SetKey (elGamal.sessionKey);
|
|
||||||
uint8_t iv[32]; // IV is first 16 bytes
|
|
||||||
SHA256(elGamal.preIV, 32, iv);
|
|
||||||
decryption->SetIV (iv);
|
|
||||||
decryption->Decrypt(buf + 514, length - 514, buf + 514);
|
|
||||||
HandleAESBlock (buf + 514, length - 514, decryption, msg->from);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Garlic: Failed to decrypt message");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GarlicDestination::HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<i2p::crypto::CBCDecryption> decryption,
|
|
||||||
std::shared_ptr<i2p::tunnel::InboundTunnel> from)
|
|
||||||
{
|
|
||||||
uint16_t tagCount = bufbe16toh (buf);
|
|
||||||
buf += 2; len -= 2;
|
|
||||||
if (tagCount > 0)
|
|
||||||
{
|
|
||||||
if (tagCount*32 > len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Garlic: Tag count ", tagCount, " exceeds length ", len);
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
for (int i = 0; i < tagCount; i++)
|
|
||||||
m_Tags[SessionTag(buf + i*32, ts)] = decryption;
|
|
||||||
}
|
|
||||||
buf += tagCount*32;
|
|
||||||
len -= tagCount*32;
|
|
||||||
uint32_t payloadSize = bufbe32toh (buf);
|
|
||||||
if (payloadSize > len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Garlic: Unexpected payload size ", payloadSize);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
buf += 4;
|
|
||||||
uint8_t * payloadHash = buf;
|
|
||||||
buf += 32;// payload hash.
|
|
||||||
if (*buf) // session key?
|
|
||||||
buf += 32; // new session key
|
|
||||||
buf++; // flag
|
|
||||||
|
|
||||||
// payload
|
|
||||||
uint8_t digest[32];
|
|
||||||
SHA256 (buf, payloadSize, digest);
|
|
||||||
if (memcmp (payloadHash, digest, 32)) // payload hash doesn't match
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Garlic: wrong payload hash");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
HandleGarlicPayload (buf, payloadSize, from);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GarlicDestination::HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
|
|
||||||
{
|
|
||||||
const uint8_t * buf1 = buf;
|
|
||||||
int numCloves = buf[0];
|
|
||||||
LogPrint (eLogDebug, "Garlic: ", numCloves," cloves");
|
|
||||||
buf++;
|
|
||||||
for (int i = 0; i < numCloves; i++)
|
|
||||||
{
|
|
||||||
// delivery instructions
|
|
||||||
uint8_t flag = buf[0];
|
|
||||||
buf++; // flag
|
|
||||||
if (flag & 0x80) // encrypted?
|
|
||||||
{
|
|
||||||
// TODO: implement
|
|
||||||
LogPrint (eLogWarning, "Garlic: clove encrypted");
|
|
||||||
buf += 32;
|
|
||||||
}
|
|
||||||
GarlicDeliveryType deliveryType = (GarlicDeliveryType)((flag >> 5) & 0x03);
|
|
||||||
switch (deliveryType)
|
|
||||||
{
|
|
||||||
case eGarlicDeliveryTypeLocal:
|
|
||||||
LogPrint (eLogDebug, "Garlic: type local");
|
|
||||||
HandleI2NPMessage (buf, len, from);
|
|
||||||
break;
|
|
||||||
case eGarlicDeliveryTypeDestination:
|
|
||||||
LogPrint (eLogDebug, "Garlic: type destination");
|
|
||||||
buf += 32; // destination. check it later or for multiple destinations
|
|
||||||
HandleI2NPMessage (buf, len, from);
|
|
||||||
break;
|
|
||||||
case eGarlicDeliveryTypeTunnel:
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "Garlic: type tunnel");
|
|
||||||
// gwHash and gwTunnel sequence is reverted
|
|
||||||
uint8_t * gwHash = buf;
|
|
||||||
buf += 32;
|
|
||||||
uint32_t gwTunnel = bufbe32toh (buf);
|
|
||||||
buf += 4;
|
|
||||||
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:
|
|
||||||
{
|
|
||||||
uint8_t * ident = buf;
|
|
||||||
buf += 32;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
buf += GetI2NPMessageLength (buf); // I2NP
|
|
||||||
buf += 4; // CloveID
|
|
||||||
buf += 8; // Date
|
|
||||||
buf += 3; // Certificate
|
|
||||||
if (buf - buf1 > (int)len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Garlic: clove is too long");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> GarlicDestination::WrapMessage (std::shared_ptr<const i2p::data::RoutingDestination> destination,
|
|
||||||
std::shared_ptr<I2NPMessage> msg, bool attachLeaseSet)
|
|
||||||
{
|
|
||||||
auto session = GetRoutingSession (destination, attachLeaseSet);
|
|
||||||
return session->WrapSingleMessage (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<GarlicRoutingSession> GarlicDestination::GetRoutingSession (
|
|
||||||
std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet)
|
|
||||||
{
|
|
||||||
GarlicRoutingSessionPtr session;
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
|
||||||
auto it = m_Sessions.find (destination->GetIdentHash ());
|
|
||||||
if (it != m_Sessions.end ())
|
|
||||||
session = it->second;
|
|
||||||
}
|
|
||||||
if (!session)
|
|
||||||
{
|
|
||||||
session = std::make_shared<GarlicRoutingSession> (this, destination,
|
|
||||||
attachLeaseSet ? m_NumTags : 4, attachLeaseSet); // specified num tags for connections and 4 for LS requests
|
|
||||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
|
||||||
m_Sessions[destination->GetIdentHash ()] = session;
|
|
||||||
}
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GarlicDestination::CleanupExpiredTags ()
|
|
||||||
{
|
|
||||||
// incoming
|
|
||||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
int numExpiredTags = 0;
|
|
||||||
for (auto it = m_Tags.begin (); it != m_Tags.end ();)
|
|
||||||
{
|
|
||||||
if (ts > it->first.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT)
|
|
||||||
{
|
|
||||||
numExpiredTags++;
|
|
||||||
it = m_Tags.erase (it);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
if (numExpiredTags > 0)
|
|
||||||
LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " tags expired for ", GetIdentHash().ToBase64 ());
|
|
||||||
|
|
||||||
// outgoing
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
|
||||||
for (auto it = m_Sessions.begin (); it != m_Sessions.end ();)
|
|
||||||
{
|
|
||||||
it->second->GetSharedRoutingPath (); // delete shared path if necessary
|
|
||||||
if (!it->second->CleanupExpiredTags ())
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Routing session to ", it->first.ToBase32 (), " deleted");
|
|
||||||
it->second->SetOwner (nullptr);
|
|
||||||
it = m_Sessions.erase (it);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// delivery status sessions
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
|
|
||||||
for (auto it = m_DeliveryStatusSessions.begin (); it != m_DeliveryStatusSessions.end (); )
|
|
||||||
{
|
|
||||||
if (it->second->GetOwner () != this)
|
|
||||||
it = m_DeliveryStatusSessions.erase (it);
|
|
||||||
else
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
|
|
||||||
m_DeliveryStatusSessions.erase (msgID);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GarlicDestination::DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
|
|
||||||
m_DeliveryStatusSessions[msgID] = session;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GarlicDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
uint32_t msgID = bufbe32toh (msg->GetPayload ());
|
|
||||||
GarlicRoutingSessionPtr session;
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
|
|
||||||
auto it = m_DeliveryStatusSessions.find (msgID);
|
|
||||||
if (it != m_DeliveryStatusSessions.end ())
|
|
||||||
{
|
|
||||||
session = it->second;
|
|
||||||
m_DeliveryStatusSessions.erase (it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (session)
|
|
||||||
{
|
|
||||||
session->MessageConfirmed (msgID);
|
|
||||||
LogPrint (eLogDebug, "Garlic: message ", msgID, " acknowledged");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GarlicDestination::SetLeaseSetUpdated ()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
|
||||||
for (auto& it: m_Sessions)
|
|
||||||
it.second->SetLeaseSetUpdated ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GarlicDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
HandleGarlicMessage (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GarlicDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
HandleDeliveryStatusMessage (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
211
Garlic.h
211
Garlic.h
@@ -1,211 +0,0 @@
|
|||||||
#ifndef GARLIC_H__
|
|
||||||
#define GARLIC_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <map>
|
|
||||||
#include <list>
|
|
||||||
#include <string>
|
|
||||||
#include <thread>
|
|
||||||
#include <mutex>
|
|
||||||
#include <memory>
|
|
||||||
#include "Crypto.h"
|
|
||||||
#include "I2NPProtocol.h"
|
|
||||||
#include "LeaseSet.h"
|
|
||||||
#include "Queue.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace tunnel
|
|
||||||
{
|
|
||||||
class OutboundTunnel;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace garlic
|
|
||||||
{
|
|
||||||
|
|
||||||
enum GarlicDeliveryType
|
|
||||||
{
|
|
||||||
eGarlicDeliveryTypeLocal = 0,
|
|
||||||
eGarlicDeliveryTypeDestination = 1,
|
|
||||||
eGarlicDeliveryTypeRouter = 2,
|
|
||||||
eGarlicDeliveryTypeTunnel = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ElGamalBlock
|
|
||||||
{
|
|
||||||
uint8_t sessionKey[32];
|
|
||||||
uint8_t preIV[32];
|
|
||||||
uint8_t padding[158];
|
|
||||||
};
|
|
||||||
|
|
||||||
const int INCOMING_TAGS_EXPIRATION_TIMEOUT = 960; // 16 minutes
|
|
||||||
const int OUTGOING_TAGS_EXPIRATION_TIMEOUT = 720; // 12 minutes
|
|
||||||
const int OUTGOING_TAGS_CONFIRMATION_TIMEOUT = 10; // 10 seconds
|
|
||||||
const int LEASET_CONFIRMATION_TIMEOUT = 4000; // in milliseconds
|
|
||||||
const int ROUTING_PATH_EXPIRATION_TIMEOUT = 30; // 30 seconds
|
|
||||||
const int ROUTING_PATH_MAX_NUM_TIMES_USED = 100; // how many times might be used
|
|
||||||
|
|
||||||
struct SessionTag: public i2p::data::Tag<32>
|
|
||||||
{
|
|
||||||
SessionTag (const uint8_t * buf, uint32_t ts = 0): Tag<32>(buf), creationTime (ts) {};
|
|
||||||
SessionTag () = default;
|
|
||||||
SessionTag (const SessionTag& ) = default;
|
|
||||||
SessionTag& operator= (const SessionTag& ) = default;
|
|
||||||
#ifndef _WIN32
|
|
||||||
SessionTag (SessionTag&& ) = default;
|
|
||||||
SessionTag& operator= (SessionTag&& ) = default;
|
|
||||||
#endif
|
|
||||||
uint32_t creationTime; // seconds since epoch
|
|
||||||
};
|
|
||||||
|
|
||||||
struct GarlicRoutingPath
|
|
||||||
{
|
|
||||||
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
|
|
||||||
std::shared_ptr<const i2p::data::Lease> remoteLease;
|
|
||||||
int rtt; // RTT
|
|
||||||
uint32_t updateTime; // seconds since epoch
|
|
||||||
int numTimesUsed;
|
|
||||||
};
|
|
||||||
|
|
||||||
class GarlicDestination;
|
|
||||||
class GarlicRoutingSession: public std::enable_shared_from_this<GarlicRoutingSession>
|
|
||||||
{
|
|
||||||
enum LeaseSetUpdateStatus
|
|
||||||
{
|
|
||||||
eLeaseSetUpToDate = 0,
|
|
||||||
eLeaseSetUpdated,
|
|
||||||
eLeaseSetSubmitted,
|
|
||||||
eLeaseSetDoNotSend
|
|
||||||
};
|
|
||||||
|
|
||||||
struct UnconfirmedTags
|
|
||||||
{
|
|
||||||
UnconfirmedTags (int n): numTags (n), tagsCreationTime (0) { sessionTags = new SessionTag[numTags]; };
|
|
||||||
~UnconfirmedTags () { delete[] sessionTags; };
|
|
||||||
uint32_t msgID;
|
|
||||||
int numTags;
|
|
||||||
SessionTag * sessionTags;
|
|
||||||
uint32_t tagsCreationTime;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
GarlicRoutingSession (GarlicDestination * owner, std::shared_ptr<const i2p::data::RoutingDestination> destination,
|
|
||||||
int numTags, bool attachLeaseSet);
|
|
||||||
GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption
|
|
||||||
~GarlicRoutingSession ();
|
|
||||||
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<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 ()
|
|
||||||
{
|
|
||||||
if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated;
|
|
||||||
};
|
|
||||||
bool IsLeaseSetNonConfirmed () const { return m_LeaseSetUpdateStatus == eLeaseSetSubmitted; };
|
|
||||||
bool IsLeaseSetUpdated () const { return m_LeaseSetUpdateStatus == eLeaseSetUpdated; };
|
|
||||||
uint64_t GetLeaseSetSubmissionTime () const { return m_LeaseSetSubmissionTime; }
|
|
||||||
|
|
||||||
std::shared_ptr<GarlicRoutingPath> GetSharedRoutingPath ();
|
|
||||||
void SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path);
|
|
||||||
|
|
||||||
const GarlicDestination * GetOwner () const { return m_Owner; }
|
|
||||||
void SetOwner (GarlicDestination * owner) { m_Owner = owner; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
size_t CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg);
|
|
||||||
size_t CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags);
|
|
||||||
size_t CreateGarlicClove (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination);
|
|
||||||
size_t CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID);
|
|
||||||
|
|
||||||
void TagsConfirmed (uint32_t msgID);
|
|
||||||
UnconfirmedTags * GenerateSessionTags ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
GarlicDestination * m_Owner;
|
|
||||||
std::shared_ptr<const i2p::data::RoutingDestination> m_Destination;
|
|
||||||
i2p::crypto::AESKey m_SessionKey;
|
|
||||||
std::list<SessionTag> m_SessionTags;
|
|
||||||
int m_NumTags;
|
|
||||||
std::map<uint32_t, std::unique_ptr<UnconfirmedTags> > m_UnconfirmedTagsMsgs; // msgID->tags
|
|
||||||
|
|
||||||
LeaseSetUpdateStatus m_LeaseSetUpdateStatus;
|
|
||||||
uint32_t m_LeaseSetUpdateMsgID;
|
|
||||||
uint64_t m_LeaseSetSubmissionTime; // in milliseconds
|
|
||||||
|
|
||||||
i2p::crypto::CBCEncryption m_Encryption;
|
|
||||||
std::unique_ptr<const i2p::crypto::ElGamalEncryption> m_ElGamalEncryption;
|
|
||||||
|
|
||||||
std::shared_ptr<GarlicRoutingPath> m_SharedRoutingPath;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// for HTTP only
|
|
||||||
size_t GetNumOutgoingTags () const { return m_SessionTags.size (); };
|
|
||||||
};
|
|
||||||
//using GarlicRoutingSessionPtr = std::shared_ptr<GarlicRoutingSession>;
|
|
||||||
typedef std::shared_ptr<GarlicRoutingSession> GarlicRoutingSessionPtr; // TODO: replace to using after switch to 4.8
|
|
||||||
|
|
||||||
class GarlicDestination: public i2p::data::LocalDestination
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
GarlicDestination (): m_NumTags (32) {}; // 32 tags by default
|
|
||||||
~GarlicDestination ();
|
|
||||||
|
|
||||||
void CleanUp ();
|
|
||||||
void SetNumTags (int numTags) { m_NumTags = numTags; };
|
|
||||||
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);
|
|
||||||
void CleanupExpiredTags ();
|
|
||||||
void RemoveDeliveryStatusSession (uint32_t msgID);
|
|
||||||
std::shared_ptr<I2NPMessage> WrapMessage (std::shared_ptr<const i2p::data::RoutingDestination> destination,
|
|
||||||
std::shared_ptr<I2NPMessage> msg, bool attachLeaseSet = false);
|
|
||||||
|
|
||||||
void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag
|
|
||||||
virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread
|
|
||||||
void DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID);
|
|
||||||
|
|
||||||
virtual void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
virtual void SetLeaseSetUpdated ();
|
|
||||||
|
|
||||||
virtual std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () = 0; // TODO
|
|
||||||
virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const = 0;
|
|
||||||
virtual void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from) = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
void HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<i2p::crypto::CBCDecryption> decryption,
|
|
||||||
std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
|
||||||
void HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
// outgoing sessions
|
|
||||||
int m_NumTags;
|
|
||||||
std::mutex m_SessionsMutex;
|
|
||||||
std::map<i2p::data::IdentHash, GarlicRoutingSessionPtr> m_Sessions;
|
|
||||||
// incoming
|
|
||||||
std::map<SessionTag, std::shared_ptr<i2p::crypto::CBCDecryption>> m_Tags;
|
|
||||||
// DeliveryStatus
|
|
||||||
std::mutex m_DeliveryStatusSessionsMutex;
|
|
||||||
std::map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// for HTTP only
|
|
||||||
size_t GetNumIncomingTags () const { return m_Tags.size (); }
|
|
||||||
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
108
Gzip.cpp
108
Gzip.cpp
@@ -1,108 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
||||||
431
HTTP.cpp
431
HTTP.cpp
@@ -1,431 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
151
HTTP.h
@@ -1,151 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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__ */
|
|
||||||
345
HTTPProxy.cpp
345
HTTPProxy.cpp
@@ -1,345 +0,0 @@
|
|||||||
#include <cstring>
|
|
||||||
#include <cassert>
|
|
||||||
#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"
|
|
||||||
#include "Streaming.h"
|
|
||||||
#include "Destination.h"
|
|
||||||
#include "ClientContext.h"
|
|
||||||
#include "I2PEndian.h"
|
|
||||||
#include "I2PTunnel.h"
|
|
||||||
#include "Config.h"
|
|
||||||
#include "HTTP.h"
|
|
||||||
|
|
||||||
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>I2Pd HTTP proxy</title>\r\n"
|
|
||||||
" <style type=\"text/css\">\r\n"
|
|
||||||
" body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: #FAFAFA; color: #103456; }\r\n"
|
|
||||||
" .header { font-size: 2.5em; text-align: center; margin: 1.5em 0; color: #894C84; }\r\n"
|
|
||||||
" </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:
|
|
||||||
|
|
||||||
bool HandleRequest();
|
|
||||||
void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
|
|
||||||
void Terminate();
|
|
||||||
void AsyncSockRead();
|
|
||||||
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 GenericProxyInfo(const char *title, const char *description);
|
|
||||||
void HostNotFound(std::string & host);
|
|
||||||
void SendProxyError(std::string & content);
|
|
||||||
|
|
||||||
uint8_t m_recv_chunk[8192];
|
|
||||||
std::string m_recv_buf; // from client
|
|
||||||
std::string m_send_buf; // to upstream
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> m_sock;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
HTTPReqHandler(HTTPProxy * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock) :
|
|
||||||
I2PServiceHandler(parent), m_sock(sock) {}
|
|
||||||
~HTTPReqHandler() { Terminate(); }
|
|
||||||
void Handle () { AsyncSockRead(); } /* overload */
|
|
||||||
};
|
|
||||||
|
|
||||||
void HTTPReqHandler::AsyncSockRead()
|
|
||||||
{
|
|
||||||
LogPrint(eLogDebug, "HTTPProxy: async sock read");
|
|
||||||
if (!m_sock) {
|
|
||||||
LogPrint(eLogError, "HTTPProxy: no socket for read");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_sock->async_read_some(boost::asio::buffer(m_recv_chunk, sizeof(m_recv_chunk)),
|
|
||||||
std::bind(&HTTPReqHandler::HandleSockRecv, shared_from_this(),
|
|
||||||
std::placeholders::_1, std::placeholders::_2));
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPReqHandler::Terminate() {
|
|
||||||
if (Kill()) return;
|
|
||||||
if (m_sock)
|
|
||||||
{
|
|
||||||
LogPrint(eLogDebug, "HTTPProxy: close sock");
|
|
||||||
m_sock->close();
|
|
||||||
m_sock = nullptr;
|
|
||||||
}
|
|
||||||
Done(shared_from_this());
|
|
||||||
}
|
|
||||||
|
|
||||||
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 HTTPReqHandler::GenericProxyInfo(const char *title, const char *description) {
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "<h1>Proxy info: " << title << "</h1>\r\n";
|
|
||||||
ss << "<p>" << description << "</p>\r\n";
|
|
||||||
std::string content = ss.str();
|
|
||||||
SendProxyError(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPReqHandler::HostNotFound(std::string & host) {
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "<h1>Proxy error: Host not found</h1>\r\n"
|
|
||||||
<< "<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";
|
|
||||||
}
|
|
||||||
ss << "</ul>\r\n";
|
|
||||||
std::string content = ss.str();
|
|
||||||
SendProxyError(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPReqHandler::SendProxyError(std::string & content)
|
|
||||||
{
|
|
||||||
i2p::http::HTTPRes res;
|
|
||||||
res.code = 500;
|
|
||||||
res.add_header("Content-Type", "text/html; charset=UTF-8");
|
|
||||||
res.add_header("Connection", "close");
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "<html>\r\n" << pageHead
|
|
||||||
<< "<body>" << content << "</body>\r\n"
|
|
||||||
<< "</html>\r\n";
|
|
||||||
res.body = ss.str();
|
|
||||||
std::string response = res.to_string();
|
|
||||||
boost::asio::async_write(*m_sock, boost::asio::buffer(response),
|
|
||||||
std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HTTPReqHandler::ExtractAddressHelper(i2p::http::URL & url, std::string & b64)
|
|
||||||
{
|
|
||||||
const char *param = "i2paddresshelper=";
|
|
||||||
std::size_t pos = url.query.find(param);
|
|
||||||
std::size_t len = std::strlen(param);
|
|
||||||
std::map<std::string, std::string> params;
|
|
||||||
|
|
||||||
if (pos == std::string::npos)
|
|
||||||
return false; /* not found */
|
|
||||||
if (!url.parse_query(params))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::string value = params["i2paddresshelper"];
|
|
||||||
len += value.length();
|
|
||||||
b64 = i2p::http::UrlDecode(value);
|
|
||||||
url.query.replace(pos, len, "");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPReqHandler::SanitizeHTTPRequest(i2p::http::HTTPReq & req)
|
|
||||||
{
|
|
||||||
/* 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 */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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.";
|
|
||||||
GenericProxyInfo("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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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, recv buf: ", m_recv_buf.length(), ", send buf: ", m_send_buf.length());
|
|
||||||
if(ecode)
|
|
||||||
{
|
|
||||||
LogPrint(eLogWarning, "HTTPProxy: sock recv got error: ", ecode);
|
|
||||||
Terminate();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_recv_buf.append(reinterpret_cast<const char *>(m_recv_chunk), len);
|
|
||||||
if (HandleRequest()) {
|
|
||||||
m_recv_buf.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
AsyncSockRead();
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPReqHandler::SentHTTPFailed(const boost::system::error_code & ecode)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
LogPrint (eLogError, "HTTPProxy: Closing socket after sending failure because: ", ecode.message ());
|
|
||||||
Terminate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPReqHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
|
|
||||||
{
|
|
||||||
if (!stream) {
|
|
||||||
LogPrint (eLogError, "HTTPProxy: error when creating the stream, check the previous warnings for more info");
|
|
||||||
GenericProxyError("Host is down", "Can't create connection to requested host, it may be down");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Kill())
|
|
||||||
return;
|
|
||||||
LogPrint (eLogDebug, "HTTPProxy: Created new I2PTunnel stream, sSID=", stream->GetSendStreamID(), ", rSID=", stream->GetRecvStreamID());
|
|
||||||
auto connection = std::make_shared<i2p::client::I2PTunnelConnection>(GetOwner(), m_sock, stream);
|
|
||||||
GetOwner()->AddHandler (connection);
|
|
||||||
connection->I2PConnect (reinterpret_cast<const uint8_t*>(m_send_buf.data()), m_send_buf.length());
|
|
||||||
Done (shared_from_this());
|
|
||||||
}
|
|
||||||
|
|
||||||
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> HTTPProxy::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
|
|
||||||
{
|
|
||||||
return std::make_shared<HTTPReqHandler> (this, socket);
|
|
||||||
}
|
|
||||||
} // http
|
|
||||||
} // i2p
|
|
||||||
21
HTTPProxy.h
21
HTTPProxy.h
@@ -1,21 +0,0 @@
|
|||||||
#ifndef HTTP_PROXY_H__
|
|
||||||
#define HTTP_PROXY_H__
|
|
||||||
|
|
||||||
namespace i2p {
|
|
||||||
namespace proxy {
|
|
||||||
class HTTPProxy: public i2p::client::TCPIPAcceptor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
HTTPProxy(const std::string& address, int port, std::shared_ptr<i2p::client::ClientDestination> localDestination = nullptr);
|
|
||||||
~HTTPProxy() {};
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// Implements TCPIPAcceptor
|
|
||||||
std::shared_ptr<i2p::client::I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket);
|
|
||||||
const char* GetName() { return "HTTP Proxy"; }
|
|
||||||
};
|
|
||||||
} // http
|
|
||||||
} // i2p
|
|
||||||
|
|
||||||
#endif
|
|
||||||
939
HTTPServer.cpp
939
HTTPServer.cpp
@@ -1,939 +0,0 @@
|
|||||||
#include <iomanip>
|
|
||||||
#include <sstream>
|
|
||||||
#include <thread>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include <boost/bind.hpp>
|
|
||||||
|
|
||||||
#include "Base.h"
|
|
||||||
#include "FS.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "Config.h"
|
|
||||||
#include "Tunnel.h"
|
|
||||||
#include "TransitTunnel.h"
|
|
||||||
#include "Transports.h"
|
|
||||||
#include "NetDb.h"
|
|
||||||
#include "HTTP.h"
|
|
||||||
#include "LeaseSet.h"
|
|
||||||
#include "Destination.h"
|
|
||||||
#include "RouterContext.h"
|
|
||||||
#include "ClientContext.h"
|
|
||||||
#include "HTTPServer.h"
|
|
||||||
#include "Daemon.h"
|
|
||||||
#include "util.h"
|
|
||||||
#ifdef WIN32_APP
|
|
||||||
#include "Win32/Win32App.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// For image and info
|
|
||||||
#include "version.h"
|
|
||||||
|
|
||||||
namespace i2p {
|
|
||||||
namespace http {
|
|
||||||
const char *itoopieFavicon =
|
|
||||||
"data:image/png;base64,"
|
|
||||||
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACx"
|
|
||||||
"jwv8YQUAAAAJcEhZcwAALiIAAC4iAari3ZIAAAAHdElNRQfgCQsUNSZrkhi1AAAAGXRFWHRTb2Z0"
|
|
||||||
"d2FyZQBwYWludC5uZXQgNC4wLjEyQwRr7AAAAoJJREFUOE9jwAUqi4Q1oEwwcDTV1+5sETaBclGB"
|
|
||||||
"vb09C5QJB6kWpvFQJoOCeLC5kmjEHCgXE2SlyETLi3h6QrkM4VL+ssWSCZUgtopITLKqaOotRTEn"
|
|
||||||
"cbAkLqAkGtOqLBLVAWLXyWSVFkkmRiqLxuaqiWb/VBYJMAYrwgckJY25VEUzniqKhjU2y+RtCRSP"
|
|
||||||
"6lUXy/1jIBV5tlYxZUaFVMq2NInwIi9hO8fSfOEAqDZUoCwal6MulvOvyS7gi69K4j9zxZT/m0ps"
|
|
||||||
"/28ptvvvquXXryIa7QYMMdTwqi0WNtVi0GIDseXl7TnUxFKfnGlxAGp0+D8j2eH/8Ub7/9e7nf7X"
|
|
||||||
"+Af/B7rwt6pI0h0l0WhQADOC9DBkhSirpImHNVZKp24ukkyoshGLnN8d5fA/y13t/44Kq/8hlnL/"
|
|
||||||
"z7fZ/58f6vcxSNpbVUVFhV1RLNBVTsQzVYZPSwhsCAhkiIfpNMrkbO6TLf071Sfk/5ZSi/+7q6z/"
|
|
||||||
"P5ns+v9mj/P/CpuI/20y+aeNGYxZoVoYGmsF3aFMBAAZlCwftnF9ke3//bU2//fXWP8/UGv731Am"
|
|
||||||
"+V+DdNblSqnUYqhSTKAiYSOqJBrVqiaa+S3UNPr/gmyH/xuKXf63hnn/B8bIP0UxHfEyyeSNQKVM"
|
|
||||||
"EB1AEB2twhcTLp+gIBJUoyKasEpVJHmqskh8qryovUG/ffCHHRU2q/Tk/YuB6eGPsbExa7ZkpLu1"
|
|
||||||
"oLEcVDtuUCgV1w60rQzElpRUE1EVSX0BYidHiInXF4nagNhYQW60EF+ApH1ktni0A1SIITSUgVlZ"
|
|
||||||
"JHYnlIsfzJjIp9xZKswL5YKBHL+coKJoRDaUSzoozxHVrygQU4JykQADAwAT5b1NHtwZugAAAABJ"
|
|
||||||
"RU5ErkJggg==";
|
|
||||||
|
|
||||||
const char *cssStyles =
|
|
||||||
"<style>\r\n"
|
|
||||||
" body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: #FAFAFA; color: #103456; }\r\n"
|
|
||||||
" a { text-decoration: none; color: #894C84; }\r\n"
|
|
||||||
" a:hover { color: #FAFAFA; background: #894C84; }\r\n"
|
|
||||||
" .header { font-size: 2.5em; text-align: center; margin: 1.5em 0; color: #894C84; }\r\n"
|
|
||||||
" .wrapper { margin: 0 auto; padding: 1em; max-width: 60em; }\r\n"
|
|
||||||
" .left { float: left; position: absolute; }\r\n"
|
|
||||||
" .right { float: left; font-size: 1em; margin-left: 13em; max-width: 46em; overflow: auto; }\r\n"
|
|
||||||
" .tunnel.established { color: #56B734; }\r\n"
|
|
||||||
" .tunnel.expiring { color: #D3AE3F; }\r\n"
|
|
||||||
" .tunnel.failed { color: #D33F3F; }\r\n"
|
|
||||||
" .tunnel.another { color: #434343; }\r\n"
|
|
||||||
" caption { font-size: 1.5em; text-align: center; color: #894C84; }\r\n"
|
|
||||||
" table { width: 100%; border-collapse: collapse; text-align: center; }\r\n"
|
|
||||||
" .private { background: black; color: black; } .private:hover { background: black; color: white } \r\n"
|
|
||||||
" .slide p, .slide [type='checkbox']{ display:none; } \r\n"
|
|
||||||
" .slide [type='checkbox']:checked ~ p { display:block; } \r\n"
|
|
||||||
"</style>\r\n";
|
|
||||||
|
|
||||||
const char HTTP_PAGE_TUNNELS[] = "tunnels";
|
|
||||||
const char HTTP_PAGE_TRANSIT_TUNNELS[] = "transit_tunnels";
|
|
||||||
const char HTTP_PAGE_TRANSPORTS[] = "transports";
|
|
||||||
const char HTTP_PAGE_LOCAL_DESTINATIONS[] = "local_destinations";
|
|
||||||
const char HTTP_PAGE_LOCAL_DESTINATION[] = "local_destination";
|
|
||||||
const char HTTP_PAGE_I2CP_LOCAL_DESTINATION[] = "i2cp_local_destination";
|
|
||||||
const char HTTP_PAGE_SAM_SESSIONS[] = "sam_sessions";
|
|
||||||
const char HTTP_PAGE_SAM_SESSION[] = "sam_session";
|
|
||||||
const char HTTP_PAGE_I2P_TUNNELS[] = "i2p_tunnels";
|
|
||||||
const char HTTP_PAGE_COMMANDS[] = "commands";
|
|
||||||
const char HTTP_PAGE_LEASESETS[] = "leasesets";
|
|
||||||
const char HTTP_COMMAND_ENABLE_TRANSIT[] = "enable_transit";
|
|
||||||
const char HTTP_COMMAND_DISABLE_TRANSIT[] = "disable_transit";
|
|
||||||
const char HTTP_COMMAND_SHUTDOWN_START[] = "shutdown_start";
|
|
||||||
const char HTTP_COMMAND_SHUTDOWN_CANCEL[] = "shutdown_cancel";
|
|
||||||
const char HTTP_COMMAND_SHUTDOWN_NOW[] = "terminate";
|
|
||||||
const char HTTP_COMMAND_RUN_PEER_TEST[] = "run_peer_test";
|
|
||||||
const char HTTP_COMMAND_RELOAD_CONFIG[] = "reload_config";
|
|
||||||
const char HTTP_PARAM_SAM_SESSION_ID[] = "id";
|
|
||||||
const char HTTP_PARAM_ADDRESS[] = "address";
|
|
||||||
|
|
||||||
static void ShowUptime (std::stringstream& s, int seconds)
|
|
||||||
{
|
|
||||||
int num;
|
|
||||||
|
|
||||||
if ((num = seconds / 86400) > 0) {
|
|
||||||
s << num << " days, ";
|
|
||||||
seconds -= num * 86400;
|
|
||||||
}
|
|
||||||
if ((num = seconds / 3600) > 0) {
|
|
||||||
s << num << " hours, ";
|
|
||||||
seconds -= num * 3600;
|
|
||||||
}
|
|
||||||
if ((num = seconds / 60) > 0) {
|
|
||||||
s << num << " min, ";
|
|
||||||
seconds -= num * 60;
|
|
||||||
}
|
|
||||||
s << seconds << " seconds";
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, int bytes)
|
|
||||||
{
|
|
||||||
std::string state;
|
|
||||||
switch (eState) {
|
|
||||||
case i2p::tunnel::eTunnelStateBuildReplyReceived :
|
|
||||||
case i2p::tunnel::eTunnelStatePending : state = "building"; break;
|
|
||||||
case i2p::tunnel::eTunnelStateBuildFailed :
|
|
||||||
case i2p::tunnel::eTunnelStateTestFailed :
|
|
||||||
case i2p::tunnel::eTunnelStateFailed : state = "failed"; break;
|
|
||||||
case i2p::tunnel::eTunnelStateExpiring : state = "expiring"; break;
|
|
||||||
case i2p::tunnel::eTunnelStateEstablished : state = "established"; break;
|
|
||||||
default: state = "unknown"; break;
|
|
||||||
}
|
|
||||||
s << "<span class=\"tunnel " << state << "\"> " << state << "</span>, ";
|
|
||||||
s << " " << (int) (bytes / 1024) << " KiB<br>\r\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ShowPageHead (std::stringstream& s)
|
|
||||||
{
|
|
||||||
s <<
|
|
||||||
"<!DOCTYPE html>\r\n"
|
|
||||||
"<html lang=\"en\">\r\n" /* TODO: Add support for locale */
|
|
||||||
" <head>\r\n" /* TODO: Find something to parse html/template system. This is horrible. */
|
|
||||||
#if (!defined(WIN32))
|
|
||||||
" <meta charset=\"UTF-8\">\r\n"
|
|
||||||
#else
|
|
||||||
" <meta charset=\"windows-1251\">\r\n"
|
|
||||||
#endif
|
|
||||||
" <link rel=\"shortcut icon\" href=\"" << itoopieFavicon << "\">\r\n"
|
|
||||||
" <title>Purple I2P " VERSION " Webconsole</title>\r\n"
|
|
||||||
<< cssStyles <<
|
|
||||||
"</head>\r\n";
|
|
||||||
s <<
|
|
||||||
"<body>\r\n"
|
|
||||||
"<div class=header><b>i2pd</b> webconsole</div>\r\n"
|
|
||||||
"<div class=wrapper>\r\n"
|
|
||||||
"<div class=left>\r\n"
|
|
||||||
" <a href=\"/\">Main page</a><br>\r\n<br>\r\n"
|
|
||||||
" <a href=\"/?page=" << HTTP_PAGE_COMMANDS << "\">Router commands</a><br>\r\n"
|
|
||||||
" <a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATIONS << "\">Local destinations</a><br>\r\n"
|
|
||||||
" <a href=\"/?page=" << HTTP_PAGE_LEASESETS << "\">LeaseSets</a><br>\r\n"
|
|
||||||
" <a href=\"/?page=" << HTTP_PAGE_TUNNELS << "\">Tunnels</a><br>\r\n"
|
|
||||||
" <a href=\"/?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">Transit tunnels</a><br>\r\n"
|
|
||||||
" <a href=\"/?page=" << HTTP_PAGE_TRANSPORTS << "\">Transports</a><br>\r\n"
|
|
||||||
" <a href=\"/?page=" << HTTP_PAGE_I2P_TUNNELS << "\">I2P tunnels</a><br>\r\n";
|
|
||||||
if (i2p::client::context.GetSAMBridge ())
|
|
||||||
s << " <a href=\"/?page=" << HTTP_PAGE_SAM_SESSIONS << "\">SAM sessions</a><br>\r\n";
|
|
||||||
s <<
|
|
||||||
"</div>\r\n"
|
|
||||||
"<div class=right>";
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ShowPageTail (std::stringstream& s)
|
|
||||||
{
|
|
||||||
s <<
|
|
||||||
"</div></div>\r\n"
|
|
||||||
"</body>\r\n"
|
|
||||||
"</html>\r\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ShowError(std::stringstream& s, const std::string& string)
|
|
||||||
{
|
|
||||||
s << "<b>ERROR:</b> " << string << "<br>\r\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ShowStatus (std::stringstream& s)
|
|
||||||
{
|
|
||||||
s << "<b>Uptime:</b> ";
|
|
||||||
ShowUptime(s, i2p::context.GetUptime ());
|
|
||||||
s << "<br>\r\n";
|
|
||||||
s << "<b>Network status:</b> ";
|
|
||||||
switch (i2p::context.GetStatus ())
|
|
||||||
{
|
|
||||||
case eRouterStatusOK: s << "OK"; break;
|
|
||||||
case eRouterStatusTesting: s << "Testing"; break;
|
|
||||||
case eRouterStatusFirewalled: s << "Firewalled"; break;
|
|
||||||
case eRouterStatusError:
|
|
||||||
{
|
|
||||||
s << "Error";
|
|
||||||
switch (i2p::context.GetError ())
|
|
||||||
{
|
|
||||||
case eRouterErrorClockSkew:
|
|
||||||
s << "<br>Clock skew";
|
|
||||||
break;
|
|
||||||
default: ;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: s << "Unknown";
|
|
||||||
}
|
|
||||||
s << "<br>\r\n";
|
|
||||||
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
|
|
||||||
if (auto remains = Daemon.gracefulShutdownInterval) {
|
|
||||||
s << "<b>Stopping in:</b> ";
|
|
||||||
s << remains << " seconds";
|
|
||||||
s << "<br>\r\n";
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
auto family = i2p::context.GetFamily ();
|
|
||||||
if (family.length () > 0)
|
|
||||||
s << "<b>Family:</b> " << family << "<br>\r\n";
|
|
||||||
s << "<b>Tunnel creation success rate:</b> " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%<br>\r\n";
|
|
||||||
s << "<b>Received:</b> ";
|
|
||||||
s << std::fixed << std::setprecision(2);
|
|
||||||
auto numKBytesReceived = (double) i2p::transport::transports.GetTotalReceivedBytes () / 1024;
|
|
||||||
if (numKBytesReceived < 1024)
|
|
||||||
s << numKBytesReceived << " KiB";
|
|
||||||
else if (numKBytesReceived < 1024 * 1024)
|
|
||||||
s << numKBytesReceived / 1024 << " MiB";
|
|
||||||
else
|
|
||||||
s << numKBytesReceived / 1024 / 1024 << " GiB";
|
|
||||||
s << " (" << (double) i2p::transport::transports.GetInBandwidth () / 1024 << " KiB/s)<br>\r\n";
|
|
||||||
s << "<b>Sent:</b> ";
|
|
||||||
auto numKBytesSent = (double) i2p::transport::transports.GetTotalSentBytes () / 1024;
|
|
||||||
if (numKBytesSent < 1024)
|
|
||||||
s << numKBytesSent << " KiB";
|
|
||||||
else if (numKBytesSent < 1024 * 1024)
|
|
||||||
s << numKBytesSent / 1024 << " MiB";
|
|
||||||
else
|
|
||||||
s << numKBytesSent / 1024 / 1024 << " GiB";
|
|
||||||
s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " KiB/s)<br>\r\n";
|
|
||||||
s << "<b>Data path:</b> " << i2p::fs::GetDataDir() << "<br>\r\n";
|
|
||||||
s << "<div class='slide'\r\n><label for='slide1'>Hidden content. Press on text to see.</label>\r\n<input type='checkbox' id='slide1'/>\r\n<p class='content'>\r\n";
|
|
||||||
s << "<b>Router Ident:</b> " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "<br>\r\n";
|
|
||||||
s << "<b>Router Family:</b> " << i2p::context.GetRouterInfo().GetProperty("family") << "<br>\r\n";
|
|
||||||
s << "<b>Router Caps:</b> " << i2p::context.GetRouterInfo().GetProperty("caps") << "<br>\r\n";
|
|
||||||
s << "<b>Our external address:</b>" << "<br>\r\n" ;
|
|
||||||
for (const auto& address : i2p::context.GetRouterInfo().GetAddresses())
|
|
||||||
{
|
|
||||||
switch (address->transportStyle)
|
|
||||||
{
|
|
||||||
case i2p::data::RouterInfo::eTransportNTCP:
|
|
||||||
if (address->host.is_v6 ())
|
|
||||||
s << "NTCP6 ";
|
|
||||||
else
|
|
||||||
s << "NTCP ";
|
|
||||||
break;
|
|
||||||
case i2p::data::RouterInfo::eTransportSSU:
|
|
||||||
if (address->host.is_v6 ())
|
|
||||||
s << "SSU6 ";
|
|
||||||
else
|
|
||||||
s << "SSU ";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
s << "Unknown ";
|
|
||||||
}
|
|
||||||
s << address->host.to_string() << ":" << address->port << "<br>\r\n";
|
|
||||||
}
|
|
||||||
s << "</p>\r\n</div>\r\n";
|
|
||||||
s << "<b>Routers:</b> " << i2p::data::netdb.GetNumRouters () << " ";
|
|
||||||
s << "<b>Floodfills:</b> " << i2p::data::netdb.GetNumFloodfills () << " ";
|
|
||||||
s << "<b>LeaseSets:</b> " << i2p::data::netdb.GetNumLeaseSets () << "<br>\r\n";
|
|
||||||
|
|
||||||
size_t clientTunnelCount = i2p::tunnel::tunnels.CountOutboundTunnels();
|
|
||||||
clientTunnelCount += i2p::tunnel::tunnels.CountInboundTunnels();
|
|
||||||
size_t transitTunnelCount = i2p::tunnel::tunnels.CountTransitTunnels();
|
|
||||||
|
|
||||||
s << "<b>Client Tunnels:</b> " << std::to_string(clientTunnelCount) << " ";
|
|
||||||
s << "<b>Transit Tunnels:</b> " << std::to_string(transitTunnelCount) << "<br>\r\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ShowLocalDestinations (std::stringstream& s)
|
|
||||||
{
|
|
||||||
s << "<b>Local Destinations:</b><br>\r\n<br>\r\n";
|
|
||||||
for (auto& it: i2p::client::context.GetDestinations ())
|
|
||||||
{
|
|
||||||
auto ident = it.second->GetIdentHash ();
|
|
||||||
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
|
|
||||||
s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a><br>\r\n" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto i2cpServer = i2p::client::context.GetI2CPServer ();
|
|
||||||
if (i2cpServer)
|
|
||||||
{
|
|
||||||
s << "<br><b>I2CP Local Destinations:</b><br>\r\n<br>\r\n";
|
|
||||||
for (auto& it: i2cpServer->GetSessions ())
|
|
||||||
{
|
|
||||||
auto dest = it.second->GetDestination ();
|
|
||||||
if (dest)
|
|
||||||
{
|
|
||||||
auto ident = dest->GetIdentHash ();
|
|
||||||
s << "<a href=\"/?page=" << HTTP_PAGE_I2CP_LOCAL_DESTINATION << "&i2cp_id=" << it.first << "\">";
|
|
||||||
s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a><br>\r\n" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr<const i2p::client::LeaseSetDestination> dest)
|
|
||||||
{
|
|
||||||
s << "<b>Base64:</b><br>\r\n<textarea readonly=\"readonly\" cols=\"64\" rows=\"11\" wrap=\"on\">";
|
|
||||||
s << dest->GetIdentity ()->ToBase64 () << "</textarea><br>\r\n<br>\r\n";
|
|
||||||
s << "<b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets () << "</i><br>\r\n";
|
|
||||||
if(dest->GetNumRemoteLeaseSets())
|
|
||||||
{
|
|
||||||
s << "<div class='slide'\r\n><label for='slide1'>Hidden content. Press on text to see.</label>\r\n<input type='checkbox' id='slide1'/>\r\n<p class='content'>\r\n";
|
|
||||||
for(auto& it: dest->GetLeaseSets ())
|
|
||||||
s << it.second->GetIdentHash ().ToBase32 () << "<br>\r\n";
|
|
||||||
s << "</p>\r\n</div>\r\n";
|
|
||||||
}
|
|
||||||
auto pool = dest->GetTunnelPool ();
|
|
||||||
if (pool)
|
|
||||||
{
|
|
||||||
s << "<b>Inbound tunnels:</b><br>\r\n";
|
|
||||||
for (auto & it : pool->GetInboundTunnels ()) {
|
|
||||||
it->Print(s);
|
|
||||||
if(it->LatencyIsKnown())
|
|
||||||
s << " ( " << it->GetMeanLatency() << "ms )";
|
|
||||||
ShowTunnelDetails(s, it->GetState (), it->GetNumReceivedBytes ());
|
|
||||||
}
|
|
||||||
s << "<br>\r\n";
|
|
||||||
s << "<b>Outbound tunnels:</b><br>\r\n";
|
|
||||||
for (auto & it : pool->GetOutboundTunnels ()) {
|
|
||||||
it->Print(s);
|
|
||||||
if(it->LatencyIsKnown())
|
|
||||||
s << " ( " << it->GetMeanLatency() << "ms )";
|
|
||||||
ShowTunnelDetails(s, it->GetState (), it->GetNumSentBytes ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s << "<br>\r\n";
|
|
||||||
s << "<b>Tags</b><br>Incoming: " << dest->GetNumIncomingTags () << "<br>Outgoing:<br>" << std::endl;
|
|
||||||
for (const auto& it: dest->GetSessions ())
|
|
||||||
{
|
|
||||||
s << i2p::client::context.GetAddressBook ().ToAddress(it.first) << " ";
|
|
||||||
s << it.second->GetNumOutgoingTags () << "<br>" << std::endl;
|
|
||||||
}
|
|
||||||
s << "<br>" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ShowLocalDestination (std::stringstream& s, const std::string& b32)
|
|
||||||
{
|
|
||||||
s << "<b>Local Destination:</b><br>\r\n<br>\r\n";
|
|
||||||
i2p::data::IdentHash ident;
|
|
||||||
ident.FromBase32 (b32);
|
|
||||||
auto dest = i2p::client::context.FindLocalDestination (ident);
|
|
||||||
if (dest)
|
|
||||||
{
|
|
||||||
ShowLeaseSetDestination (s, dest);
|
|
||||||
// show streams
|
|
||||||
s << "<br>\r\n<table><caption>Streams</caption><tr>";
|
|
||||||
s << "<th>StreamID</th>";
|
|
||||||
s << "<th>Destination</th>";
|
|
||||||
s << "<th>Sent</th>";
|
|
||||||
s << "<th>Received</th>";
|
|
||||||
s << "<th>Out</th>";
|
|
||||||
s << "<th>In</th>";
|
|
||||||
s << "<th>Buf</th>";
|
|
||||||
s << "<th>RTT</th>";
|
|
||||||
s << "<th>Window</th>";
|
|
||||||
s << "<th>Status</th>";
|
|
||||||
s << "</tr>";
|
|
||||||
|
|
||||||
for (const auto& it: dest->GetAllStreams ())
|
|
||||||
{
|
|
||||||
s << "<tr>";
|
|
||||||
s << "<td>" << it->GetSendStreamID () << "</td>";
|
|
||||||
s << "<td>" << i2p::client::context.GetAddressBook ().ToAddress(it->GetRemoteIdentity ()) << "</td>";
|
|
||||||
s << "<td>" << it->GetNumSentBytes () << "</td>";
|
|
||||||
s << "<td>" << it->GetNumReceivedBytes () << "</td>";
|
|
||||||
s << "<td>" << it->GetSendQueueSize () << "</td>";
|
|
||||||
s << "<td>" << it->GetReceiveQueueSize () << "</td>";
|
|
||||||
s << "<td>" << it->GetSendBufferSize () << "</td>";
|
|
||||||
s << "<td>" << it->GetRTT () << "</td>";
|
|
||||||
s << "<td>" << it->GetWindowSize () << "</td>";
|
|
||||||
s << "<td>" << (int)it->GetStatus () << "</td>";
|
|
||||||
s << "</tr><br>\r\n" << std::endl;
|
|
||||||
}
|
|
||||||
s << "</table>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ShowI2CPLocalDestination (std::stringstream& s, const std::string& id)
|
|
||||||
{
|
|
||||||
auto i2cpServer = i2p::client::context.GetI2CPServer ();
|
|
||||||
if (i2cpServer)
|
|
||||||
{
|
|
||||||
s << "<b>I2CP Local Destination:</b><br>\r\n<br>\r\n";
|
|
||||||
auto it = i2cpServer->GetSessions ().find (std::stoi (id));
|
|
||||||
if (it != i2cpServer->GetSessions ().end ())
|
|
||||||
ShowLeaseSetDestination (s, it->second->GetDestination ());
|
|
||||||
else
|
|
||||||
ShowError(s, "I2CP session not found");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ShowError(s, "I2CP is not enabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ShowLeasesSets(std::stringstream& s)
|
|
||||||
{
|
|
||||||
s << "<div id='leasesets'><b>LeaseSets (click on to show info):</b></div><br>\r\n";
|
|
||||||
int counter = 1;
|
|
||||||
// for each lease set
|
|
||||||
i2p::data::netdb.VisitLeaseSets(
|
|
||||||
[&s, &counter](const i2p::data::IdentHash dest, std::shared_ptr<i2p::data::LeaseSet> leaseSet)
|
|
||||||
{
|
|
||||||
// create copy of lease set so we extract leases
|
|
||||||
i2p::data::LeaseSet ls(leaseSet->GetBuffer(), leaseSet->GetBufferLen());
|
|
||||||
s << "<div class='leaseset";
|
|
||||||
if (ls.IsExpired())
|
|
||||||
s << " expired"; // additional css class for expired
|
|
||||||
s << "'>\r\n";
|
|
||||||
if (!ls.IsValid())
|
|
||||||
s << "<div class='invalid'>!! Invalid !! </div>\r\n";
|
|
||||||
s << "<div class='slide'><label for='slide" << counter << "'>" << dest.ToBase32() << "</label>\r\n";
|
|
||||||
s << "<input type='checkbox' id='slide" << (counter++) << "'/>\r\n<p class='content'>\r\n";
|
|
||||||
s << "<b>Expires:</b> " << ls.GetExpirationTime() << "<br>\r\n";
|
|
||||||
auto leases = ls.GetNonExpiredLeases();
|
|
||||||
s << "<b>Non Expired Leases: " << leases.size() << "</b><br>\r\n";
|
|
||||||
for ( auto & l : leases )
|
|
||||||
{
|
|
||||||
s << "<b>Gateway:</b> " << l->tunnelGateway.ToBase64() << "<br>\r\n";
|
|
||||||
s << "<b>TunnelID:</b> " << l->tunnelID << "<br>\r\n";
|
|
||||||
s << "<b>EndDate:</b> " << l->endDate << "<br>\r\n";
|
|
||||||
}
|
|
||||||
s << "</p>\r\n</div>\r\n</div>\r\n";
|
|
||||||
}
|
|
||||||
);
|
|
||||||
// end for each lease set
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ShowTunnels (std::stringstream& s)
|
|
||||||
{
|
|
||||||
s << "<b>Queue size:</b> " << i2p::tunnel::tunnels.GetQueueSize () << "<br>\r\n";
|
|
||||||
|
|
||||||
s << "<b>Inbound tunnels:</b><br>\r\n";
|
|
||||||
for (auto & it : i2p::tunnel::tunnels.GetInboundTunnels ()) {
|
|
||||||
it->Print(s);
|
|
||||||
if(it->LatencyIsKnown())
|
|
||||||
s << " ( " << it->GetMeanLatency() << "ms )";
|
|
||||||
ShowTunnelDetails(s, it->GetState (), it->GetNumReceivedBytes ());
|
|
||||||
}
|
|
||||||
s << "<br>\r\n";
|
|
||||||
s << "<b>Outbound tunnels:</b><br>\r\n";
|
|
||||||
for (auto & it : i2p::tunnel::tunnels.GetOutboundTunnels ()) {
|
|
||||||
it->Print(s);
|
|
||||||
if(it->LatencyIsKnown())
|
|
||||||
s << " ( " << it->GetMeanLatency() << "ms )";
|
|
||||||
ShowTunnelDetails(s, it->GetState (), it->GetNumSentBytes ());
|
|
||||||
}
|
|
||||||
s << "<br>\r\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ShowCommands (std::stringstream& s)
|
|
||||||
{
|
|
||||||
/* commands */
|
|
||||||
s << "<b>Router Commands</b><br>\r\n";
|
|
||||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "\">Run peer test</a><br>\r\n";
|
|
||||||
//s << " <a href=\"/?cmd=" << HTTP_COMMAND_RELOAD_CONFIG << "\">Reload config</a><br>\r\n";
|
|
||||||
if (i2p::context.AcceptsTunnels ())
|
|
||||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_DISABLE_TRANSIT << "\">Decline transit tunnels</a><br>\r\n";
|
|
||||||
else
|
|
||||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_ENABLE_TRANSIT << "\">Accept transit tunnels</a><br>\r\n";
|
|
||||||
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
|
|
||||||
if (Daemon.gracefulShutdownInterval)
|
|
||||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "\">Cancel graceful shutdown</a><br>";
|
|
||||||
else
|
|
||||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "\">Start graceful shutdown</a><br>\r\n";
|
|
||||||
#endif
|
|
||||||
#ifdef WIN32_APP
|
|
||||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "\">Graceful shutdown</a><br>\r\n";
|
|
||||||
#endif
|
|
||||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "\">Force shutdown</a><br>\r\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ShowTransitTunnels (std::stringstream& s)
|
|
||||||
{
|
|
||||||
s << "<b>Transit tunnels:</b><br>\r\n<br>\r\n";
|
|
||||||
for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
|
|
||||||
{
|
|
||||||
if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelGateway>(it))
|
|
||||||
s << it->GetTunnelID () << " ⇒ ";
|
|
||||||
else if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelEndpoint>(it))
|
|
||||||
s << " ⇒ " << it->GetTunnelID ();
|
|
||||||
else
|
|
||||||
s << " ⇒ " << it->GetTunnelID () << " ⇒ ";
|
|
||||||
s << " " << it->GetNumTransmittedBytes () << "<br>\r\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ShowTransports (std::stringstream& s)
|
|
||||||
{
|
|
||||||
s << "<b>Transports:</b><br>\r\n<br>\r\n";
|
|
||||||
auto ntcpServer = i2p::transport::transports.GetNTCPServer ();
|
|
||||||
if (ntcpServer)
|
|
||||||
{
|
|
||||||
s << "<b>NTCP</b><br>\r\n";
|
|
||||||
for (const auto& it: ntcpServer->GetNTCPSessions ())
|
|
||||||
{
|
|
||||||
if (it.second && it.second->IsEstablished ())
|
|
||||||
{
|
|
||||||
// incoming connection doesn't have remote RI
|
|
||||||
if (it.second->IsOutgoing ()) s << " ⇒ ";
|
|
||||||
s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
|
|
||||||
<< it.second->GetSocket ().remote_endpoint().address ().to_string ();
|
|
||||||
if (!it.second->IsOutgoing ()) s << " ⇒ ";
|
|
||||||
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
|
|
||||||
s << "<br>\r\n" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto ssuServer = i2p::transport::transports.GetSSUServer ();
|
|
||||||
if (ssuServer)
|
|
||||||
{
|
|
||||||
s << "<br>\r\n<b>SSU</b><br>\r\n";
|
|
||||||
for (const auto& it: ssuServer->GetSessions ())
|
|
||||||
{
|
|
||||||
auto endpoint = it.second->GetRemoteEndpoint ();
|
|
||||||
if (it.second->IsOutgoing ()) s << " ⇒ ";
|
|
||||||
s << endpoint.address ().to_string () << ":" << endpoint.port ();
|
|
||||||
if (!it.second->IsOutgoing ()) s << " ⇒ ";
|
|
||||||
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
|
|
||||||
if (it.second->GetRelayTag ())
|
|
||||||
s << " [itag:" << it.second->GetRelayTag () << "]";
|
|
||||||
s << "<br>\r\n" << std::endl;
|
|
||||||
}
|
|
||||||
s << "<br>\r\n<b>SSU6</b><br>\r\n";
|
|
||||||
for (const auto& it: ssuServer->GetSessionsV6 ())
|
|
||||||
{
|
|
||||||
auto endpoint = it.second->GetRemoteEndpoint ();
|
|
||||||
if (it.second->IsOutgoing ()) s << " ⇒ ";
|
|
||||||
s << endpoint.address ().to_string () << ":" << endpoint.port ();
|
|
||||||
if (!it.second->IsOutgoing ()) s << " ⇒ ";
|
|
||||||
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
|
|
||||||
s << "<br>\r\n" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ShowSAMSessions (std::stringstream& s)
|
|
||||||
{
|
|
||||||
auto sam = i2p::client::context.GetSAMBridge ();
|
|
||||||
if (!sam) {
|
|
||||||
ShowError(s, "SAM disabled");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
s << "<b>SAM Sessions:</b><br>\r\n<br>\r\n";
|
|
||||||
for (auto& it: sam->GetSessions ())
|
|
||||||
{
|
|
||||||
s << "<a href=\"/?page=" << HTTP_PAGE_SAM_SESSION << "&sam_id=" << it.first << "\">";
|
|
||||||
s << it.first << "</a><br>\r\n" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ShowSAMSession (std::stringstream& s, const std::string& id)
|
|
||||||
{
|
|
||||||
s << "<b>SAM Session:</b><br>\r\n<br>\r\n";
|
|
||||||
auto sam = i2p::client::context.GetSAMBridge ();
|
|
||||||
if (!sam) {
|
|
||||||
ShowError(s, "SAM disabled");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto session = sam->FindSession (id);
|
|
||||||
if (!session) {
|
|
||||||
ShowError(s, "SAM session not found");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto& ident = session->localDestination->GetIdentHash();
|
|
||||||
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
|
|
||||||
s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a><br>\r\n";
|
|
||||||
s << "<br>\r\n";
|
|
||||||
s << "<b>Streams:</b><br>\r\n";
|
|
||||||
for (const auto& it: session->ListSockets())
|
|
||||||
{
|
|
||||||
switch (it->GetSocketType ())
|
|
||||||
{
|
|
||||||
case i2p::client::eSAMSocketTypeSession : s << "session"; break;
|
|
||||||
case i2p::client::eSAMSocketTypeStream : s << "stream"; break;
|
|
||||||
case i2p::client::eSAMSocketTypeAcceptor : s << "acceptor"; break;
|
|
||||||
default: s << "unknown"; break;
|
|
||||||
}
|
|
||||||
s << " [" << it->GetSocket ().remote_endpoint() << "]";
|
|
||||||
s << "<br>\r\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ShowI2PTunnels (std::stringstream& s)
|
|
||||||
{
|
|
||||||
s << "<b>Client Tunnels:</b><br>\r\n<br>\r\n";
|
|
||||||
for (auto& it: i2p::client::context.GetClientTunnels ())
|
|
||||||
{
|
|
||||||
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
|
|
||||||
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
|
|
||||||
s << it.second->GetName () << "</a> ⇐ ";
|
|
||||||
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
|
|
||||||
s << "<br>\r\n"<< std::endl;
|
|
||||||
}
|
|
||||||
auto httpProxy = i2p::client::context.GetHttpProxy ();
|
|
||||||
if (httpProxy)
|
|
||||||
{
|
|
||||||
auto& ident = httpProxy->GetLocalDestination ()->GetIdentHash();
|
|
||||||
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
|
|
||||||
s << "HTTP Proxy" << "</a> ⇐ ";
|
|
||||||
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
|
|
||||||
s << "<br>\r\n"<< std::endl;
|
|
||||||
}
|
|
||||||
s << "<br>\r\n<b>Server Tunnels:</b><br>\r\n<br>\r\n";
|
|
||||||
for (auto& it: i2p::client::context.GetServerTunnels ())
|
|
||||||
{
|
|
||||||
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
|
|
||||||
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
|
|
||||||
s << it.second->GetName () << "</a> ⇒ ";
|
|
||||||
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
|
|
||||||
s << ":" << it.second->GetLocalPort ();
|
|
||||||
s << "</a><br>\r\n"<< std::endl;
|
|
||||||
}
|
|
||||||
auto& clientForwards = i2p::client::context.GetClientForwards ();
|
|
||||||
if (!clientForwards.empty ())
|
|
||||||
{
|
|
||||||
s << "<br>\r\n<b>Client Forwards:</b><br>\r\n<br>\r\n";
|
|
||||||
for (auto& it: clientForwards)
|
|
||||||
{
|
|
||||||
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
|
|
||||||
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
|
|
||||||
s << it.second->GetName () << "</a> ⇐ ";
|
|
||||||
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
|
|
||||||
s << "<br>\r\n"<< std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto& serverForwards = i2p::client::context.GetServerForwards ();
|
|
||||||
if (!serverForwards.empty ())
|
|
||||||
{
|
|
||||||
s << "<br>\r\n<b>Server Forwards:</b><br>\r\n<br>\r\n";
|
|
||||||
for (auto& it: serverForwards)
|
|
||||||
{
|
|
||||||
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
|
|
||||||
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
|
|
||||||
s << it.second->GetName () << "</a> ⇐ ";
|
|
||||||
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
|
|
||||||
s << "<br>\r\n"<< std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HTTPConnection::HTTPConnection (std::shared_ptr<boost::asio::ip::tcp::socket> socket):
|
|
||||||
m_Socket (socket), m_Timer (socket->get_io_service ()), m_BufferLen (0)
|
|
||||||
{
|
|
||||||
/* cache options */
|
|
||||||
i2p::config::GetOption("http.auth", needAuth);
|
|
||||||
i2p::config::GetOption("http.user", user);
|
|
||||||
i2p::config::GetOption("http.pass", pass);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPConnection::Receive ()
|
|
||||||
{
|
|
||||||
m_Socket->async_read_some (boost::asio::buffer (m_Buffer, HTTP_CONNECTION_BUFFER_SIZE),
|
|
||||||
std::bind(&HTTPConnection::HandleReceive, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2));
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPConnection::HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (ecode) {
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate (ecode);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_Buffer[bytes_transferred] = '\0';
|
|
||||||
m_BufferLen = bytes_transferred;
|
|
||||||
RunRequest();
|
|
||||||
Receive ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPConnection::RunRequest ()
|
|
||||||
{
|
|
||||||
HTTPReq request;
|
|
||||||
int ret = request.parse(m_Buffer);
|
|
||||||
if (ret < 0) {
|
|
||||||
m_Buffer[0] = '\0';
|
|
||||||
m_BufferLen = 0;
|
|
||||||
return; /* error */
|
|
||||||
}
|
|
||||||
if (ret == 0)
|
|
||||||
return; /* need more data */
|
|
||||||
|
|
||||||
HandleRequest (request);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPConnection::Terminate (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode == boost::asio::error::operation_aborted)
|
|
||||||
return;
|
|
||||||
boost::system::error_code ignored_ec;
|
|
||||||
m_Socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
|
|
||||||
m_Socket->close ();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HTTPConnection::CheckAuth (const HTTPReq & req) {
|
|
||||||
/* method #1: http://user:pass@127.0.0.1:7070/ */
|
|
||||||
if (req.uri.find('@') != std::string::npos) {
|
|
||||||
URL url;
|
|
||||||
if (url.parse(req.uri) && url.user == user && url.pass == pass)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
/* method #2: 'Authorization' header sent */
|
|
||||||
if (req.headers.count("Authorization") > 0) {
|
|
||||||
std::string provided = req.headers.find("Authorization")->second;
|
|
||||||
std::string expected = user + ":" + pass;
|
|
||||||
char b64_creds[64];
|
|
||||||
std::size_t len = 0;
|
|
||||||
len = i2p::data::ByteStreamToBase64((unsigned char *)expected.c_str(), expected.length(), b64_creds, sizeof(b64_creds));
|
|
||||||
b64_creds[len] = '\0';
|
|
||||||
expected = "Basic ";
|
|
||||||
expected += b64_creds;
|
|
||||||
if (provided == expected)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
LogPrint(eLogWarning, "HTTPServer: auth failure from ", m_Socket->remote_endpoint().address ());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPConnection::HandleRequest (const HTTPReq & req)
|
|
||||||
{
|
|
||||||
std::stringstream s;
|
|
||||||
std::string content;
|
|
||||||
HTTPRes res;
|
|
||||||
|
|
||||||
LogPrint(eLogDebug, "HTTPServer: request: ", req.uri);
|
|
||||||
|
|
||||||
if (needAuth && !CheckAuth(req)) {
|
|
||||||
res.code = 401;
|
|
||||||
res.add_header("WWW-Authenticate", "Basic realm=\"WebAdmin\"");
|
|
||||||
SendReply(res, content);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Html5 head start
|
|
||||||
ShowPageHead (s);
|
|
||||||
if (req.uri.find("page=") != std::string::npos) {
|
|
||||||
HandlePage (req, res, s);
|
|
||||||
} else if (req.uri.find("cmd=") != std::string::npos) {
|
|
||||||
HandleCommand (req, res, s);
|
|
||||||
} else {
|
|
||||||
ShowStatus (s);
|
|
||||||
res.add_header("Refresh", "10");
|
|
||||||
}
|
|
||||||
ShowPageTail (s);
|
|
||||||
|
|
||||||
res.code = 200;
|
|
||||||
content = s.str ();
|
|
||||||
SendReply (res, content);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPConnection::HandlePage (const HTTPReq& req, HTTPRes& res, std::stringstream& s)
|
|
||||||
{
|
|
||||||
std::map<std::string, std::string> params;
|
|
||||||
std::string page("");
|
|
||||||
URL url;
|
|
||||||
|
|
||||||
url.parse(req.uri);
|
|
||||||
url.parse_query(params);
|
|
||||||
page = params["page"];
|
|
||||||
|
|
||||||
if (page == HTTP_PAGE_TRANSPORTS)
|
|
||||||
ShowTransports (s);
|
|
||||||
else if (page == HTTP_PAGE_TUNNELS)
|
|
||||||
ShowTunnels (s);
|
|
||||||
else if (page == HTTP_PAGE_COMMANDS)
|
|
||||||
ShowCommands (s);
|
|
||||||
else if (page == HTTP_PAGE_TRANSIT_TUNNELS)
|
|
||||||
ShowTransitTunnels (s);
|
|
||||||
else if (page == HTTP_PAGE_LOCAL_DESTINATIONS)
|
|
||||||
ShowLocalDestinations (s);
|
|
||||||
else if (page == HTTP_PAGE_LOCAL_DESTINATION)
|
|
||||||
ShowLocalDestination (s, params["b32"]);
|
|
||||||
else if (page == HTTP_PAGE_I2CP_LOCAL_DESTINATION)
|
|
||||||
ShowI2CPLocalDestination (s, params["i2cp_id"]);
|
|
||||||
else if (page == HTTP_PAGE_SAM_SESSIONS)
|
|
||||||
ShowSAMSessions (s);
|
|
||||||
else if (page == HTTP_PAGE_SAM_SESSION)
|
|
||||||
ShowSAMSession (s, params["sam_id"]);
|
|
||||||
else if (page == HTTP_PAGE_I2P_TUNNELS)
|
|
||||||
ShowI2PTunnels (s);
|
|
||||||
else if (page == HTTP_PAGE_LEASESETS)
|
|
||||||
ShowLeasesSets(s);
|
|
||||||
else {
|
|
||||||
res.code = 400;
|
|
||||||
ShowError(s, "Unknown page: " + page);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPConnection::HandleCommand (const HTTPReq& req, HTTPRes& res, std::stringstream& s)
|
|
||||||
{
|
|
||||||
std::map<std::string, std::string> params;
|
|
||||||
std::string cmd("");
|
|
||||||
URL url;
|
|
||||||
|
|
||||||
url.parse(req.uri);
|
|
||||||
url.parse_query(params);
|
|
||||||
cmd = params["cmd"];
|
|
||||||
|
|
||||||
if (cmd == HTTP_COMMAND_RUN_PEER_TEST)
|
|
||||||
i2p::transport::transports.PeerTest ();
|
|
||||||
else if (cmd == HTTP_COMMAND_RELOAD_CONFIG)
|
|
||||||
i2p::client::context.ReloadConfig ();
|
|
||||||
else if (cmd == HTTP_COMMAND_ENABLE_TRANSIT)
|
|
||||||
i2p::context.SetAcceptsTunnels (true);
|
|
||||||
else if (cmd == HTTP_COMMAND_DISABLE_TRANSIT)
|
|
||||||
i2p::context.SetAcceptsTunnels (false);
|
|
||||||
else if (cmd == HTTP_COMMAND_SHUTDOWN_START) {
|
|
||||||
i2p::context.SetAcceptsTunnels (false);
|
|
||||||
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
|
|
||||||
Daemon.gracefulShutdownInterval = 10*60;
|
|
||||||
#endif
|
|
||||||
#ifdef WIN32_APP
|
|
||||||
i2p::win32::GracefulShutdown ();
|
|
||||||
#endif
|
|
||||||
} else if (cmd == HTTP_COMMAND_SHUTDOWN_CANCEL) {
|
|
||||||
i2p::context.SetAcceptsTunnels (true);
|
|
||||||
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
|
|
||||||
Daemon.gracefulShutdownInterval = 0;
|
|
||||||
#endif
|
|
||||||
} else if (cmd == HTTP_COMMAND_SHUTDOWN_NOW) {
|
|
||||||
Daemon.running = false;
|
|
||||||
} else {
|
|
||||||
res.code = 400;
|
|
||||||
ShowError(s, "Unknown command: " + cmd);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
s << "<b>SUCCESS</b>: Command accepted<br><br>\r\n";
|
|
||||||
s << "<a href=\"/?page=commands\">Back to commands list</a><br>\r\n";
|
|
||||||
s << "<p>You will be redirected in 5 seconds</b>";
|
|
||||||
res.add_header("Refresh", "5; url=/?page=commands");
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPConnection::SendReply (HTTPRes& reply, std::string& content)
|
|
||||||
{
|
|
||||||
reply.add_header("Content-Type", "text/html");
|
|
||||||
reply.body = content;
|
|
||||||
|
|
||||||
m_SendBuffer = reply.to_string();
|
|
||||||
boost::asio::async_write (*m_Socket, boost::asio::buffer(m_SendBuffer),
|
|
||||||
std::bind (&HTTPConnection::Terminate, shared_from_this (), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
|
|
||||||
HTTPServer::HTTPServer (const std::string& address, int port):
|
|
||||||
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
|
|
||||||
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string(address), port))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
HTTPServer::~HTTPServer ()
|
|
||||||
{
|
|
||||||
Stop ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPServer::Start ()
|
|
||||||
{
|
|
||||||
bool needAuth; i2p::config::GetOption("http.auth", needAuth);
|
|
||||||
std::string user; i2p::config::GetOption("http.user", user);
|
|
||||||
std::string pass; i2p::config::GetOption("http.pass", pass);
|
|
||||||
/* generate pass if needed */
|
|
||||||
if (needAuth && pass == "") {
|
|
||||||
uint8_t random[16];
|
|
||||||
char alnum[] = "0123456789"
|
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
||||||
"abcdefghijklmnopqrstuvwxyz";
|
|
||||||
pass.resize(sizeof(random));
|
|
||||||
RAND_bytes(random, sizeof(random));
|
|
||||||
for (size_t i = 0; i < sizeof(random); i++) {
|
|
||||||
pass[i] = alnum[random[i] % (sizeof(alnum) - 1)];
|
|
||||||
}
|
|
||||||
i2p::config::SetOption("http.pass", pass);
|
|
||||||
LogPrint(eLogInfo, "HTTPServer: password set to ", pass);
|
|
||||||
}
|
|
||||||
m_IsRunning = true;
|
|
||||||
m_Thread = std::unique_ptr<std::thread>(new std::thread (std::bind (&HTTPServer::Run, this)));
|
|
||||||
m_Acceptor.listen ();
|
|
||||||
Accept ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPServer::Stop ()
|
|
||||||
{
|
|
||||||
m_IsRunning = false;
|
|
||||||
m_Acceptor.close();
|
|
||||||
m_Service.stop ();
|
|
||||||
if (m_Thread)
|
|
||||||
{
|
|
||||||
m_Thread->join ();
|
|
||||||
m_Thread = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPServer::Run ()
|
|
||||||
{
|
|
||||||
while (m_IsRunning)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_Service.run ();
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "HTTPServer: runtime exception: ", ex.what ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPServer::Accept ()
|
|
||||||
{
|
|
||||||
auto newSocket = std::make_shared<boost::asio::ip::tcp::socket> (m_Service);
|
|
||||||
m_Acceptor.async_accept (*newSocket, boost::bind (&HTTPServer::HandleAccept, this,
|
|
||||||
boost::asio::placeholders::error, newSocket));
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPServer::HandleAccept(const boost::system::error_code& ecode,
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> newSocket)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
if(newSocket) newSocket->close();
|
|
||||||
LogPrint(eLogError, "HTTP Server: error handling accept ", ecode.message());
|
|
||||||
if(ecode != boost::asio::error::operation_aborted)
|
|
||||||
Accept();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CreateConnection(newSocket);
|
|
||||||
Accept ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPServer::CreateConnection(std::shared_ptr<boost::asio::ip::tcp::socket> newSocket)
|
|
||||||
{
|
|
||||||
auto conn = std::make_shared<HTTPConnection> (newSocket);
|
|
||||||
conn->Receive ();
|
|
||||||
}
|
|
||||||
} // http
|
|
||||||
} // i2p
|
|
||||||
69
HTTPServer.h
69
HTTPServer.h
@@ -1,69 +0,0 @@
|
|||||||
#ifndef HTTP_SERVER_H__
|
|
||||||
#define HTTP_SERVER_H__
|
|
||||||
|
|
||||||
namespace i2p {
|
|
||||||
namespace http {
|
|
||||||
extern const char *itoopieFavicon;
|
|
||||||
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
|
|
||||||
|
|
||||||
class HTTPConnection: public std::enable_shared_from_this<HTTPConnection>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
HTTPConnection (std::shared_ptr<boost::asio::ip::tcp::socket> socket);
|
|
||||||
void Receive ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
|
||||||
void Terminate (const boost::system::error_code& ecode);
|
|
||||||
|
|
||||||
void RunRequest ();
|
|
||||||
bool CheckAuth (const HTTPReq & req);
|
|
||||||
void HandleRequest (const HTTPReq & req);
|
|
||||||
void HandlePage (const HTTPReq & req, HTTPRes & res, std::stringstream& data);
|
|
||||||
void HandleCommand (const HTTPReq & req, HTTPRes & res, std::stringstream& data);
|
|
||||||
void SendReply (HTTPRes & res, std::string & content);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
|
|
||||||
boost::asio::deadline_timer m_Timer;
|
|
||||||
char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1];
|
|
||||||
size_t m_BufferLen;
|
|
||||||
std::string m_SendBuffer;
|
|
||||||
bool needAuth;
|
|
||||||
std::string user;
|
|
||||||
std::string pass;
|
|
||||||
};
|
|
||||||
|
|
||||||
class HTTPServer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
HTTPServer (const std::string& address, int port);
|
|
||||||
~HTTPServer ();
|
|
||||||
|
|
||||||
void Start ();
|
|
||||||
void Stop ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void Run ();
|
|
||||||
void Accept ();
|
|
||||||
void HandleAccept(const boost::system::error_code& ecode,
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> newSocket);
|
|
||||||
void CreateConnection(std::shared_ptr<boost::asio::ip::tcp::socket> newSocket);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
bool m_IsRunning;
|
|
||||||
std::unique_ptr<std::thread> m_Thread;
|
|
||||||
boost::asio::io_service m_Service;
|
|
||||||
boost::asio::io_service::work m_Work;
|
|
||||||
boost::asio::ip::tcp::acceptor m_Acceptor;
|
|
||||||
};
|
|
||||||
} // http
|
|
||||||
} // i2p
|
|
||||||
|
|
||||||
#endif /* HTTP_SERVER_H__ */
|
|
||||||
744
I2CP.cpp
744
I2CP.cpp
@@ -1,744 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 s = GetSharedFromThis ();
|
|
||||||
auto remote = FindLeaseSet (ident);
|
|
||||||
if (remote)
|
|
||||||
{
|
|
||||||
GetService ().post (
|
|
||||||
[s, msg, remote, nonce]()
|
|
||||||
{
|
|
||||||
bool sent = s->SendMsg (msg, remote);
|
|
||||||
s->m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
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
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
m_Owner.InsertSession (shared_from_this ());
|
|
||||||
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);
|
|
||||||
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 ();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool I2CPServer::InsertSession (std::shared_ptr<I2CPSession> session)
|
|
||||||
{
|
|
||||||
if (!session) return false;
|
|
||||||
if (!m_Sessions.insert({session->GetSessionID (), session}).second)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "I2CP: duplicate session id ", session->GetSessionID ());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2CPServer::RemoveSession (uint16_t sessionID)
|
|
||||||
{
|
|
||||||
m_Sessions.erase (sessionID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
661
I2NPProtocol.cpp
661
I2NPProtocol.cpp
@@ -1,661 +0,0 @@
|
|||||||
#include <string.h>
|
|
||||||
#include <atomic>
|
|
||||||
#include "Base.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "Crypto.h"
|
|
||||||
#include "I2PEndian.h"
|
|
||||||
#include "Timestamp.h"
|
|
||||||
#include "RouterContext.h"
|
|
||||||
#include "NetDb.h"
|
|
||||||
#include "Tunnel.h"
|
|
||||||
#include "Transports.h"
|
|
||||||
#include "Garlic.h"
|
|
||||||
#include "I2NPProtocol.h"
|
|
||||||
#include "version.h"
|
|
||||||
|
|
||||||
using namespace i2p::transport;
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
std::shared_ptr<I2NPMessage> NewI2NPMessage ()
|
|
||||||
{
|
|
||||||
return std::make_shared<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> >();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> NewI2NPShortMessage ()
|
|
||||||
{
|
|
||||||
return std::make_shared<I2NPMessageBuffer<I2NP_MAX_SHORT_MESSAGE_SIZE> >();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> NewI2NPMessage (size_t len)
|
|
||||||
{
|
|
||||||
return (len < I2NP_MAX_SHORT_MESSAGE_SIZE/2) ? NewI2NPShortMessage () : NewI2NPMessage ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2NPMessage::FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID)
|
|
||||||
{
|
|
||||||
SetTypeID (msgType);
|
|
||||||
if (!replyMsgID) RAND_bytes ((uint8_t *)&replyMsgID, 4);
|
|
||||||
SetMsgID (replyMsgID);
|
|
||||||
SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + I2NP_MESSAGE_EXPIRATION_TIMEOUT);
|
|
||||||
UpdateSize ();
|
|
||||||
UpdateChks ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2NPMessage::RenewI2NPMessageHeader ()
|
|
||||||
{
|
|
||||||
uint32_t msgID;
|
|
||||||
RAND_bytes ((uint8_t *)&msgID, 4);
|
|
||||||
SetMsgID (msgID);
|
|
||||||
SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + I2NP_MESSAGE_EXPIRATION_TIMEOUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool I2NPMessage::IsExpired () const
|
|
||||||
{
|
|
||||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
auto exp = GetExpiration ();
|
|
||||||
return (ts > exp + I2NP_MESSAGE_CLOCK_SKEW) || (ts < exp - 3*I2NP_MESSAGE_CLOCK_SKEW); // check if expired or too far in future
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID)
|
|
||||||
{
|
|
||||||
auto msg = NewI2NPMessage (len);
|
|
||||||
if (msg->Concat (buf, len) < len)
|
|
||||||
LogPrint (eLogError, "I2NP: message length ", len, " exceeds max length ", msg->maxLen);
|
|
||||||
msg->FillI2NPMessageHeader (msgType, replyMsgID);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> CreateI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
|
|
||||||
{
|
|
||||||
auto msg = NewI2NPMessage ();
|
|
||||||
if (msg->offset + len < msg->maxLen)
|
|
||||||
{
|
|
||||||
memcpy (msg->GetBuffer (), buf, len);
|
|
||||||
msg->len = msg->offset + len;
|
|
||||||
msg->from = from;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "I2NP: message length ", len, " exceeds max length");
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> CopyI2NPMessage (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
if (!msg) return nullptr;
|
|
||||||
auto newMsg = NewI2NPMessage (msg->len);
|
|
||||||
newMsg->offset = msg->offset;
|
|
||||||
*newMsg = *msg;
|
|
||||||
return newMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg (uint32_t msgID)
|
|
||||||
{
|
|
||||||
auto m = NewI2NPShortMessage ();
|
|
||||||
uint8_t * buf = m->GetPayload ();
|
|
||||||
if (msgID)
|
|
||||||
{
|
|
||||||
htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID);
|
|
||||||
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, i2p::util::GetMillisecondsSinceEpoch ());
|
|
||||||
}
|
|
||||||
else // for SSU establishment
|
|
||||||
{
|
|
||||||
RAND_bytes ((uint8_t *)&msgID, 4);
|
|
||||||
htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID);
|
|
||||||
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, i2p::context.GetNetID ());
|
|
||||||
}
|
|
||||||
m->len += DELIVERY_STATUS_SIZE;
|
|
||||||
m->FillI2NPMessageHeader (eI2NPDeliveryStatus);
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
|
|
||||||
uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers)
|
|
||||||
{
|
|
||||||
auto m = excludedPeers ? NewI2NPMessage () : NewI2NPShortMessage ();
|
|
||||||
uint8_t * buf = m->GetPayload ();
|
|
||||||
memcpy (buf, key, 32); // key
|
|
||||||
buf += 32;
|
|
||||||
memcpy (buf, from, 32); // from
|
|
||||||
buf += 32;
|
|
||||||
uint8_t flag = exploratory ? DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP : DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP;
|
|
||||||
if (replyTunnelID)
|
|
||||||
{
|
|
||||||
*buf = flag | DATABASE_LOOKUP_DELIVERY_FLAG; // set delivery flag
|
|
||||||
htobe32buf (buf+1, replyTunnelID);
|
|
||||||
buf += 5;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*buf = flag; // flag
|
|
||||||
buf++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (excludedPeers)
|
|
||||||
{
|
|
||||||
int cnt = excludedPeers->size ();
|
|
||||||
htobe16buf (buf, cnt);
|
|
||||||
buf += 2;
|
|
||||||
for (auto& it: *excludedPeers)
|
|
||||||
{
|
|
||||||
memcpy (buf, it, 32);
|
|
||||||
buf += 32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// nothing to exclude
|
|
||||||
htobuf16 (buf, 0);
|
|
||||||
buf += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
m->len += (buf - m->GetPayload ());
|
|
||||||
m->FillI2NPMessageHeader (eI2NPDatabaseLookup);
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
|
|
||||||
const std::set<i2p::data::IdentHash>& excludedFloodfills,
|
|
||||||
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag)
|
|
||||||
{
|
|
||||||
int cnt = excludedFloodfills.size ();
|
|
||||||
auto m = cnt > 0 ? NewI2NPMessage () : NewI2NPShortMessage ();
|
|
||||||
uint8_t * buf = m->GetPayload ();
|
|
||||||
memcpy (buf, dest, 32); // key
|
|
||||||
buf += 32;
|
|
||||||
memcpy (buf, replyTunnel->GetNextIdentHash (), 32); // reply tunnel GW
|
|
||||||
buf += 32;
|
|
||||||
*buf = DATABASE_LOOKUP_DELIVERY_FLAG | DATABASE_LOOKUP_ENCRYPTION_FLAG | DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP; // flags
|
|
||||||
buf ++;
|
|
||||||
htobe32buf (buf, replyTunnel->GetNextTunnelID ()); // reply tunnel ID
|
|
||||||
buf += 4;
|
|
||||||
|
|
||||||
// excluded
|
|
||||||
htobe16buf (buf, cnt);
|
|
||||||
buf += 2;
|
|
||||||
if (cnt > 0)
|
|
||||||
{
|
|
||||||
for (auto& it: excludedFloodfills)
|
|
||||||
{
|
|
||||||
memcpy (buf, it, 32);
|
|
||||||
buf += 32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// encryption
|
|
||||||
memcpy (buf, replyKey, 32);
|
|
||||||
buf[32] = uint8_t( 1 ); // 1 tag
|
|
||||||
memcpy (buf + 33, replyTag, 32);
|
|
||||||
buf += 65;
|
|
||||||
|
|
||||||
m->len += (buf - m->GetPayload ());
|
|
||||||
m->FillI2NPMessageHeader (eI2NPDatabaseLookup);
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident,
|
|
||||||
std::vector<i2p::data::IdentHash> routers)
|
|
||||||
{
|
|
||||||
auto m = NewI2NPShortMessage ();
|
|
||||||
uint8_t * buf = m->GetPayload ();
|
|
||||||
size_t len = 0;
|
|
||||||
memcpy (buf, ident, 32);
|
|
||||||
len += 32;
|
|
||||||
buf[len] = routers.size ();
|
|
||||||
len++;
|
|
||||||
for (const auto& it: routers)
|
|
||||||
{
|
|
||||||
memcpy (buf + len, it, 32);
|
|
||||||
len += 32;
|
|
||||||
}
|
|
||||||
memcpy (buf + len, i2p::context.GetRouterInfo ().GetIdentHash (), 32);
|
|
||||||
len += 32;
|
|
||||||
m->len += len;
|
|
||||||
m->FillI2NPMessageHeader (eI2NPDatabaseSearchReply);
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router, uint32_t replyToken)
|
|
||||||
{
|
|
||||||
if (!router) // we send own RouterInfo
|
|
||||||
router = context.GetSharedRouterInfo ();
|
|
||||||
|
|
||||||
auto m = NewI2NPShortMessage ();
|
|
||||||
uint8_t * payload = m->GetPayload ();
|
|
||||||
|
|
||||||
memcpy (payload + DATABASE_STORE_KEY_OFFSET, router->GetIdentHash (), 32);
|
|
||||||
payload[DATABASE_STORE_TYPE_OFFSET] = 0; // RouterInfo
|
|
||||||
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken);
|
|
||||||
uint8_t * buf = payload + DATABASE_STORE_HEADER_SIZE;
|
|
||||||
if (replyToken)
|
|
||||||
{
|
|
||||||
memset (buf, 0, 4); // zero tunnelID means direct reply
|
|
||||||
buf += 4;
|
|
||||||
memcpy (buf, router->GetIdentHash (), 32);
|
|
||||||
buf += 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t * sizePtr = buf;
|
|
||||||
buf += 2;
|
|
||||||
m->len += (buf - payload); // payload size
|
|
||||||
i2p::data::GzipDeflator deflator;
|
|
||||||
size_t size = deflator.Deflate (router->GetBuffer (), router->GetBufferLen (), buf, m->maxLen -m->len);
|
|
||||||
if (size)
|
|
||||||
{
|
|
||||||
htobe16buf (sizePtr, size); // size
|
|
||||||
m->len += size;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m = nullptr;
|
|
||||||
if (m)
|
|
||||||
m->FillI2NPMessageHeader (eI2NPDatabaseStore);
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 ();
|
|
||||||
uint8_t * payload = m->GetPayload ();
|
|
||||||
memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetIdentHash (), 32);
|
|
||||||
payload[DATABASE_STORE_TYPE_OFFSET] = 1; // LeaseSet
|
|
||||||
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken);
|
|
||||||
size_t size = DATABASE_STORE_HEADER_SIZE;
|
|
||||||
if (replyToken && replyTunnel)
|
|
||||||
{
|
|
||||||
if (replyTunnel)
|
|
||||||
{
|
|
||||||
htobe32buf (payload + size, replyTunnel->GetNextTunnelID ());
|
|
||||||
size += 4; // reply tunnelID
|
|
||||||
memcpy (payload + size, replyTunnel->GetNextIdentHash (), 32);
|
|
||||||
size += 32; // reply tunnel gateway
|
|
||||||
}
|
|
||||||
else
|
|
||||||
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0);
|
|
||||||
}
|
|
||||||
memcpy (payload + size, leaseSet->GetBuffer (), leaseSet->GetBufferLen ());
|
|
||||||
size += leaseSet->GetBufferLen ();
|
|
||||||
m->len += size;
|
|
||||||
m->FillI2NPMessageHeader (eI2NPDatabaseStore);
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsRouterInfoMsg (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
if (!msg || msg->GetTypeID () != eI2NPDatabaseStore) return false;
|
|
||||||
return !msg->GetPayload ()[DATABASE_STORE_TYPE_OFFSET]; // 0- RouterInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint16_t g_MaxNumTransitTunnels = DEFAULT_MAX_NUM_TRANSIT_TUNNELS; // TODO:
|
|
||||||
void SetMaxNumTransitTunnels (uint16_t maxNumTransitTunnels)
|
|
||||||
{
|
|
||||||
if (maxNumTransitTunnels > 0 && maxNumTransitTunnels <= 10000 && g_MaxNumTransitTunnels != maxNumTransitTunnels)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "I2NP: Max number of transit tunnels set to ", maxNumTransitTunnels);
|
|
||||||
g_MaxNumTransitTunnels = maxNumTransitTunnels;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < num; i++)
|
|
||||||
{
|
|
||||||
uint8_t * record = records + i*TUNNEL_BUILD_RECORD_SIZE;
|
|
||||||
if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "I2NP: Build request record ", i, " is ours");
|
|
||||||
|
|
||||||
i2p::crypto::ElGamalDecrypt (i2p::context.GetEncryptionPrivateKey (), record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText);
|
|
||||||
// replace record to reply
|
|
||||||
if (i2p::context.AcceptsTunnels () &&
|
|
||||||
i2p::tunnel::tunnels.GetTransitTunnels ().size () <= g_MaxNumTransitTunnels &&
|
|
||||||
!i2p::transport::transports.IsBandwidthExceeded ())
|
|
||||||
{
|
|
||||||
auto transitTunnel = i2p::tunnel::CreateTransitTunnel (
|
|
||||||
bufbe32toh (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
|
|
||||||
clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
|
||||||
bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
|
|
||||||
clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET,
|
|
||||||
clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET,
|
|
||||||
clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x80,
|
|
||||||
clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET ] & 0x40);
|
|
||||||
i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel);
|
|
||||||
record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 30; // always reject with bandwidth reason (30)
|
|
||||||
|
|
||||||
//TODO: fill filler
|
|
||||||
SHA256 (record + BUILD_RESPONSE_RECORD_PADDING_OFFSET, BUILD_RESPONSE_RECORD_PADDING_SIZE + 1, // + 1 byte of ret
|
|
||||||
record + BUILD_RESPONSE_RECORD_HASH_OFFSET);
|
|
||||||
// encrypt reply
|
|
||||||
i2p::crypto::CBCEncryption encryption;
|
|
||||||
for (int j = 0; j < num; j++)
|
|
||||||
{
|
|
||||||
encryption.SetKey (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET);
|
|
||||||
encryption.SetIV (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET);
|
|
||||||
uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE;
|
|
||||||
encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
int num = buf[0];
|
|
||||||
LogPrint (eLogDebug, "I2NP: VariableTunnelBuild ", num, " records");
|
|
||||||
if (len < num*BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 1)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "VaribleTunnelBuild message of ", num, " records is too short ", len);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (replyMsgID);
|
|
||||||
if (tunnel)
|
|
||||||
{
|
|
||||||
// endpoint of inbound tunnel
|
|
||||||
LogPrint (eLogDebug, "I2NP: VariableTunnelBuild reply for tunnel ", tunnel->GetTunnelID ());
|
|
||||||
if (tunnel->HandleTunnelBuildResponse (buf, len))
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been created");
|
|
||||||
tunnel->SetState (i2p::tunnel::eTunnelStateEstablished);
|
|
||||||
i2p::tunnel::tunnels.AddInboundTunnel (tunnel);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been declined");
|
|
||||||
tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
|
|
||||||
if (HandleBuildRequestRecords (num, buf + 1, clearText))
|
|
||||||
{
|
|
||||||
if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outboud tunnel
|
|
||||||
{
|
|
||||||
// so we send it to reply tunnel
|
|
||||||
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
|
||||||
CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
|
|
||||||
eI2NPVariableTunnelBuildReply, buf, len,
|
|
||||||
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
|
||||||
CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len,
|
|
||||||
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleTunnelBuildMsg (uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
if (len < NUM_TUNNEL_BUILD_RECORDS*BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "TunnelBuild message is too short ", len);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
|
|
||||||
if (HandleBuildRequestRecords (NUM_TUNNEL_BUILD_RECORDS, buf, clearText))
|
|
||||||
{
|
|
||||||
if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outbound tunnel
|
|
||||||
{
|
|
||||||
// so we send it to reply tunnel
|
|
||||||
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
|
||||||
CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
|
|
||||||
eI2NPTunnelBuildReply, buf, len,
|
|
||||||
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
|
||||||
CreateI2NPMessage (eI2NPTunnelBuild, buf, len,
|
|
||||||
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
int num = buf[0];
|
|
||||||
LogPrint (eLogDebug, "I2NP: VariableTunnelBuildReplyMsg of ", num, " records replyMsgID=", replyMsgID);
|
|
||||||
if (len < num*BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 1)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "VaribleTunnelBuildReply message of ", num, " records is too short ", len);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto tunnel = i2p::tunnel::tunnels.GetPendingOutboundTunnel (replyMsgID);
|
|
||||||
if (tunnel)
|
|
||||||
{
|
|
||||||
// reply for outbound tunnel
|
|
||||||
if (tunnel->HandleTunnelBuildResponse (buf, len))
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "I2NP: Outbound tunnel ", tunnel->GetTunnelID (), " has been created");
|
|
||||||
tunnel->SetState (i2p::tunnel::eTunnelStateEstablished);
|
|
||||||
i2p::tunnel::tunnels.AddOutboundTunnel (tunnel);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "I2NP: Outbound tunnel ", tunnel->GetTunnelID (), " has been declined");
|
|
||||||
tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "I2NP: Pending tunnel for message ", replyMsgID, " not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf)
|
|
||||||
{
|
|
||||||
auto msg = NewI2NPShortMessage ();
|
|
||||||
msg->Concat (buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE);
|
|
||||||
msg->FillI2NPMessageHeader (eI2NPTunnelData);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload)
|
|
||||||
{
|
|
||||||
auto msg = NewI2NPShortMessage ();
|
|
||||||
htobe32buf (msg->GetPayload (), tunnelID);
|
|
||||||
msg->len += 4; // tunnelID
|
|
||||||
msg->Concat (payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4);
|
|
||||||
msg->FillI2NPMessageHeader (eI2NPTunnelData);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg ()
|
|
||||||
{
|
|
||||||
auto msg = NewI2NPShortMessage ();
|
|
||||||
msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE;
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
auto msg = NewI2NPMessage (len);
|
|
||||||
uint8_t * payload = msg->GetPayload ();
|
|
||||||
htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
|
|
||||||
htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
|
|
||||||
msg->len += TUNNEL_GATEWAY_HEADER_SIZE;
|
|
||||||
if (msg->Concat (buf, len) < len)
|
|
||||||
LogPrint (eLogError, "I2NP: tunnel gateway buffer overflow ", msg->maxLen);
|
|
||||||
msg->FillI2NPMessageHeader (eI2NPTunnelGateway);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
if (msg->offset >= I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE)
|
|
||||||
{
|
|
||||||
// message is capable to be used without copying
|
|
||||||
uint8_t * payload = msg->GetBuffer () - TUNNEL_GATEWAY_HEADER_SIZE;
|
|
||||||
htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
|
|
||||||
int len = msg->GetLength ();
|
|
||||||
htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
|
|
||||||
msg->offset -= (I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE);
|
|
||||||
msg->len = msg->offset + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE +len;
|
|
||||||
msg->FillI2NPMessageHeader (eI2NPTunnelGateway);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return CreateTunnelGatewayMsg (tunnelID, msg->GetBuffer (), msg->GetLength ());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType,
|
|
||||||
const uint8_t * buf, size_t len, uint32_t replyMsgID)
|
|
||||||
{
|
|
||||||
auto msg = NewI2NPMessage (len);
|
|
||||||
size_t gatewayMsgOffset = I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE;
|
|
||||||
msg->offset += gatewayMsgOffset;
|
|
||||||
msg->len += gatewayMsgOffset;
|
|
||||||
if (msg->Concat (buf, len) < len)
|
|
||||||
LogPrint (eLogError, "I2NP: tunnel gateway buffer overflow ", msg->maxLen);
|
|
||||||
msg->FillI2NPMessageHeader (msgType, replyMsgID); // create content message
|
|
||||||
len = msg->GetLength ();
|
|
||||||
msg->offset -= gatewayMsgOffset;
|
|
||||||
uint8_t * payload = msg->GetPayload ();
|
|
||||||
htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
|
|
||||||
htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
|
|
||||||
msg->FillI2NPMessageHeader (eI2NPTunnelGateway); // gateway message
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GetI2NPMessageLength (const uint8_t * msg)
|
|
||||||
{
|
|
||||||
return bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET) + I2NP_HEADER_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleI2NPMessage (uint8_t * msg, size_t len)
|
|
||||||
{
|
|
||||||
uint8_t typeID = msg[I2NP_HEADER_TYPEID_OFFSET];
|
|
||||||
uint32_t msgID = bufbe32toh (msg + I2NP_HEADER_MSGID_OFFSET);
|
|
||||||
LogPrint (eLogDebug, "I2NP: msg received len=", len,", type=", (int)typeID, ", msgID=", (unsigned int)msgID);
|
|
||||||
uint8_t * buf = msg + I2NP_HEADER_SIZE;
|
|
||||||
int size = bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET);
|
|
||||||
switch (typeID)
|
|
||||||
{
|
|
||||||
case eI2NPVariableTunnelBuild:
|
|
||||||
HandleVariableTunnelBuildMsg (msgID, buf, size);
|
|
||||||
break;
|
|
||||||
case eI2NPVariableTunnelBuildReply:
|
|
||||||
HandleVariableTunnelBuildReplyMsg (msgID, buf, size);
|
|
||||||
break;
|
|
||||||
case eI2NPTunnelBuild:
|
|
||||||
HandleTunnelBuildMsg (buf, size);
|
|
||||||
break;
|
|
||||||
case eI2NPTunnelBuildReply:
|
|
||||||
// TODO:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LogPrint (eLogWarning, "I2NP: Unexpected message ", (int)typeID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
if (msg)
|
|
||||||
{
|
|
||||||
uint8_t typeID = msg->GetTypeID ();
|
|
||||||
LogPrint (eLogDebug, "I2NP: Handling message with type ", (int)typeID);
|
|
||||||
switch (typeID)
|
|
||||||
{
|
|
||||||
case eI2NPTunnelData:
|
|
||||||
i2p::tunnel::tunnels.PostTunnelData (msg);
|
|
||||||
break;
|
|
||||||
case eI2NPTunnelGateway:
|
|
||||||
i2p::tunnel::tunnels.PostTunnelData (msg);
|
|
||||||
break;
|
|
||||||
case eI2NPGarlic:
|
|
||||||
{
|
|
||||||
if (msg->from)
|
|
||||||
{
|
|
||||||
if (msg->from->GetTunnelPool ())
|
|
||||||
msg->from->GetTunnelPool ()->ProcessGarlicMessage (msg);
|
|
||||||
else
|
|
||||||
LogPrint (eLogInfo, "I2NP: Local destination for garlic doesn't exist anymore");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
i2p::context.ProcessGarlicMessage (msg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case eI2NPDatabaseStore:
|
|
||||||
case eI2NPDatabaseSearchReply:
|
|
||||||
case eI2NPDatabaseLookup:
|
|
||||||
// forward to netDb
|
|
||||||
i2p::data::netdb.PostI2NPMsg (msg);
|
|
||||||
break;
|
|
||||||
case eI2NPDeliveryStatus:
|
|
||||||
{
|
|
||||||
if (msg->from && msg->from->GetTunnelPool ())
|
|
||||||
msg->from->GetTunnelPool ()->ProcessDeliveryStatus (msg);
|
|
||||||
else
|
|
||||||
i2p::context.ProcessDeliveryStatusMessage (msg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case eI2NPVariableTunnelBuild:
|
|
||||||
case eI2NPVariableTunnelBuildReply:
|
|
||||||
case eI2NPTunnelBuild:
|
|
||||||
case eI2NPTunnelBuildReply:
|
|
||||||
// forward to tunnel thread
|
|
||||||
i2p::tunnel::tunnels.PostTunnelData (msg);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
I2NPMessagesHandler::~I2NPMessagesHandler ()
|
|
||||||
{
|
|
||||||
Flush ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2NPMessagesHandler::PutNextMessage (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
if (msg)
|
|
||||||
{
|
|
||||||
switch (msg->GetTypeID ())
|
|
||||||
{
|
|
||||||
case eI2NPTunnelData:
|
|
||||||
m_TunnelMsgs.push_back (msg);
|
|
||||||
break;
|
|
||||||
case eI2NPTunnelGateway:
|
|
||||||
m_TunnelGatewayMsgs.push_back (msg);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
HandleI2NPMessage (msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2NPMessagesHandler::Flush ()
|
|
||||||
{
|
|
||||||
if (!m_TunnelMsgs.empty ())
|
|
||||||
{
|
|
||||||
i2p::tunnel::tunnels.PostTunnelData (m_TunnelMsgs);
|
|
||||||
m_TunnelMsgs.clear ();
|
|
||||||
}
|
|
||||||
if (!m_TunnelGatewayMsgs.empty ())
|
|
||||||
{
|
|
||||||
i2p::tunnel::tunnels.PostTunnelData (m_TunnelGatewayMsgs);
|
|
||||||
m_TunnelGatewayMsgs.clear ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
563
I2PControl.cpp
563
I2PControl.cpp
@@ -1,563 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <sstream>
|
|
||||||
#include <openssl/x509.h>
|
|
||||||
#include <openssl/pem.h>
|
|
||||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
|
||||||
#include <boost/property_tree/ini_parser.hpp>
|
|
||||||
|
|
||||||
// There is bug in boost 1.49 with gcc 4.7 coming with Debian Wheezy
|
|
||||||
#define GCC47_BOOST149 ((BOOST_VERSION == 104900) && (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7))
|
|
||||||
#if !GCC47_BOOST149
|
|
||||||
#include <boost/property_tree/json_parser.hpp>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "Crypto.h"
|
|
||||||
#include "FS.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "HTTP.h"
|
|
||||||
#include "Config.h"
|
|
||||||
#include "NetDb.h"
|
|
||||||
#include "RouterContext.h"
|
|
||||||
#include "Daemon.h"
|
|
||||||
#include "Tunnel.h"
|
|
||||||
#include "Timestamp.h"
|
|
||||||
#include "Transports.h"
|
|
||||||
#include "version.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include "I2PControl.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
I2PControlService::I2PControlService (const std::string& address, int port):
|
|
||||||
m_IsRunning (false), m_Thread (nullptr),
|
|
||||||
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)),
|
|
||||||
m_SSLContext (m_Service, boost::asio::ssl::context::sslv23),
|
|
||||||
m_ShutdownTimer (m_Service)
|
|
||||||
{
|
|
||||||
i2p::config::GetOption("i2pcontrol.password", m_Password);
|
|
||||||
|
|
||||||
// certificate / keys
|
|
||||||
std::string i2pcp_crt; i2p::config::GetOption("i2pcontrol.cert", i2pcp_crt);
|
|
||||||
std::string i2pcp_key; i2p::config::GetOption("i2pcontrol.key", i2pcp_key);
|
|
||||||
|
|
||||||
if (i2pcp_crt.at(0) != '/')
|
|
||||||
i2pcp_crt = i2p::fs::DataDirPath(i2pcp_crt);
|
|
||||||
if (i2pcp_key.at(0) != '/')
|
|
||||||
i2pcp_key = i2p::fs::DataDirPath(i2pcp_key);
|
|
||||||
if (!i2p::fs::Exists (i2pcp_crt) || !i2p::fs::Exists (i2pcp_key)) {
|
|
||||||
LogPrint (eLogInfo, "I2PControl: creating new certificate for control connection");
|
|
||||||
CreateCertificate (i2pcp_crt.c_str(), i2pcp_key.c_str());
|
|
||||||
} else {
|
|
||||||
LogPrint(eLogDebug, "I2PControl: using cert from ", i2pcp_crt);
|
|
||||||
}
|
|
||||||
m_SSLContext.set_options (boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use);
|
|
||||||
m_SSLContext.use_certificate_file (i2pcp_crt, boost::asio::ssl::context::pem);
|
|
||||||
m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem);
|
|
||||||
|
|
||||||
// handlers
|
|
||||||
m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler;
|
|
||||||
m_MethodHandlers["Echo"] = &I2PControlService::EchoHandler;
|
|
||||||
m_MethodHandlers["I2PControl"] = &I2PControlService::I2PControlHandler;
|
|
||||||
m_MethodHandlers["RouterInfo"] = &I2PControlService::RouterInfoHandler;
|
|
||||||
m_MethodHandlers["RouterManager"] = &I2PControlService::RouterManagerHandler;
|
|
||||||
m_MethodHandlers["NetworkSetting"] = &I2PControlService::NetworkSettingHandler;
|
|
||||||
|
|
||||||
// I2PControl
|
|
||||||
m_I2PControlHandlers["i2pcontrol.password"] = &I2PControlService::PasswordHandler;
|
|
||||||
|
|
||||||
// RouterInfo
|
|
||||||
m_RouterInfoHandlers["i2p.router.uptime"] = &I2PControlService::UptimeHandler;
|
|
||||||
m_RouterInfoHandlers["i2p.router.version"] = &I2PControlService::VersionHandler;
|
|
||||||
m_RouterInfoHandlers["i2p.router.status"] = &I2PControlService::StatusHandler;
|
|
||||||
m_RouterInfoHandlers["i2p.router.netdb.knownpeers"] = &I2PControlService::NetDbKnownPeersHandler;
|
|
||||||
m_RouterInfoHandlers["i2p.router.netdb.activepeers"] = &I2PControlService::NetDbActivePeersHandler;
|
|
||||||
m_RouterInfoHandlers["i2p.router.net.bw.inbound.1s"] = &I2PControlService::InboundBandwidth1S;
|
|
||||||
m_RouterInfoHandlers["i2p.router.net.bw.outbound.1s"] = &I2PControlService::OutboundBandwidth1S;
|
|
||||||
m_RouterInfoHandlers["i2p.router.net.status"] = &I2PControlService::NetStatusHandler;
|
|
||||||
m_RouterInfoHandlers["i2p.router.net.tunnels.participating"] = &I2PControlService::TunnelsParticipatingHandler;
|
|
||||||
m_RouterInfoHandlers["i2p.router.net.total.received.bytes"] = &I2PControlService::NetTotalReceivedBytes;
|
|
||||||
m_RouterInfoHandlers["i2p.router.net.total.sent.bytes"] = &I2PControlService::NetTotalSentBytes;
|
|
||||||
|
|
||||||
// RouterManager
|
|
||||||
m_RouterManagerHandlers["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 ()
|
|
||||||
{
|
|
||||||
Stop ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::Start ()
|
|
||||||
{
|
|
||||||
if (!m_IsRunning)
|
|
||||||
{
|
|
||||||
Accept ();
|
|
||||||
m_IsRunning = true;
|
|
||||||
m_Thread = new std::thread (std::bind (&I2PControlService::Run, this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::Stop ()
|
|
||||||
{
|
|
||||||
if (m_IsRunning)
|
|
||||||
{
|
|
||||||
m_IsRunning = false;
|
|
||||||
m_Acceptor.cancel ();
|
|
||||||
m_Service.stop ();
|
|
||||||
if (m_Thread)
|
|
||||||
{
|
|
||||||
m_Thread->join ();
|
|
||||||
delete m_Thread;
|
|
||||||
m_Thread = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::Run ()
|
|
||||||
{
|
|
||||||
while (m_IsRunning)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
m_Service.run ();
|
|
||||||
} catch (std::exception& ex) {
|
|
||||||
LogPrint (eLogError, "I2PControl: runtime exception: ", ex.what ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::Accept ()
|
|
||||||
{
|
|
||||||
auto newSocket = std::make_shared<ssl_socket> (m_Service, m_SSLContext);
|
|
||||||
m_Acceptor.async_accept (newSocket->lowest_layer(), std::bind (&I2PControlService::HandleAccept, this,
|
|
||||||
std::placeholders::_1, newSocket));
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Accept ();
|
|
||||||
|
|
||||||
if (ecode) {
|
|
||||||
LogPrint (eLogError, "I2PControl: accept error: ", ecode.message ());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
LogPrint (eLogDebug, "I2PControl: new request from ", socket->lowest_layer ().remote_endpoint ());
|
|
||||||
Handshake (socket);
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::Handshake (std::shared_ptr<ssl_socket> socket)
|
|
||||||
{
|
|
||||||
socket->async_handshake(boost::asio::ssl::stream_base::server,
|
|
||||||
std::bind( &I2PControlService::HandleHandshake, this, std::placeholders::_1, socket));
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::HandleHandshake (const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket)
|
|
||||||
{
|
|
||||||
if (ecode) {
|
|
||||||
LogPrint (eLogError, "I2PControl: handshake error: ", ecode.message ());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//std::this_thread::sleep_for (std::chrono::milliseconds(5));
|
|
||||||
ReadRequest (socket);
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::ReadRequest (std::shared_ptr<ssl_socket> socket)
|
|
||||||
{
|
|
||||||
auto request = std::make_shared<I2PControlBuffer>();
|
|
||||||
socket->async_read_some (
|
|
||||||
#if defined(BOOST_ASIO_HAS_STD_ARRAY)
|
|
||||||
boost::asio::buffer (*request),
|
|
||||||
#else
|
|
||||||
boost::asio::buffer (request->data (), request->size ()),
|
|
||||||
#endif
|
|
||||||
std::bind(&I2PControlService::HandleRequestReceived, this,
|
|
||||||
std::placeholders::_1, std::placeholders::_2, socket, request));
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::HandleRequestReceived (const boost::system::error_code& ecode,
|
|
||||||
size_t bytes_transferred, std::shared_ptr<ssl_socket> socket,
|
|
||||||
std::shared_ptr<I2PControlBuffer> buf)
|
|
||||||
{
|
|
||||||
if (ecode) {
|
|
||||||
LogPrint (eLogError, "I2PControl: read error: ", ecode.message ());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* try to parse received data */
|
|
||||||
std::stringstream json;
|
|
||||||
std::string response;
|
|
||||||
bool isHTTP = false;
|
|
||||||
if (memcmp (buf->data (), "POST", 4) == 0) {
|
|
||||||
long int remains = 0;
|
|
||||||
isHTTP = true;
|
|
||||||
i2p::http::HTTPReq req;
|
|
||||||
std::size_t len = req.parse(buf->data(), bytes_transferred);
|
|
||||||
if (len <= 0) {
|
|
||||||
LogPrint(eLogError, "I2PControl: incomplete/malformed POST request");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* append to json chunk of data from 1st request */
|
|
||||||
json.write(buf->data() + len, bytes_transferred - len);
|
|
||||||
remains = req.content_length() - len;
|
|
||||||
/* if request has Content-Length header, fetch rest of data and store to json buffer */
|
|
||||||
while (remains > 0) {
|
|
||||||
len = ((long int) buf->size() < remains) ? buf->size() : remains;
|
|
||||||
bytes_transferred = boost::asio::read (*socket, boost::asio::buffer (buf->data (), len));
|
|
||||||
json.write(buf->data(), bytes_transferred);
|
|
||||||
remains -= bytes_transferred;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
json.write(buf->data(), bytes_transferred);
|
|
||||||
}
|
|
||||||
LogPrint(eLogDebug, "I2PControl: json from request: ", json.str());
|
|
||||||
#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
|
|
||||||
{
|
|
||||||
ss << "\"" << name << "\":" << value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value) const
|
|
||||||
{
|
|
||||||
ss << "\"" << name << "\":";
|
|
||||||
if (value.length () > 0)
|
|
||||||
ss << "\"" << value << "\"";
|
|
||||||
else
|
|
||||||
ss << "null";
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, double value) const
|
|
||||||
{
|
|
||||||
ss << "\"" << name << "\":" << std::fixed << std::setprecision(2) << value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::BuildErrorResponse (std::string & content, int code, const char *message) {
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "{\"id\":null,\"error\":";
|
|
||||||
ss << "{\"code\":" << -code << ",\"message\":\"" << message << "\"},";
|
|
||||||
ss << "\"jsonrpc\":\"2.0\"}";
|
|
||||||
content = ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::SendResponse (std::shared_ptr<ssl_socket> socket,
|
|
||||||
std::shared_ptr<I2PControlBuffer> buf, std::string& content, bool isHTTP)
|
|
||||||
{
|
|
||||||
if (isHTTP) {
|
|
||||||
i2p::http::HTTPRes res;
|
|
||||||
res.code = 200;
|
|
||||||
res.add_header("Content-Type", "application/json");
|
|
||||||
res.add_header("Connection", "close");
|
|
||||||
res.body = content;
|
|
||||||
std::string tmp = res.to_string();
|
|
||||||
content = tmp;
|
|
||||||
}
|
|
||||||
std::copy(content.begin(), content.end(), buf->begin());
|
|
||||||
boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), content.length()),
|
|
||||||
boost::asio::transfer_all (),
|
|
||||||
std::bind(&I2PControlService::HandleResponseSent, this,
|
|
||||||
std::placeholders::_1, std::placeholders::_2, socket, buf));
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred,
|
|
||||||
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf)
|
|
||||||
{
|
|
||||||
if (ecode) {
|
|
||||||
LogPrint (eLogError, "I2PControl: write error: ", ecode.message ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// handlers
|
|
||||||
|
|
||||||
void I2PControlService::AuthenticateHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
|
||||||
{
|
|
||||||
int api = params.get<int> ("API");
|
|
||||||
auto password = params.get<std::string> ("Password");
|
|
||||||
LogPrint (eLogDebug, "I2PControl: Authenticate API=", api, " Password=", password);
|
|
||||||
if (password != m_Password) {
|
|
||||||
LogPrint (eLogError, "I2PControl: Authenticate - Invalid password: ", password);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
InsertParam (results, "API", api);
|
|
||||||
results << ",";
|
|
||||||
std::string token = std::to_string(i2p::util::GetSecondsSinceEpoch ());
|
|
||||||
m_Tokens.insert (token);
|
|
||||||
InsertParam (results, "Token", token);
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::EchoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
|
||||||
{
|
|
||||||
auto echo = params.get<std::string> ("Echo");
|
|
||||||
LogPrint (eLogDebug, "I2PControl Echo Echo=", echo);
|
|
||||||
InsertParam (results, "Result", echo);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// I2PControl
|
|
||||||
|
|
||||||
void I2PControlService::I2PControlHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
|
||||||
{
|
|
||||||
for (auto& it: params)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "I2PControl: I2PControl request: ", it.first);
|
|
||||||
auto it1 = m_I2PControlHandlers.find (it.first);
|
|
||||||
if (it1 != m_I2PControlHandlers.end ())
|
|
||||||
{
|
|
||||||
(this->*(it1->second))(it.second.data ());
|
|
||||||
InsertParam (results, it.first, "");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "I2PControl: I2PControl unknown request: ", it.first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::PasswordHandler (const std::string& value)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "I2PControl: new password=", value, ", to make it persistent you should update your config!");
|
|
||||||
m_Password = value;
|
|
||||||
m_Tokens.clear ();
|
|
||||||
}
|
|
||||||
|
|
||||||
// RouterInfo
|
|
||||||
|
|
||||||
void I2PControlService::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
|
||||||
{
|
|
||||||
for (auto it = params.begin (); it != params.end (); ++it)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "I2PControl: RouterInfo request: ", it->first);
|
|
||||||
auto it1 = m_RouterInfoHandlers.find (it->first);
|
|
||||||
if (it1 != m_RouterInfoHandlers.end ())
|
|
||||||
{
|
|
||||||
if (it != params.begin ()) results << ",";
|
|
||||||
(this->*(it1->second))(results);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "I2PControl: RouterInfo unknown request ", it->first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::UptimeHandler (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
InsertParam (results, "i2p.router.uptime", (int)i2p::context.GetUptime ()*1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::VersionHandler (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
InsertParam (results, "i2p.router.version", VERSION);
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::StatusHandler (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
InsertParam (results, "i2p.router.status", "???"); // TODO:
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::NetDbKnownPeersHandler (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
InsertParam (results, "i2p.router.netdb.knownpeers", i2p::data::netdb.GetNumRouters ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::NetDbActivePeersHandler (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
InsertParam (results, "i2p.router.netdb.activepeers", (int)i2p::transport::transports.GetPeers ().size ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::NetStatusHandler (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
InsertParam (results, "i2p.router.net.status", (int)i2p::context.GetStatus ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::TunnelsParticipatingHandler (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
int transit = i2p::tunnel::tunnels.GetTransitTunnels ().size ();
|
|
||||||
InsertParam (results, "i2p.router.net.tunnels.participating", transit);
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::InboundBandwidth1S (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
double bw = i2p::transport::transports.GetInBandwidth ();
|
|
||||||
InsertParam (results, "i2p.router.net.bw.inbound.1s", bw);
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::OutboundBandwidth1S (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
double bw = i2p::transport::transports.GetOutBandwidth ();
|
|
||||||
InsertParam (results, "i2p.router.net.bw.outbound.1s", bw);
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::NetTotalReceivedBytes (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
InsertParam (results, "i2p.router.net.total.received.bytes", (double)i2p::transport::transports.GetTotalReceivedBytes ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::NetTotalSentBytes (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
InsertParam (results, "i2p.router.net.total.sent.bytes", (double)i2p::transport::transports.GetTotalSentBytes ());
|
|
||||||
}
|
|
||||||
|
|
||||||
// RouterManager
|
|
||||||
|
|
||||||
void I2PControlService::RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
|
||||||
{
|
|
||||||
for (auto it = params.begin (); it != params.end (); ++it)
|
|
||||||
{
|
|
||||||
if (it != params.begin ()) results << ",";
|
|
||||||
LogPrint (eLogDebug, "I2PControl: RouterManager request: ", it->first);
|
|
||||||
auto it1 = m_RouterManagerHandlers.find (it->first);
|
|
||||||
if (it1 != m_RouterManagerHandlers.end ()) {
|
|
||||||
(this->*(it1->second))(results);
|
|
||||||
} else
|
|
||||||
LogPrint (eLogError, "I2PControl: RouterManager unknown request: ", it->first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void I2PControlService::ShutdownHandler (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "I2PControl: Shutdown requested");
|
|
||||||
InsertParam (results, "Shutdown", "");
|
|
||||||
m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(1)); // 1 second to make sure response has been sent
|
|
||||||
m_ShutdownTimer.async_wait (
|
|
||||||
[](const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
Daemon.running = 0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::ShutdownGracefulHandler (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
i2p::context.SetAcceptsTunnels (false);
|
|
||||||
int timeout = i2p::tunnel::tunnels.GetTransitTunnelsExpirationTimeout ();
|
|
||||||
LogPrint (eLogInfo, "I2PControl: Graceful shutdown requested, ", timeout, " seconds remains");
|
|
||||||
InsertParam (results, "ShutdownGraceful", "");
|
|
||||||
m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(timeout + 1)); // + 1 second
|
|
||||||
m_ShutdownTimer.async_wait (
|
|
||||||
[](const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
Daemon.running = 0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::ReseedHandler (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "I2PControl: Reseed requested");
|
|
||||||
InsertParam (results, "Reseed", "");
|
|
||||||
i2p::data::netdb.Reseed ();
|
|
||||||
}
|
|
||||||
|
|
||||||
// network setting
|
|
||||||
void I2PControlService::NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
|
||||||
{
|
|
||||||
for (auto it = params.begin (); it != params.end (); ++it)
|
|
||||||
{
|
|
||||||
if (it != params.begin ()) results << ",";
|
|
||||||
LogPrint (eLogDebug, "I2PControl: NetworkSetting request: ", it->first);
|
|
||||||
auto it1 = m_NetworkSettingHandlers.find (it->first);
|
|
||||||
if (it1 != m_NetworkSettingHandlers.end ()) {
|
|
||||||
(this->*(it1->second))(it->second.data (), results);
|
|
||||||
} else
|
|
||||||
LogPrint (eLogError, "I2PControl: NetworkSetting unknown request: ", it->first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::InboundBandwidthLimit (const std::string& value, std::ostringstream& results)
|
|
||||||
{
|
|
||||||
if (value != "null")
|
|
||||||
i2p::context.SetBandwidth (std::atoi(value.c_str()));
|
|
||||||
int bw = i2p::context.GetBandwidthLimit();
|
|
||||||
InsertParam (results, "i2p.router.net.bw.in", bw);
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::OutboundBandwidthLimit (const std::string& value, std::ostringstream& results)
|
|
||||||
{
|
|
||||||
if (value != "null")
|
|
||||||
i2p::context.SetBandwidth (std::atoi(value.c_str()));
|
|
||||||
int bw = i2p::context.GetBandwidthLimit();
|
|
||||||
InsertParam (results, "i2p.router.net.bw.out", bw);
|
|
||||||
}
|
|
||||||
|
|
||||||
// certificate
|
|
||||||
void I2PControlService::CreateCertificate (const char *crt_path, const char *key_path)
|
|
||||||
{
|
|
||||||
FILE *f = NULL;
|
|
||||||
EVP_PKEY * pkey = EVP_PKEY_new ();
|
|
||||||
RSA * rsa = RSA_new ();
|
|
||||||
BIGNUM * e = BN_dup (i2p::crypto::GetRSAE ());
|
|
||||||
RSA_generate_key_ex (rsa, 4096, e, NULL);
|
|
||||||
BN_free (e);
|
|
||||||
if (rsa)
|
|
||||||
{
|
|
||||||
EVP_PKEY_assign_RSA (pkey, rsa);
|
|
||||||
X509 * x509 = X509_new ();
|
|
||||||
ASN1_INTEGER_set (X509_get_serialNumber (x509), 1);
|
|
||||||
X509_gmtime_adj (X509_get_notBefore (x509), 0);
|
|
||||||
X509_gmtime_adj (X509_get_notAfter (x509), I2P_CONTROL_CERTIFICATE_VALIDITY*24*60*60); // expiration
|
|
||||||
X509_set_pubkey (x509, pkey); // public key
|
|
||||||
X509_NAME * name = X509_get_subject_name (x509);
|
|
||||||
X509_NAME_add_entry_by_txt (name, "C", MBSTRING_ASC, (unsigned char *)"RU", -1, -1, 0); // country (Russia by default)
|
|
||||||
X509_NAME_add_entry_by_txt (name, "O", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_ORGANIZATION, -1, -1, 0); // organization
|
|
||||||
X509_NAME_add_entry_by_txt (name, "CN", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_COMMON_NAME, -1, -1, 0); // common name
|
|
||||||
X509_set_issuer_name (x509, name); // set issuer to ourselves
|
|
||||||
X509_sign (x509, pkey, EVP_sha1 ()); // sign
|
|
||||||
|
|
||||||
// save cert
|
|
||||||
if ((f = fopen (crt_path, "wb")) != NULL) {
|
|
||||||
LogPrint (eLogInfo, "I2PControl: saving new cert to ", crt_path);
|
|
||||||
PEM_write_X509 (f, x509);
|
|
||||||
fclose (f);
|
|
||||||
} else {
|
|
||||||
LogPrint (eLogError, "I2PControl: can't write cert: ", strerror(errno));
|
|
||||||
}
|
|
||||||
|
|
||||||
// save key
|
|
||||||
if ((f = fopen (key_path, "wb")) != NULL) {
|
|
||||||
LogPrint (eLogInfo, "I2PControl: saving cert key to ", key_path);
|
|
||||||
PEM_write_PrivateKey (f, pkey, NULL, NULL, 0, NULL, NULL);
|
|
||||||
fclose (f);
|
|
||||||
} else {
|
|
||||||
LogPrint (eLogError, "I2PControl: can't write key: ", strerror(errno));
|
|
||||||
}
|
|
||||||
|
|
||||||
X509_free (x509);
|
|
||||||
} else {
|
|
||||||
LogPrint (eLogError, "I2PControl: can't create RSA key for certificate");
|
|
||||||
}
|
|
||||||
EVP_PKEY_free (pkey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
214
I2PService.cpp
214
I2PService.cpp
@@ -1,214 +0,0 @@
|
|||||||
#include "Destination.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "ClientContext.h"
|
|
||||||
#include "I2PService.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
static const i2p::data::SigningKeyType I2P_SERVICE_DEFAULT_KEY_TYPE = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256;
|
|
||||||
|
|
||||||
I2PService::I2PService (std::shared_ptr<ClientDestination> localDestination):
|
|
||||||
m_LocalDestination (localDestination ? localDestination :
|
|
||||||
i2p::client::context.CreateNewLocalDestination (false, I2P_SERVICE_DEFAULT_KEY_TYPE))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
I2PService::I2PService (i2p::data::SigningKeyType kt):
|
|
||||||
m_LocalDestination (i2p::client::context.CreateNewLocalDestination (false, kt))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PService::CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port) {
|
|
||||||
assert(streamRequestComplete);
|
|
||||||
i2p::data::IdentHash identHash;
|
|
||||||
if (i2p::client::context.GetAddressBook ().GetIdentHash (dest, identHash))
|
|
||||||
m_LocalDestination->CreateStream (streamRequestComplete, identHash, port);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "I2PService: Remote destination not found: ", dest);
|
|
||||||
streamRequestComplete (nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TCPIPPipe::TCPIPPipe(I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> upstream, std::shared_ptr<boost::asio::ip::tcp::socket> downstream) : I2PServiceHandler(owner), m_up(upstream), m_down(downstream)
|
|
||||||
{
|
|
||||||
boost::asio::socket_base::receive_buffer_size option(TCP_IP_PIPE_BUFFER_SIZE);
|
|
||||||
upstream->set_option(option);
|
|
||||||
downstream->set_option(option);
|
|
||||||
}
|
|
||||||
|
|
||||||
TCPIPPipe::~TCPIPPipe()
|
|
||||||
{
|
|
||||||
Terminate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TCPIPPipe::Start()
|
|
||||||
{
|
|
||||||
AsyncReceiveUpstream();
|
|
||||||
AsyncReceiveDownstream();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TCPIPPipe::Terminate()
|
|
||||||
{
|
|
||||||
if(Kill()) return;
|
|
||||||
Done(shared_from_this());
|
|
||||||
if (m_up) {
|
|
||||||
if (m_up->is_open()) {
|
|
||||||
m_up->close();
|
|
||||||
}
|
|
||||||
m_up = nullptr;
|
|
||||||
}
|
|
||||||
if (m_down) {
|
|
||||||
if (m_down->is_open()) {
|
|
||||||
m_down->close();
|
|
||||||
}
|
|
||||||
m_down = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TCPIPPipe::AsyncReceiveUpstream()
|
|
||||||
{
|
|
||||||
if (m_up) {
|
|
||||||
m_up->async_read_some(boost::asio::buffer(m_upstream_to_down_buf, TCP_IP_PIPE_BUFFER_SIZE),
|
|
||||||
std::bind(&TCPIPPipe::HandleUpstreamReceived, shared_from_this(),
|
|
||||||
std::placeholders::_1, std::placeholders::_2));
|
|
||||||
} else {
|
|
||||||
LogPrint(eLogError, "TCPIPPipe: upstream receive: no socket");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TCPIPPipe::AsyncReceiveDownstream()
|
|
||||||
{
|
|
||||||
if (m_down) {
|
|
||||||
m_down->async_read_some(boost::asio::buffer(m_downstream_to_up_buf, TCP_IP_PIPE_BUFFER_SIZE),
|
|
||||||
std::bind(&TCPIPPipe::HandleDownstreamReceived, shared_from_this(),
|
|
||||||
std::placeholders::_1, std::placeholders::_2));
|
|
||||||
} else {
|
|
||||||
LogPrint(eLogError, "TCPIPPipe: downstream receive: no socket");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TCPIPPipe::UpstreamWrite(const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
if (m_up) {
|
|
||||||
LogPrint(eLogDebug, "TCPIPPipe: upstream: ", (int) len, " bytes written");
|
|
||||||
boost::asio::async_write(*m_up, boost::asio::buffer(buf, len),
|
|
||||||
boost::asio::transfer_all(),
|
|
||||||
std::bind(&TCPIPPipe::HandleUpstreamWrite,
|
|
||||||
shared_from_this(),
|
|
||||||
std::placeholders::_1)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
LogPrint(eLogError, "TCPIPPipe: upstream write: no socket");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TCPIPPipe::DownstreamWrite(const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
if (m_down) {
|
|
||||||
LogPrint(eLogDebug, "TCPIPPipe: downstream: ", (int) len, " bytes written");
|
|
||||||
boost::asio::async_write(*m_down, boost::asio::buffer(buf, len),
|
|
||||||
boost::asio::transfer_all(),
|
|
||||||
std::bind(&TCPIPPipe::HandleDownstreamWrite,
|
|
||||||
shared_from_this(),
|
|
||||||
std::placeholders::_1)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
LogPrint(eLogError, "TCPIPPipe: downstream write: no socket");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void TCPIPPipe::HandleDownstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered)
|
|
||||||
{
|
|
||||||
LogPrint(eLogDebug, "TCPIPPipe: downstream: ", (int) bytes_transfered, " bytes received");
|
|
||||||
if (ecode) {
|
|
||||||
LogPrint(eLogError, "TCPIPPipe: downstream read error:" , ecode.message());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate();
|
|
||||||
} else {
|
|
||||||
if (bytes_transfered > 0 ) {
|
|
||||||
memcpy(m_upstream_buf, m_downstream_to_up_buf, bytes_transfered);
|
|
||||||
UpstreamWrite(m_upstream_buf, bytes_transfered);
|
|
||||||
}
|
|
||||||
AsyncReceiveDownstream();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TCPIPPipe::HandleDownstreamWrite(const boost::system::error_code & ecode) {
|
|
||||||
if (ecode) {
|
|
||||||
LogPrint(eLogError, "TCPIPPipe: downstream write error:" , ecode.message());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TCPIPPipe::HandleUpstreamWrite(const boost::system::error_code & ecode) {
|
|
||||||
if (ecode) {
|
|
||||||
LogPrint(eLogError, "TCPIPPipe: upstream write error:" , ecode.message());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TCPIPPipe::HandleUpstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered)
|
|
||||||
{
|
|
||||||
LogPrint(eLogDebug, "TCPIPPipe: upstream ", (int)bytes_transfered, " bytes received");
|
|
||||||
if (ecode) {
|
|
||||||
LogPrint(eLogError, "TCPIPPipe: upstream read error:" , ecode.message());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate();
|
|
||||||
} else {
|
|
||||||
if (bytes_transfered > 0 ) {
|
|
||||||
memcpy(m_upstream_buf, m_upstream_to_down_buf, bytes_transfered);
|
|
||||||
DownstreamWrite(m_upstream_buf, bytes_transfered);
|
|
||||||
}
|
|
||||||
AsyncReceiveUpstream();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TCPIPAcceptor::Start ()
|
|
||||||
{
|
|
||||||
m_Acceptor.listen ();
|
|
||||||
Accept ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TCPIPAcceptor::Stop ()
|
|
||||||
{
|
|
||||||
m_Acceptor.close();
|
|
||||||
m_Timer.cancel ();
|
|
||||||
ClearHandlers();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TCPIPAcceptor::Accept ()
|
|
||||||
{
|
|
||||||
auto newSocket = std::make_shared<boost::asio::ip::tcp::socket> (GetService ());
|
|
||||||
m_Acceptor.async_accept (*newSocket, std::bind (&TCPIPAcceptor::HandleAccept, this,
|
|
||||||
std::placeholders::_1, newSocket));
|
|
||||||
}
|
|
||||||
|
|
||||||
void TCPIPAcceptor::HandleAccept (const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket)
|
|
||||||
{
|
|
||||||
if (!ecode)
|
|
||||||
{
|
|
||||||
LogPrint(eLogDebug, "I2PService: ", GetName(), " accepted");
|
|
||||||
auto handler = CreateHandler(socket);
|
|
||||||
if (handler)
|
|
||||||
{
|
|
||||||
AddHandler(handler);
|
|
||||||
handler->Handle();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
socket->close();
|
|
||||||
Accept();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
LogPrint (eLogError, "I2PService: ", GetName(), " closing socket on accept because: ", ecode.message ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
762
I2PTunnel.cpp
762
I2PTunnel.cpp
@@ -1,762 +0,0 @@
|
|||||||
#include <cassert>
|
|
||||||
#include "Base.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "Destination.h"
|
|
||||||
#include "ClientContext.h"
|
|
||||||
#include "I2PTunnel.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
|
|
||||||
/** set standard socket options */
|
|
||||||
static void I2PTunnelSetSocketOptions(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
|
|
||||||
{
|
|
||||||
if (socket && socket->is_open())
|
|
||||||
{
|
|
||||||
boost::asio::socket_base::receive_buffer_size option(I2P_TUNNEL_CONNECTION_BUFFER_SIZE);
|
|
||||||
socket->set_option(option);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
|
||||||
std::shared_ptr<const i2p::data::LeaseSet> leaseSet, int port):
|
|
||||||
I2PServiceHandler(owner), m_Socket (socket), m_RemoteEndpoint (socket->remote_endpoint ()),
|
|
||||||
m_IsQuiet (true)
|
|
||||||
{
|
|
||||||
m_Stream = GetOwner()->GetLocalDestination ()->CreateStream (leaseSet, port);
|
|
||||||
}
|
|
||||||
|
|
||||||
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner,
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> socket, std::shared_ptr<i2p::stream::Stream> stream):
|
|
||||||
I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream),
|
|
||||||
m_RemoteEndpoint (socket->remote_endpoint ()), m_IsQuiet (true)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> socket, const boost::asio::ip::tcp::endpoint& target, bool quiet):
|
|
||||||
I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream),
|
|
||||||
m_RemoteEndpoint (target), m_IsQuiet (quiet)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
I2PTunnelConnection::~I2PTunnelConnection ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PTunnelConnection::I2PConnect (const uint8_t * msg, size_t len)
|
|
||||||
{
|
|
||||||
if (m_Stream)
|
|
||||||
{
|
|
||||||
if (msg)
|
|
||||||
m_Stream->Send (msg, len); // connect and send
|
|
||||||
else
|
|
||||||
m_Stream->Send (m_Buffer, 0); // connect
|
|
||||||
}
|
|
||||||
StreamReceive ();
|
|
||||||
Receive ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PTunnelConnection::Connect ()
|
|
||||||
{
|
|
||||||
I2PTunnelSetSocketOptions(m_Socket);
|
|
||||||
if (m_Socket) {
|
|
||||||
#ifdef __linux__
|
|
||||||
// bind to 127.x.x.x address
|
|
||||||
// where x.x.x are first three bytes from ident
|
|
||||||
|
|
||||||
if (m_RemoteEndpoint.address ().is_v4 () &&
|
|
||||||
m_RemoteEndpoint.address ().to_v4 ().to_bytes ()[0] == 127)
|
|
||||||
{
|
|
||||||
m_Socket->open (boost::asio::ip::tcp::v4 ());
|
|
||||||
boost::asio::ip::address_v4::bytes_type bytes;
|
|
||||||
const uint8_t * ident = m_Stream->GetRemoteIdentity ()->GetIdentHash ();
|
|
||||||
bytes[0] = 127;
|
|
||||||
memcpy (bytes.data ()+1, ident, 3);
|
|
||||||
boost::asio::ip::address ourIP = boost::asio::ip::address_v4 (bytes);
|
|
||||||
m_Socket->bind (boost::asio::ip::tcp::endpoint (ourIP, 0));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
m_Socket->async_connect (m_RemoteEndpoint, std::bind (&I2PTunnelConnection::HandleConnect,
|
|
||||||
shared_from_this (), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PTunnelConnection::Terminate ()
|
|
||||||
{
|
|
||||||
if (Kill()) return;
|
|
||||||
if (m_Stream)
|
|
||||||
{
|
|
||||||
m_Stream->Close ();
|
|
||||||
m_Stream.reset ();
|
|
||||||
}
|
|
||||||
m_Socket->close ();
|
|
||||||
Done(shared_from_this ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PTunnelConnection::Receive ()
|
|
||||||
{
|
|
||||||
m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
|
|
||||||
std::bind(&I2PTunnelConnection::HandleReceived, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2));
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PTunnelConnection::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "I2PTunnel: read error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (m_Stream)
|
|
||||||
{
|
|
||||||
auto s = shared_from_this ();
|
|
||||||
m_Stream->AsyncSend (m_Buffer, bytes_transferred,
|
|
||||||
[s](const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (!ecode)
|
|
||||||
s->Receive ();
|
|
||||||
else
|
|
||||||
s->Terminate ();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PTunnelConnection::HandleWrite (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "I2PTunnel: write error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
StreamReceive ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PTunnelConnection::StreamReceive ()
|
|
||||||
{
|
|
||||||
if (m_Stream)
|
|
||||||
{
|
|
||||||
if (m_Stream->GetStatus () == i2p::stream::eStreamStatusNew ||
|
|
||||||
m_Stream->GetStatus () == i2p::stream::eStreamStatusOpen) // regular
|
|
||||||
{
|
|
||||||
m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
|
|
||||||
std::bind (&I2PTunnelConnection::HandleStreamReceive, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2),
|
|
||||||
I2P_TUNNEL_CONNECTION_MAX_IDLE);
|
|
||||||
}
|
|
||||||
else // closed by peer
|
|
||||||
{
|
|
||||||
// get remaning data
|
|
||||||
auto len = m_Stream->ReadSome (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE);
|
|
||||||
if (len > 0) // still some data
|
|
||||||
Write (m_StreamBuffer, len);
|
|
||||||
else // no more data
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PTunnelConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "I2PTunnel: stream read error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
if (bytes_transferred > 0)
|
|
||||||
Write (m_StreamBuffer, bytes_transferred); // postpone termination
|
|
||||||
else
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Write (m_StreamBuffer, bytes_transferred);
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PTunnelConnection::Write (const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
boost::asio::async_write (*m_Socket, boost::asio::buffer (buf, len), boost::asio::transfer_all (),
|
|
||||||
std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PTunnelConnection::HandleConnect (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "I2PTunnel: connect error: ", ecode.message ());
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "I2PTunnel: connected");
|
|
||||||
if (m_IsQuiet)
|
|
||||||
StreamReceive ();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// send destination first like received from I2P
|
|
||||||
std::string dest = m_Stream->GetRemoteIdentity ()->ToBase64 ();
|
|
||||||
dest += "\n";
|
|
||||||
memcpy (m_StreamBuffer, dest.c_str (), dest.size ());
|
|
||||||
HandleStreamReceive (boost::system::error_code (), dest.size ());
|
|
||||||
}
|
|
||||||
Receive ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
I2PTunnelConnectionHTTP::I2PTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
|
||||||
const boost::asio::ip::tcp::endpoint& target, const std::string& host):
|
|
||||||
I2PTunnelConnection (owner, stream, socket, target), m_Host (host), m_HeaderSent (false), m_From (stream->GetRemoteIdentity ())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
if (m_HeaderSent)
|
|
||||||
I2PTunnelConnection::Write (buf, len);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_InHeader.clear ();
|
|
||||||
m_InHeader.write ((const char *)buf, len);
|
|
||||||
std::string line;
|
|
||||||
bool endOfHeader = false;
|
|
||||||
while (!endOfHeader)
|
|
||||||
{
|
|
||||||
std::getline(m_InHeader, line);
|
|
||||||
if (!m_InHeader.fail ())
|
|
||||||
{
|
|
||||||
if (line == "\r") endOfHeader = true;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (m_Host.length () > 0 && line.find ("Host:") != std::string::npos)
|
|
||||||
m_OutHeader << "Host: " << m_Host << "\r\n"; // override host
|
|
||||||
else
|
|
||||||
m_OutHeader << line << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// add X-I2P fields
|
|
||||||
if (m_From)
|
|
||||||
{
|
|
||||||
m_OutHeader << X_I2P_DEST_B32 << ": " << context.GetAddressBook ().ToAddress(m_From->GetIdentHash ()) << "\r\n";
|
|
||||||
m_OutHeader << X_I2P_DEST_HASH << ": " << m_From->GetIdentHash ().ToBase64 () << "\r\n";
|
|
||||||
m_OutHeader << X_I2P_DEST_B64 << ": " << m_From->ToBase64 () << "\r\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (endOfHeader)
|
|
||||||
{
|
|
||||||
m_OutHeader << "\r\n"; // end of header
|
|
||||||
m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header
|
|
||||||
m_HeaderSent = true;
|
|
||||||
I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
I2PTunnelConnectionIRC::I2PTunnelConnectionIRC (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
|
||||||
const boost::asio::ip::tcp::endpoint& target, const std::string& webircpass):
|
|
||||||
I2PTunnelConnection (owner, stream, socket, target), m_From (stream->GetRemoteIdentity ()),
|
|
||||||
m_NeedsWebIrc (webircpass.length() ? true : false), m_WebircPass (webircpass)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PTunnelConnectionIRC::Write (const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
m_OutPacket.str ("");
|
|
||||||
if (m_NeedsWebIrc)
|
|
||||||
{
|
|
||||||
m_NeedsWebIrc = false;
|
|
||||||
m_OutPacket << "WEBIRC " << m_WebircPass << " cgiirc " << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ()) << " 127.0.0.1\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
m_InPacket.clear ();
|
|
||||||
m_InPacket.write ((const char *)buf, len);
|
|
||||||
|
|
||||||
while (!m_InPacket.eof () && !m_InPacket.fail ())
|
|
||||||
{
|
|
||||||
std::string line;
|
|
||||||
std::getline (m_InPacket, line);
|
|
||||||
if (line.length () == 0 && m_InPacket.eof ())
|
|
||||||
m_InPacket.str ("");
|
|
||||||
auto pos = line.find ("USER");
|
|
||||||
if (!pos) // start of line
|
|
||||||
{
|
|
||||||
pos = line.find (" ");
|
|
||||||
pos++;
|
|
||||||
pos = line.find (" ", pos);
|
|
||||||
pos++;
|
|
||||||
auto nextpos = line.find (" ", pos);
|
|
||||||
m_OutPacket << line.substr (0, pos);
|
|
||||||
m_OutPacket << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ());
|
|
||||||
m_OutPacket << line.substr (nextpos) << '\n';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_OutPacket << line << '\n';
|
|
||||||
}
|
|
||||||
I2PTunnelConnection::Write ((uint8_t *)m_OutPacket.str ().c_str (), m_OutPacket.str ().length ());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* This handler tries to stablish a connection with the desired server and dies if it fails to do so */
|
|
||||||
class I2PClientTunnelHandler: public I2PServiceHandler, public std::enable_shared_from_this<I2PClientTunnelHandler>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
I2PClientTunnelHandler (I2PClientTunnel * parent, i2p::data::IdentHash destination,
|
|
||||||
int destinationPort, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
|
|
||||||
I2PServiceHandler(parent), m_DestinationIdentHash(destination),
|
|
||||||
m_DestinationPort (destinationPort), m_Socket(socket) {};
|
|
||||||
void Handle();
|
|
||||||
void Terminate();
|
|
||||||
private:
|
|
||||||
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
|
|
||||||
i2p::data::IdentHash m_DestinationIdentHash;
|
|
||||||
int m_DestinationPort;
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
|
|
||||||
};
|
|
||||||
|
|
||||||
void I2PClientTunnelHandler::Handle()
|
|
||||||
{
|
|
||||||
GetOwner()->GetLocalDestination ()->CreateStream (
|
|
||||||
std::bind (&I2PClientTunnelHandler::HandleStreamRequestComplete, shared_from_this(), std::placeholders::_1),
|
|
||||||
m_DestinationIdentHash, m_DestinationPort);
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PClientTunnelHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
|
|
||||||
{
|
|
||||||
if (stream)
|
|
||||||
{
|
|
||||||
if (Kill()) return;
|
|
||||||
LogPrint (eLogDebug, "I2PTunnel: new connection");
|
|
||||||
auto connection = std::make_shared<I2PTunnelConnection>(GetOwner(), m_Socket, stream);
|
|
||||||
GetOwner()->AddHandler (connection);
|
|
||||||
connection->I2PConnect ();
|
|
||||||
Done(shared_from_this());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "I2PTunnel: Client Tunnel Issue when creating the stream, check the previous warnings for more info.");
|
|
||||||
Terminate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PClientTunnelHandler::Terminate()
|
|
||||||
{
|
|
||||||
if (Kill()) return;
|
|
||||||
if (m_Socket)
|
|
||||||
{
|
|
||||||
m_Socket->close();
|
|
||||||
m_Socket = nullptr;
|
|
||||||
}
|
|
||||||
Done(shared_from_this());
|
|
||||||
}
|
|
||||||
|
|
||||||
I2PClientTunnel::I2PClientTunnel (const std::string& name, const std::string& destination,
|
|
||||||
const std::string& address, int port, std::shared_ptr<ClientDestination> localDestination, int destinationPort):
|
|
||||||
TCPIPAcceptor (address, port, localDestination), m_Name (name), m_Destination (destination),
|
|
||||||
m_DestinationIdentHash (nullptr), m_DestinationPort (destinationPort)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PClientTunnel::Start ()
|
|
||||||
{
|
|
||||||
TCPIPAcceptor::Start ();
|
|
||||||
GetIdentHash();
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PClientTunnel::Stop ()
|
|
||||||
{
|
|
||||||
TCPIPAcceptor::Stop();
|
|
||||||
auto *originalIdentHash = m_DestinationIdentHash;
|
|
||||||
m_DestinationIdentHash = nullptr;
|
|
||||||
delete originalIdentHash;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* HACK: maybe we should create a caching IdentHash provider in AddressBook */
|
|
||||||
const i2p::data::IdentHash * I2PClientTunnel::GetIdentHash ()
|
|
||||||
{
|
|
||||||
if (!m_DestinationIdentHash)
|
|
||||||
{
|
|
||||||
i2p::data::IdentHash identHash;
|
|
||||||
if (i2p::client::context.GetAddressBook ().GetIdentHash (m_Destination, identHash))
|
|
||||||
m_DestinationIdentHash = new i2p::data::IdentHash (identHash);
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "I2PTunnel: Remote destination ", m_Destination, " not found");
|
|
||||||
}
|
|
||||||
return m_DestinationIdentHash;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2PServiceHandler> I2PClientTunnel::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
|
|
||||||
{
|
|
||||||
const i2p::data::IdentHash *identHash = GetIdentHash();
|
|
||||||
if (identHash)
|
|
||||||
return std::make_shared<I2PClientTunnelHandler>(this, *identHash, m_DestinationPort, socket);
|
|
||||||
else
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
I2PServerTunnel::I2PServerTunnel (const std::string& name, const std::string& address,
|
|
||||||
int port, std::shared_ptr<ClientDestination> localDestination, int inport, bool gzip):
|
|
||||||
I2PService (localDestination), m_Name (name), m_Address (address), m_Port (port), m_IsAccessList (false)
|
|
||||||
{
|
|
||||||
m_PortDestination = localDestination->CreateStreamingDestination (inport > 0 ? inport : port, gzip);
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PServerTunnel::Start ()
|
|
||||||
{
|
|
||||||
m_Endpoint.port (m_Port);
|
|
||||||
boost::system::error_code ec;
|
|
||||||
auto addr = boost::asio::ip::address::from_string (m_Address, ec);
|
|
||||||
if (!ec)
|
|
||||||
{
|
|
||||||
m_Endpoint.address (addr);
|
|
||||||
Accept ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto resolver = std::make_shared<boost::asio::ip::tcp::resolver>(GetService ());
|
|
||||||
resolver->async_resolve (boost::asio::ip::tcp::resolver::query (m_Address, ""),
|
|
||||||
std::bind (&I2PServerTunnel::HandleResolve, this,
|
|
||||||
std::placeholders::_1, std::placeholders::_2, resolver));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PServerTunnel::Stop ()
|
|
||||||
{
|
|
||||||
ClearHandlers ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PServerTunnel::HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::resolver> resolver)
|
|
||||||
{
|
|
||||||
if (!ecode)
|
|
||||||
{
|
|
||||||
auto addr = (*it).endpoint ().address ();
|
|
||||||
LogPrint (eLogInfo, "I2PTunnel: server tunnel ", (*it).host_name (), " has been resolved to ", addr);
|
|
||||||
m_Endpoint.address (addr);
|
|
||||||
Accept ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "I2PTunnel: Unable to resolve server tunnel address: ", ecode.message ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PServerTunnel::SetAccessList (const std::set<i2p::data::IdentHash>& accessList)
|
|
||||||
{
|
|
||||||
m_AccessList = accessList;
|
|
||||||
m_IsAccessList = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PServerTunnel::Accept ()
|
|
||||||
{
|
|
||||||
if (m_PortDestination)
|
|
||||||
m_PortDestination->SetAcceptor (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1));
|
|
||||||
|
|
||||||
auto localDestination = GetLocalDestination ();
|
|
||||||
if (localDestination)
|
|
||||||
{
|
|
||||||
if (!localDestination->IsAcceptingStreams ()) // set it as default if not set yet
|
|
||||||
localDestination->AcceptStreams (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "I2PTunnel: Local destination not set for server tunnel");
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PServerTunnel::HandleAccept (std::shared_ptr<i2p::stream::Stream> stream)
|
|
||||||
{
|
|
||||||
if (stream)
|
|
||||||
{
|
|
||||||
if (m_IsAccessList)
|
|
||||||
{
|
|
||||||
if (!m_AccessList.count (stream->GetRemoteIdentity ()->GetIdentHash ()))
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "I2PTunnel: Address ", stream->GetRemoteIdentity ()->GetIdentHash ().ToBase32 (), " is not in white list. Incoming connection dropped");
|
|
||||||
stream->Close ();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CreateI2PConnection (stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PServerTunnel::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
|
|
||||||
{
|
|
||||||
auto conn = std::make_shared<I2PTunnelConnection> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint ());
|
|
||||||
AddHandler (conn);
|
|
||||||
conn->Connect ();
|
|
||||||
}
|
|
||||||
|
|
||||||
I2PServerTunnelHTTP::I2PServerTunnelHTTP (const std::string& name, const std::string& address,
|
|
||||||
int port, std::shared_ptr<ClientDestination> localDestination,
|
|
||||||
const std::string& host, int inport, bool gzip):
|
|
||||||
I2PServerTunnel (name, address, port, localDestination, inport, gzip),
|
|
||||||
m_Host (host)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PServerTunnelHTTP::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
|
|
||||||
{
|
|
||||||
auto conn = std::make_shared<I2PTunnelConnectionHTTP> (this, stream,
|
|
||||||
std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint (), m_Host);
|
|
||||||
AddHandler (conn);
|
|
||||||
conn->Connect ();
|
|
||||||
}
|
|
||||||
|
|
||||||
I2PServerTunnelIRC::I2PServerTunnelIRC (const std::string& name, const std::string& address,
|
|
||||||
int port, std::shared_ptr<ClientDestination> localDestination,
|
|
||||||
const std::string& webircpass, int inport, bool gzip):
|
|
||||||
I2PServerTunnel (name, address, port, localDestination, inport, gzip),
|
|
||||||
m_WebircPass (webircpass)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PServerTunnelIRC::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
|
|
||||||
{
|
|
||||||
auto conn = std::make_shared<I2PTunnelConnectionIRC> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint (), this->m_WebircPass);
|
|
||||||
AddHandler (conn);
|
|
||||||
conn->Connect ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PUDPServerTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_SessionsMutex);
|
|
||||||
auto session = ObtainUDPSession(from, toPort, fromPort);
|
|
||||||
session->IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint);
|
|
||||||
session->LastActivity = i2p::util::GetMillisecondsSinceEpoch();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PUDPServerTunnel::ExpireStale(const uint64_t delta) {
|
|
||||||
std::lock_guard<std::mutex> lock(m_SessionsMutex);
|
|
||||||
uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
|
|
||||||
std::remove_if(m_Sessions.begin(), m_Sessions.end(), [now, delta](const UDPSession * u) -> bool {
|
|
||||||
return now - u->LastActivity >= delta;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
UDPSession * I2PUDPServerTunnel::ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort)
|
|
||||||
{
|
|
||||||
auto ih = from.GetIdentHash();
|
|
||||||
for ( UDPSession * s : m_Sessions )
|
|
||||||
{
|
|
||||||
if ( s->Identity == ih)
|
|
||||||
{
|
|
||||||
/** found existing session */
|
|
||||||
LogPrint(eLogDebug, "UDPServer: found session ", s->IPSocket.local_endpoint(), " ", ih.ToBase32());
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/** create new udp session */
|
|
||||||
boost::asio::ip::udp::endpoint ep(m_LocalAddress, 0);
|
|
||||||
m_Sessions.push_back(new UDPSession(ep, m_LocalDest, m_RemoteEndpoint, &ih, localPort, remotePort));
|
|
||||||
return m_Sessions.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
UDPSession::UDPSession(boost::asio::ip::udp::endpoint localEndpoint,
|
|
||||||
const std::shared_ptr<i2p::client::ClientDestination> & localDestination,
|
|
||||||
boost::asio::ip::udp::endpoint endpoint, const i2p::data::IdentHash * to,
|
|
||||||
uint16_t ourPort, uint16_t theirPort) :
|
|
||||||
m_Destination(localDestination->GetDatagramDestination()),
|
|
||||||
m_Service(localDestination->GetService()),
|
|
||||||
IPSocket(localDestination->GetService(), localEndpoint),
|
|
||||||
SendEndpoint(endpoint),
|
|
||||||
LastActivity(i2p::util::GetMillisecondsSinceEpoch()),
|
|
||||||
LocalPort(ourPort),
|
|
||||||
RemotePort(theirPort)
|
|
||||||
{
|
|
||||||
memcpy(Identity, to->data(), 32);
|
|
||||||
Receive();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void UDPSession::Receive() {
|
|
||||||
LogPrint(eLogDebug, "UDPSession: Receive");
|
|
||||||
IPSocket.async_receive_from(boost::asio::buffer(m_Buffer, I2P_UDP_MAX_MTU),
|
|
||||||
FromEndpoint, std::bind(&UDPSession::HandleReceived, this, std::placeholders::_1, std::placeholders::_2));
|
|
||||||
}
|
|
||||||
|
|
||||||
void UDPSession::HandleReceived(const boost::system::error_code & ecode, std::size_t len)
|
|
||||||
{
|
|
||||||
if(!ecode)
|
|
||||||
{
|
|
||||||
LogPrint(eLogDebug, "UDPSession: forward ", len, "B from ", FromEndpoint);
|
|
||||||
LastActivity = i2p::util::GetMillisecondsSinceEpoch();
|
|
||||||
m_Destination->SendDatagramTo(m_Buffer, len, Identity, 0, 0);
|
|
||||||
Receive();
|
|
||||||
} else {
|
|
||||||
LogPrint(eLogError, "UDPSession: ", ecode.message());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
I2PUDPServerTunnel::I2PUDPServerTunnel(const std::string & name, std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
|
||||||
const boost::asio::ip::address& localAddress, boost::asio::ip::udp::endpoint forwardTo, uint16_t port) :
|
|
||||||
m_Name(name),
|
|
||||||
LocalPort(port),
|
|
||||||
m_LocalAddress(localAddress),
|
|
||||||
m_RemoteEndpoint(forwardTo)
|
|
||||||
{
|
|
||||||
m_LocalDest = localDestination;
|
|
||||||
m_LocalDest->Start();
|
|
||||||
auto dgram = m_LocalDest->CreateDatagramDestination();
|
|
||||||
dgram->SetReceiver(std::bind(&I2PUDPServerTunnel::HandleRecvFromI2P, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
|
|
||||||
}
|
|
||||||
|
|
||||||
I2PUDPServerTunnel::~I2PUDPServerTunnel()
|
|
||||||
{
|
|
||||||
auto dgram = m_LocalDest->GetDatagramDestination();
|
|
||||||
if (dgram) dgram->ResetReceiver();
|
|
||||||
|
|
||||||
LogPrint(eLogInfo, "UDPServer: done");
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PUDPServerTunnel::Start() {
|
|
||||||
m_LocalDest->Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<DatagramSessionInfo> > I2PUDPServerTunnel::GetSessions()
|
|
||||||
{
|
|
||||||
std::vector<std::shared_ptr<DatagramSessionInfo> > sessions;
|
|
||||||
std::lock_guard<std::mutex> lock(m_SessionsMutex);
|
|
||||||
for ( UDPSession * s : m_Sessions )
|
|
||||||
{
|
|
||||||
if (!s->m_Destination) continue;
|
|
||||||
auto info = s->m_Destination->GetInfoForRemote(s->Identity);
|
|
||||||
if(!info) continue;
|
|
||||||
|
|
||||||
auto sinfo = std::make_shared<DatagramSessionInfo>();
|
|
||||||
sinfo->Name = m_Name;
|
|
||||||
sinfo->LocalIdent = std::make_shared<i2p::data::IdentHash>(m_LocalDest->GetIdentHash().data());
|
|
||||||
sinfo->RemoteIdent = std::make_shared<i2p::data::IdentHash>(s->Identity.data());
|
|
||||||
sinfo->CurrentIBGW = info->IBGW;
|
|
||||||
sinfo->CurrentOBEP = info->OBEP;
|
|
||||||
sessions.push_back(sinfo);
|
|
||||||
}
|
|
||||||
return sessions;
|
|
||||||
}
|
|
||||||
|
|
||||||
I2PUDPClientTunnel::I2PUDPClientTunnel(const std::string & name, const std::string &remoteDest,
|
|
||||||
boost::asio::ip::udp::endpoint localEndpoint,
|
|
||||||
std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
|
||||||
uint16_t remotePort) :
|
|
||||||
m_Name(name),
|
|
||||||
m_Session(nullptr),
|
|
||||||
m_RemoteDest(remoteDest),
|
|
||||||
m_LocalDest(localDestination),
|
|
||||||
m_LocalEndpoint(localEndpoint),
|
|
||||||
m_RemoteIdent(nullptr),
|
|
||||||
m_ResolveThread(nullptr),
|
|
||||||
LocalPort(localEndpoint.port()),
|
|
||||||
RemotePort(remotePort),
|
|
||||||
m_cancel_resolve(false)
|
|
||||||
{
|
|
||||||
auto dgram = m_LocalDest->CreateDatagramDestination();
|
|
||||||
dgram->SetReceiver(std::bind(&I2PUDPClientTunnel::HandleRecvFromI2P, this,
|
|
||||||
std::placeholders::_1, std::placeholders::_2,
|
|
||||||
std::placeholders::_3, std::placeholders::_4,
|
|
||||||
std::placeholders::_5));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void I2PUDPClientTunnel::Start() {
|
|
||||||
m_LocalDest->Start();
|
|
||||||
if (m_ResolveThread == nullptr)
|
|
||||||
m_ResolveThread = new std::thread(std::bind(&I2PUDPClientTunnel::TryResolving, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<DatagramSessionInfo> > I2PUDPClientTunnel::GetSessions()
|
|
||||||
{
|
|
||||||
std::vector<std::shared_ptr<DatagramSessionInfo> > infos;
|
|
||||||
if(m_Session && m_LocalDest)
|
|
||||||
{
|
|
||||||
auto s = m_Session;
|
|
||||||
if (s->m_Destination)
|
|
||||||
{
|
|
||||||
auto info = m_Session->m_Destination->GetInfoForRemote(s->Identity);
|
|
||||||
if(info)
|
|
||||||
{
|
|
||||||
auto sinfo = std::make_shared<DatagramSessionInfo>();
|
|
||||||
sinfo->Name = m_Name;
|
|
||||||
sinfo->LocalIdent = std::make_shared<i2p::data::IdentHash>(m_LocalDest->GetIdentHash().data());
|
|
||||||
sinfo->RemoteIdent = std::make_shared<i2p::data::IdentHash>(s->Identity.data());
|
|
||||||
sinfo->CurrentIBGW = info->IBGW;
|
|
||||||
sinfo->CurrentOBEP = info->OBEP;
|
|
||||||
infos.push_back(sinfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return infos;
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PUDPClientTunnel::TryResolving() {
|
|
||||||
LogPrint(eLogInfo, "UDP Tunnel: Trying to resolve ", m_RemoteDest);
|
|
||||||
m_RemoteIdent = new i2p::data::IdentHash;
|
|
||||||
m_RemoteIdent->Fill(0);
|
|
||||||
|
|
||||||
while(!context.GetAddressBook().GetIdentHash(m_RemoteDest, *m_RemoteIdent) && !m_cancel_resolve)
|
|
||||||
{
|
|
||||||
LogPrint(eLogWarning, "UDP Tunnel: failed to lookup ", m_RemoteDest);
|
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
||||||
}
|
|
||||||
if(m_cancel_resolve)
|
|
||||||
{
|
|
||||||
LogPrint(eLogError, "UDP Tunnel: lookup of ", m_RemoteDest, " was cancelled");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
LogPrint(eLogInfo, "UDP Tunnel: resolved ", m_RemoteDest, " to ", m_RemoteIdent->ToBase32());
|
|
||||||
// delete existing session
|
|
||||||
if(m_Session) delete m_Session;
|
|
||||||
|
|
||||||
boost::asio::ip::udp::endpoint ep(boost::asio::ip::address::from_string("127.0.0.1"), 0);
|
|
||||||
m_Session = new UDPSession(m_LocalEndpoint, m_LocalDest, ep, m_RemoteIdent, LocalPort, RemotePort);
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PUDPClientTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
if(m_RemoteIdent && from.GetIdentHash() == *m_RemoteIdent)
|
|
||||||
{
|
|
||||||
// address match
|
|
||||||
if(m_Session)
|
|
||||||
{
|
|
||||||
// tell session
|
|
||||||
LogPrint(eLogDebug, "UDP Client: got ", len, "B from ", from.GetIdentHash().ToBase32());
|
|
||||||
m_Session->IPSocket.send_to(boost::asio::buffer(buf, len), m_Session->FromEndpoint);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint(eLogWarning, "UDP Client: no session");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint(eLogWarning, "UDP Client: unwarrented traffic from ", from.GetIdentHash().ToBase32());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
I2PUDPClientTunnel::~I2PUDPClientTunnel() {
|
|
||||||
auto dgram = m_LocalDest->GetDatagramDestination();
|
|
||||||
if (dgram) dgram->ResetReceiver();
|
|
||||||
|
|
||||||
if (m_Session) delete m_Session;
|
|
||||||
m_cancel_resolve = true;
|
|
||||||
|
|
||||||
if(m_ResolveThread)
|
|
||||||
{
|
|
||||||
m_ResolveThread->join();
|
|
||||||
delete m_ResolveThread;
|
|
||||||
m_ResolveThread = nullptr;
|
|
||||||
}
|
|
||||||
if (m_RemoteIdent) delete m_RemoteIdent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
324
I2PTunnel.h
324
I2PTunnel.h
@@ -1,324 +0,0 @@
|
|||||||
#ifndef I2PTUNNEL_H__
|
|
||||||
#define I2PTUNNEL_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <string>
|
|
||||||
#include <set>
|
|
||||||
#include <memory>
|
|
||||||
#include <sstream>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "Destination.h"
|
|
||||||
#include "Datagram.h"
|
|
||||||
#include "Streaming.h"
|
|
||||||
#include "I2PService.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 8192;
|
|
||||||
const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds
|
|
||||||
const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds
|
|
||||||
// for HTTP tunnels
|
|
||||||
const char X_I2P_DEST_HASH[] = "X-I2P-DestHash"; // hash in base64
|
|
||||||
const char X_I2P_DEST_B64[] = "X-I2P-DestB64"; // full address in base64
|
|
||||||
const char X_I2P_DEST_B32[] = "X-I2P-DestB32"; // .b32.i2p address
|
|
||||||
|
|
||||||
class I2PTunnelConnection: public I2PServiceHandler, public std::enable_shared_from_this<I2PTunnelConnection>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
|
||||||
std::shared_ptr<const i2p::data::LeaseSet> leaseSet, int port = 0); // to I2P
|
|
||||||
I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
|
||||||
std::shared_ptr<i2p::stream::Stream> stream); // to I2P using simplified API
|
|
||||||
I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
|
||||||
const boost::asio::ip::tcp::endpoint& target, bool quiet = true); // from I2P
|
|
||||||
~I2PTunnelConnection ();
|
|
||||||
void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0);
|
|
||||||
void Connect ();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
void Terminate ();
|
|
||||||
|
|
||||||
void Receive ();
|
|
||||||
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
|
||||||
virtual void Write (const uint8_t * buf, size_t len); // can be overloaded
|
|
||||||
void HandleWrite (const boost::system::error_code& ecode);
|
|
||||||
|
|
||||||
void StreamReceive ();
|
|
||||||
void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
|
||||||
void HandleConnect (const boost::system::error_code& ecode);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
uint8_t m_Buffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE], m_StreamBuffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE];
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
|
|
||||||
std::shared_ptr<i2p::stream::Stream> m_Stream;
|
|
||||||
boost::asio::ip::tcp::endpoint m_RemoteEndpoint;
|
|
||||||
bool m_IsQuiet; // don't send destination
|
|
||||||
};
|
|
||||||
|
|
||||||
class I2PTunnelConnectionHTTP: public I2PTunnelConnection
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
I2PTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
|
||||||
const boost::asio::ip::tcp::endpoint& target, const std::string& host);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
void Write (const uint8_t * buf, size_t len);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::string m_Host;
|
|
||||||
std::stringstream m_InHeader, m_OutHeader;
|
|
||||||
bool m_HeaderSent;
|
|
||||||
std::shared_ptr<const i2p::data::IdentityEx> m_From;
|
|
||||||
};
|
|
||||||
|
|
||||||
class I2PTunnelConnectionIRC: public I2PTunnelConnection
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
I2PTunnelConnectionIRC (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
|
||||||
const boost::asio::ip::tcp::endpoint& target, const std::string& m_WebircPass);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
void Write (const uint8_t * buf, size_t len);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::shared_ptr<const i2p::data::IdentityEx> m_From;
|
|
||||||
std::stringstream m_OutPacket, m_InPacket;
|
|
||||||
bool m_NeedsWebIrc;
|
|
||||||
std::string m_WebircPass;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class I2PClientTunnel: public TCPIPAcceptor
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
|
|
||||||
// Implements TCPIPAcceptor
|
|
||||||
std::shared_ptr<I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket);
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
I2PClientTunnel (const std::string& name, const std::string& destination,
|
|
||||||
const std::string& address, int port, std::shared_ptr<ClientDestination> localDestination, int destinationPort = 0);
|
|
||||||
~I2PClientTunnel () {}
|
|
||||||
|
|
||||||
void Start ();
|
|
||||||
void Stop ();
|
|
||||||
|
|
||||||
const char* GetName() { return m_Name.c_str (); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
const i2p::data::IdentHash * GetIdentHash ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::string m_Name, m_Destination;
|
|
||||||
const i2p::data::IdentHash * m_DestinationIdentHash;
|
|
||||||
int m_DestinationPort;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/** 2 minute timeout for udp sessions */
|
|
||||||
const uint64_t I2P_UDP_SESSION_TIMEOUT = 1000 * 60 * 2;
|
|
||||||
|
|
||||||
/** max size for i2p udp */
|
|
||||||
const size_t I2P_UDP_MAX_MTU = i2p::datagram::MAX_DATAGRAM_SIZE;
|
|
||||||
|
|
||||||
struct UDPSession
|
|
||||||
{
|
|
||||||
i2p::datagram::DatagramDestination * m_Destination;
|
|
||||||
boost::asio::io_service & m_Service;
|
|
||||||
boost::asio::ip::udp::socket IPSocket;
|
|
||||||
i2p::data::IdentHash Identity;
|
|
||||||
boost::asio::ip::udp::endpoint FromEndpoint;
|
|
||||||
boost::asio::ip::udp::endpoint SendEndpoint;
|
|
||||||
uint64_t LastActivity;
|
|
||||||
|
|
||||||
uint16_t LocalPort;
|
|
||||||
uint16_t RemotePort;
|
|
||||||
|
|
||||||
uint8_t m_Buffer[I2P_UDP_MAX_MTU];
|
|
||||||
|
|
||||||
UDPSession(boost::asio::ip::udp::endpoint localEndpoint,
|
|
||||||
const std::shared_ptr<i2p::client::ClientDestination> & localDestination,
|
|
||||||
boost::asio::ip::udp::endpoint remote, const i2p::data::IdentHash * ident,
|
|
||||||
uint16_t ourPort, uint16_t theirPort);
|
|
||||||
void HandleReceived(const boost::system::error_code & ecode, std::size_t len);
|
|
||||||
void Receive();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/** read only info about a datagram session */
|
|
||||||
struct DatagramSessionInfo
|
|
||||||
{
|
|
||||||
/** the name of this forward */
|
|
||||||
std::string Name;
|
|
||||||
/** ident hash of local destination */
|
|
||||||
std::shared_ptr<const i2p::data::IdentHash> LocalIdent;
|
|
||||||
/** ident hash of remote destination */
|
|
||||||
std::shared_ptr<const i2p::data::IdentHash> RemoteIdent;
|
|
||||||
/** ident hash of IBGW in use currently in this session or nullptr if none is set */
|
|
||||||
std::shared_ptr<const i2p::data::IdentHash> CurrentIBGW;
|
|
||||||
/** ident hash of OBEP in use for this session or nullptr if none is set */
|
|
||||||
std::shared_ptr<const i2p::data::IdentHash> CurrentOBEP;
|
|
||||||
/** i2p router's udp endpoint */
|
|
||||||
boost::asio::ip::udp::endpoint LocalEndpoint;
|
|
||||||
/** client's udp endpoint */
|
|
||||||
boost::asio::ip::udp::endpoint RemoteEndpoint;
|
|
||||||
/** how long has this converstation been idle in ms */
|
|
||||||
uint64_t idle;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** server side udp tunnel, many i2p inbound to 1 ip outbound */
|
|
||||||
class I2PUDPServerTunnel
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
I2PUDPServerTunnel(const std::string & name,
|
|
||||||
std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
|
||||||
const boost::asio::ip::address & localAddress,
|
|
||||||
boost::asio::ip::udp::endpoint forwardTo, uint16_t port);
|
|
||||||
~I2PUDPServerTunnel();
|
|
||||||
/** expire stale udp conversations */
|
|
||||||
void ExpireStale(const uint64_t delta=I2P_UDP_SESSION_TIMEOUT);
|
|
||||||
void Start();
|
|
||||||
const char * GetName() const { return m_Name.c_str(); }
|
|
||||||
std::vector<std::shared_ptr<DatagramSessionInfo> > GetSessions();
|
|
||||||
std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDest; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
|
||||||
UDPSession * ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort);
|
|
||||||
|
|
||||||
private:
|
|
||||||
const std::string m_Name;
|
|
||||||
const uint16_t LocalPort;
|
|
||||||
boost::asio::ip::address m_LocalAddress;
|
|
||||||
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
|
|
||||||
std::mutex m_SessionsMutex;
|
|
||||||
std::vector<UDPSession*> m_Sessions;
|
|
||||||
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
|
|
||||||
};
|
|
||||||
|
|
||||||
class I2PUDPClientTunnel
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
I2PUDPClientTunnel(const std::string & name, const std::string &remoteDest,
|
|
||||||
boost::asio::ip::udp::endpoint localEndpoint, std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
|
||||||
uint16_t remotePort);
|
|
||||||
~I2PUDPClientTunnel();
|
|
||||||
void Start();
|
|
||||||
const char * GetName() const { return m_Name.c_str(); }
|
|
||||||
std::vector<std::shared_ptr<DatagramSessionInfo> > GetSessions();
|
|
||||||
|
|
||||||
bool IsLocalDestination(const i2p::data::IdentHash & destination) const { return destination == m_LocalDest->GetIdentHash(); }
|
|
||||||
|
|
||||||
std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDest; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
|
||||||
void TryResolving();
|
|
||||||
const std::string m_Name;
|
|
||||||
UDPSession * m_Session;
|
|
||||||
const std::string m_RemoteDest;
|
|
||||||
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
|
|
||||||
const boost::asio::ip::udp::endpoint m_LocalEndpoint;
|
|
||||||
i2p::data::IdentHash * m_RemoteIdent;
|
|
||||||
std::thread * m_ResolveThread;
|
|
||||||
uint16_t LocalPort;
|
|
||||||
uint16_t RemotePort;
|
|
||||||
bool m_cancel_resolve;
|
|
||||||
};
|
|
||||||
|
|
||||||
class I2PServerTunnel: public I2PService
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
I2PServerTunnel (const std::string& name, const std::string& address, int port,
|
|
||||||
std::shared_ptr<ClientDestination> localDestination, int inport = 0, bool gzip = true);
|
|
||||||
|
|
||||||
void Start ();
|
|
||||||
void Stop ();
|
|
||||||
|
|
||||||
void SetAccessList (const std::set<i2p::data::IdentHash>& accessList);
|
|
||||||
|
|
||||||
const std::string& GetAddress() const { return m_Address; }
|
|
||||||
int GetPort () const { return m_Port; };
|
|
||||||
uint16_t GetLocalPort () const { return m_PortDestination->GetLocalPort (); };
|
|
||||||
const boost::asio::ip::tcp::endpoint& GetEndpoint () const { return m_Endpoint; }
|
|
||||||
|
|
||||||
const char* GetName() { return m_Name.c_str (); }
|
|
||||||
|
|
||||||
void SetMaxConnsPerMinute(const uint32_t conns) { m_PortDestination->SetMaxConnsPerMinute(conns); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::resolver> resolver);
|
|
||||||
|
|
||||||
void Accept ();
|
|
||||||
void HandleAccept (std::shared_ptr<i2p::stream::Stream> stream);
|
|
||||||
virtual void CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::string m_Name, m_Address;
|
|
||||||
int m_Port;
|
|
||||||
boost::asio::ip::tcp::endpoint m_Endpoint;
|
|
||||||
std::shared_ptr<i2p::stream::StreamingDestination> m_PortDestination;
|
|
||||||
std::set<i2p::data::IdentHash> m_AccessList;
|
|
||||||
bool m_IsAccessList;
|
|
||||||
};
|
|
||||||
|
|
||||||
class I2PServerTunnelHTTP: public I2PServerTunnel
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
I2PServerTunnelHTTP (const std::string& name, const std::string& address, int port,
|
|
||||||
std::shared_ptr<ClientDestination> localDestination, const std::string& host,
|
|
||||||
int inport = 0, bool gzip = true);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::string m_Host;
|
|
||||||
};
|
|
||||||
|
|
||||||
class I2PServerTunnelIRC: public I2PServerTunnel
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
I2PServerTunnelIRC (const std::string& name, const std::string& address, int port,
|
|
||||||
std::shared_ptr<ClientDestination> localDestination, const std::string& webircpass,
|
|
||||||
int inport = 0, bool gzip = true);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::string m_WebircPass;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
604
Identity.cpp
604
Identity.cpp
@@ -1,604 +0,0 @@
|
|||||||
#include <time.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "Crypto.h"
|
|
||||||
#include "I2PEndian.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace data
|
|
||||||
{
|
|
||||||
Identity& Identity::operator=(const Keys& keys)
|
|
||||||
{
|
|
||||||
// copy public and signing keys together
|
|
||||||
memcpy (publicKey, keys.publicKey, sizeof (publicKey) + sizeof (signingKey));
|
|
||||||
memset (certificate, 0, sizeof (certificate));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Identity::FromBuffer (const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
if ( len < DEFAULT_IDENTITY_SIZE ) {
|
|
||||||
// buffer too small, don't overflow
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
memcpy (publicKey, buf, DEFAULT_IDENTITY_SIZE);
|
|
||||||
return DEFAULT_IDENTITY_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentHash Identity::Hash () const
|
|
||||||
{
|
|
||||||
IdentHash hash;
|
|
||||||
SHA256(publicKey, DEFAULT_IDENTITY_SIZE, hash);
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentityEx::IdentityEx ():
|
|
||||||
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type):
|
|
||||||
m_IsVerifierCreated (false)
|
|
||||||
{
|
|
||||||
memcpy (m_StandardIdentity.publicKey, publicKey, sizeof (m_StandardIdentity.publicKey));
|
|
||||||
if (type != SIGNING_KEY_TYPE_DSA_SHA1)
|
|
||||||
{
|
|
||||||
size_t excessLen = 0;
|
|
||||||
uint8_t * excessBuf = nullptr;
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
|
|
||||||
{
|
|
||||||
size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64
|
|
||||||
RAND_bytes (m_StandardIdentity.signingKey, padding);
|
|
||||||
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP256_KEY_LENGTH);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
|
|
||||||
{
|
|
||||||
size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96
|
|
||||||
RAND_bytes (m_StandardIdentity.signingKey, padding);
|
|
||||||
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP384_KEY_LENGTH);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
|
|
||||||
{
|
|
||||||
memcpy (m_StandardIdentity.signingKey, signingKey, 128);
|
|
||||||
excessLen = i2p::crypto::ECDSAP521_KEY_LENGTH - 128; // 4 = 132 - 128
|
|
||||||
excessBuf = new uint8_t[excessLen];
|
|
||||||
memcpy (excessBuf, signingKey + 128, excessLen);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
|
|
||||||
{
|
|
||||||
memcpy (m_StandardIdentity.signingKey, signingKey, 128);
|
|
||||||
excessLen = i2p::crypto::RSASHA2562048_KEY_LENGTH - 128; // 128 = 256 - 128
|
|
||||||
excessBuf = new uint8_t[excessLen];
|
|
||||||
memcpy (excessBuf, signingKey + 128, excessLen);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
|
|
||||||
{
|
|
||||||
memcpy (m_StandardIdentity.signingKey, signingKey, 128);
|
|
||||||
excessLen = i2p::crypto::RSASHA3843072_KEY_LENGTH - 128; // 256 = 384 - 128
|
|
||||||
excessBuf = new uint8_t[excessLen];
|
|
||||||
memcpy (excessBuf, signingKey + 128, excessLen);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
|
|
||||||
{
|
|
||||||
memcpy (m_StandardIdentity.signingKey, signingKey, 128);
|
|
||||||
excessLen = i2p::crypto::RSASHA5124096_KEY_LENGTH - 128; // 384 = 512 - 128
|
|
||||||
excessBuf = new uint8_t[excessLen];
|
|
||||||
memcpy (excessBuf, signingKey + 128, excessLen);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
|
|
||||||
{
|
|
||||||
size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32
|
|
||||||
RAND_bytes (m_StandardIdentity.signingKey, padding);
|
|
||||||
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
LogPrint (eLogError, "Identity: Signing key type ", (int)type, " is not supported");
|
|
||||||
}
|
|
||||||
m_ExtendedLen = 4 + excessLen; // 4 bytes extra + excess length
|
|
||||||
// fill certificate
|
|
||||||
m_StandardIdentity.certificate[0] = CERTIFICATE_TYPE_KEY;
|
|
||||||
htobe16buf (m_StandardIdentity.certificate + 1, m_ExtendedLen);
|
|
||||||
// fill extended buffer
|
|
||||||
m_ExtendedBuffer = new uint8_t[m_ExtendedLen];
|
|
||||||
htobe16buf (m_ExtendedBuffer, type);
|
|
||||||
htobe16buf (m_ExtendedBuffer + 2, CRYPTO_KEY_TYPE_ELGAMAL);
|
|
||||||
if (excessLen && excessBuf)
|
|
||||||
{
|
|
||||||
memcpy (m_ExtendedBuffer + 4, excessBuf, excessLen);
|
|
||||||
delete[] excessBuf;
|
|
||||||
}
|
|
||||||
// calculate ident hash
|
|
||||||
uint8_t * buf = new uint8_t[GetFullLen ()];
|
|
||||||
ToBuffer (buf, GetFullLen ());
|
|
||||||
SHA256(buf, GetFullLen (), m_IdentHash);
|
|
||||||
delete[] buf;
|
|
||||||
}
|
|
||||||
else // DSA-SHA1
|
|
||||||
{
|
|
||||||
memcpy (m_StandardIdentity.signingKey, signingKey, sizeof (m_StandardIdentity.signingKey));
|
|
||||||
memset (m_StandardIdentity.certificate, 0, sizeof (m_StandardIdentity.certificate));
|
|
||||||
m_IdentHash = m_StandardIdentity.Hash ();
|
|
||||||
m_ExtendedLen = 0;
|
|
||||||
m_ExtendedBuffer = nullptr;
|
|
||||||
}
|
|
||||||
CreateVerifier ();
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentityEx::IdentityEx (const uint8_t * buf, size_t len):
|
|
||||||
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
|
||||||
{
|
|
||||||
FromBuffer (buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentityEx::IdentityEx (const IdentityEx& other):
|
|
||||||
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
|
||||||
{
|
|
||||||
*this = other;
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentityEx::IdentityEx (const Identity& standard):
|
|
||||||
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
|
||||||
{
|
|
||||||
*this = standard;
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentityEx::~IdentityEx ()
|
|
||||||
{
|
|
||||||
delete[] m_ExtendedBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentityEx& IdentityEx::operator=(const IdentityEx& other)
|
|
||||||
{
|
|
||||||
memcpy (&m_StandardIdentity, &other.m_StandardIdentity, DEFAULT_IDENTITY_SIZE);
|
|
||||||
m_IdentHash = other.m_IdentHash;
|
|
||||||
|
|
||||||
delete[] m_ExtendedBuffer;
|
|
||||||
m_ExtendedLen = other.m_ExtendedLen;
|
|
||||||
if (m_ExtendedLen > 0)
|
|
||||||
{
|
|
||||||
m_ExtendedBuffer = new uint8_t[m_ExtendedLen];
|
|
||||||
memcpy (m_ExtendedBuffer, other.m_ExtendedBuffer, m_ExtendedLen);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_ExtendedBuffer = nullptr;
|
|
||||||
|
|
||||||
m_Verifier = nullptr;
|
|
||||||
m_IsVerifierCreated = false;
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentityEx& IdentityEx::operator=(const Identity& standard)
|
|
||||||
{
|
|
||||||
m_StandardIdentity = standard;
|
|
||||||
m_IdentHash = m_StandardIdentity.Hash ();
|
|
||||||
|
|
||||||
delete[] m_ExtendedBuffer;
|
|
||||||
m_ExtendedBuffer = nullptr;
|
|
||||||
m_ExtendedLen = 0;
|
|
||||||
|
|
||||||
m_Verifier = nullptr;
|
|
||||||
m_IsVerifierCreated = false;
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t IdentityEx::FromBuffer (const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
if (len < DEFAULT_IDENTITY_SIZE)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Identity: buffer length ", len, " is too small");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
memcpy (&m_StandardIdentity, buf, DEFAULT_IDENTITY_SIZE);
|
|
||||||
|
|
||||||
if(m_ExtendedBuffer) delete[] m_ExtendedBuffer;
|
|
||||||
m_ExtendedBuffer = nullptr;
|
|
||||||
|
|
||||||
m_ExtendedLen = bufbe16toh (m_StandardIdentity.certificate + 1);
|
|
||||||
if (m_ExtendedLen)
|
|
||||||
{
|
|
||||||
if (m_ExtendedLen + DEFAULT_IDENTITY_SIZE <= len)
|
|
||||||
{
|
|
||||||
m_ExtendedBuffer = new uint8_t[m_ExtendedLen];
|
|
||||||
memcpy (m_ExtendedBuffer, buf + DEFAULT_IDENTITY_SIZE, m_ExtendedLen);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Identity: Certificate length ", m_ExtendedLen, " exceeds buffer length ", len - DEFAULT_IDENTITY_SIZE);
|
|
||||||
m_ExtendedLen = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_ExtendedLen = 0;
|
|
||||||
m_ExtendedBuffer = nullptr;
|
|
||||||
}
|
|
||||||
SHA256(buf, GetFullLen (), m_IdentHash);
|
|
||||||
|
|
||||||
m_Verifier = nullptr;
|
|
||||||
|
|
||||||
return GetFullLen ();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t IdentityEx::ToBuffer (uint8_t * buf, size_t len) const
|
|
||||||
{
|
|
||||||
const size_t fullLen = GetFullLen();
|
|
||||||
if (fullLen > len) return 0; // buffer is too small and may overflow somewhere else
|
|
||||||
memcpy (buf, &m_StandardIdentity, DEFAULT_IDENTITY_SIZE);
|
|
||||||
if (m_ExtendedLen > 0 && m_ExtendedBuffer)
|
|
||||||
memcpy (buf + DEFAULT_IDENTITY_SIZE, m_ExtendedBuffer, m_ExtendedLen);
|
|
||||||
return fullLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t IdentityEx::FromBase64(const std::string& s)
|
|
||||||
{
|
|
||||||
const size_t slen = s.length();
|
|
||||||
std::vector<uint8_t> buf(slen); // binary data can't exceed base64
|
|
||||||
const size_t len = Base64ToByteStream (s.c_str(), slen, buf.data(), slen);
|
|
||||||
return FromBuffer (buf.data(), len);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string IdentityEx::ToBase64 () const
|
|
||||||
{
|
|
||||||
const size_t bufLen = GetFullLen();
|
|
||||||
const size_t strLen = Base64EncodingBufferSize(bufLen);
|
|
||||||
std::vector<uint8_t> buf(bufLen);
|
|
||||||
std::vector<char> str(strLen);
|
|
||||||
size_t l = ToBuffer (buf.data(), bufLen);
|
|
||||||
size_t l1 = i2p::data::ByteStreamToBase64 (buf.data(), l, str.data(), strLen);
|
|
||||||
return std::string (str.data(), l1);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t IdentityEx::GetSigningPublicKeyLen () const
|
|
||||||
{
|
|
||||||
if (!m_Verifier) CreateVerifier ();
|
|
||||||
if (m_Verifier)
|
|
||||||
return m_Verifier->GetPublicKeyLen ();
|
|
||||||
return 128;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t IdentityEx::GetSigningPrivateKeyLen () const
|
|
||||||
{
|
|
||||||
if (!m_Verifier) CreateVerifier ();
|
|
||||||
if (m_Verifier)
|
|
||||||
return m_Verifier->GetPrivateKeyLen ();
|
|
||||||
return GetSignatureLen ()/2;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t IdentityEx::GetSignatureLen () const
|
|
||||||
{
|
|
||||||
if (!m_Verifier) CreateVerifier ();
|
|
||||||
if (m_Verifier)
|
|
||||||
return m_Verifier->GetSignatureLen ();
|
|
||||||
return i2p::crypto::DSA_SIGNATURE_LENGTH;
|
|
||||||
}
|
|
||||||
bool IdentityEx::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
|
|
||||||
{
|
|
||||||
if (!m_Verifier) CreateVerifier ();
|
|
||||||
if (m_Verifier)
|
|
||||||
return m_Verifier->Verify (buf, len, signature);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SigningKeyType IdentityEx::GetSigningKeyType () const
|
|
||||||
{
|
|
||||||
if (m_StandardIdentity.certificate[0] == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer)
|
|
||||||
return bufbe16toh (m_ExtendedBuffer); // signing key
|
|
||||||
return SIGNING_KEY_TYPE_DSA_SHA1;
|
|
||||||
}
|
|
||||||
|
|
||||||
CryptoKeyType IdentityEx::GetCryptoKeyType () const
|
|
||||||
{
|
|
||||||
if (m_StandardIdentity.certificate[0] == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer)
|
|
||||||
return bufbe16toh (m_ExtendedBuffer + 2); // crypto key
|
|
||||||
return CRYPTO_KEY_TYPE_ELGAMAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IdentityEx::CreateVerifier () const
|
|
||||||
{
|
|
||||||
if (m_Verifier) return; // don't create again
|
|
||||||
auto keyType = GetSigningKeyType ();
|
|
||||||
switch (keyType)
|
|
||||||
{
|
|
||||||
case SIGNING_KEY_TYPE_DSA_SHA1:
|
|
||||||
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
|
|
||||||
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
|
|
||||||
UpdateVerifier (new i2p::crypto::ECDSAP384Verifier (m_StandardIdentity.signingKey + padding));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
|
|
||||||
{
|
|
||||||
uint8_t signingKey[i2p::crypto::ECDSAP521_KEY_LENGTH];
|
|
||||||
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
|
|
||||||
size_t excessLen = i2p::crypto::ECDSAP521_KEY_LENGTH - 128; // 4 = 132- 128
|
|
||||||
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
|
|
||||||
UpdateVerifier (new i2p::crypto::ECDSAP521Verifier (signingKey));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
|
|
||||||
{
|
|
||||||
uint8_t signingKey[i2p::crypto::RSASHA2562048_KEY_LENGTH];
|
|
||||||
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
|
|
||||||
size_t excessLen = i2p::crypto::RSASHA2562048_KEY_LENGTH - 128; // 128 = 256- 128
|
|
||||||
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
|
|
||||||
UpdateVerifier (new i2p::crypto:: RSASHA2562048Verifier (signingKey));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
|
|
||||||
{
|
|
||||||
uint8_t signingKey[i2p::crypto::RSASHA3843072_KEY_LENGTH];
|
|
||||||
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
|
|
||||||
size_t excessLen = i2p::crypto::RSASHA3843072_KEY_LENGTH - 128; // 256 = 384- 128
|
|
||||||
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
|
|
||||||
UpdateVerifier (new i2p::crypto:: RSASHA3843072Verifier (signingKey));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
|
|
||||||
{
|
|
||||||
uint8_t signingKey[i2p::crypto::RSASHA5124096_KEY_LENGTH];
|
|
||||||
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
|
|
||||||
size_t excessLen = i2p::crypto::RSASHA5124096_KEY_LENGTH - 128; // 384 = 512- 128
|
|
||||||
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
|
|
||||||
UpdateVerifier (new i2p::crypto:: RSASHA5124096Verifier (signingKey));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
|
|
||||||
{
|
|
||||||
size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32
|
|
||||||
UpdateVerifier (new i2p::crypto::EDDSA25519Verifier (m_StandardIdentity.signingKey + padding));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
LogPrint (eLogError, "Identity: Signing key type ", (int)keyType, " is not supported");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void IdentityEx::UpdateVerifier (i2p::crypto::Verifier * verifier) const
|
|
||||||
{
|
|
||||||
if (!m_Verifier)
|
|
||||||
{
|
|
||||||
auto created = m_IsVerifierCreated.exchange (true);
|
|
||||||
if (!created)
|
|
||||||
m_Verifier.reset (verifier);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
delete verifier;
|
|
||||||
int count = 0;
|
|
||||||
while (!m_Verifier && count < 500) // 5 seconds
|
|
||||||
{
|
|
||||||
std::this_thread::sleep_for (std::chrono::milliseconds(10));
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
if (!m_Verifier)
|
|
||||||
LogPrint (eLogError, "Identity: couldn't get verifier in 5 seconds");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
delete verifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IdentityEx::DropVerifier () const
|
|
||||||
{
|
|
||||||
// TODO: potential race condition with Verify
|
|
||||||
m_IsVerifierCreated = false;
|
|
||||||
m_Verifier = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
PrivateKeys& PrivateKeys::operator=(const Keys& keys)
|
|
||||||
{
|
|
||||||
m_Public = std::make_shared<IdentityEx>(Identity (keys));
|
|
||||||
memcpy (m_PrivateKey, keys.privateKey, 256); // 256
|
|
||||||
memcpy (m_SigningPrivateKey, keys.signingPrivateKey, m_Public->GetSigningPrivateKeyLen ());
|
|
||||||
m_Signer = nullptr;
|
|
||||||
CreateSigner ();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
PrivateKeys& PrivateKeys::operator=(const PrivateKeys& other)
|
|
||||||
{
|
|
||||||
m_Public = std::make_shared<IdentityEx>(*other.m_Public);
|
|
||||||
memcpy (m_PrivateKey, other.m_PrivateKey, 256); // 256
|
|
||||||
memcpy (m_SigningPrivateKey, other.m_SigningPrivateKey, m_Public->GetSigningPrivateKeyLen ());
|
|
||||||
m_Signer = nullptr;
|
|
||||||
CreateSigner ();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t PrivateKeys::FromBuffer (const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
m_Public = std::make_shared<IdentityEx>(buf, len);
|
|
||||||
size_t ret = m_Public->GetFullLen ();
|
|
||||||
memcpy (m_PrivateKey, buf + ret, 256); // private key always 256
|
|
||||||
ret += 256;
|
|
||||||
size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
|
|
||||||
if(signingPrivateKeySize + ret > len) return 0; // overflow
|
|
||||||
memcpy (m_SigningPrivateKey, buf + ret, signingPrivateKeySize);
|
|
||||||
ret += signingPrivateKeySize;
|
|
||||||
m_Signer = nullptr;
|
|
||||||
CreateSigner ();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t PrivateKeys::ToBuffer (uint8_t * buf, size_t len) const
|
|
||||||
{
|
|
||||||
size_t ret = m_Public->ToBuffer (buf, len);
|
|
||||||
memcpy (buf + ret, m_PrivateKey, 256); // private key always 256
|
|
||||||
ret += 256;
|
|
||||||
size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
|
|
||||||
if(ret + signingPrivateKeySize > len) return 0; // overflow
|
|
||||||
memcpy (buf + ret, m_SigningPrivateKey, signingPrivateKeySize);
|
|
||||||
ret += signingPrivateKeySize;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t PrivateKeys::FromBase64(const std::string& s)
|
|
||||||
{
|
|
||||||
uint8_t * buf = new uint8_t[s.length ()];
|
|
||||||
size_t l = i2p::data::Base64ToByteStream (s.c_str (), s.length (), buf, s.length ());
|
|
||||||
size_t ret = FromBuffer (buf, l);
|
|
||||||
delete[] buf;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string PrivateKeys::ToBase64 () const
|
|
||||||
{
|
|
||||||
uint8_t * buf = new uint8_t[GetFullLen ()];
|
|
||||||
char * str = new char[GetFullLen ()*2];
|
|
||||||
size_t l = ToBuffer (buf, GetFullLen ());
|
|
||||||
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, str, GetFullLen ()*2);
|
|
||||||
str[l1] = 0;
|
|
||||||
delete[] buf;
|
|
||||||
std::string ret(str);
|
|
||||||
delete[] str;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrivateKeys::Sign (const uint8_t * buf, int len, uint8_t * signature) const
|
|
||||||
{
|
|
||||||
if (!m_Signer)
|
|
||||||
CreateSigner();
|
|
||||||
m_Signer->Sign (buf, len, signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrivateKeys::CreateSigner () const
|
|
||||||
{
|
|
||||||
if (m_Signer) return;
|
|
||||||
switch (m_Public->GetSigningKeyType ())
|
|
||||||
{
|
|
||||||
case SIGNING_KEY_TYPE_DSA_SHA1:
|
|
||||||
m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey, m_Public->GetStandardIdentity ().signingKey));
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
|
|
||||||
m_Signer.reset (new i2p::crypto::ECDSAP256Signer (m_SigningPrivateKey));
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
|
|
||||||
m_Signer.reset (new i2p::crypto::ECDSAP384Signer (m_SigningPrivateKey));
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
|
|
||||||
m_Signer.reset (new i2p::crypto::ECDSAP521Signer (m_SigningPrivateKey));
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
|
|
||||||
m_Signer.reset (new i2p::crypto::RSASHA2562048Signer (m_SigningPrivateKey));
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
|
|
||||||
m_Signer.reset (new i2p::crypto::RSASHA3843072Signer (m_SigningPrivateKey));
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
|
|
||||||
m_Signer.reset (new i2p::crypto::RSASHA5124096Signer (m_SigningPrivateKey));
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
|
|
||||||
m_Signer.reset (new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LogPrint (eLogError, "Identity: Signing key type ", (int)m_Public->GetSigningKeyType (), " is not supported");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PrivateKeys PrivateKeys::CreateRandomKeys (SigningKeyType type)
|
|
||||||
{
|
|
||||||
if (type != SIGNING_KEY_TYPE_DSA_SHA1)
|
|
||||||
{
|
|
||||||
PrivateKeys keys;
|
|
||||||
// signature
|
|
||||||
uint8_t signingPublicKey[512]; // signing public key is 512 bytes max
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
|
|
||||||
i2p::crypto::CreateECDSAP256RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
|
|
||||||
i2p::crypto::CreateECDSAP384RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
|
|
||||||
i2p::crypto::CreateECDSAP521RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
|
|
||||||
i2p::crypto::CreateRSARandomKeys (i2p::crypto::RSASHA2562048_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
|
|
||||||
i2p::crypto::CreateRSARandomKeys (i2p::crypto::RSASHA3843072_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
|
|
||||||
i2p::crypto::CreateRSARandomKeys (i2p::crypto::RSASHA5124096_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
|
|
||||||
i2p::crypto::CreateEDDSA25519RandomKeys (keys.m_SigningPrivateKey, signingPublicKey);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LogPrint (eLogError, "Identity: Signing key type ", (int)type, " is not supported. Create DSA-SHA1");
|
|
||||||
return PrivateKeys (i2p::data::CreateRandomKeys ()); // DSA-SHA1
|
|
||||||
}
|
|
||||||
// encryption
|
|
||||||
uint8_t publicKey[256];
|
|
||||||
i2p::crypto::GenerateElGamalKeyPair (keys.m_PrivateKey, publicKey);
|
|
||||||
// identity
|
|
||||||
keys.m_Public = std::make_shared<IdentityEx> (publicKey, signingPublicKey, type);
|
|
||||||
|
|
||||||
keys.CreateSigner ();
|
|
||||||
return keys;
|
|
||||||
}
|
|
||||||
return PrivateKeys (i2p::data::CreateRandomKeys ()); // DSA-SHA1
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys CreateRandomKeys ()
|
|
||||||
{
|
|
||||||
Keys keys;
|
|
||||||
// encryption
|
|
||||||
i2p::crypto::GenerateElGamalKeyPair(keys.privateKey, keys.publicKey);
|
|
||||||
// signing
|
|
||||||
i2p::crypto::CreateDSARandomKeys (keys.signingPrivateKey, keys.signingKey);
|
|
||||||
return keys;
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentHash CreateRoutingKey (const IdentHash& ident)
|
|
||||||
{
|
|
||||||
uint8_t buf[41]; // ident + yyyymmdd
|
|
||||||
memcpy (buf, (const uint8_t *)ident, 32);
|
|
||||||
time_t t = time (nullptr);
|
|
||||||
struct tm tm;
|
|
||||||
#ifdef _WIN32
|
|
||||||
gmtime_s(&tm, &t);
|
|
||||||
sprintf_s((char *)(buf + 32), 9, "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
|
|
||||||
#else
|
|
||||||
gmtime_r(&t, &tm);
|
|
||||||
sprintf((char *)(buf + 32), "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
|
|
||||||
#endif
|
|
||||||
IdentHash key;
|
|
||||||
SHA256(buf, 40, key);
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
XORMetric operator^(const IdentHash& key1, const IdentHash& key2)
|
|
||||||
{
|
|
||||||
XORMetric m;
|
|
||||||
const uint64_t * hash1 = key1.GetLL (), * hash2 = key2.GetLL ();
|
|
||||||
m.metric_ll[0] = hash1[0] ^ hash2[0];
|
|
||||||
m.metric_ll[1] = hash1[1] ^ hash2[1];
|
|
||||||
m.metric_ll[2] = hash1[2] ^ hash2[2];
|
|
||||||
m.metric_ll[3] = hash1[3] ^ hash2[3];
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
194
Identity.h
194
Identity.h
@@ -1,194 +0,0 @@
|
|||||||
#ifndef IDENTITY_H__
|
|
||||||
#define IDENTITY_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <string>
|
|
||||||
#include <memory>
|
|
||||||
#include <atomic>
|
|
||||||
#include "Base.h"
|
|
||||||
#include "Signature.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace data
|
|
||||||
{
|
|
||||||
typedef Tag<32> IdentHash;
|
|
||||||
inline std::string GetIdentHashAbbreviation (const IdentHash& ident)
|
|
||||||
{
|
|
||||||
return ident.ToBase64 ().substr (0, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Keys
|
|
||||||
{
|
|
||||||
uint8_t privateKey[256];
|
|
||||||
uint8_t signingPrivateKey[20];
|
|
||||||
uint8_t publicKey[256];
|
|
||||||
uint8_t signingKey[128];
|
|
||||||
};
|
|
||||||
|
|
||||||
const uint8_t CERTIFICATE_TYPE_NULL = 0;
|
|
||||||
const uint8_t CERTIFICATE_TYPE_HASHCASH = 1;
|
|
||||||
const uint8_t CERTIFICATE_TYPE_HIDDEN = 2;
|
|
||||||
const uint8_t CERTIFICATE_TYPE_SIGNED = 3;
|
|
||||||
const uint8_t CERTIFICATE_TYPE_MULTIPLE = 4;
|
|
||||||
const uint8_t CERTIFICATE_TYPE_KEY = 5;
|
|
||||||
|
|
||||||
struct Identity
|
|
||||||
{
|
|
||||||
uint8_t publicKey[256];
|
|
||||||
uint8_t signingKey[128];
|
|
||||||
uint8_t certificate[3]; // byte 1 - type, bytes 2-3 - length
|
|
||||||
|
|
||||||
Identity () = default;
|
|
||||||
Identity (const Keys& keys) { *this = keys; };
|
|
||||||
Identity& operator=(const Keys& keys);
|
|
||||||
size_t FromBuffer (const uint8_t * buf, size_t len);
|
|
||||||
IdentHash Hash () const;
|
|
||||||
};
|
|
||||||
|
|
||||||
Keys CreateRandomKeys ();
|
|
||||||
|
|
||||||
const size_t DEFAULT_IDENTITY_SIZE = sizeof (Identity); // 387 bytes
|
|
||||||
|
|
||||||
const uint16_t CRYPTO_KEY_TYPE_ELGAMAL = 0;
|
|
||||||
const uint16_t SIGNING_KEY_TYPE_DSA_SHA1 = 0;
|
|
||||||
const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA256_P256 = 1;
|
|
||||||
const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA384_P384 = 2;
|
|
||||||
const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA512_P521 = 3;
|
|
||||||
const uint16_t SIGNING_KEY_TYPE_RSA_SHA256_2048 = 4;
|
|
||||||
const uint16_t SIGNING_KEY_TYPE_RSA_SHA384_3072 = 5;
|
|
||||||
const uint16_t SIGNING_KEY_TYPE_RSA_SHA512_4096 = 6;
|
|
||||||
const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519 = 7;
|
|
||||||
typedef uint16_t SigningKeyType;
|
|
||||||
typedef uint16_t CryptoKeyType;
|
|
||||||
|
|
||||||
class IdentityEx
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
IdentityEx ();
|
|
||||||
IdentityEx (const uint8_t * publicKey, const uint8_t * signingKey,
|
|
||||||
SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1);
|
|
||||||
IdentityEx (const uint8_t * buf, size_t len);
|
|
||||||
IdentityEx (const IdentityEx& other);
|
|
||||||
IdentityEx (const Identity& standard);
|
|
||||||
~IdentityEx ();
|
|
||||||
IdentityEx& operator=(const IdentityEx& other);
|
|
||||||
IdentityEx& operator=(const Identity& standard);
|
|
||||||
|
|
||||||
size_t FromBuffer (const uint8_t * buf, size_t len);
|
|
||||||
size_t ToBuffer (uint8_t * buf, size_t len) const;
|
|
||||||
size_t FromBase64(const std::string& s);
|
|
||||||
std::string ToBase64 () const;
|
|
||||||
const Identity& GetStandardIdentity () const { return m_StandardIdentity; };
|
|
||||||
const IdentHash& GetIdentHash () const { return m_IdentHash; };
|
|
||||||
const uint8_t * GetEncryptionPublicKey () const { return m_StandardIdentity.publicKey; };
|
|
||||||
size_t GetFullLen () const { return m_ExtendedLen + DEFAULT_IDENTITY_SIZE; };
|
|
||||||
size_t GetSigningPublicKeyLen () const;
|
|
||||||
size_t GetSigningPrivateKeyLen () const;
|
|
||||||
size_t GetSignatureLen () const;
|
|
||||||
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const;
|
|
||||||
SigningKeyType GetSigningKeyType () const;
|
|
||||||
CryptoKeyType GetCryptoKeyType () const;
|
|
||||||
void DropVerifier () const; // to save memory
|
|
||||||
|
|
||||||
bool operator == (const IdentityEx & other) const { return GetIdentHash() == other.GetIdentHash(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void CreateVerifier () const;
|
|
||||||
void UpdateVerifier (i2p::crypto::Verifier * verifier) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
Identity m_StandardIdentity;
|
|
||||||
IdentHash m_IdentHash;
|
|
||||||
mutable std::unique_ptr<i2p::crypto::Verifier> m_Verifier;
|
|
||||||
mutable std::atomic_bool m_IsVerifierCreated; // make sure we don't create twice
|
|
||||||
size_t m_ExtendedLen;
|
|
||||||
uint8_t * m_ExtendedBuffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
class PrivateKeys // for eepsites
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
PrivateKeys () = default;
|
|
||||||
PrivateKeys (const PrivateKeys& other) { *this = other; };
|
|
||||||
PrivateKeys (const Keys& keys) { *this = keys; };
|
|
||||||
PrivateKeys& operator=(const Keys& keys);
|
|
||||||
PrivateKeys& operator=(const PrivateKeys& other);
|
|
||||||
~PrivateKeys () = default;
|
|
||||||
|
|
||||||
std::shared_ptr<const IdentityEx> GetPublic () const { return m_Public; };
|
|
||||||
const uint8_t * GetPrivateKey () const { return m_PrivateKey; };
|
|
||||||
const uint8_t * GetSigningPrivateKey () const { return m_SigningPrivateKey; };
|
|
||||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const;
|
|
||||||
|
|
||||||
size_t GetFullLen () const { return m_Public->GetFullLen () + 256 + m_Public->GetSigningPrivateKeyLen (); };
|
|
||||||
size_t FromBuffer (const uint8_t * buf, size_t len);
|
|
||||||
size_t ToBuffer (uint8_t * buf, size_t len) const;
|
|
||||||
|
|
||||||
size_t FromBase64(const std::string& s);
|
|
||||||
std::string ToBase64 () const;
|
|
||||||
|
|
||||||
static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void CreateSigner () const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::shared_ptr<IdentityEx> m_Public;
|
|
||||||
uint8_t m_PrivateKey[256];
|
|
||||||
uint8_t m_SigningPrivateKey[1024]; // assume private key doesn't exceed 1024 bytes
|
|
||||||
mutable std::unique_ptr<i2p::crypto::Signer> m_Signer;
|
|
||||||
};
|
|
||||||
|
|
||||||
// kademlia
|
|
||||||
struct XORMetric
|
|
||||||
{
|
|
||||||
union
|
|
||||||
{
|
|
||||||
uint8_t metric[32];
|
|
||||||
uint64_t metric_ll[4];
|
|
||||||
};
|
|
||||||
|
|
||||||
void SetMin () { memset (metric, 0, 32); };
|
|
||||||
void SetMax () { memset (metric, 0xFF, 32); };
|
|
||||||
bool operator< (const XORMetric& other) const { return memcmp (metric, other.metric, 32) < 0; };
|
|
||||||
};
|
|
||||||
|
|
||||||
IdentHash CreateRoutingKey (const IdentHash& ident);
|
|
||||||
XORMetric operator^(const IdentHash& key1, const IdentHash& key2);
|
|
||||||
|
|
||||||
// destination for delivery instuctions
|
|
||||||
class RoutingDestination
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
RoutingDestination () {};
|
|
||||||
virtual ~RoutingDestination () {};
|
|
||||||
|
|
||||||
virtual const IdentHash& GetIdentHash () const = 0;
|
|
||||||
virtual const uint8_t * GetEncryptionPublicKey () const = 0;
|
|
||||||
virtual bool IsDestination () const = 0; // for garlic
|
|
||||||
};
|
|
||||||
|
|
||||||
class LocalDestination
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
virtual ~LocalDestination() {};
|
|
||||||
virtual const uint8_t * GetEncryptionPrivateKey () const = 0;
|
|
||||||
virtual std::shared_ptr<const IdentityEx> GetIdentity () const = 0;
|
|
||||||
|
|
||||||
const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); };
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2013-2015, The PurpleI2P Project
|
Copyright (c) 2013-2020, The PurpleI2P Project
|
||||||
|
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
|
|||||||
262
LeaseSet.cpp
262
LeaseSet.cpp
@@ -1,262 +0,0 @@
|
|||||||
#include <string.h>
|
|
||||||
#include "I2PEndian.h"
|
|
||||||
#include "Crypto.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "Timestamp.h"
|
|
||||||
#include "NetDb.h"
|
|
||||||
#include "Tunnel.h"
|
|
||||||
#include "LeaseSet.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace data
|
|
||||||
{
|
|
||||||
|
|
||||||
LeaseSet::LeaseSet (const uint8_t * buf, size_t len, bool storeLeases):
|
|
||||||
m_IsValid (true), m_StoreLeases (storeLeases), m_ExpirationTime (0)
|
|
||||||
{
|
|
||||||
m_Buffer = new uint8_t[len];
|
|
||||||
memcpy (m_Buffer, buf, len);
|
|
||||||
m_BufferLen = len;
|
|
||||||
ReadFromBuffer ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void LeaseSet::Update (const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
if (len > m_BufferLen)
|
|
||||||
{
|
|
||||||
auto oldBuffer = m_Buffer;
|
|
||||||
m_Buffer = new uint8_t[len];
|
|
||||||
delete[] oldBuffer;
|
|
||||||
}
|
|
||||||
memcpy (m_Buffer, buf, len);
|
|
||||||
m_BufferLen = len;
|
|
||||||
ReadFromBuffer (false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LeaseSet::PopulateLeases ()
|
|
||||||
{
|
|
||||||
m_StoreLeases = true;
|
|
||||||
ReadFromBuffer (false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LeaseSet::ReadFromBuffer (bool readIdentity)
|
|
||||||
{
|
|
||||||
if (readIdentity || !m_Identity)
|
|
||||||
m_Identity = std::make_shared<IdentityEx>(m_Buffer, m_BufferLen);
|
|
||||||
size_t size = m_Identity->GetFullLen ();
|
|
||||||
if (size > m_BufferLen)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "LeaseSet: identity length ", size, " exceeds buffer size ", m_BufferLen);
|
|
||||||
m_IsValid = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
memcpy (m_EncryptionKey, m_Buffer + size, 256);
|
|
||||||
size += 256; // encryption key
|
|
||||||
size += m_Identity->GetSigningPublicKeyLen (); // unused signing key
|
|
||||||
uint8_t num = m_Buffer[size];
|
|
||||||
size++; // num
|
|
||||||
LogPrint (eLogDebug, "LeaseSet: read num=", (int)num);
|
|
||||||
if (!num || num > MAX_NUM_LEASES)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "LeaseSet: incorrect number of leases", (int)num);
|
|
||||||
m_IsValid = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset existing leases
|
|
||||||
if (m_StoreLeases)
|
|
||||||
for (auto& it: m_Leases)
|
|
||||||
it->isUpdated = false;
|
|
||||||
else
|
|
||||||
m_Leases.clear ();
|
|
||||||
|
|
||||||
// process leases
|
|
||||||
m_ExpirationTime = 0;
|
|
||||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
const uint8_t * leases = m_Buffer + size;
|
|
||||||
for (int i = 0; i < num; i++)
|
|
||||||
{
|
|
||||||
Lease lease;
|
|
||||||
lease.tunnelGateway = leases;
|
|
||||||
leases += 32; // gateway
|
|
||||||
lease.tunnelID = bufbe32toh (leases);
|
|
||||||
leases += 4; // tunnel ID
|
|
||||||
lease.endDate = bufbe64toh (leases);
|
|
||||||
leases += 8; // end date
|
|
||||||
if (ts < lease.endDate + LEASE_ENDDATE_THRESHOLD)
|
|
||||||
{
|
|
||||||
if (lease.endDate > m_ExpirationTime)
|
|
||||||
m_ExpirationTime = lease.endDate;
|
|
||||||
if (m_StoreLeases)
|
|
||||||
{
|
|
||||||
auto ret = m_Leases.insert (std::make_shared<Lease>(lease));
|
|
||||||
if (!ret.second) *(*ret.first) = lease; // update existing
|
|
||||||
(*ret.first)->isUpdated = true;
|
|
||||||
// check if lease's gateway is in our netDb
|
|
||||||
if (!netdb.FindRouter (lease.tunnelGateway))
|
|
||||||
{
|
|
||||||
// if not found request it
|
|
||||||
LogPrint (eLogInfo, "LeaseSet: Lease's tunnel gateway not found, requesting");
|
|
||||||
netdb.RequestDestination (lease.tunnelGateway);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "LeaseSet: Lease is expired already ");
|
|
||||||
}
|
|
||||||
if (!m_ExpirationTime)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "LeaseSet: all leases are expired. Dropped");
|
|
||||||
m_IsValid = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_ExpirationTime += LEASE_ENDDATE_THRESHOLD;
|
|
||||||
// delete old leases
|
|
||||||
if (m_StoreLeases)
|
|
||||||
{
|
|
||||||
for (auto it = m_Leases.begin (); it != m_Leases.end ();)
|
|
||||||
{
|
|
||||||
if (!(*it)->isUpdated)
|
|
||||||
{
|
|
||||||
(*it)->endDate = 0; // somebody might still hold it
|
|
||||||
m_Leases.erase (it++);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// verify
|
|
||||||
if (!m_Identity->Verify (m_Buffer, leases - m_Buffer, leases))
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "LeaseSet: verification failed");
|
|
||||||
m_IsValid = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t LeaseSet::ExtractTimestamp (const uint8_t * buf, size_t len) const
|
|
||||||
{
|
|
||||||
if (!m_Identity) return 0;
|
|
||||||
size_t size = m_Identity->GetFullLen ();
|
|
||||||
if (size > len) return 0;
|
|
||||||
size += 256; // encryption key
|
|
||||||
size += m_Identity->GetSigningPublicKeyLen (); // unused signing key
|
|
||||||
if (size > len) return 0;
|
|
||||||
uint8_t num = buf[size];
|
|
||||||
size++; // num
|
|
||||||
if (size + num*LEASE_SIZE > len) return 0;
|
|
||||||
uint64_t timestamp= 0 ;
|
|
||||||
for (int i = 0; i < num; i++)
|
|
||||||
{
|
|
||||||
size += 36; // gateway (32) + tunnelId(4)
|
|
||||||
auto endDate = bufbe64toh (buf + size);
|
|
||||||
size += 8; // end date
|
|
||||||
if (!timestamp || endDate < timestamp)
|
|
||||||
timestamp = endDate;
|
|
||||||
}
|
|
||||||
return timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LeaseSet::IsNewer (const uint8_t * buf, size_t len) const
|
|
||||||
{
|
|
||||||
return ExtractTimestamp (buf, len) > ExtractTimestamp (m_Buffer, m_BufferLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LeaseSet::ExpiresSoon(const uint64_t dlt, const uint64_t fudge) const
|
|
||||||
{
|
|
||||||
auto now = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
if (fudge) now += rand() % fudge;
|
|
||||||
if (now >= m_ExpirationTime) return true;
|
|
||||||
return m_ExpirationTime - now <= dlt;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<std::shared_ptr<const Lease> > LeaseSet::GetNonExpiredLeases (bool withThreshold) const
|
|
||||||
{
|
|
||||||
return GetNonExpiredLeasesExcluding( [] (const Lease & l) -> bool { return false; }, withThreshold);
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<std::shared_ptr<const Lease> > LeaseSet::GetNonExpiredLeasesExcluding (LeaseInspectFunc exclude, bool withThreshold) const
|
|
||||||
{
|
|
||||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
std::vector<std::shared_ptr<const Lease> > leases;
|
|
||||||
for (const auto& it: m_Leases)
|
|
||||||
{
|
|
||||||
auto endDate = it->endDate;
|
|
||||||
if (withThreshold)
|
|
||||||
endDate += LEASE_ENDDATE_THRESHOLD;
|
|
||||||
else
|
|
||||||
endDate -= LEASE_ENDDATE_THRESHOLD;
|
|
||||||
if (ts < endDate && !exclude(*it))
|
|
||||||
leases.push_back (it);
|
|
||||||
}
|
|
||||||
return leases;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LeaseSet::HasExpiredLeases () const
|
|
||||||
{
|
|
||||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
for (const auto& it: m_Leases)
|
|
||||||
if (ts >= it->endDate) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LeaseSet::IsExpired () const
|
|
||||||
{
|
|
||||||
if (m_StoreLeases && IsEmpty ()) return true;
|
|
||||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
return ts > m_ExpirationTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalLeaseSet::LocalLeaseSet (std::shared_ptr<const IdentityEx> identity, const uint8_t * encryptionPublicKey, std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels):
|
|
||||||
m_ExpirationTime (0), m_Identity (identity)
|
|
||||||
{
|
|
||||||
int num = tunnels.size ();
|
|
||||||
if (num > MAX_NUM_LEASES) num = MAX_NUM_LEASES;
|
|
||||||
// identity
|
|
||||||
auto signingKeyLen = m_Identity->GetSigningPublicKeyLen ();
|
|
||||||
m_BufferLen = m_Identity->GetFullLen () + 256 + signingKeyLen + 1 + num*LEASE_SIZE + m_Identity->GetSignatureLen ();
|
|
||||||
m_Buffer = new uint8_t[m_BufferLen];
|
|
||||||
auto offset = m_Identity->ToBuffer (m_Buffer, m_BufferLen);
|
|
||||||
memcpy (m_Buffer + offset, encryptionPublicKey, 256);
|
|
||||||
offset += 256;
|
|
||||||
memset (m_Buffer + offset, 0, signingKeyLen);
|
|
||||||
offset += signingKeyLen;
|
|
||||||
// num leases
|
|
||||||
m_Buffer[offset] = num;
|
|
||||||
offset++;
|
|
||||||
// leases
|
|
||||||
m_Leases = m_Buffer + offset;
|
|
||||||
auto currentTime = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
for (int i = 0; i < num; i++)
|
|
||||||
{
|
|
||||||
memcpy (m_Buffer + offset, tunnels[i]->GetNextIdentHash (), 32);
|
|
||||||
offset += 32; // gateway id
|
|
||||||
htobe32buf (m_Buffer + offset, tunnels[i]->GetNextTunnelID ());
|
|
||||||
offset += 4; // tunnel id
|
|
||||||
uint64_t ts = tunnels[i]->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // 1 minute before expiration
|
|
||||||
ts *= 1000; // in milliseconds
|
|
||||||
if (ts > m_ExpirationTime) m_ExpirationTime = ts;
|
|
||||||
// make sure leaseset is newer than previous, but adding some time to expiration date
|
|
||||||
ts += (currentTime - tunnels[i]->GetCreationTime ()*1000LL)*2/i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT; // up to 2 secs
|
|
||||||
htobe64buf (m_Buffer + offset, ts);
|
|
||||||
offset += 8; // end date
|
|
||||||
}
|
|
||||||
// we don't sign it yet. must be signed later on
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalLeaseSet::LocalLeaseSet (std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len):
|
|
||||||
m_ExpirationTime (0), m_Identity (identity)
|
|
||||||
{
|
|
||||||
m_BufferLen = len;
|
|
||||||
m_Buffer = new uint8_t[m_BufferLen];
|
|
||||||
memcpy (m_Buffer, buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LocalLeaseSet::IsExpired () const
|
|
||||||
{
|
|
||||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
return ts > m_ExpirationTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
130
LeaseSet.h
130
LeaseSet.h
@@ -1,130 +0,0 @@
|
|||||||
#ifndef LEASE_SET_H__
|
|
||||||
#define LEASE_SET_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <vector>
|
|
||||||
#include <set>
|
|
||||||
#include <memory>
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "Timestamp.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
|
|
||||||
namespace tunnel
|
|
||||||
{
|
|
||||||
class InboundTunnel;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace data
|
|
||||||
{
|
|
||||||
const int LEASE_ENDDATE_THRESHOLD = 51000; // in milliseconds
|
|
||||||
struct Lease
|
|
||||||
{
|
|
||||||
IdentHash tunnelGateway;
|
|
||||||
uint32_t tunnelID;
|
|
||||||
uint64_t endDate; // 0 means invalid
|
|
||||||
bool isUpdated; // trasient
|
|
||||||
/* return true if this lease expires within t millisecond + fudge factor */
|
|
||||||
bool ExpiresWithin( const uint64_t t, const uint64_t fudge = 1000 ) const {
|
|
||||||
auto expire = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
if(fudge) expire += rand() % fudge;
|
|
||||||
return endDate - expire >= t;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LeaseCmp
|
|
||||||
{
|
|
||||||
bool operator() (std::shared_ptr<const Lease> l1, std::shared_ptr<const Lease> l2) const
|
|
||||||
{
|
|
||||||
if (l1->tunnelID != l2->tunnelID)
|
|
||||||
return l1->tunnelID < l2->tunnelID;
|
|
||||||
else
|
|
||||||
return l1->tunnelGateway < l2->tunnelGateway;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::function<bool(const Lease & l)> LeaseInspectFunc;
|
|
||||||
|
|
||||||
const size_t MAX_LS_BUFFER_SIZE = 3072;
|
|
||||||
const size_t LEASE_SIZE = 44; // 32 + 4 + 8
|
|
||||||
const uint8_t MAX_NUM_LEASES = 16;
|
|
||||||
class LeaseSet: public RoutingDestination
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
LeaseSet (const uint8_t * buf, size_t len, bool storeLeases = true);
|
|
||||||
~LeaseSet () { delete[] m_Buffer; };
|
|
||||||
void Update (const uint8_t * buf, size_t len);
|
|
||||||
bool IsNewer (const uint8_t * buf, size_t len) const;
|
|
||||||
void PopulateLeases (); // from buffer
|
|
||||||
std::shared_ptr<const IdentityEx> GetIdentity () const { return m_Identity; };
|
|
||||||
|
|
||||||
const uint8_t * GetBuffer () const { return m_Buffer; };
|
|
||||||
size_t GetBufferLen () const { return m_BufferLen; };
|
|
||||||
bool IsValid () const { return m_IsValid; };
|
|
||||||
const std::vector<std::shared_ptr<const Lease> > GetNonExpiredLeases (bool withThreshold = true) const;
|
|
||||||
const std::vector<std::shared_ptr<const Lease> > GetNonExpiredLeasesExcluding (LeaseInspectFunc exclude, bool withThreshold = true) const;
|
|
||||||
bool HasExpiredLeases () const;
|
|
||||||
bool IsExpired () const;
|
|
||||||
bool IsEmpty () const { return m_Leases.empty (); };
|
|
||||||
uint64_t GetExpirationTime () const { return m_ExpirationTime; };
|
|
||||||
bool ExpiresSoon(const uint64_t dlt=1000 * 5, const uint64_t fudge = 0) const ;
|
|
||||||
bool operator== (const LeaseSet& other) const
|
|
||||||
{ return m_BufferLen == other.m_BufferLen && !memcmp (m_Buffer, other.m_Buffer, m_BufferLen); };
|
|
||||||
|
|
||||||
// implements RoutingDestination
|
|
||||||
const IdentHash& GetIdentHash () const { return m_Identity->GetIdentHash (); };
|
|
||||||
const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionKey; };
|
|
||||||
bool IsDestination () const { return true; };
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void ReadFromBuffer (bool readIdentity = true);
|
|
||||||
uint64_t ExtractTimestamp (const uint8_t * buf, size_t len) const; // min expiration time
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
bool m_IsValid, m_StoreLeases; // we don't need to store leases for floodfill
|
|
||||||
std::set<std::shared_ptr<Lease>, LeaseCmp> m_Leases;
|
|
||||||
uint64_t m_ExpirationTime; // in milliseconds
|
|
||||||
std::shared_ptr<const IdentityEx> m_Identity;
|
|
||||||
uint8_t m_EncryptionKey[256];
|
|
||||||
uint8_t * m_Buffer;
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,242 +0,0 @@
|
|||||||
// LittleBigEndian.h fixed for 64-bits added union
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef LITTLEBIGENDIAN_H
|
|
||||||
#define LITTLEBIGENDIAN_H
|
|
||||||
|
|
||||||
// Determine Little-Endian or Big-Endian
|
|
||||||
|
|
||||||
#define CURRENT_BYTE_ORDER (*(int *)"\x01\x02\x03\x04")
|
|
||||||
#define LITTLE_ENDIAN_BYTE_ORDER 0x04030201
|
|
||||||
#define BIG_ENDIAN_BYTE_ORDER 0x01020304
|
|
||||||
#define PDP_ENDIAN_BYTE_ORDER 0x02010403
|
|
||||||
|
|
||||||
#define IS_LITTLE_ENDIAN (CURRENT_BYTE_ORDER == LITTLE_ENDIAN_BYTE_ORDER)
|
|
||||||
#define IS_BIG_ENDIAN (CURRENT_BYTE_ORDER == BIG_ENDIAN_BYTE_ORDER)
|
|
||||||
#define IS_PDP_ENDIAN (CURRENT_BYTE_ORDER == PDP_ENDIAN_BYTE_ORDER)
|
|
||||||
|
|
||||||
// Forward declaration
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct LittleEndian;
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct BigEndian;
|
|
||||||
|
|
||||||
// Little-Endian template
|
|
||||||
|
|
||||||
#pragma pack(push,1)
|
|
||||||
template<typename T>
|
|
||||||
struct LittleEndian
|
|
||||||
{
|
|
||||||
union
|
|
||||||
{
|
|
||||||
unsigned char bytes[sizeof(T)];
|
|
||||||
T raw_value;
|
|
||||||
};
|
|
||||||
|
|
||||||
LittleEndian(T t = T())
|
|
||||||
{
|
|
||||||
operator =(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
LittleEndian(const LittleEndian<T> & t)
|
|
||||||
{
|
|
||||||
raw_value = t.raw_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
LittleEndian(const BigEndian<T> & t)
|
|
||||||
{
|
|
||||||
for (unsigned i = 0; i < sizeof(T); i++)
|
|
||||||
bytes[i] = t.bytes[sizeof(T)-1-i];
|
|
||||||
}
|
|
||||||
|
|
||||||
operator const T() const
|
|
||||||
{
|
|
||||||
T t = T();
|
|
||||||
for (unsigned i = 0; i < sizeof(T); i++)
|
|
||||||
t |= T(bytes[i]) << (i << 3);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
const T operator = (const T t)
|
|
||||||
{
|
|
||||||
for (unsigned i = 0; i < sizeof(T); i++)
|
|
||||||
bytes[sizeof(T)-1 - i] = static_cast<unsigned char>(t >> (i << 3));
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
// operators
|
|
||||||
|
|
||||||
const T operator += (const T t)
|
|
||||||
{
|
|
||||||
return (*this = *this + t);
|
|
||||||
}
|
|
||||||
|
|
||||||
const T operator -= (const T t)
|
|
||||||
{
|
|
||||||
return (*this = *this - t);
|
|
||||||
}
|
|
||||||
|
|
||||||
const T operator *= (const T t)
|
|
||||||
{
|
|
||||||
return (*this = *this * t);
|
|
||||||
}
|
|
||||||
|
|
||||||
const T operator /= (const T t)
|
|
||||||
{
|
|
||||||
return (*this = *this / t);
|
|
||||||
}
|
|
||||||
|
|
||||||
const T operator %= (const T t)
|
|
||||||
{
|
|
||||||
return (*this = *this % t);
|
|
||||||
}
|
|
||||||
|
|
||||||
LittleEndian<T> operator ++ (int)
|
|
||||||
{
|
|
||||||
LittleEndian<T> tmp(*this);
|
|
||||||
operator ++ ();
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
LittleEndian<T> & operator ++ ()
|
|
||||||
{
|
|
||||||
for (unsigned i = 0; i < sizeof(T); i++)
|
|
||||||
{
|
|
||||||
++bytes[i];
|
|
||||||
if (bytes[i] != 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return (*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
LittleEndian<T> operator -- (int)
|
|
||||||
{
|
|
||||||
LittleEndian<T> tmp(*this);
|
|
||||||
operator -- ();
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
LittleEndian<T> & operator -- ()
|
|
||||||
{
|
|
||||||
for (unsigned i = 0; i < sizeof(T); i++)
|
|
||||||
{
|
|
||||||
--bytes[i];
|
|
||||||
if (bytes[i] != (T)(-1))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return (*this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#pragma pack(pop)
|
|
||||||
|
|
||||||
// Big-Endian template
|
|
||||||
|
|
||||||
#pragma pack(push,1)
|
|
||||||
template<typename T>
|
|
||||||
struct BigEndian
|
|
||||||
{
|
|
||||||
union
|
|
||||||
{
|
|
||||||
unsigned char bytes[sizeof(T)];
|
|
||||||
T raw_value;
|
|
||||||
};
|
|
||||||
|
|
||||||
BigEndian(T t = T())
|
|
||||||
{
|
|
||||||
operator =(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
BigEndian(const BigEndian<T> & t)
|
|
||||||
{
|
|
||||||
raw_value = t.raw_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
BigEndian(const LittleEndian<T> & t)
|
|
||||||
{
|
|
||||||
for (unsigned i = 0; i < sizeof(T); i++)
|
|
||||||
bytes[i] = t.bytes[sizeof(T)-1-i];
|
|
||||||
}
|
|
||||||
|
|
||||||
operator const T() const
|
|
||||||
{
|
|
||||||
T t = T();
|
|
||||||
for (unsigned i = 0; i < sizeof(T); i++)
|
|
||||||
t |= T(bytes[sizeof(T) - 1 - i]) << (i << 3);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
const T operator = (const T t)
|
|
||||||
{
|
|
||||||
for (unsigned i = 0; i < sizeof(T); i++)
|
|
||||||
bytes[sizeof(T) - 1 - i] = t >> (i << 3);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
// operators
|
|
||||||
|
|
||||||
const T operator += (const T t)
|
|
||||||
{
|
|
||||||
return (*this = *this + t);
|
|
||||||
}
|
|
||||||
|
|
||||||
const T operator -= (const T t)
|
|
||||||
{
|
|
||||||
return (*this = *this - t);
|
|
||||||
}
|
|
||||||
|
|
||||||
const T operator *= (const T t)
|
|
||||||
{
|
|
||||||
return (*this = *this * t);
|
|
||||||
}
|
|
||||||
|
|
||||||
const T operator /= (const T t)
|
|
||||||
{
|
|
||||||
return (*this = *this / t);
|
|
||||||
}
|
|
||||||
|
|
||||||
const T operator %= (const T t)
|
|
||||||
{
|
|
||||||
return (*this = *this % t);
|
|
||||||
}
|
|
||||||
|
|
||||||
BigEndian<T> operator ++ (int)
|
|
||||||
{
|
|
||||||
BigEndian<T> tmp(*this);
|
|
||||||
operator ++ ();
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
BigEndian<T> & operator ++ ()
|
|
||||||
{
|
|
||||||
for (unsigned i = 0; i < sizeof(T); i++)
|
|
||||||
{
|
|
||||||
++bytes[sizeof(T) - 1 - i];
|
|
||||||
if (bytes[sizeof(T) - 1 - i] != 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return (*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
BigEndian<T> operator -- (int)
|
|
||||||
{
|
|
||||||
BigEndian<T> tmp(*this);
|
|
||||||
operator -- ();
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
BigEndian<T> & operator -- ()
|
|
||||||
{
|
|
||||||
for (unsigned i = 0; i < sizeof(T); i++)
|
|
||||||
{
|
|
||||||
--bytes[sizeof(T) - 1 - i];
|
|
||||||
if (bytes[sizeof(T) - 1 - i] != (T)(-1))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return (*this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#pragma pack(pop)
|
|
||||||
|
|
||||||
#endif // LITTLEBIGENDIAN_H
|
|
||||||
179
Makefile
179
Makefile
@@ -1,54 +1,106 @@
|
|||||||
UNAME := $(shell uname -s)
|
.DEFAULT_GOAL := all
|
||||||
SHLIB := libi2pd.so
|
|
||||||
ARLIB := libi2pd.a
|
|
||||||
SHLIB_CLIENT := libi2pdclient.so
|
|
||||||
ARLIB_CLIENT := libi2pdclient.a
|
|
||||||
I2PD := i2pd
|
|
||||||
GREP := grep
|
|
||||||
DEPS := obj/make.dep
|
|
||||||
|
|
||||||
include filelist.mk
|
SYS := $(shell $(CXX) -dumpmachine)
|
||||||
|
|
||||||
USE_AESNI := yes
|
ifneq (, $(findstring darwin, $(SYS)))
|
||||||
USE_STATIC := no
|
SHARED_SUFFIX = dylib
|
||||||
USE_MESHNET := no
|
else ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstring cygwin, $(SYS)))
|
||||||
USE_UPNP := no
|
SHARED_SUFFIX = dll
|
||||||
|
else
|
||||||
ifeq ($(WEBSOCKETS),1)
|
SHARED_SUFFIX = so
|
||||||
NEEDED_CXXFLAGS += -DWITH_EVENTS
|
|
||||||
DAEMON_SRC += Websocket.cpp
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(UNAME),Darwin)
|
SHLIB := libi2pd.$(SHARED_SUFFIX)
|
||||||
DAEMON_SRC += DaemonLinux.cpp
|
ARLIB := libi2pd.a
|
||||||
|
SHLIB_LANG := libi2pdlang.$(SHARED_SUFFIX)
|
||||||
|
ARLIB_LANG := libi2pdlang.a
|
||||||
|
SHLIB_CLIENT := libi2pdclient.$(SHARED_SUFFIX)
|
||||||
|
ARLIB_CLIENT := libi2pdclient.a
|
||||||
|
SHLIB_WRAP := libi2pdwrapper.$(SHARED_SUFFIX)
|
||||||
|
ARLIB_WRAP := libi2pdwrapper.a
|
||||||
|
I2PD := i2pd
|
||||||
|
|
||||||
|
LIB_SRC_DIR := libi2pd
|
||||||
|
LIB_CLIENT_SRC_DIR := libi2pd_client
|
||||||
|
WRAP_SRC_DIR := libi2pd_wrapper
|
||||||
|
LANG_SRC_DIR := i18n
|
||||||
|
DAEMON_SRC_DIR := daemon
|
||||||
|
|
||||||
|
# import source files lists
|
||||||
|
include filelist.mk
|
||||||
|
|
||||||
|
USE_AESNI := $(or $(USE_AESNI),yes)
|
||||||
|
USE_STATIC := $(or $(USE_STATIC),no)
|
||||||
|
USE_UPNP := $(or $(USE_UPNP),no)
|
||||||
|
DEBUG := $(or $(DEBUG),yes)
|
||||||
|
|
||||||
|
# for debugging purposes only, when commit hash needed in trunk builds in i2pd version string
|
||||||
|
USE_GIT_VERSION := $(or $(USE_GIT_VERSION),no)
|
||||||
|
|
||||||
|
# for MacOS only, waiting for "1", not "yes"
|
||||||
|
HOMEBREW := $(or $(HOMEBREW),0)
|
||||||
|
|
||||||
|
ifeq ($(DEBUG),yes)
|
||||||
|
CXX_DEBUG = -g
|
||||||
|
else
|
||||||
|
CXX_DEBUG = -Os
|
||||||
|
LD_DEBUG = -s
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq (, $(DESTDIR))
|
||||||
|
PREFIX = $(DESTDIR)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq (, $(findstring darwin, $(SYS)))
|
||||||
|
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||||
ifeq ($(HOMEBREW),1)
|
ifeq ($(HOMEBREW),1)
|
||||||
include Makefile.homebrew
|
include Makefile.homebrew
|
||||||
else
|
else
|
||||||
include Makefile.osx
|
include Makefile.osx
|
||||||
endif
|
endif
|
||||||
else ifeq ($(shell echo $(UNAME) | $(GREP) -Ec '(Free|Open)BSD'),1)
|
else ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstring cygwin, $(SYS)))
|
||||||
DAEMON_SRC += DaemonLinux.cpp
|
DAEMON_SRC += Win32/DaemonWin32.cpp Win32/Win32App.cpp Win32/Win32Service.cpp Win32/Win32NetState.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
|
include Makefile.mingw
|
||||||
|
else ifneq (, $(findstring linux, $(SYS))$(findstring gnu, $(SYS)))
|
||||||
|
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||||
|
include Makefile.linux
|
||||||
|
else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS)))
|
||||||
|
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||||
|
include Makefile.bsd
|
||||||
|
else # not supported
|
||||||
|
$(error Not supported platform)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(USE_MESHNET),yes)
|
ifeq ($(USE_GIT_VERSION),yes)
|
||||||
NEEDED_CXXFLAGS += -DMESHNET
|
GIT_VERSION := $(shell git describe --tags)
|
||||||
|
NEEDED_CXXFLAGS += -DGITVER=\"$(GIT_VERSION)\"
|
||||||
endif
|
endif
|
||||||
|
|
||||||
all: mk_obj_dir $(ARLIB) $(ARLIB_CLIENT) $(I2PD)
|
NEEDED_CXXFLAGS += -MMD -MP -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR) -I$(LANG_SRC_DIR) -DOPENSSL_SUPPRESS_DEPRECATED
|
||||||
|
|
||||||
|
LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
|
||||||
|
LIB_CLIENT_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
|
||||||
|
LANG_OBJS += $(patsubst %.cpp,obj/%.o,$(LANG_SRC))
|
||||||
|
DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
|
||||||
|
WRAP_LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(WRAP_LIB_SRC))
|
||||||
|
DEPS += $(LIB_OBJS:.o=.d) $(LIB_CLIENT_OBJS:.o=.d) $(LANG_OBJS:.o=.d) $(DAEMON_OBJS:.o=.d) $(WRAP_LIB_OBJS:.o=.d)
|
||||||
|
|
||||||
|
## Build all code (libi2pd, libi2pdclient, libi2pdlang), link it to .a and build binary
|
||||||
|
all: $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG) $(I2PD)
|
||||||
|
|
||||||
mk_obj_dir:
|
mk_obj_dir:
|
||||||
@mkdir -p obj
|
@mkdir -p obj/$(LIB_SRC_DIR)
|
||||||
|
@mkdir -p obj/$(LIB_CLIENT_SRC_DIR)
|
||||||
|
@mkdir -p obj/$(LANG_SRC_DIR)
|
||||||
|
@mkdir -p obj/$(DAEMON_SRC_DIR)
|
||||||
|
@mkdir -p obj/$(WRAP_SRC_DIR)
|
||||||
@mkdir -p obj/Win32
|
@mkdir -p obj/Win32
|
||||||
|
|
||||||
api: mk_obj_dir $(SHLIB) $(ARLIB)
|
api: $(SHLIB) $(ARLIB)
|
||||||
api_client: mk_obj_dir $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
|
client: $(SHLIB_CLIENT) $(ARLIB_CLIENT)
|
||||||
|
lang: $(SHLIB_LANG) $(ARLIB_LANG)
|
||||||
|
api_client: api client lang
|
||||||
|
wrapper: api_client $(SHLIB_WRAP) $(ARLIB_WRAP)
|
||||||
|
|
||||||
## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time
|
## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time
|
||||||
## **without** overwriting the CXXFLAGS which we need in order to build.
|
## **without** overwriting the CXXFLAGS which we need in order to build.
|
||||||
@@ -57,40 +109,53 @@ api_client: mk_obj_dir $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
|
|||||||
## -std=c++11. If you want to remove this variable please do so in a way that allows setting
|
## -std=c++11. If you want to remove this variable please do so in a way that allows setting
|
||||||
## custom FLAGS to work at build-time.
|
## custom FLAGS to work at build-time.
|
||||||
|
|
||||||
deps: mk_obj_dir
|
obj/%.o: %.cpp | mk_obj_dir
|
||||||
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) -MM *.cpp > $(DEPS)
|
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -c -o $@ $<
|
||||||
@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
|
# '-' is 'ignore if missing' on first run
|
||||||
-include $(DEPS)
|
-include $(DEPS)
|
||||||
|
|
||||||
DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
|
$(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG)
|
||||||
$(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT)
|
$(CXX) -o $@ $(LDFLAGS) $^ $(LDLIBS)
|
||||||
$(CXX) -o $@ $^ $(LDLIBS) $(LDFLAGS)
|
|
||||||
|
|
||||||
$(SHLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
|
$(SHLIB): $(LIB_OBJS) $(SHLIB_LANG)
|
||||||
ifneq ($(USE_STATIC),yes)
|
ifneq ($(USE_STATIC),yes)
|
||||||
$(CXX) $(LDFLAGS) $(LDLIBS) -shared -o $@ $^
|
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB_LANG)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
$(SHLIB_CLIENT): $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
|
$(SHLIB_CLIENT): $(LIB_CLIENT_OBJS) $(SHLIB) $(SHLIB_LANG)
|
||||||
$(CXX) $(LDFLAGS) $(LDLIBS) -shared -o $@ $^
|
ifneq ($(USE_STATIC),yes)
|
||||||
|
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB) $(SHLIB_LANG)
|
||||||
|
endif
|
||||||
|
|
||||||
$(ARLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
|
$(SHLIB_WRAP): $(WRAP_LIB_OBJS)
|
||||||
ar -r $@ $^
|
ifneq ($(USE_STATIC),yes)
|
||||||
|
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
|
||||||
|
endif
|
||||||
|
|
||||||
$(ARLIB_CLIENT): $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
|
$(SHLIB_LANG): $(LANG_OBJS)
|
||||||
ar -r $@ $^
|
ifneq ($(USE_STATIC),yes)
|
||||||
|
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
$(ARLIB): $(LIB_OBJS)
|
||||||
|
$(AR) -r $@ $^
|
||||||
|
|
||||||
|
$(ARLIB_CLIENT): $(LIB_CLIENT_OBJS)
|
||||||
|
$(AR) -r $@ $^
|
||||||
|
|
||||||
|
$(ARLIB_WRAP): $(WRAP_LIB_OBJS)
|
||||||
|
$(AR) -r $@ $^
|
||||||
|
|
||||||
|
$(ARLIB_LANG): $(LANG_OBJS)
|
||||||
|
$(AR) -r $@ $^
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf obj
|
$(RM) -r obj
|
||||||
rm -rf docs/generated
|
$(RM) -r docs/generated
|
||||||
$(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
|
$(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT) $(SHLIB_LANG) $(ARLIB_LANG) $(SHLIB_WRAP) $(ARLIB_WRAP)
|
||||||
|
|
||||||
strip: $(I2PD) $(SHLIB_CLIENT) $(SHLIB)
|
strip: $(I2PD) $(SHLIB) $(SHLIB_CLIENT) $(SHLIB_LANG)
|
||||||
strip $^
|
strip $^
|
||||||
|
|
||||||
LATEST_TAG=$(shell git describe --tags --abbrev=0 openssl)
|
LATEST_TAG=$(shell git describe --tags --abbrev=0 openssl)
|
||||||
@@ -108,9 +173,13 @@ doxygen:
|
|||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
.PHONY: deps
|
|
||||||
.PHONY: doxygen
|
.PHONY: doxygen
|
||||||
.PHONY: dist
|
.PHONY: dist
|
||||||
|
.PHONY: last-dist
|
||||||
.PHONY: api
|
.PHONY: api
|
||||||
.PHONY: api_client
|
.PHONY: api_client
|
||||||
|
.PHONY: client
|
||||||
|
.PHONY: lang
|
||||||
.PHONY: mk_obj_dir
|
.PHONY: mk_obj_dir
|
||||||
|
.PHONY: install
|
||||||
|
.PHONY: strip
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
CXX = clang++
|
CXX = clang++
|
||||||
CXXFLAGS = -O2
|
CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation
|
||||||
## NOTE: NEEDED_CXXFLAGS is here so that custom CXXFLAGS can be specified at build time
|
## NOTE: NEEDED_CXXFLAGS is here so that custom CXXFLAGS can be specified at build time
|
||||||
## **without** overwriting the CXXFLAGS which we need in order to build.
|
## **without** overwriting the CXXFLAGS which we need in order to build.
|
||||||
## For example, when adding 'hardening flags' to the build
|
## For example, when adding 'hardening flags' to the build
|
||||||
@@ -8,5 +8,5 @@ CXXFLAGS = -O2
|
|||||||
## custom FLAGS to work at build-time.
|
## custom FLAGS to work at build-time.
|
||||||
NEEDED_CXXFLAGS = -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1
|
NEEDED_CXXFLAGS = -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1
|
||||||
INCFLAGS = -I/usr/include/ -I/usr/local/include/
|
INCFLAGS = -I/usr/include/ -I/usr/local/include/
|
||||||
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib
|
LDFLAGS = ${LD_DEBUG} -Wl,-rpath,/usr/local/lib -L/usr/local/lib
|
||||||
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||||
|
|||||||
@@ -1,29 +1,57 @@
|
|||||||
# root directory holding homebrew
|
# root directory holding homebrew
|
||||||
BREWROOT = /usr/local/
|
BREWROOT = /usr/local
|
||||||
BOOSTROOT = ${BREWROOT}/opt/boost
|
BOOSTROOT = ${BREWROOT}/opt/boost
|
||||||
SSLROOT = ${BREWROOT}/opt/libressl
|
SSLROOT = ${BREWROOT}/opt/openssl@1.1
|
||||||
CXX = clang++
|
UPNPROOT = ${BREWROOT}/opt/miniupnpc
|
||||||
CXXFLAGS = -g -Wall -std=c++11 -DMAC_OSX
|
CXXFLAGS = ${CXX_DEBUG} -Wall -std=c++11 -DMAC_OSX -Wno-overloaded-virtual
|
||||||
INCFLAGS = -I${SSLROOT}/include -I${BOOSTROOT}/include
|
INCFLAGS = -I${SSLROOT}/include -I${BOOSTROOT}/include
|
||||||
LDFLAGS = -L${SSLROOT}/lib -L${BOOSTROOT}/lib
|
LDFLAGS = ${LD_DEBUG}
|
||||||
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
|
||||||
|
ifndef TRAVIS
|
||||||
|
CXX = clang++
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(USE_STATIC),yes)
|
||||||
|
LDLIBS = -lz ${SSLROOT}/lib/libcrypto.a ${SSLROOT}/lib/libssl.a ${BOOSTROOT}/lib/libboost_system.a ${BOOSTROOT}/lib/libboost_date_time.a ${BOOSTROOT}/lib/libboost_filesystem.a ${BOOSTROOT}/lib/libboost_program_options.a -lpthread
|
||||||
|
else
|
||||||
|
LDFLAGS += -L${SSLROOT}/lib -L${BOOSTROOT}/lib
|
||||||
|
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(USE_UPNP),yes)
|
ifeq ($(USE_UPNP),yes)
|
||||||
LDFLAGS += -ldl
|
LDFLAGS += -ldl
|
||||||
CXXFLAGS += -DUSE_UPNP
|
CXXFLAGS += -DUSE_UPNP
|
||||||
|
INCFLAGS += -I${UPNPROOT}/include
|
||||||
|
ifeq ($(USE_STATIC),yes)
|
||||||
|
LDLIBS += ${UPNPROOT}/lib/libminiupnpc.a
|
||||||
|
else
|
||||||
|
LDFLAGS += -L${UPNPROOT}/lib
|
||||||
|
LDLIBS += -lminiupnpc
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# OSX Notes
|
# OSX Notes
|
||||||
# http://www.hutsby.net/2011/08/macs-with-aes-ni.html
|
# 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
|
# 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
|
# 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)
|
||||||
#ifeq ($(USE_AESNI),yes)
|
CXXFLAGS += -D__AES__ -maes
|
||||||
# CXXFLAGS += -maes -DAESNI
|
endif
|
||||||
#endif
|
|
||||||
|
|
||||||
# Disabled, since it will be the default make rule. I think its better
|
install: all
|
||||||
# to define the default rule in Makefile and not Makefile.<ostype> - torkel
|
install -d ${PREFIX}/bin
|
||||||
#install: all
|
install -m 755 ${I2PD} ${PREFIX}/bin
|
||||||
# test -d ${PREFIX} || mkdir -p ${PREFIX}/
|
install -d ${PREFIX}/etc ${PREFIX}/etc/i2pd ${PREFIX}/etc/i2pd/tunnels.conf.d
|
||||||
# cp -r i2p ${PREFIX}/
|
install -m 644 contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/etc/i2pd
|
||||||
|
install -d ${PREFIX}/share ${PREFIX}/share/doc ${PREFIX}/share/doc/i2pd
|
||||||
|
install -m 644 ChangeLog LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/share/doc/i2pd
|
||||||
|
install -d ${PREFIX}/share/i2pd
|
||||||
|
@cp -R contrib/certificates ${PREFIX}/share/i2pd/
|
||||||
|
install -d ${PREFIX}/share/man ${PREFIX}/share/man/man1
|
||||||
|
@gzip -kf debian/i2pd.1 && install debian/i2pd.1.gz ${PREFIX}/share/man/man1
|
||||||
|
install -d ${PREFIX}/var ${PREFIX}/var/lib ${PREFIX}/var/lib/i2pd
|
||||||
|
@ln -sf ${PREFIX}/share/i2pd/certificates ${PREFIX}/var/lib/i2pd/certificates
|
||||||
|
@ln -sf ${PREFIX}/etc/i2pd/tunnels.conf.d ${PREFIX}/var/lib/i2pd/tunnels.d
|
||||||
|
@ln -sf ${PREFIX}/etc/i2pd/i2pd.conf ${PREFIX}/var/lib/i2pd/i2pd.conf
|
||||||
|
@ln -sf ${PREFIX}/etc/i2pd/subscriptions.txt ${PREFIX}/var/lib/i2pd/subscriptions.txt
|
||||||
|
@ln -sf ${PREFIX}/etc/i2pd/tunnels.conf ${PREFIX}/var/lib/i2pd/tunnels.conf
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
# set defaults instead redefine
|
# set defaults instead redefine
|
||||||
CXXFLAGS ?= -g -Wall -Wextra -Wno-unused-parameter -pedantic
|
CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-psabi
|
||||||
INCFLAGS ?=
|
LDFLAGS ?= ${LD_DEBUG}
|
||||||
|
|
||||||
## NOTE: The NEEDED_CXXFLAGS are here so that custom CXXFLAGS can be specified at build time
|
## NOTE: The NEEDED_CXXFLAGS are here so that custom CXXFLAGS can be specified at build time
|
||||||
## **without** overwriting the CXXFLAGS which we need in order to build.
|
## **without** overwriting the CXXFLAGS which we need in order to build.
|
||||||
## For example, when adding 'hardening flags' to the build
|
## For example, when adding 'hardening flags' to the build
|
||||||
## (e.g. -fstack-protector-strong -Wformat -Werror=format-security), we do not want to remove
|
## (e.g. -fstack-protector-strong -Wformat -Werror=format-security), we do not want to remove
|
||||||
## -std=c++11. If you want to remove this variable please do so in a way that allows setting
|
## -std=c++11. If you want to remove this variable please do so in a way that allows setting
|
||||||
## custom FLAGS to work at build-time.
|
## custom FDLAGS to work at build-time.
|
||||||
|
|
||||||
# detect proper flag for c++11 support by compilers
|
# detect proper flag for c++11 support by compilers
|
||||||
CXXVER := $(shell $(CXX) -dumpversion)
|
CXXVER := $(shell $(CXX) -dumpversion)
|
||||||
@@ -15,52 +15,61 @@ ifeq ($(shell expr match $(CXX) 'clang'),5)
|
|||||||
NEEDED_CXXFLAGS += -std=c++11
|
NEEDED_CXXFLAGS += -std=c++11
|
||||||
else ifeq ($(shell expr match ${CXXVER} "4\.[0-9][0-9]"),4) # gcc >= 4.10
|
else ifeq ($(shell expr match ${CXXVER} "4\.[0-9][0-9]"),4) # gcc >= 4.10
|
||||||
NEEDED_CXXFLAGS += -std=c++11
|
NEEDED_CXXFLAGS += -std=c++11
|
||||||
else ifeq ($(shell expr match ${CXXVER} "4\.[7-9]"),3) # >= 4.7
|
else ifeq ($(shell expr match ${CXXVER} "4\.[8-9]"),3) # gcc 4.8 - 4.9
|
||||||
NEEDED_CXXFLAGS += -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1
|
NEEDED_CXXFLAGS += -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1
|
||||||
else ifeq ($(shell expr match ${CXXVER} "4\.6"),3) # = 4.6
|
else ifeq ($(shell expr match ${CXXVER} "[5-6]"),1) # gcc 5 - 6
|
||||||
NEEDED_CXXFLAGS += -std=c++0x
|
|
||||||
else ifeq ($(shell expr match ${CXXVER} "[5-6]\.[0-9]"),3) # gcc >= 5.0
|
|
||||||
NEEDED_CXXFLAGS += -std=c++11
|
NEEDED_CXXFLAGS += -std=c++11
|
||||||
|
LDLIBS = -latomic
|
||||||
|
else ifeq ($(shell expr match ${CXXVER} "[7-9]"),1) # gcc 7 - 9
|
||||||
|
NEEDED_CXXFLAGS += -std=c++17
|
||||||
|
LDLIBS = -latomic
|
||||||
|
else ifeq ($(shell expr match ${CXXVER} "1[0-9]"),2) # gcc 10+
|
||||||
|
# NEEDED_CXXFLAGS += -std=c++20
|
||||||
|
NEEDED_CXXFLAGS += -std=c++17
|
||||||
|
LDLIBS = -latomic
|
||||||
else # not supported
|
else # not supported
|
||||||
$(error Compiler too old)
|
$(error Compiler too old)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
NEEDED_CXXFLAGS += -fPIC
|
NEEDED_CXXFLAGS += -fPIC
|
||||||
|
|
||||||
ifeq ($(USE_STATIC),yes)
|
LDLIBS += -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lssl -lcrypto -lz
|
||||||
# NOTE: on glibc you will get this warning:
|
|
||||||
# Using 'getaddrinfo' in statically linked applications requires at runtime
|
|
||||||
# the shared libraries from the glibc version used for linking
|
|
||||||
LIBDIR := /usr/lib
|
|
||||||
LDLIBS = $(LIBDIR)/libboost_system.a
|
|
||||||
LDLIBS += $(LIBDIR)/libboost_date_time.a
|
|
||||||
LDLIBS += $(LIBDIR)/libboost_filesystem.a
|
|
||||||
LDLIBS += $(LIBDIR)/libboost_program_options.a
|
|
||||||
LDLIBS += $(LIBDIR)/libssl.a
|
|
||||||
LDLIBS += $(LIBDIR)/libcrypto.a
|
|
||||||
LDLIBS += $(LIBDIR)/libz.a
|
|
||||||
LDLIBS += -lpthread -static-libstdc++ -static-libgcc -lrt -ldl
|
|
||||||
USE_AESNI := no
|
|
||||||
else
|
|
||||||
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
|
||||||
endif
|
|
||||||
|
|
||||||
# UPNP Support (miniupnpc 1.5 and higher)
|
# UPNP Support (miniupnpc 1.5 and higher)
|
||||||
ifeq ($(USE_UPNP),yes)
|
ifeq ($(USE_UPNP),yes)
|
||||||
CXXFLAGS += -DUSE_UPNP
|
LDLIBS += -lminiupnpc
|
||||||
|
NEEDED_CXXFLAGS += -DUSE_UPNP
|
||||||
|
endif
|
||||||
|
|
||||||
|
# NOTE: on glibc you will get this warning:
|
||||||
|
# Using 'getaddrinfo' in statically linked applications requires at runtime
|
||||||
|
# the shared libraries from the glibc version used for linking
|
||||||
ifeq ($(USE_STATIC),yes)
|
ifeq ($(USE_STATIC),yes)
|
||||||
LDLIBS += $(LIBDIR)/libminiupnpc.a
|
LDLIBS += -static -ldl -lpthread -static-libgcc -static-libstdc++
|
||||||
else
|
else
|
||||||
LDLIBS += -lminiupnpc
|
LDLIBS += -lpthread
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(USE_AESNI),yes)
|
||||||
|
ifneq (, $(findstring i386, $(SYS))$(findstring i686, $(SYS))$(findstring x86_64, $(SYS))) # only x86-based CPU supports that
|
||||||
|
NEEDED_CXXFLAGS += -D__AES__ -maes
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
IS_64 := $(shell $(CXX) -dumpmachine 2>&1 | $(GREP) -c "64")
|
install: all
|
||||||
ifeq ($(USE_AESNI),yes)
|
install -d ${PREFIX}/bin
|
||||||
ifeq ($(IS_64),1)
|
install -m 755 ${I2PD} ${PREFIX}/bin
|
||||||
#check if AES-NI is supported by CPU
|
install -d ${PREFIX}/etc ${PREFIX}/etc/i2pd ${PREFIX}/etc/i2pd/tunnels.conf.d
|
||||||
ifneq ($(shell $(GREP) -c aes /proc/cpuinfo),0)
|
install -m 644 contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/etc/i2pd
|
||||||
CPU_FLAGS = -maes -DAESNI
|
install -d ${PREFIX}/share ${PREFIX}/share/doc ${PREFIX}/share/doc/i2pd
|
||||||
endif
|
install -m 644 ChangeLog LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/share/doc/i2pd
|
||||||
endif
|
install -d ${PREFIX}/share/i2pd
|
||||||
endif
|
@cp -R contrib/certificates ${PREFIX}/share/i2pd/
|
||||||
|
install -d ${PREFIX}/share/man ${PREFIX}/share/man/man1
|
||||||
|
@gzip -kf debian/i2pd.1 && install debian/i2pd.1.gz ${PREFIX}/share/man/man1
|
||||||
|
install -d ${PREFIX}/var ${PREFIX}/var/lib ${PREFIX}/var/lib/i2pd
|
||||||
|
@ln -sf ${PREFIX}/share/i2pd/certificates ${PREFIX}/var/lib/i2pd/certificates
|
||||||
|
@ln -sf ${PREFIX}/etc/i2pd/tunnels.conf.d ${PREFIX}/var/lib/i2pd/tunnels.d
|
||||||
|
@ln -sf ${PREFIX}/etc/i2pd/i2pd.conf ${PREFIX}/var/lib/i2pd/i2pd.conf
|
||||||
|
@ln -sf ${PREFIX}/etc/i2pd/subscriptions.txt ${PREFIX}/var/lib/i2pd/subscriptions.txt
|
||||||
|
@ln -sf ${PREFIX}/etc/i2pd/tunnels.conf ${PREFIX}/var/lib/i2pd/tunnels.conf
|
||||||
|
|||||||
110
Makefile.mingw
110
Makefile.mingw
@@ -1,53 +1,57 @@
|
|||||||
USE_WIN32_APP=yes
|
# Build application with GUI (tray, main window)
|
||||||
CXX = g++
|
USE_WIN32_APP := yes
|
||||||
WINDRES = windres
|
|
||||||
CXXFLAGS = -Os -D_MT -DWIN32 -D_WINDOWS -DWIN32_LEAN_AND_MEAN
|
WINDRES = windres
|
||||||
NEEDED_CXXFLAGS = -std=c++11
|
|
||||||
BOOST_SUFFIX = -mt
|
CXXFLAGS := $(CXX_DEBUG) -fPIC -msse
|
||||||
INCFLAGS = -I/usr/include/ -I/usr/local/include/
|
INCFLAGS = -I$(DAEMON_SRC_DIR) -IWin32
|
||||||
LDFLAGS = -Wl,-rpath,/usr/local/lib \
|
LDFLAGS := ${LD_DEBUG} -static
|
||||||
-L/usr/local/lib
|
|
||||||
|
NEEDED_CXXFLAGS += -std=c++17 -DWIN32_LEAN_AND_MEAN
|
||||||
# UPNP Support
|
|
||||||
ifeq ($(USE_UPNP),yes)
|
# Boost libraries suffix
|
||||||
CXXFLAGS += -DUSE_UPNP -DMINIUPNP_STATICLIB
|
BOOST_SUFFIX = -mt
|
||||||
LDLIBS = -Wl,-Bstatic -lminiupnpc
|
|
||||||
endif
|
# UPNP Support
|
||||||
|
ifeq ($(USE_UPNP),yes)
|
||||||
LDLIBS += \
|
CXXFLAGS += -DUSE_UPNP -DMINIUPNP_STATICLIB
|
||||||
-Wl,-Bstatic -lboost_system$(BOOST_SUFFIX) \
|
LDLIBS = -lminiupnpc
|
||||||
-Wl,-Bstatic -lboost_date_time$(BOOST_SUFFIX) \
|
endif
|
||||||
-Wl,-Bstatic -lboost_filesystem$(BOOST_SUFFIX) \
|
|
||||||
-Wl,-Bstatic -lboost_program_options$(BOOST_SUFFIX) \
|
LDLIBS += \
|
||||||
-Wl,-Bstatic -lssl \
|
-lboost_system$(BOOST_SUFFIX) \
|
||||||
-Wl,-Bstatic -lcrypto \
|
-lboost_date_time$(BOOST_SUFFIX) \
|
||||||
-Wl,-Bstatic -lz \
|
-lboost_filesystem$(BOOST_SUFFIX) \
|
||||||
-Wl,-Bstatic -lwsock32 \
|
-lboost_program_options$(BOOST_SUFFIX) \
|
||||||
-Wl,-Bstatic -lws2_32 \
|
-lssl \
|
||||||
-Wl,-Bstatic -lgdi32 \
|
-lcrypto \
|
||||||
-Wl,-Bstatic -liphlpapi \
|
-lz \
|
||||||
-static-libgcc -static-libstdc++ \
|
-lwsock32 \
|
||||||
-Wl,-Bstatic -lstdc++ \
|
-lws2_32 \
|
||||||
-Wl,-Bstatic -lpthread
|
-lgdi32 \
|
||||||
|
-liphlpapi \
|
||||||
ifeq ($(USE_WIN32_APP), yes)
|
-lole32 \
|
||||||
CXXFLAGS += -DWIN32_APP
|
-luuid \
|
||||||
LDFLAGS += -mwindows -s
|
-lpthread
|
||||||
DAEMON_RC += Win32/Resource.rc
|
|
||||||
DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC))
|
ifeq ($(USE_WIN32_APP), yes)
|
||||||
endif
|
NEEDED_CXXFLAGS += -DWIN32_APP
|
||||||
|
LDFLAGS += -mwindows
|
||||||
# don't change following line to ifeq ($(USE_AESNI),yes) !!!
|
DAEMON_RC += Win32/Resource.rc
|
||||||
ifeq ($(USE_AESNI),1)
|
DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC))
|
||||||
CPU_FLAGS = -maes -DAESNI
|
endif
|
||||||
else
|
|
||||||
CPU_FLAGS = -msse
|
ifeq ($(USE_WINXP_FLAGS), yes)
|
||||||
endif
|
NEEDED_CXXFLAGS += -DWINVER=0x0501 -D_WIN32_WINNT=0x0501
|
||||||
|
endif
|
||||||
ifeq ($(USE_ASLR),yes)
|
|
||||||
LDFLAGS += -Wl,--nxcompat -Wl,--high-entropy-va \
|
ifeq ($(USE_AESNI),yes)
|
||||||
-Wl,--dynamicbase,--export-all-symbols
|
NEEDED_CXXFLAGS += -D__AES__ -maes
|
||||||
endif
|
endif
|
||||||
|
|
||||||
obj/%.o : %.rc
|
ifeq ($(USE_ASLR),yes)
|
||||||
$(WINDRES) -i $< -o $@
|
LDFLAGS += -Wl,--nxcompat -Wl,--high-entropy-va -Wl,--dynamicbase,--export-all-symbols
|
||||||
|
endif
|
||||||
|
|
||||||
|
obj/%.o : %.rc | mk_obj_dir
|
||||||
|
$(WINDRES) -i $< -o $@
|
||||||
|
|||||||
40
Makefile.osx
40
Makefile.osx
@@ -1,25 +1,29 @@
|
|||||||
CXX = clang++
|
CXX = clang++
|
||||||
CXXFLAGS = -g -Wall -std=c++11 -DMAC_OSX
|
CXXFLAGS := ${CXX_DEBUG} -Wall -std=c++11 -DMAC_OSX
|
||||||
#CXXFLAGS = -g -O2 -Wall -std=c++11
|
INCFLAGS = -I/usr/local/include
|
||||||
INCFLAGS = -I/usr/local/include -I/usr/local/ssl/include
|
LDFLAGS := -Wl,-rpath,/usr/local/lib -L/usr/local/lib
|
||||||
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib -L/usr/local/ssl/lib
|
LDFLAGS += -Wl,-dead_strip
|
||||||
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
LDFLAGS += -Wl,-dead_strip_dylibs
|
||||||
|
LDFLAGS += -Wl,-bind_at_load
|
||||||
|
|
||||||
|
ifeq ($(USE_STATIC),yes)
|
||||||
|
LDLIBS = -lz /usr/local/lib/libcrypto.a /usr/local/lib/libssl.a /usr/local/lib/libboost_system.a /usr/local/lib/libboost_date_time.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread
|
||||||
|
else
|
||||||
|
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(USE_UPNP),yes)
|
ifeq ($(USE_UPNP),yes)
|
||||||
LDFLAGS += -ldl
|
LDFLAGS += -ldl
|
||||||
CXXFLAGS += -DUSE_UPNP
|
CXXFLAGS += -DUSE_UPNP
|
||||||
|
ifeq ($(USE_STATIC),yes)
|
||||||
|
LDLIBS += /usr/local/lib/libminiupnpc.a
|
||||||
|
else
|
||||||
|
LDLIBS += -lminiupnpc
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# OSX Notes
|
|
||||||
# http://www.hutsby.net/2011/08/macs-with-aes-ni.html
|
|
||||||
# Seems like all recent Mac's have AES-NI, after firmware upgrade 2.2
|
|
||||||
# Found no good way to detect it from command line. TODO: Might be some osx sysinfo magic
|
|
||||||
ifeq ($(USE_AESNI),yes)
|
ifeq ($(USE_AESNI),yes)
|
||||||
CXXFLAGS += -maes -DAESNI
|
CXXFLAGS += -D__AES__ -maes
|
||||||
|
else
|
||||||
|
CXXFLAGS += -msse
|
||||||
endif
|
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}/
|
|
||||||
|
|||||||
1032
NTCPSession.cpp
1032
NTCPSession.cpp
File diff suppressed because it is too large
Load Diff
182
NTCPSession.h
182
NTCPSession.h
@@ -1,182 +0,0 @@
|
|||||||
#ifndef NTCP_SESSION_H__
|
|
||||||
#define NTCP_SESSION_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
|
||||||
#include <thread>
|
|
||||||
#include <mutex>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include "Crypto.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "RouterInfo.h"
|
|
||||||
#include "I2NPProtocol.h"
|
|
||||||
#include "TransportSession.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace transport
|
|
||||||
{
|
|
||||||
struct NTCPPhase1
|
|
||||||
{
|
|
||||||
uint8_t pubKey[256];
|
|
||||||
uint8_t HXxorHI[32];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct NTCPPhase2
|
|
||||||
{
|
|
||||||
uint8_t pubKey[256];
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
uint8_t hxy[32];
|
|
||||||
uint8_t timestamp[4];
|
|
||||||
uint8_t filler[12];
|
|
||||||
} encrypted;
|
|
||||||
};
|
|
||||||
|
|
||||||
const size_t NTCP_MAX_MESSAGE_SIZE = 16384;
|
|
||||||
const size_t NTCP_BUFFER_SIZE = 4160; // fits 4 tunnel messages (4*1028)
|
|
||||||
const int NTCP_CONNECT_TIMEOUT = 5; // 5 seconds
|
|
||||||
const int NTCP_TERMINATION_TIMEOUT = 120; // 2 minutes
|
|
||||||
const int NTCP_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
|
|
||||||
const size_t NTCP_DEFAULT_PHASE3_SIZE = 2/*size*/ + i2p::data::DEFAULT_IDENTITY_SIZE/*387*/ + 4/*ts*/ + 15/*padding*/ + 40/*signature*/; // 448
|
|
||||||
const int NTCP_BAN_EXPIRATION_TIMEOUT = 70; // in second
|
|
||||||
const int NTCP_CLOCK_SKEW = 60; // in seconds
|
|
||||||
const int NTCP_MAX_OUTGOING_QUEUE_SIZE = 200; // how many messages we can queue up
|
|
||||||
|
|
||||||
class NTCPServer;
|
|
||||||
class NTCPSession: public TransportSession, public std::enable_shared_from_this<NTCPSession>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
NTCPSession (NTCPServer& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr);
|
|
||||||
~NTCPSession ();
|
|
||||||
void Terminate ();
|
|
||||||
void Done ();
|
|
||||||
|
|
||||||
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
|
|
||||||
bool IsEstablished () const { return m_IsEstablished; };
|
|
||||||
|
|
||||||
void ClientLogin ();
|
|
||||||
void ServerLogin ();
|
|
||||||
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
|
|
||||||
void Connected ();
|
|
||||||
void SendTimeSyncMessage ();
|
|
||||||
void SetIsEstablished (bool isEstablished) { m_IsEstablished = isEstablished; }
|
|
||||||
|
|
||||||
void CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key);
|
|
||||||
|
|
||||||
// client
|
|
||||||
void SendPhase3 ();
|
|
||||||
void HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
|
||||||
void HandlePhase2Received (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
|
||||||
void HandlePhase3Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA);
|
|
||||||
void HandlePhase4Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA);
|
|
||||||
|
|
||||||
//server
|
|
||||||
void SendPhase2 ();
|
|
||||||
void SendPhase4 (uint32_t tsA, uint32_t tsB);
|
|
||||||
void HandlePhase1Received (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
|
||||||
void HandlePhase2Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB);
|
|
||||||
void HandlePhase3Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB);
|
|
||||||
void HandlePhase3ExtraReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB, size_t paddingLen);
|
|
||||||
void HandlePhase3 (uint32_t tsB, size_t paddingLen);
|
|
||||||
void HandlePhase4Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
|
||||||
|
|
||||||
// common
|
|
||||||
void Receive ();
|
|
||||||
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
|
||||||
bool DecryptNextBlock (const uint8_t * encrypted);
|
|
||||||
|
|
||||||
void Send (std::shared_ptr<i2p::I2NPMessage> msg);
|
|
||||||
boost::asio::const_buffers_1 CreateMsgBuffer (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
void Send (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
|
|
||||||
void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector<std::shared_ptr<I2NPMessage> > msgs);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
NTCPServer& m_Server;
|
|
||||||
boost::asio::ip::tcp::socket m_Socket;
|
|
||||||
bool m_IsEstablished, m_IsTerminated;
|
|
||||||
|
|
||||||
i2p::crypto::CBCDecryption m_Decryption;
|
|
||||||
i2p::crypto::CBCEncryption m_Encryption;
|
|
||||||
|
|
||||||
struct Establisher
|
|
||||||
{
|
|
||||||
NTCPPhase1 phase1;
|
|
||||||
NTCPPhase2 phase2;
|
|
||||||
} * m_Establisher;
|
|
||||||
|
|
||||||
i2p::crypto::AESAlignedBuffer<NTCP_BUFFER_SIZE + 16> m_ReceiveBuffer;
|
|
||||||
i2p::crypto::AESAlignedBuffer<16> m_TimeSyncBuffer;
|
|
||||||
int m_ReceiveBufferOffset;
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> m_NextMessage;
|
|
||||||
size_t m_NextMessageOffset;
|
|
||||||
i2p::I2NPMessagesHandler m_Handler;
|
|
||||||
|
|
||||||
bool m_IsSending;
|
|
||||||
std::vector<std::shared_ptr<I2NPMessage> > m_SendQueue;
|
|
||||||
|
|
||||||
boost::asio::ip::address m_ConnectedFrom; // for ban
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: move to NTCP.h/.cpp
|
|
||||||
class NTCPServer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
NTCPServer ();
|
|
||||||
~NTCPServer ();
|
|
||||||
|
|
||||||
void Start ();
|
|
||||||
void Stop ();
|
|
||||||
|
|
||||||
bool AddNTCPSession (std::shared_ptr<NTCPSession> session);
|
|
||||||
void RemoveNTCPSession (std::shared_ptr<NTCPSession> session);
|
|
||||||
std::shared_ptr<NTCPSession> FindNTCPSession (const i2p::data::IdentHash& ident);
|
|
||||||
void Connect (const boost::asio::ip::address& address, int port, std::shared_ptr<NTCPSession> conn);
|
|
||||||
|
|
||||||
bool IsBoundV4() const { return m_NTCPAcceptor != nullptr; };
|
|
||||||
bool IsBoundV6() const { return m_NTCPV6Acceptor != nullptr; };
|
|
||||||
|
|
||||||
boost::asio::io_service& GetService () { return m_Service; };
|
|
||||||
void Ban (const boost::asio::ip::address& addr);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void Run ();
|
|
||||||
void HandleAccept (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error);
|
|
||||||
void HandleAcceptV6 (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error);
|
|
||||||
|
|
||||||
void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
|
|
||||||
|
|
||||||
// timer
|
|
||||||
void ScheduleTermination ();
|
|
||||||
void HandleTerminationTimer (const boost::system::error_code& ecode);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
bool m_IsRunning;
|
|
||||||
std::thread * m_Thread;
|
|
||||||
boost::asio::io_service m_Service;
|
|
||||||
boost::asio::io_service::work m_Work;
|
|
||||||
boost::asio::deadline_timer m_TerminationTimer;
|
|
||||||
boost::asio::ip::tcp::acceptor * m_NTCPAcceptor, * m_NTCPV6Acceptor;
|
|
||||||
std::map<i2p::data::IdentHash, std::shared_ptr<NTCPSession> > m_NTCPSessions; // access from m_Thread only
|
|
||||||
std::map<boost::asio::ip::address, uint32_t> m_BanList; // IP -> ban expiration time in seconds
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// for HTTP/I2PControl
|
|
||||||
const decltype(m_NTCPSessions)& GetNTCPSessions () const { return m_NTCPSessions; };
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
157
NetDb.h
157
NetDb.h
@@ -1,157 +0,0 @@
|
|||||||
#ifndef NETDB_H__
|
|
||||||
#define NETDB_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <set>
|
|
||||||
#include <map>
|
|
||||||
#include <list>
|
|
||||||
#include <string>
|
|
||||||
#include <thread>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
#include "Base.h"
|
|
||||||
#include "Gzip.h"
|
|
||||||
#include "FS.h"
|
|
||||||
#include "Queue.h"
|
|
||||||
#include "I2NPProtocol.h"
|
|
||||||
#include "RouterInfo.h"
|
|
||||||
#include "LeaseSet.h"
|
|
||||||
#include "Tunnel.h"
|
|
||||||
#include "TunnelPool.h"
|
|
||||||
#include "Reseed.h"
|
|
||||||
#include "NetDbRequests.h"
|
|
||||||
#include "Family.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace data
|
|
||||||
{
|
|
||||||
const int NETDB_MIN_ROUTERS = 90;
|
|
||||||
const int NETDB_FLOODFILL_EXPIRATION_TIMEOUT = 60*60; // 1 hour, in seconds
|
|
||||||
const int NETDB_INTRODUCEE_EXPIRATION_TIMEOUT = 65*60;
|
|
||||||
const int NETDB_MIN_EXPIRATION_TIMEOUT = 90*60; // 1.5 hours
|
|
||||||
const int NETDB_MAX_EXPIRATION_TIMEOUT = 27*60*60; // 27 hours
|
|
||||||
const int NETDB_PUBLISH_INTERVAL = 60*40;
|
|
||||||
|
|
||||||
/** function for visiting a leaseset stored in a floodfill */
|
|
||||||
typedef std::function<void(const IdentHash, std::shared_ptr<LeaseSet>)> LeaseSetVisitor;
|
|
||||||
|
|
||||||
/** function for visiting a router info we have locally */
|
|
||||||
typedef std::function<void(std::shared_ptr<const i2p::data::RouterInfo>)> RouterInfoVisitor;
|
|
||||||
|
|
||||||
/** function for visiting a router info and determining if we want to use it */
|
|
||||||
typedef std::function<bool(std::shared_ptr<const i2p::data::RouterInfo>)> RouterInfoFilter;
|
|
||||||
|
|
||||||
class NetDb
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
NetDb ();
|
|
||||||
~NetDb ();
|
|
||||||
|
|
||||||
void Start ();
|
|
||||||
void Stop ();
|
|
||||||
|
|
||||||
bool AddRouterInfo (const uint8_t * buf, int len);
|
|
||||||
bool AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len);
|
|
||||||
bool AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
|
||||||
std::shared_ptr<RouterInfo> FindRouter (const IdentHash& ident) const;
|
|
||||||
std::shared_ptr<LeaseSet> FindLeaseSet (const IdentHash& destination) const;
|
|
||||||
std::shared_ptr<RouterProfile> FindRouterProfile (const IdentHash& ident) const;
|
|
||||||
|
|
||||||
void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr);
|
|
||||||
void RequestDestinationFrom (const IdentHash& destination, const IdentHash & from, bool exploritory, RequestedDestination::RequestComplete requestComplete = nullptr);
|
|
||||||
|
|
||||||
void HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> msg);
|
|
||||||
void HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg);
|
|
||||||
void HandleDatabaseLookupMsg (std::shared_ptr<const I2NPMessage> msg);
|
|
||||||
|
|
||||||
std::shared_ptr<const RouterInfo> GetRandomRouter () const;
|
|
||||||
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
|
|
||||||
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
|
|
||||||
std::shared_ptr<const RouterInfo> GetRandomPeerTestRouter (bool v4only = true) const;
|
|
||||||
std::shared_ptr<const RouterInfo> GetRandomIntroducer () const;
|
|
||||||
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
|
|
||||||
std::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num,
|
|
||||||
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; };
|
|
||||||
|
|
||||||
// for web interface
|
|
||||||
int GetNumRouters () const { return m_RouterInfos.size (); };
|
|
||||||
int GetNumFloodfills () const { return m_Floodfills.size (); };
|
|
||||||
int GetNumLeaseSets () const { return m_LeaseSets.size (); };
|
|
||||||
|
|
||||||
/** visit all lease sets we currently store */
|
|
||||||
void VisitLeaseSets(LeaseSetVisitor v);
|
|
||||||
/** visit all router infos we have currently on disk, usually insanely expensive, does not access in memory RI */
|
|
||||||
void VisitStoredRouterInfos(RouterInfoVisitor v);
|
|
||||||
/** visit all router infos we have loaded in memory, cheaper than VisitLocalRouterInfos but locks access while visiting */
|
|
||||||
void VisitRouterInfos(RouterInfoVisitor v);
|
|
||||||
/** visit N random router that match using filter, then visit them with a visitor, return number of RouterInfos that were visited */
|
|
||||||
size_t VisitRandomRouterInfos(RouterInfoFilter f, RouterInfoVisitor v, size_t n);
|
|
||||||
|
|
||||||
void ClearRouterInfos () { m_RouterInfos.clear (); };
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void Load ();
|
|
||||||
bool LoadRouterInfo (const std::string & path);
|
|
||||||
void SaveUpdated ();
|
|
||||||
void Run (); // exploratory thread
|
|
||||||
void Explore (int numDestinations);
|
|
||||||
void Publish ();
|
|
||||||
void ManageLeaseSets ();
|
|
||||||
void ManageRequests ();
|
|
||||||
void ManageLookupResponses ();
|
|
||||||
|
|
||||||
void ReseedFromFloodfill(const RouterInfo & ri, int numRouters=40, int numFloodfills=20);
|
|
||||||
|
|
||||||
template<typename Filter>
|
|
||||||
std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
mutable std::mutex m_LeaseSetsMutex;
|
|
||||||
std::map<IdentHash, std::shared_ptr<LeaseSet> > m_LeaseSets;
|
|
||||||
mutable std::mutex m_RouterInfosMutex;
|
|
||||||
std::map<IdentHash, std::shared_ptr<RouterInfo> > m_RouterInfos;
|
|
||||||
mutable std::mutex m_FloodfillsMutex;
|
|
||||||
std::list<std::shared_ptr<RouterInfo> > m_Floodfills;
|
|
||||||
|
|
||||||
bool m_IsRunning;
|
|
||||||
uint64_t m_LastLoad;
|
|
||||||
std::thread * m_Thread;
|
|
||||||
i2p::util::Queue<std::shared_ptr<const I2NPMessage> > m_Queue; // of I2NPDatabaseStoreMsg
|
|
||||||
|
|
||||||
GzipInflator m_Inflator;
|
|
||||||
Reseeder * m_Reseeder;
|
|
||||||
Families m_Families;
|
|
||||||
i2p::fs::HashedStorage m_Storage;
|
|
||||||
|
|
||||||
friend class NetDbRequests;
|
|
||||||
NetDbRequests m_Requests;
|
|
||||||
|
|
||||||
/** router info we are bootstrapping from or nullptr if we are not currently doing that*/
|
|
||||||
std::shared_ptr<RouterInfo> m_FloodfillBootstrap;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
99
README.md
99
README.md
@@ -1,21 +1,41 @@
|
|||||||
|
[](https://github.com/PurpleI2P/i2pd/releases/latest)
|
||||||
|
[](https://snapcraft.io/i2pd)
|
||||||
|
[](https://github.com/PurpleI2P/i2pd/blob/openssl/LICENSE)
|
||||||
|
[](https://repology.org/project/i2pd/versions)
|
||||||
|
[](https://hub.docker.com/r/purplei2p/i2pd)
|
||||||
|
[](https://crowdin.com/project/i2pd)
|
||||||
|
|
||||||
|
*note: i2pd for Android can be found in [i2pd-android](https://github.com/PurpleI2P/i2pd-android) repository and with Qt GUI in [i2pd-qt](https://github.com/PurpleI2P/i2pd-qt) repository*
|
||||||
|
|
||||||
i2pd
|
i2pd
|
||||||
====
|
====
|
||||||
|
|
||||||
[Русская версия](https://github.com/PurpleI2P/i2pd_docs_ru/blob/master/README.md)
|
[Русская версия](https://github.com/PurpleI2P/i2pd_docs_ru/blob/master/README.md)
|
||||||
|
|
||||||
i2pd (I2P Daemon) is a full-featured C++ implementation of I2P client.
|
i2pd (I2P Daemon) is a full-featured C++ implementation of I2P client.
|
||||||
|
|
||||||
I2P (Invisible Internet Protocol) is a universal anonymous network layer.
|
I2P (Invisible Internet Protocol) is a universal anonymous network layer.
|
||||||
All communications over I2P are anonymous and end-to-end encrypted, participants
|
All communications over I2P are anonymous and end-to-end encrypted, participants
|
||||||
don't reveal their real IP addresses.
|
don't reveal their real IP addresses.
|
||||||
|
|
||||||
I2P client is a software used for building and using anonymous I2P
|
I2P client is a software used for building and using anonymous I2P
|
||||||
networks. Such networks are commonly used for anonymous peer-to-peer
|
networks. Such networks are commonly used for anonymous peer-to-peer
|
||||||
applications (filesharing, cryptocurrencies) and anonymous client-server
|
applications (filesharing, cryptocurrencies) and anonymous client-server
|
||||||
applications (websites, instant messengers, chat-servers).
|
applications (websites, instant messengers, chat-servers).
|
||||||
|
|
||||||
I2P allows people from all around the world to communicate and share information
|
I2P allows people from all around the world to communicate and share information
|
||||||
without restrictions.
|
without restrictions.
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
|
||||||
|
* Distributed anonymous networking framework
|
||||||
|
* End-to-end encrypted communications
|
||||||
|
* Small footprint, simple dependencies, fast performance
|
||||||
|
* Rich set of APIs for developers of secure applications
|
||||||
|
|
||||||
|
Resources
|
||||||
|
---------
|
||||||
|
|
||||||
* [Website](http://i2pd.website)
|
* [Website](http://i2pd.website)
|
||||||
* [Documentation](https://i2pd.readthedocs.io/en/latest/)
|
* [Documentation](https://i2pd.readthedocs.io/en/latest/)
|
||||||
@@ -27,37 +47,68 @@ without restrictions.
|
|||||||
Installing
|
Installing
|
||||||
----------
|
----------
|
||||||
|
|
||||||
The easiest way to install i2pd is by using
|
The easiest way to install i2pd is by using precompiled packages and binaries.
|
||||||
[precompiled binaries](https://github.com/PurpleI2P/i2pd/releases/latest).
|
You can fetch most of them on [release](https://github.com/PurpleI2P/i2pd/releases/latest) page.
|
||||||
See [documentation](https://i2pd.readthedocs.io/en/latest/) for how to build
|
Please see [documentation](https://i2pd.readthedocs.io/en/latest/user-guide/install/) for more info.
|
||||||
i2pd from source on your OS.
|
|
||||||
|
Building
|
||||||
|
--------
|
||||||
|
See [documentation](https://i2pd.readthedocs.io/en/latest/) for how to build
|
||||||
|
i2pd from source on your OS.
|
||||||
|
|
||||||
|
note: i2pd with Qt GUI can be found in [i2pd-qt](https://github.com/PurpleI2P/i2pd-qt) repository and for android in [i2pd-android](https://github.com/PurpleI2P/i2pd-android) repository.
|
||||||
|
|
||||||
|
|
||||||
|
Build instructions:
|
||||||
|
|
||||||
|
* [unix](https://i2pd.readthedocs.io/en/latest/devs/building/unix/)
|
||||||
|
* [windows](https://i2pd.readthedocs.io/en/latest/devs/building/windows/)
|
||||||
|
* [iOS](https://i2pd.readthedocs.io/en/latest/devs/building/ios/)
|
||||||
|
* [android](https://i2pd.readthedocs.io/en/latest/devs/building/android/)
|
||||||
|
|
||||||
|
|
||||||
**Supported systems:**
|
**Supported systems:**
|
||||||
|
|
||||||
* Linux x86/x64 - [](https://travis-ci.org/PurpleI2P/i2pd)
|
* GNU/Linux (Debian, Ubuntu, etc) - [](https://github.com/PurpleI2P/i2pd/actions/workflows/build.yml)
|
||||||
* Windows - [](https://ci.appveyor.com/project/PurpleI2P/i2pd)
|
* CentOS, Fedora, Mageia - [](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/)
|
||||||
* Mac OS X
|
* Alpine, ArchLinux, openSUSE, Gentoo, etc.
|
||||||
* FreeBSD
|
* Windows - [](https://github.com/PurpleI2P/i2pd/actions/workflows/build-windows.yml)
|
||||||
* Android
|
* Mac OS - [](https://github.com/PurpleI2P/i2pd/actions/workflows/build-osx.yml)
|
||||||
|
* Docker image - [](https://github.com/PurpleI2P/i2pd/actions/workflows/docker.yml)
|
||||||
|
* Snap - [](https://snapcraft.io/i2pd) [](https://snapcraft.io/i2pd)
|
||||||
|
* FreeBSD - [](https://github.com/PurpleI2P/i2pd/actions/workflows/build-freebsd.yml)
|
||||||
|
* Android - [](https://github.com/PurpleI2P/i2pd-android/actions/workflows/android.yml)
|
||||||
* iOS
|
* iOS
|
||||||
|
|
||||||
Using i2pd
|
Using i2pd
|
||||||
----------
|
----------
|
||||||
|
|
||||||
See [documentation](https://i2pd.readthedocs.io/en/latest/usage.html) and
|
See [documentation](https://i2pd.readthedocs.io/en/latest/user-guide/run/) and
|
||||||
[example config file](https://github.com/PurpleI2P/i2pd/blob/openssl/docs/i2pd.conf).
|
[example config file](https://github.com/PurpleI2P/i2pd/blob/openssl/contrib/i2pd.conf).
|
||||||
|
|
||||||
|
Localization
|
||||||
|
------------
|
||||||
|
|
||||||
|
You can help us with translation i2pd to your language using Crowdin platform!
|
||||||
|
Translation project can be found [here](https://crowdin.com/project/i2pd).
|
||||||
|
|
||||||
|
New languages can be requested on project's [discussion page](https://crowdin.com/project/i2pd/discussions).
|
||||||
|
|
||||||
|
Current status: [](https://crowdin.com/project/i2pd)
|
||||||
|
|
||||||
Donations
|
Donations
|
||||||
---------
|
---------
|
||||||
|
|
||||||
BTC: 1K7Ds6KUeR8ya287UC4rYTjvC96vXyZbDY
|
BTC: 3MDoGJW9TLMTCDGrR9bLgWXfm6sjmgy86f
|
||||||
DASH: Xw8YUrQpYzP9tZBmbjqxS3M97Q7v3vJKUF
|
|
||||||
LTC: LKQirrYrDeTuAPnpYq5y7LVKtywfkkHi59
|
LTC: LKQirrYrDeTuAPnpYq5y7LVKtywfkkHi59
|
||||||
ANC: AQJYweYYUqM1nVfLqfoSMpUMfzxvS4Xd7z
|
ETH: 0x9e5bac70d20d1079ceaa111127f4fb3bccce379d
|
||||||
DOGE: DNXLQKziRPAsD9H3DFNjk4fLQrdaSX893Y
|
DASH: Xw8YUrQpYzP9tZBmbjqxS3M97Q7v3vJKUF
|
||||||
|
ZEC: t1cTckLuXsr1dwVrK4NDzfhehss4NvMadAJ
|
||||||
|
GST: GbD2JSQHBHCKLa9WTHmigJRpyFgmBj4woG
|
||||||
|
XMR: 497pJc7X4xqKvcLBLpSUtRgWqMMyo24u4btCos3cak6gbMkpobgSU6492ztUcUBghyeHpYeczB55s38NpuHoH5WGNSPDRMH
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
|
||||||
This project is licensed under the BSD 3-clause license, which can be found in the file
|
This project is licensed under the BSD 3-clause license, which can be found in the file
|
||||||
LICENSE in the root of the project source code.
|
LICENSE in the root of the project source code.
|
||||||
|
|||||||
500
Reseed.cpp
500
Reseed.cpp
@@ -1,500 +0,0 @@
|
|||||||
#include <string.h>
|
|
||||||
#include <fstream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include <boost/asio/ssl.hpp>
|
|
||||||
#include <boost/algorithm/string.hpp>
|
|
||||||
#include <openssl/ssl.h>
|
|
||||||
#include <openssl/err.h>
|
|
||||||
#include <zlib.h>
|
|
||||||
|
|
||||||
#include "Crypto.h"
|
|
||||||
#include "I2PEndian.h"
|
|
||||||
#include "Reseed.h"
|
|
||||||
#include "FS.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "NetDb.h"
|
|
||||||
#include "HTTP.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include "Config.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace data
|
|
||||||
{
|
|
||||||
|
|
||||||
Reseeder::Reseeder()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Reseeder::~Reseeder()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int Reseeder::ReseedNowSU3 ()
|
|
||||||
{
|
|
||||||
std::string reseedURLs; i2p::config::GetOption("reseed.urls", reseedURLs);
|
|
||||||
std::vector<std::string> httpsReseedHostList;
|
|
||||||
boost::split(httpsReseedHostList, reseedURLs, boost::is_any_of(","), boost::token_compress_on);
|
|
||||||
|
|
||||||
std::string filename; i2p::config::GetOption("reseed.file", filename);
|
|
||||||
if (filename.length() > 0) // reseed file is specified
|
|
||||||
{
|
|
||||||
if (filename.length() > 8 && filename.substr(0, 8) == "https://")
|
|
||||||
{
|
|
||||||
return ReseedFromSU3 (filename); // reseed from https URL
|
|
||||||
} else {
|
|
||||||
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 reseedUrl = httpsReseedHostList[ind] + "i2pseeds.su3";
|
|
||||||
return ReseedFromSU3 (reseedUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Reseeder::ReseedFromSU3 (const std::string& url)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Reseed: Downloading SU3 from ", url);
|
|
||||||
std::string su3 = HttpsRequest (url);
|
|
||||||
if (su3.length () > 0)
|
|
||||||
{
|
|
||||||
std::stringstream s(su3);
|
|
||||||
return ProcessSU3Stream (s);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Reseed: SU3 download failed");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int Reseeder::ProcessSU3File (const char * filename)
|
|
||||||
{
|
|
||||||
std::ifstream s(filename, std::ifstream::binary);
|
|
||||||
if (s.is_open ())
|
|
||||||
return ProcessSU3Stream (s);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Reseed: Can't open file ", filename);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char SU3_MAGIC_NUMBER[]="I2Psu3";
|
|
||||||
const uint32_t ZIP_HEADER_SIGNATURE = 0x04034B50;
|
|
||||||
const uint32_t ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE = 0x02014B50;
|
|
||||||
const uint16_t ZIP_BIT_FLAG_DATA_DESCRIPTOR = 0x0008;
|
|
||||||
int Reseeder::ProcessSU3Stream (std::istream& s)
|
|
||||||
{
|
|
||||||
char magicNumber[7];
|
|
||||||
s.read (magicNumber, 7); // magic number and zero byte 6
|
|
||||||
if (strcmp (magicNumber, SU3_MAGIC_NUMBER))
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Reseed: Unexpected SU3 magic number");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
s.seekg (1, std::ios::cur); // su3 file format version
|
|
||||||
SigningKeyType signatureType;
|
|
||||||
s.read ((char *)&signatureType, 2); // signature type
|
|
||||||
signatureType = be16toh (signatureType);
|
|
||||||
uint16_t signatureLength;
|
|
||||||
s.read ((char *)&signatureLength, 2); // signature length
|
|
||||||
signatureLength = be16toh (signatureLength);
|
|
||||||
s.seekg (1, std::ios::cur); // unused
|
|
||||||
uint8_t versionLength;
|
|
||||||
s.read ((char *)&versionLength, 1); // version length
|
|
||||||
s.seekg (1, std::ios::cur); // unused
|
|
||||||
uint8_t signerIDLength;
|
|
||||||
s.read ((char *)&signerIDLength, 1); // signer ID length
|
|
||||||
uint64_t contentLength;
|
|
||||||
s.read ((char *)&contentLength, 8); // content length
|
|
||||||
contentLength = be64toh (contentLength);
|
|
||||||
s.seekg (1, std::ios::cur); // unused
|
|
||||||
uint8_t fileType;
|
|
||||||
s.read ((char *)&fileType, 1); // file type
|
|
||||||
if (fileType != 0x00) // zip file
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Reseed: Can't handle file type ", (int)fileType);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
s.seekg (1, std::ios::cur); // unused
|
|
||||||
uint8_t contentType;
|
|
||||||
s.read ((char *)&contentType, 1); // content type
|
|
||||||
if (contentType != 0x03) // reseed data
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Reseed: Unexpected content type ", (int)contentType);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
s.seekg (12, std::ios::cur); // unused
|
|
||||||
|
|
||||||
s.seekg (versionLength, std::ios::cur); // skip version
|
|
||||||
char signerID[256];
|
|
||||||
s.read (signerID, signerIDLength); // signerID
|
|
||||||
signerID[signerIDLength] = 0;
|
|
||||||
|
|
||||||
bool verify; i2p::config::GetOption("reseed.verify", verify);
|
|
||||||
if (verify)
|
|
||||||
{
|
|
||||||
//try to verify signature
|
|
||||||
auto it = m_SigningKeys.find (signerID);
|
|
||||||
if (it != m_SigningKeys.end ())
|
|
||||||
{
|
|
||||||
// TODO: implement all signature types
|
|
||||||
if (signatureType == SIGNING_KEY_TYPE_RSA_SHA512_4096)
|
|
||||||
{
|
|
||||||
size_t pos = s.tellg ();
|
|
||||||
size_t tbsLen = pos + contentLength;
|
|
||||||
uint8_t * tbs = new uint8_t[tbsLen];
|
|
||||||
s.seekg (0, std::ios::beg);
|
|
||||||
s.read ((char *)tbs, tbsLen);
|
|
||||||
uint8_t * signature = new uint8_t[signatureLength];
|
|
||||||
s.read ((char *)signature, signatureLength);
|
|
||||||
// RSA-raw
|
|
||||||
{
|
|
||||||
// calculate digest
|
|
||||||
uint8_t digest[64];
|
|
||||||
SHA512 (tbs, tbsLen, digest);
|
|
||||||
// encrypt signature
|
|
||||||
BN_CTX * bnctx = BN_CTX_new ();
|
|
||||||
BIGNUM * s = BN_new (), * n = BN_new ();
|
|
||||||
BN_bin2bn (signature, signatureLength, s);
|
|
||||||
BN_bin2bn (it->second, i2p::crypto::RSASHA5124096_KEY_LENGTH, n);
|
|
||||||
BN_mod_exp (s, s, i2p::crypto::GetRSAE (), n, bnctx); // s = s^e mod n
|
|
||||||
uint8_t * enSigBuf = new uint8_t[signatureLength];
|
|
||||||
i2p::crypto::bn2buf (s, enSigBuf, signatureLength);
|
|
||||||
// digest is right aligned
|
|
||||||
// we can't use RSA_verify due wrong padding in SU3
|
|
||||||
if (memcmp (enSigBuf + (signatureLength - 64), digest, 64))
|
|
||||||
LogPrint (eLogWarning, "Reseed: SU3 signature verification failed");
|
|
||||||
else
|
|
||||||
verify = false; // verified
|
|
||||||
delete[] enSigBuf;
|
|
||||||
BN_free (s); BN_free (n);
|
|
||||||
BN_CTX_free (bnctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete[] signature;
|
|
||||||
delete[] tbs;
|
|
||||||
s.seekg (pos, std::ios::beg);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "Reseed: Signature type ", signatureType, " is not supported");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "Reseed: Certificate for ", signerID, " not loaded");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verify) // not verified
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Reseed: SU3 verification failed");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle content
|
|
||||||
int numFiles = 0;
|
|
||||||
size_t contentPos = s.tellg ();
|
|
||||||
while (!s.eof ())
|
|
||||||
{
|
|
||||||
uint32_t signature;
|
|
||||||
s.read ((char *)&signature, 4);
|
|
||||||
signature = le32toh (signature);
|
|
||||||
if (signature == ZIP_HEADER_SIGNATURE)
|
|
||||||
{
|
|
||||||
// next local file
|
|
||||||
s.seekg (2, std::ios::cur); // version
|
|
||||||
uint16_t bitFlag;
|
|
||||||
s.read ((char *)&bitFlag, 2);
|
|
||||||
bitFlag = le16toh (bitFlag);
|
|
||||||
uint16_t compressionMethod;
|
|
||||||
s.read ((char *)&compressionMethod, 2);
|
|
||||||
compressionMethod = le16toh (compressionMethod);
|
|
||||||
s.seekg (4, std::ios::cur); // skip fields we don't care about
|
|
||||||
uint32_t compressedSize, uncompressedSize;
|
|
||||||
uint32_t crc_32;
|
|
||||||
s.read ((char *)&crc_32, 4);
|
|
||||||
crc_32 = le32toh (crc_32);
|
|
||||||
s.read ((char *)&compressedSize, 4);
|
|
||||||
compressedSize = le32toh (compressedSize);
|
|
||||||
s.read ((char *)&uncompressedSize, 4);
|
|
||||||
uncompressedSize = le32toh (uncompressedSize);
|
|
||||||
uint16_t fileNameLength, extraFieldLength;
|
|
||||||
s.read ((char *)&fileNameLength, 2);
|
|
||||||
fileNameLength = le16toh (fileNameLength);
|
|
||||||
if ( fileNameLength > 255 ) {
|
|
||||||
// too big
|
|
||||||
LogPrint(eLogError, "Reseed: SU3 fileNameLength too large: ", fileNameLength);
|
|
||||||
return numFiles;
|
|
||||||
}
|
|
||||||
s.read ((char *)&extraFieldLength, 2);
|
|
||||||
extraFieldLength = le16toh (extraFieldLength);
|
|
||||||
char localFileName[255];
|
|
||||||
s.read (localFileName, fileNameLength);
|
|
||||||
localFileName[fileNameLength] = 0;
|
|
||||||
s.seekg (extraFieldLength, std::ios::cur);
|
|
||||||
// take care about data desriptor if presented
|
|
||||||
if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR)
|
|
||||||
{
|
|
||||||
size_t pos = s.tellg ();
|
|
||||||
if (!FindZipDataDescriptor (s))
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Reseed: SU3 archive data descriptor not found");
|
|
||||||
return numFiles;
|
|
||||||
}
|
|
||||||
s.read ((char *)&crc_32, 4);
|
|
||||||
crc_32 = le32toh (crc_32);
|
|
||||||
s.read ((char *)&compressedSize, 4);
|
|
||||||
compressedSize = le32toh (compressedSize) + 4; // ??? we must consider signature as part of compressed data
|
|
||||||
s.read ((char *)&uncompressedSize, 4);
|
|
||||||
uncompressedSize = le32toh (uncompressedSize);
|
|
||||||
|
|
||||||
// now we know compressed and uncompressed size
|
|
||||||
s.seekg (pos, std::ios::beg); // back to compressed data
|
|
||||||
}
|
|
||||||
|
|
||||||
LogPrint (eLogDebug, "Reseed: Proccessing file ", localFileName, " ", compressedSize, " bytes");
|
|
||||||
if (!compressedSize)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Reseed: Unexpected size 0. Skipped");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t * compressed = new uint8_t[compressedSize];
|
|
||||||
s.read ((char *)compressed, compressedSize);
|
|
||||||
if (compressionMethod) // we assume Deflate
|
|
||||||
{
|
|
||||||
z_stream inflator;
|
|
||||||
memset (&inflator, 0, sizeof (inflator));
|
|
||||||
inflateInit2 (&inflator, -MAX_WBITS); // no zlib header
|
|
||||||
uint8_t * uncompressed = new uint8_t[uncompressedSize];
|
|
||||||
inflator.next_in = compressed;
|
|
||||||
inflator.avail_in = compressedSize;
|
|
||||||
inflator.next_out = uncompressed;
|
|
||||||
inflator.avail_out = uncompressedSize;
|
|
||||||
int err;
|
|
||||||
if ((err = inflate (&inflator, Z_SYNC_FLUSH)) >= 0)
|
|
||||||
{
|
|
||||||
uncompressedSize -= inflator.avail_out;
|
|
||||||
if (crc32 (0, uncompressed, uncompressedSize) == crc_32)
|
|
||||||
{
|
|
||||||
i2p::data::netdb.AddRouterInfo (uncompressed, uncompressedSize);
|
|
||||||
numFiles++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Reseed: CRC32 verification failed");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Reseed: SU3 decompression error ", err);
|
|
||||||
delete[] uncompressed;
|
|
||||||
inflateEnd (&inflator);
|
|
||||||
}
|
|
||||||
else // no compression
|
|
||||||
{
|
|
||||||
i2p::data::netdb.AddRouterInfo (compressed, compressedSize);
|
|
||||||
numFiles++;
|
|
||||||
}
|
|
||||||
delete[] compressed;
|
|
||||||
if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR)
|
|
||||||
s.seekg (12, std::ios::cur); // skip data descriptor section if presented (12 = 16 - 4)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (signature != ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE)
|
|
||||||
LogPrint (eLogWarning, "Reseed: Missing zip central directory header");
|
|
||||||
break; // no more files
|
|
||||||
}
|
|
||||||
size_t end = s.tellg ();
|
|
||||||
if (end - contentPos >= contentLength)
|
|
||||||
break; // we are beyond contentLength
|
|
||||||
}
|
|
||||||
if (numFiles) // check if routers are not outdated
|
|
||||||
{
|
|
||||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
int numOutdated = 0;
|
|
||||||
i2p::data::netdb.VisitRouterInfos (
|
|
||||||
[&numOutdated, ts](std::shared_ptr<const RouterInfo> r)
|
|
||||||
{
|
|
||||||
if (r && ts > r->GetTimestamp () + 10*i2p::data::NETDB_MAX_EXPIRATION_TIMEOUT*1000LL) // 270 hours
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Reseed: router ", r->GetIdentHash().ToBase64 (), " is outdated by ", (ts - r->GetTimestamp ())/1000LL/3600LL, " hours");
|
|
||||||
numOutdated++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (numOutdated > numFiles/2) // more than half
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Reseed: mammoth's shit\n"
|
|
||||||
" *_____*\n"
|
|
||||||
" *_*****_*\n"
|
|
||||||
" *_(O)_(O)_*\n"
|
|
||||||
" **____V____**\n"
|
|
||||||
" **_________**\n"
|
|
||||||
" **_________**\n"
|
|
||||||
" *_________*\n"
|
|
||||||
" ***___***");
|
|
||||||
i2p::data::netdb.ClearRouterInfos ();
|
|
||||||
numFiles = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return numFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint8_t ZIP_DATA_DESCRIPTOR_SIGNATURE[] = { 0x50, 0x4B, 0x07, 0x08 };
|
|
||||||
bool Reseeder::FindZipDataDescriptor (std::istream& s)
|
|
||||||
{
|
|
||||||
size_t nextInd = 0;
|
|
||||||
while (!s.eof ())
|
|
||||||
{
|
|
||||||
uint8_t nextByte;
|
|
||||||
s.read ((char *)&nextByte, 1);
|
|
||||||
if (nextByte == ZIP_DATA_DESCRIPTOR_SIGNATURE[nextInd])
|
|
||||||
{
|
|
||||||
nextInd++;
|
|
||||||
if (nextInd >= sizeof (ZIP_DATA_DESCRIPTOR_SIGNATURE))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
nextInd = 0;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Reseeder::LoadCertificate (const std::string& filename)
|
|
||||||
{
|
|
||||||
SSL_CTX * ctx = SSL_CTX_new (TLSv1_method ());
|
|
||||||
int ret = SSL_CTX_use_certificate_file (ctx, filename.c_str (), SSL_FILETYPE_PEM);
|
|
||||||
if (ret)
|
|
||||||
{
|
|
||||||
SSL * ssl = SSL_new (ctx);
|
|
||||||
X509 * cert = SSL_get_certificate (ssl);
|
|
||||||
// verify
|
|
||||||
if (cert)
|
|
||||||
{
|
|
||||||
// extract issuer name
|
|
||||||
char name[100];
|
|
||||||
X509_NAME_oneline (X509_get_issuer_name(cert), name, 100);
|
|
||||||
char * cn = strstr (name, "CN=");
|
|
||||||
if (cn)
|
|
||||||
{
|
|
||||||
cn += 3;
|
|
||||||
char * terminator = strchr (cn, '/');
|
|
||||||
if (terminator) terminator[0] = 0;
|
|
||||||
}
|
|
||||||
// extract RSA key (we need n only, e = 65537)
|
|
||||||
RSA * key = EVP_PKEY_get0_RSA (X509_get_pubkey (cert));
|
|
||||||
const BIGNUM * n, * e, * d;
|
|
||||||
RSA_get0_key(key, &n, &e, &d);
|
|
||||||
PublicKey value;
|
|
||||||
i2p::crypto::bn2buf (n, value, 512);
|
|
||||||
if (cn)
|
|
||||||
m_SigningKeys[cn] = value;
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Reseed: Can't find CN field in ", filename);
|
|
||||||
}
|
|
||||||
SSL_free (ssl);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Reseed: Can't open certificate file ", filename);
|
|
||||||
SSL_CTX_free (ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Reseeder::LoadCertificates ()
|
|
||||||
{
|
|
||||||
std::string certDir = i2p::fs::DataDirPath("certificates", "reseed");
|
|
||||||
std::vector<std::string> files;
|
|
||||||
int numCertificates = 0;
|
|
||||||
|
|
||||||
if (!i2p::fs::ReadDir(certDir, files)) {
|
|
||||||
LogPrint(eLogWarning, "Reseed: Can't load reseed certificates from ", certDir);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const std::string & file : files) {
|
|
||||||
if (file.compare(file.size() - 4, 4, ".crt") != 0) {
|
|
||||||
LogPrint(eLogWarning, "Reseed: ignoring file ", file);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
LoadCertificate (file);
|
|
||||||
numCertificates++;
|
|
||||||
}
|
|
||||||
LogPrint (eLogInfo, "Reseed: ", numCertificates, " certificates loaded");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Reseeder::HttpsRequest (const std::string& address)
|
|
||||||
{
|
|
||||||
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 (url.host, std::to_string(url.port)), ecode);
|
|
||||||
if (!ecode)
|
|
||||||
{
|
|
||||||
boost::asio::ssl::context ctx(service, boost::asio::ssl::context::sslv23);
|
|
||||||
ctx.set_verify_mode(boost::asio::ssl::context::verify_none);
|
|
||||||
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> s(service, ctx);
|
|
||||||
s.lowest_layer().connect (*it, ecode);
|
|
||||||
if (!ecode)
|
|
||||||
{
|
|
||||||
s.handshake (boost::asio::ssl::stream_base::client, ecode);
|
|
||||||
if (!ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "Reseed: Connected to ", url.host, ":", url.port);
|
|
||||||
i2p::http::HTTPReq req;
|
|
||||||
req.uri = url.to_string();
|
|
||||||
req.add_header("User-Agent", "Wget/1.11.4");
|
|
||||||
req.add_header("Connection", "close");
|
|
||||||
s.write_some (boost::asio::buffer (req.to_string()));
|
|
||||||
// read response
|
|
||||||
std::stringstream rs;
|
|
||||||
char recv_buf[1024]; size_t l = 0;
|
|
||||||
do {
|
|
||||||
l = s.read_some (boost::asio::buffer (recv_buf, sizeof(recv_buf)), ecode);
|
|
||||||
if (l) rs.write (recv_buf, l);
|
|
||||||
} while (!ecode && l);
|
|
||||||
// process response
|
|
||||||
std::string data = rs.str();
|
|
||||||
i2p::http::HTTPRes res;
|
|
||||||
int len = res.parse(data);
|
|
||||||
if (len <= 0) {
|
|
||||||
LogPrint(eLogWarning, "Reseed: incomplete/broken response from ", url.host);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
if (res.code != 200) {
|
|
||||||
LogPrint(eLogError, "Reseed: failed to reseed from ", url.host, ", http code ", res.code);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
data.erase(0, len); /* drop http headers from response */
|
|
||||||
LogPrint(eLogDebug, "Reseed: got ", data.length(), " bytes of data from ", url.host);
|
|
||||||
if (res.is_chunked()) {
|
|
||||||
std::stringstream in(data), out;
|
|
||||||
if (!i2p::http::MergeChunkedResponse(in, out)) {
|
|
||||||
LogPrint(eLogWarning, "Reseed: failed to merge chunked response from ", url.host);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
LogPrint(eLogDebug, "Reseed: got ", data.length(), "(", out.tellg(), ") bytes of data from ", url.host);
|
|
||||||
data = out.str();
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Reseed: SSL handshake failed: ", ecode.message ());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Reseed: Couldn't connect to ", url.host, ": ", ecode.message ());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Reseed: Couldn't resolve address ", url.host, ": ", ecode.message ());
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
47
Reseed.h
47
Reseed.h
@@ -1,47 +0,0 @@
|
|||||||
#ifndef RESEED_H
|
|
||||||
#define RESEED_H
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <map>
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "Crypto.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace data
|
|
||||||
{
|
|
||||||
|
|
||||||
class Reseeder
|
|
||||||
{
|
|
||||||
typedef Tag<512> PublicKey;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Reseeder();
|
|
||||||
~Reseeder();
|
|
||||||
int ReseedNowSU3 ();
|
|
||||||
|
|
||||||
void LoadCertificates ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void LoadCertificate (const std::string& filename);
|
|
||||||
|
|
||||||
int ReseedFromSU3 (const std::string& url);
|
|
||||||
int ProcessSU3File (const char * filename);
|
|
||||||
int ProcessSU3Stream (std::istream& s);
|
|
||||||
|
|
||||||
bool FindZipDataDescriptor (std::istream& s);
|
|
||||||
|
|
||||||
std::string HttpsRequest (const std::string& address);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::map<std::string, PublicKey> m_SigningKeys;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,465 +0,0 @@
|
|||||||
#include <fstream>
|
|
||||||
#include "Config.h"
|
|
||||||
#include "Crypto.h"
|
|
||||||
#include "Timestamp.h"
|
|
||||||
#include "I2NPProtocol.h"
|
|
||||||
#include "NetDb.h"
|
|
||||||
#include "FS.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include "version.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "Family.h"
|
|
||||||
#include "RouterContext.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
RouterContext context;
|
|
||||||
|
|
||||||
RouterContext::RouterContext ():
|
|
||||||
m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false),
|
|
||||||
m_StartupTime (0), m_Status (eRouterStatusOK), m_Error (eRouterErrorNone),
|
|
||||||
m_NetID (I2PD_NET_ID)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::Init ()
|
|
||||||
{
|
|
||||||
srand (i2p::util::GetMillisecondsSinceEpoch () % 1000);
|
|
||||||
m_StartupTime = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
if (!Load ())
|
|
||||||
CreateNewRouter ();
|
|
||||||
UpdateRouterInfo ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::CreateNewRouter ()
|
|
||||||
{
|
|
||||||
#if defined(__x86_64__) || defined(__i386__) || defined(_MSC_VER)
|
|
||||||
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519);
|
|
||||||
#else
|
|
||||||
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_DSA_SHA1);
|
|
||||||
#endif
|
|
||||||
SaveKeys ();
|
|
||||||
NewRouterInfo ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::NewRouterInfo ()
|
|
||||||
{
|
|
||||||
i2p::data::RouterInfo routerInfo;
|
|
||||||
routerInfo.SetRouterIdentity (GetIdentity ());
|
|
||||||
uint16_t port; i2p::config::GetOption("port", port);
|
|
||||||
if (!port)
|
|
||||||
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
|
|
||||||
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
|
|
||||||
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
|
|
||||||
bool nat; i2p::config::GetOption("nat", nat);
|
|
||||||
std::string ifname; i2p::config::GetOption("ifname", ifname);
|
|
||||||
std::string ifname4; i2p::config::GetOption("ifname4", ifname4);
|
|
||||||
std::string ifname6; i2p::config::GetOption("ifname6", ifname6);
|
|
||||||
if (ipv4)
|
|
||||||
{
|
|
||||||
std::string host = "127.0.0.1";
|
|
||||||
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
|
|
||||||
|
|
||||||
if(ifname4.size())
|
|
||||||
host = i2p::util::net::GetInterfaceAddress(ifname4, false).to_string();
|
|
||||||
|
|
||||||
routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ());
|
|
||||||
routerInfo.AddNTCPAddress (host.c_str(), port);
|
|
||||||
}
|
|
||||||
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
|
|
||||||
|
|
||||||
if(ifname6.size())
|
|
||||||
host = i2p::util::net::GetInterfaceAddress(ifname6, true).to_string();
|
|
||||||
|
|
||||||
routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ());
|
|
||||||
routerInfo.AddNTCPAddress (host.c_str(), port);
|
|
||||||
}
|
|
||||||
|
|
||||||
routerInfo.SetCaps (i2p::data::RouterInfo::eReachable |
|
|
||||||
i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer); // LR, BC
|
|
||||||
routerInfo.SetProperty ("netId", std::to_string (m_NetID));
|
|
||||||
routerInfo.SetProperty ("router.version", I2P_VERSION);
|
|
||||||
routerInfo.CreateBuffer (m_Keys);
|
|
||||||
m_RouterInfo.SetRouterIdentity (GetIdentity ());
|
|
||||||
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::UpdateRouterInfo ()
|
|
||||||
{
|
|
||||||
m_RouterInfo.CreateBuffer (m_Keys);
|
|
||||||
m_RouterInfo.SaveToFile (i2p::fs::DataDirPath (ROUTER_INFO));
|
|
||||||
m_LastUpdateTime = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::SetStatus (RouterStatus status)
|
|
||||||
{
|
|
||||||
if (status != m_Status)
|
|
||||||
{
|
|
||||||
m_Status = status;
|
|
||||||
m_Error = eRouterErrorNone;
|
|
||||||
switch (m_Status)
|
|
||||||
{
|
|
||||||
case eRouterStatusOK:
|
|
||||||
SetReachable ();
|
|
||||||
break;
|
|
||||||
case eRouterStatusFirewalled:
|
|
||||||
SetUnreachable ();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::UpdatePort (int port)
|
|
||||||
{
|
|
||||||
bool updated = false;
|
|
||||||
for (auto& address : m_RouterInfo.GetAddresses ())
|
|
||||||
{
|
|
||||||
if (address->port != port)
|
|
||||||
{
|
|
||||||
address->port = port;
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (updated)
|
|
||||||
UpdateRouterInfo ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::UpdateAddress (const boost::asio::ip::address& host)
|
|
||||||
{
|
|
||||||
bool updated = false;
|
|
||||||
for (auto& address : m_RouterInfo.GetAddresses ())
|
|
||||||
{
|
|
||||||
if (address->host != host && address->IsCompatible (host))
|
|
||||||
{
|
|
||||||
address->host = host;
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
if (updated || ts > m_LastUpdateTime + ROUTER_INFO_UPDATE_INTERVAL)
|
|
||||||
UpdateRouterInfo ();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RouterContext::AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer)
|
|
||||||
{
|
|
||||||
bool ret = m_RouterInfo.AddIntroducer (introducer);
|
|
||||||
if (ret)
|
|
||||||
UpdateRouterInfo ();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e)
|
|
||||||
{
|
|
||||||
if (m_RouterInfo.RemoveIntroducer (e))
|
|
||||||
UpdateRouterInfo ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::SetFloodfill (bool floodfill)
|
|
||||||
{
|
|
||||||
m_IsFloodfill = floodfill;
|
|
||||||
if (floodfill)
|
|
||||||
m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eFloodfill);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () & ~i2p::data::RouterInfo::eFloodfill);
|
|
||||||
// we don't publish number of routers and leaseset for non-floodfill
|
|
||||||
m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_LEASESETS);
|
|
||||||
m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_ROUTERS);
|
|
||||||
}
|
|
||||||
UpdateRouterInfo ();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string RouterContext::GetFamily () const
|
|
||||||
{
|
|
||||||
return m_RouterInfo.GetProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::SetFamily (const std::string& family)
|
|
||||||
{
|
|
||||||
std::string signature;
|
|
||||||
if (family.length () > 0)
|
|
||||||
signature = i2p::data::CreateFamilySignature (family, GetIdentHash ());
|
|
||||||
if (signature.length () > 0)
|
|
||||||
{
|
|
||||||
m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY, family);
|
|
||||||
m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY_SIG, signature);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY);
|
|
||||||
m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_FAMILY_SIG);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::SetBandwidth (char L) {
|
|
||||||
uint16_t limit = 0;
|
|
||||||
enum { low, high, extra } type = high;
|
|
||||||
/* detect parameters */
|
|
||||||
switch (L)
|
|
||||||
{
|
|
||||||
case i2p::data::CAPS_FLAG_LOW_BANDWIDTH1 : limit = 12; type = low; break;
|
|
||||||
case i2p::data::CAPS_FLAG_LOW_BANDWIDTH2 : limit = 48; type = low; break;
|
|
||||||
case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH1 : limit = 64; type = high; break;
|
|
||||||
case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH2 : limit = 128; type = high; break;
|
|
||||||
case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH3 : limit = 256; type = high; break;
|
|
||||||
case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH1 : limit = 2048; type = extra; break;
|
|
||||||
case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH2 : limit = 9999; type = extra; break;
|
|
||||||
default:
|
|
||||||
limit = 48; type = low;
|
|
||||||
}
|
|
||||||
/* update caps & flags in RI */
|
|
||||||
auto caps = m_RouterInfo.GetCaps ();
|
|
||||||
caps &= ~i2p::data::RouterInfo::eHighBandwidth;
|
|
||||||
caps &= ~i2p::data::RouterInfo::eExtraBandwidth;
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case low : /* not set */; break;
|
|
||||||
case extra : caps |= i2p::data::RouterInfo::eExtraBandwidth; // no break here
|
|
||||||
case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break;
|
|
||||||
}
|
|
||||||
m_RouterInfo.SetCaps (caps);
|
|
||||||
UpdateRouterInfo ();
|
|
||||||
m_BandwidthLimit = limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::SetBandwidth (int limit)
|
|
||||||
{
|
|
||||||
if (limit > 2000) { SetBandwidth('X'); }
|
|
||||||
else if (limit > 256) { SetBandwidth('P'); }
|
|
||||||
else if (limit > 128) { SetBandwidth('O'); }
|
|
||||||
else if (limit > 64) { SetBandwidth('N'); }
|
|
||||||
else if (limit > 48) { SetBandwidth('M'); }
|
|
||||||
else if (limit > 12) { SetBandwidth('L'); }
|
|
||||||
else { SetBandwidth('K'); }
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RouterContext::IsUnreachable () const
|
|
||||||
{
|
|
||||||
return m_RouterInfo.GetCaps () & i2p::data::RouterInfo::eUnreachable;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::SetUnreachable ()
|
|
||||||
{
|
|
||||||
// set caps
|
|
||||||
m_RouterInfo.SetCaps (i2p::data::RouterInfo::eUnreachable | i2p::data::RouterInfo::eSSUTesting); // LU, B
|
|
||||||
// remove NTCP address
|
|
||||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
|
||||||
for (auto it = addresses.begin (); it != addresses.end (); ++it)
|
|
||||||
{
|
|
||||||
if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportNTCP &&
|
|
||||||
(*it)->host.is_v4 ())
|
|
||||||
{
|
|
||||||
addresses.erase (it);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// delete previous introducers
|
|
||||||
for (auto& addr : addresses)
|
|
||||||
addr->introducers.clear ();
|
|
||||||
|
|
||||||
// update
|
|
||||||
UpdateRouterInfo ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::SetReachable ()
|
|
||||||
{
|
|
||||||
// update caps
|
|
||||||
uint8_t caps = m_RouterInfo.GetCaps ();
|
|
||||||
caps &= ~i2p::data::RouterInfo::eUnreachable;
|
|
||||||
caps |= i2p::data::RouterInfo::eReachable;
|
|
||||||
caps |= i2p::data::RouterInfo::eSSUIntroducer;
|
|
||||||
if (m_IsFloodfill)
|
|
||||||
caps |= i2p::data::RouterInfo::eFloodfill;
|
|
||||||
m_RouterInfo.SetCaps (caps);
|
|
||||||
|
|
||||||
// insert NTCP back
|
|
||||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
|
||||||
for (const auto& addr : addresses)
|
|
||||||
{
|
|
||||||
if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU &&
|
|
||||||
addr->host.is_v4 ())
|
|
||||||
{
|
|
||||||
// insert NTCP address with host/port from SSU
|
|
||||||
m_RouterInfo.AddNTCPAddress (addr->host.to_string ().c_str (), addr->port);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// delete previous introducers
|
|
||||||
for (auto& addr : addresses)
|
|
||||||
addr->introducers.clear ();
|
|
||||||
|
|
||||||
// update
|
|
||||||
UpdateRouterInfo ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::SetSupportsV6 (bool supportsV6)
|
|
||||||
{
|
|
||||||
if (supportsV6)
|
|
||||||
m_RouterInfo.EnableV6 ();
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
if (addr->host.is_v6 () && addr->transportStyle == i2p::data::RouterInfo::eTransportNTCP)
|
|
||||||
{
|
|
||||||
if (addr->host != host)
|
|
||||||
{
|
|
||||||
addr->host = host;
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
port = addr->port;
|
|
||||||
}
|
|
||||||
if (!found)
|
|
||||||
{
|
|
||||||
// create new address
|
|
||||||
m_RouterInfo.AddNTCPAddress (host.to_string ().c_str (), port);
|
|
||||||
auto mtu = i2p::util::net::GetMTU (host);
|
|
||||||
if (mtu)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "Router: Our v6 MTU=", mtu);
|
|
||||||
if (mtu > 1472) { // TODO: magic constant
|
|
||||||
mtu = 1472;
|
|
||||||
LogPrint(eLogWarning, "Router: MTU dropped to upper limit of 1472 bytes");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_RouterInfo.AddSSUAddress (host.to_string ().c_str (), port, GetIdentHash (), mtu ? mtu : 1472); // TODO
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
if (updated)
|
|
||||||
UpdateRouterInfo ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::UpdateStats ()
|
|
||||||
{
|
|
||||||
if (m_IsFloodfill)
|
|
||||||
{
|
|
||||||
// update routers and leasesets
|
|
||||||
m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_LEASESETS, std::to_string(i2p::data::netdb.GetNumLeaseSets ()));
|
|
||||||
m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_ROUTERS, std::to_string(i2p::data::netdb.GetNumRouters ()));
|
|
||||||
UpdateRouterInfo ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RouterContext::Load ()
|
|
||||||
{
|
|
||||||
std::ifstream fk (i2p::fs::DataDirPath (ROUTER_KEYS), std::ifstream::in | std::ifstream::binary);
|
|
||||||
if (!fk.is_open ()) return false;
|
|
||||||
fk.seekg (0, std::ios::end);
|
|
||||||
size_t len = fk.tellg();
|
|
||||||
fk.seekg (0, std::ios::beg);
|
|
||||||
|
|
||||||
if (len == sizeof (i2p::data::Keys)) // old keys file format
|
|
||||||
{
|
|
||||||
i2p::data::Keys keys;
|
|
||||||
fk.read ((char *)&keys, sizeof (keys));
|
|
||||||
m_Keys = keys;
|
|
||||||
}
|
|
||||||
else // new keys file format
|
|
||||||
{
|
|
||||||
uint8_t * buf = new uint8_t[len];
|
|
||||||
fk.read ((char *)buf, len);
|
|
||||||
m_Keys.FromBuffer (buf, len);
|
|
||||||
delete[] buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_RouterInfo.SetRouterIdentity (GetIdentity ());
|
|
||||||
i2p::data::RouterInfo routerInfo(i2p::fs::DataDirPath (ROUTER_INFO));
|
|
||||||
if (!routerInfo.IsUnreachable ()) // router.info looks good
|
|
||||||
{
|
|
||||||
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
|
|
||||||
m_RouterInfo.SetProperty ("coreVersion", I2P_VERSION);
|
|
||||||
m_RouterInfo.SetProperty ("router.version", I2P_VERSION);
|
|
||||||
|
|
||||||
// Migration to 0.9.24. TODO: remove later
|
|
||||||
m_RouterInfo.DeleteProperty ("coreVersion");
|
|
||||||
m_RouterInfo.DeleteProperty ("stat_uptime");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, ROUTER_INFO, " is malformed. Creating new");
|
|
||||||
NewRouterInfo ();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsUnreachable ())
|
|
||||||
SetReachable (); // we assume reachable until we discover firewall through peer tests
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::SaveKeys ()
|
|
||||||
{
|
|
||||||
// save in the same format as .dat files
|
|
||||||
std::ofstream fk (i2p::fs::DataDirPath (ROUTER_KEYS), std::ofstream::binary | std::ofstream::out);
|
|
||||||
size_t len = m_Keys.GetFullLen ();
|
|
||||||
uint8_t * buf = new uint8_t[len];
|
|
||||||
m_Keys.ToBuffer (buf, len);
|
|
||||||
fk.write ((char *)buf, len);
|
|
||||||
delete[] buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<i2p::tunnel::TunnelPool> RouterContext::GetTunnelPool () const
|
|
||||||
{
|
|
||||||
return i2p::tunnel::tunnels.GetExploratoryPool ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
|
|
||||||
{
|
|
||||||
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from));
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_GarlicMutex);
|
|
||||||
i2p::garlic::GarlicDestination::ProcessGarlicMessage (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_GarlicMutex);
|
|
||||||
i2p::garlic::GarlicDestination::ProcessDeliveryStatusMessage (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::CleanupDestination ()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_GarlicMutex);
|
|
||||||
i2p::garlic::GarlicDestination::CleanupExpiredTags ();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t RouterContext::GetUptime () const
|
|
||||||
{
|
|
||||||
return i2p::util::GetSecondsSinceEpoch () - m_StartupTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
129
RouterContext.h
129
RouterContext.h
@@ -1,129 +0,0 @@
|
|||||||
#ifndef ROUTER_CONTEXT_H__
|
|
||||||
#define ROUTER_CONTEXT_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <string>
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "RouterInfo.h"
|
|
||||||
#include "Garlic.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
const char ROUTER_INFO[] = "router.info";
|
|
||||||
const char ROUTER_KEYS[] = "router.keys";
|
|
||||||
const int ROUTER_INFO_UPDATE_INTERVAL = 1800; // 30 minutes
|
|
||||||
|
|
||||||
enum RouterStatus
|
|
||||||
{
|
|
||||||
eRouterStatusOK = 0,
|
|
||||||
eRouterStatusTesting = 1,
|
|
||||||
eRouterStatusFirewalled = 2,
|
|
||||||
eRouterStatusError = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
enum RouterError
|
|
||||||
{
|
|
||||||
eRouterErrorNone = 0,
|
|
||||||
eRouterErrorClockSkew = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
class RouterContext: public i2p::garlic::GarlicDestination
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
RouterContext ();
|
|
||||||
void Init ();
|
|
||||||
|
|
||||||
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
|
|
||||||
i2p::data::RouterInfo& GetRouterInfo () { return m_RouterInfo; };
|
|
||||||
std::shared_ptr<const i2p::data::RouterInfo> GetSharedRouterInfo () const
|
|
||||||
{
|
|
||||||
return std::shared_ptr<const i2p::data::RouterInfo> (&m_RouterInfo,
|
|
||||||
[](const i2p::data::RouterInfo *) {});
|
|
||||||
}
|
|
||||||
std::shared_ptr<i2p::garlic::GarlicDestination> GetSharedDestination ()
|
|
||||||
{
|
|
||||||
return std::shared_ptr<i2p::garlic::GarlicDestination> (this,
|
|
||||||
[](i2p::garlic::GarlicDestination *) {});
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t GetUptime () const;
|
|
||||||
uint32_t GetStartupTime () const { return m_StartupTime; };
|
|
||||||
uint64_t GetLastUpdateTime () const { return m_LastUpdateTime; };
|
|
||||||
uint64_t GetBandwidthLimit () const { return m_BandwidthLimit; };
|
|
||||||
RouterStatus GetStatus () const { return m_Status; };
|
|
||||||
void SetStatus (RouterStatus status);
|
|
||||||
RouterError GetError () const { return m_Error; };
|
|
||||||
void SetError (RouterError error) { m_Status = eRouterStatusError; m_Error = error; };
|
|
||||||
int GetNetID () const { return m_NetID; };
|
|
||||||
void SetNetID (int netID) { m_NetID = netID; };
|
|
||||||
|
|
||||||
void UpdatePort (int port); // called from Daemon
|
|
||||||
void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon
|
|
||||||
bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer);
|
|
||||||
void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
|
|
||||||
bool IsUnreachable () const;
|
|
||||||
void SetUnreachable ();
|
|
||||||
void SetReachable ();
|
|
||||||
bool IsFloodfill () const { return m_IsFloodfill; };
|
|
||||||
void SetFloodfill (bool floodfill);
|
|
||||||
void SetFamily (const std::string& family);
|
|
||||||
std::string GetFamily () const;
|
|
||||||
void SetBandwidth (int limit); /* in kilobytes */
|
|
||||||
void SetBandwidth (char L); /* by letter */
|
|
||||||
bool AcceptsTunnels () const { return m_AcceptsTunnels; };
|
|
||||||
void SetAcceptsTunnels (bool acceptsTunnels) { m_AcceptsTunnels = acceptsTunnels; };
|
|
||||||
bool SupportsV6 () const { return m_RouterInfo.IsV6 (); };
|
|
||||||
bool SupportsV4 () const { return m_RouterInfo.IsV4 (); };
|
|
||||||
void SetSupportsV6 (bool supportsV6);
|
|
||||||
void SetSupportsV4 (bool supportsV4);
|
|
||||||
|
|
||||||
void UpdateNTCPV6Address (const boost::asio::ip::address& host); // called from NTCP session
|
|
||||||
void UpdateStats ();
|
|
||||||
void CleanupDestination (); // garlic destination
|
|
||||||
|
|
||||||
// implements LocalDestination
|
|
||||||
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::LocalLeaseSet> GetLeaseSet () { return nullptr; };
|
|
||||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const;
|
|
||||||
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
|
||||||
|
|
||||||
// override GarlicDestination
|
|
||||||
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void CreateNewRouter ();
|
|
||||||
void NewRouterInfo ();
|
|
||||||
void UpdateRouterInfo ();
|
|
||||||
bool Load ();
|
|
||||||
void SaveKeys ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
i2p::data::RouterInfo m_RouterInfo;
|
|
||||||
i2p::data::PrivateKeys m_Keys;
|
|
||||||
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;
|
|
||||||
RouterError m_Error;
|
|
||||||
int m_NetID;
|
|
||||||
std::mutex m_GarlicMutex;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern RouterContext context;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
798
RouterInfo.cpp
798
RouterInfo.cpp
@@ -1,798 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "I2PEndian.h"
|
|
||||||
#include <fstream>
|
|
||||||
#include <boost/lexical_cast.hpp>
|
|
||||||
#include <boost/make_shared.hpp>
|
|
||||||
#if (BOOST_VERSION >= 105300)
|
|
||||||
#include <boost/atomic.hpp>
|
|
||||||
#endif
|
|
||||||
#include "version.h"
|
|
||||||
#include "Crypto.h"
|
|
||||||
#include "Base.h"
|
|
||||||
#include "Timestamp.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "NetDb.h"
|
|
||||||
#include "RouterContext.h"
|
|
||||||
#include "RouterInfo.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace data
|
|
||||||
{
|
|
||||||
RouterInfo::RouterInfo (): m_Buffer (nullptr)
|
|
||||||
{
|
|
||||||
m_Addresses = boost::make_shared<Addresses>(); // create empty list
|
|
||||||
}
|
|
||||||
|
|
||||||
RouterInfo::RouterInfo (const std::string& fullPath):
|
|
||||||
m_FullPath (fullPath), m_IsUpdated (false), m_IsUnreachable (false),
|
|
||||||
m_SupportedTransports (0), m_Caps (0)
|
|
||||||
{
|
|
||||||
m_Addresses = boost::make_shared<Addresses>(); // create empty list
|
|
||||||
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
|
|
||||||
ReadFromFile ();
|
|
||||||
}
|
|
||||||
|
|
||||||
RouterInfo::RouterInfo (const uint8_t * buf, int len):
|
|
||||||
m_IsUpdated (true), m_IsUnreachable (false), m_SupportedTransports (0), m_Caps (0)
|
|
||||||
{
|
|
||||||
m_Addresses = boost::make_shared<Addresses>(); // create empty list
|
|
||||||
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
|
|
||||||
memcpy (m_Buffer, buf, len);
|
|
||||||
m_BufferLen = len;
|
|
||||||
ReadFromBuffer (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
RouterInfo::~RouterInfo ()
|
|
||||||
{
|
|
||||||
delete[] m_Buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterInfo::Update (const uint8_t * buf, int len)
|
|
||||||
{
|
|
||||||
// verify signature since we have indentity already
|
|
||||||
int l = len - m_RouterIdentity->GetSignatureLen ();
|
|
||||||
if (m_RouterIdentity->Verify (buf, l, buf + l))
|
|
||||||
{
|
|
||||||
// clean up
|
|
||||||
m_IsUpdated = true;
|
|
||||||
m_IsUnreachable = false;
|
|
||||||
m_SupportedTransports = 0;
|
|
||||||
m_Caps = 0;
|
|
||||||
// don't clean up m_Addresses, it will be replaced in ReadFromStream
|
|
||||||
m_Properties.clear ();
|
|
||||||
// copy buffer
|
|
||||||
if (!m_Buffer)
|
|
||||||
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
|
|
||||||
memcpy (m_Buffer, buf, len);
|
|
||||||
m_BufferLen = len;
|
|
||||||
// skip identity
|
|
||||||
size_t identityLen = m_RouterIdentity->GetFullLen ();
|
|
||||||
// read new RI
|
|
||||||
std::stringstream str (std::string ((char *)m_Buffer + identityLen, m_BufferLen - identityLen));
|
|
||||||
ReadFromStream (str);
|
|
||||||
// don't delete buffer until saved to the file
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "RouterInfo: signature verification failed");
|
|
||||||
m_IsUnreachable = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterInfo::SetRouterIdentity (std::shared_ptr<const IdentityEx> identity)
|
|
||||||
{
|
|
||||||
m_RouterIdentity = identity;
|
|
||||||
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RouterInfo::LoadFile ()
|
|
||||||
{
|
|
||||||
std::ifstream s(m_FullPath, std::ifstream::binary);
|
|
||||||
if (s.is_open ())
|
|
||||||
{
|
|
||||||
s.seekg (0,std::ios::end);
|
|
||||||
m_BufferLen = s.tellg ();
|
|
||||||
if (m_BufferLen < 40 || m_BufferLen > MAX_RI_BUFFER_SIZE)
|
|
||||||
{
|
|
||||||
LogPrint(eLogError, "RouterInfo: File", m_FullPath, " is malformed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
s.seekg(0, std::ios::beg);
|
|
||||||
if (!m_Buffer)
|
|
||||||
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
|
|
||||||
s.read((char *)m_Buffer, m_BufferLen);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "RouterInfo: Can't open file ", m_FullPath);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterInfo::ReadFromFile ()
|
|
||||||
{
|
|
||||||
if (LoadFile ())
|
|
||||||
ReadFromBuffer (false);
|
|
||||||
else
|
|
||||||
m_IsUnreachable = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterInfo::ReadFromBuffer (bool verifySignature)
|
|
||||||
{
|
|
||||||
m_RouterIdentity = std::make_shared<IdentityEx>(m_Buffer, m_BufferLen);
|
|
||||||
size_t identityLen = m_RouterIdentity->GetFullLen ();
|
|
||||||
if (identityLen >= m_BufferLen)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "RouterInfo: identity length ", identityLen, " exceeds buffer size ", m_BufferLen);
|
|
||||||
m_IsUnreachable = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (verifySignature)
|
|
||||||
{
|
|
||||||
// verify signature
|
|
||||||
int l = m_BufferLen - m_RouterIdentity->GetSignatureLen ();
|
|
||||||
if (l < 0 || !m_RouterIdentity->Verify ((uint8_t *)m_Buffer, l, (uint8_t *)m_Buffer + l))
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "RouterInfo: signature verification failed");
|
|
||||||
m_IsUnreachable = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_RouterIdentity->DropVerifier ();
|
|
||||||
}
|
|
||||||
// parse RI
|
|
||||||
std::stringstream str;
|
|
||||||
str.write ((const char *)m_Buffer + identityLen, m_BufferLen - identityLen);
|
|
||||||
ReadFromStream (str);
|
|
||||||
if (!str)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "RouterInfo: malformed message");
|
|
||||||
m_IsUnreachable = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterInfo::ReadFromStream (std::istream& s)
|
|
||||||
{
|
|
||||||
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp));
|
|
||||||
m_Timestamp = be64toh (m_Timestamp);
|
|
||||||
// read addresses
|
|
||||||
auto addresses = boost::make_shared<Addresses>();
|
|
||||||
uint8_t numAddresses;
|
|
||||||
s.read ((char *)&numAddresses, sizeof (numAddresses)); if (!s) return;
|
|
||||||
bool introducers = false;
|
|
||||||
for (int i = 0; i < numAddresses; i++)
|
|
||||||
{
|
|
||||||
uint8_t supportedTransports = 0;
|
|
||||||
bool isValidAddress = true;
|
|
||||||
Address address;
|
|
||||||
s.read ((char *)&address.cost, sizeof (address.cost));
|
|
||||||
s.read ((char *)&address.date, sizeof (address.date));
|
|
||||||
char transportStyle[5];
|
|
||||||
ReadString (transportStyle, 5, s);
|
|
||||||
if (!strcmp (transportStyle, "NTCP"))
|
|
||||||
address.transportStyle = eTransportNTCP;
|
|
||||||
else if (!strcmp (transportStyle, "SSU"))
|
|
||||||
address.transportStyle = eTransportSSU;
|
|
||||||
else
|
|
||||||
address.transportStyle = eTransportUnknown;
|
|
||||||
address.port = 0;
|
|
||||||
address.mtu = 0;
|
|
||||||
uint16_t size, r = 0;
|
|
||||||
s.read ((char *)&size, sizeof (size)); if (!s) return;
|
|
||||||
size = be16toh (size);
|
|
||||||
while (r < size)
|
|
||||||
{
|
|
||||||
char key[255], value[255];
|
|
||||||
r += ReadString (key, 255, s);
|
|
||||||
s.seekg (1, std::ios_base::cur); r++; // =
|
|
||||||
r += ReadString (value, 255, s);
|
|
||||||
s.seekg (1, std::ios_base::cur); r++; // ;
|
|
||||||
if (!s) return;
|
|
||||||
if (!strcmp (key, "host"))
|
|
||||||
{
|
|
||||||
boost::system::error_code ecode;
|
|
||||||
address.host = boost::asio::ip::address::from_string (value, ecode);
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
if (address.transportStyle == eTransportNTCP)
|
|
||||||
{
|
|
||||||
supportedTransports |= eNTCPV4; // TODO:
|
|
||||||
address.addressString = value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
supportedTransports |= eSSUV4; // TODO:
|
|
||||||
address.addressString = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// add supported protocol
|
|
||||||
if (address.host.is_v4 ())
|
|
||||||
supportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4;
|
|
||||||
else
|
|
||||||
supportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!strcmp (key, "port"))
|
|
||||||
address.port = boost::lexical_cast<int>(value);
|
|
||||||
else if (!strcmp (key, "mtu"))
|
|
||||||
address.mtu = boost::lexical_cast<int>(value);
|
|
||||||
else if (!strcmp (key, "key"))
|
|
||||||
Base64ToByteStream (value, strlen (value), address.key, 32);
|
|
||||||
else if (!strcmp (key, "caps"))
|
|
||||||
ExtractCaps (value);
|
|
||||||
else if (key[0] == 'i')
|
|
||||||
{
|
|
||||||
// introducers
|
|
||||||
introducers = true;
|
|
||||||
size_t l = strlen(key);
|
|
||||||
unsigned char index = key[l-1] - '0'; // TODO:
|
|
||||||
key[l-1] = 0;
|
|
||||||
if (index > 9)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "RouterInfo: Unexpected introducer's index ", index, " skipped");
|
|
||||||
if (s) continue; else return;
|
|
||||||
}
|
|
||||||
if (index >= address.introducers.size ())
|
|
||||||
address.introducers.resize (index + 1);
|
|
||||||
Introducer& introducer = address.introducers.at (index);
|
|
||||||
if (!strcmp (key, "ihost"))
|
|
||||||
{
|
|
||||||
boost::system::error_code ecode;
|
|
||||||
introducer.iHost = boost::asio::ip::address::from_string (value, ecode);
|
|
||||||
}
|
|
||||||
else if (!strcmp (key, "iport"))
|
|
||||||
introducer.iPort = boost::lexical_cast<int>(value);
|
|
||||||
else if (!strcmp (key, "itag"))
|
|
||||||
introducer.iTag = boost::lexical_cast<uint32_t>(value);
|
|
||||||
else if (!strcmp (key, "ikey"))
|
|
||||||
Base64ToByteStream (value, strlen (value), introducer.iKey, 32);
|
|
||||||
}
|
|
||||||
if (!s) return;
|
|
||||||
}
|
|
||||||
if (isValidAddress)
|
|
||||||
{
|
|
||||||
addresses->push_back(std::make_shared<Address>(address));
|
|
||||||
m_SupportedTransports |= supportedTransports;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#if (BOOST_VERSION >= 105300)
|
|
||||||
boost::atomic_store (&m_Addresses, addresses);
|
|
||||||
#else
|
|
||||||
m_Addresses = addresses; // race condition
|
|
||||||
#endif
|
|
||||||
// read peers
|
|
||||||
uint8_t numPeers;
|
|
||||||
s.read ((char *)&numPeers, sizeof (numPeers)); if (!s) return;
|
|
||||||
s.seekg (numPeers*32, std::ios_base::cur); // TODO: read peers
|
|
||||||
// read properties
|
|
||||||
uint16_t size, r = 0;
|
|
||||||
s.read ((char *)&size, sizeof (size)); if (!s) return;
|
|
||||||
size = be16toh (size);
|
|
||||||
while (r < size)
|
|
||||||
{
|
|
||||||
char key[255], value[255];
|
|
||||||
r += ReadString (key, 255, s);
|
|
||||||
s.seekg (1, std::ios_base::cur); r++; // =
|
|
||||||
r += ReadString (value, 255, s);
|
|
||||||
s.seekg (1, std::ios_base::cur); r++; // ;
|
|
||||||
if (!s) return;
|
|
||||||
m_Properties[key] = value;
|
|
||||||
|
|
||||||
// extract caps
|
|
||||||
if (!strcmp (key, "caps"))
|
|
||||||
ExtractCaps (value);
|
|
||||||
// check netId
|
|
||||||
else if (!strcmp (key, ROUTER_INFO_PROPERTY_NETID) && atoi (value) != i2p::context.GetNetID ())
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "RouterInfo: Unexpected ", ROUTER_INFO_PROPERTY_NETID, "=", value);
|
|
||||||
m_IsUnreachable = true;
|
|
||||||
}
|
|
||||||
// family
|
|
||||||
else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY))
|
|
||||||
{
|
|
||||||
m_Family = value;
|
|
||||||
boost::to_lower (m_Family);
|
|
||||||
}
|
|
||||||
else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY_SIG))
|
|
||||||
{
|
|
||||||
if (!netdb.GetFamilies ().VerifyFamily (m_Family, GetIdentHash (), value))
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "RouterInfo: family signature verification failed");
|
|
||||||
m_Family.clear ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!s) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_SupportedTransports || !m_Addresses->size() || (UsesIntroducer () && !introducers))
|
|
||||||
SetUnreachable (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RouterInfo::IsFamily(const std::string & fam) const {
|
|
||||||
return m_Family == fam;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterInfo::ExtractCaps (const char * value)
|
|
||||||
{
|
|
||||||
const char * cap = value;
|
|
||||||
while (*cap)
|
|
||||||
{
|
|
||||||
switch (*cap)
|
|
||||||
{
|
|
||||||
case CAPS_FLAG_FLOODFILL:
|
|
||||||
m_Caps |= Caps::eFloodfill;
|
|
||||||
break;
|
|
||||||
case CAPS_FLAG_HIGH_BANDWIDTH1:
|
|
||||||
case CAPS_FLAG_HIGH_BANDWIDTH2:
|
|
||||||
case CAPS_FLAG_HIGH_BANDWIDTH3:
|
|
||||||
m_Caps |= Caps::eHighBandwidth;
|
|
||||||
break;
|
|
||||||
case CAPS_FLAG_EXTRA_BANDWIDTH1:
|
|
||||||
case CAPS_FLAG_EXTRA_BANDWIDTH2:
|
|
||||||
m_Caps |= Caps::eExtraBandwidth;
|
|
||||||
break;
|
|
||||||
case CAPS_FLAG_HIDDEN:
|
|
||||||
m_Caps |= Caps::eHidden;
|
|
||||||
break;
|
|
||||||
case CAPS_FLAG_REACHABLE:
|
|
||||||
m_Caps |= Caps::eReachable;
|
|
||||||
break;
|
|
||||||
case CAPS_FLAG_UNREACHABLE:
|
|
||||||
m_Caps |= Caps::eUnreachable;
|
|
||||||
break;
|
|
||||||
case CAPS_FLAG_SSU_TESTING:
|
|
||||||
m_Caps |= Caps::eSSUTesting;
|
|
||||||
break;
|
|
||||||
case CAPS_FLAG_SSU_INTRODUCER:
|
|
||||||
m_Caps |= Caps::eSSUIntroducer;
|
|
||||||
break;
|
|
||||||
default: ;
|
|
||||||
}
|
|
||||||
cap++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterInfo::UpdateCapsProperty ()
|
|
||||||
{
|
|
||||||
std::string caps;
|
|
||||||
if (m_Caps & eFloodfill)
|
|
||||||
{
|
|
||||||
if (m_Caps & eExtraBandwidth) caps += CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P'
|
|
||||||
caps += CAPS_FLAG_HIGH_BANDWIDTH3; // 'O'
|
|
||||||
caps += CAPS_FLAG_FLOODFILL; // floodfill
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (m_Caps & eExtraBandwidth) caps += CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P'
|
|
||||||
caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH3 /* 'O' */: CAPS_FLAG_LOW_BANDWIDTH2 /* 'L' */; // bandwidth
|
|
||||||
}
|
|
||||||
if (m_Caps & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden
|
|
||||||
if (m_Caps & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable
|
|
||||||
if (m_Caps & eUnreachable) caps += CAPS_FLAG_UNREACHABLE; // unreachable
|
|
||||||
|
|
||||||
SetProperty ("caps", caps);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterInfo::WriteToStream (std::ostream& s) const
|
|
||||||
{
|
|
||||||
uint64_t ts = htobe64 (m_Timestamp);
|
|
||||||
s.write ((const char *)&ts, sizeof (ts));
|
|
||||||
|
|
||||||
// addresses
|
|
||||||
uint8_t numAddresses = m_Addresses->size ();
|
|
||||||
s.write ((char *)&numAddresses, sizeof (numAddresses));
|
|
||||||
for (const auto& addr_ptr : *m_Addresses)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
else if (address.transportStyle == eTransportSSU)
|
|
||||||
{
|
|
||||||
WriteString ("SSU", s);
|
|
||||||
// caps
|
|
||||||
WriteString ("caps", properties);
|
|
||||||
properties << '=';
|
|
||||||
std::string caps;
|
|
||||||
if (IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING;
|
|
||||||
if (IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER;
|
|
||||||
WriteString (caps, properties);
|
|
||||||
properties << ';';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
WriteString ("", s);
|
|
||||||
|
|
||||||
WriteString ("host", properties);
|
|
||||||
properties << '=';
|
|
||||||
WriteString (address.host.to_string (), properties);
|
|
||||||
properties << ';';
|
|
||||||
if (address.transportStyle == eTransportSSU)
|
|
||||||
{
|
|
||||||
// write introducers if any
|
|
||||||
if (address.introducers.size () > 0)
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
for (const auto& introducer: address.introducers)
|
|
||||||
{
|
|
||||||
WriteString ("ihost" + boost::lexical_cast<std::string>(i), properties);
|
|
||||||
properties << '=';
|
|
||||||
WriteString (introducer.iHost.to_string (), properties);
|
|
||||||
properties << ';';
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
i = 0;
|
|
||||||
for (const auto& introducer: address.introducers)
|
|
||||||
{
|
|
||||||
WriteString ("ikey" + boost::lexical_cast<std::string>(i), properties);
|
|
||||||
properties << '=';
|
|
||||||
char value[64];
|
|
||||||
size_t l = ByteStreamToBase64 (introducer.iKey, 32, value, 64);
|
|
||||||
value[l] = 0;
|
|
||||||
WriteString (value, properties);
|
|
||||||
properties << ';';
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
i = 0;
|
|
||||||
for (const auto& introducer: address.introducers)
|
|
||||||
{
|
|
||||||
WriteString ("iport" + boost::lexical_cast<std::string>(i), properties);
|
|
||||||
properties << '=';
|
|
||||||
WriteString (boost::lexical_cast<std::string>(introducer.iPort), properties);
|
|
||||||
properties << ';';
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
i = 0;
|
|
||||||
for (const auto& introducer: address.introducers)
|
|
||||||
{
|
|
||||||
WriteString ("itag" + boost::lexical_cast<std::string>(i), properties);
|
|
||||||
properties << '=';
|
|
||||||
WriteString (boost::lexical_cast<std::string>(introducer.iTag), properties);
|
|
||||||
properties << ';';
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// write intro key
|
|
||||||
WriteString ("key", properties);
|
|
||||||
properties << '=';
|
|
||||||
char value[64];
|
|
||||||
size_t l = ByteStreamToBase64 (address.key, 32, value, 64);
|
|
||||||
value[l] = 0;
|
|
||||||
WriteString (value, properties);
|
|
||||||
properties << ';';
|
|
||||||
// write mtu
|
|
||||||
if (address.mtu)
|
|
||||||
{
|
|
||||||
WriteString ("mtu", properties);
|
|
||||||
properties << '=';
|
|
||||||
WriteString (boost::lexical_cast<std::string>(address.mtu), properties);
|
|
||||||
properties << ';';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WriteString ("port", properties);
|
|
||||||
properties << '=';
|
|
||||||
WriteString (boost::lexical_cast<std::string>(address.port), properties);
|
|
||||||
properties << ';';
|
|
||||||
|
|
||||||
uint16_t size = htobe16 (properties.str ().size ());
|
|
||||||
s.write ((char *)&size, sizeof (size));
|
|
||||||
s.write (properties.str ().c_str (), properties.str ().size ());
|
|
||||||
}
|
|
||||||
|
|
||||||
// peers
|
|
||||||
uint8_t numPeers = 0;
|
|
||||||
s.write ((char *)&numPeers, sizeof (numPeers));
|
|
||||||
|
|
||||||
// properties
|
|
||||||
std::stringstream properties;
|
|
||||||
for (const auto& p : m_Properties)
|
|
||||||
{
|
|
||||||
WriteString (p.first, properties);
|
|
||||||
properties << '=';
|
|
||||||
WriteString (p.second, properties);
|
|
||||||
properties << ';';
|
|
||||||
}
|
|
||||||
uint16_t size = htobe16 (properties.str ().size ());
|
|
||||||
s.write ((char *)&size, sizeof (size));
|
|
||||||
s.write (properties.str ().c_str (), properties.str ().size ());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RouterInfo::IsNewer (const uint8_t * buf, size_t len) const
|
|
||||||
{
|
|
||||||
if (!m_RouterIdentity) return false;
|
|
||||||
size_t size = m_RouterIdentity->GetFullLen ();
|
|
||||||
if (size + 8 > len) return false;
|
|
||||||
return bufbe64toh (buf + size) > m_Timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint8_t * RouterInfo::LoadBuffer ()
|
|
||||||
{
|
|
||||||
if (!m_Buffer)
|
|
||||||
{
|
|
||||||
if (LoadFile ())
|
|
||||||
LogPrint (eLogDebug, "RouterInfo: Buffer for ", GetIdentHashAbbreviation (GetIdentHash ()), " loaded from file");
|
|
||||||
}
|
|
||||||
return m_Buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterInfo::CreateBuffer (const PrivateKeys& privateKeys)
|
|
||||||
{
|
|
||||||
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); // refresh timstamp
|
|
||||||
std::stringstream s;
|
|
||||||
uint8_t ident[1024];
|
|
||||||
auto identLen = privateKeys.GetPublic ()->ToBuffer (ident, 1024);
|
|
||||||
s.write ((char *)ident, identLen);
|
|
||||||
WriteToStream (s);
|
|
||||||
m_BufferLen = s.str ().size ();
|
|
||||||
if (!m_Buffer)
|
|
||||||
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
|
|
||||||
memcpy (m_Buffer, s.str ().c_str (), m_BufferLen);
|
|
||||||
// signature
|
|
||||||
privateKeys.Sign ((uint8_t *)m_Buffer, m_BufferLen, (uint8_t *)m_Buffer + m_BufferLen);
|
|
||||||
m_BufferLen += privateKeys.GetPublic ()->GetSignatureLen ();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RouterInfo::SaveToFile (const std::string& fullPath)
|
|
||||||
{
|
|
||||||
m_FullPath = fullPath;
|
|
||||||
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, size_t len, std::istream& s) const
|
|
||||||
{
|
|
||||||
uint8_t l;
|
|
||||||
s.read ((char *)&l, 1);
|
|
||||||
if (l < len)
|
|
||||||
{
|
|
||||||
s.read (str, l);
|
|
||||||
if (!s) l = 0; // failed, return empty string
|
|
||||||
str[l] = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "RouterInfo: string length ", (int)l, " exceeds buffer size ", len);
|
|
||||||
s.seekg (l, std::ios::cur); // skip
|
|
||||||
str[0] = 0;
|
|
||||||
}
|
|
||||||
return l+1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterInfo::WriteString (const std::string& str, std::ostream& s) const
|
|
||||||
{
|
|
||||||
uint8_t len = str.size ();
|
|
||||||
s.write ((char *)&len, 1);
|
|
||||||
s.write (str.c_str (), len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterInfo::AddNTCPAddress (const char * host, int port)
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
if (addr->transportStyle == eTransportSSU && addr->host.is_v4 ())
|
|
||||||
{
|
|
||||||
for (auto& intro: addr->introducers)
|
|
||||||
if (intro.iTag == introducer.iTag) return false; // already presented
|
|
||||||
addr->introducers.push_back (introducer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RouterInfo::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e)
|
|
||||||
{
|
|
||||||
for (auto& addr: *m_Addresses)
|
|
||||||
{
|
|
||||||
if (addr->transportStyle == eTransportSSU && addr->host.is_v4 ())
|
|
||||||
{
|
|
||||||
for (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);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterInfo::SetCaps (uint8_t caps)
|
|
||||||
{
|
|
||||||
m_Caps = caps;
|
|
||||||
UpdateCapsProperty ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterInfo::SetCaps (const char * caps)
|
|
||||||
{
|
|
||||||
SetProperty ("caps", caps);
|
|
||||||
m_Caps = 0;
|
|
||||||
ExtractCaps (caps);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterInfo::SetProperty (const std::string& key, const std::string& value)
|
|
||||||
{
|
|
||||||
m_Properties[key] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterInfo::DeleteProperty (const std::string& key)
|
|
||||||
{
|
|
||||||
m_Properties.erase (key);
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
return m_SupportedTransports & eNTCPV4;
|
|
||||||
else
|
|
||||||
return m_SupportedTransports & (eNTCPV4 | eNTCPV6);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RouterInfo::IsSSU (bool v4only) const
|
|
||||||
{
|
|
||||||
if (v4only)
|
|
||||||
return m_SupportedTransports & eSSUV4;
|
|
||||||
else
|
|
||||||
return m_SupportedTransports & (eSSUV4 | eSSUV6);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RouterInfo::IsV6 () const
|
|
||||||
{
|
|
||||||
return m_SupportedTransports & (eNTCPV6 | eSSUV6);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 ())
|
|
||||||
{
|
|
||||||
m_SupportedTransports &= ~(eNTCPV6 | eSSUV6);
|
|
||||||
for (auto it = m_Addresses->begin (); it != m_Addresses->end ();)
|
|
||||||
{
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetNTCPAddress (bool v4only) const
|
|
||||||
{
|
|
||||||
return GetAddress (eTransportNTCP, v4only);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSUAddress (bool v4only) const
|
|
||||||
{
|
|
||||||
return GetAddress (eTransportSSU, v4only);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSUV6Address () const
|
|
||||||
{
|
|
||||||
return GetAddress (eTransportSSU, false, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetAddress (TransportStyle s, bool v4only, bool v6only) const
|
|
||||||
{
|
|
||||||
#if (BOOST_VERSION >= 105300)
|
|
||||||
auto addresses = boost::atomic_load (&m_Addresses);
|
|
||||||
#else
|
|
||||||
auto addresses = m_Addresses;
|
|
||||||
#endif
|
|
||||||
for (const auto& address : *addresses)
|
|
||||||
{
|
|
||||||
if (address->transportStyle == s)
|
|
||||||
{
|
|
||||||
if ((!v4only || address->host.is_v4 ()) && (!v6only || address->host.is_v6 ()))
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<RouterProfile> RouterInfo::GetProfile () const
|
|
||||||
{
|
|
||||||
if (!m_Profile)
|
|
||||||
m_Profile = GetRouterProfile (GetIdentHash ());
|
|
||||||
return m_Profile;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
214
RouterInfo.h
214
RouterInfo.h
@@ -1,214 +0,0 @@
|
|||||||
#ifndef ROUTER_INFO_H__
|
|
||||||
#define ROUTER_INFO_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <string>
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
#include <list>
|
|
||||||
#include <iostream>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include <boost/shared_ptr.hpp>
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "Profiling.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace data
|
|
||||||
{
|
|
||||||
const char ROUTER_INFO_PROPERTY_LEASESETS[] = "netdb.knownLeaseSets";
|
|
||||||
const char ROUTER_INFO_PROPERTY_ROUTERS[] = "netdb.knownRouters";
|
|
||||||
const char ROUTER_INFO_PROPERTY_NETID[] = "netId";
|
|
||||||
const char ROUTER_INFO_PROPERTY_FAMILY[] = "family";
|
|
||||||
const char ROUTER_INFO_PROPERTY_FAMILY_SIG[] = "family.sig";
|
|
||||||
|
|
||||||
const char CAPS_FLAG_FLOODFILL = 'f';
|
|
||||||
const char CAPS_FLAG_HIDDEN = 'H';
|
|
||||||
const char CAPS_FLAG_REACHABLE = 'R';
|
|
||||||
const char CAPS_FLAG_UNREACHABLE = 'U';
|
|
||||||
/* 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';
|
|
||||||
|
|
||||||
const int MAX_RI_BUFFER_SIZE = 2048;
|
|
||||||
class RouterInfo: public RoutingDestination
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
enum SupportedTranports
|
|
||||||
{
|
|
||||||
eNTCPV4 = 0x01,
|
|
||||||
eNTCPV6 = 0x02,
|
|
||||||
eSSUV4 = 0x04,
|
|
||||||
eSSUV6 = 0x08
|
|
||||||
};
|
|
||||||
|
|
||||||
enum Caps
|
|
||||||
{
|
|
||||||
eFloodfill = 0x01,
|
|
||||||
eHighBandwidth = 0x02,
|
|
||||||
eExtraBandwidth = 0x04,
|
|
||||||
eReachable = 0x08,
|
|
||||||
eSSUTesting = 0x10,
|
|
||||||
eSSUIntroducer = 0x20,
|
|
||||||
eHidden = 0x40,
|
|
||||||
eUnreachable = 0x80
|
|
||||||
};
|
|
||||||
|
|
||||||
enum TransportStyle
|
|
||||||
{
|
|
||||||
eTransportUnknown = 0,
|
|
||||||
eTransportNTCP,
|
|
||||||
eTransportSSU
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef Tag<32> IntroKey; // should be castable to MacKey and AESKey
|
|
||||||
struct Introducer
|
|
||||||
{
|
|
||||||
boost::asio::ip::address iHost;
|
|
||||||
int iPort;
|
|
||||||
IntroKey iKey;
|
|
||||||
uint32_t iTag;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Address
|
|
||||||
{
|
|
||||||
TransportStyle transportStyle;
|
|
||||||
boost::asio::ip::address host;
|
|
||||||
std::string addressString;
|
|
||||||
int port, mtu;
|
|
||||||
uint64_t date;
|
|
||||||
uint8_t cost;
|
|
||||||
// SSU only
|
|
||||||
IntroKey key; // intro key for SSU
|
|
||||||
std::vector<Introducer> introducers;
|
|
||||||
|
|
||||||
bool IsCompatible (const boost::asio::ip::address& other) const
|
|
||||||
{
|
|
||||||
return (host.is_v4 () && other.is_v4 ()) ||
|
|
||||||
(host.is_v6 () && other.is_v6 ());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const Address& other) const
|
|
||||||
{
|
|
||||||
return transportStyle == other.transportStyle && host == other.host && port == other.port;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const Address& other) const
|
|
||||||
{
|
|
||||||
return !(*this == other);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
typedef std::list<std::shared_ptr<Address> > Addresses;
|
|
||||||
|
|
||||||
RouterInfo ();
|
|
||||||
RouterInfo (const std::string& fullPath);
|
|
||||||
RouterInfo (const RouterInfo& ) = default;
|
|
||||||
RouterInfo& operator=(const RouterInfo& ) = default;
|
|
||||||
RouterInfo (const uint8_t * buf, int len);
|
|
||||||
~RouterInfo ();
|
|
||||||
|
|
||||||
std::shared_ptr<const IdentityEx> GetRouterIdentity () const { return m_RouterIdentity; };
|
|
||||||
void SetRouterIdentity (std::shared_ptr<const IdentityEx> identity);
|
|
||||||
std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); };
|
|
||||||
uint64_t GetTimestamp () const { return m_Timestamp; };
|
|
||||||
Addresses& GetAddresses () { return *m_Addresses; }; // should be called for local RI only, otherwise must return shared_ptr
|
|
||||||
std::shared_ptr<const Address> GetNTCPAddress (bool v4only = true) const;
|
|
||||||
std::shared_ptr<const Address> GetSSUAddress (bool v4only = true) const;
|
|
||||||
std::shared_ptr<const Address> GetSSUV6Address () const;
|
|
||||||
|
|
||||||
void AddNTCPAddress (const char * host, int port);
|
|
||||||
void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0);
|
|
||||||
bool AddIntroducer (const Introducer& introducer);
|
|
||||||
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; };
|
|
||||||
bool IsPeerTesting () const { return m_Caps & eSSUTesting; };
|
|
||||||
bool IsHidden () const { return m_Caps & eHidden; };
|
|
||||||
bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; };
|
|
||||||
bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; };
|
|
||||||
|
|
||||||
uint8_t GetCaps () const { return m_Caps; };
|
|
||||||
void SetCaps (uint8_t caps);
|
|
||||||
void SetCaps (const char * caps);
|
|
||||||
|
|
||||||
void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; };
|
|
||||||
bool IsUnreachable () const { return m_IsUnreachable; };
|
|
||||||
|
|
||||||
const uint8_t * GetBuffer () const { return m_Buffer; };
|
|
||||||
const uint8_t * LoadBuffer (); // load if necessary
|
|
||||||
int GetBufferLen () const { return m_BufferLen; };
|
|
||||||
void CreateBuffer (const PrivateKeys& privateKeys);
|
|
||||||
|
|
||||||
bool IsUpdated () const { return m_IsUpdated; };
|
|
||||||
void SetUpdated (bool updated) { m_IsUpdated = updated; };
|
|
||||||
bool SaveToFile (const std::string& fullPath);
|
|
||||||
|
|
||||||
std::shared_ptr<RouterProfile> GetProfile () const;
|
|
||||||
void SaveProfile () { if (m_Profile) m_Profile->Save (); };
|
|
||||||
|
|
||||||
void Update (const uint8_t * buf, int len);
|
|
||||||
void DeleteBuffer () { delete[] m_Buffer; m_Buffer = nullptr; };
|
|
||||||
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) const;
|
|
||||||
size_t ReadString (char* str, size_t len, std::istream& s) const;
|
|
||||||
void WriteString (const std::string& str, std::ostream& s) const;
|
|
||||||
void ExtractCaps (const char * value);
|
|
||||||
std::shared_ptr<const Address> GetAddress (TransportStyle s, bool v4only, bool v6only = false) const;
|
|
||||||
void UpdateCapsProperty ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::string m_FullPath, m_Family;
|
|
||||||
std::shared_ptr<const IdentityEx> m_RouterIdentity;
|
|
||||||
uint8_t * m_Buffer;
|
|
||||||
size_t m_BufferLen;
|
|
||||||
uint64_t m_Timestamp;
|
|
||||||
boost::shared_ptr<Addresses> m_Addresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9
|
|
||||||
std::map<std::string, std::string> m_Properties;
|
|
||||||
bool m_IsUpdated, m_IsUnreachable;
|
|
||||||
uint8_t m_SupportedTransports, m_Caps;
|
|
||||||
mutable std::shared_ptr<RouterProfile> m_Profile;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
913
SAM.cpp
913
SAM.cpp
@@ -1,913 +0,0 @@
|
|||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#include <stdlib.h>
|
|
||||||
#endif
|
|
||||||
#include "Base.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "Destination.h"
|
|
||||||
#include "ClientContext.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include "SAM.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
SAMSocket::SAMSocket (SAMBridge& owner):
|
|
||||||
m_Owner (owner), m_Socket (m_Owner.GetService ()), m_Timer (m_Owner.GetService ()),
|
|
||||||
m_BufferOffset (0), m_SocketType (eSAMSocketTypeUnknown), m_IsSilent (false),
|
|
||||||
m_Stream (nullptr), m_Session (nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SAMSocket::~SAMSocket ()
|
|
||||||
{
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::CloseStream ()
|
|
||||||
{
|
|
||||||
if (m_Stream)
|
|
||||||
{
|
|
||||||
m_Stream->Close ();
|
|
||||||
m_Stream.reset ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::Terminate ()
|
|
||||||
{
|
|
||||||
CloseStream ();
|
|
||||||
|
|
||||||
switch (m_SocketType)
|
|
||||||
{
|
|
||||||
case eSAMSocketTypeSession:
|
|
||||||
m_Owner.CloseSession (m_ID);
|
|
||||||
break;
|
|
||||||
case eSAMSocketTypeStream:
|
|
||||||
{
|
|
||||||
if (m_Session)
|
|
||||||
m_Session->DelSocket (shared_from_this ());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case eSAMSocketTypeAcceptor:
|
|
||||||
{
|
|
||||||
if (m_Session)
|
|
||||||
{
|
|
||||||
m_Session->DelSocket (shared_from_this ());
|
|
||||||
if (m_Session->localDestination)
|
|
||||||
m_Session->localDestination->StopAcceptingStreams ();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
;
|
|
||||||
}
|
|
||||||
m_SocketType = eSAMSocketTypeTerminated;
|
|
||||||
if (m_Socket.is_open()) m_Socket.close ();
|
|
||||||
m_Session = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::ReceiveHandshake ()
|
|
||||||
{
|
|
||||||
m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE),
|
|
||||||
std::bind(&SAMSocket::HandleHandshakeReceived, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SAM: handshake read error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_Buffer[bytes_transferred] = 0;
|
|
||||||
char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred);
|
|
||||||
if (eol)
|
|
||||||
*eol = 0;
|
|
||||||
LogPrint (eLogDebug, "SAM: handshake ", m_Buffer);
|
|
||||||
char * separator = strchr (m_Buffer, ' ');
|
|
||||||
if (separator)
|
|
||||||
{
|
|
||||||
separator = strchr (separator + 1, ' ');
|
|
||||||
if (separator)
|
|
||||||
*separator = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strcmp (m_Buffer, SAM_HANDSHAKE))
|
|
||||||
{
|
|
||||||
std::string version("3.0");
|
|
||||||
// try to find MIN and MAX, 3.0 if not found
|
|
||||||
if (separator)
|
|
||||||
{
|
|
||||||
separator++;
|
|
||||||
std::map<std::string, std::string> params;
|
|
||||||
ExtractParams (separator, params);
|
|
||||||
auto it = params.find (SAM_PARAM_MAX);
|
|
||||||
// TODO: check MIN as well
|
|
||||||
if (it != params.end ())
|
|
||||||
version = it->second;
|
|
||||||
}
|
|
||||||
if (version[0] == '3') // we support v3 (3.0 and 3.1) only
|
|
||||||
{
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ());
|
|
||||||
#else
|
|
||||||
size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ());
|
|
||||||
#endif
|
|
||||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_Buffer, l), boost::asio::transfer_all (),
|
|
||||||
std::bind(&SAMSocket::HandleHandshakeReplySent, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SendMessageReply (SAM_HANDSHAKE_I2P_ERROR, strlen (SAM_HANDSHAKE_I2P_ERROR), true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SAM: handshake mismatch");
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SAM: handshake reply send error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE),
|
|
||||||
std::bind(&SAMSocket::HandleMessage, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::SendMessageReply (const char * msg, size_t len, bool close)
|
|
||||||
{
|
|
||||||
if (!m_IsSilent)
|
|
||||||
boost::asio::async_write (m_Socket, boost::asio::buffer (msg, len), boost::asio::transfer_all (),
|
|
||||||
std::bind(&SAMSocket::HandleMessageReplySent, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2, close));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (close)
|
|
||||||
Terminate ();
|
|
||||||
else
|
|
||||||
Receive ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SAM: reply send error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (close)
|
|
||||||
Terminate ();
|
|
||||||
else
|
|
||||||
Receive ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SAM: read error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else if (m_SocketType == eSAMSocketTypeStream)
|
|
||||||
HandleReceived (ecode, bytes_transferred);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bytes_transferred += m_BufferOffset;
|
|
||||||
m_BufferOffset = 0;
|
|
||||||
m_Buffer[bytes_transferred] = 0;
|
|
||||||
char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred);
|
|
||||||
if (eol)
|
|
||||||
{
|
|
||||||
*eol = 0;
|
|
||||||
char * separator = strchr (m_Buffer, ' ');
|
|
||||||
if (separator)
|
|
||||||
{
|
|
||||||
separator = strchr (separator + 1, ' ');
|
|
||||||
if (separator)
|
|
||||||
*separator = 0;
|
|
||||||
else
|
|
||||||
separator = eol;
|
|
||||||
|
|
||||||
if (!strcmp (m_Buffer, SAM_SESSION_CREATE))
|
|
||||||
ProcessSessionCreate (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
|
|
||||||
else if (!strcmp (m_Buffer, SAM_STREAM_CONNECT))
|
|
||||||
ProcessStreamConnect (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
|
|
||||||
else if (!strcmp (m_Buffer, SAM_STREAM_ACCEPT))
|
|
||||||
ProcessStreamAccept (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
|
|
||||||
else if (!strcmp (m_Buffer, SAM_DEST_GENERATE))
|
|
||||||
ProcessDestGenerate ();
|
|
||||||
else if (!strcmp (m_Buffer, SAM_NAMING_LOOKUP))
|
|
||||||
ProcessNamingLookup (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
|
|
||||||
else if (!strcmp (m_Buffer, SAM_DATAGRAM_SEND))
|
|
||||||
{
|
|
||||||
size_t len = bytes_transferred - (separator - m_Buffer) - 1;
|
|
||||||
size_t processed = ProcessDatagramSend (separator + 1, len, eol + 1);
|
|
||||||
if (processed < len)
|
|
||||||
{
|
|
||||||
m_BufferOffset = len - processed;
|
|
||||||
if (processed > 0)
|
|
||||||
memmove (m_Buffer, separator + 1 + processed, m_BufferOffset);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// restore string back
|
|
||||||
*separator = ' ';
|
|
||||||
*eol = '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// since it's SAM v1 reply is not expected
|
|
||||||
Receive ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SAM: unexpected message ", m_Buffer);
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SAM: malformed message ", m_Buffer);
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "SAM: incomplete message ", bytes_transferred);
|
|
||||||
m_BufferOffset = bytes_transferred;
|
|
||||||
// try to receive remaining message
|
|
||||||
Receive ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::ProcessSessionCreate (char * buf, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "SAM: session create: ", buf);
|
|
||||||
std::map<std::string, std::string> params;
|
|
||||||
ExtractParams (buf, params);
|
|
||||||
std::string& style = params[SAM_PARAM_STYLE];
|
|
||||||
std::string& id = params[SAM_PARAM_ID];
|
|
||||||
std::string& destination = params[SAM_PARAM_DESTINATION];
|
|
||||||
m_ID = id;
|
|
||||||
if (m_Owner.FindSession (id))
|
|
||||||
{
|
|
||||||
// session exists
|
|
||||||
SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create destination
|
|
||||||
m_Session = m_Owner.CreateSession (id, destination == SAM_VALUE_TRANSIENT ? "" : destination, ¶ms);
|
|
||||||
if (m_Session)
|
|
||||||
{
|
|
||||||
m_SocketType = eSAMSocketTypeSession;
|
|
||||||
if (style == SAM_VALUE_DATAGRAM)
|
|
||||||
{
|
|
||||||
auto dest = m_Session->localDestination->CreateDatagramDestination ();
|
|
||||||
dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_Session->localDestination->IsReady ())
|
|
||||||
SendSessionCreateReplyOk ();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL));
|
|
||||||
m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer,
|
|
||||||
shared_from_this (), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_DEST, strlen(SAM_SESSION_CREATE_DUPLICATED_DEST), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
if (m_Session->localDestination->IsReady ())
|
|
||||||
SendSessionCreateReplyOk ();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL));
|
|
||||||
m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer,
|
|
||||||
shared_from_this (), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::SendSessionCreateReplyOk ()
|
|
||||||
{
|
|
||||||
uint8_t buf[1024];
|
|
||||||
char priv[1024];
|
|
||||||
size_t l = m_Session->localDestination->GetPrivateKeys ().ToBuffer (buf, 1024);
|
|
||||||
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, priv, 1024);
|
|
||||||
priv[l1] = 0;
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t l2 = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv);
|
|
||||||
#else
|
|
||||||
size_t l2 = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv);
|
|
||||||
#endif
|
|
||||||
SendMessageReply (m_Buffer, l2, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::ProcessStreamConnect (char * buf, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "SAM: stream connect: ", buf);
|
|
||||||
std::map<std::string, std::string> params;
|
|
||||||
ExtractParams (buf, params);
|
|
||||||
std::string& id = params[SAM_PARAM_ID];
|
|
||||||
std::string& destination = params[SAM_PARAM_DESTINATION];
|
|
||||||
std::string& silent = params[SAM_PARAM_SILENT];
|
|
||||||
if (silent == SAM_VALUE_TRUE) m_IsSilent = true;
|
|
||||||
m_ID = id;
|
|
||||||
m_Session = m_Owner.FindSession (id);
|
|
||||||
if (m_Session)
|
|
||||||
{
|
|
||||||
auto dest = std::make_shared<i2p::data::IdentityEx> ();
|
|
||||||
size_t len = dest->FromBase64(destination);
|
|
||||||
if (len > 0)
|
|
||||||
{
|
|
||||||
context.GetAddressBook().InsertAddress(dest);
|
|
||||||
auto leaseSet = m_Session->localDestination->FindLeaseSet(dest->GetIdentHash());
|
|
||||||
if (leaseSet)
|
|
||||||
Connect(leaseSet);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_Session->localDestination->RequestDestination(dest->GetIdentHash(),
|
|
||||||
std::bind(&SAMSocket::HandleConnectLeaseSetRequestComplete,
|
|
||||||
shared_from_this(), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SendMessageReply(SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::Connect (std::shared_ptr<const i2p::data::LeaseSet> remote)
|
|
||||||
{
|
|
||||||
m_SocketType = eSAMSocketTypeStream;
|
|
||||||
m_Session->AddSocket (shared_from_this ());
|
|
||||||
m_Stream = m_Session->localDestination->CreateStream (remote);
|
|
||||||
m_Stream->Send ((uint8_t *)m_Buffer, 0); // connect
|
|
||||||
I2PReceive ();
|
|
||||||
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::HandleConnectLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet)
|
|
||||||
{
|
|
||||||
if (leaseSet)
|
|
||||||
Connect (leaseSet);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SAM: destination to connect not found");
|
|
||||||
SendMessageReply (SAM_STREAM_STATUS_CANT_REACH_PEER, strlen(SAM_STREAM_STATUS_CANT_REACH_PEER), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::ProcessStreamAccept (char * buf, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "SAM: stream accept: ", buf);
|
|
||||||
std::map<std::string, std::string> params;
|
|
||||||
ExtractParams (buf, params);
|
|
||||||
std::string& id = params[SAM_PARAM_ID];
|
|
||||||
std::string& silent = params[SAM_PARAM_SILENT];
|
|
||||||
if (silent == SAM_VALUE_TRUE) m_IsSilent = true;
|
|
||||||
m_ID = id;
|
|
||||||
m_Session = m_Owner.FindSession (id);
|
|
||||||
if (m_Session)
|
|
||||||
{
|
|
||||||
if (!m_Session->localDestination->IsAcceptingStreams ())
|
|
||||||
{
|
|
||||||
m_SocketType = eSAMSocketTypeAcceptor;
|
|
||||||
m_Session->AddSocket (shared_from_this ());
|
|
||||||
m_Session->localDestination->AcceptStreams (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1));
|
|
||||||
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SendMessageReply (SAM_STREAM_STATUS_I2P_ERROR, strlen(SAM_STREAM_STATUS_I2P_ERROR), true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t SAMSocket::ProcessDatagramSend (char * buf, size_t len, const char * data)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "SAM: datagram send: ", buf, " ", len);
|
|
||||||
std::map<std::string, std::string> params;
|
|
||||||
ExtractParams (buf, params);
|
|
||||||
size_t size = std::stoi(params[SAM_PARAM_SIZE]), offset = data - buf;
|
|
||||||
if (offset + size <= len)
|
|
||||||
{
|
|
||||||
if (m_Session)
|
|
||||||
{
|
|
||||||
auto d = m_Session->localDestination->GetDatagramDestination ();
|
|
||||||
if (d)
|
|
||||||
{
|
|
||||||
i2p::data::IdentityEx dest;
|
|
||||||
dest.FromBase64 (params[SAM_PARAM_DESTINATION]);
|
|
||||||
d->SendDatagramTo ((const uint8_t *)data, size, dest.GetIdentHash ());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "SAM: missing datagram destination");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "SAM: session is not created from DATAGRAM SEND");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "SAM: sent datagram size ", size, " exceeds buffer ", len - offset);
|
|
||||||
return 0; // try to receive more
|
|
||||||
}
|
|
||||||
return offset + size;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::ProcessDestGenerate ()
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "SAM: dest generate");
|
|
||||||
auto keys = i2p::data::PrivateKeys::CreateRandomKeys ();
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY,
|
|
||||||
keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ());
|
|
||||||
#else
|
|
||||||
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY,
|
|
||||||
keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ());
|
|
||||||
#endif
|
|
||||||
SendMessageReply (m_Buffer, len, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::ProcessNamingLookup (char * buf, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "SAM: naming lookup: ", buf);
|
|
||||||
std::map<std::string, std::string> params;
|
|
||||||
ExtractParams (buf, params);
|
|
||||||
std::string& name = params[SAM_PARAM_NAME];
|
|
||||||
std::shared_ptr<const i2p::data::IdentityEx> identity;
|
|
||||||
i2p::data::IdentHash ident;
|
|
||||||
if (name == "ME")
|
|
||||||
SendNamingLookupReply (m_Session->localDestination->GetIdentity ());
|
|
||||||
else if ((identity = context.GetAddressBook ().GetAddress (name)) != nullptr)
|
|
||||||
SendNamingLookupReply (identity);
|
|
||||||
else if (m_Session && m_Session->localDestination &&
|
|
||||||
context.GetAddressBook ().GetIdentHash (name, ident))
|
|
||||||
{
|
|
||||||
auto leaseSet = m_Session->localDestination->FindLeaseSet (ident);
|
|
||||||
if (leaseSet)
|
|
||||||
SendNamingLookupReply (leaseSet->GetIdentity ());
|
|
||||||
else
|
|
||||||
m_Session->localDestination->RequestDestination (ident,
|
|
||||||
std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete,
|
|
||||||
shared_from_this (), std::placeholders::_1, ident));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SAM: naming failed, unknown address ", name);
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str());
|
|
||||||
#else
|
|
||||||
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str());
|
|
||||||
#endif
|
|
||||||
SendMessageReply (m_Buffer, len, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, i2p::data::IdentHash ident)
|
|
||||||
{
|
|
||||||
if (leaseSet)
|
|
||||||
{
|
|
||||||
context.GetAddressBook ().InsertAddress (leaseSet->GetIdentity ());
|
|
||||||
SendNamingLookupReply (leaseSet->GetIdentity ());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SAM: naming lookup failed. LeaseSet for ", ident.ToBase32 (), " not found");
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY,
|
|
||||||
context.GetAddressBook ().ToAddress (ident).c_str());
|
|
||||||
#else
|
|
||||||
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY,
|
|
||||||
context.GetAddressBook ().ToAddress (ident).c_str());
|
|
||||||
#endif
|
|
||||||
SendMessageReply (m_Buffer, len, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::SendNamingLookupReply (std::shared_ptr<const i2p::data::IdentityEx> identity)
|
|
||||||
{
|
|
||||||
auto base64 = identity->ToBase64 ();
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ());
|
|
||||||
#else
|
|
||||||
size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ());
|
|
||||||
#endif
|
|
||||||
SendMessageReply (m_Buffer, l, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::ExtractParams (char * buf, std::map<std::string, std::string>& params)
|
|
||||||
{
|
|
||||||
char * separator;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
separator = strchr (buf, ' ');
|
|
||||||
if (separator) *separator = 0;
|
|
||||||
char * value = strchr (buf, '=');
|
|
||||||
if (value)
|
|
||||||
{
|
|
||||||
*value = 0;
|
|
||||||
value++;
|
|
||||||
params[buf] = value;
|
|
||||||
}
|
|
||||||
buf = separator + 1;
|
|
||||||
}
|
|
||||||
while (separator);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::Receive ()
|
|
||||||
{
|
|
||||||
if (m_BufferOffset >= SAM_SOCKET_BUFFER_SIZE)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SAM: Buffer is full, terminate");
|
|
||||||
Terminate ();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_Socket.async_read_some (boost::asio::buffer(m_Buffer + m_BufferOffset, SAM_SOCKET_BUFFER_SIZE - m_BufferOffset),
|
|
||||||
std::bind((m_SocketType == eSAMSocketTypeStream) ? &SAMSocket::HandleReceived : &SAMSocket::HandleMessage,
|
|
||||||
shared_from_this (), std::placeholders::_1, std::placeholders::_2));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SAM: read error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (m_Stream)
|
|
||||||
{
|
|
||||||
auto s = shared_from_this ();
|
|
||||||
m_Stream->AsyncSend ((uint8_t *)m_Buffer, bytes_transferred,
|
|
||||||
[s](const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (!ecode)
|
|
||||||
s->Receive ();
|
|
||||||
else
|
|
||||||
s->Terminate ();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::I2PReceive ()
|
|
||||||
{
|
|
||||||
if (m_Stream)
|
|
||||||
{
|
|
||||||
if (m_Stream->GetStatus () == i2p::stream::eStreamStatusNew ||
|
|
||||||
m_Stream->GetStatus () == i2p::stream::eStreamStatusOpen) // regular
|
|
||||||
{
|
|
||||||
m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE),
|
|
||||||
std::bind (&SAMSocket::HandleI2PReceive, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2),
|
|
||||||
SAM_SOCKET_CONNECTION_MAX_IDLE);
|
|
||||||
}
|
|
||||||
else // closed by peer
|
|
||||||
{
|
|
||||||
// get remaning data
|
|
||||||
auto len = m_Stream->ReadSome (m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE);
|
|
||||||
if (len > 0) // still some data
|
|
||||||
{
|
|
||||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, len),
|
|
||||||
std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
else // no more data
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SAM: stream read error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
if (bytes_transferred > 0)
|
|
||||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred),
|
|
||||||
std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1)); // postpone termination
|
|
||||||
else
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred),
|
|
||||||
std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::HandleWriteI2PData (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SAM: socket write error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
I2PReceive ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::HandleI2PAccept (std::shared_ptr<i2p::stream::Stream> stream)
|
|
||||||
{
|
|
||||||
if (stream)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "SAM: incoming I2P connection for session ", m_ID);
|
|
||||||
m_Stream = stream;
|
|
||||||
context.GetAddressBook ().InsertAddress (stream->GetRemoteIdentity ());
|
|
||||||
auto session = m_Owner.FindSession (m_ID);
|
|
||||||
if (session)
|
|
||||||
session->localDestination->StopAcceptingStreams ();
|
|
||||||
m_SocketType = eSAMSocketTypeStream;
|
|
||||||
if (!m_IsSilent)
|
|
||||||
{
|
|
||||||
// get remote peer address
|
|
||||||
auto ident_ptr = stream->GetRemoteIdentity();
|
|
||||||
const size_t ident_len = ident_ptr->GetFullLen();
|
|
||||||
uint8_t* ident = new uint8_t[ident_len];
|
|
||||||
|
|
||||||
// send remote peer address as base64
|
|
||||||
const size_t l = ident_ptr->ToBuffer (ident, ident_len);
|
|
||||||
const size_t l1 = i2p::data::ByteStreamToBase64 (ident, l, (char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE);
|
|
||||||
delete[] ident;
|
|
||||||
m_StreamBuffer[l1] = '\n';
|
|
||||||
HandleI2PReceive (boost::system::error_code (), l1 +1); // we send identity like it has been received from stream
|
|
||||||
}
|
|
||||||
else
|
|
||||||
I2PReceive ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "SAM: I2P acceptor has been reset");
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "SAM: datagram received ", len);
|
|
||||||
auto base64 = from.ToBase64 ();
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), len);
|
|
||||||
#else
|
|
||||||
size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), len);
|
|
||||||
#endif
|
|
||||||
if (len < SAM_SOCKET_BUFFER_SIZE - l)
|
|
||||||
{
|
|
||||||
memcpy (m_StreamBuffer + l, buf, len);
|
|
||||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, len + l),
|
|
||||||
std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "SAM: received datagram size ", len," exceeds buffer");
|
|
||||||
}
|
|
||||||
|
|
||||||
SAMSession::SAMSession (std::shared_ptr<ClientDestination> dest):
|
|
||||||
localDestination (dest)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SAMSession::~SAMSession ()
|
|
||||||
{
|
|
||||||
CloseStreams();
|
|
||||||
i2p::client::context.DeleteLocalDestination (localDestination);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSession::CloseStreams ()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
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):
|
|
||||||
m_IsRunning (false), m_Thread (nullptr),
|
|
||||||
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)),
|
|
||||||
m_DatagramEndpoint (boost::asio::ip::address::from_string(address), port-1), m_DatagramSocket (m_Service, m_DatagramEndpoint)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SAMBridge::~SAMBridge ()
|
|
||||||
{
|
|
||||||
if (m_IsRunning)
|
|
||||||
Stop ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMBridge::Start ()
|
|
||||||
{
|
|
||||||
Accept ();
|
|
||||||
ReceiveDatagram ();
|
|
||||||
m_IsRunning = true;
|
|
||||||
m_Thread = new std::thread (std::bind (&SAMBridge::Run, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMBridge::Stop ()
|
|
||||||
{
|
|
||||||
m_IsRunning = false;
|
|
||||||
m_Acceptor.cancel ();
|
|
||||||
for (auto& it: m_Sessions)
|
|
||||||
it.second->CloseStreams ();
|
|
||||||
m_Sessions.clear ();
|
|
||||||
m_Service.stop ();
|
|
||||||
if (m_Thread)
|
|
||||||
{
|
|
||||||
m_Thread->join ();
|
|
||||||
delete m_Thread;
|
|
||||||
m_Thread = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMBridge::Run ()
|
|
||||||
{
|
|
||||||
while (m_IsRunning)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_Service.run ();
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SAM: runtime exception: ", ex.what ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMBridge::Accept ()
|
|
||||||
{
|
|
||||||
auto newSocket = std::make_shared<SAMSocket> (*this);
|
|
||||||
m_Acceptor.async_accept (newSocket->GetSocket (), std::bind (&SAMBridge::HandleAccept, this,
|
|
||||||
std::placeholders::_1, newSocket));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMBridge::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<SAMSocket> socket)
|
|
||||||
{
|
|
||||||
if (!ecode)
|
|
||||||
{
|
|
||||||
boost::system::error_code ec;
|
|
||||||
auto ep = socket->GetSocket ().remote_endpoint (ec);
|
|
||||||
if (!ec)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "SAM: new connection from ", ep);
|
|
||||||
socket->ReceiveHandshake ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "SAM: incoming connection error ", ec.message ());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "SAM: accept error: ", ecode.message ());
|
|
||||||
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Accept ();
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
if (destination != "")
|
|
||||||
{
|
|
||||||
i2p::data::PrivateKeys keys;
|
|
||||||
keys.FromBase64 (destination);
|
|
||||||
localDestination = i2p::client::context.CreateNewLocalDestination (keys, true, params);
|
|
||||||
}
|
|
||||||
else // transient
|
|
||||||
{
|
|
||||||
// extract signature type
|
|
||||||
i2p::data::SigningKeyType signatureType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1;
|
|
||||||
if (params)
|
|
||||||
{
|
|
||||||
auto it = params->find (SAM_PARAM_SIGNATURE_TYPE);
|
|
||||||
if (it != params->end ())
|
|
||||||
// TODO: extract string values
|
|
||||||
signatureType = std::stoi(it->second);
|
|
||||||
}
|
|
||||||
localDestination = i2p::client::context.CreateNewLocalDestination (true, signatureType, params);
|
|
||||||
}
|
|
||||||
if (localDestination)
|
|
||||||
{
|
|
||||||
auto session = std::make_shared<SAMSession>(localDestination);
|
|
||||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
|
||||||
auto ret = m_Sessions.insert (std::make_pair(id, session));
|
|
||||||
if (!ret.second)
|
|
||||||
LogPrint (eLogWarning, "SAM: Session ", id, " already exists");
|
|
||||||
return ret.first->second;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMBridge::CloseSession (const std::string& id)
|
|
||||||
{
|
|
||||||
std::shared_ptr<SAMSession> session;
|
|
||||||
{
|
|
||||||
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 ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
if (it != m_Sessions.end ())
|
|
||||||
return it->second;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMBridge::ReceiveDatagram ()
|
|
||||||
{
|
|
||||||
m_DatagramSocket.async_receive_from (
|
|
||||||
boost::asio::buffer (m_DatagramReceiveBuffer, i2p::datagram::MAX_DATAGRAM_SIZE),
|
|
||||||
m_SenderEndpoint,
|
|
||||||
std::bind (&SAMBridge::HandleReceivedDatagram, this, std::placeholders::_1, std::placeholders::_2));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMBridge::HandleReceivedDatagram (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (!ecode)
|
|
||||||
{
|
|
||||||
m_DatagramReceiveBuffer[bytes_transferred] = 0;
|
|
||||||
char * eol = strchr ((char *)m_DatagramReceiveBuffer, '\n');
|
|
||||||
*eol = 0; eol++;
|
|
||||||
size_t payloadLen = bytes_transferred - ((uint8_t *)eol - m_DatagramReceiveBuffer);
|
|
||||||
LogPrint (eLogDebug, "SAM: datagram received ", m_DatagramReceiveBuffer," size=", payloadLen);
|
|
||||||
char * sessionID = strchr ((char *)m_DatagramReceiveBuffer, ' ');
|
|
||||||
if (sessionID)
|
|
||||||
{
|
|
||||||
sessionID++;
|
|
||||||
char * destination = strchr (sessionID, ' ');
|
|
||||||
if (destination)
|
|
||||||
{
|
|
||||||
*destination = 0; destination++;
|
|
||||||
auto session = FindSession (sessionID);
|
|
||||||
if (session)
|
|
||||||
{
|
|
||||||
i2p::data::IdentityEx dest;
|
|
||||||
dest.FromBase64 (destination);
|
|
||||||
session->localDestination->GetDatagramDestination ()->
|
|
||||||
SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "SAM: Session ", sessionID, " not found");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "SAM: Missing destination key");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "SAM: Missing sessionID");
|
|
||||||
ReceiveDatagram ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "SAM: datagram receive error: ", ecode.message ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
215
SAM.h
215
SAM.h
@@ -1,215 +0,0 @@
|
|||||||
#ifndef SAM_H__
|
|
||||||
#define SAM_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <string>
|
|
||||||
#include <map>
|
|
||||||
#include <list>
|
|
||||||
#include <thread>
|
|
||||||
#include <mutex>
|
|
||||||
#include <memory>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "LeaseSet.h"
|
|
||||||
#include "Streaming.h"
|
|
||||||
#include "Destination.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
const size_t SAM_SOCKET_BUFFER_SIZE = 8192;
|
|
||||||
const int SAM_SOCKET_CONNECTION_MAX_IDLE = 3600; // in seconds
|
|
||||||
const int SAM_SESSION_READINESS_CHECK_INTERVAL = 20; // in seconds
|
|
||||||
const char SAM_HANDSHAKE[] = "HELLO VERSION";
|
|
||||||
const char SAM_HANDSHAKE_REPLY[] = "HELLO REPLY RESULT=OK VERSION=%s\n";
|
|
||||||
const char SAM_HANDSHAKE_I2P_ERROR[] = "HELLO REPLY RESULT=I2P_ERROR\n";
|
|
||||||
const char SAM_SESSION_CREATE[] = "SESSION CREATE";
|
|
||||||
const char SAM_SESSION_CREATE_REPLY_OK[] = "SESSION STATUS RESULT=OK DESTINATION=%s\n";
|
|
||||||
const char SAM_SESSION_CREATE_DUPLICATED_ID[] = "SESSION STATUS RESULT=DUPLICATED_ID\n";
|
|
||||||
const char SAM_SESSION_CREATE_DUPLICATED_DEST[] = "SESSION STATUS RESULT=DUPLICATED_DEST\n";
|
|
||||||
const char SAM_SESSION_STATUS_INVALID_KEY[] = "SESSION STATUS RESULT=INVALID_KEY\n";
|
|
||||||
const char SAM_STREAM_CONNECT[] = "STREAM CONNECT";
|
|
||||||
const char SAM_STREAM_STATUS_OK[] = "STREAM STATUS RESULT=OK\n";
|
|
||||||
const char SAM_STREAM_STATUS_INVALID_ID[] = "STREAM STATUS RESULT=INVALID_ID\n";
|
|
||||||
const char SAM_STREAM_STATUS_CANT_REACH_PEER[] = "STREAM STATUS RESULT=CANT_REACH_PEER\n";
|
|
||||||
const char SAM_STREAM_STATUS_I2P_ERROR[] = "STREAM STATUS RESULT=I2P_ERROR\n";
|
|
||||||
const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT";
|
|
||||||
const char SAM_DATAGRAM_SEND[] = "DATAGRAM SEND";
|
|
||||||
const char SAM_DEST_GENERATE[] = "DEST GENERATE";
|
|
||||||
const char SAM_DEST_REPLY[] = "DEST REPLY PUB=%s PRIV=%s\n";
|
|
||||||
const char SAM_DEST_REPLY_I2P_ERROR[] = "DEST REPLY RESULT=I2P_ERROR\n";
|
|
||||||
const char SAM_NAMING_LOOKUP[] = "NAMING LOOKUP";
|
|
||||||
const char SAM_NAMING_REPLY[] = "NAMING REPLY RESULT=OK NAME=ME VALUE=%s\n";
|
|
||||||
const char SAM_DATAGRAM_RECEIVED[] = "DATAGRAM RECEIVED DESTINATION=%s SIZE=%lu\n";
|
|
||||||
const char SAM_NAMING_REPLY_INVALID_KEY[] = "NAMING REPLY RESULT=INVALID_KEY NAME=%s\n";
|
|
||||||
const char SAM_NAMING_REPLY_KEY_NOT_FOUND[] = "NAMING REPLY RESULT=INVALID_KEY_NOT_FOUND NAME=%s\n";
|
|
||||||
const char SAM_PARAM_MIN[] = "MIN";
|
|
||||||
const char SAM_PARAM_MAX[] = "MAX";
|
|
||||||
const char SAM_PARAM_STYLE[] = "STYLE";
|
|
||||||
const char SAM_PARAM_ID[] = "ID";
|
|
||||||
const char SAM_PARAM_SILENT[] = "SILENT";
|
|
||||||
const char SAM_PARAM_DESTINATION[] = "DESTINATION";
|
|
||||||
const char SAM_PARAM_NAME[] = "NAME";
|
|
||||||
const char SAM_PARAM_SIGNATURE_TYPE[] = "SIGNATURE_TYPE";
|
|
||||||
const char SAM_PARAM_SIZE[] = "SIZE";
|
|
||||||
const char SAM_VALUE_TRANSIENT[] = "TRANSIENT";
|
|
||||||
const char SAM_VALUE_STREAM[] = "STREAM";
|
|
||||||
const char SAM_VALUE_DATAGRAM[] = "DATAGRAM";
|
|
||||||
const char SAM_VALUE_RAW[] = "RAW";
|
|
||||||
const char SAM_VALUE_TRUE[] = "true";
|
|
||||||
const char SAM_VALUE_FALSE[] = "false";
|
|
||||||
|
|
||||||
enum SAMSocketType
|
|
||||||
{
|
|
||||||
eSAMSocketTypeUnknown,
|
|
||||||
eSAMSocketTypeSession,
|
|
||||||
eSAMSocketTypeStream,
|
|
||||||
eSAMSocketTypeAcceptor,
|
|
||||||
eSAMSocketTypeTerminated
|
|
||||||
};
|
|
||||||
|
|
||||||
class SAMBridge;
|
|
||||||
struct SAMSession;
|
|
||||||
class SAMSocket: public std::enable_shared_from_this<SAMSocket>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
SAMSocket (SAMBridge& owner);
|
|
||||||
~SAMSocket ();
|
|
||||||
void CloseStream (); // TODO: implement it better
|
|
||||||
|
|
||||||
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
|
|
||||||
void ReceiveHandshake ();
|
|
||||||
void SetSocketType (SAMSocketType socketType) { m_SocketType = socketType; };
|
|
||||||
SAMSocketType GetSocketType () const { return m_SocketType; };
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void Terminate ();
|
|
||||||
void HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
|
||||||
void HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
|
||||||
void HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
|
||||||
void SendMessageReply (const char * msg, size_t len, bool close);
|
|
||||||
void HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close);
|
|
||||||
void Receive ();
|
|
||||||
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
|
||||||
|
|
||||||
void I2PReceive ();
|
|
||||||
void HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
|
||||||
void HandleI2PAccept (std::shared_ptr<i2p::stream::Stream> stream);
|
|
||||||
void HandleWriteI2PData (const boost::system::error_code& ecode);
|
|
||||||
void HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
|
||||||
|
|
||||||
void ProcessSessionCreate (char * buf, size_t len);
|
|
||||||
void ProcessStreamConnect (char * buf, size_t len);
|
|
||||||
void ProcessStreamAccept (char * buf, size_t len);
|
|
||||||
void ProcessDestGenerate ();
|
|
||||||
void ProcessNamingLookup (char * buf, size_t len);
|
|
||||||
size_t ProcessDatagramSend (char * buf, size_t len, const char * data); // from SAM 1.0
|
|
||||||
void ExtractParams (char * buf, std::map<std::string, std::string>& params);
|
|
||||||
|
|
||||||
void Connect (std::shared_ptr<const i2p::data::LeaseSet> remote);
|
|
||||||
void HandleConnectLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet);
|
|
||||||
void SendNamingLookupReply (std::shared_ptr<const i2p::data::IdentityEx> identity);
|
|
||||||
void HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, i2p::data::IdentHash ident);
|
|
||||||
void HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode);
|
|
||||||
void SendSessionCreateReplyOk ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
SAMBridge& m_Owner;
|
|
||||||
boost::asio::ip::tcp::socket m_Socket;
|
|
||||||
boost::asio::deadline_timer m_Timer;
|
|
||||||
char m_Buffer[SAM_SOCKET_BUFFER_SIZE + 1];
|
|
||||||
size_t m_BufferOffset;
|
|
||||||
uint8_t m_StreamBuffer[SAM_SOCKET_BUFFER_SIZE];
|
|
||||||
SAMSocketType m_SocketType;
|
|
||||||
std::string m_ID; // nickname
|
|
||||||
bool m_IsSilent;
|
|
||||||
std::shared_ptr<i2p::stream::Stream> m_Stream;
|
|
||||||
std::shared_ptr<SAMSession> m_Session;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SAMSession
|
|
||||||
{
|
|
||||||
std::shared_ptr<ClientDestination> localDestination;
|
|
||||||
std::list<std::shared_ptr<SAMSocket> > m_Sockets;
|
|
||||||
std::mutex m_SocketsMutex;
|
|
||||||
|
|
||||||
/** safely add a socket to this session */
|
|
||||||
void AddSocket(std::shared_ptr<SAMSocket> sock) {
|
|
||||||
std::lock_guard<std::mutex> lock(m_SocketsMutex);
|
|
||||||
m_Sockets.push_back(sock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** safely remove a socket from this session */
|
|
||||||
void DelSocket(std::shared_ptr<SAMSocket> sock) {
|
|
||||||
std::lock_guard<std::mutex> lock(m_SocketsMutex);
|
|
||||||
m_Sockets.remove(sock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** get a list holding a copy of all sam sockets from this session */
|
|
||||||
std::list<std::shared_ptr<SAMSocket> > ListSockets() {
|
|
||||||
std::list<std::shared_ptr<SAMSocket> > l;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_SocketsMutex);
|
|
||||||
for(const auto& sock : m_Sockets ) l.push_back(sock);
|
|
||||||
}
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
|
|
||||||
SAMSession (std::shared_ptr<ClientDestination> dest);
|
|
||||||
~SAMSession ();
|
|
||||||
|
|
||||||
void CloseStreams ();
|
|
||||||
};
|
|
||||||
|
|
||||||
class SAMBridge
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
SAMBridge (const std::string& address, int port);
|
|
||||||
~SAMBridge ();
|
|
||||||
|
|
||||||
void Start ();
|
|
||||||
void Stop ();
|
|
||||||
|
|
||||||
boost::asio::io_service& GetService () { return m_Service; };
|
|
||||||
std::shared_ptr<SAMSession> CreateSession (const std::string& id, const std::string& destination, // empty string means transient
|
|
||||||
const std::map<std::string, std::string> * params);
|
|
||||||
void CloseSession (const std::string& id);
|
|
||||||
std::shared_ptr<SAMSession> FindSession (const std::string& id) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void Run ();
|
|
||||||
|
|
||||||
void Accept ();
|
|
||||||
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<SAMSocket> socket);
|
|
||||||
|
|
||||||
void ReceiveDatagram ();
|
|
||||||
void HandleReceivedDatagram (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
bool m_IsRunning;
|
|
||||||
std::thread * m_Thread;
|
|
||||||
boost::asio::io_service m_Service;
|
|
||||||
boost::asio::ip::tcp::acceptor m_Acceptor;
|
|
||||||
boost::asio::ip::udp::endpoint m_DatagramEndpoint, m_SenderEndpoint;
|
|
||||||
boost::asio::ip::udp::socket m_DatagramSocket;
|
|
||||||
mutable std::mutex m_SessionsMutex;
|
|
||||||
std::map<std::string, std::shared_ptr<SAMSession> > m_Sessions;
|
|
||||||
uint8_t m_DatagramReceiveBuffer[i2p::datagram::MAX_DATAGRAM_SIZE+1];
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// for HTTP
|
|
||||||
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
747
SSU.cpp
747
SSU.cpp
@@ -1,747 +0,0 @@
|
|||||||
#include <string.h>
|
|
||||||
#include <boost/bind.hpp>
|
|
||||||
#include "Log.h"
|
|
||||||
#include "Timestamp.h"
|
|
||||||
#include "RouterContext.h"
|
|
||||||
#include "NetDb.h"
|
|
||||||
#include "SSU.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace transport
|
|
||||||
{
|
|
||||||
|
|
||||||
SSUServer::SSUServer (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_TerminationTimer (m_Service), m_TerminationTimerV6 (m_ServiceV6)
|
|
||||||
{
|
|
||||||
OpenSocketV6 ();
|
|
||||||
}
|
|
||||||
|
|
||||||
SSUServer::SSUServer (int port):
|
|
||||||
m_OnlyV6(false), m_IsRunning(false),
|
|
||||||
m_Thread (nullptr), m_ThreadV6 (nullptr), m_ReceiversThread (nullptr),
|
|
||||||
m_Work (m_Service), m_WorkV6 (m_ServiceV6), m_ReceiversWork (m_ReceiversService),
|
|
||||||
m_Endpoint (boost::asio::ip::udp::v4 (), port), m_EndpointV6 (boost::asio::ip::udp::v6 (), port),
|
|
||||||
m_Socket (m_ReceiversService), m_SocketV6 (m_ReceiversService),
|
|
||||||
m_IntroducersUpdateTimer (m_Service), m_PeerTestsCleanupTimer (m_Service),
|
|
||||||
m_TerminationTimer (m_Service), m_TerminationTimerV6 (m_ServiceV6)
|
|
||||||
{
|
|
||||||
OpenSocket ();
|
|
||||||
if (context.SupportsV6 ())
|
|
||||||
OpenSocketV6 ();
|
|
||||||
}
|
|
||||||
|
|
||||||
SSUServer::~SSUServer ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::OpenSocket ()
|
|
||||||
{
|
|
||||||
m_Socket.open (boost::asio::ip::udp::v4());
|
|
||||||
m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (65535));
|
|
||||||
m_Socket.set_option (boost::asio::socket_base::send_buffer_size (65535));
|
|
||||||
m_Socket.bind (m_Endpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::OpenSocketV6 ()
|
|
||||||
{
|
|
||||||
m_SocketV6.open (boost::asio::ip::udp::v6());
|
|
||||||
m_SocketV6.set_option (boost::asio::ip::v6_only (true));
|
|
||||||
m_SocketV6.set_option (boost::asio::socket_base::receive_buffer_size (65535));
|
|
||||||
m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (65535));
|
|
||||||
m_SocketV6.bind (m_EndpointV6);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::Start ()
|
|
||||||
{
|
|
||||||
m_IsRunning = true;
|
|
||||||
m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this));
|
|
||||||
if (!m_OnlyV6)
|
|
||||||
{
|
|
||||||
m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
|
|
||||||
m_ReceiversService.post (std::bind (&SSUServer::Receive, this));
|
|
||||||
ScheduleTermination ();
|
|
||||||
}
|
|
||||||
if (context.SupportsV6 ())
|
|
||||||
{
|
|
||||||
m_ThreadV6 = new std::thread (std::bind (&SSUServer::RunV6, this));
|
|
||||||
m_ReceiversService.post (std::bind (&SSUServer::ReceiveV6, this));
|
|
||||||
ScheduleTerminationV6 ();
|
|
||||||
}
|
|
||||||
SchedulePeerTestsCleanupTimer ();
|
|
||||||
ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::Stop ()
|
|
||||||
{
|
|
||||||
DeleteAllSessions ();
|
|
||||||
m_IsRunning = false;
|
|
||||||
m_TerminationTimer.cancel ();
|
|
||||||
m_TerminationTimerV6.cancel ();
|
|
||||||
m_Service.stop ();
|
|
||||||
m_Socket.close ();
|
|
||||||
m_ServiceV6.stop ();
|
|
||||||
m_SocketV6.close ();
|
|
||||||
m_ReceiversService.stop ();
|
|
||||||
if (m_ReceiversThread)
|
|
||||||
{
|
|
||||||
m_ReceiversThread->join ();
|
|
||||||
delete m_ReceiversThread;
|
|
||||||
m_ReceiversThread = nullptr;
|
|
||||||
}
|
|
||||||
if (m_Thread)
|
|
||||||
{
|
|
||||||
m_Thread->join ();
|
|
||||||
delete m_Thread;
|
|
||||||
m_Thread = nullptr;
|
|
||||||
}
|
|
||||||
if (m_ThreadV6)
|
|
||||||
{
|
|
||||||
m_ThreadV6->join ();
|
|
||||||
delete m_ThreadV6;
|
|
||||||
m_ThreadV6 = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::Run ()
|
|
||||||
{
|
|
||||||
while (m_IsRunning)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_Service.run ();
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SSU: server runtime exception: ", ex.what ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::RunV6 ()
|
|
||||||
{
|
|
||||||
while (m_IsRunning)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_ServiceV6.run ();
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SSU: v6 server runtime exception: ", ex.what ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::RunReceivers ()
|
|
||||||
{
|
|
||||||
while (m_IsRunning)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_ReceiversService.run ();
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SSU: receivers runtime exception: ", ex.what ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay)
|
|
||||||
{
|
|
||||||
m_Relays[tag] = relay;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<SSUSession> SSUServer::FindRelaySession (uint32_t tag)
|
|
||||||
{
|
|
||||||
auto it = m_Relays.find (tag);
|
|
||||||
if (it != m_Relays.end ())
|
|
||||||
return FindSession (it->second);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to)
|
|
||||||
{
|
|
||||||
if (to.protocol () == boost::asio::ip::udp::v4())
|
|
||||||
m_Socket.send_to (boost::asio::buffer (buf, len), to);
|
|
||||||
else
|
|
||||||
m_SocketV6.send_to (boost::asio::buffer (buf, len), to);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::Receive ()
|
|
||||||
{
|
|
||||||
SSUPacket * packet = new SSUPacket ();
|
|
||||||
m_Socket.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from,
|
|
||||||
std::bind (&SSUServer::HandleReceivedFrom, this, std::placeholders::_1, std::placeholders::_2, packet));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::ReceiveV6 ()
|
|
||||||
{
|
|
||||||
SSUPacket * packet = new SSUPacket ();
|
|
||||||
m_SocketV6.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from,
|
|
||||||
std::bind (&SSUServer::HandleReceivedFromV6, this, std::placeholders::_1, std::placeholders::_2, packet));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet)
|
|
||||||
{
|
|
||||||
if (!ecode)
|
|
||||||
{
|
|
||||||
packet->len = bytes_transferred;
|
|
||||||
std::vector<SSUPacket *> packets;
|
|
||||||
packets.push_back (packet);
|
|
||||||
|
|
||||||
boost::system::error_code ec;
|
|
||||||
size_t moreBytes = m_Socket.available(ec);
|
|
||||||
if (!ec)
|
|
||||||
{
|
|
||||||
while (moreBytes && packets.size () < 25)
|
|
||||||
{
|
|
||||||
packet = new SSUPacket ();
|
|
||||||
packet->len = m_Socket.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from, 0, ec);
|
|
||||||
if (!ec)
|
|
||||||
{
|
|
||||||
packets.push_back (packet);
|
|
||||||
moreBytes = m_Socket.available(ec);
|
|
||||||
if (ec) break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SSU: receive_from error: ", ec.message ());
|
|
||||||
delete packet;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Service.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets, &m_Sessions));
|
|
||||||
Receive ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
delete packet;
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SSU: receive error: ", ecode.message ());
|
|
||||||
m_Socket.close ();
|
|
||||||
OpenSocket ();
|
|
||||||
Receive ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet)
|
|
||||||
{
|
|
||||||
if (!ecode)
|
|
||||||
{
|
|
||||||
packet->len = bytes_transferred;
|
|
||||||
std::vector<SSUPacket *> packets;
|
|
||||||
packets.push_back (packet);
|
|
||||||
|
|
||||||
boost::system::error_code ec;
|
|
||||||
size_t moreBytes = m_SocketV6.available (ec);
|
|
||||||
if (!ec)
|
|
||||||
{
|
|
||||||
while (moreBytes && packets.size () < 25)
|
|
||||||
{
|
|
||||||
packet = new SSUPacket ();
|
|
||||||
packet->len = m_SocketV6.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from, 0, ec);
|
|
||||||
if (!ec)
|
|
||||||
{
|
|
||||||
packets.push_back (packet);
|
|
||||||
moreBytes = m_SocketV6.available(ec);
|
|
||||||
if (ec) break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SSU: v6 receive_from error: ", ec.message ());
|
|
||||||
delete packet;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_ServiceV6.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets, &m_SessionsV6));
|
|
||||||
ReceiveV6 ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
delete packet;
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SSU: v6 receive error: ", ecode.message ());
|
|
||||||
m_SocketV6.close ();
|
|
||||||
OpenSocketV6 ();
|
|
||||||
ReceiveV6 ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::HandleReceivedPackets (std::vector<SSUPacket *> packets,
|
|
||||||
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > * sessions)
|
|
||||||
{
|
|
||||||
std::shared_ptr<SSUSession> session;
|
|
||||||
for (auto& packet: packets)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!session || session->GetRemoteEndpoint () != packet->from) // we received packet for other session than previous
|
|
||||||
{
|
|
||||||
if (session) session->FlushData ();
|
|
||||||
auto it = sessions->find (packet->from);
|
|
||||||
if (it != sessions->end ())
|
|
||||||
session = it->second;
|
|
||||||
if (!session)
|
|
||||||
{
|
|
||||||
session = std::make_shared<SSUSession> (*this, packet->from);
|
|
||||||
session->WaitForConnect ();
|
|
||||||
(*sessions)[packet->from] = session;
|
|
||||||
LogPrint (eLogDebug, "SSU: new session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
session->ProcessNextMessage (packet->buf, packet->len, packet->from);
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SSU: HandleReceivedPackets ", ex.what ());
|
|
||||||
if (session) session->FlushData ();
|
|
||||||
session = nullptr;
|
|
||||||
}
|
|
||||||
delete packet;
|
|
||||||
}
|
|
||||||
if (session) session->FlushData ();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<SSUSession> SSUServer::FindSession (std::shared_ptr<const i2p::data::RouterInfo> router) const
|
|
||||||
{
|
|
||||||
if (!router) return nullptr;
|
|
||||||
auto address = router->GetSSUAddress (true); // v4 only
|
|
||||||
if (!address) return nullptr;
|
|
||||||
auto session = FindSession (boost::asio::ip::udp::endpoint (address->host, address->port));
|
|
||||||
if (session || !context.SupportsV6 ())
|
|
||||||
return session;
|
|
||||||
// try v6
|
|
||||||
address = router->GetSSUV6Address ();
|
|
||||||
if (!address) return nullptr;
|
|
||||||
return FindSession (boost::asio::ip::udp::endpoint (address->host, address->port));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<SSUSession> SSUServer::FindSession (const boost::asio::ip::udp::endpoint& e) const
|
|
||||||
{
|
|
||||||
auto& sessions = e.address ().is_v6 () ? m_SessionsV6 : m_Sessions;
|
|
||||||
auto it = sessions.find (e);
|
|
||||||
if (it != sessions.end ())
|
|
||||||
return it->second;
|
|
||||||
else
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest, bool v4only)
|
|
||||||
{
|
|
||||||
auto address = router->GetSSUAddress (v4only || !context.SupportsV6 ());
|
|
||||||
if (address)
|
|
||||||
CreateSession (router, address->host, address->port, peerTest);
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "SSU: Router ", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), " doesn't have SSU address");
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
|
|
||||||
const boost::asio::ip::address& addr, int port, bool peerTest)
|
|
||||||
{
|
|
||||||
if (router)
|
|
||||||
{
|
|
||||||
if (router->UsesIntroducer ())
|
|
||||||
m_Service.post (std::bind (&SSUServer::CreateSessionThroughIntroducer, this, router, peerTest)); // always V4 thread
|
|
||||||
else
|
|
||||||
{
|
|
||||||
boost::asio::ip::udp::endpoint remoteEndpoint (addr, port);
|
|
||||||
auto& s = addr.is_v6 () ? m_ServiceV6 : m_Service;
|
|
||||||
s.post (std::bind (&SSUServer::CreateDirectSession, this, router, remoteEndpoint, peerTest));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::CreateDirectSession (std::shared_ptr<const i2p::data::RouterInfo> router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest)
|
|
||||||
{
|
|
||||||
auto& sessions = remoteEndpoint.address ().is_v6 () ? m_SessionsV6 : m_Sessions;
|
|
||||||
auto it = sessions.find (remoteEndpoint);
|
|
||||||
if (it != sessions.end ())
|
|
||||||
{
|
|
||||||
auto session = it->second;
|
|
||||||
if (peerTest && session->GetState () == eSessionStateEstablished)
|
|
||||||
session->SendPeerTest ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// otherwise create new session
|
|
||||||
auto session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
|
|
||||||
sessions[remoteEndpoint] = session;
|
|
||||||
// connect
|
|
||||||
LogPrint (eLogDebug, "SSU: Creating new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), "] ",
|
|
||||||
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ());
|
|
||||||
session->Connect ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest)
|
|
||||||
{
|
|
||||||
if (router && router->UsesIntroducer ())
|
|
||||||
{
|
|
||||||
auto address = router->GetSSUAddress (true); // v4 only for now
|
|
||||||
if (address)
|
|
||||||
{
|
|
||||||
boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
|
|
||||||
auto it = m_Sessions.find (remoteEndpoint);
|
|
||||||
// check if session if presented alredy
|
|
||||||
if (it != m_Sessions.end ())
|
|
||||||
{
|
|
||||||
auto session = it->second;
|
|
||||||
if (peerTest && session->GetState () == eSessionStateEstablished)
|
|
||||||
session->SendPeerTest ();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// create new session
|
|
||||||
int numIntroducers = address->introducers.size ();
|
|
||||||
if (numIntroducers > 0)
|
|
||||||
{
|
|
||||||
std::shared_ptr<SSUSession> introducerSession;
|
|
||||||
const i2p::data::RouterInfo::Introducer * introducer = nullptr;
|
|
||||||
// we might have a session to introducer already
|
|
||||||
for (int i = 0; i < numIntroducers; i++)
|
|
||||||
{
|
|
||||||
auto intr = &(address->introducers[i]);
|
|
||||||
boost::asio::ip::udp::endpoint ep (intr->iHost, intr->iPort);
|
|
||||||
if (ep.address ().is_v4 ()) // ipv4 only
|
|
||||||
{
|
|
||||||
if (!introducer) introducer = intr; // we pick first one for now
|
|
||||||
it = m_Sessions.find (ep);
|
|
||||||
if (it != m_Sessions.end ())
|
|
||||||
{
|
|
||||||
introducerSession = it->second;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!introducer)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no ipv4 introducers present");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (introducerSession) // session found
|
|
||||||
LogPrint (eLogWarning, "SSU: Session to introducer already exists");
|
|
||||||
else // create new
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "SSU: Creating new session to introducer ", introducer->iHost);
|
|
||||||
boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort);
|
|
||||||
introducerSession = std::make_shared<SSUSession> (*this, introducerEndpoint, router);
|
|
||||||
m_Sessions[introducerEndpoint] = introducerSession;
|
|
||||||
}
|
|
||||||
// create session
|
|
||||||
auto session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
|
|
||||||
m_Sessions[remoteEndpoint] = session;
|
|
||||||
// introduce
|
|
||||||
LogPrint (eLogInfo, "SSU: Introduce new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()),
|
|
||||||
"] through introducer ", introducer->iHost, ":", introducer->iPort);
|
|
||||||
session->WaitForIntroduction ();
|
|
||||||
if (i2p::context.GetRouterInfo ().UsesIntroducer ()) // if we are unreachable
|
|
||||||
{
|
|
||||||
uint8_t buf[1];
|
|
||||||
Send (buf, 0, remoteEndpoint); // send HolePunch
|
|
||||||
}
|
|
||||||
introducerSession->Introduce (*introducer, router);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no introducers present");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "SSU: Router ", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), " doesn't have SSU address");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::DeleteSession (std::shared_ptr<SSUSession> session)
|
|
||||||
{
|
|
||||||
if (session)
|
|
||||||
{
|
|
||||||
session->Close ();
|
|
||||||
auto& ep = session->GetRemoteEndpoint ();
|
|
||||||
if (ep.address ().is_v6 ())
|
|
||||||
m_SessionsV6.erase (ep);
|
|
||||||
else
|
|
||||||
m_Sessions.erase (ep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::DeleteAllSessions ()
|
|
||||||
{
|
|
||||||
for (auto& it: m_Sessions)
|
|
||||||
it.second->Close ();
|
|
||||||
m_Sessions.clear ();
|
|
||||||
|
|
||||||
for (auto& it: m_SessionsV6)
|
|
||||||
it.second->Close ();
|
|
||||||
m_SessionsV6.clear ();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Filter>
|
|
||||||
std::shared_ptr<SSUSession> SSUServer::GetRandomV4Session (Filter filter) // v4 only
|
|
||||||
{
|
|
||||||
std::vector<std::shared_ptr<SSUSession> > filteredSessions;
|
|
||||||
for (const auto& s :m_Sessions)
|
|
||||||
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::GetRandomEstablishedV4Session (std::shared_ptr<const SSUSession> excluded) // v4 only
|
|
||||||
{
|
|
||||||
return GetRandomV4Session (
|
|
||||||
[excluded](std::shared_ptr<SSUSession> session)->bool
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::set<SSUSession *> SSUServer::FindIntroducers (int maxNumIntroducers)
|
|
||||||
{
|
|
||||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
std::set<SSUSession *> ret;
|
|
||||||
for (int i = 0; i < maxNumIntroducers; i++)
|
|
||||||
{
|
|
||||||
auto session = GetRandomV4Session (
|
|
||||||
[&ret, ts](std::shared_ptr<SSUSession> session)->bool
|
|
||||||
{
|
|
||||||
return session->GetRelayTag () && !ret.count (session.get ()) &&
|
|
||||||
session->GetState () == eSessionStateEstablished &&
|
|
||||||
ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (session)
|
|
||||||
{
|
|
||||||
ret.insert (session.get ());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::ScheduleIntroducersUpdateTimer ()
|
|
||||||
{
|
|
||||||
m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL));
|
|
||||||
m_IntroducersUpdateTimer.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer,
|
|
||||||
this, std::placeholders::_1));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::HandleIntroducersUpdateTimer (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
// timeout expired
|
|
||||||
if (i2p::context.GetStatus () == eRouterStatusTesting)
|
|
||||||
{
|
|
||||||
// we still don't know if we need introducers
|
|
||||||
ScheduleIntroducersUpdateTimer ();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (i2p::context.GetStatus () == eRouterStatusOK) return; // we don't need introducers anymore
|
|
||||||
// we are firewalled
|
|
||||||
if (!i2p::context.IsUnreachable ()) i2p::context.SetUnreachable ();
|
|
||||||
std::list<boost::asio::ip::udp::endpoint> newList;
|
|
||||||
size_t numIntroducers = 0;
|
|
||||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
for (const auto& it : m_Introducers)
|
|
||||||
{
|
|
||||||
auto session = FindSession (it);
|
|
||||||
if (session && ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION)
|
|
||||||
{
|
|
||||||
session->SendKeepAlive ();
|
|
||||||
newList.push_back (it);
|
|
||||||
numIntroducers++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
i2p::context.RemoveIntroducer (it);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (numIntroducers < SSU_MAX_NUM_INTRODUCERS)
|
|
||||||
{
|
|
||||||
// create new
|
|
||||||
auto introducers = FindIntroducers (SSU_MAX_NUM_INTRODUCERS);
|
|
||||||
for (const 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))
|
|
||||||
{
|
|
||||||
newList.push_back (ep);
|
|
||||||
if (newList.size () >= SSU_MAX_NUM_INTRODUCERS) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_Introducers = newList;
|
|
||||||
if (m_Introducers.size () < SSU_MAX_NUM_INTRODUCERS)
|
|
||||||
{
|
|
||||||
auto introducer = i2p::data::netdb.GetRandomIntroducer ();
|
|
||||||
if (introducer)
|
|
||||||
CreateSession (introducer);
|
|
||||||
}
|
|
||||||
ScheduleIntroducersUpdateTimer ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr<SSUSession> session)
|
|
||||||
{
|
|
||||||
m_PeerTests[nonce] = { i2p::util::GetMillisecondsSinceEpoch (), role, session };
|
|
||||||
}
|
|
||||||
|
|
||||||
PeerTestParticipant SSUServer::GetPeerTestParticipant (uint32_t nonce)
|
|
||||||
{
|
|
||||||
auto it = m_PeerTests.find (nonce);
|
|
||||||
if (it != m_PeerTests.end ())
|
|
||||||
return it->second.role;
|
|
||||||
else
|
|
||||||
return ePeerTestParticipantUnknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<SSUSession> SSUServer::GetPeerTestSession (uint32_t nonce)
|
|
||||||
{
|
|
||||||
auto it = m_PeerTests.find (nonce);
|
|
||||||
if (it != m_PeerTests.end ())
|
|
||||||
return it->second.session;
|
|
||||||
else
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::UpdatePeerTest (uint32_t nonce, PeerTestParticipant role)
|
|
||||||
{
|
|
||||||
auto it = m_PeerTests.find (nonce);
|
|
||||||
if (it != m_PeerTests.end ())
|
|
||||||
it->second.role = role;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::RemovePeerTest (uint32_t nonce)
|
|
||||||
{
|
|
||||||
m_PeerTests.erase (nonce);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::SchedulePeerTestsCleanupTimer ()
|
|
||||||
{
|
|
||||||
m_PeerTestsCleanupTimer.expires_from_now (boost::posix_time::seconds(SSU_PEER_TEST_TIMEOUT));
|
|
||||||
m_PeerTestsCleanupTimer.async_wait (std::bind (&SSUServer::HandlePeerTestsCleanupTimer,
|
|
||||||
this, std::placeholders::_1));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
int numDeleted = 0;
|
|
||||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
for (auto it = m_PeerTests.begin (); it != m_PeerTests.end ();)
|
|
||||||
{
|
|
||||||
if (ts > it->second.creationTime + SSU_PEER_TEST_TIMEOUT*1000LL)
|
|
||||||
{
|
|
||||||
numDeleted++;
|
|
||||||
it = m_PeerTests.erase (it);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
if (numDeleted > 0)
|
|
||||||
LogPrint (eLogDebug, "SSU: ", numDeleted, " peer tests have been expired");
|
|
||||||
SchedulePeerTestsCleanupTimer ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::ScheduleTermination ()
|
|
||||||
{
|
|
||||||
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(SSU_TERMINATION_CHECK_TIMEOUT));
|
|
||||||
m_TerminationTimer.async_wait (std::bind (&SSUServer::HandleTerminationTimer,
|
|
||||||
this, std::placeholders::_1));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::HandleTerminationTimer (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
for (auto& it: m_Sessions)
|
|
||||||
if (it.second->IsTerminationTimeoutExpired (ts))
|
|
||||||
{
|
|
||||||
auto session = it.second;
|
|
||||||
m_Service.post ([session]
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "SSU: no activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds");
|
|
||||||
session->Failed ();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
ScheduleTermination ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::ScheduleTerminationV6 ()
|
|
||||||
{
|
|
||||||
m_TerminationTimerV6.expires_from_now (boost::posix_time::seconds(SSU_TERMINATION_CHECK_TIMEOUT));
|
|
||||||
m_TerminationTimerV6.async_wait (std::bind (&SSUServer::HandleTerminationTimerV6,
|
|
||||||
this, std::placeholders::_1));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::HandleTerminationTimerV6 (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
for (auto& it: m_SessionsV6)
|
|
||||||
if (it.second->IsTerminationTimeoutExpired (ts))
|
|
||||||
{
|
|
||||||
auto session = it.second;
|
|
||||||
m_ServiceV6.post ([session]
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "SSU: no activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds");
|
|
||||||
session->Failed ();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
ScheduleTerminationV6 ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
134
SSU.h
134
SSU.h
@@ -1,134 +0,0 @@
|
|||||||
#ifndef SSU_H__
|
|
||||||
#define SSU_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <map>
|
|
||||||
#include <list>
|
|
||||||
#include <set>
|
|
||||||
#include <thread>
|
|
||||||
#include <mutex>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include "Crypto.h"
|
|
||||||
#include "I2PEndian.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "RouterInfo.h"
|
|
||||||
#include "I2NPProtocol.h"
|
|
||||||
#include "SSUSession.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace transport
|
|
||||||
{
|
|
||||||
const int SSU_KEEP_ALIVE_INTERVAL = 30; // 30 seconds
|
|
||||||
const int SSU_PEER_TEST_TIMEOUT = 60; // 60 seconds
|
|
||||||
const int SSU_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour
|
|
||||||
const int SSU_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
|
|
||||||
const size_t SSU_MAX_NUM_INTRODUCERS = 3;
|
|
||||||
|
|
||||||
struct SSUPacket
|
|
||||||
{
|
|
||||||
i2p::crypto::AESAlignedBuffer<1500> buf;
|
|
||||||
boost::asio::ip::udp::endpoint from;
|
|
||||||
size_t len;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SSUServer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
SSUServer (int port);
|
|
||||||
SSUServer (const boost::asio::ip::address & addr, int port); // ipv6 only constructor
|
|
||||||
~SSUServer ();
|
|
||||||
void Start ();
|
|
||||||
void Stop ();
|
|
||||||
void CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false, bool v4only = false);
|
|
||||||
void CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
|
|
||||||
const boost::asio::ip::address& addr, int port, bool peerTest = false);
|
|
||||||
void CreateDirectSession (std::shared_ptr<const i2p::data::RouterInfo> router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest);
|
|
||||||
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 ();
|
|
||||||
|
|
||||||
boost::asio::io_service& GetService () { return m_Service; };
|
|
||||||
boost::asio::io_service& GetServiceV6 () { return m_ServiceV6; };
|
|
||||||
const boost::asio::ip::udp::endpoint& GetEndpoint () const { return m_Endpoint; };
|
|
||||||
void Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to);
|
|
||||||
void AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay);
|
|
||||||
std::shared_ptr<SSUSession> FindRelaySession (uint32_t tag);
|
|
||||||
|
|
||||||
void NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr<SSUSession> session = nullptr);
|
|
||||||
PeerTestParticipant GetPeerTestParticipant (uint32_t nonce);
|
|
||||||
std::shared_ptr<SSUSession> GetPeerTestSession (uint32_t nonce);
|
|
||||||
void UpdatePeerTest (uint32_t nonce, PeerTestParticipant role);
|
|
||||||
void RemovePeerTest (uint32_t nonce);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void OpenSocket ();
|
|
||||||
void OpenSocketV6 ();
|
|
||||||
void Run ();
|
|
||||||
void RunV6 ();
|
|
||||||
void RunReceivers ();
|
|
||||||
void Receive ();
|
|
||||||
void ReceiveV6 ();
|
|
||||||
void HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet);
|
|
||||||
void HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet);
|
|
||||||
void HandleReceivedPackets (std::vector<SSUPacket *> packets,
|
|
||||||
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> >* sessions);
|
|
||||||
|
|
||||||
void CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false);
|
|
||||||
template<typename Filter>
|
|
||||||
std::shared_ptr<SSUSession> GetRandomV4Session (Filter filter);
|
|
||||||
template<typename Filter>
|
|
||||||
std::shared_ptr<SSUSession> GetRandomV6Session (Filter filter);
|
|
||||||
|
|
||||||
std::set<SSUSession *> FindIntroducers (int maxNumIntroducers);
|
|
||||||
void ScheduleIntroducersUpdateTimer ();
|
|
||||||
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode);
|
|
||||||
|
|
||||||
void SchedulePeerTestsCleanupTimer ();
|
|
||||||
void HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode);
|
|
||||||
|
|
||||||
// timer
|
|
||||||
void ScheduleTermination ();
|
|
||||||
void HandleTerminationTimer (const boost::system::error_code& ecode);
|
|
||||||
void ScheduleTerminationV6 ();
|
|
||||||
void HandleTerminationTimerV6 (const boost::system::error_code& ecode);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
struct PeerTest
|
|
||||||
{
|
|
||||||
uint64_t creationTime;
|
|
||||||
PeerTestParticipant role;
|
|
||||||
std::shared_ptr<SSUSession> session; // for Bob to Alice
|
|
||||||
};
|
|
||||||
|
|
||||||
bool m_OnlyV6;
|
|
||||||
bool m_IsRunning;
|
|
||||||
std::thread * m_Thread, * m_ThreadV6, * m_ReceiversThread;
|
|
||||||
boost::asio::io_service m_Service, m_ServiceV6, m_ReceiversService;
|
|
||||||
boost::asio::io_service::work m_Work, m_WorkV6, m_ReceiversWork;
|
|
||||||
boost::asio::ip::udp::endpoint m_Endpoint, m_EndpointV6;
|
|
||||||
boost::asio::ip::udp::socket m_Socket, m_SocketV6;
|
|
||||||
boost::asio::deadline_timer m_IntroducersUpdateTimer, m_PeerTestsCleanupTimer,
|
|
||||||
m_TerminationTimer, m_TerminationTimerV6;
|
|
||||||
std::list<boost::asio::ip::udp::endpoint> m_Introducers; // introducers we are connected to
|
|
||||||
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > m_Sessions, m_SessionsV6;
|
|
||||||
std::map<uint32_t, boost::asio::ip::udp::endpoint> m_Relays; // we are introducer
|
|
||||||
std::map<uint32_t, PeerTest> m_PeerTests; // nonce -> creation time in milliseconds
|
|
||||||
|
|
||||||
public:
|
|
||||||
// for HTTP only
|
|
||||||
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
|
|
||||||
const decltype(m_SessionsV6)& GetSessionsV6 () const { return m_SessionsV6; };
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
510
SSUData.cpp
510
SSUData.cpp
@@ -1,510 +0,0 @@
|
|||||||
#include <stdlib.h>
|
|
||||||
#include <boost/bind.hpp>
|
|
||||||
#include "Log.h"
|
|
||||||
#include "Timestamp.h"
|
|
||||||
#include "NetDb.h"
|
|
||||||
#include "SSU.h"
|
|
||||||
#include "SSUData.h"
|
|
||||||
#ifdef WITH_EVENTS
|
|
||||||
#include "Event.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace transport
|
|
||||||
{
|
|
||||||
void IncompleteMessage::AttachNextFragment (const uint8_t * fragment, size_t fragmentSize)
|
|
||||||
{
|
|
||||||
if (msg->len + fragmentSize > msg->maxLen)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "SSU: I2NP message size ", msg->maxLen, " is not enough");
|
|
||||||
auto newMsg = NewI2NPMessage ();
|
|
||||||
*newMsg = *msg;
|
|
||||||
msg = newMsg;
|
|
||||||
}
|
|
||||||
if (msg->Concat (fragment, fragmentSize) < fragmentSize)
|
|
||||||
LogPrint (eLogError, "SSU: I2NP buffer overflow ", msg->maxLen);
|
|
||||||
nextFragmentNum++;
|
|
||||||
}
|
|
||||||
|
|
||||||
SSUData::SSUData (SSUSession& session):
|
|
||||||
m_Session (session), m_ResendTimer (session.GetService ()),
|
|
||||||
m_IncompleteMessagesCleanupTimer (session.GetService ()),
|
|
||||||
m_MaxPacketSize (session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE),
|
|
||||||
m_PacketSize (m_MaxPacketSize), m_LastMessageReceivedTime (0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SSUData::~SSUData ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::Start ()
|
|
||||||
{
|
|
||||||
ScheduleIncompleteMessagesCleanup ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::Stop ()
|
|
||||||
{
|
|
||||||
m_ResendTimer.cancel ();
|
|
||||||
m_IncompleteMessagesCleanupTimer.cancel ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::AdjustPacketSize (std::shared_ptr<const i2p::data::RouterInfo> remoteRouter)
|
|
||||||
{
|
|
||||||
if (remoteRouter) return;
|
|
||||||
auto ssuAddress = remoteRouter->GetSSUAddress ();
|
|
||||||
if (ssuAddress && ssuAddress->mtu)
|
|
||||||
{
|
|
||||||
if (m_Session.IsV6 ())
|
|
||||||
m_PacketSize = ssuAddress->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE;
|
|
||||||
else
|
|
||||||
m_PacketSize = ssuAddress->mtu - IPV4_HEADER_SIZE - UDP_HEADER_SIZE;
|
|
||||||
if (m_PacketSize > 0)
|
|
||||||
{
|
|
||||||
// make sure packet size multiple of 16
|
|
||||||
m_PacketSize >>= 4;
|
|
||||||
m_PacketSize <<= 4;
|
|
||||||
if (m_PacketSize > m_MaxPacketSize) m_PacketSize = m_MaxPacketSize;
|
|
||||||
LogPrint (eLogDebug, "SSU: MTU=", ssuAddress->mtu, " packet size=", m_PacketSize);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "SSU: Unexpected MTU ", ssuAddress->mtu);
|
|
||||||
m_PacketSize = m_MaxPacketSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::UpdatePacketSize (const i2p::data::IdentHash& remoteIdent)
|
|
||||||
{
|
|
||||||
auto routerInfo = i2p::data::netdb.FindRouter (remoteIdent);
|
|
||||||
if (routerInfo)
|
|
||||||
AdjustPacketSize (routerInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::ProcessSentMessageAck (uint32_t msgID)
|
|
||||||
{
|
|
||||||
auto it = m_SentMessages.find (msgID);
|
|
||||||
if (it != m_SentMessages.end ())
|
|
||||||
{
|
|
||||||
m_SentMessages.erase (it);
|
|
||||||
if (m_SentMessages.empty ())
|
|
||||||
m_ResendTimer.cancel ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::ProcessAcks (uint8_t *& buf, uint8_t flag)
|
|
||||||
{
|
|
||||||
if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED)
|
|
||||||
{
|
|
||||||
// explicit ACKs
|
|
||||||
uint8_t numAcks =*buf;
|
|
||||||
buf++;
|
|
||||||
for (int i = 0; i < numAcks; i++)
|
|
||||||
ProcessSentMessageAck (bufbe32toh (buf+i*4));
|
|
||||||
buf += numAcks*4;
|
|
||||||
}
|
|
||||||
if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED)
|
|
||||||
{
|
|
||||||
// explicit ACK bitfields
|
|
||||||
uint8_t numBitfields =*buf;
|
|
||||||
buf++;
|
|
||||||
for (int i = 0; i < numBitfields; i++)
|
|
||||||
{
|
|
||||||
uint32_t msgID = bufbe32toh (buf);
|
|
||||||
buf += 4; // msgID
|
|
||||||
auto it = m_SentMessages.find (msgID);
|
|
||||||
// process individual Ack bitfields
|
|
||||||
bool isNonLast = false;
|
|
||||||
int fragment = 0;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
uint8_t bitfield = *buf;
|
|
||||||
isNonLast = bitfield & 0x80;
|
|
||||||
bitfield &= 0x7F; // clear MSB
|
|
||||||
if (bitfield && it != m_SentMessages.end ())
|
|
||||||
{
|
|
||||||
int numSentFragments = it->second->fragments.size ();
|
|
||||||
// process bits
|
|
||||||
uint8_t mask = 0x01;
|
|
||||||
for (int j = 0; j < 7; j++)
|
|
||||||
{
|
|
||||||
if (bitfield & mask)
|
|
||||||
{
|
|
||||||
if (fragment < numSentFragments)
|
|
||||||
it->second->fragments[fragment].reset (nullptr);
|
|
||||||
}
|
|
||||||
fragment++;
|
|
||||||
mask <<= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buf++;
|
|
||||||
}
|
|
||||||
while (isNonLast);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::ProcessFragments (uint8_t * buf)
|
|
||||||
{
|
|
||||||
uint8_t numFragments = *buf; // number of fragments
|
|
||||||
buf++;
|
|
||||||
for (int i = 0; i < numFragments; i++)
|
|
||||||
{
|
|
||||||
uint32_t msgID = bufbe32toh (buf); // message ID
|
|
||||||
buf += 4;
|
|
||||||
uint8_t frag[4];
|
|
||||||
frag[0] = 0;
|
|
||||||
memcpy (frag + 1, buf, 3);
|
|
||||||
buf += 3;
|
|
||||||
uint32_t fragmentInfo = bufbe32toh (frag); // fragment info
|
|
||||||
uint16_t fragmentSize = fragmentInfo & 0x3FFF; // bits 0 - 13
|
|
||||||
bool isLast = fragmentInfo & 0x010000; // bit 16
|
|
||||||
uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17
|
|
||||||
if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SSU: Fragment size ", fragmentSize, " exceeds max SSU packet size");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find message with msgID
|
|
||||||
auto it = m_IncompleteMessages.find (msgID);
|
|
||||||
if (it == m_IncompleteMessages.end ())
|
|
||||||
{
|
|
||||||
// create new message
|
|
||||||
auto msg = NewI2NPShortMessage ();
|
|
||||||
msg->len -= I2NP_SHORT_HEADER_SIZE;
|
|
||||||
it = m_IncompleteMessages.insert (std::make_pair (msgID,
|
|
||||||
std::unique_ptr<IncompleteMessage>(new IncompleteMessage (msg)))).first;
|
|
||||||
}
|
|
||||||
std::unique_ptr<IncompleteMessage>& incompleteMessage = it->second;
|
|
||||||
|
|
||||||
// handle current fragment
|
|
||||||
if (fragmentNum == incompleteMessage->nextFragmentNum)
|
|
||||||
{
|
|
||||||
// expected fragment
|
|
||||||
incompleteMessage->AttachNextFragment (buf, fragmentSize);
|
|
||||||
if (!isLast && !incompleteMessage->savedFragments.empty ())
|
|
||||||
{
|
|
||||||
// try saved fragments
|
|
||||||
for (auto it1 = incompleteMessage->savedFragments.begin (); it1 != incompleteMessage->savedFragments.end ();)
|
|
||||||
{
|
|
||||||
auto& savedFragment = *it1;
|
|
||||||
if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum)
|
|
||||||
{
|
|
||||||
incompleteMessage->AttachNextFragment (savedFragment->buf, savedFragment->len);
|
|
||||||
isLast = savedFragment->isLast;
|
|
||||||
incompleteMessage->savedFragments.erase (it1++);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (isLast)
|
|
||||||
LogPrint (eLogDebug, "SSU: Message ", msgID, " complete");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (fragmentNum < incompleteMessage->nextFragmentNum)
|
|
||||||
// duplicate fragment
|
|
||||||
LogPrint (eLogWarning, "SSU: Duplicate fragment ", (int)fragmentNum, " of message ", msgID, ", ignored");
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// missing fragment
|
|
||||||
LogPrint (eLogWarning, "SSU: Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID);
|
|
||||||
auto savedFragment = new Fragment (fragmentNum, buf, fragmentSize, isLast);
|
|
||||||
if (incompleteMessage->savedFragments.insert (std::unique_ptr<Fragment>(savedFragment)).second)
|
|
||||||
incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "SSU: Fragment ", (int)fragmentNum, " of message ", msgID, " already saved");
|
|
||||||
}
|
|
||||||
isLast = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isLast)
|
|
||||||
{
|
|
||||||
// delete incomplete message
|
|
||||||
auto msg = incompleteMessage->msg;
|
|
||||||
incompleteMessage->msg = nullptr;
|
|
||||||
m_IncompleteMessages.erase (msgID);
|
|
||||||
// process message
|
|
||||||
SendMsgAck (msgID);
|
|
||||||
msg->FromSSU (msgID);
|
|
||||||
if (m_Session.GetState () == eSessionStateEstablished)
|
|
||||||
{
|
|
||||||
if (!m_ReceivedMessages.count (msgID))
|
|
||||||
{
|
|
||||||
m_ReceivedMessages.insert (msgID);
|
|
||||||
m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
if (!msg->IsExpired ())
|
|
||||||
{
|
|
||||||
#ifdef WITH_EVENTS
|
|
||||||
EmitEvent({{"type", "transport.recvmsg"} , {"ident", m_Session.GetIdentHashBase64()}, {"number", "1"}});
|
|
||||||
#endif
|
|
||||||
m_Handler.PutNextMessage (msg);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogDebug, "SSU: message expired");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "SSU: Message ", msgID, " already received");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// we expect DeliveryStatus
|
|
||||||
if (msg->GetTypeID () == eI2NPDeliveryStatus)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "SSU: session established");
|
|
||||||
m_Session.Established ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "SSU: unexpected message ", (int)msg->GetTypeID ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SendFragmentAck (msgID, fragmentNum);
|
|
||||||
buf += fragmentSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::FlushReceivedMessage ()
|
|
||||||
{
|
|
||||||
m_Handler.Flush ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::ProcessMessage (uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
//uint8_t * start = buf;
|
|
||||||
uint8_t flag = *buf;
|
|
||||||
buf++;
|
|
||||||
LogPrint (eLogDebug, "SSU: Process data, flags=", (int)flag, ", len=", len);
|
|
||||||
// process acks if presented
|
|
||||||
if (flag & (DATA_FLAG_ACK_BITFIELDS_INCLUDED | DATA_FLAG_EXPLICIT_ACKS_INCLUDED))
|
|
||||||
ProcessAcks (buf, flag);
|
|
||||||
// extended data if presented
|
|
||||||
if (flag & DATA_FLAG_EXTENDED_DATA_INCLUDED)
|
|
||||||
{
|
|
||||||
uint8_t extendedDataSize = *buf;
|
|
||||||
buf++; // size
|
|
||||||
LogPrint (eLogDebug, "SSU: extended data of ", extendedDataSize, " bytes present");
|
|
||||||
buf += extendedDataSize;
|
|
||||||
}
|
|
||||||
// process data
|
|
||||||
ProcessFragments (buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::Send (std::shared_ptr<i2p::I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
uint32_t msgID = msg->ToSSU ();
|
|
||||||
if (m_SentMessages.count (msgID) > 0)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "SSU: message ", msgID, " already sent");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (m_SentMessages.empty ()) // schedule resend at first message only
|
|
||||||
ScheduleResend ();
|
|
||||||
|
|
||||||
auto ret = m_SentMessages.insert (std::make_pair (msgID, std::unique_ptr<SentMessage>(new SentMessage)));
|
|
||||||
std::unique_ptr<SentMessage>& sentMessage = ret.first->second;
|
|
||||||
if (ret.second)
|
|
||||||
{
|
|
||||||
sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL;
|
|
||||||
sentMessage->numResends = 0;
|
|
||||||
}
|
|
||||||
auto& fragments = sentMessage->fragments;
|
|
||||||
size_t payloadSize = m_PacketSize - sizeof (SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3)
|
|
||||||
size_t len = msg->GetLength ();
|
|
||||||
uint8_t * msgBuf = msg->GetSSUHeader ();
|
|
||||||
|
|
||||||
uint32_t fragmentNum = 0;
|
|
||||||
while (len > 0)
|
|
||||||
{
|
|
||||||
Fragment * fragment = new Fragment;
|
|
||||||
fragment->fragmentNum = fragmentNum;
|
|
||||||
uint8_t * buf = fragment->buf;
|
|
||||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
|
||||||
*payload = DATA_FLAG_WANT_REPLY; // for compatibility
|
|
||||||
payload++;
|
|
||||||
*payload = 1; // always 1 message fragment per message
|
|
||||||
payload++;
|
|
||||||
htobe32buf (payload, msgID);
|
|
||||||
payload += 4;
|
|
||||||
bool isLast = (len <= payloadSize);
|
|
||||||
size_t size = isLast ? len : payloadSize;
|
|
||||||
uint32_t fragmentInfo = (fragmentNum << 17);
|
|
||||||
if (isLast)
|
|
||||||
fragmentInfo |= 0x010000;
|
|
||||||
|
|
||||||
fragmentInfo |= size;
|
|
||||||
fragmentInfo = htobe32 (fragmentInfo);
|
|
||||||
memcpy (payload, (uint8_t *)(&fragmentInfo) + 1, 3);
|
|
||||||
payload += 3;
|
|
||||||
memcpy (payload, msgBuf, size);
|
|
||||||
|
|
||||||
size += payload - buf;
|
|
||||||
if (size & 0x0F) // make sure 16 bytes boundary
|
|
||||||
size = ((size >> 4) + 1) << 4; // (/16 + 1)*16
|
|
||||||
fragment->len = size;
|
|
||||||
fragments.push_back (std::unique_ptr<Fragment> (fragment));
|
|
||||||
|
|
||||||
// encrypt message with session key
|
|
||||||
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, size);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_Session.Send (buf, size);
|
|
||||||
}
|
|
||||||
catch (boost::system::system_error& ec)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "SSU: Can't send data fragment ", ec.what ());
|
|
||||||
}
|
|
||||||
if (!isLast)
|
|
||||||
{
|
|
||||||
len -= payloadSize;
|
|
||||||
msgBuf += payloadSize;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
len = 0;
|
|
||||||
fragmentNum++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::SendMsgAck (uint32_t msgID)
|
|
||||||
{
|
|
||||||
uint8_t buf[48 + 18]; // actual length is 44 = 37 + 7 but pad it to multiple of 16
|
|
||||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
|
||||||
*payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag
|
|
||||||
payload++;
|
|
||||||
*payload = 1; // number of ACKs
|
|
||||||
payload++;
|
|
||||||
htobe32buf (payload, msgID); // msgID
|
|
||||||
payload += 4;
|
|
||||||
*payload = 0; // number of fragments
|
|
||||||
|
|
||||||
// encrypt message with session key
|
|
||||||
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48);
|
|
||||||
m_Session.Send (buf, 48);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::SendFragmentAck (uint32_t msgID, int fragmentNum)
|
|
||||||
{
|
|
||||||
if (fragmentNum > 64)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "SSU: Fragment number ", fragmentNum, " exceeds 64");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uint8_t buf[64 + 18];
|
|
||||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
|
||||||
*payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag
|
|
||||||
payload++;
|
|
||||||
*payload = 1; // number of ACK bitfields
|
|
||||||
payload++;
|
|
||||||
// one ack
|
|
||||||
*(uint32_t *)(payload) = htobe32 (msgID); // msgID
|
|
||||||
payload += 4;
|
|
||||||
div_t d = div (fragmentNum, 7);
|
|
||||||
memset (payload, 0x80, d.quot); // 0x80 means non-last
|
|
||||||
payload += d.quot;
|
|
||||||
*payload = 0x01 << d.rem; // set corresponding bit
|
|
||||||
payload++;
|
|
||||||
*payload = 0; // number of fragments
|
|
||||||
|
|
||||||
size_t len = d.quot < 4 ? 48 : 64; // 48 = 37 + 7 + 4 (3+1)
|
|
||||||
// encrypt message with session key
|
|
||||||
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, len);
|
|
||||||
m_Session.Send (buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::ScheduleResend()
|
|
||||||
{
|
|
||||||
m_ResendTimer.cancel ();
|
|
||||||
m_ResendTimer.expires_from_now (boost::posix_time::seconds(RESEND_INTERVAL));
|
|
||||||
auto s = m_Session.shared_from_this();
|
|
||||||
m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode)
|
|
||||||
{ s->m_Data.HandleResendTimer (ecode); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::HandleResendTimer (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "SSU: message has not been ACKed after ", MAX_NUM_RESENDS, " attempts, deleted");
|
|
||||||
it = m_SentMessages.erase (it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
if (numResent < MAX_OUTGOING_WINDOW_SIZE)
|
|
||||||
ScheduleResend ();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SSU: resend window exceeds max size. Session terminated");
|
|
||||||
m_Session.Close ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::ScheduleIncompleteMessagesCleanup ()
|
|
||||||
{
|
|
||||||
m_IncompleteMessagesCleanupTimer.cancel ();
|
|
||||||
m_IncompleteMessagesCleanupTimer.expires_from_now (boost::posix_time::seconds(INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT));
|
|
||||||
auto s = m_Session.shared_from_this();
|
|
||||||
m_IncompleteMessagesCleanupTimer.async_wait ([s](const boost::system::error_code& ecode)
|
|
||||||
{ s->m_Data.HandleIncompleteMessagesCleanupTimer (ecode); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::HandleIncompleteMessagesCleanupTimer (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();)
|
|
||||||
{
|
|
||||||
if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "SSU: message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted");
|
|
||||||
it = m_IncompleteMessages.erase (it);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
// decay
|
|
||||||
if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES ||
|
|
||||||
i2p::util::GetSecondsSinceEpoch () > m_LastMessageReceivedTime + DECAY_INTERVAL)
|
|
||||||
m_ReceivedMessages.clear ();
|
|
||||||
|
|
||||||
ScheduleIncompleteMessagesCleanup ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
131
SSUData.h
131
SSUData.h
@@ -1,131 +0,0 @@
|
|||||||
#ifndef SSU_DATA_H__
|
|
||||||
#define SSU_DATA_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <memory>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include "I2NPProtocol.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "RouterInfo.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace transport
|
|
||||||
{
|
|
||||||
|
|
||||||
const size_t SSU_MTU_V4 = 1484;
|
|
||||||
#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;
|
|
||||||
const size_t SSU_V4_MAX_PACKET_SIZE = SSU_MTU_V4 - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; // 1456
|
|
||||||
const size_t SSU_V6_MAX_PACKET_SIZE = SSU_MTU_V6 - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; // 1424
|
|
||||||
const int RESEND_INTERVAL = 3; // in seconds
|
|
||||||
const int MAX_NUM_RESENDS = 5;
|
|
||||||
const int DECAY_INTERVAL = 20; // in seconds
|
|
||||||
const int 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;
|
|
||||||
const uint8_t DATA_FLAG_REQUEST_PREVIOUS_ACKS = 0x08;
|
|
||||||
const uint8_t DATA_FLAG_EXPLICIT_CONGESTION_NOTIFICATION = 0x10;
|
|
||||||
const uint8_t DATA_FLAG_ACK_BITFIELDS_INCLUDED = 0x40;
|
|
||||||
const uint8_t DATA_FLAG_EXPLICIT_ACKS_INCLUDED = 0x80;
|
|
||||||
|
|
||||||
struct Fragment
|
|
||||||
{
|
|
||||||
int fragmentNum;
|
|
||||||
size_t len;
|
|
||||||
bool isLast;
|
|
||||||
uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; // use biggest
|
|
||||||
|
|
||||||
Fragment () = default;
|
|
||||||
Fragment (int n, const uint8_t * b, int l, bool last):
|
|
||||||
fragmentNum (n), len (l), isLast (last) { memcpy (buf, b, len); };
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FragmentCmp
|
|
||||||
{
|
|
||||||
bool operator() (const std::unique_ptr<Fragment>& f1, const std::unique_ptr<Fragment>& f2) const
|
|
||||||
{
|
|
||||||
return f1->fragmentNum < f2->fragmentNum;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct IncompleteMessage
|
|
||||||
{
|
|
||||||
std::shared_ptr<I2NPMessage> msg;
|
|
||||||
int nextFragmentNum;
|
|
||||||
uint32_t lastFragmentInsertTime; // in seconds
|
|
||||||
std::set<std::unique_ptr<Fragment>, FragmentCmp> savedFragments;
|
|
||||||
|
|
||||||
IncompleteMessage (std::shared_ptr<I2NPMessage> m): msg (m), nextFragmentNum (0), lastFragmentInsertTime (0) {};
|
|
||||||
void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SentMessage
|
|
||||||
{
|
|
||||||
std::vector<std::unique_ptr<Fragment> > fragments;
|
|
||||||
uint32_t nextResendTime; // in seconds
|
|
||||||
int numResends;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SSUSession;
|
|
||||||
class SSUData
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
SSUData (SSUSession& session);
|
|
||||||
~SSUData ();
|
|
||||||
|
|
||||||
void Start ();
|
|
||||||
void Stop ();
|
|
||||||
|
|
||||||
void ProcessMessage (uint8_t * buf, size_t len);
|
|
||||||
void FlushReceivedMessage ();
|
|
||||||
void Send (std::shared_ptr<i2p::I2NPMessage> msg);
|
|
||||||
|
|
||||||
void AdjustPacketSize (std::shared_ptr<const i2p::data::RouterInfo> remoteRouter);
|
|
||||||
void UpdatePacketSize (const i2p::data::IdentHash& remoteIdent);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void SendMsgAck (uint32_t msgID);
|
|
||||||
void SendFragmentAck (uint32_t msgID, int fragmentNum);
|
|
||||||
void ProcessAcks (uint8_t *& buf, uint8_t flag);
|
|
||||||
void ProcessFragments (uint8_t * buf);
|
|
||||||
void ProcessSentMessageAck (uint32_t msgID);
|
|
||||||
|
|
||||||
void ScheduleResend ();
|
|
||||||
void HandleResendTimer (const boost::system::error_code& ecode);
|
|
||||||
|
|
||||||
void ScheduleIncompleteMessagesCleanup ();
|
|
||||||
void HandleIncompleteMessagesCleanupTimer (const boost::system::error_code& ecode);
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
SSUSession& m_Session;
|
|
||||||
std::map<uint32_t, std::unique_ptr<IncompleteMessage> > m_IncompleteMessages;
|
|
||||||
std::map<uint32_t, std::unique_ptr<SentMessage> > m_SentMessages;
|
|
||||||
std::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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
1179
SSUSession.cpp
1179
SSUSession.cpp
File diff suppressed because it is too large
Load Diff
162
SSUSession.h
162
SSUSession.h
@@ -1,162 +0,0 @@
|
|||||||
#ifndef SSU_SESSION_H__
|
|
||||||
#define SSU_SESSION_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <set>
|
|
||||||
#include <memory>
|
|
||||||
#include "Crypto.h"
|
|
||||||
#include "I2NPProtocol.h"
|
|
||||||
#include "TransportSession.h"
|
|
||||||
#include "SSUData.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace transport
|
|
||||||
{
|
|
||||||
const uint8_t SSU_HEADER_EXTENDED_OPTIONS_INCLUDED = 0x04;
|
|
||||||
struct SSUHeader
|
|
||||||
{
|
|
||||||
uint8_t mac[16];
|
|
||||||
uint8_t iv[16];
|
|
||||||
uint8_t flag;
|
|
||||||
uint8_t time[4];
|
|
||||||
|
|
||||||
uint8_t GetPayloadType () const { return flag >> 4; };
|
|
||||||
bool IsExtendedOptions () const { return flag & SSU_HEADER_EXTENDED_OPTIONS_INCLUDED; };
|
|
||||||
};
|
|
||||||
|
|
||||||
const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds
|
|
||||||
const int SSU_TERMINATION_TIMEOUT = 330; // 5.5 minutes
|
|
||||||
const int SSU_CLOCK_SKEW = 60; // in seconds
|
|
||||||
|
|
||||||
// payload types (4 bits)
|
|
||||||
const uint8_t PAYLOAD_TYPE_SESSION_REQUEST = 0;
|
|
||||||
const uint8_t PAYLOAD_TYPE_SESSION_CREATED = 1;
|
|
||||||
const uint8_t PAYLOAD_TYPE_SESSION_CONFIRMED = 2;
|
|
||||||
const uint8_t PAYLOAD_TYPE_RELAY_REQUEST = 3;
|
|
||||||
const uint8_t PAYLOAD_TYPE_RELAY_RESPONSE = 4;
|
|
||||||
const uint8_t PAYLOAD_TYPE_RELAY_INTRO = 5;
|
|
||||||
const uint8_t PAYLOAD_TYPE_DATA = 6;
|
|
||||||
const uint8_t PAYLOAD_TYPE_PEER_TEST = 7;
|
|
||||||
const uint8_t PAYLOAD_TYPE_SESSION_DESTROYED = 8;
|
|
||||||
|
|
||||||
// extended options
|
|
||||||
const uint16_t EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG = 0x0001;
|
|
||||||
|
|
||||||
enum SessionState
|
|
||||||
{
|
|
||||||
eSessionStateUnknown,
|
|
||||||
eSessionStateIntroduced,
|
|
||||||
eSessionStateEstablished,
|
|
||||||
eSessionStateClosed,
|
|
||||||
eSessionStateFailed
|
|
||||||
};
|
|
||||||
|
|
||||||
enum PeerTestParticipant
|
|
||||||
{
|
|
||||||
ePeerTestParticipantUnknown = 0,
|
|
||||||
ePeerTestParticipantAlice1,
|
|
||||||
ePeerTestParticipantAlice2,
|
|
||||||
ePeerTestParticipantBob,
|
|
||||||
ePeerTestParticipantCharlie
|
|
||||||
};
|
|
||||||
|
|
||||||
class SSUServer;
|
|
||||||
class SSUSession: public TransportSession, public std::enable_shared_from_this<SSUSession>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint,
|
|
||||||
std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, bool peerTest = false);
|
|
||||||
void ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
|
|
||||||
~SSUSession ();
|
|
||||||
|
|
||||||
void Connect ();
|
|
||||||
void WaitForConnect ();
|
|
||||||
void Introduce (const i2p::data::RouterInfo::Introducer& introducer,
|
|
||||||
std::shared_ptr<const i2p::data::RouterInfo> to); // Alice to Charlie
|
|
||||||
void WaitForIntroduction ();
|
|
||||||
void Close ();
|
|
||||||
void Done ();
|
|
||||||
void Failed ();
|
|
||||||
boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
|
|
||||||
bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); };
|
|
||||||
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
|
|
||||||
void SendPeerTest (); // Alice
|
|
||||||
|
|
||||||
SessionState GetState () const { return m_State; };
|
|
||||||
size_t GetNumSentBytes () const { return m_NumSentBytes; };
|
|
||||||
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
|
|
||||||
|
|
||||||
void SendKeepAlive ();
|
|
||||||
uint32_t GetRelayTag () const { return m_RelayTag; };
|
|
||||||
const i2p::data::RouterInfo::IntroKey& GetIntroKey () const { return m_IntroKey; };
|
|
||||||
uint32_t GetCreationTime () const { return m_CreationTime; };
|
|
||||||
|
|
||||||
void FlushData ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
boost::asio::io_service& GetService ();
|
|
||||||
void CreateAESandMacKey (const uint8_t * pubKey);
|
|
||||||
size_t GetSSUHeaderSize (const uint8_t * buf) const;
|
|
||||||
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
|
|
||||||
void ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for established session
|
|
||||||
void ProcessSessionRequest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
|
|
||||||
void SendSessionRequest ();
|
|
||||||
void SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer, uint32_t nonce);
|
|
||||||
void ProcessSessionCreated (uint8_t * buf, size_t len);
|
|
||||||
void SendSessionCreated (const uint8_t * x, bool sendRelayTag = true);
|
|
||||||
void ProcessSessionConfirmed (const uint8_t * buf, size_t len);
|
|
||||||
void SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, size_t ourAddressLen);
|
|
||||||
void ProcessRelayRequest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from);
|
|
||||||
void SendRelayResponse (uint32_t nonce, const boost::asio::ip::udp::endpoint& from,
|
|
||||||
const uint8_t * introKey, const boost::asio::ip::udp::endpoint& to);
|
|
||||||
void SendRelayIntro (std::shared_ptr<SSUSession> session, const boost::asio::ip::udp::endpoint& from);
|
|
||||||
void ProcessRelayResponse (const uint8_t * buf, size_t len);
|
|
||||||
void ProcessRelayIntro (const uint8_t * buf, size_t len);
|
|
||||||
void Established ();
|
|
||||||
void 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, const boost::asio::ip::address& address, uint16_t port, const uint8_t * introKey, bool toAddress = true, bool sendAddress = true);
|
|
||||||
void ProcessData (uint8_t * buf, size_t len);
|
|
||||||
void SendSesionDestroyed ();
|
|
||||||
void Send (uint8_t type, const uint8_t * payload, size_t len); // with session key
|
|
||||||
void Send (const uint8_t * buf, size_t size);
|
|
||||||
|
|
||||||
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey,
|
|
||||||
const uint8_t * iv, const i2p::crypto::MACKey& macKey, uint8_t flag = 0);
|
|
||||||
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len); // with session key
|
|
||||||
void Decrypt (uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey);
|
|
||||||
void DecryptSessionKey (uint8_t * buf, size_t len);
|
|
||||||
bool Validate (uint8_t * buf, size_t len, const i2p::crypto::MACKey& macKey);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
friend class SSUData; // TODO: change in later
|
|
||||||
SSUServer& m_Server;
|
|
||||||
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
|
|
||||||
boost::asio::deadline_timer m_ConnectTimer;
|
|
||||||
bool m_IsPeerTest;
|
|
||||||
SessionState m_State;
|
|
||||||
bool m_IsSessionKey;
|
|
||||||
uint32_t m_RelayTag;
|
|
||||||
i2p::crypto::CBCEncryption m_SessionKeyEncryption;
|
|
||||||
i2p::crypto::CBCDecryption m_SessionKeyDecryption;
|
|
||||||
i2p::crypto::AESKey m_SessionKey;
|
|
||||||
i2p::crypto::MACKey m_MacKey;
|
|
||||||
i2p::data::RouterInfo::IntroKey m_IntroKey;
|
|
||||||
uint32_t m_CreationTime; // seconds since epoch
|
|
||||||
SSUData m_Data;
|
|
||||||
bool m_IsDataReceived;
|
|
||||||
std::unique_ptr<SignedData> m_SignedData; // we need it for SessionConfirmed only
|
|
||||||
std::map<uint32_t, std::shared_ptr<const i2p::data::RouterInfo> > m_RelayRequests; // nonce->Charlie
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
491
Signature.cpp
491
Signature.cpp
@@ -1,491 +0,0 @@
|
|||||||
#include <memory>
|
|
||||||
#include "Log.h"
|
|
||||||
#include "Signature.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace crypto
|
|
||||||
{
|
|
||||||
class Ed25519
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
Ed25519 ()
|
|
||||||
{
|
|
||||||
BN_CTX * ctx = BN_CTX_new ();
|
|
||||||
BIGNUM * tmp = BN_new ();
|
|
||||||
|
|
||||||
q = BN_new ();
|
|
||||||
// 2^255-19
|
|
||||||
BN_set_bit (q, 255); // 2^255
|
|
||||||
BN_sub_word (q, 19);
|
|
||||||
|
|
||||||
l = BN_new ();
|
|
||||||
// 2^252 + 27742317777372353535851937790883648493
|
|
||||||
BN_set_bit (l, 252);
|
|
||||||
two_252_2 = BN_dup (l);
|
|
||||||
BN_dec2bn (&tmp, "27742317777372353535851937790883648493");
|
|
||||||
BN_add (l, l, tmp);
|
|
||||||
BN_sub_word (two_252_2, 2); // 2^252 - 2
|
|
||||||
|
|
||||||
// -121665*inv(121666)
|
|
||||||
d = BN_new ();
|
|
||||||
BN_set_word (tmp, 121666);
|
|
||||||
BN_mod_inverse (tmp, tmp, q, ctx);
|
|
||||||
BN_set_word (d, 121665);
|
|
||||||
BN_set_negative (d, 1);
|
|
||||||
BN_mul (d, d, tmp, ctx);
|
|
||||||
|
|
||||||
// 2^((q-1)/4)
|
|
||||||
I = BN_new ();
|
|
||||||
BN_free (tmp);
|
|
||||||
tmp = BN_dup (q);
|
|
||||||
BN_sub_word (tmp, 1);
|
|
||||||
BN_div_word (tmp, 4);
|
|
||||||
BN_set_word (I, 2);
|
|
||||||
BN_mod_exp (I, I, tmp, q, ctx);
|
|
||||||
BN_free (tmp);
|
|
||||||
|
|
||||||
// 4*inv(5)
|
|
||||||
BIGNUM * By = BN_new ();
|
|
||||||
BN_set_word (By, 5);
|
|
||||||
BN_mod_inverse (By, By, q, ctx);
|
|
||||||
BN_mul_word (By, 4);
|
|
||||||
BIGNUM * Bx = RecoverX (By, ctx);
|
|
||||||
BN_mod (Bx, Bx, q, ctx); // % q
|
|
||||||
BN_mod (By, By, q, ctx); // % q
|
|
||||||
|
|
||||||
// precalculate Bi256 table
|
|
||||||
Bi256Carry = { Bx, By }; // B
|
|
||||||
for (int i = 0; i < 32; i++)
|
|
||||||
{
|
|
||||||
Bi256[i][0] = Bi256Carry; // first point
|
|
||||||
for (int j = 1; j < 128; j++)
|
|
||||||
Bi256[i][j] = Sum (Bi256[i][j-1], Bi256[i][0], ctx); // (256+j+1)^i*B
|
|
||||||
Bi256Carry = Bi256[i][127];
|
|
||||||
for (int j = 0; j < 128; j++) // add first point 128 more times
|
|
||||||
Bi256Carry = Sum (Bi256Carry, Bi256[i][0], ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
BN_CTX_free (ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ed25519 (const Ed25519& other): q (BN_dup (other.q)), l (BN_dup (other.l)),
|
|
||||||
d (BN_dup (other.d)), I (BN_dup (other.I)), two_252_2 (BN_dup (other.two_252_2)),
|
|
||||||
Bi256Carry (other.Bi256Carry)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 32; i++)
|
|
||||||
for (int j = 0; j < 128; j++)
|
|
||||||
Bi256[i][j] = other.Bi256[i][j];
|
|
||||||
}
|
|
||||||
|
|
||||||
~Ed25519 ()
|
|
||||||
{
|
|
||||||
BN_free (q);
|
|
||||||
BN_free (l);
|
|
||||||
BN_free (d);
|
|
||||||
BN_free (I);
|
|
||||||
BN_free (two_252_2);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
EDDSAPoint GeneratePublicKey (const uint8_t * expandedPrivateKey, BN_CTX * ctx) const
|
|
||||||
{
|
|
||||||
return MulB (expandedPrivateKey, ctx); // left half of expanded key, considered as Little Endian
|
|
||||||
}
|
|
||||||
|
|
||||||
EDDSAPoint DecodePublicKey (const uint8_t * buf, BN_CTX * ctx) const
|
|
||||||
{
|
|
||||||
return DecodePoint (buf, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EncodePublicKey (const EDDSAPoint& publicKey, uint8_t * buf, BN_CTX * ctx) const
|
|
||||||
{
|
|
||||||
EncodePoint (Normalize (publicKey, ctx), buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Verify (const EDDSAPoint& publicKey, const uint8_t * digest, const uint8_t * signature) const
|
|
||||||
{
|
|
||||||
BN_CTX * ctx = BN_CTX_new ();
|
|
||||||
BIGNUM * h = DecodeBN<64> (digest);
|
|
||||||
// signature 0..31 - R, 32..63 - S
|
|
||||||
// B*S = R + PK*h => R = B*S - PK*h
|
|
||||||
// we don't decode R, but encode (B*S - PK*h)
|
|
||||||
auto Bs = MulB (signature + EDDSA25519_SIGNATURE_LENGTH/2, ctx); // B*S;
|
|
||||||
BN_mod (h, h, l, ctx); // public key is multiple of B, but B%l = 0
|
|
||||||
auto PKh = Mul (publicKey, h, ctx); // PK*h
|
|
||||||
uint8_t diff[32];
|
|
||||||
EncodePoint (Normalize (Sum (Bs, -PKh, ctx), ctx), diff); // Bs - PKh encoded
|
|
||||||
bool passed = !memcmp (signature, diff, 32); // R
|
|
||||||
BN_free (h);
|
|
||||||
BN_CTX_free (ctx);
|
|
||||||
if (!passed)
|
|
||||||
LogPrint (eLogError, "25519 signature verification failed");
|
|
||||||
return passed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Sign (const uint8_t * expandedPrivateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len,
|
|
||||||
uint8_t * signature) const
|
|
||||||
{
|
|
||||||
BN_CTX * bnCtx = BN_CTX_new ();
|
|
||||||
// calculate r
|
|
||||||
SHA512_CTX ctx;
|
|
||||||
SHA512_Init (&ctx);
|
|
||||||
SHA512_Update (&ctx, expandedPrivateKey + EDDSA25519_PRIVATE_KEY_LENGTH, EDDSA25519_PRIVATE_KEY_LENGTH); // right half of expanded key
|
|
||||||
SHA512_Update (&ctx, buf, len); // data
|
|
||||||
uint8_t digest[64];
|
|
||||||
SHA512_Final (digest, &ctx);
|
|
||||||
BIGNUM * r = DecodeBN<32> (digest); // DecodeBN<64> (digest); // for test vectors
|
|
||||||
// calculate R
|
|
||||||
uint8_t R[EDDSA25519_SIGNATURE_LENGTH/2]; // we must use separate buffer because signature might be inside buf
|
|
||||||
EncodePoint (Normalize (MulB (digest, bnCtx), bnCtx), R); // EncodePoint (Mul (B, r, bnCtx), R); // for test vectors
|
|
||||||
// calculate S
|
|
||||||
SHA512_Init (&ctx);
|
|
||||||
SHA512_Update (&ctx, R, EDDSA25519_SIGNATURE_LENGTH/2); // R
|
|
||||||
SHA512_Update (&ctx, publicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
|
|
||||||
SHA512_Update (&ctx, buf, len); // data
|
|
||||||
SHA512_Final (digest, &ctx);
|
|
||||||
BIGNUM * h = DecodeBN<64> (digest);
|
|
||||||
// S = (r + h*a) % l
|
|
||||||
BIGNUM * a = DecodeBN<EDDSA25519_PRIVATE_KEY_LENGTH> (expandedPrivateKey); // left half of expanded key
|
|
||||||
BN_mod_mul (h, h, a, l, bnCtx); // %l
|
|
||||||
BN_mod_add (h, h, r, l, bnCtx); // %l
|
|
||||||
memcpy (signature, R, EDDSA25519_SIGNATURE_LENGTH/2);
|
|
||||||
EncodeBN (h, signature + EDDSA25519_SIGNATURE_LENGTH/2, EDDSA25519_SIGNATURE_LENGTH/2); // S
|
|
||||||
BN_free (r); BN_free (h); BN_free (a);
|
|
||||||
BN_CTX_free (bnCtx);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
EDDSAPoint Sum (const EDDSAPoint& p1, const EDDSAPoint& p2, BN_CTX * ctx) const
|
|
||||||
{
|
|
||||||
// x3 = (x1*y2+y1*x2)*(z1*z2-d*t1*t2)
|
|
||||||
// y3 = (y1*y2+x1*x2)*(z1*z2+d*t1*t2)
|
|
||||||
// z3 = (z1*z2-d*t1*t2)*(z1*z2+d*t1*t2)
|
|
||||||
// t3 = (y1*y2+x1*x2)*(x1*y2+y1*x2)
|
|
||||||
BIGNUM * x3 = BN_new (), * y3 = BN_new (), * z3 = BN_new (), * t3 = BN_new ();
|
|
||||||
|
|
||||||
BN_mul (x3, p1.x, p2.x, ctx); // A = x1*x2
|
|
||||||
BN_mul (y3, p1.y, p2.y, ctx); // B = y1*y2
|
|
||||||
|
|
||||||
BIGNUM * t1 = p1.t, * t2 = p2.t;
|
|
||||||
if (!t1) { t1 = BN_new (); BN_mul (t1, p1.x, p1.y, ctx); }
|
|
||||||
if (!t2) { t2 = BN_new (); BN_mul (t2, p2.x, p2.y, ctx); }
|
|
||||||
BN_mul (t3, t1, t2, ctx);
|
|
||||||
BN_mul (t3, t3, d, ctx); // C = d*t1*t2
|
|
||||||
if (!p1.t) BN_free (t1);
|
|
||||||
if (!p2.t) BN_free (t2);
|
|
||||||
|
|
||||||
if (p1.z)
|
|
||||||
{
|
|
||||||
if (p2.z)
|
|
||||||
BN_mul (z3, p1.z, p2.z, ctx); // D = z1*z2
|
|
||||||
else
|
|
||||||
BN_copy (z3, p1.z); // D = z1
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (p2.z)
|
|
||||||
BN_copy (z3, p2.z); // D = z2
|
|
||||||
else
|
|
||||||
BN_one (z3); // D = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
BIGNUM * E = BN_new (), * F = BN_new (), * G = BN_new (), * H = BN_new ();
|
|
||||||
BN_add (E, p1.x, p1.y);
|
|
||||||
BN_add (F, p2.x, p2.y);
|
|
||||||
BN_mul (E, E, F, ctx); // (x1 + y1)*(x2 + y2)
|
|
||||||
BN_sub (E, E, x3);
|
|
||||||
BN_sub (E, E, y3); // E = (x1 + y1)*(x2 + y2) - A - B
|
|
||||||
BN_sub (F, z3, t3); // F = D - C
|
|
||||||
BN_add (G, z3, t3); // G = D + C
|
|
||||||
BN_add (H, y3, x3); // H = B + A
|
|
||||||
|
|
||||||
BN_mod_mul (x3, E, F, q, ctx); // x3 = E*F
|
|
||||||
BN_mod_mul (y3, G, H, q, ctx); // y3 = G*H
|
|
||||||
BN_mod_mul (z3, F, G, q, ctx); // z3 = F*G
|
|
||||||
BN_mod_mul (t3, E, H, q, ctx); // t3 = E*H
|
|
||||||
|
|
||||||
BN_free (E); BN_free (F); BN_free (G); BN_free (H);
|
|
||||||
|
|
||||||
return EDDSAPoint {x3, y3, z3, t3};
|
|
||||||
}
|
|
||||||
|
|
||||||
EDDSAPoint Double (const EDDSAPoint& p, BN_CTX * ctx) const
|
|
||||||
{
|
|
||||||
BIGNUM * x2 = BN_new (), * y2 = BN_new (), * z2 = BN_new (), * t2 = BN_new ();
|
|
||||||
|
|
||||||
BN_sqr (x2, p.x, ctx); // x2 = A = x^2
|
|
||||||
BN_sqr (y2, p.y, ctx); // y2 = B = y^2
|
|
||||||
if (p.t)
|
|
||||||
BN_sqr (t2, p.t, ctx); // t2 = t^2
|
|
||||||
else
|
|
||||||
{
|
|
||||||
BN_mul (t2, p.x, p.y, ctx); // t = x*y
|
|
||||||
BN_sqr (t2, t2, ctx); // t2 = t^2
|
|
||||||
}
|
|
||||||
BN_mul (t2, t2, d, ctx); // t2 = C = d*t^2
|
|
||||||
if (p.z)
|
|
||||||
BN_sqr (z2, p.z, ctx); // z2 = D = z^2
|
|
||||||
else
|
|
||||||
BN_one (z2); // z2 = 1
|
|
||||||
|
|
||||||
BIGNUM * E = BN_new (), * F = BN_new (), * G = BN_new (), * H = BN_new ();
|
|
||||||
// E = (x+y)*(x+y)-A-B = x^2+y^2+2xy-A-B = 2xy
|
|
||||||
BN_mul (E, p.x, p.y, ctx);
|
|
||||||
BN_lshift1 (E, E); // E =2*x*y
|
|
||||||
BN_sub (F, z2, t2); // F = D - C
|
|
||||||
BN_add (G, z2, t2); // G = D + C
|
|
||||||
BN_add (H, y2, x2); // H = B + A
|
|
||||||
|
|
||||||
BN_mod_mul (x2, E, F, q, ctx); // x2 = E*F
|
|
||||||
BN_mod_mul (y2, G, H, q, ctx); // y2 = G*H
|
|
||||||
BN_mod_mul (z2, F, G, q, ctx); // z2 = F*G
|
|
||||||
BN_mod_mul (t2, E, H, q, ctx); // t2 = E*H
|
|
||||||
|
|
||||||
BN_free (E); BN_free (F); BN_free (G); BN_free (H);
|
|
||||||
|
|
||||||
return EDDSAPoint {x2, y2, z2, t2};
|
|
||||||
}
|
|
||||||
|
|
||||||
EDDSAPoint Mul (const EDDSAPoint& p, const BIGNUM * e, BN_CTX * ctx) const
|
|
||||||
{
|
|
||||||
BIGNUM * zero = BN_new (), * one = BN_new ();
|
|
||||||
BN_zero (zero); BN_one (one);
|
|
||||||
EDDSAPoint res {zero, one};
|
|
||||||
if (!BN_is_zero (e))
|
|
||||||
{
|
|
||||||
int bitCount = BN_num_bits (e);
|
|
||||||
for (int i = bitCount - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
res = Double (res, ctx);
|
|
||||||
if (BN_is_bit_set (e, i)) res = Sum (res, p, ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
EDDSAPoint MulB (const uint8_t * e, BN_CTX * ctx) const // B*e, e is 32 bytes Little Endian
|
|
||||||
{
|
|
||||||
BIGNUM * zero = BN_new (), * one = BN_new ();
|
|
||||||
BN_zero (zero); BN_one (one);
|
|
||||||
EDDSAPoint res {zero, one};
|
|
||||||
bool carry = false;
|
|
||||||
for (int i = 0; i < 32; i++)
|
|
||||||
{
|
|
||||||
uint8_t x = e[i];
|
|
||||||
if (carry)
|
|
||||||
{
|
|
||||||
if (x < 255)
|
|
||||||
{
|
|
||||||
x++;
|
|
||||||
carry = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
x = 0;
|
|
||||||
}
|
|
||||||
if (x > 0)
|
|
||||||
{
|
|
||||||
if (x <= 128)
|
|
||||||
res = Sum (res, Bi256[i][x-1], ctx);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
res = Sum (res, -Bi256[i][255-x], ctx); // -Bi[256-x]
|
|
||||||
carry = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (carry) res = Sum (res, Bi256Carry, ctx);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
EDDSAPoint Normalize (const EDDSAPoint& p, BN_CTX * ctx) const
|
|
||||||
{
|
|
||||||
if (p.z)
|
|
||||||
{
|
|
||||||
BIGNUM * x = BN_new (), * y = BN_new ();
|
|
||||||
BN_mod_inverse (y, p.z, q, ctx);
|
|
||||||
BN_mod_mul (x, p.x, y, q, ctx); // x = x/z
|
|
||||||
BN_mod_mul (y, p.y, y, q, ctx); // y = y/z
|
|
||||||
return EDDSAPoint{x, y};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return EDDSAPoint{BN_dup (p.x), BN_dup (p.y)};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsOnCurve (const EDDSAPoint& p, BN_CTX * ctx) const
|
|
||||||
{
|
|
||||||
BIGNUM * x2 = BN_new ();
|
|
||||||
BN_sqr (x2, p.x, ctx); // x^2
|
|
||||||
BIGNUM * y2 = BN_new ();
|
|
||||||
BN_sqr (y2, p.y, ctx); // y^2
|
|
||||||
// y^2 - x^2 - 1 - d*x^2*y^2
|
|
||||||
BIGNUM * tmp = BN_new ();
|
|
||||||
BN_mul (tmp, d, x2, ctx);
|
|
||||||
BN_mul (tmp, tmp, y2, ctx);
|
|
||||||
BN_sub (tmp, y2, tmp);
|
|
||||||
BN_sub (tmp, tmp, x2);
|
|
||||||
BN_sub_word (tmp, 1);
|
|
||||||
BN_mod (tmp, tmp, q, ctx); // % q
|
|
||||||
bool ret = BN_is_zero (tmp);
|
|
||||||
BN_free (x2);
|
|
||||||
BN_free (y2);
|
|
||||||
BN_free (tmp);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
BIGNUM * RecoverX (const BIGNUM * y, BN_CTX * ctx) const
|
|
||||||
{
|
|
||||||
BIGNUM * y2 = BN_new ();
|
|
||||||
BN_sqr (y2, y, ctx); // y^2
|
|
||||||
// xx = (y^2 -1)*inv(d*y^2 +1)
|
|
||||||
BIGNUM * xx = BN_new ();
|
|
||||||
BN_mul (xx, d, y2, ctx);
|
|
||||||
BN_add_word (xx, 1);
|
|
||||||
BN_mod_inverse (xx, xx, q, ctx);
|
|
||||||
BN_sub_word (y2, 1);
|
|
||||||
BN_mul (xx, y2, xx, ctx);
|
|
||||||
// x = srqt(xx) = xx^(2^252-2)
|
|
||||||
BIGNUM * x = BN_new ();
|
|
||||||
BN_mod_exp (x, xx, two_252_2, q, ctx);
|
|
||||||
// check (x^2 -xx) % q
|
|
||||||
BN_sqr (y2, x, ctx);
|
|
||||||
BN_mod_sub (y2, y2, xx, q, ctx);
|
|
||||||
if (!BN_is_zero (y2))
|
|
||||||
BN_mod_mul (x, x, I, q, ctx);
|
|
||||||
if (BN_is_odd (x))
|
|
||||||
BN_sub (x, q, x);
|
|
||||||
BN_free (y2);
|
|
||||||
BN_free (xx);
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
EDDSAPoint DecodePoint (const uint8_t * buf, BN_CTX * ctx) const
|
|
||||||
{
|
|
||||||
// buf is 32 bytes Little Endian, convert it to Big Endian
|
|
||||||
uint8_t buf1[EDDSA25519_PUBLIC_KEY_LENGTH];
|
|
||||||
for (size_t i = 0; i < EDDSA25519_PUBLIC_KEY_LENGTH/2; i++) // invert bytes
|
|
||||||
{
|
|
||||||
buf1[i] = buf[EDDSA25519_PUBLIC_KEY_LENGTH -1 - i];
|
|
||||||
buf1[EDDSA25519_PUBLIC_KEY_LENGTH -1 - i] = buf[i];
|
|
||||||
}
|
|
||||||
bool isHighestBitSet = buf1[0] & 0x80;
|
|
||||||
if (isHighestBitSet)
|
|
||||||
buf1[0] &= 0x7f; // clear highest bit
|
|
||||||
BIGNUM * y = BN_new ();
|
|
||||||
BN_bin2bn (buf1, EDDSA25519_PUBLIC_KEY_LENGTH, y);
|
|
||||||
auto x = RecoverX (y, ctx);
|
|
||||||
if (BN_is_bit_set (x, 0) != isHighestBitSet)
|
|
||||||
BN_sub (x, q, x); // x = q - x
|
|
||||||
BIGNUM * z = BN_new (), * t = BN_new ();
|
|
||||||
BN_one (z); BN_mod_mul (t, x, y, q, ctx); // pre-calculate t
|
|
||||||
EDDSAPoint p {x, y, z, t};
|
|
||||||
if (!IsOnCurve (p, ctx))
|
|
||||||
LogPrint (eLogError, "Decoded point is not on 25519");
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EncodePoint (const EDDSAPoint& p, uint8_t * buf) const
|
|
||||||
{
|
|
||||||
EncodeBN (p.y, buf,EDDSA25519_PUBLIC_KEY_LENGTH);
|
|
||||||
if (BN_is_bit_set (p.x, 0)) // highest bit
|
|
||||||
buf[EDDSA25519_PUBLIC_KEY_LENGTH - 1] |= 0x80; // set highest bit
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int len>
|
|
||||||
BIGNUM * DecodeBN (const uint8_t * buf) const
|
|
||||||
{
|
|
||||||
// buf is Little Endian convert it to Big Endian
|
|
||||||
uint8_t buf1[len];
|
|
||||||
for (size_t i = 0; i < len/2; i++) // invert bytes
|
|
||||||
{
|
|
||||||
buf1[i] = buf[len -1 - i];
|
|
||||||
buf1[len -1 - i] = buf[i];
|
|
||||||
}
|
|
||||||
BIGNUM * res = BN_new ();
|
|
||||||
BN_bin2bn (buf1, len, res);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EncodeBN (const BIGNUM * bn, uint8_t * buf, size_t len) const
|
|
||||||
{
|
|
||||||
bn2buf (bn, buf, len);
|
|
||||||
// To Little Endian
|
|
||||||
for (size_t i = 0; i < len/2; i++) // invert bytes
|
|
||||||
{
|
|
||||||
uint8_t tmp = buf[i];
|
|
||||||
buf[i] = buf[len -1 - i];
|
|
||||||
buf[len -1 - i] = tmp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
BIGNUM * q, * l, * d, * I;
|
|
||||||
// transient values
|
|
||||||
BIGNUM * two_252_2; // 2^252-2
|
|
||||||
EDDSAPoint Bi256[32][128]; // per byte, Bi256[i][j] = (256+j+1)^i*B, we don't store zeroes
|
|
||||||
// if j > 128 we use 256 - j and carry 1 to next byte
|
|
||||||
// Bi256[0][0] = B, base point
|
|
||||||
EDDSAPoint Bi256Carry; // Bi256[32][0]
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::unique_ptr<Ed25519> g_Ed25519;
|
|
||||||
std::unique_ptr<Ed25519>& GetEd25519 ()
|
|
||||||
{
|
|
||||||
if (!g_Ed25519)
|
|
||||||
{
|
|
||||||
auto c = new Ed25519();
|
|
||||||
if (!g_Ed25519) // make sure it was not created already
|
|
||||||
g_Ed25519.reset (c);
|
|
||||||
else
|
|
||||||
delete c;
|
|
||||||
}
|
|
||||||
return g_Ed25519;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
EDDSA25519Verifier::EDDSA25519Verifier (const uint8_t * signingKey)
|
|
||||||
{
|
|
||||||
memcpy (m_PublicKeyEncoded, signingKey, EDDSA25519_PUBLIC_KEY_LENGTH);
|
|
||||||
BN_CTX * ctx = BN_CTX_new ();
|
|
||||||
m_PublicKey = GetEd25519 ()->DecodePublicKey (m_PublicKeyEncoded, ctx);
|
|
||||||
BN_CTX_free (ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EDDSA25519Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
|
|
||||||
{
|
|
||||||
uint8_t digest[64];
|
|
||||||
SHA512_CTX ctx;
|
|
||||||
SHA512_Init (&ctx);
|
|
||||||
SHA512_Update (&ctx, signature, EDDSA25519_SIGNATURE_LENGTH/2); // R
|
|
||||||
SHA512_Update (&ctx, m_PublicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
|
|
||||||
SHA512_Update (&ctx, buf, len); // data
|
|
||||||
SHA512_Final (digest, &ctx);
|
|
||||||
|
|
||||||
return GetEd25519 ()->Verify (m_PublicKey, digest, signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
EDDSA25519Signer::EDDSA25519Signer (const uint8_t * signingPrivateKey)
|
|
||||||
{
|
|
||||||
// expand key
|
|
||||||
SHA512 (signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH, m_ExpandedPrivateKey);
|
|
||||||
m_ExpandedPrivateKey[0] &= 0xF8; // drop last 3 bits
|
|
||||||
m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] &= 0x1F; // drop first 3 bits
|
|
||||||
m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] |= 0x40; // set second bit
|
|
||||||
// generate and encode public key
|
|
||||||
BN_CTX * ctx = BN_CTX_new ();
|
|
||||||
auto publicKey = GetEd25519 ()->GeneratePublicKey (m_ExpandedPrivateKey, ctx);
|
|
||||||
GetEd25519 ()->EncodePublicKey (publicKey, m_PublicKeyEncoded, ctx);
|
|
||||||
BN_CTX_free (ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EDDSA25519Signer::Sign (const uint8_t * buf, int len, uint8_t * signature) const
|
|
||||||
{
|
|
||||||
GetEd25519 ()->Sign (m_ExpandedPrivateKey, m_PublicKeyEncoded, buf, len, signature);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
95
Tag.h
95
Tag.h
@@ -1,95 +0,0 @@
|
|||||||
#ifndef TAG_H__
|
|
||||||
#define TAG_H__
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 <boost/static_assert.hpp>
|
|
||||||
#include <string.h>
|
|
||||||
#include <openssl/rand.h>
|
|
||||||
#include "Base.h"
|
|
||||||
|
|
||||||
namespace i2p {
|
|
||||||
namespace data {
|
|
||||||
|
|
||||||
template<size_t sz>
|
|
||||||
class Tag
|
|
||||||
{
|
|
||||||
BOOST_STATIC_ASSERT_MSG(sz % 8 == 0, "Tag size must be multiple of 8 bytes");
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Tag () = default;
|
|
||||||
Tag (const uint8_t * buf) { memcpy (m_Buf, buf, sz); }
|
|
||||||
|
|
||||||
bool operator== (const Tag& other) const { return !memcmp (m_Buf, other.m_Buf, sz); }
|
|
||||||
bool operator< (const Tag& other) const { return memcmp (m_Buf, other.m_Buf, sz) < 0; }
|
|
||||||
|
|
||||||
uint8_t * operator()() { return m_Buf; }
|
|
||||||
const uint8_t * operator()() const { return m_Buf; }
|
|
||||||
|
|
||||||
operator uint8_t * () { return m_Buf; }
|
|
||||||
operator const uint8_t * () const { return m_Buf; }
|
|
||||||
|
|
||||||
const uint8_t * data() const { return m_Buf; }
|
|
||||||
const uint64_t * GetLL () const { return ll; }
|
|
||||||
|
|
||||||
bool IsZero () const
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < sz/8; ++i)
|
|
||||||
if (ll[i]) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Fill(uint8_t c)
|
|
||||||
{
|
|
||||||
memset(m_Buf, c, sz);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Randomize()
|
|
||||||
{
|
|
||||||
RAND_bytes(m_Buf, sz);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string ToBase64 () const
|
|
||||||
{
|
|
||||||
char str[sz*2];
|
|
||||||
size_t l = i2p::data::ByteStreamToBase64 (m_Buf, sz, str, sz*2);
|
|
||||||
return std::string (str, str + l);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string ToBase32 () const
|
|
||||||
{
|
|
||||||
char str[sz*2];
|
|
||||||
size_t l = i2p::data::ByteStreamToBase32 (m_Buf, sz, str, sz*2);
|
|
||||||
return std::string (str, str + l);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 aligned
|
|
||||||
{
|
|
||||||
uint8_t m_Buf[sz];
|
|
||||||
uint64_t ll[sz/8];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // data
|
|
||||||
} // i2p
|
|
||||||
|
|
||||||
#endif /* TAG_H__ */
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
#include <inttypes.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include "Log.h"
|
|
||||||
#include "I2PEndian.h"
|
|
||||||
#include "Timestamp.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace util
|
|
||||||
{
|
|
||||||
static int64_t g_TimeOffset = 0; // in seconds
|
|
||||||
|
|
||||||
void SyncTimeWithNTP (const std::string& address)
|
|
||||||
{
|
|
||||||
boost::asio::io_service service;
|
|
||||||
boost::asio::ip::udp::resolver::query query (boost::asio::ip::udp::v4 (), address, "ntp");
|
|
||||||
boost::system::error_code ec;
|
|
||||||
auto it = boost::asio::ip::udp::resolver (service).resolve (query, ec);
|
|
||||||
if (!ec && it != boost::asio::ip::udp::resolver::iterator())
|
|
||||||
{
|
|
||||||
auto ep = (*it).endpoint (); // take first one
|
|
||||||
boost::asio::ip::udp::socket socket (service);
|
|
||||||
socket.open (boost::asio::ip::udp::v4 (), ec);
|
|
||||||
if (!ec)
|
|
||||||
{
|
|
||||||
uint8_t buf[48];// 48 bytes NTP request/response
|
|
||||||
memset (buf, 0, 48);
|
|
||||||
htobe32buf (buf, (3 << 27) | (3 << 24)); // RFC 4330
|
|
||||||
size_t len = 0;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
socket.send_to (boost::asio::buffer (buf, 48), ep);
|
|
||||||
int i = 0;
|
|
||||||
while (!socket.available() && i < 10) // 10 seconds max
|
|
||||||
{
|
|
||||||
std::this_thread::sleep_for (std::chrono::seconds(1));
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
if (socket.available ())
|
|
||||||
len = socket.receive_from (boost::asio::buffer (buf, 48), ep);
|
|
||||||
}
|
|
||||||
catch (std::exception& e)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "NTP error: ", e.what ());
|
|
||||||
}
|
|
||||||
if (len >= 8)
|
|
||||||
{
|
|
||||||
auto ourTs = GetSecondsSinceEpoch ();
|
|
||||||
uint32_t ts = bufbe32toh (buf + 32);
|
|
||||||
if (ts > 2208988800U) ts -= 2208988800U; // 1/1/1970 from 1/1/1900
|
|
||||||
g_TimeOffset = ts - ourTs;
|
|
||||||
LogPrint (eLogInfo, address, " time offset from system time is ", g_TimeOffset, " seconds");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
32
Timestamp.h
32
Timestamp.h
@@ -1,32 +0,0 @@
|
|||||||
#ifndef TIMESTAMP_H__
|
|
||||||
#define TIMESTAMP_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <chrono>
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace util
|
|
||||||
{
|
|
||||||
inline uint64_t GetMillisecondsSinceEpoch ()
|
|
||||||
{
|
|
||||||
return std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
||||||
std::chrono::system_clock::now().time_since_epoch()).count ();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline uint32_t GetHoursSinceEpoch ()
|
|
||||||
{
|
|
||||||
return std::chrono::duration_cast<std::chrono::hours>(
|
|
||||||
std::chrono::system_clock::now().time_since_epoch()).count ();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline uint64_t GetSecondsSinceEpoch ()
|
|
||||||
{
|
|
||||||
return std::chrono::duration_cast<std::chrono::seconds>(
|
|
||||||
std::chrono::system_clock::now().time_since_epoch()).count ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
#ifndef TRANSPORT_SESSION_H__
|
|
||||||
#define TRANSPORT_SESSION_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "Crypto.h"
|
|
||||||
#include "RouterInfo.h"
|
|
||||||
#include "I2NPProtocol.h"
|
|
||||||
#include "Timestamp.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace transport
|
|
||||||
{
|
|
||||||
class SignedData
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
SignedData () {}
|
|
||||||
SignedData (const SignedData& other)
|
|
||||||
{
|
|
||||||
m_Stream << other.m_Stream.rdbuf ();
|
|
||||||
}
|
|
||||||
void Insert (const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
m_Stream.write ((char *)buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void Insert (T t)
|
|
||||||
{
|
|
||||||
m_Stream.write ((char *)&t, sizeof (T));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Verify (std::shared_ptr<const i2p::data::IdentityEx> ident, const uint8_t * signature) const
|
|
||||||
{
|
|
||||||
return ident->Verify ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Sign (const i2p::data::PrivateKeys& keys, uint8_t * signature) const
|
|
||||||
{
|
|
||||||
keys.Sign ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::stringstream m_Stream;
|
|
||||||
};
|
|
||||||
|
|
||||||
class TransportSession
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
TransportSession (std::shared_ptr<const i2p::data::RouterInfo> router, int terminationTimeout):
|
|
||||||
m_DHKeysPair (nullptr), m_NumSentBytes (0), m_NumReceivedBytes (0), m_IsOutgoing (router), m_TerminationTimeout (terminationTimeout),
|
|
||||||
m_LastActivityTimestamp (i2p::util::GetSecondsSinceEpoch ())
|
|
||||||
{
|
|
||||||
if (router)
|
|
||||||
m_RemoteIdentity = router->GetRouterIdentity ();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~TransportSession () {};
|
|
||||||
virtual void Done () = 0;
|
|
||||||
|
|
||||||
std::string GetIdentHashBase64() const { return m_RemoteIdentity ? m_RemoteIdentity->GetIdentHash().ToBase64() : ""; }
|
|
||||||
|
|
||||||
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity () { return m_RemoteIdentity; };
|
|
||||||
void SetRemoteIdentity (std::shared_ptr<const i2p::data::IdentityEx> ident) { m_RemoteIdentity = ident; };
|
|
||||||
|
|
||||||
size_t GetNumSentBytes () const { return m_NumSentBytes; };
|
|
||||||
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
|
|
||||||
bool IsOutgoing () const { return m_IsOutgoing; };
|
|
||||||
|
|
||||||
int GetTerminationTimeout () const { return m_TerminationTimeout; };
|
|
||||||
void SetTerminationTimeout (int terminationTimeout) { m_TerminationTimeout = terminationTimeout; };
|
|
||||||
bool IsTerminationTimeoutExpired (uint64_t ts) const
|
|
||||||
{ return ts >= m_LastActivityTimestamp + GetTerminationTimeout (); };
|
|
||||||
|
|
||||||
virtual void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
|
|
||||||
std::shared_ptr<i2p::crypto::DHKeys> m_DHKeysPair; // X - for client and Y - for server
|
|
||||||
size_t m_NumSentBytes, m_NumReceivedBytes;
|
|
||||||
bool m_IsOutgoing;
|
|
||||||
int m_TerminationTimeout;
|
|
||||||
uint64_t m_LastActivityTimestamp;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
791
Transports.cpp
791
Transports.cpp
@@ -1,791 +0,0 @@
|
|||||||
#include "Log.h"
|
|
||||||
#include "Crypto.h"
|
|
||||||
#include "RouterContext.h"
|
|
||||||
#include "I2NPProtocol.h"
|
|
||||||
#include "NetDb.h"
|
|
||||||
#include "Transports.h"
|
|
||||||
#include "Config.h"
|
|
||||||
#ifdef WITH_EVENTS
|
|
||||||
#include "Event.h"
|
|
||||||
#include "util.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using namespace i2p::data;
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace transport
|
|
||||||
{
|
|
||||||
DHKeysPairSupplier::DHKeysPairSupplier (int size):
|
|
||||||
m_QueueSize (size), m_IsRunning (false), m_Thread (nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
DHKeysPairSupplier::~DHKeysPairSupplier ()
|
|
||||||
{
|
|
||||||
Stop ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DHKeysPairSupplier::Start ()
|
|
||||||
{
|
|
||||||
m_IsRunning = true;
|
|
||||||
m_Thread = new std::thread (std::bind (&DHKeysPairSupplier::Run, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
void DHKeysPairSupplier::Stop ()
|
|
||||||
{
|
|
||||||
m_IsRunning = false;
|
|
||||||
m_Acquired.notify_one ();
|
|
||||||
if (m_Thread)
|
|
||||||
{
|
|
||||||
m_Thread->join ();
|
|
||||||
delete m_Thread;
|
|
||||||
m_Thread = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DHKeysPairSupplier::Run ()
|
|
||||||
{
|
|
||||||
while (m_IsRunning)
|
|
||||||
{
|
|
||||||
int num, total = 0;
|
|
||||||
while ((num = m_QueueSize - (int)m_Queue.size ()) > 0 && total < 20)
|
|
||||||
{
|
|
||||||
CreateDHKeysPairs (num);
|
|
||||||
total += num;
|
|
||||||
}
|
|
||||||
if (total >= 20)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Transports: ", total, " DH keys generated at the time");
|
|
||||||
std::this_thread::sleep_for (std::chrono::seconds(1)); // take a break
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
|
||||||
m_Acquired.wait (l); // wait for element gets aquired
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DHKeysPairSupplier::CreateDHKeysPairs (int num)
|
|
||||||
{
|
|
||||||
if (num > 0)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < num; i++)
|
|
||||||
{
|
|
||||||
auto pair = std::make_shared<i2p::crypto::DHKeys> ();
|
|
||||||
pair->GenerateKeys ();
|
|
||||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
|
||||||
m_Queue.push (pair);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<i2p::crypto::DHKeys> DHKeysPairSupplier::Acquire ()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
|
||||||
if (!m_Queue.empty ())
|
|
||||||
{
|
|
||||||
auto pair = m_Queue.front ();
|
|
||||||
m_Queue.pop ();
|
|
||||||
m_Acquired.notify_one ();
|
|
||||||
return pair;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// queue is empty, create new
|
|
||||||
auto pair = std::make_shared<i2p::crypto::DHKeys> ();
|
|
||||||
pair->GenerateKeys ();
|
|
||||||
return pair;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DHKeysPairSupplier::Return (std::shared_ptr<i2p::crypto::DHKeys> pair)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
|
||||||
m_Queue.push (pair);
|
|
||||||
}
|
|
||||||
|
|
||||||
Transports transports;
|
|
||||||
|
|
||||||
Transports::Transports ():
|
|
||||||
m_IsOnline (true), m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
|
|
||||||
m_PeerCleanupTimer (m_Service), m_PeerTestTimer (m_Service),
|
|
||||||
m_NTCPServer (nullptr), m_SSUServer (nullptr), m_DHKeysPairSupplier (5), // 5 pre-generated keys
|
|
||||||
m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_InBandwidth (0), m_OutBandwidth (0),
|
|
||||||
m_LastInBandwidthUpdateBytes (0), m_LastOutBandwidthUpdateBytes (0), m_LastBandwidthUpdateTime (0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Transports::~Transports ()
|
|
||||||
{
|
|
||||||
Stop ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::Start (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 (const auto& address : addresses)
|
|
||||||
{
|
|
||||||
if (!address) continue;
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
if (m_SSUServer == nullptr && enableSSU)
|
|
||||||
{
|
|
||||||
if (address->host.is_v4())
|
|
||||||
m_SSUServer = new SSUServer (address->port);
|
|
||||||
else
|
|
||||||
m_SSUServer = new SSUServer (address->host, address->port);
|
|
||||||
LogPrint (eLogInfo, "Transports: Start listening UDP port ", address->port);
|
|
||||||
try {
|
|
||||||
m_SSUServer->Start ();
|
|
||||||
} catch ( std::exception & ex ) {
|
|
||||||
LogPrint(eLogError, "Transports: Failed to bind to UDP port", address->port);
|
|
||||||
delete m_SSUServer;
|
|
||||||
m_SSUServer = nullptr;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
DetectExternalIP ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Transports: SSU server already exists");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_PeerCleanupTimer.expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
|
|
||||||
m_PeerCleanupTimer.async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
|
|
||||||
m_PeerTestTimer.expires_from_now (boost::posix_time::minutes(PEER_TEST_INTERVAL));
|
|
||||||
m_PeerTestTimer.async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::Stop ()
|
|
||||||
{
|
|
||||||
m_PeerCleanupTimer.cancel ();
|
|
||||||
m_PeerTestTimer.cancel ();
|
|
||||||
m_Peers.clear ();
|
|
||||||
if (m_SSUServer)
|
|
||||||
{
|
|
||||||
m_SSUServer->Stop ();
|
|
||||||
delete m_SSUServer;
|
|
||||||
m_SSUServer = nullptr;
|
|
||||||
}
|
|
||||||
if (m_NTCPServer)
|
|
||||||
{
|
|
||||||
m_NTCPServer->Stop ();
|
|
||||||
delete m_NTCPServer;
|
|
||||||
m_NTCPServer = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_DHKeysPairSupplier.Stop ();
|
|
||||||
m_IsRunning = false;
|
|
||||||
m_Service.stop ();
|
|
||||||
if (m_Thread)
|
|
||||||
{
|
|
||||||
m_Thread->join ();
|
|
||||||
delete m_Thread;
|
|
||||||
m_Thread = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::Run ()
|
|
||||||
{
|
|
||||||
while (m_IsRunning)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_Service.run ();
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Transports: runtime exception: ", ex.what ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::UpdateBandwidth ()
|
|
||||||
{
|
|
||||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
if (m_LastBandwidthUpdateTime > 0)
|
|
||||||
{
|
|
||||||
auto delta = ts - m_LastBandwidthUpdateTime;
|
|
||||||
if (delta > 0)
|
|
||||||
{
|
|
||||||
m_InBandwidth = (m_TotalReceivedBytes - m_LastInBandwidthUpdateBytes)*1000/delta; // per second
|
|
||||||
m_OutBandwidth = (m_TotalSentBytes - m_LastOutBandwidthUpdateBytes)*1000/delta; // per second
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_LastBandwidthUpdateTime = ts;
|
|
||||||
m_LastInBandwidthUpdateBytes = m_TotalReceivedBytes;
|
|
||||||
m_LastOutBandwidthUpdateBytes = m_TotalSentBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Transports::IsBandwidthExceeded () const
|
|
||||||
{
|
|
||||||
auto limit = i2p::context.GetBandwidthLimit() * 1024; // convert to bytes
|
|
||||||
auto bw = std::max (m_InBandwidth, m_OutBandwidth);
|
|
||||||
return bw > limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
SendMessages (ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > {msg });
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs)
|
|
||||||
{
|
|
||||||
#ifdef WITH_EVENTS
|
|
||||||
EmitEvent({{"type" , "transport.sendmsg"}, {"ident", ident.ToBase64()}, {"number", std::to_string(msgs.size())}});
|
|
||||||
#endif
|
|
||||||
m_Service.post (std::bind (&Transports::PostMessages, this, ident, msgs));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::PostMessages (i2p::data::IdentHash ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > msgs)
|
|
||||||
{
|
|
||||||
if (ident == i2p::context.GetRouterInfo ().GetIdentHash ())
|
|
||||||
{
|
|
||||||
// we send it to ourself
|
|
||||||
for (auto& it: msgs)
|
|
||||||
m_LoopbackHandler.PutNextMessage (it);
|
|
||||||
m_LoopbackHandler.Flush ();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(RoutesRestricted() && ! IsRestrictedPeer(ident)) return;
|
|
||||||
auto it = m_Peers.find (ident);
|
|
||||||
if (it == m_Peers.end ())
|
|
||||||
{
|
|
||||||
bool connected = false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
auto r = netdb.FindRouter (ident);
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
|
||||||
it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, { 0, r, {},
|
|
||||||
i2p::util::GetSecondsSinceEpoch (), {} })).first;
|
|
||||||
}
|
|
||||||
connected = ConnectToPeer (ident, it->second);
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Transports: PostMessages exception:", ex.what ());
|
|
||||||
}
|
|
||||||
if (!connected) return;
|
|
||||||
}
|
|
||||||
if (!it->second.sessions.empty ())
|
|
||||||
it->second.sessions.front ()->SendI2NPMessages (msgs);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Transports::ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer)
|
|
||||||
{
|
|
||||||
if (peer.router) // we have RI already
|
|
||||||
{
|
|
||||||
if (!peer.numAttempts) // NTCP
|
|
||||||
{
|
|
||||||
peer.numAttempts++;
|
|
||||||
auto address = peer.router->GetNTCPAddress (!context.SupportsV6 ());
|
|
||||||
if (address && m_NTCPServer)
|
|
||||||
{
|
|
||||||
#if BOOST_VERSION >= 104900
|
|
||||||
if (!address->host.is_unspecified ()) // we have address now
|
|
||||||
#else
|
|
||||||
boost::system::error_code ecode;
|
|
||||||
address->host.to_string (ecode);
|
|
||||||
if (!ecode)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
if (!peer.router->UsesIntroducer () && !peer.router->IsUnreachable ())
|
|
||||||
{
|
|
||||||
auto s = std::make_shared<NTCPSession> (*m_NTCPServer, peer.router);
|
|
||||||
m_NTCPServer->Connect (address->host, address->port, s);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // we don't have address
|
|
||||||
{
|
|
||||||
if (address->addressString.length () > 0) // trying to resolve
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "Transports: Resolving NTCP ", address->addressString);
|
|
||||||
NTCPResolve (address->addressString, ident);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogDebug, "Transports: NTCP address is not present for ", i2p::data::GetIdentHashAbbreviation (ident), ", trying SSU");
|
|
||||||
}
|
|
||||||
if (peer.numAttempts == 1)// SSU
|
|
||||||
{
|
|
||||||
peer.numAttempts++;
|
|
||||||
if (m_SSUServer && peer.router->IsSSU (!context.SupportsV6 ()))
|
|
||||||
{
|
|
||||||
auto address = peer.router->GetSSUAddress (!context.SupportsV6 ());
|
|
||||||
#if BOOST_VERSION >= 104900
|
|
||||||
if (!address->host.is_unspecified ()) // we have address now
|
|
||||||
#else
|
|
||||||
boost::system::error_code ecode;
|
|
||||||
address->host.to_string (ecode);
|
|
||||||
if (!ecode)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
m_SSUServer->CreateSession (peer.router, address->host, address->port);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else // we don't have address
|
|
||||||
{
|
|
||||||
if (address->addressString.length () > 0) // trying to resolve
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "Transports: Resolving SSU ", address->addressString);
|
|
||||||
SSUResolve (address->addressString, ident);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LogPrint (eLogError, "Transports: No NTCP or SSU addresses available");
|
|
||||||
peer.Done ();
|
|
||||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
|
||||||
m_Peers.erase (ident);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else // otherwise request RI
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Transports: RouterInfo for ", ident.ToBase64 (), " not found, requested");
|
|
||||||
i2p::data::netdb.RequestDestination (ident, std::bind (
|
|
||||||
&Transports::RequestComplete, this, std::placeholders::_1, ident));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident)
|
|
||||||
{
|
|
||||||
m_Service.post (std::bind (&Transports::HandleRequestComplete, this, r, ident));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, i2p::data::IdentHash ident)
|
|
||||||
{
|
|
||||||
auto it = m_Peers.find (ident);
|
|
||||||
if (it != m_Peers.end ())
|
|
||||||
{
|
|
||||||
if (r)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "Transports: RouterInfo for ", ident.ToBase64 (), " found, Trying to connect");
|
|
||||||
it->second.router = r;
|
|
||||||
ConnectToPeer (ident, it->second);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Transports: RouterInfo not found, Failed to send messages");
|
|
||||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
|
||||||
m_Peers.erase (it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::NTCPResolve (const std::string& addr, const i2p::data::IdentHash& ident)
|
|
||||||
{
|
|
||||||
auto resolver = std::make_shared<boost::asio::ip::tcp::resolver>(m_Service);
|
|
||||||
resolver->async_resolve (boost::asio::ip::tcp::resolver::query (addr, ""),
|
|
||||||
std::bind (&Transports::HandleNTCPResolve, this,
|
|
||||||
std::placeholders::_1, std::placeholders::_2, ident, resolver));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::HandleNTCPResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
|
|
||||||
i2p::data::IdentHash ident, std::shared_ptr<boost::asio::ip::tcp::resolver> resolver)
|
|
||||||
{
|
|
||||||
auto it1 = m_Peers.find (ident);
|
|
||||||
if (it1 != m_Peers.end ())
|
|
||||||
{
|
|
||||||
auto& peer = it1->second;
|
|
||||||
if (!ecode && peer.router)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
m_Peers.erase (it1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::SSUResolve (const std::string& addr, const i2p::data::IdentHash& ident)
|
|
||||||
{
|
|
||||||
auto resolver = std::make_shared<boost::asio::ip::tcp::resolver>(m_Service);
|
|
||||||
resolver->async_resolve (boost::asio::ip::tcp::resolver::query (addr, ""),
|
|
||||||
std::bind (&Transports::HandleSSUResolve, this,
|
|
||||||
std::placeholders::_1, std::placeholders::_2, ident, resolver));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::HandleSSUResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
|
|
||||||
i2p::data::IdentHash ident, std::shared_ptr<boost::asio::ip::tcp::resolver> resolver)
|
|
||||||
{
|
|
||||||
auto it1 = m_Peers.find (ident);
|
|
||||||
if (it1 != m_Peers.end ())
|
|
||||||
{
|
|
||||||
auto& peer = it1->second;
|
|
||||||
if (!ecode && peer.router)
|
|
||||||
{
|
|
||||||
while (it != boost::asio::ip::tcp::resolver::iterator())
|
|
||||||
{
|
|
||||||
auto address = (*it).endpoint ().address ();
|
|
||||||
LogPrint (eLogDebug, "Transports: ", (*it).host_name (), " has been resolved to ", address);
|
|
||||||
if (address.is_v4 () || context.SupportsV6 ())
|
|
||||||
{
|
|
||||||
auto addr = peer.router->GetSSUAddress (); // TODO: take one we requested
|
|
||||||
if (addr)
|
|
||||||
{
|
|
||||||
m_SSUServer->CreateSession (peer.router, address, addr->port);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogInfo, "Transports: SSU ", address, " is not supported");
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LogPrint (eLogError, "Transports: Unable to resolve SSU address: ", ecode.message ());
|
|
||||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
|
||||||
m_Peers.erase (it1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::CloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
|
|
||||||
{
|
|
||||||
if (!router) return;
|
|
||||||
m_Service.post (std::bind (&Transports::PostCloseSession, this, router));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
|
|
||||||
{
|
|
||||||
auto ssuSession = m_SSUServer ? m_SSUServer->FindSession (router) : nullptr;
|
|
||||||
if (ssuSession) // try SSU first
|
|
||||||
{
|
|
||||||
m_SSUServer->DeleteSession (ssuSession);
|
|
||||||
LogPrint (eLogDebug, "Transports: SSU session closed");
|
|
||||||
}
|
|
||||||
auto ntcpSession = m_NTCPServer ? m_NTCPServer->FindNTCPSession(router->GetIdentHash()) : nullptr;
|
|
||||||
if (ntcpSession) // try deleting ntcp session too
|
|
||||||
{
|
|
||||||
ntcpSession->Terminate ();
|
|
||||||
LogPrint(eLogDebug, "Transports: NTCP session closed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::DetectExternalIP ()
|
|
||||||
{
|
|
||||||
if (RoutesRestricted())
|
|
||||||
{
|
|
||||||
LogPrint(eLogInfo, "Transports: restricted routes enabled, not detecting ip");
|
|
||||||
i2p::context.SetStatus (eRouterStatusOK);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (m_SSUServer)
|
|
||||||
{
|
|
||||||
bool nat; i2p::config::GetOption("nat", nat);
|
|
||||||
bool isv4 = i2p::context.SupportsV4 ();
|
|
||||||
if (nat && isv4)
|
|
||||||
i2p::context.SetStatus (eRouterStatusTesting);
|
|
||||||
for (int i = 0; i < 5; i++)
|
|
||||||
{
|
|
||||||
auto router = i2p::data::netdb.GetRandomPeerTestRouter (isv4); // v4 only if v4
|
|
||||||
if (router)
|
|
||||||
m_SSUServer->CreateSession (router, true, isv4); // peer test
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// if not peer test capable routers found pick any
|
|
||||||
router = i2p::data::netdb.GetRandomRouter ();
|
|
||||||
if (router && router->IsSSU ())
|
|
||||||
m_SSUServer->CreateSession (router); // no peer test
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Transports: Can't detect external IP. SSU is not available");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::PeerTest ()
|
|
||||||
{
|
|
||||||
if (RoutesRestricted() || !i2p::context.SupportsV4 ()) return;
|
|
||||||
if (m_SSUServer)
|
|
||||||
{
|
|
||||||
bool statusChanged = false;
|
|
||||||
for (int i = 0; i < 5; i++)
|
|
||||||
{
|
|
||||||
auto router = i2p::data::netdb.GetRandomPeerTestRouter (true); // v4 only
|
|
||||||
if (router)
|
|
||||||
{
|
|
||||||
if (!statusChanged)
|
|
||||||
{
|
|
||||||
statusChanged = true;
|
|
||||||
i2p::context.SetStatus (eRouterStatusTesting); // first time only
|
|
||||||
}
|
|
||||||
m_SSUServer->CreateSession (router, true, true); // peer test v4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!statusChanged)
|
|
||||||
LogPrint (eLogWarning, "Can't find routers for peer test");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<i2p::crypto::DHKeys> Transports::GetNextDHKeysPair ()
|
|
||||||
{
|
|
||||||
return m_DHKeysPairSupplier.Acquire ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::ReuseDHKeysPair (std::shared_ptr<i2p::crypto::DHKeys> pair)
|
|
||||||
{
|
|
||||||
m_DHKeysPairSupplier.Return (pair);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 ())
|
|
||||||
{
|
|
||||||
#ifdef WITH_EVENTS
|
|
||||||
EmitEvent({{"type" , "transport.connected"}, {"ident", ident.ToBase64()}, {"inbound", "false"}});
|
|
||||||
#endif
|
|
||||||
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
|
|
||||||
{
|
|
||||||
if(RoutesRestricted() && ! IsRestrictedPeer(ident)) {
|
|
||||||
// not trusted
|
|
||||||
LogPrint(eLogWarning, "Transports: closing untrusted inbound connection from ", ident.ToBase64());
|
|
||||||
session->Done();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#ifdef WITH_EVENTS
|
|
||||||
EmitEvent({{"type" , "transport.connected"}, {"ident", ident.ToBase64()}, {"inbound", "true"}});
|
|
||||||
#endif
|
|
||||||
session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); // send DatabaseStore
|
|
||||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
|
||||||
m_Peers.insert (std::make_pair (ident, Peer{ 0, nullptr, { session }, i2p::util::GetSecondsSinceEpoch (), {} }));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::PeerDisconnected (std::shared_ptr<TransportSession> session)
|
|
||||||
{
|
|
||||||
m_Service.post([session, this]()
|
|
||||||
{
|
|
||||||
auto remoteIdentity = session->GetRemoteIdentity ();
|
|
||||||
if (!remoteIdentity) return;
|
|
||||||
auto ident = remoteIdentity->GetIdentHash ();
|
|
||||||
#ifdef WITH_EVENTS
|
|
||||||
EmitEvent({{"type" , "transport.disconnected"}, {"ident", ident.ToBase64()}});
|
|
||||||
#endif
|
|
||||||
auto it = m_Peers.find (ident);
|
|
||||||
if (it != m_Peers.end ())
|
|
||||||
{
|
|
||||||
it->second.sessions.remove (session);
|
|
||||||
if (it->second.sessions.empty ()) // TODO: why?
|
|
||||||
{
|
|
||||||
if (it->second.delayedMessages.size () > 0)
|
|
||||||
ConnectToPeer (ident, it->second);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
|
||||||
m_Peers.erase (it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Transports::IsConnected (const i2p::data::IdentHash& ident) const
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
|
||||||
auto it = m_Peers.find (ident);
|
|
||||||
return it != m_Peers.end ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::HandlePeerCleanupTimer (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
for (auto it = m_Peers.begin (); it != m_Peers.end (); )
|
|
||||||
{
|
|
||||||
if (it->second.sessions.empty () && ts > it->second.creationTime + SESSION_CREATION_TIMEOUT)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Transports: Session to peer ", it->first.ToBase64 (), " has not been created in ", SESSION_CREATION_TIMEOUT, " seconds");
|
|
||||||
auto profile = i2p::data::GetRouterProfile(it->first);
|
|
||||||
if (profile)
|
|
||||||
{
|
|
||||||
profile->TunnelNonReplied();
|
|
||||||
profile->Save();
|
|
||||||
}
|
|
||||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
|
||||||
it = m_Peers.erase (it);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
UpdateBandwidth (); // TODO: use separate timer(s) for it
|
|
||||||
if (i2p::context.GetStatus () == eRouterStatusTesting) // if still testing, repeat peer test
|
|
||||||
DetectExternalIP ();
|
|
||||||
m_PeerCleanupTimer.expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
|
|
||||||
m_PeerCleanupTimer.async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::HandlePeerTestTimer (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
PeerTest ();
|
|
||||||
m_PeerTestTimer.expires_from_now (boost::posix_time::minutes(PEER_TEST_INTERVAL));
|
|
||||||
m_PeerTestTimer.async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRandomPeer () const
|
|
||||||
{
|
|
||||||
if (m_Peers.empty ()) return nullptr;
|
|
||||||
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::RestrictRoutesToFamilies(std::set<std::string> families)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_FamilyMutex);
|
|
||||||
m_TrustedFamilies.clear();
|
|
||||||
for ( const auto& fam : families )
|
|
||||||
m_TrustedFamilies.push_back(fam);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::RestrictRoutesToRouters(std::set<i2p::data::IdentHash> routers)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(m_TrustedRoutersMutex);
|
|
||||||
m_TrustedRouters.clear();
|
|
||||||
for (const auto & ri : routers )
|
|
||||||
m_TrustedRouters.push_back(ri);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Transports::RoutesRestricted() const {
|
|
||||||
std::unique_lock<std::mutex> famlock(m_FamilyMutex);
|
|
||||||
std::unique_lock<std::mutex> routerslock(m_TrustedRoutersMutex);
|
|
||||||
return m_TrustedFamilies.size() > 0 || m_TrustedRouters.size() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** XXX: if routes are not restricted this dies */
|
|
||||||
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRestrictedPeer() const
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> l(m_FamilyMutex);
|
|
||||||
std::string fam;
|
|
||||||
auto sz = m_TrustedFamilies.size();
|
|
||||||
if(sz > 1)
|
|
||||||
{
|
|
||||||
auto it = m_TrustedFamilies.begin ();
|
|
||||||
std::advance(it, rand() % sz);
|
|
||||||
fam = *it;
|
|
||||||
boost::to_lower(fam);
|
|
||||||
}
|
|
||||||
else if (sz == 1)
|
|
||||||
{
|
|
||||||
fam = m_TrustedFamilies[0];
|
|
||||||
}
|
|
||||||
if (fam.size())
|
|
||||||
return i2p::data::netdb.GetRandomRouterInFamily(fam);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_TrustedRoutersMutex);
|
|
||||||
auto sz = m_TrustedRouters.size();
|
|
||||||
if (sz)
|
|
||||||
{
|
|
||||||
if(sz == 1)
|
|
||||||
return i2p::data::netdb.FindRouter(m_TrustedRouters[0]);
|
|
||||||
auto it = m_TrustedRouters.begin();
|
|
||||||
std::advance(it, rand() % sz);
|
|
||||||
return i2p::data::netdb.FindRouter(*it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Transports::IsRestrictedPeer(const i2p::data::IdentHash & ih) const
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_TrustedRoutersMutex);
|
|
||||||
for (const auto & r : m_TrustedRouters )
|
|
||||||
if ( r == ih ) return true;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_FamilyMutex);
|
|
||||||
auto ri = i2p::data::netdb.FindRouter(ih);
|
|
||||||
for (const auto & fam : m_TrustedFamilies)
|
|
||||||
if(ri->IsFamily(fam)) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
185
Transports.h
185
Transports.h
@@ -1,185 +0,0 @@
|
|||||||
#ifndef TRANSPORTS_H__
|
|
||||||
#define TRANSPORTS_H__
|
|
||||||
|
|
||||||
#include <thread>
|
|
||||||
#include <mutex>
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <functional>
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
#include <queue>
|
|
||||||
#include <string>
|
|
||||||
#include <memory>
|
|
||||||
#include <atomic>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include "TransportSession.h"
|
|
||||||
#include "NTCPSession.h"
|
|
||||||
#include "SSU.h"
|
|
||||||
#include "RouterInfo.h"
|
|
||||||
#include "I2NPProtocol.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace transport
|
|
||||||
{
|
|
||||||
class DHKeysPairSupplier
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
DHKeysPairSupplier (int size);
|
|
||||||
~DHKeysPairSupplier ();
|
|
||||||
void Start ();
|
|
||||||
void Stop ();
|
|
||||||
std::shared_ptr<i2p::crypto::DHKeys> Acquire ();
|
|
||||||
void Return (std::shared_ptr<i2p::crypto::DHKeys> pair);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void Run ();
|
|
||||||
void CreateDHKeysPairs (int num);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
const int m_QueueSize;
|
|
||||||
std::queue<std::shared_ptr<i2p::crypto::DHKeys> > m_Queue;
|
|
||||||
|
|
||||||
bool m_IsRunning;
|
|
||||||
std::thread * m_Thread;
|
|
||||||
std::condition_variable m_Acquired;
|
|
||||||
std::mutex m_AcquiredMutex;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Peer
|
|
||||||
{
|
|
||||||
int numAttempts;
|
|
||||||
std::shared_ptr<const i2p::data::RouterInfo> router;
|
|
||||||
std::list<std::shared_ptr<TransportSession> > sessions;
|
|
||||||
uint64_t creationTime;
|
|
||||||
std::vector<std::shared_ptr<i2p::I2NPMessage> > delayedMessages;
|
|
||||||
|
|
||||||
void Done ()
|
|
||||||
{
|
|
||||||
for (auto& it: sessions)
|
|
||||||
it->Done ();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const size_t SESSION_CREATION_TIMEOUT = 10; // in seconds
|
|
||||||
const int PEER_TEST_INTERVAL = 71; // in minutes
|
|
||||||
const int MAX_NUM_DELAYED_MESSAGES = 50;
|
|
||||||
class Transports
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
Transports ();
|
|
||||||
~Transports ();
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
void SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg);
|
|
||||||
void SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs);
|
|
||||||
void CloseSession (std::shared_ptr<const i2p::data::RouterInfo> router);
|
|
||||||
|
|
||||||
void PeerConnected (std::shared_ptr<TransportSession> session);
|
|
||||||
void PeerDisconnected (std::shared_ptr<TransportSession> session);
|
|
||||||
bool IsConnected (const i2p::data::IdentHash& ident) const;
|
|
||||||
|
|
||||||
void UpdateSentBytes (uint64_t numBytes) { m_TotalSentBytes += numBytes; };
|
|
||||||
void UpdateReceivedBytes (uint64_t numBytes) { m_TotalReceivedBytes += numBytes; };
|
|
||||||
uint64_t GetTotalSentBytes () const { return m_TotalSentBytes; };
|
|
||||||
uint64_t GetTotalReceivedBytes () const { return m_TotalReceivedBytes; };
|
|
||||||
uint32_t GetInBandwidth () const { return m_InBandwidth; };
|
|
||||||
uint32_t GetOutBandwidth () const { return m_OutBandwidth; };
|
|
||||||
bool IsBandwidthExceeded () const;
|
|
||||||
size_t GetNumPeers () const { return m_Peers.size (); };
|
|
||||||
std::shared_ptr<const i2p::data::RouterInfo> GetRandomPeer () const;
|
|
||||||
|
|
||||||
/** get a trusted first hop for restricted routes */
|
|
||||||
std::shared_ptr<const i2p::data::RouterInfo> GetRestrictedPeer() const;
|
|
||||||
/** do we want to use restricted routes? */
|
|
||||||
bool RoutesRestricted() const;
|
|
||||||
/** restrict routes to use only these router families for first hops */
|
|
||||||
void RestrictRoutesToFamilies(std::set<std::string> families);
|
|
||||||
/** restrict routes to use only these routers for first hops */
|
|
||||||
void RestrictRoutesToRouters(std::set<i2p::data::IdentHash> routers);
|
|
||||||
|
|
||||||
bool IsRestrictedPeer(const i2p::data::IdentHash & ident) const;
|
|
||||||
|
|
||||||
void PeerTest ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void Run ();
|
|
||||||
void RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident);
|
|
||||||
void HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, i2p::data::IdentHash ident);
|
|
||||||
void PostMessages (i2p::data::IdentHash ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > msgs);
|
|
||||||
void PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router);
|
|
||||||
bool ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer);
|
|
||||||
void HandlePeerCleanupTimer (const boost::system::error_code& ecode);
|
|
||||||
void HandlePeerTestTimer (const boost::system::error_code& ecode);
|
|
||||||
|
|
||||||
void NTCPResolve (const std::string& addr, const i2p::data::IdentHash& ident);
|
|
||||||
void HandleNTCPResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
|
|
||||||
i2p::data::IdentHash ident, std::shared_ptr<boost::asio::ip::tcp::resolver> resolver);
|
|
||||||
void SSUResolve (const std::string& addr, const i2p::data::IdentHash& ident);
|
|
||||||
void HandleSSUResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
|
|
||||||
i2p::data::IdentHash ident, std::shared_ptr<boost::asio::ip::tcp::resolver> resolver);
|
|
||||||
|
|
||||||
void UpdateBandwidth ();
|
|
||||||
void DetectExternalIP ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
bool m_IsOnline, m_IsRunning;
|
|
||||||
std::thread * m_Thread;
|
|
||||||
boost::asio::io_service m_Service;
|
|
||||||
boost::asio::io_service::work m_Work;
|
|
||||||
boost::asio::deadline_timer m_PeerCleanupTimer, m_PeerTestTimer;
|
|
||||||
|
|
||||||
NTCPServer * m_NTCPServer;
|
|
||||||
SSUServer * m_SSUServer;
|
|
||||||
mutable std::mutex m_PeersMutex;
|
|
||||||
std::map<i2p::data::IdentHash, Peer> m_Peers;
|
|
||||||
|
|
||||||
DHKeysPairSupplier m_DHKeysPairSupplier;
|
|
||||||
|
|
||||||
std::atomic<uint64_t> m_TotalSentBytes, m_TotalReceivedBytes;
|
|
||||||
uint32_t m_InBandwidth, m_OutBandwidth; // bytes per second
|
|
||||||
uint64_t m_LastInBandwidthUpdateBytes, m_LastOutBandwidthUpdateBytes;
|
|
||||||
uint64_t m_LastBandwidthUpdateTime;
|
|
||||||
|
|
||||||
/** which router families to trust for first hops */
|
|
||||||
std::vector<std::string> m_TrustedFamilies;
|
|
||||||
mutable std::mutex m_FamilyMutex;
|
|
||||||
|
|
||||||
/** which routers for first hop to trust */
|
|
||||||
std::vector<i2p::data::IdentHash> m_TrustedRouters;
|
|
||||||
mutable std::mutex m_TrustedRoutersMutex;
|
|
||||||
|
|
||||||
i2p::I2NPMessagesHandler m_LoopbackHandler;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// for HTTP only
|
|
||||||
const NTCPServer * GetNTCPServer () const { return m_NTCPServer; };
|
|
||||||
const SSUServer * GetSSUServer () const { return m_SSUServer; };
|
|
||||||
const decltype(m_Peers)& GetPeers () const { return m_Peers; };
|
|
||||||
};
|
|
||||||
|
|
||||||
extern Transports transports;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
253
TunnelConfig.h
253
TunnelConfig.h
@@ -1,253 +0,0 @@
|
|||||||
#ifndef TUNNEL_CONFIG_H__
|
|
||||||
#define TUNNEL_CONFIG_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <sstream>
|
|
||||||
#include <vector>
|
|
||||||
#include <memory>
|
|
||||||
#include "Crypto.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "RouterContext.h"
|
|
||||||
#include "Timestamp.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace tunnel
|
|
||||||
{
|
|
||||||
struct TunnelHopConfig
|
|
||||||
{
|
|
||||||
std::shared_ptr<const i2p::data::IdentityEx> ident;
|
|
||||||
i2p::data::IdentHash nextIdent;
|
|
||||||
uint32_t tunnelID, nextTunnelID;
|
|
||||||
uint8_t layerKey[32];
|
|
||||||
uint8_t ivKey[32];
|
|
||||||
uint8_t replyKey[32];
|
|
||||||
uint8_t replyIV[16];
|
|
||||||
bool isGateway, isEndpoint;
|
|
||||||
|
|
||||||
TunnelHopConfig * next, * prev;
|
|
||||||
int recordIndex; // record # in tunnel build message
|
|
||||||
|
|
||||||
TunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r)
|
|
||||||
{
|
|
||||||
RAND_bytes (layerKey, 32);
|
|
||||||
RAND_bytes (ivKey, 32);
|
|
||||||
RAND_bytes (replyKey, 32);
|
|
||||||
RAND_bytes (replyIV, 16);
|
|
||||||
RAND_bytes ((uint8_t *)&tunnelID, 4);
|
|
||||||
isGateway = true;
|
|
||||||
isEndpoint = true;
|
|
||||||
ident = r;
|
|
||||||
//nextRouter = nullptr;
|
|
||||||
nextTunnelID = 0;
|
|
||||||
|
|
||||||
next = nullptr;
|
|
||||||
prev = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetNextIdent (const i2p::data::IdentHash& ident)
|
|
||||||
{
|
|
||||||
nextIdent = ident;
|
|
||||||
isEndpoint = false;
|
|
||||||
RAND_bytes ((uint8_t *)&nextTunnelID, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetReplyHop (uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent)
|
|
||||||
{
|
|
||||||
nextIdent = replyIdent;
|
|
||||||
nextTunnelID = replyTunnelID;
|
|
||||||
isEndpoint = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetNext (TunnelHopConfig * n)
|
|
||||||
{
|
|
||||||
next = n;
|
|
||||||
if (next)
|
|
||||||
{
|
|
||||||
next->prev = this;
|
|
||||||
next->isGateway = false;
|
|
||||||
isEndpoint = false;
|
|
||||||
nextIdent = next->ident->GetIdentHash ();
|
|
||||||
nextTunnelID = next->tunnelID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetPrev (TunnelHopConfig * p)
|
|
||||||
{
|
|
||||||
prev = p;
|
|
||||||
if (prev)
|
|
||||||
{
|
|
||||||
prev->next = this;
|
|
||||||
prev->isEndpoint = false;
|
|
||||||
isGateway = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID) const
|
|
||||||
{
|
|
||||||
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
|
|
||||||
htobe32buf (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID);
|
|
||||||
memcpy (clearText + BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET, ident->GetIdentHash (), 32);
|
|
||||||
htobe32buf (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID);
|
|
||||||
memcpy (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32);
|
|
||||||
memcpy (clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32);
|
|
||||||
memcpy (clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32);
|
|
||||||
memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32);
|
|
||||||
memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16);
|
|
||||||
uint8_t flag = 0;
|
|
||||||
if (isGateway) flag |= 0x80;
|
|
||||||
if (isEndpoint) flag |= 0x40;
|
|
||||||
clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag;
|
|
||||||
htobe32buf (clearText + BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetHoursSinceEpoch ());
|
|
||||||
htobe32buf (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID);
|
|
||||||
RAND_bytes (clearText + BUILD_REQUEST_RECORD_PADDING_OFFSET, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - BUILD_REQUEST_RECORD_PADDING_OFFSET);
|
|
||||||
i2p::crypto::ElGamalEncryption elGamalEncryption (ident->GetEncryptionPublicKey ());
|
|
||||||
elGamalEncryption.Encrypt (clearText, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET);
|
|
||||||
memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class TunnelConfig
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
TunnelConfig (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers) // inbound
|
|
||||||
{
|
|
||||||
CreatePeers (peers);
|
|
||||||
m_LastHop->SetNextIdent (i2p::context.GetIdentHash ());
|
|
||||||
}
|
|
||||||
|
|
||||||
TunnelConfig (std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers,
|
|
||||||
uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent) // outbound
|
|
||||||
{
|
|
||||||
CreatePeers (peers);
|
|
||||||
m_FirstHop->isGateway = false;
|
|
||||||
m_LastHop->SetReplyHop (replyTunnelID, replyIdent);
|
|
||||||
}
|
|
||||||
|
|
||||||
~TunnelConfig ()
|
|
||||||
{
|
|
||||||
TunnelHopConfig * hop = m_FirstHop;
|
|
||||||
|
|
||||||
while (hop)
|
|
||||||
{
|
|
||||||
auto tmp = hop;
|
|
||||||
hop = hop->next;
|
|
||||||
delete tmp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TunnelHopConfig * GetFirstHop () const
|
|
||||||
{
|
|
||||||
return m_FirstHop;
|
|
||||||
}
|
|
||||||
|
|
||||||
TunnelHopConfig * GetLastHop () const
|
|
||||||
{
|
|
||||||
return m_LastHop;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetNumHops () const
|
|
||||||
{
|
|
||||||
int num = 0;
|
|
||||||
TunnelHopConfig * hop = m_FirstHop;
|
|
||||||
while (hop)
|
|
||||||
{
|
|
||||||
num++;
|
|
||||||
hop = hop->next;
|
|
||||||
}
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsEmpty () const
|
|
||||||
{
|
|
||||||
return !m_FirstHop;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool IsInbound () const { return m_FirstHop->isGateway; }
|
|
||||||
|
|
||||||
virtual uint32_t GetTunnelID () const
|
|
||||||
{
|
|
||||||
if (!m_FirstHop) return 0;
|
|
||||||
return IsInbound () ? m_LastHop->nextTunnelID : m_FirstHop->tunnelID;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual uint32_t GetNextTunnelID () const
|
|
||||||
{
|
|
||||||
if (!m_FirstHop) return 0;
|
|
||||||
return m_FirstHop->tunnelID;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual const i2p::data::IdentHash& GetNextIdentHash () const
|
|
||||||
{
|
|
||||||
return m_FirstHop->ident->GetIdentHash ();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual const i2p::data::IdentHash& GetLastIdentHash () const
|
|
||||||
{
|
|
||||||
return m_LastHop->ident->GetIdentHash ();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetPeers () const
|
|
||||||
{
|
|
||||||
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers;
|
|
||||||
TunnelHopConfig * hop = m_FirstHop;
|
|
||||||
while (hop)
|
|
||||||
{
|
|
||||||
peers.push_back (hop->ident);
|
|
||||||
hop = hop->next;
|
|
||||||
}
|
|
||||||
return peers;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
// this constructor can't be called from outside
|
|
||||||
TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
template<class Peers>
|
|
||||||
void CreatePeers (const Peers& peers)
|
|
||||||
{
|
|
||||||
TunnelHopConfig * prev = nullptr;
|
|
||||||
for (const auto& it: peers)
|
|
||||||
{
|
|
||||||
auto hop = new TunnelHopConfig (it);
|
|
||||||
if (prev)
|
|
||||||
prev->SetNext (hop);
|
|
||||||
else
|
|
||||||
m_FirstHop = hop;
|
|
||||||
prev = hop;
|
|
||||||
}
|
|
||||||
m_LastHop = prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
TunnelHopConfig * m_FirstHop, * m_LastHop;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ZeroHopsTunnelConfig: public TunnelConfig
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
ZeroHopsTunnelConfig () { RAND_bytes ((uint8_t *)&m_TunnelID, 4);};
|
|
||||||
|
|
||||||
bool IsInbound () const { return true; }; // TODO:
|
|
||||||
uint32_t GetTunnelID () const { return m_TunnelID; };
|
|
||||||
uint32_t GetNextTunnelID () const { return m_TunnelID; };
|
|
||||||
const i2p::data::IdentHash& GetNextIdentHash () const { return i2p::context.GetIdentHash (); };
|
|
||||||
const i2p::data::IdentHash& GetLastIdentHash () const { return i2p::context.GetIdentHash (); };
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
uint32_t m_TunnelID;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,289 +0,0 @@
|
|||||||
#include "I2PEndian.h"
|
|
||||||
#include <string.h>
|
|
||||||
#include "Crypto.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "NetDb.h"
|
|
||||||
#include "I2NPProtocol.h"
|
|
||||||
#include "Transports.h"
|
|
||||||
#include "RouterContext.h"
|
|
||||||
#include "Timestamp.h"
|
|
||||||
#include "TunnelEndpoint.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace tunnel
|
|
||||||
{
|
|
||||||
TunnelEndpoint::~TunnelEndpoint ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelEndpoint::HandleDecryptedTunnelDataMsg (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
m_NumReceivedBytes += TUNNEL_DATA_MSG_SIZE;
|
|
||||||
|
|
||||||
uint8_t * decrypted = msg->GetPayload () + 20; // 4 + 16
|
|
||||||
uint8_t * zero = (uint8_t *)memchr (decrypted + 4, 0, TUNNEL_DATA_ENCRYPTED_SIZE - 4); // witout 4-byte checksum
|
|
||||||
if (zero)
|
|
||||||
{
|
|
||||||
uint8_t * fragment = zero + 1;
|
|
||||||
// verify checksum
|
|
||||||
memcpy (msg->GetPayload () + TUNNEL_DATA_MSG_SIZE, msg->GetPayload () + 4, 16); // copy iv to the end
|
|
||||||
uint8_t hash[32];
|
|
||||||
SHA256(fragment, TUNNEL_DATA_MSG_SIZE -(fragment - msg->GetPayload ()) + 16, hash); // payload + iv
|
|
||||||
if (memcmp (hash, decrypted, 4))
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "TunnelMessage: checksum verification failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// process fragments
|
|
||||||
while (fragment < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE)
|
|
||||||
{
|
|
||||||
uint8_t flag = fragment[0];
|
|
||||||
fragment++;
|
|
||||||
|
|
||||||
bool isFollowOnFragment = flag & 0x80, isLastFragment = true;
|
|
||||||
uint32_t msgID = 0;
|
|
||||||
int fragmentNum = 0;
|
|
||||||
TunnelMessageBlockEx m;
|
|
||||||
if (!isFollowOnFragment)
|
|
||||||
{
|
|
||||||
// first fragment
|
|
||||||
|
|
||||||
m.deliveryType = (TunnelDeliveryType)((flag >> 5) & 0x03);
|
|
||||||
switch (m.deliveryType)
|
|
||||||
{
|
|
||||||
case eDeliveryTypeLocal: // 0
|
|
||||||
break;
|
|
||||||
case eDeliveryTypeTunnel: // 1
|
|
||||||
m.tunnelID = bufbe32toh (fragment);
|
|
||||||
fragment += 4; // tunnelID
|
|
||||||
m.hash = i2p::data::IdentHash (fragment);
|
|
||||||
fragment += 32; // hash
|
|
||||||
break;
|
|
||||||
case eDeliveryTypeRouter: // 2
|
|
||||||
m.hash = i2p::data::IdentHash (fragment);
|
|
||||||
fragment += 32; // to hash
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isFragmented = flag & 0x08;
|
|
||||||
if (isFragmented)
|
|
||||||
{
|
|
||||||
// Message ID
|
|
||||||
msgID = bufbe32toh (fragment);
|
|
||||||
fragment += 4;
|
|
||||||
isLastFragment = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// follow on
|
|
||||||
msgID = bufbe32toh (fragment); // MessageID
|
|
||||||
fragment += 4;
|
|
||||||
fragmentNum = (flag >> 1) & 0x3F; // 6 bits
|
|
||||||
isLastFragment = flag & 0x01;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t size = bufbe16toh (fragment);
|
|
||||||
fragment += 2;
|
|
||||||
|
|
||||||
msg->offset = fragment - msg->buf;
|
|
||||||
msg->len = msg->offset + size;
|
|
||||||
if (msg->len > msg->maxLen)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "TunnelMessage: fragment is too long ", (int)size);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE)
|
|
||||||
{
|
|
||||||
// this is not last message. we have to copy it
|
|
||||||
m.data = NewI2NPShortMessage ();
|
|
||||||
m.data->offset += TUNNEL_GATEWAY_HEADER_SIZE; // reserve room for TunnelGateway header
|
|
||||||
m.data->len += TUNNEL_GATEWAY_HEADER_SIZE;
|
|
||||||
*(m.data) = *msg;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m.data = msg;
|
|
||||||
|
|
||||||
if (!isFollowOnFragment && isLastFragment)
|
|
||||||
HandleNextMessage (m);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (msgID) // msgID is presented, assume message is fragmented
|
|
||||||
{
|
|
||||||
if (!isFollowOnFragment) // create new incomlete message
|
|
||||||
{
|
|
||||||
m.nextFragmentNum = 1;
|
|
||||||
auto ret = m_IncompleteMessages.insert (std::pair<uint32_t, TunnelMessageBlockEx>(msgID, m));
|
|
||||||
if (ret.second)
|
|
||||||
HandleOutOfSequenceFragments (msgID, ret.first->second);
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "TunnelMessage: Incomplete message ", msgID, " already exists");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m.nextFragmentNum = fragmentNum;
|
|
||||||
HandleFollowOnFragment (msgID, isLastFragment, m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "TunnelMessage: Message is fragmented, but msgID is not presented");
|
|
||||||
}
|
|
||||||
|
|
||||||
fragment += size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "TunnelMessage: zero not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelEndpoint::HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m)
|
|
||||||
{
|
|
||||||
auto fragment = m.data->GetBuffer ();
|
|
||||||
auto size = m.data->GetLength ();
|
|
||||||
auto it = m_IncompleteMessages.find (msgID);
|
|
||||||
if (it != m_IncompleteMessages.end())
|
|
||||||
{
|
|
||||||
auto& msg = it->second;
|
|
||||||
if (m.nextFragmentNum == msg.nextFragmentNum)
|
|
||||||
{
|
|
||||||
if (msg.data->len + size < I2NP_MAX_MESSAGE_SIZE) // check if message is not too long
|
|
||||||
{
|
|
||||||
if (msg.data->len + size > msg.data->maxLen)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "TunnelMessage: I2NP message size ", msg.data->maxLen, " is not enough");
|
|
||||||
auto newMsg = NewI2NPMessage ();
|
|
||||||
*newMsg = *(msg.data);
|
|
||||||
msg.data = newMsg;
|
|
||||||
}
|
|
||||||
if (msg.data->Concat (fragment, size) < size) // concatenate fragment
|
|
||||||
LogPrint (eLogError, "TunnelMessage: I2NP buffer overflow ", msg.data->maxLen);
|
|
||||||
if (isLastFragment)
|
|
||||||
{
|
|
||||||
// message complete
|
|
||||||
HandleNextMessage (msg);
|
|
||||||
m_IncompleteMessages.erase (it);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
msg.nextFragmentNum++;
|
|
||||||
HandleOutOfSequenceFragments (msgID, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "TunnelMessage: Fragment ", m.nextFragmentNum, " of message ", msgID, "exceeds max I2NP message size, message dropped");
|
|
||||||
m_IncompleteMessages.erase (it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "TunnelMessage: Unexpected fragment ", (int)m.nextFragmentNum, " instead ", (int)msg.nextFragmentNum, " of message ", msgID, ", saved");
|
|
||||||
AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "TunnelMessage: First fragment of message ", msgID, " not found, saved");
|
|
||||||
AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelEndpoint::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr<I2NPMessage> data)
|
|
||||||
{
|
|
||||||
if (!m_OutOfSequenceFragments.insert ({{msgID, fragmentNum}, {isLastFragment, data, i2p::util::GetMillisecondsSinceEpoch () }}).second)
|
|
||||||
LogPrint (eLogInfo, "TunnelMessage: duplicate out-of-sequence fragment ", fragmentNum, " of message ", msgID);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelEndpoint::HandleOutOfSequenceFragments (uint32_t msgID, TunnelMessageBlockEx& msg)
|
|
||||||
{
|
|
||||||
while (ConcatNextOutOfSequenceFragment (msgID, msg))
|
|
||||||
{
|
|
||||||
if (!msg.nextFragmentNum) // message complete
|
|
||||||
{
|
|
||||||
HandleNextMessage (msg);
|
|
||||||
m_IncompleteMessages.erase (msgID);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TunnelEndpoint::ConcatNextOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg)
|
|
||||||
{
|
|
||||||
auto it = m_OutOfSequenceFragments.find ({msgID, msg.nextFragmentNum});
|
|
||||||
if (it != m_OutOfSequenceFragments.end ())
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "TunnelMessage: Out-of-sequence fragment ", (int)msg.nextFragmentNum, " of message ", msgID, " found");
|
|
||||||
size_t size = it->second.data->GetLength ();
|
|
||||||
if (msg.data->len + size > msg.data->maxLen)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "TunnelMessage: Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough");
|
|
||||||
auto newMsg = NewI2NPMessage ();
|
|
||||||
*newMsg = *(msg.data);
|
|
||||||
msg.data = newMsg;
|
|
||||||
}
|
|
||||||
if (msg.data->Concat (it->second.data->GetBuffer (), size) < size) // concatenate out-of-sync fragment
|
|
||||||
LogPrint (eLogError, "TunnelMessage: Tunnel endpoint I2NP buffer overflow ", msg.data->maxLen);
|
|
||||||
if (it->second.isLastFragment)
|
|
||||||
// message complete
|
|
||||||
msg.nextFragmentNum = 0;
|
|
||||||
else
|
|
||||||
msg.nextFragmentNum++;
|
|
||||||
m_OutOfSequenceFragments.erase (it);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelEndpoint::HandleNextMessage (const TunnelMessageBlock& msg)
|
|
||||||
{
|
|
||||||
if (!m_IsInbound && msg.data->IsExpired ())
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "TunnelMessage: message expired");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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) &&
|
|
||||||
!m_IsInbound && msg.deliveryType != eDeliveryTypeLocal)
|
|
||||||
i2p::data::netdb.PostI2NPMsg (CopyI2NPMessage (msg.data));
|
|
||||||
|
|
||||||
switch (msg.deliveryType)
|
|
||||||
{
|
|
||||||
case eDeliveryTypeLocal:
|
|
||||||
i2p::HandleI2NPMessage (msg.data);
|
|
||||||
break;
|
|
||||||
case eDeliveryTypeTunnel:
|
|
||||||
if (!m_IsInbound) // outbound transit tunnel
|
|
||||||
i2p::transport::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data));
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "TunnelMessage: Delivery type 'tunnel' arrived from an inbound tunnel, dropped");
|
|
||||||
break;
|
|
||||||
case eDeliveryTypeRouter:
|
|
||||||
if (!m_IsInbound) // outbound transit tunnel
|
|
||||||
i2p::transport::transports.SendMessage (msg.hash, msg.data);
|
|
||||||
else // we shouldn't send this message. possible leakage
|
|
||||||
LogPrint (eLogError, "TunnelMessage: Delivery type 'router' arrived from an inbound tunnel, dropped");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LogPrint (eLogError, "TunnelMessage: Unknown delivery type ", (int)msg.deliveryType);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelEndpoint::Cleanup ()
|
|
||||||
{
|
|
||||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
// out-of-sequence fragments
|
|
||||||
for (auto it = m_OutOfSequenceFragments.begin (); it != m_OutOfSequenceFragments.end ();)
|
|
||||||
{
|
|
||||||
if (ts > it->second.receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT)
|
|
||||||
it = m_OutOfSequenceFragments.erase (it);
|
|
||||||
else
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
#ifndef TUNNEL_ENDPOINT_H__
|
|
||||||
#define TUNNEL_ENDPOINT_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include "I2NPProtocol.h"
|
|
||||||
#include "TunnelBase.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace tunnel
|
|
||||||
{
|
|
||||||
class TunnelEndpoint
|
|
||||||
{
|
|
||||||
struct TunnelMessageBlockEx: public TunnelMessageBlock
|
|
||||||
{
|
|
||||||
uint8_t nextFragmentNum;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Fragment
|
|
||||||
{
|
|
||||||
bool isLastFragment;
|
|
||||||
std::shared_ptr<I2NPMessage> data;
|
|
||||||
uint64_t receiveTime; // milliseconds since epoch
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0) {};
|
|
||||||
~TunnelEndpoint ();
|
|
||||||
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
|
|
||||||
void Cleanup ();
|
|
||||||
|
|
||||||
void HandleDecryptedTunnelDataMsg (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m);
|
|
||||||
void HandleNextMessage (const TunnelMessageBlock& msg);
|
|
||||||
|
|
||||||
void AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr<I2NPMessage> data);
|
|
||||||
bool ConcatNextOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg); // true if something added
|
|
||||||
void HandleOutOfSequenceFragments (uint32_t msgID, TunnelMessageBlockEx& msg);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::map<uint32_t, TunnelMessageBlockEx> m_IncompleteMessages;
|
|
||||||
std::map<std::pair<uint32_t, uint8_t>, Fragment> m_OutOfSequenceFragments; // (msgID, fragment#)->fragment
|
|
||||||
bool m_IsInbound;
|
|
||||||
size_t m_NumReceivedBytes;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
583
TunnelPool.cpp
583
TunnelPool.cpp
@@ -1,583 +0,0 @@
|
|||||||
#include <algorithm>
|
|
||||||
#include "I2PEndian.h"
|
|
||||||
#include "Crypto.h"
|
|
||||||
#include "Tunnel.h"
|
|
||||||
#include "NetDb.h"
|
|
||||||
#include "Timestamp.h"
|
|
||||||
#include "Garlic.h"
|
|
||||||
#include "Transports.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "Tunnel.h"
|
|
||||||
#include "TunnelPool.h"
|
|
||||||
#include "Destination.h"
|
|
||||||
#ifdef WITH_EVENTS
|
|
||||||
#include "Event.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace tunnel
|
|
||||||
{
|
|
||||||
|
|
||||||
TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels):
|
|
||||||
m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops),
|
|
||||||
m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), m_IsActive (true),
|
|
||||||
m_CustomPeerSelector(nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TunnelPool::~TunnelPool ()
|
|
||||||
{
|
|
||||||
DetachTunnels ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelPool::SetExplicitPeers (std::shared_ptr<std::vector<i2p::data::IdentHash> > explicitPeers)
|
|
||||||
{
|
|
||||||
m_ExplicitPeers = explicitPeers;
|
|
||||||
if (m_ExplicitPeers)
|
|
||||||
{
|
|
||||||
int size = m_ExplicitPeers->size ();
|
|
||||||
if (m_NumInboundHops > size)
|
|
||||||
{
|
|
||||||
m_NumInboundHops = size;
|
|
||||||
LogPrint (eLogInfo, "Tunnels: Inbound tunnel length has beed adjusted to ", size, " for explicit peers");
|
|
||||||
}
|
|
||||||
if (m_NumOutboundHops > size)
|
|
||||||
{
|
|
||||||
m_NumOutboundHops = size;
|
|
||||||
LogPrint (eLogInfo, "Tunnels: Outbound tunnel length has beed adjusted to ", size, " for explicit peers");
|
|
||||||
}
|
|
||||||
m_NumInboundTunnels = 1;
|
|
||||||
m_NumOutboundTunnels = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelPool::DetachTunnels ()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
|
||||||
for (auto& it: m_InboundTunnels)
|
|
||||||
it->SetTunnelPool (nullptr);
|
|
||||||
m_InboundTunnels.clear ();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
|
||||||
for (auto& it: m_OutboundTunnels)
|
|
||||||
it->SetTunnelPool (nullptr);
|
|
||||||
m_OutboundTunnels.clear ();
|
|
||||||
}
|
|
||||||
m_Tests.clear ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelPool::TunnelCreated (std::shared_ptr<InboundTunnel> createdTunnel)
|
|
||||||
{
|
|
||||||
if (!m_IsActive) return;
|
|
||||||
{
|
|
||||||
#ifdef WITH_EVENTS
|
|
||||||
EmitTunnelEvent("tunnels.created", createdTunnel);
|
|
||||||
#endif
|
|
||||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
|
||||||
m_InboundTunnels.insert (createdTunnel);
|
|
||||||
}
|
|
||||||
if (m_LocalDestination)
|
|
||||||
m_LocalDestination->SetLeaseSetUpdated ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelPool::TunnelExpired (std::shared_ptr<InboundTunnel> expiredTunnel)
|
|
||||||
{
|
|
||||||
if (expiredTunnel)
|
|
||||||
{
|
|
||||||
#ifdef WITH_EVENTS
|
|
||||||
EmitTunnelEvent("tunnels.expired", expiredTunnel);
|
|
||||||
#endif
|
|
||||||
expiredTunnel->SetTunnelPool (nullptr);
|
|
||||||
for (auto& it: m_Tests)
|
|
||||||
if (it.second.second == expiredTunnel) it.second.second = nullptr;
|
|
||||||
|
|
||||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
|
||||||
m_InboundTunnels.erase (expiredTunnel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelPool::TunnelCreated (std::shared_ptr<OutboundTunnel> createdTunnel)
|
|
||||||
{
|
|
||||||
if (!m_IsActive) return;
|
|
||||||
{
|
|
||||||
#ifdef WITH_EVENTS
|
|
||||||
EmitTunnelEvent("tunnels.created", createdTunnel);
|
|
||||||
#endif
|
|
||||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
|
||||||
m_OutboundTunnels.insert (createdTunnel);
|
|
||||||
}
|
|
||||||
//CreatePairedInboundTunnel (createdTunnel);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelPool::TunnelExpired (std::shared_ptr<OutboundTunnel> expiredTunnel)
|
|
||||||
{
|
|
||||||
if (expiredTunnel)
|
|
||||||
{
|
|
||||||
#ifdef WITH_EVENTS
|
|
||||||
EmitTunnelEvent("tunnels.expired", expiredTunnel);
|
|
||||||
#endif
|
|
||||||
expiredTunnel->SetTunnelPool (nullptr);
|
|
||||||
for (auto& it: m_Tests)
|
|
||||||
if (it.second.first == expiredTunnel) it.second.first = nullptr;
|
|
||||||
|
|
||||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
|
||||||
m_OutboundTunnels.erase (expiredTunnel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<InboundTunnel> > TunnelPool::GetInboundTunnels (int num) const
|
|
||||||
{
|
|
||||||
std::vector<std::shared_ptr<InboundTunnel> > v;
|
|
||||||
int i = 0;
|
|
||||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
|
||||||
for (const auto& it : m_InboundTunnels)
|
|
||||||
{
|
|
||||||
if (i >= num) break;
|
|
||||||
if (it->IsEstablished ())
|
|
||||||
{
|
|
||||||
v.push_back (it);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<OutboundTunnel> TunnelPool::GetNextOutboundTunnel (std::shared_ptr<OutboundTunnel> excluded) const
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
|
||||||
return GetNextTunnel (m_OutboundTunnels, excluded);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<InboundTunnel> TunnelPool::GetNextInboundTunnel (std::shared_ptr<InboundTunnel> excluded) const
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
|
||||||
return GetNextTunnel (m_InboundTunnels, excluded);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class TTunnels>
|
|
||||||
typename TTunnels::value_type TunnelPool::GetNextTunnel (TTunnels& tunnels, typename TTunnels::value_type excluded) const
|
|
||||||
{
|
|
||||||
if (tunnels.empty ()) return nullptr;
|
|
||||||
uint32_t ind = rand () % (tunnels.size ()/2 + 1), i = 0;
|
|
||||||
typename TTunnels::value_type tunnel = nullptr;
|
|
||||||
for (const auto& it: tunnels)
|
|
||||||
{
|
|
||||||
if (it->IsEstablished () && it != excluded)
|
|
||||||
{
|
|
||||||
if(HasLatencyRequirement() && it->LatencyIsKnown() && !it->LatencyFitsRange(m_MinLatency, m_MaxLatency)) {
|
|
||||||
i ++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
tunnel = it;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
if (i > ind && tunnel) break;
|
|
||||||
}
|
|
||||||
if(HasLatencyRequirement() && !tunnel) {
|
|
||||||
ind = rand () % (tunnels.size ()/2 + 1), i = 0;
|
|
||||||
for (const auto& it: tunnels)
|
|
||||||
{
|
|
||||||
if (it->IsEstablished () && it != excluded)
|
|
||||||
{
|
|
||||||
tunnel = it;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
if (i > ind && tunnel) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!tunnel && excluded && excluded->IsEstablished ()) tunnel = excluded;
|
|
||||||
return tunnel;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<OutboundTunnel> TunnelPool::GetNewOutboundTunnel (std::shared_ptr<OutboundTunnel> old) const
|
|
||||||
{
|
|
||||||
if (old && old->IsEstablished ()) return old;
|
|
||||||
std::shared_ptr<OutboundTunnel> tunnel;
|
|
||||||
if (old)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
|
||||||
for (const auto& it: m_OutboundTunnels)
|
|
||||||
if (it->IsEstablished () && old->GetEndpointIdentHash () == it->GetEndpointIdentHash ())
|
|
||||||
{
|
|
||||||
tunnel = it;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tunnel)
|
|
||||||
tunnel = GetNextOutboundTunnel ();
|
|
||||||
return tunnel;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelPool::CreateTunnels ()
|
|
||||||
{
|
|
||||||
int num = 0;
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
|
||||||
for (const auto& it : m_OutboundTunnels)
|
|
||||||
if (it->IsEstablished ()) num++;
|
|
||||||
}
|
|
||||||
for (int i = num; i < m_NumOutboundTunnels; i++)
|
|
||||||
CreateOutboundTunnel ();
|
|
||||||
|
|
||||||
num = 0;
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
|
||||||
for (const auto& it : m_InboundTunnels)
|
|
||||||
if (it->IsEstablished ()) num++;
|
|
||||||
}
|
|
||||||
for (int i = num; i < m_NumInboundTunnels; i++)
|
|
||||||
CreateInboundTunnel ();
|
|
||||||
|
|
||||||
if (num < m_NumInboundTunnels && m_NumInboundHops <= 0 && m_LocalDestination) // zero hops IB
|
|
||||||
m_LocalDestination->SetLeaseSetUpdated (); // update LeaseSet immediately
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelPool::TestTunnels ()
|
|
||||||
{
|
|
||||||
decltype(m_Tests) tests;
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_TestsMutex);
|
|
||||||
tests.swap(m_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
|
|
||||||
if (it.second.first)
|
|
||||||
{
|
|
||||||
if (it.second.first->GetState () == eTunnelStateTestFailed)
|
|
||||||
{
|
|
||||||
it.second.first->SetState (eTunnelStateFailed);
|
|
||||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
|
||||||
m_OutboundTunnels.erase (it.second.first);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
it.second.first->SetState (eTunnelStateTestFailed);
|
|
||||||
}
|
|
||||||
if (it.second.second)
|
|
||||||
{
|
|
||||||
if (it.second.second->GetState () == eTunnelStateTestFailed)
|
|
||||||
{
|
|
||||||
it.second.second->SetState (eTunnelStateFailed);
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
|
||||||
m_InboundTunnels.erase (it.second.second);
|
|
||||||
}
|
|
||||||
if (m_LocalDestination)
|
|
||||||
m_LocalDestination->SetLeaseSetUpdated ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
it.second.second->SetState (eTunnelStateTestFailed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// new tests
|
|
||||||
auto it1 = m_OutboundTunnels.begin ();
|
|
||||||
auto it2 = m_InboundTunnels.begin ();
|
|
||||||
while (it1 != m_OutboundTunnels.end () && it2 != m_InboundTunnels.end ())
|
|
||||||
{
|
|
||||||
bool failed = false;
|
|
||||||
if ((*it1)->IsFailed ())
|
|
||||||
{
|
|
||||||
failed = true;
|
|
||||||
++it1;
|
|
||||||
}
|
|
||||||
if ((*it2)->IsFailed ())
|
|
||||||
{
|
|
||||||
failed = true;
|
|
||||||
++it2;
|
|
||||||
}
|
|
||||||
if (!failed)
|
|
||||||
{
|
|
||||||
uint32_t msgID;
|
|
||||||
RAND_bytes ((uint8_t *)&msgID, 4);
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_TestsMutex);
|
|
||||||
m_Tests[msgID] = std::make_pair (*it1, *it2);
|
|
||||||
}
|
|
||||||
(*it1)->SendTunnelDataMsg ((*it2)->GetNextIdentHash (), (*it2)->GetNextTunnelID (),
|
|
||||||
CreateDeliveryStatusMsg (msgID));
|
|
||||||
++it1; ++it2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelPool::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
if (m_LocalDestination)
|
|
||||||
m_LocalDestination->ProcessGarlicMessage (msg);
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "Tunnels: local destination doesn't exist, dropped");
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelPool::ProcessDeliveryStatus (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
const uint8_t * buf = msg->GetPayload ();
|
|
||||||
uint32_t msgID = bufbe32toh (buf);
|
|
||||||
buf += 4;
|
|
||||||
uint64_t timestamp = bufbe64toh (buf);
|
|
||||||
|
|
||||||
decltype(m_Tests)::mapped_type test;
|
|
||||||
bool found = false;
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_TestsMutex);
|
|
||||||
auto it = m_Tests.find (msgID);
|
|
||||||
if (it != m_Tests.end ())
|
|
||||||
{
|
|
||||||
found = true;
|
|
||||||
test = it->second;
|
|
||||||
m_Tests.erase (it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (found)
|
|
||||||
{
|
|
||||||
// restore from test failed state if any
|
|
||||||
if (test.first->GetState () == eTunnelStateTestFailed)
|
|
||||||
test.first->SetState (eTunnelStateEstablished);
|
|
||||||
if (test.second->GetState () == eTunnelStateTestFailed)
|
|
||||||
test.second->SetState (eTunnelStateEstablished);
|
|
||||||
uint64_t dlt = i2p::util::GetMillisecondsSinceEpoch () - timestamp;
|
|
||||||
LogPrint (eLogDebug, "Tunnels: test of ", msgID, " successful. ", dlt, " milliseconds");
|
|
||||||
// update latency
|
|
||||||
uint64_t latency = dlt / 2;
|
|
||||||
test.first->AddLatencySample(latency);
|
|
||||||
test.second->AddLatencySample(latency);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (m_LocalDestination)
|
|
||||||
m_LocalDestination->ProcessDeliveryStatusMessage (msg);
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "Tunnels: Local destination doesn't exist, dropped");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<const i2p::data::RouterInfo> TunnelPool::SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop) const
|
|
||||||
{
|
|
||||||
bool isExploratory = (i2p::tunnel::tunnels.GetExploratoryPool () == shared_from_this ());
|
|
||||||
auto hop = isExploratory ? i2p::data::netdb.GetRandomRouter (prevHop):
|
|
||||||
i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop);
|
|
||||||
|
|
||||||
if (!hop || hop->GetProfile ()->IsBad ())
|
|
||||||
hop = i2p::data::netdb.GetRandomRouter (prevHop);
|
|
||||||
return hop;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TunnelPool::SelectPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers, bool isInbound)
|
|
||||||
{
|
|
||||||
int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops;
|
|
||||||
// peers is empty
|
|
||||||
if (numHops <= 0) return true;
|
|
||||||
// custom peer selector in use ?
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex);
|
|
||||||
if (m_CustomPeerSelector)
|
|
||||||
return m_CustomPeerSelector->SelectPeers(peers, numHops, isInbound);
|
|
||||||
}
|
|
||||||
// explicit peers in use
|
|
||||||
if (m_ExplicitPeers) return SelectExplicitPeers (peers, isInbound);
|
|
||||||
|
|
||||||
auto prevHop = i2p::context.GetSharedRouterInfo ();
|
|
||||||
if(i2p::transport::transports.RoutesRestricted())
|
|
||||||
{
|
|
||||||
/** 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 ())
|
|
||||||
{
|
|
||||||
prevHop = r;
|
|
||||||
peers.push_back (r->GetRouterIdentity ());
|
|
||||||
numHops--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int i = 0; i < numHops; i++ )
|
|
||||||
{
|
|
||||||
auto hop = SelectNextHop (prevHop);
|
|
||||||
if (!hop)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Tunnels: Can't select next hop for ", prevHop->GetIdentHashBase64 ());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
prevHop = hop;
|
|
||||||
peers.push_back (hop->GetRouterIdentity ());
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TunnelPool::SelectExplicitPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers, bool isInbound)
|
|
||||||
{
|
|
||||||
int size = m_ExplicitPeers->size ();
|
|
||||||
std::vector<int> peerIndicies;
|
|
||||||
for (int i = 0; i < size; i++) peerIndicies.push_back(i);
|
|
||||||
std::random_shuffle (peerIndicies.begin(), peerIndicies.end());
|
|
||||||
|
|
||||||
int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops;
|
|
||||||
for (int i = 0; i < numHops; i++)
|
|
||||||
{
|
|
||||||
auto& ident = (*m_ExplicitPeers)[peerIndicies[i]];
|
|
||||||
auto r = i2p::data::netdb.FindRouter (ident);
|
|
||||||
if (r)
|
|
||||||
peers.push_back (r->GetRouterIdentity ());
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Tunnels: Can't find router for ", ident.ToBase64 ());
|
|
||||||
i2p::data::netdb.RequestDestination (ident);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelPool::CreateInboundTunnel ()
|
|
||||||
{
|
|
||||||
auto outboundTunnel = GetNextOutboundTunnel ();
|
|
||||||
if (!outboundTunnel)
|
|
||||||
outboundTunnel = tunnels.GetNextOutboundTunnel ();
|
|
||||||
LogPrint (eLogDebug, "Tunnels: Creating destination inbound tunnel...");
|
|
||||||
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers;
|
|
||||||
if (SelectPeers (peers, true))
|
|
||||||
{
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelPool::RecreateInboundTunnel (std::shared_ptr<InboundTunnel> tunnel)
|
|
||||||
{
|
|
||||||
auto outboundTunnel = GetNextOutboundTunnel ();
|
|
||||||
if (!outboundTunnel)
|
|
||||||
outboundTunnel = tunnels.GetNextOutboundTunnel ();
|
|
||||||
LogPrint (eLogDebug, "Tunnels: Re-creating destination inbound tunnel...");
|
|
||||||
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 ()
|
|
||||||
{
|
|
||||||
auto inboundTunnel = GetNextInboundTunnel ();
|
|
||||||
if (!inboundTunnel)
|
|
||||||
inboundTunnel = tunnels.GetNextInboundTunnel ();
|
|
||||||
if (inboundTunnel)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "Tunnels: Creating destination outbound tunnel...");
|
|
||||||
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers;
|
|
||||||
if (SelectPeers (peers, false))
|
|
||||||
{
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Tunnels: Can't create outbound tunnel, no inbound tunnels found");
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelPool::RecreateOutboundTunnel (std::shared_ptr<OutboundTunnel> tunnel)
|
|
||||||
{
|
|
||||||
auto inboundTunnel = GetNextInboundTunnel ();
|
|
||||||
if (!inboundTunnel)
|
|
||||||
inboundTunnel = tunnels.GetNextInboundTunnel ();
|
|
||||||
if (inboundTunnel)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "Tunnels: Re-creating destination outbound tunnel...");
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelPool::CreatePairedInboundTunnel (std::shared_ptr<OutboundTunnel> outboundTunnel)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "Tunnels: Creating paired inbound tunnel...");
|
|
||||||
auto tunnel = tunnels.CreateInboundTunnel (std::make_shared<TunnelConfig>(outboundTunnel->GetInvertedPeers ()), outboundTunnel);
|
|
||||||
tunnel->SetTunnelPool (shared_from_this ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelPool::SetCustomPeerSelector(TunnelPeerSelector selector)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex);
|
|
||||||
m_CustomPeerSelector = selector;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelPool::UnsetCustomPeerSelector()
|
|
||||||
{
|
|
||||||
SetCustomPeerSelector(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TunnelPool::HasCustomPeerSelector()
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex);
|
|
||||||
return m_CustomPeerSelector != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<InboundTunnel> TunnelPool::GetLowestLatencyInboundTunnel(std::shared_ptr<InboundTunnel> exclude) const
|
|
||||||
{
|
|
||||||
std::shared_ptr<InboundTunnel> tun = nullptr;
|
|
||||||
std::unique_lock<std::mutex> lock(m_InboundTunnelsMutex);
|
|
||||||
uint64_t min = 1000000;
|
|
||||||
for (const auto & itr : m_InboundTunnels) {
|
|
||||||
if(!itr->LatencyIsKnown()) continue;
|
|
||||||
auto l = itr->GetMeanLatency();
|
|
||||||
if (l >= min) continue;
|
|
||||||
tun = itr;
|
|
||||||
if(tun == exclude) continue;
|
|
||||||
min = l;
|
|
||||||
}
|
|
||||||
return tun;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<OutboundTunnel> TunnelPool::GetLowestLatencyOutboundTunnel(std::shared_ptr<OutboundTunnel> exclude) const
|
|
||||||
{
|
|
||||||
std::shared_ptr<OutboundTunnel> tun = nullptr;
|
|
||||||
std::unique_lock<std::mutex> lock(m_OutboundTunnelsMutex);
|
|
||||||
uint64_t min = 1000000;
|
|
||||||
for (const auto & itr : m_OutboundTunnels) {
|
|
||||||
if(!itr->LatencyIsKnown()) continue;
|
|
||||||
auto l = itr->GetMeanLatency();
|
|
||||||
if (l >= min) continue;
|
|
||||||
tun = itr;
|
|
||||||
if(tun == exclude) continue;
|
|
||||||
min = l;
|
|
||||||
}
|
|
||||||
return tun;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
205
UPnP.cpp
205
UPnP.cpp
@@ -1,205 +0,0 @@
|
|||||||
#ifdef USE_UPNP
|
|
||||||
#include <string>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#include <boost/thread/thread.hpp>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include <boost/bind.hpp>
|
|
||||||
|
|
||||||
#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>
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace transport
|
|
||||||
{
|
|
||||||
UPnP::UPnP () : m_IsRunning(false), m_Thread (nullptr), m_Timer (m_Service)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void UPnP::Stop ()
|
|
||||||
{
|
|
||||||
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()
|
|
||||||
{
|
|
||||||
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 ()
|
|
||||||
{
|
|
||||||
while (m_IsRunning)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_Service.run ();
|
|
||||||
// Discover failed
|
|
||||||
break; // terminate the thread
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "UPnP: runtime exception: ", ex.what ());
|
|
||||||
PortMapping ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void UPnP::Discover ()
|
|
||||||
{
|
|
||||||
int nerror = 0;
|
|
||||||
#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_GetValidIGD (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr));
|
|
||||||
if (r == 1)
|
|
||||||
{
|
|
||||||
r = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
|
|
||||||
if(r != UPNPCOMMAND_SUCCESS)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "UPnP: UPNP_GetExternalIPAddress() returned ", r);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!m_externalIPAddress[0])
|
|
||||||
{
|
|
||||||
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::PortMapping ()
|
|
||||||
{
|
|
||||||
const auto& a = context.GetRouterInfo().GetAddresses();
|
|
||||||
for (const auto& address : a)
|
|
||||||
{
|
|
||||||
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; 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)
|
|
||||||
{
|
|
||||||
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 (std::shared_ptr<i2p::data::RouterInfo::Address> address)
|
|
||||||
{
|
|
||||||
std::string strType (GetProto (address)), strPort (std::to_string (address->port));
|
|
||||||
int r = 0;
|
|
||||||
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 ()
|
|
||||||
{
|
|
||||||
freeUPNPDevlist (m_Devlist);
|
|
||||||
m_Devlist = 0;
|
|
||||||
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";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else /* USE_UPNP */
|
|
||||||
namespace i2p {
|
|
||||||
namespace transport {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif /* USE_UPNP */
|
|
||||||
79
UPnP.h
79
UPnP.h
@@ -1,79 +0,0 @@
|
|||||||
#ifndef __UPNP_H__
|
|
||||||
#define __UPNP_H__
|
|
||||||
|
|
||||||
#ifdef USE_UPNP
|
|
||||||
#include <string>
|
|
||||||
#include <thread>
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <mutex>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <miniupnpc/miniwget.h>
|
|
||||||
#include <miniupnpc/miniupnpc.h>
|
|
||||||
#include <miniupnpc/upnpcommands.h>
|
|
||||||
#include <miniupnpc/upnperrors.h>
|
|
||||||
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace transport
|
|
||||||
{
|
|
||||||
class UPnP
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
UPnP ();
|
|
||||||
~UPnP ();
|
|
||||||
void Close ();
|
|
||||||
|
|
||||||
void Start ();
|
|
||||||
void Stop ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
// For miniupnpc
|
|
||||||
char * m_MulticastIf = 0;
|
|
||||||
char * m_Minissdpdpath = 0;
|
|
||||||
struct UPNPDev * m_Devlist = 0;
|
|
||||||
char m_NetworkAddr[64];
|
|
||||||
char m_externalIPAddress[40];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#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__
|
|
||||||
137
Websocket.cpp
137
Websocket.cpp
@@ -1,137 +0,0 @@
|
|||||||
#include "Websocket.h"
|
|
||||||
#include "Log.h"
|
|
||||||
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include <websocketpp/config/asio_no_tls.hpp>
|
|
||||||
#include <websocketpp/server.hpp>
|
|
||||||
#include <boost/property_tree/ini_parser.hpp>
|
|
||||||
#define GCC47_BOOST149 ((BOOST_VERSION == 104900) && (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7))
|
|
||||||
#if !GCC47_BOOST149
|
|
||||||
#include <boost/property_tree/json_parser.hpp>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace event
|
|
||||||
{
|
|
||||||
|
|
||||||
typedef websocketpp::server<websocketpp::config::asio> ServerImpl;
|
|
||||||
typedef websocketpp::connection_hdl ServerConn;
|
|
||||||
|
|
||||||
class WebsocketServerImpl : public EventListener
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
typedef ServerImpl::message_ptr MessagePtr;
|
|
||||||
public:
|
|
||||||
|
|
||||||
WebsocketServerImpl(const std::string & addr, int port) : m_run(false), m_thread(nullptr)
|
|
||||||
{
|
|
||||||
m_server.init_asio();
|
|
||||||
m_server.set_open_handler(std::bind(&WebsocketServerImpl::ConnOpened, this, std::placeholders::_1));
|
|
||||||
m_server.set_close_handler(std::bind(&WebsocketServerImpl::ConnClosed, this, std::placeholders::_1));
|
|
||||||
m_server.set_message_handler(std::bind(&WebsocketServerImpl::OnConnMessage, this, std::placeholders::_1, std::placeholders::_2));
|
|
||||||
|
|
||||||
m_server.listen(boost::asio::ip::address::from_string(addr), port);
|
|
||||||
}
|
|
||||||
|
|
||||||
~WebsocketServerImpl()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Start() {
|
|
||||||
m_run = true;
|
|
||||||
m_server.start_accept();
|
|
||||||
m_thread = new std::thread([&] () {
|
|
||||||
while(m_run) {
|
|
||||||
try {
|
|
||||||
m_server.run();
|
|
||||||
} catch (std::exception & e ) {
|
|
||||||
LogPrint(eLogError, "Websocket server: ", e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Stop() {
|
|
||||||
m_run = false;
|
|
||||||
m_server.stop();
|
|
||||||
if(m_thread) {
|
|
||||||
m_thread->join();
|
|
||||||
delete m_thread;
|
|
||||||
}
|
|
||||||
m_thread = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConnOpened(ServerConn c)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_connsMutex);
|
|
||||||
m_conns.insert(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConnClosed(ServerConn c)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_connsMutex);
|
|
||||||
m_conns.erase(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnConnMessage(ServerConn conn, ServerImpl::message_ptr msg)
|
|
||||||
{
|
|
||||||
(void) conn;
|
|
||||||
(void) msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleEvent(const EventType & ev)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_connsMutex);
|
|
||||||
LogPrint(eLogDebug, "websocket event");
|
|
||||||
boost::property_tree::ptree event;
|
|
||||||
for (const auto & item : ev) {
|
|
||||||
event.put(item.first, item.second);
|
|
||||||
}
|
|
||||||
std::ostringstream ss;
|
|
||||||
write_json(ss, event);
|
|
||||||
std::string s = ss.str();
|
|
||||||
|
|
||||||
ConnList::iterator it;
|
|
||||||
for (it = m_conns.begin(); it != m_conns.end(); ++it) {
|
|
||||||
ServerImpl::connection_ptr con = m_server.get_con_from_hdl(*it);
|
|
||||||
con->send(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
typedef std::set<ServerConn, std::owner_less<ServerConn> > ConnList;
|
|
||||||
bool m_run;
|
|
||||||
std::thread * m_thread;
|
|
||||||
std::mutex m_connsMutex;
|
|
||||||
ConnList m_conns;
|
|
||||||
ServerImpl m_server;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
WebsocketServer::WebsocketServer(const std::string & addr, int port) : m_impl(new WebsocketServerImpl(addr, port)) {}
|
|
||||||
WebsocketServer::~WebsocketServer()
|
|
||||||
{
|
|
||||||
delete m_impl;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void WebsocketServer::Start()
|
|
||||||
{
|
|
||||||
m_impl->Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebsocketServer::Stop()
|
|
||||||
{
|
|
||||||
m_impl->Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
EventListener * WebsocketServer::ToListener()
|
|
||||||
{
|
|
||||||
return m_impl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
28
Websocket.h
28
Websocket.h
@@ -1,28 +0,0 @@
|
|||||||
#ifndef WEBSOCKET_H__
|
|
||||||
#define WEBSOCKET_H__
|
|
||||||
#include "Event.h"
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace event
|
|
||||||
{
|
|
||||||
|
|
||||||
class WebsocketServerImpl;
|
|
||||||
|
|
||||||
class WebsocketServer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
WebsocketServer(const std::string & addr, int port);
|
|
||||||
~WebsocketServer();
|
|
||||||
|
|
||||||
void Start();
|
|
||||||
void Stop();
|
|
||||||
|
|
||||||
EventListener * ToListener();
|
|
||||||
|
|
||||||
private:
|
|
||||||
WebsocketServerImpl * m_impl;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
14
Win32/.gitignore
vendored
14
Win32/.gitignore
vendored
@@ -1,14 +0,0 @@
|
|||||||
*
|
|
||||||
!*/
|
|
||||||
|
|
||||||
!*.h
|
|
||||||
!*.cpp
|
|
||||||
|
|
||||||
!*.bat
|
|
||||||
|
|
||||||
!*.sln
|
|
||||||
!*.vcproj
|
|
||||||
!*.vcxproj
|
|
||||||
!*.vcxproj.filters
|
|
||||||
!*.iss
|
|
||||||
!.gitignore
|
|
||||||
103
Win32/DaemonWin32.cpp
Normal file
103
Win32/DaemonWin32.cpp
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013-2023, 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 <thread>
|
||||||
|
#include <clocale>
|
||||||
|
#include "Config.h"
|
||||||
|
#include "Daemon.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "Log.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "Win32Service.h"
|
||||||
|
#ifdef WIN32_APP
|
||||||
|
#include <windows.h>
|
||||||
|
#include "Win32App.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace i2p
|
||||||
|
{
|
||||||
|
namespace util
|
||||||
|
{
|
||||||
|
bool DaemonWin32::init(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
setlocale(LC_CTYPE, "");
|
||||||
|
SetConsoleCP(1251);
|
||||||
|
SetConsoleOutputCP(1251);
|
||||||
|
//setlocale(LC_ALL, "Russian");
|
||||||
|
setlocale(LC_TIME, "C");
|
||||||
|
|
||||||
|
i2p::log::SetThrowFunction ([](const std::string& s)
|
||||||
|
{
|
||||||
|
MessageBox(0, TEXT(s.c_str ()), TEXT("i2pd"), MB_ICONERROR | MB_TASKMODAL | MB_OK );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!Daemon_Singleton::init(argc, argv))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (isDaemon)
|
||||||
|
{
|
||||||
|
LogPrint(eLogDebug, "Daemon: running as service");
|
||||||
|
I2PService service((PSTR)SERVICE_NAME);
|
||||||
|
if (!I2PService::Run(service))
|
||||||
|
{
|
||||||
|
LogPrint(eLogError, "Daemon: Service failed to run w/err 0x%08lx\n", GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DaemonWin32::start()
|
||||||
|
{
|
||||||
|
setlocale(LC_CTYPE, "");
|
||||||
|
SetConsoleCP(1251);
|
||||||
|
SetConsoleOutputCP(1251);
|
||||||
|
//setlocale(LC_ALL, "Russian");
|
||||||
|
setlocale(LC_TIME, "C");
|
||||||
|
#ifdef WIN32_APP
|
||||||
|
if (!i2p::win32::StartWin32App ()) return false;
|
||||||
|
#endif
|
||||||
|
bool ret = Daemon_Singleton::start();
|
||||||
|
if (ret && i2p::log::Logger().GetLogType() == eLogFile)
|
||||||
|
{
|
||||||
|
// TODO: find out where this garbage to console comes from
|
||||||
|
SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE);
|
||||||
|
SetStdHandle(STD_ERROR_HANDLE, INVALID_HANDLE_VALUE);
|
||||||
|
}
|
||||||
|
bool insomnia; i2p::config::GetOption("insomnia", insomnia);
|
||||||
|
if (insomnia)
|
||||||
|
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DaemonWin32::stop()
|
||||||
|
{
|
||||||
|
#ifdef WIN32_APP
|
||||||
|
i2p::win32::StopWin32App ();
|
||||||
|
#endif
|
||||||
|
return Daemon_Singleton::stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DaemonWin32::run ()
|
||||||
|
{
|
||||||
|
#ifdef WIN32_APP
|
||||||
|
i2p::win32::RunWin32App ();
|
||||||
|
#else
|
||||||
|
while (running)
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for (std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif //_WIN32
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
@echo off
|
|
||||||
convert Itoopie.svg ^
|
|
||||||
-fuzz 90%% -fill transparent -floodfill 2x2 white -fuzz 20%% -fill #AE0E99 -opaque red ^
|
|
||||||
-fill #FBBC11 -opaque yellow ^
|
|
||||||
( -clone 0 -resize 256x256 ) ^
|
|
||||||
( -clone 0 -resize 128x128 ) ^
|
|
||||||
( -clone 0 -resize 64x64 ) ^
|
|
||||||
( -clone 0 -resize 48x48 ) ^
|
|
||||||
( -clone 0 -resize 32x32 ) ^
|
|
||||||
( -clone 0 -resize 24x24 ) ^
|
|
||||||
( -clone 0 -resize 16x16 ) ^
|
|
||||||
( -size 150x57 xc:white -clone 0 -geometry 57x57+46+0 -composite -gravity center -write BMP3:ictoopie.bmp +delete ) ^
|
|
||||||
( -clone 0 -write Itoopie_purple.png +delete ) ^
|
|
||||||
-delete 0 ictoopie.ico
|
|
||||||
@@ -1,282 +0,0 @@
|
|||||||
# NSIS Installer script. (Tested with NSIS 2.64 on Windows 7)
|
|
||||||
# Author: Mikal Villa (Meeh)
|
|
||||||
# Version: 1.1
|
|
||||||
Name PurpleI2P
|
|
||||||
|
|
||||||
RequestExecutionLevel highest
|
|
||||||
SetCompressor /SOLID lzma
|
|
||||||
ShowInstDetails show
|
|
||||||
|
|
||||||
# General Symbol Definitions
|
|
||||||
!define REGKEY "SOFTWARE\$(^Name)"
|
|
||||||
!define VERSION 0.3.0.0
|
|
||||||
!define COMPANY "The Privacy Solutions Project"
|
|
||||||
!define URL "https://i2p.io"
|
|
||||||
|
|
||||||
# MUI Symbol Definitions
|
|
||||||
!define MUI_ICON "mask.ico"
|
|
||||||
#!define MUI_WELCOMEFINISHPAGE_BITMAP "../share/pixmaps/nsis-wizard.bmp"
|
|
||||||
!define MUI_HEADERIMAGE
|
|
||||||
!define MUI_HEADERIMAGE_RIGHT
|
|
||||||
#!define MUI_HEADERIMAGE_BITMAP "../share/pixmaps/nsis-header.bmp"
|
|
||||||
!define MUI_FINISHPAGE_NOAUTOCLOSE
|
|
||||||
!define MUI_STARTMENUPAGE_REGISTRY_ROOT HKLM
|
|
||||||
!define MUI_STARTMENUPAGE_REGISTRY_KEY ${REGKEY}
|
|
||||||
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME StartMenuGroup
|
|
||||||
!define MUI_STARTMENUPAGE_DEFAULTFOLDER PurpleI2P
|
|
||||||
!define MUI_FINISHPAGE_RUN $INSTDIR\i2pd.exe
|
|
||||||
!define MUI_FINISHPAGE_SHOWREADME $INSTDIR\Readme.txt
|
|
||||||
|
|
||||||
|
|
||||||
!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico"
|
|
||||||
!define MUI_UNWELCOMEFINISHPAGE_BITMAP "../share/pixmaps/nsis-wizard.bmp"
|
|
||||||
!define MUI_UNFINISHPAGE_NOAUTOCLOSE
|
|
||||||
|
|
||||||
# Included files
|
|
||||||
!include Sections.nsh
|
|
||||||
!include MUI2.nsh
|
|
||||||
!include nsDialogs.nsh
|
|
||||||
!include winmessages.nsh
|
|
||||||
!include logiclib.nsh
|
|
||||||
# Local included files
|
|
||||||
!include nsi\helper_readme.nsh
|
|
||||||
;!include nsi\servicelib.nsh
|
|
||||||
|
|
||||||
# Variables
|
|
||||||
Var StartMenuGroup
|
|
||||||
|
|
||||||
# Installer pages
|
|
||||||
# Execution flow of installer windows
|
|
||||||
!insertmacro MUI_PAGE_WELCOME
|
|
||||||
!insertmacro MUI_PAGE_README "../Readme.md"
|
|
||||||
!insertmacro MUI_PAGE_DIRECTORY
|
|
||||||
# Disabled for now. Use the bat
|
|
||||||
;Page custom mode_selection # Meeh's hack for installing and starting service.
|
|
||||||
!insertmacro MUI_PAGE_STARTMENU Application $StartMenuGroup
|
|
||||||
!insertmacro MUI_PAGE_INSTFILES
|
|
||||||
!insertmacro MUI_PAGE_FINISH
|
|
||||||
|
|
||||||
# Uninstall pages
|
|
||||||
!insertmacro MUI_UNPAGE_CONFIRM
|
|
||||||
!insertmacro MUI_UNPAGE_INSTFILES
|
|
||||||
|
|
||||||
# Installer languages
|
|
||||||
!insertmacro MUI_LANGUAGE English
|
|
||||||
|
|
||||||
# Installer attributes
|
|
||||||
OutFile PurpleI2P-0.3.0.0-win32-setup.exe
|
|
||||||
InstallDir $PROGRAMFILES\PurpleI2P
|
|
||||||
CRCCheck on
|
|
||||||
XPStyle on
|
|
||||||
BrandingText " "
|
|
||||||
ShowInstDetails show
|
|
||||||
VIProductVersion 0.3.0.0
|
|
||||||
VIAddVersionKey ProductName PurpleI2P
|
|
||||||
VIAddVersionKey ProductVersion "${VERSION}"
|
|
||||||
VIAddVersionKey CompanyName "${COMPANY}"
|
|
||||||
VIAddVersionKey CompanyWebsite "${URL}"
|
|
||||||
VIAddVersionKey FileVersion "${VERSION}"
|
|
||||||
VIAddVersionKey FileDescription ""
|
|
||||||
VIAddVersionKey LegalCopyright ""
|
|
||||||
InstallDirRegKey HKCU "${REGKEY}" Path
|
|
||||||
ShowUninstDetails show
|
|
||||||
|
|
||||||
# Readme definitions
|
|
||||||
|
|
||||||
;--------------------------------
|
|
||||||
;Languages
|
|
||||||
;Set up install lang strings for 1st lang
|
|
||||||
${ReadmeLanguage} "${LANG_ENGLISH}" \
|
|
||||||
"Read Me" \
|
|
||||||
"Please review the following important information." \
|
|
||||||
"About $(^name):" \
|
|
||||||
"$\n Click on scrollbar arrows or press Page Down to review the entire text."
|
|
||||||
|
|
||||||
;Add 2nd language
|
|
||||||
!insertmacro MUI_LANGUAGE "Norwegian"
|
|
||||||
|
|
||||||
;set up install lang strings for second lang
|
|
||||||
${ReadmeLanguage} "${LANG_NORWEGIAN}" \
|
|
||||||
"Les meg!" \
|
|
||||||
"Vennligst les informasjonen om hvordan du skal bruke PurpleI2P." \
|
|
||||||
"Om $(^name):" \
|
|
||||||
"$\n Klikk på scrollbaren til høyre for å se hele innholdet."
|
|
||||||
|
|
||||||
;--------------------------------
|
|
||||||
|
|
||||||
# Installer sections
|
|
||||||
Section -Main SEC0000
|
|
||||||
SetOutPath $INSTDIR
|
|
||||||
SetOverwrite on
|
|
||||||
File /oname=i2pd.exe Release\i2pd.exe
|
|
||||||
File /oname=install_service.bat install_service.bat
|
|
||||||
File /oname=uninstall_service.bat uninstall_service.bat
|
|
||||||
File /oname=LICENSE.txt ..\LICENSE
|
|
||||||
File /oname=Readme.txt ..\README.md
|
|
||||||
SetOutPath $INSTDIR\src
|
|
||||||
File /r /x *.nsi /x *.rc /x *.exe /x *.obj /x *.nsh /x *.sln /x *.vcxproj /x *.tlog /x *.log /x *.res /x *.pdb /x *.suo /x *.opensdf /x *.filters /x *.sdf /x *.iss /x *.aps /x .gitignore /x *.o ../\*.*
|
|
||||||
SetOutPath $INSTDIR
|
|
||||||
RMDir /r /REBOOTOK $INSTDIR\src\.git # Remove git directory
|
|
||||||
RMDir /r /REBOOTOK $INSTDIR\src\Win32\Release # Removing release directory
|
|
||||||
RMDir /r /REBOOTOK $INSTDIR\src\Win32\nsi
|
|
||||||
WriteRegStr HKCU "${REGKEY}\Components" Main 1
|
|
||||||
SectionEnd
|
|
||||||
|
|
||||||
Section -post SEC0001
|
|
||||||
WriteRegStr HKCU "${REGKEY}" Path $INSTDIR
|
|
||||||
SetOutPath $INSTDIR
|
|
||||||
WriteUninstaller $INSTDIR\uninstall.exe
|
|
||||||
!insertmacro MUI_STARTMENU_WRITE_BEGIN Application
|
|
||||||
CreateDirectory $SMPROGRAMS\$StartMenuGroup
|
|
||||||
CreateShortcut "$SMPROGRAMS\$StartMenuGroup\PurpleI2P.lnk" $INSTDIR\i2pd.exe
|
|
||||||
CreateShortcut "$SMPROGRAMS\$StartMenuGroup\Install PurpleI2P Service.lnk" $INSTDIR\install_service.bat
|
|
||||||
CreateShortcut "$SMPROGRAMS\$StartMenuGroup\Uninstall PurpleI2P Service.lnk" $INSTDIR\uninstall_service.bat
|
|
||||||
CreateShortcut "$SMPROGRAMS\$StartMenuGroup\Uninstall PurpleI2P.lnk" $INSTDIR\uninstall.exe
|
|
||||||
!insertmacro MUI_STARTMENU_WRITE_END
|
|
||||||
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayName "$(^Name)"
|
|
||||||
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayVersion "${VERSION}"
|
|
||||||
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" Publisher "${COMPANY}"
|
|
||||||
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" URLInfoAbout "${URL}"
|
|
||||||
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayIcon $INSTDIR\uninstall.exe
|
|
||||||
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" UninstallString $INSTDIR\uninstall.exe
|
|
||||||
WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoModify 1
|
|
||||||
WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoRepair 1
|
|
||||||
WriteRegStr HKCR "i2pd" "URL Protocol" ""
|
|
||||||
WriteRegStr HKCR "i2pd" "" "URL:i2pd" # TODO: if a instance of own is found, relaunch with a proxyfied browser to open webage. (e.g i2pd://meeh.i2p)
|
|
||||||
WriteRegStr HKCR "i2pd\DefaultIcon" "" $INSTDIR\i2pd.exe
|
|
||||||
WriteRegStr HKCR "i2pd\shell\open\command" "" '"$INSTDIR\i2pd.exe" "%1"'
|
|
||||||
SectionEnd
|
|
||||||
|
|
||||||
# Macro for selecting uninstaller sections
|
|
||||||
!macro SELECT_UNSECTION SECTION_NAME UNSECTION_ID
|
|
||||||
Push $R0
|
|
||||||
ReadRegStr $R0 HKCU "${REGKEY}\Components" "${SECTION_NAME}"
|
|
||||||
StrCmp $R0 1 0 next${UNSECTION_ID}
|
|
||||||
!insertmacro SelectSection "${UNSECTION_ID}"
|
|
||||||
GoTo done${UNSECTION_ID}
|
|
||||||
next${UNSECTION_ID}:
|
|
||||||
!insertmacro UnselectSection "${UNSECTION_ID}"
|
|
||||||
done${UNSECTION_ID}:
|
|
||||||
Pop $R0
|
|
||||||
!macroend
|
|
||||||
|
|
||||||
|
|
||||||
# Uninstaller sections
|
|
||||||
Section /o -un.Main UNSEC0000
|
|
||||||
Delete /REBOOTOK $INSTDIR\i2pd.exe
|
|
||||||
Delete /REBOOTOK $INSTDIR\LICENSE.txt
|
|
||||||
Delete /REBOOTOK $INSTDIR\Readme.txt
|
|
||||||
Delete /REBOOTOK $INSTDIR\install_service.bat
|
|
||||||
Delete /REBOOTOK $INSTDIR\uninstall_service.bat
|
|
||||||
RMDir /r /REBOOTOK $INSTDIR\src
|
|
||||||
DeleteRegValue HKCU "${REGKEY}\Components" Main
|
|
||||||
SectionEnd
|
|
||||||
|
|
||||||
Section -un.post UNSEC0001
|
|
||||||
DeleteRegKey HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)"
|
|
||||||
Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\Uninstall PurpleI2P.lnk"
|
|
||||||
Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\PurpleI2P.lnk"
|
|
||||||
Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\Install PurpleI2P Service.lnk"
|
|
||||||
Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\UnInstall PurpleI2P Service.lnk"
|
|
||||||
Delete /REBOOTOK "$SMSTARTUP\PurpleI2P.lnk"
|
|
||||||
Delete /REBOOTOK $INSTDIR\uninstall.exe
|
|
||||||
Delete /REBOOTOK $INSTDIR\debug.log
|
|
||||||
DeleteRegValue HKCU "${REGKEY}" StartMenuGroup
|
|
||||||
DeleteRegValue HKCU "${REGKEY}" Path
|
|
||||||
DeleteRegKey /IfEmpty HKCU "${REGKEY}\Components"
|
|
||||||
DeleteRegKey /IfEmpty HKCU "${REGKEY}"
|
|
||||||
DeleteRegKey HKCR "i2pd"
|
|
||||||
RmDir /REBOOTOK $SMPROGRAMS\$StartMenuGroup
|
|
||||||
RmDir /REBOOTOK $INSTDIR
|
|
||||||
Push $R0
|
|
||||||
StrCpy $R0 $StartMenuGroup 1
|
|
||||||
StrCmp $R0 ">" no_smgroup
|
|
||||||
no_smgroup:
|
|
||||||
Pop $R0
|
|
||||||
SectionEnd
|
|
||||||
|
|
||||||
; var hwndExecModeRadio
|
|
||||||
; var hwndRunServiceNowRadio
|
|
||||||
|
|
||||||
; Function mode_selection
|
|
||||||
; nsDialogs::Create 1018
|
|
||||||
; Pop $0
|
|
||||||
; ${NSD_CreateLabel} 0 10 75% 20u "How would you like PurpleI2P (i2pd) to run?"
|
|
||||||
; Pop $0
|
|
||||||
|
|
||||||
; ${NSD_CreateRadioButton} 20 60 80% 25u "Service Mode"
|
|
||||||
; Pop $hwndExecModeRadio
|
|
||||||
; ${NSD_AddStyle} $hwndExecModeRadio ${WS_GROUP}
|
|
||||||
|
|
||||||
; ${NSD_CreateRadioButton} 20 90 80% 25u "Command line Mode"
|
|
||||||
; Pop $0
|
|
||||||
|
|
||||||
; ${NSD_CreateButton} 20 150 -40 14u "Do it!"
|
|
||||||
; Pop $0
|
|
||||||
; ${NSD_OnClick} $0 perform_mode
|
|
||||||
|
|
||||||
; nsDialogs::Show
|
|
||||||
; FunctionEnd
|
|
||||||
|
|
||||||
; Function start_now_selection
|
|
||||||
; nsDialogs::Create 1018
|
|
||||||
; Pop $0
|
|
||||||
; ${NSD_CreateLabel} 0 10 75% 20u "Enable the service now?"
|
|
||||||
; Pop $0
|
|
||||||
|
|
||||||
; ${NSD_CreateRadioButton} 20 60 80% 25u "Yes"
|
|
||||||
; Pop $hwndRunServiceNowRadio
|
|
||||||
; ${NSD_AddStyle} $hwndRunServiceNowRadio ${WS_GROUP}
|
|
||||||
|
|
||||||
; ${NSD_CreateRadioButton} 20 90 80% 25u "No"
|
|
||||||
; Pop $0
|
|
||||||
|
|
||||||
; ${NSD_CreateButton} 20 150 -40 14u "Do it!"
|
|
||||||
; Pop $0
|
|
||||||
; ${NSD_OnClick} $0 perform_mode
|
|
||||||
|
|
||||||
; nsDialogs::Show
|
|
||||||
; FunctionEnd
|
|
||||||
|
|
||||||
; Function perform_mode
|
|
||||||
; ${NSD_GetState} $hwndExecModeRadio $0
|
|
||||||
; ${If} $0 = ${BST_CHECKED}
|
|
||||||
; Call service_mode
|
|
||||||
; ${EndIF}
|
|
||||||
; FunctionEnd
|
|
||||||
|
|
||||||
; Function start_now
|
|
||||||
; ${NSD_GetState} $hwndRunServiceNowRadio $0
|
|
||||||
; ${If} $0 = ${BST_CHECKED}
|
|
||||||
; Call start_now_selection
|
|
||||||
; ${EndIF}
|
|
||||||
; FunctionEnd
|
|
||||||
|
|
||||||
; Function service_mode
|
|
||||||
; Push "create"
|
|
||||||
; Push "PurpleI2P Service"
|
|
||||||
; Push "$INSTDIR\i2pd.exe;autostart=1;display=PurpleI2P"
|
|
||||||
; Call Service
|
|
||||||
; Pop $0 ; Actually more to write than !insertmacro, but much more fun :D
|
|
||||||
; Push "start"
|
|
||||||
; Push "PurpleI2P Service"
|
|
||||||
; Call Service
|
|
||||||
; Pop $0
|
|
||||||
; Call start_now
|
|
||||||
; !define MUI_FINISHPAGE_RUN_NOTCHECKED
|
|
||||||
; !define MUI_FINISHPAGE_RUN_TEXT "No need to run now since we already installed and launched it as a Windows service!"
|
|
||||||
; FunctionEnd
|
|
||||||
|
|
||||||
# Installer functions
|
|
||||||
Function .onInit
|
|
||||||
InitPluginsDir
|
|
||||||
!insertmacro MUI_LANGDLL_DISPLAY
|
|
||||||
FunctionEnd
|
|
||||||
|
|
||||||
# Uninstaller functions
|
|
||||||
Function un.onInit
|
|
||||||
ReadRegStr $INSTDIR HKCU "${REGKEY}" Path
|
|
||||||
!insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuGroup
|
|
||||||
!insertmacro SELECT_UNSECTION Main ${UNSEC0000}
|
|
||||||
!insertmacro MUI_UNGETLANGUAGE
|
|
||||||
FunctionEnd
|
|
||||||
@@ -1,73 +1,36 @@
|
|||||||
// Microsoft Visual C++ generated resource script.
|
|
||||||
//
|
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
|
|
||||||
#define APSTUDIO_READONLY_SYMBOLS
|
#define APSTUDIO_READONLY_SYMBOLS
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Generated from the TEXTINCLUDE 2 resource.
|
|
||||||
//
|
|
||||||
#include "winres.h"
|
#include "winres.h"
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
#undef APSTUDIO_READONLY_SYMBOLS
|
#undef APSTUDIO_READONLY_SYMBOLS
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
// English (United States) resources
|
|
||||||
|
|
||||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||||
#pragma code_page(1252)
|
#pragma code_page(1252)
|
||||||
|
|
||||||
#ifdef APSTUDIO_INVOKED
|
#ifdef APSTUDIO_INVOKED
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// TEXTINCLUDE
|
|
||||||
//
|
|
||||||
|
|
||||||
1 TEXTINCLUDE
|
1 TEXTINCLUDE
|
||||||
BEGIN
|
BEGIN
|
||||||
"resource.h\0"
|
"resource.h\0"
|
||||||
END
|
END
|
||||||
|
|
||||||
2 TEXTINCLUDE
|
2 TEXTINCLUDE
|
||||||
BEGIN
|
BEGIN
|
||||||
"#include ""winres.h""\r\n"
|
"#include ""winres.h""\r\n"
|
||||||
"\0"
|
"\0"
|
||||||
END
|
END
|
||||||
|
|
||||||
3 TEXTINCLUDE
|
3 TEXTINCLUDE
|
||||||
BEGIN
|
BEGIN
|
||||||
"\r\n"
|
"\r\n"
|
||||||
"\0"
|
"\0"
|
||||||
END
|
END
|
||||||
|
#endif // APSTUDIO_INVOKED
|
||||||
|
|
||||||
#endif // APSTUDIO_INVOKED
|
MAINICON ICON "mask.ico"
|
||||||
|
#endif // English (United States) resources
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Icon
|
|
||||||
//
|
|
||||||
|
|
||||||
// Icon with lowest ID value placed first to ensure application icon
|
|
||||||
// remains consistent on all systems.
|
|
||||||
MAINICON ICON "mask.ico"
|
|
||||||
//MAINICON ICON "anke.ico"
|
|
||||||
|
|
||||||
#endif // English (United States) resources
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef APSTUDIO_INVOKED
|
#ifndef APSTUDIO_INVOKED
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Generated from the TEXTINCLUDE 3 resource.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "Resource.rc2"
|
#include "Resource.rc2"
|
||||||
|
#endif // not APSTUDIO_INVOKED
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
#endif // not APSTUDIO_INVOKED
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,8 @@
|
|||||||
//
|
|
||||||
// Resource.RC2 - resources Microsoft Visual C++ does not edit directly
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifdef APSTUDIO_INVOKED
|
#ifdef APSTUDIO_INVOKED
|
||||||
#error this file is not editable by Microsoft Visual C++
|
#error this file is not editable by Microsoft Visual C++
|
||||||
#endif //APSTUDIO_INVOKED
|
#endif //APSTUDIO_INVOKED
|
||||||
|
|
||||||
#include "../version.h"
|
#include "../libi2pd/version.h"
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Version
|
|
||||||
//
|
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION I2PD_VERSION_MAJOR,I2PD_VERSION_MINOR,I2PD_VERSION_MICRO,I2PD_VERSION_PATCH
|
FILEVERSION I2PD_VERSION_MAJOR,I2PD_VERSION_MINOR,I2PD_VERSION_MICRO,I2PD_VERSION_PATCH
|
||||||
@@ -34,7 +25,7 @@ BEGIN
|
|||||||
VALUE "FileDescription", "C++ I2P daemon"
|
VALUE "FileDescription", "C++ I2P daemon"
|
||||||
VALUE "FileVersion", I2PD_VERSION
|
VALUE "FileVersion", I2PD_VERSION
|
||||||
VALUE "InternalName", CODENAME
|
VALUE "InternalName", CODENAME
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2013-2015, The PurpleI2P Project"
|
VALUE "LegalCopyright", "Copyright (C) 2013-2022, The PurpleI2P Project"
|
||||||
VALUE "OriginalFilename", "i2pd"
|
VALUE "OriginalFilename", "i2pd"
|
||||||
VALUE "ProductName", "Purple I2P"
|
VALUE "ProductName", "Purple I2P"
|
||||||
VALUE "ProductVersion", I2P_VERSION
|
VALUE "ProductVersion", I2P_VERSION
|
||||||
|
|||||||
@@ -1,251 +1,489 @@
|
|||||||
#include <string.h>
|
/*
|
||||||
#include <windows.h>
|
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||||
#include <shellapi.h>
|
*
|
||||||
#include "../Config.h"
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
#include "../RouterContext.h"
|
*
|
||||||
#include "../version.h"
|
* See full license text in LICENSE file at top of project tree
|
||||||
#include "resource.h"
|
*/
|
||||||
#include "Win32App.h"
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
#include <windows.h>
|
||||||
#define snprintf _snprintf
|
#include <shellapi.h>
|
||||||
#endif
|
#include "ClientContext.h"
|
||||||
|
#include "Config.h"
|
||||||
#define ID_ABOUT 2000
|
#include "NetDb.hpp"
|
||||||
#define ID_EXIT 2001
|
#include "RouterContext.h"
|
||||||
#define ID_CONSOLE 2002
|
#include "Transports.h"
|
||||||
#define ID_APP 2003
|
#include "Tunnel.h"
|
||||||
#define ID_GRACEFUL_SHUTDOWN 2004
|
#include "version.h"
|
||||||
|
#include "resource.h"
|
||||||
#define ID_TRAY_ICON 2050
|
#include "Daemon.h"
|
||||||
#define WM_TRAYICON (WM_USER + 1)
|
#include "Win32App.h"
|
||||||
|
#include "Win32NetState.h"
|
||||||
#define IDT_GRACEFUL_SHUTDOWN_TIMER 2100
|
|
||||||
|
#define ID_ABOUT 2000
|
||||||
namespace i2p
|
#define ID_EXIT 2001
|
||||||
{
|
#define ID_CONSOLE 2002
|
||||||
namespace win32
|
#define ID_APP 2003
|
||||||
{
|
#define ID_GRACEFUL_SHUTDOWN 2004
|
||||||
static void ShowPopupMenu (HWND hWnd, POINT *curpos, int wDefaultItem)
|
#define ID_STOP_GRACEFUL_SHUTDOWN 2005
|
||||||
{
|
#define ID_RELOAD 2006
|
||||||
HMENU hPopup = CreatePopupMenu();
|
#define ID_ACCEPT_TRANSIT 2007
|
||||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_CONSOLE, "Open &console");
|
#define ID_DECLINE_TRANSIT 2008
|
||||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_APP, "Show app");
|
#define ID_DATADIR 2009
|
||||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_ABOUT, "&About...");
|
|
||||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_SEPARATOR, NULL, NULL);
|
#define ID_TRAY_ICON 2050
|
||||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown");
|
#define WM_TRAYICON (WM_USER + 1)
|
||||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_EXIT, "E&xit");
|
|
||||||
SetMenuDefaultItem (hPopup, ID_CONSOLE, FALSE);
|
#define IDT_GRACEFUL_SHUTDOWN_TIMER 2100
|
||||||
SendMessage (hWnd, WM_INITMENUPOPUP, (WPARAM)hPopup, 0);
|
#define FRAME_UPDATE_TIMER 2101
|
||||||
|
#define IDT_GRACEFUL_TUNNELCHECK_TIMER 2102
|
||||||
POINT p;
|
|
||||||
if (!curpos)
|
namespace i2p
|
||||||
{
|
{
|
||||||
GetCursorPos (&p);
|
namespace win32
|
||||||
curpos = &p;
|
{
|
||||||
}
|
DWORD g_GracefulShutdownEndtime = 0;
|
||||||
|
|
||||||
WORD cmd = TrackPopupMenu (hPopup, TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY, curpos->x, curpos->y, 0, hWnd, NULL);
|
static void ShowPopupMenu (HWND hWnd, POINT *curpos, int wDefaultItem)
|
||||||
SendMessage (hWnd, WM_COMMAND, cmd, 0);
|
{
|
||||||
|
HMENU hPopup = CreatePopupMenu();
|
||||||
DestroyMenu(hPopup);
|
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_CONSOLE, "Open &console");
|
||||||
}
|
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_DATADIR, "Open &datadir");
|
||||||
|
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_APP, "&Show app");
|
||||||
static void AddTrayIcon (HWND hWnd)
|
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_ABOUT, "&About...");
|
||||||
{
|
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
|
||||||
NOTIFYICONDATA nid;
|
if(!i2p::context.AcceptsTunnels())
|
||||||
memset(&nid, 0, sizeof(nid));
|
InsertMenu (hPopup, -1,
|
||||||
nid.cbSize = sizeof(nid);
|
i2p::util::DaemonWin32::Instance ().isGraceful ? MF_BYPOSITION | MF_STRING | MF_GRAYED : MF_BYPOSITION | MF_STRING,
|
||||||
nid.hWnd = hWnd;
|
ID_ACCEPT_TRANSIT, "Accept &transit");
|
||||||
nid.uID = ID_TRAY_ICON;
|
else
|
||||||
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO;
|
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_DECLINE_TRANSIT, "Decline &transit");
|
||||||
nid.uCallbackMessage = WM_TRAYICON;
|
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_RELOAD, "&Reload tunnels config");
|
||||||
nid.hIcon = LoadIcon (GetModuleHandle(NULL), MAKEINTRESOURCE (MAINICON));
|
if (!i2p::util::DaemonWin32::Instance ().isGraceful)
|
||||||
strcpy (nid.szTip, "i2pd");
|
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown");
|
||||||
strcpy (nid.szInfo, "i2pd is running");
|
else
|
||||||
Shell_NotifyIcon(NIM_ADD, &nid );
|
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_STOP_GRACEFUL_SHUTDOWN, "Stop &graceful shutdown");
|
||||||
}
|
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_EXIT, "E&xit");
|
||||||
|
SetMenuDefaultItem (hPopup, ID_CONSOLE, FALSE);
|
||||||
static void RemoveTrayIcon (HWND hWnd)
|
SendMessage (hWnd, WM_INITMENUPOPUP, (WPARAM)hPopup, 0);
|
||||||
{
|
|
||||||
NOTIFYICONDATA nid;
|
POINT p;
|
||||||
nid.hWnd = hWnd;
|
if (!curpos)
|
||||||
nid.uID = ID_TRAY_ICON;
|
{
|
||||||
Shell_NotifyIcon (NIM_DELETE, &nid);
|
GetCursorPos (&p);
|
||||||
}
|
curpos = &p;
|
||||||
|
}
|
||||||
static LRESULT CALLBACK WndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
||||||
{
|
WORD cmd = TrackPopupMenu (hPopup, TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY, curpos->x, curpos->y, 0, hWnd, NULL);
|
||||||
switch (uMsg)
|
SendMessage (hWnd, WM_COMMAND, cmd, 0);
|
||||||
{
|
|
||||||
case WM_CREATE:
|
DestroyMenu(hPopup);
|
||||||
{
|
}
|
||||||
AddTrayIcon (hWnd);
|
|
||||||
break;
|
static void AddTrayIcon (HWND hWnd, bool notify = false)
|
||||||
}
|
{
|
||||||
case WM_CLOSE:
|
NOTIFYICONDATA nid;
|
||||||
{
|
memset(&nid, 0, sizeof(nid));
|
||||||
RemoveTrayIcon (hWnd);
|
nid.cbSize = sizeof(nid);
|
||||||
KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER);
|
nid.hWnd = hWnd;
|
||||||
PostQuitMessage (0);
|
nid.uID = ID_TRAY_ICON;
|
||||||
break;
|
nid.uFlags = notify ? NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO : NIF_ICON | NIF_MESSAGE | NIF_TIP;
|
||||||
}
|
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO;
|
||||||
case WM_COMMAND:
|
nid.uCallbackMessage = WM_TRAYICON;
|
||||||
{
|
nid.hIcon = LoadIcon (GetModuleHandle(NULL), MAKEINTRESOURCE (MAINICON));
|
||||||
switch (LOWORD(wParam))
|
strcpy (nid.szTip, "i2pd");
|
||||||
{
|
if (notify) strcpy (nid.szInfo, "i2pd is starting");
|
||||||
case ID_ABOUT:
|
Shell_NotifyIcon(NIM_ADD, &nid );
|
||||||
{
|
}
|
||||||
std::stringstream text;
|
|
||||||
text << "Version: " << I2PD_VERSION << " " << CODENAME;
|
static void RemoveTrayIcon (HWND hWnd)
|
||||||
MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK );
|
{
|
||||||
return 0;
|
NOTIFYICONDATA nid;
|
||||||
}
|
nid.hWnd = hWnd;
|
||||||
case ID_EXIT:
|
nid.uID = ID_TRAY_ICON;
|
||||||
{
|
Shell_NotifyIcon (NIM_DELETE, &nid);
|
||||||
PostMessage (hWnd, WM_CLOSE, 0, 0);
|
}
|
||||||
return 0;
|
|
||||||
}
|
static void ShowUptime (std::stringstream& s, int seconds)
|
||||||
case ID_GRACEFUL_SHUTDOWN:
|
{
|
||||||
{
|
int num;
|
||||||
i2p::context.SetAcceptsTunnels (false);
|
|
||||||
SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes
|
if ((num = seconds / 86400) > 0) {
|
||||||
return 0;
|
s << num << " days, ";
|
||||||
}
|
seconds -= num * 86400;
|
||||||
case ID_CONSOLE:
|
}
|
||||||
{
|
if ((num = seconds / 3600) > 0) {
|
||||||
char buf[30];
|
s << num << " hours, ";
|
||||||
std::string httpAddr; i2p::config::GetOption("http.address", httpAddr);
|
seconds -= num * 3600;
|
||||||
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
|
}
|
||||||
snprintf(buf, 30, "http://%s:%d", httpAddr.c_str(), httpPort);
|
if ((num = seconds / 60) > 0) {
|
||||||
ShellExecute(NULL, "open", buf, NULL, NULL, SW_SHOWNORMAL);
|
s << num << " min, ";
|
||||||
return 0;
|
seconds -= num * 60;
|
||||||
}
|
}
|
||||||
case ID_APP:
|
s << seconds << " seconds\n";
|
||||||
{
|
}
|
||||||
ShowWindow(hWnd, SW_SHOW);
|
|
||||||
return 0;
|
template <typename size> static void ShowTransfered (std::stringstream& s, size transfer)
|
||||||
}
|
{
|
||||||
}
|
auto bytes = transfer & 0x03ff;
|
||||||
break;
|
transfer >>= 10;
|
||||||
}
|
auto kbytes = transfer & 0x03ff;
|
||||||
case WM_SYSCOMMAND:
|
transfer >>= 10;
|
||||||
{
|
auto mbytes = transfer & 0x03ff;
|
||||||
switch (wParam)
|
transfer >>= 10;
|
||||||
{
|
auto gbytes = transfer;
|
||||||
case SC_MINIMIZE:
|
|
||||||
{
|
if (gbytes)
|
||||||
ShowWindow(hWnd, SW_HIDE);
|
s << gbytes << " GB, ";
|
||||||
return 0;
|
if (mbytes)
|
||||||
}
|
s << mbytes << " MB, ";
|
||||||
case SC_CLOSE:
|
if (kbytes)
|
||||||
{
|
s << kbytes << " KB, ";
|
||||||
std::string close; i2p::config::GetOption("close", close);
|
s << bytes << " Bytes\n";
|
||||||
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.",
|
static void ShowNetworkStatus (std::stringstream& s, RouterStatus status)
|
||||||
"Minimize instead of exiting?", MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON1))
|
{
|
||||||
{
|
switch (status)
|
||||||
case IDYES: close = "minimize"; break;
|
{
|
||||||
case IDNO: close = "exit"; break;
|
case eRouterStatusOK: s << "OK"; break;
|
||||||
default: return 0;
|
case eRouterStatusTesting: s << "Test"; break;
|
||||||
}
|
case eRouterStatusFirewalled: s << "FW"; break;
|
||||||
if (0 == close.compare("minimize"))
|
case eRouterStatusUnknown: s << "Unk"; break;
|
||||||
{
|
case eRouterStatusProxy: s << "Proxy"; break;
|
||||||
ShowWindow(hWnd, SW_HIDE);
|
case eRouterStatusMesh: s << "Mesh"; break;
|
||||||
return 0;
|
default: s << "Unk";
|
||||||
}
|
};
|
||||||
if (0 != close.compare("exit"))
|
if (i2p::context.GetError () != eRouterErrorNone)
|
||||||
{
|
{
|
||||||
::MessageBox(hWnd, close.c_str(), "Unknown close action in config", MB_OK | MB_ICONWARNING);
|
switch (i2p::context.GetError ())
|
||||||
return 0;
|
{
|
||||||
}
|
case eRouterErrorClockSkew:
|
||||||
}
|
s << " - Clock skew";
|
||||||
}
|
break;
|
||||||
}
|
case eRouterErrorOffline:
|
||||||
case WM_TRAYICON:
|
s << " - Offline";
|
||||||
{
|
break;
|
||||||
switch (lParam)
|
case eRouterErrorSymmetricNAT:
|
||||||
{
|
s << " - Symmetric NAT";
|
||||||
case WM_LBUTTONUP:
|
break;
|
||||||
case WM_RBUTTONUP:
|
default: ;
|
||||||
{
|
}
|
||||||
SetForegroundWindow (hWnd);
|
}
|
||||||
ShowPopupMenu(hWnd, NULL, -1);
|
}
|
||||||
PostMessage (hWnd, WM_APP + 1, 0, 0);
|
|
||||||
break;
|
static void PrintMainWindowText (std::stringstream& s)
|
||||||
}
|
{
|
||||||
}
|
s << "\n";
|
||||||
break;
|
s << "Status: ";
|
||||||
}
|
ShowNetworkStatus (s, i2p::context.GetStatus ());
|
||||||
case WM_TIMER:
|
if (i2p::context.SupportsV6 ())
|
||||||
{
|
{
|
||||||
if (wParam == IDT_GRACEFUL_SHUTDOWN_TIMER)
|
s << " / ";
|
||||||
{
|
ShowNetworkStatus (s, i2p::context.GetStatusV6 ());
|
||||||
PostMessage (hWnd, WM_CLOSE, 0, 0); // exit
|
}
|
||||||
return 0;
|
s << "; ";
|
||||||
}
|
s << "Success Rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate() << "%\n";
|
||||||
break;
|
s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ());
|
||||||
}
|
if (g_GracefulShutdownEndtime != 0)
|
||||||
}
|
{
|
||||||
return DefWindowProc( hWnd, uMsg, wParam, lParam);
|
DWORD GracefulTimeLeft = (g_GracefulShutdownEndtime - GetTickCount()) / 1000;
|
||||||
}
|
s << "Graceful shutdown, time left: "; ShowUptime(s, GracefulTimeLeft);
|
||||||
|
}
|
||||||
bool StartWin32App ()
|
else
|
||||||
{
|
s << "\n";
|
||||||
if (FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd")))
|
s << "Inbound: " << i2p::transport::transports.GetInBandwidth() / 1024 << " KiB/s; ";
|
||||||
{
|
s << "Outbound: " << i2p::transport::transports.GetOutBandwidth() / 1024 << " KiB/s\n";
|
||||||
MessageBox(NULL, TEXT("I2Pd is running already"), TEXT("Warning"), MB_OK);
|
s << "Received: "; ShowTransfered (s, i2p::transport::transports.GetTotalReceivedBytes());
|
||||||
return false;
|
s << "Sent: "; ShowTransfered (s, i2p::transport::transports.GetTotalSentBytes());
|
||||||
}
|
s << "\n";
|
||||||
// register main window
|
s << "Routers: " << i2p::data::netdb.GetNumRouters () << "; ";
|
||||||
auto hInst = GetModuleHandle(NULL);
|
s << "Floodfills: " << i2p::data::netdb.GetNumFloodfills () << "; ";
|
||||||
WNDCLASSEX wclx;
|
s << "LeaseSets: " << i2p::data::netdb.GetNumLeaseSets () << "\n";
|
||||||
memset (&wclx, 0, sizeof(wclx));
|
s << "Tunnels: ";
|
||||||
wclx.cbSize = sizeof(wclx);
|
s << "In: " << i2p::tunnel::tunnels.CountInboundTunnels() << "; ";
|
||||||
wclx.style = 0;
|
s << "Out: " << i2p::tunnel::tunnels.CountOutboundTunnels() << "; ";
|
||||||
wclx.lpfnWndProc = WndProc;
|
s << "Transit: " << i2p::tunnel::tunnels.CountTransitTunnels() << "\n";
|
||||||
wclx.cbClsExtra = 0;
|
s << "\n";
|
||||||
wclx.cbWndExtra = 0;
|
}
|
||||||
wclx.hInstance = hInst;
|
|
||||||
wclx.hIcon = LoadIcon (hInst, MAKEINTRESOURCE(MAINICON));
|
static LRESULT CALLBACK WndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||||
wclx.hCursor = LoadCursor (NULL, IDC_ARROW);
|
{
|
||||||
wclx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
|
static UINT s_uTaskbarRestart;
|
||||||
wclx.lpszMenuName = NULL;
|
|
||||||
wclx.lpszClassName = I2PD_WIN32_CLASSNAME;
|
switch (uMsg)
|
||||||
RegisterClassEx (&wclx);
|
{
|
||||||
// create new window
|
case WM_CREATE:
|
||||||
if (!CreateWindow(I2PD_WIN32_CLASSNAME, TEXT("i2pd"), WS_OVERLAPPEDWINDOW, 100, 100, 549, 738, NULL, NULL, hInst, NULL))
|
{
|
||||||
{
|
s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
|
||||||
MessageBox(NULL, "Failed to create main window", TEXT("Warning!"), MB_ICONERROR | MB_OK | MB_TOPMOST);
|
AddTrayIcon (hWnd, true);
|
||||||
return false;
|
break;
|
||||||
}
|
}
|
||||||
return true;
|
case WM_CLOSE:
|
||||||
}
|
{
|
||||||
|
RemoveTrayIcon (hWnd);
|
||||||
int RunWin32App ()
|
KillTimer (hWnd, FRAME_UPDATE_TIMER);
|
||||||
{
|
KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER);
|
||||||
MSG msg;
|
KillTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER);
|
||||||
while (GetMessage (&msg, NULL, 0, 0 ))
|
PostQuitMessage (0);
|
||||||
{
|
break;
|
||||||
TranslateMessage (&msg);
|
}
|
||||||
DispatchMessage (&msg);
|
case WM_COMMAND:
|
||||||
}
|
{
|
||||||
return msg.wParam;
|
switch (LOWORD(wParam))
|
||||||
}
|
{
|
||||||
|
case ID_ABOUT:
|
||||||
void StopWin32App ()
|
{
|
||||||
{
|
std::stringstream text;
|
||||||
UnregisterClass (I2PD_WIN32_CLASSNAME, GetModuleHandle(NULL));
|
text << "Version: " << I2PD_VERSION << " " << CODENAME;
|
||||||
}
|
MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK );
|
||||||
|
return 0;
|
||||||
bool GracefulShutdown ()
|
}
|
||||||
{
|
case ID_EXIT:
|
||||||
HWND hWnd = FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd"));
|
{
|
||||||
if (hWnd)
|
PostMessage (hWnd, WM_CLOSE, 0, 0);
|
||||||
PostMessage (hWnd, WM_COMMAND, MAKEWPARAM(ID_GRACEFUL_SHUTDOWN, 0), 0);
|
return 0;
|
||||||
return hWnd;
|
}
|
||||||
}
|
case ID_ACCEPT_TRANSIT:
|
||||||
}
|
{
|
||||||
}
|
i2p::context.SetAcceptsTunnels (true);
|
||||||
|
std::stringstream text;
|
||||||
|
text << "I2Pd now accept transit tunnels";
|
||||||
|
MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case ID_DECLINE_TRANSIT:
|
||||||
|
{
|
||||||
|
i2p::context.SetAcceptsTunnels (false);
|
||||||
|
std::stringstream text;
|
||||||
|
text << "I2Pd now decline new transit tunnels";
|
||||||
|
MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case ID_GRACEFUL_SHUTDOWN:
|
||||||
|
{
|
||||||
|
i2p::context.SetAcceptsTunnels (false);
|
||||||
|
SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes
|
||||||
|
SetTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER, 1000, nullptr); // check tunnels every second
|
||||||
|
g_GracefulShutdownEndtime = GetTickCount() + 10*60*1000;
|
||||||
|
i2p::util::DaemonWin32::Instance ().isGraceful = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case ID_STOP_GRACEFUL_SHUTDOWN:
|
||||||
|
{
|
||||||
|
i2p::context.SetAcceptsTunnels (true);
|
||||||
|
KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER);
|
||||||
|
KillTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER);
|
||||||
|
g_GracefulShutdownEndtime = 0;
|
||||||
|
i2p::util::DaemonWin32::Instance ().isGraceful = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case ID_RELOAD:
|
||||||
|
{
|
||||||
|
i2p::client::context.ReloadConfig();
|
||||||
|
std::stringstream text;
|
||||||
|
text << "I2Pd reloading configs...";
|
||||||
|
MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case ID_CONSOLE:
|
||||||
|
{
|
||||||
|
char buf[30];
|
||||||
|
std::string httpAddr; i2p::config::GetOption("http.address", httpAddr);
|
||||||
|
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
|
||||||
|
snprintf(buf, 30, "http://%s:%d", httpAddr.c_str(), httpPort);
|
||||||
|
ShellExecute(NULL, "open", buf, NULL, NULL, SW_SHOWNORMAL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case ID_APP:
|
||||||
|
{
|
||||||
|
ShowWindow(hWnd, SW_SHOW);
|
||||||
|
SetTimer(hWnd, FRAME_UPDATE_TIMER, 3000, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case ID_DATADIR:
|
||||||
|
{
|
||||||
|
std::string datadir(i2p::fs::GetUTF8DataDir());
|
||||||
|
ShellExecute(NULL, "explore", datadir.c_str(), NULL, NULL, SW_SHOWNORMAL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WM_SYSCOMMAND:
|
||||||
|
{
|
||||||
|
switch (wParam)
|
||||||
|
{
|
||||||
|
case SC_MINIMIZE:
|
||||||
|
{
|
||||||
|
ShowWindow(hWnd, SW_HIDE);
|
||||||
|
KillTimer (hWnd, FRAME_UPDATE_TIMER);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case SC_CLOSE:
|
||||||
|
{
|
||||||
|
std::string close; i2p::config::GetOption("close", close);
|
||||||
|
if (0 == close.compare("ask"))
|
||||||
|
switch(::MessageBox(hWnd, "Would you like to minimize instead of exiting?"
|
||||||
|
" You can add 'close' configuration option. Valid values are: ask, minimize, exit.",
|
||||||
|
"Minimize instead of exiting?", MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON1))
|
||||||
|
{
|
||||||
|
case IDYES: close = "minimize"; break;
|
||||||
|
case IDNO: close = "exit"; break;
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
if (0 == close.compare("minimize"))
|
||||||
|
{
|
||||||
|
ShowWindow(hWnd, SW_HIDE);
|
||||||
|
KillTimer (hWnd, FRAME_UPDATE_TIMER);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (0 != close.compare("exit"))
|
||||||
|
{
|
||||||
|
::MessageBox(hWnd, close.c_str(), "Unknown close action in config", MB_OK | MB_ICONWARNING);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case WM_TRAYICON:
|
||||||
|
{
|
||||||
|
switch (lParam)
|
||||||
|
{
|
||||||
|
case WM_LBUTTONUP:
|
||||||
|
case WM_RBUTTONUP:
|
||||||
|
{
|
||||||
|
SetForegroundWindow (hWnd);
|
||||||
|
ShowPopupMenu(hWnd, NULL, -1);
|
||||||
|
PostMessage (hWnd, WM_APP + 1, 0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WM_TIMER:
|
||||||
|
{
|
||||||
|
switch(wParam)
|
||||||
|
{
|
||||||
|
case IDT_GRACEFUL_SHUTDOWN_TIMER:
|
||||||
|
{
|
||||||
|
g_GracefulShutdownEndtime = 0;
|
||||||
|
PostMessage (hWnd, WM_CLOSE, 0, 0); // exit
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case IDT_GRACEFUL_TUNNELCHECK_TIMER:
|
||||||
|
{
|
||||||
|
if (i2p::tunnel::tunnels.CountTransitTunnels() == 0)
|
||||||
|
PostMessage (hWnd, WM_CLOSE, 0, 0);
|
||||||
|
else
|
||||||
|
SetTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER, 1000, nullptr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case FRAME_UPDATE_TIMER:
|
||||||
|
{
|
||||||
|
InvalidateRect(hWnd, NULL, TRUE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WM_PAINT:
|
||||||
|
{
|
||||||
|
HDC hDC;
|
||||||
|
PAINTSTRUCT ps;
|
||||||
|
RECT rp;
|
||||||
|
HFONT hFont;
|
||||||
|
std::stringstream s; PrintMainWindowText (s);
|
||||||
|
hDC = BeginPaint (hWnd, &ps);
|
||||||
|
GetClientRect(hWnd, &rp);
|
||||||
|
SetTextColor(hDC, 0x00D43B69);
|
||||||
|
hFont = CreateFont(18,0,0,0,0,0,0,0,DEFAULT_CHARSET,0,0,0,0,TEXT("Times New Roman"));
|
||||||
|
SelectObject(hDC,hFont);
|
||||||
|
DrawText(hDC, TEXT(s.str().c_str()), s.str().length(), &rp, DT_CENTER|DT_VCENTER);
|
||||||
|
DeleteObject(hFont);
|
||||||
|
EndPaint(hWnd, &ps);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
if (uMsg == s_uTaskbarRestart)
|
||||||
|
AddTrayIcon (hWnd, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return DefWindowProc( hWnd, uMsg, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StartWin32App ()
|
||||||
|
{
|
||||||
|
if (FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd")))
|
||||||
|
{
|
||||||
|
MessageBox(NULL, TEXT("I2Pd is running already"), TEXT("Warning"), MB_OK);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// register main window
|
||||||
|
auto hInst = GetModuleHandle(NULL);
|
||||||
|
WNDCLASSEX wclx;
|
||||||
|
memset (&wclx, 0, sizeof(wclx));
|
||||||
|
wclx.cbSize = sizeof(wclx);
|
||||||
|
wclx.style = 0;
|
||||||
|
wclx.lpfnWndProc = WndProc;
|
||||||
|
//wclx.cbClsExtra = 0;
|
||||||
|
//wclx.cbWndExtra = 0;
|
||||||
|
wclx.hInstance = hInst;
|
||||||
|
wclx.hIcon = LoadIcon (hInst, MAKEINTRESOURCE(MAINICON));
|
||||||
|
wclx.hCursor = LoadCursor (NULL, IDC_ARROW);
|
||||||
|
//wclx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
|
||||||
|
wclx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
||||||
|
wclx.lpszMenuName = NULL;
|
||||||
|
wclx.lpszClassName = I2PD_WIN32_CLASSNAME;
|
||||||
|
RegisterClassEx (&wclx);
|
||||||
|
// create new window
|
||||||
|
if (!CreateWindow(I2PD_WIN32_CLASSNAME, TEXT("i2pd"), WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, 100, 100, 350, 210, NULL, NULL, hInst, NULL))
|
||||||
|
{
|
||||||
|
MessageBox(NULL, "Failed to create main window", TEXT("Warning!"), MB_ICONERROR | MB_OK | MB_TOPMOST);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SubscribeToEvents();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RunWin32App ()
|
||||||
|
{
|
||||||
|
MSG msg;
|
||||||
|
while (GetMessage (&msg, NULL, 0, 0 ))
|
||||||
|
{
|
||||||
|
TranslateMessage (&msg);
|
||||||
|
DispatchMessage (&msg);
|
||||||
|
}
|
||||||
|
return msg.wParam;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StopWin32App ()
|
||||||
|
{
|
||||||
|
HWND hWnd = FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd"));
|
||||||
|
if (hWnd)
|
||||||
|
PostMessage (hWnd, WM_COMMAND, MAKEWPARAM(ID_EXIT, 0), 0);
|
||||||
|
// UnSubscribeFromEvents(); // TODO: understand why unsubscribing crashes app
|
||||||
|
UnregisterClass (I2PD_WIN32_CLASSNAME, GetModuleHandle(NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GracefulShutdown ()
|
||||||
|
{
|
||||||
|
HWND hWnd = FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd"));
|
||||||
|
if (hWnd)
|
||||||
|
PostMessage (hWnd, WM_COMMAND, MAKEWPARAM(ID_GRACEFUL_SHUTDOWN, 0), 0);
|
||||||
|
return hWnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StopGracefulShutdown ()
|
||||||
|
{
|
||||||
|
HWND hWnd = FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd"));
|
||||||
|
if (hWnd)
|
||||||
|
PostMessage (hWnd, WM_COMMAND, MAKEWPARAM(ID_STOP_GRACEFUL_SHUTDOWN, 0), 0);
|
||||||
|
return hWnd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,16 +1,27 @@
|
|||||||
#ifndef WIN32APP_H__
|
/*
|
||||||
#define WIN32APP_H__
|
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||||
|
*
|
||||||
#define I2PD_WIN32_CLASSNAME "i2pd main window"
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
|
*
|
||||||
namespace i2p
|
* See full license text in LICENSE file at top of project tree
|
||||||
{
|
*/
|
||||||
namespace win32
|
|
||||||
{
|
#ifndef WIN32APP_H__
|
||||||
bool StartWin32App ();
|
#define WIN32APP_H__
|
||||||
void StopWin32App ();
|
|
||||||
int RunWin32App ();
|
#define I2PD_WIN32_CLASSNAME "i2pd main window"
|
||||||
bool GracefulShutdown ();
|
|
||||||
}
|
namespace i2p
|
||||||
}
|
{
|
||||||
#endif // WIN32APP_H__
|
namespace win32
|
||||||
|
{
|
||||||
|
extern DWORD g_GracefulShutdownEndtime;
|
||||||
|
|
||||||
|
bool StartWin32App ();
|
||||||
|
void StopWin32App ();
|
||||||
|
int RunWin32App ();
|
||||||
|
bool GracefulShutdown ();
|
||||||
|
bool StopGracefulShutdown ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // WIN32APP_H__
|
||||||
|
|||||||
86
Win32/Win32NetState.cpp
Normal file
86
Win32/Win32NetState.cpp
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013-2020, 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if WINVER != 0x0501 // supported since Vista
|
||||||
|
#include "Win32NetState.h"
|
||||||
|
#include <windows.h>
|
||||||
|
#include "Log.h"
|
||||||
|
|
||||||
|
IUnknown *pUnknown = nullptr;
|
||||||
|
INetworkListManager *pNetworkListManager = nullptr;
|
||||||
|
IConnectionPointContainer *pCPContainer = nullptr;
|
||||||
|
IConnectionPoint *pConnectPoint = nullptr;
|
||||||
|
DWORD Cookie = 0;
|
||||||
|
|
||||||
|
void SubscribeToEvents()
|
||||||
|
{
|
||||||
|
LogPrint(eLogInfo, "NetState: Trying to subscribe to NetworkListManagerEvents");
|
||||||
|
CoInitialize(NULL);
|
||||||
|
|
||||||
|
HRESULT Result = CoCreateInstance(CLSID_NetworkListManager, NULL, CLSCTX_ALL, IID_IUnknown, (void **)&pUnknown);
|
||||||
|
if (SUCCEEDED(Result))
|
||||||
|
{
|
||||||
|
Result = pUnknown->QueryInterface(IID_INetworkListManager, (void **)&pNetworkListManager);
|
||||||
|
if (SUCCEEDED(Result))
|
||||||
|
{
|
||||||
|
VARIANT_BOOL IsConnect = VARIANT_FALSE;
|
||||||
|
Result = pNetworkListManager->IsConnectedToInternet(&IsConnect);
|
||||||
|
if (SUCCEEDED(Result)) {
|
||||||
|
i2p::transport::transports.SetOnline (true);
|
||||||
|
LogPrint(eLogInfo, "NetState: Current state: ", IsConnect == VARIANT_TRUE ? "connected" : "disconnected");
|
||||||
|
}
|
||||||
|
|
||||||
|
Result = pNetworkListManager->QueryInterface(IID_IConnectionPointContainer, (void **)&pCPContainer);
|
||||||
|
if (SUCCEEDED(Result))
|
||||||
|
{
|
||||||
|
Result = pCPContainer->FindConnectionPoint(IID_INetworkListManagerEvents, &pConnectPoint);
|
||||||
|
if(SUCCEEDED(Result))
|
||||||
|
{
|
||||||
|
CNetworkListManagerEvent *NetEvent = new CNetworkListManagerEvent;
|
||||||
|
Result = pConnectPoint->Advise((IUnknown *)NetEvent, &Cookie);
|
||||||
|
if (SUCCEEDED(Result))
|
||||||
|
LogPrint(eLogInfo, "NetState: Successfully subscribed to NetworkListManagerEvent messages");
|
||||||
|
else
|
||||||
|
LogPrint(eLogError, "NetState: Unable to subscribe to NetworkListManagerEvent messages");
|
||||||
|
} else
|
||||||
|
LogPrint(eLogError, "NetState: Unable to find interface connection point");
|
||||||
|
} else
|
||||||
|
LogPrint(eLogError, "NetState: Unable to query NetworkListManager interface");
|
||||||
|
} else
|
||||||
|
LogPrint(eLogError, "NetState: Unable to query global interface");
|
||||||
|
} else
|
||||||
|
LogPrint(eLogError, "NetState: Unable to create INetworkListManager interface");
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnSubscribeFromEvents()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (pConnectPoint) {
|
||||||
|
pConnectPoint->Unadvise(Cookie);
|
||||||
|
pConnectPoint->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pCPContainer)
|
||||||
|
pCPContainer->Release();
|
||||||
|
|
||||||
|
if (pNetworkListManager)
|
||||||
|
pNetworkListManager->Release();
|
||||||
|
|
||||||
|
if (pUnknown)
|
||||||
|
pUnknown->Release();
|
||||||
|
|
||||||
|
CoUninitialize();
|
||||||
|
}
|
||||||
|
catch (std::exception& ex)
|
||||||
|
{
|
||||||
|
LogPrint (eLogError, "NetState: Received exception: ", ex.what ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // WINVER
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user