mirror of
https://github.com/savoirfairelinux/opendht.git
synced 2026-11-08 20:37:23 +00:00
Compare commits
1983
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3d6d48516 | ||
|
|
799ee01d2c | ||
|
|
6d1819acb9 | ||
|
|
d6ee44b640 | ||
|
|
383c53d71f | ||
|
|
833c647524 | ||
|
|
7f6f50411c | ||
|
|
1c7089c055 | ||
|
|
4641780fe6 | ||
|
|
c67df2ead3 | ||
|
|
5f743344e5 | ||
|
|
56442acc84 | ||
|
|
41c9104b8a | ||
|
|
126f0ca966 | ||
|
|
adeca7fa1f | ||
|
|
5527b6553d | ||
|
|
5a6b9db60c | ||
|
|
8f1c075897 | ||
|
|
fca11e621c | ||
|
|
38a7234ae3 | ||
|
|
115a0c683f | ||
|
|
61b7bed0f8 | ||
|
|
dcd8ec7394 | ||
|
|
51880afcce | ||
|
|
db3a4e52b2 | ||
|
|
af8e66b813 | ||
|
|
9646eb9374 | ||
|
|
0df2fbe5de | ||
|
|
c39eceeddf | ||
|
|
11396c315e | ||
|
|
955af06e29 | ||
|
|
e207e59226 | ||
|
|
0302cee8d5 | ||
|
|
8f4031c8cb | ||
|
|
f72d1ffa64 | ||
|
|
90c65fb0be | ||
|
|
c8eb704a92 | ||
|
|
4c5e26922c | ||
|
|
724c3e431c | ||
|
|
a9ddf49ecc | ||
|
|
de3dba7ce4 | ||
|
|
cd9eda6511 | ||
|
|
ed972fd365 | ||
|
|
75834fb08f | ||
|
|
9991c976ff | ||
|
|
92b08bbbc9 | ||
|
|
4f9b50dd60 | ||
|
|
d6d73efa37 | ||
|
|
19a4729bf2 | ||
|
|
405b8739ab | ||
|
|
7ed11a02ae | ||
|
|
3442e0acd9 | ||
|
|
59f86861d2 | ||
|
|
091d01a799 | ||
|
|
1ed45cdcf9 | ||
|
|
c40ba83cb3 | ||
|
|
7dc5aa9dfd | ||
|
|
808eb688b3 | ||
|
|
12b75a612c | ||
|
|
ac79dc5761 | ||
|
|
fe6676f2e6 | ||
|
|
7915b987ec | ||
|
|
29bdc1a46f | ||
|
|
e0d32e80fb | ||
|
|
e753d200c6 | ||
|
|
85fd395c52 | ||
|
|
0f146a354b | ||
|
|
9fcaca9232 | ||
|
|
1bc65bc5f5 | ||
|
|
478d28a326 | ||
|
|
4d544d53a8 | ||
|
|
694c44bbec | ||
|
|
16915ff424 | ||
|
|
a22566599c | ||
|
|
f9b2285593 | ||
|
|
9c2268fa41 | ||
|
|
d4e6fd545a | ||
|
|
dfd0a09361 | ||
|
|
0afa40820a | ||
|
|
f984a6a69a | ||
|
|
b4bbc6ae38 | ||
|
|
bc98d974cb | ||
|
|
785e54838b | ||
|
|
bb7611fc3d | ||
|
|
f675a6bdd3 | ||
|
|
21a71b1559 | ||
|
|
ca5455302d | ||
|
|
f206a8ac9e | ||
|
|
0c4cec66ff | ||
|
|
a03124d889 | ||
|
|
9691746fde | ||
|
|
d7c9311014 | ||
|
|
290e8594b6 | ||
|
|
c59f951e5a | ||
|
|
6919a34f88 | ||
|
|
3480bb1098 | ||
|
|
27b8c870f3 | ||
|
|
be43212a73 | ||
|
|
f0b9bc56a6 | ||
|
|
5654816f5a | ||
|
|
b0fb476d97 | ||
|
|
fada8d067d | ||
|
|
90022ee3e8 | ||
|
|
de68a47a16 | ||
|
|
714af68292 | ||
|
|
e6768edaf0 | ||
|
|
6145e54543 | ||
|
|
ac51c2300e | ||
|
|
f590980c3e | ||
|
|
5dbade3d0b | ||
|
|
2028d4b13e | ||
|
|
a6f7b49b92 | ||
|
|
c2415f38fa | ||
|
|
9cf717ab43 | ||
|
|
d4589bb6f6 | ||
|
|
484878aec3 | ||
|
|
ace3a94e08 | ||
|
|
5d771e119c | ||
|
|
8dcc695947 | ||
|
|
1ae28e07b6 | ||
|
|
2de0737443 | ||
|
|
bc32f848cb | ||
|
|
edee6d63d6 | ||
|
|
af3f6f40bb | ||
|
|
32f72c245d | ||
|
|
043ef8da72 | ||
|
|
6812b733bb | ||
|
|
2ec28dcb83 | ||
|
|
db000a2f88 | ||
|
|
59880df60e | ||
|
|
75f1b07e7a | ||
|
|
f191c4f636 | ||
|
|
1b2bc7b1e3 | ||
|
|
a89e4d626e | ||
|
|
dbbfdaab0f | ||
|
|
536f30ec84 | ||
|
|
b7aec1310c | ||
|
|
31d76b1080 | ||
|
|
2fbafc3591 | ||
|
|
19fe34415a | ||
|
|
2a50f85a30 | ||
|
|
26b71ab993 | ||
|
|
04a78445c6 | ||
|
|
fa19897016 | ||
|
|
3806355509 | ||
|
|
3f6738185b | ||
|
|
e58d09e5da | ||
|
|
71cd9d22c6 | ||
|
|
6c58d4f2e9 | ||
|
|
4070eecea7 | ||
|
|
ffb53ceb1d | ||
|
|
0c00544798 | ||
|
|
471d443e4a | ||
|
|
ba9a445263 | ||
|
|
056bc2850b | ||
|
|
b668d6ad68 | ||
|
|
29fab5560f | ||
|
|
8aef63ce4a | ||
|
|
1ea7a2994e | ||
|
|
208b3fa6fe | ||
|
|
d9ff95da49 | ||
|
|
c8a0b443f3 | ||
|
|
00e595d737 | ||
|
|
a178a8f820 | ||
|
|
ec359d50fb | ||
|
|
3c5aeccd53 | ||
|
|
f99fe16a46 | ||
|
|
43dc7cf222 | ||
|
|
ad549098f8 | ||
|
|
6795ba3838 | ||
|
|
efb5f8e36a | ||
|
|
8aa24f1289 | ||
|
|
14d882d220 | ||
|
|
f4c6c54a08 | ||
|
|
4dc7a5ca0a | ||
|
|
40fdd04cb3 | ||
|
|
a24dedd225 | ||
|
|
ae2445fd07 | ||
|
|
7ba53cb4b3 | ||
|
|
fbff8389c5 | ||
|
|
827fdacc4b | ||
|
|
3efc27e9f3 | ||
|
|
f7104199e3 | ||
|
|
5c0fbf7e68 | ||
|
|
55c595330a | ||
|
|
d3fc75589f | ||
|
|
f8605f45f7 | ||
|
|
d541b17fe1 | ||
|
|
29eb1d97f8 | ||
|
|
623b1c4585 | ||
|
|
74f7d7811e | ||
|
|
7774331a20 | ||
|
|
aae30c5891 | ||
|
|
41760e4b65 | ||
|
|
9ca0de250b | ||
|
|
d0156b4637 | ||
|
|
f6cca88593 | ||
|
|
c6da80573e | ||
|
|
5527869d13 | ||
|
|
5c599b2feb | ||
|
|
122d814a33 | ||
|
|
a1a8adbe4b | ||
|
|
5a370cabb5 | ||
|
|
639ae26ac1 | ||
|
|
896b6de60c | ||
|
|
d025f9d1b5 | ||
|
|
0b029bc0bf | ||
|
|
2256434ec7 | ||
|
|
0b4caeda73 | ||
|
|
18a1a1c47f | ||
|
|
6702956521 | ||
|
|
c996220685 | ||
|
|
df85abfabe | ||
|
|
e6c760ec6b | ||
|
|
a77dc855f0 | ||
|
|
2524e167d9 | ||
|
|
638d1c91f1 | ||
|
|
ba4058dfd9 | ||
|
|
ab90a170af | ||
|
|
f780499241 | ||
|
|
91709bc999 | ||
|
|
72725dc5c1 | ||
|
|
3098439db2 | ||
|
|
6441a09e09 | ||
|
|
57e7699723 | ||
|
|
17e1cfbbbe | ||
|
|
bfe1311ca8 | ||
|
|
537a1650f2 | ||
|
|
8c852c20e7 | ||
|
|
1da46382d3 | ||
|
|
4953888af3 | ||
|
|
22de868c6c | ||
|
|
0998707e03 | ||
|
|
b5fcd49b7b | ||
|
|
81cdd9650b | ||
|
|
e028cea687 | ||
|
|
17ed4a9e26 | ||
|
|
3ff318b9e4 | ||
|
|
0c11c25c66 | ||
|
|
0d31139402 | ||
|
|
e970cac4d7 | ||
|
|
7c8eecd745 | ||
|
|
4d87c2267b | ||
|
|
88b2439deb | ||
|
|
766a06c69a | ||
|
|
aa502553bb | ||
|
|
05ff4205d2 | ||
|
|
9d937bb277 | ||
|
|
1621515b3c | ||
|
|
30261e70ad | ||
|
|
c2a4f667a7 | ||
|
|
05b95331d8 | ||
|
|
c986f44704 | ||
|
|
7f62073d93 | ||
|
|
1a562962ba | ||
|
|
065be0621d | ||
|
|
6c085ed975 | ||
|
|
4c5776f9d2 | ||
|
|
c84fac6823 | ||
|
|
1778f64048 | ||
|
|
d1000635dd | ||
|
|
1273b062e1 | ||
|
|
961b96ca3d | ||
|
|
bcb29c3588 | ||
|
|
5d9395b16f | ||
|
|
23a75a3f50 | ||
|
|
7b87d32c0a | ||
|
|
d5f4f81566 | ||
|
|
a69863da4e | ||
|
|
4d70c214b0 | ||
|
|
11afc2b468 | ||
|
|
879e4f4e02 | ||
|
|
b5b4eea253 | ||
|
|
67adc09d52 | ||
|
|
e2b39dd3a0 | ||
|
|
9dc0ce7392 | ||
|
|
1a025c5cea | ||
|
|
732e9af439 | ||
|
|
4e5666fc3a | ||
|
|
b41f5b04f5 | ||
|
|
cfa21df8b3 | ||
|
|
6ac451a5de | ||
|
|
9efb8bb662 | ||
|
|
f0aee6f909 | ||
|
|
f72bfc9df2 | ||
|
|
c9ebfd1119 | ||
|
|
cf1874baaa | ||
|
|
c221cfdafe | ||
|
|
31b84e6d92 | ||
|
|
ae05bc8ea5 | ||
|
|
acbf7cfcf0 | ||
|
|
8250116946 | ||
|
|
45e9458373 | ||
|
|
2623b86274 | ||
|
|
2e1dda8189 | ||
|
|
e26256b819 | ||
|
|
18e59ad7c8 | ||
|
|
a7a3d23f50 | ||
|
|
323a2b5093 | ||
|
|
563811ebf8 | ||
|
|
190c7f2063 | ||
|
|
6158751a32 | ||
|
|
4c7d126782 | ||
|
|
3e24ea1212 | ||
|
|
a7ac979da6 | ||
|
|
87f48f0a28 | ||
|
|
15586a3cb1 | ||
|
|
2195c0ed3e | ||
|
|
af1e904895 | ||
|
|
a4fbf7ced0 | ||
|
|
1b7e0679d5 | ||
|
|
9292c1116c | ||
|
|
a2afb5bb31 | ||
|
|
15fbfdebcc | ||
|
|
15eb221cd1 | ||
|
|
7d3781acc8 | ||
|
|
f3100a6752 | ||
|
|
e3cd2a9d02 | ||
|
|
19ccc3a327 | ||
|
|
4587c0925f | ||
|
|
fc96245891 | ||
|
|
a27815b4a9 | ||
|
|
5daaf372b4 | ||
|
|
cc51d2684c | ||
|
|
28b5893239 | ||
|
|
c754b5cfce | ||
|
|
b0c1249710 | ||
|
|
c41ee417fb | ||
|
|
47ba0415ca | ||
|
|
99857e25cc | ||
|
|
7f832ba9d5 | ||
|
|
8c4a894f93 | ||
|
|
dd3de412fb | ||
|
|
4ad5e6c9ba | ||
|
|
b445818521 | ||
|
|
7c14ea1730 | ||
|
|
7bf9f861a5 | ||
|
|
4cf5c87761 | ||
|
|
e1c6fec581 | ||
|
|
31da5b91fa | ||
|
|
01bcb1a750 | ||
|
|
35348bf77f | ||
|
|
ae58ea8312 | ||
|
|
f30d21e96f | ||
|
|
7628aab230 | ||
|
|
f4cba56b3c | ||
|
|
7155dc4fb7 | ||
|
|
c961e2e99f | ||
|
|
2fb8e1c7ee | ||
|
|
4d689ef51a | ||
|
|
face9f12b2 | ||
|
|
c92f553a24 | ||
|
|
8564cf3b86 | ||
|
|
e7c810d5d6 | ||
|
|
e76c80b8be | ||
|
|
69f8969883 | ||
|
|
bc200cd76d | ||
|
|
187c7dbce3 | ||
|
|
81131f1c4e | ||
|
|
ce9445a93f | ||
|
|
16372cba0c | ||
|
|
a2cbef315a | ||
|
|
bc7c43dfd1 | ||
|
|
771b300622 | ||
|
|
5ffb5c628d | ||
|
|
cfa4ce61b4 | ||
|
|
5f15043f03 | ||
|
|
f445b2536d | ||
|
|
6c2f791c4d | ||
|
|
780726c66d | ||
|
|
8703cfdaee | ||
|
|
1c536cbf5e | ||
|
|
a39e2443a0 | ||
|
|
57528e7aab | ||
|
|
e9d70f204a | ||
|
|
ceb35c05c1 | ||
|
|
55ccec5871 | ||
|
|
95028e3770 | ||
|
|
abede1e72a | ||
|
|
859d8dc9cf | ||
|
|
1a67140463 | ||
|
|
9ae3d3c4d4 | ||
|
|
d3508c80f5 | ||
|
|
6c43efa4da | ||
|
|
c5c4c217dd | ||
|
|
5cfcf4861d | ||
|
|
8597267c63 | ||
|
|
1b81d707f3 | ||
|
|
7791163d49 | ||
|
|
56db1bba19 | ||
|
|
a7092c2dc7 | ||
|
|
fc95c85983 | ||
|
|
3490b19fe3 | ||
|
|
4a20814d59 | ||
|
|
707fb39975 | ||
|
|
cbb2406a12 | ||
|
|
7764620460 | ||
|
|
5af0f0c3d0 | ||
|
|
ec8beec427 | ||
|
|
7b8ebbc54d | ||
|
|
dc46c2274c | ||
|
|
052ed1f58c | ||
|
|
9096f650f5 | ||
|
|
900655b11f | ||
|
|
2900b1d17f | ||
|
|
69e71dbeaa | ||
|
|
3793423b2f | ||
|
|
b012878488 | ||
|
|
c1005fa334 | ||
|
|
0f9f623b31 | ||
|
|
2cfebdf9fa | ||
|
|
481b1cb3b4 | ||
|
|
7f1d960a56 | ||
|
|
91e1e942ab | ||
|
|
ae89365f23 | ||
|
|
93cd8d541c | ||
|
|
9487bc5e42 | ||
|
|
10d48137a9 | ||
|
|
5b15fdeeef | ||
|
|
3ba43c80a2 | ||
|
|
16333f4224 | ||
|
|
0864ac9a39 | ||
|
|
616d6fe4b8 | ||
|
|
dea4b202bf | ||
|
|
dc2f354652 | ||
|
|
87041e301f | ||
|
|
189f94442e | ||
|
|
c5c3695612 | ||
|
|
6a7ad871e2 | ||
|
|
99890fca64 | ||
|
|
2dbf57d36b | ||
|
|
5438d50578 | ||
|
|
548255073f | ||
|
|
abaf091772 | ||
|
|
7094b79714 | ||
|
|
1f672773ba | ||
|
|
374fdd6cc9 | ||
|
|
54b1314ab8 | ||
|
|
51c35d8b54 | ||
|
|
fea736428e | ||
|
|
2d358adf0e | ||
|
|
26c2e16a42 | ||
|
|
e8b385c220 | ||
|
|
c1e2a368a8 | ||
|
|
c62f27c929 | ||
|
|
ec0c3d4d25 | ||
|
|
17a1178054 | ||
|
|
6256c576c9 | ||
|
|
9565adf693 | ||
|
|
c10aa32596 | ||
|
|
21c294f352 | ||
|
|
4654f78e08 | ||
|
|
38644fc934 | ||
|
|
fa8dc4c835 | ||
|
|
6dab10cac2 | ||
|
|
77a1c27f2b | ||
|
|
e8577bfd05 | ||
|
|
c0327a9881 | ||
|
|
2a1eacc949 | ||
|
|
e705bebc25 | ||
|
|
a330762f40 | ||
|
|
e7972ae897 | ||
|
|
8d2d40cb7f | ||
|
|
aceb548c26 | ||
|
|
afce4639e1 | ||
|
|
e5d248bcd0 | ||
|
|
0fbebf8e03 | ||
|
|
24efa37846 | ||
|
|
c0f14ca59d | ||
|
|
05a66cc1f1 | ||
|
|
ed6df8869c | ||
|
|
b7d410e1aa | ||
|
|
77a377a406 | ||
|
|
e0b6f152f1 | ||
|
|
dd3c789916 | ||
|
|
333087bcba | ||
|
|
5b87090cab | ||
|
|
fffee634f1 | ||
|
|
c4aa272dbb | ||
|
|
4ce78bdb4d | ||
|
|
f331f39c77 | ||
|
|
17fb92654e | ||
|
|
294034f00f | ||
|
|
36857c1f3d | ||
|
|
7e9f5303bd | ||
|
|
c2780b1ca9 | ||
|
|
16b798324d | ||
|
|
bc850bf00c | ||
|
|
0d8d302128 | ||
|
|
97013badfa | ||
|
|
31185c24f0 | ||
|
|
2f6f8e2ac1 | ||
|
|
8f141951ce | ||
|
|
43d111b7dc | ||
|
|
620ba2a89b | ||
|
|
d1598380db | ||
|
|
69fe399db0 | ||
|
|
a60dde2e13 | ||
|
|
201982b1a9 | ||
|
|
83f2b272a7 | ||
|
|
7e47d0458a | ||
|
|
f6d26757fa | ||
|
|
476d0bca4f | ||
|
|
259986e786 | ||
|
|
8ff0c979d4 | ||
|
|
913cb543c8 | ||
|
|
b380c9ffba | ||
|
|
8c717085b4 | ||
|
|
48742a1058 | ||
|
|
fe11285a03 | ||
|
|
fb34770a54 | ||
|
|
4a909419e9 | ||
|
|
dab7099369 | ||
|
|
9c84e59bbf | ||
|
|
eae383b3ed | ||
|
|
28bf78408d | ||
|
|
7ed6f74e6b | ||
|
|
cf6d9fd8c2 | ||
|
|
5df34bb752 | ||
|
|
bebb66490d | ||
|
|
5f361748eb | ||
|
|
984745624c | ||
|
|
f9e860fdc8 | ||
|
|
06ea3d23e5 | ||
|
|
f0857a5925 | ||
|
|
5a993fb902 | ||
|
|
8a18c591e9 | ||
|
|
9daa7ec283 | ||
|
|
01f2ad9ea4 | ||
|
|
2dd4cde035 | ||
|
|
cb0005d0eb | ||
|
|
eca340b785 | ||
|
|
cdd035b801 | ||
|
|
cb7d7f6614 | ||
|
|
f5a6801ab9 | ||
|
|
0049ba92e6 | ||
|
|
7838e7de0b | ||
|
|
e2d7dbc293 | ||
|
|
090da49fed | ||
|
|
22594396d2 | ||
|
|
0c8d0e2e0d | ||
|
|
18793cee3b | ||
|
|
0bde82a218 | ||
|
|
bc84655f42 | ||
|
|
ed642b0337 | ||
|
|
f710df9675 | ||
|
|
42398a541d | ||
|
|
429a5ad1d4 | ||
|
|
01bc43af35 | ||
|
|
555487012b | ||
|
|
697d554edb | ||
|
|
8edd67834c | ||
|
|
80f04430c3 | ||
|
|
e4853bd29c | ||
|
|
6863800cfb | ||
|
|
1f387a0e95 | ||
|
|
142aee12a7 | ||
|
|
95a65ce565 | ||
|
|
144fa7ddc8 | ||
|
|
b77fd2336f | ||
|
|
c0e117871e | ||
|
|
3c88eaa602 | ||
|
|
8d131449a8 | ||
|
|
fee7e5dd30 | ||
|
|
4c2a974433 | ||
|
|
a483205976 | ||
|
|
96df1d4339 | ||
|
|
432f387423 | ||
|
|
3abb9114ba | ||
|
|
727c5f0e8c | ||
|
|
f86abfaf22 | ||
|
|
f84131c820 | ||
|
|
28de983166 | ||
|
|
4b28278cb1 | ||
|
|
653a4309bd | ||
|
|
48b763f11b | ||
|
|
cbfa12a921 | ||
|
|
3e0d4cd703 | ||
|
|
4bff6d392a | ||
|
|
712b7218a9 | ||
|
|
5a8e252e32 | ||
|
|
b5be4549dc | ||
|
|
bf8fb6f321 | ||
|
|
08cf212c53 | ||
|
|
fc2a597bb2 | ||
|
|
7212b7a28b | ||
|
|
5c610895fe | ||
|
|
a567ca1dc1 | ||
|
|
97b20705a1 | ||
|
|
431da16ba6 | ||
|
|
09818d779e | ||
|
|
934e36ba13 | ||
|
|
2e47b9bab5 | ||
|
|
f938d486d5 | ||
|
|
76b4da089e | ||
|
|
2a72277194 | ||
|
|
e8d96d3b42 | ||
|
|
c92d236c1b | ||
|
|
b680407e9a | ||
|
|
6caf4b0d0b | ||
|
|
7f65fdc7f9 | ||
|
|
b00dfdf572 | ||
|
|
2b1d2fc3f6 | ||
|
|
a0566cf695 | ||
|
|
a3f2b055a1 | ||
|
|
01bf8f9303 | ||
|
|
22741436e3 | ||
|
|
d958b0ea5f | ||
|
|
6c96a47f13 | ||
|
|
15403c0f6e | ||
|
|
0eee567c4e | ||
|
|
e8843076d9 | ||
|
|
3a6ce2f499 | ||
|
|
299e632795 | ||
|
|
12aee01ffe | ||
|
|
83c5014a36 | ||
|
|
e9377f2eb6 | ||
|
|
083a28f5d8 | ||
|
|
ab7f36ff85 | ||
|
|
8856a70af5 | ||
|
|
f24b6ca3e3 | ||
|
|
ccfb76d9b4 | ||
|
|
104a9e3b28 | ||
|
|
d77c868c55 | ||
|
|
347f404a92 | ||
|
|
ed1b0220e5 | ||
|
|
739f4d0c0d | ||
|
|
8ec0ab0ae4 | ||
|
|
55c56a5203 | ||
|
|
7558444ae0 | ||
|
|
9bc2ceca68 | ||
|
|
ac10afac44 | ||
|
|
128af52ea2 | ||
|
|
8ed2bf50b3 | ||
|
|
dcdfeb0415 | ||
|
|
833dc7f5a7 | ||
|
|
d0510dcb72 | ||
|
|
6f87e9dbc3 | ||
|
|
223260bc7d | ||
|
|
c564810b8c | ||
|
|
f9ffb8650a | ||
|
|
e58fc4ef31 | ||
|
|
7fde8f63a8 | ||
|
|
166b5e352c | ||
|
|
77356b0539 | ||
|
|
c7d633e609 | ||
|
|
2576d95aa0 | ||
|
|
b31a29ea26 | ||
|
|
833bd069f4 | ||
|
|
39a186c47d | ||
|
|
c03af5f5a8 | ||
|
|
b03a7732e3 | ||
|
|
87f6f3df8b | ||
|
|
7270c353df | ||
|
|
63afc8c29a | ||
|
|
6a760a629c | ||
|
|
5068de7bc0 | ||
|
|
2a5f786f8b | ||
|
|
c690d060f9 | ||
|
|
bcf8ab5404 | ||
|
|
f7c8168f99 | ||
|
|
c37f0083d3 | ||
|
|
29e7768c9d | ||
|
|
72a06b1425 | ||
|
|
e4dacc64b5 | ||
|
|
20df5fe845 | ||
|
|
5b4f40229d | ||
|
|
6c4af9bfea | ||
|
|
ef26155b19 | ||
|
|
eb2269625f | ||
|
|
70c92d6cea | ||
|
|
fd7f644531 | ||
|
|
562bf7124c | ||
|
|
5b344cada7 | ||
|
|
7c4450fdb1 | ||
|
|
9b8358cdf1 | ||
|
|
7ae3b3af5b | ||
|
|
06d1865446 | ||
|
|
83495a94db | ||
|
|
8ee5485767 | ||
|
|
e7742489ca | ||
|
|
99fc97f3cf | ||
|
|
82d5db81e0 | ||
|
|
fbb90a71b7 | ||
|
|
316f3ccfa0 | ||
|
|
185158f460 | ||
|
|
baa9fcf820 | ||
|
|
04a383c4fb | ||
|
|
b7b6fb802d | ||
|
|
9575f84081 | ||
|
|
0b0442bd02 | ||
|
|
251fe8bf2f | ||
|
|
c211055bf9 | ||
|
|
fafe97194a | ||
|
|
d214bfebf5 | ||
|
|
d551b8a54c | ||
|
|
4a8397d71b | ||
|
|
366f76986f | ||
|
|
8072092b67 | ||
|
|
624f36467e | ||
|
|
561a660693 | ||
|
|
414e0a2e60 | ||
|
|
e0393ff8ad | ||
|
|
d26bfc58d8 | ||
|
|
d50aae0751 | ||
|
|
1a79ae45e4 | ||
|
|
c6f830f54d | ||
|
|
f018610ec9 | ||
|
|
119c162f49 | ||
|
|
144e254c40 | ||
|
|
6cc84312aa | ||
|
|
b3a7bdd880 | ||
|
|
a797ea9171 | ||
|
|
023b4aff61 | ||
|
|
86429aa360 | ||
|
|
fbea6afe5d | ||
|
|
18bcad33bd | ||
|
|
8bc43b4e47 | ||
|
|
25f3000a78 | ||
|
|
96992b803c | ||
|
|
500ae6ef54 | ||
|
|
b01d3cfca5 | ||
|
|
49f6dd3c9c | ||
|
|
4054ec6af0 | ||
|
|
3345c1b048 | ||
|
|
4b061d0886 | ||
|
|
c1f805e6e2 | ||
|
|
25c9375d95 | ||
|
|
84a9d899d5 | ||
|
|
7ea40c292b | ||
|
|
2954158bba | ||
|
|
3382195760 | ||
|
|
b67149c98c | ||
|
|
f34cb6440f | ||
|
|
9cf37eb865 | ||
|
|
c8bed06641 | ||
|
|
51aef18885 | ||
|
|
49a132a54b | ||
|
|
235c48c400 | ||
|
|
5465b08d76 | ||
|
|
a8e24e505d | ||
|
|
5c0a8986a7 | ||
|
|
460205eb78 | ||
|
|
208758ede3 | ||
|
|
de9e0906de | ||
|
|
692304c2e3 | ||
|
|
f837c13e31 | ||
|
|
5812b24d56 | ||
|
|
a72ddfdb03 | ||
|
|
e7b03e4d53 | ||
|
|
baf43d96ff | ||
|
|
0f9df9a62b | ||
|
|
630112601c | ||
|
|
b0500af062 | ||
|
|
6c985e8fda | ||
|
|
9c1e55279c | ||
|
|
593d024f51 | ||
|
|
a6e57d95d6 | ||
|
|
846f495202 | ||
|
|
6f603bfc0a | ||
|
|
9c106f9efe | ||
|
|
ff288fb4a3 | ||
|
|
17ff4c0224 | ||
|
|
45a2c6dadb | ||
|
|
477ff59e91 | ||
|
|
a001e0cafd | ||
|
|
ac63527e0b | ||
|
|
4b3d8d1231 | ||
|
|
d04a741d5e | ||
|
|
ac2b7deda8 | ||
|
|
c8b08b5ed1 | ||
|
|
f556987e47 | ||
|
|
2daf36c97e | ||
|
|
2a2555f0c2 | ||
|
|
bdfb0148e4 | ||
|
|
01be4d2eb8 | ||
|
|
93f7251805 | ||
|
|
a8718242cd | ||
|
|
297a23fb1f | ||
|
|
0d641161e8 | ||
|
|
27b3c0d47e | ||
|
|
149b33ef57 | ||
|
|
150e62de0b | ||
|
|
89bdfce840 | ||
|
|
bce41ecd69 | ||
|
|
4374d206ab | ||
|
|
b64820af59 | ||
|
|
0e509318bb | ||
|
|
bcc2baa94a | ||
|
|
bbed9d530d | ||
|
|
5a3612be98 | ||
|
|
8202c1f7d7 | ||
|
|
a200b0e14c | ||
|
|
bd32edc6c5 | ||
|
|
d0d3ef7394 | ||
|
|
712da15ae2 | ||
|
|
fcdc997d29 | ||
|
|
cf18effebb | ||
|
|
b8c2ff8a11 | ||
|
|
1fad1b9ce3 | ||
|
|
117fedb641 | ||
|
|
422f5a6c45 | ||
|
|
51a42aa2c8 | ||
|
|
be0baefee8 | ||
|
|
2ac5157ab8 | ||
|
|
fbe8834e04 | ||
|
|
89ff131750 | ||
|
|
feb498305f | ||
|
|
b00f56c94a | ||
|
|
2067cb052f | ||
|
|
51aeaeaeab | ||
|
|
c0bb8f065c | ||
|
|
126318956a | ||
|
|
9014c0a7bb | ||
|
|
728782921c | ||
|
|
d438d4ef1b | ||
|
|
2959709462 | ||
|
|
4a26168248 | ||
|
|
9c348b1df4 | ||
|
|
7e7654d1ae | ||
|
|
69ee1d212a | ||
|
|
21ec9129c3 | ||
|
|
b7185fa87c | ||
|
|
54acd67a1f | ||
|
|
045de8602c | ||
|
|
390fa48700 | ||
|
|
0d08f0d356 | ||
|
|
738424b1ae | ||
|
|
d123d8f768 | ||
|
|
a20a48506d | ||
|
|
063a2b4860 | ||
|
|
829d365475 | ||
|
|
11e6671e60 | ||
|
|
6fe473d3cc | ||
|
|
9f493b9572 | ||
|
|
2164c5ddc7 | ||
|
|
a57a4d55e8 | ||
|
|
b98cc78b42 | ||
|
|
789d41ff07 | ||
|
|
632403875c | ||
|
|
bc2737cdc9 | ||
|
|
0c6bfde1c5 | ||
|
|
cfeeaf7950 | ||
|
|
e31ebfea32 | ||
|
|
07efe8c76e | ||
|
|
5c78bf386d | ||
|
|
86088b043b | ||
|
|
fad8bb1631 | ||
|
|
9a56c099a1 | ||
|
|
8298c0c868 | ||
|
|
971f590941 | ||
|
|
1813d83273 | ||
|
|
e87d164f0b | ||
|
|
b9604807ea | ||
|
|
9f12b44179 | ||
|
|
0772d5435c | ||
|
|
368ee9e22e | ||
|
|
dd56bdd8e2 | ||
|
|
71b36e0643 | ||
|
|
61b43759df | ||
|
|
96e12e88c3 | ||
|
|
8da933c01a | ||
|
|
20c0069867 | ||
|
|
8942e0d00e | ||
|
|
bc760f9348 | ||
|
|
59702190a7 | ||
|
|
cb3e94d0be | ||
|
|
ab7b8f6027 | ||
|
|
6e5231047b | ||
|
|
777505434c | ||
|
|
18c3161261 | ||
|
|
1060038bdf | ||
|
|
9d427a6a80 | ||
|
|
f1dbac2aff | ||
|
|
a63c5d172b | ||
|
|
9c6de8cc16 | ||
|
|
fe9de44867 | ||
|
|
4bfd6bb545 | ||
|
|
120c017399 | ||
|
|
7fc7597564 | ||
|
|
f8d62dc6a7 | ||
|
|
fc3686d817 | ||
|
|
a692f7fbe2 | ||
|
|
cb349d9cf6 | ||
|
|
6266f88cd5 | ||
|
|
0a3320e2c8 | ||
|
|
d6ad67d8e2 | ||
|
|
19d0aa55f7 | ||
|
|
941e53425f | ||
|
|
10971f700f | ||
|
|
2e7767d192 | ||
|
|
baf811bfe9 | ||
|
|
76f0672e63 | ||
|
|
330ad8ed12 | ||
|
|
3252e6daa3 | ||
|
|
07599239a6 | ||
|
|
eac61678f4 | ||
|
|
078c1198b0 | ||
|
|
3d525030b9 | ||
|
|
3733fc2890 | ||
|
|
c40f35fa06 | ||
|
|
13661929d1 | ||
|
|
4e934f8902 | ||
|
|
125a05a5fc | ||
|
|
868c114bcb | ||
|
|
f0acc3e735 | ||
|
|
171e2b130b | ||
|
|
cb5a261b82 | ||
|
|
feb8bd0d4f | ||
|
|
3955368c01 | ||
|
|
b841ff1b83 | ||
|
|
7a04c0eb66 | ||
|
|
fe1659bd2f | ||
|
|
b90e14edd6 | ||
|
|
9e3e9e65e6 | ||
|
|
1c3132e0ef | ||
|
|
f3e304e661 | ||
|
|
2545282624 | ||
|
|
52c413f056 | ||
|
|
ab082543ea | ||
|
|
ff6be81929 | ||
|
|
bd994384f0 | ||
|
|
598e38dbf4 | ||
|
|
b1a35b82a2 | ||
|
|
e4516772ee | ||
|
|
c7844fc320 | ||
|
|
c7e2483160 | ||
|
|
37b12c114d | ||
|
|
77bd34ad9c | ||
|
|
44490bd9ca | ||
|
|
ca01568824 | ||
|
|
2b259639d2 | ||
|
|
84d322e428 | ||
|
|
6d5e3d8cd2 | ||
|
|
b56518f258 | ||
|
|
d807af3951 | ||
|
|
5f393490a5 | ||
|
|
efe869fe42 | ||
|
|
a31cecdeac | ||
|
|
4a4becdbec | ||
|
|
eef2838a12 | ||
|
|
a520657fac | ||
|
|
29003700db | ||
|
|
b143bc459d | ||
|
|
4d6e5ab877 | ||
|
|
248d2e3f73 | ||
|
|
0692bd9bc9 | ||
|
|
7f48c4808b | ||
|
|
32a443a1d8 | ||
|
|
6d0fcba3b5 | ||
|
|
76e2efb19b | ||
|
|
bbe1b9233f | ||
|
|
7558ca0dca | ||
|
|
38141322f1 | ||
|
|
6c5ea125ca | ||
|
|
d0362e58e0 | ||
|
|
1b3a40f345 | ||
|
|
38d854e6f4 | ||
|
|
e0241ccfbd | ||
|
|
d4311925b4 | ||
|
|
9aa56dd464 | ||
|
|
ec3b259ead | ||
|
|
332cab10ef | ||
|
|
85f2b618c7 | ||
|
|
bcf18fda53 | ||
|
|
6b78a4cad6 | ||
|
|
66d3a8aeaa | ||
|
|
958cb250a3 | ||
|
|
6a5724a868 | ||
|
|
11ac84de54 | ||
|
|
f65e8c1a1f | ||
|
|
c2fdf6ae92 | ||
|
|
91a81f64ac | ||
|
|
17b772e6cc | ||
|
|
75738ac28b | ||
|
|
84a2d771c1 | ||
|
|
f6273f9efc | ||
|
|
5881c91510 | ||
|
|
e09c55ca42 | ||
|
|
c737e4690e | ||
|
|
35a9d1f612 | ||
|
|
0f3396cd66 | ||
|
|
98cd6cc61e | ||
|
|
092cb9551e | ||
|
|
a2bbca792c | ||
|
|
5ec72e213e | ||
|
|
0f51115f44 | ||
|
|
683a111726 | ||
|
|
0fd4fed537 | ||
|
|
18aa6ddb7b | ||
|
|
0a436e17b0 | ||
|
|
763ffddf51 | ||
|
|
ef82652a1e | ||
|
|
4f03a5c64e | ||
|
|
5257ac314f | ||
|
|
0580a4e7dd | ||
|
|
fd1f2cd0e4 | ||
|
|
af940c63b1 | ||
|
|
15da502354 | ||
|
|
b7a9a52f97 | ||
|
|
6e10756e28 | ||
|
|
aad4ad03cf | ||
|
|
3d1e055c7b | ||
|
|
b02e9cfff7 | ||
|
|
453db29058 | ||
|
|
a8ba536d44 | ||
|
|
a238183031 | ||
|
|
73827cd34d | ||
|
|
876190a392 | ||
|
|
7fe321e4fc | ||
|
|
7f7075ca9a | ||
|
|
9b9f12d87e | ||
|
|
26ad6de3a4 | ||
|
|
ae91f8bd55 | ||
|
|
28eeee24ba | ||
|
|
671c55a01d | ||
|
|
a4f2e6efdd | ||
|
|
60831ee898 | ||
|
|
98dbe1cba7 | ||
|
|
6c705e525f | ||
|
|
96347d83e1 | ||
|
|
01d8e513a1 | ||
|
|
b5921b0d9d | ||
|
|
8ee7883c53 | ||
|
|
4240820463 | ||
|
|
7ebbf17f37 | ||
|
|
6821e5bd96 | ||
|
|
c4ec1e78e0 | ||
|
|
e86d717e81 | ||
|
|
db2b48d2af | ||
|
|
2b938110fd | ||
|
|
c647b19e91 | ||
|
|
8cd14646a6 | ||
|
|
d80aab9399 | ||
|
|
9b881d4e95 | ||
|
|
b4c3c2cba0 | ||
|
|
4519a23996 | ||
|
|
1ac4e08f45 | ||
|
|
5e92777702 | ||
|
|
4401e47002 | ||
|
|
215b44ca3b | ||
|
|
379fdae212 | ||
|
|
1e10e28fd7 | ||
|
|
dccdb84836 | ||
|
|
71599da8ea | ||
|
|
f418b3651f | ||
|
|
313f54c840 | ||
|
|
cdb721ade4 | ||
|
|
57ea09808e | ||
|
|
fec5a6ede7 | ||
|
|
5f8e9d9480 | ||
|
|
311a8981fc | ||
|
|
04850e1701 | ||
|
|
a30f1b8032 | ||
|
|
41f8b06743 | ||
|
|
1c74c2207f | ||
|
|
9319bec7b8 | ||
|
|
e5f2a6eef5 | ||
|
|
55f6c847b8 | ||
|
|
00cc4ebe28 | ||
|
|
aa93db4fa6 | ||
|
|
683570a3da | ||
|
|
43559577f6 | ||
|
|
ba2e179abe | ||
|
|
a5d29c904c | ||
|
|
989f32eb56 | ||
|
|
b66fa73c56 | ||
|
|
1ca181db1d | ||
|
|
1f3bf697c3 | ||
|
|
a1f8cc906c | ||
|
|
35c6a57526 | ||
|
|
39e69f813c | ||
|
|
8045c2cbf2 | ||
|
|
79a2f4280f | ||
|
|
769ce45740 | ||
|
|
75a14d3608 | ||
|
|
d523e9f259 | ||
|
|
3a3933e6d5 | ||
|
|
70f09ded3f | ||
|
|
824a1bf5fa | ||
|
|
e5b56cc0a5 | ||
|
|
36cf70f2a7 | ||
|
|
459c1344aa | ||
|
|
c0e376e15a | ||
|
|
71b9672b96 | ||
|
|
086f37a71a | ||
|
|
87e76a5e4e | ||
|
|
7f0a5dfda3 | ||
|
|
6d7f8004bf | ||
|
|
8168c39b63 | ||
|
|
d0fdbe5340 | ||
|
|
c7604fbb8d | ||
|
|
56773baa98 | ||
|
|
e6cf67c575 | ||
|
|
00ebc5e2bf | ||
|
|
ccef94c11b | ||
|
|
0066be49b9 | ||
|
|
592ed4ee90 | ||
|
|
353a6f9db6 | ||
|
|
7ebad7d3c5 | ||
|
|
14f6e6e5f3 | ||
|
|
9ce5b30931 | ||
|
|
92979fa96a | ||
|
|
0c1e7d2763 | ||
|
|
14a744f6cd | ||
|
|
3b26684a87 | ||
|
|
0a764ee440 | ||
|
|
7dda5a3d5a | ||
|
|
306d573223 | ||
|
|
20f4355b73 | ||
|
|
a2d7cd56a8 | ||
|
|
5cd53f3624 | ||
|
|
32eea55f10 | ||
|
|
f2f913f93f | ||
|
|
cb9fe9d1fe | ||
|
|
dcab32ef4f | ||
|
|
4948bd7ad3 | ||
|
|
4416b91461 | ||
|
|
378ae396f0 | ||
|
|
dc4a790a38 | ||
|
|
07dd8f5648 | ||
|
|
f7433a2289 | ||
|
|
ce4a65ff00 | ||
|
|
092f5cfef0 | ||
|
|
12a45e7558 | ||
|
|
18d359a7d2 | ||
|
|
c6bb67d1ac | ||
|
|
f4cd7636ce | ||
|
|
2dd4e640c4 | ||
|
|
9a301e3590 | ||
|
|
aa16b9a235 | ||
|
|
b2542a57df | ||
|
|
00c1be9cdc | ||
|
|
e414ca89c7 | ||
|
|
4d26537b93 | ||
|
|
00a9333de7 | ||
|
|
971f96bf95 | ||
|
|
f9a12a9dca | ||
|
|
c05a93ae54 | ||
|
|
ff83a6daab | ||
|
|
b7b5152348 | ||
|
|
a3768d1e50 | ||
|
|
94926cea22 | ||
|
|
82ca2d1993 | ||
|
|
3e148599c5 | ||
|
|
02d5592fdb | ||
|
|
328b306fd0 | ||
|
|
522a89b6f8 | ||
|
|
897b451f44 | ||
|
|
ba3a42697c | ||
|
|
5112a322de | ||
|
|
85961c6a62 | ||
|
|
1eb1f3d5ac | ||
|
|
726c138d90 | ||
|
|
1b6b1ca072 | ||
|
|
77da72c603 | ||
|
|
7fb2da7c54 | ||
|
|
6721a272ea | ||
|
|
246af2f065 | ||
|
|
57aba453b0 | ||
|
|
f54fcd768f | ||
|
|
5b81476baf | ||
|
|
5b510cadfb | ||
|
|
c794781644 | ||
|
|
2ef4999b0f | ||
|
|
6069fba18f | ||
|
|
37ddd8c7f9 | ||
|
|
23057a3082 | ||
|
|
dbdd3f8f63 | ||
|
|
751e79a250 | ||
|
|
7a961ffc51 | ||
|
|
bfada958b9 | ||
|
|
78d91a8e0d | ||
|
|
cd5dc38a57 | ||
|
|
1ff2eeb220 | ||
|
|
ffbb9e23ef | ||
|
|
af0a30c9c5 | ||
|
|
97d5e3875d | ||
|
|
2509e8bc5c | ||
|
|
e349912c07 | ||
|
|
a1fc266eac | ||
|
|
3e3679bebf | ||
|
|
e9cf16f361 | ||
|
|
3dca4db408 | ||
|
|
fb6d290692 | ||
|
|
96c60d39ed | ||
|
|
3a41f2ce56 | ||
|
|
5bd88533fe | ||
|
|
c48e24403b | ||
|
|
3cf2c092f2 | ||
|
|
aef8aa545b | ||
|
|
0c61a605ad | ||
|
|
e6b247bead | ||
|
|
6f1f18f128 | ||
|
|
f24458860d | ||
|
|
07bed77714 | ||
|
|
aa202b2cd8 | ||
|
|
4c89ce9d3f | ||
|
|
709a2c7b7c | ||
|
|
b7e483078a | ||
|
|
b2171d707a | ||
|
|
60db9fd9cd | ||
|
|
e8989f1f79 | ||
|
|
b7218bd1c9 | ||
|
|
43aab40968 | ||
|
|
ed57b3bfa9 | ||
|
|
9249f34c47 | ||
|
|
57401e8d53 | ||
|
|
f5f71a0365 | ||
|
|
1fe9f2fdb1 | ||
|
|
59d129b8ff | ||
|
|
5b5349d7ad | ||
|
|
b2ba1ca002 | ||
|
|
8606e87684 | ||
|
|
e4f640f11b | ||
|
|
e31d301d90 | ||
|
|
2a15435be3 | ||
|
|
44a411ba96 | ||
|
|
bf2f2369b1 | ||
|
|
b8b1f61268 | ||
|
|
63db2eafd9 | ||
|
|
68f52a815d | ||
|
|
9f35acf559 | ||
|
|
d8e82ca7c4 | ||
|
|
981bcfc0ba | ||
|
|
4a49a53222 | ||
|
|
9f987e24fe | ||
|
|
95f1d59a7f | ||
|
|
a4fe27223f | ||
|
|
5408dc6f09 | ||
|
|
35d4c1d332 | ||
|
|
c583b7284c | ||
|
|
2ce0c5c4c9 | ||
|
|
484194c114 | ||
|
|
b26b78c049 | ||
|
|
522a15c410 | ||
|
|
6a981772fd | ||
|
|
cc37fb50e2 | ||
|
|
1c9acfe001 | ||
|
|
c6cef856f0 | ||
|
|
9f503dbb65 | ||
|
|
7e4478123f | ||
|
|
e54d26aaf7 | ||
|
|
ee749d1ea7 | ||
|
|
a0d77e7b19 | ||
|
|
9421984a89 | ||
|
|
37ef1c2cff | ||
|
|
0a04a7f28d | ||
|
|
b47a85fe09 | ||
|
|
4b7b3cd518 | ||
|
|
1a2d783c2a | ||
|
|
7f943e07a5 | ||
|
|
f98658f10f | ||
|
|
8b6dcb1cd5 | ||
|
|
e140116fed | ||
|
|
c16a65a055 | ||
|
|
da1b7d4615 | ||
|
|
7b3ce69b62 | ||
|
|
8a52a7f72a | ||
|
|
abb9f9291b | ||
|
|
e533f5686e | ||
|
|
4234908b69 | ||
|
|
c10c4251b0 | ||
|
|
8b90c47379 | ||
|
|
fae6b13897 | ||
|
|
a48a965c1a | ||
|
|
ab0f4f6f17 | ||
|
|
e3114cd5b9 | ||
|
|
8489433060 | ||
|
|
6594d90042 | ||
|
|
7c5145bca5 | ||
|
|
e088cd10be | ||
|
|
6a2f61a0db | ||
|
|
7b88a2122a | ||
|
|
f9cf95d537 | ||
|
|
4884306135 | ||
|
|
fd11a4b3e4 | ||
|
|
b3c637536f | ||
|
|
1436af0321 | ||
|
|
1debe012c1 | ||
|
|
368a842d2e | ||
|
|
d250e9b630 | ||
|
|
3ff6863287 | ||
|
|
96621ea332 | ||
|
|
567fa1c703 | ||
|
|
20ab71aba6 | ||
|
|
1acace1b91 | ||
|
|
2f423fec48 | ||
|
|
7be550bf94 | ||
|
|
a8c0340bf1 | ||
|
|
5b71af3cec | ||
|
|
fcf7111872 | ||
|
|
38ca7b851b | ||
|
|
62bbce76f2 | ||
|
|
d9f6731de2 | ||
|
|
393a403ff2 | ||
|
|
62e4655d4f | ||
|
|
60d43319e2 | ||
|
|
7d3c613347 | ||
|
|
d2df93c75e | ||
|
|
0edee443f5 | ||
|
|
8c85f84b8a | ||
|
|
cea0d4f62e | ||
|
|
203150851f | ||
|
|
9a5ef25179 | ||
|
|
a5137e8ac4 | ||
|
|
c129a24492 | ||
|
|
8237f32bd5 | ||
|
|
546e63edaf | ||
|
|
647de16f1c | ||
|
|
2f4acd26d6 | ||
|
|
48bb804792 | ||
|
|
4261bd2c85 | ||
|
|
12db1f88dc | ||
|
|
1617c79e5d | ||
|
|
80b944d943 | ||
|
|
74bfced9b9 | ||
|
|
1c10872ea2 | ||
|
|
2b2e8c2a72 | ||
|
|
2315dc089a | ||
|
|
d7c0e9af38 | ||
|
|
460cf858d3 | ||
|
|
c81da2dd7d | ||
|
|
b02f8e98c4 | ||
|
|
ca9a367803 | ||
|
|
09f080220f | ||
|
|
547fa2eff5 | ||
|
|
2a8f21d4aa | ||
|
|
6f4283b7d5 | ||
|
|
babceb902a | ||
|
|
26bd4e7141 | ||
|
|
1531e13ca4 | ||
|
|
c9cbced0c8 | ||
|
|
2cf2112aec | ||
|
|
012f6ed2f6 | ||
|
|
fcac10c4c8 | ||
|
|
f2b692c716 | ||
|
|
22ef53acef | ||
|
|
2af70e6d50 | ||
|
|
63c9c607bd | ||
|
|
cab0813b17 | ||
|
|
06cc490ee4 | ||
|
|
6821988ddf | ||
|
|
8cce9fcb85 | ||
|
|
56caae019c | ||
|
|
f5afcbf62b | ||
|
|
0957d30e86 | ||
|
|
3a632b1544 | ||
|
|
2c0f4a897c | ||
|
|
be6a5671ed | ||
|
|
61ac297b1b | ||
|
|
da867dd07c | ||
|
|
962249ec75 | ||
|
|
c03ba57895 | ||
|
|
e2224fac5a | ||
|
|
48aaaf1e5d | ||
|
|
80ee256712 | ||
|
|
e274ffd83b | ||
|
|
bd9496e2ba | ||
|
|
4d4ba2be96 | ||
|
|
680e30be9b | ||
|
|
233301c3f3 | ||
|
|
0db21772c1 | ||
|
|
e6a2d96622 | ||
|
|
e5d83d47be | ||
|
|
3d77c76a4d | ||
|
|
043d5a7c7c | ||
|
|
636eb1972d | ||
|
|
27a250d2c0 | ||
|
|
02f4c81f2a | ||
|
|
0420ebcce3 | ||
|
|
15e3696b88 | ||
|
|
ca693a6ac1 | ||
|
|
1d6a307b6b | ||
|
|
227cca883c | ||
|
|
ef82a7ecba | ||
|
|
6e9f2cda79 | ||
|
|
10b58c5ce7 | ||
|
|
ab731cd7d0 | ||
|
|
a0d8f4734a | ||
|
|
237859d8c3 | ||
|
|
24dec0682f | ||
|
|
4b46f6a821 | ||
|
|
37a9a3d9da | ||
|
|
75cca66d7e | ||
|
|
762a1c7ad2 | ||
|
|
d19ba2cb41 | ||
|
|
557323deff | ||
|
|
29ffbf6aa7 | ||
|
|
aaa577d9e3 | ||
|
|
333c44dbd3 | ||
|
|
8243bc4c3f | ||
|
|
b1a9742889 | ||
|
|
d2f91af946 | ||
|
|
5adc5b4c94 | ||
|
|
f9d48cd15a | ||
|
|
e7d8bc4275 | ||
|
|
507326cebc | ||
|
|
b24832e0e1 | ||
|
|
6cdcd183b4 | ||
|
|
eb750f5b7f | ||
|
|
5967c89c23 | ||
|
|
4a83b36dcf | ||
|
|
eb1f9d64e1 | ||
|
|
cb72c104fd | ||
|
|
5fde50be4a | ||
|
|
1c16e6f38d | ||
|
|
6790d1ec5c | ||
|
|
4dc1847978 | ||
|
|
3a61692618 | ||
|
|
6f9afac0a5 | ||
|
|
b17c7c2965 | ||
|
|
6d238836ad | ||
|
|
5115fab9e6 | ||
|
|
93498ecf0b | ||
|
|
72d790953a | ||
|
|
72b556a09b | ||
|
|
747f86f8f4 | ||
|
|
161769f4ec | ||
|
|
9948079c97 | ||
|
|
607e5eeccf | ||
|
|
111ac57b6f | ||
|
|
153641dde5 | ||
|
|
113bebabd4 | ||
|
|
b549055034 | ||
|
|
7d7410b472 | ||
|
|
d7aaa4ae56 | ||
|
|
5a049f89f3 | ||
|
|
65e88f2fa6 | ||
|
|
4430b1e616 | ||
|
|
e2c534df5a | ||
|
|
2ca2835c9b | ||
|
|
bbe922dd7d | ||
|
|
38ad5b9ef2 | ||
|
|
67ba435ba6 | ||
|
|
6413571301 | ||
|
|
0dccae68e6 | ||
|
|
92320cef59 | ||
|
|
bf4e838e20 | ||
|
|
88b4233bda | ||
|
|
e9eb97ea9a | ||
|
|
0e865976f9 | ||
|
|
87dc080bbf | ||
|
|
9abc88f378 | ||
|
|
834d62e6e9 | ||
|
|
61a148e83f | ||
|
|
8e43775394 | ||
|
|
8344138bc8 | ||
|
|
dc66b3847a | ||
|
|
d608da4307 | ||
|
|
7ddd6629e3 | ||
|
|
20abc3e495 | ||
|
|
c4ba8670e4 | ||
|
|
a3348c85d6 | ||
|
|
ca465d7159 | ||
|
|
1413d32ee6 | ||
|
|
58fdfa9452 | ||
|
|
6f641073ae | ||
|
|
32153b11ab | ||
|
|
e33ac02351 | ||
|
|
dc99919338 | ||
|
|
fcaaa01b2d | ||
|
|
8cd3ff9fc4 | ||
|
|
0a130c7045 | ||
|
|
eb9f314356 | ||
|
|
a401aea132 | ||
|
|
41ebbe21d4 | ||
|
|
6378840d33 | ||
|
|
64f0aa1532 | ||
|
|
63366722a9 | ||
|
|
cd17858ae1 | ||
|
|
f4ebbc6651 | ||
|
|
464339822b | ||
|
|
fe09146609 | ||
|
|
a789ffe7ae | ||
|
|
476fbb1160 | ||
|
|
4501217c21 | ||
|
|
207675a181 | ||
|
|
cf6447dc80 | ||
|
|
775eb2e599 | ||
|
|
376486e64f | ||
|
|
63013bf374 | ||
|
|
488aa771a9 | ||
|
|
6bc2f19a36 | ||
|
|
2dd94fbfe8 | ||
|
|
be2b9fd7dc | ||
|
|
9d4e6e0984 | ||
|
|
4856a6b424 | ||
|
|
7df375fc75 | ||
|
|
63e0c1266a | ||
|
|
1a54aff7bc | ||
|
|
f2b9f8eeda | ||
|
|
e9f007025f | ||
|
|
6ce1531e1f | ||
|
|
84a45c4f91 | ||
|
|
e20ae56cab | ||
|
|
4ce0168916 | ||
|
|
024dd42e5a | ||
|
|
98ec789526 | ||
|
|
3bb5e7ee12 | ||
|
|
8c529b7d7f | ||
|
|
be7494961c | ||
|
|
bdb0bef0ee | ||
|
|
73f26e120d | ||
|
|
9f4b16e4f5 | ||
|
|
323ad774e5 | ||
|
|
d0bcd39341 | ||
|
|
42125367d7 | ||
|
|
d87a4ef28c | ||
|
|
a3f4388556 | ||
|
|
97ee4b6fea | ||
|
|
0abd288958 | ||
|
|
131073e211 | ||
|
|
078c4f064a | ||
|
|
f17a4e66f5 | ||
|
|
dd13ebb6d1 | ||
|
|
1c45f34262 | ||
|
|
a82f575232 | ||
|
|
1d84b34cf6 | ||
|
|
bedb51bf52 | ||
|
|
1b9e9452c4 | ||
|
|
3bdaf2e20b | ||
|
|
a1d67b881b | ||
|
|
c40750f6e4 | ||
|
|
f69d6426bd | ||
|
|
63b40d3e3a | ||
|
|
21f5918af8 | ||
|
|
6989236985 | ||
|
|
1efa120010 | ||
|
|
a99ca1cf05 | ||
|
|
8fc8209731 | ||
|
|
6f3eafc5ee | ||
|
|
83d1cb97c8 | ||
|
|
477799a2b9 | ||
|
|
7e8c9ce944 | ||
|
|
6decb6609a | ||
|
|
f1e7cd897d | ||
|
|
9bfaa224bc | ||
|
|
5657661249 | ||
|
|
e69d61d8c7 | ||
|
|
729c055f5b | ||
|
|
61f91d626b | ||
|
|
732d10713d | ||
|
|
15e5fa433b | ||
|
|
43ff0e9e52 | ||
|
|
cf32e78f1b | ||
|
|
f170726b4b | ||
|
|
ebb20bdf0c | ||
|
|
2ca7302352 | ||
|
|
8a123f9f68 | ||
|
|
7e92eb6192 | ||
|
|
56edf1720c | ||
|
|
7c32dc49b1 | ||
|
|
5cb5ab019f | ||
|
|
42fa09a6fa | ||
|
|
673884a409 | ||
|
|
ec6296e44b | ||
|
|
b96d0de8a2 | ||
|
|
98c4e39ed1 | ||
|
|
9c483880cb | ||
|
|
ae7e298946 | ||
|
|
7197c1fbb6 | ||
|
|
c10e04d526 | ||
|
|
ee883492ee | ||
|
|
2ed99db9b1 | ||
|
|
c11dcf5d76 | ||
|
|
4132e990ec | ||
|
|
76eba8c235 | ||
|
|
e1ac26773b | ||
|
|
339ea70230 | ||
|
|
943a39178c | ||
|
|
ae0cb58a5c | ||
|
|
dd116d8ad8 | ||
|
|
74c3c5bc87 | ||
|
|
b6bfa14872 | ||
|
|
50a769c8fb | ||
|
|
39a254ce9f | ||
|
|
3aa720fe1f | ||
|
|
7908a1d2ea | ||
|
|
37d8481e28 | ||
|
|
44822f944b | ||
|
|
406a1c1152 | ||
|
|
f9939ce336 | ||
|
|
d9bced965b | ||
|
|
4d78aea05b | ||
|
|
9b42afa9ca | ||
|
|
aa5b8b84a8 | ||
|
|
d0a8a92a56 | ||
|
|
cf82f74e15 | ||
|
|
a952ddec53 | ||
|
|
fa7efd086f | ||
|
|
8e28e86caf | ||
|
|
65de9622fb | ||
|
|
210c4b2077 | ||
|
|
e8cb635b3a | ||
|
|
f41c8fef99 | ||
|
|
02147b0b16 | ||
|
|
79e266aac6 | ||
|
|
9d75ed14b4 | ||
|
|
fcca618c48 | ||
|
|
73bcebf355 | ||
|
|
057443e4a0 | ||
|
|
99f8c2950b | ||
|
|
4132c63faf | ||
|
|
7f2b5a44ff | ||
|
|
7db0c15483 | ||
|
|
2fc7cc57a9 | ||
|
|
4970a2aee5 | ||
|
|
25b6df5355 | ||
|
|
732479736b | ||
|
|
696ac1bbd5 | ||
|
|
3b2e5a13bf | ||
|
|
26a7aec7cc | ||
|
|
b73a50bace | ||
|
|
70ccbe4e40 | ||
|
|
6bb00ab732 | ||
|
|
2e279852cd | ||
|
|
09b4e71dc5 | ||
|
|
b40713596c | ||
|
|
c5485a795a | ||
|
|
b7c5d4a550 | ||
|
|
0aa1c9e42d | ||
|
|
5823e98c76 | ||
|
|
3c45e1996d | ||
|
|
e2ce496779 | ||
|
|
4540be7460 | ||
|
|
7c85b74de6 | ||
|
|
fccd569059 | ||
|
|
768bfa5d44 | ||
|
|
ff6e38571d | ||
|
|
0a5a609322 | ||
|
|
c424eb9160 | ||
|
|
501ba77ec7 | ||
|
|
dc23ee0c7a | ||
|
|
e98f06e84a | ||
|
|
19c36284dc | ||
|
|
358d8bd7b3 | ||
|
|
c17508862c | ||
|
|
2277a4959c | ||
|
|
d5427df9eb | ||
|
|
36206a80fc | ||
|
|
ab7d175322 | ||
|
|
802528266a | ||
|
|
95d98f4659 | ||
|
|
4176302a03 | ||
|
|
b3119cce13 | ||
|
|
9987485f63 | ||
|
|
b08b2f14f2 | ||
|
|
1e7ba56aeb | ||
|
|
faf23195a4 | ||
|
|
0d66b9435f | ||
|
|
49353e2d5c | ||
|
|
c4d5621f67 | ||
|
|
aa2227bdfc | ||
|
|
34908d573f | ||
|
|
469774037e | ||
|
|
93c5ad7cbc | ||
|
|
03d52e4284 | ||
|
|
9d83ee169b | ||
|
|
43b181ebbf | ||
|
|
ac65ca5177 | ||
|
|
42671be21e | ||
|
|
efa6930de2 | ||
|
|
19c47ec781 | ||
|
|
435cd554e6 | ||
|
|
10c70ebd12 | ||
|
|
7da3ff1bba | ||
|
|
a222e30428 | ||
|
|
bd822fce19 | ||
|
|
cabe9dbeb5 | ||
|
|
99b7a07350 | ||
|
|
756e0bd3d8 | ||
|
|
9b0ce12b5f | ||
|
|
e29c2df002 | ||
|
|
b03a8b5b50 | ||
|
|
32f5175d68 | ||
|
|
7d3a54db57 | ||
|
|
3fb7c2acfe | ||
|
|
bce5d35b51 | ||
|
|
b6dca5ccd6 | ||
|
|
23b2712400 | ||
|
|
63bec73885 | ||
|
|
af5e0c8982 | ||
|
|
5a7c8753e1 | ||
|
|
19fdec96ca | ||
|
|
2208a06232 | ||
|
|
2cfcaf3909 | ||
|
|
620868b5d6 | ||
|
|
e7295bac7b | ||
|
|
38aa86de2a | ||
|
|
fdf476a3f3 | ||
|
|
22f27a6cc0 | ||
|
|
c72ea8ce77 | ||
|
|
ea168353b9 | ||
|
|
79b9c4838e | ||
|
|
d6e86ebcc9 | ||
|
|
197aaa47f0 | ||
|
|
ad234004aa | ||
|
|
a88e6a4c5f | ||
|
|
c776dad96f | ||
|
|
31fedd529b | ||
|
|
e9adff9dc5 | ||
|
|
bfd58a204b | ||
|
|
867bcf1514 | ||
|
|
fb882ee440 | ||
|
|
215d3d2847 | ||
|
|
43a345ba0c | ||
|
|
f73f5e2798 | ||
|
|
3724670fcc | ||
|
|
e7db64bd74 | ||
|
|
9c29b8a29e | ||
|
|
c22f0e4d43 | ||
|
|
4e2b11c24c | ||
|
|
8c34e927ea | ||
|
|
8a5de47aa0 | ||
|
|
7f84c1e397 | ||
|
|
65c915fd9d | ||
|
|
0fd4f73ab1 | ||
|
|
01117567b1 | ||
|
|
d68cc5cf40 | ||
|
|
6db16be2fa | ||
|
|
5166b1add9 | ||
|
|
9fadcc907c | ||
|
|
42171efc3f | ||
|
|
206f12648f | ||
|
|
ecd0573c4d | ||
|
|
2d9f6241a9 | ||
|
|
77ee50f5a6 | ||
|
|
297c37e609 | ||
|
|
9bb1ab43eb | ||
|
|
cd36b13c30 | ||
|
|
5d63084627 | ||
|
|
ab7d1d6582 | ||
|
|
77ea6d8c2f | ||
|
|
b5cd629160 | ||
|
|
39467c8b71 | ||
|
|
87740e3b38 | ||
|
|
19848a9b72 | ||
|
|
36e9998fe2 | ||
|
|
b2529c241e | ||
|
|
83c47037d2 | ||
|
|
125b6f9236 | ||
|
|
e260ba6b78 | ||
|
|
eafb934e85 | ||
|
|
c3d3da20f6 | ||
|
|
6036ccdbaa | ||
|
|
9404a4d069 | ||
|
|
274621dcec | ||
|
|
2ab756906d | ||
|
|
2b3f63443f | ||
|
|
c71f960948 | ||
|
|
9d3514e087 | ||
|
|
6703e039b1 | ||
|
|
29b81750ad | ||
|
|
d6fff53f6b | ||
|
|
7ee6281426 | ||
|
|
28a5740c37 | ||
|
|
09a3332cf0 | ||
|
|
f584c4849b | ||
|
|
e3663c995a | ||
|
|
1d62b3ca64 | ||
|
|
3af04a4259 | ||
|
|
78d98822a5 | ||
|
|
8c04ddc66b | ||
|
|
5277b8d23e | ||
|
|
86fea49178 | ||
|
|
5b1242f786 | ||
|
|
a8cfdbe762 | ||
|
|
70b1ebae0b | ||
|
|
9dc6004329 | ||
|
|
d4bd17a2dc | ||
|
|
6c73e5fa5c | ||
|
|
24edfb8eaf | ||
|
|
4ef5a2216e | ||
|
|
0d646eb244 | ||
|
|
757fea37f3 | ||
|
|
09e41cc9f2 | ||
|
|
71ad92e286 | ||
|
|
d4f3d3aa98 | ||
|
|
c0612a1b2c | ||
|
|
6743cbbf50 | ||
|
|
da1bdf9d4c | ||
|
|
96da4cd85d | ||
|
|
f233340cb2 | ||
|
|
cfb817d37f | ||
|
|
9440c03048 | ||
|
|
aa611bde50 | ||
|
|
7af856508e | ||
|
|
aaed64039a | ||
|
|
7c5e8c3234 | ||
|
|
a56bf3f115 | ||
|
|
2d2fa9cfa9 | ||
|
|
9f11930f3d | ||
|
|
013b1dfe2e | ||
|
|
733f699ff2 | ||
|
|
a366d502a2 | ||
|
|
eed8ffc792 | ||
|
|
998547e1a1 | ||
|
|
8567b7ad84 | ||
|
|
64118404fe | ||
|
|
bb74dc846c | ||
|
|
659d893dee | ||
|
|
e3f371c2d8 | ||
|
|
f53c7a6785 | ||
|
|
261c193b50 | ||
|
|
dc0b650aa3 | ||
|
|
87cf6faedd | ||
|
|
ca6aa6706d | ||
|
|
16e55416a6 | ||
|
|
3e24058175 | ||
|
|
d1c2b20b28 | ||
|
|
ccd0cbd8f2 | ||
|
|
e5213674e3 | ||
|
|
b9cc848810 | ||
|
|
65bdfaf22b | ||
|
|
de31d836b4 | ||
|
|
25b339956e | ||
|
|
23bb6e7a45 | ||
|
|
b44f7ec653 | ||
|
|
6d38c18572 | ||
|
|
8eaf78bfe2 | ||
|
|
dc836b38a6 | ||
|
|
b01f8d079b | ||
|
|
6f7ac21a85 | ||
|
|
7c5db84f33 | ||
|
|
4fecb2066b | ||
|
|
eabdddcfb3 | ||
|
|
ceab27d723 | ||
|
|
a9cc669a95 | ||
|
|
927c835b8f | ||
|
|
5415eaaf60 | ||
|
|
ec587f9b58 | ||
|
|
babfed73f3 | ||
|
|
2c0ce576e3 | ||
|
|
8f2cc937c5 | ||
|
|
c9f6567128 | ||
|
|
d60672e8ba | ||
|
|
fd4cce4883 | ||
|
|
df15d3d48e | ||
|
|
76fd216386 | ||
|
|
48735aad94 | ||
|
|
91a1471de5 | ||
|
|
047ba76ff1 | ||
|
|
e57eb63c87 | ||
|
|
8461f4018d | ||
|
|
c33c2e7e95 | ||
|
|
e5a9a2af4c | ||
|
|
7919ca8de1 | ||
|
|
00ef660952 | ||
|
|
862a03713b | ||
|
|
89d0d2f109 | ||
|
|
3283e34dcf | ||
|
|
34c6b264df | ||
|
|
2d8fe502f2 | ||
|
|
73df308250 | ||
|
|
cdca40cb59 | ||
|
|
2a3c301314 | ||
|
|
6f4923da2e | ||
|
|
ae26a2b39a | ||
|
|
48c40c8a27 | ||
|
|
a4a4eee495 | ||
|
|
d185428d1d | ||
|
|
6855aaa6f4 | ||
|
|
2aee4f418f | ||
|
|
95aef01a59 | ||
|
|
d2ccde4859 | ||
|
|
7ab64ea0e6 | ||
|
|
fe1b8b88e1 | ||
|
|
b6832869b1 | ||
|
|
6da8d39a0e | ||
|
|
f8cf1316ad | ||
|
|
736606a845 | ||
|
|
cba240e15e | ||
|
|
eb90fffb5e | ||
|
|
041e280bde | ||
|
|
33fae1cb66 | ||
|
|
4e8408e225 | ||
|
|
e85a423f6a | ||
|
|
532cb40665 | ||
|
|
dd58b0497e | ||
|
|
cc070c80ba | ||
|
|
e332dae576 | ||
|
|
727fd4e98b | ||
|
|
5a5f7012d8 | ||
|
|
5a239cddf7 | ||
|
|
c4928553ba | ||
|
|
48b0cff08e | ||
|
|
c8fcffbe6b | ||
|
|
63c4eeb9c9 | ||
|
|
d45f777099 | ||
|
|
9c4035dd8f | ||
|
|
acc2b71c86 | ||
|
|
09d849c93d | ||
|
|
1be1ad88c6 | ||
|
|
8ecaf25339 | ||
|
|
7a6dce4727 | ||
|
|
b357ffb0e6 | ||
|
|
517bd5f22b | ||
|
|
d1d0a67d35 | ||
|
|
85a9dbafc6 | ||
|
|
79390f72fa | ||
|
|
b7a37dd771 | ||
|
|
b24d1ccc8b | ||
|
|
4f53e10e7e | ||
|
|
a608ad3cf6 | ||
|
|
a135f81073 | ||
|
|
2da48557ff | ||
|
|
73686debaf | ||
|
|
97950621cd | ||
|
|
b3401828e4 | ||
|
|
0a7d4a68b3 | ||
|
|
8d7deffa6a | ||
|
|
669c08210e | ||
|
|
27a3231a55 | ||
|
|
86ce695939 | ||
|
|
2759a136df | ||
|
|
690682408b | ||
|
|
bfdb8a24e9 | ||
|
|
e9bf042ac5 | ||
|
|
5aab3078c8 | ||
|
|
1ac5840bb1 | ||
|
|
426b3d6834 | ||
|
|
97b573c182 | ||
|
|
5c41cbb964 | ||
|
|
e192e3beaf | ||
|
|
5dacb50d63 | ||
|
|
86d837b5a9 | ||
|
|
6454a6dc3f | ||
|
|
bbd1200ad7 | ||
|
|
aa59242843 | ||
|
|
c04ede4909 | ||
|
|
a1eaf2e83c | ||
|
|
a21d4d4adc | ||
|
|
f0a92d999c | ||
|
|
28a3498a69 | ||
|
|
7eb5d892f1 | ||
|
|
f6cd79952c | ||
|
|
9c1a0ae27f | ||
|
|
29c1a955d0 | ||
|
|
b366432a8a | ||
|
|
af8bfdc1c1 | ||
|
|
88040ad467 | ||
|
|
a35e67bcdf | ||
|
|
df3317a5b2 | ||
|
|
33f95c00a9 | ||
|
|
7383505dcc | ||
|
|
2c9a29af0e | ||
|
|
5847273b79 | ||
|
|
98204991ec | ||
|
|
b8e10bfe15 | ||
|
|
132fe66699 | ||
|
|
a28fc4dfb1 | ||
|
|
7721aa5643 | ||
|
|
68707aff17 | ||
|
|
3ecbc9adff | ||
|
|
b5a7a0ca31 | ||
|
|
2362db8528 | ||
|
|
62835bdc54 | ||
|
|
fe1cf209f1 | ||
|
|
2f72563b1f | ||
|
|
0d754d6ca8 | ||
|
|
f474f6499b | ||
|
|
97baacf609 | ||
|
|
0fa8cfdb71 | ||
|
|
78c7cae7e9 | ||
|
|
a4834efd37 | ||
|
|
07e4435a07 | ||
|
|
9f93771771 | ||
|
|
a533807fcf | ||
|
|
49129ec1b2 | ||
|
|
803354552f | ||
|
|
fa6c365679 | ||
|
|
6ac1ae3527 | ||
|
|
29834b714e | ||
|
|
bba27adf4d | ||
|
|
e3a280d7a9 | ||
|
|
63f1a736f4 | ||
|
|
0f8720c433 | ||
|
|
7a6263b940 | ||
|
|
b09e3df2e2 | ||
|
|
041a9e0e7f | ||
|
|
fa9730dd07 | ||
|
|
33f29e95f4 |
No files matched your search
@@ -0,0 +1 @@
|
||||
Checks: '-*,clang-diagnostic-*,performance-*,bugprone-*,llvm-*,clang-analyzer-*,misc-*,-llvm-namespace-comment,-misc-unused-parameters,-misc-non-private-member-variables-in-classes,-llvm-header-guard,-llvm-include-order,-bugprone-suspicious-string-compare'
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "C++",
|
||||
"build": {
|
||||
"dockerfile": "../docker/DockerfileDepsLlvm",
|
||||
},
|
||||
"runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined"],
|
||||
"settings": {},
|
||||
"extensions": [
|
||||
"ms-vscode.cpptools"
|
||||
],
|
||||
"forwardPorts": [4222],
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
name: C/C++ CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build-ubuntu:
|
||||
name: Ubuntu/GCC build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: deps
|
||||
run: |
|
||||
sudo apt install libncurses5-dev libreadline-dev nettle-dev \
|
||||
libgnutls28-dev libuv1-dev cython3 python3-dev python3-setuptools libcppunit-dev libjsoncpp-dev \
|
||||
autotools-dev autoconf libfmt-dev libhttp-parser-dev libmsgpack-dev libargon2-0-dev
|
||||
- name: autogen
|
||||
run: ./autogen.sh
|
||||
- name: asio
|
||||
run: |
|
||||
wget https://github.com/aberaud/asio/archive/b2b7a1c166390459e1c169c8ae9ef3234b361e3f.tar.gz \
|
||||
&& tar -xvf b2b7a1c166390459e1c169c8ae9ef3234b361e3f.tar.gz && cd asio-b2b7a1c166390459e1c169c8ae9ef3234b361e3f/asio \
|
||||
&& ./autogen.sh && ./configure --prefix=/usr --without-boost --disable-examples --disable-tests \
|
||||
&& sudo make install \
|
||||
&& cd ../../ && rm -rf asio*
|
||||
- name: configure
|
||||
run: ./configure
|
||||
- name: make
|
||||
run: make
|
||||
- name: make check
|
||||
run: make check
|
||||
|
||||
build-ubuntu-minimal:
|
||||
name: Ubuntu/GCC minimal build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: deps
|
||||
run: |
|
||||
sudo apt install libncurses5-dev libreadline-dev nettle-dev \
|
||||
libgnutls28-dev libcppunit-dev libmsgpack-dev libargon2-0-dev
|
||||
- name: cmake
|
||||
run: |
|
||||
mkdir build && cd build
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Debug \
|
||||
-DOPENDHT_C=Off -DOPENDHT_TESTS=On -DOPENDHT_PEER_DISCOVERY=Off -DOPENDHT_PYTHON=Off \
|
||||
-DOPENDHT_TOOLS=On -DOPENDHT_PROXY_SERVER=Off -DOPENDHT_PROXY_CLIENT=Off
|
||||
- name: make
|
||||
run: cd build && make
|
||||
|
||||
build-macos:
|
||||
name: macOS/Clang build
|
||||
runs-on: macos-11
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: deps
|
||||
run: |
|
||||
brew install msgpack-cxx asio gnutls nettle readline fmt jsoncpp argon2 openssl http-parser cppunit
|
||||
|
||||
- name: restinio
|
||||
run: |
|
||||
mkdir restinio && cd restinio
|
||||
wget https://github.com/aberaud/restinio/archive/e0a261dd8488246a3cb8bbb3ea781ea5139c3c94.tar.gz
|
||||
ls -l && tar -xzf e0a261dd8488246a3cb8bbb3ea781ea5139c3c94.tar.gz
|
||||
cd restinio-e0a261dd8488246a3cb8bbb3ea781ea5139c3c94/dev
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DRESTINIO_TEST=OFF -DRESTINIO_SAMPLE=OFF \
|
||||
-DRESTINIO_INSTALL_SAMPLES=OFF -DRESTINIO_BENCH=OFF -DRESTINIO_INSTALL_BENCHES=OFF \
|
||||
-DRESTINIO_FIND_DEPS=ON -DRESTINIO_ALLOW_SOBJECTIZER=Off -DRESTINIO_USE_BOOST_ASIO=none .
|
||||
make -j8 && sudo make install
|
||||
cd ../../.. && rm -rf restinio
|
||||
|
||||
- name: cmake
|
||||
run: |
|
||||
mkdir build && cd build
|
||||
export PATH="/opt/homebrew/opt/openssl@3/bin:$PATH"
|
||||
export LDFLAGS="-L/usr/local/opt/openssl@3/lib"
|
||||
export CPPFLAGS="-I/usr/local/opt/openssl@3/include"
|
||||
export PKG_CONFIG_PATH="/usr/local/opt/openssl@3/lib/pkgconfig"
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Debug \
|
||||
-DOPENDHT_C=On -DOPENDHT_TESTS=On -DOPENDHT_PEER_DISCOVERY=On -DOPENDHT_PYTHON=Off \
|
||||
-DOPENDHT_TOOLS=On -DOPENDHT_PROXY_SERVER=On -DOPENDHT_PROXY_CLIENT=On
|
||||
|
||||
- name: make
|
||||
run: cd build && make
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
name: Clang Static Analysis
|
||||
on:
|
||||
push
|
||||
jobs:
|
||||
clang-analyzer:
|
||||
name: Clang Static Analysis
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: deps
|
||||
run: |
|
||||
sudo apt install libncurses5-dev libreadline-dev nettle-dev \
|
||||
libgnutls28-dev libuv1-dev cython3 python3-dev python3-setuptools libcppunit-dev libjsoncpp-dev \
|
||||
autotools-dev autoconf libfmt-dev libhttp-parser-dev libmsgpack-dev libargon2-0-dev libasio-dev \
|
||||
llvm llvm-dev clang clang-tools && \
|
||||
sudo apt remove gcc g++
|
||||
|
||||
- name: restinio
|
||||
run: |
|
||||
mkdir restinio && cd restinio \
|
||||
&& wget https://github.com/aberaud/restinio/archive/e0a261dd8488246a3cb8bbb3ea781ea5139c3c94.tar.gz \
|
||||
&& ls -l && tar -xzf e0a261dd8488246a3cb8bbb3ea781ea5139c3c94.tar.gz \
|
||||
&& cd restinio-e0a261dd8488246a3cb8bbb3ea781ea5139c3c94/dev \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DRESTINIO_TEST=OFF -DRESTINIO_SAMPLE=OFF \
|
||||
-DRESTINIO_INSTALL_SAMPLES=OFF -DRESTINIO_BENCH=OFF -DRESTINIO_INSTALL_BENCHES=OFF \
|
||||
-DRESTINIO_FIND_DEPS=ON -DRESTINIO_ALLOW_SOBJECTIZER=Off -DRESTINIO_USE_BOOST_ASIO=none . \
|
||||
&& make -j8 && sudo make install \
|
||||
&& cd ../../.. && rm -rf restinio
|
||||
|
||||
- name: cmake
|
||||
run: |
|
||||
mkdir build && cd build && \
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug -DOPENDHT_C=On -DOPENDHT_PEER_DISCOVERY=On -DOPENDHT_PYTHON=Off -DOPENDHT_TOOLS=On -DOPENDHT_PROXY_SERVER=On -DOPENDHT_PROXY_CLIENT=On
|
||||
- name: scan-build
|
||||
run: cd build && scan-build --status-bugs make
|
||||
+33
@@ -19,6 +19,9 @@
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
**/dhtnode
|
||||
**/dhtchat
|
||||
**/dhtscanner
|
||||
|
||||
# Autotools
|
||||
/ac
|
||||
@@ -45,3 +48,33 @@ Makefile.in
|
||||
# KDevelop
|
||||
.kdev4/
|
||||
*.kdev4
|
||||
|
||||
# VSCode
|
||||
.vscode/*
|
||||
|
||||
# Eclipse CDT
|
||||
.cproject
|
||||
.project
|
||||
.autotools
|
||||
language.settings.xml
|
||||
|
||||
# Python
|
||||
*.pyc
|
||||
*.stamp
|
||||
python/opendht.cpp
|
||||
python/setup.py
|
||||
|
||||
# Doxygen
|
||||
doc/Doxyfile
|
||||
|
||||
# git backup files
|
||||
*.orig
|
||||
|
||||
# vim swap files
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# build dir
|
||||
build
|
||||
build_dev
|
||||
.DS_Store
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
sudo: false
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
language: cpp
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- gcc-6
|
||||
- g++-6
|
||||
- libgnutls28-dev
|
||||
- cmake
|
||||
|
||||
before_install:
|
||||
- if [ $TRAVIS_OS_NAME == linux ] && [ $CXX == g++ ]; then export CXX=/usr/bin/g++-6 && export CC=/usr/bin/gcc-6; fi
|
||||
- if [ $TRAVIS_OS_NAME == linux ]; then git clone https://github.com/msgpack/msgpack-c.git; fi
|
||||
- if [ $TRAVIS_OS_NAME == linux ]; then cd msgpack-c && git checkout cpp-1.2.0 && mkdir build && mkdir -p $HOME/usr && cd build; fi
|
||||
- if [ $TRAVIS_OS_NAME == linux ]; then cmake -DCMAKE_INSTALL_PREFIX=$HOME/usr -DMSGPACK_CXX11=ON -DMSGPACK_BUILD_EXAMPLES=OFF .. && make -j && make install && cd ../.. && pwd; fi
|
||||
- if [ $TRAVIS_OS_NAME == osx ]; then brew update && brew install llvm36 && brew install msgpack && brew install gnutls && brew link --force llvm36; fi
|
||||
|
||||
before_script:
|
||||
- if [ $TRAVIS_OS_NAME == linux ]; then travis_retry ./autogen.sh && ./configure --prefix=$HOME/usr; fi
|
||||
- if [ $TRAVIS_OS_NAME == osx ]; then travis_retry ./autogen.sh && ./configure; fi
|
||||
|
||||
script:
|
||||
- make -j
|
||||
|
||||
notifications:
|
||||
email:
|
||||
- adrien.beraud@savoirfairelinux.com
|
||||
|
||||
env:
|
||||
- LANG="en_US.UTF-8"
|
||||
|
||||
os:
|
||||
- osx
|
||||
Vendored
+54
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "dhtnode",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build_dev/tools/dhtnode",
|
||||
"args": ["-v", "-p", "4222"],
|
||||
"cwd": "${workspaceFolder}/build_dev",
|
||||
"environment": [],
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
],
|
||||
"preLaunchTask": "build",
|
||||
"linux": {
|
||||
"MIMode": "gdb",
|
||||
"externalConsole": false,
|
||||
},
|
||||
"osx": {
|
||||
"MIMode": "lldb",
|
||||
"externalConsole": true,
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "tests",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build_dev/opendht_unit_tests",
|
||||
"cwd": "${workspaceFolder}/build_dev",
|
||||
"environment": [],
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
],
|
||||
"preLaunchTask": "build",
|
||||
"linux": {
|
||||
"MIMode": "gdb",
|
||||
"externalConsole": false,
|
||||
},
|
||||
"osx": {
|
||||
"MIMode": "lldb",
|
||||
"externalConsole": true,
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+45
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"type": "shell",
|
||||
"options": {
|
||||
"cwd": "${workspaceRoot}/build_dev"
|
||||
},
|
||||
"command": "make",
|
||||
"args": ["-j4"],
|
||||
"dependsOn": "cmake"
|
||||
},
|
||||
{
|
||||
"label": "cmake",
|
||||
"type": "shell",
|
||||
"options": {
|
||||
"cwd": "${workspaceRoot}/build_dev"
|
||||
},
|
||||
"command": "cmake",
|
||||
"args": [
|
||||
"${workspaceRoot}",
|
||||
"-DCMAKE_EXPORT_COMPILE_COMMANDS=On",
|
||||
"-DCMAKE_BUILD_TYPE=Debug",
|
||||
"-DOPENDHT_PROXY_CLIENT=On",
|
||||
"-DOPENDHT_PROXY_SERVER=On",
|
||||
"-DOPENDHT_TESTS=On",
|
||||
"-DOPENDHT_C=On"
|
||||
],
|
||||
"dependsOn": "builddir"
|
||||
},
|
||||
{
|
||||
"label": "builddir",
|
||||
"type": "shell",
|
||||
"options": {
|
||||
"cwd": "${workspaceRoot}"
|
||||
},
|
||||
"command": "mkdir",
|
||||
"args": [
|
||||
"-p",
|
||||
"${workspaceRoot}/build_dev"
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
+438
-63
@@ -1,43 +1,202 @@
|
||||
cmake_minimum_required (VERSION 2.8.6)
|
||||
cmake_minimum_required (VERSION 3.10)
|
||||
if(POLICY CMP0065)
|
||||
cmake_policy(SET CMP0065 NEW)
|
||||
endif()
|
||||
if(POLICY CMP0069)
|
||||
cmake_policy(SET CMP0069 NEW)
|
||||
endif()
|
||||
if(POLICY CMP0073)
|
||||
cmake_policy(SET CMP0073 NEW)
|
||||
endif()
|
||||
if(POLICY CMP0074)
|
||||
cmake_policy(SET CMP0074 NEW)
|
||||
endif()
|
||||
project (opendht)
|
||||
set (opendht_VERSION_MAJOR 0)
|
||||
set (opendht_VERSION_MINOR 6.1)
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
include(CMakeDependentOption)
|
||||
include(FindPkgConfig)
|
||||
include(cmake/CheckAtomic.cmake)
|
||||
|
||||
set (opendht_VERSION_MAJOR 2)
|
||||
set (opendht_VERSION_MINOR 4.5)
|
||||
set (opendht_VERSION ${opendht_VERSION_MAJOR}.${opendht_VERSION_MINOR})
|
||||
set (PACKAGE_VERSION ${opendht_VERSION})
|
||||
set (VERSION "${opendht_VERSION}")
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
|
||||
|
||||
set (prefix ${CMAKE_INSTALL_PREFIX})
|
||||
set (exec_prefix "\${prefix}")
|
||||
set (libdir "\${exec_prefix}/lib")
|
||||
set (includedir "\${prefix}/include")
|
||||
|
||||
option (OPENDHT_DEBUG "Build with debug flags" OFF)
|
||||
# Options
|
||||
option (OPENDHT_STATIC "Build static library" ON)
|
||||
option (OPENDHT_SHARED "Build shared library" ON)
|
||||
option (OPENDHT_LTO "Build with LTO" OFF)
|
||||
|
||||
option (OPENDHT_LOG "Build with logs" ON)
|
||||
option (OPENDHT_PYTHON "Build Python bindings" OFF)
|
||||
option (OPENDHT_TOOLS "Build DHT tools" ON)
|
||||
option (OPENDHT_SYSTEMD "Install systemd module" OFF)
|
||||
option (OPENDHT_SYSTEMD_UNIT_FILE_LOCATION "Where to install systemd unit file")
|
||||
option (OPENDHT_SANITIZE "Build with address sanitizer and stack protector" OFF)
|
||||
option (OPENDHT_PROXY_SERVER "Enable DHT proxy server, use Restinio and jsoncpp" OFF)
|
||||
option (OPENDHT_PUSH_NOTIFICATIONS "Enable push notifications support" OFF)
|
||||
option (OPENDHT_PROXY_SERVER_IDENTITY "Allow clients to use the node identity" OFF)
|
||||
option (OPENDHT_PROXY_CLIENT "Enable DHT proxy client, use Restinio and jsoncpp" OFF)
|
||||
option (OPENDHT_PROXY_OPENSSL "Build DHT proxy with OpenSSL" ON)
|
||||
CMAKE_DEPENDENT_OPTION(OPENDHT_HTTP "Build embedded http(s) client" OFF "NOT OPENDHT_PROXY_SERVER;NOT OPENDHT_PROXY_CLIENT" ON)
|
||||
option (OPENDHT_PEER_DISCOVERY "Enable multicast peer discovery" ON)
|
||||
option (OPENDHT_INDEX "Build DHT indexation feature" OFF)
|
||||
option (OPENDHT_TESTS "Add unit tests executable" OFF)
|
||||
option (OPENDHT_TESTS_NETWORK "Enable unit tests that require network access" ON)
|
||||
option (OPENDHT_C "Build C bindings" OFF)
|
||||
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -std=c++11 -Wno-return-type -Wall -Wextra -Wnon-virtual-dtor")
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMSGPACK_DISABLE_LEGACY_NIL -DMSGPACK_DISABLE_LEGACY_CONVERT")
|
||||
if (OPENDHT_LTO)
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto -fuse-linker-plugin")
|
||||
if (CMAKE_COMPILER_IS_GNUCC)
|
||||
SET (CMAKE_AR "gcc-ar")
|
||||
SET (CMAKE_NM "gcc-nm")
|
||||
SET (CMAKE_RANLIB "gcc-ranlib")
|
||||
find_package(Doxygen)
|
||||
option (OPENDHT_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen)" ${DOXYGEN_FOUND})
|
||||
|
||||
# Dependencies
|
||||
if (NOT HAVE_CXX_ATOMICS_WITHOUT_LIB)
|
||||
link_libraries (atomic)
|
||||
endif ()
|
||||
|
||||
list (APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
|
||||
if (NOT MSVC)
|
||||
set (THREADS_PREFER_PTHREAD_FLAG TRUE)
|
||||
find_package (Threads)
|
||||
find_package (PkgConfig REQUIRED)
|
||||
find_package (GnuTLS 3.3 REQUIRED)
|
||||
pkg_search_module (Nettle REQUIRED nettle)
|
||||
find_package (msgpack REQUIRED)
|
||||
if (OPENDHT_TOOLS)
|
||||
find_package (Readline 6 REQUIRED)
|
||||
endif ()
|
||||
pkg_search_module(argon2 REQUIRED libargon2)
|
||||
pkg_search_module(Jsoncpp jsoncpp)
|
||||
if (Jsoncpp_FOUND)
|
||||
add_definitions(-DOPENDHT_JSONCPP)
|
||||
list (APPEND opendht_SOURCES
|
||||
src/base64.h
|
||||
src/base64.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if (OPENDHT_HTTP OR OPENDHT_PEER_DISCOVERY)
|
||||
find_path(ASIO_INCLUDE_DIR asio.hpp REQUIRED)
|
||||
endif ()
|
||||
|
||||
if (OPENDHT_HTTP)
|
||||
find_package(Restinio REQUIRED)
|
||||
find_library(FMT_LIBRARY fmt)
|
||||
add_library(fmt SHARED IMPORTED)
|
||||
find_library(HTTP_PARSER_LIBRARY http_parser)
|
||||
add_library(http_parser SHARED IMPORTED)
|
||||
if (NOT Jsoncpp_FOUND)
|
||||
message(SEND_ERROR "Jsoncpp is required for DHT proxy support")
|
||||
endif()
|
||||
if (OPENDHT_PROXY_OPENSSL)
|
||||
# https://cmake.org/cmake/help/latest/module/FindOpenSSL.html
|
||||
pkg_search_module(OPENSSL REQUIRED openssl)
|
||||
if (OPENSSL_FOUND)
|
||||
message(STATUS "Found OpenSSL ${OPENSSL_VERSION} ${OPENSSL_INCLUDE_DIRS}")
|
||||
include_directories(SYSTEM ${OPENSSL_INCLUDE_DIRS})
|
||||
link_directories (${OPENSSL_LIBRARY_DIRS})
|
||||
else ()
|
||||
message(SEND_ERROR "OpenSSL is required for DHT proxy as specified")
|
||||
endif()
|
||||
endif()
|
||||
else ()
|
||||
set(OPENDHT_PROXY_OPENSSL OFF)
|
||||
endif ()
|
||||
else ()
|
||||
set (WIN32_DEP_DIR ${PROJECT_SOURCE_DIR}/../)
|
||||
include_directories(${WIN32_DEP_DIR}/../msvc/include) # SMP gnutls
|
||||
include_directories(${WIN32_DEP_DIR}/argon2/include)
|
||||
include_directories(${WIN32_DEP_DIR}/jsoncpp/include)
|
||||
list (APPEND opendht_SOURCES
|
||||
src/base64.h
|
||||
src/base64.cpp
|
||||
)
|
||||
add_definitions(-DOPENDHT_JSONCPP)
|
||||
include_directories(${WIN32_DEP_DIR}/msgpack-c/include)
|
||||
if (OPENDHT_HTTP OR OPENDHT_PEER_DISCOVERY)
|
||||
include_directories(
|
||||
${WIN32_DEP_DIR}/asio/asio/include
|
||||
${WIN32_DEP_DIR}/openssl/include
|
||||
${WIN32_DEP_DIR}/restinio/dev
|
||||
${WIN32_DEP_DIR}/fmt/include
|
||||
${WIN32_DEP_DIR}/http_parser
|
||||
)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
find_package (GnuTLS 3.1 REQUIRED)
|
||||
find_package (Msgpack 1.2 REQUIRED)
|
||||
if (OPENDHT_TOOLS)
|
||||
find_package (Readline 6 REQUIRED)
|
||||
endif ()
|
||||
if (OPENDHT_HTTP OR OPENDHT_PEER_DISCOVERY)
|
||||
add_definitions(-DASIO_STANDALONE)
|
||||
endif()
|
||||
|
||||
# Build flags
|
||||
set (CMAKE_CXX_STANDARD 17)
|
||||
set (CMAKE_CXX_STANDARD_REQUIRED on)
|
||||
|
||||
if (NOT MSVC)
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-return-type -Wall -Wextra -Wnon-virtual-dtor -pedantic-errors -fvisibility=hidden")
|
||||
if (OPENDHT_SANITIZE)
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fstack-protector-strong")
|
||||
endif ()
|
||||
else ()
|
||||
add_definitions(-D_WINSOCK_DEPRECATED_NO_WARNINGS
|
||||
-D_CRT_SECURE_NO_WARNINGS
|
||||
-DWIN32_LEAN_AND_MEAN
|
||||
-DSTATIC_GETOPT
|
||||
-DGNUTLS_INTERNAL_BUILD)
|
||||
set(DISABLE_MSC_WARNINGS "/wd4101 /wd4244 /wd4267 /wd4273 /wd4804 /wd4834 /wd4996")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${DISABLE_MSC_WARNINGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
|
||||
endif ()
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMSGPACK_NO_BOOST -DMSGPACK_DISABLE_LEGACY_NIL -DMSGPACK_DISABLE_LEGACY_CONVERT")
|
||||
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
endif ()
|
||||
add_definitions(-DPACKAGE_VERSION="${opendht_VERSION}")
|
||||
if (OPENDHT_LOG)
|
||||
add_definitions(-DOPENDHT_LOG=true)
|
||||
else ()
|
||||
add_definitions(-DOPENDHT_LOG=false)
|
||||
endif()
|
||||
|
||||
if (MSGPACK_INCLUDE_DIRS)
|
||||
include_directories (SYSTEM "${MSGPACK_INCLUDE_DIRS}")
|
||||
endif ()
|
||||
if (GNUTLS_INCLUDE_DIRS)
|
||||
include_directories (SYSTEM "${GNUTLS_INCLUDE_DIRS}")
|
||||
endif ()
|
||||
if (Nettle_INCLUDE_DIRS)
|
||||
include_directories (SYSTEM "${Nettle_INCLUDE_DIRS}")
|
||||
endif ()
|
||||
if (ASIO_INCLUDE_DIR)
|
||||
include_directories (SYSTEM "${ASIO_INCLUDE_DIR}")
|
||||
endif ()
|
||||
if (Restinio_INCLUDE_DIR)
|
||||
include_directories (SYSTEM "${Restinio_INCLUDE_DIR}")
|
||||
endif ()
|
||||
if (Jsoncpp_INCLUDE_DIRS)
|
||||
include_directories (SYSTEM "${Jsoncpp_INCLUDE_DIRS}")
|
||||
endif ()
|
||||
link_directories (${Nettle_LIBRARY_DIRS})
|
||||
link_directories (${Jsoncpp_LIBRARY_DIRS})
|
||||
link_directories (${argon2_LIBRARY_DIRS})
|
||||
include_directories (
|
||||
./
|
||||
include/
|
||||
include/opendht/
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/
|
||||
)
|
||||
|
||||
# Install dirs
|
||||
include (GNUInstallDirs)
|
||||
set (prefix ${CMAKE_INSTALL_PREFIX})
|
||||
set (exec_prefix "\${prefix}")
|
||||
set (libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
|
||||
set (includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
set (bindir "${CMAKE_INSTALL_FULL_BINDIR}")
|
||||
set (sysconfdir "${CMAKE_INSTALL_FULL_SYSCONFDIR}")
|
||||
set (top_srcdir "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
# Sources
|
||||
list (APPEND opendht_SOURCES
|
||||
src/utils.cpp
|
||||
src/infohash.cpp
|
||||
@@ -46,22 +205,30 @@ list (APPEND opendht_SOURCES
|
||||
src/node.cpp
|
||||
src/value.cpp
|
||||
src/dht.cpp
|
||||
src/op_cache.cpp
|
||||
src/storage.h
|
||||
src/listener.h
|
||||
src/search.h
|
||||
src/value_cache.h
|
||||
src/op_cache.h
|
||||
src/net.h
|
||||
src/parsed_message.h
|
||||
src/request.h
|
||||
src/callbacks.cpp
|
||||
src/routing_table.cpp
|
||||
src/node_cache.cpp
|
||||
src/network_engine.cpp
|
||||
src/securedht.cpp
|
||||
src/dhtrunner.cpp
|
||||
src/argon2/argon2.c
|
||||
src/argon2/core.c
|
||||
src/argon2/blake2/blake2b.c
|
||||
src/argon2/thread.c
|
||||
src/argon2/encoding.c
|
||||
src/argon2/ref.c
|
||||
src/log.cpp
|
||||
src/network_utils.cpp
|
||||
src/thread_pool.cpp
|
||||
)
|
||||
|
||||
list (APPEND opendht_HEADERS
|
||||
include/opendht/def.h
|
||||
include/opendht/utils.h
|
||||
include/opendht/sockaddr.h
|
||||
include/opendht/rng.h
|
||||
include/opendht/crypto.h
|
||||
include/opendht/infohash.h
|
||||
@@ -69,50 +236,143 @@ list (APPEND opendht_HEADERS
|
||||
include/opendht/node.h
|
||||
include/opendht/value.h
|
||||
include/opendht/dht.h
|
||||
include/opendht/dht_interface.h
|
||||
include/opendht/callbacks.h
|
||||
include/opendht/routing_table.h
|
||||
include/opendht/node_cache.h
|
||||
include/opendht/network_engine.h
|
||||
include/opendht/scheduler.h
|
||||
include/opendht/rate_limiter.h
|
||||
include/opendht/securedht.h
|
||||
include/opendht/log.h
|
||||
include/opendht/log_enable.h
|
||||
include/opendht/thread_pool.h
|
||||
include/opendht/network_utils.h
|
||||
include/opendht.h
|
||||
)
|
||||
|
||||
configure_file (
|
||||
opendht.pc.in
|
||||
opendht.pc
|
||||
@ONLY
|
||||
)
|
||||
if (OPENDHT_PEER_DISCOVERY)
|
||||
list (APPEND opendht_SOURCES src/peer_discovery.cpp)
|
||||
list (APPEND opendht_HEADERS include/opendht/peer_discovery.h)
|
||||
add_definitions(-DOPENDHT_PEER_DISCOVERY)
|
||||
endif()
|
||||
|
||||
include_directories (
|
||||
./
|
||||
include/
|
||||
include/opendht/
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/
|
||||
)
|
||||
if (OPENDHT_PYTHON)
|
||||
message("Indexation enabled since it is required for Python support")
|
||||
set(OPENDHT_INDEX ON)
|
||||
endif()
|
||||
if (OPENDHT_INDEX)
|
||||
list (APPEND opendht_SOURCES src/indexation/pht.cpp)
|
||||
list (APPEND opendht_HEADERS include/opendht/indexation/pht.h)
|
||||
add_definitions(-DOPENDHT_INDEXATION)
|
||||
endif()
|
||||
|
||||
if (OPENDHT_DEBUG)
|
||||
set(CMAKE_BUILD_TYPE Debug)
|
||||
else ()
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
if (OPENDHT_PROXY_SERVER)
|
||||
add_definitions(-DOPENDHT_PROXY_SERVER)
|
||||
if (OPENDHT_PROXY_SERVER_IDENTITY)
|
||||
add_definitions(-DOPENDHT_PROXY_SERVER_IDENTITY)
|
||||
endif()
|
||||
list (APPEND opendht_HEADERS
|
||||
include/opendht/dht_proxy_server.h
|
||||
)
|
||||
list (APPEND opendht_SOURCES
|
||||
src/dht_proxy_server.cpp
|
||||
)
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED CMAKE_INSTALL_LIBDIR)
|
||||
set(CMAKE_INSTALL_LIBDIR lib)
|
||||
if (OPENDHT_PROXY_CLIENT)
|
||||
add_definitions(-DOPENDHT_PROXY_CLIENT)
|
||||
list (APPEND opendht_HEADERS
|
||||
include/opendht/dht_proxy_client.h
|
||||
)
|
||||
list (APPEND opendht_SOURCES
|
||||
src/dht_proxy_client.cpp
|
||||
)
|
||||
endif ()
|
||||
|
||||
if (OPENDHT_HTTP)
|
||||
if (OPENDHT_PUSH_NOTIFICATIONS)
|
||||
message("Using push notification")
|
||||
add_definitions(-DOPENDHT_PUSH_NOTIFICATIONS)
|
||||
endif ()
|
||||
list (APPEND opendht_HEADERS
|
||||
include/opendht/proxy.h
|
||||
include/opendht/http.h
|
||||
src/compat/os_cert.h
|
||||
)
|
||||
list (APPEND opendht_SOURCES
|
||||
src/http.cpp
|
||||
src/compat/os_cert.cpp
|
||||
)
|
||||
endif ()
|
||||
|
||||
if (MSVC)
|
||||
list (APPEND opendht_HEADERS src/compat/msvc/unistd.h)
|
||||
endif ()
|
||||
|
||||
# Targets
|
||||
if (OPENDHT_STATIC)
|
||||
add_library (opendht-static STATIC
|
||||
${opendht_SOURCES}
|
||||
${opendht_HEADERS}
|
||||
)
|
||||
set_target_properties (opendht-static PROPERTIES OUTPUT_NAME "opendht")
|
||||
if (OPENDHT_LTO)
|
||||
target_link_libraries(opendht-static -flto -fuse-linker-plugin)
|
||||
endif ()
|
||||
target_link_libraries(opendht-static gnutls nettle)
|
||||
install (TARGETS opendht-static DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
if (NOT MSVC)
|
||||
add_library (opendht-static STATIC
|
||||
${opendht_SOURCES}
|
||||
${opendht_HEADERS}
|
||||
)
|
||||
set_target_properties (opendht-static PROPERTIES OUTPUT_NAME "opendht")
|
||||
target_include_directories(opendht-static SYSTEM PRIVATE ${argon2_INCLUDE_DIRS})
|
||||
target_link_libraries(opendht-static
|
||||
PRIVATE ${argon2_LIBRARIES}
|
||||
PUBLIC ${CMAKE_THREAD_LIBS_INIT} ${GNUTLS_LIBRARIES} ${Nettle_STATIC_LIBRARIES}
|
||||
${Jsoncpp_STATIC_LIBRARIES} ${FMT_LIBRARY} ${HTTP_PARSER_LIBRARY}
|
||||
${OPENSSL_STATIC_LIBRARIES})
|
||||
else ()
|
||||
if (OPENDHT_TOOLS)
|
||||
function (add_obj_lib name libfile)
|
||||
add_library(${name} OBJECT IMPORTED)
|
||||
set_property(TARGET ${name} PROPERTY IMPORTED_OBJECTS ${libfile})
|
||||
endfunction ()
|
||||
add_obj_lib (win32_json ${WIN32_DEP_DIR}/../msvc/lib/x64/lib_json.lib)
|
||||
add_obj_lib (win32_gnutls ${WIN32_DEP_DIR}/../msvc/lib/x64/libgnutls.lib)
|
||||
add_obj_lib (win32_argon2 ${WIN32_DEP_DIR}/argon2/vs2015/Argon2Ref/vs2015/build/Argon2Ref.lib)
|
||||
list (APPEND obj_libs
|
||||
$<TARGET_OBJECTS:win32_json>
|
||||
$<TARGET_OBJECTS:win32_gnutls>
|
||||
$<TARGET_OBJECTS:win32_argon2>
|
||||
)
|
||||
if (OPENDHT_HTTP)
|
||||
add_obj_lib (win32_fmt ${WIN32_DEP_DIR}/fmt/msvc/Release/fmt.lib)
|
||||
add_obj_lib (win32_http_parser ${WIN32_DEP_DIR}/http_parser/x64/Release/http-parser.lib)
|
||||
add_obj_lib (win32_ssl ${WIN32_DEP_DIR}/openssl/libssl_static.lib)
|
||||
add_obj_lib (win32_crypto ${WIN32_DEP_DIR}/openssl/libcrypto_static.lib)
|
||||
list (APPEND obj_libs
|
||||
$<TARGET_OBJECTS:win32_fmt>
|
||||
$<TARGET_OBJECTS:win32_http_parser>
|
||||
$<TARGET_OBJECTS:win32_ssl>
|
||||
$<TARGET_OBJECTS:win32_crypto>
|
||||
)
|
||||
endif ()
|
||||
else ()
|
||||
list (APPEND win32_Libs
|
||||
${PROJECT_SOURCE_DIR}/../../msvc/lib/x64/libgnutls.lib
|
||||
${PROJECT_SOURCE_DIR}/../../msvc/lib/x64/lib_json.lib
|
||||
${PROJECT_SOURCE_DIR}/../argon2/vs2015/Argon2Ref/vs2015/build/Argon2Ref.lib
|
||||
)
|
||||
list (APPEND win32_Libs
|
||||
${PROJECT_SOURCE_DIR}/../fmt/msvc/Release/fmt.lib
|
||||
${PROJECT_SOURCE_DIR}/../http_parser/x64/Release/http-parser.lib
|
||||
${PROJECT_SOURCE_DIR}/../openssl/libssl.lib
|
||||
${PROJECT_SOURCE_DIR}/../openssl/libcrypto.lib
|
||||
)
|
||||
endif ()
|
||||
add_library (opendht-static STATIC
|
||||
${opendht_SOURCES}
|
||||
${opendht_HEADERS}
|
||||
${obj_libs}
|
||||
)
|
||||
target_link_libraries(opendht-static PUBLIC ${Win32_STATIC_LIBRARIES} ${Win32_IMPORT_LIBRARIES})
|
||||
set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4006")
|
||||
set_target_properties (opendht-static PROPERTIES OUTPUT_NAME "libopendht")
|
||||
endif()
|
||||
install (TARGETS opendht-static DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht)
|
||||
endif ()
|
||||
|
||||
if (OPENDHT_SHARED)
|
||||
@@ -122,20 +382,135 @@ if (OPENDHT_SHARED)
|
||||
)
|
||||
set_target_properties (opendht PROPERTIES IMPORT_SUFFIX "_import.lib")
|
||||
set_target_properties (opendht PROPERTIES SOVERSION ${opendht_VERSION_MAJOR} VERSION ${opendht_VERSION})
|
||||
if (OPENDHT_LTO)
|
||||
target_link_libraries(opendht -flto -fuse-linker-plugin)
|
||||
target_compile_definitions(opendht PRIVATE OPENDHT_BUILD)
|
||||
target_include_directories(opendht SYSTEM PRIVATE ${argon2_INCLUDE_DIRS})
|
||||
if (APPLE)
|
||||
target_link_libraries(opendht
|
||||
PUBLIC ${CMAKE_THREAD_LIBS_INIT} ${OPENSSL_LIBRARIES}
|
||||
PRIVATE ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES}
|
||||
${Jsoncpp_LIBRARIES}
|
||||
${FMT_LIBRARY} ${HTTP_PARSER_LIBRARY} ${argon2_LIBRARIES}
|
||||
SYSTEM "-framework CoreFoundation" "-framework Security")
|
||||
else ()
|
||||
target_link_libraries(opendht
|
||||
PUBLIC ${CMAKE_THREAD_LIBS_INIT} ${OPENSSL_LIBRARIES}
|
||||
PRIVATE ${GNUTLS_LIBRARIES} ${Nettle_LIBRARIES}
|
||||
${Jsoncpp_LIBRARIES}
|
||||
${FMT_LIBRARY} ${HTTP_PARSER_LIBRARY} ${argon2_LIBRARIES})
|
||||
endif ()
|
||||
target_link_libraries(opendht gnutls nettle)
|
||||
install (TARGETS opendht DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
install (TARGETS opendht DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht)
|
||||
endif ()
|
||||
|
||||
if (OPENDHT_C)
|
||||
if (OPENDHT_SHARED)
|
||||
add_library (opendht-c SHARED
|
||||
c/opendht.cpp
|
||||
c/opendht_c.h
|
||||
)
|
||||
target_compile_definitions(opendht-c PRIVATE OPENDHT_C_BUILD)
|
||||
target_link_libraries(opendht-c PRIVATE opendht)
|
||||
install (TARGETS opendht-c DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht-c)
|
||||
endif ()
|
||||
|
||||
if (OPENDHT_STATIC)
|
||||
add_library (opendht-c-static STATIC
|
||||
c/opendht.cpp
|
||||
c/opendht_c.h
|
||||
)
|
||||
target_link_libraries(opendht-c-static PRIVATE opendht-static)
|
||||
install (TARGETS opendht-c-static DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT opendht-c-static)
|
||||
endif ()
|
||||
|
||||
# PkgConfig module
|
||||
configure_file (
|
||||
opendht-c.pc.in
|
||||
opendht-c.pc
|
||||
@ONLY
|
||||
)
|
||||
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/opendht-c.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
install (FILES c/opendht_c.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include/opendht)
|
||||
endif ()
|
||||
|
||||
if (OPENDHT_TOOLS)
|
||||
add_subdirectory(tools)
|
||||
endif ()
|
||||
add_subdirectory(doc)
|
||||
|
||||
if (OPENDHT_PYTHON)
|
||||
add_subdirectory(python)
|
||||
endif ()
|
||||
|
||||
# CMake module
|
||||
write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/opendhtConfigVersion.cmake"
|
||||
VERSION ${opendht_VERSION}
|
||||
COMPATIBILITY AnyNewerVersion
|
||||
)
|
||||
# PkgConfig module
|
||||
configure_file (
|
||||
opendht.pc.in
|
||||
opendht.pc
|
||||
@ONLY
|
||||
)
|
||||
|
||||
# Install targets
|
||||
install (DIRECTORY include DESTINATION ${CMAKE_INSTALL_PREFIX})
|
||||
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/opendht.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
install (EXPORT opendht DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/opendht FILE opendhtConfig.cmake)
|
||||
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/opendhtConfigVersion.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/opendht)
|
||||
|
||||
# Unit tests
|
||||
if (OPENDHT_TESTS)
|
||||
pkg_search_module(Cppunit REQUIRED cppunit)
|
||||
# unit testing
|
||||
list (APPEND test_FILES
|
||||
tests/infohashtester.h
|
||||
tests/infohashtester.cpp
|
||||
tests/valuetester.h
|
||||
tests/valuetester.cpp
|
||||
tests/cryptotester.h
|
||||
tests/cryptotester.cpp
|
||||
tests/dhtrunnertester.h
|
||||
tests/dhtrunnertester.cpp
|
||||
tests/threadpooltester.h
|
||||
tests/threadpooltester.cpp
|
||||
)
|
||||
if (OPENDHT_TESTS_NETWORK)
|
||||
if (OPENDHT_PROXY_SERVER AND OPENDHT_PROXY_CLIENT)
|
||||
list (APPEND test_FILES
|
||||
tests/httptester.h
|
||||
tests/httptester.cpp
|
||||
tests/dhtproxytester.h
|
||||
tests/dhtproxytester.cpp
|
||||
)
|
||||
endif()
|
||||
if (OPENDHT_PEER_DISCOVERY)
|
||||
list (APPEND test_FILES
|
||||
tests/peerdiscoverytester.h
|
||||
tests/peerdiscoverytester.cpp
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
add_executable(opendht_unit_tests
|
||||
tests/tests_runner.cpp
|
||||
${test_FILES}
|
||||
)
|
||||
target_include_directories(opendht_unit_tests SYSTEM PRIVATE ${Cppunit_INCLUDE_DIR})
|
||||
target_link_directories(opendht_unit_tests PRIVATE ${Cppunit_LIBRARY_DIRS})
|
||||
if (OPENDHT_SHARED)
|
||||
target_link_libraries(opendht_unit_tests opendht)
|
||||
else ()
|
||||
target_link_libraries(opendht_unit_tests opendht-static)
|
||||
endif ()
|
||||
target_link_libraries(opendht_unit_tests
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${Cppunit_LIBRARIES}
|
||||
${GNUTLS_LIBRARIES}
|
||||
${Jsoncpp_LIBRARIES}
|
||||
)
|
||||
if (OPENDHT_PROXY_OPENSSL)
|
||||
target_link_libraries(opendht_unit_tests ${OPENSSL_LIBRARIES})
|
||||
endif()
|
||||
enable_testing()
|
||||
add_test(TEST opendht_unit_tests)
|
||||
endif()
|
||||
-248
@@ -1,248 +0,0 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
#output directories
|
||||
DebugLib/
|
||||
Debug/
|
||||
ReleaseLib/
|
||||
Release/
|
||||
.config/
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
*.VC.VC.opendb
|
||||
*.VC.db
|
||||
*.db
|
||||
*.filters
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# DNX
|
||||
project.lock.json
|
||||
artifacts/
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/packages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/packages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/packages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignoreable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Microsoft Azure ApplicationInsights config file
|
||||
ApplicationInsights.config
|
||||
|
||||
# Windows Store app package directory
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
node_modules/
|
||||
orleans.codegen.cs
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
!config.h
|
||||
@@ -1,257 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25123.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "opendht", "opendht.vcxproj", "{711397CE-E5D5-467D-9457-8716C047E50C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
DebugDLL|x64 = DebugDLL|x64
|
||||
DebugDLL|x86 = DebugDLL|x86
|
||||
DebugDLLStaticDeps|x64 = DebugDLLStaticDeps|x64
|
||||
DebugDLLStaticDeps|x86 = DebugDLLStaticDeps|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
ReleaseDLL|x64 = ReleaseDLL|x64
|
||||
ReleaseDLL|x86 = ReleaseDLL|x86
|
||||
ReleaseDLLStaticDeps|x64 = ReleaseDLLStaticDeps|x64
|
||||
ReleaseDLLStaticDeps|x86 = ReleaseDLLStaticDeps|x86
|
||||
ReleaseLTO|x64 = ReleaseLTO|x64
|
||||
ReleaseLTO|x86 = ReleaseLTO|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.Debug|x64.Build.0 = Debug|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.Debug|x86.Build.0 = Debug|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.DebugDLL|x64.ActiveCfg = Debug|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.DebugDLL|x64.Build.0 = Debug|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.DebugDLL|x86.ActiveCfg = Debug|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.DebugDLL|x86.Build.0 = Debug|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.DebugDLLStaticDeps|x64.ActiveCfg = Debug|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.DebugDLLStaticDeps|x64.Build.0 = Debug|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.DebugDLLStaticDeps|x86.ActiveCfg = Debug|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.DebugDLLStaticDeps|x86.Build.0 = Debug|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.Release|x64.ActiveCfg = Release|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.Release|x64.Build.0 = Release|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.Release|x86.ActiveCfg = Release|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.Release|x86.Build.0 = Release|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseDLL|x64.ActiveCfg = Release|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseDLL|x64.Build.0 = Release|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseDLL|x86.ActiveCfg = Release|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseDLL|x86.Build.0 = Release|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseDLLStaticDeps|x64.ActiveCfg = Release|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseDLLStaticDeps|x64.Build.0 = Release|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseDLLStaticDeps|x86.ActiveCfg = Release|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseDLLStaticDeps|x86.Build.0 = Release|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseLTO|x64.ActiveCfg = Release|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseLTO|x64.Build.0 = Release|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseLTO|x86.ActiveCfg = Release|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseLTO|x86.Build.0 = Release|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.Debug|x64.Build.0 = Debug|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.Debug|x86.Build.0 = Debug|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.DebugDLL|x64.ActiveCfg = DebugDLL|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.DebugDLL|x64.Build.0 = DebugDLL|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.DebugDLL|x86.ActiveCfg = DebugDLL|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.DebugDLL|x86.Build.0 = DebugDLL|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.DebugDLLStaticDeps|x64.ActiveCfg = DebugDLL|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.DebugDLLStaticDeps|x64.Build.0 = DebugDLL|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.DebugDLLStaticDeps|x86.ActiveCfg = DebugDLL|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.DebugDLLStaticDeps|x86.Build.0 = DebugDLL|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.Release|x64.ActiveCfg = Release|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.Release|x64.Build.0 = Release|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.Release|x86.ActiveCfg = Release|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.Release|x86.Build.0 = Release|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.ReleaseDLL|x64.ActiveCfg = ReleaseDLL|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.ReleaseDLL|x64.Build.0 = ReleaseDLL|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.ReleaseDLL|x86.ActiveCfg = ReleaseDLL|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.ReleaseDLL|x86.Build.0 = ReleaseDLL|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.ReleaseDLLStaticDeps|x64.ActiveCfg = ReleaseDLL|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.ReleaseDLLStaticDeps|x64.Build.0 = ReleaseDLL|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.ReleaseDLLStaticDeps|x86.ActiveCfg = ReleaseDLL|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.ReleaseDLLStaticDeps|x86.Build.0 = ReleaseDLL|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.ReleaseLTO|x64.ActiveCfg = ReleaseLTO|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.ReleaseLTO|x64.Build.0 = ReleaseLTO|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.ReleaseLTO|x86.ActiveCfg = ReleaseLTO|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.ReleaseLTO|x86.Build.0 = ReleaseLTO|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.Debug|x64.Build.0 = Debug|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.Debug|x86.Build.0 = Debug|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.DebugDLL|x64.ActiveCfg = DebugDLL|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.DebugDLL|x64.Build.0 = DebugDLL|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.DebugDLL|x86.ActiveCfg = DebugDLL|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.DebugDLL|x86.Build.0 = DebugDLL|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.DebugDLLStaticDeps|x64.ActiveCfg = DebugDLLStaticDeps|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.DebugDLLStaticDeps|x64.Build.0 = DebugDLLStaticDeps|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.DebugDLLStaticDeps|x86.ActiveCfg = DebugDLLStaticDeps|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.DebugDLLStaticDeps|x86.Build.0 = DebugDLLStaticDeps|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.Release|x64.ActiveCfg = Release|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.Release|x64.Build.0 = Release|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.Release|x86.ActiveCfg = Release|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.Release|x86.Build.0 = Release|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.ReleaseDLL|x64.ActiveCfg = ReleaseDLL|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.ReleaseDLL|x64.Build.0 = ReleaseDLL|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.ReleaseDLL|x86.ActiveCfg = ReleaseDLL|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.ReleaseDLL|x86.Build.0 = ReleaseDLL|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.ReleaseDLLStaticDeps|x64.ActiveCfg = ReleaseDLLStaticDeps|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.ReleaseDLLStaticDeps|x64.Build.0 = ReleaseDLLStaticDeps|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.ReleaseDLLStaticDeps|x86.ActiveCfg = ReleaseDLLStaticDeps|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.ReleaseDLLStaticDeps|x86.Build.0 = ReleaseDLLStaticDeps|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.ReleaseLTO|x64.ActiveCfg = ReleaseLTO|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.ReleaseLTO|x64.Build.0 = ReleaseLTO|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.ReleaseLTO|x86.ActiveCfg = ReleaseLTO|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.ReleaseLTO|x86.Build.0 = ReleaseLTO|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.Debug|x64.Build.0 = Debug|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.Debug|x86.Build.0 = Debug|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.DebugDLL|x64.ActiveCfg = DebugDLL|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.DebugDLL|x64.Build.0 = DebugDLL|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.DebugDLL|x86.ActiveCfg = DebugDLL|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.DebugDLL|x86.Build.0 = DebugDLL|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.DebugDLLStaticDeps|x64.ActiveCfg = DebugDLL|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.DebugDLLStaticDeps|x64.Build.0 = DebugDLL|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.DebugDLLStaticDeps|x86.ActiveCfg = DebugDLL|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.DebugDLLStaticDeps|x86.Build.0 = DebugDLL|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.Release|x64.ActiveCfg = Release|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.Release|x64.Build.0 = Release|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.Release|x86.ActiveCfg = Release|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.Release|x86.Build.0 = Release|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.ReleaseDLL|x64.ActiveCfg = ReleaseDLL|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.ReleaseDLL|x64.Build.0 = ReleaseDLL|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.ReleaseDLL|x86.ActiveCfg = ReleaseDLL|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.ReleaseDLL|x86.Build.0 = ReleaseDLL|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.ReleaseDLLStaticDeps|x64.ActiveCfg = ReleaseDLL|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.ReleaseDLLStaticDeps|x64.Build.0 = ReleaseDLL|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.ReleaseDLLStaticDeps|x86.ActiveCfg = ReleaseDLL|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.ReleaseDLLStaticDeps|x86.Build.0 = ReleaseDLL|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.ReleaseLTO|x64.ActiveCfg = ReleaseLTO|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.ReleaseLTO|x64.Build.0 = ReleaseLTO|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.ReleaseLTO|x86.ActiveCfg = ReleaseLTO|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.ReleaseLTO|x86.Build.0 = ReleaseLTO|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.Debug|x64.Build.0 = Debug|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.Debug|x86.Build.0 = Debug|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.DebugDLL|x64.ActiveCfg = DebugDLL|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.DebugDLL|x64.Build.0 = DebugDLL|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.DebugDLL|x86.ActiveCfg = DebugDLL|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.DebugDLL|x86.Build.0 = DebugDLL|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.DebugDLLStaticDeps|x64.ActiveCfg = DebugDLLStaticDeps|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.DebugDLLStaticDeps|x64.Build.0 = DebugDLLStaticDeps|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.DebugDLLStaticDeps|x86.ActiveCfg = DebugDLLStaticDeps|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.DebugDLLStaticDeps|x86.Build.0 = DebugDLLStaticDeps|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.Release|x64.ActiveCfg = Release|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.Release|x64.Build.0 = Release|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.Release|x86.ActiveCfg = Release|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.Release|x86.Build.0 = Release|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.ReleaseDLL|x64.ActiveCfg = ReleaseDLL|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.ReleaseDLL|x64.Build.0 = ReleaseDLL|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.ReleaseDLL|x86.ActiveCfg = ReleaseDLL|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.ReleaseDLL|x86.Build.0 = ReleaseDLL|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.ReleaseDLLStaticDeps|x64.ActiveCfg = ReleaseDLLStaticDeps|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.ReleaseDLLStaticDeps|x64.Build.0 = ReleaseDLLStaticDeps|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.ReleaseDLLStaticDeps|x86.ActiveCfg = ReleaseDLLStaticDeps|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.ReleaseDLLStaticDeps|x86.Build.0 = ReleaseDLLStaticDeps|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.ReleaseLTO|x64.ActiveCfg = ReleaseLTO|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.ReleaseLTO|x64.Build.0 = ReleaseLTO|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.ReleaseLTO|x86.ActiveCfg = ReleaseLTO|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.ReleaseLTO|x86.Build.0 = ReleaseLTO|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.Debug|x64.Build.0 = Debug|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.Debug|x86.Build.0 = Debug|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.DebugDLL|x64.ActiveCfg = DebugDLL|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.DebugDLL|x64.Build.0 = DebugDLL|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.DebugDLL|x86.ActiveCfg = DebugDLL|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.DebugDLL|x86.Build.0 = DebugDLL|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.DebugDLLStaticDeps|x64.ActiveCfg = DebugDLL|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.DebugDLLStaticDeps|x64.Build.0 = DebugDLL|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.DebugDLLStaticDeps|x86.ActiveCfg = DebugDLL|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.DebugDLLStaticDeps|x86.Build.0 = DebugDLL|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.Release|x64.ActiveCfg = Release|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.Release|x64.Build.0 = Release|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.Release|x86.ActiveCfg = Release|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.Release|x86.Build.0 = Release|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.ReleaseDLL|x64.ActiveCfg = ReleaseDLL|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.ReleaseDLL|x64.Build.0 = ReleaseDLL|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.ReleaseDLL|x86.ActiveCfg = ReleaseDLL|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.ReleaseDLL|x86.Build.0 = ReleaseDLL|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.ReleaseDLLStaticDeps|x64.ActiveCfg = ReleaseDLL|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.ReleaseDLLStaticDeps|x64.Build.0 = ReleaseDLL|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.ReleaseDLLStaticDeps|x86.ActiveCfg = ReleaseDLL|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.ReleaseDLLStaticDeps|x86.Build.0 = ReleaseDLL|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.ReleaseLTO|x64.ActiveCfg = ReleaseLTO|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.ReleaseLTO|x64.Build.0 = ReleaseLTO|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.ReleaseLTO|x86.ActiveCfg = ReleaseLTO|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.ReleaseLTO|x86.Build.0 = ReleaseLTO|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.Debug|x64.Build.0 = Debug|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.Debug|x86.Build.0 = Debug|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.DebugDLL|x64.ActiveCfg = DebugDLL|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.DebugDLL|x64.Build.0 = DebugDLL|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.DebugDLL|x86.ActiveCfg = DebugDLL|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.DebugDLL|x86.Build.0 = DebugDLL|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.DebugDLLStaticDeps|x64.ActiveCfg = DebugDLL|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.DebugDLLStaticDeps|x64.Build.0 = DebugDLL|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.DebugDLLStaticDeps|x86.ActiveCfg = DebugDLL|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.DebugDLLStaticDeps|x86.Build.0 = DebugDLL|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.Release|x64.ActiveCfg = Release|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.Release|x64.Build.0 = Release|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.Release|x86.ActiveCfg = Release|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.Release|x86.Build.0 = Release|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.ReleaseDLL|x64.ActiveCfg = ReleaseDLL|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.ReleaseDLL|x64.Build.0 = ReleaseDLL|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.ReleaseDLL|x86.ActiveCfg = ReleaseDLL|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.ReleaseDLL|x86.Build.0 = ReleaseDLL|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.ReleaseDLLStaticDeps|x64.ActiveCfg = ReleaseDLL|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.ReleaseDLLStaticDeps|x64.Build.0 = ReleaseDLL|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.ReleaseDLLStaticDeps|x86.ActiveCfg = ReleaseDLL|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.ReleaseDLLStaticDeps|x86.Build.0 = ReleaseDLL|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.ReleaseLTO|x64.ActiveCfg = ReleaseLTO|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.ReleaseLTO|x64.Build.0 = ReleaseLTO|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.ReleaseLTO|x86.ActiveCfg = ReleaseLTO|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.ReleaseLTO|x86.Build.0 = ReleaseLTO|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.Debug|x64.Build.0 = Debug|x64
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.Debug|x86.Build.0 = Debug|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.DebugDLL|x64.ActiveCfg = Release|x64
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.DebugDLL|x64.Build.0 = Release|x64
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.DebugDLL|x86.ActiveCfg = Debug|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.DebugDLL|x86.Build.0 = Debug|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.DebugDLLStaticDeps|x64.ActiveCfg = Release|x64
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.DebugDLLStaticDeps|x64.Build.0 = Release|x64
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.DebugDLLStaticDeps|x86.ActiveCfg = Debug|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.DebugDLLStaticDeps|x86.Build.0 = Debug|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.Release|x64.ActiveCfg = Release|x64
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.Release|x64.Build.0 = Release|x64
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.Release|x86.ActiveCfg = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.Release|x86.Build.0 = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseDLL|x64.ActiveCfg = Release|x64
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseDLL|x64.Build.0 = Release|x64
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseDLL|x86.ActiveCfg = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseDLL|x86.Build.0 = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseDLLStaticDeps|x64.ActiveCfg = Release|x64
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseDLLStaticDeps|x64.Build.0 = Release|x64
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseDLLStaticDeps|x86.ActiveCfg = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseDLLStaticDeps|x86.Build.0 = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseLTO|x64.ActiveCfg = Release|x64
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseLTO|x64.Build.0 = Release|x64
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseLTO|x86.ActiveCfg = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseLTO|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,224 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\src\callbacks.cpp" />
|
||||
<ClCompile Include="..\src\crypto.cpp" />
|
||||
<ClCompile Include="..\src\default_types.cpp" />
|
||||
<ClCompile Include="..\src\dht.cpp" />
|
||||
<ClCompile Include="..\src\dhtrunner.cpp" />
|
||||
<ClCompile Include="..\src\infohash.cpp" />
|
||||
<ClCompile Include="..\src\network_engine.cpp" />
|
||||
<ClCompile Include="..\src\node.cpp" />
|
||||
<ClCompile Include="..\src\node_cache.cpp" />
|
||||
<ClCompile Include="..\src\routing_table.cpp" />
|
||||
<ClCompile Include="..\src\securedht.cpp" />
|
||||
<ClCompile Include="..\src\utils.cpp" />
|
||||
<ClCompile Include="..\src\value.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\include\opendht.h" />
|
||||
<ClInclude Include="..\include\opendht\callbacks.h" />
|
||||
<ClInclude Include="..\include\opendht\crypto.h" />
|
||||
<ClInclude Include="..\include\opendht\default_types.h" />
|
||||
<ClInclude Include="..\include\opendht\dht.h" />
|
||||
<ClInclude Include="..\include\opendht\dhtrunner.h" />
|
||||
<ClInclude Include="..\include\opendht\infohash.h" />
|
||||
<ClInclude Include="..\include\opendht\log.h" />
|
||||
<ClInclude Include="..\include\opendht\network_engine.h" />
|
||||
<ClInclude Include="..\include\opendht\node.h" />
|
||||
<ClInclude Include="..\include\opendht\node_cache.h" />
|
||||
<ClInclude Include="..\include\opendht\request.h" />
|
||||
<ClInclude Include="..\include\opendht\rng.h" />
|
||||
<ClInclude Include="..\include\opendht\routing_table.h" />
|
||||
<ClInclude Include="..\include\opendht\scheduler.h" />
|
||||
<ClInclude Include="..\include\opendht\securedht.h" />
|
||||
<ClInclude Include="..\include\opendht\utils.h" />
|
||||
<ClInclude Include="..\include\opendht\value.h" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{711397CE-E5D5-467D-9457-8716C047E50C}</ProjectGuid>
|
||||
<RootNamespace>opendht</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<OutDir>$(ProjectDir)..\..\</OutDir>
|
||||
<IntDir>$(Platform)\$(Configuration)\</IntDir>
|
||||
<TargetName>opendhtd</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<OutDir>$(ProjectDir)..\..\</OutDir>
|
||||
<IntDir>$(Platform)\$(Configuration)\</IntDir>
|
||||
<TargetName>opendht</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<OutDir>$(ProjectDir)..\..\</OutDir>
|
||||
<TargetName>opendhtd</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<OutDir>$(ProjectDir)..\..\</OutDir>
|
||||
<TargetName>opendht</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\include;..\include;..\include\opendht;..\..\msgpack-c\include;</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4800;4101;4267;4244;4503;</DisableSpecificWarnings>
|
||||
<AdditionalOptions>-D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions)</AdditionalOptions>
|
||||
<SuppressStartupBanner>false</SuppressStartupBanner>
|
||||
</ClCompile>
|
||||
<PostBuildEvent>
|
||||
<Command>mkdir $(OutDir)\include
|
||||
mkdir $(OutDir)\include\opendht
|
||||
copy ..\include\opendht\*.h $(OutDir)\include\opendht
|
||||
copy ..\include\opendht.h $(OutDir)\include\</Command>
|
||||
</PostBuildEvent>
|
||||
<Lib>
|
||||
<OutputFile>$(OutDir)\lib\x86\$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\include;..\include;..\include\opendht;..\..\msgpack-c\include;</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4800;4101;4267;4244;4503;</DisableSpecificWarnings>
|
||||
<AdditionalOptions>-D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<PostBuildEvent>
|
||||
<Command>mkdir $(OutDir)\include
|
||||
mkdir $(OutDir)\include\opendht
|
||||
copy ..\include\opendht\*.h $(OutDir)\include\opendht
|
||||
copy ..\include\opendht.h $(OutDir)\include\</Command>
|
||||
</PostBuildEvent>
|
||||
<Lib>
|
||||
<OutputFile>$(OutDir)\lib\x64\$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\include;..\include;..\include\opendht;..\..\msgpack-c\include;</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4800;4101;4267;4244;4503;</DisableSpecificWarnings>
|
||||
<AdditionalOptions>-D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions)</AdditionalOptions>
|
||||
<ProgramDataBaseFileName>$(OutDir)\lib\x86\$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>mkdir $(OutDir)\include
|
||||
mkdir $(OutDir)\include\opendht
|
||||
copy ..\include\opendht\*.h $(OutDir)\include\opendht
|
||||
copy ..\include\opendht.h $(OutDir)\include\</Command>
|
||||
</PostBuildEvent>
|
||||
<Lib>
|
||||
<OutputFile>$(OutDir)\lib\x86\$(TargetName)$(TargetExt)</OutputFile>
|
||||
<LinkTimeCodeGeneration>false</LinkTimeCodeGeneration>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Full</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\include;..\include;..\include\opendht;..\..\msgpack-c\include;</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32_NATIVE;WIN32_LEAN_AND_MEAN;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4804;4800;4101;4267;4244;4503;</DisableSpecificWarnings>
|
||||
<AdditionalOptions>-D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions)</AdditionalOptions>
|
||||
<ProgramDataBaseFileName>$(OutDir)\lib\x64\$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>mkdir $(OutDir)\include
|
||||
mkdir $(OutDir)\include\opendht
|
||||
copy ..\include\opendht\*.h $(OutDir)\include\opendht
|
||||
copy ..\include\opendht.h $(OutDir)\include\</Command>
|
||||
</PostBuildEvent>
|
||||
<Lib>
|
||||
<OutputFile>$(OutDir)\lib\x64\$(TargetName)$(TargetExt)</OutputFile>
|
||||
<LinkTimeCodeGeneration>false</LinkTimeCodeGeneration>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -1,292 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25123.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "opendht", "opendht.vcxproj", "{711397CE-E5D5-467D-9457-8716C047E50C}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518} = {070FEF2B-0C3F-4F33-9D3C-53C7330BF518}
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2} = {122A2EA4-B283-4241-9655-786DE78283B2}
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02} = {6F610EE8-7F59-4191-AB88-F63843267C02}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dependencies", "dependencies", "{71A1A9EC-1104-4335-A87E-AF8749FD5B34}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libgmp", "..\..\gmp\MSVC\libgmp.vcxproj", "{02B94302-23D6-43EF-8865-95CDE99D5DC2}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libgnutls", "..\..\gnutls\MSVC\libgnutls.vcxproj", "{6F610EE8-7F59-4191-AB88-F63843267C02}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2} = {02B94302-23D6-43EF-8865-95CDE99D5DC2}
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518} = {070FEF2B-0C3F-4F33-9D3C-53C7330BF518}
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32} = {CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C} = {200F3D3C-8B84-46D8-953A-6C0EBD283B5C}
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B} = {CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libiconv", "..\..\libiconv\MSVC\libiconv.vcxproj", "{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libhogweed", "..\..\nettle\MSVC\libhogweed.vcxproj", "{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2} = {02B94302-23D6-43EF-8865-95CDE99D5DC2}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnettle", "..\..\nettle\MSVC\libnettle.vcxproj", "{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libzlib", "..\..\zlib\MSVC\libzlib.vcxproj", "{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MessagePack", "..\..\msgpack-c\msgpack_vc8.vcxproj", "{122A2EA4-B283-4241-9655-786DE78283B2}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
DebugDLL|x64 = DebugDLL|x64
|
||||
DebugDLL|x86 = DebugDLL|x86
|
||||
DebugDLLStaticDeps|x64 = DebugDLLStaticDeps|x64
|
||||
DebugDLLStaticDeps|x86 = DebugDLLStaticDeps|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
ReleaseDLL|x64 = ReleaseDLL|x64
|
||||
ReleaseDLL|x86 = ReleaseDLL|x86
|
||||
ReleaseDLLStaticDeps|x64 = ReleaseDLLStaticDeps|x64
|
||||
ReleaseDLLStaticDeps|x86 = ReleaseDLLStaticDeps|x86
|
||||
ReleaseLTO|x64 = ReleaseLTO|x64
|
||||
ReleaseLTO|x86 = ReleaseLTO|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.Debug|x64.Build.0 = Debug|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.Debug|x86.Build.0 = Debug|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.DebugDLL|x64.ActiveCfg = Debug|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.DebugDLL|x64.Build.0 = Debug|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.DebugDLL|x86.ActiveCfg = Debug|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.DebugDLL|x86.Build.0 = Debug|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.DebugDLLStaticDeps|x64.ActiveCfg = Debug|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.DebugDLLStaticDeps|x64.Build.0 = Debug|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.DebugDLLStaticDeps|x86.ActiveCfg = Debug|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.DebugDLLStaticDeps|x86.Build.0 = Debug|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.Release|x64.ActiveCfg = Release|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.Release|x64.Build.0 = Release|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.Release|x86.ActiveCfg = Release|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.Release|x86.Build.0 = Release|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseDLL|x64.ActiveCfg = Release|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseDLL|x64.Build.0 = Release|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseDLL|x86.ActiveCfg = Release|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseDLL|x86.Build.0 = Release|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseDLLStaticDeps|x64.ActiveCfg = Release|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseDLLStaticDeps|x64.Build.0 = Release|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseDLLStaticDeps|x86.ActiveCfg = Release|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseDLLStaticDeps|x86.Build.0 = Release|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseLTO|x64.ActiveCfg = Release|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseLTO|x64.Build.0 = Release|x64
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseLTO|x86.ActiveCfg = Release|Win32
|
||||
{711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseLTO|x86.Build.0 = Release|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.Debug|x64.Build.0 = Debug|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.Debug|x86.Build.0 = Debug|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.DebugDLL|x64.ActiveCfg = DebugDLL|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.DebugDLL|x64.Build.0 = DebugDLL|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.DebugDLL|x86.ActiveCfg = DebugDLL|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.DebugDLL|x86.Build.0 = DebugDLL|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.DebugDLLStaticDeps|x64.ActiveCfg = DebugDLL|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.DebugDLLStaticDeps|x64.Build.0 = DebugDLL|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.DebugDLLStaticDeps|x86.ActiveCfg = DebugDLL|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.DebugDLLStaticDeps|x86.Build.0 = DebugDLL|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.Release|x64.ActiveCfg = Release|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.Release|x64.Build.0 = Release|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.Release|x86.ActiveCfg = Release|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.Release|x86.Build.0 = Release|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.ReleaseDLL|x64.ActiveCfg = ReleaseDLL|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.ReleaseDLL|x64.Build.0 = ReleaseDLL|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.ReleaseDLL|x86.ActiveCfg = ReleaseDLL|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.ReleaseDLL|x86.Build.0 = ReleaseDLL|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.ReleaseDLLStaticDeps|x64.ActiveCfg = ReleaseDLL|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.ReleaseDLLStaticDeps|x64.Build.0 = ReleaseDLL|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.ReleaseDLLStaticDeps|x86.ActiveCfg = ReleaseDLL|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.ReleaseDLLStaticDeps|x86.Build.0 = ReleaseDLL|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.ReleaseLTO|x64.ActiveCfg = ReleaseLTO|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.ReleaseLTO|x64.Build.0 = ReleaseLTO|x64
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.ReleaseLTO|x86.ActiveCfg = ReleaseLTO|Win32
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2}.ReleaseLTO|x86.Build.0 = ReleaseLTO|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.Debug|x64.Build.0 = Debug|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.Debug|x86.Build.0 = Debug|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.DebugDLL|x64.ActiveCfg = DebugDLL|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.DebugDLL|x64.Build.0 = DebugDLL|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.DebugDLL|x86.ActiveCfg = DebugDLL|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.DebugDLL|x86.Build.0 = DebugDLL|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.DebugDLLStaticDeps|x64.ActiveCfg = DebugDLLStaticDeps|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.DebugDLLStaticDeps|x64.Build.0 = DebugDLLStaticDeps|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.DebugDLLStaticDeps|x86.ActiveCfg = DebugDLLStaticDeps|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.DebugDLLStaticDeps|x86.Build.0 = DebugDLLStaticDeps|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.Release|x64.ActiveCfg = Release|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.Release|x64.Build.0 = Release|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.Release|x86.ActiveCfg = Release|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.Release|x86.Build.0 = Release|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.ReleaseDLL|x64.ActiveCfg = ReleaseDLL|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.ReleaseDLL|x64.Build.0 = ReleaseDLL|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.ReleaseDLL|x86.ActiveCfg = ReleaseDLL|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.ReleaseDLL|x86.Build.0 = ReleaseDLL|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.ReleaseDLLStaticDeps|x64.ActiveCfg = ReleaseDLLStaticDeps|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.ReleaseDLLStaticDeps|x64.Build.0 = ReleaseDLLStaticDeps|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.ReleaseDLLStaticDeps|x86.ActiveCfg = ReleaseDLLStaticDeps|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.ReleaseDLLStaticDeps|x86.Build.0 = ReleaseDLLStaticDeps|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.ReleaseLTO|x64.ActiveCfg = ReleaseLTO|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.ReleaseLTO|x64.Build.0 = ReleaseLTO|x64
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.ReleaseLTO|x86.ActiveCfg = ReleaseLTO|Win32
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02}.ReleaseLTO|x86.Build.0 = ReleaseLTO|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.Debug|x64.Build.0 = Debug|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.Debug|x86.Build.0 = Debug|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.DebugDLL|x64.ActiveCfg = DebugDLL|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.DebugDLL|x64.Build.0 = DebugDLL|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.DebugDLL|x86.ActiveCfg = DebugDLL|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.DebugDLL|x86.Build.0 = DebugDLL|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.DebugDLLStaticDeps|x64.ActiveCfg = DebugDLL|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.DebugDLLStaticDeps|x64.Build.0 = DebugDLL|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.DebugDLLStaticDeps|x86.ActiveCfg = DebugDLL|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.DebugDLLStaticDeps|x86.Build.0 = DebugDLL|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.Release|x64.ActiveCfg = Release|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.Release|x64.Build.0 = Release|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.Release|x86.ActiveCfg = Release|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.Release|x86.Build.0 = Release|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.ReleaseDLL|x64.ActiveCfg = ReleaseDLL|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.ReleaseDLL|x64.Build.0 = ReleaseDLL|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.ReleaseDLL|x86.ActiveCfg = ReleaseDLL|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.ReleaseDLL|x86.Build.0 = ReleaseDLL|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.ReleaseDLLStaticDeps|x64.ActiveCfg = ReleaseDLL|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.ReleaseDLLStaticDeps|x64.Build.0 = ReleaseDLL|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.ReleaseDLLStaticDeps|x86.ActiveCfg = ReleaseDLL|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.ReleaseDLLStaticDeps|x86.Build.0 = ReleaseDLL|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.ReleaseLTO|x64.ActiveCfg = ReleaseLTO|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.ReleaseLTO|x64.Build.0 = ReleaseLTO|x64
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.ReleaseLTO|x86.ActiveCfg = ReleaseLTO|Win32
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B}.ReleaseLTO|x86.Build.0 = ReleaseLTO|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.Debug|x64.Build.0 = Debug|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.Debug|x86.Build.0 = Debug|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.DebugDLL|x64.ActiveCfg = DebugDLL|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.DebugDLL|x64.Build.0 = DebugDLL|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.DebugDLL|x86.ActiveCfg = DebugDLL|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.DebugDLL|x86.Build.0 = DebugDLL|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.DebugDLLStaticDeps|x64.ActiveCfg = DebugDLLStaticDeps|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.DebugDLLStaticDeps|x64.Build.0 = DebugDLLStaticDeps|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.DebugDLLStaticDeps|x86.ActiveCfg = DebugDLLStaticDeps|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.DebugDLLStaticDeps|x86.Build.0 = DebugDLLStaticDeps|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.Release|x64.ActiveCfg = Release|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.Release|x64.Build.0 = Release|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.Release|x86.ActiveCfg = Release|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.Release|x86.Build.0 = Release|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.ReleaseDLL|x64.ActiveCfg = ReleaseDLL|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.ReleaseDLL|x64.Build.0 = ReleaseDLL|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.ReleaseDLL|x86.ActiveCfg = ReleaseDLL|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.ReleaseDLL|x86.Build.0 = ReleaseDLL|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.ReleaseDLLStaticDeps|x64.ActiveCfg = ReleaseDLLStaticDeps|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.ReleaseDLLStaticDeps|x64.Build.0 = ReleaseDLLStaticDeps|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.ReleaseDLLStaticDeps|x86.ActiveCfg = ReleaseDLLStaticDeps|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.ReleaseDLLStaticDeps|x86.Build.0 = ReleaseDLLStaticDeps|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.ReleaseLTO|x64.ActiveCfg = ReleaseLTO|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.ReleaseLTO|x64.Build.0 = ReleaseLTO|x64
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.ReleaseLTO|x86.ActiveCfg = ReleaseLTO|Win32
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C}.ReleaseLTO|x86.Build.0 = ReleaseLTO|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.Debug|x64.Build.0 = Debug|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.Debug|x86.Build.0 = Debug|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.DebugDLL|x64.ActiveCfg = DebugDLL|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.DebugDLL|x64.Build.0 = DebugDLL|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.DebugDLL|x86.ActiveCfg = DebugDLL|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.DebugDLL|x86.Build.0 = DebugDLL|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.DebugDLLStaticDeps|x64.ActiveCfg = DebugDLL|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.DebugDLLStaticDeps|x64.Build.0 = DebugDLL|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.DebugDLLStaticDeps|x86.ActiveCfg = DebugDLL|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.DebugDLLStaticDeps|x86.Build.0 = DebugDLL|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.Release|x64.ActiveCfg = Release|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.Release|x64.Build.0 = Release|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.Release|x86.ActiveCfg = Release|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.Release|x86.Build.0 = Release|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.ReleaseDLL|x64.ActiveCfg = ReleaseDLL|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.ReleaseDLL|x64.Build.0 = ReleaseDLL|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.ReleaseDLL|x86.ActiveCfg = ReleaseDLL|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.ReleaseDLL|x86.Build.0 = ReleaseDLL|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.ReleaseDLLStaticDeps|x64.ActiveCfg = ReleaseDLL|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.ReleaseDLLStaticDeps|x64.Build.0 = ReleaseDLL|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.ReleaseDLLStaticDeps|x86.ActiveCfg = ReleaseDLL|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.ReleaseDLLStaticDeps|x86.Build.0 = ReleaseDLL|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.ReleaseLTO|x64.ActiveCfg = ReleaseLTO|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.ReleaseLTO|x64.Build.0 = ReleaseLTO|x64
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.ReleaseLTO|x86.ActiveCfg = ReleaseLTO|Win32
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518}.ReleaseLTO|x86.Build.0 = ReleaseLTO|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.Debug|x64.Build.0 = Debug|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.Debug|x86.Build.0 = Debug|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.DebugDLL|x64.ActiveCfg = DebugDLL|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.DebugDLL|x64.Build.0 = DebugDLL|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.DebugDLL|x86.ActiveCfg = DebugDLL|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.DebugDLL|x86.Build.0 = DebugDLL|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.DebugDLLStaticDeps|x64.ActiveCfg = DebugDLL|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.DebugDLLStaticDeps|x64.Build.0 = DebugDLL|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.DebugDLLStaticDeps|x86.ActiveCfg = DebugDLL|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.DebugDLLStaticDeps|x86.Build.0 = DebugDLL|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.Release|x64.ActiveCfg = Release|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.Release|x64.Build.0 = Release|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.Release|x86.ActiveCfg = Release|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.Release|x86.Build.0 = Release|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.ReleaseDLL|x64.ActiveCfg = ReleaseDLL|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.ReleaseDLL|x64.Build.0 = ReleaseDLL|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.ReleaseDLL|x86.ActiveCfg = ReleaseDLL|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.ReleaseDLL|x86.Build.0 = ReleaseDLL|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.ReleaseDLLStaticDeps|x64.ActiveCfg = ReleaseDLL|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.ReleaseDLLStaticDeps|x64.Build.0 = ReleaseDLL|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.ReleaseDLLStaticDeps|x86.ActiveCfg = ReleaseDLL|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.ReleaseDLLStaticDeps|x86.Build.0 = ReleaseDLL|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.ReleaseLTO|x64.ActiveCfg = ReleaseLTO|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.ReleaseLTO|x64.Build.0 = ReleaseLTO|x64
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.ReleaseLTO|x86.ActiveCfg = ReleaseLTO|Win32
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32}.ReleaseLTO|x86.Build.0 = ReleaseLTO|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.Debug|x64.ActiveCfg = Debug|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.Debug|x86.Build.0 = Debug|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.DebugDLL|x64.ActiveCfg = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.DebugDLL|x64.Build.0 = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.DebugDLL|x86.ActiveCfg = Debug|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.DebugDLL|x86.Build.0 = Debug|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.DebugDLLStaticDeps|x64.ActiveCfg = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.DebugDLLStaticDeps|x64.Build.0 = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.DebugDLLStaticDeps|x86.ActiveCfg = Debug|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.DebugDLLStaticDeps|x86.Build.0 = Debug|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.Release|x64.ActiveCfg = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.Release|x86.ActiveCfg = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.Release|x86.Build.0 = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseDLL|x64.ActiveCfg = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseDLL|x64.Build.0 = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseDLL|x86.ActiveCfg = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseDLL|x86.Build.0 = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseDLLStaticDeps|x64.ActiveCfg = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseDLLStaticDeps|x64.Build.0 = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseDLLStaticDeps|x86.ActiveCfg = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseDLLStaticDeps|x86.Build.0 = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseLTO|x64.ActiveCfg = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseLTO|x64.Build.0 = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseLTO|x86.ActiveCfg = Release|Win32
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2}.ReleaseLTO|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{02B94302-23D6-43EF-8865-95CDE99D5DC2} = {71A1A9EC-1104-4335-A87E-AF8749FD5B34}
|
||||
{6F610EE8-7F59-4191-AB88-F63843267C02} = {71A1A9EC-1104-4335-A87E-AF8749FD5B34}
|
||||
{CB8BB76F-D3FF-434E-A85E-7FFC0893EC9B} = {71A1A9EC-1104-4335-A87E-AF8749FD5B34}
|
||||
{200F3D3C-8B84-46D8-953A-6C0EBD283B5C} = {71A1A9EC-1104-4335-A87E-AF8749FD5B34}
|
||||
{070FEF2B-0C3F-4F33-9D3C-53C7330BF518} = {71A1A9EC-1104-4335-A87E-AF8749FD5B34}
|
||||
{CA9A4A38-CC63-4BDB-8CFB-E058965DDA32} = {71A1A9EC-1104-4335-A87E-AF8749FD5B34}
|
||||
{122A2EA4-B283-4241-9655-786DE78283B2} = {71A1A9EC-1104-4335-A87E-AF8749FD5B34}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,71 +0,0 @@
|
||||
@ECHO OFF
|
||||
SETLOCAL EnableDelayedExpansion
|
||||
|
||||
SET UPSTREAMURL=https://github.com/atraczyk
|
||||
SET DEPENDENCIES=( ^
|
||||
gmp, ^
|
||||
gnutls, ^
|
||||
nettle, ^
|
||||
msgpack-c ^
|
||||
)
|
||||
|
||||
SET PASSDEPENDENCIES=%~1
|
||||
|
||||
git status >NUL 2>&1
|
||||
IF %ERRORLEVEL% EQU 128 (
|
||||
git init
|
||||
) ELSE (
|
||||
IF %ERRORLEVEL% EQU 9009 (
|
||||
ECHO git not installed.
|
||||
EXIT /B 1
|
||||
)
|
||||
)
|
||||
|
||||
SET CURRDIR=%~dp1
|
||||
|
||||
cd ..\..
|
||||
FOR %%I IN %DEPENDENCIES% DO (
|
||||
ECHO !PASSDEPENDENCIES! | FINDSTR /C:"%%I" >NUL 2>&1 || (
|
||||
CALL :cloneOrUpdateRepo "%%I" )
|
||||
)
|
||||
cd %CURRDIR% >NUL
|
||||
GOTO exit
|
||||
|
||||
:cloneOrUpdateRepo
|
||||
SET REPONAME=%~1
|
||||
IF NOT EXIST "%REPONAME%" (
|
||||
ECHO %REPONAME%: Existing folder not found. Cloning repository...
|
||||
SET REPOURL=%UPSTREAMURL%/%REPONAME%.git
|
||||
git clone !REPOURL! --quiet
|
||||
cd %REPONAME%
|
||||
git config --local core.autocrlf false
|
||||
git rm --cached -r . --quiet
|
||||
git reset --hard --quiet
|
||||
cd ..\
|
||||
)
|
||||
|
||||
SET PASSDEPENDENCIES=%PASSDEPENDENCIES% %REPONAME%
|
||||
|
||||
IF EXIST "%REPONAME%\MSVC\project_get_dependencies.bat" (
|
||||
ECHO %REPONAME%: Found additional dependencies...
|
||||
ECHO.
|
||||
cd %REPONAME%\MSVC
|
||||
project_get_dependencies.bat "!PASSDEPENDENCIES!" || GOTO exitOnError
|
||||
cd ..\..
|
||||
)
|
||||
ECHO.
|
||||
EXIT /B %ERRORLEVEL%
|
||||
|
||||
:exitOnError
|
||||
cd %CURRDIR%
|
||||
|
||||
:exit
|
||||
(
|
||||
ENDLOCAL
|
||||
SET PASSDEPENDENCIES=%PASSDEPENDENCIES%
|
||||
)
|
||||
|
||||
ECHO %CMDCMDLINE% | FINDSTR /L %COMSPEC% >NUL 2>&1
|
||||
IF %ERRORLEVEL% == 0 IF "%~1"=="" PAUSE
|
||||
|
||||
EXIT /B 0
|
||||
+12
-4
@@ -1,6 +1,12 @@
|
||||
AM_CXXFLAGS = -pthread
|
||||
|
||||
SUBDIRS = src
|
||||
SUBDIRS =
|
||||
|
||||
SUBDIRS += src
|
||||
|
||||
if ENABLE_C
|
||||
SUBDIRS += c
|
||||
endif
|
||||
|
||||
if ENABLE_TOOLS
|
||||
SUBDIRS += tools
|
||||
@@ -10,15 +16,17 @@ if USE_CYTHON
|
||||
SUBDIRS += python
|
||||
endif
|
||||
|
||||
if HAVE_DOXYGEN
|
||||
SUBDIRS += doc
|
||||
if ENABLE_TESTS
|
||||
SUBDIRS += tests
|
||||
endif
|
||||
|
||||
SUBDIRS += doc
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
DOC_FILES = \
|
||||
README.md \
|
||||
LICENSE
|
||||
COPYING
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(DOC_FILES)
|
||||
|
||||
@@ -1,32 +1,39 @@
|
||||
OpenDHT
|
||||
===
|
||||
A lightweight C++11 Distributed Hash Table implementation originally based on https://github.com/jech/dht by Juliusz Chroboczek.
|
||||
<img src="https://raw.githubusercontent.com/savoirfairelinux/opendht/master/resources/opendht_logo_512.png" width="100" align="right">
|
||||
<br>
|
||||
<h1 style="margin-top:10px">
|
||||
<a id="user-content-opendht-" class="anchor" href="/savoirfairelinux/opendht/blob/master/README.md#opendht-" aria-hidden="true"></a>OpenDHT
|
||||
</h1>
|
||||
|
||||
* Light and fast C++11 Kademlia DHT library.
|
||||
* Distributed shared key->value data-store
|
||||
* Clean and powerfull distributed map API with storage of arbitrary binary values of up to 56 KB.
|
||||
* Optional public key cryptography layer providing data signature and encryption (using GnuTLS).
|
||||
* IPv4 and IPv6 support.
|
||||
* Python binding.
|
||||
A lightweight C++17 Distributed Hash Table implementation.
|
||||
|
||||
Documentation
|
||||
-
|
||||
OpenDHT provides an easy to use distributed in-memory data store.
|
||||
Every node in the network can read and write values to the store.
|
||||
Values are distributed over the network, with redundancy.
|
||||
|
||||
* Lightweight and scalable, designed for large networks and small devices
|
||||
* High resilience to network disruption
|
||||
* Public key cryptography layer providing optional data signature and encryption (using GnuTLS)
|
||||
* IPv4 and IPv6 support
|
||||
* Clean and powerful **C++17** map API
|
||||
* Bindings for **C, Rust & Python 3**
|
||||
* REST API with optional HTTP client+server with push notification support
|
||||
|
||||
## Documentation
|
||||
See the wiki: <https://github.com/savoirfairelinux/opendht/wiki>
|
||||
|
||||
#### How-to build and install
|
||||
|
||||
Build instructions : <https://github.com/savoirfairelinux/opendht/wiki/Build-the-library>
|
||||
Build instructions: <https://github.com/savoirfairelinux/opendht/wiki/Build-the-library>
|
||||
|
||||
#### How-to build a simple client app
|
||||
```bash
|
||||
g++ main.cpp -std=c++11 -lopendht -lgnutls
|
||||
g++ main.cpp -std=c++17 -lopendht
|
||||
```
|
||||
|
||||
Examples
|
||||
-
|
||||
## Examples
|
||||
### C++ example
|
||||
The `tools` directory includes simple example programs :
|
||||
* `dhtnode`, a command line tool, mostly used for debuging, allowing to perform operations supported by the library (get, put etc.) with text values.
|
||||
* `dhtnode`, a command line tool, allowing to run a DHT node and perform operations supported by the library (get, put etc.) with text values.
|
||||
* `dhtchat`, a very simple IM client working over the dht.
|
||||
|
||||
Example program launching a DHT node, connecting to the network and performing some basic operations:
|
||||
@@ -44,7 +51,7 @@ int main()
|
||||
|
||||
// Join the network through any running node,
|
||||
// here using a known bootstrap node.
|
||||
node.bootstrap("bootstrap.ring.cx", "4222");
|
||||
node.bootstrap("bootstrap.jami.net", "4222");
|
||||
|
||||
// put some data on the dht
|
||||
std::vector<uint8_t> some_data(5, 10);
|
||||
@@ -75,7 +82,7 @@ node.run()
|
||||
|
||||
# Join the network through any running node,
|
||||
# here using a known bootstrap node.
|
||||
node.bootstrap("bootstrap.ring.cx", "4222")
|
||||
node.bootstrap("bootstrap.jami.net", "4222")
|
||||
|
||||
# blocking call (provide callback arguments to make the call non-blocking)
|
||||
node.put(dht.InfoHash.get("unique_key"), dht.Value(b'some binary data'))
|
||||
@@ -85,22 +92,31 @@ for r in results:
|
||||
print(r)
|
||||
```
|
||||
|
||||
Dependencies
|
||||
-
|
||||
## Dependencies
|
||||
- msgpack-c 1.2+, used for data serialization.
|
||||
- GnuTLS 3.1+, used for cryptographic operations.
|
||||
- GnuTLS 3.3+, used for cryptographic operations.
|
||||
- Nettle 2.4+, a GnuTLS dependency for crypto.
|
||||
- Build tested with GCC 4.8+ (GNU/Linux, Android, Windows with MinGW), Clang/LLVM (Linux, OS X).
|
||||
- Build tested with Microsoft Visual Studio 2015
|
||||
- (optional) restinio used for the REST API.
|
||||
- (optional) jsoncpp 1.7.4-3+, used for the REST API.
|
||||
- Build tested with GCC 7+ (GNU/Linux, Windows with MinGW), Clang/LLVM (GNU/Linux, Android, macOS, iOS).
|
||||
- Build tested with Microsoft Visual Studio 2017, 2019, 2022
|
||||
|
||||
Licence
|
||||
-
|
||||
Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
## Contact
|
||||
|
||||
Licenced under the GNU General Public License version 3 (see COPYING for the full licence).
|
||||
IRC: join us on Libera.chat at [`#opendht`](https://web.libera.chat/#opendht).
|
||||
|
||||
This project is independent from another project called OpenDHT (Sean Rhea. Ph.D. Thesis, 2005), now extinct.
|
||||
## License
|
||||
Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
|
||||
Donations
|
||||
-
|
||||
OpenDHT is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
See COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html for the full GPLv3 license.
|
||||
|
||||
## Acknowledgements
|
||||
This project was originally based on https://github.com/jech/dht by Juliusz Chroboczek.
|
||||
It is independent from another project called OpenDHT (Sean Rhea. Ph.D. Thesis, 2005), now extinct.
|
||||
|
||||
## Donations
|
||||
We gratefully accept Bitcoin donations to support OpenDHT development at: `bitcoin:3EykSd1An888efq4Bq3KaV3hJ3JQ4FPnwm`.
|
||||
@@ -0,0 +1,12 @@
|
||||
lib_LTLIBRARIES = libopendht-c.la
|
||||
noinst_HEADERS = opendht_c.h
|
||||
|
||||
AM_CPPFLAGS = -isystem @top_srcdir@/include @JsonCpp_CFLAGS@ @MsgPack_CFLAGS@
|
||||
|
||||
libopendht_c_la_LIBADD = $(LIBOBJS) ../src/.libs/libopendht.la
|
||||
|
||||
libopendht_c_la_SOURCES = \
|
||||
opendht.cpp
|
||||
|
||||
nobase_include_HEADERS = \
|
||||
opendht_c.h
|
||||
+521
@@ -0,0 +1,521 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "opendht_c.h"
|
||||
|
||||
#include <opendht.h>
|
||||
#include <opendht/log.h>
|
||||
|
||||
using ValueSp = std::shared_ptr<dht::Value>;
|
||||
using PrivkeySp = std::shared_ptr<dht::crypto::PrivateKey>;
|
||||
using PubkeySp = std::shared_ptr<const dht::crypto::PublicKey>;
|
||||
using CertSp = std::shared_ptr<dht::crypto::Certificate>;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
const char* dht_version()
|
||||
{
|
||||
return dht::version();
|
||||
}
|
||||
|
||||
// dht::InfoHash
|
||||
|
||||
inline dht_infohash dht_infohash_to_c(const dht::InfoHash& h) {
|
||||
dht_infohash ret;
|
||||
*reinterpret_cast<dht::InfoHash*>(&ret) = h;
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline dht_pkid dht_pkid_to_c(const dht::PkId& h) {
|
||||
dht_pkid ret;
|
||||
*reinterpret_cast<dht::PkId*>(&ret) = h;
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char* dht_infohash_print(const dht_infohash* h) {
|
||||
return reinterpret_cast<const dht::InfoHash*>(h)->to_c_str();
|
||||
}
|
||||
|
||||
void dht_infohash_zero(dht_infohash* h) {
|
||||
*reinterpret_cast<dht::InfoHash*>(h) = dht::InfoHash{};
|
||||
}
|
||||
|
||||
void dht_infohash_random(dht_infohash* h) {
|
||||
*reinterpret_cast<dht::InfoHash*>(h) = dht::InfoHash::getRandom();
|
||||
}
|
||||
|
||||
void dht_infohash_get(dht_infohash* h, const uint8_t* dat, size_t dat_size) {
|
||||
*reinterpret_cast<dht::InfoHash*>(h) = dht::InfoHash::get(dat, dat_size);
|
||||
}
|
||||
|
||||
void dht_infohash_get_from_string(dht_infohash* h, const char* dat) {
|
||||
*reinterpret_cast<dht::InfoHash*>(h) = dht::InfoHash::get((const uint8_t*)dat, (size_t)strlen(dat));
|
||||
}
|
||||
|
||||
bool dht_infohash_is_zero(const dht_infohash* h) {
|
||||
return !static_cast<bool>(*reinterpret_cast<const dht::InfoHash*>(h));
|
||||
}
|
||||
|
||||
void dht_infohash_from_hex(dht_infohash* h, const char* dat) {
|
||||
*h = dht_infohash_to_c(dht::InfoHash(std::string_view(dat, HASH_LEN*2)));
|
||||
}
|
||||
|
||||
void dht_infohash_from_hex_null(dht_infohash* h, const char* dat) {
|
||||
*h = dht_infohash_to_c(dht::InfoHash(std::string_view(dat)));
|
||||
}
|
||||
|
||||
const char* dht_pkid_print(const dht_pkid* h) {
|
||||
return reinterpret_cast<const dht::PkId*>(h)->to_c_str();
|
||||
}
|
||||
|
||||
// dht::Blob
|
||||
void dht_blob_delete(dht_blob* data) {
|
||||
delete reinterpret_cast<dht::Blob*>(data);
|
||||
}
|
||||
|
||||
dht_data_view dht_blob_get_data(const dht_blob* data) {
|
||||
dht_data_view view;
|
||||
view.data = reinterpret_cast<const dht::Blob*>(data)->data();
|
||||
view.size = reinterpret_cast<const dht::Blob*>(data)->size();
|
||||
return view;
|
||||
}
|
||||
|
||||
// dht::Value
|
||||
dht_data_view dht_value_get_data(const dht_value* data) {
|
||||
const ValueSp& vsp(*reinterpret_cast<const ValueSp*>(data));
|
||||
dht_data_view view;
|
||||
view.data = vsp->data.data();
|
||||
view.size = vsp->data.size();
|
||||
return view;
|
||||
}
|
||||
|
||||
dht_value_id dht_value_get_id(const dht_value* data) {
|
||||
const ValueSp& vsp(*reinterpret_cast<const ValueSp*>(data));
|
||||
return vsp->id;
|
||||
}
|
||||
|
||||
dht_publickey* dht_value_get_owner(const dht_value* data) {
|
||||
const ValueSp& vsp(*reinterpret_cast<const ValueSp*>(data));
|
||||
return vsp->owner ? reinterpret_cast<dht_publickey*>(new PubkeySp(vsp->owner)) : nullptr;
|
||||
}
|
||||
|
||||
dht_infohash dht_value_get_recipient(const dht_value* data) {
|
||||
const ValueSp& vsp(*reinterpret_cast<const ValueSp*>(data));
|
||||
return dht_infohash_to_c(vsp->recipient);
|
||||
}
|
||||
|
||||
const char* dht_value_get_user_type(const dht_value* data) {
|
||||
const ValueSp& vsp(*reinterpret_cast<const ValueSp*>(data));
|
||||
return vsp->user_type.c_str();
|
||||
}
|
||||
|
||||
void dht_value_set_user_type(dht_value* data, const char* user_type) {
|
||||
(*reinterpret_cast<ValueSp*>(data))->user_type = user_type;
|
||||
}
|
||||
|
||||
dht_value* dht_value_new(const uint8_t* data, size_t size) {
|
||||
return reinterpret_cast<dht_value*>(new ValueSp(std::make_shared<dht::Value>(data, size)));
|
||||
}
|
||||
|
||||
dht_value* dht_value_new_from_string(const char* str) {
|
||||
ValueSp value = std::make_shared<dht::Value>((const uint8_t*)str, strlen(str));
|
||||
value->user_type = "text/plain";
|
||||
return reinterpret_cast<dht_value*>(new ValueSp(std::move(value)));
|
||||
}
|
||||
|
||||
dht_value* dht_value_ref(const dht_value* v) {
|
||||
return reinterpret_cast<dht_value*>(new ValueSp(*reinterpret_cast<const ValueSp*>(v)));
|
||||
}
|
||||
|
||||
void dht_value_unref(dht_value* v) {
|
||||
delete reinterpret_cast<ValueSp*>(v);
|
||||
}
|
||||
|
||||
// dht::crypto::PublicKey
|
||||
dht_publickey* dht_publickey_import(const uint8_t* dat, size_t dat_size) {
|
||||
try {
|
||||
return reinterpret_cast<dht_publickey*>(new PubkeySp(std::make_shared<const dht::crypto::PublicKey>(dat, dat_size)));
|
||||
} catch (const dht::crypto::CryptoException& e) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void dht_publickey_delete(dht_publickey* pk) {
|
||||
delete reinterpret_cast<PubkeySp*>(pk);
|
||||
}
|
||||
|
||||
int dht_publickey_export(const dht_publickey* pk, char* out, size_t* outlen) {
|
||||
const auto& pkey = *reinterpret_cast<const PubkeySp*>(pk);
|
||||
return pkey->pack((uint8_t*)out, outlen);
|
||||
}
|
||||
|
||||
dht_infohash dht_publickey_get_id(const dht_publickey* pk) {
|
||||
const auto& pkey = *reinterpret_cast<const PubkeySp*>(pk);
|
||||
return dht_infohash_to_c(pkey->getId());
|
||||
}
|
||||
|
||||
dht_pkid dht_publickey_get_long_id(const dht_publickey* pk) {
|
||||
const auto& pkey = *reinterpret_cast<const PubkeySp*>(pk);
|
||||
return dht_pkid_to_c(pkey->getLongId());
|
||||
}
|
||||
|
||||
bool dht_publickey_check_signature(const dht_publickey* pk, const char* data, size_t data_size, const char* signature, size_t signature_size) {
|
||||
const auto& pkey = *reinterpret_cast<const PubkeySp*>(pk);
|
||||
return pkey->checkSignature((const uint8_t*)data, data_size, (const uint8_t*)signature, signature_size);
|
||||
}
|
||||
|
||||
dht_blob* dht_publickey_encrypt(const dht_publickey* pk, const char* data, size_t data_size) {
|
||||
const auto& pkey = *reinterpret_cast<const PubkeySp*>(pk);
|
||||
try {
|
||||
auto rdata = std::make_unique<dht::Blob>();
|
||||
*rdata = pkey->encrypt((const uint8_t*)data, data_size);
|
||||
return (dht_blob*)rdata.release();
|
||||
} catch (...) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// dht::crypto::PrivateKey
|
||||
dht_privatekey* dht_privatekey_generate(unsigned key_length_bits) {
|
||||
if (key_length_bits == 0)
|
||||
key_length_bits = 4096;
|
||||
return reinterpret_cast<dht_privatekey*>(new PrivkeySp(std::make_shared<dht::crypto::PrivateKey>(dht::crypto::PrivateKey::generate(key_length_bits))));
|
||||
}
|
||||
|
||||
dht_privatekey* dht_privatekey_import(const uint8_t* dat, size_t dat_size, const char* password) {
|
||||
try {
|
||||
return reinterpret_cast<dht_privatekey*>(new PrivkeySp(std::make_shared<dht::crypto::PrivateKey>(dat, dat_size, password)));
|
||||
} catch (const dht::crypto::CryptoException& e) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int dht_privatekey_export(const dht_privatekey* k, char* out, size_t* out_size, const char* password) {
|
||||
if (!out or !out_size or !*out_size)
|
||||
return -1;
|
||||
const auto& key = *reinterpret_cast<const PrivkeySp*>(k);
|
||||
return key->serialize((uint8_t*)out, out_size, password);
|
||||
}
|
||||
|
||||
dht_publickey* dht_privatekey_get_publickey(const dht_privatekey* k) {
|
||||
const auto& key = *reinterpret_cast<const PrivkeySp*>(k);
|
||||
return reinterpret_cast<dht_publickey*>(new PubkeySp(key->getSharedPublicKey()));
|
||||
}
|
||||
|
||||
dht_blob* dht_privatekey_decrypt(const dht_privatekey* k, const char* data, size_t data_size) {
|
||||
const auto& key = *reinterpret_cast<const PrivkeySp*>(k);
|
||||
try {
|
||||
auto rdata = std::make_unique<dht::Blob>();
|
||||
*rdata = key->decrypt((const uint8_t*)data, data_size);
|
||||
return (dht_blob*)rdata.release();
|
||||
} catch (...) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void dht_privatekey_delete(dht_privatekey* pk) {
|
||||
delete reinterpret_cast<PrivkeySp*>(pk);
|
||||
}
|
||||
|
||||
// dht::crypto::Certificate
|
||||
dht_certificate* dht_certificate_import(const uint8_t* dat, size_t dat_size) {
|
||||
try {
|
||||
return reinterpret_cast<dht_certificate*>(new CertSp(std::make_shared<dht::crypto::Certificate>(dat, dat_size)));
|
||||
} catch (const dht::crypto::CryptoException& e) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void dht_certificate_delete(dht_certificate* c) {
|
||||
delete reinterpret_cast<CertSp*>(c);
|
||||
}
|
||||
|
||||
dht_infohash dht_certificate_get_id(const dht_certificate* c) {
|
||||
const auto& cert = *reinterpret_cast<const CertSp*>(c);
|
||||
return dht_infohash_to_c(cert->getId());
|
||||
}
|
||||
|
||||
dht_pkid dht_certificate_get_long_id(const dht_certificate* c) {
|
||||
const auto& cert = *reinterpret_cast<const CertSp*>(c);
|
||||
return dht_pkid_to_c(cert->getLongId());
|
||||
}
|
||||
|
||||
dht_publickey* dht_certificate_get_publickey(const dht_certificate* c) {
|
||||
const auto& cert = *reinterpret_cast<const CertSp*>(c);
|
||||
return reinterpret_cast<dht_publickey*>(new PubkeySp(std::make_shared<dht::crypto::PublicKey>(cert->getPublicKey())));
|
||||
}
|
||||
|
||||
// dht::crypto::Identity
|
||||
inline dht::crypto::Identity dht_identity_from_c(const dht_identity* cid) {
|
||||
dht::crypto::Identity id {};
|
||||
if (cid and cid->privatekey)
|
||||
id.first = *reinterpret_cast<const PrivkeySp*>(cid->privatekey);
|
||||
if (cid and cid->certificate)
|
||||
id.second = *reinterpret_cast<const CertSp*>(cid->certificate);
|
||||
return id;
|
||||
}
|
||||
|
||||
inline dht_identity dht_identity_to_c(const dht::crypto::Identity& id) {
|
||||
dht_identity cid {};
|
||||
cid.privatekey = id.first ? reinterpret_cast<dht_privatekey*>(new PrivkeySp(id.first)) : NULL;
|
||||
cid.certificate = id.second ? reinterpret_cast<dht_certificate*>(new CertSp(id.second)) : NULL;
|
||||
return cid;
|
||||
}
|
||||
|
||||
OPENDHT_C_PUBLIC dht_identity dht_identity_generate(const char* common_name, const dht_identity* ca) {
|
||||
return dht_identity_to_c(dht::crypto::generateIdentity(common_name, dht_identity_from_c(ca)));
|
||||
}
|
||||
|
||||
OPENDHT_C_PUBLIC void dht_identity_delete(dht_identity* id) {
|
||||
if (id->certificate) {
|
||||
dht_certificate_delete(id->certificate);
|
||||
id->certificate = NULL;
|
||||
}
|
||||
if (id->privatekey) {
|
||||
dht_privatekey_delete(id->privatekey);
|
||||
id->privatekey = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// config
|
||||
void dht_runner_config_default(dht_runner_config* config) {
|
||||
bzero(config, sizeof(dht_runner_config));
|
||||
config->threaded = true;
|
||||
}
|
||||
|
||||
// dht::DhtRunner
|
||||
dht_runner* dht_runner_new(void) {
|
||||
return reinterpret_cast<dht_runner*>(new dht::DhtRunner);
|
||||
}
|
||||
|
||||
void dht_runner_delete(dht_runner* runner) {
|
||||
delete reinterpret_cast<dht::DhtRunner*>(runner);
|
||||
}
|
||||
|
||||
int dht_runner_run(dht_runner* r, in_port_t port) {
|
||||
auto runner = reinterpret_cast<dht::DhtRunner*>(r);
|
||||
try {
|
||||
runner->run(port, {}, true);
|
||||
} catch(...) {
|
||||
return ENOTCONN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dht_runner_run_config(dht_runner* r, in_port_t port, const dht_runner_config* conf) {
|
||||
auto runner = reinterpret_cast<dht::DhtRunner*>(r);
|
||||
try {
|
||||
dht::DhtRunner::Config config;
|
||||
config.dht_config.node_config.is_bootstrap = conf->dht_config.node_config.is_bootstrap;
|
||||
config.dht_config.node_config.maintain_storage = conf->dht_config.node_config.maintain_storage;
|
||||
config.dht_config.node_config.node_id = *reinterpret_cast<const dht::InfoHash*>(&conf->dht_config.node_config.node_id);
|
||||
config.dht_config.node_config.network = conf->dht_config.node_config.network;
|
||||
config.dht_config.node_config.persist_path = conf->dht_config.node_config.persist_path
|
||||
? std::string(conf->dht_config.node_config.persist_path) : std::string{};
|
||||
|
||||
if (conf->dht_config.id.privatekey)
|
||||
config.dht_config.id.first = *reinterpret_cast<const PrivkeySp*>(conf->dht_config.id.privatekey);
|
||||
|
||||
if (conf->dht_config.id.certificate)
|
||||
config.dht_config.id.second = *reinterpret_cast<const CertSp*>(conf->dht_config.id.certificate);
|
||||
|
||||
config.threaded = conf->threaded;
|
||||
config.proxy_server = conf->proxy_server ? std::string(conf->proxy_server) : std::string{};
|
||||
config.push_node_id = conf->push_node_id ? std::string(conf->push_node_id) : std::string{};
|
||||
config.push_token = conf->push_token ? std::string(conf->push_token) : std::string{};
|
||||
config.peer_discovery = conf->peer_discovery;
|
||||
config.peer_publish = conf->peer_publish;
|
||||
|
||||
dht::DhtRunner::Context context;
|
||||
if (conf->log) {
|
||||
context.logger = dht::log::getStdLogger();
|
||||
}
|
||||
runner->run(port, config, std::move(context));
|
||||
} catch(...) {
|
||||
return ENOTCONN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dht_runner_ping(dht_runner* r, struct sockaddr* addr, socklen_t addr_len, dht_done_cb done_cb, void* cb_user_data) {
|
||||
auto runner = reinterpret_cast<dht::DhtRunner*>(r);
|
||||
if (done_cb) {
|
||||
runner->bootstrap(dht::SockAddr(addr, addr_len), [done_cb, cb_user_data](bool ok){
|
||||
done_cb(ok, cb_user_data);
|
||||
});
|
||||
} else {
|
||||
runner->bootstrap(dht::SockAddr(addr, addr_len));
|
||||
}
|
||||
}
|
||||
|
||||
void dht_runner_bootstrap(dht_runner* r, const char* host, const char* service) {
|
||||
auto runner = reinterpret_cast<dht::DhtRunner*>(r);
|
||||
if (service)
|
||||
runner->bootstrap(host, service);
|
||||
else
|
||||
runner->bootstrap(host);
|
||||
}
|
||||
|
||||
void dht_runner_get(dht_runner* r, const dht_infohash* h, dht_get_cb cb, dht_done_cb done_cb, void* cb_user_data) {
|
||||
auto runner = reinterpret_cast<dht::DhtRunner*>(r);
|
||||
auto hash = reinterpret_cast<const dht::InfoHash*>(h);
|
||||
runner->get(*hash, [cb,cb_user_data](std::shared_ptr<dht::Value> value){
|
||||
return cb(reinterpret_cast<const dht_value*>(&value), cb_user_data);
|
||||
}, [done_cb, cb_user_data](bool ok){
|
||||
if (done_cb)
|
||||
done_cb(ok, cb_user_data);
|
||||
});
|
||||
}
|
||||
|
||||
struct ScopeGuardCb {
|
||||
ScopeGuardCb(dht_shutdown_cb cb, void* data)
|
||||
: onDestroy(cb), userData(data) {}
|
||||
|
||||
~ScopeGuardCb() {
|
||||
if (onDestroy)
|
||||
onDestroy((void*)userData);
|
||||
}
|
||||
private:
|
||||
const dht_shutdown_cb onDestroy;
|
||||
void const* userData;
|
||||
};
|
||||
|
||||
dht_op_token* dht_runner_listen(dht_runner* r, const dht_infohash* h, dht_value_cb cb, dht_shutdown_cb done_cb, void* cb_user_data) {
|
||||
auto runner = reinterpret_cast<dht::DhtRunner*>(r);
|
||||
auto hash = reinterpret_cast<const dht::InfoHash*>(h);
|
||||
auto fret = new std::future<size_t>;
|
||||
*fret = runner->listen(*hash, [
|
||||
cb,
|
||||
cb_user_data,
|
||||
guard = done_cb ? std::make_shared<ScopeGuardCb>(done_cb, cb_user_data) : std::shared_ptr<ScopeGuardCb>{}
|
||||
](const std::vector<std::shared_ptr<dht::Value>>& values, bool expired) {
|
||||
for (const auto& value : values) {
|
||||
if (not cb(reinterpret_cast<const dht_value*>(&value), expired, cb_user_data))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return (dht_op_token*)fret;
|
||||
}
|
||||
|
||||
void dht_runner_cancel_listen(dht_runner* r, const dht_infohash* h, dht_op_token* t) {
|
||||
auto runner = reinterpret_cast<dht::DhtRunner*>(r);
|
||||
auto hash = reinterpret_cast<const dht::InfoHash*>(h);
|
||||
auto token = reinterpret_cast<std::future<size_t>*>(t);
|
||||
runner->cancelListen(*hash, std::move(*token));
|
||||
}
|
||||
|
||||
void dht_op_token_delete(dht_op_token* token) {
|
||||
delete reinterpret_cast<std::future<size_t>*>(token);
|
||||
}
|
||||
|
||||
void dht_runner_put(dht_runner* r, const dht_infohash* h, const dht_value* v, dht_done_cb done_cb, void* cb_user_data, bool permanent) {
|
||||
auto runner = reinterpret_cast<dht::DhtRunner*>(r);
|
||||
auto hash = reinterpret_cast<const dht::InfoHash*>(h);
|
||||
auto value = reinterpret_cast<const ValueSp*>(v);
|
||||
runner->put(*hash, *value, [done_cb, cb_user_data](bool ok){
|
||||
if (done_cb)
|
||||
done_cb(ok, cb_user_data);
|
||||
}, dht::time_point::max(), permanent);
|
||||
}
|
||||
|
||||
void dht_runner_put_signed(dht_runner* r, const dht_infohash* h, const dht_value* v, dht_done_cb done_cb, void* cb_user_data, bool permanent) {
|
||||
auto runner = reinterpret_cast<dht::DhtRunner*>(r);
|
||||
auto hash = reinterpret_cast<const dht::InfoHash*>(h);
|
||||
auto value = reinterpret_cast<const ValueSp*>(v);
|
||||
runner->putSigned(*hash, *value, [done_cb, cb_user_data](bool ok){
|
||||
if (done_cb)
|
||||
done_cb(ok, cb_user_data);
|
||||
}, permanent);
|
||||
}
|
||||
|
||||
void dht_runner_put_encrypted(dht_runner* r, const dht_infohash* h, const dht_infohash* to, const dht_value* v, dht_done_cb done_cb, void* cb_user_data, bool permanent) {
|
||||
auto runner = reinterpret_cast<dht::DhtRunner*>(r);
|
||||
auto hash = reinterpret_cast<const dht::InfoHash*>(h);
|
||||
auto toHash = reinterpret_cast<const dht::InfoHash*>(to);
|
||||
auto value = reinterpret_cast<const ValueSp*>(v);
|
||||
runner->putEncrypted(*hash, *toHash, *value, [done_cb, cb_user_data](bool ok){
|
||||
if (done_cb)
|
||||
done_cb(ok, cb_user_data);
|
||||
}, permanent);
|
||||
}
|
||||
|
||||
void dht_runner_cancel_put(dht_runner* r, const dht_infohash* h, dht_value_id value_id) {
|
||||
auto runner = reinterpret_cast<dht::DhtRunner*>(r);
|
||||
auto hash = reinterpret_cast<const dht::InfoHash*>(h);
|
||||
runner->cancelPut(*hash, value_id);
|
||||
}
|
||||
|
||||
void dht_runner_shutdown(dht_runner* r, dht_shutdown_cb done_cb, void* cb_user_data) {
|
||||
auto runner = reinterpret_cast<dht::DhtRunner*>(r);
|
||||
runner->shutdown([done_cb, cb_user_data](){
|
||||
if (done_cb)
|
||||
done_cb(cb_user_data);
|
||||
});
|
||||
}
|
||||
|
||||
bool dht_runner_is_running(const dht_runner* r) {
|
||||
if (not r) return false;
|
||||
auto runner = reinterpret_cast<const dht::DhtRunner*>(r);
|
||||
return runner->isRunning();
|
||||
}
|
||||
|
||||
in_port_t dht_runner_get_bound_port(const dht_runner* r, sa_family_t af) {
|
||||
auto runner = reinterpret_cast<const dht::DhtRunner*>(r);
|
||||
return runner->getBoundPort(af);
|
||||
}
|
||||
|
||||
dht_infohash dht_runner_get_node_id(const dht_runner* r) {
|
||||
auto runner = reinterpret_cast<const dht::DhtRunner*>(r);
|
||||
dht_infohash ret;
|
||||
*reinterpret_cast<dht::InfoHash*>(&ret) = runner->getNodeId();
|
||||
return ret;
|
||||
}
|
||||
|
||||
dht_infohash dht_runner_get_id(const dht_runner* r) {
|
||||
auto runner = reinterpret_cast<const dht::DhtRunner*>(r);
|
||||
dht_infohash ret;
|
||||
*reinterpret_cast<dht::InfoHash*>(&ret) = runner->getId();
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct sockaddr** dht_runner_get_public_address(const dht_runner* r) {
|
||||
auto runner = reinterpret_cast<const dht::DhtRunner*>(r);
|
||||
auto addrs = const_cast<dht::DhtRunner*>(runner)->getPublicAddress();
|
||||
if (addrs.empty())
|
||||
return nullptr;
|
||||
auto ret = (struct sockaddr**)malloc(sizeof(struct sockaddr*) * (addrs.size() + 1));
|
||||
for (size_t i=0; i<addrs.size(); i++) {
|
||||
if (const auto& addr = addrs[i]) {
|
||||
ret[i] = (struct sockaddr*)malloc(addr.getLength());
|
||||
memcpy((struct sockaddr*)ret[i], addr.get(), addr.getLength());
|
||||
} else {
|
||||
ret[i] = nullptr;
|
||||
}
|
||||
}
|
||||
ret[addrs.size()] = nullptr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <opendht/def.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// Non-owning data view
|
||||
struct OPENDHT_C_PUBLIC dht_data_view {
|
||||
const uint8_t* data;
|
||||
size_t size;
|
||||
};
|
||||
typedef struct dht_data_view dht_data_view;
|
||||
|
||||
OPENDHT_C_PUBLIC const char* dht_version(void);
|
||||
|
||||
// dht::Blob
|
||||
struct OPENDHT_C_PUBLIC dht_blob;
|
||||
typedef struct dht_blob dht_blob;
|
||||
OPENDHT_C_PUBLIC dht_data_view dht_blob_get_data(const dht_blob* data);
|
||||
OPENDHT_C_PUBLIC void dht_blob_delete(dht_blob* data);
|
||||
|
||||
// dht::InfoHash
|
||||
struct OPENDHT_C_PUBLIC dht_infohash { uint8_t d[HASH_LEN]; };
|
||||
typedef struct dht_infohash dht_infohash;
|
||||
OPENDHT_C_PUBLIC void dht_infohash_zero(dht_infohash* h);
|
||||
OPENDHT_C_PUBLIC void dht_infohash_random(dht_infohash* h);
|
||||
OPENDHT_C_PUBLIC void dht_infohash_from_hex(dht_infohash* h, const char* dat);
|
||||
OPENDHT_C_PUBLIC void dht_infohash_from_hex_null(dht_infohash* h, const char* dat);
|
||||
OPENDHT_C_PUBLIC void dht_infohash_get(dht_infohash* h, const uint8_t* dat, size_t dat_size);
|
||||
OPENDHT_C_PUBLIC void dht_infohash_get_from_string(dht_infohash* h, const char* str);
|
||||
OPENDHT_C_PUBLIC const char* dht_infohash_print(const dht_infohash* h);
|
||||
OPENDHT_C_PUBLIC bool dht_infohash_is_zero(const dht_infohash* h);
|
||||
|
||||
// dht::PkId
|
||||
struct OPENDHT_C_PUBLIC dht_pkid { uint8_t d[32]; };
|
||||
typedef struct dht_pkid dht_pkid;
|
||||
OPENDHT_C_PUBLIC const char* dht_pkid_print(const dht_pkid* h);
|
||||
|
||||
// dht::crypto::PublicKey
|
||||
struct OPENDHT_C_PUBLIC dht_publickey;
|
||||
typedef struct dht_publickey dht_publickey;
|
||||
OPENDHT_C_PUBLIC dht_publickey* dht_publickey_import(const uint8_t* dat, size_t dat_size);
|
||||
OPENDHT_C_PUBLIC void dht_publickey_delete(dht_publickey* pk);
|
||||
OPENDHT_C_PUBLIC int dht_publickey_export(const dht_publickey* pk, char* out, size_t* out_size);
|
||||
OPENDHT_C_PUBLIC dht_infohash dht_publickey_get_id(const dht_publickey* pk);
|
||||
OPENDHT_C_PUBLIC dht_pkid dht_publickey_get_long_id(const dht_publickey* pk);
|
||||
OPENDHT_C_PUBLIC bool dht_publickey_check_signature(const dht_publickey* pk, const char* data, size_t data_size, const char* signature, size_t signature_size);
|
||||
OPENDHT_C_PUBLIC dht_blob* dht_publickey_encrypt(const dht_publickey* pk, const char* data, size_t data_size);
|
||||
|
||||
// dht::crypto::PrivateKey
|
||||
struct OPENDHT_C_PUBLIC dht_privatekey;
|
||||
typedef struct dht_privatekey dht_privatekey;
|
||||
OPENDHT_C_PUBLIC dht_privatekey* dht_privatekey_generate(unsigned key_length_bits);
|
||||
OPENDHT_C_PUBLIC dht_privatekey* dht_privatekey_import(const uint8_t* dat, size_t dat_size, const char* password);
|
||||
OPENDHT_C_PUBLIC int dht_privatekey_export(const dht_privatekey*, char* out, size_t* out_size, const char* password);
|
||||
OPENDHT_C_PUBLIC dht_publickey* dht_privatekey_get_publickey(const dht_privatekey*);
|
||||
OPENDHT_C_PUBLIC dht_blob* dht_privatekey_decrypt(const dht_privatekey*, const char* data, size_t data_size);
|
||||
OPENDHT_C_PUBLIC void dht_privatekey_delete(dht_privatekey*);
|
||||
|
||||
// dht::crypto::Certificate
|
||||
struct OPENDHT_C_PUBLIC dht_certificate;
|
||||
typedef struct dht_certificate dht_certificate;
|
||||
OPENDHT_C_PUBLIC dht_certificate* dht_certificate_import(const uint8_t* dat, size_t dat_size);
|
||||
OPENDHT_C_PUBLIC dht_infohash dht_certificate_get_id(const dht_certificate*);
|
||||
OPENDHT_C_PUBLIC dht_pkid dht_certificate_get_long_id(const dht_certificate*);
|
||||
OPENDHT_C_PUBLIC dht_publickey* dht_certificate_get_publickey(const dht_certificate*);
|
||||
OPENDHT_C_PUBLIC void dht_certificate_delete(dht_certificate*);
|
||||
|
||||
struct OPENDHT_PUBLIC dht_identity {
|
||||
dht_privatekey* privatekey;
|
||||
dht_certificate* certificate;
|
||||
};
|
||||
typedef struct dht_identity dht_identity;
|
||||
OPENDHT_C_PUBLIC dht_identity dht_identity_generate(const char* common_name, const dht_identity* ca);
|
||||
OPENDHT_C_PUBLIC void dht_identity_delete(dht_identity*);
|
||||
|
||||
// dht::Value
|
||||
struct OPENDHT_C_PUBLIC dht_value;
|
||||
typedef struct dht_value dht_value;
|
||||
typedef uint64_t dht_value_id;
|
||||
OPENDHT_C_PUBLIC dht_value* dht_value_new(const uint8_t* data, size_t size);
|
||||
OPENDHT_C_PUBLIC dht_value* dht_value_new_from_string(const char* str);
|
||||
OPENDHT_C_PUBLIC dht_value* dht_value_ref(const dht_value*);
|
||||
OPENDHT_C_PUBLIC void dht_value_unref(dht_value*);
|
||||
OPENDHT_C_PUBLIC dht_data_view dht_value_get_data(const dht_value* data);
|
||||
OPENDHT_C_PUBLIC dht_value_id dht_value_get_id(const dht_value* data);
|
||||
OPENDHT_C_PUBLIC dht_publickey* dht_value_get_owner(const dht_value* data);
|
||||
OPENDHT_C_PUBLIC dht_infohash dht_value_get_recipient(const dht_value* data);
|
||||
OPENDHT_C_PUBLIC const char* dht_value_get_user_type(const dht_value* data);
|
||||
OPENDHT_C_PUBLIC void dht_value_set_user_type(dht_value* data, const char* user_type);
|
||||
|
||||
// callbacks
|
||||
typedef bool (*dht_get_cb)(const dht_value* value, void* user_data);
|
||||
typedef bool (*dht_value_cb)(const dht_value* value, bool expired, void* user_data);
|
||||
typedef void (*dht_done_cb)(bool ok, void* user_data);
|
||||
typedef void (*dht_shutdown_cb)(void* user_data);
|
||||
|
||||
struct OPENDHT_C_PUBLIC dht_op_token;
|
||||
typedef struct dht_op_token dht_op_token;
|
||||
OPENDHT_C_PUBLIC void dht_op_token_delete(dht_op_token* token);
|
||||
|
||||
// config
|
||||
struct OPENDHT_PUBLIC dht_node_config {
|
||||
dht_infohash node_id;
|
||||
uint32_t network;
|
||||
bool is_bootstrap;
|
||||
bool maintain_storage;
|
||||
const char* persist_path;
|
||||
};
|
||||
typedef struct dht_node_config dht_node_config;
|
||||
|
||||
struct OPENDHT_PUBLIC dht_secure_config {
|
||||
dht_node_config node_config;
|
||||
dht_identity id;
|
||||
};
|
||||
typedef struct dht_secure_config dht_secure_config;
|
||||
|
||||
struct OPENDHT_PUBLIC dht_runner_config {
|
||||
dht_secure_config dht_config;
|
||||
bool threaded;
|
||||
const char* proxy_server;
|
||||
const char* push_node_id;
|
||||
const char* push_token;
|
||||
bool peer_discovery;
|
||||
bool peer_publish;
|
||||
dht_certificate* server_ca;
|
||||
dht_identity client_identity;
|
||||
bool log;
|
||||
};
|
||||
typedef struct dht_runner_config dht_runner_config;
|
||||
OPENDHT_C_PUBLIC void dht_runner_config_default(dht_runner_config* config);
|
||||
|
||||
// dht::DhtRunner
|
||||
struct OPENDHT_C_PUBLIC dht_runner;
|
||||
typedef struct dht_runner dht_runner;
|
||||
OPENDHT_C_PUBLIC dht_runner* dht_runner_new(void);
|
||||
OPENDHT_C_PUBLIC void dht_runner_delete(dht_runner* runner);
|
||||
/* Returns 0 on success, standard error code on failure */
|
||||
OPENDHT_C_PUBLIC int dht_runner_run(dht_runner* runner, in_port_t port);
|
||||
/* Returns 0 on success, standard error code on failure */
|
||||
OPENDHT_C_PUBLIC int dht_runner_run_config(dht_runner* runner, in_port_t port, const dht_runner_config* config);
|
||||
OPENDHT_C_PUBLIC void dht_runner_ping(dht_runner* runner, struct sockaddr* addr, socklen_t addr_len, dht_done_cb done_cb, void* cb_user_data);
|
||||
OPENDHT_C_PUBLIC void dht_runner_bootstrap(dht_runner* runner, const char* host, const char* service);
|
||||
OPENDHT_C_PUBLIC void dht_runner_get(dht_runner* runner, const dht_infohash* hash, dht_get_cb cb, dht_done_cb done_cb, void* cb_user_data);
|
||||
OPENDHT_C_PUBLIC dht_op_token* dht_runner_listen(dht_runner* runner, const dht_infohash* hash, dht_value_cb cb, dht_shutdown_cb done_cb, void* cb_user_data);
|
||||
OPENDHT_C_PUBLIC void dht_runner_cancel_listen(dht_runner* runner, const dht_infohash* hash, dht_op_token* token);
|
||||
OPENDHT_C_PUBLIC void dht_runner_put(dht_runner* runner, const dht_infohash* hash, const dht_value* value, dht_done_cb done_cb, void* cb_user_data, bool permanent);
|
||||
OPENDHT_C_PUBLIC void dht_runner_put_signed(dht_runner* runner, const dht_infohash* hash, const dht_value* value, dht_done_cb done_cb, void* cb_user_data, bool permanent);
|
||||
OPENDHT_C_PUBLIC void dht_runner_put_encrypted(dht_runner* runner, const dht_infohash* hash, const dht_infohash* to, const dht_value* value, dht_done_cb done_cb, void* cb_user_data, bool permanent);
|
||||
OPENDHT_C_PUBLIC void dht_runner_cancel_put(dht_runner* runner, const dht_infohash* hash, dht_value_id value_id);
|
||||
OPENDHT_C_PUBLIC void dht_runner_shutdown(dht_runner* runner, dht_shutdown_cb done_cb, void* cb_user_data);
|
||||
OPENDHT_C_PUBLIC dht_infohash dht_runner_get_node_id(const dht_runner* runner);
|
||||
OPENDHT_C_PUBLIC dht_infohash dht_runner_get_id(const dht_runner* runner);
|
||||
OPENDHT_C_PUBLIC bool dht_runner_is_running(const dht_runner* runner);
|
||||
OPENDHT_C_PUBLIC in_port_t dht_runner_get_bound_port(const dht_runner* runner, sa_family_t af);
|
||||
/** Returns null-terminated array that must be freed after use as well as each element */
|
||||
OPENDHT_C_PUBLIC struct sockaddr** dht_runner_get_public_address(const dht_runner* runner);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,103 @@
|
||||
# atomic builtins are required for threading support.
|
||||
|
||||
INCLUDE(CheckCXXSourceCompiles)
|
||||
INCLUDE(CheckLibraryExists)
|
||||
INCLUDE("${CMAKE_CURRENT_LIST_DIR}/DetermineGCCCompatible.cmake")
|
||||
|
||||
# Sometimes linking against libatomic is required for atomic ops, if
|
||||
# the platform doesn't support lock-free atomics.
|
||||
|
||||
function(check_working_cxx_atomics varname)
|
||||
set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++11")
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
#include <atomic>
|
||||
std::atomic<int> x;
|
||||
std::atomic<short> y;
|
||||
std::atomic<char> z;
|
||||
int main() {
|
||||
++z;
|
||||
++y;
|
||||
return ++x;
|
||||
}
|
||||
" ${varname})
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
|
||||
endfunction(check_working_cxx_atomics)
|
||||
|
||||
function(check_working_cxx_atomics64 varname)
|
||||
set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
set(CMAKE_REQUIRED_FLAGS "-std=c++11 ${CMAKE_REQUIRED_FLAGS}")
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
std::atomic<uint64_t> x (0);
|
||||
int main() {
|
||||
uint64_t i = x.load(std::memory_order_relaxed);
|
||||
(void)i;
|
||||
return 0;
|
||||
}
|
||||
" ${varname})
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
|
||||
endfunction(check_working_cxx_atomics64)
|
||||
|
||||
|
||||
# Check for (non-64-bit) atomic operations.
|
||||
if(MSVC)
|
||||
set(HAVE_CXX_ATOMICS_WITHOUT_LIB True)
|
||||
elseif(LLVM_COMPILER_IS_GCC_COMPATIBLE OR CMAKE_CXX_COMPILER_ID MATCHES "XL")
|
||||
# First check if atomics work without the library.
|
||||
check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITHOUT_LIB)
|
||||
# If not, check if the library exists, and atomics work with it.
|
||||
if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB)
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic")
|
||||
check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITH_LIB)
|
||||
if (NOT HAVE_CXX_ATOMICS_WITH_LIB)
|
||||
message(FATAL_ERROR "Host compiler must support std::atomic!")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Check for 64 bit atomic operations.
|
||||
if(MSVC)
|
||||
set(HAVE_CXX_ATOMICS64_WITHOUT_LIB True)
|
||||
elseif(LLVM_COMPILER_IS_GCC_COMPATIBLE OR CMAKE_CXX_COMPILER_ID MATCHES "XL")
|
||||
# First check if atomics work without the library.
|
||||
check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITHOUT_LIB)
|
||||
# If not, check if the library exists, and atomics work with it.
|
||||
if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB)
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic")
|
||||
check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITH_LIB)
|
||||
if (NOT HAVE_CXX_ATOMICS64_WITH_LIB)
|
||||
message(FATAL_ERROR "Host compiler must support 64-bit std::atomic!")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
## TODO: This define is only used for the legacy atomic operations in
|
||||
## llvm's Atomic.h, which should be replaced. Other code simply
|
||||
## assumes C++11 <atomic> works.
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
#ifdef _MSC_VER
|
||||
#include <windows.h>
|
||||
#endif
|
||||
int main() {
|
||||
#ifdef _MSC_VER
|
||||
volatile LONG val = 1;
|
||||
MemoryBarrier();
|
||||
InterlockedCompareExchange(&val, 0, 1);
|
||||
InterlockedIncrement(&val);
|
||||
InterlockedDecrement(&val);
|
||||
#else
|
||||
volatile unsigned long val = 1;
|
||||
__sync_synchronize();
|
||||
__sync_val_compare_and_swap(&val, 1, 0);
|
||||
__sync_add_and_fetch(&val, 1);
|
||||
__sync_sub_and_fetch(&val, 1);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
" LLVM_HAS_ATOMICS)
|
||||
|
||||
if( NOT LLVM_HAS_ATOMICS )
|
||||
message(STATUS "Warning: LLVM will be built thread-unsafe because atomic builtins are missing")
|
||||
endif()
|
||||
@@ -0,0 +1,13 @@
|
||||
# Determine if the compiler has GCC-compatible command-line syntax.
|
||||
|
||||
if(NOT DEFINED LLVM_COMPILER_IS_GCC_COMPATIBLE)
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(LLVM_COMPILER_IS_GCC_COMPATIBLE ON)
|
||||
elseif( MSVC )
|
||||
set(LLVM_COMPILER_IS_GCC_COMPATIBLE OFF)
|
||||
elseif( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" )
|
||||
set(LLVM_COMPILER_IS_GCC_COMPATIBLE ON)
|
||||
elseif( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Intel" )
|
||||
set(LLVM_COMPILER_IS_GCC_COMPATIBLE ON)
|
||||
endif()
|
||||
endif()
|
||||
@@ -1,64 +0,0 @@
|
||||
# - Try to find msgpack
|
||||
# Once done this will define
|
||||
# MSGPACK_FOUND - System has msgpack
|
||||
# MSGPACK_INCLUDE_DIRS - The msgpack include directories
|
||||
# MSGPACK_LIBRARIES - The libraries needed to use msgpack
|
||||
|
||||
if(NOT MSGPACK_USE_BUNDLED)
|
||||
find_package(PkgConfig)
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_search_module(PC_MSGPACK QUIET
|
||||
msgpackc>=${Msgpack_FIND_VERSION}
|
||||
msgpack>=${Msgpack_FIND_VERSION})
|
||||
endif()
|
||||
else()
|
||||
set(PC_MSGPACK_INCLUDEDIR)
|
||||
set(PC_MSGPACK_INCLUDE_DIRS)
|
||||
set(PC_MSGPACK_LIBDIR)
|
||||
set(PC_MSGPACK_LIBRARY_DIRS)
|
||||
set(LIMIT_SEARCH NO_DEFAULT_PATH)
|
||||
endif()
|
||||
|
||||
set(MSGPACK_DEFINITIONS ${PC_MSGPACK_CFLAGS_OTHER})
|
||||
|
||||
find_path(MSGPACK_INCLUDE_DIR msgpack/version_master.h
|
||||
HINTS ${PC_MSGPACK_INCLUDEDIR} ${PC_MSGPACK_INCLUDE_DIRS}
|
||||
${LIMIT_SEARCH})
|
||||
|
||||
if(MSGPACK_INCLUDE_DIR)
|
||||
file(READ ${MSGPACK_INCLUDE_DIR}/msgpack/version_master.h msgpack_version_h)
|
||||
string(REGEX REPLACE ".*MSGPACK_VERSION_MAJOR +([0-9]+).*" "\\1" MSGPACK_VERSION_MAJOR "${msgpack_version_h}")
|
||||
string(REGEX REPLACE ".*MSGPACK_VERSION_MINOR +([0-9]+).*" "\\1" MSGPACK_VERSION_MINOR "${msgpack_version_h}")
|
||||
string(REGEX REPLACE ".*MSGPACK_VERSION_REVISION +([0-9]+).*" "\\1" MSGPACK_VERSION_REVISION "${msgpack_version_h}")
|
||||
set(MSGPACK_VERSION_STRING "${MSGPACK_VERSION_MAJOR}.${MSGPACK_VERSION_MINOR}.${MSGPACK_VERSION_REVISION}")
|
||||
else()
|
||||
set(MSGPACK_VERSION_STRING)
|
||||
endif()
|
||||
|
||||
# If we're asked to use static linkage, add libmsgpack{,c}.a as a preferred library name.
|
||||
if(MSGPACK_USE_STATIC)
|
||||
list(APPEND MSGPACK_NAMES
|
||||
"${CMAKE_STATIC_LIBRARY_PREFIX}msgpackc${CMAKE_STATIC_LIBRARY_SUFFIX}"
|
||||
"${CMAKE_STATIC_LIBRARY_PREFIX}msgpack${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
||||
endif()
|
||||
|
||||
list(APPEND MSGPACK_NAMES msgpackc msgpack)
|
||||
|
||||
find_library(MSGPACK_LIBRARY NAMES ${MSGPACK_NAMES}
|
||||
# Check each directory for all names to avoid using headers/libraries from
|
||||
# different places.
|
||||
NAMES_PER_DIR
|
||||
HINTS ${PC_MSGPACK_LIBDIR} ${PC_MSGPACK_LIBRARY_DIRS}
|
||||
${LIMIT_SEARCH})
|
||||
|
||||
mark_as_advanced(MSGPACK_INCLUDE_DIR MSGPACK_LIBRARY)
|
||||
|
||||
set(MSGPACK_LIBRARIES ${MSGPACK_LIBRARY})
|
||||
set(MSGPACK_INCLUDE_DIRS ${MSGPACK_INCLUDE_DIR})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
# handle the QUIETLY and REQUIRED arguments and set MSGPACK_FOUND to TRUE
|
||||
# if all listed variables are TRUE
|
||||
find_package_handle_standard_args(Msgpack
|
||||
REQUIRED_VARS MSGPACK_LIBRARY MSGPACK_INCLUDE_DIR
|
||||
VERSION_VAR MSGPACK_VERSION_STRING)
|
||||
@@ -0,0 +1,14 @@
|
||||
# header-only does not produce a library
|
||||
if(NOT Restinio_FOUND)
|
||||
find_path (Restinio_INCLUDE_DIR restinio
|
||||
HINTS
|
||||
"/usr/include"
|
||||
"/usr/local/include"
|
||||
"/opt/local/include")
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Restinio DEFAULT_MSG Restinio_INCLUDE_DIR)
|
||||
if (Restinio_INCLUDE_DIR)
|
||||
set(Restinio_FOUND TRUE)
|
||||
set(Restinio_INCLUDE_DIRS ${Restinio_INCLUDE_DIR})
|
||||
endif()
|
||||
endif()
|
||||
+127
-18
@@ -1,23 +1,60 @@
|
||||
AC_INIT(opendht, 0.6.1)
|
||||
dnl define macros
|
||||
m4_define([opendht_major_version], 2)
|
||||
m4_define([opendht_minor_version], 4)
|
||||
m4_define([opendht_patch_version], 5)
|
||||
m4_define([opendht_version],
|
||||
[opendht_major_version.opendht_minor_version.opendht_patch_version])
|
||||
|
||||
AC_INIT(opendht, [opendht_version])
|
||||
AC_CONFIG_AUX_DIR(ac)
|
||||
AM_INIT_AUTOMAKE([foreign subdir-objects])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CANONICAL_HOST
|
||||
|
||||
AC_SUBST(OPENDHT_MAJOR_VERSION, opendht_major_version)
|
||||
AC_SUBST(OPENDHT_MINOR_VERSION, opendht_minor_version)
|
||||
AC_SUBST(OPENDHT_PATCH_VERSION, opendht_patch_version)
|
||||
|
||||
AC_ARG_ENABLE([debug], AS_HELP_STRING([--enable-debug], [Build in debug mode, adds stricter warnings, disables optimization]))
|
||||
AS_IF([test "x$enable_debug" = "xyes"],
|
||||
[CXXFLAGS="${CXXFLAGS} -g -Wno-return-type -Wall -Wextra -Wnon-virtual-dtor -O0"],
|
||||
[CXXFLAGS="${CXXFLAGS} -O3"])
|
||||
[CXXFLAGS="${CXXFLAGS} -g -Wno-return-type -Wall -Wextra -Wnon-virtual-dtor -O0 -pedantic-errors"],
|
||||
[CXXFLAGS="${CXXFLAGS} -O3 -pedantic-errors"])
|
||||
|
||||
AC_PROG_CXX
|
||||
AM_PROG_AR
|
||||
|
||||
dnl Check for logs
|
||||
AC_ARG_ENABLE([logs], [AS_HELP_STRING([--disable-logs], [Disable DHT logs])])
|
||||
AS_IF([test "x$enable_logs" != "xno"], [
|
||||
AC_DEFINE([OPENDHT_LOG], [true], [Define if DHT logs are enabled])
|
||||
], [
|
||||
AC_DEFINE([OPENDHT_LOG], [false], [Define if DHT logs are enabled])
|
||||
])
|
||||
|
||||
dnl Check for C binding
|
||||
AC_ARG_ENABLE([c], [AS_HELP_STRING([--disable-c], [Disable DHT C binding])])
|
||||
AM_CONDITIONAL(ENABLE_C, test x$enable_c != "xno")
|
||||
AM_COND_IF(ENABLE_C, [
|
||||
AC_DEFINE([OPENDHT_C], [], [Define if DHT C biding is enabled])
|
||||
])
|
||||
|
||||
dnl Check for indexation
|
||||
AC_ARG_ENABLE([indexation], [AS_HELP_STRING([--disable-indexation], [Disable DHT indexation])])
|
||||
AM_CONDITIONAL(ENABLE_INDEXATION, test x$enable_indexation != "xno")
|
||||
AM_COND_IF(ENABLE_INDEXATION, [
|
||||
AC_DEFINE([OPENDHT_INDEXATION], [], [Define if DHT indexation is enabled])
|
||||
])
|
||||
|
||||
AC_ARG_ENABLE([peer-discovery], [AS_HELP_STRING([--disable-peer-discovery], [Disable peer-discovery])])
|
||||
AM_CONDITIONAL(ENABLE_PEER_DISCOVERY, test x$enable_peer_discovery != "xno")
|
||||
AM_COND_IF(ENABLE_PEER_DISCOVERY, [AC_DEFINE([OPENDHT_PEER_DISCOVERY], [], [Define if peer discovery is enabled])])
|
||||
|
||||
dnl Check for Doxygen
|
||||
AC_ARG_ENABLE([doc], AS_HELP_STRING([--enable-doc], [Enable documentation generation (doxygen)]))
|
||||
AS_IF([test "x$enable_doc" = "xyes"], [
|
||||
AC_CHECK_PROGS([DOXYGEN], [doxygen])
|
||||
AS_IF([test -z "$DOXYGEN"], [AC_MSG_WARN([Doxygen not found - continuing without Doxygen support])])
|
||||
AS_IF([test -z "$DOXYGEN"], [AC_MSG_WARN([Doxygen not found - continuing without Doxygen support])])
|
||||
])
|
||||
AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"])
|
||||
|
||||
@@ -26,8 +63,8 @@ AC_ARG_ENABLE([python], [AS_HELP_STRING([--disable-python], [Disble python bindi
|
||||
AS_IF([test "x$enable_python" != "xno"], [
|
||||
AM_PATH_PYTHON([3.3],, [:])
|
||||
AS_IF([test -n "$PYTHON"],[
|
||||
AC_CHECK_PROGS([CYTHON], [cython3])
|
||||
AS_IF([test -z "$CYTHON"],[AC_MSG_WARN([Cython not found - continuing without python support])])
|
||||
echo 'import Cython' | $PYTHON
|
||||
AS_IF([test $? == 0],[CYTHON=yes],[AC_MSG_WARN([Cython not found - continuing without python support])])
|
||||
AC_CHECK_PROGS([PIP], [pip3])
|
||||
AS_IF([test -z "$PIP"],[AC_MSG_WARN([pip not found - continuing without python uninstall support])])
|
||||
])
|
||||
@@ -72,13 +109,80 @@ AS_IF([test "x$SYS" = "xandroid"],
|
||||
LT_INIT()
|
||||
LT_LANG(C++)
|
||||
|
||||
AX_CXX_COMPILE_STDCXX(11,[noext],[mandatory])
|
||||
AX_CXX_COMPILE_STDCXX(17,[noext],[mandatory])
|
||||
|
||||
PKG_PROG_PKG_CONFIG()
|
||||
|
||||
AC_ARG_ENABLE([proxy_server], AS_HELP_STRING([--enable-proxy-server], [Enable proxy server ability]), proxy_server=yes, proxy_server=no)
|
||||
AM_CONDITIONAL(ENABLE_PROXY_SERVER, test x$proxy_server == xyes)
|
||||
|
||||
AC_ARG_ENABLE([push_notifications], AS_HELP_STRING([--enable-push-notifications], [Enable push notifications support]), push_notifications=yes, push_notifications=no)
|
||||
AM_CONDITIONAL(ENABLE_PUSH_NOTIFICATIONS, test x$push_notifications == xyes)
|
||||
|
||||
AC_ARG_ENABLE([proxy_server_identity], AS_HELP_STRING([--enable-proxy-server-identity],
|
||||
[Enable proxy server ability]), proxy_server_identity=yes, proxy_server_identity=no)
|
||||
AM_CONDITIONAL(ENABLE_PROXY_SERVER_IDENTITY, test x$proxy_server_identity == xyes -a x$proxy_server == xyes)
|
||||
AC_ARG_ENABLE([proxy_server_identity], AS_HELP_STRING([--enable-proxy-server-identity],
|
||||
[Enable proxy server ability]), proxy_server_identity=yes, proxy_server_identity=no)
|
||||
|
||||
AC_ARG_ENABLE([proxy_client], AS_HELP_STRING([--enable-proxy-client], [Enable proxy client ability]), proxy_client=yes, proxy_client=no)
|
||||
AM_CONDITIONAL(ENABLE_PROXY_CLIENT, test x$proxy_client == xyes)
|
||||
|
||||
AC_ARG_ENABLE([tests], AS_HELP_STRING([--enable-tests], [Enable tests]), build_tests=yes, build_tests=no)
|
||||
AM_CONDITIONAL(ENABLE_TESTS, test "x$build_tests" == xyes)
|
||||
AS_IF([test "x$build_tests" == xyes], [
|
||||
PKG_CHECK_MODULES([CppUnit], [cppunit >= 1.12])
|
||||
])
|
||||
|
||||
AM_CONDITIONAL(PROXY_CLIENT_OR_SERVER, test x$proxy_client == xyes || test x$proxy_server == xyes)
|
||||
|
||||
PKG_CHECK_MODULES([Nettle], [nettle >= 2.4])
|
||||
PKG_CHECK_MODULES([GnuTLS], [gnutls >= 3.1])
|
||||
PKG_CHECK_MODULES([GnuTLS], [gnutls >= 3.3])
|
||||
PKG_CHECK_MODULES([MsgPack], [msgpack >= 1.2])
|
||||
CXXFLAGS="${CXXFLAGS} -DMSGPACK_DISABLE_LEGACY_NIL -DMSGPACK_DISABLE_LEGACY_CONVERT"
|
||||
PKG_CHECK_MODULES([Argon2], [libargon2])
|
||||
AC_SUBST(argon2_lib, [", libargon2"])
|
||||
|
||||
AC_ARG_WITH([jsoncpp], AS_HELP_STRING([--without-jsoncpp], [Build without JsonCpp support]))
|
||||
AS_IF([test "x$with_jsoncpp" != "xno"],
|
||||
[PKG_CHECK_MODULES([JsonCpp], [jsoncpp >= 1.7.2], [have_jsoncpp=yes], [have_jsoncpp=no])],
|
||||
[have_jsoncpp=no])
|
||||
AS_IF([test "x$have_jsoncpp" = "xyes"], [
|
||||
AC_MSG_NOTICE([Using JsonCpp])
|
||||
CPPFLAGS+=" -DOPENDHT_JSONCPP"
|
||||
AC_SUBST(jsoncpp_lib, [", jsoncpp"])
|
||||
], [
|
||||
AC_MSG_NOTICE([Not using JsonCpp])
|
||||
AM_COND_IF(PROXY_CLIENT_OR_SERVER, AC_MSG_ERROR(["JsonCpp is required for proxy/push notification support"]))
|
||||
])
|
||||
|
||||
AC_ARG_WITH([openssl], AS_HELP_STRING([--without-openssl], [Build with OpenSSL support]))
|
||||
AS_IF([test "x$with_openssl" != "xno"],
|
||||
[PKG_CHECK_MODULES([OpenSSL], [openssl >= 1.1], [have_openssl=yes], [have_openssl=no])],
|
||||
[have_openssl=no])
|
||||
AS_IF([test "x$have_openssl" = "xyes"], [
|
||||
AC_MSG_NOTICE([Using OpenSSL])
|
||||
AC_SUBST(openssl_lib, [", openssl"])
|
||||
], [
|
||||
AC_MSG_NOTICE([Not using OpenSSL])
|
||||
])
|
||||
|
||||
AM_COND_IF([PROXY_CLIENT_OR_SERVER], [
|
||||
AC_CHECK_HEADERS([asio.hpp], exit,, AC_MSG_ERROR([Missing Asio headers files]))
|
||||
CXXFLAGS="${CXXFLAGS} -DASIO_STANDALONE"
|
||||
PKG_CHECK_MODULES([Fmt], [fmt >= 5.3.0], [have_fmt=yes], [have_fmt=no])
|
||||
AS_IF([test "x$have_fmt" = "xyes"], [
|
||||
AC_MSG_NOTICE([Using libfmt])
|
||||
], [
|
||||
AC_MSG_NOTICE([Missing libfmt files])
|
||||
])
|
||||
# http_parser has no pkgconfig, instead we check with:
|
||||
AC_CHECK_LIB(http_parser, exit,, AC_MSG_ERROR([Missing HttpParser library files]))
|
||||
AC_CHECK_HEADERS([http_parser.h], [http_parser_headers=yes; break;])
|
||||
AC_SUBST(http_parser_lib, ["-lhttp_parser"])
|
||||
AS_IF([test "x$http_parser_headers" != "xyes"], AC_MSG_ERROR([Missing HttpParser headers files]))
|
||||
])
|
||||
|
||||
CXXFLAGS="${CXXFLAGS} -DMSGPACK_NO_BOOST -DMSGPACK_DISABLE_LEGACY_NIL -DMSGPACK_DISABLE_LEGACY_CONVERT"
|
||||
|
||||
AC_ARG_ENABLE([tools], AS_HELP_STRING([--disable-tools],[Disable tools (CLI DHT node)]),,build_tools=yes)
|
||||
AM_CONDITIONAL(ENABLE_TOOLS, test x$build_tools == xyes)
|
||||
@@ -88,20 +192,25 @@ AM_COND_IF([ENABLE_TOOLS], [
|
||||
])
|
||||
])
|
||||
|
||||
AM_COND_IF([HAVE_DOXYGEN], [AC_CONFIG_FILES([doc/Doxyfile doc/Makefile])])
|
||||
AM_COND_IF(ENABLE_PROXY_SERVER, AC_DEFINE([OPENDHT_PROXY_SERVER], [], [Building with proxy server]))
|
||||
AM_COND_IF(ENABLE_PROXY_CLIENT, AC_DEFINE([OPENDHT_PROXY_CLIENT], [], [Building with proxy client]))
|
||||
AM_COND_IF(ENABLE_PUSH_NOTIFICATIONS, [CPPFLAGS+=" -DOPENDHT_PUSH_NOTIFICATIONS"], [])
|
||||
AM_COND_IF(ENABLE_PROXY_SERVER_IDENTITY, [CPPFLAGS+=" -DOPENDHT_PROXY_SERVER_IDENTITY"], [])
|
||||
|
||||
dnl Configure setup.py if we build the python module
|
||||
AM_COND_IF([USE_CYTHON], [
|
||||
AC_SUBST(CURRENT_SOURCE_DIR, ".")
|
||||
AC_SUBST(CURRENT_BINARY_DIR, ".")
|
||||
AC_SUBST(PROJECT_SOURCE_DIR, "..")
|
||||
AC_SUBST(PROJECT_BINARY_DIR, "../src/.libs")
|
||||
AC_CONFIG_FILES([python/Makefile python/setup.py])
|
||||
])
|
||||
AC_SUBST(CURRENT_SOURCE_DIR, ".")
|
||||
AC_SUBST(CURRENT_BINARY_DIR, ".")
|
||||
AC_SUBST(PROJECT_SOURCE_DIR, "..")
|
||||
AC_SUBST(PROJECT_BINARY_DIR, "../src/.libs")
|
||||
|
||||
AC_CONFIG_FILES([Makefile
|
||||
src/Makefile
|
||||
src/argon2/Makefile
|
||||
c/Makefile
|
||||
tools/Makefile
|
||||
python/Makefile
|
||||
python/setup.py
|
||||
tests/Makefile
|
||||
doc/Makefile
|
||||
doc/Doxyfile
|
||||
opendht.pc])
|
||||
AC_OUTPUT
|
||||
@@ -0,0 +1,16 @@
|
||||
if (OPENDHT_TOOLS)
|
||||
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/dhtnode.1 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1)
|
||||
endif ()
|
||||
|
||||
if (OPENDHT_DOCUMENTATION)
|
||||
if (NOT DOXYGEN_FOUND)
|
||||
message(FATAL_ERROR "Doxygen is needed to build the documentation.")
|
||||
endif()
|
||||
configure_file (Doxyfile.in Doxyfile @ONLY)
|
||||
add_custom_target(doc ALL
|
||||
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "Generating API documentation with Doxygen"
|
||||
VERBATIM)
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION share/doc/opendht)
|
||||
endif()
|
||||
+8
-1
@@ -1,4 +1,10 @@
|
||||
doxyfile.stamp:
|
||||
dist_man1_MANS = dhtnode.1
|
||||
|
||||
noinst_HEADERS = \
|
||||
Doxyfile
|
||||
|
||||
if HAVE_DOXYGEN
|
||||
doxyfile.stamp: Doxyfile
|
||||
$(DOXYGEN) Doxyfile
|
||||
echo stamp > doxyfile.stamp
|
||||
|
||||
@@ -7,3 +13,4 @@ CLEANFILES = doxyfile.stamp
|
||||
all-local: doxyfile.stamp
|
||||
clean-local:
|
||||
rm -rf $(top_srcdir)/doc/man $(top_srcdir)/doc/html
|
||||
endif
|
||||
Binary file not shown.
@@ -0,0 +1,142 @@
|
||||
\documentclass[11pt]{article}
|
||||
|
||||
\usepackage[utf8x]{inputenc}
|
||||
\usepackage[top=2cm,bottom=2cm]{geometry}
|
||||
|
||||
\usepackage{forloop}
|
||||
\newcounter{counter}
|
||||
|
||||
% maths
|
||||
\usepackage{amssymb}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{mathrsfs}
|
||||
\usepackage{shadethm}
|
||||
\usepackage{amsthm}
|
||||
\newshadetheorem{shadeDef}{Definition}[section]
|
||||
\newtheorem{remark}{Remark}[section]
|
||||
\renewcommand{\P}{\mathbb{P}}
|
||||
|
||||
\usepackage{float}
|
||||
\usepackage{booktabs}
|
||||
|
||||
\setlength{\parindent}{0ex}
|
||||
\setlength{\parskip}{0.5em}
|
||||
|
||||
\begin{document}
|
||||
\title{Annex 1: Probabilistic analysis of connectivity changes}
|
||||
\author{Adrien Béraud, Simon Désaulniers, Guillaume Roguez}
|
||||
\maketitle
|
||||
\pagestyle{empty}
|
||||
\begin{shadeDef}
|
||||
A node flagged as \emph{``expired''} by a node $n$ is a node which has not responded to any
|
||||
of $n$'s last three requests.
|
||||
\end{shadeDef}
|
||||
\begin{remark}
|
||||
An expired node will not be contacted before 10 minutes from its expiration time.
|
||||
\end{remark}
|
||||
|
||||
Let $N$ the DHT network, $n_0\in N$, a given node and the following probabilistic events:
|
||||
\begin{itemize}
|
||||
\item $A$: $\forall n \in N$ $n$ is unreachable by $n_0$, \emph{i.e.} $n_0$ lost connection
|
||||
with $N$;
|
||||
\item $B$: $S\subset N$, the nodes unreachable by $n_0$ with $k={|S|\over|N|}$;
|
||||
\item $C$: $m \le |N|$ nodes are flagged as ``expired''.
|
||||
\end{itemize}
|
||||
|
||||
We are interested in knowing $\P(A|C)$, \emph{i.e.} the probability of the event where $A$ occurs
|
||||
prior to $C$. From the above, we immediately get
|
||||
$$\left\{
|
||||
\begin{array}{ll}
|
||||
\P(C|A) & = 1\\
|
||||
\P(A) + \P(B) & = 1
|
||||
\end{array}
|
||||
\right.$$
|
||||
Also, the event $A|C$ can be abstracted as the urn problem of draw without replacement. Then,
|
||||
$$\P(C|B) = \prod_{i=0}^m \left[k|N| - i \over |N|\right] = \prod_{i=0}^m \left[k - { i \over |N| }\right]$$
|
||||
Furthermore, using Bayes' theroem we have
|
||||
\begin{align*}
|
||||
\P(A|C) & = { \P(C|A)\P(A) \over \P(C|A)\P(A) + \P(C|B)\P(B)}\\
|
||||
& = { \P(A) \over \P(A) + \P(C|B)\P(B) }\\
|
||||
& = { \P(A) \over \P(A) + \P(C|B)\left[1 - \P(A)\right] }\\
|
||||
\Rightarrow \forloop{counter}{0}{\value{counter} < 5}{\qquad}\quad \P(A) & =
|
||||
\P(A|C)\left[\P(A) + \P(C|B)\left(1 - \P(A)\right) \right] \\
|
||||
\Rightarrow \forloop{counter}{0}{\value{counter} < 2}{\qquad}\;\:\, \P(A)\left[{ 1 \over \P(A|C)} - 1\right] & =
|
||||
\P(C|B)\left(1 - \P(A)\right) \\
|
||||
\end{align*}
|
||||
Finally,
|
||||
\begin{equation}
|
||||
\label{eq:final}
|
||||
\left[{\P(A) \over 1 - \P(A)}\right]\left[{ 1 \over \P(A|C)} - 1\right] =
|
||||
\prod_{i=0}^m \left[k - { i \over |N| }\right]
|
||||
\end{equation}
|
||||
|
||||
From \eqref{eq:final}, we may set a plausible configuration $\{\P(A),\P(A|C),k,|N|\}$ letting us
|
||||
produce results such as in table \ref{tbl:k_1_2}, \ref{tbl:k_2_3} and \ref{tbl:k_3_4}.
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\caption{The values for $m$ assuming $\P(A|C) \ge 0.95,\, k = {1 \over 2}$}
|
||||
\label{tbl:k_1_2}
|
||||
\begin{tabular}{lcccc}
|
||||
\toprule
|
||||
$|N| \diagdown \P(A)$ & ${1 \over 10 }$ & ${1 \over 100 }$ & ${1 \over 1000 }$ & ${1 \over 10000 }$\\
|
||||
\midrule
|
||||
$2^0$ & 1 & 1 & 1 & 1\\
|
||||
$2^1$ & 1 & 1 & 1 & 1\\
|
||||
$2^2$ & 2 & 2 & 2 & 2\\
|
||||
$2^3$ & 4 & 4 & 4 & 4\\
|
||||
$2^4$ & 5 & 6 & 7 & 8\\
|
||||
$2^5$ & 5 & 7 & 9 & 10\\
|
||||
$2^6$ & 6 & 9 & 11 & 13\\
|
||||
$2^7$ & 6 & 9 & 12 & 14\\
|
||||
$2^8$ & 7 & 10 & 13 & 16\\
|
||||
$2^9$ & 7 & 10 & 13 & 16\\
|
||||
$2^{10}$ & 7 & 10 & 13 & 17\\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\caption{The values for $m$ assuming $\P(A|C) \ge 0.95,\, k = {2 \over 3}$}
|
||||
\label{tbl:k_2_3}
|
||||
\begin{tabular}{lcccc}
|
||||
\toprule
|
||||
$|N| \diagdown \P(A)$ & ${1 \over 10 }$ & ${1 \over 100 }$ & ${1 \over 1000 }$ & ${1 \over 10000 }$\\
|
||||
\midrule
|
||||
$2^0$ & 1 & 1 & 1 & 1\\
|
||||
$2^1$ & 2 & 2 & 2 & 2\\
|
||||
$2^2$ & 3 & 3 & 4 & 4\\
|
||||
$2^3$ & 5 & 5 & 6 & 8\\
|
||||
$2^4$ & 6 & 8 & 9 & 10\\
|
||||
$2^5$ & 8 & 10 & 12 & 14\\
|
||||
$2^6$ & 9 & 13 & 16 & 18\\
|
||||
$2^7$ & 11 & 15 & 18 & 22\\
|
||||
$2^8$ & 11 & 16 & 21 & 25\\
|
||||
$2^9$ & 12 & 17 & 22 & 27\\
|
||||
$2^{10}$ & 12 & 18 & 23 & 28\\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\caption{The values for $m$ assuming $\P(A|C) \ge 0.95,\, k = {3 \over 4}$}
|
||||
\label{tbl:k_3_4}
|
||||
\begin{tabular}{lcccc}
|
||||
\toprule
|
||||
$|N| \diagdown \P(A)$ & ${1 \over 10 }$ & ${1 \over 100 }$ & ${1 \over 1000 }$ & ${1 \over 10000 }$\\
|
||||
\midrule
|
||||
$2^0$ & 1 & 1 & 1 & 1\\
|
||||
$2^1$ & 2 & 2 & 2 & 2\\
|
||||
$2^2$ & 3 & 3 & 3 & 3\\
|
||||
$2^3$ & 5 & 6 & 6 & 6\\
|
||||
$2^4$ & 7 & 9 & 10 & 11\\
|
||||
$2^5$ & 10 & 12 & 14 & 16\\
|
||||
$2^6$ & 12 & 16 & 19 & 22\\
|
||||
$2^7$ & 14 & 19 & 23 & 27\\
|
||||
$2^8$ & 15 & 21 & 27 & 32\\
|
||||
$2^9$ & 16 & 23 & 30 & 36\\
|
||||
$2^{10}$ & 17 & 24 & 31 & 38\\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
\end{document}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
.TH DHTNODE 1 2019-06-08
|
||||
.SH NAME
|
||||
.B dhtnode
|
||||
- a simple OpenDHT command line node runner.
|
||||
.SH SYNOPSIS
|
||||
.B dhtnode
|
||||
[\fB\-h\fR]
|
||||
[\fB\-v\fR [\fB\-l\fR \fIlogfile\fR] [\fB\-L\fR]]
|
||||
[\fB\-i\fR [\fB\-\-save\-identity\fR \fIfile\fR]]
|
||||
[\fB\-d\fR] [\fB\-s\fR]
|
||||
[\fB\-n\fR \fInetwork_id\fR]
|
||||
[\fB\-p\fR \fIlocal_port\fR]
|
||||
[\fB\-b\fR \fIbootstrap_host\fR[:\fIport\fR]]
|
||||
[\fB\-\-certificate\fR \fIfile\fR]
|
||||
[\fB\-\-privkey\fR \fIfile\fR]
|
||||
[\fB\-\-privkey\-password\fR \fIpassword\fR]
|
||||
[\fB\-\-proxyserver\fR \fIport\fR]
|
||||
[\fB\-\-proxyclient\fR \fIserver\fR]
|
||||
.SH DESCRIPTION
|
||||
Runs an OpenDHT node, with a CLI (default) or as a daemon (with \fB'-d'\fP or \fB'-s'\fP).
|
||||
Commands available in the interactive shell are:
|
||||
.EE
|
||||
h, help Print this help message.
|
||||
q, quit Quit the program.
|
||||
log Start/stop printing DHT logs.
|
||||
|
||||
Node information:
|
||||
ll Print basic information and stats about the current node.
|
||||
ls Print basic information about current searches.
|
||||
ld Print basic information about currenty stored values on this node.
|
||||
lr Print the full current routing table of this node
|
||||
|
||||
Operations on the DHT:
|
||||
b ip:port Ping potential node at given IP address/port.
|
||||
g [key] Get values at [key].
|
||||
l [key] Listen for value changes at [key].
|
||||
p [key] [str] Put string value at [key].
|
||||
s [key] [str] Put string value at [key], signed with our generated
|
||||
private key.
|
||||
e [key] [dest] [str] Put string value at [key], encrypted for [dest] with
|
||||
its public key (if found).
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-h\fP
|
||||
Prints some help.
|
||||
.TP
|
||||
\fB\-v\fP
|
||||
Enable the verbose mode (log to stdout/stderr by default)
|
||||
.TP
|
||||
\fB\-l\fP \fIlog_file\fP
|
||||
Write log to file instead of stdout/stderr
|
||||
.TP
|
||||
\fB\-L\fP
|
||||
Write log to syslog instead of stdout/stderr
|
||||
.TP
|
||||
\fB\-i\fP
|
||||
Generate cryptographic identity for the node.
|
||||
.TP
|
||||
\fB\-\-save\-identity\fP \fIfile\fP
|
||||
Save generated identity (certificate and private key) to given file prefix.
|
||||
.TP
|
||||
\fB\-\-certificate\fP \fIfile\fP
|
||||
Load identity certificate from given file.
|
||||
.TP
|
||||
\fB\-\-privkey\fP \fIfile\fP
|
||||
Load identity private key from given file.
|
||||
.TP
|
||||
\fB\-\-privkey\-password\fP \fIpassword\fP
|
||||
Password to use for private key encryption or decryption (optional).
|
||||
.TP
|
||||
\fB\-d\fP
|
||||
Run the program in daemon mode (will fork in the background).
|
||||
.TP
|
||||
\fB\-s\fP
|
||||
Run the program in service mode (non\-forking daemon).
|
||||
.TP
|
||||
\fB\-D\fP
|
||||
Enables multicast automatic local peer discovery.
|
||||
.TP
|
||||
\fB\-f\fP \fIfile\fP
|
||||
Specify a file path to persist/load the node state.
|
||||
.TP
|
||||
\fB\-n\fP \fInetwork_id\fP
|
||||
Specify the network id. This let you connect to distinct networks and prevents
|
||||
the merge of two different networks (available since OpenDHT v0.6.1).
|
||||
.TP
|
||||
\fB\-p\fP \fIlocal_port\fP
|
||||
Use UDP port \fIlocal_port\fP for the program to bind to.
|
||||
.TP
|
||||
\fB\-b\fP \fIbootstrap_host\fP[:\fIport\fP]
|
||||
The program needs to be given a node to connect to the network. You use this
|
||||
option to provide the ip address of that node.
|
||||
.TP
|
||||
\fB\-\-proxyserver\fP \fIlocal_port\fP
|
||||
Run a proxy server bound to this DHT node on HTTP port \fIlocal_port\fP
|
||||
.TP
|
||||
\fB\-\-proxyclient\fP \fIserver\fP
|
||||
Run this DHT node in proxy client mode, and connect to \fIserver\fP
|
||||
.SH AUTHORS
|
||||
.TP
|
||||
Program written by
|
||||
.IP \(bu
|
||||
.\}
|
||||
Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
.TP
|
||||
Man page written by
|
||||
.IP \(bu
|
||||
.\}
|
||||
Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
@@ -0,0 +1,16 @@
|
||||
FROM ghcr.io/savoirfairelinux/opendht/opendht-deps:latest
|
||||
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
|
||||
LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendht
|
||||
|
||||
RUN git clone https://github.com/savoirfairelinux/opendht.git \
|
||||
&& cd opendht && mkdir build && cd build \
|
||||
&& cmake .. -DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=On \
|
||||
-DOPENDHT_C=On \
|
||||
-DOPENDHT_PEER_DISCOVERY=On \
|
||||
-DOPENDHT_PYTHON=On \
|
||||
-DOPENDHT_TOOLS=On \
|
||||
-DOPENDHT_PROXY_SERVER=On \
|
||||
-DOPENDHT_PROXY_CLIENT=On \
|
||||
&& make -j8 && make install \
|
||||
&& cd ../.. && rm -rf opendht
|
||||
@@ -0,0 +1,16 @@
|
||||
FROM ghcr.io/savoirfairelinux/opendht/opendht-deps-bionic:latest
|
||||
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
|
||||
LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendht
|
||||
|
||||
RUN git clone https://github.com/savoirfairelinux/opendht.git \
|
||||
&& cd opendht && mkdir build && cd build \
|
||||
&& cmake .. -DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=On \
|
||||
-DOPENDHT_C=On \
|
||||
-DOPENDHT_PEER_DISCOVERY=On \
|
||||
-DOPENDHT_PYTHON=On \
|
||||
-DOPENDHT_TOOLS=On \
|
||||
-DOPENDHT_PROXY_SERVER=On \
|
||||
-DOPENDHT_PROXY_CLIENT=On \
|
||||
&& make -j8 && make install \
|
||||
&& cd ../.. && rm -rf opendht
|
||||
@@ -0,0 +1,28 @@
|
||||
FROM ubuntu:20.04
|
||||
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
|
||||
LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendht
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
dialog apt-utils \
|
||||
&& apt-get clean \
|
||||
&& echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
build-essential pkg-config cmake git wget \
|
||||
libtool autotools-dev autoconf \
|
||||
cython3 python3-dev python3-setuptools \
|
||||
libncurses5-dev libreadline-dev nettle-dev libcppunit-dev \
|
||||
libgnutls28-dev libuv1-dev libjsoncpp-dev libargon2-dev \
|
||||
libssl-dev libfmt-dev libhttp-parser-dev libasio-dev libmsgpack-dev \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/* /var/cache/apt/*
|
||||
|
||||
RUN echo "*** Downloading RESTinio ***" \
|
||||
&& mkdir restinio && cd restinio \
|
||||
&& wget https://github.com/aberaud/restinio/archive/e0a261dd8488246a3cb8bbb3ea781ea5139c3c94.tar.gz \
|
||||
&& ls -l && tar -xzf e0a261dd8488246a3cb8bbb3ea781ea5139c3c94.tar.gz \
|
||||
&& cd restinio-e0a261dd8488246a3cb8bbb3ea781ea5139c3c94/dev \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DRESTINIO_TEST=OFF -DRESTINIO_SAMPLE=OFF \
|
||||
-DRESTINIO_INSTALL_SAMPLES=OFF -DRESTINIO_BENCH=OFF -DRESTINIO_INSTALL_BENCHES=OFF \
|
||||
-DRESTINIO_FIND_DEPS=ON -DRESTINIO_ALLOW_SOBJECTIZER=Off -DRESTINIO_USE_BOOST_ASIO=none . \
|
||||
&& make -j8 && make install \
|
||||
&& cd ../../.. && rm -rf restinio
|
||||
@@ -0,0 +1,32 @@
|
||||
FROM ubuntu:18.04
|
||||
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
|
||||
LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendht
|
||||
|
||||
RUN echo "APT::Acquire::Retries \"3\";" > /etc/apt/apt.conf.d/80-retries
|
||||
RUN apt-get update && apt-get install -y \
|
||||
apt-transport-https build-essential pkg-config git wget libncurses5-dev libreadline-dev nettle-dev \
|
||||
libgnutls28-dev libuv1-dev cython3 python3-dev python3-setuptools libcppunit-dev libjsoncpp-dev \
|
||||
libargon2-0-dev \
|
||||
autotools-dev autoconf libfmt-dev libhttp-parser-dev libmsgpack-dev libssl-dev python3-pip \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/* /var/cache/apt/*
|
||||
|
||||
RUN pip3 install --upgrade cmake
|
||||
|
||||
# libasio-dev (1.10) is too old
|
||||
RUN echo "** Building a recent version of asio ***" \
|
||||
&& wget https://github.com/aberaud/asio/archive/a7d66ef4017d8f1b7f2cef1bb4ba8e23b0961571.tar.gz \
|
||||
&& tar -xvf a7d66ef4017d8f1b7f2cef1bb4ba8e23b0961571.tar.gz && cd asio-a7d66ef4017d8f1b7f2cef1bb4ba8e23b0961571/asio \
|
||||
&& ./autogen.sh && ./configure --prefix=/usr --without-boost --disable-examples --disable-tests \
|
||||
&& make install \
|
||||
&& cd ../../ && rm -rf asio*
|
||||
|
||||
RUN echo "*** Downloading RESTinio ***" \
|
||||
&& mkdir restinio && cd restinio \
|
||||
&& wget https://github.com/aberaud/restinio/archive/8d5d3e8237e0947adb9ba1ffc8281f4ad7cb2a59.tar.gz \
|
||||
&& ls -l && tar -xzf 8d5d3e8237e0947adb9ba1ffc8281f4ad7cb2a59.tar.gz \
|
||||
&& cd restinio-8d5d3e8237e0947adb9ba1ffc8281f4ad7cb2a59/dev \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DRESTINIO_TEST=OFF -DRESTINIO_SAMPLE=OFF \
|
||||
-DRESTINIO_INSTALL_SAMPLES=OFF -DRESTINIO_BENCH=OFF -DRESTINIO_INSTALL_BENCHES=OFF \
|
||||
-DRESTINIO_FIND_DEPS=ON -DRESTINIO_ALLOW_SOBJECTIZER=Off -DRESTINIO_USE_BOOST_ASIO=none . \
|
||||
&& make -j8 && make install \
|
||||
&& cd ../../ && rm -rf restinio*
|
||||
@@ -0,0 +1,32 @@
|
||||
FROM ubuntu:20.04
|
||||
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
|
||||
LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendht
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
dialog apt-utils \
|
||||
&& apt-get clean \
|
||||
&& echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y llvm llvm-dev lldb clang gdb make cmake pkg-config \
|
||||
libtool git wget libncurses5-dev libreadline-dev \
|
||||
nettle-dev libgnutls28-dev libuv1-dev libmsgpack-dev libjsoncpp-dev cython3 python3-dev \
|
||||
python3-setuptools libcppunit-dev python3-pip \
|
||||
autotools-dev autoconf libssl-dev libargon2-dev \
|
||||
libfmt-dev libhttp-parser-dev libasio-dev \
|
||||
&& apt-get remove -y gcc g++ && apt-get autoremove -y \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/* /var/cache/apt/*
|
||||
|
||||
ENV CC cc
|
||||
ENV CXX c++
|
||||
|
||||
RUN echo "*** Downloading RESTinio ***" \
|
||||
&& mkdir restinio && cd restinio \
|
||||
&& wget https://github.com/aberaud/restinio/archive/e0a261dd8488246a3cb8bbb3ea781ea5139c3c94.tar.gz \
|
||||
&& ls -l && tar -xzf e0a261dd8488246a3cb8bbb3ea781ea5139c3c94.tar.gz \
|
||||
&& cd restinio-e0a261dd8488246a3cb8bbb3ea781ea5139c3c94/dev \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DRESTINIO_TEST=OFF -DRESTINIO_SAMPLE=OFF \
|
||||
-DRESTINIO_INSTALL_SAMPLES=OFF -DRESTINIO_BENCH=OFF -DRESTINIO_INSTALL_BENCHES=OFF \
|
||||
-DRESTINIO_FIND_DEPS=ON -DRESTINIO_ALLOW_SOBJECTIZER=Off -DRESTINIO_USE_BOOST_ASIO=none . \
|
||||
&& make -j8 && make install \
|
||||
&& cd ../../ && rm -rf restinio*
|
||||
@@ -0,0 +1,16 @@
|
||||
FROM ghcr.io/savoirfairelinux/opendht/opendht-deps-llvm:latest
|
||||
LABEL maintainer="Adrien Béraud <adrien.beraud@savoirfairelinux.com>"
|
||||
LABEL org.opencontainers.image.source https://github.com/savoirfairelinux/opendht
|
||||
|
||||
RUN git clone https://github.com/savoirfairelinux/opendht.git \
|
||||
&& cd opendht && mkdir build && cd build \
|
||||
&& cmake .. -DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=On \
|
||||
-DOPENDHT_C=On \
|
||||
-DOPENDHT_PEER_DISCOVERY=On \
|
||||
-DOPENDHT_PYTHON=On \
|
||||
-DOPENDHT_TOOLS=On \
|
||||
-DOPENDHT_PROXY_SERVER=On \
|
||||
-DOPENDHT_PROXY_CLIENT=On \
|
||||
&& make -j8 && make install \
|
||||
&& cd ../.. && rm -rf opendht
|
||||
+2
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -13,12 +13,10 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "opendht/dhtrunner.h"
|
||||
#include "opendht/log.h"
|
||||
#include "opendht/default_types.h"
|
||||
+140
-20
@@ -1,6 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -13,8 +15,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
@@ -25,52 +26,171 @@
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
#include <json/json.h>
|
||||
#endif
|
||||
|
||||
namespace dht {
|
||||
|
||||
struct Node;
|
||||
|
||||
/**
|
||||
* Current status of a DHT node.
|
||||
*/
|
||||
enum class NodeStatus {
|
||||
Disconnected, // 0 nodes
|
||||
Connecting, // 1+ nodes
|
||||
Connected // 1+ good nodes
|
||||
};
|
||||
|
||||
struct Config {
|
||||
InfoHash node_id;
|
||||
bool is_bootstrap;
|
||||
inline constexpr const char*
|
||||
statusToStr(NodeStatus status) {
|
||||
return status == NodeStatus::Connected ? "connected" : (
|
||||
status == NodeStatus::Connecting ? "connecting" :
|
||||
"disconnected");
|
||||
}
|
||||
|
||||
struct OPENDHT_PUBLIC NodeStats {
|
||||
unsigned good_nodes {0},
|
||||
dubious_nodes {0},
|
||||
cached_nodes {0},
|
||||
incoming_nodes {0};
|
||||
unsigned table_depth {0};
|
||||
unsigned searches {0};
|
||||
unsigned node_cache_size {0};
|
||||
unsigned getKnownNodes() const { return good_nodes + dubious_nodes; }
|
||||
unsigned long getNetworkSizeEstimation() const { return 8 * std::exp2(table_depth); }
|
||||
std::string toString() const;
|
||||
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
/**
|
||||
* Build a json object from a NodeStats
|
||||
*/
|
||||
Json::Value toJson() const;
|
||||
NodeStats() {};
|
||||
explicit NodeStats(const Json::Value& v);
|
||||
#endif
|
||||
|
||||
MSGPACK_DEFINE_MAP(good_nodes, dubious_nodes, cached_nodes, incoming_nodes, table_depth, searches, node_cache_size)
|
||||
};
|
||||
|
||||
struct SecureDhtConfig
|
||||
{
|
||||
Config node_config;
|
||||
crypto::Identity id;
|
||||
struct OPENDHT_PUBLIC NodeInfo {
|
||||
InfoHash id;
|
||||
InfoHash node_id;
|
||||
NodeStats ipv4 {};
|
||||
NodeStats ipv6 {};
|
||||
size_t ongoing_ops {0};
|
||||
size_t storage_values {0};
|
||||
size_t storage_size {0};
|
||||
in_port_t bound4 {0};
|
||||
in_port_t bound6 {0};
|
||||
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
/**
|
||||
* Build a json object from a NodeStats
|
||||
*/
|
||||
Json::Value toJson() const;
|
||||
NodeInfo() {};
|
||||
explicit NodeInfo(const Json::Value& v);
|
||||
#endif
|
||||
|
||||
MSGPACK_DEFINE_MAP(id, node_id, ipv4, ipv6)
|
||||
};
|
||||
|
||||
/**
|
||||
* Dht configuration.
|
||||
*/
|
||||
struct OPENDHT_PUBLIC Config {
|
||||
/** DHT node ID */
|
||||
InfoHash node_id {};
|
||||
|
||||
/**
|
||||
* DHT network ID. A node will only talk with other nodes having
|
||||
* the same network ID.
|
||||
* Network ID 0 (default) represents the main public network.
|
||||
*/
|
||||
NetId network {0};
|
||||
|
||||
/** For testing purposes only, enables bootstrap mode */
|
||||
bool is_bootstrap {false};
|
||||
|
||||
/** Makes the DHT responsible to maintain its stored values. Consumes more ressources. */
|
||||
bool maintain_storage {false};
|
||||
|
||||
/** If set, the dht will load its state from this file on start and save its state in this file on shutdown */
|
||||
std::string persist_path {};
|
||||
|
||||
/** If non-0, overrides the default global rate-limit. -1 means no limit. */
|
||||
ssize_t max_req_per_sec {0};
|
||||
|
||||
/** If non-0, overrides the default per-IP address rate-limit. -1 means no limit. */
|
||||
ssize_t max_peer_req_per_sec {0};
|
||||
|
||||
/* If non-0, overrides the default maximum number of searches. -1 means no limit. */
|
||||
ssize_t max_searches {0};
|
||||
|
||||
/* If non-0, overrides the default maximum store size. -1 means no limit. */
|
||||
ssize_t max_store_size {0};
|
||||
|
||||
/* If non-0, overrides the default maximum store key count. -1 means no limit. */
|
||||
ssize_t max_store_keys {0};
|
||||
|
||||
/**
|
||||
* Use appropriate bahavior for a public IP, stable node:
|
||||
* - No connectivity change triggered when a search fails
|
||||
* - Larger listen refresh time
|
||||
*/
|
||||
bool public_stable {false};
|
||||
};
|
||||
|
||||
/**
|
||||
* SecureDht configuration.
|
||||
*/
|
||||
struct OPENDHT_PUBLIC SecureDhtConfig
|
||||
{
|
||||
Config node_config {};
|
||||
crypto::Identity id {};
|
||||
|
||||
/**
|
||||
* Cache all encountered public keys and certificates,
|
||||
* for use by the certificate store, putEncrypted and putSigned
|
||||
*/
|
||||
bool cert_cache_all {false};
|
||||
};
|
||||
|
||||
static constexpr size_t DEFAULT_STORAGE_LIMIT {1024 * 1024 * 64};
|
||||
|
||||
using ValuesExport = std::pair<InfoHash, Blob>;
|
||||
|
||||
using QueryCallback = std::function<bool(const std::vector<std::shared_ptr<FieldValueIndex>>& fields)>;
|
||||
using GetCallback = std::function<bool(const std::vector<std::shared_ptr<Value>>& values)>;
|
||||
using ValueCallback = std::function<bool(const std::vector<std::shared_ptr<Value>>& values, bool expired)>;
|
||||
using GetCallbackSimple = std::function<bool(std::shared_ptr<Value> value)>;
|
||||
using ShutdownCallback = std::function<void()>;
|
||||
using IdentityAnnouncedCb = std::function<void(bool)>;
|
||||
|
||||
using CertificateStoreQuery = std::function<std::vector<std::shared_ptr<crypto::Certificate>>(const InfoHash& pk_id)>;
|
||||
|
||||
typedef bool (*GetCallbackRaw)(std::shared_ptr<Value>, void *user_data);
|
||||
|
||||
static constexpr size_t DEFAULT_STORAGE_LIMIT {1024 * 1024 * 64};
|
||||
|
||||
GetCallbackSimple bindGetCb(GetCallbackRaw raw_cb, void* user_data);
|
||||
GetCallback bindGetCb(GetCallbackSimple cb);
|
||||
typedef bool (*ValueCallbackRaw)(std::shared_ptr<Value>, bool expired, void *user_data);
|
||||
|
||||
using DoneCallback = std::function<void(bool success, const std::vector<std::shared_ptr<Node>>& nodes)>;
|
||||
typedef void (*DoneCallbackRaw)(bool, std::vector<std::shared_ptr<Node>>*, void *user_data);
|
||||
typedef void (*ShutdownCallbackRaw)(void *user_data);
|
||||
typedef void (*DoneCallbackSimpleRaw)(bool, void *user_data);
|
||||
typedef bool (*FilterRaw)(const Value&, void *user_data);
|
||||
|
||||
using DoneCallbackSimple = std::function<void(bool success)>;
|
||||
|
||||
ShutdownCallback bindShutdownCb(ShutdownCallbackRaw shutdown_cb_raw, void* user_data);
|
||||
DoneCallback bindDoneCb(DoneCallbackSimple donecb);
|
||||
DoneCallback bindDoneCb(DoneCallbackRaw raw_cb, void* user_data);
|
||||
|
||||
OPENDHT_PUBLIC GetCallbackSimple bindGetCb(GetCallbackRaw raw_cb, void* user_data);
|
||||
OPENDHT_PUBLIC GetCallback bindGetCb(GetCallbackSimple cb);
|
||||
OPENDHT_PUBLIC ValueCallback bindValueCb(ValueCallbackRaw raw_cb, void* user_data);
|
||||
OPENDHT_PUBLIC ShutdownCallback bindShutdownCb(ShutdownCallbackRaw shutdown_cb_raw, void* user_data);
|
||||
OPENDHT_PUBLIC DoneCallback bindDoneCb(DoneCallbackSimple donecb);
|
||||
OPENDHT_PUBLIC DoneCallback bindDoneCb(DoneCallbackRaw raw_cb, void* user_data);
|
||||
OPENDHT_PUBLIC DoneCallbackSimple bindDoneCbSimple(DoneCallbackSimpleRaw raw_cb, void* user_data);
|
||||
OPENDHT_PUBLIC Value::Filter bindFilterRaw(FilterRaw raw_filter, void* user_data);
|
||||
|
||||
}
|
||||
+473
-41
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Vsevolod Ivanov <vsevolod.ivanov@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -13,19 +14,20 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "infohash.h"
|
||||
#include "utils.h"
|
||||
#include "rng.h"
|
||||
|
||||
extern "C" {
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <gnutls/abstract.h>
|
||||
#include <gnutls/x509.h>
|
||||
#include <gnutls/ocsp.h>
|
||||
}
|
||||
|
||||
#include <vector>
|
||||
@@ -36,38 +38,52 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
namespace dht {
|
||||
|
||||
/**
|
||||
* Contains all crypto primitives
|
||||
*/
|
||||
namespace crypto {
|
||||
|
||||
class CryptoException : public std::runtime_error {
|
||||
public:
|
||||
CryptoException(const std::string& str) : std::runtime_error(str) {};
|
||||
class OPENDHT_PUBLIC CryptoException : public std::runtime_error {
|
||||
public:
|
||||
explicit CryptoException(const std::string& str) : std::runtime_error(str) {};
|
||||
explicit CryptoException(const char* str) : std::runtime_error(str) {};
|
||||
CryptoException(const CryptoException& e) noexcept = default;
|
||||
CryptoException& operator=(const CryptoException&) noexcept = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* Exception thrown when a decryption error happened.
|
||||
*/
|
||||
class DecryptError : public CryptoException {
|
||||
public:
|
||||
DecryptError(const std::string& str = "") : CryptoException(str) {};
|
||||
class OPENDHT_PUBLIC DecryptError : public CryptoException {
|
||||
public:
|
||||
explicit DecryptError(const std::string& str) : CryptoException(str) {};
|
||||
explicit DecryptError(const char* str) : CryptoException(str) {};
|
||||
DecryptError(const DecryptError& e) noexcept = default;
|
||||
DecryptError& operator=(const DecryptError&) noexcept = default;
|
||||
};
|
||||
|
||||
struct PrivateKey;
|
||||
struct Certificate;
|
||||
class RevocationList;
|
||||
|
||||
using Identity = std::pair<std::shared_ptr<PrivateKey>, std::shared_ptr<Certificate>>;
|
||||
|
||||
/**
|
||||
* A public key.
|
||||
*/
|
||||
struct PublicKey
|
||||
struct OPENDHT_PUBLIC PublicKey
|
||||
{
|
||||
PublicKey() {}
|
||||
PublicKey();
|
||||
|
||||
/**
|
||||
* Takes ownership of an existing gnutls_pubkey.
|
||||
*/
|
||||
PublicKey(gnutls_pubkey_t k) : pk(k) {}
|
||||
PublicKey(const Blob& pk);
|
||||
|
||||
/** Import public key from serialized data */
|
||||
PublicKey(const uint8_t* dat, size_t dat_size);
|
||||
PublicKey(const Blob& pk) : PublicKey(pk.data(), pk.size()) {}
|
||||
PublicKey(PublicKey&& o) noexcept : pk(o.pk) { o.pk = nullptr; };
|
||||
|
||||
~PublicKey();
|
||||
@@ -81,13 +97,32 @@ struct PublicKey
|
||||
|
||||
PublicKey& operator=(PublicKey&& o) noexcept;
|
||||
|
||||
/**
|
||||
* Get public key fingerprint
|
||||
*/
|
||||
InfoHash getId() const;
|
||||
bool checkSignature(const Blob& data, const Blob& signature) const;
|
||||
Blob encrypt(const Blob&) const;
|
||||
|
||||
/**
|
||||
* Get public key long fingerprint
|
||||
*/
|
||||
PkId getLongId() const;
|
||||
|
||||
bool checkSignature(const uint8_t* data, size_t data_len, const uint8_t* signature, size_t signature_len) const;
|
||||
inline bool checkSignature(const Blob& data, const Blob& signature) const {
|
||||
return checkSignature(data.data(), data.size(), signature.data(), signature.size());
|
||||
}
|
||||
|
||||
Blob encrypt(const uint8_t* data, size_t data_len) const;
|
||||
inline Blob encrypt(const Blob& data) const {
|
||||
return encrypt(data.data(), data.size());
|
||||
}
|
||||
|
||||
void pack(Blob& b) const;
|
||||
int pack(uint8_t* out, size_t* out_len) const;
|
||||
void unpack(const uint8_t* dat, size_t dat_size);
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& p) const
|
||||
{
|
||||
@@ -97,9 +132,11 @@ struct PublicKey
|
||||
p.pack_bin_body((const char*)b.data(), b.size());
|
||||
}
|
||||
|
||||
void msgpack_unpack(msgpack::object o);
|
||||
void msgpack_unpack(const msgpack::object& o);
|
||||
|
||||
gnutls_pubkey_t pk {};
|
||||
gnutls_digest_algorithm_t getPreferredDigest() const;
|
||||
|
||||
gnutls_pubkey_t pk {nullptr};
|
||||
private:
|
||||
PublicKey(const PublicKey&) = delete;
|
||||
PublicKey& operator=(const PublicKey&) = delete;
|
||||
@@ -109,7 +146,7 @@ private:
|
||||
/**
|
||||
* A private key, including the corresponding public key.
|
||||
*/
|
||||
struct PrivateKey
|
||||
struct OPENDHT_PUBLIC PrivateKey
|
||||
{
|
||||
PrivateKey();
|
||||
//PrivateKey(gnutls_privkey_t k) : key(k) {}
|
||||
@@ -122,24 +159,31 @@ struct PrivateKey
|
||||
PrivateKey(PrivateKey&& o) noexcept;
|
||||
PrivateKey& operator=(PrivateKey&& o) noexcept;
|
||||
|
||||
PrivateKey(const Blob& import, const std::string& password = {});
|
||||
PrivateKey(const uint8_t* src, size_t src_size, const char* password = nullptr);
|
||||
PrivateKey(const Blob& src, const std::string& password = {}) : PrivateKey(src.data(), src.size(), password.data()) {}
|
||||
~PrivateKey();
|
||||
explicit operator bool() const { return key; }
|
||||
PublicKey getPublicKey() const;
|
||||
|
||||
const PublicKey& getPublicKey() const;
|
||||
const std::shared_ptr<PublicKey>& getSharedPublicKey() const;
|
||||
|
||||
int serialize(uint8_t* out, size_t* out_len, const std::string& password = {}) const;
|
||||
Blob serialize(const std::string& password = {}) const;
|
||||
|
||||
/**
|
||||
* Sign the provided binary object.
|
||||
* @returns the signature data.
|
||||
*/
|
||||
Blob sign(const Blob&) const;
|
||||
Blob sign(const uint8_t* data, size_t data_len) const;
|
||||
inline Blob sign(const Blob& dat) const { return sign(dat.data(), dat.size()); }
|
||||
|
||||
/**
|
||||
* Try to decrypt the provided cypher text.
|
||||
* In case of failure a CryptoException is thrown.
|
||||
* @returns the decrypted data.
|
||||
*/
|
||||
Blob decrypt(const Blob& cypher) const;
|
||||
Blob decrypt(const uint8_t* cypher, size_t cypher_len) const;
|
||||
Blob decrypt(const Blob& cypher) const { return decrypt(cypher.data(), cypher.size()); }
|
||||
|
||||
/**
|
||||
* Generate a new RSA key pair
|
||||
@@ -148,6 +192,7 @@ struct PrivateKey
|
||||
* Recommended values: 4096, 8192
|
||||
*/
|
||||
static PrivateKey generate(unsigned key_length = 4096);
|
||||
static PrivateKey generateEC();
|
||||
|
||||
gnutls_privkey_t key {};
|
||||
gnutls_x509_privkey_t x509_key {};
|
||||
@@ -156,16 +201,177 @@ private:
|
||||
PrivateKey& operator=(const PrivateKey&) = delete;
|
||||
Blob decryptBloc(const uint8_t* src, size_t src_size) const;
|
||||
|
||||
//friend dht::crypto::Identity dht::crypto::generateIdentity(const std::string&, dht::crypto::Identity, unsigned key_length);
|
||||
mutable std::shared_ptr<PublicKey> publicKey_ {};
|
||||
};
|
||||
|
||||
struct Certificate {
|
||||
Certificate() {}
|
||||
class OPENDHT_PUBLIC RevocationList
|
||||
{
|
||||
using clock = std::chrono::system_clock;
|
||||
using time_point = clock::time_point;
|
||||
using duration = clock::duration;
|
||||
public:
|
||||
RevocationList();
|
||||
RevocationList(const Blob& b);
|
||||
RevocationList(RevocationList&& o) noexcept : crl(o.crl) { o.crl = nullptr; }
|
||||
~RevocationList();
|
||||
|
||||
RevocationList& operator=(RevocationList&& o) { crl = o.crl; o.crl = nullptr; return *this; }
|
||||
|
||||
void pack(Blob& b) const;
|
||||
void unpack(const uint8_t* dat, size_t dat_size);
|
||||
Blob getPacked() const {
|
||||
Blob b;
|
||||
pack(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& p) const
|
||||
{
|
||||
Blob b = getPacked();
|
||||
p.pack_bin(b.size());
|
||||
p.pack_bin_body((const char*)b.data(), b.size());
|
||||
}
|
||||
|
||||
void msgpack_unpack(const msgpack::object& o);
|
||||
|
||||
void revoke(const Certificate& crt, time_point t = time_point::min());
|
||||
|
||||
bool isRevoked(const Certificate& crt) const;
|
||||
|
||||
/**
|
||||
* Sign this revocation list using provided key and certificate.
|
||||
* Validity_period sets the duration until next update (default to no next update).
|
||||
*/
|
||||
void sign(const PrivateKey&, const Certificate&, duration validity_period = {});
|
||||
void sign(const Identity& id) { sign(*id.first, *id.second); }
|
||||
|
||||
bool isSignedBy(const Certificate& issuer) const;
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
/**
|
||||
* Read the CRL number extension field.
|
||||
*/
|
||||
Blob getNumber() const;
|
||||
|
||||
/** Read CRL issuer Common Name (CN) */
|
||||
std::string getIssuerName() const;
|
||||
|
||||
/** Read CRL issuer User ID (UID) */
|
||||
std::string getIssuerUID() const;
|
||||
|
||||
time_point getUpdateTime() const;
|
||||
time_point getNextUpdateTime() const;
|
||||
|
||||
gnutls_x509_crl_t get() { return crl; }
|
||||
gnutls_x509_crl_t getCopy() const {
|
||||
if (not crl)
|
||||
return nullptr;
|
||||
auto copy = RevocationList(getPacked());
|
||||
gnutls_x509_crl_t ret = copy.crl;
|
||||
copy.crl = nullptr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
gnutls_x509_crl_t crl {};
|
||||
RevocationList(const RevocationList&) = delete;
|
||||
RevocationList& operator=(const RevocationList&) = delete;
|
||||
};
|
||||
|
||||
enum class NameType { UNKNOWN = 0, RFC822, DNS, URI, IP };
|
||||
|
||||
class OPENDHT_PUBLIC CertificateRequest {
|
||||
public:
|
||||
CertificateRequest();
|
||||
CertificateRequest(const uint8_t* data, size_t size);
|
||||
CertificateRequest(const Blob& data) : CertificateRequest(data.data(), data.size()) {}
|
||||
|
||||
CertificateRequest(CertificateRequest&& o) noexcept : request(std::move(o.request)) {
|
||||
o.request = nullptr;
|
||||
}
|
||||
CertificateRequest& operator=(CertificateRequest&& o) noexcept;
|
||||
|
||||
~CertificateRequest();
|
||||
|
||||
void setName(const std::string& name);
|
||||
void setUID(const std::string& name);
|
||||
void setAltName(NameType type, const std::string& name);
|
||||
|
||||
std::string getName() const;
|
||||
std::string getUID() const;
|
||||
|
||||
void sign(const PrivateKey& key, const std::string& password = {});
|
||||
|
||||
bool verify() const;
|
||||
|
||||
Blob pack() const;
|
||||
std::string toString() const;
|
||||
|
||||
gnutls_x509_crq_t get() const { return request; }
|
||||
private:
|
||||
CertificateRequest(const CertificateRequest& o) = delete;
|
||||
CertificateRequest& operator=(const CertificateRequest& o) = delete;
|
||||
gnutls_x509_crq_t request {nullptr};
|
||||
};
|
||||
|
||||
class OPENDHT_PUBLIC OcspRequest
|
||||
{
|
||||
public:
|
||||
OcspRequest(gnutls_ocsp_req_t r) : request(r) {};
|
||||
OcspRequest(const uint8_t* dat_ptr, size_t dat_size);
|
||||
~OcspRequest();
|
||||
|
||||
/*
|
||||
* Get OCSP Request in readable format.
|
||||
*/
|
||||
std::string toString(const bool compact = true) const;
|
||||
|
||||
Blob pack() const;
|
||||
Blob getNonce() const;
|
||||
private:
|
||||
gnutls_ocsp_req_t request;
|
||||
};
|
||||
|
||||
class OPENDHT_PUBLIC OcspResponse
|
||||
{
|
||||
public:
|
||||
OcspResponse(const uint8_t* dat_ptr, size_t dat_size);
|
||||
OcspResponse(const std::string& response) : OcspResponse((const uint8_t*)response.data(), response.size()) {};
|
||||
~OcspResponse();
|
||||
|
||||
Blob pack() const;
|
||||
/*
|
||||
* Get OCSP Response in readable format.
|
||||
*/
|
||||
std::string toString(const bool compact = true) const;
|
||||
|
||||
/*
|
||||
* Get OCSP response certificate status.
|
||||
* Return certificate status.
|
||||
* http://www.gnu.org/software/gnutls/reference/gnutls-ocsp.html#gnutls-ocsp-cert-status-t
|
||||
*/
|
||||
gnutls_ocsp_cert_status_t getCertificateStatus() const;
|
||||
|
||||
/*
|
||||
* Verify OCSP response and return OCSP status.
|
||||
* Throws CryptoException in case of error in the response.
|
||||
* http://www.gnu.org/software/gnutls/reference/gnutls-ocsp.html#gnutls-ocsp-verify-reason-t
|
||||
*/
|
||||
gnutls_ocsp_cert_status_t verifyDirect(const Certificate& crt, const Blob& nonce);
|
||||
|
||||
private:
|
||||
gnutls_ocsp_resp_t response;
|
||||
};
|
||||
|
||||
struct OPENDHT_PUBLIC Certificate {
|
||||
Certificate() noexcept {}
|
||||
|
||||
/**
|
||||
* Take ownership of existing gnutls structure
|
||||
*/
|
||||
Certificate(gnutls_x509_crt_t crt) : cert(crt) {}
|
||||
Certificate(gnutls_x509_crt_t crt) noexcept : cert(crt) {}
|
||||
|
||||
Certificate(Certificate&& o) noexcept : cert(o.cert), issuer(std::move(o.issuer)) { o.cert = nullptr; };
|
||||
|
||||
@@ -174,7 +380,10 @@ struct Certificate {
|
||||
* ordered from subject to issuer
|
||||
*/
|
||||
Certificate(const Blob& crt);
|
||||
Certificate(const uint8_t* dat, size_t dat_size) {
|
||||
Certificate(const std::string& pem) : cert(nullptr) {
|
||||
unpack((const uint8_t*)pem.data(), pem.size());
|
||||
}
|
||||
Certificate(const uint8_t* dat, size_t dat_size) : cert(nullptr) {
|
||||
unpack(dat, dat_size);
|
||||
}
|
||||
|
||||
@@ -271,13 +480,17 @@ struct Certificate {
|
||||
p.pack_bin_body((const char*)b.data(), b.size());
|
||||
}
|
||||
|
||||
void msgpack_unpack(msgpack::object o);
|
||||
void msgpack_unpack(const msgpack::object& o);
|
||||
|
||||
explicit operator bool() const { return cert; }
|
||||
PublicKey getPublicKey() const;
|
||||
|
||||
/** Same as getPublicKey().getId() */
|
||||
InfoHash getId() const;
|
||||
/** Same as getPublicKey().getLongId() */
|
||||
PkId getLongId() const;
|
||||
|
||||
Blob getSerialNumber() const;
|
||||
|
||||
/** Read certificate Common Name (CN) */
|
||||
std::string getName() const;
|
||||
@@ -291,13 +504,15 @@ struct Certificate {
|
||||
/** Read certificate issuer User ID (UID) */
|
||||
std::string getIssuerUID() const;
|
||||
|
||||
enum class NameType { UNKNOWN = 0, RFC822, DNS, URI, IP };
|
||||
|
||||
/** Read certificate alternative names */
|
||||
std::vector<std::pair<NameType, std::string>> getAltNames() const;
|
||||
|
||||
std::chrono::system_clock::time_point getActivation() const;
|
||||
std::chrono::system_clock::time_point getExpiration() const;
|
||||
|
||||
/**
|
||||
* Returns true if the certificate is marked as a Certificate Authority.
|
||||
* Returns true if the certificate is marked as a Certificate Authority
|
||||
* and has necessary key usage flags to sign certificates.
|
||||
*/
|
||||
bool isCA() const;
|
||||
|
||||
@@ -307,15 +522,216 @@ struct Certificate {
|
||||
*/
|
||||
std::string toString(bool chain = true) const;
|
||||
|
||||
static Certificate generate(const PrivateKey& key, const std::string& name = "dhtnode", Identity ca = {}, bool is_ca = false);
|
||||
std::string print() const;
|
||||
|
||||
gnutls_x509_crt_t cert {};
|
||||
/**
|
||||
* As a CA, revoke a certificate, adding it to
|
||||
* the attached Certificate Revocation List (CRL)
|
||||
*/
|
||||
void revoke(const PrivateKey&, const Certificate&);
|
||||
|
||||
/**
|
||||
* Get the list of certificates revoked as as CA.
|
||||
*/
|
||||
std::vector<std::shared_ptr<RevocationList>> getRevocationLists() const;
|
||||
|
||||
/**
|
||||
* Attach existing revocation list.
|
||||
*/
|
||||
void addRevocationList(RevocationList&&);
|
||||
void addRevocationList(std::shared_ptr<RevocationList>);
|
||||
|
||||
static Certificate generate(const PrivateKey& key, const std::string& name = "dhtnode", const Identity& ca = {}, bool is_ca = false, int64_t validity = 0);
|
||||
static Certificate generate(const CertificateRequest& request, const Identity& ca, int64_t validity = 0);
|
||||
|
||||
gnutls_x509_crt_t getCopy() const {
|
||||
if (not cert)
|
||||
return nullptr;
|
||||
auto copy = Certificate(getPacked());
|
||||
gnutls_x509_crt_t ret = copy.cert;
|
||||
copy.cert = nullptr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<gnutls_x509_crt_t>
|
||||
getChain(bool copy = false) const
|
||||
{
|
||||
if (not cert)
|
||||
return {};
|
||||
std::vector<gnutls_x509_crt_t> crts;
|
||||
for (auto c = this; c; c = c->issuer.get())
|
||||
crts.emplace_back(copy ? c->getCopy() : c->cert);
|
||||
return crts;
|
||||
}
|
||||
|
||||
std::pair<
|
||||
std::vector<gnutls_x509_crt_t>,
|
||||
std::vector<gnutls_x509_crl_t>
|
||||
>
|
||||
getChainWithRevocations(bool copy = false) const
|
||||
{
|
||||
if (not cert)
|
||||
return {};
|
||||
std::vector<gnutls_x509_crt_t> crts;
|
||||
std::vector<gnutls_x509_crl_t> crls;
|
||||
for (auto c = this; c; c = c->issuer.get()) {
|
||||
crts.emplace_back(copy ? c->getCopy() : c->cert);
|
||||
crls.reserve(crls.size() + c->revocation_lists.size());
|
||||
for (const auto& crl : c->revocation_lists)
|
||||
crls.emplace_back(copy ? crl->getCopy() : crl->get());
|
||||
}
|
||||
return {crts, crls};
|
||||
}
|
||||
|
||||
gnutls_digest_algorithm_t getPreferredDigest() const;
|
||||
|
||||
/*
|
||||
* Generate OCSP request.
|
||||
* Return GnuTLS error code.
|
||||
* https://www.gnutls.org/manual/html_node/Error-codes.html
|
||||
*/
|
||||
std::pair<std::string, Blob> generateOcspRequest(gnutls_x509_crt_t& issuer);
|
||||
|
||||
/**
|
||||
* Change certificate's expiration
|
||||
*/
|
||||
void setValidity(const Identity& ca, int64_t validity);
|
||||
void setValidity(const PrivateKey& key, int64_t validity);
|
||||
|
||||
gnutls_x509_crt_t cert {nullptr};
|
||||
std::shared_ptr<Certificate> issuer {};
|
||||
std::shared_ptr<OcspResponse> ocspResponse;
|
||||
private:
|
||||
Certificate(const Certificate&) = delete;
|
||||
Certificate& operator=(const Certificate&) = delete;
|
||||
mutable InfoHash cachedId_ {};
|
||||
mutable PkId cachedLongId_ {};
|
||||
|
||||
struct crlNumberCmp {
|
||||
bool operator() (const std::shared_ptr<RevocationList>& lhs, const std::shared_ptr<RevocationList>& rhs) const {
|
||||
return lhs->getNumber() < rhs->getNumber();
|
||||
}
|
||||
};
|
||||
|
||||
std::set<std::shared_ptr<RevocationList>, crlNumberCmp> revocation_lists;
|
||||
};
|
||||
|
||||
struct OPENDHT_PUBLIC TrustList
|
||||
{
|
||||
struct VerifyResult {
|
||||
int ret;
|
||||
unsigned result;
|
||||
bool hasError() const { return ret < 0; }
|
||||
bool isValid() const { return !hasError() and !(result & GNUTLS_CERT_INVALID); }
|
||||
explicit operator bool() const { return isValid(); }
|
||||
std::string toString() const;
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const VerifyResult& h);
|
||||
};
|
||||
|
||||
TrustList();
|
||||
TrustList(TrustList&& o) noexcept : trust(std::move(o.trust)) {
|
||||
o.trust = nullptr;
|
||||
}
|
||||
TrustList& operator=(TrustList&& o) noexcept;
|
||||
~TrustList();
|
||||
void add(const Certificate& crt);
|
||||
void add(const RevocationList& crl);
|
||||
void remove(const Certificate& crt, bool parents = true);
|
||||
VerifyResult verify(const Certificate& crt) const;
|
||||
|
||||
private:
|
||||
TrustList(const TrustList& o) = delete;
|
||||
TrustList& operator=(const TrustList& o) = delete;
|
||||
gnutls_x509_trust_list_t trust {nullptr};
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class OPENDHT_PUBLIC secure_vector
|
||||
{
|
||||
public:
|
||||
secure_vector() {}
|
||||
secure_vector(secure_vector<T> const&) = default;
|
||||
secure_vector(secure_vector<T> &&) = default;
|
||||
explicit secure_vector(unsigned size): data_(size) {}
|
||||
explicit secure_vector(unsigned size, T _item): data_(size, _item) {}
|
||||
explicit secure_vector(const std::vector<T>& c): data_(c) {}
|
||||
secure_vector(std::vector<T>&& c): data_(std::move(c)) {}
|
||||
~secure_vector() { clean(); }
|
||||
|
||||
static secure_vector<T> getRandom(size_t size) {
|
||||
secure_vector<T> ret(size);
|
||||
crypto::random_device rdev;
|
||||
#ifdef _WIN32
|
||||
std::uniform_int_distribution<int> rand_byte{ 0, std::numeric_limits<uint8_t>::max() };
|
||||
#else
|
||||
std::uniform_int_distribution<uint8_t> rand_byte;
|
||||
#endif
|
||||
std::generate_n((uint8_t*)ret.data_.data(), ret.size()*sizeof(T), std::bind(rand_byte, std::ref(rdev)));
|
||||
return ret;
|
||||
}
|
||||
secure_vector<T>& operator=(const secure_vector<T>& c) {
|
||||
if (&c == this)
|
||||
return *this;
|
||||
clean();
|
||||
data_ = c.data_;
|
||||
return *this;
|
||||
}
|
||||
secure_vector<T>& operator=(secure_vector<T>&& c) {
|
||||
if (&c == this)
|
||||
return *this;
|
||||
clean();
|
||||
data_ = std::move(c.data_);
|
||||
return *this;
|
||||
}
|
||||
secure_vector<T>& operator=(std::vector<T>&& c) {
|
||||
clean();
|
||||
data_ = std::move(c);
|
||||
return *this;
|
||||
}
|
||||
std::vector<T>& writable() { clean(); return data_; }
|
||||
const std::vector<T>& makeInsecure() const { return data_; }
|
||||
const uint8_t* data() const { return data_.data(); }
|
||||
|
||||
void clean() {
|
||||
clean(data_.begin(), data_.end());
|
||||
}
|
||||
|
||||
void clear() { clean(); data_.clear(); }
|
||||
|
||||
size_t size() const { return data_.size(); }
|
||||
bool empty() const { return data_.empty(); }
|
||||
|
||||
void swap(secure_vector<T>& other) { data_.swap(other.data_); }
|
||||
void resize(size_t s) {
|
||||
if (s == data_.size()) return;
|
||||
if (s < data_.size()) {
|
||||
//shrink
|
||||
clean(data_.begin()+s, data_.end());
|
||||
data_.resize(s);
|
||||
} else {
|
||||
//grow
|
||||
auto data = std::move(data_); // move protected data
|
||||
clear();
|
||||
data_.resize(s);
|
||||
std::copy(data.begin(), data.end(), data_.begin());
|
||||
clean(data.begin(), data.end());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Securely wipe memory
|
||||
*/
|
||||
static void clean(const typename std::vector<T>::iterator& i, const typename std::vector<T>::iterator& j) {
|
||||
volatile uint8_t* b = reinterpret_cast<uint8_t*>(&*i);
|
||||
volatile uint8_t* e = reinterpret_cast<uint8_t*>(&*j);
|
||||
std::fill(b, e, 0);
|
||||
}
|
||||
|
||||
std::vector<T> data_;
|
||||
};
|
||||
|
||||
using SecureBlob = secure_vector<uint8_t>;
|
||||
|
||||
/**
|
||||
* Generate an RSA key pair (4096 bits) and a certificate.
|
||||
@@ -324,14 +740,25 @@ private:
|
||||
* If not set, the generated certificate will be a self-signed CA.
|
||||
* @param key_length stength of the generated private key (bits).
|
||||
*/
|
||||
Identity generateIdentity(const std::string& name, Identity ca, unsigned key_length, bool is_ca);
|
||||
Identity generateIdentity(const std::string& name = "dhtnode", Identity ca = {}, unsigned key_length = 4096);
|
||||
OPENDHT_PUBLIC Identity generateIdentity(const std::string& name, const Identity& ca, unsigned key_length, bool is_ca);
|
||||
OPENDHT_PUBLIC Identity generateIdentity(const std::string& name = "dhtnode", const Identity& ca = {}, unsigned key_length = 4096);
|
||||
|
||||
OPENDHT_PUBLIC Identity generateEcIdentity(const std::string& name, const Identity& ca, bool is_ca);
|
||||
OPENDHT_PUBLIC Identity generateEcIdentity(const std::string& name = "dhtnode", const Identity& ca = {});
|
||||
|
||||
OPENDHT_PUBLIC void saveIdentity(const Identity& id, const std::string& path, const std::string& privkey_password = {});
|
||||
|
||||
/**
|
||||
* SHA512
|
||||
* Performs SHA512, SHA256 or SHA1, depending on hash_length.
|
||||
* Attempts to choose an hash function with
|
||||
* output size of at least hash_length bytes, Current implementation
|
||||
* will use SHA1 for hash_length up to 20 bytes,
|
||||
* will use SHA256 for hash_length up to 32 bytes,
|
||||
* will use SHA512 for hash_length of 33 bytes and more.
|
||||
*/
|
||||
Blob hash(const Blob& data);
|
||||
OPENDHT_PUBLIC Blob hash(const Blob& data, size_t hash_length = 512/8);
|
||||
|
||||
OPENDHT_PUBLIC void hash(const uint8_t* data, size_t data_length, uint8_t* hash, size_t hash_length);
|
||||
|
||||
/**
|
||||
* Generates an encryption key from a text password,
|
||||
@@ -340,19 +767,24 @@ Blob hash(const Blob& data);
|
||||
* that can be transmitted in clear, and will be generated if
|
||||
* not provided (32 bytes).
|
||||
*/
|
||||
Blob stretchKey(const std::string& password, Blob& salt);
|
||||
OPENDHT_PUBLIC Blob stretchKey(const std::string& password, Blob& salt, size_t key_length = 512/8);
|
||||
|
||||
/**
|
||||
* AES-GCM encryption. Key must be 128, 192 or 256 bits long (16, 24 or 32 bytes).
|
||||
*/
|
||||
Blob aesEncrypt(const Blob& data, const Blob& key);
|
||||
Blob aesEncrypt(const Blob& data, const std::string& password);
|
||||
OPENDHT_PUBLIC Blob aesEncrypt(const uint8_t* data, size_t data_length, const Blob& key);
|
||||
OPENDHT_PUBLIC inline Blob aesEncrypt(const Blob& data, const Blob& key) {
|
||||
return aesEncrypt(data.data(), data.size(), key);
|
||||
}
|
||||
OPENDHT_PUBLIC Blob aesEncrypt(const Blob& data, const std::string& password);
|
||||
|
||||
/**
|
||||
* AES-GCM decryption.
|
||||
*/
|
||||
Blob aesDecrypt(const Blob& data, const Blob& key);
|
||||
Blob aesDecrypt(const Blob& data, const std::string& password);
|
||||
OPENDHT_PUBLIC Blob aesDecrypt(const uint8_t* data, size_t data_length, const Blob& key);
|
||||
OPENDHT_PUBLIC inline Blob aesDecrypt(const Blob& data, const Blob& key) { return aesDecrypt(data.data(), data.size(), key); }
|
||||
OPENDHT_PUBLIC Blob aesDecrypt(const uint8_t* data, size_t data_length, const std::string& password);
|
||||
OPENDHT_PUBLIC inline Blob aesDecrypt(const Blob& data, const std::string& password) { return aesDecrypt(data.data(), data.size(), password); }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
// Generic helper definitions for shared library support
|
||||
#if defined _WIN32 || defined __CYGWIN__
|
||||
#define OPENDHT_IMPORT __declspec(dllimport)
|
||||
#define OPENDHT_EXPORT __declspec(dllexport)
|
||||
#define OPENDHT_HIDDEN
|
||||
#else
|
||||
#define OPENDHT_IMPORT __attribute__ ((visibility ("default")))
|
||||
#define OPENDHT_EXPORT __attribute__ ((visibility ("default")))
|
||||
#define OPENDHT_HIDDEN __attribute__ ((visibility ("hidden")))
|
||||
#endif
|
||||
|
||||
// Now we use the generic helper definitions above to define OPENDHT_PUBLIC and OPENDHT_LOCAL.
|
||||
// OPENDHT_PUBLIC is used for the public API symbols. It either DLL imports or DLL exports (or does nothing for static build)
|
||||
// OPENDHT_LOCAL is used for non-api symbols.
|
||||
|
||||
#ifdef opendht_EXPORTS // defined if OpenDHT is compiled as a shared library
|
||||
#ifdef OPENDHT_BUILD // defined if we are building the OpenDHT shared library (instead of using it)
|
||||
#define OPENDHT_PUBLIC OPENDHT_EXPORT
|
||||
#else
|
||||
#define OPENDHT_PUBLIC OPENDHT_IMPORT
|
||||
#endif // OPENDHT_BUILD
|
||||
#define OPENDHT_LOCAL OPENDHT_HIDDEN
|
||||
#else // opendht_EXPORTS is not defined: this means OpenDHT is a static lib.
|
||||
#define OPENDHT_PUBLIC
|
||||
#define OPENDHT_LOCAL
|
||||
#endif // opendht_EXPORTS
|
||||
|
||||
|
||||
#ifdef opendht_c_EXPORTS // defined if OpenDHT is compiled as a shared library
|
||||
#ifdef OPENDHT_C_BUILD // defined if we are building the OpenDHT shared library (instead of using it)
|
||||
#define OPENDHT_C_PUBLIC OPENDHT_EXPORT
|
||||
#else
|
||||
#define OPENDHT_C_PUBLIC OPENDHT_IMPORT
|
||||
#endif // OPENDHT_BUILD
|
||||
#define OPENDHT_C_LOCAL OPENDHT_HIDDEN
|
||||
#else // opendht_EXPORTS is not defined: this means OpenDHT is a static lib.
|
||||
#define OPENDHT_C_PUBLIC
|
||||
#define OPENDHT_C_LOCAL
|
||||
#endif // opendht_EXPORTS
|
||||
|
||||
// bytes
|
||||
#define HASH_LEN 20u
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -13,13 +13,13 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "value.h"
|
||||
#include "sockaddr.h"
|
||||
|
||||
namespace dht {
|
||||
enum class ImStatus : uint8_t {
|
||||
@@ -29,16 +29,16 @@ enum class ImStatus : uint8_t {
|
||||
READ
|
||||
};
|
||||
}
|
||||
MSGPACK_ADD_ENUM(dht::ImStatus);
|
||||
MSGPACK_ADD_ENUM(dht::ImStatus)
|
||||
|
||||
namespace dht {
|
||||
|
||||
class DhtMessage : public Value::Serializable<DhtMessage>
|
||||
class OPENDHT_PUBLIC DhtMessage : public Value::Serializable<DhtMessage>
|
||||
{
|
||||
public:
|
||||
static const ValueType TYPE;
|
||||
|
||||
DhtMessage(std::string s = {}, Blob msg = {}) : service(s), data(msg) {}
|
||||
DhtMessage(const std::string& s = {}, const Blob& msg = {}) : service(s), data(msg) {}
|
||||
|
||||
std::string getService() const {
|
||||
return service;
|
||||
@@ -46,28 +46,30 @@ public:
|
||||
|
||||
static Value::Filter getFilter() { return {}; }
|
||||
|
||||
static bool storePolicy(InfoHash key, std::shared_ptr<Value>& value, InfoHash from, const sockaddr* from_addr, socklen_t from_len);
|
||||
static bool storePolicy(InfoHash key, std::shared_ptr<Value>& value, const InfoHash& from, const SockAddr&);
|
||||
|
||||
static Value::Filter ServiceFilter(std::string s);
|
||||
static Value::Filter ServiceFilter(const std::string& s);
|
||||
|
||||
/** print value for debugging */
|
||||
friend std::ostream& operator<< (std::ostream&, const DhtMessage&);
|
||||
|
||||
std::string service;
|
||||
Blob data;
|
||||
MSGPACK_DEFINE(service, data);
|
||||
MSGPACK_DEFINE(service, data)
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class SignedValue : public Value::Serializable<T>
|
||||
class OPENDHT_PUBLIC SignedValue : public Value::Serializable<T>
|
||||
{
|
||||
private:
|
||||
using BaseClass = Value::Serializable<T>;
|
||||
|
||||
public:
|
||||
virtual void unpackValue(const Value& v) override {
|
||||
if (v.owner)
|
||||
if (v.owner) {
|
||||
owner = v.owner;
|
||||
from = v.owner->getId();
|
||||
}
|
||||
BaseClass::unpackValue(v);
|
||||
}
|
||||
|
||||
@@ -75,11 +77,12 @@ public:
|
||||
return [](const Value& v){ return v.isSigned(); };
|
||||
}
|
||||
|
||||
Sp<crypto::PublicKey> owner;
|
||||
dht::InfoHash from;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class EncryptedValue : public SignedValue<T>
|
||||
class OPENDHT_PUBLIC EncryptedValue : public SignedValue<T>
|
||||
{
|
||||
public:
|
||||
using BaseClass = SignedValue<T>;
|
||||
@@ -93,7 +96,7 @@ public:
|
||||
static Value::Filter getFilter() {
|
||||
return Value::Filter::chain(
|
||||
BaseClass::getFilter(),
|
||||
[](const Value& v){ return v.recipient != InfoHash(); }
|
||||
[](const Value& v) { return static_cast<bool>(v.recipient); }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -103,7 +106,7 @@ public:
|
||||
|
||||
|
||||
|
||||
class ImMessage : public SignedValue<ImMessage>
|
||||
class OPENDHT_PUBLIC ImMessage : public SignedValue<ImMessage>
|
||||
{
|
||||
private:
|
||||
using BaseClass = SignedValue<ImMessage>;
|
||||
@@ -114,6 +117,10 @@ public:
|
||||
ImMessage() {}
|
||||
ImMessage(dht::Value::Id id, std::string&& m, long d = 0)
|
||||
: id(id), msg(std::move(m)), date(d) {}
|
||||
ImMessage(dht::Value::Id id, std::string &&dt, std::string &&m, long d = 0)
|
||||
: id(id), msg(std::move(m)), datatype(std::move(dt)), date(d) {}
|
||||
ImMessage(dht::Value::Id id, std::string &&dt, std::string &&m, std::map<std::string, std::string> &&md, long d = 0)
|
||||
: id(id), msg(std::move(m)), datatype(std::move(dt)), metadatas(std::move(md)), date(d) {}
|
||||
|
||||
virtual void unpackValue(const Value& v) override {
|
||||
to = v.recipient;
|
||||
@@ -121,15 +128,17 @@ public:
|
||||
}
|
||||
|
||||
dht::InfoHash to;
|
||||
dht::Value::Id id;
|
||||
dht::Value::Id id {0};
|
||||
std::string msg;
|
||||
std::string datatype;
|
||||
std::map<std::string, std::string> metadatas;
|
||||
long date {0};
|
||||
ImStatus status {ImStatus::NONE};
|
||||
|
||||
MSGPACK_DEFINE_MAP(id, msg, date, status);
|
||||
MSGPACK_DEFINE_MAP(id, msg, date, status, datatype, metadatas)
|
||||
};
|
||||
|
||||
class TrustRequest : public EncryptedValue<TrustRequest>
|
||||
class OPENDHT_PUBLIC TrustRequest : public EncryptedValue<TrustRequest>
|
||||
{
|
||||
private:
|
||||
using BaseClass = EncryptedValue<TrustRequest>;
|
||||
@@ -138,19 +147,21 @@ public:
|
||||
static const ValueType TYPE;
|
||||
|
||||
TrustRequest() {}
|
||||
TrustRequest(std::string s) : service(s) {}
|
||||
TrustRequest(std::string s, const Blob& d) : service(s), payload(d) {}
|
||||
TrustRequest(std::string s, std::string ci = {}) : service(s), conversationId(ci) {}
|
||||
TrustRequest(std::string s, std::string ci, const Blob& d) : service(s), conversationId(ci), payload(d) {}
|
||||
|
||||
static Value::Filter getFilter() {
|
||||
return EncryptedValue::getFilter();
|
||||
}
|
||||
|
||||
std::string service;
|
||||
std::string conversationId;
|
||||
Blob payload;
|
||||
MSGPACK_DEFINE(service, payload);
|
||||
bool confirm {false};
|
||||
MSGPACK_DEFINE_MAP(service, conversationId, payload, confirm)
|
||||
};
|
||||
|
||||
class IceCandidates : public EncryptedValue<IceCandidates>
|
||||
class OPENDHT_PUBLIC IceCandidates : public EncryptedValue<IceCandidates>
|
||||
{
|
||||
private:
|
||||
using BaseClass = EncryptedValue<IceCandidates>;
|
||||
@@ -175,14 +186,14 @@ public:
|
||||
pk.pack_bin_body((const char*)ice_data.data(), ice_data.size());
|
||||
#else
|
||||
// hack for backward compatibility with old opendht compiled with msgpack 1.0
|
||||
// remove when enough people have moved to new versions
|
||||
// remove when enough people have moved to new versions
|
||||
pk.pack_array(ice_data.size());
|
||||
for (uint8_t b : ice_data)
|
||||
pk.pack(b);
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual void msgpack_unpack(msgpack::object o)
|
||||
virtual void msgpack_unpack(const msgpack::object& o)
|
||||
{
|
||||
if (o.type != msgpack::type::ARRAY) throw msgpack::type_error();
|
||||
if (o.via.array.size < 2) throw msgpack::type_error();
|
||||
@@ -190,13 +201,13 @@ public:
|
||||
ice_data = unpackBlob(o.via.array.ptr[1]);
|
||||
}
|
||||
|
||||
Value::Id id;
|
||||
Value::Id id {0};
|
||||
Blob ice_data;
|
||||
};
|
||||
|
||||
/* "Peer" announcement
|
||||
*/
|
||||
class IpServiceAnnouncement : public Value::Serializable<IpServiceAnnouncement>
|
||||
class OPENDHT_PUBLIC IpServiceAnnouncement : public Value::Serializable<IpServiceAnnouncement>
|
||||
{
|
||||
private:
|
||||
using BaseClass = Value::Serializable<IpServiceAnnouncement>;
|
||||
@@ -204,15 +215,12 @@ private:
|
||||
public:
|
||||
static const ValueType TYPE;
|
||||
|
||||
IpServiceAnnouncement(in_port_t p = 0) {
|
||||
ss.ss_family = 0;
|
||||
setPort(p);
|
||||
IpServiceAnnouncement(sa_family_t family = AF_UNSPEC, in_port_t p = 0) {
|
||||
addr.setFamily(family);
|
||||
addr.setPort(p);
|
||||
}
|
||||
|
||||
IpServiceAnnouncement(const sockaddr* sa, socklen_t sa_len) {
|
||||
if (sa)
|
||||
std::copy_n((const uint8_t*)sa, sa_len, (uint8_t*)&ss);
|
||||
}
|
||||
IpServiceAnnouncement(const SockAddr& sa) : addr(sa) {}
|
||||
|
||||
IpServiceAnnouncement(const Blob& b) {
|
||||
msgpack_unpack(unpackMsg(b).get());
|
||||
@@ -221,58 +229,45 @@ public:
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& pk) const
|
||||
{
|
||||
pk.pack_array(2);
|
||||
pk.pack(getPort());
|
||||
if (ss.ss_family == AF_INET) {
|
||||
pk.pack_bin(sizeof(in_addr));
|
||||
pk.pack_bin_body((const char*)&reinterpret_cast<const sockaddr_in*>(&ss)->sin_addr, sizeof(in_addr));
|
||||
} else if (ss.ss_family == AF_INET6) {
|
||||
pk.pack_bin(sizeof(in6_addr));
|
||||
pk.pack_bin_body((const char*)&reinterpret_cast<const sockaddr_in6*>(&ss)->sin6_addr, sizeof(in6_addr));
|
||||
}
|
||||
pk.pack_bin(addr.getLength());
|
||||
pk.pack_bin_body((const char*)addr.get(), addr.getLength());
|
||||
}
|
||||
|
||||
virtual void msgpack_unpack(msgpack::object o)
|
||||
virtual void msgpack_unpack(const msgpack::object& o)
|
||||
{
|
||||
if (o.type != msgpack::type::ARRAY) throw msgpack::type_error();
|
||||
if (o.via.array.size < 2) throw msgpack::type_error();
|
||||
setPort(o.via.array.ptr[0].as<in_port_t>());
|
||||
auto ip_dat = o.via.array.ptr[1].as<Blob>();
|
||||
if (ip_dat.size() == sizeof(in_addr))
|
||||
std::copy(ip_dat.begin(), ip_dat.end(), (char*)&reinterpret_cast<sockaddr_in*>(&ss)->sin_addr);
|
||||
else if (ip_dat.size() == sizeof(in6_addr))
|
||||
std::copy(ip_dat.begin(), ip_dat.end(), (char*)&reinterpret_cast<sockaddr_in6*>(&ss)->sin6_addr);
|
||||
if (o.type == msgpack::type::BIN)
|
||||
addr = {(sockaddr*)o.via.bin.ptr, (socklen_t)o.via.bin.size};
|
||||
else
|
||||
throw msgpack::type_error();
|
||||
}
|
||||
|
||||
in_port_t getPort() const {
|
||||
return ntohs(reinterpret_cast<const sockaddr_in*>(&ss)->sin_port);
|
||||
return addr.getPort();
|
||||
}
|
||||
void setPort(in_port_t p) {
|
||||
reinterpret_cast<sockaddr_in*>(&ss)->sin_port = htons(p);
|
||||
addr.setPort(p);
|
||||
}
|
||||
|
||||
sockaddr_storage getPeerAddr() const {
|
||||
return ss;
|
||||
const SockAddr& getPeerAddr() const {
|
||||
return addr;
|
||||
}
|
||||
|
||||
virtual const ValueType& getType() const {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
static bool storePolicy(InfoHash, std::shared_ptr<Value>&, InfoHash, const sockaddr*, socklen_t);
|
||||
static bool storePolicy(InfoHash, std::shared_ptr<Value>&, const InfoHash&, const SockAddr&);
|
||||
|
||||
/** print value for debugging */
|
||||
friend std::ostream& operator<< (std::ostream&, const IpServiceAnnouncement&);
|
||||
|
||||
private:
|
||||
sockaddr_storage ss;
|
||||
SockAddr addr;
|
||||
};
|
||||
|
||||
|
||||
extern const std::array<std::reference_wrapper<const ValueType>, 5> DEFAULT_TYPES;
|
||||
OPENDHT_PUBLIC extern const std::array<std::reference_wrapper<const ValueType>, 5> DEFAULT_TYPES;
|
||||
|
||||
extern const std::array<std::reference_wrapper<const ValueType>, 1> DEFAULT_INSECURE_TYPES;
|
||||
OPENDHT_PUBLIC extern const std::array<std::reference_wrapper<const ValueType>, 1> DEFAULT_INSECURE_TYPES;
|
||||
|
||||
}
|
||||
+334
-225
@@ -1,7 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -14,11 +15,9 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "infohash.h"
|
||||
@@ -28,15 +27,13 @@
|
||||
#include "scheduler.h"
|
||||
#include "routing_table.h"
|
||||
#include "callbacks.h"
|
||||
#include "dht_interface.h"
|
||||
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <queue>
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -45,7 +42,15 @@
|
||||
|
||||
namespace dht {
|
||||
|
||||
namespace net {
|
||||
struct Request;
|
||||
} /* namespace net */
|
||||
|
||||
struct Storage;
|
||||
struct ValueStorage;
|
||||
class StorageBucket;
|
||||
struct Listener;
|
||||
struct LocalListener;
|
||||
|
||||
/**
|
||||
* Main Dht class.
|
||||
@@ -54,42 +59,43 @@ struct Request;
|
||||
* Must be given open UDP sockets and ::periodic must be
|
||||
* called regularly.
|
||||
*/
|
||||
class Dht {
|
||||
class OPENDHT_PUBLIC Dht final : public DhtInterface {
|
||||
public:
|
||||
|
||||
// [[deprecated]]
|
||||
using NodeExport = dht::NodeExport;
|
||||
|
||||
// [[deprecated]]
|
||||
using Status = NodeStatus;
|
||||
|
||||
Dht();
|
||||
|
||||
/**
|
||||
* Initialise the Dht with two open sockets (for IPv4 and IP6)
|
||||
* and an ID for the node.
|
||||
*/
|
||||
Dht(int s, int s6, Config config);
|
||||
Dht(std::unique_ptr<net::DatagramSocket>&& sock, const Config& config, const Sp<Logger>& l = {});
|
||||
|
||||
Dht(std::unique_ptr<net::DatagramSocket>&& sock, const Config& config, const Logger& l = {})
|
||||
: Dht(std::move(sock), config, std::make_shared<Logger>(l)) {}
|
||||
|
||||
virtual ~Dht();
|
||||
|
||||
/**
|
||||
* Get the ID of the node.
|
||||
*/
|
||||
inline const InfoHash& getNodeId() const { return myid; }
|
||||
inline const InfoHash& getNodeId() const override { return myid; }
|
||||
|
||||
NodeStatus updateStatus(sa_family_t af) override;
|
||||
|
||||
/**
|
||||
* Get the current status of the node for the given family.
|
||||
*/
|
||||
NodeStatus getStatus(sa_family_t af) const;
|
||||
NodeStatus getStatus(sa_family_t af) const override {
|
||||
return dht(af).status;
|
||||
}
|
||||
|
||||
NodeStatus getStatus() const {
|
||||
NodeStatus getStatus() const override {
|
||||
return std::max(getStatus(AF_INET), getStatus(AF_INET6));
|
||||
}
|
||||
|
||||
net::DatagramSocket* getSocket() const override { return network_engine.getSocket(); };
|
||||
|
||||
/**
|
||||
* Performs final operations before quitting.
|
||||
*/
|
||||
void shutdown(ShutdownCallback cb);
|
||||
void shutdown(ShutdownCallback cb, bool stop = false) override;
|
||||
|
||||
/**
|
||||
* Returns true if the node is running (have access to an open socket).
|
||||
@@ -97,19 +103,22 @@ public:
|
||||
* af: address family. If non-zero, will return true if the node
|
||||
* is running for the provided family.
|
||||
*/
|
||||
bool isRunning(sa_family_t af = 0) const;
|
||||
bool isRunning(sa_family_t af = 0) const override;
|
||||
|
||||
/**
|
||||
* Enable or disable logging of DHT internal messages
|
||||
*/
|
||||
void setLoggers(LogMethod error = NOLOG, LogMethod warn = NOLOG, LogMethod debug = NOLOG);
|
||||
|
||||
virtual void registerType(const ValueType& type) {
|
||||
types[type.id] = type;
|
||||
virtual void registerType(const ValueType& type) override {
|
||||
types.registerType(type);
|
||||
}
|
||||
const ValueType& getType(ValueType::Id type_id) const {
|
||||
const auto& t_it = types.find(type_id);
|
||||
return (t_it == types.end()) ? ValueType::USER_DATA : t_it->second;
|
||||
const ValueType& getType(ValueType::Id type_id) const override {
|
||||
return types.getType(type_id);
|
||||
}
|
||||
|
||||
void addBootstrap(const std::string& host, const std::string& service) override {
|
||||
bootstrap_nodes.emplace_back(host, service);
|
||||
startBootstrap();
|
||||
}
|
||||
|
||||
void clearBootstrap() override {
|
||||
bootstrap_nodes.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,14 +126,17 @@ public:
|
||||
* The node is not pinged, so this should be
|
||||
* used to bootstrap efficiently from previously known nodes.
|
||||
*/
|
||||
bool insertNode(const InfoHash& id, const sockaddr*, socklen_t);
|
||||
bool insertNode(const NodeExport& n) {
|
||||
return insertNode(n.id, reinterpret_cast<const sockaddr*>(&n.ss), n.sslen);
|
||||
void insertNode(const InfoHash& id, const SockAddr&) override;
|
||||
void insertNode(const NodeExport& n) override {
|
||||
insertNode(n.id, SockAddr(n.ss, n.sslen));
|
||||
}
|
||||
|
||||
int pingNode(const sockaddr*, socklen_t);
|
||||
void pingNode(SockAddr, DoneCallbackSimple&& cb={}) override;
|
||||
|
||||
time_point periodic(const uint8_t *buf, size_t buflen, const sockaddr *from, socklen_t fromlen);
|
||||
time_point periodic(const uint8_t *buf, size_t buflen, SockAddr, const time_point& now) override;
|
||||
time_point periodic(const uint8_t *buf, size_t buflen, const sockaddr* from, socklen_t fromlen, const time_point& now) override {
|
||||
return periodic(buf, buflen, SockAddr(from, fromlen), now);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a value by searching on all available protocols (IPv4, IPv6),
|
||||
@@ -133,50 +145,60 @@ public:
|
||||
* @param cb a function called when new values are found on the network.
|
||||
* It should return false to stop the operation.
|
||||
* @param donecb a function called when the operation is complete.
|
||||
cb and donecb won't be called again afterward.
|
||||
cb and donecb won't be called again afterward.
|
||||
* @param f a filter function used to prefilter values.
|
||||
*/
|
||||
virtual void get(const InfoHash& key, GetCallback cb, DoneCallback donecb={}, Value::Filter&& f={});
|
||||
virtual void get(const InfoHash& key, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter&& f={}) {
|
||||
get(key, cb, bindDoneCb(donecb), std::forward<Value::Filter>(f));
|
||||
virtual void get(const InfoHash& key, GetCallback cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {}) override;
|
||||
virtual void get(const InfoHash& key, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter&& f={}, Where&& w = {}) override {
|
||||
get(key, cb, bindDoneCb(donecb), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter&& f={}) {
|
||||
get(key, bindGetCb(cb), donecb, std::forward<Value::Filter>(f));
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {}) override {
|
||||
get(key, bindGetCb(cb), donecb, std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallbackSimple donecb, Value::Filter&& f={}) {
|
||||
get(key, bindGetCb(cb), bindDoneCb(donecb), std::forward<Value::Filter>(f));
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallbackSimple donecb, Value::Filter&& f={}, Where&& w = {}) override {
|
||||
get(key, bindGetCb(cb), bindDoneCb(donecb), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
/**
|
||||
* Similar to Dht::get, but sends a Query to filter data remotely.
|
||||
* @param key the key for which to query data for.
|
||||
* @param cb a function called when new values are found on the network.
|
||||
* It should return false to stop the operation.
|
||||
* @param done_cb a function called when the operation is complete.
|
||||
cb and done_cb won't be called again afterward.
|
||||
* @param q a query used to filter values on the remotes before they send a
|
||||
* response.
|
||||
*/
|
||||
virtual void query(const InfoHash& key, QueryCallback cb, DoneCallback done_cb = {}, Query&& q = {}) override;
|
||||
virtual void query(const InfoHash& key, QueryCallback cb, DoneCallbackSimple done_cb = {}, Query&& q = {}) override {
|
||||
query(key, cb, bindDoneCb(done_cb), std::forward<Query>(q));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get locally stored data for the given hash.
|
||||
*/
|
||||
std::vector<std::shared_ptr<Value>> getLocal(const InfoHash& key, Value::Filter f = Value::AllFilter()) const;
|
||||
std::vector<Sp<Value>> getLocal(const InfoHash& key, const Value::Filter& f = {}) const override;
|
||||
|
||||
/**
|
||||
* Get locally stored data for the given key and value id.
|
||||
*/
|
||||
std::shared_ptr<Value> getLocalById(const InfoHash& key, Value::Id vid) const;
|
||||
Sp<Value> getLocalById(const InfoHash& key, Value::Id vid) const override;
|
||||
|
||||
/**
|
||||
* Announce a value on all available protocols (IPv4, IPv6), and
|
||||
* automatically re-announce when it's about to expire.
|
||||
* Announce a value on all available protocols (IPv4, IPv6).
|
||||
*
|
||||
* The operation will start as soon as the node is connected to the network.
|
||||
* The done callback will be called once, when the first announce succeeds, or fails.
|
||||
*
|
||||
* A "put" operation will never end by itself because the value will need to be
|
||||
* reannounced on a regular basis.
|
||||
* User can call #cancelPut(InfoHash, Value::Id) to cancel a put operation.
|
||||
*/
|
||||
void put(const InfoHash& key,
|
||||
std::shared_ptr<Value>,
|
||||
Sp<Value>,
|
||||
DoneCallback cb=nullptr,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false);
|
||||
bool permanent = false) override;
|
||||
void put(const InfoHash& key,
|
||||
const std::shared_ptr<Value>& v,
|
||||
const Sp<Value>& v,
|
||||
DoneCallbackSimple cb,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false)
|
||||
bool permanent = false) override
|
||||
{
|
||||
put(key, v, bindDoneCb(cb), created, permanent);
|
||||
}
|
||||
@@ -185,7 +207,7 @@ public:
|
||||
Value&& v,
|
||||
DoneCallback cb=nullptr,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false)
|
||||
bool permanent = false) override
|
||||
{
|
||||
put(key, std::make_shared<Value>(std::move(v)), cb, created, permanent);
|
||||
}
|
||||
@@ -193,7 +215,7 @@ public:
|
||||
Value&& v,
|
||||
DoneCallbackSimple cb,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false)
|
||||
bool permanent = false) override
|
||||
{
|
||||
put(key, std::forward<Value>(v), bindDoneCb(cb), created, permanent);
|
||||
}
|
||||
@@ -201,18 +223,18 @@ public:
|
||||
/**
|
||||
* Get data currently being put at the given hash.
|
||||
*/
|
||||
std::vector<std::shared_ptr<Value>> getPut(const InfoHash&);
|
||||
std::vector<Sp<Value>> getPut(const InfoHash&) const override;
|
||||
|
||||
/**
|
||||
* Get data currently being put at the given hash with the given id.
|
||||
*/
|
||||
std::shared_ptr<Value> getPut(const InfoHash&, const Value::Id&);
|
||||
Sp<Value> getPut(const InfoHash&, const Value::Id&) const override;
|
||||
|
||||
/**
|
||||
* Stop any put/announce operation at the given location,
|
||||
* for the value with the given id.
|
||||
*/
|
||||
bool cancelPut(const InfoHash&, const Value::Id&);
|
||||
bool cancelPut(const InfoHash&, const Value::Id&) override;
|
||||
|
||||
/**
|
||||
* Listen on the network for any changes involving a specified hash.
|
||||
@@ -221,59 +243,81 @@ public:
|
||||
*
|
||||
* @return a token to cancel the listener later.
|
||||
*/
|
||||
virtual size_t listen(const InfoHash&, GetCallback, Value::Filter&&={});
|
||||
virtual size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}) {
|
||||
return listen(key, bindGetCb(cb), std::forward<Value::Filter>(f));
|
||||
size_t listen(const InfoHash&, ValueCallback, Value::Filter={}, Where={}) override;
|
||||
|
||||
size_t listen(const InfoHash& key, GetCallback cb, Value::Filter f={}, Where w={}) override {
|
||||
return listen(key, [cb](const std::vector<Sp<Value>>& vals, bool expired){
|
||||
if (not expired)
|
||||
return cb(vals);
|
||||
return true;
|
||||
}, std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}, Where w={}) override {
|
||||
return listen(key, bindGetCb(cb), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
|
||||
virtual bool cancelListen(const InfoHash&, size_t token);
|
||||
bool cancelListen(const InfoHash&, size_t token) override;
|
||||
|
||||
/**
|
||||
* Inform the DHT of lower-layer connectivity changes.
|
||||
* This will cause the DHT to assume a public IP address change.
|
||||
* The DHT will recontact neighbor nodes, re-register for listen ops etc.
|
||||
*/
|
||||
void connectivityChanged();
|
||||
void connectivityChanged(sa_family_t) override;
|
||||
void connectivityChanged() override {
|
||||
reported_addr.clear();
|
||||
connectivityChanged(AF_INET);
|
||||
connectivityChanged(AF_INET6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of good nodes for local storage saving purposes
|
||||
* The list is ordered to minimize the back-to-work delay.
|
||||
*/
|
||||
std::vector<NodeExport> exportNodes();
|
||||
std::vector<NodeExport> exportNodes() const override;
|
||||
|
||||
std::vector<ValuesExport> exportValues() const;
|
||||
void importValues(const std::vector<ValuesExport>&);
|
||||
std::vector<ValuesExport> exportValues() const override;
|
||||
void importValues(const std::vector<ValuesExport>&) override;
|
||||
|
||||
int getNodesStats(sa_family_t af, unsigned *good_return, unsigned *dubious_return, unsigned *cached_return,
|
||||
unsigned *incoming_return) const;
|
||||
std::string getStorageLog() const;
|
||||
std::string getRoutingTablesLog(sa_family_t) const;
|
||||
std::string getSearchesLog(sa_family_t) const;
|
||||
void saveState(const std::string& path) const;
|
||||
void loadState(const std::string& path);
|
||||
|
||||
void dumpTables() const;
|
||||
std::vector<unsigned> getNodeMessageStats(bool in = false) {
|
||||
NodeStats getNodesStats(sa_family_t af) const override;
|
||||
|
||||
std::string getStorageLog() const override;
|
||||
std::string getStorageLog(const InfoHash&) const override;
|
||||
|
||||
std::string getRoutingTablesLog(sa_family_t) const override;
|
||||
std::string getSearchesLog(sa_family_t) const override;
|
||||
std::string getSearchLog(const InfoHash&, sa_family_t af = AF_UNSPEC) const override;
|
||||
|
||||
void dumpTables() const override;
|
||||
std::vector<unsigned> getNodeMessageStats(bool in = false) override {
|
||||
return network_engine.getNodeMessageStats(in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the in-memory storage limit in bytes
|
||||
*/
|
||||
void setStorageLimit(size_t limit = DEFAULT_STORAGE_LIMIT) {
|
||||
void setStorageLimit(size_t limit = DEFAULT_STORAGE_LIMIT) override {
|
||||
max_store_size = limit;
|
||||
}
|
||||
size_t getStorageLimit() const override {
|
||||
return max_store_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total memory usage of stored values and the number
|
||||
* of stored values.
|
||||
*/
|
||||
std::pair<size_t, size_t> getStoreSize() const {
|
||||
std::pair<size_t, size_t> getStoreSize() const override {
|
||||
return {total_store_size, total_values};
|
||||
}
|
||||
|
||||
std::vector<Address> getPublicAddress(sa_family_t family = 0);
|
||||
std::vector<SockAddr> getPublicAddress(sa_family_t family = 0) override;
|
||||
|
||||
protected:
|
||||
Logger DHT_LOG;
|
||||
void pushNotificationReceived(const std::map<std::string, std::string>&) override {}
|
||||
void resubscribe(unsigned) {}
|
||||
|
||||
private:
|
||||
|
||||
@@ -282,20 +326,27 @@ private:
|
||||
the target 8 turn out to be dead. */
|
||||
static constexpr unsigned SEARCH_NODES {14};
|
||||
|
||||
/* Concurrent requests during a search */
|
||||
static constexpr unsigned SEARCH_REQUESTS {4};
|
||||
/* The number of bad nodes is limited in order to help determine
|
||||
* presence of connectivity changes. See
|
||||
* https://github.com/savoirfairelinux/opendht/issues/137 for details.
|
||||
*
|
||||
* According to the tables, 25 is a good average value for big networks. If
|
||||
* the network is small, normal search expiration process will handle the
|
||||
* situation.
|
||||
* */
|
||||
static constexpr unsigned SEARCH_MAX_BAD_NODES {25};
|
||||
|
||||
/* Concurrent search nodes requested count */
|
||||
static constexpr unsigned MAX_REQUESTED_SEARCH_NODES {4};
|
||||
|
||||
/* Number of listening nodes */
|
||||
static constexpr unsigned LISTEN_NODES {4};
|
||||
|
||||
/* The maximum number of values we store for a given hash. */
|
||||
static constexpr unsigned MAX_VALUES {1024};
|
||||
|
||||
/* The maximum number of hashes we're willing to track. */
|
||||
static constexpr unsigned MAX_HASHES {16384};
|
||||
static constexpr unsigned MAX_HASHES {1024 * 1024 * 1024};
|
||||
|
||||
/* The maximum number of searches we keep data about. */
|
||||
static constexpr unsigned MAX_SEARCHES {2048};
|
||||
static constexpr unsigned MAX_SEARCHES {1024 * 1024};
|
||||
|
||||
static constexpr std::chrono::minutes MAX_STORAGE_MAINTENANCE_EXPIRE_TIME {10};
|
||||
|
||||
@@ -303,220 +354,278 @@ private:
|
||||
static constexpr std::chrono::minutes SEARCH_EXPIRE_TIME {62};
|
||||
|
||||
/* Timeout for listen */
|
||||
static constexpr std::chrono::seconds LISTEN_EXPIRE_TIME {30};
|
||||
static constexpr duration LISTEN_EXPIRE_TIME {std::chrono::seconds(30)};
|
||||
static constexpr duration LISTEN_EXPIRE_TIME_PUBLIC {std::chrono::minutes(5)};
|
||||
|
||||
static constexpr std::chrono::seconds REANNOUNCE_MARGIN {5};
|
||||
static constexpr duration REANNOUNCE_MARGIN {std::chrono::seconds(10)};
|
||||
|
||||
static constexpr size_t TOKEN_SIZE {64};
|
||||
static constexpr std::chrono::seconds BOOTSTRAP_PERIOD {10};
|
||||
|
||||
static constexpr size_t TOKEN_SIZE {32};
|
||||
|
||||
// internal structures
|
||||
struct SearchNode;
|
||||
|
||||
/**
|
||||
* A single "get" operation data
|
||||
*/
|
||||
struct Get {
|
||||
time_point start;
|
||||
Value::Filter filter;
|
||||
GetCallback get_cb;
|
||||
DoneCallback done_cb;
|
||||
};
|
||||
|
||||
/**
|
||||
* A single "put" operation data
|
||||
*/
|
||||
struct Announce {
|
||||
bool permanent;
|
||||
std::shared_ptr<Value> value;
|
||||
time_point created;
|
||||
DoneCallback callback;
|
||||
};
|
||||
|
||||
/**
|
||||
* A single "listen" operation data
|
||||
*/
|
||||
struct LocalListener {
|
||||
Value::Filter filter;
|
||||
GetCallback get_cb;
|
||||
};
|
||||
|
||||
/**
|
||||
* A search is a list of the nodes we think are responsible
|
||||
* for storing values for a given hash.
|
||||
*/
|
||||
struct Get;
|
||||
struct Announce;
|
||||
struct Search;
|
||||
|
||||
struct ValueStorage {
|
||||
std::shared_ptr<Value> data {};
|
||||
time_point time {};
|
||||
|
||||
ValueStorage() {}
|
||||
ValueStorage(const std::shared_ptr<Value>& v, time_point t) : data(v), time(t) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Foreign nodes asking for updates about an InfoHash.
|
||||
*/
|
||||
struct Listener {
|
||||
size_t rid {};
|
||||
time_point time {};
|
||||
|
||||
/*constexpr*/ Listener(size_t rid, time_point t) : rid(rid), time(t) {}
|
||||
|
||||
void refresh(size_t tid, time_point t) {
|
||||
rid = tid;
|
||||
time = t;
|
||||
}
|
||||
};
|
||||
|
||||
struct Storage;
|
||||
|
||||
// prevent copy
|
||||
Dht(const Dht&) = delete;
|
||||
Dht& operator=(const Dht&) = delete;
|
||||
|
||||
std::mt19937_64 rd {crypto::getSeededRandomEngine<std::mt19937_64>()};
|
||||
|
||||
InfoHash myid {};
|
||||
|
||||
std::array<uint8_t, 8> secret {{}};
|
||||
std::array<uint8_t, 8> oldsecret {{}};
|
||||
uint64_t secret {};
|
||||
uint64_t oldsecret {};
|
||||
|
||||
// registred types
|
||||
std::map<ValueType::Id, ValueType> types;
|
||||
TypeStore types;
|
||||
|
||||
// are we a bootstrap node ?
|
||||
// note: Any running node can be used as a bootstrap node.
|
||||
// Only nodes running only as bootstrap nodes should
|
||||
// be put in bootstrap mode.
|
||||
const bool is_bootstrap {false};
|
||||
using SearchMap = std::map<InfoHash, Sp<Search>>;
|
||||
struct Kad {
|
||||
RoutingTable buckets {};
|
||||
SearchMap searches {};
|
||||
unsigned pending_pings {0};
|
||||
NodeStatus status;
|
||||
|
||||
// the stuff
|
||||
RoutingTable buckets {};
|
||||
RoutingTable buckets6 {};
|
||||
NodeStatus getStatus(time_point now) const;
|
||||
NodeStats getNodesStats(time_point now, const InfoHash& myid) const;
|
||||
};
|
||||
|
||||
std::vector<Storage> store;
|
||||
Kad dht4 {};
|
||||
Kad dht6 {};
|
||||
|
||||
std::vector<std::pair<std::string,std::string>> bootstrap_nodes {};
|
||||
std::chrono::steady_clock::duration bootstrap_period {BOOTSTRAP_PERIOD};
|
||||
Sp<Scheduler::Job> bootstrapJob {};
|
||||
|
||||
std::map<InfoHash, Storage> store;
|
||||
std::map<SockAddr, StorageBucket, SockAddr::ipCmp> store_quota;
|
||||
size_t total_values {0};
|
||||
size_t total_store_size {0};
|
||||
size_t max_store_keys {MAX_HASHES};
|
||||
size_t max_store_size {DEFAULT_STORAGE_LIMIT};
|
||||
|
||||
std::map<InfoHash, std::shared_ptr<Search>> searches4 {};
|
||||
std::map<InfoHash, std::shared_ptr<Search>> searches6 {};
|
||||
uint16_t search_id {0};
|
||||
size_t max_searches {MAX_SEARCHES};
|
||||
size_t search_id {0};
|
||||
|
||||
// map a global listen token to IPv4, IPv6 specific listen tokens.
|
||||
// 0 is the invalid token.
|
||||
std::map<size_t, std::tuple<size_t, size_t, size_t>> listeners {};
|
||||
size_t listener_token {1};
|
||||
|
||||
|
||||
// timing
|
||||
Scheduler scheduler {};
|
||||
std::shared_ptr<Scheduler::Job> nextNodesConfirmation {};
|
||||
time_point mybucket_grow_time {time_point::min()}, mybucket6_grow_time {time_point::min()};
|
||||
Scheduler scheduler;
|
||||
Sp<Scheduler::Job> nextNodesConfirmation {};
|
||||
Sp<Scheduler::Job> nextStorageMaintenance {};
|
||||
|
||||
NetworkEngine network_engine;
|
||||
|
||||
using ReportedAddr = std::pair<unsigned, Address>;
|
||||
net::NetworkEngine network_engine;
|
||||
using ReportedAddr = std::pair<unsigned, SockAddr>;
|
||||
std::vector<ReportedAddr> reported_addr;
|
||||
|
||||
std::string persistPath;
|
||||
|
||||
// are we a bootstrap node ?
|
||||
// note: Any running node can be used as a bootstrap node.
|
||||
// Only nodes running only as bootstrap nodes should
|
||||
// be put in bootstrap mode.
|
||||
const bool is_bootstrap {false};
|
||||
const bool maintain_storage {false};
|
||||
const bool public_stable {false};
|
||||
|
||||
inline const duration& getListenExpiration() const {
|
||||
return public_stable ? LISTEN_EXPIRE_TIME_PUBLIC : LISTEN_EXPIRE_TIME;
|
||||
}
|
||||
|
||||
void rotateSecrets();
|
||||
|
||||
Blob makeToken(const sockaddr *sa, bool old) const;
|
||||
bool tokenMatch(const Blob& token, const sockaddr *sa) const;
|
||||
Blob makeToken(const SockAddr&, bool old) const;
|
||||
bool tokenMatch(const Blob& token, const SockAddr&) const;
|
||||
|
||||
void reportedAddr(const sockaddr *sa, socklen_t sa_len);
|
||||
void reportedAddr(const SockAddr&);
|
||||
|
||||
// Storage
|
||||
decltype(Dht::store)::iterator findStorage(const InfoHash& id);
|
||||
decltype(Dht::store)::const_iterator findStorage(const InfoHash& id) const;
|
||||
void storageAddListener(const InfoHash& id, const Sp<Node>& node, size_t tid, Query&& = {}, int version = 0);
|
||||
bool storageStore(const InfoHash& id, const Sp<Value>& value, time_point created, const SockAddr& sa = {}, bool permanent = false);
|
||||
bool storageRefresh(const InfoHash& id, Value::Id vid);
|
||||
void expireStore();
|
||||
void expireStorage(InfoHash h);
|
||||
void expireStore(decltype(store)::iterator);
|
||||
|
||||
void storageAddListener(const InfoHash& id, const std::shared_ptr<Node>& node, size_t tid);
|
||||
bool storageStore(const InfoHash& id, const std::shared_ptr<Value>& value, time_point created);
|
||||
void expireStorage();
|
||||
void storageChanged(Storage& st, ValueStorage&);
|
||||
void storageRemoved(const InfoHash& id, Storage& st, const std::vector<Sp<Value>>& values, size_t totalSize);
|
||||
void storageChanged(const InfoHash& id, Storage& st, const Sp<Value>&, bool newValue);
|
||||
std::string printStorageLog(const decltype(store)::value_type&) const;
|
||||
|
||||
/**
|
||||
* For a given storage, if values don't belong there anymore because this
|
||||
* node is too far from the target, values are sent to the appropriate
|
||||
* nodes.
|
||||
*/
|
||||
void dataPersistence();
|
||||
size_t maintainStorage(InfoHash id, bool force=false, DoneCallback donecb=nullptr);
|
||||
void dataPersistence(InfoHash id);
|
||||
size_t maintainStorage(decltype(store)::value_type&, bool force=false, const DoneCallback& donecb={});
|
||||
|
||||
// Buckets
|
||||
Kad& dht(sa_family_t af) { return af == AF_INET ? dht4 : dht6; }
|
||||
const Kad& dht(sa_family_t af) const { return af == AF_INET ? dht4 : dht6; }
|
||||
RoutingTable& buckets(sa_family_t af) { return dht(af).buckets; }
|
||||
const RoutingTable& buckets(sa_family_t af) const { return dht(af).buckets; }
|
||||
Bucket* findBucket(const InfoHash& id, sa_family_t af) {
|
||||
RoutingTable::iterator b;
|
||||
switch (af) {
|
||||
case AF_INET:
|
||||
b = buckets.findBucket(id);
|
||||
return b == buckets.end() ? nullptr : &(*b);
|
||||
case AF_INET6:
|
||||
b = buckets6.findBucket(id);
|
||||
return b == buckets6.end() ? nullptr : &(*b);
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
auto& b = buckets(af);
|
||||
auto it = b.findBucket(id);
|
||||
return it == b.end() ? nullptr : &(*it);
|
||||
}
|
||||
const Bucket* findBucket(const InfoHash& id, sa_family_t af) const {
|
||||
return const_cast<Dht*>(this)->findBucket(id, af);
|
||||
}
|
||||
|
||||
void expireBuckets(RoutingTable&);
|
||||
int sendCachedPing(Bucket& b);
|
||||
void sendCachedPing(Bucket& b);
|
||||
bool bucketMaintenance(RoutingTable&);
|
||||
void dumpBucket(const Bucket& b, std::ostream& out) const;
|
||||
void bootstrap();
|
||||
void startBootstrap();
|
||||
void stopBootstrap();
|
||||
|
||||
// Nodes
|
||||
void onNewNode(const std::shared_ptr<Node>& node, int confirm);
|
||||
std::shared_ptr<Node> findNode(const InfoHash& id, sa_family_t af);
|
||||
const std::shared_ptr<Node> findNode(const InfoHash& id, sa_family_t af) const;
|
||||
bool trySearchInsert(const std::shared_ptr<Node>& node);
|
||||
void onNewNode(const Sp<Node>& node, int confirm);
|
||||
const Sp<Node> findNode(const InfoHash& id, sa_family_t af) const;
|
||||
bool trySearchInsert(const Sp<Node>& node);
|
||||
|
||||
// Searches
|
||||
inline SearchMap& searches(sa_family_t af) { return dht(af).searches; }
|
||||
inline const SearchMap& searches(sa_family_t af) const { return dht(af).searches; }
|
||||
|
||||
/**
|
||||
* Low-level method that will perform a search on the DHT for the
|
||||
* specified infohash (id), using the specified IP version (IPv4 or IPv6).
|
||||
* The values can be filtered by an arbitrary provided filter.
|
||||
* Low-level method that will perform a search on the DHT for the specified
|
||||
* infohash (id), using the specified IP version (IPv4 or IPv6).
|
||||
*/
|
||||
std::shared_ptr<Search> search(const InfoHash& id, sa_family_t af, GetCallback = nullptr, DoneCallback = nullptr, Value::Filter = Value::AllFilter());
|
||||
void announce(const InfoHash& id, sa_family_t af, std::shared_ptr<Value> value, DoneCallback callback, time_point created=time_point::max(), bool permanent = false);
|
||||
size_t listenTo(const InfoHash& id, sa_family_t af, GetCallback cb, Value::Filter f = Value::AllFilter());
|
||||
Sp<Search> search(const InfoHash& id, sa_family_t af, GetCallback = {}, QueryCallback = {}, DoneCallback = {}, Value::Filter = {}, const Sp<Query>& q = {});
|
||||
|
||||
void bootstrapSearch(Search& sr);
|
||||
Search *findSearch(unsigned short tid, sa_family_t af);
|
||||
void announce(const InfoHash& id, sa_family_t af, Sp<Value> value, DoneCallback callback, time_point created=time_point::max(), bool permanent = false);
|
||||
size_t listenTo(const InfoHash& id, sa_family_t af, ValueCallback cb, Value::Filter f = {}, const Sp<Query>& q = {});
|
||||
|
||||
/**
|
||||
* Refill the search with good nodes if possible.
|
||||
*
|
||||
* @param sr The search to refill.
|
||||
*
|
||||
* @return the number inserted nodes.
|
||||
*/
|
||||
unsigned refill(Search& sr);
|
||||
void expireSearches();
|
||||
|
||||
void confirmNodes();
|
||||
void expire();
|
||||
|
||||
void onConnected();
|
||||
void onDisconnected();
|
||||
|
||||
/**
|
||||
* Generic function to execute when a 'get' request has completed.
|
||||
*
|
||||
* @param status The request passed by the network engine.
|
||||
* @param answer The answer from the network engine.
|
||||
* @param ws A weak pointer to the search concerned by the request.
|
||||
* @param query The query sent to the node.
|
||||
*/
|
||||
void searchNodeGetDone(const net::Request& status,
|
||||
net::RequestAnswer&& answer,
|
||||
std::weak_ptr<Search> ws,
|
||||
Sp<Query> query);
|
||||
|
||||
/**
|
||||
* Generic function to execute when a 'get' request expires.
|
||||
*
|
||||
* @param status The request passed by the network engine.
|
||||
* @param over Whether we're done to try sending the request to the node
|
||||
* or not. This lets us mark a node as candidate.
|
||||
* @param ws A weak pointer to the search concerned by the request.
|
||||
* @param query The query sent to the node.
|
||||
*/
|
||||
void searchNodeGetExpired(const net::Request& status, bool over, std::weak_ptr<Search> ws, Sp<Query> query);
|
||||
|
||||
/**
|
||||
* This method recovers sends individual request for values per id.
|
||||
*
|
||||
* @param ws A weak pointer to the Search.
|
||||
* @param query The initial query passed through the API.
|
||||
* @param n The node to which send the requests.
|
||||
*/
|
||||
void paginate(std::weak_ptr<Search> ws, Sp<Query> query, SearchNode* n);
|
||||
|
||||
/**
|
||||
* If update is true, this method will also send message to synced but non-updated search nodes.
|
||||
*/
|
||||
SearchNode* searchSendGetValues(std::shared_ptr<Search> sr, SearchNode *n = nullptr, bool update = true);
|
||||
SearchNode* searchSendGetValues(Sp<Search> sr, SearchNode *n = nullptr, bool update = true);
|
||||
|
||||
/**
|
||||
* Forwards an 'announce' request for a list of nodes to the network engine.
|
||||
*
|
||||
* @param sr The search for which we want to announce a value.
|
||||
* @param announce The 'announce' structure.
|
||||
*/
|
||||
void searchSendAnnounceValue(const Sp<Search>& sr);
|
||||
|
||||
/**
|
||||
* Main process of a Search's operations. This function will demand the
|
||||
* network engine to send requests packets for all pending operations
|
||||
* ('get', 'put' and 'listen').
|
||||
*
|
||||
* @param sr The search to execute its operations.
|
||||
*/
|
||||
void searchStep(std::weak_ptr<Search> ws);
|
||||
|
||||
void searchSynchedNodeListen(const Sp<Search>&, SearchNode&);
|
||||
|
||||
void searchStep(std::shared_ptr<Search> sr);
|
||||
void dumpSearch(const Search& sr, std::ostream& out) const;
|
||||
|
||||
bool neighbourhoodMaintenance(RoutingTable&);
|
||||
|
||||
void processMessage(const uint8_t *buf, size_t buflen, const sockaddr *from, socklen_t fromlen);
|
||||
|
||||
void onError(std::shared_ptr<Request> node, DhtProtocolException e);
|
||||
void onError(Sp<net::Request> node, net::DhtProtocolException e);
|
||||
/* when our address is reported by a distant peer. */
|
||||
void onReportedAddr(const InfoHash& id, sockaddr* sa , socklen_t salen);
|
||||
void onReportedAddr(const InfoHash& id, const SockAddr&);
|
||||
/* when we receive a ping request */
|
||||
NetworkEngine::RequestAnswer onPing(std::shared_ptr<Node> node);
|
||||
net::RequestAnswer onPing(Sp<Node> node);
|
||||
/* when we receive a "find node" request */
|
||||
NetworkEngine::RequestAnswer onFindNode(std::shared_ptr<Node> node, InfoHash& hash, want_t want);
|
||||
void onFindNodeDone(const Request& status, NetworkEngine::RequestAnswer& a, std::shared_ptr<Search> sr);
|
||||
net::RequestAnswer onFindNode(Sp<Node> node, const InfoHash& hash, want_t want);
|
||||
void onFindNodeDone(const Sp<Node>& status,
|
||||
net::RequestAnswer& a,
|
||||
Sp<Search> sr);
|
||||
/* when we receive a "get values" request */
|
||||
NetworkEngine::RequestAnswer onGetValues(std::shared_ptr<Node> node, InfoHash& hash, want_t want);
|
||||
void onGetValuesDone(const Request& status, NetworkEngine::RequestAnswer& a, std::shared_ptr<Search> sr);
|
||||
net::RequestAnswer onGetValues(Sp<Node> node,
|
||||
const InfoHash& hash,
|
||||
want_t want,
|
||||
const Query& q);
|
||||
void onGetValuesDone(const Sp<Node>& status,
|
||||
net::RequestAnswer& a,
|
||||
Sp<Search>& sr,
|
||||
const Sp<Query>& orig_query);
|
||||
/* when we receive a listen request */
|
||||
NetworkEngine::RequestAnswer onListen(std::shared_ptr<Node> node, InfoHash& hash, Blob& token, size_t rid);
|
||||
void onListenDone(const Request& status, NetworkEngine::RequestAnswer& a, std::shared_ptr<Search>& sr);
|
||||
net::RequestAnswer onListen(Sp<Node> node,
|
||||
const InfoHash& hash,
|
||||
const Blob& token,
|
||||
size_t socket_id,
|
||||
const Query& query,
|
||||
int version = 0);
|
||||
void onListenDone(const Sp<Node>& status,
|
||||
net::RequestAnswer& a,
|
||||
Sp<Search>& sr);
|
||||
/* when we receive an announce request */
|
||||
NetworkEngine::RequestAnswer onAnnounce(std::shared_ptr<Node> node,
|
||||
InfoHash& hash, Blob& token, std::vector<std::shared_ptr<Value>> v, time_point created);
|
||||
void onAnnounceDone(const Request& status, NetworkEngine::RequestAnswer& a, std::shared_ptr<Search>& sr);
|
||||
net::RequestAnswer onAnnounce(Sp<Node> node,
|
||||
const InfoHash& hash,
|
||||
const Blob& token,
|
||||
const std::vector<Sp<Value>>& v,
|
||||
const time_point& created);
|
||||
net::RequestAnswer onRefresh(Sp<Node> node,
|
||||
const InfoHash& hash,
|
||||
const Blob& token,
|
||||
const Value::Id& vid);
|
||||
void onAnnounceDone(const Sp<Node>& status,
|
||||
net::RequestAnswer& a,
|
||||
Sp<Search>& sr);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "infohash.h"
|
||||
#include "log_enable.h"
|
||||
|
||||
#include <queue>
|
||||
|
||||
namespace dht {
|
||||
|
||||
namespace net {
|
||||
class DatagramSocket;
|
||||
}
|
||||
|
||||
class OPENDHT_PUBLIC DhtInterface {
|
||||
public:
|
||||
DhtInterface() = default;
|
||||
DhtInterface(const Logger& l) : logger_(std::make_shared<Logger>(l)) {};
|
||||
DhtInterface(const std::shared_ptr<Logger>& l) : logger_(l) {};
|
||||
virtual ~DhtInterface() = default;
|
||||
|
||||
// [[deprecated]]
|
||||
using Status = NodeStatus;
|
||||
// [[deprecated]]
|
||||
using NodeExport = dht::NodeExport;
|
||||
|
||||
/**
|
||||
* Get the current status of the node for the given family.
|
||||
*/
|
||||
virtual NodeStatus updateStatus(sa_family_t af) { return getStatus(af); };
|
||||
virtual NodeStatus getStatus(sa_family_t af) const = 0;
|
||||
virtual NodeStatus getStatus() const = 0;
|
||||
|
||||
void addOnConnectedCallback(std::function<void()> cb) {
|
||||
onConnectCallbacks_.emplace(std::move(cb));
|
||||
}
|
||||
|
||||
virtual net::DatagramSocket* getSocket() const { return {}; };
|
||||
|
||||
/**
|
||||
* Get the ID of the DHT node.
|
||||
*/
|
||||
virtual const InfoHash& getNodeId() const = 0;
|
||||
|
||||
/**
|
||||
* Performs final operations before quitting.
|
||||
* stop: if true, cancel ongoing operations and call their 'done'
|
||||
* callbacks synchronously.
|
||||
*/
|
||||
virtual void shutdown(ShutdownCallback cb, bool stop = false) = 0;
|
||||
|
||||
/**
|
||||
* Returns true if the node is running (have access to an open socket).
|
||||
*
|
||||
* af: address family. If non-zero, will return true if the node
|
||||
* is running for the provided family.
|
||||
*/
|
||||
virtual bool isRunning(sa_family_t af = 0) const = 0;
|
||||
|
||||
virtual void registerType(const ValueType& type) = 0;
|
||||
|
||||
virtual const ValueType& getType(ValueType::Id type_id) const = 0;
|
||||
|
||||
virtual void addBootstrap(const std::string& /*host*/, const std::string& /*service*/) {};
|
||||
virtual void clearBootstrap() {};
|
||||
|
||||
/**
|
||||
* Insert a node in the main routing table.
|
||||
* The node is not pinged, so this should be
|
||||
* used to bootstrap efficiently from previously known nodes.
|
||||
*/
|
||||
virtual void insertNode(const InfoHash& id, const SockAddr&) = 0;
|
||||
virtual void insertNode(const NodeExport& n) = 0;
|
||||
|
||||
virtual void pingNode(SockAddr, DoneCallbackSimple&& cb={}) = 0;
|
||||
|
||||
virtual time_point periodic(const uint8_t *buf, size_t buflen, SockAddr, const time_point& now) = 0;
|
||||
virtual time_point periodic(const uint8_t *buf, size_t buflen, const sockaddr* from, socklen_t fromlen, const time_point& now) = 0;
|
||||
|
||||
/**
|
||||
* Get a value by searching on all available protocols (IPv4, IPv6),
|
||||
* and call the provided get callback when values are found at key.
|
||||
* The operation will start as soon as the node is connected to the network.
|
||||
* @param cb a function called when new values are found on the network.
|
||||
* It should return false to stop the operation.
|
||||
* @param donecb a function called when the operation is complete.
|
||||
cb and donecb won't be called again afterward.
|
||||
* @param f a filter function used to prefilter values.
|
||||
*/
|
||||
virtual void get(const InfoHash& key, GetCallback cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {}) = 0;
|
||||
virtual void get(const InfoHash& key, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter&& f={}, Where&& w = {}) = 0;
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {}) = 0;
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallbackSimple donecb, Value::Filter&& f={}, Where&& w = {}) = 0;
|
||||
|
||||
/**
|
||||
* Similar to Dht::get, but sends a Query to filter data remotely.
|
||||
* @param key the key for which to query data for.
|
||||
* @param cb a function called when new values are found on the network.
|
||||
* It should return false to stop the operation.
|
||||
* @param done_cb a function called when the operation is complete.
|
||||
cb and done_cb won't be called again afterward.
|
||||
* @param q a query used to filter values on the remotes before they send a
|
||||
* response.
|
||||
*/
|
||||
virtual void query(const InfoHash& key, QueryCallback cb, DoneCallback done_cb = {}, Query&& q = {}) = 0;
|
||||
virtual void query(const InfoHash& key, QueryCallback cb, DoneCallbackSimple done_cb = {}, Query&& q = {}) = 0;
|
||||
|
||||
/**
|
||||
* Get locally stored data for the given hash.
|
||||
*/
|
||||
virtual std::vector<Sp<Value>> getLocal(const InfoHash& key, const Value::Filter& f = {}) const = 0;
|
||||
|
||||
/**
|
||||
* Get locally stored data for the given key and value id.
|
||||
*/
|
||||
virtual Sp<Value> getLocalById(const InfoHash& key, Value::Id vid) const = 0;
|
||||
|
||||
/**
|
||||
* Announce a value on all available protocols (IPv4, IPv6).
|
||||
*
|
||||
* The operation will start as soon as the node is connected to the network.
|
||||
* The done callback will be called once, when the first announce succeeds, or fails.
|
||||
*/
|
||||
virtual void put(const InfoHash& key,
|
||||
Sp<Value>,
|
||||
DoneCallback cb=nullptr,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false) = 0;
|
||||
virtual void put(const InfoHash& key,
|
||||
const Sp<Value>& v,
|
||||
DoneCallbackSimple cb,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false) = 0;
|
||||
virtual void put(const InfoHash& key,
|
||||
Value&& v,
|
||||
DoneCallback cb=nullptr,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false) = 0;
|
||||
virtual void put(const InfoHash& key,
|
||||
Value&& v,
|
||||
DoneCallbackSimple cb,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false) = 0;
|
||||
|
||||
/**
|
||||
* Get data currently being put at the given hash.
|
||||
*/
|
||||
virtual std::vector<Sp<Value>> getPut(const InfoHash&) const = 0;
|
||||
|
||||
/**
|
||||
* Get data currently being put at the given hash with the given id.
|
||||
*/
|
||||
virtual Sp<Value> getPut(const InfoHash&, const Value::Id&) const = 0;
|
||||
|
||||
/**
|
||||
* Stop any put/announce operation at the given location,
|
||||
* for the value with the given id.
|
||||
*/
|
||||
virtual bool cancelPut(const InfoHash&, const Value::Id&) = 0;
|
||||
|
||||
/**
|
||||
* Listen on the network for any changes involving a specified hash.
|
||||
* The node will register to receive updates from relevent nodes when
|
||||
* new values are added or removed.
|
||||
*
|
||||
* @return a token to cancel the listener later.
|
||||
*/
|
||||
virtual size_t listen(const InfoHash&, GetCallback, Value::Filter={}, Where w = {}) = 0;
|
||||
virtual size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}, Where w = {}) = 0;
|
||||
virtual size_t listen(const InfoHash&, ValueCallback, Value::Filter={}, Where w = {}) = 0;
|
||||
|
||||
virtual bool cancelListen(const InfoHash&, size_t token) = 0;
|
||||
|
||||
/**
|
||||
* Inform the DHT of lower-layer connectivity changes.
|
||||
* This will cause the DHT to assume a public IP address change.
|
||||
* The DHT will recontact neighbor nodes, re-register for listen ops etc.
|
||||
*/
|
||||
virtual void connectivityChanged(sa_family_t) = 0;
|
||||
virtual void connectivityChanged() = 0;
|
||||
|
||||
/**
|
||||
* Get the list of good nodes for local storage saving purposes
|
||||
* The list is ordered to minimize the back-to-work delay.
|
||||
*/
|
||||
virtual std::vector<NodeExport> exportNodes() const = 0;
|
||||
|
||||
virtual std::vector<ValuesExport> exportValues() const = 0;
|
||||
virtual void importValues(const std::vector<ValuesExport>&) = 0;
|
||||
|
||||
virtual NodeStats getNodesStats(sa_family_t af) const = 0;
|
||||
|
||||
virtual std::string getStorageLog() const = 0;
|
||||
virtual std::string getStorageLog(const InfoHash&) const = 0;
|
||||
|
||||
virtual std::string getRoutingTablesLog(sa_family_t) const = 0;
|
||||
virtual std::string getSearchesLog(sa_family_t) const = 0;
|
||||
virtual std::string getSearchLog(const InfoHash&, sa_family_t af = AF_UNSPEC) const = 0;
|
||||
|
||||
virtual void dumpTables() const = 0;
|
||||
virtual std::vector<unsigned> getNodeMessageStats(bool in = false) = 0;
|
||||
|
||||
/**
|
||||
* Set the in-memory storage limit in bytes
|
||||
*/
|
||||
virtual void setStorageLimit(size_t limit = DEFAULT_STORAGE_LIMIT) = 0;
|
||||
virtual size_t getStorageLimit() const = 0;
|
||||
|
||||
/**
|
||||
* Returns the total memory usage of stored values and the number
|
||||
* of stored values.
|
||||
*/
|
||||
virtual std::pair<size_t, size_t> getStoreSize() const = 0;
|
||||
|
||||
virtual std::vector<SockAddr> getPublicAddress(sa_family_t family = 0) = 0;
|
||||
|
||||
/**
|
||||
* Enable or disable logging of DHT internal messages
|
||||
*/
|
||||
virtual void setLoggers(LogMethod error = {}, LogMethod warn = {}, LogMethod debug = {}) {
|
||||
if (logger_) {
|
||||
logger_->DBG = std::move(debug);
|
||||
logger_->WARN = std::move(warn);
|
||||
logger_->ERR = std::move(error);
|
||||
} else
|
||||
logger_= std::make_shared<Logger>(std::move(error), std::move(warn), std::move(debug));
|
||||
}
|
||||
|
||||
virtual void setLogger(const Logger& l) {
|
||||
if (logger_)
|
||||
*logger_ = l;
|
||||
else
|
||||
logger_= std::make_shared<Logger>(l);
|
||||
}
|
||||
|
||||
virtual void setLogger(const std::shared_ptr<Logger>& l) {
|
||||
logger_ = l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only print logs related to the given InfoHash (if given), or disable filter (if zeroes).
|
||||
*/
|
||||
virtual void setLogFilter(const InfoHash& f)
|
||||
{
|
||||
if (logger_)
|
||||
logger_->setFilter(f);
|
||||
}
|
||||
|
||||
virtual void setPushNotificationToken(const std::string&) {};
|
||||
|
||||
/**
|
||||
* Call linked callback with a push notification
|
||||
* @param notification to process
|
||||
*/
|
||||
virtual void pushNotificationReceived(const std::map<std::string, std::string>& data) = 0;
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Logger> logger_ {};
|
||||
std::queue<std::function<void()>> onConnectCallbacks_ {};
|
||||
};
|
||||
|
||||
} // namespace dht
|
||||
@@ -0,0 +1,416 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
* Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Vsevolod Ivanov <vsevolod.ivanov@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
|
||||
#include "callbacks.h"
|
||||
#include "def.h"
|
||||
#include "dht_interface.h"
|
||||
#include "proxy.h"
|
||||
#include "http.h"
|
||||
|
||||
#include <restinio/all.hpp>
|
||||
#include <json/json.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
namespace Json {
|
||||
class Value;
|
||||
}
|
||||
|
||||
namespace http {
|
||||
class Resolver;
|
||||
class Request;
|
||||
}
|
||||
|
||||
namespace dht {
|
||||
|
||||
class OPENDHT_PUBLIC DhtProxyClient final : public DhtInterface {
|
||||
public:
|
||||
|
||||
DhtProxyClient();
|
||||
|
||||
explicit DhtProxyClient(
|
||||
std::shared_ptr<crypto::Certificate> serverCA, crypto::Identity clientIdentity,
|
||||
std::function<void()> loopSignal, const std::string& serverHost,
|
||||
const std::string& pushClientId = "", std::shared_ptr<Logger> logger = {});
|
||||
|
||||
void setHeaderFields(http::Request& request);
|
||||
|
||||
virtual void setPushNotificationToken(const std::string& token) override {
|
||||
#ifdef OPENDHT_PUSH_NOTIFICATIONS
|
||||
deviceKey_ = token;
|
||||
#else
|
||||
(void) token;
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual ~DhtProxyClient();
|
||||
|
||||
/**
|
||||
* Get the ID of the node.
|
||||
*/
|
||||
inline const InfoHash& getNodeId() const override { return myid; }
|
||||
|
||||
/**
|
||||
* Get the current status of the node for the given family.
|
||||
*/
|
||||
NodeStatus getStatus(sa_family_t af) const override;
|
||||
NodeStatus getStatus() const override {
|
||||
return std::max(getStatus(AF_INET), getStatus(AF_INET6));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs final operations before quitting.
|
||||
*/
|
||||
void shutdown(ShutdownCallback cb, bool) override;
|
||||
|
||||
/**
|
||||
* Returns true if the node is running (have access to an open socket).
|
||||
*
|
||||
* af: address family. If non-zero, will return true if the node
|
||||
* is running for the provided family.
|
||||
*/
|
||||
bool isRunning(sa_family_t af = 0) const override;
|
||||
|
||||
/**
|
||||
* Get a value by asking the proxy and call the provided get callback when
|
||||
* values are found at key.
|
||||
* The operation will start as soon as the node is connected to the network.
|
||||
* @param cb a function called when new values are found on the network.
|
||||
* It should return false to stop the operation.
|
||||
* @param donecb a function called when the operation is complete.
|
||||
cb and donecb won't be called again afterward.
|
||||
* @param f a filter function used to prefilter values.
|
||||
*/
|
||||
virtual void get(const InfoHash& key, GetCallback cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {}) override;
|
||||
virtual void get(const InfoHash& key, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter&& f={}, Where&& w = {}) override {
|
||||
get(key, cb, bindDoneCb(std::move(donecb)), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {}) override {
|
||||
get(key, bindGetCb(cb), std::move(donecb), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallbackSimple donecb, Value::Filter&& f={}, Where&& w = {}) override {
|
||||
get(key, bindGetCb(cb), bindDoneCb(std::move(donecb)), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
|
||||
/**
|
||||
* Announce a value on all available protocols (IPv4, IPv6).
|
||||
*
|
||||
* The operation will start as soon as the node is connected to the network.
|
||||
* The done callback will be called once, when the first announce succeeds, or fails.
|
||||
* NOTE: For now, created parameter is ignored.
|
||||
*/
|
||||
void put(const InfoHash& key,
|
||||
Sp<Value>,
|
||||
DoneCallback cb=nullptr,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false) override;
|
||||
void put(const InfoHash& key,
|
||||
const Sp<Value>& v,
|
||||
DoneCallbackSimple cb,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false) override
|
||||
{
|
||||
put(key, v, bindDoneCb(std::move(cb)), created, permanent);
|
||||
}
|
||||
|
||||
void put(const InfoHash& key,
|
||||
Value&& v,
|
||||
DoneCallback cb=nullptr,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false) override
|
||||
{
|
||||
put(key, std::make_shared<Value>(std::move(v)), std::move(cb), created, permanent);
|
||||
}
|
||||
void put(const InfoHash& key,
|
||||
Value&& v,
|
||||
DoneCallbackSimple cb,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false) override
|
||||
{
|
||||
put(key, std::forward<Value>(v), bindDoneCb(std::move(cb)), created, permanent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param af the socket family
|
||||
* @return node stats from the proxy
|
||||
*/
|
||||
NodeStats getNodesStats(sa_family_t af) const override;
|
||||
|
||||
/**
|
||||
* @param family the socket family
|
||||
* @return public address
|
||||
*/
|
||||
std::vector<SockAddr> getPublicAddress(sa_family_t family = 0) override;
|
||||
|
||||
/**
|
||||
* Listen on the network for any changes involving a specified hash.
|
||||
* The node will register to receive updates from relevent nodes when
|
||||
* new values are added or removed.
|
||||
*
|
||||
* @return a token to cancel the listener later.
|
||||
*/
|
||||
virtual size_t listen(const InfoHash&, ValueCallback, Value::Filter={}, Where={}) override;
|
||||
|
||||
virtual size_t listen(const InfoHash& key, GetCallback cb, Value::Filter f={}, Where w={}) override {
|
||||
return listen(key, [cb=std::move(cb)](const std::vector<Sp<Value>>& vals, bool expired){
|
||||
if (not expired)
|
||||
return cb(vals);
|
||||
return true;
|
||||
}, std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
virtual size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}, Where w={}) override {
|
||||
return listen(key, bindGetCb(std::move(cb)), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
/*
|
||||
* This function relies on the cache implementation.
|
||||
* It means that there are no true cancel here, it keeps the caching in higher priority.
|
||||
*/
|
||||
virtual bool cancelListen(const InfoHash& key, size_t token) override;
|
||||
|
||||
/**
|
||||
* Call linked callback with a push notification
|
||||
* @param notification to process
|
||||
*/
|
||||
void pushNotificationReceived(const std::map<std::string, std::string>& notification) override;
|
||||
|
||||
time_point periodic(const uint8_t*, size_t, SockAddr, const time_point& now) override;
|
||||
time_point periodic(const uint8_t* buf, size_t buflen, const sockaddr* from, socklen_t fromlen, const time_point& now) override {
|
||||
return periodic(buf, buflen, SockAddr(from, fromlen), now);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to Dht::get, but sends a Query to filter data remotely.
|
||||
* @param key the key for which to query data for.
|
||||
* @param cb a function called when new values are found on the network.
|
||||
* It should return false to stop the operation.
|
||||
* @param done_cb a function called when the operation is complete.
|
||||
cb and done_cb won't be called again afterward.
|
||||
* @param q a query used to filter values on the remotes before they send a
|
||||
* response.
|
||||
*/
|
||||
virtual void query(const InfoHash& /*key*/, QueryCallback /*cb*/, DoneCallback /*done_cb*/ = {}, Query&& /*q*/ = {}) override { }
|
||||
virtual void query(const InfoHash& key, QueryCallback cb, DoneCallbackSimple done_cb = {}, Query&& q = {}) override {
|
||||
query(key, cb, bindDoneCb(std::move(done_cb)), std::forward<Query>(q));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data currently being put at the given hash.
|
||||
*/
|
||||
std::vector<Sp<Value>> getPut(const InfoHash&) const override;
|
||||
|
||||
/**
|
||||
* Get data currently being put at the given hash with the given id.
|
||||
*/
|
||||
Sp<Value> getPut(const InfoHash&, const Value::Id&) const override;
|
||||
|
||||
/**
|
||||
* Stop any put/announce operation at the given location,
|
||||
* for the value with the given id.
|
||||
*/
|
||||
bool cancelPut(const InfoHash&, const Value::Id&) override;
|
||||
|
||||
void pingNode(SockAddr, DoneCallbackSimple&& /*cb*/={}) override { }
|
||||
|
||||
virtual void registerType(const ValueType& type) override {
|
||||
types.registerType(type);
|
||||
}
|
||||
const ValueType& getType(ValueType::Id type_id) const override {
|
||||
return types.getType(type_id);
|
||||
}
|
||||
|
||||
std::vector<Sp<Value>> getLocal(const InfoHash& k, const Value::Filter& filter) const override;
|
||||
Sp<Value> getLocalById(const InfoHash& k, Value::Id id) const override;
|
||||
|
||||
/**
|
||||
* NOTE: The following methods will not be implemented because the
|
||||
* DhtProxyClient doesn't have any storage nor synchronization process
|
||||
*/
|
||||
void insertNode(const InfoHash&, const SockAddr&) override { }
|
||||
void insertNode(const NodeExport&) override { }
|
||||
std::pair<size_t, size_t> getStoreSize() const override { return {}; }
|
||||
std::vector<NodeExport> exportNodes() const override { return {}; }
|
||||
std::vector<ValuesExport> exportValues() const override { return {}; }
|
||||
void importValues(const std::vector<ValuesExport>&) override {}
|
||||
std::string getStorageLog() const override { return {}; }
|
||||
std::string getStorageLog(const InfoHash&) const override { return {}; }
|
||||
std::string getRoutingTablesLog(sa_family_t) const override { return {}; }
|
||||
std::string getSearchesLog(sa_family_t) const override { return {}; }
|
||||
std::string getSearchLog(const InfoHash&, sa_family_t) const override { return {}; }
|
||||
void dumpTables() const override {}
|
||||
std::vector<unsigned> getNodeMessageStats(bool) override { return {}; }
|
||||
void setStorageLimit(size_t) override {}
|
||||
virtual size_t getStorageLimit() const override { return 0; }
|
||||
void connectivityChanged(sa_family_t) override {
|
||||
getProxyInfos();
|
||||
}
|
||||
void connectivityChanged() override {
|
||||
getProxyInfos();
|
||||
loopSignal_();
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Start the connection with a server.
|
||||
*/
|
||||
void startProxy();
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* Get informations from the proxy node
|
||||
* @return the JSON returned by the proxy
|
||||
*/
|
||||
struct InfoState;
|
||||
void getProxyInfos();
|
||||
void queryProxyInfo(const std::shared_ptr<InfoState>& infoState, const std::shared_ptr<http::Resolver>& resolver, sa_family_t family);
|
||||
void onProxyInfos(const Json::Value& val, const sa_family_t family);
|
||||
SockAddr parsePublicAddress(const Json::Value& val);
|
||||
|
||||
void opFailed();
|
||||
|
||||
void handleExpireListener(const asio::error_code &ec, const InfoHash& key);
|
||||
|
||||
struct Listener;
|
||||
struct OperationState;
|
||||
enum class ListenMethod {
|
||||
LISTEN,
|
||||
SUBSCRIBE,
|
||||
RESUBSCRIBE,
|
||||
};
|
||||
using CacheValueCallback = std::function<bool(const std::vector<std::shared_ptr<Value>>& values, bool expired, system_clock::time_point)>;
|
||||
|
||||
/**
|
||||
* Send Listen with httpClient_
|
||||
*/
|
||||
void sendListen(const restinio::http_request_header_t& header, const CacheValueCallback& cb,
|
||||
const Sp<OperationState>& opstate, Listener& listener, ListenMethod method = ListenMethod::LISTEN);
|
||||
void handleResubscribe(const asio::error_code& ec, const InfoHash& key,
|
||||
const size_t token, std::shared_ptr<OperationState> opstate);
|
||||
|
||||
void doPut(const InfoHash&, Sp<Value>, DoneCallbackSimple, time_point created, bool permanent);
|
||||
void handleRefreshPut(const asio::error_code& ec, InfoHash key, Value::Id id);
|
||||
|
||||
/**
|
||||
* Initialize statusIpvX_
|
||||
*/
|
||||
void getConnectivityStatus();
|
||||
/**
|
||||
* cancel all Listeners
|
||||
*/
|
||||
void cancelAllListeners();
|
||||
|
||||
std::atomic_bool isDestroying_ {false};
|
||||
|
||||
std::string proxyUrl_;
|
||||
dht::crypto::Identity clientIdentity_;
|
||||
std::shared_ptr<dht::crypto::Certificate> serverCertificate_;
|
||||
//std::pair<std::string, std::string> serverHostService_;
|
||||
std::string pushClientId_;
|
||||
std::string pushSessionId_;
|
||||
|
||||
mutable std::mutex lockCurrentProxyInfos_;
|
||||
NodeStatus statusIpv4_ {NodeStatus::Disconnected};
|
||||
NodeStatus statusIpv6_ {NodeStatus::Disconnected};
|
||||
NodeStats stats4_ {};
|
||||
NodeStats stats6_ {};
|
||||
SockAddr publicAddressV4_;
|
||||
SockAddr publicAddressV6_;
|
||||
std::atomic_bool launchConnectedCbs_ {false};
|
||||
|
||||
InfoHash myid {};
|
||||
|
||||
// registred types
|
||||
TypeStore types;
|
||||
|
||||
/*
|
||||
* ASIO I/O Context for sockets in httpClient_
|
||||
* Note: Each context is used in one thread only
|
||||
*/
|
||||
asio::io_context httpContext_;
|
||||
std::shared_ptr<http::Resolver> resolver_;
|
||||
|
||||
mutable std::mutex requestLock_;
|
||||
std::map<unsigned, std::shared_ptr<http::Request>> requests_;
|
||||
/*
|
||||
* Thread for executing the http io_context.run() blocking call
|
||||
*/
|
||||
std::thread httpClientThread_;
|
||||
|
||||
/**
|
||||
* Store listen requests.
|
||||
*/
|
||||
struct ProxySearch;
|
||||
|
||||
mutable std::mutex searchLock_;
|
||||
size_t listenerToken_ {0};
|
||||
std::map<InfoHash, ProxySearch> searches_;
|
||||
|
||||
/**
|
||||
* Callbacks should be executed in the main thread.
|
||||
*/
|
||||
std::mutex lockCallbacks_;
|
||||
std::vector<std::function<void()>> callbacks_;
|
||||
|
||||
Sp<InfoState> infoState_;
|
||||
|
||||
/**
|
||||
* Retrieve if we can connect to the proxy (update statusIpvX_)
|
||||
*/
|
||||
void handleProxyConfirm(const asio::error_code &ec);
|
||||
std::unique_ptr<asio::steady_timer> nextProxyConfirmationTimer_;
|
||||
std::unique_ptr<asio::steady_timer> listenerRestartTimer_;
|
||||
|
||||
/**
|
||||
* Relaunch LISTEN requests if the client disconnect/reconnect.
|
||||
*/
|
||||
void restartListeners(const asio::error_code &ec);
|
||||
|
||||
/**
|
||||
* Refresh a listen via a token
|
||||
* @param token
|
||||
*/
|
||||
void resubscribe(const InfoHash& key, const size_t token, Listener& listener);
|
||||
|
||||
/**
|
||||
* If we want to use push notifications by default.
|
||||
* NOTE: empty by default to avoid to use services like FCM or APN.
|
||||
*/
|
||||
std::string deviceKey_ {};
|
||||
|
||||
const std::function<void()> loopSignal_;
|
||||
|
||||
#ifdef OPENDHT_PUSH_NOTIFICATIONS
|
||||
std::string fillBody(bool resubscribe);
|
||||
void getPushRequest(Json::Value&) const;
|
||||
#endif // OPENDHT_PUSH_NOTIFICATIONS
|
||||
|
||||
Json::StreamWriterBuilder jsonBuilder_;
|
||||
std::unique_ptr<Json::CharReader> jsonReader_;
|
||||
|
||||
std::shared_ptr<http::Request> buildRequest(const std::string& target = {});
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,453 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
* Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Vsevolod Ivanov <vsevolod.ivanov@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "callbacks.h"
|
||||
#include "def.h"
|
||||
#include "infohash.h"
|
||||
#include "proxy.h"
|
||||
#include "scheduler.h"
|
||||
#include "sockaddr.h"
|
||||
#include "value.h"
|
||||
#include "http.h"
|
||||
|
||||
#include <restinio/all.hpp>
|
||||
#include <restinio/tls.hpp>
|
||||
#include <json/json.h>
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
namespace dht {
|
||||
enum class PushType {
|
||||
None = 0,
|
||||
Android,
|
||||
iOS
|
||||
};
|
||||
}
|
||||
MSGPACK_ADD_ENUM(dht::PushType)
|
||||
|
||||
namespace http {
|
||||
class Request;
|
||||
struct ListenerSession;
|
||||
}
|
||||
|
||||
namespace Json {
|
||||
class Value;
|
||||
}
|
||||
|
||||
namespace dht {
|
||||
|
||||
class DhtRunner;
|
||||
|
||||
using RestRouter = restinio::router::express_router_t<>;
|
||||
using RequestStatus = restinio::request_handling_status_t;
|
||||
|
||||
struct ProxyServerConfig {
|
||||
in_port_t port {8000};
|
||||
std::string pushServer {};
|
||||
std::string persistStatePath {};
|
||||
dht::crypto::Identity identity {};
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the REST API
|
||||
*/
|
||||
class OPENDHT_PUBLIC DhtProxyServer
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Start the Http server for OpenDHT
|
||||
* @param dht the DhtRunner linked to this proxy server
|
||||
* @param port to listen
|
||||
* @param pushServer where to push notifications
|
||||
* @note if the server fails to start (if port is already used or reserved),
|
||||
* it will fails silently
|
||||
*/
|
||||
DhtProxyServer(const std::shared_ptr<DhtRunner>& dht,
|
||||
const ProxyServerConfig& config = {},
|
||||
const std::shared_ptr<dht::Logger>& logger = {});
|
||||
|
||||
virtual ~DhtProxyServer();
|
||||
|
||||
DhtProxyServer(const DhtProxyServer& other) = delete;
|
||||
DhtProxyServer(DhtProxyServer&& other) = delete;
|
||||
DhtProxyServer& operator=(const DhtProxyServer& other) = delete;
|
||||
DhtProxyServer& operator=(DhtProxyServer&& other) = delete;
|
||||
|
||||
asio::io_context& io_context() const;
|
||||
|
||||
struct ServerStats {
|
||||
/** Current number of listen operations */
|
||||
size_t listenCount {0};
|
||||
/** Current number of permanent put operations (hash used) */
|
||||
size_t putCount {0};
|
||||
/** Current number of permanent put values */
|
||||
size_t totalPermanentPuts {0};
|
||||
/** Current number of push tokens with at least one listen operation */
|
||||
size_t pushListenersCount {0};
|
||||
/** Average requests per second */
|
||||
double requestRate {0};
|
||||
/** Node Info **/
|
||||
std::shared_ptr<NodeInfo> nodeInfo {};
|
||||
|
||||
std::string toString() const {
|
||||
std::ostringstream ss;
|
||||
ss << "Listens: " << listenCount << " Puts: " << putCount << " PushListeners: " << pushListenersCount << std::endl;
|
||||
ss << "Requests: " << requestRate << " per second." << std::endl;
|
||||
if (nodeInfo) {
|
||||
auto& ipv4 = nodeInfo->ipv4;
|
||||
if (ipv4.table_depth > 1)
|
||||
ss << "IPv4 Network estimation: " << ipv4.getNetworkSizeEstimation() << std::endl;;
|
||||
auto& ipv6 = nodeInfo->ipv6;
|
||||
if (ipv6.table_depth > 1)
|
||||
ss << "IPv6 Network estimation: " << ipv6.getNetworkSizeEstimation() << std::endl;;
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a json object from a NodeStats
|
||||
*/
|
||||
Json::Value toJson() const {
|
||||
Json::Value result;
|
||||
result["listenCount"] = static_cast<Json::UInt64>(listenCount);
|
||||
result["putCount"] = static_cast<Json::UInt64>(putCount);
|
||||
result["totalPermanentPuts"] = static_cast<Json::UInt64>(totalPermanentPuts);
|
||||
result["pushListenersCount"] = static_cast<Json::UInt64>(pushListenersCount);
|
||||
result["requestRate"] = requestRate;
|
||||
if (nodeInfo)
|
||||
result["nodeInfo"] = nodeInfo->toJson();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<ServerStats> stats() const { return stats_; }
|
||||
|
||||
std::shared_ptr<ServerStats> updateStats(std::shared_ptr<NodeInfo> info) const;
|
||||
|
||||
std::shared_ptr<DhtRunner> getNode() const { return dht_; }
|
||||
|
||||
private:
|
||||
class ConnectionListener;
|
||||
struct RestRouterTraitsTls;
|
||||
struct RestRouterTraits;
|
||||
|
||||
template <typename HttpResponse>
|
||||
static HttpResponse initHttpResponse(HttpResponse response);
|
||||
static restinio::request_handling_status_t serverError(restinio::request_t& request);
|
||||
|
||||
template< typename ServerSettings >
|
||||
void addServerSettings(ServerSettings& serverSettings,
|
||||
const unsigned int max_pipelined_requests = 16);
|
||||
|
||||
std::unique_ptr<RestRouter> createRestRouter();
|
||||
|
||||
void onConnectionClosed(restinio::connection_id_t);
|
||||
|
||||
/**
|
||||
* Return the PublicKey id, the node id and node stats
|
||||
* Method: GET "/"
|
||||
* Result: HTTP 200, body: Node infos in JSON format
|
||||
* On error: HTTP 503, body: {"err":"xxxx"}
|
||||
* @param session
|
||||
*/
|
||||
RequestStatus getNodeInfo(restinio::request_handle_t request,
|
||||
restinio::router::route_params_t params) const;
|
||||
|
||||
/**
|
||||
* Return ServerStats in JSON format
|
||||
* Method: STATS "/"
|
||||
* Result: HTTP 200, body: Node infos in JSON format
|
||||
* @param session
|
||||
*/
|
||||
RequestStatus getStats(restinio::request_handle_t request,
|
||||
restinio::router::route_params_t params);
|
||||
|
||||
/**
|
||||
* Return Values of an infoHash
|
||||
* Method: GET "/{InfoHash: .*}"
|
||||
* Return: Multiple JSON object in parts. Example:
|
||||
* Value in JSON format\n
|
||||
* Value in JSON format
|
||||
*
|
||||
* On error: HTTP 503, body: {"err":"xxxx"}
|
||||
* @param session
|
||||
*/
|
||||
RequestStatus get(restinio::request_handle_t request,
|
||||
restinio::router::route_params_t params);
|
||||
|
||||
/**
|
||||
* Listen incoming Values of an infoHash.
|
||||
* Method: LISTEN "/{InfoHash: .*}"
|
||||
* Return: Multiple JSON object in parts. Example:
|
||||
* Value in JSON format\n
|
||||
* Value in JSON format
|
||||
*
|
||||
* On error: HTTP 503, body: {"err":"xxxx"}
|
||||
* @param session
|
||||
*/
|
||||
RequestStatus listen(restinio::request_handle_t request,
|
||||
restinio::router::route_params_t params);
|
||||
|
||||
/**
|
||||
* Put a value on the DHT
|
||||
* Method: POST "/{InfoHash: .*}"
|
||||
* body = Value to put in JSON
|
||||
* Return: HTTP 200 if success and the value put in JSON
|
||||
* On error: HTTP 503, body: {"err":"xxxx"} if no dht
|
||||
* HTTP 400, body: {"err":"xxxx"} if bad json or HTTP 502 if put fails
|
||||
* @param session
|
||||
*/
|
||||
RequestStatus put(restinio::request_handle_t request,
|
||||
restinio::router::route_params_t params);
|
||||
|
||||
void handleCancelPermamentPut(const asio::error_code &ec, const InfoHash& key, Value::Id vid);
|
||||
|
||||
#ifdef OPENDHT_PROXY_SERVER_IDENTITY
|
||||
/**
|
||||
* Put a value to sign by the proxy on the DHT
|
||||
* Method: SIGN "/{InfoHash: .*}"
|
||||
* body = Value to put in JSON
|
||||
* Return: HTTP 200 if success and the value put in JSON
|
||||
* On error: HTTP 503, body: {"err":"xxxx"} if no dht
|
||||
* HTTP 400, body: {"err":"xxxx"} if bad json
|
||||
* @param session
|
||||
*/
|
||||
RequestStatus putSigned(restinio::request_handle_t request,
|
||||
restinio::router::route_params_t params) const;
|
||||
|
||||
/**
|
||||
* Put a value to encrypt by the proxy on the DHT
|
||||
* Method: ENCRYPT "/{hash: .*}"
|
||||
* body = Value to put in JSON + "to":"infoHash"
|
||||
* Return: HTTP 200 if success and the value put in JSON
|
||||
* On error: HTTP 503, body: {"err":"xxxx"} if no dht
|
||||
* HTTP 400, body: {"err":"xxxx"} if bad json
|
||||
* @param session
|
||||
*/
|
||||
RequestStatus putEncrypted(restinio::request_handle_t request,
|
||||
restinio::router::route_params_t params);
|
||||
|
||||
#endif // OPENDHT_PROXY_SERVER_IDENTITY
|
||||
|
||||
/**
|
||||
* Return Values of an infoHash filtered by a value id
|
||||
* Method: GET "/{InfoHash: .*}/{ValueId: .*}"
|
||||
* Return: Multiple JSON object in parts. Example:
|
||||
* Value in JSON format\n
|
||||
* Value in JSON format
|
||||
*
|
||||
* On error: HTTP 503, body: {"err":"xxxx"}
|
||||
* @param session
|
||||
*/
|
||||
RequestStatus getFiltered(restinio::request_handle_t request,
|
||||
restinio::router::route_params_t params);
|
||||
|
||||
/**
|
||||
* Respond allowed Methods
|
||||
* Method: OPTIONS "/{hash: .*}"
|
||||
* Return: HTTP 200 + Allow: allowed methods
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS
|
||||
* @param session
|
||||
*/
|
||||
RequestStatus options(restinio::request_handle_t request,
|
||||
restinio::router::route_params_t params);
|
||||
|
||||
#ifdef OPENDHT_PUSH_NOTIFICATIONS
|
||||
/**
|
||||
* Subscribe to push notifications for an iOS or Android device.
|
||||
* Method: SUBSCRIBE "/{InfoHash: .*}"
|
||||
* Body: {"key": "device_key", (optional)"isAndroid":false (default true)}"
|
||||
* Return: {"token": x}" where x if a token to save
|
||||
* @note: the listen will timeout after six hours (and send a push notification).
|
||||
* so you need to refresh the operation each six hours.
|
||||
* @param session
|
||||
*/
|
||||
RequestStatus subscribe(restinio::request_handle_t request,
|
||||
restinio::router::route_params_t params);
|
||||
|
||||
/**
|
||||
* Unsubscribe to push notifications for an iOS or Android device.
|
||||
* Method: UNSUBSCRIBE "/{InfoHash: .*}"
|
||||
* Body: {"key": "device_key", "token": x} where x if the token to cancel
|
||||
* Return: nothing
|
||||
* @param session
|
||||
*/
|
||||
RequestStatus unsubscribe(restinio::request_handle_t request,
|
||||
restinio::router::route_params_t params);
|
||||
|
||||
/**
|
||||
* Send a push notification via a gorush push gateway
|
||||
* @param key of the device
|
||||
* @param json, the content to send
|
||||
*/
|
||||
void sendPushNotification(const std::string& key, Json::Value&& json, PushType type, bool highPriority);
|
||||
|
||||
/**
|
||||
* Send push notification with an expire timeout.
|
||||
* @param ec
|
||||
* @param pushToken
|
||||
* @param json
|
||||
* @param type
|
||||
*/
|
||||
void handleNotifyPushListenExpire(const asio::error_code &ec, const std::string pushToken,
|
||||
std::function<Json::Value()> json, PushType type);
|
||||
|
||||
/**
|
||||
* Remove a push listener between a client and a hash
|
||||
* @param ec
|
||||
* @param pushToken
|
||||
* @param key
|
||||
* @param clientId
|
||||
*/
|
||||
void handleCancelPushListen(const asio::error_code &ec, const std::string pushToken,
|
||||
const InfoHash key, const std::string clientId);
|
||||
|
||||
#endif //OPENDHT_PUSH_NOTIFICATIONS
|
||||
|
||||
void handlePrintStats(const asio::error_code &ec);
|
||||
void updateStats();
|
||||
|
||||
template <typename Os>
|
||||
void saveState(Os& stream);
|
||||
|
||||
template <typename Is>
|
||||
void loadState(Is& is, size_t size);
|
||||
|
||||
using clock = std::chrono::steady_clock;
|
||||
using time_point = clock::time_point;
|
||||
|
||||
std::shared_ptr<asio::io_context> ioContext_;
|
||||
std::shared_ptr<DhtRunner> dht_;
|
||||
Json::StreamWriterBuilder jsonBuilder_;
|
||||
Json::CharReaderBuilder jsonReaderBuilder_;
|
||||
std::mt19937_64 rd {crypto::getSeededRandomEngine<std::mt19937_64>()};
|
||||
|
||||
std::string persistPath_;
|
||||
|
||||
// http server
|
||||
std::thread serverThread_;
|
||||
std::unique_ptr<restinio::http_server_t<RestRouterTraitsTls>> httpsServer_;
|
||||
std::unique_ptr<restinio::http_server_t<RestRouterTraits>> httpServer_;
|
||||
|
||||
// http client
|
||||
std::pair<std::string, std::string> pushHostPort_;
|
||||
|
||||
mutable std::mutex requestLock_;
|
||||
std::map<unsigned int /*id*/, std::shared_ptr<http::Request>> requests_;
|
||||
|
||||
std::shared_ptr<dht::Logger> logger_;
|
||||
|
||||
std::shared_ptr<ServerStats> stats_;
|
||||
std::shared_ptr<NodeInfo> nodeInfo_ {};
|
||||
std::unique_ptr<asio::steady_timer> printStatsTimer_;
|
||||
|
||||
// Thread-safe access to listeners map.
|
||||
std::mutex lockListener_;
|
||||
// Shared with connection listener.
|
||||
std::map<restinio::connection_id_t, http::ListenerSession> listeners_;
|
||||
// Connection Listener observing conn state changes.
|
||||
std::shared_ptr<ConnectionListener> connListener_;
|
||||
|
||||
struct PushSessionContext {
|
||||
std::mutex lock;
|
||||
std::string sessionId;
|
||||
PushSessionContext(const std::string& id) : sessionId(id) {}
|
||||
};
|
||||
struct PermanentPut {
|
||||
time_point expiration;
|
||||
std::string pushToken;
|
||||
std::string clientId;
|
||||
std::shared_ptr<PushSessionContext> sessionCtx;
|
||||
std::unique_ptr<asio::steady_timer> expireTimer;
|
||||
std::unique_ptr<asio::steady_timer> expireNotifyTimer;
|
||||
Sp<Value> value;
|
||||
PushType type;
|
||||
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& p) const
|
||||
{
|
||||
p.pack_map(2 + (sessionCtx ? 1 : 0) + (clientId.empty() ? 0 : 1) + (type == PushType::None ? 0 : 2));
|
||||
p.pack("value"); p.pack(value);
|
||||
p.pack("exp"); p.pack(to_time_t(expiration));
|
||||
if (not clientId.empty()) {
|
||||
p.pack("cid"); p.pack(clientId);
|
||||
}
|
||||
if (sessionCtx) {
|
||||
std::lock_guard<std::mutex> l(sessionCtx->lock);
|
||||
p.pack("sid"); p.pack(sessionCtx->sessionId);
|
||||
}
|
||||
if (type != PushType::None) {
|
||||
p.pack("t"); p.pack(type);
|
||||
p.pack("token"); p.pack(pushToken);
|
||||
}
|
||||
}
|
||||
|
||||
void msgpack_unpack(const msgpack::object& o);
|
||||
};
|
||||
struct SearchPuts {
|
||||
std::map<dht::Value::Id, PermanentPut> puts;
|
||||
MSGPACK_DEFINE_ARRAY(puts)
|
||||
};
|
||||
std::mutex lockSearchPuts_;
|
||||
std::map<InfoHash, SearchPuts> puts_;
|
||||
|
||||
mutable std::atomic<size_t> requestNum_ {0};
|
||||
mutable std::atomic<time_point> lastStatsReset_ {time_point::min()};
|
||||
|
||||
std::string pushServer_;
|
||||
|
||||
#ifdef OPENDHT_PUSH_NOTIFICATIONS
|
||||
struct Listener {
|
||||
time_point expiration;
|
||||
std::string clientId;
|
||||
std::shared_ptr<PushSessionContext> sessionCtx;
|
||||
std::future<size_t> internalToken;
|
||||
std::unique_ptr<asio::steady_timer> expireTimer;
|
||||
std::unique_ptr<asio::steady_timer> expireNotifyTimer;
|
||||
PushType type;
|
||||
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& p) const
|
||||
{
|
||||
p.pack_map(sessionCtx ? 4 : 3);
|
||||
p.pack("cid"); p.pack(clientId);
|
||||
p.pack("exp"); p.pack(to_time_t(expiration));
|
||||
if (sessionCtx) {
|
||||
std::lock_guard<std::mutex> l(sessionCtx->lock);
|
||||
p.pack("sid"); p.pack(sessionCtx->sessionId);
|
||||
}
|
||||
p.pack("t"); p.pack(type);
|
||||
}
|
||||
|
||||
void msgpack_unpack(const msgpack::object& o);
|
||||
};
|
||||
struct PushListener {
|
||||
std::map<InfoHash, std::vector<Listener>> listeners;
|
||||
MSGPACK_DEFINE_ARRAY(listeners)
|
||||
};
|
||||
std::map<std::string, PushListener> pushListeners_;
|
||||
proxy::ListenToken tokenPushNotif_ {0};
|
||||
#endif //OPENDHT_PUSH_NOTIFICATIONS
|
||||
};
|
||||
|
||||
}
|
||||
+265
-105
@@ -1,7 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -14,16 +15,18 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//#include "securedht.h"
|
||||
#include "def.h"
|
||||
#include "infohash.h"
|
||||
#include "value.h"
|
||||
#include "callbacks.h"
|
||||
#include "sockaddr.h"
|
||||
#include "log_enable.h"
|
||||
#include "network_utils.h"
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
@@ -38,6 +41,7 @@ namespace dht {
|
||||
|
||||
struct Node;
|
||||
class SecureDht;
|
||||
class PeerDiscovery;
|
||||
struct SecureDhtConfig;
|
||||
|
||||
/**
|
||||
@@ -46,33 +50,56 @@ struct SecureDhtConfig;
|
||||
* either wait for (expectedly frequent) calls to ::loop() or start an internal
|
||||
* thread that will update the DHT when appropriate.
|
||||
*/
|
||||
class DhtRunner {
|
||||
class OPENDHT_PUBLIC DhtRunner {
|
||||
|
||||
public:
|
||||
typedef std::function<void(NodeStatus, NodeStatus)> StatusCallback;
|
||||
using StatusCallback = std::function<void(NodeStatus, NodeStatus)>;
|
||||
|
||||
struct Config {
|
||||
SecureDhtConfig dht_config {};
|
||||
bool threaded {true};
|
||||
std::string proxy_server {};
|
||||
std::string push_node_id {};
|
||||
std::string push_token {};
|
||||
bool peer_discovery {false};
|
||||
bool peer_publish {false};
|
||||
std::shared_ptr<dht::crypto::Certificate> server_ca;
|
||||
dht::crypto::Identity client_identity;
|
||||
SockAddr bind4 {}, bind6 {};
|
||||
};
|
||||
|
||||
struct Context {
|
||||
std::shared_ptr<Logger> logger {};
|
||||
std::unique_ptr<net::DatagramSocket> sock;
|
||||
std::shared_ptr<PeerDiscovery> peerDiscovery {};
|
||||
StatusCallback statusChangedCallback {};
|
||||
CertificateStoreQuery certificateStore {};
|
||||
IdentityAnnouncedCb identityAnnouncedCb {};
|
||||
Context() {}
|
||||
};
|
||||
|
||||
DhtRunner();
|
||||
virtual ~DhtRunner();
|
||||
|
||||
void get(InfoHash id, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter f = Value::AllFilter()) {
|
||||
get(id, bindGetCb(cb), donecb, f);
|
||||
void get(InfoHash id, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter f = {}, Where w = {}) {
|
||||
get(id, bindGetCb(cb), donecb, f, w);
|
||||
}
|
||||
|
||||
void get(InfoHash id, GetCallbackSimple cb, DoneCallbackSimple donecb={}, Value::Filter f = Value::AllFilter()) {
|
||||
get(id, bindGetCb(cb), donecb, f);
|
||||
void get(InfoHash id, GetCallbackSimple cb, DoneCallbackSimple donecb={}, Value::Filter f = {}, Where w = {}) {
|
||||
get(id, bindGetCb(cb), donecb, f, w);
|
||||
}
|
||||
|
||||
void get(InfoHash hash, GetCallback vcb, DoneCallback dcb, Value::Filter f={});
|
||||
void get(InfoHash hash, GetCallback vcb, DoneCallback dcb, Value::Filter f={}, Where w = {});
|
||||
|
||||
void get(InfoHash id, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter f = Value::AllFilter()) {
|
||||
get(id, cb, bindDoneCb(donecb), f);
|
||||
void get(InfoHash id, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter f = {}, Where w = {}) {
|
||||
get(id, cb, bindDoneCb(donecb), f, w);
|
||||
}
|
||||
void get(const std::string& key, GetCallback vcb, DoneCallbackSimple dcb={}, Value::Filter f = Value::AllFilter());
|
||||
void get(const std::string& key, GetCallback vcb, DoneCallbackSimple dcb={}, Value::Filter f = {}, Where w = {});
|
||||
|
||||
template <class T>
|
||||
void get(InfoHash hash, std::function<bool(std::vector<T>&&)> cb, DoneCallbackSimple dcb={})
|
||||
{
|
||||
get(hash, [=](const std::vector<std::shared_ptr<Value>>& vals) {
|
||||
get(hash, [cb=std::move(cb)](const std::vector<std::shared_ptr<Value>>& vals) {
|
||||
return cb(unpackVector<T>(vals));
|
||||
},
|
||||
dcb,
|
||||
@@ -81,7 +108,7 @@ public:
|
||||
template <class T>
|
||||
void get(InfoHash hash, std::function<bool(T&&)> cb, DoneCallbackSimple dcb={})
|
||||
{
|
||||
get(hash, [=](const std::vector<std::shared_ptr<Value>>& vals) {
|
||||
get(hash, [cb=std::move(cb)](const std::vector<std::shared_ptr<Value>>& vals) {
|
||||
for (const auto& v : vals) {
|
||||
try {
|
||||
if (not cb(Value::unpack<T>(*v)))
|
||||
@@ -96,7 +123,7 @@ public:
|
||||
getFilterSet<T>());
|
||||
}
|
||||
|
||||
std::future<std::vector<std::shared_ptr<dht::Value>>> get(InfoHash key, Value::Filter f = Value::AllFilter()) {
|
||||
std::future<std::vector<std::shared_ptr<dht::Value>>> get(InfoHash key, Value::Filter f = {}, Where w = {}) {
|
||||
auto p = std::make_shared<std::promise<std::vector<std::shared_ptr< dht::Value >>>>();
|
||||
auto values = std::make_shared<std::vector<std::shared_ptr< dht::Value >>>();
|
||||
get(key, [=](const std::vector<std::shared_ptr<dht::Value>>& vlist) {
|
||||
@@ -105,7 +132,7 @@ public:
|
||||
}, [=](bool) {
|
||||
p->set_value(std::move(*values));
|
||||
},
|
||||
f);
|
||||
f, w);
|
||||
return p->get_future();
|
||||
}
|
||||
|
||||
@@ -122,24 +149,46 @@ public:
|
||||
return p->get_future();
|
||||
}
|
||||
|
||||
std::future<size_t> listen(InfoHash key, GetCallback vcb, Value::Filter f = Value::AllFilter());
|
||||
std::future<size_t> listen(const std::string& key, GetCallback vcb, Value::Filter f = Value::AllFilter());
|
||||
std::future<size_t> listen(InfoHash key, GetCallbackSimple cb, Value::Filter f = Value::AllFilter()) {
|
||||
return listen(key, bindGetCb(cb), f);
|
||||
void query(const InfoHash& hash, QueryCallback cb, DoneCallback done_cb = {}, Query q = {});
|
||||
void query(const InfoHash& hash, QueryCallback cb, DoneCallbackSimple done_cb = {}, Query q = {}) {
|
||||
query(hash, cb, bindDoneCb(done_cb), q);
|
||||
}
|
||||
|
||||
std::future<size_t> listen(InfoHash key, ValueCallback vcb, Value::Filter f = {}, Where w = {});
|
||||
|
||||
std::future<size_t> listen(InfoHash key, GetCallback cb, Value::Filter f={}, Where w={}) {
|
||||
return listen(key, [cb=std::move(cb)](const std::vector<Sp<Value>>& vals, bool expired){
|
||||
if (not expired)
|
||||
return cb(vals);
|
||||
return true;
|
||||
}, std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
std::future<size_t> listen(const std::string& key, GetCallback vcb, Value::Filter f = {}, Where w = {});
|
||||
std::future<size_t> listen(InfoHash key, GetCallbackSimple cb, Value::Filter f = {}, Where w = {}) {
|
||||
return listen(key, bindGetCb(cb), f, w);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::future<size_t> listen(InfoHash hash, std::function<bool(std::vector<T>&&)> cb)
|
||||
{
|
||||
return listen(hash, [=](const std::vector<std::shared_ptr<Value>>& vals) {
|
||||
return listen(hash, [cb=std::move(cb)](const std::vector<std::shared_ptr<Value>>& vals) {
|
||||
return cb(unpackVector<T>(vals));
|
||||
},
|
||||
getFilterSet<T>());
|
||||
}
|
||||
template <typename T>
|
||||
std::future<size_t> listen(InfoHash hash, std::function<bool(T&&)> cb, Value::Filter f = Value::AllFilter())
|
||||
template <class T>
|
||||
std::future<size_t> listen(InfoHash hash, std::function<bool(std::vector<T>&&, bool)> cb)
|
||||
{
|
||||
return listen(hash, [=](const std::vector<std::shared_ptr<Value>>& vals) {
|
||||
return listen(hash, [cb=std::move(cb)](const std::vector<std::shared_ptr<Value>>& vals, bool expired) {
|
||||
return cb(unpackVector<T>(vals), expired);
|
||||
},
|
||||
getFilterSet<T>());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::future<size_t> listen(InfoHash hash, std::function<bool(T&&)> cb, Value::Filter f = {}, Where w = {})
|
||||
{
|
||||
return listen(hash, [cb=std::move(cb)](const std::vector<std::shared_ptr<Value>>& vals) {
|
||||
for (const auto& v : vals) {
|
||||
try {
|
||||
if (not cb(Value::unpack<T>(*v)))
|
||||
@@ -150,50 +199,107 @@ public:
|
||||
}
|
||||
return true;
|
||||
},
|
||||
getFilterSet<T>(f));
|
||||
getFilterSet<T>(f), w);
|
||||
}
|
||||
template <typename T>
|
||||
std::future<size_t> listen(InfoHash hash, std::function<bool(T&&, bool)> cb, Value::Filter f = {}, Where w = {})
|
||||
{
|
||||
return listen(hash, [cb=std::move(cb)](const std::vector<std::shared_ptr<Value>>& vals, bool expired) {
|
||||
for (const auto& v : vals) {
|
||||
try {
|
||||
if (not cb(Value::unpack<T>(*v), expired))
|
||||
return false;
|
||||
} catch (const std::exception&) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
getFilterSet<T>(f), w);
|
||||
}
|
||||
|
||||
void cancelListen(InfoHash h, size_t token);
|
||||
void cancelListen(InfoHash h, std::shared_future<size_t> token);
|
||||
|
||||
void put(InfoHash hash, std::shared_ptr<Value> value, DoneCallback cb={}, bool permanent = false);
|
||||
void put(InfoHash hash, std::shared_ptr<Value> value, DoneCallbackSimple cb, bool permanent = false) {
|
||||
put(hash, value, bindDoneCb(cb), permanent);
|
||||
void put(InfoHash hash, std::shared_ptr<Value> value, DoneCallback cb={}, time_point created=time_point::max(), bool permanent = false);
|
||||
void put(InfoHash hash, std::shared_ptr<Value> value, DoneCallbackSimple cb, time_point created=time_point::max(), bool permanent = false) {
|
||||
put(hash, value, bindDoneCb(cb), created, permanent);
|
||||
}
|
||||
|
||||
void put(InfoHash hash, Value&& value, DoneCallback cb={}, bool permanent = false);
|
||||
void put(InfoHash hash, Value&& value, DoneCallbackSimple cb, bool permanent = false) {
|
||||
put(hash, std::forward<Value>(value), bindDoneCb(cb), permanent);
|
||||
void put(InfoHash hash, Value&& value, DoneCallback cb={}, time_point created=time_point::max(), bool permanent = false);
|
||||
void put(InfoHash hash, Value&& value, DoneCallbackSimple cb, time_point created=time_point::max(), bool permanent = false) {
|
||||
put(hash, std::forward<Value>(value), bindDoneCb(cb), created, permanent);
|
||||
}
|
||||
void put(const std::string& key, Value&& value, DoneCallbackSimple cb={}, bool permanent = false);
|
||||
void put(const std::string& key, Value&& value, DoneCallbackSimple cb={}, time_point created=time_point::max(), bool permanent = false);
|
||||
|
||||
void cancelPut(const InfoHash& h, const Value::Id& id);
|
||||
void cancelPut(const InfoHash& h, Value::Id id);
|
||||
void cancelPut(const InfoHash& h, const std::shared_ptr<Value>& value);
|
||||
|
||||
void putSigned(InfoHash hash, std::shared_ptr<Value> value, DoneCallback cb={});
|
||||
void putSigned(InfoHash hash, std::shared_ptr<Value> value, DoneCallbackSimple cb) {
|
||||
putSigned(hash, value, bindDoneCb(cb));
|
||||
void putSigned(InfoHash hash, std::shared_ptr<Value> value, DoneCallback cb={}, bool permanent = false);
|
||||
void putSigned(InfoHash hash, std::shared_ptr<Value> value, DoneCallbackSimple cb, bool permanent = false) {
|
||||
putSigned(hash, value, bindDoneCb(cb), permanent);
|
||||
}
|
||||
|
||||
void putSigned(InfoHash hash, Value&& value, DoneCallback cb={});
|
||||
void putSigned(InfoHash hash, Value&& value, DoneCallbackSimple cb) {
|
||||
putSigned(hash, std::forward<Value>(value), bindDoneCb(cb));
|
||||
void putSigned(InfoHash hash, Value&& value, DoneCallback cb={}, bool permanent = false);
|
||||
void putSigned(InfoHash hash, Value&& value, DoneCallbackSimple cb, bool permanent = false) {
|
||||
putSigned(hash, std::forward<Value>(value), bindDoneCb(cb), permanent);
|
||||
}
|
||||
void putSigned(const std::string& key, Value&& value, DoneCallbackSimple cb={});
|
||||
void putSigned(const std::string& key, Value&& value, DoneCallbackSimple cb={}, bool permanent = false);
|
||||
|
||||
void putEncrypted(InfoHash hash, InfoHash to, std::shared_ptr<Value> value, DoneCallback cb={});
|
||||
void putEncrypted(InfoHash hash, InfoHash to, std::shared_ptr<Value> value, DoneCallbackSimple cb) {
|
||||
putEncrypted(hash, to, value, bindDoneCb(cb));
|
||||
void putEncrypted(InfoHash hash, InfoHash to, std::shared_ptr<Value> value, DoneCallback cb={}, bool permanent = false);
|
||||
void putEncrypted(InfoHash hash, InfoHash to, std::shared_ptr<Value> value, DoneCallbackSimple cb, bool permanent = false) {
|
||||
putEncrypted(hash, to, value, bindDoneCb(cb), permanent);
|
||||
}
|
||||
|
||||
void putEncrypted(InfoHash hash, InfoHash to, Value&& value, DoneCallback cb={});
|
||||
void putEncrypted(InfoHash hash, InfoHash to, Value&& value, DoneCallbackSimple cb) {
|
||||
putEncrypted(hash, to, std::forward<Value>(value), bindDoneCb(cb));
|
||||
void putEncrypted(InfoHash hash, InfoHash to, Value&& value, DoneCallback cb={}, bool permanent = false);
|
||||
void putEncrypted(InfoHash hash, InfoHash to, Value&& value, DoneCallbackSimple cb, bool permanent = false) {
|
||||
putEncrypted(hash, to, std::forward<Value>(value), bindDoneCb(cb), permanent);
|
||||
}
|
||||
void putEncrypted(const std::string& key, InfoHash to, Value&& value, DoneCallback cb={});
|
||||
void putEncrypted(const std::string& key, InfoHash to, Value&& value, DoneCallback cb={}, bool permanent = false);
|
||||
|
||||
void bootstrap(const char* host, const char* service);
|
||||
void bootstrap(const std::vector<std::pair<sockaddr_storage, socklen_t>>& nodes);
|
||||
void bootstrap(const std::vector<NodeExport>& nodes);
|
||||
void putEncrypted(InfoHash hash, const std::shared_ptr<crypto::PublicKey>& to, std::shared_ptr<Value> value, DoneCallback cb={}, bool permanent = false);
|
||||
void putEncrypted(InfoHash hash, const std::shared_ptr<crypto::PublicKey>& to, std::shared_ptr<Value> value, DoneCallbackSimple cb, bool permanent = false) {
|
||||
putEncrypted(hash, to, value, bindDoneCb(cb), permanent);
|
||||
}
|
||||
|
||||
void putEncrypted(InfoHash hash, const std::shared_ptr<crypto::PublicKey>& to, Value&& value, DoneCallback cb={}, bool permanent = false);
|
||||
void putEncrypted(InfoHash hash, const std::shared_ptr<crypto::PublicKey>& to, Value&& value, DoneCallbackSimple cb, bool permanent = false) {
|
||||
putEncrypted(hash, to, std::forward<Value>(value), bindDoneCb(cb), permanent);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Insert known nodes to the routing table, without necessarly ping them.
|
||||
* Usefull to restart a node and get things running fast without putting load on the network.
|
||||
*/
|
||||
void bootstrap(std::vector<SockAddr> nodes, DoneCallbackSimple cb={});
|
||||
void bootstrap(SockAddr addr, DoneCallbackSimple cb={});
|
||||
|
||||
/**
|
||||
* Insert known nodes to the routing table, without necessarly ping them.
|
||||
* Usefull to restart a node and get things running fast without putting load on the network.
|
||||
*/
|
||||
void bootstrap(std::vector<NodeExport> nodes);
|
||||
|
||||
/**
|
||||
* Add host:service to bootstrap nodes, and ping this node.
|
||||
* DNS resolution is performed asynchronously.
|
||||
* When disconnected, all bootstrap nodes added with this method will be tried regularly until connection
|
||||
* to the DHT network is established.
|
||||
*/
|
||||
void bootstrap(const std::string& host, const std::string& service);
|
||||
void bootstrap(const std::string& hostService);
|
||||
|
||||
/**
|
||||
* Insert known nodes to the routing table, without necessarly ping them.
|
||||
* Usefull to restart a node and get things running fast without putting load on the network.
|
||||
*/
|
||||
void bootstrap(const InfoHash& id, const SockAddr& address);
|
||||
|
||||
/**
|
||||
* Clear the list of bootstrap added using bootstrap(const std::string&, const std::string&).
|
||||
*/
|
||||
void clearBootstrap();
|
||||
|
||||
/**
|
||||
* Inform the DHT of lower-layer connectivity changes.
|
||||
@@ -204,98 +310,99 @@ public:
|
||||
|
||||
void dumpTables() const;
|
||||
|
||||
/**
|
||||
* Get the public key fingerprint if an identity is used with this node, 0 otherwise.
|
||||
*/
|
||||
InfoHash getId() const;
|
||||
std::shared_ptr<crypto::PublicKey> getPublicKey() const;
|
||||
|
||||
/**
|
||||
* Get the ID of the DHT node.
|
||||
*/
|
||||
InfoHash getNodeId() const;
|
||||
|
||||
/**
|
||||
* Returns the currently bound address.
|
||||
* @param f: address family of the bound address to retreive.
|
||||
*/
|
||||
const Address& getBound(sa_family_t f = AF_INET) const {
|
||||
return (f == AF_INET) ? bound4 : bound6;
|
||||
}
|
||||
SockAddr getBound(sa_family_t f = AF_INET) const;
|
||||
|
||||
/**
|
||||
* Returns the currently bound port, in host byte order.
|
||||
* @param f: address family of the bound port to retreive.
|
||||
*/
|
||||
in_port_t getBoundPort(sa_family_t f = AF_INET) const {
|
||||
return ntohs(((sockaddr_in*)&getBound(f).first)->sin_port);
|
||||
}
|
||||
in_port_t getBoundPort(sa_family_t f = AF_INET) const;
|
||||
|
||||
std::pair<size_t, size_t> getStoreSize() const;
|
||||
|
||||
void getStorageLimit() const;
|
||||
void setStorageLimit(size_t limit = DEFAULT_STORAGE_LIMIT);
|
||||
|
||||
std::vector<NodeExport> exportNodes() const;
|
||||
|
||||
std::vector<ValuesExport> exportValues() const;
|
||||
|
||||
void setLoggers(LogMethod err = NOLOG, LogMethod warn = NOLOG, LogMethod debug = NOLOG);
|
||||
void setLogger(const Sp<Logger>& logger = {});
|
||||
void setLogger(const Logger& logger) {
|
||||
setLogger(std::make_shared<Logger>(logger));
|
||||
}
|
||||
void setLoggers(LogMethod err = {}, LogMethod warn = {}, LogMethod debug = {});
|
||||
|
||||
/**
|
||||
* Only print logs related to the given InfoHash (if given), or disable filter (if zeroes).
|
||||
*/
|
||||
void setLogFilter(const InfoHash& f = {});
|
||||
|
||||
void registerType(const ValueType& type);
|
||||
|
||||
void importValues(const std::vector<ValuesExport>& values);
|
||||
|
||||
bool isRunning() const {
|
||||
return running;
|
||||
return running != State::Idle;
|
||||
}
|
||||
|
||||
int getNodesStats(sa_family_t af, unsigned *good_return, unsigned *dubious_return, unsigned *cached_return, unsigned *incoming_return) const;
|
||||
NodeStats getNodesStats(sa_family_t af) const;
|
||||
unsigned getNodesStats(sa_family_t af, unsigned *good_return, unsigned *dubious_return, unsigned *cached_return, unsigned *incoming_return) const;
|
||||
NodeInfo getNodeInfo() const;
|
||||
void getNodeInfo(std::function<void(std::shared_ptr<NodeInfo>)>);
|
||||
|
||||
std::vector<unsigned> getNodeMessageStats(bool in = false) const;
|
||||
std::string getStorageLog() const;
|
||||
std::string getStorageLog(const InfoHash&) const;
|
||||
std::string getRoutingTablesLog(sa_family_t af) const;
|
||||
std::string getSearchesLog(sa_family_t af = 0) const;
|
||||
std::vector<Address> getPublicAddress(sa_family_t af = 0);
|
||||
std::vector<std::string> getPublicAddressStr(sa_family_t af = 0);
|
||||
std::string getSearchesLog(sa_family_t af = AF_UNSPEC) const;
|
||||
std::string getSearchLog(const InfoHash&, sa_family_t af = AF_UNSPEC) const;
|
||||
std::vector<SockAddr> getPublicAddress(sa_family_t af = AF_UNSPEC);
|
||||
std::vector<std::string> getPublicAddressStr(sa_family_t af = AF_UNSPEC);
|
||||
void getPublicAddress(std::function<void(std::vector<SockAddr>&&)>, sa_family_t af = AF_UNSPEC);
|
||||
|
||||
// securedht methods
|
||||
|
||||
void findCertificate(InfoHash hash, std::function<void(const std::shared_ptr<crypto::Certificate>)>);
|
||||
void findCertificate(InfoHash hash, std::function<void(const std::shared_ptr<crypto::Certificate>&)>);
|
||||
void registerCertificate(std::shared_ptr<crypto::Certificate> cert);
|
||||
void setLocalCertificateStore(CertificateStoreQuery&& query_method);
|
||||
|
||||
struct Config {
|
||||
SecureDhtConfig dht_config;
|
||||
bool threaded;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param port: Local port to bind. Both IPv4 and IPv6 will be tried (ANY).
|
||||
* @param identity: RSA key pair to use for cryptographic operations.
|
||||
* @param threaded: If false, ::loop() must be called periodically. Otherwise a thread is launched.
|
||||
* @param cb: Optional callback to receive general state information.
|
||||
*/
|
||||
void run(in_port_t port, const crypto::Identity identity, bool threaded = false, bool is_bootstrap = false) {
|
||||
run(port, {
|
||||
/*.dht_config = */{
|
||||
/*.node_config = */{
|
||||
/*.node_id = */{},
|
||||
/*.is_bootstrap = */is_bootstrap
|
||||
},
|
||||
/*.id = */identity
|
||||
},
|
||||
/*.threaded = */threaded
|
||||
});
|
||||
void run(in_port_t port = dht::net::DHT_DEFAULT_PORT, const crypto::Identity& identity = {}, bool threaded = true, NetId network = 0) {
|
||||
Config config;
|
||||
config.dht_config.node_config.network = network;
|
||||
config.dht_config.id = identity;
|
||||
config.threaded = threaded;
|
||||
run(port, config);
|
||||
}
|
||||
void run(in_port_t port, Config config);
|
||||
|
||||
/**
|
||||
* @param local4: Local IPv4 address and port to bind. Can be null.
|
||||
* @param local6: Local IPv6 address and port to bind. Can be null.
|
||||
* You should allways bind to a global IPv6 address.
|
||||
* @param identity: RSA key pair to use for cryptographic operations.
|
||||
* @param threaded: If false, loop() must be called periodically. Otherwise a thread is launched.
|
||||
* @param cb: Optional callback to receive general state information.
|
||||
*/
|
||||
void run(const sockaddr_in* local4, const sockaddr_in6* local6, Config config);
|
||||
void run(in_port_t port, Config& config, Context&& context = {});
|
||||
|
||||
/**
|
||||
* Same as @run(sockaddr_in, sockaddr_in6, Identity, bool, StatusCallback), but with string IP addresses and service (port).
|
||||
*/
|
||||
void run(const char* ip4, const char* ip6, const char* service, Config config);
|
||||
void run(const char* ip4, const char* ip6, const char* service, Config& config, Context&& context = {});
|
||||
|
||||
void run(const Config& config, Context&& context);
|
||||
|
||||
void setOnStatusChanged(StatusCallback&& cb) {
|
||||
statusCb = std::move(cb);
|
||||
@@ -314,7 +421,7 @@ public:
|
||||
/**
|
||||
* Gracefuly disconnect from network.
|
||||
*/
|
||||
void shutdown(ShutdownCallback cb);
|
||||
void shutdown(ShutdownCallback cb = {}, bool stop = false);
|
||||
|
||||
/**
|
||||
* Quit and wait for all threads to terminate.
|
||||
@@ -323,38 +430,91 @@ public:
|
||||
*/
|
||||
void join();
|
||||
|
||||
std::shared_ptr<PeerDiscovery> getPeerDiscovery() const { return peerDiscovery_; };
|
||||
|
||||
void setProxyServer(const std::string& proxy, const std::string& pushNodeId = "");
|
||||
|
||||
/**
|
||||
* Start or stop the proxy
|
||||
* @param proxify if we want to use the proxy
|
||||
*/
|
||||
void enableProxy(bool proxify);
|
||||
|
||||
/* Push notification methods */
|
||||
|
||||
/**
|
||||
* Updates the push notification device token
|
||||
*/
|
||||
void setPushNotificationToken(const std::string& token);
|
||||
|
||||
/**
|
||||
* Insert a push notification to process for OpenDHT
|
||||
*/
|
||||
void pushNotificationReceived(const std::map<std::string, std::string>& data);
|
||||
|
||||
/* Proxy server mothods */
|
||||
void forwardAllMessages(bool forward);
|
||||
|
||||
private:
|
||||
enum class State {
|
||||
Idle,
|
||||
Running,
|
||||
Stopping
|
||||
};
|
||||
|
||||
void doRun(const sockaddr_in* sin4, const sockaddr_in6* sin6, SecureDhtConfig config);
|
||||
time_point loop_();
|
||||
|
||||
static std::vector<std::pair<sockaddr_storage, socklen_t>> getAddrInfo(const char* host, const char* service);
|
||||
|
||||
NodeStatus getStatus() const {
|
||||
return std::max(status4, status6);
|
||||
}
|
||||
|
||||
bool checkShutdown();
|
||||
void opEnded();
|
||||
DoneCallback bindOpDoneCallback(DoneCallback&& cb);
|
||||
DoneCallbackSimple bindOpDoneCallback(DoneCallbackSimple&& cb);
|
||||
|
||||
/** DHT instance */
|
||||
std::unique_ptr<SecureDht> dht_;
|
||||
|
||||
/** true if we are currently using a proxy */
|
||||
std::atomic_bool use_proxy {false};
|
||||
|
||||
/** Current configuration */
|
||||
Config config_;
|
||||
IdentityAnnouncedCb identityAnnouncedCb_;
|
||||
|
||||
/**
|
||||
* reset dht clients
|
||||
*/
|
||||
void resetDht();
|
||||
|
||||
mutable std::mutex dht_mtx {};
|
||||
std::thread dht_thread {};
|
||||
std::condition_variable cv {};
|
||||
|
||||
std::thread rcv_thread {};
|
||||
std::mutex sock_mtx {};
|
||||
std::vector<std::pair<Blob, std::pair<sockaddr_storage, socklen_t>>> rcv {};
|
||||
net::PacketList rcv {};
|
||||
decltype(rcv) rcv_free {};
|
||||
|
||||
std::queue<std::function<void(SecureDht&)>> pending_ops_prio {};
|
||||
std::queue<std::function<void(SecureDht&)>> pending_ops {};
|
||||
std::mutex storage_mtx {};
|
||||
|
||||
std::atomic<bool> running {false};
|
||||
std::atomic<State> running {State::Idle};
|
||||
std::atomic_size_t ongoing_ops {0};
|
||||
std::vector<ShutdownCallback> shutdownCallbacks_;
|
||||
|
||||
NodeStatus status4 {NodeStatus::Disconnected},
|
||||
status6 {NodeStatus::Disconnected};
|
||||
StatusCallback statusCb {nullptr};
|
||||
|
||||
Address bound4 {};
|
||||
Address bound6 {};
|
||||
/** PeerDiscovery Parameters */
|
||||
std::shared_ptr<PeerDiscovery> peerDiscovery_;
|
||||
|
||||
/**
|
||||
* The Logger instance is used in enableProxy and other methods that
|
||||
* would create instances of classes using a common logger.
|
||||
*/
|
||||
std::shared_ptr<dht::Logger> logger_;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,377 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Vsevolod Ivanov <vsevolod.ivanov@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "def.h"
|
||||
#include "infohash.h"
|
||||
#include "crypto.h"
|
||||
|
||||
// some libraries may try to redefine snprintf
|
||||
// but restinio will use it in std namespace
|
||||
#ifdef _MSC_VER
|
||||
# undef snprintf
|
||||
# define snprintf snprintf
|
||||
#endif
|
||||
|
||||
#include <asio/ssl/context.hpp>
|
||||
#include <restinio/http_headers.hpp>
|
||||
#include <restinio/message_builders.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
|
||||
namespace Json {
|
||||
class Value;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
struct http_parser;
|
||||
struct http_parser_settings;
|
||||
}
|
||||
|
||||
namespace restinio {
|
||||
namespace impl {
|
||||
class tls_socket_t;
|
||||
}
|
||||
}
|
||||
|
||||
namespace dht {
|
||||
struct Logger;
|
||||
|
||||
namespace crypto {
|
||||
struct Certificate;
|
||||
}
|
||||
|
||||
namespace http {
|
||||
|
||||
using HandlerCb = std::function<void(const asio::error_code& ec)>;
|
||||
using BytesHandlerCb = std::function<void(const asio::error_code& ec, size_t bytes)>;
|
||||
using ConnectHandlerCb = std::function<void(const asio::error_code& ec,
|
||||
const asio::ip::tcp::endpoint& endpoint)>;
|
||||
|
||||
using ssl_socket_t = restinio::impl::tls_socket_t;
|
||||
using socket_t = asio::ip::tcp::socket;
|
||||
|
||||
class OPENDHT_PUBLIC Url
|
||||
{
|
||||
public:
|
||||
Url() = default;
|
||||
Url(const std::string& url);
|
||||
std::string url;
|
||||
std::string protocol {"http"};
|
||||
std::string host;
|
||||
std::string service;
|
||||
std::string target;
|
||||
std::string query;
|
||||
std::string fragment;
|
||||
|
||||
std::string toString() const;
|
||||
};
|
||||
|
||||
class OPENDHT_PUBLIC Connection : public std::enable_shared_from_this<Connection>
|
||||
{
|
||||
public:
|
||||
Connection(asio::io_context& ctx, const bool ssl = true, std::shared_ptr<dht::Logger> l = {});
|
||||
Connection(asio::io_context& ctx, std::shared_ptr<dht::crypto::Certificate> server_ca,
|
||||
const dht::crypto::Identity& identity, std::shared_ptr<dht::Logger> l = {});
|
||||
~Connection();
|
||||
|
||||
inline unsigned int id() const { return id_; };
|
||||
bool is_open() const;
|
||||
bool is_ssl() const;
|
||||
void checkOcsp(bool check = true) { checkOcsp_ = check; }
|
||||
|
||||
void set_ssl_verification(const std::string& hostname, const asio::ssl::verify_mode verify_mode);
|
||||
|
||||
asio::streambuf& input();
|
||||
std::istream& data() { return istream_; }
|
||||
|
||||
std::string read_bytes(size_t bytes = 0);
|
||||
std::string read_until(const char delim);
|
||||
|
||||
void async_connect(std::vector<asio::ip::tcp::endpoint>&& endpoints, ConnectHandlerCb);
|
||||
void async_handshake(HandlerCb cb);
|
||||
void async_write(BytesHandlerCb cb);
|
||||
void async_read_until(const char* delim, BytesHandlerCb cb);
|
||||
void async_read_until(char delim, BytesHandlerCb cb);
|
||||
void async_read(size_t bytes, BytesHandlerCb cb);
|
||||
void async_read_some(size_t bytes, BytesHandlerCb cb);
|
||||
|
||||
void timeout(const std::chrono::seconds timeout, HandlerCb cb = {});
|
||||
void close();
|
||||
|
||||
private:
|
||||
|
||||
template<typename T>
|
||||
T wrapCallabck(T cb) const {
|
||||
return [t=shared_from_this(),cb=std::move(cb)](auto ...params) {
|
||||
cb(params...);
|
||||
};
|
||||
}
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
|
||||
unsigned int id_;
|
||||
static std::atomic_uint ids_;
|
||||
|
||||
asio::io_context& ctx_;
|
||||
std::unique_ptr<socket_t> socket_;
|
||||
std::shared_ptr<asio::ssl::context> ssl_ctx_;
|
||||
std::unique_ptr<ssl_socket_t> ssl_socket_;
|
||||
|
||||
asio::ip::tcp::endpoint endpoint_;
|
||||
|
||||
asio::streambuf write_buf_;
|
||||
asio::streambuf read_buf_;
|
||||
std::istream istream_;
|
||||
|
||||
std::unique_ptr<asio::steady_timer> timeout_timer_;
|
||||
std::shared_ptr<dht::Logger> logger_;
|
||||
bool checkOcsp_ {false};
|
||||
};
|
||||
|
||||
/**
|
||||
* Session value associated with a connection_id_t key.
|
||||
*/
|
||||
struct ListenerSession
|
||||
{
|
||||
ListenerSession() = default;
|
||||
dht::InfoHash hash;
|
||||
std::future<size_t> token;
|
||||
std::shared_ptr<restinio::response_builder_t<restinio::chunked_output_t>> response;
|
||||
};
|
||||
|
||||
/* @class Resolver
|
||||
* @brief The purpose is to only resolve once to avoid mutliple dns requests per operation.
|
||||
*/
|
||||
class OPENDHT_PUBLIC Resolver
|
||||
{
|
||||
public:
|
||||
using ResolverCb = std::function<void(const asio::error_code& ec,
|
||||
const std::vector<asio::ip::tcp::endpoint>& endpoints)>;
|
||||
|
||||
Resolver(asio::io_context& ctx, const std::string& url, std::shared_ptr<dht::Logger> logger = {});
|
||||
Resolver(asio::io_context& ctx, const std::string& host, const std::string& service,
|
||||
const bool ssl = false, std::shared_ptr<dht::Logger> logger = {});
|
||||
|
||||
// use already resolved endpoints with classes using this resolver
|
||||
Resolver(asio::io_context& ctx, std::vector<asio::ip::tcp::endpoint> endpoints,
|
||||
const bool ssl = false, std::shared_ptr<dht::Logger> logger = {});
|
||||
Resolver(asio::io_context& ctx, const std::string& url, std::vector<asio::ip::tcp::endpoint> endpoints,
|
||||
std::shared_ptr<dht::Logger> logger = {});
|
||||
|
||||
~Resolver();
|
||||
|
||||
inline const Url& get_url() const {
|
||||
return url_;
|
||||
}
|
||||
|
||||
void add_callback(ResolverCb cb, sa_family_t family = AF_UNSPEC);
|
||||
|
||||
std::shared_ptr<Logger> getLogger() const {
|
||||
return logger_;
|
||||
}
|
||||
|
||||
private:
|
||||
void resolve(const std::string& host, const std::string& service);
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
|
||||
Url url_;
|
||||
asio::error_code ec_;
|
||||
asio::ip::tcp::resolver resolver_;
|
||||
std::shared_ptr<bool> destroyed_;
|
||||
std::vector<asio::ip::tcp::endpoint> endpoints_;
|
||||
|
||||
bool completed_ {false};
|
||||
std::queue<ResolverCb> cbs_;
|
||||
|
||||
std::shared_ptr<dht::Logger> logger_;
|
||||
};
|
||||
|
||||
class Request;
|
||||
|
||||
struct Response
|
||||
{
|
||||
unsigned status_code {0};
|
||||
std::map<std::string, std::string> headers;
|
||||
std::string body;
|
||||
bool aborted {false};
|
||||
std::weak_ptr<Request> request;
|
||||
};
|
||||
|
||||
class OPENDHT_PUBLIC Request : public std::enable_shared_from_this<Request>
|
||||
{
|
||||
public:
|
||||
enum class State {
|
||||
CREATED,
|
||||
SENDING,
|
||||
HEADER_RECEIVED,
|
||||
RECEIVING,
|
||||
DONE
|
||||
};
|
||||
using OnStatusCb = std::function<void(unsigned status_code)>;
|
||||
using OnDataCb = std::function<void(const char* at, size_t length)>;
|
||||
using OnStateChangeCb = std::function<void(State state, const Response& response)>;
|
||||
using OnJsonCb = std::function<void(Json::Value value, const Response& response)>;
|
||||
using OnDoneCb = std::function<void(const Response& response)>;
|
||||
|
||||
// resolves implicitly
|
||||
Request(asio::io_context& ctx, const std::string& url, const Json::Value& json, OnJsonCb jsoncb,
|
||||
std::shared_ptr<dht::Logger> logger = {});
|
||||
Request(asio::io_context& ctx, const std::string& url, OnJsonCb jsoncb,
|
||||
std::shared_ptr<dht::Logger> logger = {});
|
||||
|
||||
Request(asio::io_context& ctx, const std::string& url, std::shared_ptr<dht::Logger> logger = {});
|
||||
Request(asio::io_context& ctx, const std::string& host, const std::string& service,
|
||||
const bool ssl = false, std::shared_ptr<dht::Logger> logger = {});
|
||||
Request(asio::io_context& ctx, const std::string& url, OnDoneCb onDone, std::shared_ptr<dht::Logger> logger = {});
|
||||
|
||||
// user defined resolver
|
||||
Request(asio::io_context& ctx, std::shared_ptr<Resolver> resolver, sa_family_t family = AF_UNSPEC);
|
||||
Request(asio::io_context& ctx, std::shared_ptr<Resolver> resolver, const std::string& target, sa_family_t family = AF_UNSPEC);
|
||||
|
||||
// user defined resolved endpoints
|
||||
Request(asio::io_context& ctx, std::vector<asio::ip::tcp::endpoint>&& endpoints,
|
||||
const bool ssl = false, std::shared_ptr<dht::Logger> logger = {});
|
||||
|
||||
~Request();
|
||||
|
||||
inline unsigned int id() const { return id_; };
|
||||
void set_connection(std::shared_ptr<Connection> connection);
|
||||
std::shared_ptr<Connection> get_connection() const;
|
||||
inline const Url& get_url() const {
|
||||
return resolver_->get_url();
|
||||
};
|
||||
|
||||
/** The previous request in case of redirect following */
|
||||
std::shared_ptr<Request> getPrevious() const {
|
||||
return prev_.lock();
|
||||
}
|
||||
|
||||
inline std::string& to_string() {
|
||||
return request_;
|
||||
}
|
||||
|
||||
void set_certificate_authority(std::shared_ptr<dht::crypto::Certificate> certificate);
|
||||
void set_identity(const dht::crypto::Identity& identity);
|
||||
void set_logger(std::shared_ptr<dht::Logger> logger);
|
||||
|
||||
/**
|
||||
* Define the HTTP header/body as per https://tools.ietf.org/html/rfc7230.
|
||||
*/
|
||||
void set_header(restinio::http_request_header_t header);
|
||||
void set_method(restinio::http_method_id_t method);
|
||||
void set_target(std::string target);
|
||||
void set_header_field(restinio::http_field_t field, std::string value);
|
||||
void set_connection_type(restinio::http_connection_header_t connection);
|
||||
void set_body(std::string body);
|
||||
void set_auth(const std::string& username, const std::string& password);
|
||||
|
||||
void add_on_status_callback(OnStatusCb cb);
|
||||
void add_on_body_callback(OnDataCb cb);
|
||||
void add_on_state_change_callback(OnStateChangeCb cb);
|
||||
void add_on_done_callback(OnDoneCb cb);
|
||||
|
||||
void send();
|
||||
|
||||
/** Send and block for response */
|
||||
const Response& await();
|
||||
|
||||
/**
|
||||
* User action to cancel the Request and call the completion callbacks.
|
||||
*/
|
||||
void cancel();
|
||||
void terminate(const asio::error_code& ec);
|
||||
|
||||
private:
|
||||
using OnCompleteCb = std::function<void()>;
|
||||
|
||||
struct Callbacks {
|
||||
OnStatusCb on_status;
|
||||
OnDataCb on_header_field;
|
||||
OnDataCb on_header_value;
|
||||
OnDataCb on_body;
|
||||
OnStateChangeCb on_state_change;
|
||||
};
|
||||
|
||||
static std::string getRelativePath(const Url& origin, const std::string& path);
|
||||
|
||||
void notify_state_change(State state);
|
||||
|
||||
void build();
|
||||
|
||||
void init_default_headers();
|
||||
/**
|
||||
* Initialized and wraps the http_parser callbacks with our user defined callbacks.
|
||||
*/
|
||||
void init_parser();
|
||||
|
||||
void connect(std::vector<asio::ip::tcp::endpoint>&& endpoints, HandlerCb cb = {});
|
||||
|
||||
void post();
|
||||
|
||||
void handle_request(const asio::error_code& ec);
|
||||
void handle_response(const asio::error_code& ec, size_t bytes);
|
||||
|
||||
void onHeadersComplete();
|
||||
void onBody(const char* at, size_t length);
|
||||
void onComplete();
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
|
||||
std::shared_ptr<dht::Logger> logger_;
|
||||
|
||||
restinio::http_request_header_t header_;
|
||||
std::map<restinio::http_field_t, std::string> headers_;
|
||||
restinio::http_connection_header_t connection_type_ {restinio::http_connection_header_t::close};
|
||||
std::string body_;
|
||||
|
||||
Callbacks cbs_;
|
||||
State state_;
|
||||
|
||||
dht::crypto::Identity client_identity_;
|
||||
std::shared_ptr<dht::crypto::Certificate> server_ca_;
|
||||
std::string service_;
|
||||
std::string host_;
|
||||
|
||||
unsigned int id_;
|
||||
static std::atomic_uint ids_;
|
||||
asio::io_context& ctx_;
|
||||
sa_family_t family_ = AF_UNSPEC;
|
||||
std::shared_ptr<Connection> conn_;
|
||||
std::shared_ptr<Resolver> resolver_;
|
||||
|
||||
Response response_ {};
|
||||
std::string request_;
|
||||
std::atomic<bool> finishing_ {false};
|
||||
std::unique_ptr<http_parser> parser_;
|
||||
std::unique_ptr<http_parser_settings> parser_s_;
|
||||
|
||||
// Next request in case of redirect following
|
||||
std::shared_ptr<Request> next_;
|
||||
std::weak_ptr<Request> prev_;
|
||||
unsigned num_redirect {0};
|
||||
bool follow_redirect {true};
|
||||
};
|
||||
|
||||
} // namespace http
|
||||
} // namespace dht
|
||||
|
||||
@@ -0,0 +1,533 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Nicolas Reynaud <nicolas.reynaud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
#include <bitset>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "../value.h"
|
||||
#include "../dhtrunner.h"
|
||||
#include "../infohash.h"
|
||||
|
||||
namespace dht {
|
||||
namespace indexation {
|
||||
|
||||
/*!
|
||||
* @class Prefix
|
||||
* @brief A blob structure which prefixes a Key in the PHT.
|
||||
* @details
|
||||
* Since the PHT structure is a "trie", every node in this structure have a
|
||||
* label which is defined by the path from the root of the trie to the node. If
|
||||
* the node in question is a leaf, *the label is a prefix of all the keys
|
||||
* contained in the leaf*.
|
||||
*/
|
||||
struct OPENDHT_PUBLIC Prefix {
|
||||
Prefix() {}
|
||||
Prefix(InfoHash h) : size_(h.size() * 8), content_(h.begin(), h.end()) { }
|
||||
Prefix(const Blob& d, const Blob& f={}) : size_(d.size()*8), flags_(f), content_(d) { }
|
||||
|
||||
Prefix(const Prefix& p, size_t first) :
|
||||
size_(std::min(first, p.content_.size()*8)),
|
||||
content_(Blob(p.content_.begin(), p.content_.begin()+size_/8))
|
||||
{
|
||||
|
||||
auto rem = size_ % 8;
|
||||
if ( not p.flags_.empty() ) {
|
||||
flags_ = Blob(p.flags_.begin(), p.flags_.begin()+size_/8);
|
||||
if (rem)
|
||||
flags_.push_back(p.flags_[size_/8] & (0xFF << (8 - rem)));
|
||||
}
|
||||
|
||||
if (rem)
|
||||
content_.push_back(p.content_[size_/8] & (0xFF << (8 - rem)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a sub prefix of the Prefix
|
||||
*
|
||||
* @param len lenght of the prefix to get, could be negative
|
||||
* if len is negativ then you will get the prefix
|
||||
* of size of the previous prefix minus len
|
||||
*
|
||||
* @return Sub-prefix of size len or if len is negative sub-prefix of size
|
||||
* of prefix minus len
|
||||
*
|
||||
* @throw out_of_range if len is larger than size of the content
|
||||
*/
|
||||
Prefix getPrefix(ssize_t len) const {
|
||||
if ((size_t)std::abs(len) >= content_.size() * 8)
|
||||
throw std::out_of_range("len larger than prefix size.");
|
||||
if (len < 0)
|
||||
len += size_;
|
||||
|
||||
return Prefix(*this, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flags are considered as active if flag is empty or if the flag
|
||||
* at pos 'pos' is active
|
||||
ee *
|
||||
* @see isActiveBit in private function
|
||||
*/
|
||||
bool isFlagActive(size_t pos) const {
|
||||
return flags_.empty() or isActiveBit(flags_, pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see isActiveBit in private function
|
||||
*/
|
||||
bool isContentBitActive(size_t pos) const {
|
||||
return isActiveBit(content_, pos);
|
||||
}
|
||||
|
||||
Prefix getFullSize() { return Prefix(*this, content_.size()*8); }
|
||||
|
||||
/**
|
||||
* This methods gets the prefix of its sibling in the PHT structure.
|
||||
*
|
||||
* @return The prefix of this sibling.
|
||||
*/
|
||||
Prefix getSibling() const {
|
||||
Prefix copy = *this;
|
||||
if ( size_ )
|
||||
copy.swapContentBit(size_ - 1);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
InfoHash hash() const {
|
||||
Blob copy(content_);
|
||||
copy.push_back(size_);
|
||||
return InfoHash::get(copy);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method count total of bit in common between 2 prefix
|
||||
*
|
||||
* @param p1 first prefix to compared
|
||||
* @param p2 second prefix to compared
|
||||
* @return Lenght of the larger common prefix between both
|
||||
*/
|
||||
static inline unsigned commonBits(const Prefix& p1, const Prefix& p2) {
|
||||
unsigned i, j;
|
||||
uint8_t x;
|
||||
auto longest_prefix_size = std::min(p1.size_, p2.size_);
|
||||
|
||||
for (i = 0; i < longest_prefix_size; i++) {
|
||||
if (p1.content_.data()[i] != p2.content_.data()[i]
|
||||
or not p1.isFlagActive(i)
|
||||
or not p2.isFlagActive(i) ) {
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == longest_prefix_size)
|
||||
return 8*longest_prefix_size;
|
||||
|
||||
x = p1.content_.data()[i] ^ p2.content_.data()[i];
|
||||
|
||||
j = 0;
|
||||
while ((x & 0x80) == 0) {
|
||||
x <<= 1;
|
||||
j++;
|
||||
}
|
||||
|
||||
return 8 * i + j;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see doc of swap private function
|
||||
*/
|
||||
void swapContentBit(size_t bit) {
|
||||
swapBit(content_, bit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see doc of swap private function
|
||||
*/
|
||||
void swapFlagBit(size_t bit) {
|
||||
swapBit(flags_, bit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see doc of addPadding private function
|
||||
*/
|
||||
void addPaddingContent(size_t size) {
|
||||
content_ = addPadding(content_, size);
|
||||
}
|
||||
|
||||
void updateFlags() {
|
||||
/* Fill first known bit */
|
||||
auto csize = size_ - flags_.size() * 8;
|
||||
while(csize >= 8) {
|
||||
flags_.push_back(0xFF);
|
||||
csize -= 8;
|
||||
}
|
||||
|
||||
/* if needed fill remaining bit */
|
||||
if ( csize )
|
||||
flags_.push_back(0xFF << (8 - csize));
|
||||
|
||||
/* Complet vector space missing */
|
||||
for ( auto i = flags_.size(); i < content_.size(); i++ )
|
||||
flags_.push_back(0xFF);
|
||||
}
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
size_t size_ {0};
|
||||
|
||||
/* Will contain flags according to content_.
|
||||
If flags_[i] == 0, then content_[i] is unknown
|
||||
else if flags_[i] == 1, then content_[i] is known */
|
||||
Blob flags_ {};
|
||||
Blob content_ {};
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Add a padding to the input blob
|
||||
*
|
||||
* @param toP : Prefix where to add a padding
|
||||
* @param size : Final size of the prefix with padding
|
||||
*
|
||||
* @return Copy of the input Blob but with a padding
|
||||
*/
|
||||
Blob addPadding(Blob toP, size_t size) {
|
||||
Blob copy = toP;
|
||||
for ( auto i = copy.size(); i < size; i++ )
|
||||
copy.push_back(0);
|
||||
|
||||
swapBit(copy, size_ + 1);
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the bit a pos 'pos' is active, i.e. equal to 1
|
||||
*
|
||||
* @param b : Blob to check
|
||||
* @param pos : Position to check
|
||||
*
|
||||
* @return true if the bit is equal to 1, false otherwise
|
||||
*
|
||||
* @throw out_of_range if bit is superior to blob size * 8
|
||||
*/
|
||||
bool isActiveBit(const Blob &b, size_t pos) const {
|
||||
if ( pos >= content_.size() * 8 )
|
||||
throw std::out_of_range("Can't detect active bit at pos, pos larger than prefix size or empty prefix");
|
||||
|
||||
return ((b[pos / 8] >> (7 - (pos % 8)) ) & 1) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap bit at position bit [from 0 to 1 and vice-versa]
|
||||
*
|
||||
* @param b : Blob to swap
|
||||
* @param bit : Bit to swap on b
|
||||
*
|
||||
* @return the input prefix with the bit at pos 'bit' swapped
|
||||
*
|
||||
* @throw out_of_range if bit is superior to blob size * 8
|
||||
*/
|
||||
void swapBit(Blob &b, size_t bit) {
|
||||
if ( bit >= b.size() * 8 )
|
||||
throw std::out_of_range("bit larger than prefix size.");
|
||||
|
||||
size_t offset_bit = (8 - bit) % 8;
|
||||
b[bit / 8] ^= (1 << offset_bit);
|
||||
}
|
||||
};
|
||||
|
||||
using Value = std::pair<InfoHash, dht::Value::Id>;
|
||||
struct OPENDHT_PUBLIC IndexEntry : public dht::Value::Serializable<IndexEntry> {
|
||||
static const ValueType TYPE;
|
||||
|
||||
virtual void unpackValue(const dht::Value& v) {
|
||||
Serializable<IndexEntry>::unpackValue(v);
|
||||
name = v.user_type;
|
||||
}
|
||||
|
||||
virtual dht::Value packValue() const {
|
||||
auto pack = Serializable<IndexEntry>::packValue();
|
||||
pack.user_type = name;
|
||||
return pack;
|
||||
}
|
||||
|
||||
Blob prefix;
|
||||
Value value;
|
||||
std::string name;
|
||||
MSGPACK_DEFINE_MAP(prefix, value)
|
||||
};
|
||||
|
||||
class OPENDHT_PUBLIC Pht {
|
||||
static constexpr const char* INVALID_KEY = "Key does not match the PHT key spec.";
|
||||
|
||||
/* Prefixes the user_type for all dht values put on the DHT */
|
||||
static constexpr const char* INDEX_PREFIX = "index.pht.";
|
||||
|
||||
public:
|
||||
|
||||
/* This is the maximum number of entries per node. This parameter is
|
||||
* critical and influences the traffic a lot during a lookup operation.
|
||||
*/
|
||||
static constexpr const size_t MAX_NODE_ENTRY_COUNT {16};
|
||||
|
||||
/* A key for a an index entry */
|
||||
using Key = std::map<std::string, Blob>;
|
||||
|
||||
/* Specifications of the keys. It defines the number, the length and the
|
||||
* serialization order of fields. */
|
||||
using KeySpec = std::map<std::string, size_t>;
|
||||
using LookupCallback = std::function<void(std::vector<std::shared_ptr<Value>>& values, const Prefix& p)>;
|
||||
|
||||
typedef void (*LookupCallbackRaw)(std::vector<std::shared_ptr<Value>>* values, Prefix* p, void *user_data);
|
||||
static LookupCallback
|
||||
bindLookupCb(LookupCallbackRaw raw_cb, void* user_data) {
|
||||
if (not raw_cb) return {};
|
||||
return [=](std::vector<std::shared_ptr<Value>>& values, const Prefix& p) {
|
||||
raw_cb((std::vector<std::shared_ptr<Value>>*) &values, (Prefix*) &p, user_data);
|
||||
};
|
||||
}
|
||||
using LookupCallbackSimple = std::function<void(std::vector<std::shared_ptr<Value>>& values)>;
|
||||
typedef void (*LookupCallbackSimpleRaw)(std::vector<std::shared_ptr<Value>>* values, void *user_data);
|
||||
static LookupCallbackSimple
|
||||
bindLookupCbSimple(LookupCallbackSimpleRaw raw_cb, void* user_data) {
|
||||
if (not raw_cb) return {};
|
||||
return [=](std::vector<std::shared_ptr<Value>>& values) {
|
||||
raw_cb((std::vector<std::shared_ptr<Value>>*) &values, user_data);
|
||||
};
|
||||
}
|
||||
|
||||
Pht(std::string name, KeySpec k_spec, std::shared_ptr<DhtRunner> dht)
|
||||
: name_(INDEX_PREFIX + name), canary_(name_ + ".canary"), keySpec_(k_spec), dht_(dht) {}
|
||||
|
||||
virtual ~Pht () { }
|
||||
|
||||
/**
|
||||
* Lookup a key for a value.
|
||||
*/
|
||||
void lookup(Key k, LookupCallback cb = {}, DoneCallbackSimple done_cb = {}, bool exact_match = true);
|
||||
void lookup(Key k, LookupCallbackSimple cb = {}, DoneCallbackSimple done_cb = {}, bool exact_match = true)
|
||||
{
|
||||
lookup(k, [cb=std::move(cb)](std::vector<std::shared_ptr<Value>>& values, Prefix) { cb(values); }, done_cb, exact_match);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function which call the private one.
|
||||
*
|
||||
* @param k : Key to insert [i.e map of string, blob]
|
||||
* @param v : Value to insert
|
||||
* @param done_cb : Callbakc which going to be call when all the insert is done
|
||||
*/
|
||||
void insert(Key k, Value v, DoneCallbackSimple done_cb = {}) {
|
||||
Prefix p = linearize(k);
|
||||
|
||||
auto lo = std::make_shared<int>(0);
|
||||
auto hi = std::make_shared<int>(p.size_);
|
||||
|
||||
IndexEntry entry;
|
||||
entry.value = v;
|
||||
entry.prefix = p.content_;
|
||||
entry.name = name_;
|
||||
|
||||
Pht::insert(p, entry, lo, hi, clock::now(), true, done_cb);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Insert function which really insert onto the pht
|
||||
*
|
||||
* @param kp : Prefix to insert (linearize the the key)
|
||||
* @param entry : Entry created from the value
|
||||
* @param lo : Lowest point to start in the prefix
|
||||
* @param hi : Highest point to end in the prefix
|
||||
* @param time_p : Timepoint to use for the insertion into the dht (must be < now)
|
||||
* @param check_split : If this flag is true then the algoritm will not use the merge algorithm
|
||||
* @param done_cb : Callback to call when the insert is done
|
||||
*/
|
||||
|
||||
void insert(const Prefix& kp, IndexEntry entry, std::shared_ptr<int> lo, std::shared_ptr<int> hi, time_point time_p,
|
||||
bool check_split, DoneCallbackSimple done_cb = {});
|
||||
|
||||
class Cache {
|
||||
public:
|
||||
/**
|
||||
* Insert all needed node into the tree according to a prefix
|
||||
* @param p : Prefix that we need to insert
|
||||
*/
|
||||
void insert(const Prefix& p);
|
||||
|
||||
/**
|
||||
* Lookup into the tree to return the maximum prefix length in the cache tree
|
||||
*
|
||||
* @param p : Prefix that we are looking for
|
||||
*
|
||||
* @return : The size of the longest prefix known in the cache between 0 and p.size_
|
||||
*/
|
||||
|
||||
int lookup(const Prefix& p);
|
||||
|
||||
private:
|
||||
static constexpr const size_t MAX_ELEMENT {1024};
|
||||
static constexpr const std::chrono::minutes NODE_EXPIRE_TIME {5};
|
||||
|
||||
struct Node {
|
||||
time_point last_reply; /* Made the assocation between leaves and leaves multimap */
|
||||
std::shared_ptr<Node> parent; /* Share_ptr to the parent, it allow the self destruction of tree */
|
||||
std::weak_ptr<Node> left_child; /* Left child, for bit equal to 1 */
|
||||
std::weak_ptr<Node> right_child; /* Right child, for bit equal to 0 */
|
||||
};
|
||||
|
||||
std::weak_ptr<Node> root_; /* Root of the tree */
|
||||
|
||||
/**
|
||||
* This mutlimap contains all prefix insert in the tree in time order
|
||||
* We could then delete the last one if there is too much node
|
||||
* The tree will self destroy is branch ( thanks to share_ptr )
|
||||
*/
|
||||
std::multimap<time_point, std::shared_ptr<Node>> leaves_;
|
||||
};
|
||||
|
||||
/* Callback used for insert value by using the pht */
|
||||
using RealInsertCallback = std::function<void(const Prefix& p, IndexEntry entry)>;
|
||||
using LookupCallbackWrapper = std::function<void(std::vector<std::shared_ptr<IndexEntry>>& values, const Prefix& p)>;
|
||||
|
||||
/**
|
||||
* Performs a step in the lookup operation. Each steps are performed
|
||||
* asynchronously.
|
||||
*
|
||||
* @param k : Prefix on which the lookup is performed
|
||||
* @param lo : lowest bound on the prefix (where to start)
|
||||
* @param hi : highest bound on the prefix (where to stop)
|
||||
* @param vals : Shared ptr to a vector of IndexEntry (going to contains all values found)
|
||||
* @param cb : Callback to use at the end of the lookupStep (call on the value of vals)
|
||||
* @param done_cb : Callback at the end of the lookupStep
|
||||
* @param max_common_prefix_len: used in the inexacte lookup match case, indicate the longest common prefix found
|
||||
* @param start : If start is set then lo and hi will be ignore for the first step, if the step fail lo and hi will be used
|
||||
* @param all_values : If all value is true then all value met during the lookupstep will be in the vector vals
|
||||
*/
|
||||
void lookupStep(Prefix k, std::shared_ptr<int> lo, std::shared_ptr<int> hi,
|
||||
std::shared_ptr<std::vector<std::shared_ptr<IndexEntry>>> vals,
|
||||
LookupCallbackWrapper cb, DoneCallbackSimple done_cb,
|
||||
std::shared_ptr<unsigned> max_common_prefix_len,
|
||||
int start = -1, bool all_values = false);
|
||||
|
||||
/**
|
||||
* Apply the zcurve algorithm on the list of input prefix
|
||||
*
|
||||
* @param all_prefix : Vector of prefix to interleave
|
||||
*
|
||||
* @return The output prefix where all flags and content are interleaves
|
||||
*/
|
||||
Prefix zcurve(const std::vector<Prefix>& all_prefix) const;
|
||||
|
||||
/**
|
||||
* Linearizes the key into a unidimensional key. A pht only takes
|
||||
* unidimensional key.
|
||||
*
|
||||
* @param Key The initial key.
|
||||
*
|
||||
* @return the prefix of the linearized key.
|
||||
*/
|
||||
virtual Prefix linearize(Key k) const;
|
||||
|
||||
/**
|
||||
* Looking where to put the data cause if there is free space on the node
|
||||
* above then this node will became the real leave.
|
||||
*
|
||||
* @param p Share_ptr on the Prefix to check
|
||||
* @param entry The entry to put at the prefix p
|
||||
* @param end_cb Callback to use at the end of counting
|
||||
*/
|
||||
void getRealPrefix(const std::shared_ptr<Prefix>& p, IndexEntry entry, RealInsertCallback end_cb );
|
||||
|
||||
/**
|
||||
* Looking where to put the data cause if there is free space on the node
|
||||
* above then this node will became the real leave.
|
||||
*
|
||||
* @param p Share_ptr on the Prefix to check
|
||||
* @param entry The entry to put at the prefix p
|
||||
* @param end_cb Callback to use at the end of counting
|
||||
*/
|
||||
void checkPhtUpdate(Prefix p, IndexEntry entry, time_point time_p);
|
||||
|
||||
/**
|
||||
* Search for the split location by comparing 'compared' to all values in vals.
|
||||
*
|
||||
* @param compared : Value which going to be compared
|
||||
* @param vals : The vector of values to compare with comapred
|
||||
* @return position compared diverge from all others
|
||||
*/
|
||||
static size_t findSplitLocation(const Prefix& compared, const std::vector<std::shared_ptr<IndexEntry>>& vals) {
|
||||
for ( size_t i = 0; i < compared.content_.size() * 8 - 1; i++ )
|
||||
for ( auto const& v : vals)
|
||||
if ( Prefix(v->prefix).isContentBitActive(i) != compared.isContentBitActive(i) )
|
||||
return i + 1;
|
||||
return compared.content_.size() * 8 - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put canary from the split point until the last known canary and add the prefix at the good place
|
||||
*
|
||||
* @param insert : Prefix to insertm but also prefix which going to check where we need to split
|
||||
* @param vals : Vector of vals for the comparaison
|
||||
* @param entry : Entry to put on the pht
|
||||
* @param end_cb : Callback to apply to the insert prefi (here does the insert)
|
||||
*/
|
||||
void split(const Prefix& insert, const std::vector<std::shared_ptr<IndexEntry>>& vals, IndexEntry entry, RealInsertCallback end_cb);
|
||||
|
||||
/**
|
||||
* Tells if the key is valid according to the key spec.
|
||||
*/
|
||||
bool validKey(const Key& k) const {
|
||||
return k.size() == keySpec_.size() and
|
||||
std::equal(k.begin(), k.end(), keySpec_.begin(),
|
||||
[&](const Key::value_type& key, const KeySpec::value_type& key_spec) {
|
||||
return key.first == key_spec.first and key.second.size() <= key_spec.second;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the canary token on the node responsible for the specified
|
||||
* Prefix.
|
||||
*/
|
||||
void updateCanary(Prefix p);
|
||||
|
||||
const std::string name_;
|
||||
const std::string canary_;
|
||||
const KeySpec keySpec_;
|
||||
Cache cache_;
|
||||
std::shared_ptr<DhtRunner> dht_;
|
||||
};
|
||||
|
||||
} /* indexation */
|
||||
} /* dht */
|
||||
|
||||
+272
-83
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -13,12 +13,14 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "def.h"
|
||||
#include "rng.h"
|
||||
|
||||
#include <msgpack.hpp>
|
||||
|
||||
#ifndef _WIN32
|
||||
@@ -38,87 +40,144 @@ typedef uint16_t in_port_t;
|
||||
#include <iomanip>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <string_view>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
// bytes
|
||||
#define HASH_LEN 20u
|
||||
#include <cstddef>
|
||||
|
||||
namespace dht {
|
||||
|
||||
/**
|
||||
* Represents an InfoHash.
|
||||
* An InfoHash is a byte array of HASH_LEN bytes.
|
||||
* InfoHashes identify nodes and values in the Dht.
|
||||
*/
|
||||
class InfoHash final : public std::array<uint8_t, HASH_LEN> {
|
||||
public:
|
||||
constexpr InfoHash() : std::array<uint8_t, HASH_LEN>() {}
|
||||
constexpr InfoHash(const std::array<uint8_t, HASH_LEN>& h) : std::array<uint8_t, HASH_LEN>(h) {}
|
||||
InfoHash(const uint8_t* h, size_t h_len) : std::array<uint8_t, HASH_LEN>() {
|
||||
if (h_len < HASH_LEN)
|
||||
fill(0);
|
||||
else
|
||||
std::copy_n(h, HASH_LEN, begin());
|
||||
}
|
||||
using byte = uint8_t;
|
||||
|
||||
namespace crypto {
|
||||
OPENDHT_PUBLIC void hash(const uint8_t* data, size_t data_length, uint8_t* hash, size_t hash_length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an Hash,
|
||||
* a byte array of N bytes.
|
||||
* Hashes identify nodes and values in the Dht.
|
||||
*/
|
||||
template <size_t N>
|
||||
class OPENDHT_PUBLIC Hash {
|
||||
public:
|
||||
using T = std::array<uint8_t, N>;
|
||||
typedef typename T::iterator iterator;
|
||||
typedef typename T::const_iterator const_iterator;
|
||||
|
||||
Hash() noexcept {
|
||||
data_.fill(0);
|
||||
}
|
||||
Hash(const uint8_t* h, size_t data_len) {
|
||||
if (data_len < N)
|
||||
data_.fill(0);
|
||||
else
|
||||
std::copy_n(h, N, data_.begin());
|
||||
}
|
||||
/**
|
||||
* Constructor from an hexadecimal string (without "0x").
|
||||
* hex must be at least 2.HASH_LEN characters long.
|
||||
* If too long, only the first 2.HASH_LEN characters are read.
|
||||
*/
|
||||
explicit InfoHash(const std::string& hex);
|
||||
explicit Hash(std::string_view hex) {
|
||||
if (hex.size() < 2*N)
|
||||
data_.fill(0);
|
||||
else
|
||||
fromString(hex.data());
|
||||
}
|
||||
|
||||
InfoHash(const msgpack::object& o) {
|
||||
Hash(const msgpack::object& o) {
|
||||
msgpack_unpack(o);
|
||||
}
|
||||
|
||||
static constexpr size_t size() noexcept { return N; }
|
||||
const uint8_t* data() const { return data_.data(); }
|
||||
uint8_t* data() { return data_.data(); }
|
||||
iterator begin() { return data_.begin(); }
|
||||
const_iterator cbegin() const { return data_.cbegin(); }
|
||||
iterator end() { return data_.end(); }
|
||||
const_iterator cend() const { return data_.cend(); }
|
||||
|
||||
static constexpr inline Hash zero() noexcept { return Hash{}; }
|
||||
|
||||
bool operator==(const Hash& h) const {
|
||||
auto a = reinterpret_cast<const uint32_t*>(data_.data());
|
||||
auto b = reinterpret_cast<const uint32_t*>(h.data_.data());
|
||||
constexpr unsigned n = N / sizeof(uint32_t);
|
||||
for (unsigned i=0; i < n; i++)
|
||||
if (a[i] != b[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool operator!=(const Hash& h) const { return !(*this == h); }
|
||||
|
||||
bool operator<(const Hash& o) const {
|
||||
for(unsigned i = 0; i < N; i++) {
|
||||
if(data_[i] != o.data_[i])
|
||||
return data_[i] < o.data_[i];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Hash operator^(const Hash& o) const {
|
||||
Hash result;
|
||||
for(auto i = 0u; i < N; i++) {
|
||||
result[i] = data_[i] ^ o.data_[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
auto a = reinterpret_cast<const uint32_t*>(data_.data());
|
||||
auto b = reinterpret_cast<const uint32_t*>(data_.data() + N);
|
||||
for (; a != b; a++) {
|
||||
if (*a)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t& operator[](size_t index) { return data_[index]; }
|
||||
const uint8_t& operator[](size_t index) const { return data_[index]; }
|
||||
|
||||
/**
|
||||
* Find the lowest 1 bit in an id.
|
||||
* Result will allways be lower than 8*HASH_LEN
|
||||
* Result will allways be lower than 8*N
|
||||
*/
|
||||
inline unsigned lowbit() const {
|
||||
inline int lowbit() const {
|
||||
int i, j;
|
||||
for(i = HASH_LEN-1; i >= 0; i--)
|
||||
if((*this)[i] != 0)
|
||||
for(i = N-1; i >= 0; i--)
|
||||
if(data_[i] != 0)
|
||||
break;
|
||||
if(i < 0)
|
||||
return -1;
|
||||
for(j = 7; j >= 0; j--)
|
||||
if(((*this)[i] & (0x80 >> j)) != 0)
|
||||
if((data_[i] & (0x80 >> j)) != 0)
|
||||
break;
|
||||
return 8 * i + j;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forget about the ``XOR-metric''. An id is just a path from the
|
||||
* root of the tree, so bits are numbered from the start.
|
||||
*/
|
||||
#ifdef WIN32_NATIVE
|
||||
static inline int cmp(const InfoHash& __restrict id1, const InfoHash& __restrict id2) {
|
||||
#else
|
||||
static inline int cmp(const InfoHash& __restrict__ id1, const InfoHash& __restrict__ id2) {
|
||||
#endif
|
||||
return std::memcmp(id1.data(), id2.data(), HASH_LEN);
|
||||
static inline int cmp(const Hash& id1, const Hash& id2) {
|
||||
return std::memcmp(id1.data_.data(), id2.data_.data(), N);
|
||||
}
|
||||
|
||||
/** Find how many bits two ids have in common. */
|
||||
static inline unsigned
|
||||
commonBits(const InfoHash& id1, const InfoHash& id2)
|
||||
commonBits(const Hash& id1, const Hash& id2)
|
||||
{
|
||||
unsigned i, j;
|
||||
uint8_t x;
|
||||
for(i = 0; i < HASH_LEN; i++) {
|
||||
if(id1[i] != id2[i])
|
||||
for(i = 0; i < N; i++) {
|
||||
if(id1.data_[i] != id2.data_[i])
|
||||
break;
|
||||
}
|
||||
|
||||
if(i == HASH_LEN)
|
||||
return 8*HASH_LEN;
|
||||
if(i == N)
|
||||
return 8*N;
|
||||
|
||||
x = id1[i] ^ id2[i];
|
||||
x = id1.data_[i] ^ id2.data_[i];
|
||||
|
||||
j = 0;
|
||||
while((x & 0x80) == 0) {
|
||||
@@ -131,18 +190,14 @@ public:
|
||||
|
||||
/** Determine whether id1 or id2 is closer to this */
|
||||
int
|
||||
xorCmp(const InfoHash& id1, const InfoHash& id2) const
|
||||
xorCmp(const Hash& id1, const Hash& id2) const
|
||||
{
|
||||
for(unsigned i = 0; i < HASH_LEN; i++) {
|
||||
uint8_t xor1, xor2;
|
||||
if(id1[i] == id2[i])
|
||||
for (unsigned i = 0; i < N; i++) {
|
||||
if(id1.data_[i] == id2.data_[i])
|
||||
continue;
|
||||
xor1 = id1[i] ^ (*this)[i];
|
||||
xor2 = id2[i] ^ (*this)[i];
|
||||
if(xor1 < xor2)
|
||||
return -1;
|
||||
else
|
||||
return 1;
|
||||
uint8_t xor1 = id1.data_[i] ^ data_[i];
|
||||
uint8_t xor2 = id2.data_[i] ^ data_[i];
|
||||
return (xor1 < xor2) ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -150,7 +205,7 @@ public:
|
||||
bool
|
||||
getBit(unsigned nbit) const
|
||||
{
|
||||
auto& num = *(cbegin()+(nbit/8));
|
||||
auto& num = *(data_.cbegin()+(nbit/8));
|
||||
unsigned bit = 7 - (nbit % 8);
|
||||
return (num >> bit) & 1;
|
||||
}
|
||||
@@ -158,69 +213,203 @@ public:
|
||||
void
|
||||
setBit(unsigned nbit, bool b)
|
||||
{
|
||||
auto& num = (*this)[nbit/8];
|
||||
auto& num = data_[nbit/8];
|
||||
unsigned bit = 7 - (nbit % 8);
|
||||
num ^= (-b ^ num) & (1 << bit);
|
||||
}
|
||||
|
||||
double
|
||||
toFloat() const
|
||||
{
|
||||
double toFloat() const {
|
||||
using D = size_t;
|
||||
double v = 0.;
|
||||
for (unsigned i = 0; i < std::min<size_t>(HASH_LEN, sizeof(unsigned)-1); i++)
|
||||
v += *(cbegin()+i)/(double)(1<<(8*(i+1)));
|
||||
for (size_t i = 0; i < std::min<size_t>(N, sizeof(D)-1); i++)
|
||||
v += *(data_.cbegin()+i) / (double)((D)1 << 8*(i+1));
|
||||
return v;
|
||||
}
|
||||
|
||||
bool
|
||||
operator<(const InfoHash& o) const {
|
||||
for(unsigned i = 0; i < HASH_LEN; i++) {
|
||||
if((*this)[i] != o[i])
|
||||
return (*this)[i] < o[i];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline InfoHash get(const std::string& data) {
|
||||
static inline Hash get(std::string_view data) {
|
||||
return get((const uint8_t*)data.data(), data.size());
|
||||
}
|
||||
|
||||
static inline InfoHash get(const std::vector<uint8_t>& data) {
|
||||
static inline Hash get(const std::vector<uint8_t>& data) {
|
||||
return get(data.data(), data.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the hash from a given data buffer of size data_len.
|
||||
*/
|
||||
static InfoHash get(const uint8_t* data, size_t data_len);
|
||||
static Hash get(const uint8_t* data, size_t data_len)
|
||||
{
|
||||
Hash ret;
|
||||
crypto::hash(data, data_len, ret.data(), N);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static InfoHash getRandom();
|
||||
static Hash getRandom();
|
||||
|
||||
friend std::ostream& operator<< (std::ostream& s, const InfoHash& h);
|
||||
template <typename Rd>
|
||||
static Hash getRandom(Rd&);
|
||||
|
||||
template <size_t M>
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const Hash<M>& h);
|
||||
|
||||
template <size_t M>
|
||||
OPENDHT_PUBLIC friend std::istream& operator>> (std::istream& s, Hash<M>& h);
|
||||
|
||||
const char* to_c_str() const;
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& pk) const
|
||||
{
|
||||
pk.pack_bin(HASH_LEN);
|
||||
pk.pack_bin_body((char*)data(), HASH_LEN);
|
||||
pk.pack_bin(N);
|
||||
pk.pack_bin_body((char*)data_.data(), N);
|
||||
}
|
||||
|
||||
void msgpack_unpack(msgpack::object o) {
|
||||
if (o.type != msgpack::type::BIN or o.via.bin.size != HASH_LEN)
|
||||
if (o.type != msgpack::type::BIN or o.via.bin.size != N)
|
||||
throw msgpack::type_error();
|
||||
std::copy_n(o.via.bin.ptr, HASH_LEN, data());
|
||||
std::copy_n(o.via.bin.ptr, N, data_.data());
|
||||
}
|
||||
|
||||
private:
|
||||
T data_;
|
||||
void fromString(const char*);
|
||||
};
|
||||
|
||||
static constexpr const InfoHash zeroes {};
|
||||
#define HASH_LEN 20u
|
||||
using InfoHash = Hash<HASH_LEN>;
|
||||
using h256 = Hash<32>;
|
||||
using PkId = h256;
|
||||
|
||||
struct NodeExport {
|
||||
template <size_t N>
|
||||
std::ostream& operator<< (std::ostream& s, const Hash<N>& h)
|
||||
{
|
||||
s.write(h.to_c_str(), N*2);
|
||||
return s;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
std::istream& operator>> (std::istream& s, Hash<N>& h)
|
||||
{
|
||||
std::array<char, h.size()*2> dat;
|
||||
s.exceptions(std::istream::eofbit | std::istream::failbit);
|
||||
s.read(&(*dat.begin()), dat.size());
|
||||
fromString(dat.data());
|
||||
return s;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
void
|
||||
Hash<N>::fromString(const char* in) {
|
||||
auto hex2bin = [](char c) -> uint8_t {
|
||||
if (c >= 'a' and c <= 'f') return 10 + c - 'a';
|
||||
else if (c >= 'A' and c <= 'F') return 10 + c - 'A';
|
||||
else if (c >= '0' and c <= '9') return c - '0';
|
||||
else throw std::domain_error("not an hex character");
|
||||
};
|
||||
try {
|
||||
for (size_t i=0; i<N; i++)
|
||||
data_[i] = (hex2bin(in[2*i]) << 4) | hex2bin(in[2*i+1]);
|
||||
} catch (const std::domain_error&) {
|
||||
data_.fill(0);
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
Hash<N>
|
||||
Hash<N>::getRandom()
|
||||
{
|
||||
Hash h;
|
||||
crypto::random_device rdev;
|
||||
std::uniform_int_distribution<uint32_t> rand_int;
|
||||
auto a = reinterpret_cast<uint32_t*>(h.data());
|
||||
auto b = reinterpret_cast<uint32_t*>(h.data() + h.size());
|
||||
std::generate(a, b, std::bind(rand_int, std::ref(rdev)));
|
||||
return h;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
template <typename Rd>
|
||||
Hash<N>
|
||||
Hash<N>::getRandom(Rd& rdev)
|
||||
{
|
||||
Hash h;
|
||||
std::uniform_int_distribution<uint32_t> rand_int;
|
||||
auto a = reinterpret_cast<uint32_t*>(h.data());
|
||||
auto b = reinterpret_cast<uint32_t*>(h.data() + h.size());
|
||||
std::generate(a, b, std::bind(rand_int, std::ref(rdev)));
|
||||
return h;
|
||||
}
|
||||
|
||||
struct alignas(std::max_align_t) HexMap : public std::array<std::array<char, 2>, 256> {
|
||||
HexMap() {
|
||||
for (size_t i=0; i<size(); i++) {
|
||||
auto& e = (*this)[i];
|
||||
e[0] = hex_digits[(i >> 4) & 0x0F];
|
||||
e[1] = hex_digits[i & 0x0F];
|
||||
}
|
||||
}
|
||||
private:
|
||||
static constexpr const char* hex_digits = "0123456789abcdef";
|
||||
};
|
||||
|
||||
OPENDHT_PUBLIC extern const HexMap hex_map;
|
||||
|
||||
inline std::string
|
||||
toHex(const uint8_t* data, size_t size) {
|
||||
std::string ret(size * 2, '\0');
|
||||
for (size_t i=0; i<size; i++) {
|
||||
auto b = ret.data()+i*2;
|
||||
const auto& m = hex_map[data[i]];
|
||||
*((uint16_t*)b) = *((uint16_t*)&m);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline std::string
|
||||
toHex(const std::vector<uint8_t>& data) {
|
||||
return toHex(data.data(), data.size());
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
const char*
|
||||
Hash<N>::to_c_str() const
|
||||
{
|
||||
alignas(std::max_align_t) thread_local std::array<char, N*2+1> buf;
|
||||
for (size_t i=0; i<N; i++) {
|
||||
auto b = buf.data()+i*2;
|
||||
const auto& m = hex_map[data_[i]];
|
||||
*((uint16_t*)b) = *((uint16_t*)&m);
|
||||
}
|
||||
return buf.data();
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
std::string
|
||||
Hash<N>::toString() const
|
||||
{
|
||||
return std::string(to_c_str(), N*2);
|
||||
}
|
||||
|
||||
struct OPENDHT_PUBLIC NodeExport {
|
||||
InfoHash id;
|
||||
sockaddr_storage ss;
|
||||
socklen_t sslen;
|
||||
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& pk) const
|
||||
{
|
||||
pk.pack_map(2);
|
||||
pk.pack(std::string("id"));
|
||||
pk.pack(id);
|
||||
pk.pack(std::string("addr"));
|
||||
pk.pack_bin(sslen);
|
||||
pk.pack_bin_body((char*)&ss, sslen);
|
||||
}
|
||||
|
||||
void msgpack_unpack(msgpack::object o);
|
||||
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const NodeExport& h);
|
||||
};
|
||||
|
||||
}
|
||||
+30
-43
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
@@ -14,18 +14,24 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "dhtrunner.h"
|
||||
#include "def.h"
|
||||
#include "log_enable.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
|
||||
namespace dht {
|
||||
|
||||
class DhtRunner;
|
||||
|
||||
/**
|
||||
* Logging-related functions
|
||||
*/
|
||||
namespace log {
|
||||
|
||||
/**
|
||||
@@ -61,48 +67,29 @@ constexpr const Color::Modifier yellow(Color::FG_YELLOW);
|
||||
/**
|
||||
* Print va_list to std::ostream (used for logging).
|
||||
*/
|
||||
void
|
||||
printLog(std::ostream& s, char const* m, va_list args) {
|
||||
static constexpr int BUF_SZ = 8192;
|
||||
char buffer[BUF_SZ];
|
||||
int ret = vsnprintf(buffer, sizeof(buffer), m, args);
|
||||
if (ret < 0)
|
||||
return;
|
||||
s.write(buffer, std::min(ret, BUF_SZ));
|
||||
if (ret >= BUF_SZ)
|
||||
s << "[[TRUNCATED]]";
|
||||
s.put('\n');
|
||||
}
|
||||
OPENDHT_PUBLIC void
|
||||
printLog(std::ostream &s, char const *m, va_list args);
|
||||
|
||||
void
|
||||
enableLogging(dht::DhtRunner& dht)
|
||||
{
|
||||
dht.setLoggers(
|
||||
[](char const* m, va_list args){ std::cerr << red; printLog(std::cerr, m, args); std::cerr << def; },
|
||||
[](char const* m, va_list args){ std::cout << yellow; printLog(std::cout, m, args); std::cout << def; },
|
||||
[](char const* m, va_list args){ printLog(std::cout, m, args); }
|
||||
);
|
||||
}
|
||||
OPENDHT_PUBLIC
|
||||
std::shared_ptr<Logger> getStdLogger();
|
||||
|
||||
void
|
||||
enableFileLogging(dht::DhtRunner& dht, const std::string& path)
|
||||
{
|
||||
auto logfile = std::make_shared<std::fstream>();
|
||||
logfile->open(path, std::ios::out);
|
||||
OPENDHT_PUBLIC
|
||||
std::shared_ptr<Logger> getFileLogger(const std::string &path);
|
||||
|
||||
dht.setLoggers(
|
||||
[=](char const* m, va_list args){ printLog(*logfile, m, args); },
|
||||
[=](char const* m, va_list args){ printLog(*logfile, m, args); },
|
||||
[=](char const* m, va_list args){ printLog(*logfile, m, args); }
|
||||
);
|
||||
}
|
||||
OPENDHT_PUBLIC
|
||||
std::shared_ptr<Logger> getSyslogLogger(const char* name);
|
||||
|
||||
void
|
||||
disableLogging(dht::DhtRunner& dht)
|
||||
{
|
||||
dht.setLoggers(dht::NOLOG, dht::NOLOG, dht::NOLOG);
|
||||
}
|
||||
OPENDHT_PUBLIC void
|
||||
enableLogging(dht::DhtRunner &dht);
|
||||
|
||||
OPENDHT_PUBLIC void
|
||||
enableFileLogging(dht::DhtRunner &dht, const std::string &path);
|
||||
|
||||
OPENDHT_PUBLIC void
|
||||
disableLogging(dht::DhtRunner &dht);
|
||||
|
||||
OPENDHT_PUBLIC void
|
||||
enableSyslog(dht::DhtRunner &dht, const char* name);
|
||||
|
||||
} /* log */
|
||||
} /* dht */
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "infohash.h"
|
||||
|
||||
#include <cstdarg>
|
||||
|
||||
#ifndef OPENDHT_LOG
|
||||
#define OPENDHT_LOG true
|
||||
#endif
|
||||
|
||||
namespace dht {
|
||||
|
||||
// Logging related utility functions
|
||||
|
||||
/**
|
||||
* Wrapper for logging methods
|
||||
*/
|
||||
struct LogMethod {
|
||||
LogMethod() = default;
|
||||
|
||||
LogMethod(LogMethod&& l) : func(std::move(l.func)) {}
|
||||
LogMethod(const LogMethod& l) : func(l.func) {}
|
||||
|
||||
LogMethod& operator=(dht::LogMethod&& l) {
|
||||
func = std::forward<LogMethod>(l.func);
|
||||
return *this;
|
||||
}
|
||||
LogMethod& operator=(const dht::LogMethod& l) {
|
||||
func = l.func;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
explicit LogMethod(T&& t) : func(std::forward<T>(t)) {}
|
||||
|
||||
template<typename T>
|
||||
LogMethod(const T& t) : func(t) {}
|
||||
|
||||
void operator()(char const* format, ...) const {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
func(format, args);
|
||||
va_end(args);
|
||||
}
|
||||
inline void log(char const* format, va_list args) const {
|
||||
func(format, args);
|
||||
}
|
||||
explicit operator bool() const {
|
||||
return (bool)func;
|
||||
}
|
||||
|
||||
void logPrintable(const uint8_t *buf, size_t buflen) const {
|
||||
std::string buf_clean(buflen, '\0');
|
||||
for (size_t i=0; i<buflen; i++)
|
||||
buf_clean[i] = isprint(buf[i]) ? buf[i] : '.';
|
||||
(*this)("%s", buf_clean.c_str());
|
||||
}
|
||||
private:
|
||||
std::function<void(char const*, va_list)> func;
|
||||
};
|
||||
|
||||
struct Logger {
|
||||
LogMethod ERR = {};
|
||||
LogMethod WARN = {};
|
||||
LogMethod DBG = {};
|
||||
|
||||
Logger() = default;
|
||||
Logger(LogMethod&& err, LogMethod&& warn, LogMethod&& dbg)
|
||||
: ERR(std::move(err)), WARN(std::move(warn)), DBG(std::move(dbg)) {}
|
||||
void setFilter(const InfoHash& f) {
|
||||
filter_ = f;
|
||||
filterEnable_ = static_cast<bool>(filter_);
|
||||
}
|
||||
inline void log0(const LogMethod& logger, char const* format, va_list args) const {
|
||||
#if OPENDHT_LOG
|
||||
if (logger and not filterEnable_)
|
||||
logger.log(format, args);
|
||||
#endif
|
||||
}
|
||||
inline void log1(const LogMethod& logger, const InfoHash& f, char const* format, va_list args) const {
|
||||
#if OPENDHT_LOG
|
||||
if (logger and (not filterEnable_ or f == filter_))
|
||||
logger.log(format, args);
|
||||
#endif
|
||||
}
|
||||
inline void log2(const LogMethod& logger, const InfoHash& f1, const InfoHash& f2, char const* format, va_list args) const {
|
||||
#if OPENDHT_LOG
|
||||
if (logger and (not filterEnable_ or f1 == filter_ or f2 == filter_))
|
||||
logger.log(format, args);
|
||||
#endif
|
||||
}
|
||||
inline void d(char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log0(DBG, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
inline void d(const InfoHash& f, char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log1(DBG, f, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
inline void d(const InfoHash& f1, const InfoHash& f2, char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log2(DBG, f1, f2, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
inline void w(char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log0(WARN, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
inline void w(const InfoHash& f, char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log1(WARN, f, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
inline void w(const InfoHash& f1, const InfoHash& f2, char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log2(WARN, f1, f2, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
inline void e(char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log0(ERR, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
inline void e(const InfoHash& f, char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log1(ERR, f, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
inline void e(const InfoHash& f1, const InfoHash& f2, char const* format, ...) const {
|
||||
#if OPENDHT_LOG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log2(ERR, f1, f2, format, args);
|
||||
va_end(args);
|
||||
#endif
|
||||
}
|
||||
private:
|
||||
bool filterEnable_ {false};
|
||||
InfoHash filter_ {};
|
||||
};
|
||||
|
||||
}
|
||||
+383
-257
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -14,11 +14,9 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "node_cache.h"
|
||||
@@ -28,26 +26,40 @@
|
||||
#include "scheduler.h"
|
||||
#include "utils.h"
|
||||
#include "rng.h"
|
||||
#include "rate_limiter.h"
|
||||
#include "log_enable.h"
|
||||
#include "network_utils.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <queue>
|
||||
|
||||
namespace dht {
|
||||
namespace net {
|
||||
|
||||
struct Request;
|
||||
struct Socket;
|
||||
struct TransId;
|
||||
|
||||
#ifndef MSG_CONFIRM
|
||||
#define MSG_CONFIRM 0
|
||||
#endif
|
||||
|
||||
struct NetworkConfig {
|
||||
NetId network {0};
|
||||
ssize_t max_req_per_sec {0};
|
||||
ssize_t max_peer_req_per_sec {0};
|
||||
};
|
||||
|
||||
class DhtProtocolException : public DhtException {
|
||||
public:
|
||||
// sent to another peer (http-like).
|
||||
static const constexpr uint16_t NON_AUTHORITATIVE_INFORMATION {203}; /* incomplete request packet. */
|
||||
static const constexpr uint16_t UNAUTHORIZED {401}; /* wrong tokens. */
|
||||
static const constexpr uint16_t NOT_FOUND {404}; /* storage not found */
|
||||
// for internal use (custom).
|
||||
static const constexpr uint16_t INVALID_TID_SIZE {421}; /* id was truncated. */
|
||||
static const constexpr uint16_t UNKNOWN_TID {422}; /* unknown tid */
|
||||
@@ -58,23 +70,40 @@ public:
|
||||
static const std::string LISTEN_WRONG_TOKEN; /* wrong token in "listen" request */
|
||||
static const std::string PUT_NO_INFOHASH; /* no infohash in "put" request */
|
||||
static const std::string PUT_WRONG_TOKEN; /* got "put" request with wrong token */
|
||||
static const std::string STORAGE_NOT_FOUND; /* got access request for an unknown storage */
|
||||
static const std::string PUT_INVALID_ID; /* invalid id in "put" request */
|
||||
|
||||
DhtProtocolException(uint16_t code, const std::string& msg="", InfoHash failing_node_id={})
|
||||
: DhtException(msg), msg(msg), code(code), failing_node_id(failing_node_id) {}
|
||||
|
||||
std::string getMsg() const { return msg; }
|
||||
const std::string& getMsg() const { return msg; }
|
||||
uint16_t getCode() const { return code; }
|
||||
const InfoHash getNodeId() const { return failing_node_id; }
|
||||
const InfoHash& getNodeId() const { return failing_node_id; }
|
||||
|
||||
private:
|
||||
std::string msg;
|
||||
uint16_t code;
|
||||
const InfoHash failing_node_id;
|
||||
InfoHash failing_node_id;
|
||||
};
|
||||
|
||||
struct ParsedMessage;
|
||||
|
||||
/**
|
||||
* Answer for a request.
|
||||
*/
|
||||
struct RequestAnswer {
|
||||
Blob ntoken {};
|
||||
Value::Id vid {};
|
||||
std::vector<Sp<Value>> values {};
|
||||
std::vector<Value::Id> refreshed_values {};
|
||||
std::vector<Value::Id> expired_values {};
|
||||
std::vector<Sp<FieldValueIndex>> fields {};
|
||||
std::vector<Sp<Node>> nodes4 {};
|
||||
std::vector<Sp<Node>> nodes6 {};
|
||||
RequestAnswer() {}
|
||||
RequestAnswer(ParsedMessage&& msg);
|
||||
};
|
||||
|
||||
/*!
|
||||
* @class NetworkEngine
|
||||
* @brief An abstraction of communication protocol on the network.
|
||||
@@ -91,247 +120,308 @@ struct ParsedMessage;
|
||||
* @param onGetValues callback for "get values" request.
|
||||
* @param onListen callback for "listen" request.
|
||||
* @param onAnnounce callback for "announce" request.
|
||||
* @param onRefresh callback for "refresh" request.
|
||||
*/
|
||||
class NetworkEngine final {
|
||||
struct TransPrefix : public std::array<uint8_t, 2> {
|
||||
TransPrefix(const std::string& str) : std::array<uint8_t, 2>({{(uint8_t)str[0], (uint8_t)str[1]}}) {}
|
||||
static const TransPrefix PING;
|
||||
static const TransPrefix FIND_NODE;
|
||||
static const TransPrefix GET_VALUES;
|
||||
static const TransPrefix ANNOUNCE_VALUES;
|
||||
static const TransPrefix LISTEN;
|
||||
};
|
||||
public:
|
||||
|
||||
/* Transaction-ids are 4-bytes long, with the first two bytes identifying
|
||||
* the kind of request, and the remaining two a sequence number in
|
||||
* host order.
|
||||
*/
|
||||
struct TransId final : public std::array<uint8_t, 4> {
|
||||
static const constexpr uint16_t INVALID {0};
|
||||
|
||||
TransId() {}
|
||||
TransId(const std::array<char, 4>& o) { std::copy(o.begin(), o.end(), begin()); }
|
||||
TransId(const TransPrefix prefix, uint16_t seqno = 0) {
|
||||
std::copy_n(prefix.begin(), prefix.size(), begin());
|
||||
*reinterpret_cast<uint16_t*>(data()+prefix.size()) = seqno;
|
||||
}
|
||||
|
||||
TransId(const char* q, size_t l) : array<uint8_t, 4>() {
|
||||
if (l > 4) {
|
||||
length = 0;
|
||||
} else {
|
||||
std::copy_n(q, l, begin());
|
||||
length = l;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t getTid() const {
|
||||
return *reinterpret_cast<const uint16_t*>(&(*this)[2]);
|
||||
}
|
||||
|
||||
bool matches(const TransPrefix prefix, uint16_t* tid = nullptr) const {
|
||||
if (std::equal(begin(), begin()+2, prefix.begin())) {
|
||||
if (tid)
|
||||
*tid = getTid();
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned length {4};
|
||||
};
|
||||
|
||||
/*!
|
||||
* @class RequestAnswer
|
||||
* @brief Answer for a request.
|
||||
* @details
|
||||
* Answer for a request to be (de)serialized. Used for reponding to a node
|
||||
* and looking up the response from a node.
|
||||
*/
|
||||
struct RequestAnswer {
|
||||
Blob ntoken {};
|
||||
Value::Id vid {};
|
||||
std::vector<std::shared_ptr<Value>> values {};
|
||||
std::vector<std::shared_ptr<Node>> nodes4 {};
|
||||
std::vector<std::shared_ptr<Node>> nodes6 {};
|
||||
RequestAnswer() {}
|
||||
RequestAnswer(ParsedMessage&& msg);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Cancel a request. Setting req->cancelled = true is not enough in the case
|
||||
* a request is "persistent".
|
||||
*/
|
||||
void cancelRequest(std::shared_ptr<Request>& req);
|
||||
|
||||
void connectivityChanged();
|
||||
|
||||
class NetworkEngine final
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* Called when we receive an error message.
|
||||
*/
|
||||
std::function<void(Sp<Request>, DhtProtocolException)> onError;
|
||||
|
||||
/**
|
||||
* @brief when we receive an error message.
|
||||
*
|
||||
* @param node (type: std::shared_ptr<Request>) the associated request for
|
||||
* which we got an error;
|
||||
*/
|
||||
std::function<void(std::shared_ptr<Request>, DhtProtocolException)> onError;
|
||||
/**
|
||||
* @brief when a new node happens.
|
||||
*
|
||||
* Called for every packets received for handling new nodes contacting us.
|
||||
*
|
||||
* @param id (type: InfoHash) id of the node.
|
||||
* @param saddr (type: sockaddr*) sockaddr* pointer containing address ip information.
|
||||
* @param saddr_len (type: socklen_t) lenght of the sockaddr struct.
|
||||
* @param confirm (type: int) 1 if the node sent a message, 2 if it sent us a reply.
|
||||
* @param node: the node
|
||||
* @param confirm: 1 if the node sent a message, 2 if it sent us a reply.
|
||||
*/
|
||||
std::function<void(const std::shared_ptr<Node>&, int)> onNewNode;
|
||||
std::function<void(const Sp<Node>&, int)> onNewNode;
|
||||
/**
|
||||
* @brief when an addres is reported from a distant node.
|
||||
* Called when an addres is reported from a requested node.
|
||||
*
|
||||
* @param id (type: InfoHash) id of the node.
|
||||
* @param saddr (type: sockaddr*) sockaddr* pointer containing address ip information.
|
||||
* @param h: id
|
||||
* @param saddr_len (type: socklen_t) lenght of the sockaddr struct.
|
||||
*/
|
||||
std::function<void(const InfoHash&, sockaddr*, socklen_t)> onReportedAddr;
|
||||
std::function<void(const InfoHash&, const SockAddr&)> onReportedAddr;
|
||||
/**
|
||||
* @brief on ping request callback.
|
||||
* Called on ping reception.
|
||||
*
|
||||
* @param node (type: std::shared_ptr<Node>) the requesting node.
|
||||
* @param node (type: Sp<Node>) the requesting node.
|
||||
*/
|
||||
std::function<RequestAnswer(std::shared_ptr<Node>)> onPing {};
|
||||
std::function<RequestAnswer(Sp<Node>)> onPing {};
|
||||
/**
|
||||
* @brief on find node request callback.
|
||||
* Called on find node request.
|
||||
*
|
||||
* @param node (type: std::shared_ptr<Node>) the requesting node.
|
||||
* @param vhash (type: InfoHash) hash of the value of interest.
|
||||
* @param node (type: Sp<Node>) the requesting node.
|
||||
* @param h (type: InfoHash) hash of the value of interest.
|
||||
* @param want (type: want_t) states if nodes sent in the response are ipv4
|
||||
* or ipv6.
|
||||
*/
|
||||
std::function<RequestAnswer(std::shared_ptr<Node>,
|
||||
InfoHash&,
|
||||
want_t)> onFindNode {};
|
||||
std::function<RequestAnswer(Sp<Node>, const InfoHash&, want_t)> onFindNode {};
|
||||
/**
|
||||
* @brief on "get values" request callback.
|
||||
* Called on "get values" request.
|
||||
*
|
||||
* @param node (type: std::shared_ptr<Node>) the requesting node.
|
||||
* @param vhash (type: InfoHash) hash of the value of interest.
|
||||
* @param node (type: Sp<Node>) the requesting node.
|
||||
* @param h (type: InfoHash) hash of the value of interest.
|
||||
* @param want (type: want_t) states if nodes sent in the response are ipv4
|
||||
* or ipv6.
|
||||
*/
|
||||
std::function<RequestAnswer(std::shared_ptr<Node>,
|
||||
InfoHash&,
|
||||
want_t)> onGetValues {};
|
||||
std::function<RequestAnswer(Sp<Node>, const InfoHash&, want_t, const Query&)> onGetValues {};
|
||||
/**
|
||||
* @brief on listen request callback.
|
||||
* Called on listen request.
|
||||
*
|
||||
* @param node (type: std::shared_ptr<Node>) the requesting node.
|
||||
* @param vhash (type: InfoHash) hash of the value of interest.
|
||||
* @param node (type: Sp<Node>) the requesting node.
|
||||
* @param h (type: InfoHash) hash of the value of interest.
|
||||
* @param token (type: Blob) security token.
|
||||
* @param rid (type: uint16_t) request id.
|
||||
*/
|
||||
std::function<RequestAnswer(std::shared_ptr<Node>,
|
||||
InfoHash&,
|
||||
Blob&,
|
||||
uint16_t)> onListen {};
|
||||
std::function<RequestAnswer(Sp<Node>,
|
||||
const InfoHash&,
|
||||
const Blob&,
|
||||
Tid,
|
||||
const Query&,
|
||||
int)> onListen {};
|
||||
/**
|
||||
* @brief on announce request callback.
|
||||
* Called on announce request.
|
||||
*
|
||||
* @param node (type: std::shared_ptr<Node>) the requesting node.
|
||||
* @param vhash (type: InfoHash) hash of the value of interest.
|
||||
* @param node (type: Sp<Node>) the requesting node.
|
||||
* @param h (type: InfoHash) hash of the value of interest.
|
||||
* @param token (type: Blob) security token.
|
||||
* @param values (type: std::vector<std::shared_ptr<Value>>) values to store.
|
||||
* @param values (type: std::vector<Sp<Value>>) values to store.
|
||||
* @param created (type: time_point) time when the value was created.
|
||||
*/
|
||||
std::function<RequestAnswer(std::shared_ptr<Node>,
|
||||
InfoHash&,
|
||||
Blob&,
|
||||
std::vector<std::shared_ptr<Value>>,
|
||||
time_point)> onAnnounce {};
|
||||
std::function<RequestAnswer(Sp<Node>,
|
||||
const InfoHash&,
|
||||
const Blob&,
|
||||
const std::vector<Sp<Value>>&,
|
||||
const time_point&)> onAnnounce {};
|
||||
/**
|
||||
* Called on refresh request.
|
||||
*
|
||||
* @param node (type: Sp<Node>) the requesting node.
|
||||
* @param h (type: InfoHash) hash of the value of interest.
|
||||
* @param token (type: Blob) security token.
|
||||
* @param vid (type: Value::id) the value id.
|
||||
*/
|
||||
std::function<RequestAnswer(Sp<Node>,
|
||||
const InfoHash&,
|
||||
const Blob&,
|
||||
const Value::Id&)> onRefresh {};
|
||||
|
||||
public:
|
||||
using RequestCb = std::function<void(const Request&, RequestAnswer&&)>;
|
||||
using RequestErrorCb = std::function<bool(const Request&, DhtProtocolException&&)>;
|
||||
using RequestExpiredCb = std::function<void(const Request&, bool)>;
|
||||
|
||||
NetworkEngine(Logger& log, Scheduler& scheduler) : myid(zeroes), DHT_LOG(log), scheduler(scheduler) {}
|
||||
NetworkEngine(InfoHash& myid, int s, int s6, Logger& log, Scheduler& scheduler,
|
||||
decltype(NetworkEngine::onError) onError,
|
||||
decltype(NetworkEngine::onNewNode) onNewNode,
|
||||
decltype(NetworkEngine::onReportedAddr) onReportedAddr,
|
||||
decltype(NetworkEngine::onPing) onPing,
|
||||
decltype(NetworkEngine::onFindNode) onFindNode,
|
||||
decltype(NetworkEngine::onGetValues) onGetValues,
|
||||
decltype(NetworkEngine::onListen) onListen,
|
||||
decltype(NetworkEngine::onAnnounce) onAnnounce) :
|
||||
onError(onError), onNewNode(onNewNode), onReportedAddr(onReportedAddr), onPing(onPing), onFindNode(onFindNode),
|
||||
onGetValues(onGetValues), onListen(onListen), onAnnounce(onAnnounce), myid(myid),
|
||||
dht_socket(s), dht_socket6(s6), DHT_LOG(log), scheduler(scheduler)
|
||||
{
|
||||
transaction_id = std::uniform_int_distribution<decltype(transaction_id)>{1}(rd_device);
|
||||
}
|
||||
virtual ~NetworkEngine() {
|
||||
clear();
|
||||
};
|
||||
NetworkEngine(
|
||||
InfoHash& myid,
|
||||
NetworkConfig config,
|
||||
std::unique_ptr<DatagramSocket>&& sock,
|
||||
const Sp<Logger>& log,
|
||||
std::mt19937_64& rd,
|
||||
Scheduler& scheduler,
|
||||
decltype(NetworkEngine::onError)&& onError,
|
||||
decltype(NetworkEngine::onNewNode)&& onNewNode,
|
||||
decltype(NetworkEngine::onReportedAddr)&& onReportedAddr,
|
||||
decltype(NetworkEngine::onPing)&& onPing,
|
||||
decltype(NetworkEngine::onFindNode)&& onFindNode,
|
||||
decltype(NetworkEngine::onGetValues)&& onGetValues,
|
||||
decltype(NetworkEngine::onListen)&& onListen,
|
||||
decltype(NetworkEngine::onAnnounce)&& onAnnounce,
|
||||
decltype(NetworkEngine::onRefresh)&& onRefresh);
|
||||
|
||||
~NetworkEngine();
|
||||
|
||||
net::DatagramSocket* getSocket() const { return dht_socket.get(); };
|
||||
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Sends values (with closest nodes) to a listenner.
|
||||
* Sends values (with closest nodes) to a listener.
|
||||
*
|
||||
* @param sa The address of the listenner.
|
||||
* @param sslen The length of the sockaddr structure.
|
||||
* @param rid The request id of the initial listen request.
|
||||
* @param hash The hash key of the value.
|
||||
* @param want Wether to send ipv4 and/or ipv6 nodes.
|
||||
* @param ntoken Listen security token.
|
||||
* @param nodes The ipv4 closest nodes.
|
||||
* @param nodes6 The ipv6 closest nodes.
|
||||
* @param values The values to send.
|
||||
* @deprecated
|
||||
* @param sa The address of the listener.
|
||||
* @param sslen The length of the sockaddr structure.
|
||||
* @param socket_id The tid to use to write to the request socket.
|
||||
* @param hash The hash key of the value.
|
||||
* @param want Wether to send ipv4 and/or ipv6 nodes.
|
||||
* @param ntoken Listen security token.
|
||||
* @param nodes The ipv4 closest nodes.
|
||||
* @param nodes6 The ipv6 closest nodes.
|
||||
* @param values The values to send.
|
||||
* @param version If version = 1, a request will be used to answer to the listener
|
||||
*/
|
||||
void tellListener(std::shared_ptr<Node> n, uint16_t rid, InfoHash hash, want_t want, Blob ntoken,
|
||||
std::vector<std::shared_ptr<Node>> nodes, std::vector<std::shared_ptr<Node>> nodes6,
|
||||
std::vector<std::shared_ptr<Value>> values);
|
||||
void tellListener(const Sp<Node>& n, Tid socket_id, const InfoHash& hash, want_t want, const Blob& ntoken,
|
||||
std::vector<Sp<Node>>&& nodes, std::vector<Sp<Node>>&& nodes6,
|
||||
std::vector<Sp<Value>>&& values, const Query& q, int version);
|
||||
|
||||
void tellListenerRefreshed(const Sp<Node>& n, Tid socket_id, const InfoHash& hash, const Blob& ntoken, const std::vector<Value::Id>& values, int version);
|
||||
void tellListenerExpired(const Sp<Node>& n, Tid socket_id, const InfoHash& hash, const Blob& ntoken, const std::vector<Value::Id>& values, int version);
|
||||
|
||||
bool isRunning(sa_family_t af) const;
|
||||
inline want_t want () const { return dht_socket >= 0 && dht_socket6 >= 0 ? (WANT4 | WANT6) : -1; }
|
||||
inline want_t want () const { return dht_socket->hasIPv4() and dht_socket->hasIPv6() ? (WANT4 | WANT6) : -1; }
|
||||
|
||||
void connectivityChanged(sa_family_t);
|
||||
|
||||
/**************
|
||||
* Requests *
|
||||
**************/
|
||||
std::shared_ptr<Request>
|
||||
sendPing(std::shared_ptr<Node> n, RequestCb on_done, RequestExpiredCb on_expired);
|
||||
std::shared_ptr<Request>
|
||||
sendPing(const sockaddr* sa, socklen_t salen, RequestCb on_done, RequestExpiredCb on_expired) {
|
||||
return sendPing(std::make_shared<Node>(zeroes, sa, salen), on_done, on_expired);
|
||||
}
|
||||
std::shared_ptr<Request>
|
||||
sendFindNode(std::shared_ptr<Node> n,
|
||||
const InfoHash& target,
|
||||
want_t want,
|
||||
RequestCb on_done,
|
||||
RequestExpiredCb on_expired);
|
||||
std::shared_ptr<Request>
|
||||
sendGetValues(std::shared_ptr<Node> n,
|
||||
const InfoHash& target,
|
||||
want_t want,
|
||||
RequestCb on_done,
|
||||
RequestExpiredCb on_expired);
|
||||
std::shared_ptr<Request>
|
||||
sendListen(std::shared_ptr<Node> n,
|
||||
const InfoHash& infohash,
|
||||
const Blob& token,
|
||||
RequestCb on_done,
|
||||
RequestExpiredCb on_expired);
|
||||
std::shared_ptr<Request>
|
||||
sendAnnounceValue(std::shared_ptr<Node> n,
|
||||
const InfoHash& infohash,
|
||||
const Value& v,
|
||||
time_point created,
|
||||
const Blob& token,
|
||||
RequestCb on_done,
|
||||
RequestExpiredCb on_expired);
|
||||
|
||||
/**
|
||||
* Send a "ping" request to a given node.
|
||||
*
|
||||
* @param n The node.
|
||||
* @param on_done Request callback when the request is completed.
|
||||
* @param on_expired Request callback when the request expires.
|
||||
*
|
||||
* @return the request with information concerning its success.
|
||||
*/
|
||||
Sp<Request>
|
||||
sendPing(const Sp<Node>& n, RequestCb&& on_done, RequestExpiredCb&& on_expired);
|
||||
|
||||
/**
|
||||
* Send a "ping" request to a given node.
|
||||
*
|
||||
* @param sa The node's ip sockaddr info.
|
||||
* @param salen The associated sockaddr struct length.
|
||||
* @param on_done Request callback when the request is completed.
|
||||
* @param on_expired Request callback when the request expires.
|
||||
*
|
||||
* @return the request with information concerning its success.
|
||||
*/
|
||||
Sp<Request>
|
||||
sendPing(SockAddr&& sa, RequestCb&& on_done, RequestExpiredCb&& on_expired) {
|
||||
return sendPing(std::make_shared<Node>(InfoHash::zero(), std::move(sa), rd),
|
||||
std::forward<RequestCb>(on_done),
|
||||
std::forward<RequestExpiredCb>(on_expired));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a "find node" request to a given node.
|
||||
*
|
||||
* @param n The node.
|
||||
* @param target The target hash.
|
||||
* @param want Indicating wether IPv4 or IPv6 are wanted in response.
|
||||
* Use NetworkEngine::want()
|
||||
* @param on_done Request callback when the request is completed.
|
||||
* @param on_expired Request callback when the request expires.
|
||||
*
|
||||
* @return the request with information concerning its success.
|
||||
*/
|
||||
Sp<Request> sendFindNode(const Sp<Node>& n,
|
||||
const InfoHash& hash,
|
||||
want_t want = -1,
|
||||
RequestCb&& on_done = {},
|
||||
RequestExpiredCb&& on_expired = {});
|
||||
/**
|
||||
* Send a "get" request to a given node.
|
||||
*
|
||||
* @param n The node.
|
||||
* @param hash The target hash.
|
||||
* @param query The query describing filters.
|
||||
* @param token A security token.
|
||||
* @param want Indicating wether IPv4 or IPv6 are wanted in response.
|
||||
* Use NetworkEngine::want()
|
||||
* @param on_done Request callback when the request is completed.
|
||||
* @param on_expired Request callback when the request expires.
|
||||
*
|
||||
* @return the request with information concerning its success.
|
||||
*/
|
||||
Sp<Request> sendGetValues(const Sp<Node>& n,
|
||||
const InfoHash& hash,
|
||||
const Query& query,
|
||||
want_t want,
|
||||
RequestCb&& on_done,
|
||||
RequestExpiredCb&& on_expired);
|
||||
/**
|
||||
* Send a "listen" request to a given node.
|
||||
*
|
||||
* @param n The node.
|
||||
* @param hash The storage's hash.
|
||||
* @param query The query describing filters.
|
||||
* @param token A security token.
|
||||
* @param previous The previous request "listen" sent to this node.
|
||||
* @param socket **UNUSED** The socket for further response.
|
||||
*
|
||||
* For backward compatibility purpose, sendListen has to
|
||||
* handle creation of the socket. Therefor, you cannot
|
||||
* use openSocket yourself. TODO: Once we don't support
|
||||
* the old "listen" negociation, sendListen shall not
|
||||
* create the socket itself.
|
||||
*
|
||||
* @param on_done Request callback when the request is completed.
|
||||
* @param on_expired Request callback when the request expires.
|
||||
* @param socket_cb Callback to execute each time new updates arrive on
|
||||
* the socket.
|
||||
*
|
||||
* @return the request with information concerning its success.
|
||||
*/
|
||||
Sp<Request> sendListen(const Sp<Node>& n,
|
||||
const InfoHash& hash,
|
||||
const Query& query,
|
||||
const Blob& token,
|
||||
Tid socketId,
|
||||
RequestCb&& on_done,
|
||||
RequestExpiredCb&& on_expired);
|
||||
/**
|
||||
* Send a "announce" request to a given node.
|
||||
*
|
||||
* @param n The node.
|
||||
* @param hash The target hash.
|
||||
* @param created The time when the value was created (avoiding extended
|
||||
* value lifetime)
|
||||
* @param token A security token.
|
||||
* @param on_done Request callback when the request is completed.
|
||||
* @param on_expired Request callback when the request expires.
|
||||
*
|
||||
* @return the request with information concerning its success.
|
||||
*/
|
||||
Sp<Request> sendAnnounceValue(const Sp<Node>& n,
|
||||
const InfoHash& hash,
|
||||
const Sp<Value>& v,
|
||||
time_point created,
|
||||
const Blob& token,
|
||||
RequestCb&& on_done,
|
||||
RequestExpiredCb&& on_expired);
|
||||
/**
|
||||
* Send a "refresh" request to a given node. Asks a node to keep the
|
||||
* associated value Value.type.expiration more minutes in its storage.
|
||||
*
|
||||
* @param n The node.
|
||||
* @param hash The target hash.
|
||||
* @param vid The value id.
|
||||
* @param token A security token.
|
||||
* @param on_done Request callback when the request is completed.
|
||||
* @param on_expired Request callback when the request expires.
|
||||
*
|
||||
* @return the request with information concerning its success.
|
||||
*/
|
||||
Sp<Request> sendRefreshValue(const Sp<Node>& n,
|
||||
const InfoHash& hash,
|
||||
const Value::Id& vid,
|
||||
const Blob& token,
|
||||
RequestCb&& on_done,
|
||||
RequestErrorCb&& on_error,
|
||||
RequestExpiredCb&& on_expired);
|
||||
/**
|
||||
* Send a "update" request to a given node. Used for Listen operations
|
||||
*
|
||||
* @param n The node.
|
||||
* @param hash The target hash.
|
||||
* @param values The values.
|
||||
* @param created Time id.
|
||||
* @param token A security token.
|
||||
* @param sid The socket id.
|
||||
*
|
||||
* @return the request with information concerning its success.
|
||||
*/
|
||||
Sp<Request> sendUpdateValues(const Sp<Node>& n,
|
||||
const InfoHash& infohash,
|
||||
const std::vector<Sp<Value>>& values,
|
||||
time_point created,
|
||||
const Blob& token,
|
||||
const size_t& sid);
|
||||
|
||||
/**
|
||||
* Parses a message and calls appropriate callbacks.
|
||||
@@ -342,10 +432,12 @@ public:
|
||||
* @param fromlen The length of the corresponding sockaddr structure.
|
||||
* @param now The time to adjust the clock in the network engine.
|
||||
*/
|
||||
void processMessage(const uint8_t *buf, size_t buflen, const sockaddr* from, socklen_t fromlen);
|
||||
void processMessage(const uint8_t *buf, size_t buflen, SockAddr addr);
|
||||
|
||||
std::shared_ptr<Node> insertNode(const InfoHash& myid, const sockaddr* from, socklen_t fromlen) {
|
||||
return cache.getNode(myid, from, fromlen, scheduler.time(), 0);
|
||||
Sp<Node> insertNode(const InfoHash& id, const SockAddr& addr) {
|
||||
auto n = cache.getNode(id, addr, scheduler.time(), 0);
|
||||
onNewNode(n, 0);
|
||||
return n;
|
||||
}
|
||||
|
||||
std::vector<unsigned> getNodeMessageStats(bool in) {
|
||||
@@ -355,113 +447,147 @@ public:
|
||||
return stats;
|
||||
}
|
||||
|
||||
void blacklistNode(const std::shared_ptr<Node>& n);
|
||||
void blacklistNode(const Sp<Node>& n);
|
||||
|
||||
std::vector<Sp<Node>> getCachedNodes(const InfoHash& id, sa_family_t sa_f, size_t count) {
|
||||
return cache.getCachedNodes(id, sa_f, count);
|
||||
}
|
||||
|
||||
size_t getNodeCacheSize() const {
|
||||
return cache.size();
|
||||
}
|
||||
size_t getNodeCacheSize(sa_family_t af) const {
|
||||
return cache.size(af);
|
||||
}
|
||||
|
||||
size_t getRateLimiterSize() const {
|
||||
return address_rate_limiter.size();
|
||||
}
|
||||
|
||||
size_t getPartialCount() const {
|
||||
return partial_messages.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
struct PartialMessage;
|
||||
|
||||
/***************
|
||||
* Constants *
|
||||
***************/
|
||||
static constexpr long unsigned MAX_REQUESTS_PER_SEC {1600};
|
||||
/* the length of a node info buffer in ipv4 format */
|
||||
static const constexpr size_t NODE4_INFO_BUF_LEN {26};
|
||||
static const constexpr size_t NODE4_INFO_BUF_LEN {HASH_LEN + sizeof(in_addr) + sizeof(in_port_t)};
|
||||
/* the length of a node info buffer in ipv6 format */
|
||||
static const constexpr size_t NODE6_INFO_BUF_LEN {38};
|
||||
/* TODO */
|
||||
static const constexpr size_t NODE6_INFO_BUF_LEN {HASH_LEN + sizeof(in6_addr) + sizeof(in_port_t)};
|
||||
/* after a UDP reply, the period during which we tell the link layer about it */
|
||||
static constexpr std::chrono::seconds UDP_REPLY_TIME {15};
|
||||
|
||||
/* Max. time to receive a full fragmented packet */
|
||||
static constexpr std::chrono::seconds RX_MAX_PACKET_TIME {10};
|
||||
/* Max. time between packet fragments */
|
||||
static constexpr std::chrono::seconds RX_TIMEOUT {3};
|
||||
/* The maximum number of nodes that we snub. There is probably little
|
||||
reason to increase this value. */
|
||||
static constexpr unsigned BLACKLISTED_MAX {10};
|
||||
/* TODO */
|
||||
|
||||
static constexpr size_t MTU {1280};
|
||||
static constexpr size_t MAX_PACKET_VALUE_SIZE {600};
|
||||
|
||||
static const std::string my_v;
|
||||
|
||||
/* DHT info */
|
||||
const InfoHash& myid;
|
||||
const int dht_socket {-1};
|
||||
const int dht_socket6 {-1};
|
||||
const Logger& DHT_LOG;
|
||||
void process(std::unique_ptr<ParsedMessage>&&, const SockAddr& from);
|
||||
|
||||
NodeCache cache {};
|
||||
sockaddr_storage blacklist[BLACKLISTED_MAX] {};
|
||||
unsigned next_blacklisted = 0;
|
||||
bool rateLimit(const SockAddr& addr);
|
||||
|
||||
bool rateLimit();
|
||||
static bool isMartian(const SockAddr& addr);
|
||||
bool isNodeBlacklisted(const SockAddr& addr) const;
|
||||
|
||||
static bool isMartian(const sockaddr* sa, socklen_t len);
|
||||
bool isNodeBlacklisted(const sockaddr*, socklen_t) const;
|
||||
|
||||
void requestStep(std::shared_ptr<Request> req);
|
||||
void requestStep(Sp<Request> req);
|
||||
|
||||
/**
|
||||
* Sends a request to a node. Request::MAX_ATTEMPT_COUNT attempts will
|
||||
* be made before the request expires.
|
||||
*/
|
||||
void sendRequest(std::shared_ptr<Request>& request);
|
||||
|
||||
/**
|
||||
* Generates a new request id, skipping the invalid id.
|
||||
*
|
||||
* @return the new id.
|
||||
*/
|
||||
uint16_t getNewTid() {
|
||||
++transaction_id;
|
||||
return transaction_id == TransId::INVALID ? ++transaction_id : transaction_id;
|
||||
}
|
||||
void sendRequest(const Sp<Request>& request);
|
||||
|
||||
struct MessageStats {
|
||||
unsigned ping {0};
|
||||
unsigned find {0};
|
||||
unsigned get {0};
|
||||
unsigned put {0};
|
||||
unsigned listen {0};
|
||||
unsigned ping {0};
|
||||
unsigned find {0};
|
||||
unsigned get {0};
|
||||
unsigned put {0};
|
||||
unsigned listen {0};
|
||||
unsigned refresh {0};
|
||||
unsigned updateValue {0};
|
||||
};
|
||||
|
||||
|
||||
// basic wrapper for socket sendto function
|
||||
int send(const char *buf, size_t len, int flags, const sockaddr *sa, socklen_t salen);
|
||||
int send(const SockAddr& addr, const char *buf, size_t len, bool confirmed = false);
|
||||
|
||||
void sendValueParts(Tid tid, const std::vector<Blob>& svals, const SockAddr& addr);
|
||||
std::vector<Blob> packValueHeader(msgpack::sbuffer&, const std::vector<Sp<Value>>&);
|
||||
void maintainRxBuffer(Tid tid);
|
||||
|
||||
/*************
|
||||
* Answers *
|
||||
*************/
|
||||
/* answer to a ping request */
|
||||
void sendPong(const sockaddr* sa, socklen_t salen, TransId tid);
|
||||
void sendPong(const SockAddr& addr, Tid tid);
|
||||
/* answer to findnodes/getvalues request */
|
||||
void sendNodesValues(const sockaddr* sa,
|
||||
socklen_t salen,
|
||||
TransId tid,
|
||||
void sendNodesValues(const SockAddr& addr,
|
||||
Tid tid,
|
||||
const Blob& nodes,
|
||||
const Blob& nodes6,
|
||||
const std::vector<std::shared_ptr<Value>>& st,
|
||||
const std::vector<Sp<Value>>& st,
|
||||
const Query& query,
|
||||
const Blob& token);
|
||||
Blob bufferNodes(sa_family_t af, const InfoHash& id, std::vector<std::shared_ptr<Node>>& nodes);
|
||||
Blob bufferNodes(sa_family_t af, const InfoHash& id, std::vector<Sp<Node>>& nodes);
|
||||
|
||||
std::pair<Blob, Blob> bufferNodes(sa_family_t af,
|
||||
const InfoHash& id,
|
||||
want_t want,
|
||||
std::vector<std::shared_ptr<Node>>& nodes,
|
||||
std::vector<std::shared_ptr<Node>>& nodes6);
|
||||
std::vector<Sp<Node>>& nodes,
|
||||
std::vector<Sp<Node>>& nodes6);
|
||||
/* answer to a listen request */
|
||||
void sendListenConfirmation(const sockaddr* sa, socklen_t salen, TransId tid);
|
||||
void sendListenConfirmation(const SockAddr& addr, Tid tid);
|
||||
/* answer to put request */
|
||||
void sendValueAnnounced(const sockaddr* sa, socklen_t salen, TransId, Value::Id);
|
||||
void sendValueAnnounced(const SockAddr& addr, Tid, Value::Id);
|
||||
/* answer in case of error */
|
||||
void sendError(const sockaddr* sa,
|
||||
socklen_t salen,
|
||||
TransId tid,
|
||||
void sendError(const SockAddr& addr,
|
||||
Tid tid,
|
||||
uint16_t code,
|
||||
const std::string& message,
|
||||
bool include_id=false);
|
||||
|
||||
void deserializeNodesValues(ParsedMessage& msg);
|
||||
void deserializeNodes(ParsedMessage& msg, const SockAddr& from);
|
||||
|
||||
std::queue<time_point> rate_limit_time {};
|
||||
static std::mt19937 rd_device;
|
||||
/* DHT info */
|
||||
const InfoHash& myid;
|
||||
const NetworkConfig config {};
|
||||
const std::unique_ptr<DatagramSocket> dht_socket;
|
||||
Sp<Logger> logger_;
|
||||
std::mt19937_64& rd;
|
||||
|
||||
NodeCache cache;
|
||||
|
||||
// global limiting should be triggered by at least 8 different IPs
|
||||
using IpLimiter = RateLimiter;
|
||||
using IpLimiterMap = std::map<SockAddr, IpLimiter, SockAddr::ipCmp>;
|
||||
IpLimiterMap address_rate_limiter;
|
||||
RateLimiter rate_limiter;
|
||||
ssize_t limiter_maintenance {0};
|
||||
|
||||
// requests handling
|
||||
uint16_t transaction_id {1};
|
||||
std::map<uint16_t, std::shared_ptr<Request>> requests {};
|
||||
std::map<Tid, Sp<Request>> requests {};
|
||||
std::map<Tid, PartialMessage> partial_messages;
|
||||
|
||||
MessageStats in_stats {}, out_stats {};
|
||||
std::set<SockAddr> blacklist {};
|
||||
|
||||
Scheduler& scheduler;
|
||||
|
||||
bool logIncoming_ {false};
|
||||
};
|
||||
|
||||
}
|
||||
} /* namespace net */
|
||||
} /* namespace dht */
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "def.h"
|
||||
|
||||
#include "sockaddr.h"
|
||||
#include "utils.h"
|
||||
#include "log_enable.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <ws2tcpip.h>
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <list>
|
||||
|
||||
namespace dht {
|
||||
namespace net {
|
||||
|
||||
static const constexpr in_port_t DHT_DEFAULT_PORT = 4222;
|
||||
static const constexpr size_t RX_QUEUE_MAX_SIZE = 1024 * 64;
|
||||
static const constexpr std::chrono::milliseconds RX_QUEUE_MAX_DELAY(650);
|
||||
|
||||
int bindSocket(const SockAddr& addr, SockAddr& bound);
|
||||
|
||||
bool setNonblocking(int fd, bool nonblocking = true);
|
||||
|
||||
#ifdef _WIN32
|
||||
void udpPipe(int fds[2]);
|
||||
#endif
|
||||
struct ReceivedPacket {
|
||||
Blob data;
|
||||
SockAddr from;
|
||||
time_point received;
|
||||
};
|
||||
using PacketList = std::list<ReceivedPacket>;
|
||||
|
||||
class OPENDHT_PUBLIC DatagramSocket {
|
||||
public:
|
||||
/** A function that takes a list of new received packets and
|
||||
* optionally returns consumed packets for recycling.
|
||||
**/
|
||||
using OnReceive = std::function<PacketList(PacketList&& packets)>;
|
||||
virtual ~DatagramSocket() {};
|
||||
|
||||
virtual int sendTo(const SockAddr& dest, const uint8_t* data, size_t size, bool replied) = 0;
|
||||
|
||||
inline void setOnReceive(OnReceive&& cb) {
|
||||
std::lock_guard<std::mutex> lk(lock);
|
||||
rx_callback = std::move(cb);
|
||||
}
|
||||
|
||||
virtual bool hasIPv4() const = 0;
|
||||
virtual bool hasIPv6() const = 0;
|
||||
|
||||
SockAddr getBound(sa_family_t family = AF_UNSPEC) const {
|
||||
std::lock_guard<std::mutex> lk(lock);
|
||||
return getBoundRef(family);
|
||||
}
|
||||
in_port_t getPort(sa_family_t family = AF_UNSPEC) const {
|
||||
std::lock_guard<std::mutex> lk(lock);
|
||||
return getBoundRef(family).getPort();
|
||||
}
|
||||
|
||||
virtual const SockAddr& getBoundRef(sa_family_t family = AF_UNSPEC) const = 0;
|
||||
|
||||
/** Virtual resolver mothod allows to implement custom resolver */
|
||||
virtual std::vector<SockAddr> resolve(const std::string& host, const std::string& service = {}) {
|
||||
return SockAddr::resolve(host, service);
|
||||
}
|
||||
|
||||
virtual void stop() = 0;
|
||||
protected:
|
||||
|
||||
PacketList getNewPacket() {
|
||||
PacketList pkts;
|
||||
if (toRecycle_.empty()) {
|
||||
pkts.emplace_back();
|
||||
} else {
|
||||
auto begIt = toRecycle_.begin();
|
||||
auto begItNext = std::next(begIt);
|
||||
pkts.splice(pkts.end(), toRecycle_, begIt, begItNext);
|
||||
}
|
||||
return pkts;
|
||||
}
|
||||
|
||||
inline void onReceived(PacketList&& packets) {
|
||||
std::lock_guard<std::mutex> lk(lock);
|
||||
if (rx_callback) {
|
||||
auto r = rx_callback(std::move(packets));
|
||||
if (not r.empty() and toRecycle_.size() < RX_QUEUE_MAX_SIZE)
|
||||
toRecycle_.splice(toRecycle_.end(), std::move(r));
|
||||
}
|
||||
}
|
||||
protected:
|
||||
mutable std::mutex lock;
|
||||
private:
|
||||
OnReceive rx_callback;
|
||||
PacketList toRecycle_;
|
||||
};
|
||||
|
||||
class OPENDHT_PUBLIC UdpSocket : public DatagramSocket {
|
||||
public:
|
||||
UdpSocket(in_port_t port, const std::shared_ptr<Logger>& l = {});
|
||||
UdpSocket(const SockAddr& bind4, const SockAddr& bind6, const std::shared_ptr<Logger>& l = {});
|
||||
~UdpSocket();
|
||||
|
||||
int sendTo(const SockAddr& dest, const uint8_t* data, size_t size, bool replied) override;
|
||||
|
||||
const SockAddr& getBoundRef(sa_family_t family = AF_UNSPEC) const override {
|
||||
return (family == AF_INET6) ? bound6 : bound4;
|
||||
}
|
||||
|
||||
bool hasIPv4() const override {
|
||||
std::lock_guard<std::mutex> lk(lock);
|
||||
return s4 != -1;
|
||||
}
|
||||
bool hasIPv6() const override {
|
||||
std::lock_guard<std::mutex> lk(lock);
|
||||
return s6 != -1;
|
||||
}
|
||||
|
||||
void stop() override;
|
||||
private:
|
||||
std::shared_ptr<Logger> logger;
|
||||
int s4 {-1};
|
||||
int s6 {-1};
|
||||
int stopfd {-1};
|
||||
SockAddr bound4, bound6;
|
||||
std::thread rcv_thread {};
|
||||
std::atomic_bool running {false};
|
||||
|
||||
void openSockets(const SockAddr& bind4, const SockAddr& bind6);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
+107
-36
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -14,70 +14,137 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "infohash.h" // includes socket structures
|
||||
#include "utils.h"
|
||||
#include "sockaddr.h"
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
|
||||
namespace dht {
|
||||
|
||||
struct Node;
|
||||
namespace net {
|
||||
struct Request;
|
||||
struct Socket;
|
||||
struct RequestAnswer;
|
||||
} /* namespace net */
|
||||
|
||||
using Tid = uint32_t;
|
||||
using SocketCb = std::function<void(const Sp<Node>&, net::RequestAnswer&&)>;
|
||||
struct Socket {
|
||||
Socket() {}
|
||||
Socket(SocketCb&& on_receive) :
|
||||
on_receive(std::move(on_receive)) {}
|
||||
SocketCb on_receive {};
|
||||
};
|
||||
|
||||
struct Node {
|
||||
friend class NetworkEngine;
|
||||
const InfoHash id;
|
||||
|
||||
InfoHash id;
|
||||
Node(const InfoHash& id, const SockAddr& addr, std::mt19937_64& rd, bool client=false);
|
||||
Node(const InfoHash& id, SockAddr&& addr, std::mt19937_64& rd, bool client=false);
|
||||
Node(const InfoHash& id, const sockaddr* sa, socklen_t salen, std::mt19937_64& rd)
|
||||
: Node(id, SockAddr(sa, salen), rd) {}
|
||||
|
||||
socklen_t sslen {0};
|
||||
sockaddr_storage ss;
|
||||
|
||||
time_point time {time_point::min()}; /* last time eared about */
|
||||
time_point reply_time {time_point::min()}; /* time of last correct reply received */
|
||||
|
||||
Node(const InfoHash& id, const sockaddr* sa, socklen_t salen)
|
||||
: id(id), sslen(salen), ss() {
|
||||
std::copy_n((const uint8_t*)sa, salen, (uint8_t*)&ss);
|
||||
if ((unsigned)salen < sizeof(ss))
|
||||
std::fill_n((uint8_t*)&ss+salen, sizeof(ss)-salen, 0);
|
||||
}
|
||||
InfoHash getId() const {
|
||||
return id;
|
||||
}
|
||||
std::pair<const sockaddr*, socklen_t> getAddr() const {
|
||||
return {(const sockaddr*)&ss, sslen};
|
||||
}
|
||||
const SockAddr& getAddr() const { return addr; }
|
||||
std::string getAddrStr() const {
|
||||
return print_addr(ss, sslen);
|
||||
return addr.toString();
|
||||
}
|
||||
bool isClient() const { return is_client; }
|
||||
bool isIncoming() { return time > reply_time; }
|
||||
|
||||
const time_point& getTime() const { return time; }
|
||||
const time_point& getReplyTime() const { return reply_time; }
|
||||
void setTime(const time_point& t) { time = t; }
|
||||
|
||||
/**
|
||||
* Makes notice about an additionnal authentication error with this node. Up
|
||||
* to MAX_AUTH_ERRORS errors are accepted in order to let the node recover.
|
||||
* Upon this limit, the node expires.
|
||||
*/
|
||||
void authError() {
|
||||
if (++auth_errors > MAX_AUTH_ERRORS)
|
||||
setExpired();
|
||||
}
|
||||
void authSuccess() { auth_errors = 0; }
|
||||
|
||||
bool isExpired() const { return expired_; }
|
||||
bool isGood(time_point now) const;
|
||||
bool isPendingMessage() const;
|
||||
size_t getPendingMessageCount() const;
|
||||
|
||||
NodeExport exportNode() const { return NodeExport {id, ss, sslen}; }
|
||||
sa_family_t getFamily() const { return ss.ss_family; }
|
||||
bool isOld(const time_point& now) const {
|
||||
return time + NODE_EXPIRE_TIME < now;
|
||||
}
|
||||
bool isRemovable(const time_point& now) const {
|
||||
return isExpired() and isOld(now);
|
||||
}
|
||||
|
||||
void update(const sockaddr* sa, socklen_t salen);
|
||||
NodeExport exportNode() const {
|
||||
NodeExport ne;
|
||||
ne.id = id;
|
||||
ne.sslen = addr.getLength();
|
||||
std::memcpy(&ne.ss, addr.get(), ne.sslen);
|
||||
return ne;
|
||||
}
|
||||
sa_family_t getFamily() const { return addr.getFamily(); }
|
||||
|
||||
void requested(std::shared_ptr<Request>& req);
|
||||
void received(time_point now, std::shared_ptr<Request> req);
|
||||
void update(const SockAddr&);
|
||||
|
||||
void requested(const Sp<net::Request>& req);
|
||||
void received(time_point now, const Sp<net::Request>& req);
|
||||
Sp<net::Request> getRequest(Tid tid);
|
||||
void cancelRequest(const Sp<net::Request>& req);
|
||||
|
||||
void setExpired();
|
||||
|
||||
/**
|
||||
* Opens a socket on which a node will be able allowed to write for further
|
||||
* additionnal updates following the response to a previous request.
|
||||
*
|
||||
* @param node The node which will be allowed to write on this socket.
|
||||
* @param cb The callback to execute once updates arrive on the socket.
|
||||
*
|
||||
* @return the socket.
|
||||
*/
|
||||
Tid openSocket(SocketCb&& cb);
|
||||
|
||||
Sp<Socket> getSocket(Tid id);
|
||||
|
||||
/**
|
||||
* Closes a socket so that no further data will be red on that socket.
|
||||
*
|
||||
* @param socket The socket to close.
|
||||
*/
|
||||
void closeSocket(Tid id);
|
||||
|
||||
/**
|
||||
* Resets the state of the node so it's not expired anymore.
|
||||
*/
|
||||
void reset() { expired_ = false; }
|
||||
void reset() { expired_ = false; reply_time = time_point::min(); }
|
||||
|
||||
/**
|
||||
* Generates a new request id, skipping the invalid id.
|
||||
*
|
||||
* @return the new id.
|
||||
*/
|
||||
Tid getNewTid() {
|
||||
++transaction_id;
|
||||
return transaction_id ? ++transaction_id : transaction_id;
|
||||
}
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
friend std::ostream& operator<< (std::ostream& s, const Node& h);
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const Node& h);
|
||||
|
||||
static constexpr const std::chrono::minutes NODE_GOOD_TIME {120};
|
||||
|
||||
@@ -88,16 +155,20 @@ struct Node {
|
||||
static constexpr const std::chrono::seconds MAX_RESPONSE_TIME {1};
|
||||
|
||||
private:
|
||||
/* Number of times we accept authentication errors from this node. */
|
||||
static const constexpr unsigned MAX_AUTH_ERRORS {3};
|
||||
|
||||
std::list<std::weak_ptr<Request>> requests_ {};
|
||||
SockAddr addr;
|
||||
bool is_client {false};
|
||||
time_point time {time_point::min()}; /* last time eared about */
|
||||
time_point reply_time {time_point::min()}; /* time of last correct reply received */
|
||||
unsigned auth_errors {0};
|
||||
bool expired_ {false};
|
||||
Tid transaction_id;
|
||||
using TransactionDist = std::uniform_int_distribution<decltype(transaction_id)>;
|
||||
|
||||
void clearPendingQueue() {
|
||||
requests_.remove_if([](std::weak_ptr<Request>& w) {
|
||||
return w.expired();
|
||||
});
|
||||
}
|
||||
|
||||
std::map<Tid, Sp<net::Request>> requests_ {};
|
||||
std::map<Tid, Sp<Socket>> sockets_;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -13,8 +13,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
@@ -27,8 +26,16 @@
|
||||
namespace dht {
|
||||
|
||||
struct NodeCache {
|
||||
std::shared_ptr<Node> getNode(const InfoHash& id, sa_family_t family);
|
||||
std::shared_ptr<Node> getNode(const InfoHash& id, const sockaddr* sa, socklen_t sa_len, time_point now, int confirmed);
|
||||
size_t size(sa_family_t family) const {
|
||||
return cache(family).count();
|
||||
}
|
||||
size_t size() const {
|
||||
return size(AF_INET) + size(AF_INET6);
|
||||
}
|
||||
|
||||
Sp<Node> getNode(const InfoHash& id, sa_family_t family);
|
||||
Sp<Node> getNode(const InfoHash& id, const SockAddr&, time_point now, bool confirmed, bool client=false);
|
||||
std::vector<Sp<Node>> getCachedNodes(const InfoHash& id, sa_family_t sa_f, size_t count) const;
|
||||
|
||||
/**
|
||||
* Reset the connectivity state of every node,
|
||||
@@ -37,16 +44,28 @@ struct NodeCache {
|
||||
*/
|
||||
void clearBadNodes(sa_family_t family = 0);
|
||||
|
||||
NodeCache(std::mt19937_64& r) : rd(r) {};
|
||||
~NodeCache();
|
||||
|
||||
private:
|
||||
class NodeMap : public std::map<InfoHash, std::weak_ptr<Node>> {
|
||||
class NodeMap : private std::map<InfoHash, std::weak_ptr<Node>> {
|
||||
public:
|
||||
std::shared_ptr<Node> getNode(const InfoHash& id);
|
||||
std::shared_ptr<Node> getNode(const InfoHash& id, const sockaddr* sa, socklen_t sa_len, time_point now, int confirmed);
|
||||
Sp<Node> getNode(const InfoHash& id);
|
||||
Sp<Node> getNode(const InfoHash& id, const SockAddr&, time_point now, bool confirmed, bool client, std::mt19937_64& rd);
|
||||
std::vector<Sp<Node>> getCachedNodes(const InfoHash& id, size_t count) const;
|
||||
void clearBadNodes();
|
||||
void setExpired();
|
||||
void cleanup();
|
||||
size_t count() const { return size(); }
|
||||
private:
|
||||
size_t cleanup_counter {0};
|
||||
};
|
||||
|
||||
const NodeMap& cache(sa_family_t af) const { return af == AF_INET ? cache_4 : cache_6; }
|
||||
NodeMap& cache(sa_family_t af) { return af == AF_INET ? cache_4 : cache_6; }
|
||||
NodeMap cache_4;
|
||||
NodeMap cache_6;
|
||||
std::mt19937_64& rd;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
|
||||
* Vsevolod Ivanov <vsevolod.ivanov@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "def.h"
|
||||
#include "sockaddr.h"
|
||||
#include "infohash.h"
|
||||
#include "log_enable.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
namespace asio {
|
||||
class io_context;
|
||||
}
|
||||
|
||||
namespace dht {
|
||||
|
||||
class OPENDHT_PUBLIC PeerDiscovery
|
||||
{
|
||||
public:
|
||||
static constexpr in_port_t DEFAULT_PORT = 8888;
|
||||
using ServiceDiscoveredCallback = std::function<void(msgpack::object&&, SockAddr&&)>;
|
||||
|
||||
PeerDiscovery(in_port_t port = DEFAULT_PORT, std::shared_ptr<asio::io_context> ioContext = {}, std::shared_ptr<Logger> logger = {});
|
||||
~PeerDiscovery();
|
||||
|
||||
/**
|
||||
* startDiscovery - Keep Listening data from the sender until node is joinned or stop is called
|
||||
*/
|
||||
void startDiscovery(const std::string &type, ServiceDiscoveredCallback callback);
|
||||
|
||||
template<typename T>
|
||||
void startDiscovery(const std::string &type, std::function<void(T&&, SockAddr&&)> cb) {
|
||||
startDiscovery(type, [cb](msgpack::object&& ob, SockAddr&& addr) {
|
||||
cb(ob.as<T>(), std::move(addr));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* startPublish - Keeping sending data until node is joinned or stop is called
|
||||
*/
|
||||
void startPublish(const std::string &type, const msgpack::sbuffer &pack_buf);
|
||||
void startPublish(sa_family_t domain, const std::string &type, const msgpack::sbuffer &pack_buf);
|
||||
|
||||
template<typename T>
|
||||
void startPublish(const std::string &type, const T& object) {
|
||||
msgpack::sbuffer buf;
|
||||
msgpack::pack(buf, object);
|
||||
startPublish(type, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Thread Stopper
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* Remove possible callBack to discovery
|
||||
*/
|
||||
bool stopDiscovery(const std::string &type);
|
||||
|
||||
/**
|
||||
* Remove different serivce message to send
|
||||
*/
|
||||
bool stopPublish(const std::string &type);
|
||||
bool stopPublish(sa_family_t domain, const std::string &type);
|
||||
|
||||
void connectivityChanged();
|
||||
|
||||
private:
|
||||
class DomainPeerDiscovery;
|
||||
std::unique_ptr<DomainPeerDiscovery> peerDiscovery4_;
|
||||
std::unique_ptr<DomainPeerDiscovery> peerDiscovery6_;
|
||||
std::shared_ptr<asio::io_context> ioContext_;
|
||||
std::thread ioRunnner_;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace dht {
|
||||
namespace proxy {
|
||||
|
||||
constexpr const std::chrono::hours OP_TIMEOUT {24}; // one day
|
||||
constexpr const std::chrono::hours OP_MARGIN {2}; // two hours
|
||||
constexpr const char* const HTTP_PROTO {"http://"};
|
||||
using ListenToken = uint64_t;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "utils.h"
|
||||
#include <queue>
|
||||
|
||||
namespace dht {
|
||||
|
||||
class RateLimiter {
|
||||
public:
|
||||
RateLimiter(size_t quota, const duration& period = std::chrono::seconds(1))
|
||||
: quota_(quota), period_(period) {}
|
||||
|
||||
/** Clear outdated records and return current quota usage */
|
||||
size_t maintain(const time_point& now) {
|
||||
auto limit = now - period_;
|
||||
while (not records.empty() and records.front() < limit)
|
||||
records.pop();
|
||||
return records.size();
|
||||
}
|
||||
/** Return false if quota is reached, insert record and return true otherwise. */
|
||||
bool limit(const time_point& now) {
|
||||
if (quota_ == std::numeric_limits<size_t>::max())
|
||||
return true;
|
||||
if (maintain(now) >= quota_)
|
||||
return false;
|
||||
records.emplace(now);
|
||||
return true;
|
||||
}
|
||||
bool empty() const {
|
||||
return records.empty();
|
||||
}
|
||||
private:
|
||||
const size_t quota_;
|
||||
const duration period_;
|
||||
std::queue<time_point> records {};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
namespace dht {
|
||||
|
||||
class NetworkEngine;
|
||||
struct ParsedMessage;
|
||||
|
||||
/*!
|
||||
* @class Request
|
||||
* @brief An atomic request destined to a node.
|
||||
* @details
|
||||
* A request contains data used by the NetworkEngine to process a request
|
||||
* desitned to specific node and std::function callbacks to execute when the
|
||||
* request is done.
|
||||
*/
|
||||
struct Request {
|
||||
friend class dht::NetworkEngine;
|
||||
std::shared_ptr<Node> node {}; /* the node to whom the request is destined. */
|
||||
time_point reply_time {time_point::min()}; /* time when we received the response to the request. */
|
||||
|
||||
enum class State
|
||||
{
|
||||
PENDING,
|
||||
CANCELLED,
|
||||
EXPIRED,
|
||||
COMPLETED
|
||||
};
|
||||
|
||||
bool expired() const { return state_ == State::EXPIRED; }
|
||||
bool completed() const { return state_ == State::COMPLETED; }
|
||||
bool cancelled() const { return state_ == State::CANCELLED; }
|
||||
bool pending() const { return state_ == State::PENDING; }
|
||||
bool over() const { return not pending(); }
|
||||
State getState() const { return state_; }
|
||||
|
||||
Request() {}
|
||||
Request(uint16_t tid,
|
||||
std::shared_ptr<Node> node,
|
||||
Blob&& msg,
|
||||
std::function<void(const Request& req_status, ParsedMessage&&)> on_done,
|
||||
std::function<void(const Request& req_status, bool)> on_expired,
|
||||
bool persistent = false) :
|
||||
node(node), on_done(on_done), on_expired(on_expired), tid(tid), msg(std::move(msg)), persistent(persistent) { }
|
||||
|
||||
void setExpired() {
|
||||
if (pending()) {
|
||||
state_ = Request::State::EXPIRED;
|
||||
on_expired(*this, true);
|
||||
clear();
|
||||
}
|
||||
}
|
||||
void setDone(ParsedMessage&& msg) {
|
||||
if (pending() or persistent) {
|
||||
state_ = Request::State::COMPLETED;
|
||||
on_done(*this, std::forward<ParsedMessage>(msg));
|
||||
if (not persistent)
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
void cancel() {
|
||||
if (pending()) {
|
||||
state_ = State::CANCELLED;
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static const constexpr size_t MAX_ATTEMPT_COUNT {3};
|
||||
|
||||
bool isExpired(time_point now) const {
|
||||
return pending() and now > last_try + Node::MAX_RESPONSE_TIME and attempt_count >= Request::MAX_ATTEMPT_COUNT;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
on_done = {};
|
||||
on_expired = {};
|
||||
msg.clear();
|
||||
}
|
||||
|
||||
State state_ {State::PENDING};
|
||||
|
||||
unsigned attempt_count {0}; /* number of attempt to process the request. */
|
||||
time_point start {time_point::min()}; /* time when the request is created. */
|
||||
time_point last_try {time_point::min()}; /* time of the last attempt to process the request. */
|
||||
|
||||
std::function<void(const Request& req_status, ParsedMessage&&)> on_done {};
|
||||
std::function<void(const Request& req_status, bool)> on_expired {};
|
||||
|
||||
const uint16_t tid {0}; /* the request id. */
|
||||
Blob msg {}; /* the serialized message. */
|
||||
const bool persistent {false}; /* the request is not erased upon completion. */
|
||||
};
|
||||
|
||||
}
|
||||
+26
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -13,18 +13,21 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace dht {
|
||||
namespace crypto {
|
||||
|
||||
#ifndef WIN32_NATIVE
|
||||
#ifndef _MSC_VER
|
||||
#ifdef _WIN32
|
||||
|
||||
/**
|
||||
@@ -42,8 +45,8 @@ public:
|
||||
* result_type is unsigned int so this is currently safe.
|
||||
*/
|
||||
static_assert(
|
||||
sizeof(result_type) == 2 ||
|
||||
sizeof(result_type) == 4 ||
|
||||
sizeof(result_type) == 2 ||
|
||||
sizeof(result_type) == 4 ||
|
||||
sizeof(result_type) == 8,
|
||||
"result_type must be 16, 32 or 64 bits");
|
||||
|
||||
@@ -107,4 +110,21 @@ using random_device = std::random_device;
|
||||
using random_device = std::random_device;
|
||||
#endif
|
||||
|
||||
template<class T = std::mt19937, std::size_t N = T::state_size>
|
||||
auto getSeededRandomEngine () -> typename std::enable_if<!!N, T>::type {
|
||||
typename T::result_type random_data[N];
|
||||
for (unsigned i=0; i<256; i++) {
|
||||
try {
|
||||
random_device source;
|
||||
std::generate(std::begin(random_data), std::end(random_data), std::ref(source));
|
||||
std::seed_seq seeds(std::begin(random_data), std::end(random_data));
|
||||
T seededEngine (seeds);
|
||||
return seededEngine;
|
||||
} catch (...) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("Can't seed random engine");
|
||||
}
|
||||
|
||||
}} // dht::crypto
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -13,8 +14,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
@@ -23,27 +23,42 @@
|
||||
|
||||
namespace dht {
|
||||
|
||||
static constexpr unsigned TARGET_NODES {8};
|
||||
namespace net {
|
||||
class NetworkEngine;
|
||||
}
|
||||
|
||||
struct Bucket {
|
||||
Bucket() : cached() {}
|
||||
Bucket(sa_family_t af, const InfoHash& f = {}, time_point t = time_point::min())
|
||||
: af(af), first(f), time(t), cached() {}
|
||||
sa_family_t af {0};
|
||||
InfoHash first {};
|
||||
time_point time {time_point::min()}; /* time of last reply in this bucket */
|
||||
std::list<std::shared_ptr<Node>> nodes {};
|
||||
std::shared_ptr<Node> cached; /* the address of a likely candidate */
|
||||
time_point time {time_point::min()}; /* time of last reply in this bucket */
|
||||
std::list<Sp<Node>> nodes {};
|
||||
Sp<Node> cached; /* the address of a likely candidate */
|
||||
|
||||
/** Return a random node in a bucket. */
|
||||
std::shared_ptr<Node> randomNode();
|
||||
Sp<Node> randomNode(std::mt19937_64& rd);
|
||||
|
||||
void sendCachedPing(net::NetworkEngine& ne);
|
||||
void connectivityChanged() {
|
||||
time = time_point::min();
|
||||
for (auto& node : nodes)
|
||||
node->setTime(time_point::min());
|
||||
}
|
||||
};
|
||||
|
||||
class RoutingTable : public std::list<Bucket> {
|
||||
public:
|
||||
using std::list<Bucket>::list;
|
||||
|
||||
time_point grow_time {time_point::min()};
|
||||
bool is_client {false};
|
||||
|
||||
InfoHash middle(const RoutingTable::const_iterator&) const;
|
||||
|
||||
std::vector<std::shared_ptr<Node>> findClosestNodes(const InfoHash id, time_point now, size_t count = TARGET_NODES) const;
|
||||
std::vector<Sp<Node>> findClosestNodes(const InfoHash id, time_point now, size_t count = TARGET_NODES) const;
|
||||
|
||||
RoutingTable::iterator findBucket(const InfoHash& id);
|
||||
RoutingTable::const_iterator findBucket(const InfoHash& id) const;
|
||||
@@ -63,10 +78,18 @@ public:
|
||||
return empty() || (size() == 1 && front().nodes.empty());
|
||||
}
|
||||
|
||||
void connectivityChanged(const time_point& now) {
|
||||
grow_time = now;
|
||||
for (auto& b : *this)
|
||||
b.connectivityChanged();
|
||||
}
|
||||
|
||||
bool onNewNode(const Sp<Node>& node, int comfirm, const time_point& now, const InfoHash& myid, net::NetworkEngine& ne);
|
||||
|
||||
/**
|
||||
* Return a random id in the bucket's range.
|
||||
*/
|
||||
InfoHash randomId(const RoutingTable::const_iterator& bucket) const;
|
||||
InfoHash randomId(const RoutingTable::const_iterator& bucket, std::mt19937_64& rd) const;
|
||||
|
||||
unsigned depth(const RoutingTable::const_iterator& bucket) const;
|
||||
|
||||
|
||||
+35
-21
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -14,8 +14,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
@@ -37,8 +36,10 @@ namespace dht {
|
||||
class Scheduler {
|
||||
public:
|
||||
struct Job {
|
||||
Job(std::function<void()>&& f) : do_(std::forward<std::function<void()>>(f)) {}
|
||||
Job(std::function<void()>&& f, time_point t) : do_(std::move(f)), t_(t) {}
|
||||
std::function<void()> do_;
|
||||
const time_point t_;
|
||||
void cancel() { do_ = {}; }
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -49,8 +50,8 @@ public:
|
||||
*
|
||||
* @return pointer to the newly scheduled job.
|
||||
*/
|
||||
std::shared_ptr<Scheduler::Job> add(time_point t, std::function<void()>&& job_func) {
|
||||
auto job = std::make_shared<Job>(std::forward<std::function<void()>>(job_func));
|
||||
Sp<Scheduler::Job> add(time_point t, std::function<void()>&& job_func) {
|
||||
auto job = std::make_shared<Job>(std::move(job_func), t);
|
||||
if (t != time_point::max())
|
||||
timers.emplace(std::move(t), job);
|
||||
return job;
|
||||
@@ -59,17 +60,31 @@ public:
|
||||
/**
|
||||
* Reschedules a job.
|
||||
*
|
||||
* @param time The time at which the job shall be rescheduled.
|
||||
* @param job The job to edit.
|
||||
*
|
||||
* @return pointer to the newly scheduled job.
|
||||
* @param t The time at which the job shall be rescheduled.
|
||||
*/
|
||||
void edit(std::shared_ptr<Scheduler::Job>& job, time_point t) {
|
||||
if (not job) {
|
||||
std::cerr << "editing an empty job" << std::endl;
|
||||
void edit(Sp<Scheduler::Job>& job, time_point t) {
|
||||
if (not job)
|
||||
return;
|
||||
// std::function move doesn't garantee to leave the object empty.
|
||||
// Force clearing old value.
|
||||
auto task = std::move(job->do_);
|
||||
cancel(job);
|
||||
job = add(t, std::move(task));
|
||||
}
|
||||
|
||||
bool cancel(Sp<Scheduler::Job>& job) {
|
||||
if (job) {
|
||||
job->cancel();
|
||||
for (auto r = timers.equal_range(job->t_); r.first != r.second; ++r.first) {
|
||||
if (r.first->second == job) {
|
||||
timers.erase(r.first);
|
||||
job.reset();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
job = add(t, std::move(job->do_));
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,7 +93,6 @@ public:
|
||||
* @return The time for the next job to run.
|
||||
*/
|
||||
time_point run() {
|
||||
syncTime();
|
||||
while (not timers.empty()) {
|
||||
auto timer = timers.begin();
|
||||
/*
|
||||
@@ -89,17 +103,16 @@ public:
|
||||
if (timer->first > now)
|
||||
break;
|
||||
|
||||
const auto& job = *timer->second;
|
||||
if (job.do_)
|
||||
job.do_();
|
||||
auto job = std::move(timer->second);
|
||||
timers.erase(timer);
|
||||
|
||||
if (job->do_)
|
||||
job->do_();
|
||||
}
|
||||
return getNextJobTime();
|
||||
}
|
||||
|
||||
inline time_point getNextJobTime() const {
|
||||
//if (not timers.empty())
|
||||
// std::cout << "Next job in " << print_dt(timers.begin()->first - clock::now()) << std::endl;
|
||||
return timers.empty() ? time_point::max() : timers.begin()->first;
|
||||
}
|
||||
|
||||
@@ -109,10 +122,11 @@ public:
|
||||
*/
|
||||
inline const time_point& time() const { return now; }
|
||||
inline time_point syncTime() { return (now = clock::now()); }
|
||||
inline void syncTime(const time_point& n) { now = n; }
|
||||
|
||||
private:
|
||||
time_point now {clock::now()};
|
||||
std::multimap<time_point, std::shared_ptr<Job>> timers {}; /* the jobs ordered by time */
|
||||
std::multimap<time_point, Sp<Job>> timers {}; /* the jobs ordered by time */
|
||||
};
|
||||
|
||||
}
|
||||
+258
-53
@@ -1,6 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
* Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -13,19 +15,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Additional permission under GNU GPL version 3 section 7:
|
||||
*
|
||||
* If you modify this program, or any covered work, by linking or
|
||||
* combining it with the OpenSSL project's OpenSSL library (or a
|
||||
* modified version of that library), containing parts covered by the
|
||||
* terms of the OpenSSL or SSLeay licenses, Savoir-faire Linux Inc.
|
||||
* grants you additional permission to convey the resulting work.
|
||||
* Corresponding Source for a non-source form of such a combination
|
||||
* shall include the source code for the parts of OpenSSL used as well
|
||||
* as that of the covered work.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
@@ -40,14 +30,20 @@
|
||||
|
||||
namespace dht {
|
||||
|
||||
class SecureDht : public Dht {
|
||||
class OPENDHT_PUBLIC SecureDht final : public DhtInterface {
|
||||
public:
|
||||
|
||||
typedef std::function<void(bool)> SignatureCheckCallback;
|
||||
|
||||
using Config = SecureDhtConfig;
|
||||
|
||||
SecureDht() {}
|
||||
static dht::Config getConfig(const SecureDht::Config& conf)
|
||||
{
|
||||
auto c = conf.node_config;
|
||||
if (not c.node_id and conf.id.second)
|
||||
c.node_id = InfoHash::get("node:"+conf.id.second->getId().toString());
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* s, s6: bound socket descriptors for IPv4 and IPv6, respectively.
|
||||
@@ -55,13 +51,19 @@ public:
|
||||
* id: the identity to use for the crypto layer and to compute
|
||||
* our own hash on the Dht.
|
||||
*/
|
||||
SecureDht(int s, int s6, Config config);
|
||||
SecureDht(std::unique_ptr<DhtInterface> dht, Config config, IdentityAnnouncedCb iacb = {}, const std::shared_ptr<Logger>& l = {});
|
||||
|
||||
virtual ~SecureDht();
|
||||
|
||||
InfoHash getId() const {
|
||||
return key_ ? key_->getPublicKey().getId() : InfoHash();
|
||||
}
|
||||
PkId getLongId() const {
|
||||
return key_ ? key_->getPublicKey().getLongId() : PkId();
|
||||
}
|
||||
Sp<crypto::PublicKey> getPublicKey() const {
|
||||
return key_ ? key_->getSharedPublicKey() : Sp<crypto::PublicKey>{};
|
||||
}
|
||||
|
||||
ValueType secureType(ValueType&& type);
|
||||
|
||||
@@ -70,14 +72,17 @@ public:
|
||||
return secureType(std::move(tmp_type));
|
||||
}
|
||||
|
||||
virtual void registerType(const ValueType& type) {
|
||||
Dht::registerType(secureType(type));
|
||||
void registerType(const ValueType& type) override {
|
||||
if (dht_)
|
||||
dht_->registerType(secureType(type));
|
||||
}
|
||||
virtual void registerType(ValueType&& type) {
|
||||
Dht::registerType(secureType(std::forward<ValueType>(type)));
|
||||
void registerType(ValueType&& type) {
|
||||
if (dht_)
|
||||
dht_->registerType(secureType(std::forward<ValueType>(type)));
|
||||
}
|
||||
virtual void registerInsecureType(const ValueType& type) {
|
||||
Dht::registerType(type);
|
||||
void registerInsecureType(const ValueType& type) {
|
||||
if (dht_)
|
||||
dht_->registerType(type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,23 +90,21 @@ public:
|
||||
* If the signature can't be checked, or if the data can't be decrypted, it is not returned.
|
||||
* Public, non-signed & non-encrypted data is retransmitted as-is.
|
||||
*/
|
||||
virtual void get(const InfoHash& id, GetCallback cb, DoneCallback donecb={}, Value::Filter&& = {}) override;
|
||||
virtual void get(const InfoHash& id, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter&& f = {}) override {
|
||||
get(id, cb, bindDoneCb(donecb), std::forward<Value::Filter>(f));
|
||||
void get(const InfoHash& id, GetCallback cb, DoneCallback donecb={}, Value::Filter&& = {}, Where&& w = {}) override;
|
||||
void get(const InfoHash& id, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter&& f = {}, Where&& w = {}) override {
|
||||
get(id, cb, bindDoneCb(donecb), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter&& f={}) override {
|
||||
get(key, bindGetCb(cb), donecb, std::forward<Value::Filter>(f));
|
||||
void get(const InfoHash& key, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {}) override {
|
||||
get(key, bindGetCb(cb), donecb, std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallbackSimple donecb, Value::Filter&& f={}) override {
|
||||
get(key, bindGetCb(cb), bindDoneCb(donecb), std::forward<Value::Filter>(f));
|
||||
void get(const InfoHash& key, GetCallbackSimple cb, DoneCallbackSimple donecb, Value::Filter&& f={}, Where&& w = {}) override {
|
||||
get(key, bindGetCb(cb), bindDoneCb(donecb), std::forward<Value::Filter>(f), std::forward<Where>(w));
|
||||
}
|
||||
|
||||
virtual size_t listen(const InfoHash& id, GetCallback cb, Value::Filter&& = {}) override;
|
||||
|
||||
/**
|
||||
* Will take ownership of the value, sign it using our private key and put it in the DHT.
|
||||
*/
|
||||
void putSigned(const InfoHash& hash, std::shared_ptr<Value> val, DoneCallback callback, bool permanent = false);
|
||||
void putSigned(const InfoHash& hash, Sp<Value> val, DoneCallback callback, bool permanent = false);
|
||||
void putSigned(const InfoHash& hash, Value&& v, DoneCallback callback, bool permanent = false) {
|
||||
putSigned(hash, std::make_shared<Value>(std::move(v)), callback, permanent);
|
||||
}
|
||||
@@ -111,10 +114,14 @@ public:
|
||||
* and put it in the DHT.
|
||||
* The operation will be immediate if the recipient' public key is known (otherwise it will be retrived first).
|
||||
*/
|
||||
void putEncrypted(const InfoHash& hash, const InfoHash& to, std::shared_ptr<Value> val, DoneCallback callback, bool permanent = false);
|
||||
void putEncrypted(const InfoHash& hash, const InfoHash& to, Sp<Value> val, DoneCallback callback, bool permanent = false);
|
||||
void putEncrypted(const InfoHash& hash, const InfoHash& to, Value&& v, DoneCallback callback, bool permanent = false) {
|
||||
putEncrypted(hash, to, std::make_shared<Value>(std::move(v)), callback, permanent);
|
||||
}
|
||||
void putEncrypted(const InfoHash& hash, const crypto::PublicKey& to, Sp<Value> val, DoneCallback callback, bool permanent = false);
|
||||
void putEncrypted(const InfoHash& hash, const crypto::PublicKey& to, Value&& v, DoneCallback callback, bool permanent = false) {
|
||||
putEncrypted(hash, to, std::make_shared<Value>(std::move(v)), callback, permanent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take ownership of the value and sign it using our private key.
|
||||
@@ -125,17 +132,14 @@ public:
|
||||
|
||||
Value decrypt(const Value& v);
|
||||
|
||||
void findCertificate(const InfoHash& node, std::function<void(const std::shared_ptr<crypto::Certificate>)> cb);
|
||||
void findPublicKey(const InfoHash& node, std::function<void(const std::shared_ptr<crypto::PublicKey>)> cb);
|
||||
void findCertificate(const InfoHash& node, const std::function<void(const Sp<crypto::Certificate>)>& cb);
|
||||
void findPublicKey(const InfoHash& node, const std::function<void(const Sp<crypto::PublicKey>)>& cb);
|
||||
|
||||
const std::shared_ptr<crypto::Certificate> registerCertificate(const InfoHash& node, const Blob& cert);
|
||||
void registerCertificate(std::shared_ptr<crypto::Certificate>& cert);
|
||||
Sp<crypto::Certificate> registerCertificate(const InfoHash& node, const Blob& cert);
|
||||
void registerCertificate(Sp<crypto::Certificate>& cert);
|
||||
|
||||
const std::shared_ptr<crypto::Certificate> getCertificate(const InfoHash& node) const;
|
||||
const std::shared_ptr<crypto::PublicKey> getPublicKey(const InfoHash& node) const;
|
||||
|
||||
|
||||
|
||||
Sp<crypto::Certificate> getCertificate(const InfoHash& node) const;
|
||||
Sp<crypto::PublicKey> getPublicKey(const InfoHash& node) const;
|
||||
|
||||
/**
|
||||
* Allows to set a custom callback called by the library to find a locally-stored certificate.
|
||||
@@ -146,30 +150,231 @@ public:
|
||||
localQueryMethod_ = std::move(query_method);
|
||||
}
|
||||
|
||||
/**
|
||||
* SecureDht to Dht proxy
|
||||
*/
|
||||
void shutdown(ShutdownCallback cb, bool stop = false) override {
|
||||
dht_->shutdown(cb, stop);
|
||||
}
|
||||
void dumpTables() const override {
|
||||
dht_->dumpTables();
|
||||
}
|
||||
inline const InfoHash& getNodeId() const override { return dht_->getNodeId(); }
|
||||
|
||||
std::pair<size_t, size_t> getStoreSize() const override {
|
||||
return dht_->getStoreSize();
|
||||
}
|
||||
std::string getStorageLog() const override {
|
||||
return dht_->getStorageLog();
|
||||
}
|
||||
std::string getStorageLog(const InfoHash& h) const override {
|
||||
return dht_->getStorageLog(h);
|
||||
}
|
||||
void setStorageLimit(size_t limit = DEFAULT_STORAGE_LIMIT) override {
|
||||
dht_->setStorageLimit(limit);
|
||||
}
|
||||
size_t getStorageLimit() const override {
|
||||
return dht_->getStorageLimit();
|
||||
}
|
||||
|
||||
std::vector<NodeExport> exportNodes() const override {
|
||||
return dht_->exportNodes();
|
||||
}
|
||||
std::vector<ValuesExport> exportValues() const override {
|
||||
return dht_->exportValues();
|
||||
}
|
||||
void importValues(const std::vector<ValuesExport>& v) override {
|
||||
dht_->importValues(v);
|
||||
}
|
||||
NodeStats getNodesStats(sa_family_t af) const override {
|
||||
return dht_->getNodesStats(af);
|
||||
}
|
||||
std::vector<unsigned> getNodeMessageStats(bool in = false) override {
|
||||
return dht_->getNodeMessageStats(in);
|
||||
}
|
||||
std::string getRoutingTablesLog(sa_family_t af) const override {
|
||||
return dht_->getRoutingTablesLog(af);
|
||||
}
|
||||
std::string getSearchesLog(sa_family_t af) const override {
|
||||
return dht_->getSearchesLog(af);
|
||||
}
|
||||
std::string getSearchLog(const InfoHash& h, sa_family_t af = AF_UNSPEC) const override {
|
||||
return dht_->getSearchLog(h, af);
|
||||
}
|
||||
std::vector<SockAddr> getPublicAddress(sa_family_t family = 0) override {
|
||||
return dht_->getPublicAddress(family);
|
||||
}
|
||||
time_point periodic(const uint8_t *buf, size_t buflen, SockAddr sa, const time_point& now) override {
|
||||
return dht_->periodic(buf, buflen, std::move(sa), now);
|
||||
}
|
||||
time_point periodic(const uint8_t *buf, size_t buflen, const sockaddr* from, socklen_t fromlen, const time_point& now) override {
|
||||
return dht_->periodic(buf, buflen, from, fromlen, now);
|
||||
}
|
||||
NodeStatus updateStatus(sa_family_t af) override {
|
||||
return dht_->updateStatus(af);
|
||||
}
|
||||
NodeStatus getStatus(sa_family_t af) const override {
|
||||
return dht_->getStatus(af);
|
||||
}
|
||||
NodeStatus getStatus() const override {
|
||||
return dht_->getStatus();
|
||||
}
|
||||
net::DatagramSocket* getSocket() const override {
|
||||
return dht_->getSocket();
|
||||
};
|
||||
bool isRunning(sa_family_t af = 0) const override {
|
||||
return dht_->isRunning(af);
|
||||
}
|
||||
const ValueType& getType(ValueType::Id type_id) const override {
|
||||
return dht_->getType(type_id);
|
||||
}
|
||||
void addBootstrap(const std::string& host, const std::string& service) override {
|
||||
dht_->addBootstrap(host, service);
|
||||
}
|
||||
void clearBootstrap() override {
|
||||
dht_->clearBootstrap();
|
||||
}
|
||||
void insertNode(const InfoHash& id, const SockAddr& sa) override {
|
||||
dht_->insertNode(id, sa);
|
||||
}
|
||||
void insertNode(const NodeExport& n) override {
|
||||
dht_->insertNode(n);
|
||||
}
|
||||
void pingNode(SockAddr sa, DoneCallbackSimple&& cb={}) override {
|
||||
dht_->pingNode(std::move(sa), std::move(cb));
|
||||
}
|
||||
void query(const InfoHash& key, QueryCallback cb, DoneCallback done_cb = {}, Query&& q = {}) override {
|
||||
dht_->query(key, cb, done_cb, std::move(q));
|
||||
}
|
||||
void query(const InfoHash& key, QueryCallback cb, DoneCallbackSimple done_cb = {}, Query&& q = {}) override {
|
||||
dht_->query(key, cb, done_cb, std::move(q));
|
||||
}
|
||||
std::vector<Sp<Value>> getLocal(const InfoHash& key, const Value::Filter& f = {}) const override {
|
||||
return dht_->getLocal(key, f);
|
||||
}
|
||||
Sp<Value> getLocalById(const InfoHash& key, Value::Id vid) const override {
|
||||
return dht_->getLocalById(key, vid);
|
||||
}
|
||||
void put(const InfoHash& key,
|
||||
Sp<Value> v,
|
||||
DoneCallback cb=nullptr,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false) override
|
||||
{
|
||||
dht_->put(key, v, cb, created, permanent);
|
||||
}
|
||||
void put(const InfoHash& key,
|
||||
const Sp<Value>& v,
|
||||
DoneCallbackSimple cb,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false) override
|
||||
{
|
||||
dht_->put(key, v, cb, created, permanent);
|
||||
}
|
||||
|
||||
void put(const InfoHash& key,
|
||||
Value&& v,
|
||||
DoneCallback cb=nullptr,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false) override
|
||||
{
|
||||
dht_->put(key, std::move(v), cb, created, permanent);
|
||||
}
|
||||
void put(const InfoHash& key,
|
||||
Value&& v,
|
||||
DoneCallbackSimple cb,
|
||||
time_point created=time_point::max(),
|
||||
bool permanent = false) override
|
||||
{
|
||||
dht_->put(key, std::move(v), cb, created, permanent);
|
||||
}
|
||||
std::vector<Sp<Value>> getPut(const InfoHash& h) const override {
|
||||
return dht_->getPut(h);
|
||||
}
|
||||
Sp<Value> getPut(const InfoHash& h, const Value::Id& vid) const override {
|
||||
return dht_->getPut(h, vid);
|
||||
}
|
||||
bool cancelPut(const InfoHash& h, const Value::Id& vid) override {
|
||||
return dht_->cancelPut(h, vid);
|
||||
}
|
||||
|
||||
size_t listen(const InfoHash& key, ValueCallback, Value::Filter={}, Where={}) override;
|
||||
size_t listen(const InfoHash& key, GetCallback cb, Value::Filter = {}, Where w = {}) override;
|
||||
size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}, Where w = {}) override {
|
||||
return listen(key, bindGetCb(cb), f, w);
|
||||
}
|
||||
bool cancelListen(const InfoHash& h, size_t token) override {
|
||||
return dht_->cancelListen(h, token);
|
||||
}
|
||||
void connectivityChanged(sa_family_t af) override {
|
||||
dht_->connectivityChanged(af);
|
||||
}
|
||||
void connectivityChanged() override {
|
||||
dht_->connectivityChanged();
|
||||
}
|
||||
|
||||
void forwardAllMessages(bool forward) {
|
||||
forward_all_ = forward;
|
||||
}
|
||||
|
||||
void setPushNotificationToken(const std::string& token = "") override {
|
||||
dht_->setPushNotificationToken(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call linked callback with push_notification
|
||||
* @param notification to process
|
||||
*/
|
||||
void pushNotificationReceived(const std::map<std::string, std::string>& notification) override {
|
||||
dht_->pushNotificationReceived(notification);
|
||||
}
|
||||
|
||||
void setLogger(const Logger& logger) override {
|
||||
DhtInterface::setLogger(logger);
|
||||
dht_->setLogger(logger);
|
||||
}
|
||||
|
||||
void setLogger(const std::shared_ptr<Logger>& logger) override {
|
||||
DhtInterface::setLogger(logger);
|
||||
dht_->setLogger(logger);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only print logs related to the given InfoHash (if given), or disable filter (if zeroes).
|
||||
*/
|
||||
void setLogFilter(const InfoHash& f) override {
|
||||
DhtInterface::setLogFilter(f);
|
||||
dht_->setLogFilter(f);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<DhtInterface> dht_;
|
||||
// prevent copy
|
||||
SecureDht(const SecureDht&) = delete;
|
||||
SecureDht& operator=(const SecureDht&) = delete;
|
||||
|
||||
GetCallback getCallbackFilter(GetCallback, Value::Filter&&);
|
||||
Sp<Value> checkValue(const Sp<Value>& v);
|
||||
ValueCallback getCallbackFilter(const ValueCallback&, Value::Filter&&);
|
||||
GetCallback getCallbackFilter(const GetCallback&, Value::Filter&&);
|
||||
|
||||
std::shared_ptr<crypto::PrivateKey> key_ {};
|
||||
std::shared_ptr<crypto::Certificate> certificate_ {};
|
||||
Sp<crypto::PrivateKey> key_ {};
|
||||
Sp<crypto::Certificate> certificate_ {};
|
||||
|
||||
// method to query the local certificate store
|
||||
CertificateStoreQuery localQueryMethod_ {};
|
||||
|
||||
// our certificate cache
|
||||
std::map<InfoHash, std::shared_ptr<crypto::Certificate>> nodesCertificates_ {};
|
||||
std::map<InfoHash, std::shared_ptr<crypto::PublicKey>> nodesPubKeys_ {};
|
||||
std::map<InfoHash, Sp<crypto::Certificate>> nodesCertificates_ {};
|
||||
std::map<InfoHash, Sp<crypto::PublicKey>> nodesPubKeys_ {};
|
||||
|
||||
std::uniform_int_distribution<Value::Id> rand_id {};
|
||||
std::atomic_bool forward_all_ {false};
|
||||
bool enableCache_ {false};
|
||||
};
|
||||
|
||||
const ValueType CERTIFICATE_TYPE = {
|
||||
8, "Certificate", std::chrono::hours(24 * 7),
|
||||
// A certificate can only be stored at it's public key ID.
|
||||
[](InfoHash id, std::shared_ptr<Value>& v, InfoHash, const sockaddr*, socklen_t) {
|
||||
// A certificate can only be stored at its public key ID.
|
||||
[](InfoHash id, Sp<Value>& v, const InfoHash&, const SockAddr&) {
|
||||
try {
|
||||
crypto::Certificate crt(v->data);
|
||||
// TODO check certificate signature
|
||||
@@ -177,7 +382,7 @@ const ValueType CERTIFICATE_TYPE = {
|
||||
} catch (const std::exception& e) {}
|
||||
return false;
|
||||
},
|
||||
[](InfoHash, const std::shared_ptr<Value>& o, std::shared_ptr<Value>& n, InfoHash, const sockaddr*, socklen_t) {
|
||||
[](InfoHash, const Sp<Value>& o, Sp<Value>& n, const InfoHash&, const SockAddr&) {
|
||||
try {
|
||||
return crypto::Certificate(o->data).getPublicKey().getId() == crypto::Certificate(n->data).getPublicKey().getId();
|
||||
} catch (const std::exception& e) {}
|
||||
|
||||
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "def.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#ifdef __ANDROID__
|
||||
typedef uint16_t in_port_t;
|
||||
#endif
|
||||
#else
|
||||
#include <iso646.h>
|
||||
#include <stdint.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2def.h>
|
||||
#include <ws2tcpip.h>
|
||||
typedef uint16_t sa_family_t;
|
||||
typedef uint16_t in_port_t;
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <cstddef>
|
||||
|
||||
namespace dht {
|
||||
|
||||
OPENDHT_PUBLIC std::string print_addr(const sockaddr* sa, socklen_t slen);
|
||||
OPENDHT_PUBLIC std::string print_addr(const sockaddr_storage& ss, socklen_t sslen);
|
||||
|
||||
/**
|
||||
* A Socket Address (sockaddr*), with abstraction for IPv4, IPv6 address families.
|
||||
*/
|
||||
class OPENDHT_PUBLIC SockAddr {
|
||||
public:
|
||||
SockAddr() {}
|
||||
SockAddr(const SockAddr& o) {
|
||||
set(o.get(), o.getLength());
|
||||
}
|
||||
SockAddr(SockAddr&& o) noexcept : addr(std::move(o.addr)), len(o.len) {
|
||||
o.len = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build from existing address.
|
||||
*/
|
||||
SockAddr(const sockaddr* sa, socklen_t length) {
|
||||
if (length > static_cast<socklen_t>(sizeof(sockaddr_storage)))
|
||||
throw std::runtime_error("Socket address length is too large");
|
||||
set(sa, length);
|
||||
}
|
||||
SockAddr(const sockaddr* sa) {
|
||||
socklen_t len = 0;
|
||||
if (sa) {
|
||||
if (sa->sa_family == AF_INET)
|
||||
len = sizeof(sockaddr_in);
|
||||
else if(sa->sa_family == AF_INET6)
|
||||
len = sizeof(sockaddr_in6);
|
||||
else
|
||||
throw std::runtime_error("Unknown address family");
|
||||
}
|
||||
set(sa, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build from an existing sockaddr_storage structure.
|
||||
*/
|
||||
SockAddr(const sockaddr_storage& ss, socklen_t len) : SockAddr((const sockaddr*)&ss, len) {}
|
||||
|
||||
static std::vector<SockAddr> resolve(const std::string& host, const std::string& service = {});
|
||||
|
||||
bool operator<(const SockAddr& o) const {
|
||||
if (len != o.len)
|
||||
return len < o.len;
|
||||
return std::memcmp((const uint8_t*)get(), (const uint8_t*)o.get(), len) < 0;
|
||||
}
|
||||
|
||||
bool equals(const SockAddr& o) const {
|
||||
return len == o.len
|
||||
&& std::memcmp((const uint8_t*)get(), (const uint8_t*)o.get(), len) == 0;
|
||||
}
|
||||
SockAddr& operator=(const SockAddr& o) {
|
||||
set(o.get(), o.getLength());
|
||||
return *this;
|
||||
}
|
||||
SockAddr& operator=(SockAddr&& o) {
|
||||
len = o.len;
|
||||
o.len = 0;
|
||||
addr = std::move(o.addr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string toString() const {
|
||||
return print_addr(get(), getLength());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address family or AF_UNSPEC if the address is not set.
|
||||
*/
|
||||
sa_family_t getFamily() const { return len ? addr->sa_family : AF_UNSPEC; }
|
||||
|
||||
/**
|
||||
* Resize the managed structure to the appropriate size (if needed),
|
||||
* in which case the sockaddr structure is cleared to zero,
|
||||
* and set the address family field (sa_family).
|
||||
*/
|
||||
void setFamily(sa_family_t af) {
|
||||
socklen_t new_length;
|
||||
switch(af) {
|
||||
case AF_INET:
|
||||
new_length = sizeof(sockaddr_in);
|
||||
break;
|
||||
case AF_INET6:
|
||||
new_length = sizeof(sockaddr_in6);
|
||||
break;
|
||||
default:
|
||||
new_length = 0;
|
||||
}
|
||||
if (new_length != len) {
|
||||
len = new_length;
|
||||
if (len) addr.reset((sockaddr*)::calloc(len, 1));
|
||||
else addr.reset();
|
||||
}
|
||||
if (len)
|
||||
addr->sa_family = af;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Network Interface to any
|
||||
*/
|
||||
void setAny() {
|
||||
auto family = getFamily();
|
||||
switch(family) {
|
||||
case AF_INET:
|
||||
getIPv4().sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
break;
|
||||
case AF_INET6:
|
||||
getIPv6().sin6_addr = in6addr_any;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retreive the port (in host byte order) or 0 if the address is not
|
||||
* of a supported family.
|
||||
*/
|
||||
in_port_t getPort() const {
|
||||
switch(getFamily()) {
|
||||
case AF_INET:
|
||||
return ntohs(getIPv4().sin_port);
|
||||
case AF_INET6:
|
||||
return ntohs(getIPv6().sin6_port);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Set the port. The address must be of a supported family.
|
||||
* @param p The port in host byte order.
|
||||
*/
|
||||
void setPort(in_port_t p) {
|
||||
switch(getFamily()) {
|
||||
case AF_INET:
|
||||
getIPv4().sin_port = htons(p);
|
||||
break;
|
||||
case AF_INET6:
|
||||
getIPv6().sin6_port = htons(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the address part of the socket address from a numeric IP address (string representation).
|
||||
* Family must be already set. Throws in case of parse failue.
|
||||
*/
|
||||
void setAddress(const char* address);
|
||||
|
||||
/**
|
||||
* Returns the accessible byte length at the pointer returned by #get().
|
||||
* If zero, #get() returns null.
|
||||
*/
|
||||
socklen_t getLength() const { return len; }
|
||||
|
||||
/**
|
||||
* An address is defined to be true if its length is not zero.
|
||||
*/
|
||||
explicit operator bool() const noexcept {
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address to the managed sockaddr structure.
|
||||
* The accessible length is returned by #getLength().
|
||||
*/
|
||||
const sockaddr* get() const { return addr.get(); }
|
||||
|
||||
/**
|
||||
* Returns the address to the managed sockaddr structure.
|
||||
* The accessible length is returned by #getLength().
|
||||
*/
|
||||
sockaddr* get() { return addr.get(); }
|
||||
|
||||
inline const sockaddr_in& getIPv4() const {
|
||||
return *reinterpret_cast<const sockaddr_in*>(get());
|
||||
}
|
||||
inline const sockaddr_in6& getIPv6() const {
|
||||
return *reinterpret_cast<const sockaddr_in6*>(get());
|
||||
}
|
||||
inline sockaddr_in& getIPv4() {
|
||||
return *reinterpret_cast<sockaddr_in*>(get());
|
||||
}
|
||||
inline sockaddr_in6& getIPv6() {
|
||||
return *reinterpret_cast<sockaddr_in6*>(get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if address is a loopback IP address.
|
||||
*/
|
||||
bool isLoopback() const;
|
||||
|
||||
/**
|
||||
* Return true if address is not a public IP address.
|
||||
*/
|
||||
bool isPrivate() const;
|
||||
|
||||
bool isUnspecified() const;
|
||||
|
||||
bool isMappedIPv4() const;
|
||||
SockAddr getMappedIPv4();
|
||||
SockAddr getMappedIPv6();
|
||||
|
||||
/**
|
||||
* A comparator to classify IP addresses, only considering the
|
||||
* first 64 bits in IPv6.
|
||||
*/
|
||||
struct ipCmp {
|
||||
bool operator()(const SockAddr& a, const SockAddr& b) const {
|
||||
if (a.len != b.len)
|
||||
return a.len < b.len;
|
||||
socklen_t start, len;
|
||||
switch(a.getFamily()) {
|
||||
case AF_INET:
|
||||
start = offsetof(sockaddr_in, sin_addr);
|
||||
len = sizeof(in_addr);
|
||||
break;
|
||||
case AF_INET6:
|
||||
start = offsetof(sockaddr_in6, sin6_addr);
|
||||
// don't consider more than 64 bits (IPv6)
|
||||
len = 8;
|
||||
break;
|
||||
default:
|
||||
start = 0;
|
||||
len = a.len;
|
||||
break;
|
||||
}
|
||||
return std::memcmp((uint8_t*)a.get()+start,
|
||||
(uint8_t*)b.get()+start, len) < 0;
|
||||
}
|
||||
};
|
||||
private:
|
||||
struct free_delete { void operator()(void* p) { ::free(p); } };
|
||||
std::unique_ptr<sockaddr, free_delete> addr {};
|
||||
socklen_t len {0};
|
||||
|
||||
void set(const sockaddr* sa, socklen_t length) {
|
||||
if (len != length) {
|
||||
len = length;
|
||||
if (len) addr.reset((sockaddr*)::malloc(len));
|
||||
else addr.reset();
|
||||
}
|
||||
if (len)
|
||||
std::memcpy((uint8_t*)get(), (const uint8_t*)sa, len);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
OPENDHT_PUBLIC bool operator==(const SockAddr& a, const SockAddr& b);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "def.h"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <future>
|
||||
#include <functional>
|
||||
|
||||
#include <ciso646> // fix windows compiler bug
|
||||
|
||||
namespace dht {
|
||||
|
||||
class OPENDHT_PUBLIC ThreadPool {
|
||||
public:
|
||||
static ThreadPool& computation();
|
||||
static ThreadPool& io();
|
||||
|
||||
ThreadPool();
|
||||
ThreadPool(size_t maxThreads);
|
||||
~ThreadPool();
|
||||
|
||||
void run(std::function<void()>&& cb);
|
||||
|
||||
template<class T>
|
||||
std::future<T> get(std::function<T()>&& cb) {
|
||||
auto ret = std::make_shared<std::promise<T>>();
|
||||
run([cb = std::move(cb), ret]() mutable {
|
||||
try {
|
||||
ret->set_value(cb());
|
||||
} catch (...) {
|
||||
try {
|
||||
ret->set_exception(std::current_exception());
|
||||
} catch(...) {}
|
||||
}
|
||||
});
|
||||
return ret->get_future();
|
||||
}
|
||||
template<class T>
|
||||
std::shared_future<T> getShared(std::function<T()>&& cb) {
|
||||
return get(std::move(cb));
|
||||
}
|
||||
|
||||
void stop();
|
||||
void join();
|
||||
|
||||
private:
|
||||
std::mutex lock_ {};
|
||||
std::condition_variable cv_ {};
|
||||
std::queue<std::function<void()>> tasks_ {};
|
||||
std::vector<std::unique_ptr<std::thread>> threads_;
|
||||
unsigned readyThreads_ {0};
|
||||
bool running_ {true};
|
||||
|
||||
const unsigned maxThreads_;
|
||||
};
|
||||
|
||||
class OPENDHT_PUBLIC Executor : public std::enable_shared_from_this<Executor> {
|
||||
public:
|
||||
Executor(ThreadPool& pool, unsigned maxConcurrent = 1)
|
||||
: threadPool_(pool), maxConcurrent_(maxConcurrent)
|
||||
{}
|
||||
|
||||
void run(std::function<void()>&& task);
|
||||
|
||||
private:
|
||||
std::reference_wrapper<ThreadPool> threadPool_;
|
||||
const unsigned maxConcurrent_ {1};
|
||||
std::mutex lock_ {};
|
||||
unsigned current_ {0};
|
||||
std::queue<std::function<void()>> tasks_ {};
|
||||
|
||||
void run_(std::function<void()>&& task);
|
||||
void schedule();
|
||||
};
|
||||
|
||||
class OPENDHT_PUBLIC ExecutionContext {
|
||||
public:
|
||||
ExecutionContext(ThreadPool& pool)
|
||||
: threadPool_(pool), state_(std::make_shared<SharedState>())
|
||||
{}
|
||||
|
||||
~ExecutionContext() {
|
||||
state_->destroy();
|
||||
}
|
||||
|
||||
/** Wait for ongoing tasks to complete execution and drop other pending tasks */
|
||||
void stop() {
|
||||
state_->destroy(false);
|
||||
}
|
||||
|
||||
void run(std::function<void()>&& task) {
|
||||
std::lock_guard<std::mutex> lock(state_->mtx);
|
||||
if (state_->shutdown_) return;
|
||||
state_->pendingTasks++;
|
||||
threadPool_.get().run([task = std::move(task), state = state_] {
|
||||
state->run(task);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
struct SharedState {
|
||||
std::mutex mtx {};
|
||||
std::condition_variable cv {};
|
||||
unsigned pendingTasks {0};
|
||||
unsigned ongoingTasks {0};
|
||||
/** When true, prevents new tasks to be scheduled */
|
||||
bool shutdown_ {false};
|
||||
/** When true, prevents scheduled tasks to be executed */
|
||||
std::atomic_bool destroyed {false};
|
||||
|
||||
void destroy(bool wait = true) {
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
if (destroyed) return;
|
||||
if (wait) {
|
||||
cv.wait(lock, [this] { return pendingTasks == 0 && ongoingTasks == 0; });
|
||||
}
|
||||
shutdown_ = true;
|
||||
if (not wait) {
|
||||
cv.wait(lock, [this] { return ongoingTasks == 0; });
|
||||
}
|
||||
destroyed = true;
|
||||
}
|
||||
|
||||
void run(const std::function<void()>& task) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
pendingTasks--;
|
||||
ongoingTasks++;
|
||||
}
|
||||
if (destroyed) return;
|
||||
task();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
ongoingTasks--;
|
||||
cv.notify_all();
|
||||
}
|
||||
}
|
||||
};
|
||||
std::reference_wrapper<ThreadPool> threadPool_;
|
||||
std::shared_ptr<SharedState> state_;
|
||||
};
|
||||
|
||||
}
|
||||
+77
-64
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -13,24 +13,15 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define WANT4 1
|
||||
#define WANT6 2
|
||||
#include "def.h"
|
||||
|
||||
#include <msgpack.hpp>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/socket.h>
|
||||
#else
|
||||
#include <ws2def.h>
|
||||
#include <ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
#include <chrono>
|
||||
#include <random>
|
||||
#include <functional>
|
||||
@@ -38,16 +29,22 @@
|
||||
|
||||
#include <cstdarg>
|
||||
|
||||
#define WANT4 1
|
||||
#define WANT6 2
|
||||
|
||||
/**
|
||||
* OpenDHT C++ namespace
|
||||
*/
|
||||
namespace dht {
|
||||
|
||||
static constexpr unsigned TARGET_NODES {8};
|
||||
|
||||
using Address = std::pair<sockaddr_storage, socklen_t>;
|
||||
using NetId = uint32_t;
|
||||
using want_t = int_fast8_t;
|
||||
|
||||
std::string print_addr(const sockaddr* sa, socklen_t slen);
|
||||
std::string print_addr(const sockaddr_storage& ss, socklen_t sslen);
|
||||
std::string printAddr(const Address& addr);
|
||||
OPENDHT_PUBLIC const char* version();
|
||||
|
||||
// shortcut for std::shared_ptr
|
||||
template<class T>
|
||||
using Sp = std::shared_ptr<T>;
|
||||
|
||||
template <typename Key, typename Item, typename Condition>
|
||||
void erase_if(std::map<Key, Item>& map, const Condition& condition)
|
||||
@@ -59,22 +56,41 @@ void erase_if(std::map<Key, Item>& map, const Condition& condition)
|
||||
}
|
||||
}
|
||||
|
||||
class DhtException : public std::runtime_error {
|
||||
public:
|
||||
DhtException(const std::string &str = "") :
|
||||
std::runtime_error("DhtException occured: " + str) {}
|
||||
/**
|
||||
* Split "[host]:port" or "host:port" to pair<"host", "port">.
|
||||
*/
|
||||
OPENDHT_PUBLIC std::pair<std::string, std::string>
|
||||
splitPort(const std::string& s);
|
||||
|
||||
class OPENDHT_PUBLIC DhtException : public std::runtime_error {
|
||||
public:
|
||||
DhtException(const std::string &str = "") :
|
||||
std::runtime_error("DhtException occurred: " + str) {}
|
||||
};
|
||||
|
||||
class OPENDHT_PUBLIC SocketException : public DhtException {
|
||||
public:
|
||||
SocketException(int err) :
|
||||
DhtException(strerror(err)) {}
|
||||
};
|
||||
|
||||
// Time related definitions and utility functions
|
||||
|
||||
using clock = std::chrono::steady_clock;
|
||||
using system_clock = std::chrono::system_clock;
|
||||
using time_point = clock::time_point;
|
||||
using duration = clock::duration;
|
||||
|
||||
time_point from_time_t(std::time_t t);
|
||||
std::time_t to_time_t(time_point t);
|
||||
|
||||
inline std::string
|
||||
to_str(double d) {
|
||||
char buf[16];
|
||||
auto ret = snprintf(buf, sizeof(buf), "%.3g", d);
|
||||
return (ret < 0) ? std::to_string(d) : std::string(buf, ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts std::chrono::duration to floating-point seconds.
|
||||
*/
|
||||
@@ -84,8 +100,32 @@ print_dt(DT d) {
|
||||
return std::chrono::duration_cast<std::chrono::duration<double>>(d).count();
|
||||
}
|
||||
|
||||
static /*constexpr*/ const time_point TIME_INVALID = {time_point::min()};
|
||||
static /*constexpr*/ const time_point TIME_MAX {time_point::max()};
|
||||
template <class DT>
|
||||
static std::string
|
||||
print_duration(DT d) {
|
||||
if (d < std::chrono::seconds(0)) {
|
||||
return "-" + print_duration(-d);
|
||||
} else if (d < std::chrono::milliseconds(1)) {
|
||||
return to_str(std::chrono::duration_cast<std::chrono::duration<double, std::micro>>(d).count()) + " us";
|
||||
} else if (d < std::chrono::seconds(1)) {
|
||||
return to_str(std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(d).count()) + " ms";
|
||||
} else if (d < std::chrono::minutes(1)) {
|
||||
return to_str(std::chrono::duration_cast<std::chrono::duration<double>>(d).count()) + " s";
|
||||
} else if (d < std::chrono::hours(1)) {
|
||||
return to_str(std::chrono::duration_cast<std::chrono::duration<double, std::ratio<60>>>(d).count()) + " min";
|
||||
} else {
|
||||
return to_str(std::chrono::duration_cast<std::chrono::duration<double, std::ratio<3600>>>(d).count()) + " h";
|
||||
}
|
||||
}
|
||||
|
||||
template <class TimePoint>
|
||||
static std::string
|
||||
print_time_relative(TimePoint now, TimePoint d) {
|
||||
if (d == TimePoint::min()) return "never";
|
||||
if (d == now) return "now";
|
||||
return (d > now) ? std::string("in ") + print_duration(d - now)
|
||||
: print_duration(now - d) + std::string(" ago");
|
||||
}
|
||||
|
||||
template <typename Duration = duration>
|
||||
class uniform_duration_distribution : public std::uniform_int_distribution<typename Duration::rep> {
|
||||
@@ -103,53 +143,17 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// Logging related utility functions
|
||||
|
||||
/**
|
||||
* Dummy function used to disable logging
|
||||
*/
|
||||
inline void NOLOG(char const*, va_list) {}
|
||||
|
||||
/**
|
||||
* Wrapper for logging methods
|
||||
*/
|
||||
struct LogMethod {
|
||||
LogMethod() = default;
|
||||
|
||||
template<typename T>
|
||||
LogMethod(T&& t) : func(std::forward<T>(t)) {}
|
||||
|
||||
void operator()(char const* format, ...) const {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
func(format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void logPrintable(const uint8_t *buf, size_t buflen) const {
|
||||
std::string buf_clean(buflen, '\0');
|
||||
for (size_t i=0; i<buflen; i++)
|
||||
buf_clean[i] = isprint(buf[i]) ? buf[i] : '.';
|
||||
(*this)("%s", buf_clean.c_str());
|
||||
}
|
||||
private:
|
||||
std::function<void(char const*, va_list)> func;
|
||||
};
|
||||
|
||||
struct Logger {
|
||||
LogMethod DEBUG = NOLOG;
|
||||
LogMethod WARN = NOLOG;
|
||||
LogMethod ERR = NOLOG;
|
||||
};
|
||||
|
||||
// Serialization related definitions and utility functions
|
||||
|
||||
/**
|
||||
* Arbitrary binary data.
|
||||
*/
|
||||
using Blob = std::vector<uint8_t>;
|
||||
|
||||
/**
|
||||
* Provides backward compatibility with msgpack 1.0
|
||||
*/
|
||||
Blob unpackBlob(msgpack::object& o);
|
||||
OPENDHT_PUBLIC Blob unpackBlob(const msgpack::object& o);
|
||||
|
||||
template <typename Type>
|
||||
Blob
|
||||
@@ -169,4 +173,13 @@ unpackMsg(Blob b) {
|
||||
|
||||
msgpack::unpacked unpackMsg(Blob b);
|
||||
|
||||
msgpack::object* findMapValue(const msgpack::object& map, const char* key, size_t length);
|
||||
|
||||
inline msgpack::object* findMapValue(const msgpack::object& map, const char* key) {
|
||||
return findMapValue(map, key, strlen(key));
|
||||
}
|
||||
inline msgpack::object* findMapValue(const msgpack::object& map, std::string_view key) {
|
||||
return findMapValue(map, key.data(), key.size());
|
||||
}
|
||||
|
||||
} // namespace dht
|
||||
+603
-72
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2016 Savoir-faire Linux Inc.
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author(s) : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
* Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
* Simon Désaulniers <simon.desaulniers@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -14,8 +14,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
@@ -23,10 +22,12 @@
|
||||
#include "infohash.h"
|
||||
#include "crypto.h"
|
||||
#include "utils.h"
|
||||
#include "sockaddr.h"
|
||||
|
||||
#include <msgpack.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <sstream>
|
||||
#include <bitset>
|
||||
#include <vector>
|
||||
@@ -35,10 +36,30 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
#include <set>
|
||||
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
#include <json/json.h>
|
||||
#endif
|
||||
|
||||
namespace dht {
|
||||
using namespace std::literals;
|
||||
|
||||
static constexpr auto VALUE_KEY_ID("id");
|
||||
static const std::string VALUE_KEY_DAT("dat");
|
||||
static const std::string VALUE_KEY_PRIO("p");
|
||||
static const std::string VALUE_KEY_SIGNATURE("sig");
|
||||
|
||||
static const std::string VALUE_KEY_SEQ("seq");
|
||||
static const std::string VALUE_KEY_DATA("data");
|
||||
static const std::string VALUE_KEY_OWNER("owner");
|
||||
static const std::string VALUE_KEY_TYPE("type");
|
||||
static const std::string VALUE_KEY_TO("to");
|
||||
static const std::string VALUE_KEY_BODY("body");
|
||||
static const std::string VALUE_KEY_USERTYPE("utype");
|
||||
|
||||
struct Value;
|
||||
struct Query;
|
||||
|
||||
/**
|
||||
* A storage policy is applied once to every incoming value storage requests.
|
||||
@@ -50,7 +71,7 @@ struct Value;
|
||||
* @param form_addr: network address of the incoming request.
|
||||
* @param from_len: network address lendth of the incoming request.
|
||||
*/
|
||||
using StorePolicy = std::function<bool(InfoHash key, std::shared_ptr<Value>& value, InfoHash from, const sockaddr* from_addr, socklen_t from_len)>;
|
||||
using StorePolicy = std::function<bool(InfoHash key, std::shared_ptr<Value>& value, const InfoHash& from, const SockAddr& addr)>;
|
||||
|
||||
/**
|
||||
* An edition policy is applied once to every incoming value storage requests,
|
||||
@@ -66,15 +87,15 @@ using StorePolicy = std::function<bool(InfoHash key, std::shared_ptr<Value>& val
|
||||
* @param form_addr: network address of the incoming request.
|
||||
* @param from_len: network address lendth of the incoming request.
|
||||
*/
|
||||
using EditPolicy = std::function<bool(InfoHash key, const std::shared_ptr<Value>& old_val, std::shared_ptr<Value>& new_val, InfoHash from, const sockaddr* from_addr, socklen_t from_len)>;
|
||||
using EditPolicy = std::function<bool(InfoHash key, const std::shared_ptr<Value>& old_val, std::shared_ptr<Value>& new_val, const InfoHash& from, const SockAddr& addr)>;
|
||||
|
||||
static constexpr const size_t MAX_VALUE_SIZE {1024 * 14};
|
||||
static constexpr const size_t MAX_VALUE_SIZE {1024 * 64};
|
||||
|
||||
struct ValueType {
|
||||
struct OPENDHT_PUBLIC ValueType {
|
||||
typedef uint16_t Id;
|
||||
|
||||
static bool DEFAULT_STORE_POLICY(InfoHash, std::shared_ptr<Value>& v, InfoHash, const sockaddr*, socklen_t);
|
||||
static bool DEFAULT_EDIT_POLICY(InfoHash, const std::shared_ptr<Value>&, std::shared_ptr<Value>&, InfoHash, const sockaddr*, socklen_t) {
|
||||
static bool DEFAULT_STORE_POLICY(InfoHash, const std::shared_ptr<Value>& v, const InfoHash&, const SockAddr&);
|
||||
static bool DEFAULT_EDIT_POLICY(InfoHash, const std::shared_ptr<Value>&, std::shared_ptr<Value>&, const InfoHash&, const SockAddr&) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -98,11 +119,26 @@ struct ValueType {
|
||||
|
||||
Id id {0};
|
||||
std::string name {};
|
||||
duration expiration {60 * 10};
|
||||
duration expiration {std::chrono::minutes(10)};
|
||||
StorePolicy storePolicy {DEFAULT_STORE_POLICY};
|
||||
EditPolicy editPolicy {DEFAULT_EDIT_POLICY};
|
||||
};
|
||||
|
||||
class TypeStore {
|
||||
public:
|
||||
void registerType(const ValueType& type) {
|
||||
types[type.id] = type;
|
||||
}
|
||||
const ValueType& getType(ValueType::Id type_id) const {
|
||||
const auto& t_it = types.find(type_id);
|
||||
return (t_it == types.end()) ? ValueType::USER_DATA : t_it->second;
|
||||
}
|
||||
private:
|
||||
std::map<ValueType::Id, ValueType> types {};
|
||||
};
|
||||
|
||||
struct CryptoValueCache;
|
||||
|
||||
/**
|
||||
* A "value" is data potentially stored on the Dht, with some metadata.
|
||||
*
|
||||
@@ -112,14 +148,29 @@ struct ValueType {
|
||||
* Values are stored at a given InfoHash in the Dht, but also have a
|
||||
* unique ID to distinguish between values stored at the same location.
|
||||
*/
|
||||
struct Value
|
||||
struct OPENDHT_PUBLIC Value
|
||||
{
|
||||
enum class Field : int {
|
||||
None = 0,
|
||||
Id, /* Value::id */
|
||||
ValueType, /* Value::type */
|
||||
OwnerPk, /* Value::owner */
|
||||
SeqNum, /* Value::seq */
|
||||
UserType, /* Value::user_type */
|
||||
|
||||
COUNT /* the total number of fields */
|
||||
};
|
||||
|
||||
typedef uint64_t Id;
|
||||
static const Id INVALID_ID {0};
|
||||
static const constexpr Id INVALID_ID {0};
|
||||
|
||||
class Filter : public std::function<bool(const Value&)> {
|
||||
using std::function<bool(const Value&)>::function;
|
||||
public:
|
||||
Filter() {}
|
||||
|
||||
template<typename Functor>
|
||||
Filter(Functor f) : std::function<bool(const Value&)>::function(f) {}
|
||||
|
||||
Filter chain(Filter&& f2) {
|
||||
auto f1 = *this;
|
||||
return chain(std::move(f1), std::move(f2));
|
||||
@@ -129,35 +180,64 @@ struct Value
|
||||
return chainOr(std::move(f1), std::move(f2));
|
||||
}
|
||||
static Filter chain(Filter&& f1, Filter&& f2) {
|
||||
if (not f1) return std::move(f2);
|
||||
if (not f2) return std::move(f1);
|
||||
return [f1 = std::move(f1), f2 = std::move(f2)](const Value& v) {
|
||||
return f1(v) and f2(v);
|
||||
};
|
||||
}
|
||||
static Filter chain(const Filter& f1, const Filter& f2) {
|
||||
if (not f1) return f2;
|
||||
if (not f2) return f1;
|
||||
return [f1,f2](const Value& v) {
|
||||
return f1(v) and f2(v);
|
||||
};
|
||||
}
|
||||
static Filter chain(std::initializer_list<Filter> l) {
|
||||
const std::vector<Filter> list(l.begin(), l.end());
|
||||
return [list](const Value& v){
|
||||
for (const auto& f : list)
|
||||
static Filter chainAll(std::vector<Filter>&& set) {
|
||||
if (set.empty()) return {};
|
||||
return [set = std::move(set)](const Value& v) {
|
||||
for (const auto& f : set)
|
||||
if (f and not f(v))
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
}
|
||||
static Filter chain(std::initializer_list<Filter> l) {
|
||||
return chainAll(std::vector<Filter>(l.begin(), l.end()));
|
||||
}
|
||||
static Filter chainOr(Filter&& f1, Filter&& f2) {
|
||||
if (not f1 or not f2) return AllFilter();
|
||||
return [f1,f2](const Value& v) {
|
||||
if (not f1 or not f2) return {};
|
||||
return [f1=std::move(f1),f2=std::move(f2)](const Value& v) {
|
||||
return f1(v) or f2(v);
|
||||
};
|
||||
}
|
||||
static Filter notFilter(Filter&& f) {
|
||||
if (not f) return [](const Value&) { return false; };
|
||||
return [f = std::move(f)](const Value& v) { return not f(v); };
|
||||
}
|
||||
std::vector<Sp<Value>> filter(const std::vector<Sp<Value>>& values) {
|
||||
if (not (*this))
|
||||
return values;
|
||||
std::vector<Sp<Value>> ret;
|
||||
for (const auto& v : values)
|
||||
if ((*this)(v))
|
||||
ret.emplace_back(v);
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
/* Sneaky functions disguised in classes */
|
||||
|
||||
static const Filter AllFilter() {
|
||||
return [](const Value&){return true;};
|
||||
return {};
|
||||
}
|
||||
|
||||
static Filter TypeFilter(const ValueType& t) {
|
||||
const auto tid = t.id;
|
||||
return [tid = t.id](const Value& v) {
|
||||
return v.type == tid;
|
||||
};
|
||||
}
|
||||
static Filter TypeFilter(const ValueType::Id& tid) {
|
||||
return [tid](const Value& v) {
|
||||
return v.type == tid;
|
||||
};
|
||||
@@ -169,12 +249,34 @@ struct Value
|
||||
};
|
||||
}
|
||||
|
||||
static Filter recipientFilter(const InfoHash& r) {
|
||||
static Filter RecipientFilter(const InfoHash& r) {
|
||||
return [r](const Value& v) {
|
||||
return v.recipient == r;
|
||||
};
|
||||
}
|
||||
|
||||
static Filter OwnerFilter(const crypto::PublicKey& pk) {
|
||||
return OwnerFilter(pk.getId());
|
||||
}
|
||||
|
||||
static Filter OwnerFilter(const InfoHash& pkh) {
|
||||
return [pkh](const Value& v) {
|
||||
return v.owner and v.owner->getId() == pkh;
|
||||
};
|
||||
}
|
||||
|
||||
static Filter SeqNumFilter(uint16_t seq_no) {
|
||||
return [seq_no](const Value& v) {
|
||||
return v.seq == seq_no;
|
||||
};
|
||||
}
|
||||
|
||||
static Filter UserTypeFilter(std::string ut) {
|
||||
return [ut = std::move(ut)](const Value& v) {
|
||||
return v.user_type == ut;
|
||||
};
|
||||
}
|
||||
|
||||
class SerializableBase
|
||||
{
|
||||
public:
|
||||
@@ -241,10 +343,10 @@ struct Value
|
||||
return unpack<T>(*this);
|
||||
}
|
||||
|
||||
bool isEncrypted() const {
|
||||
inline bool isEncrypted() const {
|
||||
return not cypher.empty();
|
||||
}
|
||||
bool isSigned() const {
|
||||
inline bool isSigned() const {
|
||||
return owner and not signature.empty();
|
||||
}
|
||||
|
||||
@@ -253,37 +355,24 @@ struct Value
|
||||
* Afterward, checkSignature() will return true and owner will
|
||||
* be set to the corresponding public key.
|
||||
*/
|
||||
void sign(const crypto::PrivateKey& key) {
|
||||
if (isEncrypted())
|
||||
throw DhtException("Can't sign encrypted data.");
|
||||
owner = std::make_shared<crypto::PublicKey>(key.getPublicKey());
|
||||
signature = key.sign(getToSign());
|
||||
}
|
||||
void sign(const crypto::PrivateKey& key);
|
||||
|
||||
/**
|
||||
* Check that the value is signed and that the signature matches.
|
||||
* If true, the owner field will contain the signer public key.
|
||||
*/
|
||||
bool checkSignature() const {
|
||||
inline bool checkSignature() const {
|
||||
return isSigned() and owner->checkSignature(getToSign(), signature);
|
||||
}
|
||||
|
||||
std::shared_ptr<const crypto::PublicKey> getOwner() const {
|
||||
return std::static_pointer_cast<const crypto::PublicKey>(owner);
|
||||
inline std::shared_ptr<crypto::PublicKey> getOwner() const {
|
||||
return owner;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Sign the value with from and returns the encrypted version for to.
|
||||
*/
|
||||
Value encrypt(const crypto::PrivateKey& from, const crypto::PublicKey& to) {
|
||||
if (isEncrypted())
|
||||
throw DhtException("Data is already encrypted.");
|
||||
setRecipient(to.getId());
|
||||
sign(from);
|
||||
Value nv {id};
|
||||
nv.setCypher(to.encrypt(getToEncrypt()));
|
||||
return nv;
|
||||
}
|
||||
Value encrypt(const crypto::PrivateKey& from, const crypto::PublicKey& to);
|
||||
|
||||
Value() {}
|
||||
|
||||
@@ -297,6 +386,14 @@ struct Value
|
||||
Value(ValueType::Id t, const uint8_t* dat_ptr, size_t dat_len, Id id = INVALID_ID)
|
||||
: id(id), type(t), data(dat_ptr, dat_ptr+dat_len) {}
|
||||
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
/**
|
||||
* Build a value from a json object
|
||||
* @param json
|
||||
*/
|
||||
Value(const Json::Value& json);
|
||||
#endif
|
||||
|
||||
template <typename Type>
|
||||
Value(ValueType::Id t, const Type& d, Id id = INVALID_ID)
|
||||
: id(id), type(t), data(packMsg(d)) {}
|
||||
@@ -312,7 +409,9 @@ struct Value
|
||||
|
||||
Value(Value&& o) noexcept
|
||||
: id(o.id), owner(std::move(o.owner)), recipient(o.recipient),
|
||||
type(o.type), data(std::move(o.data)), user_type(std::move(o.user_type)), seq(o.seq), signature(std::move(o.signature)), cypher(std::move(o.cypher)) {}
|
||||
type(o.type), data(std::move(o.data)), user_type(std::move(o.user_type)), seq(o.seq)
|
||||
, signature(std::move(o.signature)), cypher(std::move(o.cypher))
|
||||
, priority(o.priority) {}
|
||||
|
||||
template <typename Type>
|
||||
Value(const Type& vs)
|
||||
@@ -325,24 +424,34 @@ struct Value
|
||||
msgpack_unpack(o);
|
||||
}
|
||||
|
||||
inline bool operator== (const Value& o) {
|
||||
return id == o.id &&
|
||||
(isEncrypted() ? cypher == o.cypher :
|
||||
((owner == o.owner || *owner == *o.owner) && type == o.type && data == o.data && user_type == o.user_type && signature == o.signature));
|
||||
/**
|
||||
* Returns true if value contents are equals (not considering the value ID)
|
||||
*/
|
||||
inline bool contentEquals(const Value& o) {
|
||||
return isEncrypted() ? cypher == o.cypher :
|
||||
((owner == o.owner || (owner and o.owner and *owner == *o.owner))
|
||||
&& type == o.type
|
||||
&& data == o.data
|
||||
&& user_type == o.user_type
|
||||
&& signature == o.signature);
|
||||
}
|
||||
|
||||
void setRecipient(const InfoHash& r) {
|
||||
inline bool operator== (const Value& o) {
|
||||
return id == o.id and contentEquals(o);
|
||||
}
|
||||
|
||||
inline void setRecipient(const InfoHash& r) {
|
||||
recipient = r;
|
||||
}
|
||||
|
||||
void setCypher(Blob&& c) {
|
||||
inline void setCypher(Blob&& c) {
|
||||
cypher = std::move(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack part of the data to be signed (must always be done the same way)
|
||||
*/
|
||||
Blob getToSign() const {
|
||||
inline Blob getToSign() const {
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
msgpack_pack_to_sign(pk);
|
||||
@@ -352,7 +461,7 @@ struct Value
|
||||
/**
|
||||
* Pack part of the data to be encrypted
|
||||
*/
|
||||
Blob getToEncrypt() const {
|
||||
inline Blob getToEncrypt() const {
|
||||
msgpack::sbuffer buffer;
|
||||
msgpack::packer<msgpack::sbuffer> pk(&buffer);
|
||||
msgpack_pack_to_encrypt(pk);
|
||||
@@ -360,14 +469,26 @@ struct Value
|
||||
}
|
||||
|
||||
/** print value for debugging */
|
||||
friend std::ostream& operator<< (std::ostream& s, const Value& v);
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const Value& v);
|
||||
|
||||
std::string toString() const {
|
||||
inline std::string toString() const {
|
||||
std::stringstream ss;
|
||||
ss << *this;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
/**
|
||||
* Build a json object from a value
|
||||
* Example:
|
||||
* {
|
||||
* "data":"base64ofdata",
|
||||
* id":"0", "seq":0,"type":3
|
||||
* }
|
||||
*/
|
||||
Json::Value toJson() const;
|
||||
#endif
|
||||
|
||||
/** Return the size in bytes used by this value in memory (minimum). */
|
||||
size_t size() const;
|
||||
|
||||
@@ -375,19 +496,19 @@ struct Value
|
||||
void msgpack_pack_to_sign(Packer& pk) const
|
||||
{
|
||||
bool has_owner = owner && *owner;
|
||||
pk.pack_map((user_type.empty()?0:1) + (has_owner?(recipient == InfoHash() ? 4 : 5):2));
|
||||
pk.pack_map((user_type.empty()?0:1) + (has_owner?(recipient ? 5 : 4):2));
|
||||
if (has_owner) { // isSigned
|
||||
pk.pack(std::string("seq")); pk.pack(seq);
|
||||
pk.pack(std::string("owner")); owner->msgpack_pack(pk);
|
||||
if (recipient != InfoHash()) {
|
||||
pk.pack(std::string("to")); pk.pack(recipient);
|
||||
pk.pack(VALUE_KEY_SEQ); pk.pack(seq);
|
||||
pk.pack(VALUE_KEY_OWNER); owner->msgpack_pack(pk);
|
||||
if (recipient) {
|
||||
pk.pack(VALUE_KEY_TO); pk.pack(recipient);
|
||||
}
|
||||
}
|
||||
pk.pack(std::string("type")); pk.pack(type);
|
||||
pk.pack(std::string("data")); pk.pack_bin(data.size());
|
||||
pk.pack_bin_body((const char*)data.data(), data.size());
|
||||
pk.pack(VALUE_KEY_TYPE); pk.pack(type);
|
||||
pk.pack(VALUE_KEY_DATA); pk.pack_bin(data.size());
|
||||
pk.pack_bin_body((const char*)data.data(), data.size());
|
||||
if (not user_type.empty()) {
|
||||
pk.pack(std::string("utype")); pk.pack(user_type);
|
||||
pk.pack(VALUE_KEY_USERTYPE); pk.pack(user_type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,10 +520,10 @@ struct Value
|
||||
pk.pack_bin_body((const char*)cypher.data(), cypher.size());
|
||||
} else {
|
||||
pk.pack_map(isSigned() ? 2 : 1);
|
||||
pk.pack(std::string("body")); msgpack_pack_to_sign(pk);
|
||||
pk.pack(VALUE_KEY_BODY); msgpack_pack_to_sign(pk);
|
||||
if (isSigned()) {
|
||||
pk.pack(std::string("sig")); pk.pack_bin(signature.size());
|
||||
pk.pack_bin_body((const char*)signature.data(), signature.size());
|
||||
pk.pack(VALUE_KEY_SIGNATURE); pk.pack_bin(signature.size());
|
||||
pk.pack_bin_body((const char*)signature.data(), signature.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -410,12 +531,43 @@ struct Value
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& pk) const
|
||||
{
|
||||
pk.pack_map(2);
|
||||
pk.pack(std::string("id")); pk.pack(id);
|
||||
pk.pack(std::string("dat")); msgpack_pack_to_encrypt(pk);
|
||||
pk.pack_map(2 + (priority?1:0));
|
||||
pk.pack(VALUE_KEY_ID); pk.pack(id);
|
||||
pk.pack(VALUE_KEY_DAT); msgpack_pack_to_encrypt(pk);
|
||||
if (priority) {
|
||||
pk.pack(VALUE_KEY_PRIO); pk.pack(priority);
|
||||
}
|
||||
}
|
||||
|
||||
void msgpack_unpack(msgpack::object o);
|
||||
template <typename Packer>
|
||||
void msgpack_pack_fields(const std::set<Value::Field>& fields, Packer& pk) const
|
||||
{
|
||||
for (const auto& field : fields)
|
||||
switch (field) {
|
||||
case Value::Field::Id:
|
||||
pk.pack(static_cast<uint64_t>(id));
|
||||
break;
|
||||
case Value::Field::ValueType:
|
||||
pk.pack(static_cast<uint64_t>(type));
|
||||
break;
|
||||
case Value::Field::OwnerPk:
|
||||
if (owner)
|
||||
owner->msgpack_pack(pk);
|
||||
else
|
||||
InfoHash().msgpack_pack(pk);
|
||||
break;
|
||||
case Value::Field::SeqNum:
|
||||
pk.pack(static_cast<uint64_t>(seq));
|
||||
break;
|
||||
case Value::Field::UserType:
|
||||
pk.pack(user_type);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void msgpack_unpack(const msgpack::object& o);
|
||||
void msgpack_unpack_body(const msgpack::object& o);
|
||||
Blob getPacked() const {
|
||||
msgpack::sbuffer buffer;
|
||||
@@ -424,6 +576,8 @@ struct Value
|
||||
return {buffer.data(), buffer.data()+buffer.size()};
|
||||
}
|
||||
|
||||
void msgpack_unpack_fields(const std::set<Value::Field>& fields, const msgpack::object& o, unsigned offset);
|
||||
|
||||
Id id {INVALID_ID};
|
||||
|
||||
/**
|
||||
@@ -463,10 +617,381 @@ struct Value
|
||||
* Hold encrypted version of the data.
|
||||
*/
|
||||
Blob cypher {};
|
||||
|
||||
/**
|
||||
* Priority of this value (used for push notifications)
|
||||
* 0 = high priority
|
||||
* 1 = normal priority
|
||||
*/
|
||||
unsigned priority {0};
|
||||
|
||||
bool checkSignature();
|
||||
Sp<Value> decrypt(const crypto::PrivateKey& key);
|
||||
|
||||
private:
|
||||
/* Cache for crypto ops */
|
||||
bool signatureChecked {false};
|
||||
bool signatureValid {false};
|
||||
bool decrypted {false};
|
||||
Sp<Value> decryptedValue {};
|
||||
};
|
||||
|
||||
using ValuesExport = std::pair<InfoHash, Blob>;
|
||||
|
||||
/**
|
||||
* @class FieldValue
|
||||
* @brief Describes a value filter.
|
||||
* @details
|
||||
* This structure holds the value for a specified field. It's type can either be
|
||||
* uint64_t, InfoHash or Blob.
|
||||
*/
|
||||
struct OPENDHT_PUBLIC FieldValue
|
||||
{
|
||||
FieldValue() {}
|
||||
FieldValue(Value::Field f, uint64_t int_value) : field(f), intValue(int_value) {}
|
||||
FieldValue(Value::Field f, InfoHash hash_value) : field(f), hashValue(hash_value) {}
|
||||
FieldValue(Value::Field f, Blob blob_value) : field(f), blobValue(std::move(blob_value)) {}
|
||||
|
||||
bool operator==(const FieldValue& fd) const;
|
||||
|
||||
// accessors
|
||||
Value::Field getField() const { return field; }
|
||||
uint64_t getInt() const { return intValue; }
|
||||
InfoHash getHash() const { return hashValue; }
|
||||
Blob getBlob() const { return blobValue; }
|
||||
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& p) const {
|
||||
p.pack_map(2);
|
||||
p.pack("f"sv); p.pack(static_cast<uint8_t>(field));
|
||||
|
||||
p.pack("v"sv);
|
||||
switch (field) {
|
||||
case Value::Field::Id:
|
||||
case Value::Field::ValueType:
|
||||
p.pack(intValue);
|
||||
break;
|
||||
case Value::Field::OwnerPk:
|
||||
p.pack(hashValue);
|
||||
break;
|
||||
case Value::Field::UserType:
|
||||
p.pack_bin(blobValue.size());
|
||||
p.pack_bin_body((const char*)blobValue.data(), blobValue.size());
|
||||
break;
|
||||
default:
|
||||
throw msgpack::type_error();
|
||||
}
|
||||
}
|
||||
|
||||
void msgpack_unpack(const msgpack::object& msg) {
|
||||
hashValue = {};
|
||||
blobValue.clear();
|
||||
|
||||
if (auto f = findMapValue(msg, "f"sv))
|
||||
field = (Value::Field)f->as<unsigned>();
|
||||
else
|
||||
throw msgpack::type_error();
|
||||
|
||||
auto v = findMapValue(msg, "v"sv);
|
||||
if (not v)
|
||||
throw msgpack::type_error();
|
||||
else
|
||||
switch (field) {
|
||||
case Value::Field::Id:
|
||||
case Value::Field::ValueType:
|
||||
intValue = v->as<decltype(intValue)>();
|
||||
break;
|
||||
case Value::Field::OwnerPk:
|
||||
hashValue = v->as<decltype(hashValue)>();
|
||||
break;
|
||||
case Value::Field::UserType:
|
||||
blobValue = unpackBlob(*v);
|
||||
break;
|
||||
default:
|
||||
throw msgpack::type_error();
|
||||
}
|
||||
}
|
||||
|
||||
Value::Filter getLocalFilter() const;
|
||||
|
||||
private:
|
||||
Value::Field field {Value::Field::None};
|
||||
// three possible value types
|
||||
uint64_t intValue {};
|
||||
InfoHash hashValue {};
|
||||
Blob blobValue {};
|
||||
};
|
||||
|
||||
/**
|
||||
* @class Select
|
||||
* @brief Serializable Value field selection.
|
||||
* @details
|
||||
* This is a container for a list of FieldSelectorDescription instances. It
|
||||
* describes a complete SELECT query for dht::Value.
|
||||
*/
|
||||
struct OPENDHT_PUBLIC Select
|
||||
{
|
||||
Select() { }
|
||||
Select(std::string_view q_str);
|
||||
|
||||
bool isSatisfiedBy(const Select& os) const;
|
||||
|
||||
/**
|
||||
* Selects a field of type Value::Field.
|
||||
*
|
||||
* @param field the field to require.
|
||||
*
|
||||
* @return the resulting Select instance.
|
||||
*/
|
||||
Select& field(Value::Field field) {
|
||||
if (std::find(fieldSelection_.begin(), fieldSelection_.end(), field) == fieldSelection_.end())
|
||||
fieldSelection_.emplace_back(field);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the set of selected fields based on previous require* calls.
|
||||
*
|
||||
* @return the set of fields.
|
||||
*/
|
||||
std::set<Value::Field> getSelection() const {
|
||||
return {fieldSelection_.begin(), fieldSelection_.end()};
|
||||
}
|
||||
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& pk) const { pk.pack(fieldSelection_); }
|
||||
void msgpack_unpack(const msgpack::object& o) {
|
||||
fieldSelection_ = o.as<decltype(fieldSelection_)>();
|
||||
}
|
||||
|
||||
std::string toString() const {
|
||||
std::stringstream ss;
|
||||
ss << *this;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
bool empty() const { return fieldSelection_.empty(); }
|
||||
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<<(std::ostream& s, const dht::Select& q);
|
||||
private:
|
||||
std::vector<Value::Field> fieldSelection_ {};
|
||||
};
|
||||
|
||||
/**
|
||||
* @class Where
|
||||
* @brief Serializable dht::Value filter.
|
||||
* @details
|
||||
* This is container for a list of FieldValue instances. It describes a
|
||||
* complete WHERE query for dht::Value.
|
||||
*/
|
||||
struct OPENDHT_PUBLIC Where
|
||||
{
|
||||
Where() { }
|
||||
Where(std::string_view q_str);
|
||||
|
||||
bool isSatisfiedBy(const Where& where) const;
|
||||
|
||||
/**
|
||||
* Adds restriction on Value::Id based on the id argument.
|
||||
*
|
||||
* @param id the id.
|
||||
*
|
||||
* @return the resulting Where instance.
|
||||
*/
|
||||
Where& id(Value::Id id) {
|
||||
FieldValue fv {Value::Field::Id, id};
|
||||
if (std::find(filters_.begin(), filters_.end(), fv) == filters_.end())
|
||||
filters_.emplace_back(std::move(fv));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds restriction on Value::ValueType based on the type argument.
|
||||
*
|
||||
* @param type the value type.
|
||||
*
|
||||
* @return the resulting Where instance.
|
||||
*/
|
||||
Where& valueType(ValueType::Id type) {
|
||||
FieldValue fv {Value::Field::ValueType, type};
|
||||
if (std::find(filters_.begin(), filters_.end(), fv) == filters_.end())
|
||||
filters_.emplace_back(std::move(fv));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds restriction on Value::OwnerPk based on the owner_pk_hash argument.
|
||||
*
|
||||
* @param owner_pk_hash the owner public key fingerprint.
|
||||
*
|
||||
* @return the resulting Where instance.
|
||||
*/
|
||||
Where& owner(InfoHash owner_pk_hash) {
|
||||
FieldValue fv {Value::Field::OwnerPk, owner_pk_hash};
|
||||
if (std::find(filters_.begin(), filters_.end(), fv) == filters_.end())
|
||||
filters_.emplace_back(std::move(fv));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds restriction on Value::OwnerPk based on the owner_pk_hash argument.
|
||||
*
|
||||
* @param owner_pk_hash the owner public key fingerprint.
|
||||
*
|
||||
* @return the resulting Where instance.
|
||||
*/
|
||||
Where& seq(uint16_t seq_no) {
|
||||
FieldValue fv {Value::Field::SeqNum, seq_no};
|
||||
if (std::find(filters_.begin(), filters_.end(), fv) == filters_.end())
|
||||
filters_.emplace_back(std::move(fv));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds restriction on Value::UserType based on the user_type argument.
|
||||
*
|
||||
* @param user_type the user type.
|
||||
*
|
||||
* @return the resulting Where instance.
|
||||
*/
|
||||
Where& userType(std::string_view user_type) {
|
||||
FieldValue fv {Value::Field::UserType, Blob {user_type.begin(), user_type.end()}};
|
||||
if (std::find(filters_.begin(), filters_.end(), fv) == filters_.end())
|
||||
filters_.emplace_back(std::move(fv));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the Value::Filter based on the list of field value set.
|
||||
*
|
||||
* @return the resulting Value::Filter.
|
||||
*/
|
||||
Value::Filter getFilter() const {
|
||||
if (filters_.empty()) return {};
|
||||
std::vector<Value::Filter> fset;
|
||||
fset.reserve(filters_.size());
|
||||
for (const auto& f : filters_) {
|
||||
if (auto lf = f.getLocalFilter())
|
||||
fset.emplace_back(std::move(lf));
|
||||
}
|
||||
return Value::Filter::chainAll(std::move(fset));
|
||||
}
|
||||
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& pk) const { pk.pack(filters_); }
|
||||
void msgpack_unpack(const msgpack::object& o) {
|
||||
filters_.clear();
|
||||
filters_ = o.as<decltype(filters_)>();
|
||||
}
|
||||
|
||||
std::string toString() const {
|
||||
std::stringstream ss;
|
||||
ss << *this;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return filters_.empty();
|
||||
}
|
||||
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<<(std::ostream& s, const dht::Where& q);
|
||||
|
||||
private:
|
||||
std::vector<FieldValue> filters_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class Query
|
||||
* @brief Describes a query destined to another peer.
|
||||
* @details
|
||||
* This class describes the list of filters on field values and the field
|
||||
* itselves to include in the peer response to a GET operation. See
|
||||
* FieldValue.
|
||||
*/
|
||||
struct OPENDHT_PUBLIC Query
|
||||
{
|
||||
static const std::string QUERY_PARSE_ERROR;
|
||||
|
||||
Query(Select s = {}, Where w = {}, bool none = false) : select(std::move(s)), where(std::move(w)), none(none) { };
|
||||
|
||||
/**
|
||||
* Initializes a query based on a SQL-ish formatted string. The abstract
|
||||
* form of such a string is the following:
|
||||
*
|
||||
* [SELECT $field$ [WHERE $field$=$value$]]
|
||||
*
|
||||
* where
|
||||
*
|
||||
* - $field$ = *|id|value_type|owner_pk|user_type
|
||||
* - $value$ = $string$|$integer$
|
||||
* - $string$: a simple string WITHOUT SPACES.
|
||||
* - $integer$: a simple integer.
|
||||
*/
|
||||
Query(std::string_view q_str) {
|
||||
auto pos_W = q_str.find("WHERE");
|
||||
auto pos_w = q_str.find("where");
|
||||
auto pos = std::min(pos_W != std::string_view::npos ? pos_W : q_str.size(),
|
||||
pos_w != std::string_view::npos ? pos_w : q_str.size());
|
||||
select = q_str.substr(0, pos);
|
||||
where = q_str.substr(pos, q_str.size()-pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the query is satisfied by another query.
|
||||
*/
|
||||
bool isSatisfiedBy(const Query& q) const;
|
||||
|
||||
template <typename Packer>
|
||||
void msgpack_pack(Packer& pk) const {
|
||||
pk.pack_map(2);
|
||||
pk.pack(std::string("s")); pk.pack(select); /* packing field selectors */
|
||||
pk.pack(std::string("w")); pk.pack(where); /* packing filters */
|
||||
}
|
||||
|
||||
void msgpack_unpack(const msgpack::object& o);
|
||||
|
||||
std::string toString() const {
|
||||
std::stringstream ss;
|
||||
ss << *this;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<<(std::ostream& s, const dht::Query& q) {
|
||||
return s << "Query[" << q.select << " " << q.where << "]";
|
||||
}
|
||||
|
||||
Select select {};
|
||||
Where where {};
|
||||
bool none {false}; /* When true, any query satisfies this. */
|
||||
};
|
||||
|
||||
/*!
|
||||
* @class FieldValueIndex
|
||||
* @brief An index for field values.
|
||||
* @details
|
||||
* This structures is meant to manipulate a subset of fields normally contained
|
||||
* in Value.
|
||||
*/
|
||||
struct OPENDHT_PUBLIC FieldValueIndex {
|
||||
FieldValueIndex() {}
|
||||
FieldValueIndex(const Value& v, const Select& s = {});
|
||||
/**
|
||||
* Tells if all the fields of this are contained in the other
|
||||
* FieldValueIndex with the same value.
|
||||
*
|
||||
* @param other The other FieldValueIndex instance.
|
||||
*/
|
||||
bool containedIn(const FieldValueIndex& other) const;
|
||||
|
||||
OPENDHT_PUBLIC friend std::ostream& operator<<(std::ostream& os, const FieldValueIndex& fvi);
|
||||
|
||||
void msgpack_unpack_fields(const std::set<Value::Field>& fields,
|
||||
const msgpack::object& o,
|
||||
unsigned offset);
|
||||
|
||||
std::map<Value::Field, FieldValue> index {};
|
||||
};
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_base_of<Value::SerializableBase, T>::value, T>::type* = nullptr>
|
||||
Value::Filter
|
||||
@@ -503,7 +1028,7 @@ template <typename T,
|
||||
Value::Filter
|
||||
getFilterSet()
|
||||
{
|
||||
return Value::AllFilter();
|
||||
return {};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
@@ -519,4 +1044,10 @@ unpackVector(const std::vector<std::shared_ptr<Value>>& vals) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef OPENDHT_JSONCPP
|
||||
uint64_t unpackId(const Json::Value& json, const std::string& key);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
MSGPACK_ADD_ENUM(dht::Value::Field)
|
||||
+477
-34
@@ -1,5 +1,5 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
@@ -10,13 +10,13 @@
|
||||
#
|
||||
# Check for baseline language coverage in the compiler for the specified
|
||||
# version of the C++ standard. If necessary, add switches to CXX and
|
||||
# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard)
|
||||
# or '14' (for the C++14 standard).
|
||||
# CXXCPP to enable support. VERSION may be '11', '14', '17', or '20' for
|
||||
# the respective C++ standard version.
|
||||
#
|
||||
# The second argument, if specified, indicates whether you insist on an
|
||||
# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
|
||||
# -std=c++11). If neither is specified, you get whatever works, with
|
||||
# preference for an extended mode.
|
||||
# preference for no added switch, and then for an extended mode.
|
||||
#
|
||||
# The third argument, if specified 'mandatory' or if left unspecified,
|
||||
# indicates that baseline support for the specified C++ standard is
|
||||
@@ -33,21 +33,26 @@
|
||||
# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
|
||||
# Copyright (c) 2015 Paul Norman <penorman@mac.com>
|
||||
# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
|
||||
# Copyright (c) 2016, 2018 Krzesimir Nowak <qdlacz@gmail.com>
|
||||
# Copyright (c) 2019 Enji Cooper <yaneurabeya@gmail.com>
|
||||
# Copyright (c) 2020 Jason Merrill <jason@redhat.com>
|
||||
# Copyright (c) 2021 Jörn Heusipp <osmanx@problemloesungsmaschine.de>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 4
|
||||
#serial 14
|
||||
|
||||
dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
|
||||
dnl (serial version number 13).
|
||||
|
||||
AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
|
||||
m4_if([$1], [11], [],
|
||||
[$1], [14], [],
|
||||
[$1], [17], [m4_fatal([support for C++17 not yet implemented in AX_CXX_COMPILE_STDCXX])],
|
||||
m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"],
|
||||
[$1], [14], [ax_cxx_compile_alternatives="14 1y"],
|
||||
[$1], [17], [ax_cxx_compile_alternatives="17 1z"],
|
||||
[$1], [20], [ax_cxx_compile_alternatives="20"],
|
||||
[m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
|
||||
m4_if([$2], [], [],
|
||||
[$2], [ext], [],
|
||||
@@ -59,18 +64,21 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
|
||||
[m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
|
||||
AC_LANG_PUSH([C++])dnl
|
||||
ac_success=no
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
|
||||
ax_cv_cxx_compile_cxx$1,
|
||||
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||
[ax_cv_cxx_compile_cxx$1=yes],
|
||||
[ax_cv_cxx_compile_cxx$1=no])])
|
||||
if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
|
||||
ac_success=yes
|
||||
fi
|
||||
|
||||
m4_if([$2], [], [dnl
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
|
||||
ax_cv_cxx_compile_cxx$1,
|
||||
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||
[ax_cv_cxx_compile_cxx$1=yes],
|
||||
[ax_cv_cxx_compile_cxx$1=no])])
|
||||
if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
|
||||
ac_success=yes
|
||||
fi])
|
||||
|
||||
m4_if([$2], [noext], [], [dnl
|
||||
if test x$ac_success = xno; then
|
||||
for switch in -std=gnu++$1 -std=gnu++0x; do
|
||||
for alternative in ${ax_cxx_compile_alternatives}; do
|
||||
switch="-std=gnu++${alternative}"
|
||||
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
|
||||
$cachevar,
|
||||
@@ -96,22 +104,27 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
|
||||
dnl HP's aCC needs +std=c++11 according to:
|
||||
dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
|
||||
dnl Cray's crayCC needs "-h std=c++11"
|
||||
for switch in -std=c++$1 -std=c++0x +std=c++$1 "-h std=c++$1"; do
|
||||
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
|
||||
$cachevar,
|
||||
[ac_save_CXX="$CXX"
|
||||
CXX="$CXX $switch"
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||
[eval $cachevar=yes],
|
||||
[eval $cachevar=no])
|
||||
CXX="$ac_save_CXX"])
|
||||
if eval test x\$$cachevar = xyes; then
|
||||
CXX="$CXX $switch"
|
||||
if test -n "$CXXCPP" ; then
|
||||
CXXCPP="$CXXCPP $switch"
|
||||
for alternative in ${ax_cxx_compile_alternatives}; do
|
||||
for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
|
||||
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
|
||||
$cachevar,
|
||||
[ac_save_CXX="$CXX"
|
||||
CXX="$CXX $switch"
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||
[eval $cachevar=yes],
|
||||
[eval $cachevar=no])
|
||||
CXX="$ac_save_CXX"])
|
||||
if eval test x\$$cachevar = xyes; then
|
||||
CXX="$CXX $switch"
|
||||
if test -n "$CXXCPP" ; then
|
||||
CXXCPP="$CXXCPP $switch"
|
||||
fi
|
||||
ac_success=yes
|
||||
break
|
||||
fi
|
||||
ac_success=yes
|
||||
done
|
||||
if test x$ac_success = xyes; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
@@ -140,7 +153,6 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||
)
|
||||
|
||||
|
||||
dnl Test body for checking C++14 support
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
|
||||
@@ -148,6 +160,23 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
|
||||
)
|
||||
|
||||
dnl Test body for checking C++17 support
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_17
|
||||
)
|
||||
|
||||
dnl Test body for checking C++20 support
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_20],
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_17
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_20
|
||||
)
|
||||
|
||||
|
||||
dnl Tests for new features in C++11
|
||||
|
||||
@@ -185,11 +214,13 @@ namespace cxx11
|
||||
|
||||
struct Base
|
||||
{
|
||||
virtual ~Base() {}
|
||||
virtual void f() {}
|
||||
};
|
||||
|
||||
struct Derived : public Base
|
||||
{
|
||||
virtual ~Derived() override {}
|
||||
virtual void f() override {}
|
||||
};
|
||||
|
||||
@@ -518,7 +549,7 @@ namespace cxx14
|
||||
|
||||
}
|
||||
|
||||
namespace test_digit_seperators
|
||||
namespace test_digit_separators
|
||||
{
|
||||
|
||||
constexpr auto ten_million = 100'000'000;
|
||||
@@ -560,3 +591,415 @@ namespace cxx14
|
||||
#endif // __cplusplus >= 201402L
|
||||
|
||||
]])
|
||||
|
||||
|
||||
dnl Tests for new features in C++17
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
|
||||
|
||||
// If the compiler admits that it is not ready for C++17, why torture it?
|
||||
// Hopefully, this will speed up the test.
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#error "This is not a C++ compiler"
|
||||
|
||||
#elif __cplusplus < 201703L
|
||||
|
||||
#error "This is not a C++17 compiler"
|
||||
|
||||
#else
|
||||
|
||||
#include <initializer_list>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
namespace cxx17
|
||||
{
|
||||
|
||||
namespace test_constexpr_lambdas
|
||||
{
|
||||
|
||||
constexpr int foo = [](){return 42;}();
|
||||
|
||||
}
|
||||
|
||||
namespace test::nested_namespace::definitions
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
namespace test_fold_expression
|
||||
{
|
||||
|
||||
template<typename... Args>
|
||||
int multiply(Args... args)
|
||||
{
|
||||
return (args * ... * 1);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
bool all(Args... args)
|
||||
{
|
||||
return (args && ...);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_extended_static_assert
|
||||
{
|
||||
|
||||
static_assert (true);
|
||||
|
||||
}
|
||||
|
||||
namespace test_auto_brace_init_list
|
||||
{
|
||||
|
||||
auto foo = {5};
|
||||
auto bar {5};
|
||||
|
||||
static_assert(std::is_same<std::initializer_list<int>, decltype(foo)>::value);
|
||||
static_assert(std::is_same<int, decltype(bar)>::value);
|
||||
}
|
||||
|
||||
namespace test_typename_in_template_template_parameter
|
||||
{
|
||||
|
||||
template<template<typename> typename X> struct D;
|
||||
|
||||
}
|
||||
|
||||
namespace test_fallthrough_nodiscard_maybe_unused_attributes
|
||||
{
|
||||
|
||||
int f1()
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
|
||||
[[nodiscard]] int f2()
|
||||
{
|
||||
[[maybe_unused]] auto unused = f1();
|
||||
|
||||
switch (f1())
|
||||
{
|
||||
case 17:
|
||||
f1();
|
||||
[[fallthrough]];
|
||||
case 42:
|
||||
f1();
|
||||
}
|
||||
return f1();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_extended_aggregate_initialization
|
||||
{
|
||||
|
||||
struct base1
|
||||
{
|
||||
int b1, b2 = 42;
|
||||
};
|
||||
|
||||
struct base2
|
||||
{
|
||||
base2() {
|
||||
b3 = 42;
|
||||
}
|
||||
int b3;
|
||||
};
|
||||
|
||||
struct derived : base1, base2
|
||||
{
|
||||
int d;
|
||||
};
|
||||
|
||||
derived d1 {{1, 2}, {}, 4}; // full initialization
|
||||
derived d2 {{}, {}, 4}; // value-initialized bases
|
||||
|
||||
}
|
||||
|
||||
namespace test_general_range_based_for_loop
|
||||
{
|
||||
|
||||
struct iter
|
||||
{
|
||||
int i;
|
||||
|
||||
int& operator* ()
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
||||
const int& operator* () const
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
||||
iter& operator++()
|
||||
{
|
||||
++i;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
struct sentinel
|
||||
{
|
||||
int i;
|
||||
};
|
||||
|
||||
bool operator== (const iter& i, const sentinel& s)
|
||||
{
|
||||
return i.i == s.i;
|
||||
}
|
||||
|
||||
bool operator!= (const iter& i, const sentinel& s)
|
||||
{
|
||||
return !(i == s);
|
||||
}
|
||||
|
||||
struct range
|
||||
{
|
||||
iter begin() const
|
||||
{
|
||||
return {0};
|
||||
}
|
||||
|
||||
sentinel end() const
|
||||
{
|
||||
return {5};
|
||||
}
|
||||
};
|
||||
|
||||
void f()
|
||||
{
|
||||
range r {};
|
||||
|
||||
for (auto i : r)
|
||||
{
|
||||
[[maybe_unused]] auto v = i;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_lambda_capture_asterisk_this_by_value
|
||||
{
|
||||
|
||||
struct t
|
||||
{
|
||||
int i;
|
||||
int foo()
|
||||
{
|
||||
return [*this]()
|
||||
{
|
||||
return i;
|
||||
}();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace test_enum_class_construction
|
||||
{
|
||||
|
||||
enum class byte : unsigned char
|
||||
{};
|
||||
|
||||
byte foo {42};
|
||||
|
||||
}
|
||||
|
||||
namespace test_constexpr_if
|
||||
{
|
||||
|
||||
template <bool cond>
|
||||
int f ()
|
||||
{
|
||||
if constexpr(cond)
|
||||
{
|
||||
return 13;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_selection_statement_with_initializer
|
||||
{
|
||||
|
||||
int f()
|
||||
{
|
||||
return 13;
|
||||
}
|
||||
|
||||
int f2()
|
||||
{
|
||||
if (auto i = f(); i > 0)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
switch (auto i = f(); i + 4)
|
||||
{
|
||||
case 17:
|
||||
return 2;
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_template_argument_deduction_for_class_templates
|
||||
{
|
||||
|
||||
template <typename T1, typename T2>
|
||||
struct pair
|
||||
{
|
||||
pair (T1 p1, T2 p2)
|
||||
: m1 {p1},
|
||||
m2 {p2}
|
||||
{}
|
||||
|
||||
T1 m1;
|
||||
T2 m2;
|
||||
};
|
||||
|
||||
void f()
|
||||
{
|
||||
[[maybe_unused]] auto p = pair{13, 42u};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_non_type_auto_template_parameters
|
||||
{
|
||||
|
||||
template <auto n>
|
||||
struct B
|
||||
{};
|
||||
|
||||
B<5> b1;
|
||||
B<'a'> b2;
|
||||
|
||||
}
|
||||
|
||||
namespace test_structured_bindings
|
||||
{
|
||||
|
||||
int arr[2] = { 1, 2 };
|
||||
std::pair<int, int> pr = { 1, 2 };
|
||||
|
||||
auto f1() -> int(&)[2]
|
||||
{
|
||||
return arr;
|
||||
}
|
||||
|
||||
auto f2() -> std::pair<int, int>&
|
||||
{
|
||||
return pr;
|
||||
}
|
||||
|
||||
struct S
|
||||
{
|
||||
int x1 : 2;
|
||||
volatile double y1;
|
||||
};
|
||||
|
||||
S f3()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
auto [ x1, y1 ] = f1();
|
||||
auto& [ xr1, yr1 ] = f1();
|
||||
auto [ x2, y2 ] = f2();
|
||||
auto& [ xr2, yr2 ] = f2();
|
||||
const auto [ x3, y3 ] = f3();
|
||||
|
||||
}
|
||||
|
||||
namespace test_exception_spec_type_system
|
||||
{
|
||||
|
||||
struct Good {};
|
||||
struct Bad {};
|
||||
|
||||
void g1() noexcept;
|
||||
void g2();
|
||||
|
||||
template<typename T>
|
||||
Bad
|
||||
f(T*, T*);
|
||||
|
||||
template<typename T1, typename T2>
|
||||
Good
|
||||
f(T1*, T2*);
|
||||
|
||||
static_assert (std::is_same_v<Good, decltype(f(g1, g2))>);
|
||||
|
||||
}
|
||||
|
||||
namespace test_inline_variables
|
||||
{
|
||||
|
||||
template<class T> void f(T)
|
||||
{}
|
||||
|
||||
template<class T> inline T g(T)
|
||||
{
|
||||
return T{};
|
||||
}
|
||||
|
||||
template<> inline void f<>(int)
|
||||
{}
|
||||
|
||||
template<> int g<>(int)
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace cxx17
|
||||
|
||||
#endif // __cplusplus < 201703L
|
||||
|
||||
]])
|
||||
|
||||
|
||||
dnl Tests for new features in C++20
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_20], [[
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#error "This is not a C++ compiler"
|
||||
|
||||
#elif __cplusplus < 202002L
|
||||
|
||||
#error "This is not a C++20 compiler"
|
||||
|
||||
#else
|
||||
|
||||
#include <version>
|
||||
|
||||
namespace cxx20
|
||||
{
|
||||
|
||||
// As C++20 supports feature test macros in the standard, there is no
|
||||
// immediate need to actually test for feature availability on the
|
||||
// Autoconf side.
|
||||
|
||||
} // namespace cxx20
|
||||
|
||||
#endif // __cplusplus < 202002L
|
||||
|
||||
]])
|
||||
@@ -0,0 +1,10 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
Name: OpenDHT (C Wrapper)
|
||||
Description: Distributed Hash Table library (C Wrapper)
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lopendht-c
|
||||
Requires.private: opendht gnutls >= 3.1 @argon2_lib@
|
||||
Cflags: -I${includedir}
|
||||
+3
-3
@@ -3,9 +3,9 @@ exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
Name: OpenDHT
|
||||
Description: C++ Distributed Hash Table library
|
||||
Description: C++14 Distributed Hash Table library
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lopendht
|
||||
Libs.private: -lpthread
|
||||
Requires.private: gnutls >= 3.1
|
||||
Libs.private: @http_parser_lib@ -pthread
|
||||
Requires.private: gnutls >= 3.3, nettle >= 2.4@argon2_lib@@jsoncpp_lib@@openssl_lib@
|
||||
Cflags: -I${includedir}
|
||||
@@ -9,3 +9,6 @@ add_custom_target(python ALL
|
||||
DEPENDS opendht opendht_cpp.pxd opendht.pyx)
|
||||
|
||||
install(CODE "execute_process(COMMAND python3 setup.py install --root=\$ENV{DESTDIR}/ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})")
|
||||
if (OPENDHT_TOOLS)
|
||||
install(PROGRAMS tools/dhtcluster.py DESTINATION ${CMAKE_INSTALL_BINDIR} RENAME dhtcluster)
|
||||
endif()
|
||||
@@ -1,5 +1,9 @@
|
||||
if USE_CYTHON
|
||||
|
||||
noinst_HEADERS = \
|
||||
opendht.pyx \
|
||||
opendht_cpp.pxd
|
||||
|
||||
PYTHON_INSTALL_RECORD = $(builddir)/install_record.txt
|
||||
|
||||
pybuild.stamp:
|
||||
|
||||
+382
-62
@@ -1,11 +1,11 @@
|
||||
# distutils: language = c++
|
||||
# distutils: extra_compile_args = -std=c++11
|
||||
# distutils: extra_compile_args = -std=c++17
|
||||
# distutils: include_dirs = ../../include
|
||||
# distutils: library_dirs = ../../src
|
||||
# distutils: libraries = opendht gnutls
|
||||
# cython: language_level=3
|
||||
#
|
||||
# Copyright (c) 2015-2016 Savoir-faire Linux Inc.
|
||||
# Copyright (c) 2015-2022 Savoir-faire Linux Inc.
|
||||
# Author(s): Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
|
||||
# Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
# Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
@@ -25,34 +25,56 @@
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with OpenDHT Python Wrapper. If not, see <http://www.gnu.org/licenses/>.
|
||||
# along with OpenDHT Python Wrapper. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
from libcpp.map cimport map as map
|
||||
from libcpp cimport bool
|
||||
from libcpp.utility cimport pair
|
||||
from libcpp.string cimport string
|
||||
from libcpp.memory cimport shared_ptr
|
||||
|
||||
from cython.parallel import parallel, prange
|
||||
from cython.operator cimport dereference as deref, preincrement as inc, predecrement as dec
|
||||
from cpython cimport ref
|
||||
from datetime import timedelta
|
||||
|
||||
cimport opendht_cpp as cpp
|
||||
|
||||
import threading
|
||||
|
||||
cdef inline void lookup_callback(cpp.vector[cpp.shared_ptr[cpp.IndexValue]]* values, cpp.Prefix* p, void *user_data) with gil:
|
||||
cbs = <object>user_data
|
||||
if 'lookup' in cbs and cbs['lookup']:
|
||||
vals = []
|
||||
for val in deref(values):
|
||||
v = IndexValue()
|
||||
v._value = val
|
||||
vals.append(v)
|
||||
cbs['lookup'](vals, p.toString())
|
||||
|
||||
cdef inline void shutdown_callback(void* user_data) with gil:
|
||||
cbs = <object>user_data
|
||||
if 'shutdown' in cbs and cbs['shutdown']:
|
||||
cbs['shutdown']()
|
||||
ref.Py_DECREF(cbs)
|
||||
|
||||
cdef inline bool get_callback(cpp.shared_ptr[cpp.Value] value, void *user_data) with gil:
|
||||
cb = (<object>user_data)['get']
|
||||
cdef inline bool get_callback(shared_ptr[cpp.Value] value, void *user_data) with gil:
|
||||
cbs = <object>user_data
|
||||
cb = cbs['get']
|
||||
f = cbs['filter'] if 'filter' in cbs else None
|
||||
pv = Value()
|
||||
pv._value = value
|
||||
return cb(pv)
|
||||
return cb(pv) if not f or f(pv) else True
|
||||
|
||||
cdef inline void done_callback(bool done, cpp.vector[cpp.shared_ptr[cpp.Node]]* nodes, void *user_data) with gil:
|
||||
cdef inline bool value_callback(shared_ptr[cpp.Value] value, bool expired, void *user_data) with gil:
|
||||
cbs = <object>user_data
|
||||
cb = cbs['valcb']
|
||||
f = cbs['filter'] if 'filter' in cbs else None
|
||||
pv = Value()
|
||||
pv._value = value
|
||||
return cb(pv, expired) if not f or f(pv) else True
|
||||
|
||||
cdef inline void done_callback(bool done, cpp.vector[shared_ptr[cpp.Node]]* nodes, void *user_data) with gil:
|
||||
node_ids = []
|
||||
for n in deref(nodes):
|
||||
h = NodeEntry()
|
||||
@@ -64,6 +86,12 @@ cdef inline void done_callback(bool done, cpp.vector[cpp.shared_ptr[cpp.Node]]*
|
||||
cbs['done'](done, node_ids)
|
||||
ref.Py_DECREF(cbs)
|
||||
|
||||
cdef inline void done_callback_simple(bool done, void *user_data) with gil:
|
||||
cbs = <object>user_data
|
||||
if 'done' in cbs and cbs['done']:
|
||||
cbs['done'](done)
|
||||
ref.Py_DECREF(cbs)
|
||||
|
||||
cdef class _WithID(object):
|
||||
def __repr__(self):
|
||||
return "<%s '%s'>" % (self.__class__.__name__, str(self))
|
||||
@@ -72,10 +100,10 @@ cdef class _WithID(object):
|
||||
|
||||
cdef class InfoHash(_WithID):
|
||||
cdef cpp.InfoHash _infohash
|
||||
def __init__(self, bytes str=b''):
|
||||
self._infohash = cpp.InfoHash(str)
|
||||
def __cinit__(self, bytes str=b''):
|
||||
self._infohash = cpp.InfoHash(str) if str else cpp.InfoHash()
|
||||
def __bool__(InfoHash self):
|
||||
return not (self._infohash == cpp.InfoHash())
|
||||
return <bool>self._infohash
|
||||
def __richcmp__(InfoHash self, InfoHash other, int op):
|
||||
if op == 0:
|
||||
return self._infohash < other._infohash
|
||||
@@ -108,8 +136,31 @@ cdef class InfoHash(_WithID):
|
||||
h._infohash = cpp.InfoHash.getRandom()
|
||||
return h
|
||||
|
||||
cdef class SockAddr(object):
|
||||
cdef cpp.SockAddr _addr
|
||||
def toString(SockAddr self):
|
||||
return self._addr.toString()
|
||||
def getPort(SockAddr self):
|
||||
return self._addr.getPort()
|
||||
def getFamily(SockAddr self):
|
||||
return self._addr.getFamily()
|
||||
def setPort(SockAddr self, cpp.in_port_t port):
|
||||
return self._addr.setPort(port)
|
||||
def setFamily(SockAddr self, cpp.sa_family_t af):
|
||||
return self._addr.setFamily(af)
|
||||
def isLoopback(SockAddr self):
|
||||
return self._addr.isLoopback()
|
||||
def isPrivate(SockAddr self):
|
||||
return self._addr.isPrivate()
|
||||
def isUnspecified(SockAddr self):
|
||||
return self._addr.isUnspecified()
|
||||
def __str__(self):
|
||||
return self.toString().decode()
|
||||
def __repr__(self):
|
||||
return "<%s '%s'>" % (self.__class__.__name__, str(self))
|
||||
|
||||
cdef class Node(_WithID):
|
||||
cdef cpp.shared_ptr[cpp.Node] _node
|
||||
cdef shared_ptr[cpp.Node] _node
|
||||
def getId(self):
|
||||
h = InfoHash()
|
||||
h._infohash = self._node.get().getId()
|
||||
@@ -120,7 +171,7 @@ cdef class Node(_WithID):
|
||||
return self._node.get().isExpired()
|
||||
|
||||
cdef class NodeEntry(_WithID):
|
||||
cdef cpp.pair[cpp.InfoHash, cpp.shared_ptr[cpp.Node]] _v
|
||||
cdef cpp.pair[cpp.InfoHash, shared_ptr[cpp.Node]] _v
|
||||
def getId(self):
|
||||
h = InfoHash()
|
||||
h._infohash = self._v.first
|
||||
@@ -130,10 +181,64 @@ cdef class NodeEntry(_WithID):
|
||||
n._node = self._v.second
|
||||
return n
|
||||
|
||||
cdef class Query(object):
|
||||
cdef cpp.Query _query
|
||||
def __cinit__(self, str q_str=''):
|
||||
self._query = cpp.Query(q_str.encode())
|
||||
def __str__(self):
|
||||
return self._query.toString().decode()
|
||||
def buildFrom(self, Select s, Where w):
|
||||
self._query = cpp.Query(s._select, w._where)
|
||||
def isSatisfiedBy(self, Query q):
|
||||
return self._query.isSatisfiedBy(q._query)
|
||||
|
||||
cdef class Select(object):
|
||||
cdef cpp.Select _select
|
||||
def __cinit__(self, str q_str=None):
|
||||
if q_str:
|
||||
self._select = cpp.Select(q_str.encode())
|
||||
else:
|
||||
self._select = cpp.Select()
|
||||
def __str__(self):
|
||||
return self._select.toString().decode()
|
||||
def isSatisfiedBy(self, Select os):
|
||||
return self._select.isSatisfiedBy(os._select)
|
||||
def field(self, int field):
|
||||
self._select.field(<cpp.Field> field)
|
||||
return self
|
||||
|
||||
cdef class Where(object):
|
||||
cdef cpp.Where _where
|
||||
def __cinit__(self, str q_str=None):
|
||||
if q_str:
|
||||
self._where = cpp.Where(q_str.encode())
|
||||
else:
|
||||
self._where = cpp.Where()
|
||||
def __str__(self):
|
||||
return self._where.toString().decode()
|
||||
def isSatisfiedBy(self, Where where):
|
||||
return self._where.isSatisfiedBy(where._where)
|
||||
def id(self, cpp.uint64_t id):
|
||||
self._where.id(id)
|
||||
return self
|
||||
def valueType(self, cpp.uint16_t type):
|
||||
self._where.valueType(type)
|
||||
return self
|
||||
def owner(self, InfoHash owner_pk_hash):
|
||||
self._where.owner(owner_pk_hash._infohash)
|
||||
return self
|
||||
def seq(self, cpp.uint16_t seq_no):
|
||||
self._where.seq(seq_no)
|
||||
return self
|
||||
def userType(self, str user_type):
|
||||
self._where.userType(user_type.encode())
|
||||
return self
|
||||
|
||||
cdef class Value(object):
|
||||
cdef cpp.shared_ptr[cpp.Value] _value
|
||||
def __init__(self, bytes val=b''):
|
||||
self._value.reset(new cpp.Value(val, len(val)))
|
||||
cdef shared_ptr[cpp.Value] _value
|
||||
def __init__(self, bytes val=b'', cpp.uint16_t id=0):
|
||||
self._value.reset(new cpp.Value(id, val, len(val)))
|
||||
|
||||
def __str__(self):
|
||||
return self._value.get().toString().decode()
|
||||
property owner:
|
||||
@@ -153,6 +258,11 @@ cdef class Value(object):
|
||||
return string(<char*>self._value.get().data.data(), self._value.get().data.size())
|
||||
def __set__(self, bytes value):
|
||||
self._value.get().data = value
|
||||
property user_type:
|
||||
def __get__(self):
|
||||
return self._value.get().user_type.decode()
|
||||
def __set__(self, str t):
|
||||
self._value.get().user_type = t.encode()
|
||||
property id:
|
||||
def __get__(self):
|
||||
return self._value.get().id
|
||||
@@ -162,9 +272,17 @@ cdef class Value(object):
|
||||
def __get__(self):
|
||||
return self._value.get().size()
|
||||
|
||||
cdef class ValueType(object):
|
||||
cdef cpp.ValueType * _value
|
||||
def __init__(self, cpp.uint16_t id, str name, expiration: timedelta):
|
||||
if not isinstance(expiration, timedelta):
|
||||
raise TypeError("expiration argument must be of type timedelta")
|
||||
cdef cpp.seconds duration = cpp.seconds(int(expiration.total_seconds()))
|
||||
self._value = new cpp.ValueType(id, name.encode(), duration)
|
||||
|
||||
cdef class NodeSetIter(object):
|
||||
cdef map[cpp.InfoHash, cpp.shared_ptr[cpp.Node]]* _nodes
|
||||
cdef map[cpp.InfoHash, cpp.shared_ptr[cpp.Node]].iterator _curIter
|
||||
cdef map[cpp.InfoHash, shared_ptr[cpp.Node]]* _nodes
|
||||
cdef map[cpp.InfoHash, shared_ptr[cpp.Node]].iterator _curIter
|
||||
def __init__(self, NodeSet s):
|
||||
self._nodes = &s._nodes
|
||||
self._curIter = self._nodes.begin()
|
||||
@@ -177,11 +295,11 @@ cdef class NodeSetIter(object):
|
||||
return h
|
||||
|
||||
cdef class NodeSet(object):
|
||||
cdef map[cpp.InfoHash, cpp.shared_ptr[cpp.Node]] _nodes
|
||||
cdef map[cpp.InfoHash, shared_ptr[cpp.Node]] _nodes
|
||||
def size(self):
|
||||
return self._nodes.size()
|
||||
def insert(self, NodeEntry l):
|
||||
self._nodes.insert(l._v)
|
||||
return self._nodes.insert(l._v).second
|
||||
def extend(self, li):
|
||||
for n in li:
|
||||
self.insert(n)
|
||||
@@ -199,7 +317,7 @@ cdef class NodeSet(object):
|
||||
return h
|
||||
def __str__(self):
|
||||
s = ''
|
||||
cdef map[cpp.InfoHash, cpp.shared_ptr[cpp.Node]].iterator it = self._nodes.begin()
|
||||
cdef map[cpp.InfoHash, shared_ptr[cpp.Node]].iterator it = self._nodes.begin()
|
||||
while it != self._nodes.end():
|
||||
s += deref(it).first.toString().decode() + ' ' + deref(it).second.get().getAddrStr().decode() + '\n'
|
||||
inc(it)
|
||||
@@ -207,19 +325,100 @@ cdef class NodeSet(object):
|
||||
def __iter__(self):
|
||||
return NodeSetIter(self)
|
||||
|
||||
cdef class PublicKey(_WithID):
|
||||
cdef cpp.PublicKey _key
|
||||
cdef class PrivateKey(_WithID):
|
||||
cdef shared_ptr[cpp.PrivateKey] _key
|
||||
def getId(self):
|
||||
h = InfoHash()
|
||||
h._infohash = self._key.getId()
|
||||
h._infohash = self._key.get().getSharedPublicKey().get().getId()
|
||||
return h
|
||||
def getPublicKey(self):
|
||||
pk = PublicKey()
|
||||
pk._key = self._key.get().getSharedPublicKey()
|
||||
return pk
|
||||
def decrypt(self, bytes dat):
|
||||
cdef size_t d_len = len(dat)
|
||||
cdef cpp.uint8_t* d_ptr = <cpp.uint8_t*>dat
|
||||
cdef cpp.Blob indat
|
||||
indat.assign(d_ptr, <cpp.uint8_t*>(d_ptr + d_len))
|
||||
cdef cpp.Blob decrypted = self._key.get().decrypt(indat)
|
||||
cdef char* decrypted_c_str = <char *>decrypted.data()
|
||||
cdef Py_ssize_t length = decrypted.size()
|
||||
return decrypted_c_str[:length]
|
||||
def __str__(self):
|
||||
return self.getId().toString().decode()
|
||||
@staticmethod
|
||||
def generate():
|
||||
k = PrivateKey()
|
||||
k._key = cpp.make_shared[cpp.PrivateKey](cpp.PrivateKey.generate())
|
||||
return k
|
||||
@staticmethod
|
||||
def generateEC():
|
||||
k = PrivateKey()
|
||||
k._key = cpp.make_shared[cpp.PrivateKey](cpp.PrivateKey.generateEC())
|
||||
return k
|
||||
|
||||
cdef class PublicKey(_WithID):
|
||||
cdef cpp.shared_ptr[cpp.PublicKey] _key
|
||||
def getId(self):
|
||||
h = InfoHash()
|
||||
h._infohash = self._key.get().getId()
|
||||
return h
|
||||
def encrypt(self, bytes dat):
|
||||
cdef size_t d_len = len(dat)
|
||||
cdef cpp.uint8_t* d_ptr = <cpp.uint8_t*>dat
|
||||
cdef cpp.Blob indat
|
||||
indat.assign(d_ptr, <cpp.uint8_t*>(d_ptr + d_len))
|
||||
cdef cpp.Blob encrypted = self._key.get().encrypt(indat)
|
||||
cdef char* encrypted_c_str = <char *>encrypted.data()
|
||||
cdef Py_ssize_t length = encrypted.size()
|
||||
return encrypted_c_str[:length]
|
||||
|
||||
cdef class Certificate(_WithID):
|
||||
cdef cpp.shared_ptr[cpp.Certificate] _cert
|
||||
cdef shared_ptr[cpp.Certificate] _cert
|
||||
def __init__(self, bytes dat = None):
|
||||
if dat:
|
||||
self._cert = cpp.make_shared[cpp.Certificate](<cpp.string>dat)
|
||||
def getId(self):
|
||||
h = InfoHash()
|
||||
h._infohash = self._cert.get().getId()
|
||||
if self._cert:
|
||||
h._infohash = self._cert.get().getId()
|
||||
return h
|
||||
def toString(self):
|
||||
return self._cert.get().toString().decode()
|
||||
def getName(self):
|
||||
return self._cert.get().getName()
|
||||
def revoke(self, PrivateKey k, Certificate c):
|
||||
self._cert.get().revoke(deref(k._key.get()), deref(c._cert.get()));
|
||||
def __bytes__(self):
|
||||
return self._cert.get().toString() if self._cert else b''
|
||||
property issuer:
|
||||
def __get__(self):
|
||||
c = Certificate()
|
||||
c._cert = self._cert.get().issuer
|
||||
return c;
|
||||
@staticmethod
|
||||
def generate(PrivateKey k, str name, Identity i = Identity(), bool is_ca = False):
|
||||
c = Certificate()
|
||||
c._cert = cpp.make_shared[cpp.Certificate](cpp.Certificate.generate(deref(k._key.get()), name.encode(), i._id, is_ca))
|
||||
return c
|
||||
|
||||
cdef class VerifyResult(object):
|
||||
cdef cpp.TrustListVerifyResult _result
|
||||
def __bool__(self):
|
||||
return self._result.isValid()
|
||||
def __str(self):
|
||||
return self._result.toString()
|
||||
|
||||
cdef class TrustList(object):
|
||||
cdef cpp.TrustList _trust
|
||||
def add(self, Certificate cert):
|
||||
self._trust.add(deref(cert._cert.get()))
|
||||
def remove(self, Certificate cert):
|
||||
self._trust.remove(deref(cert._cert.get()))
|
||||
def verify(self, Certificate cert):
|
||||
r = VerifyResult()
|
||||
r._result = self._trust.verify(deref(cert._cert.get()))
|
||||
return r
|
||||
|
||||
cdef class ListenToken(object):
|
||||
cdef cpp.InfoHash _h
|
||||
@@ -228,18 +427,31 @@ cdef class ListenToken(object):
|
||||
|
||||
cdef class Identity(object):
|
||||
cdef cpp.Identity _id
|
||||
def generate(self, str name = "pydht", Identity ca = Identity(), unsigned bits = 4096):
|
||||
self._id = cpp.generateIdentity(name.encode(), ca._id, bits)
|
||||
property PublicKey:
|
||||
def __init__(self, PrivateKey k = None, Certificate c = None):
|
||||
if k:
|
||||
self._id.first = k._key
|
||||
if c:
|
||||
self._id.second = c._cert
|
||||
@staticmethod
|
||||
def generate(str name = "pydht", Identity ca = Identity(), unsigned bits = 4096):
|
||||
i = Identity()
|
||||
i._id = cpp.generateIdentity(name.encode(), ca._id, bits)
|
||||
return i
|
||||
property publickey:
|
||||
def __get__(self):
|
||||
k = PublicKey()
|
||||
k._key = self._id.first.get().getPublicKey()
|
||||
k._key = self._id.first.get().getSharedPublicKey()
|
||||
return k
|
||||
property Certificate:
|
||||
property certificate:
|
||||
def __get__(self):
|
||||
c = Certificate()
|
||||
c._cert = self._id.second
|
||||
return c
|
||||
property key:
|
||||
def __get__(self):
|
||||
k = PrivateKey()
|
||||
k._key = self._id.first
|
||||
return k
|
||||
|
||||
cdef class DhtConfig(object):
|
||||
cdef cpp.DhtRunnerConfig _config
|
||||
@@ -252,66 +464,110 @@ cdef class DhtConfig(object):
|
||||
self._config.dht_config.node_config.is_bootstrap = bootstrap
|
||||
def setNodeId(self, InfoHash id):
|
||||
self._config.dht_config.node_config.node_id = id._infohash
|
||||
def setNetwork(self, unsigned netid):
|
||||
self._config.dht_config.node_config.network = netid
|
||||
def setMaintainStorage(self, bool maintain_storage):
|
||||
self._config.dht_config.node_config.maintain_storage = maintain_storage
|
||||
def setRateLimit(self, ssize_t max_req_per_sec, ssize_t max_peer_req_per_sec):
|
||||
self._config.dht_config.node_config.max_req_per_sec = max_req_per_sec
|
||||
self._config.dht_config.node_config.max_peer_req_per_sec = max_peer_req_per_sec
|
||||
|
||||
cdef class DhtRunner(_WithID):
|
||||
cdef cpp.DhtRunner* thisptr
|
||||
cdef cpp.shared_ptr[cpp.DhtRunner] thisptr
|
||||
def __cinit__(self):
|
||||
self.thisptr = new cpp.DhtRunner()
|
||||
self.thisptr.reset(new cpp.DhtRunner())
|
||||
def getId(self):
|
||||
h = InfoHash()
|
||||
h._infohash = self.thisptr.getId()
|
||||
if self.thisptr:
|
||||
h._infohash = self.thisptr.get().getId()
|
||||
return h
|
||||
def getNodeId(self):
|
||||
return self.thisptr.getNodeId().toString()
|
||||
def bootstrap(self, str host, str port):
|
||||
self.thisptr.bootstrap(host.encode(), port.encode())
|
||||
return self.thisptr.get().getNodeId().toString()
|
||||
def ping(self, SockAddr addr, done_cb=None):
|
||||
if done_cb:
|
||||
cb_obj = {'done':done_cb}
|
||||
ref.Py_INCREF(cb_obj)
|
||||
self.thisptr.get().bootstrap(addr._addr, cpp.bindDoneCbSimple(done_callback_simple, <void*>cb_obj))
|
||||
else:
|
||||
lock = threading.Condition()
|
||||
pending = 0
|
||||
ok = False
|
||||
def tmp_done(ok_ret):
|
||||
nonlocal pending, ok, lock
|
||||
with lock:
|
||||
ok = ok_ret
|
||||
pending -= 1
|
||||
lock.notify()
|
||||
with lock:
|
||||
pending += 1
|
||||
self.ping(addr, done_cb=tmp_done)
|
||||
while pending > 0:
|
||||
lock.wait()
|
||||
return ok
|
||||
def bootstrap(self, str host, str port=None):
|
||||
host_bytes = host.encode()
|
||||
port_bytes = port.encode() if port else b'4222'
|
||||
self.thisptr.get().bootstrap(<cpp.const_char*>host_bytes, <cpp.const_char*>port_bytes)
|
||||
def run(self, Identity id=None, is_bootstrap=False, cpp.in_port_t port=0, str ipv4="", str ipv6="", DhtConfig config=DhtConfig()):
|
||||
if id:
|
||||
config.setIdentity(id)
|
||||
if ipv4 or ipv6:
|
||||
bind4 = ipv4.encode() if ipv4 else b''
|
||||
bind6 = ipv6.encode() if ipv6 else b''
|
||||
self.thisptr.run(bind4, bind6, str(port).encode(), config._config)
|
||||
self.thisptr.get().run(bind4, bind6, str(port).encode(), config._config)
|
||||
else:
|
||||
self.thisptr.run(port, config._config)
|
||||
self.thisptr.get().run(port, config._config)
|
||||
def join(self):
|
||||
self.thisptr.join()
|
||||
self.thisptr.get().join()
|
||||
def shutdown(self, shutdown_cb=None):
|
||||
cb_obj = {'shutdown':shutdown_cb}
|
||||
ref.Py_INCREF(cb_obj)
|
||||
self.thisptr.shutdown(cpp.bindShutdownCb(shutdown_callback, <void*>cb_obj))
|
||||
self.thisptr.get().shutdown(cpp.bindShutdownCb(shutdown_callback, <void*>cb_obj))
|
||||
def registerType(self, ValueType type):
|
||||
self.thisptr.get().registerType(deref(type._value))
|
||||
def enableLogging(self):
|
||||
cpp.enableLogging(self.thisptr[0])
|
||||
cpp.enableLogging(self.thisptr.get()[0])
|
||||
def disableLogging(self):
|
||||
cpp.disableLogging(self.thisptr[0])
|
||||
cpp.disableLogging(self.thisptr.get()[0])
|
||||
def enableFileLogging(self, str path):
|
||||
cpp.enableFileLogging(self.thisptr[0], path)
|
||||
cpp.enableFileLogging(self.thisptr.get()[0], path.encode())
|
||||
def isRunning(self):
|
||||
return self.thisptr.isRunning()
|
||||
return self.thisptr.get().isRunning()
|
||||
def getBound(self, cpp.sa_family_t af = 0):
|
||||
s = SockAddr()
|
||||
s._addr = self.thisptr.get().getBound(af)
|
||||
return s
|
||||
def getStorageLog(self):
|
||||
return self.thisptr.getStorageLog().decode()
|
||||
return self.thisptr.get().getStorageLog().decode()
|
||||
def getRoutingTablesLog(self, cpp.sa_family_t af):
|
||||
return self.thisptr.getRoutingTablesLog(af).decode()
|
||||
return self.thisptr.get().getRoutingTablesLog(af).decode()
|
||||
def getSearchesLog(self, cpp.sa_family_t af):
|
||||
return self.thisptr.getSearchesLog(af).decode()
|
||||
return self.thisptr.get().getSearchesLog(af).decode()
|
||||
def getNodeMessageStats(self):
|
||||
stats = []
|
||||
cdef cpp.vector[unsigned] res = self.thisptr.getNodeMessageStats(False)
|
||||
cdef cpp.vector[unsigned] res = self.thisptr.get().getNodeMessageStats(False)
|
||||
for n in res:
|
||||
stats.append(n)
|
||||
return stats
|
||||
|
||||
def get(self, InfoHash key, get_cb=None, done_cb=None):
|
||||
def get(self, InfoHash key, get_cb=None, done_cb=None, filter=None, Where where=None):
|
||||
"""Retreive values associated with a key on the DHT.
|
||||
|
||||
key -- the key for which to search
|
||||
get_cb -- is set, makes the operation non-blocking. Called when a value is found on the DHT.
|
||||
done_cb -- optional callback used when get_cb is set. Called when the operation is completed.
|
||||
key -- the key for which to search
|
||||
get_cb -- is set, makes the operation non-blocking. Called when a value
|
||||
is found on the DHT.
|
||||
done_cb -- optional callback used when get_cb is set. Called when the
|
||||
operation is completed.
|
||||
"""
|
||||
if get_cb:
|
||||
cb_obj = {'get':get_cb, 'done':done_cb}
|
||||
cb_obj = {'get':get_cb, 'done':done_cb, 'filter':filter}
|
||||
ref.Py_INCREF(cb_obj)
|
||||
self.thisptr.get(key._infohash, cpp.bindGetCb(get_callback, <void*>cb_obj), cpp.bindDoneCb(done_callback, <void*>cb_obj))
|
||||
if where is None:
|
||||
where = Where()
|
||||
self.thisptr.get().get(key._infohash, cpp.bindGetCb(get_callback, <void*>cb_obj),
|
||||
cpp.bindDoneCb(done_callback, <void*>cb_obj),
|
||||
cpp.nullptr, #filter implemented in the get_callback
|
||||
where._where)
|
||||
else:
|
||||
lock = threading.Condition()
|
||||
pending = 0
|
||||
@@ -327,21 +583,21 @@ cdef class DhtRunner(_WithID):
|
||||
lock.notify()
|
||||
with lock:
|
||||
pending += 1
|
||||
self.get(key, get_cb=tmp_get, done_cb=tmp_done)
|
||||
self.get(key, get_cb=tmp_get, done_cb=tmp_done, filter=filter, where=where)
|
||||
while pending > 0:
|
||||
lock.wait()
|
||||
return res
|
||||
def put(self, InfoHash key, Value val, done_cb=None):
|
||||
"""Publish a new value on the DHT at key.
|
||||
|
||||
key -- the DHT key where to put the value
|
||||
val -- the value to put on the DHT
|
||||
key -- the DHT key where to put the value
|
||||
val -- the value to put on the DHT
|
||||
done_cb -- optional callback called when the operation is completed.
|
||||
"""
|
||||
if done_cb:
|
||||
cb_obj = {'done':done_cb}
|
||||
ref.Py_INCREF(cb_obj)
|
||||
self.thisptr.put(key._infohash, val._value, cpp.bindDoneCb(done_callback, <void*>cb_obj))
|
||||
self.thisptr.get().put(key._infohash, val._value, cpp.bindDoneCb(done_callback, <void*>cb_obj))
|
||||
else:
|
||||
lock = threading.Condition()
|
||||
pending = 0
|
||||
@@ -358,16 +614,80 @@ cdef class DhtRunner(_WithID):
|
||||
while pending > 0:
|
||||
lock.wait()
|
||||
return ok
|
||||
def listen(self, InfoHash key, get_cb):
|
||||
def listen(self, InfoHash key, value_cb):
|
||||
t = ListenToken()
|
||||
t._h = key._infohash
|
||||
cb_obj = {'get':get_cb}
|
||||
cb_obj = {'valcb':value_cb}
|
||||
t._cb['cb'] = cb_obj
|
||||
# avoid the callback being destructed if the token is destroyed
|
||||
ref.Py_INCREF(cb_obj)
|
||||
t._t = self.thisptr.listen(t._h, cpp.bindGetCb(get_callback, <void*>cb_obj)).share()
|
||||
t._t = self.thisptr.get().listen(t._h, cpp.bindValueCb(value_callback, <void*>cb_obj)).share()
|
||||
return t
|
||||
def cancelListen(self, ListenToken token):
|
||||
self.thisptr.cancelListen(token._h, token._t)
|
||||
self.thisptr.get().cancelListen(token._h, token._t)
|
||||
ref.Py_DECREF(<object>token._cb['cb'])
|
||||
# fixme: not thread safe
|
||||
|
||||
cdef class IndexValue(object):
|
||||
cdef cpp.shared_ptr[cpp.IndexValue] _value
|
||||
def __init__(self, InfoHash h=None, cpp.uint64_t vid=0):
|
||||
cdef cpp.InfoHash hh = h._infohash
|
||||
self._value.reset(new cpp.IndexValue(hh, vid))
|
||||
def __str__(self):
|
||||
return "(" + self.getKey().toString().decode() +", "+ str(self.getValueId()) +")"
|
||||
def getKey(self):
|
||||
h = InfoHash()
|
||||
h._infohash = self._value.get().first
|
||||
return h
|
||||
def getValueId(self):
|
||||
return self._value.get().second
|
||||
|
||||
cdef class Pht(object):
|
||||
cdef cpp.Pht* thisptr
|
||||
def __cinit__(self, bytes name, key_spec, DhtRunner dht):
|
||||
cdef cpp.IndexKeySpec cpp_key_spec
|
||||
for kk, size in key_spec.items():
|
||||
cpp_key_spec[bytes(kk, 'utf-8')] = size
|
||||
self.thisptr = new cpp.Pht(name, cpp_key_spec, dht.thisptr)
|
||||
property MAX_NODE_ENTRY_COUNT:
|
||||
def __get__(self):
|
||||
return cpp.PHT_MAX_NODE_ENTRY_COUNT
|
||||
def lookup(self, key, lookup_cb=None, done_cb=None):
|
||||
"""Query the Index with a specified key.
|
||||
|
||||
key -- the key for to the entry in the index.
|
||||
lookup_cb -- function called when the operation is completed. This
|
||||
function takes a list of IndexValue objects and a string
|
||||
representation of the prefix where the value was indexed in
|
||||
the PHT.
|
||||
"""
|
||||
cb_obj = {'lookup':lookup_cb, 'done':done_cb} # TODO: donecallback is to be removed
|
||||
ref.Py_INCREF(cb_obj)
|
||||
cdef cpp.IndexKey cppk
|
||||
for kk, v in key.items():
|
||||
cppk[bytes(kk, 'utf-8')] = bytes(v)
|
||||
self.thisptr.lookup(
|
||||
cppk,
|
||||
cpp.Pht.bindLookupCb(lookup_callback, <void*>cb_obj),
|
||||
cpp.bindDoneCbSimple(done_callback_simple, <void*>cb_obj)
|
||||
)
|
||||
def insert(self, key, IndexValue value, done_cb=None):
|
||||
"""Add an index entry to the Index.
|
||||
|
||||
key -- the key for to the entry in the index.
|
||||
value -- an IndexValue object describing the indexed value.
|
||||
done_cb -- Called when the operation is completed.
|
||||
"""
|
||||
cb_obj = {'done':done_cb}
|
||||
ref.Py_INCREF(cb_obj)
|
||||
cdef cpp.IndexKey cppk
|
||||
for kk, v in key.items():
|
||||
cppk[bytes(kk, 'utf-8')] = bytes(v)
|
||||
cdef cpp.IndexValue val
|
||||
val.first = (<InfoHash>value.getKey())._infohash
|
||||
val.second = value.getValueId()
|
||||
self.thisptr.insert(
|
||||
cppk,
|
||||
val,
|
||||
cpp.bindDoneCbSimple(done_callback_simple, <void*>cb_obj)
|
||||
)
|
||||
+141
-13
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2015-2016 Savoir-faire Linux Inc.
|
||||
# Copyright (c) 2015-2022 Savoir-faire Linux Inc.
|
||||
# Author(s): Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
# Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
#
|
||||
@@ -13,23 +13,36 @@
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||
# along with this program; If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
from libc.stdint cimport *
|
||||
from libcpp cimport bool
|
||||
from libcpp cimport bool, nullptr_t, nullptr
|
||||
from libcpp.string cimport string
|
||||
from libcpp.vector cimport vector
|
||||
from libcpp.utility cimport pair
|
||||
from libcpp.map cimport map
|
||||
from libc.string cimport const_char, const_uchar
|
||||
|
||||
ctypedef uint16_t in_port_t
|
||||
ctypedef unsigned short int sa_family_t;
|
||||
|
||||
cdef extern from "<iostream>" namespace "std::chrono" nogil:
|
||||
cdef cppclass duration[ulong]:
|
||||
duration() except +
|
||||
|
||||
cdef cppclass seconds:
|
||||
duration seconds(uint64_t) except +
|
||||
duration seconds()
|
||||
|
||||
cdef extern from "<memory>" namespace "std" nogil:
|
||||
cdef cppclass shared_ptr[T]:
|
||||
shared_ptr() except +
|
||||
shared_ptr(T*) except +
|
||||
T* get()
|
||||
T operator*()
|
||||
bool operator bool() const
|
||||
void reset(T*)
|
||||
shared_ptr[T] make_shared[T](...) except +
|
||||
|
||||
cdef extern from "<functional>" namespace "std" nogil:
|
||||
cdef cppclass hash[T]:
|
||||
@@ -60,8 +73,23 @@ cdef extern from "opendht/infohash.h" namespace "dht":
|
||||
InfoHash get(string s)
|
||||
@staticmethod
|
||||
InfoHash getRandom()
|
||||
bool operator==(InfoHash)
|
||||
bool operator<(InfoHash)
|
||||
bool operator==(InfoHash) const
|
||||
bool operator<(InfoHash) const
|
||||
bool operator bool() const
|
||||
|
||||
cdef extern from "opendht/sockaddr.h" namespace "dht":
|
||||
cdef cppclass SockAddr:
|
||||
SockAddr() except +
|
||||
string toString() const
|
||||
in_port_t getPort() const
|
||||
void setPort(in_port_t p)
|
||||
sa_family_t getFamily() const
|
||||
void setFamily(sa_family_t f)
|
||||
bool isLoopback() const
|
||||
bool isPrivate() const
|
||||
bool isUnspecified() const
|
||||
|
||||
ctypedef vector[uint8_t] Blob
|
||||
|
||||
cdef extern from "opendht/crypto.h" namespace "dht::crypto":
|
||||
ctypedef pair[shared_ptr[PrivateKey], shared_ptr[Certificate]] Identity
|
||||
@@ -69,21 +97,58 @@ cdef extern from "opendht/crypto.h" namespace "dht::crypto":
|
||||
|
||||
cdef cppclass PrivateKey:
|
||||
PrivateKey()
|
||||
PublicKey getPublicKey() const
|
||||
shared_ptr[PublicKey] getSharedPublicKey() const
|
||||
Blob decrypt(Blob data) const
|
||||
@staticmethod
|
||||
PrivateKey generate()
|
||||
@staticmethod
|
||||
PrivateKey generateEC()
|
||||
|
||||
cdef cppclass PublicKey:
|
||||
PublicKey()
|
||||
InfoHash getId() const
|
||||
Blob encrypt(Blob data) const
|
||||
|
||||
cdef cppclass Certificate:
|
||||
Certificate()
|
||||
Certificate(string pem)
|
||||
InfoHash getId() const
|
||||
string toString() const
|
||||
string getName() const
|
||||
void revoke(PrivateKey key, Certificate cert)
|
||||
@staticmethod
|
||||
Certificate generate(PrivateKey key, string name, Identity ca, bool is_ca)
|
||||
shared_ptr[Certificate] issuer
|
||||
|
||||
cdef cppclass TrustList:
|
||||
cppclass VerifyResult:
|
||||
bool operator bool() const
|
||||
bool isValid() const
|
||||
string toString() const
|
||||
TrustList()
|
||||
void add(Certificate)
|
||||
void remove(Certificate)
|
||||
VerifyResult verify(Certificate);
|
||||
|
||||
ctypedef TrustList.VerifyResult TrustListVerifyResult
|
||||
|
||||
cdef extern from "opendht/value.h" namespace "dht::Value":
|
||||
cdef cppclass Field:
|
||||
pass
|
||||
|
||||
cdef extern from "opendht/value.h" namespace "dht::Value::Field":
|
||||
cdef Field None
|
||||
cdef Field Id
|
||||
cdef Field OwnerPk
|
||||
cdef Field SeqNum
|
||||
cdef Field UserType
|
||||
cdef Field COUNT
|
||||
|
||||
cdef extern from "opendht/value.h" namespace "dht":
|
||||
cdef cppclass Value:
|
||||
Value() except +
|
||||
Value(vector[uint8_t]) except +
|
||||
Value(const uint8_t* dat_ptr, size_t dat_len) except +
|
||||
Value(const uint16_t t, const uint8_t* dat_ptr, size_t dat_len) except +
|
||||
string toString() const
|
||||
size_t size() const
|
||||
uint64_t id
|
||||
@@ -92,6 +157,37 @@ cdef extern from "opendht/value.h" namespace "dht":
|
||||
vector[uint8_t] data
|
||||
string user_type
|
||||
|
||||
cdef cppclass ValueType:
|
||||
ValueType(uint16_t id, string name, seconds expiration) except +
|
||||
uint16_t id
|
||||
string name
|
||||
seconds expiration
|
||||
|
||||
cdef cppclass Query:
|
||||
Query() except +
|
||||
Query(Select s, Where w) except +
|
||||
Query(string q_str) except +
|
||||
bool isSatisfiedBy(const Query& q) const
|
||||
string toString() const
|
||||
|
||||
cdef cppclass Select:
|
||||
Select() except +
|
||||
Select(const string& q_str) except +
|
||||
bool isSatisfiedBy(const Select& os) const
|
||||
Select& field(Field field)
|
||||
string toString() const
|
||||
|
||||
cdef cppclass Where:
|
||||
Where() except +
|
||||
Where(const string& q_str)
|
||||
bool isSatisfiedBy(const Where& where) const
|
||||
Where& id(uint64_t id)
|
||||
Where& valueType(uint16_t type)
|
||||
Where& owner(InfoHash owner_pk_hash)
|
||||
Where& seq(uint16_t seq_no)
|
||||
Where& userType(string user_type)
|
||||
string toString() const
|
||||
|
||||
cdef extern from "opendht/node.h" namespace "dht":
|
||||
cdef cppclass Node:
|
||||
Node() except +
|
||||
@@ -102,24 +198,35 @@ cdef extern from "opendht/node.h" namespace "dht":
|
||||
cdef extern from "opendht/callbacks.h" namespace "dht":
|
||||
ctypedef void (*ShutdownCallbackRaw)(void *user_data)
|
||||
ctypedef bool (*GetCallbackRaw)(shared_ptr[Value] values, void *user_data)
|
||||
ctypedef bool (*ValueCallbackRaw)(shared_ptr[Value] values, bool expired, void *user_data)
|
||||
ctypedef void (*DoneCallbackRaw)(bool done, vector[shared_ptr[Node]]* nodes, void *user_data)
|
||||
ctypedef void (*DoneCallbackSimpleRaw)(bool done, void *user_data)
|
||||
|
||||
cppclass ShutdownCallback:
|
||||
ShutdownCallback() except +
|
||||
cppclass GetCallback:
|
||||
GetCallback() except +
|
||||
#GetCallback(GetCallbackRaw cb, void *user_data) except +
|
||||
cppclass ValueCallback:
|
||||
ValueCallback() except +
|
||||
cppclass DoneCallback:
|
||||
DoneCallback() except +
|
||||
#DoneCallback(DoneCallbackRaw, void *user_data) except +
|
||||
cppclass DoneCallbackSimple:
|
||||
DoneCallbackSimple() except +
|
||||
|
||||
cdef ShutdownCallback bindShutdownCb(ShutdownCallbackRaw cb, void *user_data)
|
||||
cdef GetCallback bindGetCb(GetCallbackRaw cb, void *user_data)
|
||||
cdef ValueCallback bindValueCb(ValueCallbackRaw cb, void *user_data)
|
||||
cdef DoneCallback bindDoneCb(DoneCallbackRaw cb, void *user_data)
|
||||
cdef DoneCallbackSimple bindDoneCbSimple(DoneCallbackSimpleRaw cb, void *user_data)
|
||||
|
||||
cppclass Config:
|
||||
InfoHash node_id
|
||||
uint32_t network
|
||||
bool is_bootstrap
|
||||
bool maintain_storage
|
||||
string persist_path
|
||||
size_t max_req_per_sec
|
||||
size_t max_peer_req_per_sec
|
||||
cppclass SecureDhtConfig:
|
||||
Config node_config
|
||||
Identity id
|
||||
@@ -134,18 +241,21 @@ cdef extern from "opendht/dhtrunner.h" namespace "dht":
|
||||
bool threaded
|
||||
InfoHash getId() const
|
||||
InfoHash getNodeId() const
|
||||
void bootstrap(const char*, const char*)
|
||||
void bootstrap(const_char*, const_char*)
|
||||
void bootstrap(const SockAddr&, DoneCallbackSimple done_cb)
|
||||
void run(in_port_t, Config config)
|
||||
void run(const char*, const char*, const char*, Config config)
|
||||
void run(const_char*, const_char*, const_char*, Config config)
|
||||
void join()
|
||||
void shutdown(ShutdownCallback)
|
||||
void registerType(ValueType& value);
|
||||
bool isRunning()
|
||||
SockAddr getBound(sa_family_t af) const
|
||||
string getStorageLog() const
|
||||
string getRoutingTablesLog(sa_family_t af) const
|
||||
string getSearchesLog(sa_family_t af) const
|
||||
void get(InfoHash key, GetCallback get_cb, DoneCallback done_cb)
|
||||
void get(InfoHash key, GetCallback get_cb, DoneCallback done_cb, nullptr_t f, Where w)
|
||||
void put(InfoHash key, shared_ptr[Value] val, DoneCallback done_cb)
|
||||
ListenToken listen(InfoHash key, GetCallback get_cb)
|
||||
ListenToken listen(InfoHash key, ValueCallback get_cb)
|
||||
void cancelListen(InfoHash key, SharedListenToken token)
|
||||
vector[unsigned] getNodeMessageStats(bool i)
|
||||
|
||||
@@ -156,3 +266,21 @@ cdef extern from "opendht/log.h" namespace "dht::log":
|
||||
void disableLogging(DhtRunner& dht)
|
||||
void enableFileLogging(DhtRunner& dht, const string& path)
|
||||
|
||||
cdef extern from "opendht/indexation/pht.h" namespace "dht::indexation":
|
||||
size_t PHT_MAX_NODE_ENTRY_COUNT "dht::indexation::Pht::MAX_NODE_ENTRY_COUNT"
|
||||
cdef cppclass Prefix:
|
||||
Prefix() except +
|
||||
Prefix(vector[uint8_t]) except +
|
||||
string toString() const
|
||||
ctypedef pair[InfoHash, uint64_t] IndexValue "dht::indexation::Value"
|
||||
ctypedef map[string, vector[uint8_t]] IndexKey "dht::indexation::Pht::Key"
|
||||
ctypedef map[string, uint32_t] IndexKeySpec "dht::indexation::Pht::KeySpec"
|
||||
ctypedef void (*LookupCallbackRaw)(vector[shared_ptr[IndexValue]]* values, Prefix* p, void* user_data);
|
||||
cdef cppclass Pht:
|
||||
cppclass LookupCallback:
|
||||
LookupCallback() except +
|
||||
Pht(string, IndexKeySpec, shared_ptr[DhtRunner]) except +
|
||||
void lookup(IndexKey k, LookupCallback cb, DoneCallbackSimple doneCb);
|
||||
void insert(IndexKey k, IndexValue v, DoneCallbackSimple cb)
|
||||
@staticmethod
|
||||
LookupCallback bindLookupCb(LookupCallbackRaw cb, void *user_data)
|
||||
+5
-5
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2015-2016 Savoir-faire Linux Inc.
|
||||
# Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
# Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
|
||||
# Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
#
|
||||
@@ -18,7 +18,7 @@
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with OpenDHT Python Wrapper. If not, see <http://www.gnu.org/licenses/>.
|
||||
# along with OpenDHT Python Wrapper. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
from setuptools import setup, Extension
|
||||
@@ -27,7 +27,7 @@ from Cython.Distutils import build_ext
|
||||
|
||||
setup(name="opendht",
|
||||
version="@PACKAGE_VERSION@",
|
||||
description="Python wrapper for opendht",
|
||||
description="Python wrapper for OpenDHT",
|
||||
url='https://github.com/savoirfairelinux/opendht',
|
||||
author="Adrien Béraud, Guillaume Roguez, Simon Désaulniers",
|
||||
license="GPLv3",
|
||||
@@ -50,8 +50,8 @@ setup(name="opendht",
|
||||
["@CURRENT_SOURCE_DIR@/opendht.pyx"],
|
||||
include_dirs = ['@PROJECT_SOURCE_DIR@/include'],
|
||||
language="c++",
|
||||
extra_compile_args=["-std=c++11"],
|
||||
extra_link_args=["-std=c++11"],
|
||||
extra_compile_args=["-std=c++17"],
|
||||
extra_link_args=["-std=c++17"],
|
||||
libraries=["opendht"],
|
||||
library_dirs = ['@CURRENT_BINARY_DIR@', '@PROJECT_BINARY_DIR@']
|
||||
))
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import unittest
|
||||
import opendht as dht
|
||||
|
||||
class OpenDhtTester(unittest.TestCase):
|
||||
|
||||
# test that DhtRunner can be instatiated and deleted without throwing
|
||||
def test_instance(self):
|
||||
for i in range(10):
|
||||
r = dht.DhtRunner()
|
||||
r.run()
|
||||
del r
|
||||
|
||||
# test that bootstraping works (raw address)
|
||||
def test_bootstrap(self):
|
||||
a = dht.DhtRunner()
|
||||
a.run()
|
||||
b = dht.DhtRunner()
|
||||
b.run()
|
||||
self.assertTrue(b.ping(a.getBound()))
|
||||
|
||||
def test_crypto(self):
|
||||
i = dht.Identity.generate("id")
|
||||
message = dht.InfoHash.getRandom().toString()
|
||||
encrypted = i.publickey.encrypt(message)
|
||||
decrypted = i.key.decrypt(encrypted)
|
||||
self.assertTrue(message == decrypted)
|
||||
|
||||
def test_crypto_ec(self):
|
||||
key = dht.PrivateKey.generateEC()
|
||||
cert = dht.Certificate.generate(key, "CA", is_ca=True)
|
||||
ca_id = dht.Identity(key, cert)
|
||||
self.assertTrue(cert.getId() == key.getPublicKey().getId())
|
||||
key2 = dht.PrivateKey.generateEC()
|
||||
cert2 = dht.Certificate.generate(key2, "cert", ca_id)
|
||||
trust = dht.TrustList()
|
||||
trust.add(cert)
|
||||
self.assertTrue(trust.verify(cert2))
|
||||
|
||||
def test_trust(self):
|
||||
main_id = dht.Identity.generate("id_1")
|
||||
sub_id1 = dht.Identity.generate("sid_1", main_id)
|
||||
sub_id2 = dht.Identity.generate("sid_2", main_id)
|
||||
main_id.certificate.revoke(main_id.key, sub_id2.certificate)
|
||||
main_id2 = dht.Identity.generate("id_2")
|
||||
trust = dht.TrustList()
|
||||
trust.add(main_id.certificate)
|
||||
self.assertTrue(trust.verify(main_id.certificate))
|
||||
self.assertTrue(trust.verify(sub_id1.certificate))
|
||||
self.assertFalse(trust.verify(sub_id2.certificate))
|
||||
self.assertFalse(trust.verify(main_id2.certificate))
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,45 @@
|
||||
# Benchmark
|
||||
|
||||
The `benchmark.py` script is used for testing OpenDHT in various cases. If you
|
||||
run `benchmark.py --help`, you should find the following text:
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--performance Launches performance benchmark test. Available args
|
||||
for "-t" are: gets.
|
||||
--data-persistence Launches data persistence benchmark test. Available
|
||||
args for "-t" are: delete, replace, mult_time.
|
||||
Available args for "-o" are : dump_str_log,
|
||||
keep_alive, trigger, traffic_plot, op_plot. Use "-m"
|
||||
to specify the number of producers on the DHT.Use "-e"
|
||||
to specify the number of values to put on the DHT.
|
||||
|
||||
These options specify the feature to be tested. Each feature has its own tests.
|
||||
You specify the test by using `-t` flag (see `benchmark.py --help` for full
|
||||
help).
|
||||
|
||||
## Python dependencies
|
||||
|
||||
- pyroute2 >=0.6.9
|
||||
- matplotlib
|
||||
- GeoIP (used by `scanner.py` for drawing map of the world)
|
||||
- ipaddress
|
||||
- netifaces
|
||||
- networkx
|
||||
- numpy
|
||||
|
||||
## Usage
|
||||
|
||||
Before running the script, you have to build and install OpenDHT and its cython
|
||||
wrapper (`cython3` has to be installed) on the system so that it can be found by
|
||||
the benchmark script.
|
||||
|
||||
$ cd $OPENDHT_SRC_DIR
|
||||
$ ./autogen.sh
|
||||
$ ./configure
|
||||
$ make && sudo make install
|
||||
|
||||
Then, you can use the script like so:
|
||||
|
||||
$ cd $OPENDHT_SRC_DIR/python/tools/
|
||||
$ python3 benchmark.py --performance -t gets -n 2048
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2015-2016 Savoir-faire Linux Inc.
|
||||
# Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
# Author(s): Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
# Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
#
|
||||
@@ -27,7 +27,7 @@ import random
|
||||
|
||||
from dht.network import DhtNetwork
|
||||
from dht.network import DhtNetworkSubProcess
|
||||
from dht.tests import PerformanceTest, PersistenceTest
|
||||
from dht.tests import PerformanceTest, PersistenceTest, PhtTest
|
||||
from dht import virtual_network_builder
|
||||
from dht import network as dhtnetwork
|
||||
|
||||
@@ -66,7 +66,11 @@ class WorkBench():
|
||||
|
||||
def create_virtual_net(self):
|
||||
if self.virtual_locs > 1:
|
||||
cmd = ["python3", os.path.abspath(virtual_network_builder.__file__), "-i", self.ifname, "-n", str(self.clusters), '-l', str(self.loss), '-d', str(self.delay)]
|
||||
cmd = ["python3", os.path.abspath(virtual_network_builder.__file__),
|
||||
"-i", self.ifname,
|
||||
"-n", str(self.clusters),
|
||||
'-l', str(self.loss),
|
||||
'-d', str(self.delay)]
|
||||
if not self.disable_ipv4:
|
||||
cmd.append('-4')
|
||||
if not self.disable_ipv6:
|
||||
@@ -90,10 +94,17 @@ class WorkBench():
|
||||
cmd.extend(['-b', self.local_bootstrap.ip4])
|
||||
if not self.disable_ipv6 and self.local_bootstrap.ip6:
|
||||
cmd.extend(['-b6', self.local_bootstrap.ip6])
|
||||
self.procs[i] = DhtNetworkSubProcess('node'+str(i), cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
while DhtNetworkSubProcess.NOTIFY_TOKEN not in self.procs[i].getline():
|
||||
# waiting for process to spawn
|
||||
time.sleep(0.5)
|
||||
lock = threading.Condition()
|
||||
def dcb(success):
|
||||
nonlocal lock
|
||||
if not success:
|
||||
DhtNetwork.Log.err("Failed to initialize network...")
|
||||
with lock:
|
||||
lock.notify()
|
||||
with lock:
|
||||
self.procs[i] = DhtNetworkSubProcess('node'+str(i), cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
self.procs[i].sendPing(done_cb=dcb)
|
||||
lock.wait()
|
||||
else:
|
||||
raise Exception('First create bootstrap.')
|
||||
|
||||
@@ -157,11 +168,16 @@ if __name__ == '__main__':
|
||||
featureArgs = parser.add_mutually_exclusive_group(required=True)
|
||||
featureArgs.add_argument('--performance', action='store_true', default=False,
|
||||
help='Launches performance benchmark test. Available args for "-t" are: gets.')
|
||||
featureArgs.add_argument('--pht', action='store_true', default=False,
|
||||
help='Launches PHT benchmark test. '\
|
||||
'Available args for "-t" are: insert. '\
|
||||
'Timer available by adding "timer" to "-o" args'\
|
||||
'Use "-m" option for fixing number of keys to create during the test.')
|
||||
featureArgs.add_argument('--data-persistence', action='store_true', default=0,
|
||||
help='Launches data persistence benchmark test. '\
|
||||
'Available args for "-t" are: delete, replace, mult_time. '\
|
||||
'Available args for "-o" are : dump_str_log, keep_alive, trigger, traffic_plot, op_plot. '\
|
||||
'Use "-m" to specify the number of producers on the DHT.'\
|
||||
'Use "-m" to specify the number of producers on the DHT. '\
|
||||
'Use "-e" to specify the number of values to put on the DHT.')
|
||||
|
||||
args = parser.parse_args()
|
||||
@@ -170,6 +186,7 @@ if __name__ == '__main__':
|
||||
wb = WorkBench(args.ifname, args.virtual_locs, args.node_num, loss=args.loss,
|
||||
delay=args.delay, disable_ipv4=args.disable_ipv4,
|
||||
disable_ipv6=args.disable_ipv6)
|
||||
wb.create_virtual_net()
|
||||
bootstrap = wb.get_bootstrap()
|
||||
|
||||
bs_dht_log_enabled = False
|
||||
@@ -187,7 +204,6 @@ if __name__ == '__main__':
|
||||
bs_dht_log_enabled = True
|
||||
bootstrap.front().enableLogging()
|
||||
|
||||
wb.create_virtual_net()
|
||||
bootstrap.resize(1)
|
||||
print("Launching", wb.node_num, "nodes (", wb.clusters, "clusters of", wb.node_per_loc, "nodes)")
|
||||
|
||||
@@ -206,6 +222,10 @@ if __name__ == '__main__':
|
||||
PerformanceTest(args.test, wb, test_opt).run()
|
||||
elif args.data_persistence:
|
||||
PersistenceTest(args.test, wb, test_opt).run()
|
||||
elif args.pht:
|
||||
if args.m:
|
||||
test_opt.update({ 'num_keys' : args.m })
|
||||
PhtTest(args.test, wb, test_opt).run()
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
+236
-176
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015-2016 Savoir-faire Linux Inc.
|
||||
# Copyright (c) 2015-2022 Savoir-faire Linux Inc.
|
||||
# Author(s): Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
# Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
#
|
||||
@@ -25,11 +25,13 @@ import time
|
||||
import threading
|
||||
import queue
|
||||
import re
|
||||
import traceback
|
||||
|
||||
import ipaddress
|
||||
import netifaces
|
||||
import numpy as np
|
||||
from pyroute2.netns.process.proxy import NSPopen
|
||||
from pyroute2 import NSPopen
|
||||
import msgpack
|
||||
|
||||
from opendht import *
|
||||
|
||||
@@ -37,8 +39,6 @@ from opendht import *
|
||||
# useful functions
|
||||
b_space_join = lambda *l: b' '.join(map(bytes, l))
|
||||
|
||||
# TODO: find where token "notifyend" gets printed... Or switch to MSGPACK for
|
||||
# serialisation of packets between both processes.
|
||||
class DhtNetworkSubProcess(NSPopen):
|
||||
"""
|
||||
Handles communication with DhtNetwork sub process.
|
||||
@@ -47,20 +47,21 @@ class DhtNetworkSubProcess(NSPopen):
|
||||
process' stdout until it finds 'DhtNetworkSubProcess.NOTIFY_TOKEN' token,
|
||||
therefor, waits for the sub process to spawn.
|
||||
"""
|
||||
# Packet types
|
||||
REQUEST = 'DhtNetworkSubProcess.request'
|
||||
ANSWER = 'DhtNetworkSubProcess.answer'
|
||||
OUT = 'DhtNetworkSubProcess.out'
|
||||
|
||||
# requests
|
||||
NODE_PUT_REQ = b"np"
|
||||
NEW_NODE_REQ = b"nn"
|
||||
REMOVE_NODE_REQ = b"rn"
|
||||
SHUTDOWN_NODE_REQ = b"sdn"
|
||||
SHUTDOWN_REPLACE_NODE_REQ = b'sdrn'
|
||||
SHUTDOWN_CLUSTER_REQ = b"sdc"
|
||||
DUMP_STORAGE_REQ = b"strl"
|
||||
MESSAGE_STATS = b"gms"
|
||||
|
||||
|
||||
# tokens
|
||||
NOTIFY_TOKEN = 'notify'
|
||||
NOTIFY_END_TOKEN = 'notifyend'
|
||||
PING_REQ = "p"
|
||||
NODE_PUT_REQ = "np" # "np <hash> <value>"
|
||||
NEW_NODE_REQ = "nn" # "nn"
|
||||
REMOVE_NODE_REQ = "rn" # "rn <id0>[ <id1>[ id2[...]]]"
|
||||
SHUTDOWN_NODE_REQ = "sdn" # "sdn <id0>[ <id1>[ id2[...]]]"
|
||||
SHUTDOWN_REPLACE_NODE_REQ = "sdrn" # "sdn <id0>[ <id1>[ id2[...]]]"
|
||||
SHUTDOWN_CLUSTER_REQ = "sdc" # "sdc"
|
||||
DUMP_STORAGE_REQ = "strl" # "strl"
|
||||
MESSAGE_STATS = "gms" # "gms"
|
||||
|
||||
def __init__(self, ns, cmd, quit=False, **kwargs):
|
||||
super(DhtNetworkSubProcess, self).__init__(ns, cmd, **kwargs)
|
||||
@@ -70,11 +71,11 @@ class DhtNetworkSubProcess(NSPopen):
|
||||
self._quit = quit
|
||||
self._lock = threading.Condition()
|
||||
self._in_queue = queue.Queue()
|
||||
self._out_queue = queue.Queue()
|
||||
self._callbacks = {}
|
||||
self._tid = 0
|
||||
|
||||
# starting thread
|
||||
self._thread = threading.Thread(target=self._communicate)
|
||||
self._thread.daemon = True
|
||||
self._thread.start()
|
||||
|
||||
def __repr__(self):
|
||||
@@ -92,42 +93,38 @@ class DhtNetworkSubProcess(NSPopen):
|
||||
"""
|
||||
Communication thread. This reads and writes to the sub process.
|
||||
"""
|
||||
ENCODING = 'utf-8'
|
||||
sleep_time = 0.1
|
||||
stdin_line, stdout_line = '', ''
|
||||
|
||||
# first read of process living. Expecting NOTIFY_TOKEN
|
||||
while DhtNetworkSubProcess.NOTIFY_TOKEN not in stdout_line:
|
||||
stdout_line = self.stdout.readline().decode()
|
||||
time.sleep(sleep_time)
|
||||
|
||||
with self._lock:
|
||||
self._out_queue.put(stdout_line)
|
||||
|
||||
while not self._quit:
|
||||
with self._lock:
|
||||
try:
|
||||
stdin_line = self._in_queue.get_nowait()
|
||||
packet = self._in_queue.get_nowait()
|
||||
|
||||
# sending data to sub process
|
||||
self.stdin.write(stdin_line if isinstance(stdin_line, bytes) else
|
||||
bytes(str(stdin_line), encoding=ENCODING))
|
||||
self.stdin.write(packet)
|
||||
self.stdin.flush()
|
||||
except queue.Empty:
|
||||
#waiting for next stdin req to send
|
||||
self._lock.wait(timeout=sleep_time)
|
||||
pass
|
||||
|
||||
# reading response from sub process
|
||||
for stdout_line in iter(self.stdout.readline, b''):
|
||||
stdout_line = stdout_line.decode().replace('\n', '')
|
||||
if stdout_line:
|
||||
with self._lock:
|
||||
self._out_queue.put(stdout_line)
|
||||
# reading from sub process
|
||||
out_string = ''
|
||||
for p in msgpack.Unpacker(self.stdout):
|
||||
if isinstance(p, dict):
|
||||
self._process_packet(p)
|
||||
else:
|
||||
# Some non-msgpack data could slip into the stream. We
|
||||
# have to treat those as characters.
|
||||
out_string += chr(p)
|
||||
if out_string:
|
||||
print(out_string)
|
||||
|
||||
#waiting for next stdin req to send
|
||||
self._lock.wait(timeout=sleep_time)
|
||||
|
||||
with self._lock:
|
||||
self._lock.notify()
|
||||
|
||||
def stop_communicating(self):
|
||||
def _stop_communicating(self):
|
||||
"""
|
||||
Stops the I/O thread from communicating with the subprocess.
|
||||
"""
|
||||
@@ -142,12 +139,12 @@ class DhtNetworkSubProcess(NSPopen):
|
||||
Notifies thread and sub process to terminate. This is blocking call
|
||||
until the sub process finishes.
|
||||
"""
|
||||
self.stop_communicating()
|
||||
self._stop_communicating()
|
||||
self.send_signal(signal.SIGINT);
|
||||
self.wait()
|
||||
self.release()
|
||||
|
||||
def send(self, msg):
|
||||
def _send(self, msg):
|
||||
"""
|
||||
Send data to sub process.
|
||||
"""
|
||||
@@ -155,83 +152,76 @@ class DhtNetworkSubProcess(NSPopen):
|
||||
self._in_queue.put(msg)
|
||||
self._lock.notify()
|
||||
|
||||
def getline(self):
|
||||
def _process_packet(self, p):
|
||||
"""
|
||||
Read line from sub process.
|
||||
|
||||
@return: A line on sub process' stdout.
|
||||
@rtype : str
|
||||
Process msgpack packet received from
|
||||
"""
|
||||
line = ''
|
||||
with self._lock:
|
||||
try:
|
||||
line = self._out_queue.get_nowait()
|
||||
except queue.Empty:
|
||||
pass
|
||||
return line
|
||||
if not b'tid' in p:
|
||||
DhtNetwork.Log.err('Bad packet...')
|
||||
try:
|
||||
self._callbacks[p[b'tid']](p)
|
||||
except KeyError:
|
||||
DhtNetwork.Log.err('Unknown tid...')
|
||||
|
||||
def getlinesUntilNotify(self, answer_cb=None):
|
||||
"""
|
||||
Reads the stdout queue until a proper notification is given by the sub
|
||||
process.
|
||||
|
||||
@param answer_cb: Callback to call when an answer is given after notify.
|
||||
The function takes a list of strings as argument.
|
||||
@type answer_cb: function
|
||||
"""
|
||||
notified = False
|
||||
answer = []
|
||||
while True:
|
||||
out = self.getline()
|
||||
if out.split(' ')[0] == DhtNetworkSubProcess.NOTIFY_TOKEN:
|
||||
notified = True
|
||||
elif notified and out.split(' ')[0] == DhtNetworkSubProcess.NOTIFY_END_TOKEN:
|
||||
if answer_cb:
|
||||
answer_cb(answer)
|
||||
break
|
||||
elif notified:
|
||||
answer.append(out)
|
||||
elif out:
|
||||
yield out
|
||||
else:
|
||||
time.sleep(0.1)
|
||||
|
||||
def _sendRequest(self, request, answer_cb=None):
|
||||
def _sendRequest(self, request, tid, done_cb):
|
||||
"""
|
||||
Sends a request to the sub network and wait for output.
|
||||
|
||||
@param request: The serialized request.
|
||||
@type request: bytes
|
||||
@type request: Msgpack object
|
||||
"""
|
||||
self.send(request + b'\n')
|
||||
for line in self.getlinesUntilNotify(answer_cb=answer_cb):
|
||||
DhtNetwork.log(line)
|
||||
self._callbacks[tid] = done_cb
|
||||
self._send(request)
|
||||
|
||||
def sendGetMessageStats(self):
|
||||
def sendPing(self, done_cb=None):
|
||||
"""Sends a ping request to the DhtNetworkSubProcess.
|
||||
|
||||
@param done_cb: The callback to be executed when we get a response. This
|
||||
function takes a boolean "success" as parameter.
|
||||
@type done_cb: Function
|
||||
"""
|
||||
self._tid += 1
|
||||
def dcb(packet):
|
||||
try:
|
||||
done_cb(packet[b'success'])
|
||||
except KeyError:
|
||||
done_cb(False)
|
||||
self._sendRequest(msgpack.packb({
|
||||
DhtNetworkSubProcess.REQUEST : True,
|
||||
'tid' : self._tid,
|
||||
'req' : DhtNetworkSubProcess.PING_REQ
|
||||
}), self._tid, dcb)
|
||||
|
||||
def sendGetMessageStats(self, done_cb=None):
|
||||
"""
|
||||
Sends DhtNetwork sub process statistics request about nodes messages
|
||||
sent.
|
||||
|
||||
@param done_cb: A function taking as parameter the returned list of
|
||||
stats.
|
||||
@type done_cb: function
|
||||
|
||||
@return: A list [num_nodes, ping, find, get, put, listen].
|
||||
@rtype : list
|
||||
"""
|
||||
stats = []
|
||||
def cb(answer):
|
||||
"""
|
||||
Callback fed to getlinesUntilNotify made to recover answer from the
|
||||
DhtNetwork sub process.
|
||||
self._tid += 1
|
||||
def dcb(packet):
|
||||
nonlocal done_cb
|
||||
if not done_cb:
|
||||
return
|
||||
try:
|
||||
stats = packet[b'stats']
|
||||
done_cb([] if not isinstance(stats, list) else done_cb(stats))
|
||||
except KeyError:
|
||||
done_cb([])
|
||||
self._sendRequest(msgpack.packb({
|
||||
DhtNetworkSubProcess.REQUEST : True,
|
||||
'tid' : self._tid,
|
||||
'req' : DhtNetworkSubProcess.MESSAGE_STATS
|
||||
}), self._tid, dcb)
|
||||
|
||||
@param answer: the list of lines answered by the sub process.
|
||||
@type answer: function
|
||||
"""
|
||||
nonlocal stats
|
||||
if answer:
|
||||
stats = [int(v) for v in re.findall("[0-9]+", answer.pop())]
|
||||
|
||||
self._sendRequest(DhtNetworkSubProcess.MESSAGE_STATS, answer_cb=cb)
|
||||
return stats
|
||||
|
||||
def sendNodePutRequest(self, _hash, value):
|
||||
def sendClusterPutRequest(self, _hash, value, done_cb=None):
|
||||
"""
|
||||
Sends a put operation request.
|
||||
|
||||
@@ -239,40 +229,89 @@ class DhtNetworkSubProcess(NSPopen):
|
||||
@type _hash: bytes.
|
||||
@param value: the value.
|
||||
@type value: bytes.
|
||||
@param done_cb: A function taking as parameter a boolean "success".
|
||||
@type done_cb: function
|
||||
"""
|
||||
self._sendRequest(b_space_join(DhtNetworkSubProcess.NODE_PUT_REQ, _hash,
|
||||
value))
|
||||
self._tid += 1
|
||||
def dcb(packet):
|
||||
nonlocal done_cb
|
||||
if not done_cb:
|
||||
return
|
||||
try:
|
||||
done_cb(packet[b'success'])
|
||||
except KeyError:
|
||||
done_cb(False)
|
||||
self._sendRequest(msgpack.packb({
|
||||
DhtNetworkSubProcess.REQUEST : True,
|
||||
'tid' : self._tid,
|
||||
'req' : DhtNetworkSubProcess.NODE_PUT_REQ,
|
||||
'hash' : _hash,
|
||||
'value' : value
|
||||
}), self._tid, dcb)
|
||||
|
||||
def sendNodesRequest(self, request, ids=b''):
|
||||
def sendClusterRequest(self, request, ids=[], done_cb=None):
|
||||
"""
|
||||
Send request to a list of nodes or the whole cluster.
|
||||
|
||||
@param request: The request. Possible values are:
|
||||
DhtNetworkSubProcess.NODE_PUT_REQ
|
||||
DhtNetworkSubProcess.REMOVE_NODE_REQ
|
||||
DhtNetworkSubProcess.SHUTDOWN_NODE_REQ
|
||||
DhtNetworkSubProcess.SHUTDOWN_REPLACE_NODE_REQ
|
||||
DhtNetworkSubProcess.SHUTDOWN_CLUSTER_REQ
|
||||
DhtNetworkSubProcess.DUMP_STORAGE_REQ
|
||||
DhtNetworkSubProcess.MESSAGE_STATS
|
||||
@type request: bytes
|
||||
@param ids: The list of ids concerned by the request.
|
||||
@type ids: list
|
||||
"""
|
||||
self._sendRequest(b_space_join(request, b_space_join(*ids)))
|
||||
self._tid += 1
|
||||
def dcb(packet):
|
||||
nonlocal done_cb
|
||||
if not done_cb:
|
||||
return
|
||||
try:
|
||||
done_cb(packet[b'success'])
|
||||
except KeyError:
|
||||
done_cb(False)
|
||||
self._sendRequest(msgpack.packb({
|
||||
DhtNetworkSubProcess.REQUEST : True,
|
||||
'tid' : self._tid,
|
||||
'req' : request,
|
||||
'ids' : ids
|
||||
}), self._tid, dcb)
|
||||
|
||||
|
||||
class DhtNetwork(object):
|
||||
nodes = []
|
||||
|
||||
@staticmethod
|
||||
def log(*to_print):
|
||||
class Log(object):
|
||||
BOLD = "\033[1m"
|
||||
NORMAL = "\033[0m"
|
||||
print('%s[DhtNetwork-%s]%s' % (BOLD, DhtNetwork.iface, NORMAL), ':' , *to_print, file=sys.stderr)
|
||||
WHITE = "\033[97m"
|
||||
RED = "\033[31m"
|
||||
YELLOW = "\033[33m"
|
||||
|
||||
@staticmethod
|
||||
def _log_with_color(*to_print, color=None):
|
||||
color = color if color else DhtNetwork.Log.WHITE
|
||||
print('%s%s[DhtNetwork-%s]%s%s' %
|
||||
(DhtNetwork.Log.BOLD, color, DhtNetwork.iface, DhtNetwork.Log.NORMAL, color),
|
||||
*to_print, DhtNetwork.Log.NORMAL, file=sys.stderr)
|
||||
|
||||
@staticmethod
|
||||
def log(*to_print):
|
||||
DhtNetwork.Log._log_with_color(*to_print, color=DhtNetwork.Log.WHITE)
|
||||
|
||||
@staticmethod
|
||||
def warn(*to_print):
|
||||
DhtNetwork.Log._log_with_color(*to_print, color=DhtNetwork.Log.YELLOW)
|
||||
|
||||
@staticmethod
|
||||
def err(*to_print):
|
||||
DhtNetwork.Log._log_with_color(*to_print, color=DhtNetwork.Log.RED)
|
||||
|
||||
@staticmethod
|
||||
def run_node(ip4, ip6, p, bootstrap=[], is_bootstrap=False):
|
||||
DhtNetwork.log("run_node", ip4, ip6, p, bootstrap)
|
||||
DhtNetwork.Log.log("run_node", ip4, ip6, p, bootstrap)
|
||||
n = DhtRunner()
|
||||
n.run(ipv4=ip4 if ip4 else "", ipv6=ip6 if ip6 else "", port=p, is_bootstrap=is_bootstrap)
|
||||
for b in bootstrap:
|
||||
@@ -297,7 +336,7 @@ class DhtNetwork(object):
|
||||
self.ip6 = ip6 if ip6 else ips[1]
|
||||
self.bootstrap = bootstrap
|
||||
if first_bootstrap:
|
||||
DhtNetwork.log("Starting bootstrap node")
|
||||
DhtNetwork.Log.log("Starting bootstrap node")
|
||||
self.nodes.append(DhtNetwork.run_node(self.ip4, self.ip6, self.port, self.bootstrap, is_bootstrap=True))
|
||||
self.bootstrap = [(self.ip4, str(self.port))]
|
||||
self.port += 1
|
||||
@@ -309,6 +348,9 @@ class DhtNetwork(object):
|
||||
return self.nodes[0][1]
|
||||
|
||||
def get(self, i=None):
|
||||
if not self.nodes:
|
||||
return None
|
||||
|
||||
if i is None:
|
||||
l = list(self.nodes)
|
||||
random.shuffle(l)
|
||||
@@ -321,14 +363,13 @@ class DhtNetwork(object):
|
||||
for n in self.nodes:
|
||||
if n[1].getNodeId() == id:
|
||||
return n
|
||||
else:
|
||||
return None
|
||||
return None
|
||||
|
||||
def launch_node(self):
|
||||
n = DhtNetwork.run_node(self.ip4, self.ip6, self.port, self.bootstrap)
|
||||
self.nodes.append(n)
|
||||
if not self.bootstrap:
|
||||
DhtNetwork.log("Using fallback bootstrap", self.ip4, self.port)
|
||||
DhtNetwork.Log.log("Using fallback bootstrap", self.ip4, self.port)
|
||||
self.bootstrap = [(self.ip4, str(self.port))]
|
||||
self.port += 1
|
||||
return n
|
||||
@@ -346,25 +387,25 @@ class DhtNetwork(object):
|
||||
lock = threading.Condition()
|
||||
def shutdown_cb():
|
||||
nonlocal lock
|
||||
DhtNetwork.log('Done.')
|
||||
DhtNetwork.Log.log('Done.')
|
||||
with lock:
|
||||
lock.notify()
|
||||
|
||||
if not self.nodes:
|
||||
return
|
||||
elif id is not None:
|
||||
elif id:
|
||||
n = self.getNodeInfoById(id)
|
||||
if n:
|
||||
if shutdown:
|
||||
with lock:
|
||||
DhtNetwork.log('Waiting for node to shutdown... ')
|
||||
DhtNetwork.Log.log('Waiting for node to shutdown... ')
|
||||
n[1].shutdown(shutdown_cb)
|
||||
lock.wait()
|
||||
if last_msg_stats:
|
||||
last_msg_stats.append(self.getMessageStats())
|
||||
n[1].join()
|
||||
self.nodes.remove(n)
|
||||
DhtNetwork.log(id, 'deleted !')
|
||||
DhtNetwork.Log.log(id, 'deleted !')
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
@@ -385,11 +426,11 @@ class DhtNetwork(object):
|
||||
if n == l:
|
||||
return
|
||||
if n > l:
|
||||
DhtNetwork.log("Launching", n-l, "nodes", self.ip4, self.ip6)
|
||||
DhtNetwork.Log.log("Launching", n-l, "nodes", self.ip4, self.ip6)
|
||||
for i in range(l, n):
|
||||
self.launch_node()
|
||||
else:
|
||||
DhtNetwork.log("Ending", l-n, "nodes", self.ip4, self.ip6)
|
||||
DhtNetwork.Log.log("Ending", l-n, "nodes", self.ip4, self.ip6)
|
||||
#random.shuffle(self.nodes)
|
||||
for i in range(n, l):
|
||||
self.end_node()
|
||||
@@ -409,30 +450,39 @@ if __name__ == '__main__':
|
||||
lock = threading.Condition()
|
||||
quit = False
|
||||
|
||||
def notify_benchmark(answer=[]):
|
||||
sys.stdout.write('%s\n' % DhtNetworkSubProcess.NOTIFY_TOKEN)
|
||||
for line in answer:
|
||||
sys.stdout.write(str(line)+'\n')
|
||||
sys.stdout.write('%s\n' % DhtNetworkSubProcess.NOTIFY_END_TOKEN)
|
||||
sys.stdout.flush()
|
||||
def send_msgpack_packet(packet):
|
||||
sys.stdout.buffer.write(packet)
|
||||
sys.stdout.buffer.flush()
|
||||
|
||||
def listen_to_mother_nature(stdin, q):
|
||||
def notify_benchmark(packet, success):
|
||||
"""Notifies the benchmark when an operation has been completed.
|
||||
|
||||
@param success: If the operation has been successful
|
||||
@type success: boolean
|
||||
@param packet: The packet we are providing an answer for.
|
||||
@type packet: dict
|
||||
"""
|
||||
send_msgpack_packet(msgpack.packb({
|
||||
DhtNetworkSubProcess.ANSWER : True,
|
||||
'tid' : packet[b'tid'],
|
||||
'success' : success
|
||||
}))
|
||||
|
||||
def send_stats(packet, stats):
|
||||
send_msgpack_packet(msgpack.packb({
|
||||
DhtNetworkSubProcess.ANSWER : True,
|
||||
'tid' : packet[b'tid'],
|
||||
'stats' : stats
|
||||
}))
|
||||
|
||||
def listen_to_mother_nature(q):
|
||||
global quit
|
||||
|
||||
def parse_req(req):
|
||||
split_req = req.split(' ')
|
||||
|
||||
op = split_req[0]
|
||||
hashes = [this_hash.replace('\n', '').encode() for this_hash in split_req[1:]]
|
||||
|
||||
return (op, hashes)
|
||||
|
||||
while not quit:
|
||||
req = stdin.readline()
|
||||
parsed_req = parse_req(req)
|
||||
q.put(parsed_req)
|
||||
with lock:
|
||||
lock.notify()
|
||||
for p in msgpack.Unpacker(sys.stdin.buffer.raw):
|
||||
if isinstance(p, dict) and DhtNetworkSubProcess.REQUEST.encode() in p:
|
||||
with lock:
|
||||
q.put(p)
|
||||
lock.notify()
|
||||
|
||||
def handler(signum, frame):
|
||||
global quit
|
||||
@@ -466,48 +516,52 @@ if __name__ == '__main__':
|
||||
net.resize(args.node_num)
|
||||
|
||||
q = queue.Queue()
|
||||
t = threading.Thread(target=listen_to_mother_nature, args=(sys.stdin, q))
|
||||
t = threading.Thread(target=listen_to_mother_nature, args=tuple([q]))
|
||||
t.daemon = True
|
||||
t.start()
|
||||
|
||||
notify_benchmark()
|
||||
|
||||
msg_stats = []
|
||||
|
||||
with lock:
|
||||
while not quit:
|
||||
lock.wait()
|
||||
try:
|
||||
req,req_args = q.get_nowait()
|
||||
packet = q.get_nowait()
|
||||
except queue.Empty:
|
||||
pass
|
||||
lock.wait()
|
||||
else:
|
||||
NODE_PUT_REQ = DhtNetworkSubProcess.NODE_PUT_REQ.decode()
|
||||
NEW_NODE_REQ = DhtNetworkSubProcess.NEW_NODE_REQ.decode()
|
||||
REMOVE_NODE_REQ = DhtNetworkSubProcess.REMOVE_NODE_REQ.decode()
|
||||
SHUTDOWN_NODE_REQ = DhtNetworkSubProcess.SHUTDOWN_NODE_REQ.decode()
|
||||
SHUTDOWN_REPLACE_NODE_REQ = DhtNetworkSubProcess.SHUTDOWN_REPLACE_NODE_REQ.decode()
|
||||
SHUTDOWN_CLUSTER_REQ = DhtNetworkSubProcess.SHUTDOWN_CLUSTER_REQ.decode()
|
||||
DUMP_STORAGE_REQ = DhtNetworkSubProcess.DUMP_STORAGE_REQ.decode()
|
||||
MESSAGE_STATS = DhtNetworkSubProcess.MESSAGE_STATS.decode()
|
||||
NODE_PUT_REQ = DhtNetworkSubProcess.NODE_PUT_REQ
|
||||
NEW_NODE_REQ = DhtNetworkSubProcess.NEW_NODE_REQ
|
||||
REMOVE_NODE_REQ = DhtNetworkSubProcess.REMOVE_NODE_REQ
|
||||
SHUTDOWN_NODE_REQ = DhtNetworkSubProcess.SHUTDOWN_NODE_REQ
|
||||
SHUTDOWN_REPLACE_NODE_REQ = DhtNetworkSubProcess.SHUTDOWN_REPLACE_NODE_REQ
|
||||
SHUTDOWN_CLUSTER_REQ = DhtNetworkSubProcess.SHUTDOWN_CLUSTER_REQ
|
||||
DUMP_STORAGE_REQ = DhtNetworkSubProcess.DUMP_STORAGE_REQ
|
||||
MESSAGE_STATS = DhtNetworkSubProcess.MESSAGE_STATS
|
||||
|
||||
if req in [SHUTDOWN_NODE_REQ,
|
||||
SHUTDOWN_REPLACE_NODE_REQ,
|
||||
REMOVE_NODE_REQ]:
|
||||
def delete_request(req, n):
|
||||
req = packet[b'req'].decode()
|
||||
success = True
|
||||
if req in [SHUTDOWN_NODE_REQ, SHUTDOWN_REPLACE_NODE_REQ, REMOVE_NODE_REQ]:
|
||||
def delete_request(req, nid):
|
||||
global msg_stats
|
||||
if not nid:
|
||||
return
|
||||
if req == SHUTDOWN_NODE_REQ:
|
||||
net.end_node(id=n, shutdown=True, last_msg_stats=msg_stats)
|
||||
net.end_node(id=nid, shutdown=True, last_msg_stats=msg_stats)
|
||||
elif req == SHUTDOWN_REPLACE_NODE_REQ:
|
||||
net.replace_node(id=n, shutdown=True, last_msg_stats=msg_stats)
|
||||
net.replace_node(id=nid, shutdown=True, last_msg_stats=msg_stats)
|
||||
elif req == REMOVE_NODE_REQ:
|
||||
net.end_node(id=n, last_msg_stats=msg_stats)
|
||||
net.end_node(id=nid, last_msg_stats=msg_stats)
|
||||
|
||||
if len(req) > 0:
|
||||
for n in req_args:
|
||||
delete_request(req, n)
|
||||
nodes = packet[b'ids']
|
||||
if nodes:
|
||||
for nid in nodes:
|
||||
delete_request(req, nid)
|
||||
else:
|
||||
delete_request(req, net.get().getNodeId())
|
||||
n = net.get()
|
||||
if n:
|
||||
delete_request(req, n.getNodeId())
|
||||
else:
|
||||
success = False
|
||||
elif req == SHUTDOWN_CLUSTER_REQ:
|
||||
for n in net.nodes:
|
||||
net.end_node(id=n[2], shutdown=True, last_msg_stats=msg_stats)
|
||||
@@ -515,20 +569,26 @@ if __name__ == '__main__':
|
||||
elif req == NEW_NODE_REQ:
|
||||
net.launch_node()
|
||||
elif req == NODE_PUT_REQ:
|
||||
_hash, v = req_args[:2]
|
||||
net.get().put(InfoHash(_hash), Value(v))
|
||||
|
||||
_hash = packet[b'hash']
|
||||
v = packet[b'value']
|
||||
n = net.get()
|
||||
if n:
|
||||
n.put(InfoHash(_hash), Value(v))
|
||||
else:
|
||||
success = False
|
||||
elif req == DUMP_STORAGE_REQ:
|
||||
for n in [m[1] for m in net.nodes if m[1].getNodeId() in req_args]:
|
||||
hashes = packet[b'ids']
|
||||
for n in [m[1] for m in net.nodes if m[1].getNodeId() in hashes]:
|
||||
net.log(n.getStorageLog())
|
||||
elif MESSAGE_STATS in req:
|
||||
elif req == MESSAGE_STATS:
|
||||
stats = sum([np.array(x) for x in [net.getMessageStats()]+msg_stats])
|
||||
notify_benchmark(answer=[stats])
|
||||
send_stats(packet, [int(_) for _ in stats])
|
||||
msg_stats.clear()
|
||||
continue
|
||||
notify_benchmark()
|
||||
notify_benchmark(packet, success)
|
||||
except Exception as e:
|
||||
DhtNetwork.log(e)
|
||||
traceback.print_tb(e.__traceback__)
|
||||
print(type(e).__name__+':', e, file=sys.stderr)
|
||||
finally:
|
||||
if net:
|
||||
net.resize(0)
|
||||
+364
-121
@@ -1,8 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2015 Savoir-Faire Linux Inc.
|
||||
# Copyright (C) 2015-2022 Savoir-Faire Linux Inc.
|
||||
# Author(s): Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
# Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
|
||||
import sys
|
||||
import os
|
||||
import threading
|
||||
import random
|
||||
@@ -10,10 +11,17 @@ import string
|
||||
import time
|
||||
import subprocess
|
||||
import re
|
||||
import traceback
|
||||
import collections
|
||||
|
||||
from matplotlib.ticker import FuncFormatter
|
||||
import math
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.ticker import FuncFormatter
|
||||
import networkx as nx
|
||||
from networkx.drawing.nx_agraph import graphviz_layout
|
||||
|
||||
|
||||
from opendht import *
|
||||
from dht.network import DhtNetwork, DhtNetworkSubProcess
|
||||
@@ -27,8 +35,40 @@ bit_format = None
|
||||
Kbit_format = FuncFormatter(lambda x, pos: '%1.1f' % (x*1024**-1) + 'Kb')
|
||||
Mbit_format = FuncFormatter(lambda x, pos: '%1.1f' % (x*1024**-2) + 'Mb')
|
||||
|
||||
def random_str_val(size=1024):
|
||||
"""Creates a random string value of specified size.
|
||||
|
||||
@param size: Size, in bytes, of the value.
|
||||
@type size: int
|
||||
|
||||
@return: Random string value
|
||||
@rtype : str
|
||||
"""
|
||||
return ''.join(random.choice(string.hexdigits) for _ in range(size))
|
||||
|
||||
|
||||
def random_hash():
|
||||
return InfoHash(''.join(random.SystemRandom().choice(string.hexdigits) for _ in range(40)).encode())
|
||||
"""Creates random InfoHash.
|
||||
"""
|
||||
return InfoHash(random_str_val(size=40).encode())
|
||||
|
||||
def timer(f, *args):
|
||||
"""
|
||||
Start a timer which count time taken for execute function f
|
||||
|
||||
@param f : Function to time
|
||||
@type f : function
|
||||
|
||||
@param args : Arguments of the function f
|
||||
@type args : list
|
||||
|
||||
@rtype : timer
|
||||
@return : Time taken by the function f
|
||||
"""
|
||||
start = time.time()
|
||||
f(*args)
|
||||
|
||||
return time.time() - start
|
||||
|
||||
def reset_before_test(featureTestMethod):
|
||||
"""
|
||||
@@ -70,6 +110,21 @@ def display_plot(yvals, xvals=None, yformatter=None, display_time=3, **kwargs):
|
||||
plt.plot(yvals, **kwargs)
|
||||
plt.pause(display_time)
|
||||
|
||||
def display_traffic_plot(ifname):
|
||||
"""Displays the traffic plot for a given interface name.
|
||||
|
||||
@param ifname: Interface name.
|
||||
@type ifname: string
|
||||
"""
|
||||
ydata = []
|
||||
xdata = []
|
||||
# warning: infinite loop
|
||||
interval = 2
|
||||
for rate in iftop_traffic_data(ifname, interval=interval):
|
||||
ydata.append(rate)
|
||||
xdata.append((xdata[-1] if len(xdata) > 0 else 0) + interval)
|
||||
display_plot(ydata, xvals=xdata, yformatter=Kbit_format, color='blue')
|
||||
|
||||
def iftop_traffic_data(ifname, interval=2, rate_type='send_receive'):
|
||||
"""
|
||||
Generator (yields data) function collecting traffic data from iftop
|
||||
@@ -125,13 +180,13 @@ def iftop_traffic_data(ifname, interval=2, rate_type='send_receive'):
|
||||
###########
|
||||
|
||||
class FeatureTest(object):
|
||||
done = 0
|
||||
lock = None
|
||||
|
||||
"""
|
||||
This is a base test.
|
||||
"""
|
||||
|
||||
done = 0
|
||||
lock = None
|
||||
|
||||
def __init__(self, test, workbench):
|
||||
"""
|
||||
@param test: The test string indicating the test to run. This string is
|
||||
@@ -143,6 +198,7 @@ class FeatureTest(object):
|
||||
"""
|
||||
self._test = test
|
||||
self._workbench = workbench
|
||||
self._bootstrap = self._workbench.get_bootstrap()
|
||||
|
||||
def _reset(self):
|
||||
"""
|
||||
@@ -156,7 +212,155 @@ class FeatureTest(object):
|
||||
def run(self):
|
||||
raise NotImplementedError('This method must be implemented.')
|
||||
|
||||
##################################
|
||||
# PHT #
|
||||
##################################
|
||||
|
||||
class PhtTest(FeatureTest):
|
||||
"""TODO
|
||||
"""
|
||||
|
||||
indexEntries = None
|
||||
prefix = None
|
||||
key = None
|
||||
|
||||
def __init__(self, test, workbench, opts):
|
||||
"""
|
||||
@param test: is one of the following:
|
||||
- 'insert': indexes a considerable amount of data in
|
||||
the PHT structure.
|
||||
TODO
|
||||
@type test: string
|
||||
|
||||
@param opts: Dictionnary containing options for the test. Allowed
|
||||
options are:
|
||||
- 'num_keys': this specifies the number of keys to insert
|
||||
in the PHT during the test.
|
||||
@type opts: dict
|
||||
"""
|
||||
super(PhtTest, self).__init__(test, workbench)
|
||||
self._num_keys = opts['num_keys'] if 'num_keys' in opts else 32
|
||||
self._timer = True if 'timer' in opts else False
|
||||
|
||||
def _reset(self):
|
||||
super(PhtTest, self)._reset()
|
||||
PhtTest.indexEntries = []
|
||||
|
||||
@staticmethod
|
||||
def lookupCb(vals, prefix):
|
||||
PhtTest.indexEntries = list(vals)
|
||||
PhtTest.prefix = prefix.decode()
|
||||
DhtNetwork.log('Index name: <todo>')
|
||||
DhtNetwork.log('Leaf prefix:', prefix)
|
||||
for v in vals:
|
||||
DhtNetwork.log('[ENTRY]:', v)
|
||||
|
||||
@staticmethod
|
||||
def lookupDoneCb(ok):
|
||||
DhtNetwork.log('[LOOKUP]:', PhtTest.key, "--", "success!" if ok else "Fail...")
|
||||
with FeatureTest.lock:
|
||||
FeatureTest.lock.notify()
|
||||
|
||||
@staticmethod
|
||||
def insertDoneCb(ok):
|
||||
DhtNetwork.log('[INSERT]:', PhtTest.key, "--", "success!" if ok else "Fail...")
|
||||
with FeatureTest.lock:
|
||||
FeatureTest.lock.notify()
|
||||
|
||||
@staticmethod
|
||||
def drawTrie(trie_dict):
|
||||
"""
|
||||
Draws the trie structure of the PHT from dictionnary.
|
||||
|
||||
@param trie_dict: Dictionnary of index entries (prefix -> entry).
|
||||
@type trie_dict: dict
|
||||
"""
|
||||
prefixes = list(trie_dict.keys())
|
||||
if len(prefixes) == 0:
|
||||
return
|
||||
|
||||
edges = list([])
|
||||
for prefix in prefixes:
|
||||
for i in range(-1, len(prefix)-1):
|
||||
u = prefix[:i+1]
|
||||
x = ("." if i == -1 else u, u+"0")
|
||||
y = ("." if i == -1 else u, u+"1")
|
||||
if x not in edges:
|
||||
edges.append(x)
|
||||
if y not in edges:
|
||||
edges.append(y)
|
||||
|
||||
# TODO: use a binary tree position layout...
|
||||
# UPDATE : In a better way [change lib]
|
||||
G = nx.Graph(sorted(edges, key=lambda x: len(x[0])))
|
||||
plt.title("PHT: Tree")
|
||||
pos=graphviz_layout(G,prog='dot')
|
||||
nx.draw(G, pos, with_labels=True, node_color='white')
|
||||
plt.show()
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
if self._test == 'insert':
|
||||
self._insertTest()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
finally:
|
||||
self._bootstrap.resize(1)
|
||||
|
||||
###########
|
||||
# Tests #
|
||||
###########
|
||||
|
||||
@reset_before_test
|
||||
def _insertTest(self):
|
||||
"""TODO: Docstring for _massIndexTest.
|
||||
"""
|
||||
bootstrap = self._bootstrap
|
||||
bootstrap.resize(2)
|
||||
dht = bootstrap.get(1)
|
||||
|
||||
NUM_DIG = max(math.log(self._num_keys, 2)/4, 5) # at least 5 digit keys.
|
||||
keyspec = collections.OrderedDict([('foo', NUM_DIG)])
|
||||
pht = Pht(b'foo_index', keyspec, dht)
|
||||
|
||||
DhtNetwork.log('PHT has',
|
||||
pht.MAX_NODE_ENTRY_COUNT,
|
||||
'node'+ ('s' if pht.MAX_NODE_ENTRY_COUNT > 1 else ''),
|
||||
'per leaf bucket.')
|
||||
keys = [{
|
||||
[_ for _ in keyspec.keys()][0] :
|
||||
''.join(random.SystemRandom().choice(string.hexdigits)
|
||||
for _ in range(NUM_DIG)).encode()
|
||||
} for n in range(self._num_keys)]
|
||||
all_entries = {}
|
||||
|
||||
# Index all entries.
|
||||
for key in keys:
|
||||
PhtTest.key = key
|
||||
with FeatureTest.lock:
|
||||
time_taken = timer(pht.insert, key, IndexValue(random_hash()), PhtTest.insertDoneCb)
|
||||
if self._timer:
|
||||
DhtNetwork.log('This insert step took : ', time_taken, 'second')
|
||||
FeatureTest.lock.wait()
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
# Recover entries now that the trie is complete.
|
||||
for key in keys:
|
||||
PhtTest.key = key
|
||||
with FeatureTest.lock:
|
||||
time_taken = timer(pht.lookup, key, PhtTest.lookupCb, PhtTest.lookupDoneCb)
|
||||
if self._timer:
|
||||
DhtNetwork.log('This lookup step took : ', time_taken, 'second')
|
||||
FeatureTest.lock.wait()
|
||||
|
||||
all_entries[PhtTest.prefix] = [e.__str__()
|
||||
for e in PhtTest.indexEntries]
|
||||
|
||||
for p in all_entries.keys():
|
||||
DhtNetwork.log('All entries under prefix', p, ':')
|
||||
DhtNetwork.log(all_entries[p])
|
||||
PhtTest.drawTrie(all_entries)
|
||||
|
||||
##################################
|
||||
# DHT #
|
||||
@@ -164,7 +368,7 @@ class FeatureTest(object):
|
||||
|
||||
class DhtFeatureTest(FeatureTest):
|
||||
"""
|
||||
This is base test. A method run() implementation is required.
|
||||
This is a base dht test.
|
||||
"""
|
||||
#static variables used by class callbacks
|
||||
successfullTransfer = lambda lv,fv: len(lv) == len(fv)
|
||||
@@ -173,7 +377,6 @@ class DhtFeatureTest(FeatureTest):
|
||||
|
||||
def __init__(self, test, workbench):
|
||||
super(DhtFeatureTest, self).__init__(test, workbench)
|
||||
self.bootstrap = self._workbench.get_bootstrap()
|
||||
|
||||
def _reset(self):
|
||||
super(DhtFeatureTest, self)._reset()
|
||||
@@ -183,15 +386,15 @@ class DhtFeatureTest(FeatureTest):
|
||||
@staticmethod
|
||||
def getcb(value):
|
||||
vstr = value.__str__()[:100]
|
||||
DhtNetwork.log('[GET]: %s' % vstr + ("..." if len(vstr) > 100 else ""))
|
||||
DhtNetwork.Log.log('[GET]: %s' % vstr + ("..." if len(vstr) > 100 else ""))
|
||||
DhtFeatureTest.foreignValues.append(value)
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def putDoneCb(ok, nodes):
|
||||
if not ok:
|
||||
DhtNetwork.log("[PUT]: failed!")
|
||||
with FeatureTest.lock:
|
||||
if not ok:
|
||||
DhtNetwork.Log.log("[PUT]: failed!")
|
||||
FeatureTest.done -= 1
|
||||
FeatureTest.lock.notify()
|
||||
|
||||
@@ -199,7 +402,7 @@ class DhtFeatureTest(FeatureTest):
|
||||
def getDoneCb(ok, nodes):
|
||||
with FeatureTest.lock:
|
||||
if not ok:
|
||||
DhtNetwork.log("[GET]: failed!")
|
||||
DhtNetwork.Log.log("[GET]: failed!")
|
||||
else:
|
||||
for node in nodes:
|
||||
if not node.getNode().isExpired():
|
||||
@@ -210,10 +413,10 @@ class DhtFeatureTest(FeatureTest):
|
||||
def _dhtPut(self, producer, _hash, *values):
|
||||
with FeatureTest.lock:
|
||||
for val in values:
|
||||
vstr = val.__str__()[:100]
|
||||
DhtNetwork.log('[PUT]:', _hash.toString(), '->', vstr + ("..." if len(vstr) > 100 else ""))
|
||||
FeatureTest.done += 1
|
||||
producer.put(_hash, val, DhtFeatureTest.putDoneCb)
|
||||
vstr = val.__str__()[:100]
|
||||
DhtNetwork.Log.log('[PUT]:', _hash.toString(), '->', vstr + ("..." if len(vstr) > 100 else ""))
|
||||
FeatureTest.done += 1
|
||||
producer.put(_hash, val, DhtFeatureTest.putDoneCb)
|
||||
while FeatureTest.done > 0:
|
||||
FeatureTest.lock.wait()
|
||||
|
||||
@@ -222,7 +425,7 @@ class DhtFeatureTest(FeatureTest):
|
||||
DhtFeatureTest.foreignNodes = []
|
||||
with FeatureTest.lock:
|
||||
FeatureTest.done += 1
|
||||
DhtNetwork.log('[GET]:', _hash.toString())
|
||||
DhtNetwork.Log.log('[GET]:', _hash.toString())
|
||||
consumer.get(_hash, DhtFeatureTest.getcb, DhtFeatureTest.getDoneCb)
|
||||
while FeatureTest.done > 0:
|
||||
FeatureTest.lock.wait()
|
||||
@@ -234,7 +437,6 @@ class DhtFeatureTest(FeatureTest):
|
||||
for n in DhtFeatureTest.foreignNodes:
|
||||
nodes.add(n)
|
||||
|
||||
|
||||
class PersistenceTest(DhtFeatureTest):
|
||||
"""
|
||||
This tests persistence of data on the network.
|
||||
@@ -292,8 +494,8 @@ class PersistenceTest(DhtFeatureTest):
|
||||
config.setNodeId(InfoHash(_hash_str.encode()))
|
||||
n = DhtRunner()
|
||||
n.run(config=config)
|
||||
n.bootstrap(self.bootstrap.ip4,
|
||||
str(self.bootstrap.port))
|
||||
n.bootstrap(self._bootstrap.ip4,
|
||||
str(self._bootstrap.port))
|
||||
DhtNetwork.log('Node','['+_hash_str+']',
|
||||
'started around', _hash.toString().decode()
|
||||
if n.isRunning() else
|
||||
@@ -302,24 +504,24 @@ class PersistenceTest(DhtFeatureTest):
|
||||
trigger_nodes.append(n)
|
||||
|
||||
def _result(self, local_values, new_nodes):
|
||||
bootstrap = self.bootstrap
|
||||
bootstrap = self._bootstrap
|
||||
if not DhtFeatureTest.successfullTransfer(local_values, DhtFeatureTest.foreignValues):
|
||||
DhtNetwork.log('[GET]: Only %s on %s values persisted.' %
|
||||
DhtNetwork.Log.log('[GET]: Only %s on %s values persisted.' %
|
||||
(len(DhtFeatureTest.foreignValues), len(local_values)))
|
||||
else:
|
||||
DhtNetwork.log('[GET]: All values successfully persisted.')
|
||||
DhtNetwork.Log.log('[GET]: All values successfully persisted.')
|
||||
if DhtFeatureTest.foreignValues:
|
||||
if new_nodes:
|
||||
DhtNetwork.log('Values are newly found on:')
|
||||
DhtNetwork.Log.log('Values are newly found on:')
|
||||
for node in new_nodes:
|
||||
DhtNetwork.log(node)
|
||||
DhtNetwork.Log.log(node)
|
||||
if self._dump_storage:
|
||||
DhtNetwork.log('Dumping all storage log from '\
|
||||
DhtNetwork.Log.log('Dumping all storage log from '\
|
||||
'hosting nodes.')
|
||||
for proc in self._workbench.procs:
|
||||
proc.sendNodesRequest(DhtNetworkSubProcess.DUMP_STORAGE_REQ, DhtFeatureTest.foreignNodes)
|
||||
proc.sendClusterRequest(DhtNetworkSubProcess.DUMP_STORAGE_REQ, DhtFeatureTest.foreignNodes)
|
||||
else:
|
||||
DhtNetwork.log("Values didn't reach new hosting nodes after shutdown.")
|
||||
DhtNetwork.Log.log("Values didn't reach new hosting nodes after shutdown.")
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
@@ -334,13 +536,14 @@ class PersistenceTest(DhtFeatureTest):
|
||||
else:
|
||||
raise NameError("This test is not defined '" + self._test + "'")
|
||||
except Exception as e:
|
||||
print(e)
|
||||
traceback.print_tb(e.__traceback__)
|
||||
print(type(e).__name__+':', e, file=sys.stderr)
|
||||
finally:
|
||||
if self._traffic_plot or self._op_plot:
|
||||
plot_fname = "traffic-plot"
|
||||
print('plot saved to', plot_fname)
|
||||
plt.savefig(plot_fname)
|
||||
self.bootstrap.resize(1)
|
||||
self._bootstrap.resize(1)
|
||||
|
||||
###########
|
||||
# Tests #
|
||||
@@ -353,71 +556,89 @@ class PersistenceTest(DhtFeatureTest):
|
||||
"""
|
||||
trigger_nodes = []
|
||||
wb = self._workbench
|
||||
bootstrap = self.bootstrap
|
||||
bootstrap = self._bootstrap
|
||||
# Value representing an ICE packet. Each ICE packet is around 1KB.
|
||||
VALUE_SIZE = 1024
|
||||
NUM_VALUES = self._num_values/wb.node_num if self._num_values else 5
|
||||
nr_values = NUM_VALUES * wb.node_num
|
||||
nr_nodes = wb.node_num
|
||||
nr_nodes_cv = threading.Condition()
|
||||
num_values_per_hash = self._num_values/wb.node_num if self._num_values else 5
|
||||
|
||||
values = [b''.join(random.choice(string.hexdigits).encode() for _ in range(VALUE_SIZE)) for __ in range(NUM_VALUES)]
|
||||
# nodes and values counters
|
||||
total_nr_values = 0
|
||||
nr_nodes = wb.node_num
|
||||
op_cv = threading.Condition()
|
||||
|
||||
# values string in string format. Used for sending cluster request.
|
||||
hashes = [random_hash() for _ in range(wb.node_num)]
|
||||
|
||||
# initial set of values
|
||||
i = 0
|
||||
for h in hashes:
|
||||
self._dhtPut(bootstrap.front(), h, *[Value(v) for v in values])
|
||||
print("at: ", i)
|
||||
i += 1
|
||||
|
||||
def normalBehavior(do, t, log=None):
|
||||
nonlocal nr_values
|
||||
def normalBehavior(do, t):
|
||||
nonlocal total_nr_values, op_cv
|
||||
while True:
|
||||
do()
|
||||
time.sleep(random.choice(range(t)))
|
||||
with op_cv:
|
||||
do()
|
||||
time.sleep(random.uniform(0.0, float(t)))
|
||||
|
||||
def putRequest():
|
||||
nonlocal hashes, values, nr_values
|
||||
nr_values += 1
|
||||
DhtNetwork.log("Random value put on the DHT.", "(now "+ str(nr_values)+" values on the dht)")
|
||||
random.choice(wb.procs).sendNodePutRequest(random.choice(hashes).toString(), random.choice(values))
|
||||
puts = threading.Thread(target=normalBehavior, args=(putRequest, 30))
|
||||
nonlocal hashes, VALUE_SIZE, total_nr_values
|
||||
lock = threading.Condition()
|
||||
def dcb(success):
|
||||
nonlocal total_nr_values, lock
|
||||
if success:
|
||||
total_nr_values += 1
|
||||
DhtNetwork.Log.log("INFO: "+ str(total_nr_values)+" values put on the dht since begining")
|
||||
with lock:
|
||||
lock.notify()
|
||||
with lock:
|
||||
DhtNetwork.Log.warn("Random value put on the DHT...")
|
||||
random.choice(wb.procs).sendClusterPutRequest(random.choice(hashes).toString(),
|
||||
random_str_val(size=VALUE_SIZE).encode(),
|
||||
done_cb=dcb)
|
||||
lock.wait()
|
||||
|
||||
puts = threading.Thread(target=normalBehavior, args=(putRequest, 30.0/wb.node_num))
|
||||
puts.daemon = True
|
||||
puts.start()
|
||||
|
||||
def newNodeRequest():
|
||||
nonlocal nr_nodes
|
||||
with nr_nodes_cv:
|
||||
lock = threading.Condition()
|
||||
def dcb(success):
|
||||
nonlocal nr_nodes, lock
|
||||
nr_nodes += 1
|
||||
DhtNetwork.log("Node joining the DHT.", "(now "+str(nr_nodes)+" nodes on the dht)")
|
||||
nr_nodes_cv.notify()
|
||||
random.choice(wb.procs).sendNodesRequest(DhtNetworkSubProcess.NEW_NODE_REQ)
|
||||
connections = threading.Thread(target=normalBehavior, args=(newNodeRequest, 1*60))
|
||||
DhtNetwork.Log.log("INFO: now "+str(nr_nodes)+" nodes on the dht")
|
||||
with lock:
|
||||
lock.notify()
|
||||
with lock:
|
||||
DhtNetwork.Log.warn("Node joining...")
|
||||
random.choice(wb.procs).sendClusterRequest(DhtNetworkSubProcess.NEW_NODE_REQ, done_cb=dcb)
|
||||
lock.wait()
|
||||
|
||||
connections = threading.Thread(target=normalBehavior, args=(newNodeRequest, 1*50.0/wb.node_num))
|
||||
connections.daemon = True
|
||||
connections.start()
|
||||
|
||||
def shutdownNodeRequest():
|
||||
nonlocal nr_nodes
|
||||
with nr_nodes_cv:
|
||||
nr_nodes -= 1
|
||||
DhtNetwork.log("Node quitting the DHT.", "(now "+str(nr_nodes)+" nodes on the dht)")
|
||||
nr_nodes_cv.notify()
|
||||
random.choice(wb.procs).sendNodesRequest(DhtNetworkSubProcess.SHUTDOWN_NODE_REQ)
|
||||
shutdowns = threading.Thread(target=normalBehavior, args=(shutdownNodeRequest, 1*60))
|
||||
lock = threading.Condition()
|
||||
def dcb(success):
|
||||
nonlocal nr_nodes, lock
|
||||
if success:
|
||||
nr_nodes -= 1
|
||||
DhtNetwork.Log.log("INFO: now "+str(nr_nodes)+" nodes on the dht")
|
||||
else:
|
||||
DhtNetwork.Log.err("Oops.. No node to shutodwn.")
|
||||
|
||||
with lock:
|
||||
lock.notify()
|
||||
with lock:
|
||||
DhtNetwork.Log.warn("Node shutting down...")
|
||||
random.choice(wb.procs).sendClusterRequest(DhtNetworkSubProcess.SHUTDOWN_NODE_REQ, done_cb=dcb)
|
||||
lock.wait()
|
||||
|
||||
shutdowns = threading.Thread(target=normalBehavior, args=(shutdownNodeRequest, 1*60.0/wb.node_num))
|
||||
shutdowns.daemon = True
|
||||
shutdowns.start()
|
||||
|
||||
for h in hashes:
|
||||
self._trigger_dp(trigger_nodes, h)
|
||||
|
||||
if self._traffic_plot:
|
||||
ydata = []
|
||||
xdata = []
|
||||
# warning: infinite loop
|
||||
interval = 2
|
||||
for rate in iftop_traffic_data("br"+wb.ifname, interval=interval):
|
||||
ydata.append(rate)
|
||||
xdata.append((xdata[-1] if len(xdata) > 0 else 0) + interval)
|
||||
display_plot(ydata, xvals=xdata, yformatter=Kbit_format, color='blue')
|
||||
display_traffic_plot('br'+wb.ifname)
|
||||
else:
|
||||
# blocks in matplotlib thread
|
||||
while True:
|
||||
@@ -430,7 +651,7 @@ class PersistenceTest(DhtFeatureTest):
|
||||
It uses Dht shutdown call from the API to gracefuly finish the nodes one
|
||||
after the other.
|
||||
"""
|
||||
bootstrap = self.bootstrap
|
||||
bootstrap = self._bootstrap
|
||||
|
||||
ops_count = []
|
||||
|
||||
@@ -447,36 +668,54 @@ class PersistenceTest(DhtFeatureTest):
|
||||
self._dhtGet(consumer, myhash)
|
||||
if not DhtFeatureTest.successfullTransfer(local_values, DhtFeatureTest.foreignValues):
|
||||
if DhtFeatureTest.foreignValues:
|
||||
DhtNetwork.log('[GET]: Only ', len(DhtFeatureTest.foreignValues) ,' on ',
|
||||
DhtNetwork.Log.log('[GET]: Only ', len(DhtFeatureTest.foreignValues) ,' on ',
|
||||
len(local_values), ' values successfully put.')
|
||||
else:
|
||||
DhtNetwork.log('[GET]: 0 values successfully put')
|
||||
DhtNetwork.Log.log('[GET]: 0 values successfully put')
|
||||
|
||||
|
||||
if DhtFeatureTest.foreignValues and DhtFeatureTest.foreignNodes:
|
||||
DhtNetwork.log('Values are found on :')
|
||||
DhtNetwork.Log.log('Values are found on :')
|
||||
for node in DhtFeatureTest.foreignNodes:
|
||||
DhtNetwork.log(node)
|
||||
DhtNetwork.Log.log(node)
|
||||
|
||||
for _ in range(max(1, int(self._workbench.node_num/32))):
|
||||
DhtNetwork.log('Removing all nodes hosting target values...')
|
||||
DhtNetwork.Log.log('Removing all nodes hosting target values...')
|
||||
cluster_ops_count = 0
|
||||
for proc in self._workbench.procs:
|
||||
DhtNetwork.log('[REMOVE]: sending shutdown request to', proc)
|
||||
proc.sendNodesRequest(
|
||||
DhtNetwork.Log.log('[REMOVE]: sending shutdown request to', proc)
|
||||
lock = threading.Condition()
|
||||
def dcb(success):
|
||||
nonlocal lock
|
||||
if not success:
|
||||
DhtNetwork.Log.err("Failed to shutdown.")
|
||||
with lock:
|
||||
lock.notify()
|
||||
|
||||
with lock:
|
||||
proc.sendClusterRequest(
|
||||
DhtNetworkSubProcess.SHUTDOWN_NODE_REQ,
|
||||
DhtFeatureTest.foreignNodes
|
||||
)
|
||||
DhtNetwork.log('sending message stats request')
|
||||
stats = proc.sendGetMessageStats()
|
||||
cluster_ops_count += sum(stats[1:])
|
||||
DhtNetwork.log("5 seconds wait...")
|
||||
DhtFeatureTest.foreignNodes,
|
||||
done_cb=dcb
|
||||
)
|
||||
lock.wait()
|
||||
DhtNetwork.Log.log('sending message stats request')
|
||||
def msg_dcb(stats):
|
||||
nonlocal cluster_ops_count, lock
|
||||
if stats:
|
||||
cluster_ops_count += sum(stats[1:])
|
||||
with lock:
|
||||
lock.notify()
|
||||
with lock:
|
||||
proc.sendGetMessageStats(done_cb=msg_dcb)
|
||||
lock.wait()
|
||||
DhtNetwork.Log.log("5 seconds wait...")
|
||||
time.sleep(5)
|
||||
ops_count.append(cluster_ops_count/self._workbench.node_num)
|
||||
|
||||
# checking if values were transfered to new nodes
|
||||
foreignNodes_before_delete = DhtFeatureTest.foreignNodes
|
||||
DhtNetwork.log('[GET]: trying to fetch persistent values')
|
||||
DhtNetwork.Log.log('[GET]: trying to fetch persistent values')
|
||||
self._dhtGet(consumer, myhash)
|
||||
new_nodes = set(DhtFeatureTest.foreignNodes) - set(foreignNodes_before_delete)
|
||||
|
||||
@@ -485,7 +724,11 @@ class PersistenceTest(DhtFeatureTest):
|
||||
if self._op_plot:
|
||||
display_plot(ops_count, color='blue')
|
||||
else:
|
||||
DhtNetwork.log("[GET]: either couldn't fetch values or nodes hosting values...")
|
||||
DhtNetwork.Log.log("[GET]: either couldn't fetch values or nodes hosting values...")
|
||||
|
||||
if traffic_plot_thread:
|
||||
print("Traffic plot running for ever. Ctrl-c for stopping it.")
|
||||
traffic_plot_thread.join()
|
||||
|
||||
@reset_before_test
|
||||
def _replaceClusterTest(self):
|
||||
@@ -494,7 +737,7 @@ class PersistenceTest(DhtFeatureTest):
|
||||
"""
|
||||
clusters = 8
|
||||
|
||||
bootstrap = self.bootstrap
|
||||
bootstrap = self._bootstrap
|
||||
|
||||
bootstrap.resize(3)
|
||||
consumer = bootstrap.get(1)
|
||||
@@ -507,16 +750,16 @@ class PersistenceTest(DhtFeatureTest):
|
||||
self._dhtGet(consumer, myhash)
|
||||
initial_nodes = DhtFeatureTest.foreignNodes
|
||||
|
||||
DhtNetwork.log('Replacing', clusters, 'random clusters successively...')
|
||||
DhtNetwork.Log.log('Replacing', clusters, 'random clusters successively...')
|
||||
for n in range(clusters):
|
||||
i = random.randint(0, len(self._workbench.procs)-1)
|
||||
proc = self._workbench.procs[i]
|
||||
DhtNetwork.log('Replacing', proc)
|
||||
proc.sendNodesRequest(DhtNetworkSubProcess.SHUTDOWN_CLUSTER_REQ)
|
||||
DhtNetwork.Log.log('Replacing', proc)
|
||||
proc.sendClusterRequest(DhtNetworkSubProcess.SHUTDOWN_CLUSTER_REQ)
|
||||
self._workbench.stop_cluster(i)
|
||||
self._workbench.start_cluster(i)
|
||||
|
||||
DhtNetwork.log('[GET]: trying to fetch persistent values')
|
||||
DhtNetwork.Log.log('[GET]: trying to fetch persistent values')
|
||||
self._dhtGet(consumer, myhash)
|
||||
new_nodes = set(DhtFeatureTest.foreignNodes) - set(initial_nodes)
|
||||
|
||||
@@ -531,17 +774,17 @@ class PersistenceTest(DhtFeatureTest):
|
||||
minutes for the nodes to trigger storage maintenance.
|
||||
"""
|
||||
trigger_nodes = []
|
||||
bootstrap = self.bootstrap
|
||||
bootstrap = self._bootstrap
|
||||
|
||||
N_PRODUCERS = self._num_producers if self._num_values else 16
|
||||
DP_TIMEOUT = 1
|
||||
|
||||
hashes = []
|
||||
|
||||
# Generating considerable amount of values of size 1KB.
|
||||
# Generating considerable amount of values of size 1KB.
|
||||
VALUE_SIZE = 1024
|
||||
NUM_VALUES = self._num_values if self._num_values else 50
|
||||
values = [Value(''.join(random.choice(string.hexdigits) for _ in range(VALUE_SIZE)).encode()) for _ in range(NUM_VALUES)]
|
||||
values = [Value(random_str_val(size=VALUE_SIZE).encode()) for _ in range(NUM_VALUES)]
|
||||
|
||||
bootstrap.resize(N_PRODUCERS+2)
|
||||
consumer = bootstrap.get(N_PRODUCERS+1)
|
||||
@@ -555,21 +798,21 @@ class PersistenceTest(DhtFeatureTest):
|
||||
nodes = set([])
|
||||
self._gottaGetThemAllPokeNodes(consumer, hashes, nodes=nodes)
|
||||
|
||||
DhtNetwork.log("Values are found on:")
|
||||
DhtNetwork.Log.log("Values are found on:")
|
||||
for n in nodes:
|
||||
DhtNetwork.log(n)
|
||||
DhtNetwork.Log.log(n)
|
||||
|
||||
DhtNetwork.log("Creating 8 nodes around all of these hashes...")
|
||||
DhtNetwork.Log.log("Creating 8 nodes around all of these hashes...")
|
||||
for _hash in hashes:
|
||||
self._trigger_dp(trigger_nodes, _hash, count=8)
|
||||
|
||||
DhtNetwork.log('Waiting', DP_TIMEOUT+1, 'minutes for normal storage maintenance.')
|
||||
DhtNetwork.Log.log('Waiting', DP_TIMEOUT+1, 'minutes for normal storage maintenance.')
|
||||
time.sleep((DP_TIMEOUT+1)*60)
|
||||
|
||||
DhtNetwork.log('Deleting old nodes from previous search.')
|
||||
DhtNetwork.Log.log('Deleting old nodes from previous search.')
|
||||
for proc in self._workbench.procs:
|
||||
DhtNetwork.log('[REMOVE]: sending delete request to', proc)
|
||||
proc.sendNodesRequest(
|
||||
DhtNetwork.Log.log('[REMOVE]: sending delete request to', proc)
|
||||
proc.sendClusterRequest(
|
||||
DhtNetworkSubProcess.REMOVE_NODE_REQ,
|
||||
nodes)
|
||||
|
||||
@@ -610,9 +853,10 @@ class PerformanceTest(DhtFeatureTest):
|
||||
else:
|
||||
raise NameError("This test is not defined '" + self._test + "'")
|
||||
except Exception as e:
|
||||
print(e)
|
||||
traceback.print_tb(e.__traceback__)
|
||||
print(type(e).__name__+':', e, file=sys.stderr)
|
||||
finally:
|
||||
self.bootstrap.resize(1)
|
||||
self._bootstrap.resize(1)
|
||||
|
||||
|
||||
###########
|
||||
@@ -624,7 +868,7 @@ class PerformanceTest(DhtFeatureTest):
|
||||
"""
|
||||
Tests for performance of the DHT doing multiple get() operation.
|
||||
"""
|
||||
bootstrap = self.bootstrap
|
||||
bootstrap = self._bootstrap
|
||||
|
||||
plt.ion()
|
||||
|
||||
@@ -649,7 +893,7 @@ class PerformanceTest(DhtFeatureTest):
|
||||
|
||||
def getcb(v):
|
||||
nonlocal bootstrap
|
||||
DhtNetwork.log("found", v)
|
||||
DhtNetwork.Log.log("found", v)
|
||||
return True
|
||||
|
||||
def donecb(ok, nodes, start):
|
||||
@@ -657,7 +901,7 @@ class PerformanceTest(DhtFeatureTest):
|
||||
t = time.time()-start
|
||||
with lock:
|
||||
if not ok:
|
||||
DhtNetwork.log("failed !")
|
||||
DhtNetwork.Log.log("failed !")
|
||||
times.append(t)
|
||||
done -= 1
|
||||
lock.notify()
|
||||
@@ -689,7 +933,7 @@ class PerformanceTest(DhtFeatureTest):
|
||||
for n in range(10):
|
||||
self._workbench.replace_cluster()
|
||||
plt.pause(2)
|
||||
DhtNetwork.log("Getting 50 random hashes succesively.")
|
||||
DhtNetwork.Log.log("Getting 50 random hashes succesively.")
|
||||
for i in range(50):
|
||||
with lock:
|
||||
for _ in range(1):
|
||||
@@ -713,8 +957,7 @@ class PerformanceTest(DhtFeatureTest):
|
||||
deleting around the target hash.
|
||||
"""
|
||||
|
||||
|
||||
bootstrap = self.bootstrap
|
||||
bootstrap = self._bootstrap
|
||||
|
||||
bootstrap.resize(3)
|
||||
consumer = bootstrap.get(1)
|
||||
@@ -725,28 +968,28 @@ class PerformanceTest(DhtFeatureTest):
|
||||
|
||||
for _ in range(max(1, int(self._workbench.node_num/32))):
|
||||
self._dhtGet(consumer, myhash)
|
||||
DhtNetwork.log("Waiting 15 seconds...")
|
||||
DhtNetwork.Log.log("Waiting 15 seconds...")
|
||||
time.sleep(15)
|
||||
|
||||
self._dhtPut(producer, myhash, *local_values)
|
||||
|
||||
#checking if values were transfered
|
||||
self._dhtGet(consumer, myhash)
|
||||
DhtNetwork.log('Values are found on :')
|
||||
DhtNetwork.Log.log('Values are found on :')
|
||||
for node in DhtFeatureTest.foreignNodes:
|
||||
DhtNetwork.log(node)
|
||||
DhtNetwork.Log.log(node)
|
||||
|
||||
if not DhtFeatureTest.successfullTransfer(local_values, DhtFeatureTest.foreignValues):
|
||||
if DhtFeatureTest.foreignValues:
|
||||
DhtNetwork.log('[GET]: Only ', len(DhtFeatureTest.foreignValues) ,' on ',
|
||||
DhtNetwork.Log.log('[GET]: Only ', len(DhtFeatureTest.foreignValues) ,' on ',
|
||||
len(local_values), ' values successfully put.')
|
||||
else:
|
||||
DhtNetwork.log('[GET]: 0 values successfully put')
|
||||
DhtNetwork.Log.log('[GET]: 0 values successfully put')
|
||||
|
||||
DhtNetwork.log('Removing all nodes hosting target values...')
|
||||
DhtNetwork.Log.log('Removing all nodes hosting target values...')
|
||||
for proc in self._workbench.procs:
|
||||
DhtNetwork.log('[REMOVE]: sending shutdown request to', proc)
|
||||
proc.sendNodesRequest(
|
||||
DhtNetwork.Log.log('[REMOVE]: sending shutdown request to', proc)
|
||||
proc.sendClusterRequest(
|
||||
DhtNetworkSubProcess.SHUTDOWN_NODE_REQ,
|
||||
DhtFeatureTest.foreignNodes
|
||||
)
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015-2016 Savoir-faire Linux Inc.
|
||||
# Copyright (c) 2015-2022 Savoir-faire Linux Inc.
|
||||
# Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
@@ -15,107 +15,253 @@
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import argparse, subprocess
|
||||
from pyroute2 import NDB, NSPopen
|
||||
|
||||
from pyroute2 import IPDB, NetNS
|
||||
from pyroute2.netns.process.proxy import NSPopen
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description='Creates a virtual network topology for testing')
|
||||
parser.add_argument('-i', '--ifname', help='interface name', default='ethdht')
|
||||
parser.add_argument('-n', '--ifnum', type=int, help='number of isolated interfaces to create', default=1)
|
||||
parser.add_argument('-r', '--remove', help='remove instead of adding network interfaces', action="store_true")
|
||||
parser.add_argument('-l', '--loss', help='simulated packet loss (percent)', type=int, default=0)
|
||||
parser.add_argument('-d', '--delay', help='simulated latency (ms)', type=int, default=0)
|
||||
parser.add_argument('-4', '--ipv4', help='Enable IPv4', action="store_true")
|
||||
parser.add_argument('-6', '--ipv6', help='Enable IPv6', action="store_true")
|
||||
def int_range(mini, maxi):
|
||||
def check_ifnum(arg):
|
||||
try:
|
||||
ret = int(arg)
|
||||
except ValueError:
|
||||
raise argparse.ArgumentTypeError('must be an integer')
|
||||
if ret > maxi or ret < mini:
|
||||
raise argparse.ArgumentTypeError(
|
||||
f'must be {mini} <= int <= {maxi}'
|
||||
)
|
||||
return ret
|
||||
|
||||
return check_ifnum
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Creates a virtual network topology for testing'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-i', '--ifname', help='interface name', default='ethdht'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-n',
|
||||
'--ifnum',
|
||||
type=int_range(1, 245),
|
||||
help='number of isolated interfaces to create',
|
||||
default=1,
|
||||
)
|
||||
parser.add_argument(
|
||||
'-r',
|
||||
'--remove',
|
||||
help='remove instead of adding network interfaces',
|
||||
action='store_true',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-l',
|
||||
'--loss',
|
||||
help='simulated packet loss (percent)',
|
||||
type=int,
|
||||
default=0,
|
||||
)
|
||||
parser.add_argument(
|
||||
'-d', '--delay', help='simulated latency (ms)', type=int, default=0
|
||||
)
|
||||
parser.add_argument(
|
||||
'-4', '--ipv4', help='Enable IPv4', action='store_true'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-6', '--ipv6', help='Enable IPv6', action='store_true'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-b',
|
||||
'--debug',
|
||||
help='Turn on debug logging and dump topology databases',
|
||||
action='store_true',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-v',
|
||||
'--verbose',
|
||||
help='Turn on verbose output on netns and interfaces operations',
|
||||
action='store_true',
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
local_addr4 = '10.0.42.'
|
||||
local_addr6 = '2001:db9::'
|
||||
brige_name = 'br'+args.ifname
|
||||
bripv4 = f'{local_addr4}1/24'
|
||||
bripv6 = f'{local_addr6}1/64'
|
||||
bridge_name = f'br{args.ifname}'
|
||||
tap_name = f'tap{args.ifname}'
|
||||
veth_names = []
|
||||
namespaces = []
|
||||
ipv4addrs = []
|
||||
ipv6addrs = []
|
||||
for ifn in range(args.ifnum):
|
||||
namespaces.append(f'node{ifn}')
|
||||
veth_names.append(f'{args.ifname}{ifn}')
|
||||
ipv4addrs.append(f'{local_addr4}{ifn+8}/24' if args.ipv4 else None)
|
||||
ipv6addrs.append(f'{local_addr6}{ifn+8}/64' if args.ipv6 else None)
|
||||
|
||||
ip = None
|
||||
try:
|
||||
ip = IPDB()
|
||||
with NDB(log='debug' if args.debug else None) as ndb:
|
||||
if args.remove:
|
||||
# cleanup interfaces
|
||||
for ifn in range(args.ifnum):
|
||||
iface = args.ifname+str(ifn)
|
||||
if iface in ip.interfaces:
|
||||
with ip.interfaces[iface] as i:
|
||||
i.remove()
|
||||
if 'tap'+args.ifname in ip.interfaces:
|
||||
with ip.interfaces['tap'+args.ifname] as i:
|
||||
i.remove()
|
||||
if brige_name in ip.interfaces:
|
||||
with ip.interfaces[brige_name] as i:
|
||||
i.remove()
|
||||
for ifn in range(args.ifnum):
|
||||
netns = NetNS('node'+str(ifn))
|
||||
netns.close()
|
||||
netns.remove()
|
||||
else:
|
||||
for ifn in range(args.ifnum):
|
||||
iface = args.ifname+str(ifn)
|
||||
if not iface in ip.interfaces:
|
||||
ip.create(kind='veth', ifname=iface, peer=iface+'.1').commit()
|
||||
# cleanup interfaces in the main namespace
|
||||
for iface in veth_names + [bridge_name] + [tap_name]:
|
||||
if iface in ndb.interfaces:
|
||||
ndb.interfaces[iface].remove().commit()
|
||||
if args.verbose:
|
||||
print(f'link: del main/{iface}')
|
||||
|
||||
ip.create(kind='tuntap', ifname='tap'+args.ifname, mode='tap').commit()
|
||||
|
||||
with ip.create(kind='bridge', ifname=brige_name) as i:
|
||||
for ifn in range(args.ifnum):
|
||||
iface = args.ifname+str(ifn)
|
||||
i.add_port(ip.interfaces[iface])
|
||||
i.add_port(ip.interfaces['tap'+args.ifname])
|
||||
if args.ipv4:
|
||||
i.add_ip(local_addr4+'1/24')
|
||||
if args.ipv6:
|
||||
i.add_ip(local_addr6+'1/64')
|
||||
i.up()
|
||||
|
||||
with ip.interfaces['tap'+args.ifname] as tap:
|
||||
tap.up()
|
||||
|
||||
for ifn in range(args.ifnum):
|
||||
iface = args.ifname+str(ifn)
|
||||
|
||||
nns = NetNS('node'+str(ifn))
|
||||
iface1 = iface+'.1'
|
||||
with ip.interfaces[iface1] as i:
|
||||
i.net_ns_fd = nns.netns
|
||||
|
||||
with ip.interfaces[iface] as i:
|
||||
i.up()
|
||||
|
||||
ip_ns = IPDB(nl=nns)
|
||||
# cleanup namespaces
|
||||
for nsname in namespaces:
|
||||
try:
|
||||
with ip_ns.interfaces.lo as lo:
|
||||
lo.up()
|
||||
with ip_ns.interfaces[iface1] as i:
|
||||
if args.ipv4:
|
||||
i.add_ip(local_addr4+str(ifn+8)+'/24')
|
||||
if args.ipv6:
|
||||
i.add_ip(local_addr6+str(ifn+8)+'/64')
|
||||
i.up()
|
||||
finally:
|
||||
ip_ns.release()
|
||||
|
||||
nsp = NSPopen(nns.netns, ["tc", "qdisc", "add", "dev", iface1, "root", "netem", "delay", str(args.delay)+"ms", str(int(args.delay/2))+"ms", "loss", str(args.loss)+"%", "25%"], stdout=subprocess.PIPE)
|
||||
#print(nsp.communicate()[0].decode())
|
||||
ndb.netns[nsname].remove().commit()
|
||||
if args.verbose:
|
||||
print(f'netns: del {nsname}')
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
# create ports
|
||||
for veth, nsname, ipv4addr, ipv6addr in zip(
|
||||
veth_names, namespaces, ipv4addrs, ipv6addrs
|
||||
):
|
||||
# create a network namespace and launch NDB for it
|
||||
#
|
||||
# another possible solution could be simply to attach
|
||||
# the namespace to the main NDB instance, but it can
|
||||
# take a lot of memory in case of many interfaces, thus
|
||||
# launch and discard netns NDB instances
|
||||
netns = NDB(
|
||||
log='debug' if args.debug else None,
|
||||
sources=[
|
||||
{
|
||||
'target': 'localhost',
|
||||
'netns': nsname,
|
||||
'kind': 'netns',
|
||||
}
|
||||
],
|
||||
)
|
||||
if args.verbose:
|
||||
print(f'netns: add {nsname}')
|
||||
# create the port and push the peer into the namespace
|
||||
(
|
||||
ndb.interfaces.create(
|
||||
**{
|
||||
'ifname': veth,
|
||||
'kind': 'veth',
|
||||
'state': 'up',
|
||||
'peer': {'ifname': veth, 'net_ns_fd': nsname},
|
||||
}
|
||||
).commit()
|
||||
)
|
||||
if args.verbose:
|
||||
print(f'link: add main/{veth} <-> {nsname}/{veth}')
|
||||
# bring up namespace's loopback
|
||||
(
|
||||
netns.interfaces.wait(ifname='lo', timeout=3)
|
||||
.set('state', 'up')
|
||||
.commit()
|
||||
)
|
||||
if args.verbose:
|
||||
print(f'link: set {nsname}/lo')
|
||||
# bring up the peer
|
||||
with netns.interfaces.wait(ifname=veth, timeout=3) as i:
|
||||
i.set('state', 'up')
|
||||
if args.ipv4:
|
||||
i.add_ip(ipv4addr)
|
||||
if args.ipv6:
|
||||
i.add_ip(ipv6addr)
|
||||
if args.verbose:
|
||||
print(f'link: set {nsname}/{veth}, {ipv4addr}, {ipv6addr}')
|
||||
# disconnect the namespace NDB agent, not removing the NS
|
||||
if args.debug:
|
||||
fname = f'{nsname}-ndb.db'
|
||||
print(f'dump: netns topology database {fname}')
|
||||
netns.schema.backup(fname)
|
||||
netns.close()
|
||||
# set up the emulation QDisc
|
||||
nsp = NSPopen(
|
||||
nsname,
|
||||
[
|
||||
'tc',
|
||||
'qdisc',
|
||||
'add',
|
||||
'dev',
|
||||
veth,
|
||||
'root',
|
||||
'netem',
|
||||
'delay',
|
||||
f'{args.delay}ms',
|
||||
f'{int(args.delay)/2}ms',
|
||||
'loss',
|
||||
f'{args.loss}%',
|
||||
'25%',
|
||||
],
|
||||
stdout=subprocess.PIPE,
|
||||
)
|
||||
nsp.communicate()
|
||||
nsp.wait()
|
||||
nsp.release()
|
||||
if args.verbose:
|
||||
print(
|
||||
f'netem: add {nsname}/{veth}, '
|
||||
f'{args.delay}, {args.loss}'
|
||||
)
|
||||
|
||||
if args.ipv4:
|
||||
subprocess.call(["sysctl", "-w", "net.ipv4.conf."+brige_name+".forwarding=1"])
|
||||
if args.ipv6:
|
||||
subprocess.call(["sysctl", "-w", "net.ipv6.conf."+brige_name+".forwarding=1"])
|
||||
# create the tap
|
||||
#
|
||||
# for some reason we should create the tap inteface first,
|
||||
# and only then bring it up, thus two commit() calls
|
||||
(
|
||||
ndb.interfaces.create(
|
||||
kind='tuntap', ifname=tap_name, mode='tap'
|
||||
)
|
||||
.commit()
|
||||
.set('state', 'up')
|
||||
.commit()
|
||||
)
|
||||
if args.verbose:
|
||||
print(f'link: add main/{tap_name}')
|
||||
|
||||
except Exception as e:
|
||||
print('Error',e)
|
||||
finally:
|
||||
if ip:
|
||||
ip.release()
|
||||
# create the bridge and add all the ports
|
||||
with ndb.interfaces.create(
|
||||
ifname=bridge_name, kind='bridge', state='up'
|
||||
) as i:
|
||||
if args.ipv4:
|
||||
i.add_ip(bripv4)
|
||||
if args.ipv6:
|
||||
i.add_ip(bripv6)
|
||||
for iface in veth_names + [tap_name]:
|
||||
i.add_port(iface)
|
||||
if args.verbose:
|
||||
print(f'link: add main/{bridge_name}, {bripv4}, {bripv6}')
|
||||
|
||||
with open(os.devnull, 'w') as fnull:
|
||||
if args.ipv4:
|
||||
subprocess.call(
|
||||
[
|
||||
'sysctl',
|
||||
'-w',
|
||||
f'net.ipv4.conf.{bridge_name}.forwarding=1',
|
||||
],
|
||||
stdout=fnull,
|
||||
)
|
||||
if args.verbose:
|
||||
print(f'sysctl: set {bridge_name} ipv4 forwarding')
|
||||
if args.ipv6:
|
||||
subprocess.call(
|
||||
[
|
||||
'sysctl',
|
||||
'-w',
|
||||
f'net.ipv6.conf.{bridge_name}.forwarding=1',
|
||||
],
|
||||
stdout=fnull,
|
||||
)
|
||||
if args.verbose:
|
||||
print(f'sysctl: set {bridge_name} ipv4 forwarding')
|
||||
|
||||
if args.debug:
|
||||
fname = 'main-ndb.db'
|
||||
print('dump: the main netns topology database')
|
||||
ndb.schema.backup(fname)
|
||||
Executable
+270
@@ -0,0 +1,270 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
# Author(s): Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os, sys, time, cmd
|
||||
import signal
|
||||
import argparse
|
||||
import logging
|
||||
import logging.handlers
|
||||
|
||||
import opendht as dht
|
||||
|
||||
logger = logging.getLogger('dhtcluster')
|
||||
|
||||
class NodeCluster(object):
|
||||
nodes = []
|
||||
node_uid = 0
|
||||
|
||||
@staticmethod
|
||||
def run_node(ip4, ip6, p, bootstrap=None, is_bootstrap=False, logfile=None):
|
||||
logger.info("Running node on port %s with bootstrap %s", p, bootstrap)
|
||||
n = dht.DhtRunner()
|
||||
n.run(ipv4=ip4 if ip4 else "", ipv6=ip6 if ip6 else "", port=p, is_bootstrap=is_bootstrap)
|
||||
if logfile:
|
||||
n.enableFileLogging(logfile)
|
||||
if bootstrap:
|
||||
n.bootstrap(bootstrap[0], bootstrap[1])
|
||||
time.sleep(.01)
|
||||
return ((ip4, ip6, p), n, id)
|
||||
|
||||
@staticmethod
|
||||
def find_ip(iface):
|
||||
if not iface or iface == 'any':
|
||||
return ('0.0.0.0','::0')
|
||||
|
||||
if_ip4 = netifaces.ifaddresses(iface)[netifaces.AF_INET][0]['addr']
|
||||
if_ip6 = netifaces.ifaddresses(iface)[netifaces.AF_INET6][0]['addr']
|
||||
return (if_ip4, if_ip6)
|
||||
|
||||
def __init__(self, iface=None, ip4=None, ip6=None, port=4000, bootstrap=None, first_bootstrap=False, logfile=None):
|
||||
NodeCluster.iface = iface
|
||||
self.port = port
|
||||
ips = NodeCluster.find_ip(iface)
|
||||
self.logfile = logfile
|
||||
self.ip4 = ip4 if ip4 else ips[0]
|
||||
self.ip6 = ip6 if ip6 else ips[1]
|
||||
self.bootstrap = bootstrap
|
||||
if bootstrap:
|
||||
self.bootstrap = (bootstrap.hostname, str(bootstrap.port) if bootstrap.port else "4222")
|
||||
else:
|
||||
logger.info("Using fallback bootstrap %s %s", self.ip4, self.port)
|
||||
self.bootstrap = ((self.ip4, str(self.port)))
|
||||
if first_bootstrap:
|
||||
logger.info("Starting bootstrap node")
|
||||
self.nodes.append(NodeCluster.run_node(self.ip4, self.ip6, self.port, self.bootstrap, is_bootstrap=True))
|
||||
self.bootstrap = ((self.ip4, str(self.port)))
|
||||
self.port += 1
|
||||
#print(self.ip4, self.ip6, self.port)
|
||||
|
||||
def front(self):
|
||||
return self.nodes[0][1] if self.nodes else None
|
||||
|
||||
def get(self, i):
|
||||
if not self.nodes or i < 0 or i >= len(self.nodes):
|
||||
return None
|
||||
return self.nodes[i][1]
|
||||
|
||||
def getNodeInfoById(self, id=None):
|
||||
if id:
|
||||
for n in self.nodes:
|
||||
if n[1].getNodeId() == id:
|
||||
return n
|
||||
return None
|
||||
|
||||
def launch_node(self):
|
||||
node_logfile = (self.logfile + str(self.node_uid) + '.log') if self.logfile else None
|
||||
n = NodeCluster.run_node(self.ip4, self.ip6, self.port, self.bootstrap, logfile=node_logfile)
|
||||
self.nodes.append(n)
|
||||
self.port += 1
|
||||
self.node_uid += 1
|
||||
return n
|
||||
|
||||
def end_node(self):
|
||||
if not self.nodes:
|
||||
return
|
||||
else:
|
||||
n = self.nodes.pop()
|
||||
n[1].join()
|
||||
return True
|
||||
|
||||
def resize(self, n):
|
||||
n = min(n, 500)
|
||||
l = len(self.nodes)
|
||||
if n == l:
|
||||
return
|
||||
if n > l:
|
||||
logger.info("Launching %d nodes bound on IPv4 %s IPv6 %s", n-l, self.ip4, self.ip6)
|
||||
for i in range(l, n):
|
||||
self.launch_node()
|
||||
else:
|
||||
logger.info("Ending %d nodes", l-n)
|
||||
for i in range(n, l):
|
||||
self.end_node()
|
||||
|
||||
def close(self):
|
||||
self.resize(0)
|
||||
|
||||
def getMessageStats(self):
|
||||
stats = np.array([0,0,0,0,0])
|
||||
for n in self.nodes:
|
||||
stats += np.array(n[1].getNodeMessageStats())
|
||||
stats_list = [len(self.nodes)]
|
||||
stats_list.extend(stats.tolist())
|
||||
return stats_list
|
||||
|
||||
class ClusterShell(cmd.Cmd):
|
||||
intro = 'Welcome to the OpenDHT node cluster control. Type help or ? to list commands.\n'
|
||||
prompt = '>> '
|
||||
net = None
|
||||
node = None
|
||||
log = False
|
||||
def __init__(self, network):
|
||||
super(ClusterShell, self).__init__()
|
||||
self.net = network
|
||||
def setNode(self, node=None, num=0):
|
||||
if node == self.node:
|
||||
return
|
||||
if self.node:
|
||||
self.node.disableLogging()
|
||||
self.node = node
|
||||
if self.node:
|
||||
self.prompt = '('+str(num)+') >> '
|
||||
if self.log:
|
||||
self.node.enableLogging()
|
||||
else:
|
||||
self.prompt = '>> '
|
||||
def do_exit(self, arg):
|
||||
self.close()
|
||||
return True
|
||||
def do_node(self, arg):
|
||||
if not arg:
|
||||
self.setNode()
|
||||
else:
|
||||
nodenum = int(arg)
|
||||
node = self.net.get(nodenum-1)
|
||||
if not node:
|
||||
print("Invalid node number:", nodenum, " (accepted: 1-", len(self.net.nodes), ")")
|
||||
else:
|
||||
self.setNode(node, nodenum)
|
||||
def do_resize(self, arg):
|
||||
if not arg:
|
||||
return
|
||||
try:
|
||||
nodenum = int(arg)
|
||||
self.net.resize(nodenum)
|
||||
except Exception as e:
|
||||
print("Can't resize:", e)
|
||||
def do_ll(self, arg):
|
||||
if self.node:
|
||||
print('Node', self.node.getNodeId().decode())
|
||||
else:
|
||||
print(len(self.net.nodes), 'nodes running.')
|
||||
def do_ls(self, arg):
|
||||
if self.node:
|
||||
print(self.node.getSearchesLog(0))
|
||||
else:
|
||||
print('No node selected.')
|
||||
def do_log(self, arg):
|
||||
if self.node:
|
||||
self.log = not self.log
|
||||
if self.log:
|
||||
self.node.enableLogging()
|
||||
else:
|
||||
self.node.disableLogging()
|
||||
def do_EOF(self, line):
|
||||
self.close()
|
||||
return True
|
||||
def close(self):
|
||||
if self.net:
|
||||
self.net.close()
|
||||
self.net = None
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
from urllib.parse import urlparse
|
||||
net = None
|
||||
run = True
|
||||
def clean_quit():
|
||||
global net, run
|
||||
if run:
|
||||
run = False
|
||||
if net:
|
||||
net.resize(0)
|
||||
net = None
|
||||
def quit_signal(signum, frame):
|
||||
clean_quit()
|
||||
|
||||
try:
|
||||
parser = argparse.ArgumentParser(description='Create a dht network of -n nodes')
|
||||
parser.add_argument('-n', '--node-num', help='number of dht nodes to run', type=int, default=32)
|
||||
parser.add_argument('-I', '--iface', help='local interface to bind', default='any')
|
||||
parser.add_argument('-p', '--port', help='start of port range (port, port+node_num)', type=int, default=4000)
|
||||
parser.add_argument('-b', '--bootstrap', help='bootstrap address')
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
group.add_argument('-d', '--daemonize', help='daemonize process', action='store_true')
|
||||
group.add_argument('-s', '--service', help='service mode (not forking)', action='store_true')
|
||||
parser.add_argument('-l', '--log', help='log file prefix')
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.bootstrap:
|
||||
args.bootstrap = urlparse('dht://'+args.bootstrap)
|
||||
|
||||
# setup logging
|
||||
if args.daemonize or args.service:
|
||||
syslog = logging.handlers.SysLogHandler(address = '/dev/log')
|
||||
syslog.setLevel(logging.DEBUG)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
logger.addHandler(syslog)
|
||||
else:
|
||||
logging.basicConfig(stream=sys.stdout,level=logging.DEBUG)
|
||||
|
||||
# start cluster
|
||||
net = NodeCluster(iface=args.iface, port=args.port, bootstrap=args.bootstrap, logfile=args.log)
|
||||
|
||||
# main loop
|
||||
if args.daemonize:
|
||||
import daemon
|
||||
context = daemon.DaemonContext()
|
||||
context.signal_map = {
|
||||
signal.SIGHUP: 'terminate',
|
||||
signal.SIGTERM: quit_signal,
|
||||
signal.SIGINT: quit_signal,
|
||||
signal.SIGQUIT: quit_signal
|
||||
}
|
||||
with context:
|
||||
net.resize(args.node_num)
|
||||
while net:
|
||||
time.sleep(1)
|
||||
elif args.service:
|
||||
signal.signal(signal.SIGTERM, quit_signal)
|
||||
signal.signal(signal.SIGINT, quit_signal)
|
||||
signal.signal(signal.SIGQUIT, quit_signal)
|
||||
net.resize(args.node_num)
|
||||
while net:
|
||||
time.sleep(1)
|
||||
else:
|
||||
net.resize(args.node_num)
|
||||
ClusterShell(net).cmdloop()
|
||||
except Exception:
|
||||
logger.error("Exception ", exc_info=1)
|
||||
finally:
|
||||
clean_quit()
|
||||
logger.warning("Ending node cluster")
|
||||
for handler in logger.handlers:
|
||||
handler.close()
|
||||
logger.removeHandler(handler)
|
||||
Executable
+78
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016-2022 Savoir-faire Linux Inc.
|
||||
# Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from twisted.web import server, resource
|
||||
from twisted.internet import reactor, endpoints
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import opendht as dht
|
||||
import base64, json
|
||||
|
||||
class DhtServer(resource.Resource):
|
||||
isLeaf = True
|
||||
node = dht.DhtRunner()
|
||||
|
||||
def __init__(self, port, bootstrap):
|
||||
self.node.run(port=port)
|
||||
b_url = urlparse('//'+bootstrap)
|
||||
self.node.bootstrap(b_url.hostname, str(b_url.port) if b_url.port else '4222')
|
||||
|
||||
def render_GET(self, req):
|
||||
uri = req.uri[1:].decode().rsplit('?', 1)[0]
|
||||
h = dht.InfoHash(uri.encode()) if len(uri) == 40 else dht.InfoHash.get(uri)
|
||||
w = dht.Where('WHERE '+''.join(k.decode()+'='+req.args[k][0].decode()+','
|
||||
for k in req.args.keys()
|
||||
if k in [b'id', b'user_type', b'value_type', b'owner', b'seq'])[:-1])
|
||||
print('GET', '"'+uri+'"', h, w)
|
||||
res = self.node.get(h, where=w)
|
||||
req.setHeader(b"content-type", b"application/json")
|
||||
return json.dumps({'{:x}'.format(v.id):{'base64':base64.b64encode(v.data).decode()} for v in res}).encode()
|
||||
|
||||
def render_POST(self, req):
|
||||
uri = req.uri[1:]
|
||||
data = req.args[b'data'][0] if b'data' in req.args else None
|
||||
user_type = req.args[b'user_type'][0].decode() if b'user_type' in req.args else ""
|
||||
try:
|
||||
vid = int(req.args[b'id'][0].decode()) if b'id' in req.args else 0
|
||||
except ValueError:
|
||||
vid = 0
|
||||
if not data and b'base64' in req.args:
|
||||
data = base64.b64decode(req.args[b'base64'][0])
|
||||
h = dht.InfoHash(uri) if len(uri) == 40 else dht.InfoHash.get(uri.decode())
|
||||
print('POST', h, data)
|
||||
req.setHeader(b"content-type", b"application/json")
|
||||
if data:
|
||||
v = dht.Value(data)
|
||||
if vid != 0:
|
||||
v.id = vid
|
||||
v.user_type = user_type
|
||||
self.node.put(h, v)
|
||||
return json.dumps({'success':True}).encode()
|
||||
else:
|
||||
req.setResponseCode(400)
|
||||
return json.dumps({'success':False, 'error':'no data parameter'}).encode()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description='Launch an OpenDHT node with an HTTP control interface')
|
||||
parser.add_argument('-p', '--port', help='OpenDHT port to bind', type=int, default=4222)
|
||||
parser.add_argument('-hp', '--http-port', help='HTTP port to bind', type=int, default=8080)
|
||||
parser.add_argument('-b', '--bootstrap', help='bootstrap address', default="bootstrap.ring.cx:4222")
|
||||
args = parser.parse_args()
|
||||
endpoints.serverFromString(reactor, "tcp:"+str(args.http_port)).listen(server.Site(DhtServer(args.port, args.bootstrap)))
|
||||
reactor.run()
|
||||
Executable
+83
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2022 Savoir-faire Linux Inc.
|
||||
# Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import time
|
||||
import argparse
|
||||
import time
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
|
||||
import opendht as dht
|
||||
|
||||
parser = argparse.ArgumentParser(description='Create a dht network of -n nodes')
|
||||
parser.add_argument('-b', '--bootstrap', help='bootstrap address', default='bootstrap.ring.cx')
|
||||
parser.add_argument('-n', '--num-ops', help='number of concurrent operations on the DHT', type=int, default=8)
|
||||
parser.add_argument('-p', '--period', help='duration between each test (seconds)', type=int, default=60)
|
||||
parser.add_argument('-t', '--timeout', help='timeout for a test to complete (seconds)', type=float, default=15)
|
||||
args = parser.parse_args()
|
||||
|
||||
node1 = dht.DhtRunner()
|
||||
node1.run()
|
||||
|
||||
node2 = dht.DhtRunner()
|
||||
node2.run()
|
||||
|
||||
node1.bootstrap(args.bootstrap)
|
||||
node2.bootstrap(args.bootstrap)
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
pending_tests = {}
|
||||
keys = [dht.InfoHash.getRandom() for _ in range(args.num_ops)]
|
||||
|
||||
def listen_cb(key, val, expired):
|
||||
global pending_tests
|
||||
kstr = str(key)
|
||||
if kstr in pending_tests:
|
||||
if pending_tests[kstr]['v'].id == val.id:
|
||||
pending_tests.pop(kstr, None)
|
||||
else:
|
||||
print("Expected vid", val.id, "got", pending_tests[kstr]['v'].id)
|
||||
return True
|
||||
|
||||
def listen(key):
|
||||
node1.listen(key, lambda v, e: loop.call_soon_threadsafe(listen_cb, key, v, e))
|
||||
|
||||
for key in keys:
|
||||
listen(key)
|
||||
|
||||
next_test = time.time()
|
||||
while True:
|
||||
start = time.time()
|
||||
#print(datetime.fromtimestamp(start).strftime('%Y-%m-%d %H:%M:%S'), 'Test started')
|
||||
for key in keys:
|
||||
val = dht.Value(str(dht.InfoHash.getRandom()).encode())
|
||||
pending_tests[str(key)] = {'v':val, 'c':0}
|
||||
node2.put(key, val, lambda ok, nodes: ok)
|
||||
while len(pending_tests):
|
||||
loop.stop()
|
||||
loop.run_forever()
|
||||
time.sleep(1)
|
||||
if time.time()-start > args.timeout:
|
||||
print('Test timeout !')
|
||||
exit(1)
|
||||
|
||||
end = time.time()
|
||||
print(datetime.fromtimestamp(end).strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'Test completed successfully in', end-start)
|
||||
next_test += args.period
|
||||
if next_test > end:
|
||||
time.sleep(next_test-end)
|
||||
Executable
+79
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2022 Savoir-faire Linux Inc.
|
||||
# Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import opendht as dht
|
||||
import time
|
||||
import asyncio
|
||||
|
||||
config = dht.DhtConfig()
|
||||
config.setRateLimit(-1, -1)
|
||||
|
||||
ping_node = dht.DhtRunner()
|
||||
ping_node.run(config=config)
|
||||
#ping_node.enableLogging()
|
||||
#ping_node.bootstrap("bootstrap.ring.cx", "4222")
|
||||
|
||||
pong_node = dht.DhtRunner()
|
||||
pong_node.run(config=config)
|
||||
#pong_node.enableLogging()
|
||||
pong_node.ping(ping_node.getBound())
|
||||
|
||||
loc_ping = dht.InfoHash.get("toto99")
|
||||
loc_pong = dht.InfoHash.get(str(loc_ping))
|
||||
|
||||
net = []
|
||||
for i in range(1,10):
|
||||
node = dht.DhtRunner()
|
||||
node.run(config=config)
|
||||
node.ping(ping_node.getBound())
|
||||
net.append(node)
|
||||
|
||||
i = 0
|
||||
MAX = 2048
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
def done(h, ok):
|
||||
pass
|
||||
#print(h, "over", ok)
|
||||
|
||||
def ping(node, h):
|
||||
global i
|
||||
i += 1
|
||||
if i < MAX:
|
||||
node.put(h, dht.Value(b"hey"), lambda ok, nodes: done(node.getNodeId().decode(), ok))
|
||||
else:
|
||||
loop.stop()
|
||||
|
||||
def pong(node, h):
|
||||
#print(node.getNodeId().decode(), "got ping", h, i)
|
||||
loop.call_soon_threadsafe(ping, node, h)
|
||||
return True
|
||||
|
||||
t1 = time.time()
|
||||
|
||||
ping_node.listen(loc_ping, lambda v, e: pong(pong_node, loc_pong) if not e else True)
|
||||
pong_node.listen(loc_pong, lambda v, e: pong(ping_node, loc_ping) if not e else True)
|
||||
|
||||
ping(pong_node, loc_ping)
|
||||
|
||||
loop.run_forever()
|
||||
|
||||
t2 = time.time()
|
||||
|
||||
print(MAX, "ping-pong done, took", t2 - t1, "s")
|
||||
print(1000 * (t2 - t1)/MAX, "ms per rt", MAX/(t2 - t1), "rt per s")
|
||||
+134
-111
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2016 Savoir-faire Linux Inc.
|
||||
# Copyright (c) 2015-2022 Savoir-faire Linux Inc.
|
||||
# Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
@@ -18,9 +18,10 @@
|
||||
import time, sys, os
|
||||
from pprint import pprint
|
||||
from math import cos, sin, pi
|
||||
from urllib import request
|
||||
import urllib3
|
||||
import gzip
|
||||
import asyncio
|
||||
import queue
|
||||
from geoip import geolite2
|
||||
|
||||
sys.path.append('..')
|
||||
from opendht import *
|
||||
@@ -35,14 +36,27 @@ from mpl_toolkits.basemap import Basemap
|
||||
|
||||
import GeoIP
|
||||
|
||||
http = urllib3.PoolManager()
|
||||
|
||||
run = True
|
||||
done = 0
|
||||
all_nodes = NodeSet()
|
||||
nodes_ip4s = {}
|
||||
nodes_ip6s = {}
|
||||
lats = []
|
||||
lons = []
|
||||
cities=[]
|
||||
xys = []
|
||||
colors = []
|
||||
all_lines = []
|
||||
points = []
|
||||
not_found = []
|
||||
|
||||
plt.ion()
|
||||
plt.figaspect(2.)
|
||||
|
||||
fig, axes = plt.subplots(2, 1)
|
||||
fig.set_size_inches(12,16,forward=True)
|
||||
#fig.set_size_inches(8,16,forward=True)
|
||||
fig.tight_layout()
|
||||
fig.canvas.set_window_title('OpenDHT scanner')
|
||||
|
||||
@@ -68,74 +82,115 @@ exitbtn = Button(exitax, 'Exit')
|
||||
reloadax = plt.axes([0.92, 0.90, 0.07, 0.04])
|
||||
button = Button(reloadax, 'Reload')
|
||||
|
||||
def check_dl(fname, url):
|
||||
if os.path.isfile(fname):
|
||||
return
|
||||
print('downloading', url)
|
||||
ghandle = gzip.GzipFile(fileobj=request.urlopen(url))
|
||||
with open(fname, 'wb') as out:
|
||||
for line in ghandle:
|
||||
out.write(line)
|
||||
collection = None
|
||||
infos = [ringx.text(1.2, -0.8, ""),
|
||||
ringx.text(1.2, -0.9, "")]
|
||||
|
||||
check_dl("GeoLiteCity.dat", "http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz")
|
||||
check_dl("GeoLiteCityv6.dat", "http://geolite.maxmind.com/download/geoip/database/GeoLiteCityv6-beta/GeoLiteCityv6.dat.gz")
|
||||
|
||||
gi = GeoIP.open("GeoLiteCity.dat", GeoIP.GEOIP_INDEX_CACHE | GeoIP.GEOIP_CHECK_CACHE)
|
||||
gi6 = GeoIP.open("GeoLiteCityv6.dat", GeoIP.GEOIP_INDEX_CACHE | GeoIP.GEOIP_CHECK_CACHE)
|
||||
def exitcb(arg):
|
||||
global run
|
||||
run = False
|
||||
exitbtn.on_clicked(exitcb)
|
||||
|
||||
def gcb(v):
|
||||
return True
|
||||
|
||||
r = DhtRunner()
|
||||
r.run(port=4112)
|
||||
r.bootstrap("bootstrap.ring.cx", "4222")
|
||||
r.bootstrap("bootstrap.jami.net", "4222")
|
||||
|
||||
all_lines = []
|
||||
plt.pause(1)
|
||||
|
||||
plt.pause(2)
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
q = queue.Queue()
|
||||
|
||||
def step(cur_h, cur_depth):
|
||||
global done, all_nodes, all_lines
|
||||
global done
|
||||
done += 1
|
||||
a = 2.*pi*cur_h.toFloat()
|
||||
b = a + 2.*pi/(2**(cur_depth))
|
||||
arc = []
|
||||
lines = []
|
||||
q.put((stepUi, (cur_h, cur_depth, arc, lines)))
|
||||
print("step", cur_h, cur_depth)
|
||||
arc = ringx.add_patch(mpatches.Wedge([0.,0,], 1., a*180/pi, b*180/pi, fill=True, color="blue", alpha=0.5))
|
||||
lines = ringx.plot([0, cos(a)], [0, sin(a)], 'k-', lw=1.2)
|
||||
all_lines.extend(lines)
|
||||
r.get(cur_h, gcb, lambda d, nodes: loop.call_soon_threadsafe(nextstep, cur_h, cur_depth, d, nodes, arc, lines))
|
||||
r.get(cur_h, gcb, lambda d, nodes: stepdone(cur_h, cur_depth, d, nodes, arc, lines))
|
||||
|
||||
def nextstep(cur_h, cur_depth, ok, nodes, arc=None, lines=[]):
|
||||
global done, all_nodes
|
||||
if arc:
|
||||
arc.remove()
|
||||
del arc
|
||||
def stepdone(cur_h, cur_depth, d, nodes, arc, lines):
|
||||
#print("stepdone", cur_h, cur_depth)
|
||||
q.put((nextstepUi, (nodes, arc, lines)))
|
||||
nextstep(cur_h, cur_depth, d, nodes)
|
||||
|
||||
def nextstep(cur_h, cur_depth, ok, nodes):
|
||||
global done
|
||||
if nodes:
|
||||
for l in lines:
|
||||
l.set_color('#444444')
|
||||
snodes = NodeSet()
|
||||
snodes.extend(nodes)
|
||||
all_nodes.extend(nodes)
|
||||
depth = min(8, InfoHash.commonBits(snodes.first(), snodes.last())+6)
|
||||
commonBits = 0
|
||||
if len(nodes) > 1:
|
||||
snodes = NodeSet()
|
||||
snodes.extend(nodes)
|
||||
commonBits = InfoHash.commonBits(snodes.first(), snodes.last())
|
||||
depth = min(8, commonBits+6)
|
||||
if cur_depth < depth:
|
||||
for b in range(cur_depth, depth):
|
||||
new_h = InfoHash(cur_h.toString());
|
||||
new_h.setBit(b, 1);
|
||||
step(new_h, b+1);
|
||||
new_h = InfoHash(cur_h.toString())
|
||||
new_h.setBit(b, 1)
|
||||
step(new_h, b+1)
|
||||
else:
|
||||
print("step done with no nodes", ok, cur_h.toString().decode())
|
||||
done -= 1
|
||||
|
||||
run = True
|
||||
def exitcb(arg):
|
||||
global run
|
||||
run = False
|
||||
exitbtn.on_clicked(exitcb)
|
||||
def stepUi(cur_h, cur_depth, arc, lines):
|
||||
global all_lines
|
||||
a = 2.*pi*cur_h.toFloat()
|
||||
b = a + 2.*pi/(2**(cur_depth))
|
||||
arc.append(ringx.add_patch(mpatches.Wedge([0.,0,], 1., a*180/pi, b*180/pi, fill=True, color="blue", alpha=0.5)))
|
||||
lines.extend(ringx.plot([0, cos(a)], [0, sin(a)], 'k-', lw=1.2))
|
||||
all_lines.extend(lines)
|
||||
|
||||
def nextstepUi(nodes, arc=None, lines=[]):
|
||||
for a in arc:
|
||||
if a:
|
||||
a.remove()
|
||||
del a
|
||||
for l in lines:
|
||||
l.set_color('#aaaaaa')
|
||||
if nodes:
|
||||
appendNodes(nodes)
|
||||
|
||||
def appendNodes(nodes):
|
||||
global all_nodes
|
||||
for n in nodes:
|
||||
if all_nodes.insert(n):
|
||||
appendNewNode(n)
|
||||
|
||||
def appendNewNode(n):
|
||||
global nodes_ip4s, nodes_ip6s, colors, xys
|
||||
addr = b':'.join(n.getNode().getAddr().split(b':')[0:-1]).decode()
|
||||
colors.append('red' if n.getNode().isExpired() else 'blue')
|
||||
node_val = n.getId().toFloat()
|
||||
xys.append((cos(node_val*2*pi), sin(node_val*2*pi)))
|
||||
georecord = None
|
||||
if addr[0] == '[':
|
||||
addr = addr[1:-1]
|
||||
if addr in nodes_ip6s:
|
||||
nodes_ip6s[addr][1] += 1
|
||||
else:
|
||||
georecord = geolite2.lookup(addr)
|
||||
nodes_ip6s[addr] = [n, 1, georecord]
|
||||
else:
|
||||
if addr in nodes_ip4s:
|
||||
nodes_ip4s[addr][1] += 1
|
||||
else:
|
||||
georecord = geolite2.lookup(addr)
|
||||
nodes_ip4s[addr] = [n, 1, georecord]
|
||||
if georecord:
|
||||
appendMapPoint(georecord)
|
||||
|
||||
def appendMapPoint(res):
|
||||
global lons, lats, cities
|
||||
lons.append(res.location[1])
|
||||
lats.append(res.location[0])
|
||||
cities.append(res.city if res.city else (str(int(res.location[0]))+'-'+str(int(res.location[1]))))
|
||||
|
||||
def restart(arg):
|
||||
global collection, all_lines, all_nodes, points, done
|
||||
global collection, all_lines, all_nodes, points, done, nodes_ip4s, nodes_ip6s, lats, lons, cities, xys, colors
|
||||
if done:
|
||||
return
|
||||
for l in all_lines:
|
||||
@@ -143,6 +198,13 @@ def restart(arg):
|
||||
del l
|
||||
all_lines = []
|
||||
all_nodes = NodeSet()
|
||||
nodes_ip4s = {}
|
||||
nodes_ip6s = {}
|
||||
lats = []
|
||||
lons = []
|
||||
cities=[]
|
||||
xys = []
|
||||
colors = []
|
||||
if collection:
|
||||
collection.remove()
|
||||
del collection
|
||||
@@ -158,78 +220,43 @@ def restart(arg):
|
||||
step(start_h, 0)
|
||||
plt.draw()
|
||||
|
||||
collection = None
|
||||
points = []
|
||||
not_found = []
|
||||
infos = [ringx.text(1.2, -0.8, ""),
|
||||
ringx.text(1.2, -0.9, "")]
|
||||
|
||||
def num_nodes(node_set):
|
||||
return sorted([x for x in node_set.items()], key=lambda ip: ip[1][1])
|
||||
|
||||
def generate_set():
|
||||
node_ipv4 = {}
|
||||
node_ipv6 = {}
|
||||
for n in all_nodes:
|
||||
addr = b''.join(n.getNode().getAddr().split(b':')[0:-1]).decode()
|
||||
if addr[0] == '[':
|
||||
addr = addr[1:-1]
|
||||
if addr in node_ipv6:
|
||||
node_ipv6[addr][1] += 1
|
||||
else:
|
||||
node_ipv6[addr] = [n, 1, gi6.record_by_name_v6(addr)]
|
||||
else:
|
||||
if addr in node_ipv4:
|
||||
node_ipv4[addr][1] += 1
|
||||
else:
|
||||
node_ipv4[addr] = [n, 1, gi.record_by_name(addr)]
|
||||
return num_nodes(node_ipv4), num_nodes(node_ipv6)
|
||||
|
||||
def update_plot():
|
||||
global done, m, collection, not_found, points
|
||||
#print("update_plot", done)
|
||||
global m, collection, points
|
||||
for p in points:
|
||||
p.remove()
|
||||
del p
|
||||
points = []
|
||||
lats = []
|
||||
lons = []
|
||||
cities=[]
|
||||
colors=[]
|
||||
not_found.clear()
|
||||
ip4s, ip6s = generate_set()
|
||||
ares = []
|
||||
for addr, n in ip4s:
|
||||
ares.append((addr, n[0].getNode(), n[2]))
|
||||
for addr, n in ip6s:
|
||||
ares.append((addr, n[0].getNode(), n[2]))
|
||||
for r in ares:
|
||||
res = r[2]
|
||||
n = r[1]
|
||||
if res:
|
||||
lats.append(res['latitude'])
|
||||
lons.append(res['longitude'])
|
||||
cities.append(res['city'] if res['city'] else (str(int(res['latitude']))+'-'+str(int(res['longitude']))))
|
||||
colors.append('red' if n.isExpired() else 'blue')
|
||||
else:
|
||||
not_found.append(r[0])
|
||||
|
||||
x,y = m(lons,lats)
|
||||
points.extend(m.plot(x,y,'bo'))
|
||||
for name, xpt, ypt in zip(cities, x, y):
|
||||
points.append(mpx.text(xpt+50000, ypt+50000, name))
|
||||
node_val = [n.getId().toFloat() for n in all_nodes]
|
||||
xys = [(cos(d*2*pi), sin(d*2*pi)) for d in node_val]
|
||||
if collection:
|
||||
collection.remove()
|
||||
del collection
|
||||
collection = None
|
||||
collection = ringx.add_collection(RegularPolyCollection(
|
||||
fig.dpi, 6, sizes=(10,), facecolors=colors,
|
||||
int(fig.dpi), 6, sizes=(10,), facecolors=colors,
|
||||
offsets = xys, transOffset = ringx.transData))
|
||||
|
||||
node_ip4s, node_ip6s = generate_set()
|
||||
infos[0].set_text("{} different IPv4s".format(len(node_ip4s)))
|
||||
infos[1].set_text("{} different IPv6s".format(len(node_ip6s)))
|
||||
infos[0].set_text("{} different IPv4s".format(len(nodes_ip4s)))
|
||||
infos[1].set_text("{} different IPv6s".format(len(nodes_ip6s)))
|
||||
|
||||
def d(arg):
|
||||
pass
|
||||
|
||||
def handle_tasks():
|
||||
while True:
|
||||
try:
|
||||
f = q.get_nowait()
|
||||
f[0](*f[1])
|
||||
q.task_done()
|
||||
except Exception as e:
|
||||
break;
|
||||
update_plot()
|
||||
|
||||
if run:
|
||||
# start first step
|
||||
@@ -237,15 +264,9 @@ if run:
|
||||
start_h.setBit(159, 1)
|
||||
step(start_h, 0)
|
||||
|
||||
def d(arg):
|
||||
pass
|
||||
|
||||
while run:
|
||||
while run and done > 0:
|
||||
loop.stop()
|
||||
loop.run_forever()
|
||||
update_plot()
|
||||
plt.draw()
|
||||
handle_tasks()
|
||||
plt.pause(.5)
|
||||
|
||||
if not run:
|
||||
@@ -253,13 +274,14 @@ while run:
|
||||
|
||||
button.on_clicked(restart)
|
||||
|
||||
node_ip4s, node_ip6s = generate_set()
|
||||
node_ip4s = num_nodes(nodes_ip4s)
|
||||
node_ip6s = num_nodes(nodes_ip6s)
|
||||
|
||||
print(all_nodes.size(), " nodes found")
|
||||
print(all_nodes)
|
||||
print(len(not_found), " nodes not geolocalized")
|
||||
for n in not_found:
|
||||
print(n)
|
||||
#print(len(not_found), " nodes not geolocalized")
|
||||
#for n in not_found:
|
||||
# print(n)
|
||||
print('')
|
||||
print(len(node_ip4s), " different IPv4s :")
|
||||
for ip in node_ip4s:
|
||||
@@ -269,6 +291,7 @@ while run:
|
||||
for ip in node_ip6s:
|
||||
print(ip[0], ":", str(ip[1][1]), "nodes", ("(" + ip[1][2]['city'] + ")") if ip[1][2] and ip[1][2]['city'] else "")
|
||||
|
||||
handle_tasks()
|
||||
while run and done == 0:
|
||||
plt.pause(.5)
|
||||
button.on_clicked(d)
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="svg3492"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
xml:space="preserve"
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 256 255.99999"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90"><title
|
||||
id="title3357">OpenDHT</title><metadata
|
||||
id="metadata3498"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title>OpenDHT</dc:title><dc:rights><cc:Agent><dc:title></dc:title></cc:Agent></dc:rights><dc:creator><cc:Agent><dc:title>Savoir-faire Linux Inc.</dc:title></cc:Agent></dc:creator><dc:relation>https://opendht.net</dc:relation></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs3496"><clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath3534"><path
|
||||
d="m 0,0 1152,0 0,648 L 0,648 0,0 Z"
|
||||
id="path3536"
|
||||
inkscape:connector-curvature="0" /></clipPath></defs><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1855"
|
||||
inkscape:window-height="1056"
|
||||
id="namedview3494"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
fit-margin-top="5"
|
||||
fit-margin-right="5"
|
||||
fit-margin-bottom="5"
|
||||
fit-margin-left="5"
|
||||
inkscape:zoom="1.9555556"
|
||||
inkscape:cx="63.161953"
|
||||
inkscape:cy="141.16919"
|
||||
inkscape:window-x="1985"
|
||||
inkscape:window-y="24"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g3532" /><g
|
||||
id="g3500"
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="SFL-logo-DHT"
|
||||
transform="matrix(1.25,0,0,-1.25,-589.00821,604.10631)"><g
|
||||
id="g3530"><g
|
||||
id="g3532"
|
||||
clip-path="url(#clipPath3534)"><g
|
||||
id="g3359"
|
||||
transform="matrix(0.9931555,0,0,0.99058777,1.5489503,0.9813209)"><g
|
||||
transform="translate(485.3798,383.7826)"
|
||||
id="g3538"><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3540"
|
||||
style="fill:#47b3d1;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c 0,50.03 40.554,90.586 90.586,90.586 50.028,0 90.587,-40.556 90.587,-90.586 0,-50.03 -40.559,-90.588 -90.587,-90.588 C 40.554,-90.588 0,-50.03 0,0" /></g><g
|
||||
transform="translate(576.0338,313.2273)"
|
||||
id="g3542"><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3544"
|
||||
style="fill:#0091ba;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c 0,54.307 -24.009,102.977 -61.954,136.075 -17.613,-16.526 -28.632,-39.996 -28.632,-66.056 0,-49.629 39.916,-89.912 89.393,-90.555 0.396,-0.009 0.792,-0.033 1.193,-0.033 L 0,-0.082 0,0 Z" /></g><g
|
||||
transform="translate(575.9661,292.6626)"
|
||||
id="g3546"><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3548"
|
||||
style="fill:#007aa3;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c -1.439,49.482 -41.105,89.355 -90.504,91.12 0,-0.178 -0.015,-0.355 -0.015,-0.536 C -90.519,40.578 -49.998,0.039 0,0" /></g><g
|
||||
transform="translate(532.4411,337.497)"
|
||||
id="g3550"><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3552"
|
||||
style="fill:#006382;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c -10.555,0 -20.236,-3.754 -27.777,-10 16.565,-21.182 42.472,-34.815 71.442,-34.834 0.01,0.415 -0.072,0.821 -0.072,1.241 C 43.593,-19.516 24.077,0 0,0" /></g><g
|
||||
transform="translate(576.0338,313.2273)"
|
||||
id="g3554"><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3556"
|
||||
style="fill:#0091ba;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="M 0,0 C 0,54.307 24.014,102.977 61.954,136.075 79.567,119.549 90.586,96.079 90.586,70.019 90.586,20.39 50.675,-19.893 1.198,-20.536 0.797,-20.545 0.406,-20.569 0.005,-20.569 l 0,20.487 C 0.005,-0.053 0,-0.029 0,0" /></g><g
|
||||
transform="translate(576.1063,292.6626)"
|
||||
id="g3558"><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3560"
|
||||
style="fill:#007aa3;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c 1.44,49.482 41.105,89.355 90.504,91.12 0,-0.178 0.01,-0.355 0.01,-0.536 C 90.514,40.578 49.998,0.039 0,0" /></g><g
|
||||
transform="translate(619.6266,337.497)"
|
||||
id="g3562"><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3564"
|
||||
style="fill:#006382;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c 10.56,0 20.241,-3.754 27.782,-10 -16.57,-21.182 -42.405,-34.815 -71.375,-34.834 -0.009,0.415 0,0.821 0,1.241 C -43.593,-19.516 -24.072,0 0,0" /></g></g></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
@@ -0,0 +1,2 @@
|
||||
*.lock
|
||||
target
|
||||
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "opendht"
|
||||
version = "0.1.0"
|
||||
authors = ["Sébastien Blin <sebastien.blin@savoirfairelinux.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
libc="0.2.0"
|
||||
os_socketaddr="0.1.0"
|
||||
@@ -0,0 +1,21 @@
|
||||
# Rust bindings for opendht
|
||||
|
||||
## Build
|
||||
|
||||
Install rust on your system (https://rustup.rs/ is the recommanded way). Once done:
|
||||
|
||||
```
|
||||
cargo build
|
||||
```
|
||||
|
||||
## Run tests
|
||||
|
||||
```
|
||||
cargo test
|
||||
```
|
||||
|
||||
## Run dht node
|
||||
|
||||
```
|
||||
cargo run --example dhtnode
|
||||
```
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
extern crate opendht;
|
||||
use std::{ thread, time };
|
||||
|
||||
use opendht::{ InfoHash, DhtRunner, DhtRunnerConfig, Value };
|
||||
// use opendht::crypto::*;
|
||||
|
||||
fn main() {
|
||||
println!("{}", InfoHash::random());
|
||||
println!("{}", InfoHash::new());
|
||||
println!("{}", InfoHash::new().is_zero());
|
||||
println!("{}", InfoHash::get("alice"));
|
||||
println!("{}", InfoHash::get("alice").is_zero());
|
||||
|
||||
|
||||
let mut dht = DhtRunner::new();
|
||||
let /*mut*/ config = DhtRunnerConfig::new();
|
||||
//// If you want to inject a certificate, uncomment the following lines and previous mut.
|
||||
//// Note: you can generate a certificate with
|
||||
//// openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes -keyout example.key -out example.crt -subj /CN=example.com
|
||||
//let cert = DhtCertificate::import("example.crt").ok().expect("Invalid cert file");
|
||||
//let pk = PrivateKey::import("example.key", "");
|
||||
//config.set_identity(cert, pk);
|
||||
dht.run_config(1412, config);
|
||||
dht.bootstrap("bootstrap.jami.net", 4222);
|
||||
println!("Current node id: {}", dht.node_id());
|
||||
|
||||
let /* mut */ data = 42;
|
||||
let mut get_cb = |v: Box<Value>| {
|
||||
//data += 1;
|
||||
println!("GET: VALUE CB - data: {} - v: {}", data, v);
|
||||
true
|
||||
};
|
||||
let mut done_cb = |ok: bool| {
|
||||
println!("GET: DONE CB - data: {} - ok: {}", data, ok);
|
||||
};
|
||||
|
||||
dht.get(&InfoHash::get("alice"), &mut get_cb, &mut done_cb);
|
||||
|
||||
let mut put_done_cb = |ok: bool| {
|
||||
println!("PUT: DONE CB - data: {} - ok: {}", data, ok);
|
||||
};
|
||||
dht.put(&InfoHash::get("bob"), Value::new("hi!"), &mut put_done_cb, false);
|
||||
|
||||
|
||||
println!("Start listening /foo");
|
||||
let mut value_cb = |v, expired| {
|
||||
println!("LISTEN: DONE CB - data: {} - v: {} - expired: {}", data, v, expired);
|
||||
true
|
||||
};
|
||||
let token = dht.listen(&InfoHash::get("foo"), &mut value_cb);
|
||||
let one_min = time::Duration::from_secs(10);
|
||||
thread::sleep(one_min);
|
||||
dht.cancel_listen(&InfoHash::get("foo"), token);
|
||||
println!("Public ips: {:#?}", dht.public_addresses());
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use crate::ffi::*;
|
||||
|
||||
pub use crate::ffi::Blob;
|
||||
|
||||
impl Blob {
|
||||
pub fn data(&self) -> DataView {
|
||||
unsafe {
|
||||
dht_blob_get_data(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Blob {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
dht_blob_delete(&mut *self)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
pub use crate::ffi::*;
|
||||
use std::ffi::CString;
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
use std::fs::File;
|
||||
use std::ptr;
|
||||
|
||||
impl PublicKey {
|
||||
pub fn new(data: &str) -> Box<PublicKey> {
|
||||
unsafe {
|
||||
Box::from_raw(dht_publickey_import(data.as_ptr(), data.len()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unpack(&mut self, data: Vec<u8>) -> i32 {
|
||||
unsafe {
|
||||
dht_publickey_unpack(&mut *self, data.as_ptr(), data.len())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pack(&mut self, data: &str) -> i32 {
|
||||
let data = CString::new(data).unwrap();
|
||||
unsafe {
|
||||
dht_publickey_pack(&mut *self,
|
||||
data.as_ptr(),
|
||||
data.as_bytes().len())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> InfoHash {
|
||||
unsafe {
|
||||
dht_publickey_get_id(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn long_id(&self) -> PkId {
|
||||
unsafe {
|
||||
dht_publickey_get_long_id(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_signature(&self, data: &str, signature: &str) -> bool {
|
||||
let data = CString::new(data).unwrap();
|
||||
let signature = CString::new(signature).unwrap();
|
||||
unsafe {
|
||||
dht_publickey_check_signature(self,
|
||||
data.as_ptr(), data.as_bytes().len(),
|
||||
signature.as_ptr(), signature.as_bytes().len())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encrypt(&self, data: &str) -> Box<Blob> {
|
||||
let data = CString::new(data).unwrap();
|
||||
unsafe {
|
||||
Box::from_raw(dht_publickey_encrypt(self,
|
||||
data.as_ptr(), data.as_bytes().len()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PublicKey {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
dht_publickey_delete(&mut *self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PrivateKey {
|
||||
pub fn new(key_length_bits: u32) -> Box<PrivateKey> {
|
||||
unsafe {
|
||||
Box::from_raw(dht_privatekey_generate(key_length_bits))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn import(file: &str, password: &str) -> io::Result<Box<PrivateKey>> {
|
||||
let mut f = File::open(file)?;
|
||||
let mut buffer = Vec::new();
|
||||
f.read_to_end(&mut buffer)?;
|
||||
Ok(PrivateKey::from_bytes(&buffer, password))
|
||||
}
|
||||
|
||||
pub fn from_bytes(buffer: &Vec<u8>, password: &str) -> Box<PrivateKey> {
|
||||
unsafe {
|
||||
Box::from_raw(dht_privatekey_import((&*buffer).as_ptr(), buffer.len(),
|
||||
password.as_ptr() as *const i8))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn public_key(&self) -> Box<PublicKey> {
|
||||
unsafe {
|
||||
Box::from_raw(dht_privatekey_get_publickey(self))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Drop for PrivateKey {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
dht_privatekey_delete(&mut *self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DhtCertificate {
|
||||
pub fn import(file: &str) -> io::Result<Box<DhtCertificate>> {
|
||||
let mut f = File::open(file)?;
|
||||
let mut buffer = Vec::new();
|
||||
f.read_to_end(&mut buffer)?;
|
||||
Ok(DhtCertificate::from_bytes(&buffer))
|
||||
}
|
||||
|
||||
pub fn from_bytes(buffer: &Vec<u8>) -> Box<DhtCertificate> {
|
||||
unsafe {
|
||||
Box::from_raw(dht_certificate_import((&*buffer).as_ptr(), buffer.len()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_slice(buffer: &str) -> Box<DhtCertificate> {
|
||||
unsafe {
|
||||
Box::from_raw(dht_certificate_import((&*buffer).as_ptr(), buffer.len()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> InfoHash {
|
||||
unsafe {
|
||||
dht_certificate_get_id(&*self)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn long_id(&self) -> PkId {
|
||||
unsafe {
|
||||
dht_certificate_get_long_id(&*self)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn publickey(&self) -> Box<PublicKey> {
|
||||
unsafe {
|
||||
Box::from_raw(dht_certificate_get_publickey(&*self))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DhtCertificate {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
dht_certificate_delete(&mut *self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DhtIdentity {
|
||||
pub fn new(common_name: &str) -> DhtIdentity {
|
||||
unsafe {
|
||||
DhtIdentity::generate(common_name, Box::from_raw(ptr::null_mut()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate(common_name: &str, ca: Box<DhtIdentity>) -> DhtIdentity {
|
||||
let common_name = CString::new(common_name).unwrap();
|
||||
unsafe {
|
||||
dht_identity_generate(common_name.as_ptr(), &*ca)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DhtIdentity {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
dht_identity_delete(&mut *self)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
use libc::c_void;
|
||||
use std::ffi::CString;
|
||||
use std::ptr;
|
||||
|
||||
pub use crate::ffi::*;
|
||||
use std::net::SocketAddr;
|
||||
use os_socketaddr::OsSocketAddr;
|
||||
|
||||
impl DhtRunnerConfig {
|
||||
|
||||
pub fn new() -> Box<DhtRunnerConfig> {
|
||||
let mut config: Box<DhtRunnerConfig> = Box::new(DhtRunnerConfig {
|
||||
dht_config: DhtSecureConfig {
|
||||
node_config: DhtNodeConfig {
|
||||
node_id: InfoHash::new(),
|
||||
network: 0,
|
||||
is_bootstrap: false,
|
||||
maintain_storage: false,
|
||||
persist_path: ptr::null(),
|
||||
},
|
||||
id: DhtIdentity {
|
||||
privatekey: ptr::null_mut(),
|
||||
certificate: ptr::null_mut(),
|
||||
},
|
||||
},
|
||||
threaded: false,
|
||||
proxy_server: ptr::null(),
|
||||
push_node_id: ptr::null(),
|
||||
push_token: ptr::null(),
|
||||
peer_discovery: false,
|
||||
peer_publish: false,
|
||||
server_ca: ptr::null_mut(),
|
||||
client_identity: DhtIdentity {
|
||||
privatekey: ptr::null_mut(),
|
||||
certificate: ptr::null_mut(),
|
||||
},
|
||||
});
|
||||
unsafe {
|
||||
dht_runner_config_default(&mut *config);
|
||||
}
|
||||
config
|
||||
}
|
||||
|
||||
pub fn set_proxy_server(&mut self, proxy_server: &str) {
|
||||
self.proxy_server = CString::new(proxy_server).unwrap().as_ptr();
|
||||
}
|
||||
|
||||
pub fn set_push_node_id(&mut self, push_node_id: &str) {
|
||||
self.push_node_id = CString::new(push_node_id).unwrap().as_ptr();
|
||||
}
|
||||
|
||||
pub fn set_push_token(&mut self, push_token: &str) {
|
||||
self.push_token = CString::new(push_token).unwrap().as_ptr();
|
||||
}
|
||||
|
||||
pub fn set_identity(&mut self, certificate: Box<DhtCertificate>, privatekey: Box<PrivateKey>) {
|
||||
self.dht_config.id.privatekey = Box::into_raw(privatekey);
|
||||
self.dht_config.id.certificate = Box::into_raw(certificate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl DhtNodeConfig
|
||||
{
|
||||
pub fn set_persist_path(&mut self, persist_path: &str) {
|
||||
self.persist_path = CString::new(persist_path).unwrap().as_ptr();
|
||||
}
|
||||
}
|
||||
|
||||
struct GetHandler<'a>
|
||||
{
|
||||
get_cb: &'a mut(dyn FnMut(Box<Value>) -> bool),
|
||||
done_cb: &'a mut(dyn FnMut(bool))
|
||||
}
|
||||
|
||||
impl<'a> GetHandler<'a>
|
||||
{
|
||||
fn get_cb(&mut self, v: Box<Value>) -> bool{
|
||||
(self.get_cb)(v)
|
||||
}
|
||||
|
||||
fn done_cb(&mut self, ok: bool) {
|
||||
(self.done_cb)(ok)
|
||||
}
|
||||
}
|
||||
|
||||
extern fn get_handler_cb(v: *mut Value, ptr: *mut c_void) -> bool {
|
||||
if ptr.is_null() {
|
||||
return true;
|
||||
}
|
||||
unsafe {
|
||||
let handler = ptr as *mut GetHandler;
|
||||
(*handler).get_cb((*v).boxed())
|
||||
}
|
||||
}
|
||||
|
||||
extern fn done_handler_cb(ok: bool, ptr: *mut c_void) {
|
||||
unsafe {
|
||||
let handler = Box::from_raw(ptr as *mut GetHandler);
|
||||
(*handler.done_cb)(ok)
|
||||
}
|
||||
}
|
||||
|
||||
struct PutHandler<'a>
|
||||
{
|
||||
done_cb: &'a mut(dyn FnMut(bool))
|
||||
}
|
||||
|
||||
impl<'a> PutHandler<'a>
|
||||
{
|
||||
fn done_cb(&mut self, ok: bool) {
|
||||
(self.done_cb)(ok)
|
||||
}
|
||||
}
|
||||
|
||||
extern fn put_handler_done(ok: bool, ptr: *mut c_void) {
|
||||
unsafe {
|
||||
let handler = Box::from_raw(ptr as *mut PutHandler);
|
||||
(*handler.done_cb)(ok)
|
||||
}
|
||||
}
|
||||
|
||||
struct ListenHandler<'a>
|
||||
{
|
||||
cb: &'a mut(dyn FnMut(Box<Value>, bool) -> bool)
|
||||
}
|
||||
|
||||
impl<'a> ListenHandler<'a>
|
||||
{
|
||||
fn cb(&mut self, v: Box<Value>, expired: bool) -> bool {
|
||||
(self.cb)(v, expired)
|
||||
}
|
||||
}
|
||||
|
||||
extern fn listen_handler(v: *mut Value, expired: bool, ptr: *mut c_void) -> bool {
|
||||
unsafe {
|
||||
let handler = ptr as *mut ListenHandler;
|
||||
(*handler).cb((*v).boxed(), expired)
|
||||
}
|
||||
}
|
||||
|
||||
extern fn listen_handler_done(ptr: *mut c_void) {
|
||||
unsafe {
|
||||
Box::from_raw(ptr as *mut ListenHandler);
|
||||
}
|
||||
}
|
||||
|
||||
impl DhtRunner {
|
||||
pub fn new() -> Box<DhtRunner> {
|
||||
unsafe {
|
||||
Box::from_raw(dht_runner_new())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self, port: u16) {
|
||||
unsafe {
|
||||
dht_runner_run(&mut *self, port)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_config(&mut self, port: u16, config: Box<DhtRunnerConfig>) {
|
||||
unsafe {
|
||||
dht_runner_run_config(&mut *self, port, &*config)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bootstrap(&mut self, host: &str, service: u16) {
|
||||
unsafe {
|
||||
dht_runner_bootstrap(&mut *self,
|
||||
CString::new(host).unwrap().as_ptr(),
|
||||
CString::new(service.to_string()).unwrap().as_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn node_id(&self) -> InfoHash {
|
||||
unsafe {
|
||||
dht_runner_get_node_id(&*self)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> InfoHash {
|
||||
unsafe {
|
||||
dht_runner_get_id(&*self)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get<'a>(&mut self, h: &InfoHash,
|
||||
get_cb: &'a mut(dyn FnMut(Box<Value>) -> bool),
|
||||
done_cb: &'a mut(dyn FnMut(bool))) {
|
||||
let handler = Box::new(GetHandler {
|
||||
get_cb,
|
||||
done_cb,
|
||||
});
|
||||
let handler = Box::into_raw(handler) as *mut c_void;
|
||||
unsafe {
|
||||
dht_runner_get(&mut *self, h, get_handler_cb, done_handler_cb, handler)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn put<'a>(&mut self, h: &InfoHash, v: Box<Value>,
|
||||
done_cb: &'a mut(dyn FnMut(bool)), permanent: bool) {
|
||||
let handler = Box::new(PutHandler {
|
||||
done_cb,
|
||||
});
|
||||
let handler = Box::into_raw(handler) as *mut c_void;
|
||||
unsafe {
|
||||
dht_runner_put(&mut *self, h, &*v, put_handler_done, handler, permanent)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn put_signed<'a>(&mut self, h: &InfoHash, v: Box<Value>,
|
||||
done_cb: &'a mut(dyn FnMut(bool)), permanent: bool) {
|
||||
let handler = Box::new(PutHandler {
|
||||
done_cb,
|
||||
});
|
||||
let handler = Box::into_raw(handler) as *mut c_void;
|
||||
unsafe {
|
||||
dht_runner_put_signed(&mut *self, h, &*v, put_handler_done, handler, permanent)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn put_encrypted<'a>(&mut self, h: &InfoHash, to: &InfoHash, v: Box<Value>,
|
||||
done_cb: &'a mut(dyn FnMut(bool)), permanent: bool) {
|
||||
let handler = Box::new(PutHandler {
|
||||
done_cb,
|
||||
});
|
||||
let handler = Box::into_raw(handler) as *mut c_void;
|
||||
unsafe {
|
||||
dht_runner_put_encrypted(&mut *self, h, to, &*v, put_handler_done, handler, permanent)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cancel_put<'a>(&mut self, h: &InfoHash, vid: u64) {
|
||||
unsafe {
|
||||
dht_runner_cancel_put(&mut *self, h, vid)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn listen<'a>(&mut self, h: &InfoHash,
|
||||
cb: &'a mut(dyn FnMut(Box<Value>, bool) -> bool)) -> Box<OpToken> {
|
||||
let handler = Box::new(ListenHandler {
|
||||
cb,
|
||||
});
|
||||
let handler = Box::into_raw(handler) as *mut c_void;
|
||||
unsafe {
|
||||
Box::from_raw(dht_runner_listen(&mut *self, h, listen_handler, listen_handler_done, handler))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cancel_listen(&mut self, h: &InfoHash, token: Box<OpToken>) {
|
||||
unsafe {
|
||||
dht_runner_cancel_listen(&mut *self, h, &*token)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shutdown(&mut self,
|
||||
done_cb: extern fn(bool, *mut c_void),
|
||||
cb_user_data: *mut c_void)
|
||||
{
|
||||
unsafe {
|
||||
dht_runner_shutdown(&mut *self, done_cb, cb_user_data)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn public_addresses(&self) -> Vec<SocketAddr> {
|
||||
let mut result = Vec::new();
|
||||
unsafe {
|
||||
let mut addresses = dht_runner_get_public_address(&*self);
|
||||
while !addresses.is_null() && !(*addresses).is_null() {
|
||||
let sock = (*(*addresses)).into_addr();
|
||||
if sock.is_some() {
|
||||
result.push(sock.unwrap());
|
||||
}
|
||||
addresses = (addresses as usize + std::mem::size_of::<*mut OsSocketAddr>()) as *mut *mut OsSocketAddr;
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DhtRunner {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
dht_runner_delete(&mut *self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for OpToken {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
dht_op_token_delete(&mut *self)
|
||||
}
|
||||
}
|
||||
}
|
||||
+222
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
use libc::{c_char, c_int, c_uint, c_void, in_port_t, size_t};
|
||||
use os_socketaddr::OsSocketAddr;
|
||||
|
||||
const HASH_LEN: usize = 20;
|
||||
const PKID_LEN: usize = 32;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct DataView
|
||||
{
|
||||
pub data: *const u8,
|
||||
pub size: size_t
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Value
|
||||
{
|
||||
_opaque: [u8; 0]
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Blob
|
||||
{
|
||||
_opaque: [u8; 0]
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(PartialEq)]
|
||||
pub struct InfoHash
|
||||
{
|
||||
pub d: [u8; HASH_LEN],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct PkId
|
||||
{
|
||||
pub d: [u8; PKID_LEN],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct PublicKey
|
||||
{
|
||||
_opaque: [u8; 0]
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct PrivateKey
|
||||
{
|
||||
_opaque: [u8; 0]
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct OpToken
|
||||
{
|
||||
_opaque: [u8; 0]
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct DhtRunner
|
||||
{
|
||||
_opaque: [u8; 0]
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct DhtNodeConfig
|
||||
{
|
||||
pub node_id: InfoHash,
|
||||
pub network: u32,
|
||||
pub is_bootstrap: bool,
|
||||
pub maintain_storage: bool,
|
||||
pub persist_path: *const c_char,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct DhtCertificate
|
||||
{
|
||||
_opaque: [u8; 0]
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct DhtIdentity
|
||||
{
|
||||
pub privatekey: *mut PrivateKey,
|
||||
pub certificate: *mut DhtCertificate,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct DhtSecureConfig
|
||||
{
|
||||
pub node_config: DhtNodeConfig,
|
||||
pub id: DhtIdentity,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct DhtRunnerConfig
|
||||
{
|
||||
pub dht_config: DhtSecureConfig,
|
||||
pub threaded: bool,
|
||||
pub proxy_server: *const c_char,
|
||||
pub push_node_id: *const c_char,
|
||||
pub push_token: *const c_char,
|
||||
pub peer_discovery: bool,
|
||||
pub peer_publish: bool,
|
||||
pub server_ca: *mut DhtCertificate,
|
||||
pub client_identity: DhtIdentity,
|
||||
}
|
||||
|
||||
|
||||
#[link(name = "opendht-c")]
|
||||
extern {
|
||||
// dht::Value
|
||||
pub fn dht_value_new(data: *const u8, size: size_t) -> *mut Value;
|
||||
pub fn dht_value_ref(data: *const Value) -> *mut Value;
|
||||
pub fn dht_value_unref(data: *mut Value);
|
||||
pub fn dht_value_get_data(data: *const Value) -> DataView;
|
||||
pub fn dht_value_get_id(data: *const Value) -> u64;
|
||||
pub fn dht_value_get_owner(data: *const Value) -> *mut PublicKey;
|
||||
pub fn dht_value_get_recipient(data: *const Value) -> InfoHash;
|
||||
pub fn dht_value_get_user_type(data: *const Value) -> *const c_char;
|
||||
|
||||
// dht::Blob
|
||||
pub fn dht_blob_get_data(data: *const Blob) -> DataView;
|
||||
pub fn dht_blob_delete(data: *mut Blob);
|
||||
|
||||
// dht::InfoHash
|
||||
pub fn dht_infohash_print(h: *const InfoHash) -> *const c_char;
|
||||
pub fn dht_infohash_random(h: *mut InfoHash);
|
||||
pub fn dht_infohash_get(h: *mut InfoHash, dat: *mut u8, dat_size: size_t);
|
||||
pub fn dht_infohash_from_hex(h: *mut InfoHash, dat: *const c_char);
|
||||
pub fn dht_infohash_is_zero(j: *const InfoHash) -> bool;
|
||||
|
||||
// dht::PkId
|
||||
pub fn dht_pkid_print(h: *const PkId) -> *const c_char;
|
||||
|
||||
// dht::crypto::PublicKey
|
||||
pub fn dht_publickey_import(dat: *const u8, dat_size: size_t) -> *mut PublicKey;
|
||||
pub fn dht_publickey_delete(pk: *mut PublicKey);
|
||||
pub fn dht_publickey_unpack(pk: *mut PublicKey, dat: *const u8, dat_size: size_t) -> c_int;
|
||||
pub fn dht_publickey_pack(pk: *mut PublicKey, out: *const c_char, out_size: size_t) -> c_int;
|
||||
pub fn dht_publickey_get_id(pk: *const PublicKey) -> InfoHash;
|
||||
pub fn dht_publickey_get_long_id(pk: *const PublicKey) -> PkId;
|
||||
pub fn dht_publickey_check_signature(pk: *const PublicKey, data: *const c_char, data_size: size_t, signature: *const c_char, signature_size: size_t) -> bool;
|
||||
pub fn dht_publickey_encrypt(pk: *const PublicKey, data: *const c_char, data_size: size_t) -> *mut Blob;
|
||||
|
||||
// dht::crypto::PrivateKey
|
||||
pub fn dht_privatekey_generate(key_length_bits: c_uint) -> *mut PrivateKey;
|
||||
pub fn dht_privatekey_import(dat: *const u8, data_size: size_t, password: *const c_char) -> *mut PrivateKey;
|
||||
pub fn dht_privatekey_get_publickey(pk: *const PrivateKey) -> *mut PublicKey;
|
||||
pub fn dht_privatekey_delete(pk: *mut PrivateKey);
|
||||
|
||||
// dht::crypto::Certificate
|
||||
pub fn dht_certificate_import(dat: *const u8, dat_size: size_t) -> *mut DhtCertificate;
|
||||
pub fn dht_certificate_get_id(cert: *const DhtCertificate) -> InfoHash;
|
||||
pub fn dht_certificate_get_long_id(cert: *const DhtCertificate) -> PkId;
|
||||
pub fn dht_certificate_get_publickey(cert: *const DhtCertificate) -> *mut PublicKey;
|
||||
pub fn dht_certificate_delete(cert: *mut DhtCertificate);
|
||||
|
||||
pub fn dht_identity_generate(common_name: *const c_char, ca: *const DhtIdentity) -> DhtIdentity;
|
||||
pub fn dht_identity_delete(ca: *mut DhtIdentity);
|
||||
|
||||
// dht::OpToken
|
||||
pub fn dht_op_token_delete(token: *mut OpToken);
|
||||
|
||||
// dht::DhtRunner
|
||||
pub fn dht_runner_config_default(config: *mut DhtRunnerConfig);
|
||||
pub fn dht_runner_new() -> *mut DhtRunner;
|
||||
pub fn dht_runner_get_id(dht: *const DhtRunner) -> InfoHash;
|
||||
pub fn dht_runner_get_node_id(dht: *const DhtRunner) -> InfoHash;
|
||||
pub fn dht_runner_delete(dht: *mut DhtRunner);
|
||||
pub fn dht_runner_run(dht: *mut DhtRunner, port: in_port_t);
|
||||
pub fn dht_runner_run_config(dht: *mut DhtRunner, port: in_port_t, config: *const DhtRunnerConfig);
|
||||
pub fn dht_runner_bootstrap(dht: *mut DhtRunner, host: *const c_char, service: *const c_char);
|
||||
pub fn dht_runner_get(dht: *mut DhtRunner, h: *const InfoHash,
|
||||
get_cb: extern fn(*mut Value, *mut c_void) -> bool,
|
||||
done_cb: extern fn(bool, *mut c_void),
|
||||
cb_user_data: *mut c_void);
|
||||
pub fn dht_runner_put(dht: *mut DhtRunner, h: *const InfoHash, v: *const Value,
|
||||
done_cb: extern fn(bool, *mut c_void),
|
||||
cb_user_data: *mut c_void,
|
||||
permanent: bool);
|
||||
pub fn dht_runner_put_signed(dht: *mut DhtRunner, h: *const InfoHash, v: *const Value,
|
||||
done_cb: extern fn(bool, *mut c_void),
|
||||
cb_user_data: *mut c_void,
|
||||
permanent: bool);
|
||||
pub fn dht_runner_put_encrypted(dht: *mut DhtRunner, h: *const InfoHash,
|
||||
to: *const InfoHash, v: *const Value,
|
||||
done_cb: extern fn(bool, *mut c_void),
|
||||
cb_user_data: *mut c_void,
|
||||
permanent: bool);
|
||||
pub fn dht_runner_put_permanent(dht: *mut DhtRunner, h: *const InfoHash, v: *const Value,
|
||||
done_cb: extern fn(bool, *mut c_void),
|
||||
cb_user_data: *mut c_void);
|
||||
pub fn dht_runner_cancel_put(dht: *mut DhtRunner, h: *const InfoHash, vid: u64);
|
||||
pub fn dht_runner_listen(dht: *mut DhtRunner, h: *const InfoHash,
|
||||
cb: extern fn(*mut Value, bool, *mut c_void) -> bool,
|
||||
done_cb: extern fn(*mut c_void),
|
||||
cb_user_data: *mut c_void) -> *mut OpToken;
|
||||
pub fn dht_runner_cancel_listen(dht: *mut DhtRunner, h: *const InfoHash,
|
||||
token: *const OpToken);
|
||||
pub fn dht_runner_shutdown(dht: *mut DhtRunner, done_cb: extern fn(bool, *mut c_void),
|
||||
cb_user_data: *mut c_void);
|
||||
pub fn dht_runner_get_public_address(dht: *const DhtRunner) -> *mut *mut OsSocketAddr;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use crate::ffi::*;
|
||||
use std::ffi::CStr;
|
||||
use std::ffi::CString;
|
||||
use std::fmt;
|
||||
|
||||
pub use crate::ffi::InfoHash;
|
||||
|
||||
impl InfoHash {
|
||||
pub fn new() -> InfoHash {
|
||||
InfoHash {
|
||||
d: [0; 20]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn random() -> InfoHash {
|
||||
let mut h = InfoHash::new();
|
||||
unsafe {
|
||||
dht_infohash_random(&mut h);
|
||||
}
|
||||
h
|
||||
}
|
||||
|
||||
pub fn get(data: &str) -> InfoHash {
|
||||
let mut h = InfoHash::new();
|
||||
unsafe {
|
||||
let c_str = CString::new(data).unwrap();
|
||||
dht_infohash_get(&mut h, c_str.as_ptr() as *mut u8, data.len());
|
||||
}
|
||||
h
|
||||
}
|
||||
|
||||
pub fn from_bytes(data: &Vec<u8>) -> InfoHash {
|
||||
let mut h = InfoHash::new();
|
||||
unsafe {
|
||||
dht_infohash_get(&mut h, data.as_ptr() as *mut u8, data.len());
|
||||
}
|
||||
h
|
||||
}
|
||||
|
||||
pub fn from_hex(data: &str) -> InfoHash {
|
||||
let mut h = InfoHash::new();
|
||||
unsafe {
|
||||
let c_str = CString::new(data).unwrap();
|
||||
dht_infohash_from_hex(&mut h, c_str.as_ptr());
|
||||
}
|
||||
h
|
||||
}
|
||||
|
||||
pub fn is_zero(&self) -> bool {
|
||||
unsafe {
|
||||
dht_infohash_is_zero(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for InfoHash {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
unsafe {
|
||||
let self_str = CStr::from_ptr(
|
||||
dht_infohash_print(self)
|
||||
).to_str().unwrap_or("");
|
||||
write!(f, "{}", self_str)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
extern crate libc;
|
||||
extern crate os_socketaddr;
|
||||
|
||||
mod blob;
|
||||
pub mod crypto;
|
||||
mod dhtrunner;
|
||||
mod ffi;
|
||||
mod infohash;
|
||||
mod pkid;
|
||||
mod value;
|
||||
|
||||
pub use blob::Blob;
|
||||
pub use dhtrunner::{ DhtRunner, DhtRunnerConfig, OpToken };
|
||||
pub use infohash::InfoHash;
|
||||
pub use pkid::PkId;
|
||||
pub use value::{ DataView, Value };
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use crate::ffi::*;
|
||||
use std::ffi::CStr;
|
||||
use std::fmt;
|
||||
|
||||
pub use crate::ffi::PkId;
|
||||
|
||||
impl fmt::Display for PkId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
unsafe {
|
||||
let self_str = CStr::from_ptr(
|
||||
dht_pkid_print(self)
|
||||
).to_str().unwrap_or("");
|
||||
write!(f, "{}", self_str)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2022 Savoir-faire Linux Inc.
|
||||
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#![allow(dead_code)]
|
||||
|
||||
use crate::ffi::*;
|
||||
use std::fmt;
|
||||
use std::ffi::{ CString, IntoStringError };
|
||||
use std::str;
|
||||
use std::slice;
|
||||
|
||||
pub use crate::ffi::{DataView, Value};
|
||||
|
||||
impl Value {
|
||||
pub fn new(data: &str) -> Box<Value> {
|
||||
unsafe {
|
||||
Box::from_raw(dht_value_new(data.as_bytes().as_ptr(),
|
||||
data.as_bytes().len()))
|
||||
}
|
||||
}
|
||||
pub fn from_bytes(data: &Vec<u8>) -> Box<Value> {
|
||||
unsafe {
|
||||
Box::from_raw(dht_value_new(data.as_ptr(),
|
||||
data.len()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> u64 {
|
||||
unsafe {
|
||||
dht_value_get_id(self)
|
||||
}
|
||||
}
|
||||
|
||||
fn dataview(&self) -> DataView {
|
||||
unsafe {
|
||||
dht_value_get_data(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_bytes(&self) -> Vec<u8> {
|
||||
unsafe {
|
||||
let dv = dht_value_get_data(self);
|
||||
let slice = slice::from_raw_parts_mut(dv.data as *mut _, dv.size);
|
||||
slice.to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn boxed(&mut self) -> Box<Value> {
|
||||
unsafe {
|
||||
Box::from_raw(dht_value_ref(self))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recipient(&self) -> InfoHash {
|
||||
unsafe {
|
||||
dht_value_get_recipient(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn owner(&self) -> Option<Box<PublicKey>> {
|
||||
unsafe {
|
||||
let ptr = dht_value_get_owner(self);
|
||||
if ptr.is_null() {
|
||||
return None;
|
||||
}
|
||||
Some(Box::from_raw(ptr))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn user_type(&self) -> Result<String, IntoStringError> {
|
||||
unsafe {
|
||||
CString::from_raw(dht_value_get_user_type(self) as *mut _).into_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Value {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
dht_value_unref(&mut *self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Value {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
unsafe {
|
||||
let dataview = self.dataview();
|
||||
let slice = slice::from_raw_parts(dataview.data, dataview.size);
|
||||
write!(f, "{}", str::from_utf8(slice).unwrap_or(""))
|
||||
}
|
||||
}
|
||||
}
|
||||
+53
-9
@@ -1,13 +1,19 @@
|
||||
SUBDIRS = argon2
|
||||
|
||||
lib_LTLIBRARIES = libopendht.la
|
||||
|
||||
AM_CPPFLAGS = -I../include/opendht
|
||||
libopendht_la_LDFLAGS = @LDFLAGS@ @GnuTLS_LIBS@ @Nettle_LIBS@
|
||||
libopendht_la_LIBADD = ./argon2/libargon2.la
|
||||
|
||||
libopendht_la_SOURCES = \
|
||||
libopendht_la_CPPFLAGS = @CPPFLAGS@ -I$(top_srcdir)/include/opendht @Argon2_CFLAGS@ @JsonCpp_CFLAGS@ @MsgPack_CFLAGS@ @OpenSSL_CFLAGS@ @Fmt_CFLAGS@
|
||||
libopendht_la_LIBADD = @Argon2_LIBS@ @JsonCpp_LIBS@ @GnuTLS_LIBS@ @Nettle_LIBS@ @OpenSSL_LIBS@ @Fmt_LIBS@
|
||||
libopendht_la_LDFLAGS = @LDFLAGS@ -version-number @OPENDHT_MAJOR_VERSION@:@OPENDHT_MINOR_VERSION@:@OPENDHT_PATCH_VERSION@
|
||||
libopendht_la_SOURCES = \
|
||||
dht.cpp \
|
||||
storage.h \
|
||||
listener.h \
|
||||
request.h \
|
||||
search.h \
|
||||
value_cache.h \
|
||||
op_cache.h \
|
||||
op_cache.cpp \
|
||||
net.h \
|
||||
parsed_message.h \
|
||||
node_cache.cpp \
|
||||
callbacks.cpp \
|
||||
routing_table.cpp \
|
||||
@@ -19,7 +25,10 @@ libopendht_la_SOURCES = \
|
||||
crypto.cpp \
|
||||
securedht.cpp \
|
||||
dhtrunner.cpp \
|
||||
default_types.cpp
|
||||
default_types.cpp \
|
||||
log.cpp \
|
||||
network_utils.cpp \
|
||||
thread_pool.cpp
|
||||
|
||||
if WIN32
|
||||
libopendht_la_SOURCES += rng.cpp
|
||||
@@ -27,13 +36,16 @@ endif
|
||||
|
||||
nobase_include_HEADERS = \
|
||||
../include/opendht.h \
|
||||
../include/opendht/def.h \
|
||||
../include/opendht/dht.h \
|
||||
../include/opendht/callbacks.h \
|
||||
../include/opendht/node_cache.h \
|
||||
../include/opendht/routing_table.h \
|
||||
../include/opendht/network_engine.h \
|
||||
../include/opendht/scheduler.h \
|
||||
../include/opendht/rate_limiter.h \
|
||||
../include/opendht/utils.h \
|
||||
../include/opendht/sockaddr.h \
|
||||
../include/opendht/infohash.h \
|
||||
../include/opendht/node.h \
|
||||
../include/opendht/value.h \
|
||||
@@ -42,4 +54,36 @@ nobase_include_HEADERS = \
|
||||
../include/opendht/dhtrunner.h \
|
||||
../include/opendht/default_types.h \
|
||||
../include/opendht/log.h \
|
||||
../include/opendht/rng.h
|
||||
../include/opendht/log_enable.h \
|
||||
../include/opendht/network_utils.h \
|
||||
../include/opendht/rng.h \
|
||||
../include/opendht/thread_pool.h
|
||||
|
||||
if ENABLE_PROXY_SERVER
|
||||
libopendht_la_SOURCES += dht_proxy_server.cpp
|
||||
nobase_include_HEADERS += ../include/opendht/dht_proxy_server.h
|
||||
endif
|
||||
|
||||
if ENABLE_PROXY_CLIENT
|
||||
libopendht_la_SOURCES += dht_proxy_client.cpp
|
||||
nobase_include_HEADERS += ../include/opendht/dht_proxy_client.h ../include/opendht/dht_interface.h
|
||||
endif
|
||||
|
||||
libopendht_la_SOURCES += base64.h base64.cpp
|
||||
if PROXY_CLIENT_OR_SERVER
|
||||
libopendht_la_SOURCES += http.cpp compat/os_cert.cpp
|
||||
nobase_include_HEADERS += ../include/opendht/proxy.h ../include/opendht/http.h compat/os_cert.h
|
||||
endif
|
||||
|
||||
if ENABLE_PEER_DISCOVERY
|
||||
libopendht_la_SOURCES += peer_discovery.cpp
|
||||
nobase_include_HEADERS += ../include/opendht/peer_discovery.h
|
||||
endif
|
||||
|
||||
if ENABLE_INDEXATION
|
||||
libopendht_la_SOURCES += indexation/pht.cpp
|
||||
nobase_include_HEADERS += ../include/opendht/indexation/pht.h
|
||||
endif
|
||||
|
||||
clean-local:
|
||||
rm -rf libargon2.la
|
||||
Loaded 100 of 191 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user