Compare commits
380 commits
xethyrion-
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
e599dd2f34 | ||
ee0c287a34 | |||
662825493d | |||
|
0432315d33 | ||
9491e69d76 | |||
94b782cfaa | |||
35b20c9a2e | |||
|
0037be0594 | ||
|
a0731525ea | ||
|
31e6142e68 | ||
|
30a59c950e | ||
|
8644dbbb56 | ||
|
9bdfed92f1 | ||
|
65a61268a3 | ||
|
14ad68b9bb | ||
|
48b9647a3c | ||
|
f3db50c3ba | ||
|
f96209fc4b | ||
|
d9f8260c6e | ||
|
6160dd841b | ||
|
8b2a8ef3ab | ||
|
5e034f14bc | ||
|
01e5222ea7 | ||
|
0b6add5146 | ||
|
db0a519e66 | ||
|
df7a3e8761 | ||
|
1fdd490e89 | ||
|
eeabd85bc3 | ||
|
77dc2f1472 | ||
|
80f97f4aff | ||
|
8ce3c68b72 | ||
|
e839163d91 | ||
|
711ec1df30 | ||
|
0c31ef7331 | ||
|
b72f247939 | ||
|
9087c58113 | ||
|
f9bec0ac20 | ||
|
91f5c50a71 | ||
|
9afaf535d6 | ||
|
3d7dd87ef3 | ||
ae6fe6dc29 | |||
4e556221bd | |||
|
a315af8534 | ||
|
e394723fb6 | ||
|
cfde84b15c | ||
|
b399762d82 | ||
|
8cdaf919fe | ||
|
575be24be2 | ||
|
98b1e0fef4 | ||
|
f69ab92469 | ||
|
b99a78a4d3 | ||
|
c0855c9630 | ||
|
a6a54fb69b | ||
|
71c216defb | ||
|
996687cfce | ||
|
9c63ed1315 | ||
|
99ffd120e8 | ||
|
861ca0a332 | ||
|
2ee0a87439 | ||
|
42aa1646ab | ||
|
8aa7b7ca77 | ||
|
b9c28ceed4 | ||
|
7259028bd0 | ||
|
596a811d5e | ||
3b2e94ebe2 | |||
495a92f5f8 | |||
|
cd922633b2 | ||
|
c65301dc28 | ||
|
27d36d8320 | ||
|
7aa81936ec | ||
|
87cc96a011 | ||
|
7f89e80d7b | ||
|
296d164e84 | ||
|
3d425bb30f | ||
|
f5250a71f3 | ||
|
76b860c06f | ||
|
f0a53f2753 | ||
|
3418ee5435 | ||
|
81e103f1e0 | ||
|
058a1d64ab | ||
|
daf715929a | ||
|
2cc7f52fbf | ||
|
5bcd3a99f2 | ||
|
630985c698 | ||
8b8a2844bd | |||
|
abb607c905 | ||
8e56826b8d | |||
6fc2614fe7 | |||
|
f0d19bab97 | ||
|
88baee62a2 | ||
|
5fc40e51ee | ||
|
982c7e9e6e | ||
|
4b1cd8cb11 | ||
|
5404fce6ec | ||
b4e3817d62 | |||
|
9402f31841 | ||
0b45b25d6e | |||
|
d872fb079d | ||
395eb4f031 | |||
|
90262ef657 | ||
|
c54cf989c0 | ||
|
b133a703cc | ||
|
a1b887ba76 | ||
|
9425e2fee3 | ||
|
e094528293 | ||
|
0eab9cc3fc | ||
|
378dd7c051 | ||
e0d5fed53e | |||
|
b301c7e4d2 | ||
|
2051f7714a | ||
a907f1dd2d | |||
|
c06625a00e | ||
|
cfffe36fb2 | ||
|
07551151da | ||
|
215af9cfce | ||
|
9a49ddd27b | ||
|
81d2252229 | ||
|
794217ed6c | ||
|
c0419f2424 | ||
|
262216f6db | ||
|
6db0830c7d | ||
87b0d6b13c | |||
64891c9198 | |||
|
acf506296e | ||
|
fc8fc582b1 | ||
|
395c7d94cf | ||
|
d5c3e01267 | ||
|
3eec8553a6 | ||
|
478fa403fd | ||
|
d0eb0acc9d | ||
0b113cb95d | |||
|
a9481631df | ||
|
78904ff1b6 | ||
|
cb1ea3ceff | ||
|
bf7ba9049c | ||
|
bf87d93136 | ||
|
7dbb6fca29 | ||
|
407f85bc89 | ||
|
f9d836ef6f | ||
|
1eab27fa0e | ||
|
49bd61feb1 | ||
|
b3af783f4e | ||
|
0066670f60 | ||
|
5678a14eb3 | ||
|
1b69cdf1e8 | ||
d66bbdea65 | |||
|
60c6085469 | ||
|
0bc975dfcf | ||
|
d13e4b4318 | ||
|
5b242a2d08 | ||
|
5de67539ef | ||
|
c58bfc9b1e | ||
|
8a492c1e2f | ||
|
5e8683e64d | ||
|
0e24b0a1d3 | ||
|
dd69216c75 | ||
|
d226e480c9 | ||
|
8944e407e9 | ||
|
ced96dcf81 | ||
|
c58134db1c | ||
|
6754ba77b3 | ||
|
46b6921ddf | ||
|
3cc75058f0 | ||
|
2b24b00324 | ||
|
b578e5c13a | ||
|
ef53c886a0 | ||
|
d1d4d19fa8 | ||
|
e58909b875 | ||
|
3fa822aed6 | ||
|
5910d63c3d | ||
|
17dbad3bce | ||
|
e5e565272e | ||
|
9131559a04 | ||
|
986d054c6c | ||
|
0638996684 | ||
7632949e30 | |||
|
836fb5387b | ||
|
fa7a6826b1 | ||
|
82c8911b71 | ||
|
bc01f36839 | ||
|
835b70c97f | ||
|
ac5a622400 | ||
|
3a68619016 | ||
|
e4f1c9b39c | ||
|
e22da601ea | ||
|
9bceade4d8 | ||
|
d969f48324 | ||
|
45c997cd9a | ||
|
059a3286c9 | ||
5941a3593a | |||
|
b3bb857f3a | ||
|
73d7d5daed | ||
|
e1135fef3b | ||
|
10aa932e08 | ||
|
692d3406c8 | ||
|
108792ca44 | ||
|
3a8a85e628 | ||
|
9924d5dcf7 | ||
|
4368e052e2 | ||
|
5507937bff | ||
|
a63910e0d7 | ||
bde4d4debe | |||
f7ddfa0088 | |||
|
937504c3f2 | ||
|
b26c94ec2d | ||
cce43e2bce | |||
|
2bb70b01be | ||
|
f47a6116f0 | ||
|
2232d03854 | ||
|
1fe5be1c5c | ||
|
6e19c14bab | ||
|
250b88601a | ||
|
cb05b249ba | ||
|
d038b5d6f7 | ||
|
069379acf4 | ||
|
656b0b50b7 | ||
f2e7c3c475 | |||
297283168c | |||
13b8b8bf27 | |||
8da685b5cf | |||
4af0bedba1 | |||
9f190fb5dc | |||
8fb67ec7e1 | |||
51639b354a | |||
a88636ae7e | |||
a2e31f1edd | |||
aa4b433fb0 | |||
|
6c936de20e | ||
|
a5e5a71ddd | ||
|
d6ee0f7d5d | ||
|
def5331d4c | ||
|
3de4b757e4 | ||
|
e756fdf510 | ||
|
a991dab1bc | ||
|
43bf2caeff | ||
|
03696dd6e7 | ||
|
a2a08156a7 | ||
|
427b5c948f | ||
|
2b0a29a06b | ||
|
62096ffc1c | ||
|
5d558f5229 | ||
|
794608a7ea | ||
|
bc19434490 | ||
|
94d6005587 | ||
|
950ad31760 | ||
|
2efd59cbd5 | ||
|
7b61c70639 | ||
|
089d072736 | ||
|
87cf779704 | ||
|
58b5bc8cdd | ||
|
624c2280e4 | ||
|
6f94f7e6c0 | ||
|
396a27d712 | ||
|
01531693d6 | ||
|
e90d966e44 | ||
|
96b97ad347 | ||
|
59af1c0b9e | ||
|
8feb946087 | ||
|
6c64b55176 | ||
|
d25e088b7c | ||
|
640e4be5be | ||
|
25a2fa0c65 | ||
|
618264c709 | ||
|
83242b0046 | ||
|
8edc94509c | ||
|
cfcabaecd0 | ||
|
0b238eb5ac | ||
|
e8b8d06f24 | ||
|
93c4988f90 | ||
|
f494344b15 | ||
|
43ca313854 | ||
|
15a8abff9a | ||
|
cd5d347e63 | ||
|
d4fee344a7 | ||
3a38623dcc | |||
|
f1aac5d707 | ||
|
7a6b138444 | ||
|
ae48e058dd | ||
|
daefe7604f | ||
|
f8e4286740 | ||
fbd862431c | |||
|
9379d0904f | ||
|
2307309fa2 | ||
|
ea5f04e27a | ||
|
8a2aabc013 | ||
|
81fe9305bf | ||
4cd4750f6f | |||
|
c972852c9a | ||
0522bedd2a | |||
de6c4b2495 | |||
cd09930077 | |||
1535273259 | |||
|
33431a3007 | ||
|
8e19636de5 | ||
2431aba246 | |||
|
be4bee3119 | ||
|
e88f40cad0 | ||
|
a3ec7bf41d | ||
|
631835e23b | ||
3e39fec066 | |||
|
929bc07168 | ||
|
1a13f39cf6 | ||
7d3418e9c7 | |||
3c3474eacd | |||
6448a35a72 | |||
4c330e29be | |||
|
9882c28743 | ||
|
61d0adc445 | ||
e1582544b1 | |||
f7a029ade3 | |||
9d194ad484 | |||
ef380f2a69 | |||
d9774095d3 | |||
37bab54a5c | |||
|
addb5ba302 | ||
|
c2fe60949e | ||
00bf29b0fa | |||
|
396dfc6abc | ||
|
fa3dd77e96 | ||
|
9d77b2709f | ||
|
8f44c857db | ||
|
3004f1411a | ||
|
1dcbf8962e | ||
|
15b432c70e | ||
|
8d2aeab016 | ||
|
914d3caaed | ||
a569159cc2 | |||
5f22be643c | |||
7691fe0dc2 | |||
049078effc | |||
|
da4b1f86f4 | ||
|
7fa0fc6a7b | ||
|
f8cdaf0a97 | ||
|
c8fdb084e4 | ||
b17403b61a | |||
6b9d205ef2 | |||
41b7579f21 | |||
a60300d160 | |||
5b8678b48c | |||
a94aa609b9 | |||
b914745b47 | |||
|
a654022d32 | ||
|
57ed4fd5e4 | ||
|
bece919c40 | ||
|
a1166e840a | ||
fea5d3b8bf | |||
aa49c7dc35 | |||
|
87f0cbedbc | ||
|
1d63cdb4a1 | ||
|
34301c286e | ||
|
5168bc87e0 | ||
|
033dd767b4 | ||
|
88d177cf4a | ||
|
bf0cbfb412 | ||
9ec06749f2 | |||
|
876e2dfcf2 | ||
|
363a773a1e | ||
|
4a99078c12 | ||
|
a36ba20557 | ||
|
4fe768c2c5 | ||
|
e0b0552b39 | ||
786bd5d80b | |||
73a7690725 | |||
7a3d0c5e0b | |||
84f24797f4 | |||
|
e69f10a68f | ||
|
75bee8d1ea | ||
|
4a6b88871a | ||
f8723eb8c7 | |||
|
a180a63160 | ||
|
da6490ac7f | ||
|
8a80d2afc7 | ||
|
a7d8bc82b7 | ||
|
f9d927f375 | ||
|
0245aa6549 | ||
|
5f452969fd | ||
|
f79d2f9f0c | ||
496ae3ec6c | |||
|
bf9ddfd6f6 | ||
|
362ef81cb7 |
240 changed files with 22952 additions and 1806 deletions
42
.cproject
42
.cproject
|
@ -21,15 +21,20 @@
|
||||||
<builder id="cdt.managedbuild.target.gnu.builder.base.1870748436" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.base"/>
|
<builder id="cdt.managedbuild.target.gnu.builder.base.1870748436" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.base"/>
|
||||||
<tool id="cdt.managedbuild.tool.gnu.archiver.base.1038880733" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/>
|
<tool id="cdt.managedbuild.tool.gnu.archiver.base.1038880733" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/>
|
||||||
<tool id="cdt.managedbuild.tool.gnu.cpp.compiler.base.1701788660" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.base">
|
<tool id="cdt.managedbuild.tool.gnu.cpp.compiler.base.1701788660" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.base">
|
||||||
|
<option id="gnu.cpp.compiler.option.include.paths.776431500" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath">
|
||||||
|
<listOptionValue builtIn="false" value=""${workspace_loc:/c-libp2p/include}""/>
|
||||||
|
<listOptionValue builtIn="false" value=""${workspace_loc:/c-multiaddr/include}""/>
|
||||||
|
</option>
|
||||||
<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1773271782" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
|
<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1773271782" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
|
||||||
</tool>
|
</tool>
|
||||||
<tool id="cdt.managedbuild.tool.gnu.c.compiler.base.365209117" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.base">
|
<tool id="cdt.managedbuild.tool.gnu.c.compiler.base.365209117" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.base">
|
||||||
<option id="gnu.c.compiler.option.include.paths.106437885" name="Include paths (-I)" superClass="gnu.c.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath">
|
<option id="gnu.c.compiler.option.include.paths.106437885" name="Include paths (-I)" superClass="gnu.c.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath">
|
||||||
<listOptionValue builtIn="false" value=""${workspace_loc:/c-libp2p/include}""/>
|
|
||||||
<listOptionValue builtIn="false" value=""${workspace_loc:/c-multihash/include}""/>
|
<listOptionValue builtIn="false" value=""${workspace_loc:/c-multihash/include}""/>
|
||||||
<listOptionValue builtIn="false" value=""${workspace_loc:/c-multiaddr}""/>
|
|
||||||
<listOptionValue builtIn="false" value=""${workspace_loc:/lmdb}""/>
|
<listOptionValue builtIn="false" value=""${workspace_loc:/lmdb}""/>
|
||||||
<listOptionValue builtIn="false" value=""${workspace_loc:/c-ipfs/include}""/>
|
<listOptionValue builtIn="false" value=""${workspace_loc:/c-ipfs/include}""/>
|
||||||
|
<listOptionValue builtIn="false" value=""${workspace_loc:/c-protobuf}""/>
|
||||||
|
<listOptionValue builtIn="false" value=""${workspace_loc:/c-libp2p/include}""/>
|
||||||
|
<listOptionValue builtIn="false" value=""${workspace_loc:/c-multiaddr/include}""/>
|
||||||
</option>
|
</option>
|
||||||
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.581176638" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
|
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.581176638" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
|
||||||
</tool>
|
</tool>
|
||||||
|
@ -41,6 +46,10 @@
|
||||||
</inputType>
|
</inputType>
|
||||||
</tool>
|
</tool>
|
||||||
<tool id="cdt.managedbuild.tool.gnu.assembler.base.451195806" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.base">
|
<tool id="cdt.managedbuild.tool.gnu.assembler.base.451195806" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.base">
|
||||||
|
<option id="gnu.both.asm.option.include.paths.1115343131" name="Include paths (-I)" superClass="gnu.both.asm.option.include.paths" valueType="includePath">
|
||||||
|
<listOptionValue builtIn="false" value=""${workspace_loc:/c-libp2p/include}""/>
|
||||||
|
<listOptionValue builtIn="false" value=""${workspace_loc:/c-multiaddr/include}""/>
|
||||||
|
</option>
|
||||||
<inputType id="cdt.managedbuild.tool.gnu.assembler.input.1339940304" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
|
<inputType id="cdt.managedbuild.tool.gnu.assembler.input.1339940304" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
|
||||||
</tool>
|
</tool>
|
||||||
</toolChain>
|
</toolChain>
|
||||||
|
@ -72,7 +81,6 @@
|
||||||
<buildTargets>
|
<buildTargets>
|
||||||
<target name="all" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
<target name="all" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
||||||
<buildCommand>make</buildCommand>
|
<buildCommand>make</buildCommand>
|
||||||
<buildArguments/>
|
|
||||||
<buildTarget>all</buildTarget>
|
<buildTarget>all</buildTarget>
|
||||||
<stopOnError>true</stopOnError>
|
<stopOnError>true</stopOnError>
|
||||||
<useDefaultCommand>true</useDefaultCommand>
|
<useDefaultCommand>true</useDefaultCommand>
|
||||||
|
@ -80,7 +88,6 @@
|
||||||
</target>
|
</target>
|
||||||
<target name="clean" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
<target name="clean" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
||||||
<buildCommand>make</buildCommand>
|
<buildCommand>make</buildCommand>
|
||||||
<buildArguments/>
|
|
||||||
<buildTarget>clean</buildTarget>
|
<buildTarget>clean</buildTarget>
|
||||||
<stopOnError>true</stopOnError>
|
<stopOnError>true</stopOnError>
|
||||||
<useDefaultCommand>true</useDefaultCommand>
|
<useDefaultCommand>true</useDefaultCommand>
|
||||||
|
@ -88,7 +95,6 @@
|
||||||
</target>
|
</target>
|
||||||
<target name="rebuild" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
<target name="rebuild" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
||||||
<buildCommand>make</buildCommand>
|
<buildCommand>make</buildCommand>
|
||||||
<buildArguments/>
|
|
||||||
<buildTarget>rebuild</buildTarget>
|
<buildTarget>rebuild</buildTarget>
|
||||||
<stopOnError>true</stopOnError>
|
<stopOnError>true</stopOnError>
|
||||||
<useDefaultCommand>true</useDefaultCommand>
|
<useDefaultCommand>true</useDefaultCommand>
|
||||||
|
@ -96,13 +102,39 @@
|
||||||
</target>
|
</target>
|
||||||
<target name="clean" path="thirdparty" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
<target name="clean" path="thirdparty" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
||||||
<buildCommand>make</buildCommand>
|
<buildCommand>make</buildCommand>
|
||||||
|
<buildArguments/>
|
||||||
<buildTarget>clean</buildTarget>
|
<buildTarget>clean</buildTarget>
|
||||||
<stopOnError>true</stopOnError>
|
<stopOnError>true</stopOnError>
|
||||||
<useDefaultCommand>false</useDefaultCommand>
|
<useDefaultCommand>false</useDefaultCommand>
|
||||||
<runAllBuilders>true</runAllBuilders>
|
<runAllBuilders>true</runAllBuilders>
|
||||||
</target>
|
</target>
|
||||||
|
<target name="all" path="test" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
||||||
|
<buildCommand>make</buildCommand>
|
||||||
|
<buildArguments/>
|
||||||
|
<buildTarget>all</buildTarget>
|
||||||
|
<stopOnError>true</stopOnError>
|
||||||
|
<useDefaultCommand>true</useDefaultCommand>
|
||||||
|
<runAllBuilders>true</runAllBuilders>
|
||||||
|
</target>
|
||||||
|
<target name="clean" path="test" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
||||||
|
<buildCommand>make</buildCommand>
|
||||||
|
<buildArguments/>
|
||||||
|
<buildTarget>clean</buildTarget>
|
||||||
|
<stopOnError>true</stopOnError>
|
||||||
|
<useDefaultCommand>true</useDefaultCommand>
|
||||||
|
<runAllBuilders>true</runAllBuilders>
|
||||||
|
</target>
|
||||||
|
<target name="rebuild" path="test" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
||||||
|
<buildCommand>make</buildCommand>
|
||||||
|
<buildArguments/>
|
||||||
|
<buildTarget>rebuild</buildTarget>
|
||||||
|
<stopOnError>true</stopOnError>
|
||||||
|
<useDefaultCommand>true</useDefaultCommand>
|
||||||
|
<runAllBuilders>true</runAllBuilders>
|
||||||
|
</target>
|
||||||
<target name="clean" path="thirdparty/ipfsaddr" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
<target name="clean" path="thirdparty/ipfsaddr" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
||||||
<buildCommand>make</buildCommand>
|
<buildCommand>make</buildCommand>
|
||||||
|
<buildArguments/>
|
||||||
<buildTarget>clean</buildTarget>
|
<buildTarget>clean</buildTarget>
|
||||||
<stopOnError>true</stopOnError>
|
<stopOnError>true</stopOnError>
|
||||||
<useDefaultCommand>false</useDefaultCommand>
|
<useDefaultCommand>false</useDefaultCommand>
|
||||||
|
|
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
custom: https://www.blockchain.com/btc/payment_request?address=1AFGT5gVj7xhfjgHTuwEoaV56WTCh7Gjf1#BITCOIN_ONLY
|
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -7,3 +7,10 @@
|
||||||
*.o
|
*.o
|
||||||
.settings/language.settings.xml
|
.settings/language.settings.xml
|
||||||
test/test_ipfs
|
test/test_ipfs
|
||||||
|
main/ipfs
|
||||||
|
test/test1.txt
|
||||||
|
test/test2.txt
|
||||||
|
test/scripts/hello.bin
|
||||||
|
test/scripts/hello2.bin
|
||||||
|
test/scripts/testlog.txt
|
||||||
|
test/scripts/generate_file
|
||||||
|
|
7
.gitmodules
vendored
Normal file
7
.gitmodules
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[submodule "c-libp2p"]
|
||||||
|
path = c-libp2p
|
||||||
|
url = https://github.com/Agorise/c-libp2p.git
|
||||||
|
[submodule "lmdb"]
|
||||||
|
path = lmdb
|
||||||
|
url = https://github.com/jmjatlanta/lmdb.git
|
||||||
|
branch = mdb.master
|
5
LICENSE
5
LICENSE
|
@ -1,6 +1,9 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2016 BitShares Munich IVS
|
Copyright (c) 2019 AGORISE, LTD.
|
||||||
|
An International Business Company, Cyprus Reg# ΗΕ375959
|
||||||
|
|
||||||
|
Contains works from BitShares Munich IVS
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
30
Makefile
30
Makefile
|
@ -3,31 +3,57 @@ DEBUG = true
|
||||||
export DEBUG
|
export DEBUG
|
||||||
|
|
||||||
all:
|
all:
|
||||||
|
cd c-libp2p; make all;
|
||||||
|
cd lmdb/libraries/liblmdb; make all;
|
||||||
cd blocks; make all;
|
cd blocks; make all;
|
||||||
cd cid; make all;
|
cd cid; make all;
|
||||||
cd cmd; make all;
|
cd cmd; make all;
|
||||||
cd commands; make all;
|
cd commands; make all;
|
||||||
cd core; make all;
|
cd core; make all;
|
||||||
|
cd exchange; make all;
|
||||||
|
cd importer; make all;
|
||||||
|
cd journal; make all;
|
||||||
|
cd merkledag; make all;
|
||||||
cd multibase; make all;
|
cd multibase; make all;
|
||||||
cd os; make all;
|
cd pin; make all;
|
||||||
cd repo; make all;
|
cd repo; make all;
|
||||||
cd flatfs; make all;
|
cd flatfs; make all;
|
||||||
cd datastore; make all;
|
cd datastore; make all;
|
||||||
cd thirdparty; make all;
|
cd thirdparty; make all;
|
||||||
|
cd unixfs; make all;
|
||||||
|
cd routing; make all;
|
||||||
|
cd dnslink; make all;
|
||||||
|
cd namesys; make all;
|
||||||
|
cd path; make all;
|
||||||
|
cd util; make all;
|
||||||
|
cd main; make all;
|
||||||
cd test; make all;
|
cd test; make all;
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
cd c-libp2p; make clean;
|
||||||
|
cd lmdb/libraries/liblmdb; make clean;
|
||||||
cd blocks; make clean;
|
cd blocks; make clean;
|
||||||
cd cid; make clean;
|
cd cid; make clean;
|
||||||
cd cmd; make clean;
|
cd cmd; make clean;
|
||||||
cd commands; make clean;
|
cd commands; make clean;
|
||||||
cd core; make clean;
|
cd core; make clean;
|
||||||
|
cd exchange; make clean;
|
||||||
|
cd importer; make clean;
|
||||||
|
cd journal; make clean;
|
||||||
|
cd merkledag; make clean;
|
||||||
cd multibase; make clean;
|
cd multibase; make clean;
|
||||||
cd os; make clean;
|
cd pin; make clean;
|
||||||
cd repo; make clean;
|
cd repo; make clean;
|
||||||
cd flatfs; make clean;
|
cd flatfs; make clean;
|
||||||
cd datastore; make clean;
|
cd datastore; make clean;
|
||||||
cd thirdparty; make clean;
|
cd thirdparty; make clean;
|
||||||
|
cd unixfs; make clean;
|
||||||
|
cd main; make clean;
|
||||||
|
cd routing; make clean;
|
||||||
|
cd dnslink; make clean;
|
||||||
|
cd namesys; make clean;
|
||||||
|
cd path; make clean;
|
||||||
|
cd util; make clean;
|
||||||
cd test; make clean;
|
cd test; make clean;
|
||||||
|
|
||||||
rebuild: clean all
|
rebuild: clean all
|
||||||
|
|
35
README.md
35
README.md
|
@ -1,7 +1,28 @@
|
||||||
# c-ipfs
|
# C-IPFS
|
||||||
IPFS implementation in C, (not just an API client library).<br>
|
IPFS implementation in C, (not just an API client library).
|
||||||
<br>
|
|
||||||
getting started: https://github.com/ipfs/specs/blob/master/overviews/implement-ipfs.md <br>
|
## Quick start for users:
|
||||||
specifications: https://github.com/ipfs/specs <br>
|
* **ipfs init** to create an ipfs repository on your machine
|
||||||
getting started: https://github.com/ipfs/community/issues/177 <br>
|
* **ipfs add MyFile.txt** to add a file to the repository, will return with a hash that can be used to retrieve the file.
|
||||||
libp2p: https://github.com/libp2p/specs <br>
|
* **ipfs cat _hash_** to retrieve a file from the repository
|
||||||
|
|
||||||
|
## For techies (ipfs spec docs):
|
||||||
|
* [getting started](https://github.com/ipfs/specs/blob/master/overviews/implement-ipfs.md)
|
||||||
|
* [specifications](https://github.com/ipfs/specs)
|
||||||
|
* [getting started](https://github.com/ipfs/community/issues/177)
|
||||||
|
* [libp2p](https://github.com/libp2p/specs)
|
||||||
|
|
||||||
|
## Prerequisites: To compile the C version you will need, all included as submodules:
|
||||||
|
* [lmdb](https://github.com/jmjatlanta/lmdb)
|
||||||
|
* [c-protobuf](https://github.com/Agorise/c-protobuf)
|
||||||
|
* [c-multihash](https://github.com/Agorise/c-multihash)
|
||||||
|
* [c-multiaddr](https://github.com/Agorise/c-multiaddr)
|
||||||
|
* [c-libp2p](https://github.com/Agorise/c-libp2p)
|
||||||
|
|
||||||
|
And of course this project at https://github.com/Agorise/c-ipfs
|
||||||
|
|
||||||
|
## How to compile the C version:
|
||||||
|
```
|
||||||
|
git submodule update --init --recursive
|
||||||
|
make all
|
||||||
|
```
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -O0 -I../include -I../../c-libp2p/include -I../../c-multihash/include -I../../c-multiaddr/include
|
CFLAGS = -O0 -I../include -I../c-libp2p/include -I../c-libp2p/c-multihash/include -I../c-libp2p/c-multiaddr/include -I../c-libp2p/c-protobuf -Wall -std=gnu99
|
||||||
|
|
||||||
ifdef DEBUG
|
ifdef DEBUG
|
||||||
CFLAGS += -g3
|
CFLAGS += -g3
|
||||||
|
|
157
blocks/block.c
157
blocks/block.c
|
@ -9,6 +9,105 @@
|
||||||
#include "ipfs/blocks/block.h"
|
#include "ipfs/blocks/block.h"
|
||||||
#include "ipfs/cid/cid.h"
|
#include "ipfs/cid/cid.h"
|
||||||
|
|
||||||
|
/***
|
||||||
|
* The protobuf functions
|
||||||
|
*/
|
||||||
|
// protobuf fields: data & data_length cid
|
||||||
|
enum WireType ipfs_block_message_fields[] = { WIRETYPE_LENGTH_DELIMITED, WIRETYPE_LENGTH_DELIMITED};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the approximate size of an encoded block
|
||||||
|
* @param block the block to measure
|
||||||
|
* @returns the approximate size needed to encode the protobuf
|
||||||
|
*/
|
||||||
|
size_t ipfs_blocks_block_protobuf_encode_size(const struct Block* block) {
|
||||||
|
return 22 + ipfs_cid_protobuf_encode_size(block->cid) + block->data_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode the Block into protobuf format
|
||||||
|
* @param block the block to encode
|
||||||
|
* @param buffer the buffer to fill
|
||||||
|
* @param max_buffer_size the max size of the buffer
|
||||||
|
* @param bytes_written the number of bytes used
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_blocks_block_protobuf_encode(const struct Block* block, unsigned char* buffer, size_t max_buffer_length, size_t* bytes_written) {
|
||||||
|
// data & data_size
|
||||||
|
size_t bytes_used = 0;
|
||||||
|
*bytes_written = 0;
|
||||||
|
int retVal = 0;
|
||||||
|
retVal = protobuf_encode_length_delimited(1, ipfs_block_message_fields[0], (char*)block->data, block->data_length, &buffer[*bytes_written], max_buffer_length - *bytes_written, &bytes_used);
|
||||||
|
*bytes_written += bytes_used;
|
||||||
|
// cid
|
||||||
|
size_t cid_size = ipfs_cid_protobuf_encode_size(block->cid);
|
||||||
|
unsigned char cid[cid_size];
|
||||||
|
retVal = ipfs_cid_protobuf_encode(block->cid, cid, cid_size, &cid_size);
|
||||||
|
if (retVal == 0)
|
||||||
|
return 0;
|
||||||
|
retVal = protobuf_encode_length_delimited(2, ipfs_block_message_fields[1], (char*)cid, cid_size, &buffer[*bytes_written], max_buffer_length - *bytes_written, &bytes_used);
|
||||||
|
if (retVal == 0)
|
||||||
|
return 0;
|
||||||
|
*bytes_written += bytes_used;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Decode from a protobuf stream into a Block struct
|
||||||
|
* @param buffer the buffer to pull from
|
||||||
|
* @param buffer_length the length of the buffer
|
||||||
|
* @param block the block to fill
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_blocks_block_protobuf_decode(const unsigned char* buffer, const size_t buffer_length, struct Block** block) {
|
||||||
|
size_t pos = 0;
|
||||||
|
int retVal = 0;
|
||||||
|
unsigned char* temp_buffer = NULL;
|
||||||
|
size_t temp_size;
|
||||||
|
|
||||||
|
*block = ipfs_block_new();
|
||||||
|
if (*block == NULL)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
while(pos < buffer_length) {
|
||||||
|
size_t bytes_read = 0;
|
||||||
|
int field_no;
|
||||||
|
enum WireType field_type;
|
||||||
|
if (protobuf_decode_field_and_type(&buffer[pos], buffer_length, &field_no, &field_type, &bytes_read) == 0) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
pos += bytes_read;
|
||||||
|
switch(field_no) {
|
||||||
|
case (1): // data
|
||||||
|
if (protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&((*block)->data), &((*block)->data_length), &bytes_read) == 0)
|
||||||
|
goto exit;
|
||||||
|
pos += bytes_read;
|
||||||
|
break;
|
||||||
|
case (2): // cid
|
||||||
|
if (protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&temp_buffer, &temp_size, &bytes_read) == 0)
|
||||||
|
goto exit;
|
||||||
|
pos += bytes_read;
|
||||||
|
if (ipfs_cid_protobuf_decode(temp_buffer, temp_size, &((*block)->cid)) == 0)
|
||||||
|
goto exit;
|
||||||
|
free(temp_buffer);
|
||||||
|
temp_buffer = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
retVal = 1;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
if (retVal == 0) {
|
||||||
|
ipfs_block_free(*block);
|
||||||
|
}
|
||||||
|
if (temp_buffer != NULL)
|
||||||
|
free(temp_buffer);
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Create a new block based on the incoming data
|
* Create a new block based on the incoming data
|
||||||
* @param data the data to base the block on
|
* @param data the data to base the block on
|
||||||
|
@ -16,35 +115,40 @@
|
||||||
* @param block a pointer to the struct Block that will be created
|
* @param block a pointer to the struct Block that will be created
|
||||||
* @returns true(1) on success
|
* @returns true(1) on success
|
||||||
*/
|
*/
|
||||||
int ipfs_blocks_block_new(const unsigned char* data, size_t data_size, struct Block** block) {
|
struct Block* ipfs_block_new() {
|
||||||
|
|
||||||
// allocate memory for structure
|
// allocate memory for structure
|
||||||
(*block) = (struct Block*)malloc(sizeof(struct Block));
|
struct Block* block = (struct Block*)malloc(sizeof(struct Block));
|
||||||
if ((*block) == NULL)
|
if ( block == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
block->data = NULL;
|
||||||
|
block->data_length = 0;
|
||||||
|
block->cid = NULL;
|
||||||
|
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ipfs_blocks_block_add_data(const unsigned char* data, size_t data_size, struct Block* block) {
|
||||||
// cid
|
// cid
|
||||||
unsigned char hash[32];
|
unsigned char hash[32];
|
||||||
if (libp2p_crypto_hashing_sha256(data, data_size, &hash[0]) == 0) {
|
if (libp2p_crypto_hashing_sha256(data, data_size, &hash[0]) == 0) {
|
||||||
free(*block);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ipfs_cid_new(0, hash, 32, CID_PROTOBUF, &((*block)->cid)) == 0) {
|
block->cid = ipfs_cid_new(0, hash, 32, CID_DAG_PROTOBUF);
|
||||||
free(*block);
|
if (block->cid == NULL) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
(*block)->data_length = data_size;
|
block->data_length = data_size;
|
||||||
|
|
||||||
(*block)->data = malloc(sizeof(unsigned char) * data_size);
|
block->data = malloc(sizeof(unsigned char) * data_size);
|
||||||
if ( (*block)->data == NULL) {
|
if ( block->data == NULL) {
|
||||||
ipfs_cid_free((*block)->cid);
|
ipfs_cid_free(block->cid);
|
||||||
free(*block);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy( (*block)->data, data, data_size);
|
memcpy( block->data, data, data_size);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,10 +157,37 @@ int ipfs_blocks_block_new(const unsigned char* data, size_t data_size, struct Bl
|
||||||
* @param block the block to free
|
* @param block the block to free
|
||||||
* @returns true(1) on success
|
* @returns true(1) on success
|
||||||
*/
|
*/
|
||||||
int ipfs_blocks_block_free(struct Block* block) {
|
int ipfs_block_free(struct Block* block) {
|
||||||
|
if (block != NULL) {
|
||||||
|
if (block->cid != NULL)
|
||||||
ipfs_cid_free(block->cid);
|
ipfs_cid_free(block->cid);
|
||||||
if (block->data != NULL)
|
if (block->data != NULL)
|
||||||
free(block->data);
|
free(block->data);
|
||||||
free(block);
|
free(block);
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Make a copy of a block
|
||||||
|
* @param original the original
|
||||||
|
* @returns a new Block that is a copy
|
||||||
|
*/
|
||||||
|
struct Block* ipfs_block_copy(struct Block* original) {
|
||||||
|
struct Block* copy = ipfs_block_new();
|
||||||
|
if (copy != NULL) {
|
||||||
|
copy->data_length = original->data_length;
|
||||||
|
copy->data = (unsigned char*) malloc(original->data_length);
|
||||||
|
if (copy->data == NULL) {
|
||||||
|
ipfs_block_free(copy);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memcpy(copy->data, original->data, original->data_length);
|
||||||
|
copy->cid = ipfs_cid_copy(original->cid);
|
||||||
|
if (copy->cid == NULL) {
|
||||||
|
ipfs_block_free(copy);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
|
@ -4,15 +4,54 @@
|
||||||
#include "libp2p/crypto/encoding/base32.h"
|
#include "libp2p/crypto/encoding/base32.h"
|
||||||
#include "ipfs/cid/cid.h"
|
#include "ipfs/cid/cid.h"
|
||||||
#include "ipfs/blocks/block.h"
|
#include "ipfs/blocks/block.h"
|
||||||
|
#include "ipfs/blocks/blockstore.h"
|
||||||
#include "ipfs/datastore/ds_helper.h"
|
#include "ipfs/datastore/ds_helper.h"
|
||||||
#include "ipfs/repo/fsrepo/fs_repo.h"
|
#include "ipfs/repo/fsrepo/fs_repo.h"
|
||||||
|
#include "libp2p/os/utils.h"
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Create a new Blockstore struct
|
||||||
|
* @param fs_repo the FSRepo to use
|
||||||
|
* @returns the new Blockstore struct, or NULL if there was a problem.
|
||||||
|
*/
|
||||||
|
struct Blockstore* ipfs_blockstore_new(const struct FSRepo* fs_repo) {
|
||||||
|
struct Blockstore* blockstore = (struct Blockstore*) malloc(sizeof(struct Blockstore));
|
||||||
|
if(blockstore != NULL) {
|
||||||
|
blockstore->blockstoreContext = (struct BlockstoreContext*) malloc(sizeof(struct BlockstoreContext));
|
||||||
|
if (blockstore->blockstoreContext == NULL) {
|
||||||
|
free(blockstore);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
blockstore->blockstoreContext->fs_repo = fs_repo;
|
||||||
|
blockstore->Delete = ipfs_blockstore_delete;
|
||||||
|
blockstore->Get = ipfs_blockstore_get;
|
||||||
|
blockstore->Has = ipfs_blockstore_has;
|
||||||
|
blockstore->Put = ipfs_blockstore_put;
|
||||||
|
}
|
||||||
|
return blockstore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release resources of a Blockstore struct
|
||||||
|
* @param blockstore the struct to free
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_blockstore_free(struct Blockstore* blockstore) {
|
||||||
|
if (blockstore != NULL) {
|
||||||
|
if (blockstore->blockstoreContext != NULL)
|
||||||
|
free(blockstore->blockstoreContext);
|
||||||
|
free(blockstore);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a block based on its Cid
|
* Delete a block based on its Cid
|
||||||
* @param cid the Cid to look for
|
* @param cid the Cid to look for
|
||||||
* @param returns true(1) on success
|
* @param returns true(1) on success
|
||||||
*/
|
*/
|
||||||
int ipfs_blockstore_delete(struct Cid* cid, struct FSRepo* fs_repo) {
|
int ipfs_blockstore_delete(const struct BlockstoreContext* context, struct Cid* cid) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,18 +60,88 @@ int ipfs_blockstore_delete(struct Cid* cid, struct FSRepo* fs_repo) {
|
||||||
* @param cid the Cid to look for
|
* @param cid the Cid to look for
|
||||||
* @returns true(1) if found
|
* @returns true(1) if found
|
||||||
*/
|
*/
|
||||||
int ipfs_blockstore_has(struct Cid* cid, struct FSRepo* fs_repo) {
|
int ipfs_blockstore_has(const struct BlockstoreContext* context, struct Cid* cid) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned char* ipfs_blockstore_cid_to_base32(const struct Cid* cid) {
|
||||||
|
size_t key_length = libp2p_crypto_encoding_base32_encode_size(cid->hash_length);
|
||||||
|
unsigned char* buffer = (unsigned char*)malloc(key_length + 1);
|
||||||
|
if (buffer == NULL)
|
||||||
|
return NULL;
|
||||||
|
int retVal = ipfs_datastore_helper_ds_key_from_binary(cid->hash, cid->hash_length, &buffer[0], key_length, &key_length);
|
||||||
|
if (retVal == 0) {
|
||||||
|
free(buffer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
buffer[key_length] = 0;
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* ipfs_blockstore_hash_to_base32(const unsigned char* hash, size_t hash_length) {
|
||||||
|
size_t key_length = libp2p_crypto_encoding_base32_encode_size(hash_length);
|
||||||
|
unsigned char* buffer = (unsigned char*)malloc(key_length + 1);
|
||||||
|
if (buffer == NULL)
|
||||||
|
return NULL;
|
||||||
|
int retVal = ipfs_datastore_helper_ds_key_from_binary(hash, hash_length, &buffer[0], key_length, &key_length);
|
||||||
|
if (retVal == 0) {
|
||||||
|
free(buffer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
buffer[key_length] = 0;
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* ipfs_blockstore_path_get(const struct FSRepo* fs_repo, const char* filename) {
|
||||||
|
int filepath_size = strlen(fs_repo->path) + 12;
|
||||||
|
char filepath[filepath_size];
|
||||||
|
int retVal = os_utils_filepath_join(fs_repo->path, "blockstore", filepath, filepath_size);
|
||||||
|
if (retVal == 0) {
|
||||||
|
free(filepath);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int complete_filename_size = strlen(filepath) + strlen(filename) + 2;
|
||||||
|
char* complete_filename = (char*)malloc(complete_filename_size);
|
||||||
|
if (complete_filename == NULL)
|
||||||
|
return NULL;
|
||||||
|
retVal = os_utils_filepath_join(filepath, filename, complete_filename, complete_filename_size);
|
||||||
|
return complete_filename;
|
||||||
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Find a block based on its Cid
|
* Find a block based on its Cid
|
||||||
* @param cid the Cid to look for
|
* @param cid the Cid to look for
|
||||||
* @param block where to put the data to be returned
|
* @param block where to put the data to be returned
|
||||||
* @returns true(1) on success
|
* @returns true(1) on success
|
||||||
*/
|
*/
|
||||||
int ipfs_blockstore_get(struct Cid* cid, struct Block* block, struct FSRepo* fs_repo) {
|
int ipfs_blockstore_get(const struct BlockstoreContext* context, struct Cid* cid, struct Block** block) {
|
||||||
return 0;
|
int retVal = 0;
|
||||||
|
// get datastore key, which is a base32 key of the multihash
|
||||||
|
unsigned char* key = ipfs_blockstore_hash_to_base32(cid->hash, cid->hash_length);
|
||||||
|
|
||||||
|
char* filename = ipfs_blockstore_path_get(context->fs_repo, (char*)key);
|
||||||
|
|
||||||
|
size_t file_size = os_utils_file_size(filename);
|
||||||
|
unsigned char buffer[file_size];
|
||||||
|
|
||||||
|
FILE* file = fopen(filename, "rb");
|
||||||
|
if (file == NULL)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
size_t bytes_read = fread(buffer, 1, file_size, file);
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
if (!ipfs_blocks_block_protobuf_decode(buffer, bytes_read, block))
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
(*block)->cid = ipfs_cid_copy(cid);
|
||||||
|
|
||||||
|
retVal = 1;
|
||||||
|
exit:
|
||||||
|
free(key);
|
||||||
|
free(filename);
|
||||||
|
|
||||||
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
|
@ -40,16 +149,217 @@ int ipfs_blockstore_get(struct Cid* cid, struct Block* block, struct FSRepo* fs_
|
||||||
* @param block the block to store
|
* @param block the block to store
|
||||||
* @returns true(1) on success
|
* @returns true(1) on success
|
||||||
*/
|
*/
|
||||||
int ipfs_blockstore_put(struct Block* block, struct FSRepo* fs_repo) {
|
int ipfs_blockstore_put(const struct BlockstoreContext* context, struct Block* block, size_t* bytes_written) {
|
||||||
// from blockstore.go line 118
|
// from blockstore.go line 118
|
||||||
// Get Datastore key, which is a base32 key of the binary,
|
int retVal = 0;
|
||||||
size_t key_length = libp2p_crypto_encoding_base32_encode_size(block->data_length);
|
|
||||||
unsigned char key[key_length];
|
|
||||||
int retVal = ipfs_datastore_helper_ds_key_from_binary(block->data, block->data_length, &key[0], key_length, &key_length);
|
|
||||||
if (retVal == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// send to Put with key
|
// Get Datastore key, which is a base32 key of the multihash,
|
||||||
fs_repo->config->datastore->datastore_put(key, key_length, block, fs_repo->config->datastore);
|
unsigned char* key = ipfs_blockstore_cid_to_base32(block->cid);
|
||||||
|
if (key == NULL) {
|
||||||
|
free(key);
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: put this in subdirectories
|
||||||
|
|
||||||
|
// turn the block into a binary array
|
||||||
|
size_t protobuf_len = ipfs_blocks_block_protobuf_encode_size(block);
|
||||||
|
unsigned char protobuf[protobuf_len];
|
||||||
|
retVal = ipfs_blocks_block_protobuf_encode(block, protobuf, protobuf_len, &protobuf_len);
|
||||||
|
if (retVal == 0) {
|
||||||
|
free(key);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now write byte array to file
|
||||||
|
char* filename = ipfs_blockstore_path_get(context->fs_repo, (char*)key);
|
||||||
|
if (filename == NULL) {
|
||||||
|
free(key);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* file = fopen(filename, "wb");
|
||||||
|
*bytes_written = fwrite(protobuf, 1, protobuf_len, file);
|
||||||
|
fclose(file);
|
||||||
|
if (*bytes_written != protobuf_len) {
|
||||||
|
free(key);
|
||||||
|
free(filename);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// send to Put with key (this is now done separately)
|
||||||
|
//fs_repo->config->datastore->datastore_put(key, key_length, block->data, block->data_length, fs_repo->config->datastore);
|
||||||
|
|
||||||
|
free(key);
|
||||||
|
free(filename);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Put a struct UnixFS in the blockstore
|
||||||
|
* @param unix_fs the structure
|
||||||
|
* @param fs_repo the repo to place the strucure in
|
||||||
|
* @param bytes_written the number of bytes written to the blockstore
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_blockstore_put_unixfs(const struct UnixFS* unix_fs, const struct FSRepo* fs_repo, size_t* bytes_written) {
|
||||||
|
// from blockstore.go line 118
|
||||||
|
int retVal = 0;
|
||||||
|
|
||||||
|
// Get Datastore key, which is a base32 key of the multihash,
|
||||||
|
unsigned char* key = ipfs_blockstore_hash_to_base32(unix_fs->hash, unix_fs->hash_length);
|
||||||
|
if (key == NULL) {
|
||||||
|
free(key);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: put this in subdirectories
|
||||||
|
|
||||||
|
// turn the block into a binary array
|
||||||
|
size_t protobuf_len = ipfs_unixfs_protobuf_encode_size(unix_fs);
|
||||||
|
unsigned char protobuf[protobuf_len];
|
||||||
|
retVal = ipfs_unixfs_protobuf_encode(unix_fs, protobuf, protobuf_len, &protobuf_len);
|
||||||
|
if (retVal == 0) {
|
||||||
|
free(key);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now write byte array to file
|
||||||
|
char* filename = ipfs_blockstore_path_get(fs_repo, (char*)key);
|
||||||
|
if (filename == NULL) {
|
||||||
|
free(key);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* file = fopen(filename, "wb");
|
||||||
|
*bytes_written = fwrite(protobuf, 1, protobuf_len, file);
|
||||||
|
fclose(file);
|
||||||
|
if (*bytes_written != protobuf_len) {
|
||||||
|
free(key);
|
||||||
|
free(filename);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(key);
|
||||||
|
free(filename);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Find a UnixFS struct based on its hash
|
||||||
|
* @param hash the hash to look for
|
||||||
|
* @param hash_length the length of the hash
|
||||||
|
* @param unix_fs the struct to fill
|
||||||
|
* @param fs_repo where to look for the data
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_blockstore_get_unixfs(const unsigned char* hash, size_t hash_length, struct UnixFS** block, const struct FSRepo* fs_repo) {
|
||||||
|
// get datastore key, which is a base32 key of the multihash
|
||||||
|
unsigned char* key = ipfs_blockstore_hash_to_base32(hash, hash_length);
|
||||||
|
|
||||||
|
char* filename = ipfs_blockstore_path_get(fs_repo, (char*)key);
|
||||||
|
|
||||||
|
size_t file_size = os_utils_file_size(filename);
|
||||||
|
unsigned char buffer[file_size];
|
||||||
|
|
||||||
|
FILE* file = fopen(filename, "rb");
|
||||||
|
size_t bytes_read = fread(buffer, 1, file_size, file);
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
int retVal = ipfs_unixfs_protobuf_decode(buffer, bytes_read, block);
|
||||||
|
|
||||||
|
free(key);
|
||||||
|
free(filename);
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Put a struct Node in the blockstore
|
||||||
|
* @param node the structure
|
||||||
|
* @param fs_repo the repo to place the strucure in
|
||||||
|
* @param bytes_written the number of bytes written to the blockstore
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_blockstore_put_node(const struct HashtableNode* node, const struct FSRepo* fs_repo, size_t* bytes_written) {
|
||||||
|
// from blockstore.go line 118
|
||||||
|
int retVal = 0;
|
||||||
|
|
||||||
|
// Get Datastore key, which is a base32 key of the multihash,
|
||||||
|
unsigned char* key = ipfs_blockstore_hash_to_base32(node->hash, node->hash_size);
|
||||||
|
if (key == NULL) {
|
||||||
|
free(key);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: put this in subdirectories
|
||||||
|
|
||||||
|
// turn the block into a binary array
|
||||||
|
size_t protobuf_len = ipfs_hashtable_node_protobuf_encode_size(node);
|
||||||
|
unsigned char protobuf[protobuf_len];
|
||||||
|
retVal = ipfs_hashtable_node_protobuf_encode(node, protobuf, protobuf_len, &protobuf_len);
|
||||||
|
if (retVal == 0) {
|
||||||
|
free(key);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now write byte array to file
|
||||||
|
char* filename = ipfs_blockstore_path_get(fs_repo, (char*)key);
|
||||||
|
if (filename == NULL) {
|
||||||
|
free(key);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* file = fopen(filename, "wb");
|
||||||
|
*bytes_written = fwrite(protobuf, 1, protobuf_len, file);
|
||||||
|
fclose(file);
|
||||||
|
if (*bytes_written != protobuf_len) {
|
||||||
|
free(key);
|
||||||
|
free(filename);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(key);
|
||||||
|
free(filename);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Find a UnixFS struct based on its hash
|
||||||
|
* @param hash the hash to look for
|
||||||
|
* @param hash_length the length of the hash
|
||||||
|
* @param unix_fs the struct to fill
|
||||||
|
* @param fs_repo where to look for the data
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_blockstore_get_node(const unsigned char* hash, size_t hash_length, struct HashtableNode** node, const struct FSRepo* fs_repo) {
|
||||||
|
// get datastore key, which is a base32 key of the multihash
|
||||||
|
unsigned char* key = ipfs_blockstore_hash_to_base32(hash, hash_length);
|
||||||
|
|
||||||
|
char* filename = ipfs_blockstore_path_get(fs_repo, (char*)key);
|
||||||
|
|
||||||
|
size_t file_size = os_utils_file_size(filename);
|
||||||
|
unsigned char buffer[file_size];
|
||||||
|
|
||||||
|
FILE* file = fopen(filename, "rb");
|
||||||
|
size_t bytes_read = fread(buffer, 1, file_size, file);
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
// now we have the block, convert it to a node
|
||||||
|
struct Block* block;
|
||||||
|
if (!ipfs_blocks_block_protobuf_decode(buffer, bytes_read, &block)) {
|
||||||
|
free(key);
|
||||||
|
free(filename);
|
||||||
|
ipfs_block_free(block);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int retVal = ipfs_hashtable_node_protobuf_decode(block->data, block->data_length, node);
|
||||||
|
|
||||||
|
free(key);
|
||||||
|
free(filename);
|
||||||
|
ipfs_block_free(block);
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
1
c-libp2p
Submodule
1
c-libp2p
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit d0c319a88cd1f2cb3a219420b5a0b930e72562e2
|
|
@ -1,5 +1,5 @@
|
||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -O0 -I../include -I../../c-libp2p/include -I../../c-multihash/include -I../../c-multiaddr/include
|
CFLAGS = -O0 -I../include -I../c-libp2p/include -I../c-libp2p/c-multihash/include -I../c-libp2p/c-multiaddr/include -I../c-libp2p/c-protobuf -Wall -std=gnu99
|
||||||
|
|
||||||
ifdef DEBUG
|
ifdef DEBUG
|
||||||
CFLAGS += -g3
|
CFLAGS += -g3
|
||||||
|
@ -7,7 +7,7 @@ endif
|
||||||
|
|
||||||
LFLAGS =
|
LFLAGS =
|
||||||
DEPS =
|
DEPS =
|
||||||
OBJS = cid.o
|
OBJS = cid.o set.o
|
||||||
|
|
||||||
%.o: %.c $(DEPS)
|
%.o: %.c $(DEPS)
|
||||||
$(CC) -c -o $@ $< $(CFLAGS)
|
$(CC) -c -o $@ $< $(CFLAGS)
|
||||||
|
|
265
cid/cid.c
265
cid/cid.c
|
@ -9,38 +9,114 @@
|
||||||
#include "ipfs/cid/cid.h"
|
#include "ipfs/cid/cid.h"
|
||||||
#include "libp2p/crypto/encoding/base58.h"
|
#include "libp2p/crypto/encoding/base58.h"
|
||||||
#include "ipfs/multibase/multibase.h"
|
#include "ipfs/multibase/multibase.h"
|
||||||
|
#include "mh/hashes.h"
|
||||||
#include "mh/multihash.h"
|
#include "mh/multihash.h"
|
||||||
#include "multiaddr/varint.h"
|
#include "varint.h"
|
||||||
|
|
||||||
|
enum WireType ipfs_cid_message_fields[] = { WIRETYPE_VARINT, WIRETYPE_VARINT, WIRETYPE_LENGTH_DELIMITED };
|
||||||
|
|
||||||
|
|
||||||
|
size_t ipfs_cid_protobuf_encode_size(const struct Cid* cid) {
|
||||||
|
if (cid != NULL)
|
||||||
|
return 11+12+cid->hash_length+11;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ipfs_cid_protobuf_encode(const struct Cid* cid, unsigned char* buffer, size_t buffer_length, size_t* bytes_written) {
|
||||||
|
size_t bytes_used;
|
||||||
|
*bytes_written = 0;
|
||||||
|
int retVal = 0;
|
||||||
|
if (cid != NULL) {
|
||||||
|
retVal = protobuf_encode_varint(1, ipfs_cid_message_fields[0], cid->version, buffer, buffer_length, &bytes_used);
|
||||||
|
if (retVal == 0)
|
||||||
|
return 0;
|
||||||
|
*bytes_written += bytes_used;
|
||||||
|
retVal = protobuf_encode_varint(2, ipfs_cid_message_fields[1], cid->codec, &buffer[*bytes_written], buffer_length - (*bytes_written), &bytes_used);
|
||||||
|
if (retVal == 0)
|
||||||
|
return 0;
|
||||||
|
*bytes_written += bytes_used;
|
||||||
|
retVal = protobuf_encode_length_delimited(3, ipfs_cid_message_fields[2], (char*)cid->hash, cid->hash_length, &buffer[*bytes_written], buffer_length - (*bytes_written), &bytes_used);
|
||||||
|
if (retVal == 0)
|
||||||
|
return 0;
|
||||||
|
*bytes_written += bytes_used;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ipfs_cid_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct Cid** output) {
|
||||||
|
// short cut for nulls
|
||||||
|
if (buffer_length == 0) {
|
||||||
|
*output = NULL;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pos = 0;
|
||||||
|
int version = 0;
|
||||||
|
unsigned char* hash;
|
||||||
|
size_t hash_length;
|
||||||
|
char codec = 0;
|
||||||
|
int retVal = 0;
|
||||||
|
|
||||||
|
while(pos < buffer_length) {
|
||||||
|
size_t bytes_read = 0;
|
||||||
|
int field_no;
|
||||||
|
enum WireType field_type;
|
||||||
|
if (protobuf_decode_field_and_type(&buffer[pos], buffer_length, &field_no, &field_type, &bytes_read) == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
pos += bytes_read;
|
||||||
|
switch(field_no) {
|
||||||
|
case (1):
|
||||||
|
version = varint_decode(&buffer[pos], buffer_length - pos, &bytes_read);
|
||||||
|
pos += bytes_read;
|
||||||
|
break;
|
||||||
|
case (2):
|
||||||
|
codec = varint_decode(&buffer[pos], buffer_length - pos, &bytes_read);
|
||||||
|
pos += bytes_read;
|
||||||
|
break;
|
||||||
|
case (3):
|
||||||
|
retVal = protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&hash, &hash_length, &bytes_read);
|
||||||
|
if (retVal == 0)
|
||||||
|
return 0;
|
||||||
|
pos += bytes_read;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
*output = ipfs_cid_new(version, hash, hash_length, codec);
|
||||||
|
retVal = *output != NULL;
|
||||||
|
free(hash);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new CID based on the given hash
|
* Create a new CID based on the given hash
|
||||||
* @param version the version
|
* @param version the version
|
||||||
* @param hash the multihash
|
* @param hash the multihash
|
||||||
* @param hash_length the length of the multihash in bytes
|
* @param hash_length the length of the multihash in bytes
|
||||||
* @param codec the codec to be used (NOTE: For version 0, this should be CID_PROTOBUF)
|
* @param codec the codec to be used (NOTE: For version 0, this should be CID_DAG_PROTOBUF)
|
||||||
* @param cid where to put the results
|
* @returns the new Cid or NULL if there was a problem
|
||||||
* @returns true(1) on success
|
|
||||||
*/
|
*/
|
||||||
int ipfs_cid_new(int version, unsigned char* hash, size_t hash_length, const char codec, struct Cid** ptrToCid) {
|
struct Cid* ipfs_cid_new(int version, const unsigned char* hash, size_t hash_length, const char codec) {
|
||||||
// allocate memory
|
struct Cid* cid = (struct Cid*) malloc(sizeof(struct Cid));
|
||||||
*ptrToCid = (struct Cid*)malloc(sizeof(struct Cid));
|
if (cid != NULL) {
|
||||||
struct Cid* cid = *ptrToCid;
|
cid->hash_length = hash_length;
|
||||||
if (cid == NULL)
|
if (hash_length == 0 || hash == NULL) {
|
||||||
return 0;
|
cid->hash = NULL;
|
||||||
cid->hash = malloc(sizeof(unsigned char) * hash_length);
|
} else {
|
||||||
|
cid->hash = (unsigned char*) malloc(sizeof(unsigned char) * hash_length);
|
||||||
if (cid->hash == NULL) {
|
if (cid->hash == NULL) {
|
||||||
free(cid);
|
free(cid);
|
||||||
return 0;
|
return NULL;
|
||||||
}
|
}
|
||||||
// assign values
|
memcpy(cid->hash, hash, hash_length);
|
||||||
|
}
|
||||||
|
// assign other values
|
||||||
cid->version = version;
|
cid->version = version;
|
||||||
cid->codec = codec;
|
cid->codec = codec;
|
||||||
memcpy(cid->hash, hash, hash_length);
|
}
|
||||||
cid->hash_length = hash_length;
|
return cid;
|
||||||
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
|
@ -49,20 +125,63 @@ int ipfs_cid_new(int version, unsigned char* hash, size_t hash_length, const cha
|
||||||
* @returns 1
|
* @returns 1
|
||||||
*/
|
*/
|
||||||
int ipfs_cid_free(struct Cid* cid) {
|
int ipfs_cid_free(struct Cid* cid) {
|
||||||
if (cid->hash != NULL)
|
if (cid != NULL) {
|
||||||
|
if (cid->hash != NULL) {
|
||||||
free(cid->hash);
|
free(cid->hash);
|
||||||
|
cid->hash = NULL;
|
||||||
|
}
|
||||||
free(cid);
|
free(cid);
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Fill a Cid struct based on a base 58 encoded string
|
* Make a copy of a Cid
|
||||||
|
* @param original the original
|
||||||
|
* @returns a copy of the original
|
||||||
|
*/
|
||||||
|
struct Cid* ipfs_cid_copy(const struct Cid* original) {
|
||||||
|
struct Cid* copy = (struct Cid*) malloc(sizeof(struct Cid));
|
||||||
|
if (copy != NULL) {
|
||||||
|
copy->codec = original->codec;
|
||||||
|
copy->version = original->version;
|
||||||
|
copy->hash_length = original->hash_length;
|
||||||
|
copy->hash = (unsigned char*) malloc(original->hash_length);
|
||||||
|
if (copy->hash == NULL) {
|
||||||
|
ipfs_cid_free(copy);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memcpy(copy->hash, original->hash, original->hash_length);
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Create a CID from an ipfs or ipns string (i.e. "/ipns/QmAb12CD..."
|
||||||
|
* @param incoming the incoming string
|
||||||
|
* @param cid the resultant Cid
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_cid_decode_hash_from_ipfs_ipns_string(const char* incoming, struct Cid** cid) {
|
||||||
|
if (incoming == NULL)
|
||||||
|
return 0;
|
||||||
|
if (strstr(incoming, "/ipfs/") != incoming && strstr(incoming, "/ipns/") != incoming)
|
||||||
|
return 0;
|
||||||
|
const char* base58 = &incoming[6];
|
||||||
|
char* slash = strstr(incoming, "/");
|
||||||
|
if (slash != NULL)
|
||||||
|
slash[0] = '\0';
|
||||||
|
return ipfs_cid_decode_hash_from_base58((unsigned char*)base58, strlen(base58), cid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Fill a Cid struct based on a base 58 encoded multihash
|
||||||
* @param incoming the string
|
* @param incoming the string
|
||||||
* @param incoming_size the size of the string
|
* @param incoming_size the size of the string
|
||||||
* @cid the Cid struct to fill
|
* @cid the Cid struct to fill
|
||||||
* @return true(1) on success
|
* @return true(1) on success
|
||||||
*/
|
*/
|
||||||
int ipfs_cid_decode_from_string(const unsigned char* incoming, size_t incoming_length, struct Cid** cid) {
|
int ipfs_cid_decode_hash_from_base58(const unsigned char* incoming, size_t incoming_length, struct Cid** cid) {
|
||||||
int retVal = 0;
|
int retVal = 0;
|
||||||
|
|
||||||
if (incoming_length < 2)
|
if (incoming_length < 2)
|
||||||
|
@ -77,7 +196,8 @@ int ipfs_cid_decode_from_string(const unsigned char* incoming, size_t incoming_l
|
||||||
if (retVal == 0)
|
if (retVal == 0)
|
||||||
return 0;
|
return 0;
|
||||||
// now we have the hash, build the object
|
// now we have the hash, build the object
|
||||||
return ipfs_cid_new(0, hash, hash_length, CID_PROTOBUF, cid);
|
*cid = ipfs_cid_new(0, &hash[2], hash_length - 2, CID_DAG_PROTOBUF);
|
||||||
|
return *cid != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: finish this
|
// TODO: finish this
|
||||||
|
@ -99,17 +219,65 @@ int ipfs_cid_decode_from_string(const unsigned char* incoming, size_t incoming_l
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn a cid hash into a base 58
|
||||||
|
* @param hash the hash to work with
|
||||||
|
* @param hash_length the length of the existing hash
|
||||||
|
* @param buffer where to put the results
|
||||||
|
* @param max_buffer_length the maximum space reserved for the results
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_cid_hash_to_base58(const unsigned char* hash, size_t hash_length, unsigned char* buffer, size_t max_buffer_length) {
|
||||||
|
|
||||||
|
int multihash_len = hash_length + 2;
|
||||||
|
unsigned char multihash[multihash_len];
|
||||||
|
if (mh_new(multihash, MH_H_SHA2_256, hash, hash_length) < 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// base58
|
||||||
|
size_t b58_size = libp2p_crypto_encoding_base58_encode_size(multihash_len);
|
||||||
|
|
||||||
|
if (b58_size > max_buffer_length) // buffer too small
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if( libp2p_crypto_encoding_base58_encode(multihash, multihash_len, &buffer, &max_buffer_length) == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Turn the hash of this CID into a c string
|
||||||
|
* @param cid the cid
|
||||||
|
* @param result a place to allocate and store the string
|
||||||
|
* @returns a pointer to the string (*result) or NULL if there was a problem
|
||||||
|
*/
|
||||||
|
char* ipfs_cid_to_string(const struct Cid* cid, char **result) {
|
||||||
|
size_t str_len = libp2p_crypto_encoding_base58_encode_size(cid->hash_length) + 1;
|
||||||
|
char *str = (char*) malloc(str_len);
|
||||||
|
*result = str;
|
||||||
|
if (str != NULL) {
|
||||||
|
if (!libp2p_crypto_encoding_base58_encode(cid->hash, cid->hash_length, (unsigned char**)&str, &str_len)) {
|
||||||
|
free(str);
|
||||||
|
str = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Turn a multibase decoded string of bytes into a Cid struct
|
* Turn a multibase decoded string of bytes into a Cid struct
|
||||||
* @param incoming the multibase decoded array
|
* @param incoming the multibase decoded array
|
||||||
* @param incoming_size the size of the array
|
* @param incoming_size the size of the array
|
||||||
* @param cid the Cid structure to fill
|
* @param cid the Cid structure to fill
|
||||||
*/
|
*/
|
||||||
int ipfs_cid_cast(unsigned char* incoming, size_t incoming_size, struct Cid* cid) {
|
int ipfs_cid_cast(const unsigned char* incoming, size_t incoming_size, struct Cid* cid) {
|
||||||
// this is a multihash
|
|
||||||
if (incoming_size == 34 && incoming[0] == 18 && incoming[1] == 32) {
|
if (incoming_size == 34 && incoming[0] == 18 && incoming[1] == 32) {
|
||||||
|
// this is a multihash
|
||||||
cid->hash_length = mh_multihash_length(incoming, incoming_size);
|
cid->hash_length = mh_multihash_length(incoming, incoming_size);
|
||||||
cid->codec = CID_PROTOBUF;
|
cid->codec = CID_DAG_PROTOBUF;
|
||||||
cid->version = 0;
|
cid->version = 0;
|
||||||
|
|
||||||
mh_multihash_digest(incoming, incoming_size, &cid->hash, &cid->hash_length);
|
mh_multihash_digest(incoming, incoming_size, &cid->hash, &cid->hash_length);
|
||||||
|
@ -118,22 +286,57 @@ int ipfs_cid_cast(unsigned char* incoming, size_t incoming_size, struct Cid* cid
|
||||||
|
|
||||||
// This is not a multihash. Perhaps it is using varints. Try to peel the information out of the bytes.
|
// This is not a multihash. Perhaps it is using varints. Try to peel the information out of the bytes.
|
||||||
// first the version
|
// first the version
|
||||||
int pos = 0, retVal = 0;
|
int pos = 0;
|
||||||
size_t num_bytes = 0;
|
size_t num_bytes = 0;
|
||||||
num_bytes = uvarint_decode32(&incoming[pos], incoming_size - pos, &cid->version);
|
cid->version = varint_decode(&incoming[pos], incoming_size - pos, &num_bytes);
|
||||||
if (num_bytes < 0 || cid->version > 1 || cid->version < 0)
|
if (num_bytes == 0 || cid->version > 1 || cid->version < 0)
|
||||||
return 0;
|
return 0;
|
||||||
pos = num_bytes;
|
pos = num_bytes;
|
||||||
// now the codec
|
// now the codec
|
||||||
uint32_t codec = 0;
|
uint32_t codec = 0;
|
||||||
num_bytes = uvarint_decode32(&incoming[pos], incoming_size - pos, &codec);
|
codec = varint_decode(&incoming[pos], incoming_size - pos, &num_bytes);
|
||||||
if (num_bytes < 0)
|
if (num_bytes == 0)
|
||||||
return 0;
|
return 0;
|
||||||
cid->codec = codec;
|
cid->codec = codec;
|
||||||
pos += num_bytes;
|
pos += num_bytes;
|
||||||
// now what is left
|
// now what is left
|
||||||
cid->hash_length = incoming_size - pos;
|
cid->hash_length = incoming_size - pos;
|
||||||
cid->hash = &incoming[pos];
|
cid->hash = (unsigned char*)(&incoming[pos]);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare two cids
|
||||||
|
*
|
||||||
|
* TODO: find a common denominator between versions and codecs so that
|
||||||
|
* we can compare apples to apples.
|
||||||
|
*
|
||||||
|
* @param a side A
|
||||||
|
* @param b side B
|
||||||
|
* @returns < 0 if side A is greater, > 0 if side B is greater, or 0 if equal
|
||||||
|
*/
|
||||||
|
int ipfs_cid_compare(const struct Cid* a, const struct Cid* b) {
|
||||||
|
if (a == NULL && b == NULL)
|
||||||
|
return 0;
|
||||||
|
if (a != NULL && b == NULL)
|
||||||
|
return -1;
|
||||||
|
if (a == NULL && b != NULL)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (a->version != b->version) {
|
||||||
|
return b->version - a->version;
|
||||||
|
}
|
||||||
|
if (a->codec != b->codec) {
|
||||||
|
return ((int)b->codec - (int)a->codec);
|
||||||
|
}
|
||||||
|
if (a->hash_length != b->hash_length) {
|
||||||
|
return b->hash_length - a->hash_length;
|
||||||
|
}
|
||||||
|
for(size_t i = 0; i < a->hash_length; i++) {
|
||||||
|
if (a->hash[i] != b->hash[i]) {
|
||||||
|
return ((int)b->hash[i] - (int)a->hash[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
185
cid/set.c
Normal file
185
cid/set.c
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "ipfs/cid/cid.h"
|
||||||
|
#include "ipfs/util/errs.h"
|
||||||
|
|
||||||
|
struct CidSet *ipfs_cid_set_new ()
|
||||||
|
{
|
||||||
|
return calloc(1, sizeof(struct CidSet));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ipfs_cid_set_destroy (struct CidSet **set)
|
||||||
|
{
|
||||||
|
struct CidSet *prev;
|
||||||
|
|
||||||
|
if (set) {
|
||||||
|
while (*set) {
|
||||||
|
prev = *set;
|
||||||
|
*set = (*set)->next;
|
||||||
|
if (prev->cid) {
|
||||||
|
free (prev->cid);
|
||||||
|
}
|
||||||
|
free (prev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ipfs_cid_set_add (struct CidSet *set, struct Cid *cid, int visit)
|
||||||
|
{
|
||||||
|
if (!set || !cid) {
|
||||||
|
return ErrInvalidParam;
|
||||||
|
}
|
||||||
|
if (!set->cid) { // First item.
|
||||||
|
set->cid = malloc(sizeof (struct Cid));
|
||||||
|
if (!set->cid) {
|
||||||
|
return ErrAllocFailed;
|
||||||
|
}
|
||||||
|
memcpy(set->cid, cid, sizeof (struct Cid));
|
||||||
|
set->cid->hash = calloc(1, cid->hash_length);
|
||||||
|
if (!set->cid->hash) {
|
||||||
|
free (set->cid);
|
||||||
|
return ErrAllocFailed;
|
||||||
|
}
|
||||||
|
memcpy(set->cid->hash, cid->hash, cid->hash_length);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
for (;;) {
|
||||||
|
if ((set->cid->hash_length == cid->hash_length) &&
|
||||||
|
(memcmp(set->cid->hash, cid->hash, cid->hash_length)==0)) {
|
||||||
|
// Already added.
|
||||||
|
if (!visit) {
|
||||||
|
// update with new cid.
|
||||||
|
free(set->cid->hash);
|
||||||
|
memcpy(set->cid, cid, sizeof (struct Cid));
|
||||||
|
set->cid->hash = calloc(1, cid->hash_length);
|
||||||
|
if (!set->cid->hash) {
|
||||||
|
return ErrAllocFailed;
|
||||||
|
}
|
||||||
|
memcpy(set->cid->hash, cid->hash, cid->hash_length);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!set->next) {
|
||||||
|
set->next = ipfs_cid_set_new();
|
||||||
|
if (!set->next) {
|
||||||
|
return ErrAllocFailed;
|
||||||
|
}
|
||||||
|
set = set->next;
|
||||||
|
set->cid = malloc(sizeof (struct Cid));
|
||||||
|
if (!set->cid) {
|
||||||
|
return ErrAllocFailed;
|
||||||
|
}
|
||||||
|
memcpy(set->cid, cid, sizeof (struct Cid));
|
||||||
|
set->cid->hash = calloc(1, cid->hash_length);
|
||||||
|
if (!set->cid->hash) {
|
||||||
|
return ErrAllocFailed;
|
||||||
|
}
|
||||||
|
memcpy(set->cid->hash, cid->hash, cid->hash_length);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
set = set->next;
|
||||||
|
}
|
||||||
|
//this should never get hit
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ipfs_cid_set_has (struct CidSet *set, struct Cid *cid)
|
||||||
|
{
|
||||||
|
if (!set || !cid || !set->cid) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
for (;;) {
|
||||||
|
if ((set->cid->hash_length == cid->hash_length) &&
|
||||||
|
(memcmp(set->cid->hash, cid->hash, cid->hash_length)==0)) {
|
||||||
|
return 1; // has
|
||||||
|
}
|
||||||
|
if (!set->next) {
|
||||||
|
return 0; // end without found.
|
||||||
|
}
|
||||||
|
set = set->next;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ipfs_cid_set_remove (struct CidSet *set, struct Cid *cid)
|
||||||
|
{
|
||||||
|
struct CidSet *prev = set;
|
||||||
|
|
||||||
|
if (!set || !cid || !set->cid) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
for (;;) {
|
||||||
|
if ((set->cid->hash_length == cid->hash_length) &&
|
||||||
|
(memcmp(set->cid->hash, cid->hash, cid->hash_length)==0)) {
|
||||||
|
free (set->cid);
|
||||||
|
if (prev == set) { // first item
|
||||||
|
set = set->next;
|
||||||
|
if (!set) {
|
||||||
|
prev->cid = NULL;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
prev->cid = set->cid;
|
||||||
|
} else {
|
||||||
|
prev->next = set->next;
|
||||||
|
}
|
||||||
|
free (set);
|
||||||
|
return 1; // removed
|
||||||
|
}
|
||||||
|
if (!set->next) {
|
||||||
|
return 0; // end without found.
|
||||||
|
}
|
||||||
|
prev = set;
|
||||||
|
set = set->next;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ipfs_cid_set_len (struct CidSet *set)
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (!set || !set->cid) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (len = 0 ; set ; len++, set = set->next);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char **ipfs_cid_set_keys (struct CidSet *set)
|
||||||
|
{
|
||||||
|
int i, len=ipfs_cid_set_len(set);
|
||||||
|
unsigned char **ret;
|
||||||
|
|
||||||
|
ret = calloc(len+1, sizeof(char*));
|
||||||
|
if (ret) {
|
||||||
|
for (i = 0 ; i<len ; len++) {
|
||||||
|
if (set && set->cid && set->cid->hash) {
|
||||||
|
ret[i] = calloc(1, set->cid->hash_length + 1);
|
||||||
|
if (ret[i]) {
|
||||||
|
memcpy(ret[i], set->cid->hash, set->cid->hash_length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set = set->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ipfs_cid_set_foreach (struct CidSet *set, int (*func)(struct Cid *))
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
while (set) {
|
||||||
|
if (set->cid) {
|
||||||
|
err = func (set->cid);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set = set->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
20
cmd/Makefile
20
cmd/Makefile
|
@ -1,5 +1,23 @@
|
||||||
all:
|
CC = gcc
|
||||||
|
CFLAGS = -O0 -I../include -I../c-libp2p/include -I../c-libp2p/c-multiaddr/include -I../c-libp2p/c-protobuf -Wall -std=gnu99
|
||||||
|
|
||||||
|
ifdef DEBUG
|
||||||
|
CFLAGS += -g3
|
||||||
|
endif
|
||||||
|
|
||||||
|
LFLAGS =
|
||||||
|
DEPS =
|
||||||
|
OBJS = cli.o
|
||||||
|
|
||||||
|
%.o: %.c $(DEPS)
|
||||||
|
$(CC) -c -o $@ $< $(CFLAGS)
|
||||||
|
|
||||||
|
ipfs: $(OBJS)
|
||||||
|
$(CC) -o $@ $^ $(LFLAGS)
|
||||||
|
|
||||||
|
all: $(OBJS)
|
||||||
cd ipfs; make all;
|
cd ipfs; make all;
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
rm -f *.o
|
||||||
cd ipfs; make clean;
|
cd ipfs; make clean;
|
59
cmd/cli.c
Normal file
59
cmd/cli.c
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "ipfs/cmd/cli.h"
|
||||||
|
|
||||||
|
int cli_is_switch(int argc, char** argv, int index) {
|
||||||
|
char* to_test = argv[index];
|
||||||
|
if (to_test[0] == '-') {
|
||||||
|
if (strcmp(to_test, "-c") == 0 || strcmp(to_test, "--config") == 0) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int cli_get_verb_index(struct CliArguments* args) {
|
||||||
|
for(int i = 1; i < args->argc; i++) {
|
||||||
|
int advance_by_more_than_one = cli_is_switch(args->argc, args->argv, i);
|
||||||
|
if (advance_by_more_than_one == 0) {
|
||||||
|
// this is the verb
|
||||||
|
return i;
|
||||||
|
} else {
|
||||||
|
if (advance_by_more_than_one == 2) {
|
||||||
|
// skip the next one
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* cli_get_config_dir(struct CliArguments* args) {
|
||||||
|
for (int i = 1; i < args->argc; i++) {
|
||||||
|
if (args->argv[i][0] == '-') {
|
||||||
|
char* param = args->argv[i];
|
||||||
|
if ((strcmp(param, "-c") == 0 || strcmp(param, "--config") == 0) && args->argc > i + 1) {
|
||||||
|
return args->argv[i+1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct CliArguments* cli_arguments_new(int argc, char** argv) {
|
||||||
|
struct CliArguments* args = (struct CliArguments*) malloc(sizeof(struct CliArguments));
|
||||||
|
if (args != NULL) {
|
||||||
|
args->argc = argc;
|
||||||
|
args->argv = argv;
|
||||||
|
args->verb_index = cli_get_verb_index(args);
|
||||||
|
args->config_dir = cli_get_config_dir(args);
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cli_arguments_free(struct CliArguments* args) {
|
||||||
|
free(args);
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -O0 -I../../include -I../../../c-libp2p/include
|
CFLAGS = -O0 -I../../include -I../../c-libp2p/include -I../../c-libp2p/c-multiaddr/include -I../../c-libp2p/c-protobuf -Wall
|
||||||
|
|
||||||
ifdef DEBUG
|
ifdef DEBUG
|
||||||
CFLAGS += -g3
|
CFLAGS += -g3
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
#include "ipfs/cmd/ipfs/init.h"
|
#include "ipfs/cmd/ipfs/init.h"
|
||||||
#include "ipfs/commands/request.h"
|
#include "ipfs/commands/request.h"
|
||||||
#include "ipfs/commands/command_option.h"
|
#include "ipfs/commands/command_option.h"
|
||||||
#include "ipfs/os/utils.h"
|
#include "libp2p/os/utils.h"
|
||||||
#include "ipfs/core/ipfs_node.h"
|
#include "ipfs/core/ipfs_node.h"
|
||||||
#include "ipfs/core/builder.h"
|
#include "ipfs/core/builder.h"
|
||||||
#include "ipfs/repo/config/config.h"
|
#include "ipfs/repo/config/config.h"
|
||||||
|
@ -34,9 +35,9 @@ int initialize_ipns_keyspace(struct FSRepo* repo) {
|
||||||
return 0;
|
return 0;
|
||||||
//TODO: make a new node, then close it
|
//TODO: make a new node, then close it
|
||||||
//TODO: setup offline routing on new node
|
//TODO: setup offline routing on new node
|
||||||
struct IpfsNode* ipfs_node;
|
struct IpfsNode* ipfs_node = NULL;
|
||||||
struct Context* ctx;
|
struct Context* ctx = NULL;
|
||||||
struct BuildCfg* bld_cfg;
|
struct BuildCfg* bld_cfg = NULL;
|
||||||
//TODO: see line 185 of init.go, what does core.BldCfg{Repo: r} do? BldCfg is a structure
|
//TODO: see line 185 of init.go, what does core.BldCfg{Repo: r} do? BldCfg is a structure
|
||||||
retVal = ipfs_core_builder_new_node(ctx, bld_cfg, ipfs_node);
|
retVal = ipfs_core_builder_new_node(ctx, bld_cfg, ipfs_node);
|
||||||
//return namesys_initialize_keyspace(ctx, ipfs_node->DAG, ipfs_node->Namesys, ipfs_node->pinning, ipfs_node->private_key);
|
//return namesys_initialize_keyspace(ctx, ipfs_node->DAG, ipfs_node->Namesys, ipfs_node->pinning, ipfs_node->private_key);
|
||||||
|
@ -60,8 +61,8 @@ int do_init(FILE* out_file, char* repo_root, int empty, int num_bits_for_keypair
|
||||||
if (fs_repo_is_initialized(repo_root))
|
if (fs_repo_is_initialized(repo_root))
|
||||||
return 0;
|
return 0;
|
||||||
//TODO: If the conf is null, make one
|
//TODO: If the conf is null, make one
|
||||||
if ( conf->identity->peer_id == NULL) {
|
if ( conf->identity->peer == NULL || conf->identity->peer->id == NULL) {
|
||||||
int retVal = ipfs_repo_config_init(conf, num_bits_for_keypair, repo_root);
|
int retVal = ipfs_repo_config_init(conf, num_bits_for_keypair, repo_root, 4001, NULL);
|
||||||
if (retVal == 0)
|
if (retVal == 0)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -88,7 +89,8 @@ int init_run(struct Request* request) {
|
||||||
// TODO: check parameters for logic errors
|
// TODO: check parameters for logic errors
|
||||||
// TODO: Initialize
|
// TODO: Initialize
|
||||||
struct RepoConfig* conf;
|
struct RepoConfig* conf;
|
||||||
int retVal = ipfs_repo_config_new(&conf);
|
if (ipfs_repo_config_new(&conf) == 0)
|
||||||
|
return 0;
|
||||||
// TODO: handle files in request
|
// TODO: handle files in request
|
||||||
// do the heavy lifting
|
// do the heavy lifting
|
||||||
int num_bits_for_key_pair = request->cmd.options[0]->default_int_val;
|
int num_bits_for_key_pair = request->cmd.options[0]->default_int_val;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -O0 -I../include -I../../c-libp2p/include
|
CFLAGS = -O0 -I../include -I../c-libp2p/include -Wall -std=gnu99
|
||||||
LFLAGS =
|
LFLAGS =
|
||||||
DEPS = ../include/ipfs/commands/argument.h ../include/ipfs/commands/command_option.h \
|
DEPS = ../include/ipfs/commands/argument.h ../include/ipfs/commands/command_option.h \
|
||||||
../include/ipfs/commands/command.h ../include/ipfs/commands/context.h \
|
../include/ipfs/commands/command.h ../include/ipfs/commands/context.h \
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -O0 -I../../include -I../../../c-libp2p/include
|
CFLAGS = -O0 -I../../include -I../../c-libp2p/include -Wall
|
||||||
LFLAGS =
|
LFLAGS =
|
||||||
DEPS = parse.h
|
DEPS = parse.h
|
||||||
OBJS = parse.o
|
OBJS = parse.o
|
||||||
|
|
|
@ -1,5 +1,23 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "libp2p/utils/logger.h"
|
||||||
#include "ipfs/commands/cli/parse.h"
|
#include "ipfs/commands/cli/parse.h"
|
||||||
|
|
||||||
int cli_parse(char** params, FILE* inStream, struct Command* cmd, struct Request* request) {
|
|
||||||
|
/***
|
||||||
|
* Parse command line arguments, and place them in a Command struct
|
||||||
|
* @param argc number of arguments
|
||||||
|
* @param argv arguments
|
||||||
|
* @param inStream an incoming stream (not implemented yet)
|
||||||
|
* @param cmd the Command struct to allocate
|
||||||
|
* @param request not sure what this is for yet
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int cli_parse(int argc, char** argv, FILE* inStream, struct Command** cmd, struct Request* request) {
|
||||||
|
*cmd = (struct Command*) malloc(sizeof(struct Command));
|
||||||
|
if (*cmd == NULL) {
|
||||||
|
libp2p_logger_error("parse", "Unable to allocate memory for the command structure.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,3 @@
|
||||||
//
|
|
||||||
// command.c
|
|
||||||
// c-ipfs
|
|
||||||
//
|
|
||||||
// Created by John Jones on 10/27/16.
|
|
||||||
// Copyright © 2016 JMJAtlanta. All rights reserved.
|
|
||||||
//
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "ipfs/commands/command.h"
|
#include "ipfs/commands/command.h"
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
//
|
|
||||||
// option.c
|
|
||||||
// c-ipfs
|
|
||||||
//
|
|
||||||
// Created by John Jones on 10/26/16.
|
|
||||||
// Copyright © 2016 JMJAtlanta. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -O0 -I../include -I../../c-libp2p/include
|
CFLAGS = -O0 -I../include -I../c-libp2p/include -I../c-libp2p/c-multiaddr/include -I../c-libp2p/c-protobuf -Wall -std=gnu99
|
||||||
|
|
||||||
|
ifdef DEBUG
|
||||||
|
CFLAGS += -g3
|
||||||
|
endif
|
||||||
|
|
||||||
LFLAGS =
|
LFLAGS =
|
||||||
DEPS = builder.h ipfs_node.h
|
DEPS = builder.h ipfs_node.h
|
||||||
OBJS = builder.o
|
OBJS = builder.o daemon.o null.o ping.o bootstrap.o ipfs_node.o api.o client_api.o http_request.o swarm.o
|
||||||
|
|
||||||
%.o: %.c $(DEPS)
|
%.o: %.c $(DEPS)
|
||||||
$(CC) -c -o $@ $< $(CFLAGS)
|
$(CC) -c -o $@ $< $(CFLAGS)
|
||||||
|
|
772
core/api.c
Normal file
772
core/api.c
Normal file
|
@ -0,0 +1,772 @@
|
||||||
|
/**
|
||||||
|
* Methods for lightweight/specific HTTP for API communication.
|
||||||
|
*/
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#define __USE_GNU
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "libp2p/net/p2pnet.h"
|
||||||
|
#include "libp2p/os/memstream.h"
|
||||||
|
#include "libp2p/utils/logger.h"
|
||||||
|
#include "libp2p/utils/urlencode.h"
|
||||||
|
#include "ipfs/core/api.h"
|
||||||
|
#include "ipfs/importer/exporter.h"
|
||||||
|
#include "ipfs/core/http_request.h"
|
||||||
|
|
||||||
|
//pthread_mutex_t conns_lock;
|
||||||
|
//int conns_count;
|
||||||
|
|
||||||
|
struct ApiContext api_list;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write two strings on one write.
|
||||||
|
* @param fd file descriptor to write.
|
||||||
|
* @param str1 first string to write.
|
||||||
|
* @param str2 second string to write.
|
||||||
|
*/
|
||||||
|
size_t write_dual(int fd, char *str1, char *str2)
|
||||||
|
{
|
||||||
|
struct iovec iov[2];
|
||||||
|
|
||||||
|
iov[0].iov_base = str1;
|
||||||
|
iov[0].iov_len = strlen(str1);
|
||||||
|
iov[1].iov_base = str2;
|
||||||
|
iov[1].iov_len = strlen(str2);
|
||||||
|
|
||||||
|
return writev(fd, iov, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int find_chunk(char *buf, const size_t buf_size, size_t *pos, size_t *size)
|
||||||
|
{
|
||||||
|
char *p = NULL;
|
||||||
|
|
||||||
|
*size = strtol(buf, &p, 16);
|
||||||
|
if (!p || p < buf || p > (buf + 10)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*pos = (int)(p - buf);
|
||||||
|
if (p[0] == '\r' && p[1] == '\n') {
|
||||||
|
*pos += 2;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int read_chunked(int fd, struct s_request *req, char *already, size_t already_size)
|
||||||
|
{
|
||||||
|
char buf[MAX_READ], *p;
|
||||||
|
size_t pos, nsize, buf_size = 0, r;
|
||||||
|
|
||||||
|
if (already_size > 0) {
|
||||||
|
if (already_size <= sizeof(buf)) {
|
||||||
|
memcpy(buf, already, already_size);
|
||||||
|
buf_size += already_size;
|
||||||
|
already_size = 0;
|
||||||
|
} else {
|
||||||
|
memcpy(buf, already, sizeof(buf));
|
||||||
|
already += sizeof(buf);
|
||||||
|
buf_size += sizeof(buf);
|
||||||
|
already_size -= sizeof(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while(buf_size) {
|
||||||
|
if (!find_chunk(buf, buf_size, &pos, &nsize)) {
|
||||||
|
libp2p_logger_error("api", "fail find_chunk.\n");
|
||||||
|
libp2p_logger_error("api", "nsize = %d.\n", nsize);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (nsize == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p = realloc(req->buf, req->size + nsize);
|
||||||
|
if (!p) {
|
||||||
|
libp2p_logger_error("api", "fail realloc.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
req->buf = p;
|
||||||
|
req->size += nsize;
|
||||||
|
|
||||||
|
CPCHUNK:
|
||||||
|
r = nsize;
|
||||||
|
buf_size -= pos;
|
||||||
|
if (r > buf_size) {
|
||||||
|
r = buf_size;
|
||||||
|
}
|
||||||
|
memcpy(req->buf + req->body + req->body_size, buf + pos, r);
|
||||||
|
req->body_size += r;
|
||||||
|
nsize -= r;
|
||||||
|
buf_size -= r;
|
||||||
|
if (buf_size > 0) {
|
||||||
|
memmove(buf, buf + pos + r, buf_size);
|
||||||
|
}
|
||||||
|
pos = 0;
|
||||||
|
if (already_size > 0) {
|
||||||
|
r = sizeof(buf) - buf_size;
|
||||||
|
if (already_size <= r) {
|
||||||
|
memcpy(buf, already, already_size);
|
||||||
|
buf_size += already_size;
|
||||||
|
already_size = 0;
|
||||||
|
} else {
|
||||||
|
memcpy(buf, already, r);
|
||||||
|
already += r;
|
||||||
|
buf_size += r;
|
||||||
|
already_size -= r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (socket_read_select4(fd, 5) > 0) {
|
||||||
|
r = sizeof(buf) - buf_size;
|
||||||
|
r = read(fd, buf+buf_size, r);
|
||||||
|
buf_size += r;
|
||||||
|
if (r == 0 && nsize == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r <= 0) {
|
||||||
|
libp2p_logger_error("api", "read fail.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nsize > 0)
|
||||||
|
goto CPCHUNK; // still have data to transfer on current chunk.
|
||||||
|
|
||||||
|
if (memcmp (buf, "\r\n", 2)!=0) {
|
||||||
|
libp2p_logger_error("api", "fail CRLF.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int read_all(int fd, struct s_request *req, char *already, size_t alread_size)
|
||||||
|
{
|
||||||
|
char buf[MAX_READ], *p;
|
||||||
|
size_t size = 0;
|
||||||
|
|
||||||
|
if (alread_size > 0) {
|
||||||
|
p = realloc(req->buf, req->size + alread_size);
|
||||||
|
if (!p) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
req->buf = p;
|
||||||
|
req->size += alread_size;
|
||||||
|
memcpy(req->buf + req->body + req->body_size, already, alread_size);
|
||||||
|
req->body_size += alread_size;
|
||||||
|
}
|
||||||
|
for(;;) {
|
||||||
|
if (socket_read_select4(fd, 5) <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
size = read(fd, buf, sizeof buf);
|
||||||
|
if (size <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p = realloc(req->buf, req->size + size);
|
||||||
|
if (!p) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
req->buf = p;
|
||||||
|
req->size += size;
|
||||||
|
memcpy(req->buf + req->body + req->body_size, buf, size);
|
||||||
|
req->body_size += size;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a token in a string array.
|
||||||
|
* @param string array and token string.
|
||||||
|
* @returns the pointer after where the token was found or NULL if it fails.
|
||||||
|
*/
|
||||||
|
char *str_tok(char *str, char *tok)
|
||||||
|
{
|
||||||
|
char *p = strstr(str, tok);
|
||||||
|
if (p) {
|
||||||
|
p += strlen(tok);
|
||||||
|
while(*p == ' ') p++;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a token in a binary array.
|
||||||
|
* @param array, size of array, token and size of token.
|
||||||
|
* @returns the pointer after where the token was found or NULL if it fails.
|
||||||
|
*/
|
||||||
|
char *bin_tok(char *bin, size_t limit, char *tok, size_t tok_size)
|
||||||
|
{
|
||||||
|
char *p = memmem(bin, limit, tok, tok_size);
|
||||||
|
if (p) {
|
||||||
|
p += tok_size;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if header contain a especific value.
|
||||||
|
* @param request structure, header name and value to check.
|
||||||
|
* @returns the pointer where the value was found or NULL if it fails.
|
||||||
|
*/
|
||||||
|
char *header_value_cmp(struct s_request *req, char *header, char *value)
|
||||||
|
{
|
||||||
|
char *p = str_tok(req->buf + req->header, header);
|
||||||
|
if (p) {
|
||||||
|
if (strstart(p, value)) {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup for boundary at buffer string.
|
||||||
|
* @param body buffer string, boundary id, filename and content-type string.
|
||||||
|
* @returns the pointer where the multipart start.
|
||||||
|
*/
|
||||||
|
char *boundary_find(char *str, char *boundary, char **filename, char **contenttype)
|
||||||
|
{
|
||||||
|
char *p = str_tok(str, "--");
|
||||||
|
while (p) {
|
||||||
|
if (strstart(p, boundary)) {
|
||||||
|
// skip to the beginning, ignoring the header for now, if there is.
|
||||||
|
// TODO: return filename and content-type
|
||||||
|
p = strstr(p, "\r\n\r\n");
|
||||||
|
if (p) {
|
||||||
|
return p + 4; // ignore 4 bytes CRLF 2x
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p = str_tok(str, "--");
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the size of boundary.
|
||||||
|
* @param boundary buffer, boundary id.
|
||||||
|
* @returns the size of boundary or 0 if fails.
|
||||||
|
*/
|
||||||
|
size_t boundary_size(char *str, char *boundary, size_t limit)
|
||||||
|
{
|
||||||
|
char *p = bin_tok(str, limit, "\r\n--", 4);
|
||||||
|
while (p) {
|
||||||
|
if (strstart(p, boundary)) {
|
||||||
|
if (cstrstart(p + strlen(boundary), "--\r\n")) {
|
||||||
|
p -= 4;
|
||||||
|
return (size_t)(p - str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p = bin_tok(p, limit, "\r\n--", 4);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ApiConnectionParam {
|
||||||
|
int index;
|
||||||
|
struct IpfsNode* this_node;
|
||||||
|
};
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Take an s_request and turn it into an HttpRequest
|
||||||
|
* @param req the incoming s_request
|
||||||
|
* @returns the resultant HttpRequest or NULL on error
|
||||||
|
*/
|
||||||
|
struct HttpRequest* api_build_http_request(struct s_request* req) {
|
||||||
|
struct HttpRequest* request = ipfs_core_http_request_new();
|
||||||
|
if (request != NULL) {
|
||||||
|
char *segs = malloc (strlen(req->buf + req->request) + 1);
|
||||||
|
if (segs) {
|
||||||
|
strcpy(segs, req->buf + req->request);
|
||||||
|
request->command = segs;
|
||||||
|
segs = strchr(segs, '/');
|
||||||
|
if (segs) {
|
||||||
|
*segs++ = '\0';
|
||||||
|
request->sub_command = segs; // sub_command can contain another level as filters/add
|
||||||
|
}
|
||||||
|
if (req->query) {
|
||||||
|
segs = libp2p_utils_url_decode(req->buf + req->query);
|
||||||
|
if (segs) {
|
||||||
|
while (segs) {
|
||||||
|
char *value, *name = segs;
|
||||||
|
segs = strchr(segs, '&');
|
||||||
|
if (segs) { // calc next to split before search for = on another parameter.
|
||||||
|
*segs++ = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
value = strchr(name, '=');
|
||||||
|
if (value) {
|
||||||
|
*value++ = '\0';
|
||||||
|
}
|
||||||
|
if (value && (strcmp(name, "arg")==0)) {
|
||||||
|
libp2p_utils_vector_add(request->arguments, strdup(value));
|
||||||
|
} else {
|
||||||
|
struct HttpParam *hp = ipfs_core_http_param_new();
|
||||||
|
if (hp) {
|
||||||
|
hp->name = strdup(name);
|
||||||
|
hp->value = strdup(value); // maybe null ?
|
||||||
|
libp2p_utils_vector_add(request->params, hp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(segs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write bytes into chunks.
|
||||||
|
* @param socket, buffer array, length
|
||||||
|
* @returns 1 when success or 0 if it fails.
|
||||||
|
*/
|
||||||
|
int api_send_resp_chunks(int fd, void *buf, size_t size)
|
||||||
|
{
|
||||||
|
char head[20];
|
||||||
|
size_t s;
|
||||||
|
int l;
|
||||||
|
struct iovec iov[3];
|
||||||
|
|
||||||
|
// will be reused in each write, so defined only once.
|
||||||
|
iov[2].iov_base = "\r\n";
|
||||||
|
iov[2].iov_len = 2;
|
||||||
|
|
||||||
|
while (size > 0) {
|
||||||
|
s = size > MAX_CHUNK ? MAX_CHUNK : size; // write only MAX_CHUNK at once
|
||||||
|
l = snprintf(head, sizeof head, "%x\r\n", (unsigned int)s);
|
||||||
|
if (l <= 0)
|
||||||
|
return 0; // fail at snprintf
|
||||||
|
|
||||||
|
iov[0].iov_base = head;
|
||||||
|
iov[0].iov_len = l; // head length.
|
||||||
|
iov[1].iov_base = buf;
|
||||||
|
iov[1].iov_len = s;
|
||||||
|
|
||||||
|
buf += s;
|
||||||
|
size -= s;
|
||||||
|
|
||||||
|
if (size == 0) { // last chunk
|
||||||
|
iov[2].iov_base = "\r\n0\r\n\r\n";
|
||||||
|
iov[2].iov_len = 7;
|
||||||
|
}
|
||||||
|
libp2p_logger_debug("api", "writing chunk block of %d bytes\n", s);
|
||||||
|
if (writev(fd, iov, 3) == -1)
|
||||||
|
return 0; // fail writing.
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pthread to take care of each client connection.
|
||||||
|
* @param ptr an ApiConnectionParam
|
||||||
|
* @returns nothing
|
||||||
|
*/
|
||||||
|
void *api_connection_thread (void *ptr)
|
||||||
|
{
|
||||||
|
int timeout, s, r;
|
||||||
|
struct ApiConnectionParam* params = (struct ApiConnectionParam*)ptr;
|
||||||
|
char resp[MAX_READ+1], buf[MAX_READ+1], *p, *body;
|
||||||
|
char client[INET_ADDRSTRLEN];
|
||||||
|
struct s_request req;
|
||||||
|
int (*read_func)(int, struct s_request*, char*, size_t) = read_all;
|
||||||
|
|
||||||
|
req.buf = NULL; // sanity.
|
||||||
|
|
||||||
|
buf[MAX_READ] = '\0';
|
||||||
|
|
||||||
|
s = params->this_node->api_context->conns[params->index]->socket;
|
||||||
|
timeout = params->this_node->api_context->timeout;
|
||||||
|
|
||||||
|
if (socket_read_select4(s, timeout) <= 0) {
|
||||||
|
libp2p_logger_error("api", "Client connection timeout.\n");
|
||||||
|
goto quit;
|
||||||
|
}
|
||||||
|
r = read(s, buf, sizeof buf);
|
||||||
|
if (r <= 0) {
|
||||||
|
// this is a common occurrence, so moved from error to debug
|
||||||
|
libp2p_logger_debug("api", "Read from client fail.\n");
|
||||||
|
goto quit;
|
||||||
|
}
|
||||||
|
buf[r] = '\0';
|
||||||
|
|
||||||
|
p = strstr(buf, "\r\n\r\n");
|
||||||
|
|
||||||
|
if (p) {
|
||||||
|
body = p + 4;
|
||||||
|
|
||||||
|
req.size = p - buf + 1;
|
||||||
|
req.buf = malloc(req.size);
|
||||||
|
if (!req.buf) {
|
||||||
|
// memory allocation fail.
|
||||||
|
libp2p_logger_error("api", "malloc fail.\n");
|
||||||
|
write_cstr (s, HTTP_500);
|
||||||
|
goto quit;
|
||||||
|
}
|
||||||
|
memcpy(req.buf, buf, req.size - 1);
|
||||||
|
req.buf[req.size-1] = '\0';
|
||||||
|
|
||||||
|
req.method = 0;
|
||||||
|
p = strchr(req.buf + req.method, ' ');
|
||||||
|
if (!p) {
|
||||||
|
libp2p_logger_error("api", "fail looking for space on method '%s'.\n", req.buf + req.method);
|
||||||
|
write_cstr (s, HTTP_400);
|
||||||
|
goto quit;
|
||||||
|
}
|
||||||
|
*p++ = '\0'; // End of method.
|
||||||
|
req.path = p - req.buf;
|
||||||
|
if (strchr(p, '?')) {
|
||||||
|
p = strchr(p, '?');
|
||||||
|
*p++ = '\0';
|
||||||
|
req.query = p - req.buf;
|
||||||
|
} else {
|
||||||
|
req.query = 0;
|
||||||
|
}
|
||||||
|
p = strchr(p, ' ');
|
||||||
|
if (!p) {
|
||||||
|
libp2p_logger_error("api", "fail looking for space on path '%s'.\n", req.buf + req.path);
|
||||||
|
write_cstr (s, HTTP_400);
|
||||||
|
goto quit;
|
||||||
|
}
|
||||||
|
*p++ = '\0'; // End of path.
|
||||||
|
req.http_ver = p - req.buf;
|
||||||
|
p = strchr(req.buf + req.http_ver, '\r');
|
||||||
|
if (!p) {
|
||||||
|
libp2p_logger_error("api", "fail looking for CR on http_ver '%s'.\n", req.buf + req.http_ver);
|
||||||
|
write_cstr (s, HTTP_400);
|
||||||
|
goto quit;
|
||||||
|
}
|
||||||
|
*p++ = '\0'; // End of http version.
|
||||||
|
while (*p == '\r' || *p == '\n') p++;
|
||||||
|
req.header = p - req.buf;
|
||||||
|
req.body = req.size;
|
||||||
|
req.body_size = 0;
|
||||||
|
|
||||||
|
if (header_value_cmp(&req, "Transfer-Encoding:", "chunked")) {
|
||||||
|
read_func = read_chunked;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!read_func(s, &req, body, r - (body - buf))) {
|
||||||
|
libp2p_logger_error("api", "fail read_func.\n");
|
||||||
|
write_cstr (s, HTTP_500);
|
||||||
|
goto quit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strncmp(req.buf + req.method, "GET", 3)==0) {
|
||||||
|
if (strcmp (req.buf + req.path, "/")==0 ||
|
||||||
|
strcmp (req.buf + req.path, "/webui")==0 ||
|
||||||
|
strcmp (req.buf + req.path, "/webui/")==0) {
|
||||||
|
char *redir;
|
||||||
|
size_t size = sizeof(HTTP_301) + (sizeof(WEBUI_ADDR)*2);
|
||||||
|
|
||||||
|
redir = malloc(size);
|
||||||
|
if (redir) {
|
||||||
|
snprintf(redir, size, HTTP_301, WEBUI_ADDR, WEBUI_ADDR);
|
||||||
|
redir[size-1] = '\0'; // just in case
|
||||||
|
write_dual (s, req.buf + req.http_ver, strchr (redir, ' '));
|
||||||
|
free (redir);
|
||||||
|
} else {
|
||||||
|
write_cstr (s, HTTP_500);
|
||||||
|
}
|
||||||
|
} else if (!cstrstart(req.buf + req.path, API_V0_START)) {
|
||||||
|
// TODO: handle download file here.
|
||||||
|
// move out of the if to do further processing
|
||||||
|
}
|
||||||
|
// end of GET
|
||||||
|
} else if (strncmp(req.buf + req.method, "POST", 4)==0) {
|
||||||
|
// TODO: Handle gzip/json POST requests.
|
||||||
|
|
||||||
|
p = header_value_cmp(&req, "Content-Type:", "multipart/form-data;");
|
||||||
|
if (p) {
|
||||||
|
p = str_tok(p, "boundary=");
|
||||||
|
if (p) {
|
||||||
|
char *boundary, *l;
|
||||||
|
int len;
|
||||||
|
if (*p == '"') {
|
||||||
|
p++;
|
||||||
|
l = strchr(p, '"');
|
||||||
|
} else {
|
||||||
|
l = p;
|
||||||
|
while (*l != '\r' && *l != '\0') l++;
|
||||||
|
}
|
||||||
|
len = l - p;
|
||||||
|
boundary = malloc (len+1);
|
||||||
|
if (boundary) {
|
||||||
|
memcpy(boundary, p, len);
|
||||||
|
boundary[len] = '\0';
|
||||||
|
|
||||||
|
p = boundary_find(req.buf + req.body, boundary, NULL, NULL);
|
||||||
|
if (p) {
|
||||||
|
req.boundary_size = boundary_size(p, boundary, req.size - (p - buf));
|
||||||
|
if (req.boundary_size > 0) {
|
||||||
|
req.boundary = p - req.buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free (boundary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.boundary > 0) {
|
||||||
|
libp2p_logger_error("api", "boundary index = %d, size = %d\n", req.boundary, req.boundary_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
libp2p_logger_debug("api", "method = '%s'\n"
|
||||||
|
"path = '%s'\n"
|
||||||
|
"http_ver = '%s'\n"
|
||||||
|
"header {\n%s\n}\n"
|
||||||
|
"body_size = %d\n",
|
||||||
|
req.buf+req.method, req.buf+req.path, req.buf+req.http_ver,
|
||||||
|
req.buf+req.header, req.body_size);
|
||||||
|
// end of POST
|
||||||
|
} else {
|
||||||
|
// Unexpected???
|
||||||
|
libp2p_logger_error("api", "fail unexpected '%s'.\n", req.buf + req.method);
|
||||||
|
write_cstr (s, HTTP_500);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cstrstart(req.buf + req.path, API_V0_START)) {
|
||||||
|
req.request = req.path + sizeof(API_V0_START) - 1;
|
||||||
|
// now do something with the request we have built
|
||||||
|
struct HttpRequest* http_request = api_build_http_request(&req);
|
||||||
|
if (http_request != NULL) {
|
||||||
|
struct HttpResponse* http_response = NULL;
|
||||||
|
if (!ipfs_core_http_request_process(params->this_node, http_request, &http_response)) {
|
||||||
|
libp2p_logger_error("api", "ipfs_core_http_request_process returned false.\n");
|
||||||
|
// 404
|
||||||
|
write_str(s, HTTP_404);
|
||||||
|
} else {
|
||||||
|
snprintf(resp, MAX_READ+1, "%s 200 OK\r\n" \
|
||||||
|
"Content-Type: %s\r\n"
|
||||||
|
"Server: c-ipfs/0.0.0-dev\r\n"
|
||||||
|
"X-Chunked-Output: 1\r\n"
|
||||||
|
"Connection: close\r\n"
|
||||||
|
"Transfer-Encoding: chunked\r\n"
|
||||||
|
"\r\n"
|
||||||
|
,req.buf + req.http_ver, http_response->content_type);
|
||||||
|
write_str (s, resp);
|
||||||
|
api_send_resp_chunks(s, http_response->bytes, http_response->bytes_size);
|
||||||
|
libp2p_logger_debug("api", "resp = {\n%s\n}\n", resp);
|
||||||
|
}
|
||||||
|
ipfs_core_http_request_free(http_request);
|
||||||
|
ipfs_core_http_response_free(http_response);
|
||||||
|
} else {
|
||||||
|
// uh oh... something went wrong converting to the HttpRequest struct
|
||||||
|
libp2p_logger_error("api", "Unable to build HttpRequest struct.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
libp2p_logger_error("api", "fail looking for body.\n");
|
||||||
|
write_cstr (s, HTTP_400);
|
||||||
|
}
|
||||||
|
|
||||||
|
quit:
|
||||||
|
if (req.buf)
|
||||||
|
free(req.buf);
|
||||||
|
if (inet_ntop(AF_INET, &( params->this_node->api_context->conns[params->index]->ipv4), client, INET_ADDRSTRLEN) == NULL)
|
||||||
|
strcpy(client, "UNKNOW");
|
||||||
|
libp2p_logger_debug("api", "Closing client connection %s:%d (%d).\n", client, params->this_node->api_context->conns[params->index]->port, params->index+1);
|
||||||
|
pthread_mutex_lock(¶ms->this_node->api_context->conns_lock);
|
||||||
|
close(s);
|
||||||
|
free ( params->this_node->api_context->conns[params->index]);
|
||||||
|
params->this_node->api_context->conns[params->index] = NULL;
|
||||||
|
params->this_node->api_context->conns_count--;
|
||||||
|
pthread_mutex_unlock(¶ms->this_node->api_context->conns_lock);
|
||||||
|
free(params);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close all connections stopping respectives pthreads and free allocated memory.
|
||||||
|
*/
|
||||||
|
void api_connections_cleanup (struct IpfsNode* local_node)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&local_node->api_context->conns_lock);
|
||||||
|
if (local_node->api_context->conns_count > 0 && local_node->api_context->conns) {
|
||||||
|
for (i = 0 ; i < local_node->api_context->max_conns ; i++) {
|
||||||
|
if (local_node->api_context->conns[i]->pthread) {
|
||||||
|
pthread_cancel (local_node->api_context->conns[i]->pthread);
|
||||||
|
close (local_node->api_context->conns[i]->socket);
|
||||||
|
free (local_node->api_context->conns[i]);
|
||||||
|
local_node->api_context->conns[i] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
local_node->api_context->conns_count = 0;
|
||||||
|
}
|
||||||
|
if (local_node->api_context->conns) {
|
||||||
|
free (local_node->api_context->conns);
|
||||||
|
local_node->api_context->conns = NULL;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&local_node->api_context->conns_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pthread to keep in background dealing with client connections.
|
||||||
|
* @param ptr is not used.
|
||||||
|
* @returns nothing
|
||||||
|
*/
|
||||||
|
void *api_listen_thread (void *ptr)
|
||||||
|
{
|
||||||
|
int s;
|
||||||
|
INT_TYPE i;
|
||||||
|
uint32_t ipv4;
|
||||||
|
uint16_t port;
|
||||||
|
char client[INET_ADDRSTRLEN];
|
||||||
|
struct IpfsNode* local_node = (struct IpfsNode*)ptr;
|
||||||
|
|
||||||
|
local_node->api_context->conns_count = 0;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
s = socket_accept4(local_node->api_context->socket, &ipv4, &port);
|
||||||
|
if (s <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (local_node->api_context->conns_count >= local_node->api_context->max_conns) { // limit reached.
|
||||||
|
libp2p_logger_error("api", "Limit of connections reached (%d).\n", local_node->api_context->max_conns);
|
||||||
|
close (s);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&local_node->api_context->conns_lock);
|
||||||
|
for (i = 0 ; i < local_node->api_context->max_conns && local_node->api_context->conns[i] ; i++);
|
||||||
|
local_node->api_context->conns[i] = malloc (sizeof (struct s_conns));
|
||||||
|
if (!local_node->api_context->conns[i]) {
|
||||||
|
libp2p_logger_error("api", "Fail to allocate memory to accept connection.\n");
|
||||||
|
pthread_mutex_unlock(&local_node->api_context->conns_lock);
|
||||||
|
close (s);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (inet_ntop(AF_INET, &ipv4, client, INET_ADDRSTRLEN) == NULL)
|
||||||
|
strcpy(client, "UNKNOW");
|
||||||
|
local_node->api_context->conns[i]->socket = s;
|
||||||
|
local_node->api_context->conns[i]->ipv4 = ipv4;
|
||||||
|
local_node->api_context->conns[i]->port = port;
|
||||||
|
// create a struct, which the thread is responsible to destroy
|
||||||
|
struct ApiConnectionParam* connection_param = (struct ApiConnectionParam*) malloc(sizeof(struct ApiConnectionParam));
|
||||||
|
if (connection_param == NULL) {
|
||||||
|
libp2p_logger_error("api", "api_listen_thread: Unable to allocate memory.\n");
|
||||||
|
pthread_mutex_unlock(&local_node->api_context->conns_lock);
|
||||||
|
close (s);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
connection_param->index = i;
|
||||||
|
connection_param->this_node = local_node;
|
||||||
|
if (pthread_create(&(local_node->api_context->conns[i]->pthread), NULL, api_connection_thread, (void*)connection_param)) {
|
||||||
|
libp2p_logger_error("api", "Create pthread fail.\n");
|
||||||
|
free (local_node->api_context->conns[i]);
|
||||||
|
local_node->api_context->conns[i] = NULL;
|
||||||
|
local_node->api_context->conns_count--;
|
||||||
|
close(s);
|
||||||
|
} else {
|
||||||
|
local_node->api_context->conns_count++;
|
||||||
|
}
|
||||||
|
libp2p_logger_debug("api", "API for %s: Accept connection %s:%d (%d/%d), pthread %d.\n", local_node->identity->peer->id, client, port, local_node->api_context->conns_count, local_node->api_context->max_conns, i+1);
|
||||||
|
pthread_mutex_unlock(&local_node->api_context->conns_lock);
|
||||||
|
}
|
||||||
|
api_connections_cleanup (local_node);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ApiContext* api_context_new() {
|
||||||
|
struct ApiContext* context = (struct ApiContext*) malloc(sizeof(struct ApiContext));
|
||||||
|
if (context != NULL) {
|
||||||
|
context->conns = NULL;
|
||||||
|
context->conns_count = 0;
|
||||||
|
context->ipv4 = 0;
|
||||||
|
context->max_conns = 0;
|
||||||
|
context->port = 0;
|
||||||
|
context->socket = 0;
|
||||||
|
context->timeout = 0;
|
||||||
|
pthread_mutex_init(&context->conns_lock, NULL);
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start API interface daemon.
|
||||||
|
* @param local_node the context
|
||||||
|
* @param max_conns.
|
||||||
|
* @param timeout time out of client connection.
|
||||||
|
* @returns 0 when failure or 1 if success.
|
||||||
|
*/
|
||||||
|
int api_start (struct IpfsNode* local_node, int max_conns, int timeout)
|
||||||
|
{
|
||||||
|
int s;
|
||||||
|
size_t alloc_size = sizeof(void*) * max_conns;
|
||||||
|
|
||||||
|
struct MultiAddress* my_address = multiaddress_new_from_string(local_node->repo->config->addresses->api);
|
||||||
|
|
||||||
|
char* ip = NULL;
|
||||||
|
multiaddress_get_ip_address(my_address, &ip);
|
||||||
|
int port = multiaddress_get_ip_port(my_address);
|
||||||
|
|
||||||
|
local_node->api_context = api_context_new();
|
||||||
|
if (local_node->api_context == NULL) {
|
||||||
|
multiaddress_free(my_address);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
local_node->api_context->ipv4 = hostname_to_ip(ip); // api is listening only on loopback.
|
||||||
|
if (ip != NULL)
|
||||||
|
free(ip);
|
||||||
|
local_node->api_context->port = port;
|
||||||
|
|
||||||
|
if ((s = socket_listen(socket_tcp4(), &(local_node->api_context->ipv4), &(local_node->api_context->port))) <= 0) {
|
||||||
|
libp2p_logger_error("api", "Failed to init API. port: %d\n", port);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
local_node->api_context->socket = s;
|
||||||
|
local_node->api_context->max_conns = max_conns;
|
||||||
|
local_node->api_context->timeout = timeout;
|
||||||
|
|
||||||
|
local_node->api_context->conns = malloc (alloc_size);
|
||||||
|
if (!local_node->api_context->conns) {
|
||||||
|
close (s);
|
||||||
|
libp2p_logger_error("api", "Error allocating memory.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memset(local_node->api_context->conns, 0, alloc_size);
|
||||||
|
|
||||||
|
if (pthread_create(&local_node->api_context->api_thread, NULL, api_listen_thread, (void*)local_node)) {
|
||||||
|
close (s);
|
||||||
|
free (local_node->api_context->conns);
|
||||||
|
local_node->api_context->conns = NULL;
|
||||||
|
local_node->api_context->api_thread = 0;
|
||||||
|
libp2p_logger_error("api", "Error creating thread for API.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
libp2p_logger_info("api", "API server listening on %d.\n", port);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop API.
|
||||||
|
* @returns 0 when failure or 1 if success.
|
||||||
|
*/
|
||||||
|
int api_stop (struct IpfsNode *local_node)
|
||||||
|
{
|
||||||
|
if (local_node->api_context->api_thread == 0) return 0;
|
||||||
|
shutdown(local_node->api_context->socket, SHUT_RDWR);
|
||||||
|
pthread_cancel(local_node->api_context->api_thread);
|
||||||
|
|
||||||
|
api_connections_cleanup (local_node);
|
||||||
|
|
||||||
|
local_node->api_context->api_thread = 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
79
core/bootstrap.c
Normal file
79
core/bootstrap.c
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include "libp2p/peer/peer.h"
|
||||||
|
#include "libp2p/utils/logger.h"
|
||||||
|
#include "ipfs/routing/routing.h"
|
||||||
|
#include "ipfs/core/ipfs_node.h"
|
||||||
|
#include "ipfs/thirdparty/ipfsaddr/ipfs_addr.h"
|
||||||
|
#include "multiaddr/multiaddr.h"
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Begin to connect to the swarm
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
void *ipfs_bootstrap_swarm(void* param) {
|
||||||
|
//TODO:
|
||||||
|
struct IpfsNode* local_node = (struct IpfsNode*)param;
|
||||||
|
// read the config file and get the bootstrap peers
|
||||||
|
for(int i = 0; i < local_node->repo->config->peer_addresses.num_peers; i++) { // loop through the peers
|
||||||
|
struct IPFSAddr* ipfs_addr = local_node->repo->config->peer_addresses.peers[i];
|
||||||
|
struct MultiAddress* ma = multiaddress_new_from_string(ipfs_addr->entire_string);
|
||||||
|
// get the id
|
||||||
|
char* ptr;
|
||||||
|
if ( (ptr = strstr(ipfs_addr->entire_string, "/ipfs/")) != NULL) { // look for the peer id
|
||||||
|
ptr += 6;
|
||||||
|
if (ptr[0] == 'Q' && ptr[1] == 'm') { // things look good
|
||||||
|
struct Libp2pPeer* peer = libp2p_peer_new_from_data(ptr, strlen(ptr), ma);
|
||||||
|
libp2p_peerstore_add_peer(local_node->peerstore, peer);
|
||||||
|
}
|
||||||
|
// TODO: attempt to connect to the peer
|
||||||
|
|
||||||
|
} // we have a good peer ID
|
||||||
|
|
||||||
|
}
|
||||||
|
return (void*)1;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Announce to the network all of the files that I have in storage
|
||||||
|
* @param local_node the context
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
void ipfs_bootstrap_announce_files(struct IpfsNode* local_node) {
|
||||||
|
struct Datastore* db = local_node->repo->config->datastore;
|
||||||
|
if (!db->datastore_cursor_open(db))
|
||||||
|
return;
|
||||||
|
unsigned char* key = NULL;
|
||||||
|
int key_size = 0;
|
||||||
|
enum DatastoreCursorOp op = CURSOR_FIRST;
|
||||||
|
while (db->datastore_cursor_get(&key, &key_size, NULL, 0, op, db)) {
|
||||||
|
libp2p_logger_debug("bootstrap", "Announcing a file to the world.\n");
|
||||||
|
local_node->routing->Provide(local_node->routing, key, key_size);
|
||||||
|
op = CURSOR_NEXT;
|
||||||
|
free(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// close cursor
|
||||||
|
db->datastore_cursor_close(db);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/***
|
||||||
|
* connect to the swarm
|
||||||
|
* NOTE: This fills in the IpfsNode->routing struct
|
||||||
|
*
|
||||||
|
* @param param the IpfsNode information
|
||||||
|
* @returns nothing useful
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
void *ipfs_bootstrap_routing(void* param) {
|
||||||
|
struct IpfsNode* local_node = (struct IpfsNode*)param;
|
||||||
|
local_node->routing = ipfs_routing_new_online(local_node, &local_node->identity->private_key, NULL);
|
||||||
|
local_node->routing->Bootstrap(local_node->routing);
|
||||||
|
ipfs_bootstrap_announce_files(local_node);
|
||||||
|
return (void*)2;
|
||||||
|
}
|
||||||
|
*/
|
|
@ -1,11 +1,4 @@
|
||||||
//
|
#include <pthread.h>
|
||||||
// builder.c
|
|
||||||
// c-ipfs
|
|
||||||
//
|
|
||||||
// Created by John Jones on 10/27/16.
|
|
||||||
// Copyright © 2016 JMJAtlanta. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "ipfs/core/builder.h"
|
#include "ipfs/core/builder.h"
|
||||||
|
|
||||||
int ipfs_core_builder_new_node(struct Context* context, struct BuildCfg* build_cfg, struct IpfsNode* buildConfig) {
|
int ipfs_core_builder_new_node(struct Context* context, struct BuildCfg* build_cfg, struct IpfsNode* buildConfig) {
|
||||||
|
|
60
core/client_api.c
Normal file
60
core/client_api.c
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include "ipfs/core/client_api.h"
|
||||||
|
#include "multiaddr/multiaddr.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the API is running (by attempting to connect to the port)
|
||||||
|
* @param local_node the context
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int api_running(struct IpfsNode* local_node) {
|
||||||
|
struct MultiAddress* my_multiaddress = multiaddress_new_from_string(local_node->repo->config->addresses->api);
|
||||||
|
char* ip = NULL;
|
||||||
|
int portno = 0;
|
||||||
|
|
||||||
|
if (my_multiaddress == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
portno = multiaddress_get_ip_port(my_multiaddress);
|
||||||
|
multiaddress_get_ip_address(my_multiaddress, &ip);
|
||||||
|
|
||||||
|
multiaddress_free(my_multiaddress);
|
||||||
|
|
||||||
|
if (ip == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int sockfd;
|
||||||
|
struct sockaddr_in serv_addr;
|
||||||
|
struct hostent *server;
|
||||||
|
|
||||||
|
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (sockfd < 0) {
|
||||||
|
free(ip);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
server = gethostbyname(ip);
|
||||||
|
free(ip);
|
||||||
|
|
||||||
|
if (server == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&serv_addr, 0, sizeof(struct sockaddr_in));
|
||||||
|
serv_addr.sin_family = AF_INET;
|
||||||
|
memmove(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length);
|
||||||
|
serv_addr.sin_port = htons(portno);
|
||||||
|
int retVal = connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr));
|
||||||
|
close(sockfd);
|
||||||
|
return retVal >= 0;
|
||||||
|
}
|
80
core/daemon.c
Normal file
80
core/daemon.c
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include "libp2p/net/p2pnet.h"
|
||||||
|
#include "libp2p/peer/peerstore.h"
|
||||||
|
#include "ipfs/core/daemon.h"
|
||||||
|
#include "ipfs/core/null.h" // for ipfs_null_shutdown
|
||||||
|
#include "ipfs/core/ipfs_node.h"
|
||||||
|
#include "ipfs/core/bootstrap.h"
|
||||||
|
#include "ipfs/repo/fsrepo/fs_repo.h"
|
||||||
|
#include "ipfs/repo/init.h"
|
||||||
|
#include "libp2p/utils/logger.h"
|
||||||
|
|
||||||
|
int ipfs_daemon_start(char* repo_path) {
|
||||||
|
int count_pths = 0, retVal = 0;
|
||||||
|
pthread_t work_pths[MAX];
|
||||||
|
struct IpfsNodeListenParams listen_param;
|
||||||
|
struct MultiAddress* ma = NULL;
|
||||||
|
|
||||||
|
libp2p_logger_info("daemon", "Initializing daemon for %s...\n", repo_path);
|
||||||
|
|
||||||
|
struct IpfsNode* local_node = NULL;
|
||||||
|
if (!ipfs_node_online_new(repo_path, &local_node))
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
// Set null router param
|
||||||
|
ma = multiaddress_new_from_string(local_node->repo->config->addresses->swarm_head->item);
|
||||||
|
listen_param.port = multiaddress_get_ip_port(ma);
|
||||||
|
listen_param.ipv4 = 0; // ip 0.0.0.0, all interfaces
|
||||||
|
listen_param.local_node = local_node;
|
||||||
|
|
||||||
|
// Create pthread for swarm listener.
|
||||||
|
if (pthread_create(&work_pths[count_pths++], NULL, local_node->routing->Listen, &listen_param)) {
|
||||||
|
libp2p_logger_error("daemon", "Error creating thread for ipfs null listen\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
local_node->routing->Bootstrap(local_node->routing);
|
||||||
|
|
||||||
|
libp2p_logger_info("daemon", "Daemon for %s is ready on port %d\n", listen_param.local_node->identity->peer->id, listen_param.port);
|
||||||
|
|
||||||
|
// Wait for pthreads to finish.
|
||||||
|
while (count_pths) {
|
||||||
|
if (pthread_join(work_pths[--count_pths], NULL)) {
|
||||||
|
libp2p_logger_error("daemon", "Error joining thread\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
retVal = 1;
|
||||||
|
exit:
|
||||||
|
libp2p_logger_debug("daemon", "Cleaning up daemon processes for %s\n", repo_path);
|
||||||
|
// clean up
|
||||||
|
if (ma != NULL)
|
||||||
|
multiaddress_free(ma);
|
||||||
|
if (local_node != NULL) {
|
||||||
|
ipfs_node_free(local_node);
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int ipfs_daemon_stop() {
|
||||||
|
return ipfs_null_shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ipfs_daemon (int argc, char **argv)
|
||||||
|
{
|
||||||
|
char* repo_path = NULL;
|
||||||
|
|
||||||
|
libp2p_logger_add_class("daemon");
|
||||||
|
if (!ipfs_repo_get_directory(argc, argv, &repo_path)) {
|
||||||
|
libp2p_logger_error("daemon", "Unable to open repo: %s\n", repo_path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipfs_daemon_start(repo_path);
|
||||||
|
}
|
628
core/http_request.c
Normal file
628
core/http_request.c
Normal file
|
@ -0,0 +1,628 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include "libp2p/os/memstream.h"
|
||||||
|
#include "libp2p/utils/vector.h"
|
||||||
|
#include "libp2p/utils/logger.h"
|
||||||
|
#include "ipfs/cid/cid.h"
|
||||||
|
#include "ipfs/core/http_request.h"
|
||||||
|
#include "ipfs/importer/exporter.h"
|
||||||
|
#include "ipfs/namesys/resolver.h"
|
||||||
|
#include "ipfs/namesys/publisher.h"
|
||||||
|
#include "ipfs/routing/routing.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles HttpRequest and HttpParam
|
||||||
|
*/
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Build a new HttpRequest
|
||||||
|
* @returns the newly allocated HttpRequest struct
|
||||||
|
*/
|
||||||
|
struct HttpRequest* ipfs_core_http_request_new() {
|
||||||
|
struct HttpRequest* result = (struct HttpRequest*) malloc(sizeof(struct HttpRequest));
|
||||||
|
if (result != NULL) {
|
||||||
|
result->command = NULL;
|
||||||
|
result->sub_command = NULL;
|
||||||
|
result->params = libp2p_utils_vector_new(1);
|
||||||
|
result->arguments = libp2p_utils_vector_new(1);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Clean up resources of a HttpRequest struct
|
||||||
|
* @param request the struct to destroy
|
||||||
|
*/
|
||||||
|
void ipfs_core_http_request_free(struct HttpRequest* request) {
|
||||||
|
if (request != NULL) {
|
||||||
|
//if (request->command != NULL)
|
||||||
|
// free(request->command);
|
||||||
|
//if (request->sub_command != NULL)
|
||||||
|
// free(request->sub_command);
|
||||||
|
if (request->params != NULL) {
|
||||||
|
for(int i = 0; i < request->params->total; i++) {
|
||||||
|
struct HttpParam* curr_param = (struct HttpParam*) libp2p_utils_vector_get(request->params, i);
|
||||||
|
ipfs_core_http_param_free(curr_param);
|
||||||
|
}
|
||||||
|
libp2p_utils_vector_free(request->params);
|
||||||
|
}
|
||||||
|
if (request->arguments != NULL) {
|
||||||
|
//for(int i = 0; i < request->arguments->total; i++) {
|
||||||
|
// free((char*)libp2p_utils_vector_get(request->arguments, i));
|
||||||
|
//}
|
||||||
|
libp2p_utils_vector_free(request->arguments);
|
||||||
|
}
|
||||||
|
free(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HttpResponse* ipfs_core_http_response_new() {
|
||||||
|
struct HttpResponse* response = (struct HttpResponse*) malloc(sizeof(struct HttpResponse));
|
||||||
|
if (response != NULL) {
|
||||||
|
response->content_type = NULL;
|
||||||
|
response->bytes = NULL;
|
||||||
|
response->bytes_size = 0;
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ipfs_core_http_response_free(struct HttpResponse* response) {
|
||||||
|
if (response != NULL) {
|
||||||
|
// NOTE: content_type should not be dynamically allocated
|
||||||
|
if (response->bytes != NULL)
|
||||||
|
free(response->bytes);
|
||||||
|
free(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Build a new HttpParam
|
||||||
|
* @returns a newly allocated HttpParam struct
|
||||||
|
*/
|
||||||
|
struct HttpParam* ipfs_core_http_param_new() {
|
||||||
|
struct HttpParam* param = (struct HttpParam*) malloc(sizeof(struct HttpParam));
|
||||||
|
if (param != NULL) {
|
||||||
|
param->name = NULL;
|
||||||
|
param->value = NULL;
|
||||||
|
}
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Clean up resources allocated by a HttpParam struct
|
||||||
|
* @param param the struct to destroy
|
||||||
|
*/
|
||||||
|
void ipfs_core_http_param_free(struct HttpParam* param) {
|
||||||
|
if (param != NULL) {
|
||||||
|
if (param->name != NULL)
|
||||||
|
free(param->name);
|
||||||
|
if (param->value != NULL)
|
||||||
|
free(param->value);
|
||||||
|
free(param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Handle processing of the "name" command
|
||||||
|
* @param local_node the context
|
||||||
|
* @param request the incoming request
|
||||||
|
* @param response the response
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_core_http_process_name(struct IpfsNode* local_node, struct HttpRequest* request, struct HttpResponse** response) {
|
||||||
|
int retVal = 0;
|
||||||
|
char* path = NULL;
|
||||||
|
char* result = NULL;
|
||||||
|
if (request->arguments == NULL || request->arguments->total == 0) {
|
||||||
|
libp2p_logger_error("http_request", "process_name: empty arguments\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
path = (char*) libp2p_utils_vector_get(request->arguments, 0);
|
||||||
|
if (strcmp(request->sub_command, "resolve") == 0) {
|
||||||
|
retVal = ipfs_namesys_resolver_resolve(local_node, path, 1, &result);
|
||||||
|
if (retVal) {
|
||||||
|
*response = ipfs_core_http_response_new();
|
||||||
|
struct HttpResponse* res = *response;
|
||||||
|
res->content_type = "application/json";
|
||||||
|
res->bytes = (uint8_t*) malloc(strlen(local_node->identity->peer->id) + strlen(path) + 30);
|
||||||
|
if (res->bytes == NULL) {
|
||||||
|
free(result);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
sprintf((char*)res->bytes, "{ \"Path\": \"%s\" }", result);
|
||||||
|
res->bytes_size = strlen((char*)res->bytes);
|
||||||
|
}
|
||||||
|
free(result);
|
||||||
|
} else if (strcmp(request->sub_command, "publish") == 0) {
|
||||||
|
retVal = ipfs_namesys_publisher_publish(local_node, path);
|
||||||
|
if (retVal) {
|
||||||
|
*response = ipfs_core_http_response_new();
|
||||||
|
struct HttpResponse* res = *response;
|
||||||
|
res->content_type = "application/json";
|
||||||
|
res->bytes = (uint8_t*) malloc(strlen(local_node->identity->peer->id) + strlen(path) + 30);
|
||||||
|
if (res->bytes == NULL)
|
||||||
|
return 0;
|
||||||
|
sprintf((char*)res->bytes, "{ \"Name\": \"%s\"\n \"Value\": \"%s\" }", local_node->identity->peer->id, path);
|
||||||
|
res->bytes_size = strlen((char*)res->bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Handle processing of the "object" command
|
||||||
|
* @param local_node the context
|
||||||
|
* @param request the incoming request
|
||||||
|
* @param response the response
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_core_http_process_object(struct IpfsNode* local_node, struct HttpRequest* request, struct HttpResponse** response) {
|
||||||
|
int retVal = 0;
|
||||||
|
if (strcmp(request->sub_command, "get") == 0) {
|
||||||
|
// do an object_get
|
||||||
|
if (request->arguments->total == 1) {
|
||||||
|
char* hash = (char*)libp2p_utils_vector_get(request->arguments, 0);
|
||||||
|
struct Cid* cid = NULL;
|
||||||
|
if (!ipfs_cid_decode_hash_from_base58((unsigned char*)hash, strlen(hash), &cid)) {
|
||||||
|
ipfs_cid_free(cid);
|
||||||
|
cid = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*response = ipfs_core_http_response_new();
|
||||||
|
struct HttpResponse* res = *response;
|
||||||
|
res->content_type = "application/json";
|
||||||
|
FILE* response_file = open_memstream((char**)&res->bytes, &res->bytes_size);
|
||||||
|
retVal = ipfs_exporter_object_cat_to_file(local_node, cid->hash, cid->hash_length, response_file);
|
||||||
|
ipfs_cid_free(cid);
|
||||||
|
fclose(response_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ipfs_core_http_process_dht_provide(struct IpfsNode* local_node, struct HttpRequest* request, struct HttpResponse** response) {
|
||||||
|
int failedCount = 0;
|
||||||
|
for (int i = 0; i < request->arguments->total; i++) {
|
||||||
|
char* hash = (char*)libp2p_utils_vector_get(request->arguments, i);
|
||||||
|
struct Cid* cid;
|
||||||
|
if (!ipfs_cid_decode_hash_from_base58((unsigned char*)hash, strlen(hash), &cid)) {
|
||||||
|
ipfs_cid_free(cid);
|
||||||
|
cid = NULL;
|
||||||
|
failedCount++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!local_node->routing->Provide(local_node->routing, cid->hash, cid->hash_length)) {
|
||||||
|
ipfs_cid_free(cid);
|
||||||
|
cid = NULL;
|
||||||
|
failedCount++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ipfs_cid_free(cid);
|
||||||
|
}
|
||||||
|
*response = ipfs_core_http_response_new();
|
||||||
|
struct HttpResponse* res = *response;
|
||||||
|
res->content_type = "application/json";
|
||||||
|
res->bytes = (uint8_t*) malloc(1024);
|
||||||
|
if (res->bytes == NULL) {
|
||||||
|
res->bytes_size = 0;
|
||||||
|
} else {
|
||||||
|
if (!failedCount) {
|
||||||
|
// complete success
|
||||||
|
// TODO: do the right thing
|
||||||
|
snprintf((char*)res->bytes, 1024, "{\n\t\"ID\": \"<string>\"\n" \
|
||||||
|
"\t\"Type\": \"<int>\"\n"
|
||||||
|
"\t\"Responses\": [\n"
|
||||||
|
"\t\t{\n"
|
||||||
|
"\t\t\t\"ID\": \"<string>\"\n"
|
||||||
|
"\t\t\t\"Addrs\": [\n"
|
||||||
|
"\t\t\t\t\"<object>\"\n"
|
||||||
|
"\t\t\t]\n"
|
||||||
|
"\t\t}\n"
|
||||||
|
"\t]\n"
|
||||||
|
"\t\"Extra\": \"<string>\"\n"
|
||||||
|
"}\n"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// at least some failed
|
||||||
|
// TODO: do the right thing
|
||||||
|
snprintf((char*)res->bytes, 1024, "{\n\t\"ID\": \"<string>\",\n" \
|
||||||
|
"\t\"Type\": \"<int>\",\n"
|
||||||
|
"\t\"Responses\": [\n"
|
||||||
|
"\t\t{\n"
|
||||||
|
"\t\t\t\"ID\": \"<string>\",\n"
|
||||||
|
"\t\t\t\"Addrs\": [\n"
|
||||||
|
"\t\t\t\t\"<object>\"\n"
|
||||||
|
"\t\t\t]\n"
|
||||||
|
"\t\t}\n"
|
||||||
|
"\t],\n"
|
||||||
|
"\t\"Extra\": \"<string>\"\n"
|
||||||
|
"}\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
res->bytes_size = strlen((char*)res->bytes);
|
||||||
|
}
|
||||||
|
return failedCount < request->arguments->total;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ipfs_core_http_process_dht_get(struct IpfsNode* local_node, struct HttpRequest* request, struct HttpResponse** response) {
|
||||||
|
int failedCount = 0;
|
||||||
|
// for now, we can only handle 1 argument at a time
|
||||||
|
if (request->arguments != NULL && request->arguments->total != 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
*response = ipfs_core_http_response_new();
|
||||||
|
struct HttpResponse* res = *response;
|
||||||
|
res->content_type = "application/octet-stream";
|
||||||
|
|
||||||
|
for (int i = 0; i < request->arguments->total; i++) {
|
||||||
|
char* hash = (char*)libp2p_utils_vector_get(request->arguments, i);
|
||||||
|
struct Cid* cid;
|
||||||
|
if (!ipfs_cid_decode_hash_from_base58((unsigned char*)hash, strlen(hash), &cid)) {
|
||||||
|
ipfs_cid_free(cid);
|
||||||
|
cid = NULL;
|
||||||
|
failedCount++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!local_node->routing->GetValue(local_node->routing, cid->hash, cid->hash_length, (void**)&res->bytes, &res->bytes_size)) {
|
||||||
|
ipfs_cid_free(cid);
|
||||||
|
cid = NULL;
|
||||||
|
failedCount++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ipfs_cid_free(cid);
|
||||||
|
//TODO: we need to handle multiple arguments
|
||||||
|
}
|
||||||
|
return failedCount < request->arguments->total;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* process dht commands
|
||||||
|
* @param local_node the context
|
||||||
|
* @param request the request
|
||||||
|
* @param response where to put the results
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_core_http_process_dht(struct IpfsNode* local_node, struct HttpRequest* request, struct HttpResponse** response) {
|
||||||
|
int retVal = 0;
|
||||||
|
if (strcmp(request->sub_command, "provide") == 0) {
|
||||||
|
// do a dht provide
|
||||||
|
retVal = ipfs_core_http_process_dht_provide(local_node, request, response);
|
||||||
|
} else if (strcmp(request->sub_command, "get") == 0) {
|
||||||
|
// do a dht get
|
||||||
|
retVal = ipfs_core_http_process_dht_get(local_node, request, response);
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int ipfs_core_http_process_swarm_connect(struct IpfsNode* local_node, struct HttpRequest* request, struct HttpResponse** resp) {
|
||||||
|
// get the address
|
||||||
|
if (request->arguments == NULL || request->arguments->total < 0)
|
||||||
|
return 0;
|
||||||
|
const char* address = (char*) libp2p_utils_vector_get(request->arguments, 0);
|
||||||
|
if (address == NULL)
|
||||||
|
return 0;
|
||||||
|
// TODO: see if we are already connected, or at least already have this peer in our peerstore
|
||||||
|
// attempt to connect
|
||||||
|
struct MultiAddress* ma = multiaddress_new_from_string(address);
|
||||||
|
if (ma == NULL) {
|
||||||
|
libp2p_logger_error("http_request", "swarm_connect: Unable to convert %s to a MultiAddress.\n", address);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
struct Libp2pPeer* new_peer = libp2p_peer_new_from_multiaddress(ma);
|
||||||
|
if (!libp2p_peer_connect(local_node->dialer, new_peer, local_node->peerstore, local_node->repo->config->datastore, 30)) {
|
||||||
|
libp2p_logger_error("http_request", "swarm_connect: Unable to connect to peer %s.\n", libp2p_peer_id_to_string(new_peer));
|
||||||
|
libp2p_peer_free(new_peer);
|
||||||
|
multiaddress_free(ma);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*resp = ipfs_core_http_response_new();
|
||||||
|
struct HttpResponse* response = *resp;
|
||||||
|
if (response == NULL) {
|
||||||
|
libp2p_logger_error("http_response", "swarm_connect: Unable to allocate memory for the response.\n");
|
||||||
|
libp2p_peer_free(new_peer);
|
||||||
|
multiaddress_free(ma);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
response->content_type = "application/json";
|
||||||
|
char* json = "{ \"Strings\": [ \"%s\"] }";
|
||||||
|
response->bytes_size = strlen(json) + strlen(address) + 1;
|
||||||
|
response->bytes = (uint8_t*) malloc(response->bytes_size);
|
||||||
|
if (response->bytes == NULL) {
|
||||||
|
response->bytes_size = 0;
|
||||||
|
response->content_type = NULL;
|
||||||
|
libp2p_peer_free(new_peer);
|
||||||
|
multiaddress_free(ma);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
sprintf((char*)response->bytes, json, address);
|
||||||
|
// getting rid of the peer here will close the connection. That's not what we want
|
||||||
|
//libp2p_peer_free(new_peer);
|
||||||
|
multiaddress_free(ma);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int ipfs_core_http_process_swarm(struct IpfsNode* local_node, struct HttpRequest* request, struct HttpResponse** response) {
|
||||||
|
int retVal = 0;
|
||||||
|
if (strcmp(request->sub_command, "connect") == 0) {
|
||||||
|
// connect to a peer
|
||||||
|
retVal = ipfs_core_http_process_swarm_connect(local_node, request, response);
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Process the parameters passed in from an http request
|
||||||
|
* @param local_node the context
|
||||||
|
* @param request the request
|
||||||
|
* @param response the response
|
||||||
|
* @returns true(1) on success, false(0) otherwise.
|
||||||
|
*/
|
||||||
|
int ipfs_core_http_request_process(struct IpfsNode* local_node, struct HttpRequest* request, struct HttpResponse** response) {
|
||||||
|
if (request == NULL)
|
||||||
|
return 0;
|
||||||
|
int retVal = 0;
|
||||||
|
|
||||||
|
if (strcmp(request->command, "name") == 0) {
|
||||||
|
retVal = ipfs_core_http_process_name(local_node, request, response);
|
||||||
|
} else if (strcmp(request->command, "object") == 0) {
|
||||||
|
retVal = ipfs_core_http_process_object(local_node, request, response);
|
||||||
|
} else if (strcmp(request->command, "dht") == 0) {
|
||||||
|
retVal = ipfs_core_http_process_dht(local_node, request, response);
|
||||||
|
} else if (strcmp(request->command, "swarm") == 0) {
|
||||||
|
retVal = ipfs_core_http_process_swarm(local_node, request, response);
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* just builds the basics of an http api url
|
||||||
|
* @param local_node where to get the ip and port
|
||||||
|
* @returns a string in the form of "http://127.0.0.1:5001/api/v0" (or whatever was in the config file for the API)
|
||||||
|
*/
|
||||||
|
char* ipfs_core_http_request_build_url_start(struct IpfsNode* local_node) {
|
||||||
|
char* host = NULL;
|
||||||
|
char port[10] = "";
|
||||||
|
struct MultiAddress* ma = multiaddress_new_from_string(local_node->repo->config->addresses->api);
|
||||||
|
if (ma == NULL)
|
||||||
|
return NULL;
|
||||||
|
if (!multiaddress_get_ip_address(ma, &host)) {
|
||||||
|
multiaddress_free(ma);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int portInt = multiaddress_get_ip_port(ma);
|
||||||
|
sprintf(port, "%d", portInt);
|
||||||
|
int len = 18 + strlen(host) + strlen(port);
|
||||||
|
char* retVal = malloc(len);
|
||||||
|
if (retVal != NULL) {
|
||||||
|
sprintf(retVal, "http://%s:%s/api/v0", host, port);
|
||||||
|
}
|
||||||
|
free(host);
|
||||||
|
multiaddress_free(ma);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Adds commands to url
|
||||||
|
* @param request the request
|
||||||
|
* @param url the starting url, and will hold the results (i.e. http://127.0.0.1:5001/api/v0/<command>/<sub_command>
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_core_http_request_add_commands(struct HttpRequest* request, char** url) {
|
||||||
|
// command
|
||||||
|
int addl_length = strlen(request->command) + 2;
|
||||||
|
char* string1 = (char*) malloc(strlen(*url) + addl_length);
|
||||||
|
if (string1 != NULL) {
|
||||||
|
sprintf(string1, "%s/%s", *url, request->command);
|
||||||
|
free(*url);
|
||||||
|
*url = string1;
|
||||||
|
// sub_command
|
||||||
|
if (request->sub_command != NULL) {
|
||||||
|
addl_length = strlen(request->sub_command) + 2;
|
||||||
|
string1 = (char*) malloc(strlen(*url) + addl_length);
|
||||||
|
sprintf(string1, "%s/%s", *url, request->sub_command);
|
||||||
|
free(*url);
|
||||||
|
*url = string1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string1 != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Add parameters and arguments to the URL
|
||||||
|
* @param request the request
|
||||||
|
* @param url the url to continue to build
|
||||||
|
*/
|
||||||
|
int ipfs_core_http_request_add_parameters(struct HttpRequest* request, char** url) {
|
||||||
|
// params
|
||||||
|
if (request->params != NULL) {
|
||||||
|
for (int i = 0; i < request->params->total; i++) {
|
||||||
|
struct HttpParam* curr_param = (struct HttpParam*) libp2p_utils_vector_get(request->params, i);
|
||||||
|
int len = strlen(curr_param->name) + strlen(curr_param->value) + 2;
|
||||||
|
char* str = (char*) malloc(strlen(*url) + len);
|
||||||
|
if (str == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
sprintf(str, "%s/%s=%s", *url, curr_param->name, curr_param->value);
|
||||||
|
free(*url);
|
||||||
|
*url = str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// args
|
||||||
|
if (request->arguments != NULL) {
|
||||||
|
int isFirst = 1;
|
||||||
|
for(int i = 0; i < request->arguments->total; i++) {
|
||||||
|
char* arg = (char*) libp2p_utils_vector_get(request->arguments, i);
|
||||||
|
int len = strlen(arg) + strlen(*url) + 6;
|
||||||
|
char* str = (char*) malloc(len);
|
||||||
|
if (str == NULL)
|
||||||
|
return 0;
|
||||||
|
if (isFirst)
|
||||||
|
sprintf(str, "%s?arg=%s", *url, arg);
|
||||||
|
else
|
||||||
|
sprintf(str, "%s&arg=%s", *url, arg);
|
||||||
|
free(*url);
|
||||||
|
*url = str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct curl_string {
|
||||||
|
char* ptr;
|
||||||
|
size_t len;
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t curl_cb(void* ptr, size_t size, size_t nmemb, struct curl_string* str) {
|
||||||
|
size_t new_len = str->len + size * nmemb;
|
||||||
|
str->ptr = realloc(str->ptr, new_len + 1);
|
||||||
|
if (str->ptr != NULL) {
|
||||||
|
memcpy(str->ptr + str->len, ptr, size*nmemb);
|
||||||
|
str->ptr[new_len] = '\0';
|
||||||
|
str->len = new_len;
|
||||||
|
}
|
||||||
|
return size * nmemb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do an HTTP Get to the local API
|
||||||
|
* @param local_node the context
|
||||||
|
* @param request the request
|
||||||
|
* @param result the results
|
||||||
|
* @param result_size the size of the results
|
||||||
|
* @returns true(1) on success, false(0) on error
|
||||||
|
*/
|
||||||
|
int ipfs_core_http_request_get(struct IpfsNode* local_node, struct HttpRequest* request, char** result, size_t *result_size) {
|
||||||
|
if (request == NULL || request->command == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
char* url = ipfs_core_http_request_build_url_start(local_node);
|
||||||
|
if (url == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!ipfs_core_http_request_add_commands(request, &url)) {
|
||||||
|
free(url);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ipfs_core_http_request_add_parameters(request, &url)) {
|
||||||
|
free(url);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// do the GET using libcurl
|
||||||
|
CURL *curl;
|
||||||
|
CURLcode res;
|
||||||
|
struct curl_string s;
|
||||||
|
s.len = 0;
|
||||||
|
s.ptr = malloc(1);
|
||||||
|
s.ptr[0] = '\0';
|
||||||
|
|
||||||
|
curl = curl_easy_init();
|
||||||
|
if (!curl) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_cb);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
|
||||||
|
res = curl_easy_perform(curl);
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
if (res == CURLE_OK) {
|
||||||
|
if (strcmp(s.ptr, "404 page not found") != 0) {
|
||||||
|
*result = s.ptr;
|
||||||
|
*result_size = s.len;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
res = -1;
|
||||||
|
} else {
|
||||||
|
libp2p_logger_error("http_request", "Results of [%s] returned failure. Return value: %d.\n", url, res);
|
||||||
|
if (s.ptr != NULL)
|
||||||
|
free(s.ptr);
|
||||||
|
}
|
||||||
|
return res == CURLE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do an HTTP Post to the local API
|
||||||
|
* @param local_node the context
|
||||||
|
* @param request the request
|
||||||
|
* @param result the results
|
||||||
|
* @param result_size the size of the results
|
||||||
|
* @param data the array with post data
|
||||||
|
* @param data_size the data length
|
||||||
|
* @returns true(1) on success, false(0) on error
|
||||||
|
*/
|
||||||
|
int ipfs_core_http_request_post(struct IpfsNode* local_node, struct HttpRequest* request, char** result, size_t* result_size, char *data, size_t data_size) {
|
||||||
|
if (request == NULL || request->command == NULL || data == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
char* url = ipfs_core_http_request_build_url_start(local_node);
|
||||||
|
if (url == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!ipfs_core_http_request_add_commands(request, &url)) {
|
||||||
|
free(url);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ipfs_core_http_request_add_parameters(request, &url)) {
|
||||||
|
free(url);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// do the POST using libcurl
|
||||||
|
CURL *curl;
|
||||||
|
CURLcode res;
|
||||||
|
struct curl_string s;
|
||||||
|
s.len = 0;
|
||||||
|
s.ptr = malloc(1);
|
||||||
|
s.ptr[0] = '\0';
|
||||||
|
|
||||||
|
struct curl_httppost *post = NULL, *last = NULL;
|
||||||
|
CURLFORMcode curl_form_ret = curl_formadd(&post, &last,
|
||||||
|
CURLFORM_COPYNAME, "filename",
|
||||||
|
CURLFORM_PTRCONTENTS, data,
|
||||||
|
CURLFORM_CONTENTTYPE, "application/octet-stream",
|
||||||
|
CURLFORM_FILENAME, "",
|
||||||
|
CURLFORM_CONTENTSLENGTH, data_size,
|
||||||
|
CURLFORM_END);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (CURL_FORMADD_OK != curl_form_ret) {
|
||||||
|
// i'm always getting curl_form_ret == 4 here
|
||||||
|
// it means CURL_FORMADD_UNKNOWN_OPTION
|
||||||
|
// what i'm doing wrong?
|
||||||
|
fprintf(stderr, "curl_form_ret = %d\n", (int)curl_form_ret);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
curl = curl_easy_init();
|
||||||
|
if (!curl) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_cb);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_HTTPPOST, post);
|
||||||
|
res = curl_easy_perform(curl);
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
if (res == CURLE_OK) {
|
||||||
|
if (strcmp(s.ptr, "404 page not found") != 0) {
|
||||||
|
*result = s.ptr;
|
||||||
|
*result_size = s.len;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
res = -1;
|
||||||
|
} else {
|
||||||
|
//libp2p_logger_error("http_request", "Results of [%s] returned failure. Return value: %d.\n", url, res);
|
||||||
|
fprintf(stderr, "Results of [%s] returned failure. Return value: %d.\n", url, res);
|
||||||
|
if (s.ptr != NULL)
|
||||||
|
free(s.ptr);
|
||||||
|
}
|
||||||
|
return res == CURLE_OK;
|
||||||
|
}
|
191
core/ipfs_node.c
Normal file
191
core/ipfs_node.c
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include "libp2p/conn/dialer.h"
|
||||||
|
#include "libp2p/identify/identify.h"
|
||||||
|
#include "libp2p/net/multistream.h"
|
||||||
|
#include "libp2p/utils/vector.h"
|
||||||
|
#include "libp2p/secio/secio.h"
|
||||||
|
#include "libp2p/routing/dht_protocol.h"
|
||||||
|
#include "libp2p/yamux/yamux.h"
|
||||||
|
#include "ipfs/core/api.h"
|
||||||
|
#include "ipfs/core/client_api.h"
|
||||||
|
#include "ipfs/core/ipfs_node.h"
|
||||||
|
#include "ipfs/exchange/bitswap/bitswap.h"
|
||||||
|
#include "ipfs/journal/journal.h"
|
||||||
|
|
||||||
|
struct IpfsNode* ipfs_node_new() {
|
||||||
|
struct IpfsNode* node = malloc(sizeof(struct IpfsNode));
|
||||||
|
if (node != NULL) {
|
||||||
|
node->blockstore = NULL;
|
||||||
|
node->exchange = NULL;
|
||||||
|
node->identity = NULL;
|
||||||
|
node->mode = MODE_OFFLINE;
|
||||||
|
node->peerstore = NULL;
|
||||||
|
node->protocol_handlers = NULL;
|
||||||
|
node->providerstore = NULL;
|
||||||
|
node->repo = NULL;
|
||||||
|
node->routing = NULL;
|
||||||
|
node->api_context = NULL;
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Libp2pVector* ipfs_node_online_build_protocol_handlers(struct IpfsNode* node) {
|
||||||
|
struct Libp2pVector* retVal = libp2p_utils_vector_new(1);
|
||||||
|
if (retVal != NULL) {
|
||||||
|
// secio
|
||||||
|
libp2p_utils_vector_add(retVal, libp2p_secio_build_protocol_handler(&node->identity->private_key, node->peerstore));
|
||||||
|
// journal
|
||||||
|
libp2p_utils_vector_add(retVal, ipfs_journal_build_protocol_handler(node));
|
||||||
|
// kademlia
|
||||||
|
libp2p_utils_vector_add(retVal, libp2p_routing_dht_build_protocol_handler(node->peerstore, node->providerstore, node->repo->config->datastore, node->repo->config->filestore));
|
||||||
|
// bitswap
|
||||||
|
libp2p_utils_vector_add(retVal, ipfs_bitswap_build_protocol_handler(node));
|
||||||
|
// multistream
|
||||||
|
libp2p_utils_vector_add(retVal, libp2p_net_multistream_build_protocol_handler(retVal));
|
||||||
|
// yamux
|
||||||
|
libp2p_utils_vector_add(retVal, libp2p_yamux_build_protocol_handler());
|
||||||
|
// identify
|
||||||
|
libp2p_utils_vector_add(retVal, libp2p_identify_build_protocol_handler(node->identity->peer->id, node->identity->peer->id_size));
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ipfs_node_online_protocol_handlers_free(struct Libp2pVector* handlers) {
|
||||||
|
for(int i = 0; i < handlers->total; i++) {
|
||||||
|
struct Libp2pProtocolHandler* current = (struct Libp2pProtocolHandler*) libp2p_utils_vector_get(handlers, i);
|
||||||
|
current->Shutdown(current->context);
|
||||||
|
free(current);
|
||||||
|
}
|
||||||
|
libp2p_utils_vector_free(handlers);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* build an online IpfsNode
|
||||||
|
* @param repo_path where the IPFS repository directory is
|
||||||
|
* @param node the completed IpfsNode struct
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_node_online_new(const char* repo_path, struct IpfsNode** node) {
|
||||||
|
struct FSRepo* fs_repo = NULL;
|
||||||
|
|
||||||
|
*node = ipfs_node_new();
|
||||||
|
if(*node == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
struct IpfsNode* local_node = *node;
|
||||||
|
|
||||||
|
// build the struct
|
||||||
|
if (!ipfs_repo_fsrepo_new(repo_path, NULL, &fs_repo)) {
|
||||||
|
ipfs_node_free(local_node);
|
||||||
|
*node = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// open the repo
|
||||||
|
if (!ipfs_repo_fsrepo_open(fs_repo)) {
|
||||||
|
ipfs_node_free(local_node);
|
||||||
|
*node = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill in the node
|
||||||
|
local_node->repo = fs_repo;
|
||||||
|
local_node->identity = fs_repo->config->identity;
|
||||||
|
local_node->peerstore = libp2p_peerstore_new(local_node->identity->peer);
|
||||||
|
local_node->providerstore = libp2p_providerstore_new(fs_repo->config->datastore, local_node->identity->peer);
|
||||||
|
local_node->blockstore = ipfs_blockstore_new(fs_repo);
|
||||||
|
local_node->protocol_handlers = ipfs_node_online_build_protocol_handlers(local_node);
|
||||||
|
local_node->mode = MODE_OFFLINE;
|
||||||
|
local_node->routing = ipfs_routing_new_online(local_node, &fs_repo->config->identity->private_key);
|
||||||
|
local_node->exchange = ipfs_bitswap_new(local_node);
|
||||||
|
local_node->swarm = libp2p_swarm_new(local_node->protocol_handlers, local_node->repo->config->datastore, local_node->repo->config->filestore);
|
||||||
|
local_node->dialer = libp2p_conn_dialer_new(local_node->identity->peer, local_node->peerstore, &local_node->identity->private_key, local_node->swarm);
|
||||||
|
|
||||||
|
// fire up the API
|
||||||
|
api_start(local_node, 10, 5);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* build an offline IpfsNode
|
||||||
|
* @param repo_path where the IPFS repository directory is
|
||||||
|
* @param node the completed IpfsNode struct
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_node_offline_new(const char* repo_path, struct IpfsNode** node) {
|
||||||
|
struct FSRepo* fs_repo = NULL;
|
||||||
|
|
||||||
|
*node = ipfs_node_new();
|
||||||
|
if(*node == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
struct IpfsNode* local_node = *node;
|
||||||
|
|
||||||
|
// build the struct
|
||||||
|
if (!ipfs_repo_fsrepo_new(repo_path, NULL, &fs_repo)) {
|
||||||
|
ipfs_node_free(local_node);
|
||||||
|
*node = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// open the repo
|
||||||
|
if (!ipfs_repo_fsrepo_open(fs_repo)) {
|
||||||
|
ipfs_node_free(local_node);
|
||||||
|
*node = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill in the node
|
||||||
|
local_node->repo = fs_repo;
|
||||||
|
local_node->identity = fs_repo->config->identity;
|
||||||
|
local_node->peerstore = libp2p_peerstore_new(local_node->identity->peer);
|
||||||
|
local_node->providerstore = libp2p_providerstore_new(fs_repo->config->datastore, local_node->identity->peer);
|
||||||
|
local_node->blockstore = ipfs_blockstore_new(fs_repo);
|
||||||
|
local_node->protocol_handlers = ipfs_node_online_build_protocol_handlers(local_node);
|
||||||
|
local_node->mode = MODE_OFFLINE;
|
||||||
|
local_node->routing = ipfs_routing_new_offline(local_node, &fs_repo->config->identity->private_key);
|
||||||
|
local_node->exchange = ipfs_bitswap_new(local_node);
|
||||||
|
local_node->swarm = libp2p_swarm_new(local_node->protocol_handlers, local_node->repo->config->datastore, local_node->repo->config->filestore);
|
||||||
|
local_node->dialer = libp2p_conn_dialer_new(local_node->identity->peer, local_node->peerstore, &local_node->identity->private_key, local_node->swarm);
|
||||||
|
|
||||||
|
if (api_running(local_node))
|
||||||
|
local_node->mode = MODE_API_AVAILABLE;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Free resources from the creation of an IpfsNode
|
||||||
|
* @param node the node to free
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_node_free(struct IpfsNode* node) {
|
||||||
|
if (node != NULL) {
|
||||||
|
if (node->api_context != NULL && node->api_context->api_thread != 0)
|
||||||
|
api_stop(node);
|
||||||
|
if (node->exchange != NULL) {
|
||||||
|
node->exchange->Close(node->exchange);
|
||||||
|
}
|
||||||
|
if (node->providerstore != NULL)
|
||||||
|
libp2p_providerstore_free(node->providerstore);
|
||||||
|
if (node->peerstore != NULL)
|
||||||
|
libp2p_peerstore_free(node->peerstore);
|
||||||
|
if (node->repo != NULL)
|
||||||
|
ipfs_repo_fsrepo_free(node->repo);
|
||||||
|
if (node->protocol_handlers != NULL)
|
||||||
|
ipfs_node_online_protocol_handlers_free(node->protocol_handlers);
|
||||||
|
if (node->mode == MODE_ONLINE) {
|
||||||
|
ipfs_routing_online_free(node->routing);
|
||||||
|
}
|
||||||
|
if (node->mode == MODE_OFFLINE || node->mode == MODE_API_AVAILABLE) {
|
||||||
|
ipfs_routing_offline_free(node->routing);
|
||||||
|
}
|
||||||
|
if (node->blockstore != NULL) {
|
||||||
|
ipfs_blockstore_free(node->blockstore);
|
||||||
|
}
|
||||||
|
free(node);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
42
core/net.c
Normal file
42
core/net.c
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
|
||||||
|
#include "ipfs/core/net.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do a socket accept
|
||||||
|
* @param listener the listener
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_core_net_accept(struct IpfsListener listener) {
|
||||||
|
//TODO: Implement this
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen using a particular protocol
|
||||||
|
* @param node the node
|
||||||
|
* @param protocol the protocol to use
|
||||||
|
* @param listener the results
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_core_net_listen(struct IpfsNode* node, char* protocol, struct IpfsListener* listener){
|
||||||
|
// TODO: Implement this
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Dial a peer
|
||||||
|
* @param node this node
|
||||||
|
* @param peer_id who to dial (null terminated string)
|
||||||
|
* @param protocol the protocol to use
|
||||||
|
* @param stream the resultant stream
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipsf_core_net_dial(const struct IpfsNode* node, const char* peer_id, const char* protocol, struct Stream* stream) {
|
||||||
|
//TODO: Implement this
|
||||||
|
// get the multiaddress from the peerstore
|
||||||
|
struct Libp2pPeer* peer = libp2p_peerstore_get_peer(node->peerstore, peer_id, strlen(peer_id));
|
||||||
|
// attempt to connect
|
||||||
|
|
||||||
|
// attempt to use the protocol passed in
|
||||||
|
return 0;
|
||||||
|
}
|
238
core/null.c
Normal file
238
core/null.c
Normal file
|
@ -0,0 +1,238 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "libp2p/conn/session.h"
|
||||||
|
#include "libp2p/net/connectionstream.h"
|
||||||
|
#include "libp2p/net/multistream.h"
|
||||||
|
#include "libp2p/net/p2pnet.h"
|
||||||
|
#include "libp2p/net/protocol.h"
|
||||||
|
#include "libp2p/nodeio/nodeio.h"
|
||||||
|
#include "libp2p/os/utils.h"
|
||||||
|
#include "libp2p/record/message.h"
|
||||||
|
#include "libp2p/routing/dht_protocol.h"
|
||||||
|
#include "libp2p/secio/secio.h"
|
||||||
|
#include "libp2p/utils/logger.h"
|
||||||
|
#include "libp2p/swarm/swarm.h"
|
||||||
|
#include "ipfs/core/daemon.h"
|
||||||
|
#include "ipfs/core/ipfs_node.h"
|
||||||
|
#include "ipfs/exchange/bitswap/network.h"
|
||||||
|
#include "ipfs/journal/journal.h"
|
||||||
|
#include "ipfs/merkledag/merkledag.h"
|
||||||
|
#include "ipfs/merkledag/node.h"
|
||||||
|
#include "ipfs/routing/routing.h"
|
||||||
|
#include "ipfs/util/thread_pool.h"
|
||||||
|
#include "libp2p/swarm/swarm.h"
|
||||||
|
|
||||||
|
#define BUF_SIZE 4096
|
||||||
|
|
||||||
|
// this should be set to 5 for normal operation, perhaps higher for debugging purposes
|
||||||
|
#define DEFAULT_NETWORK_TIMEOUT 5
|
||||||
|
|
||||||
|
static int null_shutting_down = 0;
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Listens on a particular stream, and marshals the request
|
||||||
|
* @param stream the stream to listen to
|
||||||
|
* @param protocol_handlers a vector of protocol handlers
|
||||||
|
* @returns <0 on error, 0 if we shouldn't handle this anymore, or 1 on success
|
||||||
|
*/
|
||||||
|
int ipfs_null_listen_and_handle(struct Stream* stream, struct Libp2pVector* protocol_handlers) {
|
||||||
|
struct StreamMessage* results = NULL;
|
||||||
|
int retVal = 0;
|
||||||
|
// Read from the network
|
||||||
|
if (!stream->read(stream->stream_context, &results, DEFAULT_NETWORK_TIMEOUT)) {
|
||||||
|
libp2p_logger_error("null", "Unable to read from network. Exiting.\n");
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
if (results != NULL) {
|
||||||
|
libp2p_logger_debug("null", "Attempting to marshal %d bytes from network.\n", results->data_size);
|
||||||
|
retVal = libp2p_protocol_marshal(results, stream, protocol_handlers);
|
||||||
|
libp2p_logger_debug("null", "The return value from the attempt to marshal %d bytes was %d.\n", results->data_size, retVal);
|
||||||
|
libp2p_stream_message_free(results);
|
||||||
|
} else {
|
||||||
|
libp2p_logger_debug("null", "Attempted read, but results were null. This is normal.\n");
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We've received a new connection. Find out what they want.
|
||||||
|
*
|
||||||
|
* @param ptr a pointer to a null_connection_params struct
|
||||||
|
*/
|
||||||
|
void ipfs_null_connection (void *ptr) {
|
||||||
|
struct null_connection_params *connection_param = (struct null_connection_params*) ptr;
|
||||||
|
int retVal = 0;
|
||||||
|
|
||||||
|
struct SessionContext* session = libp2p_session_context_new();
|
||||||
|
if (session == NULL) {
|
||||||
|
libp2p_logger_error("null", "Unable to allocate SessionContext. Out of memory?\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
session->datastore = connection_param->local_node->repo->config->datastore;
|
||||||
|
session->filestore = connection_param->local_node->repo->config->filestore;
|
||||||
|
session->host = connection_param->ip;
|
||||||
|
session->port = connection_param->port;
|
||||||
|
session->insecure_stream = libp2p_net_connection_new(connection_param->file_descriptor, connection_param->ip, connection_param->port, session);
|
||||||
|
session->default_stream = session->insecure_stream;
|
||||||
|
|
||||||
|
libp2p_logger_info("null", "Connection %d, count %d\n", connection_param->file_descriptor, *(connection_param->count));
|
||||||
|
|
||||||
|
// try to read from the network
|
||||||
|
// handle the call
|
||||||
|
for(;;) {
|
||||||
|
// Read from the network
|
||||||
|
retVal = ipfs_null_listen_and_handle(session->default_stream, connection_param->local_node->protocol_handlers);
|
||||||
|
if (retVal < 0) {
|
||||||
|
// exit the loop on error
|
||||||
|
libp2p_logger_debug("null", "Exiting loop due to retVal being %d.\n", retVal);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} // end of loop
|
||||||
|
|
||||||
|
(*(connection_param->count))--; // update counter.
|
||||||
|
if (connection_param->ip != NULL)
|
||||||
|
free(connection_param->ip);
|
||||||
|
free (connection_param);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ipfs_null_do_maintenance(struct IpfsNode* local_node, struct Libp2pPeer* peer) {
|
||||||
|
if (peer == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (peer->is_local) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
// Is this peer one of our backup partners?
|
||||||
|
struct ReplicationPeer* replication_peer = repo_config_get_replication_peer(local_node->repo->config->replication, peer);
|
||||||
|
long long announce_secs = local_node->repo->config->replication->announce_minutes * 60;
|
||||||
|
// If so, has there been enough time since the last attempt a backup?
|
||||||
|
if (replication_peer != NULL) {
|
||||||
|
announce_secs -= os_utils_gmtime() - replication_peer->lastConnect;
|
||||||
|
libp2p_logger_debug("null", "Checking to see if we should send backup notification to peer %s. Time since last backup: %lld.\n", libp2p_peer_id_to_string(replication_peer->peer), announce_secs);
|
||||||
|
}
|
||||||
|
// should we attempt to connect if we're not already?
|
||||||
|
if (replication_peer != NULL && local_node->repo->config->replication->announce && announce_secs < 0) {
|
||||||
|
// try to connect if we aren't already
|
||||||
|
if (peer->connection_type != CONNECTION_TYPE_CONNECTED) {
|
||||||
|
if (!libp2p_peer_connect(local_node->dialer, peer, local_node->peerstore, local_node->repo->config->datastore, 2)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// attempt a backup, don't forget to reset timer
|
||||||
|
libp2p_logger_debug("null", "Attempting a sync of node %s.\n", libp2p_peer_id_to_string(peer));
|
||||||
|
ipfs_journal_sync(local_node, replication_peer);
|
||||||
|
libp2p_logger_debug("null", "Sync message sent. Maintenance complete for node %s.\n", libp2p_peer_id_to_string(peer));
|
||||||
|
} else {
|
||||||
|
unsigned long long last_comm = libp2p_peer_last_comm(peer);
|
||||||
|
if (os_utils_gmtime() - last_comm > 180) {
|
||||||
|
// try a ping, but only if we're connected
|
||||||
|
libp2p_logger_debug("null", "Attempting ping of %s.\n", libp2p_peer_id_to_string(peer));
|
||||||
|
if (peer->connection_type == CONNECTION_TYPE_CONNECTED && !local_node->routing->Ping(local_node->routing, peer)) {
|
||||||
|
libp2p_logger_debug("null", "Attempted ping of %s failed.\n", peer->id);
|
||||||
|
peer->connection_type = CONNECTION_TYPE_NOT_CONNECTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Called by the daemon to listen for connections
|
||||||
|
* @param ptr a pointer to an IpfsNodeListenParams struct
|
||||||
|
* @returns nothing useful.
|
||||||
|
*/
|
||||||
|
void* ipfs_null_listen (void *ptr)
|
||||||
|
{
|
||||||
|
null_shutting_down = 0;
|
||||||
|
int socketfd, s, count = 0;
|
||||||
|
//threadpool thpool = thpool_init(25);
|
||||||
|
struct IpfsNodeListenParams *listen_param;
|
||||||
|
|
||||||
|
listen_param = (struct IpfsNodeListenParams*) ptr;
|
||||||
|
|
||||||
|
if ((socketfd = socket_listen(socket_tcp4(), &(listen_param->ipv4), &(listen_param->port))) <= 0) {
|
||||||
|
libp2p_logger_error("null", "Failed to init null router. Address: %d, Port: %d\n", listen_param->ipv4, listen_param->port);
|
||||||
|
return (void*) 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
libp2p_logger_info("null", "Ipfs listening on %d\n", listen_param->port);
|
||||||
|
|
||||||
|
// when we have nothing to do, check on the connections to see if we're still connected
|
||||||
|
struct Libp2pLinkedList* current_peer_entry = NULL;
|
||||||
|
if (listen_param->local_node->peerstore->head_entry != NULL)
|
||||||
|
current_peer_entry = listen_param->local_node->peerstore->head_entry;
|
||||||
|
|
||||||
|
// the main loop, listening for new connections
|
||||||
|
for (;;) {
|
||||||
|
//libp2p_logger_debug("null", "%s Attempting socket read with fd %d.\n", listen_param->local_node->identity->peer->id, socketfd);
|
||||||
|
int numDescriptors = socket_read_select4(socketfd, 2);
|
||||||
|
if (null_shutting_down) {
|
||||||
|
libp2p_logger_debug("null", "%s null_listen shutting down.\n", listen_param->local_node->identity->peer->id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (numDescriptors > 0) {
|
||||||
|
s = socket_accept4(socketfd, &(listen_param->ipv4), &(listen_param->port));
|
||||||
|
if (count >= CONNECTIONS) { // limit reached.
|
||||||
|
close (s);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the new connection to the swarm
|
||||||
|
libp2p_swarm_add_connection(listen_param->local_node->swarm, s, listen_param->ipv4, listen_param->port);
|
||||||
|
|
||||||
|
/*
|
||||||
|
count++;
|
||||||
|
connection_param = malloc (sizeof (struct null_connection_params));
|
||||||
|
if (connection_param) {
|
||||||
|
connection_param->file_descriptor = s;
|
||||||
|
connection_param->count = &count;
|
||||||
|
connection_param->local_node = listen_param->local_node;
|
||||||
|
connection_param->port = listen_param->port;
|
||||||
|
connection_param->ip = malloc(INET_ADDRSTRLEN);
|
||||||
|
if (connection_param->ip == NULL) {
|
||||||
|
// we are out of memory
|
||||||
|
free(connection_param);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (inet_ntop(AF_INET, &(listen_param->ipv4), connection_param->ip, INET_ADDRSTRLEN) == NULL) {
|
||||||
|
free(connection_param->ip);
|
||||||
|
connection_param->ip = NULL;
|
||||||
|
connection_param->port = 0;
|
||||||
|
}
|
||||||
|
// Create pthread for ipfs_null_connection.
|
||||||
|
thpool_add_work(thpool, ipfs_null_connection, connection_param);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
} else {
|
||||||
|
// timeout... do maintenance
|
||||||
|
//struct PeerEntry* entry = current_peer_entry->item;
|
||||||
|
// JMJ Debugging
|
||||||
|
//ipfs_null_do_maintenance(listen_param->local_node, entry->peer);
|
||||||
|
if (current_peer_entry != NULL)
|
||||||
|
current_peer_entry = current_peer_entry->next;
|
||||||
|
if (current_peer_entry == NULL)
|
||||||
|
current_peer_entry = listen_param->local_node->peerstore->head_entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//thpool_destroy(thpool);
|
||||||
|
|
||||||
|
close(socketfd);
|
||||||
|
|
||||||
|
return (void*) 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ipfs_null_shutdown() {
|
||||||
|
null_shutting_down = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
124
core/ping.c
Normal file
124
core/ping.c
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include "libp2p/net/p2pnet.h"
|
||||||
|
#include "libp2p/net/multistream.h"
|
||||||
|
#include "libp2p/record/message.h"
|
||||||
|
#include "libp2p/secio/secio.h"
|
||||||
|
#include "libp2p/routing/dht_protocol.h"
|
||||||
|
#include "ipfs/repo/fsrepo/fs_repo.h"
|
||||||
|
#include "ipfs/repo/init.h"
|
||||||
|
#include "ipfs/core/ipfs_node.h"
|
||||||
|
#include "ipfs/routing/routing.h"
|
||||||
|
#include "ipfs/importer/resolver.h"
|
||||||
|
#include "multiaddr/multiaddr.h"
|
||||||
|
|
||||||
|
#define BUF_SIZE 4096
|
||||||
|
|
||||||
|
int ipfs_ping (int argc, char **argv)
|
||||||
|
{
|
||||||
|
int retVal = 0;
|
||||||
|
struct IpfsNode local_node;
|
||||||
|
struct Libp2pPeer* peer_to_ping = NULL;
|
||||||
|
char* id = NULL;
|
||||||
|
struct FSRepo* fs_repo = NULL;
|
||||||
|
char* repo_path = NULL;
|
||||||
|
struct timeval time1, time2;
|
||||||
|
int time_us;
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
local_node.peerstore = NULL;
|
||||||
|
local_node.providerstore = NULL;
|
||||||
|
if (argc < 3)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
if (!ipfs_repo_get_directory(argc, argv, &repo_path)) {
|
||||||
|
fprintf(stderr, "Unable to open repo: %s\n", repo_path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// read the configuration
|
||||||
|
if (!ipfs_repo_fsrepo_new(NULL, NULL, &fs_repo))
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
// open the repository and read the file
|
||||||
|
if (!ipfs_repo_fsrepo_open(fs_repo))
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
local_node.identity = fs_repo->config->identity;
|
||||||
|
local_node.repo = fs_repo;
|
||||||
|
local_node.mode = MODE_ONLINE;
|
||||||
|
local_node.routing = ipfs_routing_new_online(&local_node, &fs_repo->config->identity->private_key);
|
||||||
|
local_node.peerstore = libp2p_peerstore_new(local_node.identity->peer);
|
||||||
|
local_node.providerstore = libp2p_providerstore_new(fs_repo->config->datastore, fs_repo->config->identity->peer);
|
||||||
|
|
||||||
|
if (local_node.routing->Bootstrap(local_node.routing) != 0)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
if (strstr(argv[2], "Qm") == &argv[2][0]) {
|
||||||
|
// resolve the peer id
|
||||||
|
fprintf (stderr, "Looking up peer %s\n", argv[2]);
|
||||||
|
peer_to_ping = ipfs_resolver_find_peer(argv[2], &local_node);
|
||||||
|
} else {
|
||||||
|
// perhaps they passed an IP and port
|
||||||
|
if (argc >= 3) {
|
||||||
|
char* str = malloc(strlen(argv[2]) + strlen(argv[3]) + 100);
|
||||||
|
if (str == NULL) {
|
||||||
|
// memory issue
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
sprintf(str, "/ip4/%s/tcp/%s", argv[2], argv[3]);
|
||||||
|
peer_to_ping = libp2p_peer_new();
|
||||||
|
if (peer_to_ping) {
|
||||||
|
peer_to_ping->addr_head = libp2p_utils_linked_list_new();
|
||||||
|
peer_to_ping->addr_head->item = multiaddress_new_from_string(str);
|
||||||
|
peer_to_ping->id = str;
|
||||||
|
peer_to_ping->id_size = strlen(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//TODO: Error checking
|
||||||
|
}
|
||||||
|
|
||||||
|
if (peer_to_ping == NULL)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
id = malloc(peer_to_ping->id_size + 1);
|
||||||
|
if (id) {
|
||||||
|
memcpy(id, peer_to_ping->id, peer_to_ping->id_size);
|
||||||
|
id[peer_to_ping->id_size] = 0;
|
||||||
|
fprintf (stderr, "PING %s.\n", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
gettimeofday(&time1, NULL);
|
||||||
|
if (!local_node.routing->Ping(local_node.routing, peer_to_ping)) {
|
||||||
|
fprintf(stderr, "Unable to ping %s\n", id);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
gettimeofday(&time2, NULL);
|
||||||
|
|
||||||
|
// calculate microseconds
|
||||||
|
time_us = (time2.tv_sec - time1.tv_sec) * 1000000;
|
||||||
|
time_us += (time2.tv_usec - time1.tv_usec);
|
||||||
|
fprintf (stderr, "Pong received: time=%d.%03d ms\n", time_us / 1000, time_us % 1000);
|
||||||
|
if (time_us < 1000000) { // if the ping took less than a second...
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
retVal = 1;
|
||||||
|
exit:
|
||||||
|
if (id != NULL)
|
||||||
|
free(id);
|
||||||
|
if (fs_repo != NULL)
|
||||||
|
ipfs_repo_fsrepo_free(fs_repo);
|
||||||
|
if (local_node.peerstore != NULL)
|
||||||
|
libp2p_peerstore_free(local_node.peerstore);
|
||||||
|
if (local_node.providerstore != NULL)
|
||||||
|
libp2p_providerstore_free(local_node.providerstore);
|
||||||
|
return retVal;
|
||||||
|
|
||||||
|
}
|
74
core/swarm.c
Normal file
74
core/swarm.c
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "libp2p/utils/logger.h"
|
||||||
|
#include "ipfs/core/ipfs_node.h"
|
||||||
|
#include "ipfs/core/swarm.h"
|
||||||
|
#include "ipfs/core/http_request.h"
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Connect to a swarm
|
||||||
|
* @param local_node the local node
|
||||||
|
* @param address the address of the remote
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_swarm_connect(struct IpfsNode* local_node, const char* address) {
|
||||||
|
char* response = NULL;
|
||||||
|
size_t response_size;
|
||||||
|
// use the API to connect
|
||||||
|
struct HttpRequest* request = ipfs_core_http_request_new();
|
||||||
|
if (request == NULL)
|
||||||
|
return 0;
|
||||||
|
request->command = "swarm";
|
||||||
|
request->sub_command = "connect";
|
||||||
|
libp2p_utils_vector_add(request->arguments, address);
|
||||||
|
int retVal = ipfs_core_http_request_get(local_node, request, &response, &response_size);
|
||||||
|
if (response != NULL && response_size > 0) {
|
||||||
|
fwrite(response, 1, response_size, stdout);
|
||||||
|
free(response);
|
||||||
|
}
|
||||||
|
ipfs_core_http_request_free(request);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Handle command line swarm call
|
||||||
|
*/
|
||||||
|
int ipfs_swarm (struct CliArguments* args) {
|
||||||
|
int retVal = 0;
|
||||||
|
struct IpfsNode* client_node = NULL;
|
||||||
|
|
||||||
|
if (args->argc < (args->verb_index + 2)) {
|
||||||
|
libp2p_logger_error("swarm", "Not enough command line arguments. Should be \"swarm connect\" or \"swarm disconnect\" etc.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure API is running
|
||||||
|
if (!ipfs_node_offline_new(args->config_dir, &client_node)) {
|
||||||
|
libp2p_logger_error("swarm", "Unable to create offline node.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (client_node->mode != MODE_API_AVAILABLE) {
|
||||||
|
libp2p_logger_error("swarm", "API must be running.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* which = args->argv[args->verb_index + 1];
|
||||||
|
const char* path = args->argv[args->verb_index + 2];
|
||||||
|
// determine what we're doing
|
||||||
|
if (strcmp(which, "connect") == 0) {
|
||||||
|
retVal = ipfs_swarm_connect(client_node, path);
|
||||||
|
} else if (strcmp(which, "disconnect") == 0) {
|
||||||
|
libp2p_logger_error("swarm", "Swarm disconnect not implemented yet.\n");
|
||||||
|
retVal = 0;
|
||||||
|
} else {
|
||||||
|
libp2p_logger_error("swarm", "Nothing useful found on command line. Should be \"swarm connect\" or \"swarm disconnect\".\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
// shut everything down
|
||||||
|
ipfs_node_free(client_node);
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -O0 -I../include -I../../c-libp2p/include -I../../c-multihash/include -I../../c-multiaddr/include -I../../lmdb/libraries/liblmdb
|
CFLAGS = -O0 -I../include -I../c-libp2p/include -I../c-libp2p/c-multihash/include -I../c-libp2p/c-multiaddr/include -I../lmdb/libraries/liblmdb -I../c-libp2p/c-protobuf -Wall -std=gnu99
|
||||||
|
|
||||||
ifdef DEBUG
|
ifdef DEBUG
|
||||||
CFLAGS += -g3
|
CFLAGS += -g3
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
/**
|
/**
|
||||||
* Some code to help with the datastore / blockstore interface
|
* Some code to help with the datastore / blockstore interface
|
||||||
|
* NOTE: the datastore stores things under a multihash key
|
||||||
*/
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
#include "libp2p/crypto/encoding/base32.h"
|
#include "libp2p/crypto/encoding/base32.h"
|
||||||
#include "ipfs/datastore/ds_helper.h"
|
#include "ipfs/datastore/ds_helper.h"
|
||||||
/**
|
/**
|
||||||
* Generate a key based on the passed in binary_array
|
* Generate a base32 key based on the passed in binary_array (which is normally a multihash)
|
||||||
* @param binary_array what to base the key on
|
* @param binary_array what to base the key on
|
||||||
* @param array_length the size of the binary array
|
* @param array_length the size of the binary array
|
||||||
* @param results where the key will be put
|
* @param results where the key will be put
|
||||||
|
@ -12,8 +14,8 @@
|
||||||
* @param results_length the length of the generated key
|
* @param results_length the length of the generated key
|
||||||
* @returns true(1) on success
|
* @returns true(1) on success
|
||||||
*/
|
*/
|
||||||
int ipfs_datastore_helper_ds_key_from_binary(unsigned char* binary_array, size_t array_length,
|
int ipfs_datastore_helper_ds_key_from_binary(const unsigned char* binary_array, size_t array_length,
|
||||||
char* results, size_t max_results_length, size_t* results_length) {
|
unsigned char* results, size_t max_results_length, size_t* results_length) {
|
||||||
|
|
||||||
size_t encoded_length = libp2p_crypto_encoding_base32_encode_size(array_length);
|
size_t encoded_length = libp2p_crypto_encoding_base32_encode_size(array_length);
|
||||||
if (encoded_length > max_results_length)
|
if (encoded_length > max_results_length)
|
||||||
|
@ -30,7 +32,7 @@ int ipfs_datastore_helper_ds_key_from_binary(unsigned char* binary_array, size_t
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a binary array based on the passed in datastore key
|
* Generate a binary array (normally a multihash) based on the passed in datastore key
|
||||||
* @param ds_key the base32 encoded key
|
* @param ds_key the base32 encoded key
|
||||||
* @param key_length the length of the base32 "string"
|
* @param key_length the length of the base32 "string"
|
||||||
* @param binary_array where to put the decoded value
|
* @param binary_array where to put the decoded value
|
||||||
|
@ -38,7 +40,7 @@ int ipfs_datastore_helper_ds_key_from_binary(unsigned char* binary_array, size_t
|
||||||
* @param completed_binary_array_length the length of what was written to the binary_array
|
* @param completed_binary_array_length the length of what was written to the binary_array
|
||||||
* @returns true(1) on success
|
* @returns true(1) on success
|
||||||
*/
|
*/
|
||||||
int ipfs_datastore_helper_binary_from_ds_key(unsigned char* ds_key, size_t key_length, unsigned char* binary_array,
|
int ipfs_datastore_helper_binary_from_ds_key(const unsigned char* ds_key, size_t key_length, unsigned char* binary_array,
|
||||||
size_t max_binary_array_length, size_t* completed_binary_array_length) {
|
size_t max_binary_array_length, size_t* completed_binary_array_length) {
|
||||||
|
|
||||||
size_t decoded_length = libp2p_crypto_encoding_base32_decode_size(key_length);
|
size_t decoded_length = libp2p_crypto_encoding_base32_decode_size(key_length);
|
||||||
|
@ -53,3 +55,41 @@ int ipfs_datastore_helper_binary_from_ds_key(unsigned char* ds_key, size_t key_l
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Add a record in the datastore based on a block
|
||||||
|
* @param block the block
|
||||||
|
* @param datastore the Datastore
|
||||||
|
* @reutrns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_datastore_helper_add_block_to_datastore(struct Block* block, struct Datastore* datastore) {
|
||||||
|
struct DatastoreRecord* rec = libp2p_datastore_record_new();
|
||||||
|
if (rec == NULL)
|
||||||
|
return 0;
|
||||||
|
rec->key_size = block->cid->hash_length;
|
||||||
|
rec->key = (uint8_t*) malloc(rec->key_size);
|
||||||
|
if (rec->key == NULL) {
|
||||||
|
libp2p_datastore_record_free(rec);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memcpy(rec->key, block->cid->hash, rec->key_size);
|
||||||
|
rec->timestamp = 0;
|
||||||
|
// convert the key to base32, and store it in the DatabaseRecord->value section
|
||||||
|
size_t fs_key_length = 100;
|
||||||
|
uint8_t fs_key[fs_key_length];
|
||||||
|
if (!ipfs_datastore_helper_ds_key_from_binary(block->cid->hash, block->cid->hash_length, fs_key, fs_key_length, &fs_key_length)) {
|
||||||
|
libp2p_datastore_record_free(rec);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
rec->value_size = fs_key_length;
|
||||||
|
rec->value = malloc(rec->value_size);
|
||||||
|
if (rec->value == NULL) {
|
||||||
|
libp2p_datastore_record_free(rec);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memcpy(rec->value, fs_key, rec->value_size);
|
||||||
|
int retVal = datastore->datastore_put(rec, datastore);
|
||||||
|
libp2p_datastore_record_free(rec);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
18
dnslink/Makefile
Normal file
18
dnslink/Makefile
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
CC = gcc
|
||||||
|
CFLAGS = -O0 -I../include -I../c-libp2p/include -I../c-libp2p/c-multihash/include -I../c-libp2p/c-multiaddr/include -I../c-libp2p/c-protobuf -Wall -std=gnu99
|
||||||
|
|
||||||
|
ifdef DEBUG
|
||||||
|
CFLAGS += -g3
|
||||||
|
endif
|
||||||
|
|
||||||
|
LFLAGS =
|
||||||
|
DEPS =
|
||||||
|
OBJS = dnslink.o
|
||||||
|
|
||||||
|
%.o: %.c $(DEPS)
|
||||||
|
$(CC) -c -o $@ $< $(CFLAGS)
|
||||||
|
|
||||||
|
all: $(OBJS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o
|
407
dnslink/dnslink.c
Normal file
407
dnslink/dnslink.c
Normal file
|
@ -0,0 +1,407 @@
|
||||||
|
/*
|
||||||
|
Package dnslink implements a dns link resolver. dnslink is a basic
|
||||||
|
standard for placing traversable links in dns itself. See dnslink.info
|
||||||
|
|
||||||
|
A dnslink is a path link in a dns TXT record, like this:
|
||||||
|
|
||||||
|
dnslink=/ipfs/QmR7tiySn6vFHcEjBeZNtYGAFh735PJHfEMdVEycj9jAPy
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
> dig TXT ipfs.io
|
||||||
|
ipfs.io. 120 IN TXT dnslink=/ipfs/QmR7tiySn6vFHcEjBeZNtYGAFh735PJHfEMdVEycj9jAPy
|
||||||
|
|
||||||
|
This package eases resolving and working with thse dns links. For example:
|
||||||
|
|
||||||
|
import (
|
||||||
|
dnslink "github.com/jbenet/go-dnslink"
|
||||||
|
)
|
||||||
|
|
||||||
|
link, err := dnslink.Resolve("ipfs.io")
|
||||||
|
// link = "/ipfs/QmR7tiySn6vFHcEjBeZNtYGAFh735PJHfEMdVEycj9jAPy"
|
||||||
|
|
||||||
|
It even supports recursive resolution. Suppose you have three domains with
|
||||||
|
dnslink records like these:
|
||||||
|
|
||||||
|
> dig TXT foo.com
|
||||||
|
foo.com. 120 IN TXT dnslink=/dns/bar.com/f/o/o
|
||||||
|
> dig TXT bar.com
|
||||||
|
bar.com. 120 IN TXT dnslink=/dns/long.test.baz.it/b/a/r
|
||||||
|
> dig TXT long.test.baz.it
|
||||||
|
long.test.baz.it. 120 IN TXT dnslink=/b/a/z
|
||||||
|
|
||||||
|
Expect these resolutions:
|
||||||
|
|
||||||
|
dnslink.ResolveN("long.test.baz.it", 0) // "/dns/long.test.baz.it"
|
||||||
|
dnslink.Resolve("long.test.baz.it") // "/b/a/z"
|
||||||
|
|
||||||
|
dnslink.ResolveN("bar.com", 1) // "/dns/long.test.baz.it/b/a/r"
|
||||||
|
dnslink.Resolve("bar.com") // "/b/a/z/b/a/r"
|
||||||
|
|
||||||
|
dnslink.ResolveN("foo.com", 1) // "/dns/bar.com/f/o/o/"
|
||||||
|
dnslink.ResolveN("foo.com", 2) // "/dns/long.test.baz.it/b/a/r/f/o/o/"
|
||||||
|
dnslink.Resolve("foo.com") // "/b/a/z/b/a/r/f/o/o"
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define u_char unsigned char
|
||||||
|
#define u_int16_t uint16_t
|
||||||
|
#define u_int32_t uint32_t
|
||||||
|
#define NS_MAXDNAME 1025 /*%< maximum domain name */
|
||||||
|
#define ns_c_in 1
|
||||||
|
#define ns_t_txt 16
|
||||||
|
|
||||||
|
typedef enum __ns_sect {
|
||||||
|
ns_s_qd = 0, /*%< Query: Question. */
|
||||||
|
ns_s_zn = 0, /*%< Update: Zone. */
|
||||||
|
ns_s_an = 1, /*%< Query: Answer. */
|
||||||
|
ns_s_pr = 1, /*%< Update: Prerequisites. */
|
||||||
|
ns_s_ns = 2, /*%< Query: Name servers. */
|
||||||
|
ns_s_ud = 2, /*%< Update: Update. */
|
||||||
|
ns_s_ar = 3, /*%< Query|Update: Additional records. */
|
||||||
|
ns_s_max = 4
|
||||||
|
} ns_sect;
|
||||||
|
|
||||||
|
typedef struct __ns_msg {
|
||||||
|
const unsigned char *_msg, *_eom;
|
||||||
|
u_int16_t _id, _flags, _counts[ns_s_max];
|
||||||
|
const unsigned char *_sections[ns_s_max];
|
||||||
|
ns_sect _sect;
|
||||||
|
int _rrnum;
|
||||||
|
const unsigned char *_msg_ptr;
|
||||||
|
} ns_msg;
|
||||||
|
|
||||||
|
typedef struct __ns_rr {
|
||||||
|
char name[NS_MAXDNAME];
|
||||||
|
u_int16_t type;
|
||||||
|
u_int16_t rr_class;
|
||||||
|
u_int32_t ttl;
|
||||||
|
u_int16_t rdlength;
|
||||||
|
const u_char * rdata;
|
||||||
|
} ns_rr;
|
||||||
|
#else
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/nameser.h>
|
||||||
|
#include <resolv.h>
|
||||||
|
#endif
|
||||||
|
#include "ipfs/namesys/namesys.h"
|
||||||
|
#define IPFS_DNSLINK_C
|
||||||
|
#include "ipfs/dnslink/dnslink.h"
|
||||||
|
#include "ipfs/cid/cid.h"
|
||||||
|
#include "ipfs/path/path.h"
|
||||||
|
|
||||||
|
int ipfs_dns (int argc, char **argv)
|
||||||
|
{
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
fprintf (stderr, "Sorry, dns has not yet been implemented for the Windows version.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
int err, r=0, i;
|
||||||
|
char **txt, *path, *param;
|
||||||
|
|
||||||
|
if (argc == 4 && strcmp ("-r", argv[2])==0) {
|
||||||
|
r = 1;
|
||||||
|
argc--; argv++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc != 3) {
|
||||||
|
fprintf (stderr, "usage: ipfs dns [-r] dns.name.com\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
param = malloc (strlen (argv[2]) + 1);
|
||||||
|
if (!param) {
|
||||||
|
fprintf (stderr, "memory allocation failed.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
strcpy (param, argv[2]);
|
||||||
|
|
||||||
|
for (i = 0 ; i < DefaultDepthLimit ; i++) {
|
||||||
|
if (memcmp(param, "/ipns/", 6) == 0) {
|
||||||
|
err = ipfs_dnslink_resolv_lookupTXT (&txt, param+6);
|
||||||
|
} else {
|
||||||
|
err = ipfs_dnslink_resolv_lookupTXT (&txt, param);
|
||||||
|
}
|
||||||
|
free (param);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
fprintf (stderr, "dns lookupTXT: %s\n", Err[err]);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ipfs_dnslink_parse_txt(&path, *txt);
|
||||||
|
if (err) {
|
||||||
|
free (*txt);
|
||||||
|
free (txt);
|
||||||
|
fprintf (stderr, "dns parse_txt: %s\n", Err[err]);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
free (*txt);
|
||||||
|
free (txt);
|
||||||
|
param = path;
|
||||||
|
|
||||||
|
if (! r) {
|
||||||
|
// not recursive.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memcmp(path, "/ipfs/", 6) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (--r);
|
||||||
|
fprintf (stdout, "%s\n", param);
|
||||||
|
free (param);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif // MINGW
|
||||||
|
|
||||||
|
// ipfs_dnslink_resolve resolves the dnslink at a particular domain. It will
|
||||||
|
// recursively keep resolving until reaching the defaultDepth of Resolver. If
|
||||||
|
// the depth is reached, ipfs_dnslink_resolve will return the last value
|
||||||
|
// retrieved, and ErrResolveLimit. If TXT records are found but are not valid
|
||||||
|
// dnslink records, ipfs_dnslink_resolve will return ErrInvalidDNSLink.
|
||||||
|
// ipfs_dnslink_resolve will check every TXT record returned. If resolution
|
||||||
|
// fails otherwise, ipfs_dnslink_resolve will return ErrResolveFailed
|
||||||
|
int ipfs_dnslink_resolve (char **p, char *domain)
|
||||||
|
{
|
||||||
|
return ipfs_dnslink_resolve_n (p, domain, DefaultDepthLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ipfs_dnslink_lookup_txt is a function that looks up a TXT record in some dns resolver.
|
||||||
|
// This is useful for testing or passing your own dns resolution process, which
|
||||||
|
// could take into account non-standard TLDs like .bit, .onion, .ipfs, etc.
|
||||||
|
int (*ipfs_dnslink_lookup_txt)(char ***txt, char *name) = NULL;
|
||||||
|
|
||||||
|
// ipfs_dnslink_resolve_n is just like Resolve, with the option to specify a
|
||||||
|
// maximum resolution depth.
|
||||||
|
int ipfs_dnslink_resolve_n (char **p, char *d, int depth)
|
||||||
|
{
|
||||||
|
int err, i, l;
|
||||||
|
char *rest, **link, tail[500], buf[500], domain[500];
|
||||||
|
char dns_prefix[] = "/dns/";
|
||||||
|
|
||||||
|
domain[sizeof(domain)-1] = '\0';
|
||||||
|
strncpy (domain, d, sizeof(domain) - 1);
|
||||||
|
for (i=0 ; i < depth ; i++) {
|
||||||
|
err = ipfs_dnslink_resolve_once (&link, domain);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if does not have /dns/ as a prefix, done.
|
||||||
|
if (memcmp (*link, dns_prefix, sizeof(dns_prefix) - 1)!=0) {
|
||||||
|
l = strlen(*link) + strlen(tail);
|
||||||
|
*p = malloc(l + 1);
|
||||||
|
if (!*p) {
|
||||||
|
free(*link);
|
||||||
|
free(link);
|
||||||
|
return ErrAllocFailed;
|
||||||
|
}
|
||||||
|
*p[l] = '\0';
|
||||||
|
strncpy(*p, *link, l);
|
||||||
|
free(*link);
|
||||||
|
free(link);
|
||||||
|
strncat(*p, tail, l - strlen(*p));
|
||||||
|
return 0; // done
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep resolving
|
||||||
|
err = ipfs_dnslink_parse_link_domain (&d, &rest, *link);
|
||||||
|
free (*link);
|
||||||
|
free (link);
|
||||||
|
if (err) {
|
||||||
|
*p = NULL;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy (domain, d, sizeof(domain) - 1);
|
||||||
|
free (d);
|
||||||
|
strncpy (buf, tail, sizeof(buf) - 1);
|
||||||
|
strncpy (tail, rest, sizeof(tail) - 1);
|
||||||
|
strncat (tail, buf, sizeof(tail) - 1 - strlen(tail));
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy (buf, tail, sizeof(buf) - 1);
|
||||||
|
strncpy (tail, dns_prefix, sizeof(tail) - 1);
|
||||||
|
strncat (tail, domain, sizeof(tail) - 1 - strlen(tail));
|
||||||
|
strncat (tail, buf, sizeof(tail) - 1 - strlen(tail));
|
||||||
|
return ErrResolveLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef __MINGW32__
|
||||||
|
// lookup using libresolv -lresolv
|
||||||
|
int ipfs_dnslink_resolv_lookupTXT(char ***txt, char *domain)
|
||||||
|
{
|
||||||
|
char buf[4096], *p;
|
||||||
|
int responseLength;
|
||||||
|
int i, l, n = 0;
|
||||||
|
ns_msg query_parse_msg;
|
||||||
|
ns_rr query_parse_rr;
|
||||||
|
u_char responseByte[4096];
|
||||||
|
|
||||||
|
// Use res_query from libresolv to retrieve TXT record from DNS server.
|
||||||
|
if ((responseLength = res_query(domain,ns_c_in,ns_t_txt,responseByte,sizeof(responseByte))) < 0 ||
|
||||||
|
ns_initparse(responseByte,responseLength,&query_parse_msg) < 0) {
|
||||||
|
return ErrResolveFailed;
|
||||||
|
} else {
|
||||||
|
l = sizeof (buf);
|
||||||
|
buf[--l] = '\0';
|
||||||
|
p = buf;
|
||||||
|
// save every TXT record to buffer separating with a \0
|
||||||
|
for (i=0 ; i < ns_msg_count(query_parse_msg,ns_s_an) ; i++) {
|
||||||
|
if (ns_parserr(&query_parse_msg,ns_s_an,i,&query_parse_rr)) {
|
||||||
|
return ErrResolveFailed;
|
||||||
|
} else {
|
||||||
|
const unsigned char *rdata = ns_rr_rdata(query_parse_rr);
|
||||||
|
memcpy(p, rdata+1, *rdata); // first byte is record length
|
||||||
|
p += *rdata; // update pointer
|
||||||
|
*p++ = '\0'; // mark end-of-record and update pointer to next record.
|
||||||
|
n++; // update record count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// allocate array for all records + NULL pointer terminator.
|
||||||
|
*txt = calloc(n+1, sizeof(void*));
|
||||||
|
if (!*txt) {
|
||||||
|
return ErrAllocFailed;
|
||||||
|
}
|
||||||
|
l = p - buf; // length of all records in buffer.
|
||||||
|
p = malloc(l); // allocate memory that will be used as string data at *txt array.
|
||||||
|
if (!p) {
|
||||||
|
free(*txt);
|
||||||
|
*txt = NULL;
|
||||||
|
return ErrAllocFailed;
|
||||||
|
}
|
||||||
|
memcpy(p, buf, l); // transfer from buffer to allocated memory.
|
||||||
|
for (i = 0 ; i < n ; i++) {
|
||||||
|
*txt[i] = p; // save position of current record at *txt array.
|
||||||
|
p = memchr(p, '\0', l - (p - *txt[0])) + 1; // find next record position after next \0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// ipfs_dnslink_resolve_once implements resolver.
|
||||||
|
int ipfs_dnslink_resolve_once (char ***p, char *domain)
|
||||||
|
{
|
||||||
|
int err, i;
|
||||||
|
char **txt;
|
||||||
|
|
||||||
|
if (!p || !domain) {
|
||||||
|
return ErrInvalidParam;
|
||||||
|
}
|
||||||
|
|
||||||
|
*p = NULL;
|
||||||
|
|
||||||
|
if (!ipfs_isdomain_is_domain (domain)) {
|
||||||
|
return ErrInvalidDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef __MINGW32__
|
||||||
|
if (!ipfs_dnslink_lookup_txt) { // if not set
|
||||||
|
ipfs_dnslink_lookup_txt = ipfs_dnslink_resolv_lookupTXT; // use default libresolv
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ipfs_dnslink_lookup_txt (&txt, domain);
|
||||||
|
#endif
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ErrResolveFailed;
|
||||||
|
for (i=0 ; txt[i] ; i++) {
|
||||||
|
err = ipfs_dnslink_parse_txt(*p, txt[i]);
|
||||||
|
if (!err) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(*txt);
|
||||||
|
free(txt);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ipfs_dnslink_parse_txt parses a TXT record value for a dnslink value.
|
||||||
|
// The TXT record must follow the dnslink format:
|
||||||
|
// TXT dnslink=<path>
|
||||||
|
// TXT dnslink=/foo/bar/baz
|
||||||
|
// ipfs_dnslink_parse_txt will return ErrInvalidDNSLink if parsing fails.
|
||||||
|
int ipfs_dnslink_parse_txt (char **path, char *txt)
|
||||||
|
{
|
||||||
|
char **parts;
|
||||||
|
|
||||||
|
if (!path || !txt) {
|
||||||
|
return ErrInvalidParam;
|
||||||
|
}
|
||||||
|
parts = ipfs_path_split_n (txt, "=", 2);
|
||||||
|
if (!parts) {
|
||||||
|
return ErrAllocFailed;
|
||||||
|
}
|
||||||
|
if (ipfs_path_segments_length (parts) == 2 && strcmp(parts[0], "dnslink")==0 && memcmp(parts[1], "/", 1)==0) {
|
||||||
|
*path = ipfs_path_clean_path(parts[1]);
|
||||||
|
if (path) {
|
||||||
|
ipfs_path_free_segments (&parts);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ipfs_path_free_segments (&parts);
|
||||||
|
*path = NULL;
|
||||||
|
return ErrInvalidDNSLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ipfs_dnslink_parse_link_domain parses a domain from a dnslink path.
|
||||||
|
// The link path must follow the dnslink format:
|
||||||
|
// /dns/<domain>/<path>
|
||||||
|
// /dns/ipfs.io
|
||||||
|
// /dns/ipfs.io/blog/0-hello-worlds
|
||||||
|
// ipfs_dnslink_parse_link_domain will return ErrInvalidDNSLink if parsing
|
||||||
|
// fails, and ErrInvalidDomain if the domain is not valid.
|
||||||
|
int ipfs_dnslink_parse_link_domain (char **domain, char**rest, char *txt)
|
||||||
|
{
|
||||||
|
char **parts;
|
||||||
|
int parts_len;
|
||||||
|
|
||||||
|
if (!domain || !rest || !txt) {
|
||||||
|
return ErrInvalidParam;
|
||||||
|
}
|
||||||
|
|
||||||
|
*domain = *rest = NULL;
|
||||||
|
|
||||||
|
parts = ipfs_path_split_n (txt, "/", 4);
|
||||||
|
parts_len = ipfs_path_segments_length(parts);
|
||||||
|
if (!parts || parts_len < 3 || parts[0][0]!='\0' || strcmp(parts[1], "dns") != 0) {
|
||||||
|
return ErrInvalidDNSLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! ipfs_isdomain_is_domain (parts[2])) {
|
||||||
|
ipfs_path_free_segments (&parts);
|
||||||
|
return ErrInvalidDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
*domain = malloc(strlen (parts[2]) + 1);
|
||||||
|
if (!*domain) {
|
||||||
|
ipfs_path_free_segments (&parts);
|
||||||
|
return ErrAllocFailed;
|
||||||
|
}
|
||||||
|
strcpy(*domain, parts[2]);
|
||||||
|
|
||||||
|
if (parts_len > 3) {
|
||||||
|
*rest = malloc(strlen (parts[3]) + 1);
|
||||||
|
if (!*rest) {
|
||||||
|
ipfs_path_free_segments (&parts);
|
||||||
|
free (*domain);
|
||||||
|
*domain = NULL;
|
||||||
|
return ErrAllocFailed;
|
||||||
|
}
|
||||||
|
strcpy(*rest, parts[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
18
exchange/Makefile
Normal file
18
exchange/Makefile
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
CC = gcc
|
||||||
|
CFLAGS = -O0 -I../include -I../c-libp2p/include -std=gnu99
|
||||||
|
|
||||||
|
ifdef DEBUG
|
||||||
|
CFLAGS += -g3
|
||||||
|
endif
|
||||||
|
|
||||||
|
DEPS =
|
||||||
|
OBJS =
|
||||||
|
|
||||||
|
%.o: %.c $(DEPS)
|
||||||
|
$(CC) -c -o $@ $< $(CFLAGS)
|
||||||
|
|
||||||
|
all: $(OBJS)
|
||||||
|
cd bitswap; make all;
|
||||||
|
clean:
|
||||||
|
rm -f *.o
|
||||||
|
cd bitswap; make clean;
|
18
exchange/bitswap/Makefile
Normal file
18
exchange/bitswap/Makefile
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
CC = gcc
|
||||||
|
CFLAGS = -O0 -I../../include -I../../c-libp2p/include -I../../c-libp2p/c-multiaddr/include -I../../c-libp2p/c-multihash/include -I../../c-libp2p/c-protobuf -Wall -std=c99
|
||||||
|
|
||||||
|
ifdef DEBUG
|
||||||
|
CFLAGS += -g3
|
||||||
|
endif
|
||||||
|
|
||||||
|
LFLAGS =
|
||||||
|
DEPS =
|
||||||
|
OBJS = bitswap.o message.o network.o peer_request_queue.o want_manager.o wantlist_queue.o engine.o
|
||||||
|
|
||||||
|
%.o: %.c $(DEPS)
|
||||||
|
$(CC) -c -o $@ $< $(CFLAGS)
|
||||||
|
|
||||||
|
all: $(OBJS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o
|
249
exchange/bitswap/bitswap.c
Normal file
249
exchange/bitswap/bitswap.c
Normal file
|
@ -0,0 +1,249 @@
|
||||||
|
/**
|
||||||
|
* Methods for the Bitswap exchange
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h> // for sleep()
|
||||||
|
#include <pthread.h>
|
||||||
|
#include "libp2p/os/utils.h"
|
||||||
|
#include "libp2p/utils/logger.h"
|
||||||
|
#include "libp2p/net/stream.h"
|
||||||
|
#include "libp2p/net/connectionstream.h"
|
||||||
|
#include "ipfs/core/ipfs_node.h"
|
||||||
|
#include "ipfs/datastore/ds_helper.h"
|
||||||
|
#include "ipfs/exchange/exchange.h"
|
||||||
|
#include "ipfs/exchange/bitswap/bitswap.h"
|
||||||
|
#include "ipfs/exchange/bitswap/message.h"
|
||||||
|
#include "ipfs/exchange/bitswap/network.h"
|
||||||
|
#include "ipfs/exchange/bitswap/peer_request_queue.h"
|
||||||
|
#include "ipfs/exchange/bitswap/want_manager.h"
|
||||||
|
|
||||||
|
int ipfs_bitswap_can_handle(const struct StreamMessage* msg) {
|
||||||
|
if (msg == NULL || msg->data == NULL || msg->data_size == 0)
|
||||||
|
return 0;
|
||||||
|
char* result = strnstr((char*)msg->data, "/ipfs/bitswap", msg->data_size);
|
||||||
|
if(result == NULL || result != (char*)msg->data)
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ipfs_bitswap_shutdown_handler(void* context) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Handles the message
|
||||||
|
* @param incoming the incoming data buffer
|
||||||
|
* @param incoming_size the size of the incoming data buffer
|
||||||
|
* @param session_context the information about the incoming connection
|
||||||
|
* @param protocol_context the protocol-dependent context
|
||||||
|
* @returns 0 if the caller should not continue looping, <0 on error, >0 on success
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_handle_message(const struct StreamMessage* msg, struct Stream* stream, void* protocol_context) {
|
||||||
|
struct IpfsNode* local_node = (struct IpfsNode*)protocol_context;
|
||||||
|
struct SessionContext* session_context = libp2p_net_connection_get_session_context(stream);
|
||||||
|
if (session_context == NULL)
|
||||||
|
return -1;
|
||||||
|
int retVal = ipfs_bitswap_network_handle_message(local_node, session_context, msg->data, msg->data_size);
|
||||||
|
if (retVal == 0)
|
||||||
|
return -1;
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Libp2pProtocolHandler* ipfs_bitswap_build_protocol_handler(const struct IpfsNode* local_node) {
|
||||||
|
struct Libp2pProtocolHandler* handler = (struct Libp2pProtocolHandler*) malloc(sizeof(struct Libp2pProtocolHandler));
|
||||||
|
if (handler != NULL) {
|
||||||
|
handler->context = (void*)local_node;
|
||||||
|
handler->CanHandle = ipfs_bitswap_can_handle;
|
||||||
|
handler->HandleMessage = ipfs_bitswap_handle_message;
|
||||||
|
handler->Shutdown = ipfs_bitswap_shutdown_handler;
|
||||||
|
}
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new bitswap exchange
|
||||||
|
* @param sessionContext the context
|
||||||
|
* @returns an allocated Exchange structure
|
||||||
|
*/
|
||||||
|
struct Exchange* ipfs_bitswap_new(struct IpfsNode* ipfs_node) {
|
||||||
|
struct Exchange* exchange = (struct Exchange*) malloc(sizeof(struct Exchange));
|
||||||
|
if (exchange != NULL) {
|
||||||
|
struct BitswapContext* bitswapContext = (struct BitswapContext*) malloc(sizeof(struct BitswapContext));
|
||||||
|
if (bitswapContext == NULL) {
|
||||||
|
free(exchange);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
bitswapContext->bitswap_engine = ipfs_bitswap_engine_new();
|
||||||
|
if (bitswapContext->bitswap_engine == NULL) {
|
||||||
|
free(bitswapContext);
|
||||||
|
free(exchange);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
bitswapContext->localWantlist = ipfs_bitswap_wantlist_queue_new();
|
||||||
|
bitswapContext->peerRequestQueue = ipfs_bitswap_peer_request_queue_new();
|
||||||
|
bitswapContext->ipfsNode = ipfs_node;
|
||||||
|
|
||||||
|
exchange->exchangeContext = (void*) bitswapContext;
|
||||||
|
exchange->IsOnline = ipfs_bitswap_is_online;
|
||||||
|
exchange->Close = ipfs_bitswap_close;
|
||||||
|
exchange->HasBlock = ipfs_bitswap_has_block;
|
||||||
|
exchange->GetBlock = ipfs_bitswap_get_block;
|
||||||
|
exchange->GetBlockAsync = ipfs_bitswap_get_block_async;
|
||||||
|
exchange->GetBlocks = ipfs_bitswap_get_blocks;
|
||||||
|
|
||||||
|
// Start the threads for the network
|
||||||
|
ipfs_bitswap_engine_start(bitswapContext);
|
||||||
|
libp2p_logger_debug("bitswap", "Bitswap engine started\n");
|
||||||
|
}
|
||||||
|
return exchange;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean up resources within an Exchange struct
|
||||||
|
* @param exchange the exchange to free
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_free(struct Exchange* exchange) {
|
||||||
|
if (exchange != NULL) {
|
||||||
|
if (exchange->exchangeContext != NULL) {
|
||||||
|
struct BitswapContext* bitswapContext = (struct BitswapContext*) exchange->exchangeContext;
|
||||||
|
if (bitswapContext != NULL)
|
||||||
|
ipfs_bitswap_engine_stop(bitswapContext);
|
||||||
|
if (bitswapContext->localWantlist != NULL) {
|
||||||
|
ipfs_bitswap_wantlist_queue_free(bitswapContext->localWantlist);
|
||||||
|
bitswapContext->localWantlist = NULL;
|
||||||
|
}
|
||||||
|
if (bitswapContext->peerRequestQueue != NULL) {
|
||||||
|
ipfs_bitswap_peer_request_queue_free(bitswapContext->peerRequestQueue);
|
||||||
|
bitswapContext->peerRequestQueue = NULL;
|
||||||
|
}
|
||||||
|
free(exchange->exchangeContext);
|
||||||
|
}
|
||||||
|
free(exchange);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the Exchange->IsOnline method
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_is_online(struct Exchange* exchange) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Implements the Exchange->Close method
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_close(struct Exchange* exchange) {
|
||||||
|
ipfs_bitswap_free(exchange);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the Exchange->HasBlock method
|
||||||
|
* Some notes from the GO version say that this is normally called by user
|
||||||
|
* interaction (i.e. user added a file).
|
||||||
|
* But this does not make sense right now, as the GO code looks like it
|
||||||
|
* adds the block to the blockstore. This still has to be sorted.
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_has_block(struct Exchange* exchange, struct Block* block) {
|
||||||
|
// add the block to the blockstore
|
||||||
|
struct BitswapContext* context = exchange->exchangeContext;
|
||||||
|
size_t bytes_written;
|
||||||
|
context->ipfsNode->blockstore->Put(context->ipfsNode->blockstore->blockstoreContext, block, &bytes_written);
|
||||||
|
// add it to the datastore
|
||||||
|
ipfs_datastore_helper_add_block_to_datastore(block, context->ipfsNode->repo->config->datastore);
|
||||||
|
// update requests
|
||||||
|
struct WantListQueueEntry* queueEntry = ipfs_bitswap_wantlist_queue_find(context->localWantlist, block->cid);
|
||||||
|
if (queueEntry != NULL) {
|
||||||
|
queueEntry->block = block;
|
||||||
|
}
|
||||||
|
// TODO: Announce to world that we now have the block
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the Exchange->GetBlock method
|
||||||
|
* We're asking for this method to get the block from peers. Perhaps this should be
|
||||||
|
* taking in a pointer to a callback, as this could take a while (or fail).
|
||||||
|
* @param exchangeContext a BitswapContext
|
||||||
|
* @param cid the Cid to look for
|
||||||
|
* @param block a pointer to where to put the result
|
||||||
|
* @returns true(1) if found, false(0) if not
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_get_block(struct Exchange* exchange, struct Cid* cid, struct Block** block) {
|
||||||
|
struct BitswapContext* bitswapContext = (struct BitswapContext*)exchange->exchangeContext;
|
||||||
|
if (bitswapContext != NULL) {
|
||||||
|
// check locally first
|
||||||
|
if (bitswapContext->ipfsNode->blockstore->Get(bitswapContext->ipfsNode->blockstore->blockstoreContext, cid, block))
|
||||||
|
return 1;
|
||||||
|
// now ask the network
|
||||||
|
//NOTE: this timeout should be configurable
|
||||||
|
int timeout = 60;
|
||||||
|
int waitSecs = 1;
|
||||||
|
int timeTaken = 0;
|
||||||
|
struct WantListSession *wantlist_session = ipfs_bitswap_wantlist_session_new();
|
||||||
|
wantlist_session->type = WANTLIST_SESSION_TYPE_LOCAL;
|
||||||
|
wantlist_session->context = (void*)bitswapContext->ipfsNode;
|
||||||
|
struct WantListQueueEntry* want_entry = ipfs_bitswap_want_manager_add(bitswapContext, cid, wantlist_session);
|
||||||
|
if (want_entry != NULL) {
|
||||||
|
// loop waiting for it to fill
|
||||||
|
while(1) {
|
||||||
|
if (want_entry->block != NULL) {
|
||||||
|
*block = ipfs_block_copy(want_entry->block);
|
||||||
|
// error or not, we no longer need the block (decrement reference count)
|
||||||
|
ipfs_bitswap_want_manager_remove(bitswapContext, cid);
|
||||||
|
if (*block == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
//TODO: This is a busy-loop. Find another way.
|
||||||
|
timeTaken += waitSecs;
|
||||||
|
if (timeTaken >= timeout) {
|
||||||
|
// It took too long. Stop looking.
|
||||||
|
ipfs_bitswap_want_manager_remove(bitswapContext, cid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sleep(waitSecs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the Exchange->GetBlock method
|
||||||
|
* We're asking for this method to get the block from peers. Perhaps this should be
|
||||||
|
* taking in a pointer to a callback, as this could take a while (or fail).
|
||||||
|
* @param exchangeContext a BitswapContext
|
||||||
|
* @param cid the Cid to look for
|
||||||
|
* @param block a pointer to where to put the result
|
||||||
|
* @returns true(1) if found, false(0) if not
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_get_block_async(struct Exchange* exchange, struct Cid* cid, struct Block** block) {
|
||||||
|
struct BitswapContext* bitswapContext = (struct BitswapContext*)exchange->exchangeContext;
|
||||||
|
if (bitswapContext != NULL) {
|
||||||
|
// check locally first
|
||||||
|
struct Block* block;
|
||||||
|
if (bitswapContext->ipfsNode->blockstore->Get(bitswapContext->ipfsNode->blockstore->blockstoreContext, cid, &block)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
// now ask the network
|
||||||
|
struct WantListSession* wantlist_session = ipfs_bitswap_wantlist_session_new();
|
||||||
|
wantlist_session->type = WANTLIST_SESSION_TYPE_LOCAL;
|
||||||
|
wantlist_session->context = (void*)bitswapContext->ipfsNode;
|
||||||
|
ipfs_bitswap_want_manager_add(bitswapContext, cid, wantlist_session);
|
||||||
|
// TODO: return something that they can watch
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the Exchange->GetBlocks method
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_get_blocks(struct Exchange* exchange, struct Libp2pVector* Cids, struct Libp2pVector** blocks) {
|
||||||
|
// TODO: Implement this method
|
||||||
|
return 0;
|
||||||
|
}
|
198
exchange/bitswap/engine.c
Normal file
198
exchange/bitswap/engine.c
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include "libp2p/utils/logger.h"
|
||||||
|
#include "ipfs/core/null.h"
|
||||||
|
#include "ipfs/exchange/bitswap/engine.h"
|
||||||
|
#include "ipfs/exchange/bitswap/wantlist_queue.h"
|
||||||
|
#include "ipfs/exchange/bitswap/peer_request_queue.h"
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Implementation of the bitswap engine
|
||||||
|
*/
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Allocate resources for a BitswapEngine
|
||||||
|
* @returns a new struct BitswapEngine
|
||||||
|
*/
|
||||||
|
struct BitswapEngine* ipfs_bitswap_engine_new() {
|
||||||
|
struct BitswapEngine* engine = (struct BitswapEngine*) malloc(sizeof(struct BitswapEngine));
|
||||||
|
if (engine != NULL) {
|
||||||
|
engine->shutting_down = 0;
|
||||||
|
}
|
||||||
|
return engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Deallocate resources from struct BitswapEngine
|
||||||
|
* @param engine the engine to free
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_engine_free(struct BitswapEngine* engine) {
|
||||||
|
free(engine);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* A separate thread that processes the queue of local requests
|
||||||
|
* @param context the context
|
||||||
|
*/
|
||||||
|
void* ipfs_bitswap_engine_wantlist_processor_start(void* ctx) {
|
||||||
|
struct BitswapContext* context = (struct BitswapContext*)ctx;
|
||||||
|
// the loop
|
||||||
|
while (!context->bitswap_engine->shutting_down) {
|
||||||
|
struct WantListQueueEntry* item = ipfs_bitswap_wantlist_queue_pop(context->localWantlist);
|
||||||
|
if (item != NULL) {
|
||||||
|
if (item->attempts > 10) {
|
||||||
|
// we have tried too many times
|
||||||
|
for (int i = 0; i < item->sessionsRequesting->total; i++) {
|
||||||
|
struct WantListSession* curr_session = (struct WantListSession*) libp2p_utils_vector_get(item->sessionsRequesting, i);
|
||||||
|
ipfs_bitswap_wantlist_queue_remove(context->localWantlist, item->cid, curr_session);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// if there is something on the queue process it.
|
||||||
|
ipfs_bitswap_wantlist_process_entry(context, item);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if there is nothing on the queue, wait...
|
||||||
|
sleep(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* A separate thread that processes the queue of remote requests
|
||||||
|
* @param context the context
|
||||||
|
*/
|
||||||
|
void* ipfs_bitswap_engine_peer_request_processor_start(void* ctx) {
|
||||||
|
struct BitswapContext* context = (struct BitswapContext*)ctx;
|
||||||
|
// the loop
|
||||||
|
struct Libp2pLinkedList* current = context->ipfsNode->peerstore->head_entry;
|
||||||
|
int did_some_processing = 0;
|
||||||
|
while (1) {
|
||||||
|
if (context->bitswap_engine->shutting_down) // system shutting down
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (current == NULL) { // the PeerStore is empty
|
||||||
|
libp2p_logger_debug("bitswap_engine", "Peerstore is empty. Pausing.\n");
|
||||||
|
sleep(1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (current->item == NULL) {
|
||||||
|
// error
|
||||||
|
libp2p_logger_error("bitswap_engine", "Peerstore has a null entry.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// see if they want something
|
||||||
|
struct Libp2pPeer* current_peer_entry = ((struct PeerEntry*)current->item)->peer;
|
||||||
|
if (current_peer_entry == NULL) {
|
||||||
|
// error
|
||||||
|
libp2p_logger_error("bitswap_engine", "Peerstore has an item that is a null peer.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (current_peer_entry->connection_type == CONNECTION_TYPE_CONNECTED) {
|
||||||
|
if (current_peer_entry->sessionContext == NULL || current_peer_entry->sessionContext->default_stream == NULL) {
|
||||||
|
current_peer_entry->connection_type = CONNECTION_TYPE_NOT_CONNECTED;
|
||||||
|
} else {
|
||||||
|
// check the network to see if there is anything waiting for us (if the stream is idle)
|
||||||
|
if (libp2p_stream_try_lock(current_peer_entry->sessionContext->default_stream)) {
|
||||||
|
int retVal = current_peer_entry->sessionContext->default_stream->peek(current_peer_entry->sessionContext);
|
||||||
|
if (retVal < 0) {
|
||||||
|
libp2p_logger_debug("bitswap_engine", "We thought we were connected, but Peek reported an error.\n");
|
||||||
|
libp2p_peer_handle_connection_error(current_peer_entry);
|
||||||
|
} else if (retVal > 0) {
|
||||||
|
libp2p_logger_debug("bitswap_engine", "%d bytes waiting on network for peer %s.\n", retVal, libp2p_peer_id_to_string(current_peer_entry));
|
||||||
|
struct StreamMessage* buffer = NULL;
|
||||||
|
if (current_peer_entry->sessionContext->default_stream->read(current_peer_entry->sessionContext, &buffer, 1)) {
|
||||||
|
// handle it
|
||||||
|
libp2p_logger_debug("bitswap_engine", "%lu bytes read, result: [%s].\n", buffer->data_size, buffer->data);
|
||||||
|
int retVal = libp2p_protocol_marshal(buffer, current_peer_entry->sessionContext->default_stream, context->ipfsNode->protocol_handlers);
|
||||||
|
libp2p_stream_message_free(buffer);
|
||||||
|
did_some_processing = 1;
|
||||||
|
if (retVal == -1) {
|
||||||
|
libp2p_logger_error("bitswap_engine", "protocol_marshal tried to handle the network traffic, but failed.\n");
|
||||||
|
// there was a problem. Clean up
|
||||||
|
libp2p_peer_handle_connection_error(current_peer_entry);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
libp2p_logger_error("bitswap_engine", "It was said that there was %d bytes to read, but there wasn't. Cleaning up connection.\n");
|
||||||
|
libp2p_peer_handle_connection_error(current_peer_entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
libp2p_stream_unlock(current_peer_entry->sessionContext->default_stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (current_peer_entry->is_local) {
|
||||||
|
//libp2p_logger_debug("bitswap_engine", "Local peer %s. Skipping.\n", current_peer_entry->id);
|
||||||
|
} else {
|
||||||
|
//libp2p_logger_debug("bitswap_engine", "We are not connected to this peer %s.\n", current_peer_entry->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// attempt to get queue and process
|
||||||
|
struct PeerRequestEntry* entry = ipfs_bitswap_peer_request_queue_find_entry(context->peerRequestQueue, current_peer_entry);
|
||||||
|
if (entry != NULL) {
|
||||||
|
//libp2p_logger_debug("bitswap_engine", "Processing queue for peer %s.\n", current_peer_entry->id);
|
||||||
|
// we have a queue. Do some queue processing
|
||||||
|
struct PeerRequest* item = entry->current;
|
||||||
|
if (item != NULL) {
|
||||||
|
// if there is something on the queue process it.
|
||||||
|
if (ipfs_bitswap_peer_request_process_entry(context, item))
|
||||||
|
did_some_processing = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// get next peer (or reset to head entry)
|
||||||
|
if (current->next == NULL) {
|
||||||
|
current = context->ipfsNode->peerstore->head_entry;
|
||||||
|
if (!did_some_processing) {
|
||||||
|
// we did nothing in this run through the peerstore. sleep for a sec
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
did_some_processing = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//libp2p_logger_debug("bitswap_engine", "Moving on to the next peer.\n");
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the bitswap engine that processes queue items. There
|
||||||
|
* should only be one of these per ipfs instance.
|
||||||
|
*
|
||||||
|
* @param context the context
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_engine_start(const struct BitswapContext* context) {
|
||||||
|
context->bitswap_engine->shutting_down = 0;
|
||||||
|
|
||||||
|
// fire off the threads
|
||||||
|
if (pthread_create(&context->bitswap_engine->wantlist_processor_thread, NULL, ipfs_bitswap_engine_wantlist_processor_start, (void*)context)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (pthread_create(&context->bitswap_engine->peer_request_processor_thread, NULL, ipfs_bitswap_engine_peer_request_processor_start, (void*)context)) {
|
||||||
|
ipfs_bitswap_engine_stop(context);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Shut down the engine
|
||||||
|
* @param context the context
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_engine_stop(const struct BitswapContext* context) {
|
||||||
|
context->bitswap_engine->shutting_down = 1;
|
||||||
|
|
||||||
|
int error1 = pthread_join(context->bitswap_engine->wantlist_processor_thread, NULL);
|
||||||
|
int error2 = pthread_join(context->bitswap_engine->peer_request_processor_thread, NULL);
|
||||||
|
|
||||||
|
ipfs_bitswap_engine_free(context->bitswap_engine);
|
||||||
|
|
||||||
|
return !error1 && !error2;
|
||||||
|
}
|
742
exchange/bitswap/message.c
Normal file
742
exchange/bitswap/message.c
Normal file
|
@ -0,0 +1,742 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "protobuf.h"
|
||||||
|
#include "varint.h"
|
||||||
|
#include "libp2p/utils/vector.h"
|
||||||
|
#include "ipfs/blocks/block.h"
|
||||||
|
#include "ipfs/exchange/bitswap/message.h"
|
||||||
|
#include "ipfs/exchange/bitswap/peer_request_queue.h"
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Allocate memory for a struct BitswapBlock
|
||||||
|
* @returns a new BitswapBlock
|
||||||
|
*/
|
||||||
|
struct BitswapBlock* ipfs_bitswap_block_new() {
|
||||||
|
struct BitswapBlock* block = (struct BitswapBlock*) malloc(sizeof(struct BitswapBlock));
|
||||||
|
if (block != NULL) {
|
||||||
|
block->bytes_size = 0;
|
||||||
|
block->bytes = NULL;
|
||||||
|
block->prefix_size = 0;
|
||||||
|
block->prefix = NULL;
|
||||||
|
}
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deallocate memory for a struct BitswapBlock
|
||||||
|
* @param block the block to deallocate
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_block_free(struct BitswapBlock* block) {
|
||||||
|
if (block != NULL) {
|
||||||
|
if (block->bytes != NULL)
|
||||||
|
free(block->bytes);
|
||||||
|
if (block->prefix != NULL)
|
||||||
|
free(block->prefix);
|
||||||
|
free(block);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an estimate of the size of a protobuf'd BitswapBlock
|
||||||
|
* @returns the approximate (maximum actually) size of a protobuf'd BitswapBlock
|
||||||
|
*/
|
||||||
|
size_t ipfs_bitswap_message_block_protobuf_size(struct BitswapBlock* block) {
|
||||||
|
// protobuf prefix + prefix + bytes = 33 + array sizes
|
||||||
|
return 33 + block->prefix_size + block->bytes_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Encode a BitswapBlock
|
||||||
|
* @param incoming the block to encode
|
||||||
|
* @param outgoing where to place the results
|
||||||
|
* @param max_size the maximum allocated space for outgoing
|
||||||
|
* @param bytes_written the number of bytes written to outgoing
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_message_block_protobuf_encode(struct BitswapBlock* incoming, uint8_t* outgoing, size_t max_size, size_t* bytes_written) {
|
||||||
|
// 2 WIRETYPE_LENGTH_DELIMITED fields of prefix and bytes
|
||||||
|
size_t bytes_used;
|
||||||
|
*bytes_written = 0;
|
||||||
|
|
||||||
|
if (incoming != NULL) {
|
||||||
|
if (!protobuf_encode_length_delimited(1, WIRETYPE_LENGTH_DELIMITED, (char*)incoming->prefix, incoming->prefix_size, outgoing, max_size, &bytes_used))
|
||||||
|
return 0;
|
||||||
|
*bytes_written += bytes_used;
|
||||||
|
if (!protobuf_encode_length_delimited(2, WIRETYPE_LENGTH_DELIMITED, (char*)incoming->bytes, incoming->bytes_size, &outgoing[*bytes_written], max_size - (*bytes_written), &bytes_used))
|
||||||
|
return 0;
|
||||||
|
*bytes_written += bytes_used;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Decode a protobuf to a BitswapBlock
|
||||||
|
* @param buffer the incoming protobuf
|
||||||
|
* @param buffer_length the length of the incoming protobuf buffer
|
||||||
|
* @param output a pointer to the BitswapBlock that will be allocated
|
||||||
|
* @returns true(1) on success, false(0) if not. If false, any memory was deallocated
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_message_block_protobuf_decode(uint8_t* buffer, size_t buffer_length, struct BitswapBlock** output) {
|
||||||
|
size_t pos = 0;
|
||||||
|
int retVal = 0;
|
||||||
|
|
||||||
|
*output = NULL;
|
||||||
|
|
||||||
|
// short cut for nulls
|
||||||
|
if (buffer_length == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
*output = (struct BitswapBlock*) malloc(sizeof(struct BitswapBlock));
|
||||||
|
if (*output == NULL)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
struct BitswapBlock* block = *output;
|
||||||
|
|
||||||
|
while(pos < buffer_length) {
|
||||||
|
size_t bytes_read = 0;
|
||||||
|
int field_no;
|
||||||
|
enum WireType field_type;
|
||||||
|
if (protobuf_decode_field_and_type(&buffer[pos], buffer_length, &field_no, &field_type, &bytes_read) == 0) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
pos += bytes_read;
|
||||||
|
switch(field_no) {
|
||||||
|
case (1):
|
||||||
|
if (!protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&block->prefix, &block->prefix_size, &bytes_read))
|
||||||
|
goto exit;
|
||||||
|
break;
|
||||||
|
case (2):
|
||||||
|
if (!protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&block->bytes, &block->bytes_size, &bytes_read))
|
||||||
|
goto exit;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
retVal = 1;
|
||||||
|
exit:
|
||||||
|
if (retVal == 0) {
|
||||||
|
if (*output != NULL)
|
||||||
|
free(*output);
|
||||||
|
*output = NULL;
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Allocate memory for a new WantlistEntry
|
||||||
|
* @returns the newly allocated WantlistEntry
|
||||||
|
*/
|
||||||
|
struct WantlistEntry* ipfs_bitswap_wantlist_entry_new() {
|
||||||
|
struct WantlistEntry* entry = (struct WantlistEntry*) malloc(sizeof(struct WantlistEntry));
|
||||||
|
if (entry == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
entry->block = NULL;
|
||||||
|
entry->block_size = 0;
|
||||||
|
entry->cancel = 0;
|
||||||
|
entry->priority = 1;
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Free allocations of a WantlistEntry
|
||||||
|
* @param entry the WantlistEntry
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_wantlist_entry_free(struct WantlistEntry* entry) {
|
||||||
|
if (entry != NULL) {
|
||||||
|
if (entry->block != NULL)
|
||||||
|
free(entry->block);
|
||||||
|
free(entry);
|
||||||
|
entry = NULL;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an estimate of the size of a protobuf'd WantlistEntry
|
||||||
|
* @param entry the struct to examine
|
||||||
|
* @returns the approximate (maximum actually) size of a protobuf'd WantlistEntry
|
||||||
|
*/
|
||||||
|
size_t ipfs_bitswap_wantlist_entry_protobuf_encode_size(struct WantlistEntry* entry) {
|
||||||
|
// protobuf prefix + block + cancel + priority
|
||||||
|
return 33 + entry->block_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Encode a WantlistEntry into a Protobuf
|
||||||
|
* @param entry the WantlistEntry to encode
|
||||||
|
* @param buffer where to put the results
|
||||||
|
* @param buffer_length the maximum size of the buffer
|
||||||
|
* @param bytes_written the number of bytes written into the buffer
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_wantlist_entry_protobuf_encode(struct WantlistEntry* entry, unsigned char* buffer, size_t buffer_length, size_t* bytes_written) {
|
||||||
|
size_t bytes_used;
|
||||||
|
*bytes_written = 0;
|
||||||
|
|
||||||
|
if (entry != NULL) {
|
||||||
|
if (!protobuf_encode_length_delimited(1, WIRETYPE_LENGTH_DELIMITED, (char*)entry->block, entry->block_size, &buffer[*bytes_written], buffer_length - (*bytes_written), &bytes_used))
|
||||||
|
return 0;
|
||||||
|
*bytes_written += bytes_used;
|
||||||
|
if (!protobuf_encode_varint(2, WIRETYPE_VARINT, entry->cancel, &buffer[*bytes_written], buffer_length - (*bytes_written), &bytes_used))
|
||||||
|
return 0;
|
||||||
|
*bytes_written += bytes_used;
|
||||||
|
if (!protobuf_encode_varint(3, WIRETYPE_VARINT, entry->priority, &buffer[*bytes_written], buffer_length - (*bytes_written), &bytes_used))
|
||||||
|
return 0;
|
||||||
|
*bytes_written += bytes_used;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Decode a protobuf into a struct WantlistEntry
|
||||||
|
* @param buffer the protobuf buffer
|
||||||
|
* @param buffer_length the length of the data in the protobuf buffer
|
||||||
|
* @param output the resultant WantlistEntry
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_wantlist_entry_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct WantlistEntry** output) {
|
||||||
|
size_t pos = 0;
|
||||||
|
int retVal = 0;
|
||||||
|
struct WantlistEntry* entry = NULL;
|
||||||
|
|
||||||
|
*output = NULL;
|
||||||
|
|
||||||
|
// short cut for nulls
|
||||||
|
if (buffer_length == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
*output = (struct WantlistEntry*) malloc(sizeof(struct WantlistEntry));
|
||||||
|
if (*output == NULL)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
entry = *output;
|
||||||
|
|
||||||
|
while(pos < buffer_length) {
|
||||||
|
size_t bytes_read = 0;
|
||||||
|
int field_no;
|
||||||
|
enum WireType field_type;
|
||||||
|
if (protobuf_decode_field_and_type(&buffer[pos], buffer_length, &field_no, &field_type, &bytes_read) == 0) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
pos += bytes_read;
|
||||||
|
switch(field_no) {
|
||||||
|
case (1):
|
||||||
|
if (!protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&entry->block, &entry->block_size, &bytes_read))
|
||||||
|
goto exit;
|
||||||
|
pos += bytes_read;
|
||||||
|
break;
|
||||||
|
case (2):
|
||||||
|
entry->cancel = varint_decode(&buffer[pos], buffer_length - pos, &bytes_read);
|
||||||
|
pos += bytes_read;
|
||||||
|
break;
|
||||||
|
case (3):
|
||||||
|
entry->priority = varint_decode(&buffer[pos], buffer_length - pos, &bytes_read);
|
||||||
|
pos += bytes_read;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
retVal = 1;
|
||||||
|
exit:
|
||||||
|
if (retVal == 0) {
|
||||||
|
if (entry != NULL)
|
||||||
|
free(entry);
|
||||||
|
*output = NULL;
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Allocate memory for a new Bitswap Message WantList
|
||||||
|
* @returns the allocated struct BitswapWantlist
|
||||||
|
*/
|
||||||
|
struct BitswapWantlist* ipfs_bitswap_wantlist_new() {
|
||||||
|
struct BitswapWantlist* list = (struct BitswapWantlist*) malloc(sizeof(struct BitswapWantlist));
|
||||||
|
|
||||||
|
if (list != NULL) {
|
||||||
|
list->entries = NULL;
|
||||||
|
list->full = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free the resources used by a Wantlist
|
||||||
|
* @param list the list to free
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_wantlist_free(struct BitswapWantlist* list) {
|
||||||
|
if (list != NULL) {
|
||||||
|
if (list->entries != NULL) {
|
||||||
|
for(int i = 0; i < list->entries->total; i++) {
|
||||||
|
// free each item in the vector
|
||||||
|
struct WantlistEntry* entry = (struct WantlistEntry*) libp2p_utils_vector_get(list->entries, i);
|
||||||
|
ipfs_bitswap_wantlist_entry_free(entry);
|
||||||
|
}
|
||||||
|
libp2p_utils_vector_free(list->entries);
|
||||||
|
}
|
||||||
|
free(list);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Calculate the maximum size of a protobuf'd BitswapWantlist
|
||||||
|
* @param list the Wantlist
|
||||||
|
* @returns the maximum size of the protobuf'd list
|
||||||
|
*/
|
||||||
|
size_t ipfs_bitswap_wantlist_protobuf_encode_size(struct BitswapWantlist* list) {
|
||||||
|
size_t total = 0;
|
||||||
|
if (list != NULL) {
|
||||||
|
for(int i = 0; i < list->entries->total; i++) {
|
||||||
|
struct WantlistEntry* entry = (struct WantlistEntry*) libp2p_utils_vector_get(list->entries, i);
|
||||||
|
total += ipfs_bitswap_wantlist_entry_protobuf_encode_size(entry);
|
||||||
|
}
|
||||||
|
total += 11 + 12 + 11;
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Encode a BitswapWantlist into a protobuf buffer
|
||||||
|
* @param list the list to encode
|
||||||
|
* @param buffer the buffer to fill
|
||||||
|
* @param buffer_length the length of the allocated buffer
|
||||||
|
* @param bytes_written the total number of bytes written to the buffer
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_wantlist_protobuf_encode(struct BitswapWantlist* list, unsigned char* buffer, size_t buffer_length, size_t* bytes_written) {
|
||||||
|
size_t bytes_used = 0;
|
||||||
|
*bytes_written = 0;
|
||||||
|
|
||||||
|
if (list != NULL) {
|
||||||
|
// the vector of entries
|
||||||
|
for(int i = 0; i < list->entries->total; i++) {
|
||||||
|
struct WantlistEntry* entry = (struct WantlistEntry*) libp2p_utils_vector_get(list->entries, i);
|
||||||
|
// protobuf the entry
|
||||||
|
size_t temp_buffer_size = ipfs_bitswap_wantlist_entry_protobuf_encode_size(entry);
|
||||||
|
uint8_t* temp_buffer = (uint8_t*) malloc(temp_buffer_size);
|
||||||
|
if (temp_buffer == NULL)
|
||||||
|
return 0;
|
||||||
|
if (!ipfs_bitswap_wantlist_entry_protobuf_encode(entry, temp_buffer, temp_buffer_size, &temp_buffer_size)) {
|
||||||
|
free(temp_buffer);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// we've got the protobuf'd entry, now put it in the real buffer
|
||||||
|
if (!protobuf_encode_length_delimited(1, WIRETYPE_LENGTH_DELIMITED, (char*)temp_buffer, temp_buffer_size, &buffer[*bytes_written], buffer_length - (*bytes_written), &bytes_used)) {
|
||||||
|
free(temp_buffer);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// all went okay. Clean up and do it again...
|
||||||
|
free(temp_buffer);
|
||||||
|
*bytes_written += bytes_used;
|
||||||
|
}
|
||||||
|
// if this is the full list or not...
|
||||||
|
if (!protobuf_encode_varint(2, WIRETYPE_VARINT, list->full, &buffer[*bytes_written], buffer_length - (*bytes_written), &bytes_used))
|
||||||
|
return 0;
|
||||||
|
*bytes_written += bytes_used;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Decode a Wantlist from a protobuf
|
||||||
|
* @param buffer the protobuf
|
||||||
|
* @param buffer_length the length of the protobuf
|
||||||
|
* @param output the newly allocated BitswapWantlist
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_wantlist_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct BitswapWantlist** output) {
|
||||||
|
size_t pos = 0;
|
||||||
|
|
||||||
|
*output = NULL;
|
||||||
|
|
||||||
|
// short cut for nulls
|
||||||
|
if (buffer_length == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
*output = ipfs_bitswap_wantlist_new();
|
||||||
|
if (*output == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
struct BitswapWantlist* list = *output;
|
||||||
|
|
||||||
|
while(pos < buffer_length) {
|
||||||
|
size_t bytes_read = 0;
|
||||||
|
int field_no;
|
||||||
|
enum WireType field_type;
|
||||||
|
if (protobuf_decode_field_and_type(&buffer[pos], buffer_length, &field_no, &field_type, &bytes_read) == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
pos += bytes_read;
|
||||||
|
switch(field_no) {
|
||||||
|
case (1): {
|
||||||
|
// a WantlistEntry
|
||||||
|
size_t temp_size = 0;
|
||||||
|
uint8_t* temp = NULL;
|
||||||
|
if (!protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&temp, &temp_size, &bytes_read)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
struct WantlistEntry* entry = NULL;
|
||||||
|
if (!ipfs_bitswap_wantlist_entry_protobuf_decode(temp, temp_size, &entry)) {
|
||||||
|
free(temp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
free(temp);
|
||||||
|
if (list->entries == NULL) {
|
||||||
|
list->entries = libp2p_utils_vector_new(1);
|
||||||
|
}
|
||||||
|
libp2p_utils_vector_add(list->entries, (void*)entry);
|
||||||
|
pos += bytes_read;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case (2): {
|
||||||
|
list->full = varint_decode(&buffer[pos], buffer_length - pos, &bytes_read);
|
||||||
|
pos += bytes_read;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Bitswap Message
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Allocate memory for a new Bitswap Message
|
||||||
|
* @returns the allocated struct BitswapMessage
|
||||||
|
*/
|
||||||
|
struct BitswapMessage* ipfs_bitswap_message_new() {
|
||||||
|
struct BitswapMessage* message = (struct BitswapMessage*) malloc(sizeof(struct BitswapMessage));
|
||||||
|
|
||||||
|
if (message != NULL) {
|
||||||
|
message->blocks = NULL;
|
||||||
|
message->payload = NULL;
|
||||||
|
message->wantlist = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free the resources used by a BitswapMessage
|
||||||
|
* @param message the BitswapMessage to free
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_message_free(struct BitswapMessage* message) {
|
||||||
|
if (message != NULL) {
|
||||||
|
if (message->blocks != NULL) {
|
||||||
|
// blocks are just byte arrays in bitswap 1.0.0, so throw it in a struct
|
||||||
|
// so it can be put in a vector
|
||||||
|
for(int i = 0; i < message->blocks->total; i++) {
|
||||||
|
// free each item in the vector
|
||||||
|
struct Block* entry = (struct Block*) libp2p_utils_vector_get(message->blocks, i);
|
||||||
|
ipfs_block_free(entry);
|
||||||
|
}
|
||||||
|
libp2p_utils_vector_free(message->blocks);
|
||||||
|
}
|
||||||
|
if (message->payload != NULL) {
|
||||||
|
for(int i = 0; i < message->payload->total; i++) {
|
||||||
|
// free each item in the vector
|
||||||
|
struct Block* entry = (struct Block*) libp2p_utils_vector_get(message->payload, i);
|
||||||
|
ipfs_block_free(entry);
|
||||||
|
}
|
||||||
|
libp2p_utils_vector_free(message->payload);
|
||||||
|
}
|
||||||
|
if (message->wantlist != NULL) {
|
||||||
|
ipfs_bitswap_wantlist_free(message->wantlist);
|
||||||
|
}
|
||||||
|
free(message);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Calculate the maximum size of a protobuf'd BitswapMessage
|
||||||
|
* @param message the BitswapMessage
|
||||||
|
* @returns the maximum size of the protobuf'd BitswapMessage
|
||||||
|
*/
|
||||||
|
size_t ipfs_bitswap_message_protobuf_encode_size(const struct BitswapMessage* message) {
|
||||||
|
size_t total = 0;
|
||||||
|
if (message != NULL) {
|
||||||
|
if (message->blocks != NULL) {
|
||||||
|
for(int i = 0; i < message->blocks->total; i++) {
|
||||||
|
struct Block* entry = (struct Block*) libp2p_utils_vector_get(message->blocks, i);
|
||||||
|
total += 11 + entry->data_length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (message->payload != NULL) {
|
||||||
|
for(int i = 0; i < message->payload->total; i++) {
|
||||||
|
struct Block* entry = (struct Block*) libp2p_utils_vector_get(message->payload, i);
|
||||||
|
total += 11 + ipfs_blocks_block_protobuf_encode_size(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (message->wantlist != NULL) {
|
||||||
|
total += ipfs_bitswap_wantlist_protobuf_encode_size(message->wantlist);
|
||||||
|
}
|
||||||
|
total += 11 + 12 + 11;
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Encode a BitswapMessage into a protobuf buffer
|
||||||
|
* @param message the message to encode
|
||||||
|
* @param buffer the buffer to fill
|
||||||
|
* @param buffer_length the length of the allocated buffer
|
||||||
|
* @param bytes_written the total number of bytes written to the buffer
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_message_protobuf_encode(const struct BitswapMessage* message, unsigned char* buffer, size_t buffer_length, size_t* bytes_written) {
|
||||||
|
size_t bytes_used = 0;
|
||||||
|
*bytes_written = 0;
|
||||||
|
|
||||||
|
if (message != NULL) {
|
||||||
|
// the vector of blocks that are actually to be turned back into byte arrays
|
||||||
|
if (message->blocks != NULL) {
|
||||||
|
for(int i = 0; i < message->blocks->total; i++) {
|
||||||
|
struct Block* entry = (struct Block*) libp2p_utils_vector_get(message->blocks, i);
|
||||||
|
// blocks are just variable length byte streams
|
||||||
|
if (!protobuf_encode_length_delimited(1, WIRETYPE_LENGTH_DELIMITED, (char*)entry->data, entry->data_length, &buffer[*bytes_written], buffer_length - (*bytes_written), &bytes_used)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*bytes_written += bytes_used;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// the vector of Blocks that are actually blocks
|
||||||
|
if (message->payload != NULL) {
|
||||||
|
for(int i = 0; i < message->payload->total; i++) {
|
||||||
|
struct Block* entry = (struct Block*) libp2p_utils_vector_get(message->payload, i);
|
||||||
|
// protobuf it
|
||||||
|
size_t temp_size = ipfs_blocks_block_protobuf_encode_size(entry);
|
||||||
|
uint8_t* temp = (uint8_t*) malloc(temp_size);
|
||||||
|
if (temp == NULL) {
|
||||||
|
// memory issues
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!ipfs_blocks_block_protobuf_encode(entry, temp, temp_size, &temp_size)) {
|
||||||
|
free(temp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// put it in the buffer
|
||||||
|
if (!protobuf_encode_length_delimited(2, WIRETYPE_LENGTH_DELIMITED, (char*)temp, temp_size, &buffer[*bytes_written], buffer_length - (*bytes_written), &bytes_used)) {
|
||||||
|
free(temp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*bytes_written += bytes_used;
|
||||||
|
free(temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// the WantList
|
||||||
|
if (message->wantlist != NULL) {
|
||||||
|
size_t temp_size = ipfs_bitswap_wantlist_protobuf_encode_size(message->wantlist);
|
||||||
|
uint8_t* temp = (uint8_t*) malloc(temp_size);
|
||||||
|
if (temp == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!ipfs_bitswap_wantlist_protobuf_encode(message->wantlist, temp, temp_size, &temp_size)) {
|
||||||
|
free(temp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!protobuf_encode_length_delimited(3, WIRETYPE_LENGTH_DELIMITED, (char*)temp, temp_size, &buffer[*bytes_written], buffer_length - (*bytes_written), &bytes_used)) {
|
||||||
|
free(temp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*bytes_written += bytes_used;
|
||||||
|
free(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Decode a BitswapMessage from a protobuf
|
||||||
|
* @param buffer the protobuf
|
||||||
|
* @param buffer_length the length of the protobuf
|
||||||
|
* @param output the newly allocated BitswapMessage
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_message_protobuf_decode(const uint8_t* buffer, size_t buffer_length, struct BitswapMessage** output) {
|
||||||
|
size_t pos = 0;
|
||||||
|
|
||||||
|
*output = NULL;
|
||||||
|
|
||||||
|
// short cut for nulls
|
||||||
|
if (buffer_length == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
*output = (struct BitswapMessage*) ipfs_bitswap_message_new();
|
||||||
|
if (*output == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
struct BitswapMessage* message = *output;
|
||||||
|
|
||||||
|
while(pos < buffer_length) {
|
||||||
|
size_t bytes_read = 0;
|
||||||
|
int field_no;
|
||||||
|
enum WireType field_type;
|
||||||
|
if (protobuf_decode_field_and_type(&buffer[pos], buffer_length, &field_no, &field_type, &bytes_read) == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
pos += bytes_read;
|
||||||
|
switch(field_no) {
|
||||||
|
case (1): {
|
||||||
|
// a Blocks entry that is just an array of bytes
|
||||||
|
struct Block* temp = ipfs_block_new();
|
||||||
|
if (!protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&temp->data, &temp->data_length, &bytes_read)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (message->blocks == NULL) {
|
||||||
|
message->blocks = libp2p_utils_vector_new(1);
|
||||||
|
}
|
||||||
|
libp2p_utils_vector_add(message->blocks, (void*)temp);
|
||||||
|
pos += bytes_read;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case (2): {
|
||||||
|
// a block entry that is a real block struct
|
||||||
|
size_t temp_size = 0;
|
||||||
|
uint8_t* temp = NULL;
|
||||||
|
if (!protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&temp, &temp_size, &bytes_read)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// we have the bytes, turn it into a Block struct
|
||||||
|
struct Block* block = NULL;
|
||||||
|
if (!ipfs_blocks_block_protobuf_decode(temp, temp_size, &block)) {
|
||||||
|
free(temp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
free(temp);
|
||||||
|
if (message->payload == NULL) {
|
||||||
|
message->payload = libp2p_utils_vector_new(1);
|
||||||
|
}
|
||||||
|
libp2p_utils_vector_add(message->payload, (void*)block);
|
||||||
|
pos += bytes_read;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case(3): {
|
||||||
|
// a Wantlist
|
||||||
|
size_t temp_size = 0;
|
||||||
|
uint8_t* temp = NULL;
|
||||||
|
if (!protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&temp, &temp_size, &bytes_read)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// we have the protobuf'd wantlist, now turn it into a Wantlist struct.
|
||||||
|
if (!ipfs_bitswap_wantlist_protobuf_decode(temp, temp_size, &message->wantlist)) {
|
||||||
|
free(temp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
free(temp);
|
||||||
|
pos += bytes_read;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/****
|
||||||
|
* Add a vector of Cids to the bitswap message
|
||||||
|
* @param message the message
|
||||||
|
* @param cids a Libp2pVector of cids
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_message_add_wantlist_items(struct BitswapMessage* message, struct Libp2pVector* cids) {
|
||||||
|
if (message->wantlist == NULL) {
|
||||||
|
message->wantlist = ipfs_bitswap_wantlist_new();
|
||||||
|
if (message->wantlist == NULL)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (message->wantlist->entries == NULL) {
|
||||||
|
message->wantlist->entries = libp2p_utils_vector_new(1);
|
||||||
|
if (message->wantlist->entries == NULL)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
for(int i = 0; i < cids->total; i++) {
|
||||||
|
struct CidEntry* cidEntry = (struct CidEntry*)libp2p_utils_vector_get(cids, i);
|
||||||
|
if (cidEntry->cancel && cidEntry->cancel_has_been_sent)
|
||||||
|
continue;
|
||||||
|
if (!cidEntry->cancel && cidEntry->request_has_been_sent)
|
||||||
|
continue;
|
||||||
|
struct WantlistEntry* entry = ipfs_bitswap_wantlist_entry_new();
|
||||||
|
entry->block_size = ipfs_cid_protobuf_encode_size(cidEntry->cid);
|
||||||
|
entry->block = (unsigned char*) malloc(entry->block_size);
|
||||||
|
if (entry->block == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!ipfs_cid_protobuf_encode(cidEntry->cid, entry->block, entry->block_size, &entry->block_size)) {
|
||||||
|
// TODO: we should do more than return a half-baked list
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
entry->cancel = cidEntry->cancel;
|
||||||
|
entry->priority = 1;
|
||||||
|
libp2p_utils_vector_add(message->wantlist->entries, entry);
|
||||||
|
if (cidEntry->cancel)
|
||||||
|
cidEntry->cancel_has_been_sent = 1;
|
||||||
|
else
|
||||||
|
cidEntry->request_has_been_sent = 1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Look through vector for specific Cid, then mark it cancel
|
||||||
|
* @param vector the vector of CidEntrys
|
||||||
|
* @param incoming_cid the cid to look for
|
||||||
|
* @returns true(1) if found one, false(0) if not
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_message_cancel_cid(struct Libp2pVector* vector, struct Cid* incoming_cid) {
|
||||||
|
for(int i = 0; i < vector->total; i++) {
|
||||||
|
struct CidEntry* entry = (struct CidEntry*)libp2p_utils_vector_get(vector, i);
|
||||||
|
if (ipfs_cid_compare(entry->cid, incoming_cid) == 0) {
|
||||||
|
entry->cancel = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Add the blocks to the BitswapMessage
|
||||||
|
* @param message the message
|
||||||
|
* @param blocks the requested blocks
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_message_add_blocks(struct BitswapMessage* message, struct Libp2pVector* blocks, struct Libp2pVector* cids_they_want) {
|
||||||
|
// bitswap 1.0 uses blocks, bitswap 1.1 uses payload
|
||||||
|
|
||||||
|
if (message == NULL)
|
||||||
|
return 0;
|
||||||
|
if (blocks == NULL || blocks->total == 0)
|
||||||
|
return 0;
|
||||||
|
if (message->payload == NULL) {
|
||||||
|
message->payload = libp2p_utils_vector_new(blocks->total);
|
||||||
|
if (message->payload == NULL)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int tot_blocks = blocks->total;
|
||||||
|
for(int i = 0; i < tot_blocks; i++) {
|
||||||
|
const struct Block* current = (const struct Block*) libp2p_utils_vector_get(blocks, i);
|
||||||
|
libp2p_utils_vector_add(message->payload, current);
|
||||||
|
ipfs_bitswap_message_cancel_cid(cids_they_want, current->cid);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < tot_blocks; i++) {
|
||||||
|
libp2p_utils_vector_delete(blocks, 0);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
146
exchange/bitswap/network.c
Normal file
146
exchange/bitswap/network.c
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
/***
|
||||||
|
* This implements the BitswapNetwork. Members of this network can fill requests and
|
||||||
|
* smartly handle queues of local and remote requests.
|
||||||
|
*
|
||||||
|
* For a somewhat accurate diagram of how this may work, @see https://github.com/ipfs/js-ipfs-bitswap
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include "libp2p/utils/logger.h"
|
||||||
|
#include "ipfs/exchange/bitswap/network.h"
|
||||||
|
#include "ipfs/exchange/bitswap/peer_request_queue.h"
|
||||||
|
|
||||||
|
/****
|
||||||
|
* send a message to a particular peer
|
||||||
|
* @param context the BitswapContext
|
||||||
|
* @param peer the peer that is the recipient
|
||||||
|
* @param message the message to send
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_network_send_message(const struct BitswapContext* context, struct Libp2pPeer* peer, const struct BitswapMessage* message) {
|
||||||
|
libp2p_logger_debug("bitswap_network", "Sending bitswap message to %s.\n", libp2p_peer_id_to_string(peer));
|
||||||
|
// get a connection to the peer
|
||||||
|
if (peer->connection_type != CONNECTION_TYPE_CONNECTED || peer->sessionContext == NULL) {
|
||||||
|
libp2p_peer_connect(context->ipfsNode->dialer, peer, context->ipfsNode->peerstore, context->ipfsNode->repo->config->datastore, 10);
|
||||||
|
if(peer->connection_type != CONNECTION_TYPE_CONNECTED)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// protobuf the message
|
||||||
|
size_t buf_size = ipfs_bitswap_message_protobuf_encode_size(message);
|
||||||
|
uint8_t* buf = (uint8_t*) malloc(buf_size + 20);
|
||||||
|
if (buf == NULL)
|
||||||
|
return 0;
|
||||||
|
if (!ipfs_bitswap_message_protobuf_encode(message, &buf[20], buf_size, &buf_size)) {
|
||||||
|
free(buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// tack on the protocol header
|
||||||
|
memcpy(buf, "/ipfs/bitswap/1.1.0\n", 20);
|
||||||
|
buf_size += 20;
|
||||||
|
// send it
|
||||||
|
struct StreamMessage outgoing;
|
||||||
|
outgoing.data = buf;
|
||||||
|
outgoing.data_size = buf_size;
|
||||||
|
int bytes_written = peer->sessionContext->default_stream->write(peer->sessionContext, &outgoing);
|
||||||
|
if (bytes_written <= 0) {
|
||||||
|
free(buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
free(buf);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Remove a cid from the queue
|
||||||
|
* @param cids the vector of cids
|
||||||
|
* @param cid the cid to remove
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_network_adjust_cid_queue(struct Libp2pVector* collection, struct Cid* cid, int cancel) {
|
||||||
|
if (collection == NULL || cid == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for(int i = 0; i < collection->total; collection++) {
|
||||||
|
const struct CidEntry* current = (const struct CidEntry*)libp2p_utils_vector_get(collection, i);
|
||||||
|
if (ipfs_cid_compare(current->cid, cid) == 0) {
|
||||||
|
if (cancel)
|
||||||
|
libp2p_utils_vector_delete(collection, i);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// not found. Add it if we're not cancelling
|
||||||
|
if (!cancel) {
|
||||||
|
struct CidEntry* cidEntry = ipfs_bitswap_peer_request_cid_entry_new();
|
||||||
|
cidEntry->cid = cid;
|
||||||
|
cidEntry->cancel = 0;
|
||||||
|
libp2p_utils_vector_add(collection, cidEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Handle a raw incoming bitswap message from the network
|
||||||
|
* @param node us
|
||||||
|
* @param sessionContext the connection context
|
||||||
|
* @param bytes the message
|
||||||
|
* @param bytes_size the size of the message
|
||||||
|
* @returns true(1) on success, false(0) otherwise.
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_network_handle_message(const struct IpfsNode* node, const struct SessionContext* sessionContext, const uint8_t* bytes, size_t bytes_length) {
|
||||||
|
struct BitswapContext* bitswapContext = (struct BitswapContext*)node->exchange->exchangeContext;
|
||||||
|
// strip off the protocol header
|
||||||
|
int start = -1;
|
||||||
|
for(int i = 0; i < bytes_length; i++) {
|
||||||
|
if (bytes[i] == '\n') {
|
||||||
|
start = i+1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (start == -1)
|
||||||
|
return 0;
|
||||||
|
// un-protobuf the message
|
||||||
|
struct BitswapMessage* message = NULL;
|
||||||
|
if (!ipfs_bitswap_message_protobuf_decode(&bytes[start], bytes_length - start, &message))
|
||||||
|
return 0;
|
||||||
|
// process the message
|
||||||
|
// payload - what we want
|
||||||
|
if (message->payload != NULL) {
|
||||||
|
for(int i = 0; i < message->payload->total; i++) {
|
||||||
|
struct Block* blk = (struct Block*)libp2p_utils_vector_get(message->payload, i);
|
||||||
|
// we need a copy of the block so it survives the destruction of the message
|
||||||
|
node->exchange->HasBlock(node->exchange, ipfs_block_copy(blk));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// wantlist - what they want
|
||||||
|
if (message->wantlist != NULL && message->wantlist->entries != NULL && message->wantlist->entries->total > 0) {
|
||||||
|
// get the peer
|
||||||
|
if (sessionContext->remote_peer_id == NULL) {
|
||||||
|
ipfs_bitswap_message_free(message);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
struct Libp2pPeer* peer = libp2p_peerstore_get_or_add_peer_by_id(node->peerstore, (unsigned char*)sessionContext->remote_peer_id, strlen(sessionContext->remote_peer_id));
|
||||||
|
if (peer == NULL) {
|
||||||
|
libp2p_logger_error("bitswap_network", "Unable to find or add peer %s of length %d to peerstore.\n", sessionContext->remote_peer_id, strlen(sessionContext->remote_peer_id));
|
||||||
|
ipfs_bitswap_message_free(message);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// find the queue (adds it if it is not there)
|
||||||
|
struct PeerRequest* peerRequest = ipfs_peer_request_queue_find_peer(bitswapContext->peerRequestQueue, peer);
|
||||||
|
for(int i = 0; i < message->wantlist->entries->total; i++) {
|
||||||
|
struct WantlistEntry* entry = (struct WantlistEntry*) libp2p_utils_vector_get(message->wantlist->entries, i);
|
||||||
|
// turn the "block" back into a cid
|
||||||
|
struct Cid* cid = NULL;
|
||||||
|
if (!ipfs_cid_protobuf_decode(entry->block, entry->block_size, &cid) || cid->hash_length == 0) {
|
||||||
|
libp2p_logger_error("bitswap_network", "Message had invalid CID\n");
|
||||||
|
ipfs_cid_free(cid);
|
||||||
|
ipfs_bitswap_message_free(message);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ipfs_bitswap_network_adjust_cid_queue(peerRequest->cids_they_want, cid, entry->cancel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ipfs_bitswap_message_free(message);
|
||||||
|
return 1;
|
||||||
|
}
|
447
exchange/bitswap/peer_request_queue.c
Normal file
447
exchange/bitswap/peer_request_queue.c
Normal file
|
@ -0,0 +1,447 @@
|
||||||
|
/***
|
||||||
|
* A queue for requests from remote peers
|
||||||
|
* NOTE: This should handle multiple threads
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "libp2p/conn/session.h"
|
||||||
|
#include "libp2p/utils/logger.h"
|
||||||
|
#include "ipfs/cid/cid.h"
|
||||||
|
#include "ipfs/exchange/bitswap/peer_request_queue.h"
|
||||||
|
#include "ipfs/exchange/bitswap/message.h"
|
||||||
|
#include "ipfs/exchange/bitswap/network.h"
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Allocate memory for CidEntry
|
||||||
|
* @returns new CidEntry struct
|
||||||
|
*/
|
||||||
|
struct CidEntry* ipfs_bitswap_peer_request_cid_entry_new() {
|
||||||
|
struct CidEntry* entry = (struct CidEntry*) malloc(sizeof(struct CidEntry));
|
||||||
|
if (entry != NULL) {
|
||||||
|
entry->cid = NULL;
|
||||||
|
entry->cancel = 0;
|
||||||
|
entry->cancel_has_been_sent = 0;
|
||||||
|
entry->request_has_been_sent = 0;
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Allocate resources for a new PeerRequest
|
||||||
|
* @returns a new PeerRequest struct or NULL if there was a problem
|
||||||
|
*/
|
||||||
|
struct PeerRequest* ipfs_bitswap_peer_request_new() {
|
||||||
|
int retVal = 0;
|
||||||
|
struct PeerRequest* request = (struct PeerRequest*) malloc(sizeof(struct PeerRequest));
|
||||||
|
if (request != NULL) {
|
||||||
|
request->cids_they_want = libp2p_utils_vector_new(1);
|
||||||
|
if (request->cids_they_want == NULL)
|
||||||
|
goto exit;
|
||||||
|
request->cids_we_want = libp2p_utils_vector_new(1);
|
||||||
|
if (request->cids_we_want == NULL)
|
||||||
|
goto exit;
|
||||||
|
request->blocks_we_want_to_send = libp2p_utils_vector_new(1);
|
||||||
|
if (request->blocks_we_want_to_send == NULL)
|
||||||
|
goto exit;
|
||||||
|
request->peer = NULL;
|
||||||
|
}
|
||||||
|
retVal = 1;
|
||||||
|
exit:
|
||||||
|
if (retVal == 0 && request != NULL) {
|
||||||
|
if (request->blocks_we_want_to_send != NULL)
|
||||||
|
libp2p_utils_vector_free(request->blocks_we_want_to_send);
|
||||||
|
if (request->cids_they_want != NULL)
|
||||||
|
libp2p_utils_vector_free(request->cids_they_want);
|
||||||
|
if (request->cids_we_want != NULL)
|
||||||
|
libp2p_utils_vector_free(request->cids_we_want);
|
||||||
|
free(request);
|
||||||
|
request = NULL;
|
||||||
|
}
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ipfs_bitswap_cid_entry_free(struct CidEntry* entry) {
|
||||||
|
if (entry != NULL) {
|
||||||
|
if (entry->cid != NULL) {
|
||||||
|
ipfs_cid_free(entry->cid);
|
||||||
|
entry->cid = NULL;
|
||||||
|
}
|
||||||
|
free(entry);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free resources from a PeerRequest
|
||||||
|
* @param request the request to free
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_peer_request_free(struct PeerRequest* request) {
|
||||||
|
if (request != NULL) {
|
||||||
|
for(int i = 0; i < request->cids_we_want->total; i++) {
|
||||||
|
struct CidEntry* entry = (struct CidEntry*)libp2p_utils_vector_get(request->cids_we_want, i);
|
||||||
|
ipfs_bitswap_cid_entry_free(entry);
|
||||||
|
}
|
||||||
|
libp2p_utils_vector_free(request->cids_we_want);
|
||||||
|
request->cids_we_want = NULL;
|
||||||
|
for(int i = 0; i < request->cids_they_want->total; i++) {
|
||||||
|
struct CidEntry* entry = (struct CidEntry*)libp2p_utils_vector_get(request->cids_they_want, i);
|
||||||
|
ipfs_bitswap_cid_entry_free(entry);
|
||||||
|
}
|
||||||
|
libp2p_utils_vector_free(request->cids_they_want);
|
||||||
|
request->cids_they_want = NULL;
|
||||||
|
for(int i = 0; i < request->blocks_we_want_to_send->total; i++) {
|
||||||
|
struct Block* block = (struct Block*)libp2p_utils_vector_get(request->blocks_we_want_to_send, i);
|
||||||
|
ipfs_block_free(block);
|
||||||
|
}
|
||||||
|
libp2p_utils_vector_free(request->blocks_we_want_to_send);
|
||||||
|
request->blocks_we_want_to_send = NULL;
|
||||||
|
free(request);
|
||||||
|
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate resources for a new queue
|
||||||
|
*/
|
||||||
|
struct PeerRequestQueue* ipfs_bitswap_peer_request_queue_new() {
|
||||||
|
struct PeerRequestQueue* queue = malloc(sizeof(struct PeerRequestQueue));
|
||||||
|
if (queue != NULL) {
|
||||||
|
pthread_mutex_init(&queue->queue_mutex, NULL);
|
||||||
|
queue->first = NULL;
|
||||||
|
queue->last = NULL;
|
||||||
|
}
|
||||||
|
return queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free all resources related to the queue
|
||||||
|
* @param queue the queue
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_peer_request_queue_free(struct PeerRequestQueue* queue) {
|
||||||
|
pthread_mutex_lock(&queue->queue_mutex);
|
||||||
|
struct PeerRequestEntry* current = queue->last;
|
||||||
|
while (current != NULL) {
|
||||||
|
struct PeerRequestEntry* prior = current->prior;
|
||||||
|
ipfs_bitswap_peer_request_entry_free(current);
|
||||||
|
current = prior;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&queue->queue_mutex);
|
||||||
|
free(queue);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a peer request to the end of the queue
|
||||||
|
* @param queue the queue
|
||||||
|
* @param request the request
|
||||||
|
* @returns true(1) on success, otherwise false
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_peer_request_queue_add(struct PeerRequestQueue* queue, struct PeerRequest* request) {
|
||||||
|
if (request != NULL) {
|
||||||
|
struct PeerRequestEntry* entry = ipfs_bitswap_peer_request_entry_new();
|
||||||
|
entry->current = request;
|
||||||
|
pthread_mutex_lock(&queue->queue_mutex);
|
||||||
|
entry->prior = queue->last;
|
||||||
|
queue->last = entry;
|
||||||
|
if (queue->first == NULL) {
|
||||||
|
queue->first = entry;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&queue->queue_mutex);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a peer request from the queue, no mather where it is
|
||||||
|
* @param queue the queue
|
||||||
|
* @param request the request
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_peer_request_queue_remove(struct PeerRequestQueue* queue, struct PeerRequest* request) {
|
||||||
|
if (request != NULL) {
|
||||||
|
struct PeerRequestEntry* entry = ipfs_bitswap_peer_request_queue_find_entry(queue, request->peer);
|
||||||
|
if (entry != NULL) {
|
||||||
|
pthread_mutex_lock(&queue->queue_mutex);
|
||||||
|
// remove the entry's link, and hook prior and next together
|
||||||
|
entry->prior->next = entry->next;
|
||||||
|
entry->prior = NULL;
|
||||||
|
entry->next = NULL;
|
||||||
|
ipfs_bitswap_peer_request_entry_free(entry);
|
||||||
|
pthread_mutex_unlock(&queue->queue_mutex);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a PeerRequestEntry that contains the specified Peer
|
||||||
|
* @param queue the queue to look through
|
||||||
|
* @param peer what we're looking for
|
||||||
|
* @returns the PeerRequestEntry or NULL if not found
|
||||||
|
*/
|
||||||
|
struct PeerRequestEntry* ipfs_bitswap_peer_request_queue_find_entry(struct PeerRequestQueue* queue, struct Libp2pPeer* peer) {
|
||||||
|
if (peer != NULL) {
|
||||||
|
struct PeerRequestEntry* current = queue->first;
|
||||||
|
while (current != NULL) {
|
||||||
|
if (libp2p_peer_compare(current->current->peer, peer) == 0)
|
||||||
|
return current;
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Determine if any of the cids in the list are waiting to be filled
|
||||||
|
* @param cidEntries a Vector of CidEntry objects
|
||||||
|
* @returns true(1) if we have some waiting, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_peer_request_cids_waiting(struct Libp2pVector* cidEntries) {
|
||||||
|
if (cidEntries == NULL)
|
||||||
|
return 0;
|
||||||
|
for(int i = 0; i < cidEntries->total; i++) {
|
||||||
|
const struct CidEntry* entry = (const struct CidEntry*)libp2p_utils_vector_get(cidEntries, i);
|
||||||
|
if (entry != NULL && !entry->cancel)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Determine if there is something to process in this request
|
||||||
|
* @param entry the entry to look at
|
||||||
|
* @returns true(1) if there is something to do
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_peer_request_something_to_do(struct PeerRequestEntry* entry) {
|
||||||
|
if (entry != NULL) {
|
||||||
|
struct PeerRequest* request = entry->current;
|
||||||
|
// do we have something in the queue?
|
||||||
|
if (request->blocks_we_want_to_send->total > 0)
|
||||||
|
return 1;
|
||||||
|
if (request->cids_we_want->total > 0)
|
||||||
|
return 1;
|
||||||
|
if (ipfs_bitswap_peer_request_cids_waiting(request->cids_they_want))
|
||||||
|
return 1;
|
||||||
|
// is there something waiting for us on the network?
|
||||||
|
if (request->peer->connection_type == CONNECTION_TYPE_CONNECTED) {
|
||||||
|
int retVal = request->peer->sessionContext->default_stream->peek(request->peer->sessionContext);
|
||||||
|
if (retVal < 0) {
|
||||||
|
libp2p_logger_debug("peer_request_queue", "Connection returned %d. Marking connection NOT CONNECTED.\n", retVal);
|
||||||
|
libp2p_peer_handle_connection_error(request->peer);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (retVal > 0) {
|
||||||
|
libp2p_logger_debug("peer_request_queue", "We have something to read. %d bytes.\n", retVal);
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pull a PeerRequest off the queue
|
||||||
|
* @param queue the queue
|
||||||
|
* @returns the PeerRequest that should be handled next, or NULL if the queue is empty
|
||||||
|
*/
|
||||||
|
struct PeerRequest* ipfs_bitswap_peer_request_queue_pop(struct PeerRequestQueue* queue) {
|
||||||
|
struct PeerRequest* retVal = NULL;
|
||||||
|
if (queue != NULL) {
|
||||||
|
pthread_mutex_lock(&queue->queue_mutex);
|
||||||
|
struct PeerRequestEntry* entry = queue->first;
|
||||||
|
if (entry != NULL) {
|
||||||
|
if (ipfs_bitswap_peer_request_something_to_do(entry)) {
|
||||||
|
retVal = entry->current;
|
||||||
|
// move to the end of the queue
|
||||||
|
if (queue->first->next != NULL) {
|
||||||
|
queue->first = queue->first->next;
|
||||||
|
queue->last->next = entry;
|
||||||
|
queue->last = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&queue->queue_mutex);
|
||||||
|
// disable temporarily
|
||||||
|
// JMJ Debugging
|
||||||
|
/*
|
||||||
|
if (entry != NULL)
|
||||||
|
ipfs_bitswap_peer_request_entry_free(entry);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Allocate resources for a PeerRequestEntry struct
|
||||||
|
* @returns the allocated struct or NULL if there was a problem
|
||||||
|
*/
|
||||||
|
struct PeerRequestEntry* ipfs_bitswap_peer_request_entry_new() {
|
||||||
|
struct PeerRequestEntry* entry = (struct PeerRequestEntry*) malloc(sizeof(struct PeerRequestEntry));
|
||||||
|
if (entry != NULL) {
|
||||||
|
entry->current = NULL;
|
||||||
|
entry->next = NULL;
|
||||||
|
entry->prior = NULL;
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees resources allocated
|
||||||
|
* @param entry the PeerRequestEntry to free
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_peer_request_entry_free(struct PeerRequestEntry* entry) {
|
||||||
|
entry->next = NULL;
|
||||||
|
entry->prior = NULL;
|
||||||
|
ipfs_bitswap_peer_request_free(entry->current);
|
||||||
|
entry->current = NULL;
|
||||||
|
free(entry);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Add a block to the appropriate peer's queue
|
||||||
|
* @param queue the queue
|
||||||
|
* @param who the session context that identifies the peer
|
||||||
|
* @param block the block
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_peer_request_queue_fill(struct PeerRequestQueue* queue, struct Libp2pPeer* who, struct Block* block) {
|
||||||
|
// find the right entry
|
||||||
|
struct PeerRequest* entry = ipfs_peer_request_queue_find_peer(queue, who);
|
||||||
|
if (entry != NULL)
|
||||||
|
{
|
||||||
|
// add to the block array
|
||||||
|
libp2p_utils_vector_add(entry->blocks_we_want_to_send, block);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/****
|
||||||
|
* Find blocks they want, and put them in the request
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_peer_request_get_blocks_they_want(const struct BitswapContext* context, struct PeerRequest* request) {
|
||||||
|
for(int i = 0; i < request->cids_they_want->total; i++) {
|
||||||
|
struct CidEntry* cidEntry = (struct CidEntry*)libp2p_utils_vector_get(request->cids_they_want, i);
|
||||||
|
if (cidEntry != NULL && !cidEntry->cancel) {
|
||||||
|
struct Block* block = NULL;
|
||||||
|
context->ipfsNode->blockstore->Get(context->ipfsNode->blockstore->blockstoreContext, cidEntry->cid, &block);
|
||||||
|
if (block != NULL) {
|
||||||
|
libp2p_utils_vector_add(request->blocks_we_want_to_send, block);
|
||||||
|
cidEntry->cancel = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Determine if we have anything we want (that we haven't sent already)
|
||||||
|
* @param cid_entries the list of CidEntries that are in our queue to be sent
|
||||||
|
* @returns true(1) if we have something to send, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_peer_request_we_want_cids(struct Libp2pVector* cid_entries) {
|
||||||
|
if (cid_entries == NULL)
|
||||||
|
return 0;
|
||||||
|
if (cid_entries->total == 0)
|
||||||
|
return 0;
|
||||||
|
for(int i = 0; i < cid_entries->total; i++) {
|
||||||
|
const struct CidEntry* entry = (const struct CidEntry*) libp2p_utils_vector_get(cid_entries, i);
|
||||||
|
if (entry->cancel && !entry->cancel_has_been_sent)
|
||||||
|
return 1;
|
||||||
|
if (!entry->cancel && !entry->request_has_been_sent)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/****
|
||||||
|
* Handle a PeerRequest
|
||||||
|
* @param context the BitswapContext
|
||||||
|
* @param request the request to process
|
||||||
|
* @returns true(1) if something was done, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_peer_request_process_entry(const struct BitswapContext* context, struct PeerRequest* request) {
|
||||||
|
// determine if we have enough information to continue
|
||||||
|
if (request == NULL)
|
||||||
|
return 0;
|
||||||
|
if (request->peer == NULL)
|
||||||
|
return 0;
|
||||||
|
if (!request->peer->is_local) {
|
||||||
|
if (request->peer->connection_type != CONNECTION_TYPE_CONNECTED)
|
||||||
|
if (request->peer->addr_head == NULL || request->peer->addr_head->item == NULL)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// determine if we're connected
|
||||||
|
int connected = request->peer->is_local || request->peer->connection_type == CONNECTION_TYPE_CONNECTED;
|
||||||
|
int need_to_connect = ipfs_bitswap_peer_request_we_want_cids(request->cids_we_want) || ipfs_bitswap_peer_request_cids_waiting(request->cids_they_want) || request->blocks_we_want_to_send->total != 0;
|
||||||
|
|
||||||
|
// determine if we need to connect
|
||||||
|
if (need_to_connect) {
|
||||||
|
if (!connected) {
|
||||||
|
// connect
|
||||||
|
connected = libp2p_peer_connect(context->ipfsNode->dialer, request->peer, context->ipfsNode->peerstore, context->ipfsNode->repo->config->datastore, 0);
|
||||||
|
}
|
||||||
|
if (connected) {
|
||||||
|
// build a message
|
||||||
|
struct BitswapMessage* msg = ipfs_bitswap_message_new();
|
||||||
|
// see if we can fulfill any of their requests. If so, fill in msg->payload
|
||||||
|
ipfs_bitswap_peer_request_get_blocks_they_want(context, request);
|
||||||
|
ipfs_bitswap_message_add_blocks(msg, request->blocks_we_want_to_send, request->cids_they_want);
|
||||||
|
// add requests that we would like
|
||||||
|
ipfs_bitswap_message_add_wantlist_items(msg, request->cids_we_want);
|
||||||
|
// send message
|
||||||
|
if (ipfs_bitswap_network_send_message(context, request->peer, msg)) {
|
||||||
|
ipfs_bitswap_message_free(msg);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
ipfs_bitswap_message_free(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Find a PeerRequest related to a peer. If one is not found, it is created.
|
||||||
|
*
|
||||||
|
* @param peer_request_queue the queue to look through
|
||||||
|
* @param peer the peer to look for
|
||||||
|
* @returns a PeerRequestEntry or NULL on error
|
||||||
|
*/
|
||||||
|
struct PeerRequest* ipfs_peer_request_queue_find_peer(struct PeerRequestQueue* queue, struct Libp2pPeer* peer) {
|
||||||
|
|
||||||
|
struct PeerRequestEntry* entry = queue->first;
|
||||||
|
while (entry != NULL) {
|
||||||
|
if (libp2p_peer_compare(entry->current->peer, peer) == 0) {
|
||||||
|
return entry->current;
|
||||||
|
}
|
||||||
|
entry = entry->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we didn't find one, so create one
|
||||||
|
entry = ipfs_bitswap_peer_request_entry_new();
|
||||||
|
entry->current = ipfs_bitswap_peer_request_new();
|
||||||
|
entry->current->peer = peer;
|
||||||
|
// attach it to the queue
|
||||||
|
if (queue->first == NULL) {
|
||||||
|
queue->first = entry;
|
||||||
|
queue->last = entry;
|
||||||
|
} else {
|
||||||
|
queue->last->next = entry;
|
||||||
|
entry->prior = queue->last;
|
||||||
|
queue->last = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry->current;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
66
exchange/bitswap/want_manager.c
Normal file
66
exchange/bitswap/want_manager.c
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include "ipfs/exchange/bitswap/want_manager.h"
|
||||||
|
#include "ipfs/exchange/bitswap/wantlist_queue.h"
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Add a Cid to the wantlist
|
||||||
|
* @param context the context
|
||||||
|
* @param cid the Cid
|
||||||
|
* @returns the added entry
|
||||||
|
*/
|
||||||
|
struct WantListQueueEntry* ipfs_bitswap_want_manager_add(const struct BitswapContext* context, const struct Cid* cid, const struct WantListSession* session) {
|
||||||
|
// add if not there, and increment reference count
|
||||||
|
return ipfs_bitswap_wantlist_queue_add(context->localWantlist, cid, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Checks to see if the requested block has been received
|
||||||
|
* @param context the context
|
||||||
|
* @param cid the Cid
|
||||||
|
* @returns true(1) if it has been received, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_want_manager_received(const struct BitswapContext* context, const struct Cid* cid) {
|
||||||
|
// find the entry
|
||||||
|
struct WantListQueueEntry* entry = ipfs_bitswap_wantlist_queue_find(context->localWantlist, cid);
|
||||||
|
// check the status
|
||||||
|
if (entry != NULL && entry->block != NULL) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* retrieve a block from the WantManager. NOTE: a call to want_manager_received should be done first
|
||||||
|
* @param context the context
|
||||||
|
* @param cid the Cid to get
|
||||||
|
* @param block a pointer to the block that will be allocated
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_want_manager_get_block(const struct BitswapContext* context, const struct Cid* cid, struct Block** block) {
|
||||||
|
struct WantListQueueEntry* entry = ipfs_bitswap_wantlist_queue_find(context->localWantlist, cid);
|
||||||
|
if (entry != NULL && entry->block != NULL) {
|
||||||
|
// return a copy of the block
|
||||||
|
*block = ipfs_block_copy(entry->block);
|
||||||
|
if ( (*block) != NULL) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* We no longer are requesting this block, so remove it from the queue
|
||||||
|
* NOTE: This is reference counted, as another process may have asked for it.
|
||||||
|
* @param context the context
|
||||||
|
* @param cid the Cid
|
||||||
|
* @returns true(1) on success, false(0) otherwise.
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_want_manager_remove(const struct BitswapContext* context, const struct Cid* cid) {
|
||||||
|
// decrement the reference count
|
||||||
|
// if it is zero, remove the entry (lock first)
|
||||||
|
struct WantListSession session;
|
||||||
|
session.type = WANTLIST_SESSION_TYPE_LOCAL;
|
||||||
|
session.context = (void*) context->ipfsNode;
|
||||||
|
return ipfs_bitswap_wantlist_queue_remove(context->localWantlist, cid, &session);
|
||||||
|
}
|
335
exchange/bitswap/wantlist_queue.c
Normal file
335
exchange/bitswap/wantlist_queue.c
Normal file
|
@ -0,0 +1,335 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "libp2p/conn/session.h"
|
||||||
|
#include "libp2p/utils/vector.h"
|
||||||
|
#include "ipfs/exchange/bitswap/wantlist_queue.h"
|
||||||
|
#include "ipfs/exchange/bitswap/peer_request_queue.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of the WantlistQueue
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remove this session from the lists of sessions that are looking for this WantListQueueEntry
|
||||||
|
* @param entry the entry
|
||||||
|
* @param session who was looking for it
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_wantlist_queue_entry_decrement(struct WantListQueueEntry* entry, const struct WantListSession* session) {
|
||||||
|
for(size_t i = 0; i < entry->sessionsRequesting->total; i++) {
|
||||||
|
const struct WantListSession* current = (const struct WantListSession*)libp2p_utils_vector_get(entry->sessionsRequesting, i);
|
||||||
|
if (ipfs_bitswap_wantlist_session_compare(session, current) == 0) {
|
||||||
|
libp2p_utils_vector_delete(entry->sessionsRequesting, i);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Initialize a new Wantlist (there should only be 1 per instance)
|
||||||
|
* @returns a new WantList
|
||||||
|
*/
|
||||||
|
struct WantListQueue* ipfs_bitswap_wantlist_queue_new() {
|
||||||
|
struct WantListQueue* wantlist = (struct WantListQueue*) malloc(sizeof(struct WantListQueue));
|
||||||
|
if (wantlist != NULL) {
|
||||||
|
pthread_mutex_init(&wantlist->wantlist_mutex, NULL);
|
||||||
|
wantlist->queue = NULL;
|
||||||
|
}
|
||||||
|
return wantlist;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Deallocate resources of a WantList
|
||||||
|
* @param wantlist the WantList
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_wantlist_queue_free(struct WantListQueue* wantlist) {
|
||||||
|
if (wantlist != NULL) {
|
||||||
|
if (wantlist->queue != NULL) {
|
||||||
|
for(int i = 0; i < wantlist->queue->total; i++) {
|
||||||
|
struct WantListQueueEntry* entry = (struct WantListQueueEntry*)libp2p_utils_vector_get(wantlist->queue, i);
|
||||||
|
ipfs_bitswap_wantlist_queue_entry_free(entry);
|
||||||
|
}
|
||||||
|
libp2p_utils_vector_free(wantlist->queue);
|
||||||
|
wantlist->queue = NULL;
|
||||||
|
}
|
||||||
|
free(wantlist);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Add a Cid to the WantList
|
||||||
|
* @param wantlist the WantList to add to
|
||||||
|
* @param cid the Cid to add
|
||||||
|
* @returns the correct WantListEntry or NULL if error
|
||||||
|
*/
|
||||||
|
struct WantListQueueEntry* ipfs_bitswap_wantlist_queue_add(struct WantListQueue* wantlist, const struct Cid* cid, const struct WantListSession* session) {
|
||||||
|
struct WantListQueueEntry* entry = NULL;
|
||||||
|
if (wantlist != NULL) {
|
||||||
|
pthread_mutex_lock(&wantlist->wantlist_mutex);
|
||||||
|
if (wantlist->queue == NULL) {
|
||||||
|
wantlist->queue = libp2p_utils_vector_new(1);
|
||||||
|
}
|
||||||
|
entry = ipfs_bitswap_wantlist_queue_find(wantlist, cid);
|
||||||
|
if (entry == NULL) {
|
||||||
|
// create a new one
|
||||||
|
entry = ipfs_bitswap_wantlist_queue_entry_new();
|
||||||
|
entry->cid = ipfs_cid_copy(cid);
|
||||||
|
entry->priority = 1;
|
||||||
|
libp2p_utils_vector_add(entry->sessionsRequesting, session);
|
||||||
|
libp2p_utils_vector_add(wantlist->queue, entry);
|
||||||
|
}
|
||||||
|
libp2p_utils_vector_add(entry->sessionsRequesting, session);
|
||||||
|
pthread_mutex_unlock(&wantlist->wantlist_mutex);
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Remove (decrement the counter) a Cid from the WantList
|
||||||
|
* @param wantlist the WantList
|
||||||
|
* @param cid the Cid
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_wantlist_queue_remove(struct WantListQueue* wantlist, const struct Cid* cid, const struct WantListSession* session) {
|
||||||
|
//TODO: remove if counter is <= 0
|
||||||
|
if (wantlist != NULL) {
|
||||||
|
struct WantListQueueEntry* entry = ipfs_bitswap_wantlist_queue_find(wantlist, cid);
|
||||||
|
if (entry != NULL) {
|
||||||
|
ipfs_bitswap_wantlist_queue_entry_decrement(entry, session);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Find a Cid in the WantList
|
||||||
|
* @param wantlist the list
|
||||||
|
* @param cid the Cid
|
||||||
|
* @returns the WantListQueueEntry
|
||||||
|
*/
|
||||||
|
struct WantListQueueEntry* ipfs_bitswap_wantlist_queue_find(struct WantListQueue* wantlist, const struct Cid* cid) {
|
||||||
|
for (size_t i = 0; i < wantlist->queue->total; i++) {
|
||||||
|
struct WantListQueueEntry* entry = (struct WantListQueueEntry*) libp2p_utils_vector_get(wantlist->queue, i);
|
||||||
|
if (entry == NULL) {
|
||||||
|
//TODO: something went wrong. This should be logged.
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (ipfs_cid_compare(cid, entry->cid) == 0) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Pops the top one off the queue
|
||||||
|
*
|
||||||
|
* @param wantlist the list
|
||||||
|
* @returns the WantListQueueEntry
|
||||||
|
*/
|
||||||
|
struct WantListQueueEntry* ipfs_bitswap_wantlist_queue_pop(struct WantListQueue* wantlist) {
|
||||||
|
struct WantListQueueEntry* entry = NULL;
|
||||||
|
|
||||||
|
if (wantlist == NULL || wantlist->queue == NULL || wantlist->queue->total == 0)
|
||||||
|
return entry;
|
||||||
|
|
||||||
|
//TODO: This should be a linked list, not an array
|
||||||
|
pthread_mutex_lock(&wantlist->wantlist_mutex);
|
||||||
|
for(int i = 0; i < wantlist->queue->total; i++) {
|
||||||
|
struct WantListQueueEntry* current = (struct WantListQueueEntry*)libp2p_utils_vector_get(wantlist->queue, i);
|
||||||
|
if (current->block == NULL && !current->asked_network) {
|
||||||
|
entry = current;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//libp2p_utils_vector_delete(wantlist->queue, 0);
|
||||||
|
pthread_mutex_unlock(&wantlist->wantlist_mutex);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Initialize a WantListQueueEntry
|
||||||
|
* @returns a new WantListQueueEntry
|
||||||
|
*/
|
||||||
|
struct WantListQueueEntry* ipfs_bitswap_wantlist_queue_entry_new() {
|
||||||
|
struct WantListQueueEntry* entry = (struct WantListQueueEntry*) malloc(sizeof(struct WantListQueueEntry));
|
||||||
|
if (entry != NULL) {
|
||||||
|
entry->sessionsRequesting = libp2p_utils_vector_new(1);
|
||||||
|
if (entry->sessionsRequesting == NULL) {
|
||||||
|
free(entry);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
entry->block = NULL;
|
||||||
|
entry->cid = NULL;
|
||||||
|
entry->priority = 0;
|
||||||
|
entry->attempts = 0;
|
||||||
|
entry->asked_network = 0;
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Free a WantListQueueENtry struct
|
||||||
|
* @param entry the struct
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_wantlist_queue_entry_free(struct WantListQueueEntry* entry) {
|
||||||
|
if (entry != NULL) {
|
||||||
|
if (entry->block != NULL) {
|
||||||
|
ipfs_block_free(entry->block);
|
||||||
|
entry->block = NULL;
|
||||||
|
}
|
||||||
|
if (entry->cid != NULL) {
|
||||||
|
ipfs_cid_free(entry->cid);
|
||||||
|
entry->cid = NULL;
|
||||||
|
}
|
||||||
|
if (entry->sessionsRequesting != NULL) {
|
||||||
|
libp2p_utils_vector_free(entry->sessionsRequesting);
|
||||||
|
entry->sessionsRequesting = NULL;
|
||||||
|
}
|
||||||
|
free(entry);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ipfs_bitswap_wantlist_session_compare(const struct WantListSession* a, const struct WantListSession* b) {
|
||||||
|
if (a == NULL && b == NULL)
|
||||||
|
return 0;
|
||||||
|
if (a == NULL && b != NULL)
|
||||||
|
return -1;
|
||||||
|
if (a != NULL && b == NULL)
|
||||||
|
return 1;
|
||||||
|
if (a->type != b->type)
|
||||||
|
return b->type - a->type;
|
||||||
|
if (a->type == WANTLIST_SESSION_TYPE_LOCAL) {
|
||||||
|
// it's local, there should be only 1
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
struct Libp2pPeer* contextA = (struct Libp2pPeer*)a->context;
|
||||||
|
struct Libp2pPeer* contextB = (struct Libp2pPeer*)b->context;
|
||||||
|
return libp2p_peer_compare(contextA, contextB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new WantListSession
|
||||||
|
* @returns the newly allocated WantListSession
|
||||||
|
*/
|
||||||
|
struct WantListSession* ipfs_bitswap_wantlist_session_new() {
|
||||||
|
struct WantListSession* ret = (struct WantListSession*) malloc(sizeof(struct WantListSession));
|
||||||
|
if (ret != NULL) {
|
||||||
|
ret->context = NULL;
|
||||||
|
ret->type = WANTLIST_SESSION_TYPE_LOCAL;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* determine if any of the sessions are referring to the local node
|
||||||
|
* @param sessions a vector of WantlistSession
|
||||||
|
* @returns true(1) if any of the sessions are local, false otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_wantlist_local_request(struct Libp2pVector* sessions)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < sessions->total; i++) {
|
||||||
|
struct WantListSession* curr = (struct WantListSession*) libp2p_utils_vector_get(sessions, i);
|
||||||
|
if (curr != NULL && curr->type == WANTLIST_SESSION_TYPE_LOCAL)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Attempt to retrieve a block from the local blockstore
|
||||||
|
*
|
||||||
|
* @param context the BitswapContext
|
||||||
|
* @param cid the id to look for
|
||||||
|
* @param block where to put the results
|
||||||
|
* @returns true(1) if found, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_wantlist_get_block_locally(struct BitswapContext* context, struct Cid* cid, struct Block** block) {
|
||||||
|
return context->ipfsNode->blockstore->Get(context->ipfsNode->blockstore->blockstoreContext, cid, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Retrieve a block. The only information we have is the cid
|
||||||
|
*
|
||||||
|
* This will ask the network for who has the file, using the router.
|
||||||
|
* It will then ask the specific nodes for the file. This method
|
||||||
|
* does not queue anything. It actually does the work. The remotes
|
||||||
|
* will queue the file, but we'll return before they respond.
|
||||||
|
*
|
||||||
|
* @param context the BitswapContext
|
||||||
|
* @param cid the id of the file
|
||||||
|
* @returns true(1) if we found some providers to ask, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_wantlist_get_block_remote(struct BitswapContext* context, struct Cid* cid) {
|
||||||
|
// find out who may have the file
|
||||||
|
struct Libp2pVector* providers = NULL;
|
||||||
|
if (context->ipfsNode->routing->FindProviders(context->ipfsNode->routing, cid->hash, cid->hash_length, &providers)) {
|
||||||
|
for(int i = 0; i < providers->total; i++) {
|
||||||
|
struct Libp2pPeer* current = (struct Libp2pPeer*) libp2p_utils_vector_get(providers, i);
|
||||||
|
// add this to their queue
|
||||||
|
struct PeerRequest* queueEntry = ipfs_peer_request_queue_find_peer(context->peerRequestQueue, current);
|
||||||
|
struct CidEntry* entry = ipfs_bitswap_peer_request_cid_entry_new();
|
||||||
|
entry->cid = ipfs_cid_copy(cid);
|
||||||
|
libp2p_utils_vector_add(queueEntry->cids_we_want, entry);
|
||||||
|
// process this queue via bitswap protocol
|
||||||
|
ipfs_bitswap_peer_request_process_entry(context, queueEntry);
|
||||||
|
//libp2p_peer_free(current);
|
||||||
|
}
|
||||||
|
libp2p_utils_vector_free(providers);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the Bitswap engine, this processes an item on the WantListQueue. This is called when
|
||||||
|
* we want a file locally from a remote source. Send a message immediately, adding in stuff that
|
||||||
|
* perhaps the remote source wanted.
|
||||||
|
*
|
||||||
|
* @param context the context
|
||||||
|
* @param entry the WantListQueueEntry
|
||||||
|
* @returns true(1) on success, false(0) if not.
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_wantlist_process_entry(struct BitswapContext* context, struct WantListQueueEntry* entry) {
|
||||||
|
int local_request = ipfs_bitswap_wantlist_local_request(entry->sessionsRequesting);
|
||||||
|
int have_local = ipfs_bitswap_wantlist_get_block_locally(context, entry->cid, &entry->block);
|
||||||
|
// should we go get it?
|
||||||
|
if (!local_request && !have_local) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (local_request && !have_local) {
|
||||||
|
if (!ipfs_bitswap_wantlist_get_block_remote(context, entry->cid)) {
|
||||||
|
// if we were unsuccessful in retrieving it, put it back in the queue?
|
||||||
|
// I don't think so. But I'm keeping this counter here until we have
|
||||||
|
// a final decision. Maybe lower the priority?
|
||||||
|
entry->attempts++;
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
entry->asked_network = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (entry->block != NULL) {
|
||||||
|
// okay we have the block.
|
||||||
|
// fulfill the requests
|
||||||
|
for(size_t i = 0; i < entry->sessionsRequesting->total; i++) {
|
||||||
|
// TODO: Review this code.
|
||||||
|
struct WantListSession* session = (struct WantListSession*) libp2p_utils_vector_get(entry->sessionsRequesting, i);
|
||||||
|
if (session->type == WANTLIST_SESSION_TYPE_LOCAL) {
|
||||||
|
//context->ipfsNode->exchange->HasBlock(context->ipfsNode->exchange, entry->block);
|
||||||
|
} else {
|
||||||
|
struct Libp2pPeer* peer = (struct Libp2pPeer*) session->context;
|
||||||
|
ipfs_bitswap_peer_request_queue_fill(context->peerRequestQueue, peer, entry->block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
CC = gcc
|
CC = gcc
|
||||||
|
|
||||||
CFLAGS = -O0 -I../include -I../../c-libp2p/include
|
CFLAGS = -O0 -I../include -I../c-libp2p/include -Wall -std=gnu99
|
||||||
|
|
||||||
ifdef DEBUG
|
ifdef DEBUG
|
||||||
CFLAGS += -g3
|
CFLAGS += -g3
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "ipfs/os/utils.h"
|
#include "libp2p/os/utils.h"
|
||||||
|
|
||||||
#define FLATFS_MAX_PREFIX_LENGTH 16
|
#define FLATFS_MAX_PREFIX_LENGTH 16
|
||||||
|
|
||||||
|
@ -50,8 +50,13 @@ int ipfs_flatfs_create_directory(const char* full_directory) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// it is not there, create it
|
// it is not there, create it
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
if (mkdir(full_directory) == -1)
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
if (mkdir(full_directory, S_IRWXU) == -1)
|
if (mkdir(full_directory, S_IRWXU) == -1)
|
||||||
return 0;
|
return 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
18
importer/Makefile
Normal file
18
importer/Makefile
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
CC = gcc
|
||||||
|
CFLAGS = -O0 -I../include -I../c-libp2p/include -I../c-libp2p/c-multihash/include -I../c-libp2p/c-multiaddr/include -I../c-libp2p/c-protobuf -Wall -std=gnu99
|
||||||
|
|
||||||
|
ifdef DEBUG
|
||||||
|
CFLAGS += -g3
|
||||||
|
endif
|
||||||
|
|
||||||
|
LFLAGS =
|
||||||
|
DEPS =
|
||||||
|
OBJS = importer.o exporter.o resolver.o
|
||||||
|
|
||||||
|
%.o: %.c $(DEPS)
|
||||||
|
$(CC) -c -o $@ $< $(CFLAGS)
|
||||||
|
|
||||||
|
all: $(OBJS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o
|
340
importer/exporter.c
Normal file
340
importer/exporter.c
Normal file
|
@ -0,0 +1,340 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include "ipfs/cid/cid.h"
|
||||||
|
#include "ipfs/core/http_request.h"
|
||||||
|
#include "ipfs/importer/exporter.h"
|
||||||
|
#include "ipfs/merkledag/merkledag.h"
|
||||||
|
#include "ipfs/merkledag/node.h"
|
||||||
|
#include "ipfs/repo/fsrepo/fs_repo.h"
|
||||||
|
#include "ipfs/repo/init.h"
|
||||||
|
#include "ipfs/core/ipfs_node.h"
|
||||||
|
#include "libp2p/utils/logger.h"
|
||||||
|
#include "ipfs/namesys/name.h"
|
||||||
|
#include "ipfs/repo/fsrepo/jsmn.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pull objects from ipfs
|
||||||
|
*/
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Helper method to retrieve a protobuf'd Node from the router
|
||||||
|
* @param local_node the context
|
||||||
|
* @param hash the hash to retrieve
|
||||||
|
* @param hash_size the length of the hash
|
||||||
|
* @param result a place to store the Node
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_exporter_get_node(struct IpfsNode* local_node, const unsigned char* hash, const size_t hash_size,
|
||||||
|
struct HashtableNode** result) {
|
||||||
|
unsigned char *buffer = NULL;
|
||||||
|
size_t buffer_size = 0;
|
||||||
|
int retVal = 0;
|
||||||
|
struct KademliaMessage* msg = NULL;
|
||||||
|
|
||||||
|
if (!local_node->routing->GetValue(local_node->routing, hash, hash_size, (void**)&buffer, &buffer_size)) {
|
||||||
|
libp2p_logger_debug("exporter", "get_node got no value. Returning false.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// unprotobuf
|
||||||
|
if (!ipfs_hashtable_node_protobuf_decode(buffer, buffer_size, result)) {
|
||||||
|
libp2p_logger_error("exporter", "Conversion to HashtableNode not successful\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy in the hash
|
||||||
|
(*result)->hash_size = hash_size;
|
||||||
|
(*result)->hash = malloc(hash_size);
|
||||||
|
if ( (*result)->hash == NULL) {
|
||||||
|
// memory issue
|
||||||
|
libp2p_logger_error("exporter", "get_node: Unable to allocate memory.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
memcpy((*result)->hash, hash, hash_size);
|
||||||
|
|
||||||
|
retVal = 1;
|
||||||
|
exit:
|
||||||
|
if (buffer != NULL)
|
||||||
|
free(buffer);
|
||||||
|
if (msg != NULL)
|
||||||
|
libp2p_message_free(msg);
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Get a file by its hash, and write the data to a filestream
|
||||||
|
* @param hash the base58 multihash of the cid
|
||||||
|
* @param file_descriptor where to write
|
||||||
|
* @param local_node the context
|
||||||
|
*/
|
||||||
|
int ipfs_exporter_to_filestream(const unsigned char* hash, FILE* file_descriptor, struct IpfsNode* local_node) {
|
||||||
|
|
||||||
|
// convert hash to cid
|
||||||
|
struct Cid* cid = NULL;
|
||||||
|
if ( ipfs_cid_decode_hash_from_base58(hash, strlen((char*)hash), &cid) == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find block
|
||||||
|
struct HashtableNode* read_node = NULL;
|
||||||
|
if (!ipfs_exporter_get_node(local_node, cid->hash, cid->hash_length, &read_node)) {
|
||||||
|
ipfs_cid_free(cid);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no longer need the cid
|
||||||
|
ipfs_cid_free(cid);
|
||||||
|
|
||||||
|
if (read_node->head_link == NULL) {
|
||||||
|
// convert the node's data into a UnixFS data block
|
||||||
|
struct UnixFS* unix_fs;
|
||||||
|
ipfs_unixfs_protobuf_decode(read_node->data, read_node->data_size, &unix_fs);
|
||||||
|
size_t bytes_written = fwrite(unix_fs->bytes, 1, unix_fs->bytes_size, file_descriptor);
|
||||||
|
if (bytes_written != unix_fs->bytes_size) {
|
||||||
|
ipfs_hashtable_node_free(read_node);
|
||||||
|
ipfs_unixfs_free(unix_fs);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ipfs_unixfs_free(unix_fs);
|
||||||
|
} else {
|
||||||
|
struct NodeLink* link = read_node->head_link;
|
||||||
|
struct HashtableNode* link_node = NULL;
|
||||||
|
while (link != NULL) {
|
||||||
|
if ( !ipfs_exporter_get_node(local_node, link->hash, link->hash_size, &link_node)) {
|
||||||
|
ipfs_hashtable_node_free(read_node);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
struct UnixFS* unix_fs;
|
||||||
|
ipfs_unixfs_protobuf_decode(link_node->data, link_node->data_size, &unix_fs);
|
||||||
|
size_t bytes_written = fwrite(unix_fs->bytes, 1, unix_fs->bytes_size, file_descriptor);
|
||||||
|
if (bytes_written != unix_fs->bytes_size) {
|
||||||
|
ipfs_hashtable_node_free(link_node);
|
||||||
|
ipfs_hashtable_node_free(read_node);
|
||||||
|
ipfs_unixfs_free(unix_fs);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ipfs_hashtable_node_free(link_node);
|
||||||
|
ipfs_unixfs_free(unix_fs);
|
||||||
|
link = link->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read_node != NULL)
|
||||||
|
ipfs_hashtable_node_free(read_node);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get a file by its hash, and write the data to a file
|
||||||
|
* @param hash the base58 multihash of the cid
|
||||||
|
* @param file_name the file name to write to
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_exporter_to_file(const unsigned char* hash, const char* file_name, struct IpfsNode *local_node) {
|
||||||
|
// process blocks
|
||||||
|
FILE* file = fopen(file_name, "wb");
|
||||||
|
if (file == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int retVal = ipfs_exporter_to_filestream(hash, file, local_node);
|
||||||
|
fclose(file);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get a file by its hash, and write the data to a file
|
||||||
|
* @param hash the base58 multihash of the cid
|
||||||
|
* @param file_name the file name to write to
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_exporter_to_console(const unsigned char* hash, struct IpfsNode *local_node) {
|
||||||
|
// convert hash to cid
|
||||||
|
struct Cid* cid = NULL;
|
||||||
|
if ( ipfs_cid_decode_hash_from_base58(hash, strlen((char*)hash), &cid) == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find block
|
||||||
|
struct HashtableNode* read_node = NULL;
|
||||||
|
if (!ipfs_exporter_get_node(local_node, cid->hash, cid->hash_length, &read_node)) {
|
||||||
|
ipfs_cid_free(cid);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no longer need the cid
|
||||||
|
ipfs_cid_free(cid);
|
||||||
|
|
||||||
|
// process blocks
|
||||||
|
struct NodeLink* link = read_node->head_link;
|
||||||
|
printf("{Links:[");
|
||||||
|
while (link != NULL) {
|
||||||
|
unsigned char b58[100];
|
||||||
|
ipfs_cid_hash_to_base58(link->hash, link->hash_size, b58, 100);
|
||||||
|
printf("{\"Name\":\"%s\",\"Hash\":\"%s\",\"Size\":%lu}", (link->name != NULL ? link->name : ""), (char*)b58, link->t_size);
|
||||||
|
link = link->next;
|
||||||
|
}
|
||||||
|
printf("],\"Data\":\"");
|
||||||
|
for(size_t i = 0LU; i < read_node->data_size; i++) {
|
||||||
|
printf("%02x", read_node->data[i]);
|
||||||
|
}
|
||||||
|
printf("\"}\n");
|
||||||
|
|
||||||
|
if (read_node != NULL)
|
||||||
|
ipfs_hashtable_node_free(read_node);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Called from the command line with ipfs object get [hash].
|
||||||
|
* Retrieves the object pointed to by hash, and displays
|
||||||
|
* its block data (links and data elements)
|
||||||
|
* @param argc number of arguments
|
||||||
|
* @param argv arguments
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_exporter_object_get(int argc, char** argv) {
|
||||||
|
char* repo_path = NULL;
|
||||||
|
|
||||||
|
if (!ipfs_repo_get_directory(argc, argv, &repo_path)) {
|
||||||
|
fprintf(stderr, "Unable to open repository: %s\n", repo_path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IpfsNode* local_node = NULL;
|
||||||
|
if (!ipfs_node_online_new(repo_path, &local_node))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// find hash
|
||||||
|
int retVal = ipfs_exporter_to_console((unsigned char*)argv[3], local_node);
|
||||||
|
|
||||||
|
ipfs_node_free(local_node);
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rebuild a file based on this HashtableNode, traversing links
|
||||||
|
* @param node the HashtableNode to start with
|
||||||
|
* @param local_node the context
|
||||||
|
* @param file the filestream to fill
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_exporter_cat_node(struct HashtableNode* node, struct IpfsNode* local_node, FILE *file) {
|
||||||
|
// process this node, then move on to the links
|
||||||
|
|
||||||
|
// build the unixfs
|
||||||
|
struct UnixFS* unix_fs;
|
||||||
|
if (!ipfs_unixfs_protobuf_decode(node->data, node->data_size, &unix_fs)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
for(size_t i = 0LU; i < unix_fs->bytes_size; i++) {
|
||||||
|
fprintf(file, "%c", unix_fs->bytes[i]);
|
||||||
|
}
|
||||||
|
ipfs_unixfs_free(unix_fs);
|
||||||
|
// process links
|
||||||
|
struct NodeLink* current = node->head_link;
|
||||||
|
while (current != NULL) {
|
||||||
|
// find the node
|
||||||
|
struct HashtableNode* child_node = NULL;
|
||||||
|
if (!ipfs_exporter_get_node(local_node, current->hash, current->hash_size, &child_node)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ipfs_exporter_cat_node(child_node, local_node, file);
|
||||||
|
ipfs_hashtable_node_free(child_node);
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ipfs_exporter_object_cat_to_file(struct IpfsNode *local_node, unsigned char* hash, int hash_size, FILE* file) {
|
||||||
|
struct HashtableNode* read_node = NULL;
|
||||||
|
|
||||||
|
// find block
|
||||||
|
if (!ipfs_exporter_get_node(local_node, hash, hash_size, &read_node)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int retVal = ipfs_exporter_cat_node(read_node, local_node, file);
|
||||||
|
ipfs_hashtable_node_free(read_node);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Called from the command line with ipfs cat [hash]. Retrieves the object
|
||||||
|
* pointed to by hash, and displays its raw block data to the console
|
||||||
|
* @param argc number of arguments
|
||||||
|
* @param argv arguments
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_exporter_object_cat(struct CliArguments* args, FILE* output_file) {
|
||||||
|
struct IpfsNode *local_node = NULL;
|
||||||
|
char* repo_dir = NULL;
|
||||||
|
|
||||||
|
if (!ipfs_repo_get_directory(args->argc, args->argv, &repo_dir)) {
|
||||||
|
libp2p_logger_error("exporter", "Unable to open repo: %s\n", repo_dir);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ipfs_node_offline_new(repo_dir, &local_node)) {
|
||||||
|
libp2p_logger_error("exporter", "Unable to create new offline node based on config at %s.\n", repo_dir);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (local_node->mode == MODE_API_AVAILABLE) {
|
||||||
|
const char ipns_prefix[] = "/ipns/";
|
||||||
|
const char ipfs_prefix[] = "/ipfs/";
|
||||||
|
char* hash = args->argv[args->verb_index + 1];
|
||||||
|
libp2p_logger_debug("exporter", "We're attempting to use the API for this object get of %s.\n", hash);
|
||||||
|
if (memcmp(hash, ipfs_prefix, sizeof(ipfs_prefix)-1) == 0) {
|
||||||
|
// skip ipfs_prefix;
|
||||||
|
hash += sizeof(ipfs_prefix)-1;
|
||||||
|
} else if (memcmp(hash, ipns_prefix, sizeof(ipns_prefix)-1) == 0) {
|
||||||
|
char *response = NULL;
|
||||||
|
size_t response_size;
|
||||||
|
if (ipfs_name_resolve(local_node, hash, &response, &response_size) && response && response_size > 0) {
|
||||||
|
hash = jsmn_simple_parser(response, response_size, "Path");
|
||||||
|
if (!hash) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct HttpRequest* request = ipfs_core_http_request_new();
|
||||||
|
char* response = NULL;
|
||||||
|
request->command = "object";
|
||||||
|
request->sub_command = "get";
|
||||||
|
request->arguments = libp2p_utils_vector_new(1);
|
||||||
|
libp2p_utils_vector_add(request->arguments, hash);
|
||||||
|
size_t response_size = 0;
|
||||||
|
int retVal = ipfs_core_http_request_post(local_node, request, &response, &response_size, "", 0);
|
||||||
|
if (response != NULL && response_size > 0) {
|
||||||
|
fwrite(response, 1, response_size, output_file);
|
||||||
|
free(response);
|
||||||
|
} else {
|
||||||
|
retVal = 0;
|
||||||
|
}
|
||||||
|
ipfs_core_http_request_free(request);
|
||||||
|
return retVal;
|
||||||
|
} else {
|
||||||
|
libp2p_logger_debug("exporter", "API not available, using direct access.\n");
|
||||||
|
struct Cid* cid = NULL;
|
||||||
|
if ( ipfs_cid_decode_hash_from_base58((unsigned char*)args->argv[args->verb_index+1], strlen(args->argv[args->verb_index+1]), &cid) == 0) {
|
||||||
|
libp2p_logger_error("exporter", "Unable to decode hash from base58 [%s]\n", args->argv[args->verb_index+1]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int retVal = ipfs_exporter_object_cat_to_file(local_node, cid->hash, cid->hash_length, output_file);
|
||||||
|
ipfs_cid_free(cid);
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
473
importer/importer.c
Normal file
473
importer/importer.c
Normal file
|
@ -0,0 +1,473 @@
|
||||||
|
// these two for strdup
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#define __USE_GNU
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include "ipfs/importer/importer.h"
|
||||||
|
#include "ipfs/merkledag/merkledag.h"
|
||||||
|
#include "libp2p/os/utils.h"
|
||||||
|
#include "ipfs/cmd/cli.h"
|
||||||
|
#include "ipfs/core/ipfs_node.h"
|
||||||
|
#include "ipfs/core/http_request.h"
|
||||||
|
#include "ipfs/repo/fsrepo/fs_repo.h"
|
||||||
|
#include "ipfs/repo/init.h"
|
||||||
|
#include "ipfs/unixfs/unixfs.h"
|
||||||
|
|
||||||
|
#define MAX_DATA_SIZE 262144 // 1024 * 256;
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Imports OS files into the datastore
|
||||||
|
*/
|
||||||
|
|
||||||
|
/***
|
||||||
|
* adds a blocksize to the UnixFS structure stored in the data
|
||||||
|
* element of a Node
|
||||||
|
* @param node the node to work with
|
||||||
|
* @param blocksize the blocksize to add
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_importer_add_filesize_to_data_section(struct HashtableNode* node, size_t bytes_read) {
|
||||||
|
// now add to the data section
|
||||||
|
struct UnixFS* data_section = NULL;
|
||||||
|
if (node->data == NULL) {
|
||||||
|
// nothing in data section yet, create new UnixFS
|
||||||
|
ipfs_unixfs_new(&data_section);
|
||||||
|
data_section->data_type = UNIXFS_FILE;
|
||||||
|
} else {
|
||||||
|
ipfs_unixfs_protobuf_decode(node->data, node->data_size, &data_section);
|
||||||
|
}
|
||||||
|
struct UnixFSBlockSizeNode bs;
|
||||||
|
bs.block_size = bytes_read;
|
||||||
|
ipfs_unixfs_add_blocksize(&bs, data_section);
|
||||||
|
data_section->file_size += bytes_read;
|
||||||
|
// put the new data back in the data section
|
||||||
|
size_t protobuf_size = ipfs_unixfs_protobuf_encode_size(data_section); //delay bytes_size entry
|
||||||
|
unsigned char protobuf[protobuf_size];
|
||||||
|
ipfs_unixfs_protobuf_encode(data_section, protobuf, protobuf_size, &protobuf_size);
|
||||||
|
ipfs_unixfs_free(data_section);
|
||||||
|
ipfs_hashtable_node_set_data(node, protobuf, protobuf_size);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* read the next chunk of bytes, create a node, and add a link to the node in the passed-in node
|
||||||
|
* @param file the file handle
|
||||||
|
* @param node the node to add to
|
||||||
|
* @returns number of bytes read
|
||||||
|
*/
|
||||||
|
size_t ipfs_import_chunk(FILE* file, struct HashtableNode* parent_node, struct FSRepo* fs_repo, size_t* total_size, size_t* bytes_written) {
|
||||||
|
unsigned char buffer[MAX_DATA_SIZE];
|
||||||
|
size_t bytes_read = fread(buffer, 1, MAX_DATA_SIZE, file);
|
||||||
|
|
||||||
|
// structs used by this method
|
||||||
|
struct UnixFS* new_unixfs = NULL;
|
||||||
|
struct HashtableNode* new_node = NULL;
|
||||||
|
struct NodeLink* new_link = NULL;
|
||||||
|
|
||||||
|
// put the file bits into a new UnixFS file
|
||||||
|
if (ipfs_unixfs_new(&new_unixfs) == 0)
|
||||||
|
return 0;
|
||||||
|
new_unixfs->data_type = UNIXFS_FILE;
|
||||||
|
new_unixfs->file_size = bytes_read;
|
||||||
|
if (ipfs_unixfs_add_data(&buffer[0], bytes_read, new_unixfs) == 0) {
|
||||||
|
ipfs_unixfs_free(new_unixfs);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// protobuf the UnixFS
|
||||||
|
size_t protobuf_size = ipfs_unixfs_protobuf_encode_size(new_unixfs);
|
||||||
|
if (protobuf_size == 0) {
|
||||||
|
ipfs_unixfs_free(new_unixfs);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
unsigned char protobuf[protobuf_size];
|
||||||
|
*bytes_written = 0;
|
||||||
|
if (ipfs_unixfs_protobuf_encode(new_unixfs, protobuf, protobuf_size, bytes_written) == 0) {
|
||||||
|
ipfs_unixfs_free(new_unixfs);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we're done with the UnixFS object
|
||||||
|
ipfs_unixfs_free(new_unixfs);
|
||||||
|
|
||||||
|
size_t size_of_node = 0;
|
||||||
|
|
||||||
|
// if there is more to read, create a new node.
|
||||||
|
if (bytes_read == MAX_DATA_SIZE) {
|
||||||
|
// create a new node
|
||||||
|
if (ipfs_hashtable_node_new_from_data(protobuf, *bytes_written, &new_node) == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// persist
|
||||||
|
size_t size_of_node = 0;
|
||||||
|
if (ipfs_merkledag_add(new_node, fs_repo, &size_of_node) == 0) {
|
||||||
|
ipfs_hashtable_node_free(new_node);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// put link in parent node
|
||||||
|
if (ipfs_node_link_create(NULL, new_node->hash, new_node->hash_size, &new_link) == 0) {
|
||||||
|
ipfs_hashtable_node_free(new_node);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
new_link->t_size = size_of_node;
|
||||||
|
*total_size += new_link->t_size;
|
||||||
|
// NOTE: disposal of this link object happens when the parent is disposed
|
||||||
|
if (ipfs_hashtable_node_add_link(parent_node, new_link) == 0) {
|
||||||
|
ipfs_hashtable_node_free(new_node);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ipfs_importer_add_filesize_to_data_section(parent_node, bytes_read);
|
||||||
|
ipfs_hashtable_node_free(new_node);
|
||||||
|
*bytes_written = size_of_node;
|
||||||
|
size_of_node = 0;
|
||||||
|
} else {
|
||||||
|
// if there are no existing links, put what we pulled from the file into parent_node
|
||||||
|
// otherwise, add it as a link
|
||||||
|
if (parent_node->head_link == NULL) {
|
||||||
|
ipfs_hashtable_node_set_data(parent_node, protobuf, *bytes_written);
|
||||||
|
} else {
|
||||||
|
// there are existing links. put the data in a new node, save it, then put the link in parent_node
|
||||||
|
// create a new node
|
||||||
|
if (ipfs_hashtable_node_new_from_data(protobuf, *bytes_written, &new_node) == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// persist
|
||||||
|
if (ipfs_merkledag_add(new_node, fs_repo, &size_of_node) == 0) {
|
||||||
|
ipfs_hashtable_node_free(new_node);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// put link in parent node
|
||||||
|
if (ipfs_node_link_create(NULL, new_node->hash, new_node->hash_size, &new_link) == 0) {
|
||||||
|
ipfs_hashtable_node_free(new_node);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
new_link->t_size = size_of_node;
|
||||||
|
*total_size += new_link->t_size;
|
||||||
|
// NOTE: disposal of this link object happens when the parent is disposed
|
||||||
|
if (ipfs_hashtable_node_add_link(parent_node, new_link) == 0) {
|
||||||
|
ipfs_hashtable_node_free(new_node);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ipfs_importer_add_filesize_to_data_section(parent_node, bytes_read);
|
||||||
|
ipfs_hashtable_node_free(new_node);
|
||||||
|
}
|
||||||
|
// persist the main node
|
||||||
|
ipfs_merkledag_add(parent_node, fs_repo, bytes_written);
|
||||||
|
*bytes_written += size_of_node;
|
||||||
|
} // add to parent vs add as link
|
||||||
|
|
||||||
|
return bytes_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints to the console the results of a node import
|
||||||
|
* @param node the node imported
|
||||||
|
* @param file_name the name of the file
|
||||||
|
* @returns true(1) if successful, false(0) if couldn't generate the MultiHash to be displayed
|
||||||
|
*/
|
||||||
|
int ipfs_import_print_node_results(const struct HashtableNode* node, const char* file_name) {
|
||||||
|
// give some results to the user
|
||||||
|
//TODO: if directory_entry is itself a directory, traverse and report files
|
||||||
|
int buffer_len = 100;
|
||||||
|
unsigned char buffer[buffer_len];
|
||||||
|
if (ipfs_cid_hash_to_base58(node->hash, node->hash_size, buffer, buffer_len) == 0) {
|
||||||
|
printf("Unable to generate hash for file %s.\n", file_name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
printf("added %s %s\n", buffer, file_name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a node based on an incoming file or directory
|
||||||
|
* NOTE: this can be called recursively for directories
|
||||||
|
* NOTE: When this function completes, parent_node will be either:
|
||||||
|
* 1) the complete file, in the case of a small file (<256k-ish)
|
||||||
|
* 2) a node with links to the various pieces of a large file
|
||||||
|
* 3) a node with links to files and directories if 'fileName' is a directory
|
||||||
|
* @param root_dir the directory for where to look for the file
|
||||||
|
* @param file_name the file (or directory) to import
|
||||||
|
* @param parent_node the root node (has links to others in case this is a large file and is split)
|
||||||
|
* @param fs_repo the ipfs repository
|
||||||
|
* @param bytes_written number of bytes written to disk
|
||||||
|
* @param recursive true if we should navigate directories
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_import_file(const char* root_dir, const char* fileName, struct HashtableNode** parent_node, struct IpfsNode* local_node, size_t* bytes_written, int recursive) {
|
||||||
|
/**
|
||||||
|
* NOTE: When this function completes, parent_node will be either:
|
||||||
|
* 1) the complete file, in the case of a small file (<256k-ish)
|
||||||
|
* 2) a node with links to the various pieces of a large file
|
||||||
|
* 3) a node with links to files and directories if 'fileName' is a directory
|
||||||
|
*/
|
||||||
|
int retVal = 1;
|
||||||
|
int bytes_read = MAX_DATA_SIZE;
|
||||||
|
size_t total_size = 0;
|
||||||
|
|
||||||
|
if (os_utils_is_directory(fileName)) {
|
||||||
|
// calculate the new root_dir
|
||||||
|
char* new_root_dir = (char*)root_dir;
|
||||||
|
char* path = NULL;
|
||||||
|
char* file = NULL;
|
||||||
|
os_utils_split_filename(fileName, &path, &file);
|
||||||
|
if (root_dir == NULL) {
|
||||||
|
new_root_dir = file;
|
||||||
|
} else {
|
||||||
|
free(path);
|
||||||
|
path = malloc(strlen(root_dir) + strlen(file) + 2);
|
||||||
|
if (path == NULL) {
|
||||||
|
// memory issue
|
||||||
|
if (file != NULL)
|
||||||
|
free(file);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
os_utils_filepath_join(root_dir, file, path, strlen(root_dir) + strlen(file) + 2);
|
||||||
|
new_root_dir = path;
|
||||||
|
}
|
||||||
|
// initialize parent_node as a directory
|
||||||
|
if (ipfs_hashtable_node_create_directory(parent_node) == 0) {
|
||||||
|
if (path != NULL)
|
||||||
|
free(path);
|
||||||
|
if (file != NULL)
|
||||||
|
free(file);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// get list of files
|
||||||
|
struct FileList* first = os_utils_list_directory(fileName);
|
||||||
|
struct FileList* next = first;
|
||||||
|
if (recursive) {
|
||||||
|
while (next != NULL) {
|
||||||
|
// process each file. NOTE: could be an embedded directory
|
||||||
|
*bytes_written = 0;
|
||||||
|
struct HashtableNode* file_node;
|
||||||
|
// put the filename together from fileName, which is the directory, and next->file_name
|
||||||
|
// which is a file (or a directory) within the directory we just found.
|
||||||
|
size_t filename_len = strlen(fileName) + strlen(next->file_name) + 2;
|
||||||
|
char full_file_name[filename_len];
|
||||||
|
os_utils_filepath_join(fileName, next->file_name, full_file_name, filename_len);
|
||||||
|
// adjust root directory
|
||||||
|
|
||||||
|
if (ipfs_import_file(new_root_dir, full_file_name, &file_node, local_node, bytes_written, recursive) == 0) {
|
||||||
|
ipfs_hashtable_node_free(*parent_node);
|
||||||
|
os_utils_free_file_list(first);
|
||||||
|
if (file != NULL)
|
||||||
|
free(file);
|
||||||
|
if (path != NULL)
|
||||||
|
free (path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// TODO: probably need to display what was imported
|
||||||
|
int len = strlen(next->file_name) + strlen(new_root_dir) + 2;
|
||||||
|
char full_path[len];
|
||||||
|
os_utils_filepath_join(new_root_dir, next->file_name, full_path, len);
|
||||||
|
ipfs_import_print_node_results(file_node, full_path);
|
||||||
|
// TODO: Determine what needs to be done if this file_node is a file, a split file, or a directory
|
||||||
|
// Create link from file_node
|
||||||
|
struct NodeLink* file_node_link;
|
||||||
|
ipfs_node_link_create(next->file_name, file_node->hash, file_node->hash_size, &file_node_link);
|
||||||
|
file_node_link->t_size = *bytes_written;
|
||||||
|
// add file_node as link to parent_node
|
||||||
|
ipfs_hashtable_node_add_link(*parent_node, file_node_link);
|
||||||
|
// clean up file_node
|
||||||
|
ipfs_hashtable_node_free(file_node);
|
||||||
|
// move to next file in list
|
||||||
|
next = next->next;
|
||||||
|
} // while going through files
|
||||||
|
}
|
||||||
|
// save the parent_node (the directory)
|
||||||
|
size_t bytes_written;
|
||||||
|
ipfs_merkledag_add(*parent_node, local_node->repo, &bytes_written);
|
||||||
|
if (file != NULL)
|
||||||
|
free(file);
|
||||||
|
if (path != NULL)
|
||||||
|
free (path);
|
||||||
|
os_utils_free_file_list(first);
|
||||||
|
} else {
|
||||||
|
// process this file
|
||||||
|
FILE* file = fopen(fileName, "rb");
|
||||||
|
if (file == 0)
|
||||||
|
return 0;
|
||||||
|
retVal = ipfs_hashtable_node_new(parent_node);
|
||||||
|
if (retVal == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add all nodes (will be called multiple times for large files)
|
||||||
|
while ( bytes_read == MAX_DATA_SIZE) {
|
||||||
|
size_t written = 0;
|
||||||
|
bytes_read = ipfs_import_chunk(file, *parent_node, local_node->repo, &total_size, &written);
|
||||||
|
*bytes_written += written;
|
||||||
|
}
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
// notify the network
|
||||||
|
struct HashtableNode *htn = *parent_node;
|
||||||
|
local_node->routing->Provide(local_node->routing, htn->hash, htn->hash_size);
|
||||||
|
// notify the network of the subnodes too
|
||||||
|
struct NodeLink *nl = htn->head_link;
|
||||||
|
while (nl != NULL) {
|
||||||
|
local_node->routing->Provide(local_node->routing, nl->hash, nl->hash_size);
|
||||||
|
nl = nl->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pulls list of files from command line parameters
|
||||||
|
* @param argc number of command line parameters
|
||||||
|
* @param argv command line parameters
|
||||||
|
* @returns a FileList linked list of filenames
|
||||||
|
*/
|
||||||
|
struct FileList* ipfs_import_get_filelist(struct CliArguments* args) {
|
||||||
|
struct FileList* first = NULL;
|
||||||
|
struct FileList* last = NULL;
|
||||||
|
|
||||||
|
for (int i = args->verb_index + 1; i < args->argc; i++) {
|
||||||
|
if (strcmp(args->argv[i], "add") == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
struct FileList* current = (struct FileList*)malloc(sizeof(struct FileList));
|
||||||
|
if (current == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
current->next = NULL;
|
||||||
|
current->file_name = args->argv[i];
|
||||||
|
// now wire it in
|
||||||
|
if (first == NULL) {
|
||||||
|
first = current;
|
||||||
|
}
|
||||||
|
if (last != NULL) {
|
||||||
|
last->next = current;
|
||||||
|
}
|
||||||
|
// now set last to current
|
||||||
|
last = current;
|
||||||
|
}
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See if the recursive flag was passed on the command line
|
||||||
|
* @param argc number of command line parameters
|
||||||
|
* @param argv command line parameters
|
||||||
|
* @returns true(1) if -r was passed, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_import_is_recursive(int argc, char** argv) {
|
||||||
|
for(int i = 0; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "-r") == 0)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* called from the command line to import multiple files or directories
|
||||||
|
* @param argc the number of arguments
|
||||||
|
* @param argv the arguments
|
||||||
|
*/
|
||||||
|
int ipfs_import_files(struct CliArguments* args) {
|
||||||
|
/*
|
||||||
|
* Param 0: ipfs
|
||||||
|
* param 1: add
|
||||||
|
* param 2: -r (optional)
|
||||||
|
* param 3: directoryname
|
||||||
|
*/
|
||||||
|
struct IpfsNode* local_node = NULL;
|
||||||
|
char* repo_path = NULL;
|
||||||
|
int retVal = 0;
|
||||||
|
struct FileList* first = NULL;
|
||||||
|
struct FileList* current = NULL;
|
||||||
|
char* path = NULL;
|
||||||
|
char* filename = NULL;
|
||||||
|
struct HashtableNode* directory_entry = NULL;
|
||||||
|
|
||||||
|
int recursive = ipfs_import_is_recursive(args->argc, args->argv);
|
||||||
|
|
||||||
|
// parse the command line
|
||||||
|
first = ipfs_import_get_filelist(args);
|
||||||
|
|
||||||
|
// open the repo
|
||||||
|
if (!ipfs_repo_get_directory(args->argc, args->argv, &repo_path)) {
|
||||||
|
fprintf(stderr, "Repo does not exist: %s\n", repo_path);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
ipfs_node_offline_new(repo_path, &local_node);
|
||||||
|
|
||||||
|
/** disabling for the time being
|
||||||
|
if (local_node->mode == MODE_API_AVAILABLE) {
|
||||||
|
// do this through the API
|
||||||
|
struct HttpRequest* request = ipfs_core_http_request_new();
|
||||||
|
request->command = "add";
|
||||||
|
struct HttpParam* recursive_param = ipfs_core_http_param_new();
|
||||||
|
recursive_param->name = strdup("recursive");
|
||||||
|
recursive_param->value = strdup((recursive ? "true" : "false"));
|
||||||
|
libp2p_utils_vector_add(request->params, recursive_param);
|
||||||
|
current = first;
|
||||||
|
while (current != NULL) {
|
||||||
|
libp2p_utils_vector_add(request->arguments, current->file_name);
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
uint8_t* result = NULL;
|
||||||
|
size_t result_size = 0;
|
||||||
|
if (!ipfs_core_http_request_post(local_node, request, &result, &result_size, data, data_size)) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
*/
|
||||||
|
// No daemon is running. Do this without using the API
|
||||||
|
// import the file(s)
|
||||||
|
current = first;
|
||||||
|
while (current != NULL) {
|
||||||
|
if (current->file_name[0] != '-') { // not a switch
|
||||||
|
os_utils_split_filename(current->file_name, &path, &filename);
|
||||||
|
size_t bytes_written = 0;
|
||||||
|
if (!ipfs_import_file(NULL, current->file_name, &directory_entry, local_node, &bytes_written, recursive))
|
||||||
|
goto exit;
|
||||||
|
ipfs_import_print_node_results(directory_entry, filename);
|
||||||
|
// cleanup
|
||||||
|
if (path != NULL) {
|
||||||
|
free(path);
|
||||||
|
path = NULL;
|
||||||
|
}
|
||||||
|
if (filename != NULL) {
|
||||||
|
free(filename);
|
||||||
|
filename = NULL;
|
||||||
|
}
|
||||||
|
if (directory_entry != NULL) {
|
||||||
|
ipfs_hashtable_node_free(directory_entry);
|
||||||
|
directory_entry = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
// } uncomment this line when the api is up and running with file transfer
|
||||||
|
|
||||||
|
retVal = 1;
|
||||||
|
exit:
|
||||||
|
if (local_node != NULL)
|
||||||
|
ipfs_node_free(local_node);
|
||||||
|
// free file list
|
||||||
|
current = first;
|
||||||
|
while (current != NULL) {
|
||||||
|
first = current->next;
|
||||||
|
free(current);
|
||||||
|
current = first;
|
||||||
|
}
|
||||||
|
if (path != NULL)
|
||||||
|
free(path);
|
||||||
|
if (filename != NULL)
|
||||||
|
free(filename);
|
||||||
|
if (directory_entry != NULL)
|
||||||
|
ipfs_hashtable_node_free(directory_entry);
|
||||||
|
//if (repo_path != NULL)
|
||||||
|
// free(repo_path);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
321
importer/resolver.c
Normal file
321
importer/resolver.c
Normal file
|
@ -0,0 +1,321 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "ipfs/importer/resolver.h"
|
||||||
|
#include "libp2p/utils/logger.h"
|
||||||
|
#include "libp2p/crypto/encoding/base58.h"
|
||||||
|
#include "libp2p/conn/session.h"
|
||||||
|
#include "libp2p/routing/dht_protocol.h"
|
||||||
|
#include "ipfs/merkledag/node.h"
|
||||||
|
#include "ipfs/merkledag/merkledag.h"
|
||||||
|
#include "ipfs/repo/fsrepo/fs_repo.h"
|
||||||
|
#include "libp2p/net/multistream.h"
|
||||||
|
#include "libp2p/record/message.h"
|
||||||
|
#include "multiaddr/multiaddr.h"
|
||||||
|
#include "libp2p/record/message.h"
|
||||||
|
#include "libp2p/conn/dialer.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return the next chunk of a path
|
||||||
|
* @param path the path
|
||||||
|
* @param next_part a pointer to a string NOTE: don't forget to free
|
||||||
|
* @returns true(1) on success, false(0) on error, or no more parts
|
||||||
|
*/
|
||||||
|
int ipfs_resolver_next_path(const char* path, char** next_part) {
|
||||||
|
for (int i = 0; i < strlen(path); i++) {
|
||||||
|
if (path[i] != '/') { // we have the next section
|
||||||
|
char* pos = strchr(&path[i+1], '/');
|
||||||
|
if (pos == NULL) {
|
||||||
|
*next_part = (char*)malloc(strlen(path) + 1);
|
||||||
|
if ( *next_part == NULL) {
|
||||||
|
// memory issue
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
strcpy(*next_part, path);
|
||||||
|
} else {
|
||||||
|
*next_part = (char*)malloc(pos - &path[i] + 1);
|
||||||
|
if (*next_part == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
strncpy(*next_part, &path[i], pos-&path[i]);
|
||||||
|
(*next_part)[pos-&path[i]] = 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove preceding slash and "/ipfs/" or "/ipns/" as well as the local multihash (if it is local)
|
||||||
|
* @param path the path from the command line
|
||||||
|
* @param fs_repo the local repo
|
||||||
|
* @returns the modified path
|
||||||
|
*/
|
||||||
|
const char* ipfs_resolver_remove_path_prefix(const char* path, const struct FSRepo* fs_repo) {
|
||||||
|
int pos = 0;
|
||||||
|
int first_non_slash = -1;
|
||||||
|
while(&path[pos] != NULL) {
|
||||||
|
if (path[pos] == '/') {
|
||||||
|
pos++;
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
if (first_non_slash == -1)
|
||||||
|
first_non_slash = pos;
|
||||||
|
if (pos == first_non_slash && (strncmp(&path[pos], "ipfs", 4) == 0 || strncmp(&path[pos], "ipns", 4) == 0) ) {
|
||||||
|
// ipfs or ipns should be up front. Otherwise, it could be part of the path
|
||||||
|
pos += 4;
|
||||||
|
} else if (strncmp(&path[pos], fs_repo->config->identity->peer->id, fs_repo->config->identity->peer->id_size) == 0) {
|
||||||
|
pos += fs_repo->config->identity->peer->id_size + 1; // the slash
|
||||||
|
} else {
|
||||||
|
return &path[pos];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if this path is a remote path
|
||||||
|
* @param path the path to examine
|
||||||
|
* @param fs_repo the local repo
|
||||||
|
* @returns true(1) if this path is a remote path
|
||||||
|
*/
|
||||||
|
int ipfs_resolver_is_remote(const char* path, const struct FSRepo* fs_repo) {
|
||||||
|
int pos = 0;
|
||||||
|
|
||||||
|
// skip the first slash
|
||||||
|
while (&path[pos] != NULL && path[pos] == '/') {
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
if (&path[pos] == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// skip the ipfs prefix
|
||||||
|
if (strncmp(&path[pos], "ipfs/", 5) == 0 || strncmp(&path[pos], "ipns/", 5) == 0) {
|
||||||
|
pos += 5; //the word plus the slash
|
||||||
|
} else
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// if this is a Qm code, see if it is a local Qm code
|
||||||
|
if (path[pos] == 'Q' && path[pos+1] == 'm') {
|
||||||
|
if (strncmp(&path[pos], fs_repo->config->identity->peer->id, fs_repo->config->identity->peer->id_size) != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a node from a remote source
|
||||||
|
* @param path the path to retrieve
|
||||||
|
* @param from where to start
|
||||||
|
* @param fs_repo the local repo
|
||||||
|
* @returns the node, or NULL if not found
|
||||||
|
*/
|
||||||
|
struct HashtableNode* ipfs_resolver_remote_get(const char* path, struct HashtableNode* from, const struct IpfsNode* ipfs_node) {
|
||||||
|
// parse the path
|
||||||
|
const char* temp = ipfs_resolver_remove_path_prefix(path, ipfs_node->repo);
|
||||||
|
if (temp == NULL)
|
||||||
|
return NULL;
|
||||||
|
char* pos = strchr(temp, '/');
|
||||||
|
if (pos == NULL || pos - temp > 254)
|
||||||
|
return NULL;
|
||||||
|
char id[255];
|
||||||
|
size_t id_size = pos - temp;
|
||||||
|
strncpy(id, temp, id_size);
|
||||||
|
id[id_size] = 0;
|
||||||
|
char* key = &pos[1];
|
||||||
|
pos = strchr(key, '/');
|
||||||
|
if (pos == NULL || pos - key > 254)
|
||||||
|
return NULL;
|
||||||
|
pos[0] = '\0';
|
||||||
|
// get the multiaddress for this
|
||||||
|
struct Libp2pPeer* peer = libp2p_peerstore_get_peer(ipfs_node->peerstore, (unsigned char*)id, id_size);
|
||||||
|
if (peer == NULL) {
|
||||||
|
//TODO: We don't have the peer address. Ask the swarm for the data related to the hash
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!libp2p_peer_connect(ipfs_node->dialer, peer, ipfs_node->peerstore, ipfs_node->repo->config->datastore, 10))
|
||||||
|
return NULL;
|
||||||
|
struct Stream* kademlia_stream = libp2p_conn_dialer_get_stream(ipfs_node->dialer, peer, "kademlia");
|
||||||
|
if (kademlia_stream == NULL)
|
||||||
|
return NULL;
|
||||||
|
// build the request
|
||||||
|
struct KademliaMessage* message = libp2p_message_new();
|
||||||
|
message->message_type = MESSAGE_TYPE_GET_VALUE;
|
||||||
|
message->key = key;
|
||||||
|
message->key_size = strlen(key);
|
||||||
|
size_t b58size = 100;
|
||||||
|
uint8_t *b58key = (uint8_t*) malloc(b58size);
|
||||||
|
if (b58key == NULL) {
|
||||||
|
libp2p_crypto_encoding_base58_encode((unsigned char*)message->key, message->key_size, (unsigned char**) &b58key, &b58size);
|
||||||
|
libp2p_logger_debug("resolver", "Attempting to use kademlia to get key %s.\n", b58key);
|
||||||
|
free(b58key);
|
||||||
|
}
|
||||||
|
size_t message_protobuf_size = libp2p_message_protobuf_encode_size(message);
|
||||||
|
unsigned char message_protobuf[message_protobuf_size];
|
||||||
|
libp2p_message_protobuf_encode(message, message_protobuf, message_protobuf_size, &message_protobuf_size);
|
||||||
|
libp2p_message_free(message);
|
||||||
|
struct StreamMessage outgoing;
|
||||||
|
outgoing.data = message_protobuf;
|
||||||
|
outgoing.data_size = message_protobuf_size;
|
||||||
|
kademlia_stream->write(kademlia_stream->stream_context, &outgoing);
|
||||||
|
struct StreamMessage* response;
|
||||||
|
// we should get back a protobuf'd record
|
||||||
|
kademlia_stream->read(kademlia_stream->stream_context, &response, 5);
|
||||||
|
if (response->data_size == 1)
|
||||||
|
return NULL;
|
||||||
|
// turn the protobuf into a Node
|
||||||
|
struct HashtableNode* node;
|
||||||
|
ipfs_hashtable_node_protobuf_decode(response->data, response->data_size, &node);
|
||||||
|
libp2p_stream_message_free(response);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interogate the path and the current node, looking
|
||||||
|
* for the desired node.
|
||||||
|
* @param path the current path
|
||||||
|
* @param from the current node (or NULL if it is the first call)
|
||||||
|
* @returns what we are looking for, or NULL if it wasn't found
|
||||||
|
*/
|
||||||
|
struct HashtableNode* ipfs_resolver_get(const char* path, struct HashtableNode* from, const struct IpfsNode* ipfs_node) {
|
||||||
|
|
||||||
|
struct FSRepo* fs_repo = ipfs_node->repo;
|
||||||
|
|
||||||
|
// shortcut for remote files
|
||||||
|
if (from == NULL && ipfs_resolver_is_remote(path, fs_repo)) {
|
||||||
|
return ipfs_resolver_remote_get(path, from, ipfs_node);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Memory management notes:
|
||||||
|
* If we find what we're looking for, we clean up "from" and return the object
|
||||||
|
* If we don't find what we're looking for, but we can continue the search, we clean up "from"
|
||||||
|
* If we don't find what we're looking for, and we cannot continue, we do not clean up "from"
|
||||||
|
*/
|
||||||
|
// remove unnecessary stuff
|
||||||
|
if (from == NULL)
|
||||||
|
path = ipfs_resolver_remove_path_prefix(path, fs_repo);
|
||||||
|
// grab the portion of the path to work with
|
||||||
|
char* path_section;
|
||||||
|
if (ipfs_resolver_next_path(path, &path_section) == 0)
|
||||||
|
return NULL;
|
||||||
|
struct HashtableNode* current_node = NULL;
|
||||||
|
if (from == NULL) {
|
||||||
|
// this is the first time around. Grab the root node
|
||||||
|
if (path_section[0] == 'Q' && path_section[1] == 'm') {
|
||||||
|
// we have a hash. Convert to a real hash, and find the node
|
||||||
|
size_t hash_length = libp2p_crypto_encoding_base58_decode_size(strlen(path_section));
|
||||||
|
unsigned char hash[hash_length];
|
||||||
|
unsigned char* ptr = &hash[0];
|
||||||
|
if (libp2p_crypto_encoding_base58_decode((unsigned char*)path_section, strlen(path_section), &ptr, &hash_length) == 0) {
|
||||||
|
free(path_section);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (ipfs_merkledag_get_by_multihash(hash, hash_length, ¤t_node, fs_repo) == 0) {
|
||||||
|
free(path_section);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
// we have the root node, now see if we want this or something further down
|
||||||
|
int pos = strlen(path_section);
|
||||||
|
if (pos == strlen(path)) {
|
||||||
|
free(path_section);
|
||||||
|
return current_node;
|
||||||
|
} else {
|
||||||
|
// look on...
|
||||||
|
free(path_section);
|
||||||
|
struct HashtableNode* newNode = ipfs_resolver_get(&path[pos+1], current_node, ipfs_node); // the +1 is the slash
|
||||||
|
return newNode;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// we don't have a current node, and we don't have a hash. Something is wrong
|
||||||
|
free(path_section);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// we were passed a node. If it is a directory, see if what we're looking for is in it
|
||||||
|
if (ipfs_hashtable_node_is_directory(from)) {
|
||||||
|
struct NodeLink* curr_link = from->head_link;
|
||||||
|
while (curr_link != NULL) {
|
||||||
|
// if it matches the name, we found what we're looking for.
|
||||||
|
// If so, load up the node by its hash
|
||||||
|
if (strcmp(curr_link->name, path_section) == 0) {
|
||||||
|
if (ipfs_merkledag_get(curr_link->hash, curr_link->hash_size, ¤t_node, fs_repo) == 0) {
|
||||||
|
free(path_section);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (strlen(path_section) == strlen(path)) {
|
||||||
|
// we are at the end of our search
|
||||||
|
ipfs_hashtable_node_free(from);
|
||||||
|
from = NULL;
|
||||||
|
free(path_section);
|
||||||
|
return current_node;
|
||||||
|
} else {
|
||||||
|
char* next_path_section;
|
||||||
|
ipfs_resolver_next_path(&path[strlen(path_section)], &next_path_section);
|
||||||
|
free(path_section);
|
||||||
|
// if we're at the end of the path, return the node
|
||||||
|
// continue looking for the next part of the path
|
||||||
|
ipfs_hashtable_node_free(from);
|
||||||
|
from = NULL;
|
||||||
|
struct HashtableNode* newNode = ipfs_resolver_get(next_path_section, current_node, ipfs_node);
|
||||||
|
return newNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
curr_link = curr_link->next;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// we're asking for a file from an object that is not a directory. Bail.
|
||||||
|
free(path_section);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// it should never get here
|
||||||
|
free(path_section);
|
||||||
|
if (from != NULL)
|
||||||
|
ipfs_hashtable_node_free(from);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interrogate the path, looking for the peer.
|
||||||
|
* NOTE: only called locally. Not for remote callers
|
||||||
|
* @param path the peer path to search for in the form like "/ipfs/QmKioji..."
|
||||||
|
* @param ipfs_node the context
|
||||||
|
* @returns a peer struct, or NULL if not found
|
||||||
|
*/
|
||||||
|
struct Libp2pPeer* ipfs_resolver_find_peer(const char* path, const struct IpfsNode* ipfs_node) {
|
||||||
|
struct FSRepo* fs_repo = ipfs_node->repo;
|
||||||
|
struct Libp2pLinkedList *addresses = NULL;
|
||||||
|
struct Libp2pPeer* peer = NULL;
|
||||||
|
|
||||||
|
// shortcut for if this node is the node we're looking for
|
||||||
|
if (!ipfs_resolver_is_remote(path, fs_repo)) {
|
||||||
|
// turn the string list into a multiaddress list
|
||||||
|
struct Libp2pLinkedList* current_list_string = fs_repo->config->addresses->swarm_head;
|
||||||
|
struct Libp2pLinkedList* current_list_ma = addresses;
|
||||||
|
while(current_list_string != NULL) {
|
||||||
|
struct Libp2pLinkedList* item = libp2p_utils_linked_list_new();
|
||||||
|
item->item = multiaddress_new_from_string(current_list_string->item);
|
||||||
|
if (addresses == NULL) {
|
||||||
|
addresses = item;
|
||||||
|
} else {
|
||||||
|
current_list_ma->next = item;
|
||||||
|
}
|
||||||
|
current_list_ma = item;
|
||||||
|
current_list_string = current_list_string->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ask the swarm for the peer
|
||||||
|
const char* address_string = ipfs_resolver_remove_path_prefix(path, fs_repo);
|
||||||
|
ipfs_node->routing->FindPeer(ipfs_node->routing, (const unsigned char*)address_string, strlen(address_string), &peer);
|
||||||
|
|
||||||
|
return peer;
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/***
|
/***
|
||||||
* IPFS has the notion of storage blocks.
|
* IPFS has the notion of storage blocks.
|
||||||
|
* Raw data with a multihash key (the Cid)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __IPFS_BLOCKS_BLOCK_H__
|
#ifndef __IPFS_BLOCKS_BLOCK_H__
|
||||||
|
@ -14,19 +15,51 @@ struct Block {
|
||||||
};
|
};
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Create a new block based on the incoming data.
|
* Create a new block
|
||||||
* @param data the data to base the block on
|
* @returns a new allocated Block struct
|
||||||
* @param data_size the length of the data array
|
|
||||||
* @param block a pointer to the struct Block that will be created
|
|
||||||
* @returns true(1) on success
|
|
||||||
*/
|
*/
|
||||||
int ipfs_blocks_block_new(const unsigned char* data, size_t data_size, struct Block** block);
|
struct Block* ipfs_block_new();
|
||||||
|
|
||||||
|
int ipfs_blocks_block_add_data(const unsigned char* data, size_t data_size, struct Block* block);
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Free resources used by the creation of a block
|
* Free resources used by the creation of a block
|
||||||
* @param block the block to free
|
* @param block the block to free
|
||||||
* @returns true(1) on success
|
* @returns true(1) on success
|
||||||
*/
|
*/
|
||||||
int ipfs_blocks_block_free(struct Block* block);
|
int ipfs_block_free(struct Block* block);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the approximate size of an encoded block
|
||||||
|
* @param block the block to measure
|
||||||
|
* @returns the approximate size needed to encode the protobuf
|
||||||
|
*/
|
||||||
|
size_t ipfs_blocks_block_protobuf_encode_size(const struct Block* block);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode the Block into protobuf format
|
||||||
|
* @param block the block to encode
|
||||||
|
* @param buffer the buffer to fill
|
||||||
|
* @param max_buffer_size the max size of the buffer
|
||||||
|
* @param bytes_written the number of bytes used
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_blocks_block_protobuf_encode(const struct Block* block, unsigned char* buffer, size_t max_buffer_size, size_t* bytes_written);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Decode from a protobuf stream into a Block struct
|
||||||
|
* @param buffer the buffer to pull from
|
||||||
|
* @param buffer_length the length of the buffer
|
||||||
|
* @param block the block to fill
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_blocks_block_protobuf_decode(const unsigned char* buffer, const size_t buffer_length, struct Block** block);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Make a copy of a block
|
||||||
|
* @param original the original
|
||||||
|
* @returns a new Block that is a copy
|
||||||
|
*/
|
||||||
|
struct Block* ipfs_block_copy(struct Block* original);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -3,36 +3,95 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __IPFS_BLOCKS_BLOCKSTORE_H__
|
#ifndef __IPFS_BLOCKS_BLOCKSTORE_H__
|
||||||
#ifndef __IPFS_BLOCKS_BLOCKSTORE_H__
|
#define __IPFS_BLOCKS_BLOCKSTORE_H__
|
||||||
|
|
||||||
|
#include "ipfs/cid/cid.h"
|
||||||
|
#include "ipfs/repo/fsrepo/fs_repo.h"
|
||||||
|
|
||||||
|
struct BlockstoreContext {
|
||||||
|
const struct FSRepo* fs_repo;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Blockstore {
|
||||||
|
struct BlockstoreContext* blockstoreContext;
|
||||||
|
int (*Delete)(const struct BlockstoreContext* context, struct Cid* cid);
|
||||||
|
int (*Has)(const struct BlockstoreContext* context, struct Cid* cid);
|
||||||
|
/**
|
||||||
|
* Retrieve a block from the blockstore
|
||||||
|
*/
|
||||||
|
int (*Get)(const struct BlockstoreContext* context, struct Cid* cid, struct Block** block);
|
||||||
|
int (*Put)(const struct BlockstoreContext* context, struct Block* block, size_t* bytes_written);
|
||||||
|
};
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Create a new Blockstore struct
|
||||||
|
* @param fs_repo the FSRepo to use
|
||||||
|
* @returns the new Blockstore struct, or NULL if there was a problem.
|
||||||
|
*/
|
||||||
|
struct Blockstore* ipfs_blockstore_new(const struct FSRepo* fs_repo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release resources of a Blockstore struct
|
||||||
|
* @param blockstore the struct to free
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_blockstore_free(struct Blockstore* blockstore);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a block based on its Cid
|
* Delete a block based on its Cid
|
||||||
|
* @param context the context
|
||||||
* @param cid the Cid to look for
|
* @param cid the Cid to look for
|
||||||
* @param returns true(1) on success
|
* @param returns true(1) on success
|
||||||
*/
|
*/
|
||||||
int ipfs_blockstore_delete(struct Cid* cid, struct FSRepo* fs_repo);
|
int ipfs_blockstore_delete(const struct BlockstoreContext* context, struct Cid* cid);
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Determine if the Cid can be found
|
* Determine if the Cid can be found
|
||||||
|
* @param context the context
|
||||||
* @param cid the Cid to look for
|
* @param cid the Cid to look for
|
||||||
* @returns true(1) if found
|
* @returns true(1) if found
|
||||||
*/
|
*/
|
||||||
int ipfs_blockstore_has(struct Cid* cid, struct FSRepo* fs_repo);
|
int ipfs_blockstore_has(const struct BlockstoreContext* context, struct Cid* cid);
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Find a block based on its Cid
|
* Find a block based on its Cid
|
||||||
|
* @param context the context
|
||||||
* @param cid the Cid to look for
|
* @param cid the Cid to look for
|
||||||
* @param block where to put the data to be returned
|
* @param block where to put the data to be returned
|
||||||
* @returns true(1) on success
|
* @returns true(1) on success
|
||||||
*/
|
*/
|
||||||
int ipfs_blockstore_get(struct Cid* cid, struct Block* block, struct FSRepo* fs_repo);
|
int ipfs_blockstore_get(const struct BlockstoreContext* context, struct Cid* cid, struct Block** block);
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Put a block in the blockstore
|
* Put a block in the blockstore
|
||||||
* @param block the block to store
|
* @param block the block to store
|
||||||
* @returns true(1) on success
|
* @returns true(1) on success
|
||||||
*/
|
*/
|
||||||
int ipfs_blockstore_put(struct Block* block, struct FSRepo* fs_repo);
|
int ipfs_blockstore_put(const struct BlockstoreContext* context, struct Block* block, size_t* bytes_written);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Put a struct UnixFS in the blockstore
|
||||||
|
* @param unix_fs the structure
|
||||||
|
* @param fs_repo the repo to place the strucure in
|
||||||
|
* @param bytes_written the number of bytes written to the blockstore
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_blockstore_put_unixfs(const struct UnixFS* unix_fs, const struct FSRepo* fs_repo, size_t* bytes_written);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Find a UnixFS struct based on its hash
|
||||||
|
* @param hash the hash to look for
|
||||||
|
* @param hash_length the length of the hash
|
||||||
|
* @param unix_fs the struct to fill
|
||||||
|
* @param fs_repo where to look for the data
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_blockstore_get_unixfs(const unsigned char* hash, size_t hash_length, struct UnixFS** block, const struct FSRepo* fs_repo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put a struct Node in the blockstore
|
||||||
|
*/
|
||||||
|
int ipfs_blockstore_put_node(const struct HashtableNode* node, const struct FSRepo* fs_repo, size_t* bytes_written);
|
||||||
|
int ipfs_blockstore_get_node(const unsigned char* hash, size_t hash_length, struct HashtableNode** node, const struct FSRepo* fs_repo);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -6,36 +6,82 @@
|
||||||
#define __IPFS_CID_CID_H
|
#define __IPFS_CID_CID_H
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include "protobuf.h"
|
||||||
|
|
||||||
#define CID_PROTOBUF 0x70
|
// these are multicodec packed content types. They should match
|
||||||
#define CID_CBOR 0x71
|
// the codes described in the authoratative document:
|
||||||
#define CID_RAW 0x72
|
// https://github.com/multiformats/multicodec/blob/master/table.csv
|
||||||
#define CID_JSON 0x73
|
#define CID_RAW 0x55
|
||||||
|
#define CID_DAG_PROTOBUF 0x70
|
||||||
|
#define CID_DAG_CBOR 0x71
|
||||||
|
#define CID_GIT_RAW 0x78
|
||||||
#define CID_ETHEREUM_BLOCK 0x90
|
#define CID_ETHEREUM_BLOCK 0x90
|
||||||
#define CID_ETHEREUM_TX 0x91
|
#define CID_ETHEREUM_BLOCKLIST 0x91
|
||||||
|
#define CID_ETHEREUM_TRIE 0x92
|
||||||
|
#define CID_ETHEREUM_TX 0x93
|
||||||
|
#define CID_ETHEREUM_TX_RECEIPT_TRIE 0x94
|
||||||
|
#define CID_ETHEREUM_TX_RECEIPT 0x95
|
||||||
|
#define CID_ETHEREUM_STATE_TRIE 0x96
|
||||||
|
#define CID_ETHEREUM_ACCOUNT_SNAPSHOT 0x97
|
||||||
|
#define CID_ETHEREUM_STORAGE_TRIE 0x98
|
||||||
#define CID_BITCOIN_BLOCK 0xb0
|
#define CID_BITCOIN_BLOCK 0xb0
|
||||||
#define CID_BITCOIN_TX 0xb1
|
#define CID_BITCOIN_TX 0xb1
|
||||||
#define CID_ZCASH_BLOCK 0xc0
|
#define CID_ZCASH_BLOCK 0xc0
|
||||||
#define CID_ZCASH_TX 0xc1
|
#define CID_ZCASH_TX 0xc1
|
||||||
|
|
||||||
|
/***
|
||||||
|
* A note about CID versions:
|
||||||
|
* Version 0 only contained the multihash address. The extra parameters of multibase,
|
||||||
|
* multicodec, cid-version were implied (base58btc, protobuf-mdag, and cidv0
|
||||||
|
* respectively) are implied.
|
||||||
|
*/
|
||||||
|
|
||||||
struct Cid {
|
struct Cid {
|
||||||
int version;
|
int version; // CID version
|
||||||
char codec;
|
int codec; // codec used (i.e. CID_RAW, CID_PROTOBUF
|
||||||
unsigned char* hash; // a multihash
|
unsigned char* hash; // the multihash
|
||||||
size_t hash_length;
|
size_t hash_length; // the length of hash
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CidSet {
|
||||||
|
struct Cid *cid;
|
||||||
|
struct CidSet *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
/***
|
||||||
|
* encode a Cid into a protobuf array of bytes
|
||||||
|
* @param incoming the incoming Cid struct
|
||||||
|
* @param buffer the buffer
|
||||||
|
* @param max_buffer_length the length of the buffer
|
||||||
|
* @param bytes_written the number of bytes written
|
||||||
|
*/
|
||||||
|
int ipfs_cid_protobuf_encode(const struct Cid* incoming, unsigned char* buffer, size_t max_buffer_length, size_t* bytes_written);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* decode an array of bytes into a Cid structure
|
||||||
|
* @param buffer the incming array of bytes
|
||||||
|
* @param buffer_length the length of the buffer
|
||||||
|
* @param output the Cid struct NOTE: all allocations are made by this function. Be sure to call free
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_cid_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct Cid** output);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Returns a safe estimate of the required buffer size to encode the Cid struct
|
||||||
|
* @param incoming the struct to encode
|
||||||
|
* @returns the number of approximate bytes
|
||||||
|
*/
|
||||||
|
size_t ipfs_cid_protobuf_encode_size(const struct Cid* incoming);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new CID based on the given hash
|
* Create a new CID based on the given hash
|
||||||
* @param version the version
|
* @param version the version
|
||||||
* @param hash the multihash
|
* @param hash the multihash
|
||||||
* @param hash_length the length of the multihash in bytes
|
* @param hash_length the length of the multihash in bytes
|
||||||
* @param codec the codec to be used (NOTE: For version 0, this should be CID_PROTOBUF)
|
* @param codec the codec to be used (NOTE: For version 0, this should be CID_DAG_PROTOBUF)
|
||||||
* @param cid where to put the results
|
* @returns the Cid, or NULL if there was a problem
|
||||||
* @returns true(1) on success
|
|
||||||
*/
|
*/
|
||||||
int ipfs_cid_new(int version, unsigned char* hash, size_t hash_length, const char codec, struct Cid** cid);
|
struct Cid* ipfs_cid_new(int version, const unsigned char* hash, size_t hash_length, const char codec);
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Free the resources from a Cid
|
* Free the resources from a Cid
|
||||||
|
@ -44,6 +90,13 @@ int ipfs_cid_new(int version, unsigned char* hash, size_t hash_length, const cha
|
||||||
*/
|
*/
|
||||||
int ipfs_cid_free(struct Cid* cid);
|
int ipfs_cid_free(struct Cid* cid);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Make a copy of a Cid
|
||||||
|
* @param original the original
|
||||||
|
* @returns a copy of the original
|
||||||
|
*/
|
||||||
|
struct Cid* ipfs_cid_copy(const struct Cid* original);
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Fill a Cid struct based on a base 58 encoded string
|
* Fill a Cid struct based on a base 58 encoded string
|
||||||
* @param incoming the string
|
* @param incoming the string
|
||||||
|
@ -51,7 +104,32 @@ int ipfs_cid_free(struct Cid* cid);
|
||||||
* @cid the Cid struct to fill
|
* @cid the Cid struct to fill
|
||||||
* @return true(1) on success
|
* @return true(1) on success
|
||||||
*/
|
*/
|
||||||
int ipfs_cid_decode_from_string(const unsigned char* incoming, size_t incoming_length, struct Cid** cid);
|
int ipfs_cid_decode_hash_from_base58(const unsigned char* incoming, size_t incoming_length, struct Cid** cid);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Create a CID from an ipfs or ipns string (i.e. "/ipns/QmAb12CD..."
|
||||||
|
* @param incoming the incoming string
|
||||||
|
* @param cid the resultant Cid
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_cid_decode_hash_from_ipfs_ipns_string(const char* incoming, struct Cid** cid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn a cid into a base 58 of a multihash of the cid hash
|
||||||
|
* @param cid the cid to work with
|
||||||
|
* @param buffer where to put the results
|
||||||
|
* @param max_buffer_length the maximum space reserved for the results
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_cid_hash_to_base58(const unsigned char* hash, size_t hash_length, unsigned char* buffer, size_t max_buffer_length);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Turn the hash of this CID into a c string
|
||||||
|
* @param cid the cid
|
||||||
|
* @param result a place to allocate and store the string
|
||||||
|
* @returns a pointer to the string (*result) or NULL if there was a problem
|
||||||
|
*/
|
||||||
|
char* ipfs_cid_to_string(const struct Cid* cid, char **result);
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Turn a multibase decoded string of bytes into a Cid struct
|
* Turn a multibase decoded string of bytes into a Cid struct
|
||||||
|
@ -59,6 +137,23 @@ int ipfs_cid_decode_from_string(const unsigned char* incoming, size_t incoming_l
|
||||||
* @param incoming_size the size of the array
|
* @param incoming_size the size of the array
|
||||||
* @param cid the Cid structure to fill
|
* @param cid the Cid structure to fill
|
||||||
*/
|
*/
|
||||||
int ipfs_cid_cast(unsigned char* incoming, size_t incoming_size, struct Cid* cid);
|
int ipfs_cid_cast(const unsigned char* incoming, size_t incoming_size, struct Cid* cid);
|
||||||
|
|
||||||
|
struct CidSet *ipfs_cid_set_new ();
|
||||||
|
void ipfs_cid_set_destroy (struct CidSet **set);
|
||||||
|
int ipfs_cid_set_add (struct CidSet *set, struct Cid *cid, int visit);
|
||||||
|
int ipfs_cid_set_has (struct CidSet *set, struct Cid *cid);
|
||||||
|
int ipfs_cid_set_remove (struct CidSet *set, struct Cid *cid);
|
||||||
|
int ipfs_cid_set_len (struct CidSet *set);
|
||||||
|
unsigned char **ipfs_cid_set_keys (struct CidSet *set);
|
||||||
|
int ipfs_cid_set_foreach (struct CidSet *set, int (*func)(struct Cid *));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare two cids
|
||||||
|
* @param a side A
|
||||||
|
* @param b side B
|
||||||
|
* @returns < 0 if side A is greater, > 0 if side B is greater, or 0 if equal
|
||||||
|
*/
|
||||||
|
int ipfs_cid_compare(const struct Cid* a, const struct Cid* b);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
19
include/ipfs/cmd/cli.h
Normal file
19
include/ipfs/cmd/cli.h
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helps parse the command line.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A structure to hold the command line arguments
|
||||||
|
*/
|
||||||
|
struct CliArguments {
|
||||||
|
int argc;
|
||||||
|
char** argv;
|
||||||
|
int verb_index;
|
||||||
|
char* config_dir;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CliArguments* cli_arguments_new(int argc, char** argv);
|
||||||
|
|
||||||
|
void cli_arguments_free(struct CliArguments* args);
|
|
@ -20,4 +20,12 @@ int ipfs_cmd_ipfs_init_command_new(struct Command* command);
|
||||||
*/
|
*/
|
||||||
int ipfs_cmd_ipfs_init_command_free(struct Command* command);
|
int ipfs_cmd_ipfs_init_command_free(struct Command* command);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Parse the command line
|
||||||
|
* @param argc the number of arguments
|
||||||
|
* @param argv the actual arguments
|
||||||
|
* @returns a command structure
|
||||||
|
*/
|
||||||
|
struct Command* ipfs_cmd_parse_command_line(int argc, char** argv);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
* @param request the end result, something that can be passed on that actually does something
|
* @param request the end result, something that can be passed on that actually does something
|
||||||
* @returns 0 if something bad happens, otherwise 1
|
* @returns 0 if something bad happens, otherwise 1
|
||||||
*/
|
*/
|
||||||
int cli_parse(char** params, FILE* inStream, struct Command* cmd, struct Request* request);
|
int cli_parse(int argc, char** params, FILE* inStream, struct Command** cmd, struct Request* request);
|
||||||
|
|
||||||
int cli_parse_opts(char** params, struct Command* cmd, char* path, char** stringVals);
|
int cli_parse_opts(char** params, struct Command* cmd, char* path, char** stringVals);
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
//
|
|
||||||
// option.h
|
|
||||||
// c-ipfs
|
|
||||||
//
|
|
||||||
// Created by John Jones on 10/26/16.
|
|
||||||
// Copyright © 2016 JMJAtlanta. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef __COMMANDS_COMMAND_OPTION_H__
|
#ifndef __COMMANDS_COMMAND_OPTION_H__
|
||||||
#define __COMMANDS_COMMAND_OPTION_H__
|
#define __COMMANDS_COMMAND_OPTION_H__
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
//
|
|
||||||
// context.h
|
|
||||||
// c-ipfs
|
|
||||||
//
|
|
||||||
// Created by John Jones on 10/27/16.
|
|
||||||
// Copyright © 2016 JMJAtlanta. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef __COMMANDS_CONTEXT_H__
|
#ifndef __COMMANDS_CONTEXT_H__
|
||||||
#define __COMMANDS_CONTEXT_H__
|
#define __COMMANDS_CONTEXT_H__
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
//
|
|
||||||
// req_log.h
|
|
||||||
// c-ipfs
|
|
||||||
//
|
|
||||||
// Created by John Jones on 10/27/16.
|
|
||||||
// Copyright © 2016 JMJAtlanta. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef __COMMANDS_REQ_LOG_H__
|
#ifndef __COMMANDS_REQ_LOG_H__
|
||||||
#define __COMMANDS_REQ_LOG_H__
|
#define __COMMANDS_REQ_LOG_H__
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
//
|
|
||||||
// request.h
|
|
||||||
// c-ipfs
|
|
||||||
//
|
|
||||||
// Created by John Jones on 10/26/16.
|
|
||||||
// Copyright © 2016 JMJAtlanta. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef __COMMANDS_REQUEST_H__
|
#ifndef __COMMANDS_REQUEST_H__
|
||||||
#define __COMMANDS_REQUEST_H__
|
#define __COMMANDS_REQUEST_H__
|
||||||
|
|
||||||
|
|
95
include/ipfs/core/api.h
Normal file
95
include/ipfs/core/api.h
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include "ipfs/core/ipfs_node.h"
|
||||||
|
|
||||||
|
#ifdef __x86_64__
|
||||||
|
#define INT_TYPE uint64_t
|
||||||
|
#else
|
||||||
|
#define INT_TYPE uint32_t
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MAX_READ (32*1024) // 32k
|
||||||
|
#define MAX_CHUNK (32*1024) // 32k
|
||||||
|
|
||||||
|
struct ApiContext {
|
||||||
|
int socket;
|
||||||
|
uint32_t ipv4;
|
||||||
|
uint16_t port;
|
||||||
|
int max_conns;
|
||||||
|
int timeout;
|
||||||
|
pthread_mutex_t conns_lock;
|
||||||
|
int conns_count;
|
||||||
|
pthread_t api_thread;
|
||||||
|
struct s_conns {
|
||||||
|
int socket;
|
||||||
|
uint32_t ipv4;
|
||||||
|
uint16_t port;
|
||||||
|
pthread_t pthread;
|
||||||
|
} **conns;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct s_request {
|
||||||
|
char *buf;
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
int method;
|
||||||
|
int path;
|
||||||
|
int request;
|
||||||
|
int query;
|
||||||
|
int http_ver;
|
||||||
|
int header;
|
||||||
|
int body;
|
||||||
|
size_t body_size;
|
||||||
|
int boundary;
|
||||||
|
size_t boundary_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define API_V0_START "/api/v0/"
|
||||||
|
|
||||||
|
#define WEBUI_ADDR "/ipfs/QmPhnvn747LqwPYMJmQVorMaGbMSgA7mRRoyyZYz3DoZRQ/"
|
||||||
|
|
||||||
|
#define HTTP_301 "HTTP/1.1 301 Moved Permanently\r\n" \
|
||||||
|
"Location: %s\r\n" \
|
||||||
|
"Content-Type: text/html\r\n\r\n" \
|
||||||
|
"<a href=\"%s\">Moved Permanently</a>.\r\n\r\n"
|
||||||
|
|
||||||
|
#define HTTP_302 "HTTP/1.1 302 Found\r\n" \
|
||||||
|
"Content-Type: text/html\r\n" \
|
||||||
|
"Connection: close\r\n" \
|
||||||
|
"Location: %s\r\n" \
|
||||||
|
"X-Ipfs-Path: %s\r\n\r\n" \
|
||||||
|
"<a href=\"%s\">Found</a>.\r\n\r\n"
|
||||||
|
|
||||||
|
#define HTTP_400 "HTTP/1.1 400 Bad Request\r\n" \
|
||||||
|
"Content-Type: text/plain\r\n" \
|
||||||
|
"Connection: close\r\n\r\n" \
|
||||||
|
"400 Bad Request"
|
||||||
|
|
||||||
|
#define HTTP_404 "HTTP/1.1 404 Not Found\r\n" \
|
||||||
|
"Content-Type: text/plain\r\n" \
|
||||||
|
"Connection: close\r\n\r\n" \
|
||||||
|
"404 page not found"
|
||||||
|
|
||||||
|
#define HTTP_500 "HTTP/1.1 500 Internal server error\r\n" \
|
||||||
|
"Content-Type: text/plain\r\n" \
|
||||||
|
"Connection: close\r\n\r\n" \
|
||||||
|
"500 Internal server error"
|
||||||
|
|
||||||
|
#define HTTP_501 "HTTP/1.1 501 Not Implemented\r\n" \
|
||||||
|
"Content-Type: text/plain\r\n" \
|
||||||
|
"Connection: close\r\n\r\n" \
|
||||||
|
"501 Not Implemented"
|
||||||
|
|
||||||
|
#define write_cstr(f,s) write(f,s,sizeof(s)-1)
|
||||||
|
#define write_str(f,s) write(f,s,strlen(s))
|
||||||
|
|
||||||
|
#define cstrstart(a,b) (memcmp(a,b,sizeof(b)-1)==0)
|
||||||
|
#define strstart(a,b) (memcmp(a,b,strlen(b))==0)
|
||||||
|
|
||||||
|
int api_send_resp_chunks(int fd, void *buf, size_t size);
|
||||||
|
void *api_connection_thread (void *ptr);
|
||||||
|
void api_connections_cleanup (struct IpfsNode* node);
|
||||||
|
void *api_listen_thread (void *ptr);
|
||||||
|
int api_start (struct IpfsNode* local_node, int max_conns, int timeout);
|
||||||
|
int api_stop (struct IpfsNode* local_node);
|
14
include/ipfs/core/bootstrap.h
Normal file
14
include/ipfs/core/bootstrap.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires up the connection to peers
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to the peers in the config file
|
||||||
|
* @param param a IpfsNode object
|
||||||
|
* @returns nothing useful
|
||||||
|
*/
|
||||||
|
void *ipfs_bootstrap_swarm(void* param);
|
||||||
|
|
||||||
|
void *ipfs_bootstrap_routing(void* param);
|
|
@ -1,11 +1,3 @@
|
||||||
//
|
|
||||||
// builder.h
|
|
||||||
// c-ipfs
|
|
||||||
//
|
|
||||||
// Created by John Jones on 10/27/16.
|
|
||||||
// Copyright © 2016 JMJAtlanta. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef __CORE_BUILDER_H__
|
#ifndef __CORE_BUILDER_H__
|
||||||
#define __CORE_BUILDER_H__
|
#define __CORE_BUILDER_H__
|
||||||
|
|
||||||
|
@ -13,6 +5,7 @@
|
||||||
|
|
||||||
#include "ipfs/commands/context.h"
|
#include "ipfs/commands/context.h"
|
||||||
#include "ipfs/repo/config/config.h"
|
#include "ipfs/repo/config/config.h"
|
||||||
|
#include "ipfs/core/ipfs_node.h"
|
||||||
|
|
||||||
struct BuildCfg {
|
struct BuildCfg {
|
||||||
int online;
|
int online;
|
||||||
|
|
8
include/ipfs/core/client_api.h
Normal file
8
include/ipfs/core/client_api.h
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#include "ipfs/core/ipfs_node.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the API is running
|
||||||
|
* @param local_node the context
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int api_running(struct IpfsNode* local_node);
|
34
include/ipfs/core/daemon.h
Normal file
34
include/ipfs/core/daemon.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef DAEMON_H
|
||||||
|
#define DAEMON_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "ipfs/core/ipfs_node.h"
|
||||||
|
|
||||||
|
#define MAX 5
|
||||||
|
#define CONNECTIONS 50
|
||||||
|
|
||||||
|
struct null_connection_params {
|
||||||
|
int file_descriptor;
|
||||||
|
int *count;
|
||||||
|
char* ip;
|
||||||
|
int port;
|
||||||
|
struct IpfsNode* local_node;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct null_listen_params {
|
||||||
|
uint32_t ipv4;
|
||||||
|
uint16_t port;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IpfsNodeListenParams {
|
||||||
|
uint32_t ipv4;
|
||||||
|
uint16_t port;
|
||||||
|
struct IpfsNode* local_node;
|
||||||
|
};
|
||||||
|
|
||||||
|
int ipfs_daemon (int argc, char **argv);
|
||||||
|
int ipfs_daemon_start(char* repo_path);
|
||||||
|
int ipfs_daemon_stop();
|
||||||
|
int ipfs_ping (int argc, char **argv);
|
||||||
|
|
||||||
|
#endif // DAEMON_H
|
89
include/ipfs/core/http_request.h
Normal file
89
include/ipfs/core/http_request.h
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ipfs/core/ipfs_node.h"
|
||||||
|
|
||||||
|
/***
|
||||||
|
* A name/value pair of http parameters
|
||||||
|
*/
|
||||||
|
struct HttpParam {
|
||||||
|
char* name; // the name of the parameter
|
||||||
|
char* value; // the value of the parameter
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A struct to help with incoming http requests
|
||||||
|
*/
|
||||||
|
struct HttpRequest {
|
||||||
|
char* command; // the command
|
||||||
|
char* sub_command; // the sub command
|
||||||
|
struct Libp2pVector* params; // a collection of HttpParam structs
|
||||||
|
struct Libp2pVector* arguments; // a collection of chars that are arguments
|
||||||
|
};
|
||||||
|
|
||||||
|
/***
|
||||||
|
* A struct to hold the response to be sent via http
|
||||||
|
*/
|
||||||
|
struct HttpResponse {
|
||||||
|
char* content_type; // a const char, not dynamically allocated
|
||||||
|
uint8_t* bytes; // dynamically allocated
|
||||||
|
size_t bytes_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Build a new HttpRequest
|
||||||
|
* @returns the newly allocated HttpRequest struct
|
||||||
|
*/
|
||||||
|
struct HttpRequest* ipfs_core_http_request_new();
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Clean up resources of a HttpRequest struct
|
||||||
|
* @param request the struct to destroy
|
||||||
|
*/
|
||||||
|
void ipfs_core_http_request_free(struct HttpRequest* request);
|
||||||
|
|
||||||
|
struct HttpResponse* ipfs_core_http_response_new();
|
||||||
|
|
||||||
|
void ipfs_core_http_response_free(struct HttpResponse* response);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Build a new HttpParam
|
||||||
|
* @returns a newly allocated HttpParam struct
|
||||||
|
*/
|
||||||
|
struct HttpParam* ipfs_core_http_param_new();
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Clean up resources allocated by a HttpParam struct
|
||||||
|
* @param param the struct to destroy
|
||||||
|
*/
|
||||||
|
void ipfs_core_http_param_free(struct HttpParam* param);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Process the parameters passed in from an http request
|
||||||
|
* @param local_node the context
|
||||||
|
* @param request the request
|
||||||
|
* @param response the response
|
||||||
|
* @returns true(1) on success, false(0) otherwise.
|
||||||
|
*/
|
||||||
|
int ipfs_core_http_request_process(struct IpfsNode* local_node, struct HttpRequest* request, struct HttpResponse** response);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do an HTTP Get to the local API
|
||||||
|
* @param local_node the context
|
||||||
|
* @param request the request
|
||||||
|
* @param result the results
|
||||||
|
* @param result_size the size of the results
|
||||||
|
* @returns true(1) on success, false(0) on error
|
||||||
|
*/
|
||||||
|
int ipfs_core_http_request_get(struct IpfsNode* local_node, struct HttpRequest* request, char** result, size_t* result_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do an HTTP Post to the local API
|
||||||
|
* @param local_node the context
|
||||||
|
* @param request the request
|
||||||
|
* @param result the results
|
||||||
|
* @param result_size the size of the results
|
||||||
|
* @param data the array with post data
|
||||||
|
* @param data_size the data length
|
||||||
|
* @returns true(1) on success, false(0) on error
|
||||||
|
*/
|
||||||
|
int ipfs_core_http_request_post(struct IpfsNode* local_node, struct HttpRequest* request, char** result, size_t* result_size, char *data, size_t data_size);
|
|
@ -1,21 +1,70 @@
|
||||||
//
|
#pragma once
|
||||||
// ipfs_node.h
|
|
||||||
// c-ipfs
|
|
||||||
//
|
|
||||||
// Created by John Jones on 10/27/16.
|
|
||||||
// Copyright © 2016 JMJAtlanta. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef __CORE_IPFS_NODE_H__
|
#include <pthread.h>
|
||||||
#define __CORE_IPFS_NODE_H__
|
#include "libp2p/peer/peerstore.h"
|
||||||
|
#include "libp2p/peer/providerstore.h"
|
||||||
|
#include "libp2p/swarm/swarm.h"
|
||||||
|
#include "ipfs/blocks/blockstore.h"
|
||||||
|
#include "ipfs/exchange/exchange.h"
|
||||||
|
#include "ipfs/repo/config/identity.h"
|
||||||
|
#include "ipfs/repo/fsrepo/fs_repo.h"
|
||||||
|
#include "ipfs/routing/routing.h"
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Holds information about the local node
|
||||||
|
*/
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Modes:
|
||||||
|
* MODE_OFFLINE: Do everything yourself
|
||||||
|
* MODE_API_AVAILABLE: If you want to, the API is running
|
||||||
|
* MODE_ONLINE: You are the API
|
||||||
|
*/
|
||||||
|
enum NodeMode { MODE_OFFLINE, MODE_API_AVAILABLE, MODE_ONLINE };
|
||||||
|
|
||||||
struct IpfsNode {
|
struct IpfsNode {
|
||||||
//struct PeerId identity;
|
/***
|
||||||
//struct Repo repo;
|
* Modes:
|
||||||
|
* MODE_OFFLINE: Do everything yourself
|
||||||
|
* MODE_API_AVAILABLE: If you want to, the API is running
|
||||||
|
* MODE_ONLINE: You are the API
|
||||||
|
*/
|
||||||
|
enum NodeMode mode;
|
||||||
|
struct Identity* identity;
|
||||||
|
struct FSRepo* repo;
|
||||||
|
struct Peerstore* peerstore;
|
||||||
|
struct ProviderStore* providerstore;
|
||||||
|
struct IpfsRouting* routing;
|
||||||
|
struct Blockstore* blockstore;
|
||||||
|
struct Exchange* exchange;
|
||||||
|
struct Libp2pVector* protocol_handlers;
|
||||||
|
struct ApiContext* api_context;
|
||||||
|
struct Dialer* dialer;
|
||||||
|
struct SwarmContext* swarm;
|
||||||
//struct Pinner pinning; // an interface
|
//struct Pinner pinning; // an interface
|
||||||
//struct Mount** mounts;
|
//struct Mount** mounts;
|
||||||
//struct PrivKey* private_key;
|
|
||||||
// TODO: Add more here
|
// TODO: Add more here
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* ipfs_node_h */
|
/***
|
||||||
|
* build an online IpfsNode
|
||||||
|
* @param repo_path where the IPFS repository directory is
|
||||||
|
* @param node the completed IpfsNode struct
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_node_online_new(const char* repo_path, struct IpfsNode** node);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* build an offline IpfsNode
|
||||||
|
* @param repo_path where the IPFS repository directory is
|
||||||
|
* @param node the completed IpfsNode struct
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_node_offline_new(const char* repo_path, struct IpfsNode** node);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Free resources from the creation of an IpfsNode
|
||||||
|
* @param node the node to free
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_node_free(struct IpfsNode* node);
|
||||||
|
|
35
include/ipfs/core/net.h
Normal file
35
include/ipfs/core/net.h
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "libp2p/net/stream.h"
|
||||||
|
|
||||||
|
struct IpfsListener {
|
||||||
|
char* conCh;
|
||||||
|
char* protocol;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do a socket accept
|
||||||
|
* @param listener the listener
|
||||||
|
* @param stream the returned stream
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_core_net_accept(struct IpfsListener* listener, struct Stream* stream);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen using a particular protocol
|
||||||
|
* @param node the node
|
||||||
|
* @param protocol the protocol to use
|
||||||
|
* @param listener the results
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_core_net_listen(struct IpfsNode* node, char* protocol, struct IpfsListener* listener);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Dial a peer
|
||||||
|
* @param node this node
|
||||||
|
* @param peer_id who to dial
|
||||||
|
* @param protocol the protocol to use
|
||||||
|
* @param stream the resultant stream
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipsf_core_net_dial(struct IpfsNode* node, char* peer_id, char* protocol, struct Stream* stream);
|
19
include/ipfs/core/null.h
Normal file
19
include/ipfs/core/null.h
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "libp2p/conn/session.h"
|
||||||
|
#include "ipfs/core/ipfs_node.h"
|
||||||
|
|
||||||
|
void *ipfs_null_connection (void *ptr);
|
||||||
|
void *ipfs_null_listen (void *ptr);
|
||||||
|
int ipfs_null_shutdown();
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Handle the incoming request from a Multistream
|
||||||
|
* @param incoming the incoming request
|
||||||
|
* @param incoming_size the size of the request in bytes
|
||||||
|
* @param session the session context
|
||||||
|
* @param connection_param the connection parameters
|
||||||
|
* @returns True(1) on success, False(0) on error
|
||||||
|
*/
|
||||||
|
int ipfs_multistream_marshal(const unsigned char* incoming, size_t incoming_size, struct SessionContext* session, struct IpfsNode* local_node);
|
||||||
|
|
8
include/ipfs/core/swarm.h
Normal file
8
include/ipfs/core/swarm.h
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ipfs/cmd/cli.h"
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Handle command line swarm call
|
||||||
|
*/
|
||||||
|
int ipfs_swarm (struct CliArguments* args);
|
|
@ -1,10 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Some code to help with the datastore / blockstore interface
|
* Some code to help with the datastore / blockstore interface
|
||||||
*/
|
*/
|
||||||
#ifndef __IPFS_DATASTORE_DS_HELPER_H__
|
|
||||||
#define __IPFS_DATASTORE_DS_HELPER_H__
|
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include "ipfs/blocks/block.h"
|
||||||
|
#include "libp2p/db/datastore.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a key based on the passed in binary_array
|
* Generate a key based on the passed in binary_array
|
||||||
|
@ -15,8 +17,8 @@
|
||||||
* @param results_length the length of the generated key
|
* @param results_length the length of the generated key
|
||||||
* @returns true(1) on success
|
* @returns true(1) on success
|
||||||
*/
|
*/
|
||||||
int ipfs_datastore_helper_ds_key_from_binary(unsigned char* binary_array, size_t array_length,
|
int ipfs_datastore_helper_ds_key_from_binary(const unsigned char* binary_array, size_t array_length,
|
||||||
char* results, size_t max_results_length, size_t* results_length);
|
unsigned char* results, size_t max_results_length, size_t* results_length);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a binary array based on the passed in datastore key
|
* Generate a binary array based on the passed in datastore key
|
||||||
|
@ -27,7 +29,13 @@ int ipfs_datastore_helper_ds_key_from_binary(unsigned char* binary_array, size_t
|
||||||
* @param completed_binary_array_length the length of what was written to the binary_array
|
* @param completed_binary_array_length the length of what was written to the binary_array
|
||||||
* @returns true(1) on success
|
* @returns true(1) on success
|
||||||
*/
|
*/
|
||||||
int ipfs_datastore_helper_binary_from_ds_key(unsigned char* ds_key, size_t key_length, unsigned char* binary_array,
|
int ipfs_datastore_helper_binary_from_ds_key(const unsigned char* ds_key, size_t key_length, unsigned char* binary_array,
|
||||||
size_t max_binary_array_length, size_t* completed_binary_array_length);
|
size_t max_binary_array_length, size_t* completed_binary_array_length);
|
||||||
|
|
||||||
#endif
|
/***
|
||||||
|
* Add a record in the datastore based on a block
|
||||||
|
* @param block the block
|
||||||
|
* @param datastore the Datastore
|
||||||
|
* @reutrns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_datastore_helper_add_block_to_datastore(struct Block* block, struct Datastore* datastore);
|
||||||
|
|
27
include/ipfs/dnslink/dnslink.h
Normal file
27
include/ipfs/dnslink/dnslink.h
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef DNSLINK_H
|
||||||
|
#define DNSLINK_H
|
||||||
|
|
||||||
|
#include "ipfs/util/errs.h"
|
||||||
|
|
||||||
|
// DefaultDepthLimit controls how many dns links to resolve through before
|
||||||
|
// returning. Users can override this default.
|
||||||
|
#ifndef DefaultDepthLimit
|
||||||
|
#define DefaultDepthLimit 16
|
||||||
|
#endif
|
||||||
|
// MaximumDepthLimit governs the max number of recursive resolutions.
|
||||||
|
#ifndef MaximumDepthLimit
|
||||||
|
#define MaximumDepthLimit 256
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef IPFS_DNSLINK_C
|
||||||
|
extern int (*ipfs_dnslink_lookup_txt)(char ***, char *);
|
||||||
|
#endif // IPFS_DNSLINK_C
|
||||||
|
|
||||||
|
int ipfs_dns (int argc, char **argv);
|
||||||
|
int ipfs_dnslink_resolve (char **p, char *domain);
|
||||||
|
int ipfs_dnslink_resolve_n (char **p, char *d, int depth);
|
||||||
|
int ipfs_dnslink_resolv_lookupTXT(char ***txt, char *domain);
|
||||||
|
int ipfs_dnslink_resolve_once (char ***p, char *domain);
|
||||||
|
int ipfs_dnslink_parse_txt (char **path, char *txt);
|
||||||
|
int ipfs_dnslink_parse_link_domain (char **domain, char**rest, char *txt);
|
||||||
|
#endif // DNSLINK_H
|
89
include/ipfs/exchange/bitswap/bitswap.h
Normal file
89
include/ipfs/exchange/bitswap/bitswap.h
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Bitswap implements the "exchange" and "Libp2pProtocolHandler" interfaces
|
||||||
|
* @see ../exchange.h
|
||||||
|
* @see libp2p/net/protocol.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libp2p/net/protocol.h"
|
||||||
|
#include "ipfs/core/ipfs_node.h"
|
||||||
|
#include "ipfs/exchange/exchange.h"
|
||||||
|
#include "ipfs/exchange/bitswap/engine.h"
|
||||||
|
#include "ipfs/exchange/bitswap/wantlist_queue.h"
|
||||||
|
|
||||||
|
struct Libp2pProtocolHandler* ipfs_bitswap_build_protocol_handler(const struct IpfsNode* local_node);
|
||||||
|
|
||||||
|
|
||||||
|
struct BitswapContext {
|
||||||
|
struct IpfsNode* ipfsNode;
|
||||||
|
struct WantListQueue* localWantlist;
|
||||||
|
struct PeerRequestQueue* peerRequestQueue;
|
||||||
|
struct BitswapEngine* bitswap_engine;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start up the bitswap exchange
|
||||||
|
* @param ipfsNode the context
|
||||||
|
* @returns an Exchange struct that refers to the exchange
|
||||||
|
*/
|
||||||
|
struct Exchange* ipfs_bitswap_new(struct IpfsNode* ipfsNode);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* These are the implementation methods for the exchange "Interface"
|
||||||
|
*/
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Checks to see if the Bitswap service is online
|
||||||
|
* @param exhcnageContext a pointer to a BitswapContext
|
||||||
|
* @reutrns true(1) if online, false(0) otherwise.
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_is_online(struct Exchange* exchange);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Closes down the Bitswap network
|
||||||
|
* @param exchangeContext a pointer to a BitswapContext
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_close(struct Exchange* exchange);
|
||||||
|
|
||||||
|
/****
|
||||||
|
* Notify the BitswapNetwork that we have this block
|
||||||
|
* @param exchangeContext a pointer to a BitswapContext
|
||||||
|
* @block the block that we have
|
||||||
|
* @reutrns true(1) if successful, false(0) if not.
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_has_block(struct Exchange* exchange, struct Block* block);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a block from the BitswapNetwork
|
||||||
|
* Note: This may pull the file from the local blockstore.
|
||||||
|
* Note: If false(0) is returned, block will be NULL
|
||||||
|
*
|
||||||
|
* @param exchangeContext a pointer to a BitswapContext
|
||||||
|
* @param cid the Cid of the block we're looking for
|
||||||
|
* @param block a pointer to the block when we find it.
|
||||||
|
* @returns true(1) on success, false(0) otherwise.
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_get_block(struct Exchange* exchange, struct Cid* cid, struct Block** block);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a block from the BitswapNetwork
|
||||||
|
*
|
||||||
|
* @param exchangeContext a pointer to a BitswapContext
|
||||||
|
* @param cid the Cid of the block we're looking for
|
||||||
|
* @param queue a pointer to the queue that will change if the block arrives
|
||||||
|
* @returns true(1) on success, false(0) otherwise.
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_get_block_async(struct Exchange* exchange, struct Cid* cid, struct Block** block);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Retrieve a collection of blocks from the BitswapNetwork
|
||||||
|
* Note: The return of false(0) means that not all blocks were found.
|
||||||
|
*
|
||||||
|
* @param exchangeContext a pointer to a BitswapContext
|
||||||
|
* @param cids a collection of Cid structs
|
||||||
|
* @param blocks a collection that contains the results.
|
||||||
|
* @param true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_get_blocks(struct Exchange* exchange, struct Libp2pVector* cids, struct Libp2pVector** blocks);
|
42
include/ipfs/exchange/bitswap/engine.h
Normal file
42
include/ipfs/exchange/bitswap/engine.h
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
//#include "ipfs/exchange/bitswap/bitswap.h" we must forward declare here, as BitswapContext has a reference to BitswapEngine
|
||||||
|
|
||||||
|
struct BitswapContext;
|
||||||
|
|
||||||
|
struct BitswapEngine {
|
||||||
|
int shutting_down;
|
||||||
|
pthread_t wantlist_processor_thread;
|
||||||
|
pthread_t peer_request_processor_thread;
|
||||||
|
};
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Allocate resources for a BitswapEngine
|
||||||
|
* @returns a new struct BitswapEngine
|
||||||
|
*/
|
||||||
|
struct BitswapEngine* ipfs_bitswap_engine_new();
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Deallocate resources from struct BitswapEngine
|
||||||
|
* @param engine the engine to free
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_engine_free(struct BitswapEngine* engine);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the bitswap engine that processes queue items. There
|
||||||
|
* should only be one of these per ipfs instance.
|
||||||
|
*
|
||||||
|
* @param context the context
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_engine_start(const struct BitswapContext* context);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Shut down the engine
|
||||||
|
*
|
||||||
|
* @param context the context
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_engine_stop(const struct BitswapContext* context);
|
218
include/ipfs/exchange/bitswap/message.h
Normal file
218
include/ipfs/exchange/bitswap/message.h
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
#pragma once
|
||||||
|
/***
|
||||||
|
* A protobuf-able Bitswap Message
|
||||||
|
*/
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "libp2p/utils/vector.h"
|
||||||
|
|
||||||
|
struct WantlistEntry {
|
||||||
|
// optional string block = 1, the block cid (cidV0 in bitswap 1.0.0, cidV1 in bitswap 1.1.0
|
||||||
|
unsigned char* block;
|
||||||
|
size_t block_size;
|
||||||
|
// optional int32 priority = 2, the priority (normalized). default to 1
|
||||||
|
uint32_t priority;
|
||||||
|
// optional bool cancel = 3, whether this revokes an entry
|
||||||
|
uint8_t cancel;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BitswapWantlist {
|
||||||
|
// repeated WantlistEntry entries = 1, a list of wantlist entries
|
||||||
|
struct Libp2pVector* entries;
|
||||||
|
// optional bool full = 2, whether this is the full wantlist. default to false
|
||||||
|
uint8_t full;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BitswapBlock {
|
||||||
|
// optional bytes prefix = 1, // CID prefix (cid version, multicodec, and multihash prefix (type + length))
|
||||||
|
uint8_t* prefix;
|
||||||
|
size_t prefix_size;
|
||||||
|
// optional bytes data = 2
|
||||||
|
uint8_t* bytes;
|
||||||
|
size_t bytes_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BitswapMessage {
|
||||||
|
// optional Wantlist wantlist = 1
|
||||||
|
struct BitswapWantlist* wantlist;
|
||||||
|
// repeated bytes blocks = 2, used to send Blocks in bitswap 1.0.0
|
||||||
|
struct Libp2pVector* blocks;
|
||||||
|
// repeated Block payload = 3, used to send Blocks in bitswap 1.1.0
|
||||||
|
struct Libp2pVector* payload;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Allocate memory for a struct BitswapBlock
|
||||||
|
* @returns a new BitswapBlock
|
||||||
|
*/
|
||||||
|
struct BitswapBlock* ipfs_bitswap_block_new();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deallocate memory for a struct BitswapBlock
|
||||||
|
* @param block the block to deallocate
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_block_free(struct BitswapBlock* block);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an estimate of the size of a protobuf'd BitswapBlock
|
||||||
|
* @returns the approximate (maximum actually) size of a protobuf'd BitswapBlock
|
||||||
|
*/
|
||||||
|
size_t ipfs_bitswap_message_block_protobuf_size(struct BitswapBlock* block);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Encode a BitswapBlock
|
||||||
|
* @param incoming the block to encode
|
||||||
|
* @param outgoing where to place the results
|
||||||
|
* @param max_size the maximum allocated space for outgoing
|
||||||
|
* @param bytes_written the number of bytes written to outgoing
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_message_block_protobuf_encode(struct BitswapBlock* incoming, uint8_t* outgoing, size_t max_size, size_t* bytes_written);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Decode a protobuf to a BitswapBlock
|
||||||
|
* @param buffer the incoming protobuf
|
||||||
|
* @param buffer_length the length of the incoming protobuf buffer
|
||||||
|
* @param output a pointer to the BitswapBlock that will be allocated
|
||||||
|
* @returns true(1) on success, false(0) if not. If false, any memory was deallocated
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_message_block_protobuf_decode(uint8_t* buffer, size_t buffer_length, struct BitswapBlock** output);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Allocate memory for a new WantlistEntry
|
||||||
|
* @returns the newly allocated WantlistEntry
|
||||||
|
*/
|
||||||
|
struct WantlistEntry* ipfs_bitswap_wantlist_entry_new();
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Free allocations of a WantlistEntry
|
||||||
|
* @param entry the WantlistEntry
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_wantlist_entry_free(struct WantlistEntry* entry);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an estimate of the size of a protobuf'd WantlistEntry
|
||||||
|
* @param entry the struct to examine
|
||||||
|
* @returns the approximate (maximum actually) size of a protobuf'd WantlistEntry
|
||||||
|
*/
|
||||||
|
size_t ipfs_bitswap_wantlist_entry_protobuf_encode_size(struct WantlistEntry* entry);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Encode a WantlistEntry into a Protobuf
|
||||||
|
* @param entry the WantlistEntry to encode
|
||||||
|
* @param buffer where to put the results
|
||||||
|
* @param buffer_length the maximum size of the buffer
|
||||||
|
* @param bytes_written the number of bytes written into the buffer
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_wantlist_entry_protobuf_encode(struct WantlistEntry* entry, unsigned char* buffer, size_t buffer_length, size_t* bytes_written);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Decode a protobuf into a struct WantlistEntry
|
||||||
|
* @param buffer the protobuf buffer
|
||||||
|
* @param buffer_length the length of the data in the protobuf buffer
|
||||||
|
* @param output the resultant WantlistEntry
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_wantlist_entry_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct WantlistEntry** output);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Allocate memory for a new Bitswap Message WantList
|
||||||
|
* @returns the allocated struct BitswapWantlist
|
||||||
|
*/
|
||||||
|
struct BitswapWantlist* ipfs_bitswap_wantlist_new();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free the resources used by a Wantlist
|
||||||
|
* @param list the list to free
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_wantlist_free(struct BitswapWantlist* list);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Calculate the maximum size of a protobuf'd BitswapWantlist
|
||||||
|
* @param list the Wantlist
|
||||||
|
* @returns the maximum size of the protobuf'd list
|
||||||
|
*/
|
||||||
|
size_t ipfs_bitswap_wantlist_protobuf_encode_size(struct BitswapWantlist* list);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Encode a BitswapWantlist into a protobuf buffer
|
||||||
|
* @param list the list to encode
|
||||||
|
* @param buffer the buffer to fill
|
||||||
|
* @param buffer_length the length of the allocated buffer
|
||||||
|
* @param bytes_written the total number of bytes written to the buffer
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_wantlist_protobuf_encode(struct BitswapWantlist* list, unsigned char* buffer, size_t buffer_length, size_t* bytes_written);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Decode a Wantlist from a protobuf
|
||||||
|
* @param buffer the protobuf
|
||||||
|
* @param buffer_length the length of the protobuf
|
||||||
|
* @param output the newly allocated BitswapWantlist
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_wantlist_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct BitswapWantlist** output);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Bitswap Message
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Allocate memory for a new Bitswap Message
|
||||||
|
* @returns the allocated struct BitswapMessage
|
||||||
|
*/
|
||||||
|
struct BitswapMessage* ipfs_bitswap_message_new();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free the resources used by a BitswapMessage
|
||||||
|
* @param message the BitswapMessage to free
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_message_free(struct BitswapMessage* message);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Calculate the maximum size of a protobuf'd BitswapMessage
|
||||||
|
* @param message the BitswapMessage
|
||||||
|
* @returns the maximum size of the protobuf'd BitswapMessage
|
||||||
|
*/
|
||||||
|
size_t ipfs_bitswap_message_protobuf_encode_size(const struct BitswapMessage* message);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Encode a BitswapMessage into a protobuf buffer
|
||||||
|
* @param message the message to encode
|
||||||
|
* @param buffer the buffer to fill
|
||||||
|
* @param buffer_length the length of the allocated buffer
|
||||||
|
* @param bytes_written the total number of bytes written to the buffer
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_message_protobuf_encode(const struct BitswapMessage* message, unsigned char* buffer, size_t buffer_length, size_t* bytes_written);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Decode a BitswapMessage from a protobuf
|
||||||
|
* @param buffer the protobuf
|
||||||
|
* @param buffer_length the length of the protobuf
|
||||||
|
* @param output the newly allocated BitswapMessage
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_message_protobuf_decode(const uint8_t* buffer, size_t buffer_length, struct BitswapMessage** output);
|
||||||
|
|
||||||
|
/****
|
||||||
|
* Add a vector of Cids to the bitswap message
|
||||||
|
* @param message the message
|
||||||
|
* @param cids a Libp2pVector of cids
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_message_add_wantlist_items(struct BitswapMessage* message, struct Libp2pVector* cids);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Add the blocks to the BitswapMessage
|
||||||
|
* @param message the message
|
||||||
|
* @param blocks the requested blocks
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_message_add_blocks(struct BitswapMessage* message, struct Libp2pVector* blocks, struct Libp2pVector* cids_they_want);
|
83
include/ipfs/exchange/bitswap/network.h
Normal file
83
include/ipfs/exchange/bitswap/network.h
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
/***
|
||||||
|
* This implements the BitswapNetwork. Members of this network can fill requests and
|
||||||
|
* smartly handle queues of local and remote requests.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libp2p/conn/session.h"
|
||||||
|
#include "libp2p/peer/peer.h"
|
||||||
|
#include "ipfs/exchange/bitswap/bitswap.h"
|
||||||
|
#include "ipfs/exchange/bitswap/message.h"
|
||||||
|
|
||||||
|
struct BitswapRouting {
|
||||||
|
/**
|
||||||
|
* Find the provider of a key asyncronously
|
||||||
|
* @param context the session context
|
||||||
|
* @param hash the key we're looking for
|
||||||
|
* @param forWhat I have yet to research this
|
||||||
|
* @param responseMethod a function pointer to call when results are found
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int (*FindProviderAsync)(struct SessionContext* context, unsigned char* hash, int forWhat, void (*responseMethod)(void*));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the key to the network. Is this an announcement or a fill?
|
||||||
|
* I think it is an announcement
|
||||||
|
* @param context the session context
|
||||||
|
* @param hash the hash to announce
|
||||||
|
* @returns true(1) on success, false(0) on error
|
||||||
|
*/
|
||||||
|
int (*Provide)(struct SessionContext* context, unsigned char* hash);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BitswapNetwork {
|
||||||
|
/***
|
||||||
|
* Send a message to a particular peer
|
||||||
|
* @param context the context
|
||||||
|
* @param peerId the peer ID of who to send to
|
||||||
|
* @param message the message to send
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int (*SendMessage)(struct SessionContext* context, unsigned char* peerId, struct BitswapMessage* message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The BitswapReceiver is who receives messages from the network
|
||||||
|
* @param receiver the struct that contains function pointers for receiving messages
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
//TODO: Implement this
|
||||||
|
//int (*SetDelegate)(struct BitswapReceiver* receiver);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt a connection to a particular peer
|
||||||
|
* @param context the session context
|
||||||
|
* @param peerId the id of the peer
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int (*ConnectTo)(struct SessionContext* context, unsigned char* peerId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A pointer to the method that creates a new BitswapMessageSender
|
||||||
|
* @param context the session context
|
||||||
|
* @param peerId the peer id of whom we should send the message to.
|
||||||
|
* @reutrns a pointer to the allocated struct that contains the initialized BitswapMessageSender or NULL if there was a problem
|
||||||
|
*/
|
||||||
|
struct BitswapMessageSender* (*NewMessageSender)(struct SessionContext* context, unsigned char* peerId);
|
||||||
|
};
|
||||||
|
|
||||||
|
/****
|
||||||
|
* send a message to a particular peer
|
||||||
|
* @param context the BitswapContext
|
||||||
|
* @param peer the peer that is the recipient
|
||||||
|
* @param message the message to send
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_network_send_message(const struct BitswapContext* context, struct Libp2pPeer* peer, const struct BitswapMessage* message);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Handle a raw incoming bitswap message from the network
|
||||||
|
* @param node us
|
||||||
|
* @param sessionContext the connection context
|
||||||
|
* @param bytes the message
|
||||||
|
* @param bytes_size the size of the message
|
||||||
|
* @returns true(1) on success, false(0) otherwise.
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_network_handle_message(const struct IpfsNode* node, const struct SessionContext* sessionContext, const uint8_t* bytes, size_t bytes_length);
|
145
include/ipfs/exchange/bitswap/peer_request_queue.h
Normal file
145
include/ipfs/exchange/bitswap/peer_request_queue.h
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
#pragma once
|
||||||
|
/***
|
||||||
|
* A queue for requests to/from remote peers
|
||||||
|
* NOTE: This must handle multiple threads
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include "libp2p/peer/peer.h"
|
||||||
|
#include "ipfs/exchange/bitswap/bitswap.h"
|
||||||
|
#include "ipfs/blocks/block.h"
|
||||||
|
|
||||||
|
struct CidEntry {
|
||||||
|
struct Cid* cid;
|
||||||
|
int cancel;
|
||||||
|
int cancel_has_been_sent;
|
||||||
|
int request_has_been_sent;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PeerRequest {
|
||||||
|
pthread_mutex_t request_mutex;
|
||||||
|
struct Libp2pPeer* peer;
|
||||||
|
// CidEntry collection of cids that they want
|
||||||
|
struct Libp2pVector* cids_they_want;
|
||||||
|
// CidEntry collection of cids that we want or are canceling
|
||||||
|
struct Libp2pVector* cids_we_want;
|
||||||
|
// blocks to send to them
|
||||||
|
struct Libp2pVector* blocks_we_want_to_send;
|
||||||
|
// blocks they sent us are processed immediately, so no queue necessary
|
||||||
|
// although the cid can go in cids_we_want again, with a cancel flag
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PeerRequestEntry {
|
||||||
|
struct PeerRequestEntry* prior;
|
||||||
|
struct PeerRequest* current;
|
||||||
|
struct PeerRequestEntry* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PeerRequestQueue {
|
||||||
|
pthread_mutex_t queue_mutex;
|
||||||
|
struct PeerRequestEntry* first;
|
||||||
|
struct PeerRequestEntry* last;
|
||||||
|
};
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Allocate memory for CidEntry
|
||||||
|
* @returns new CidEntry struct
|
||||||
|
*/
|
||||||
|
struct CidEntry* ipfs_bitswap_peer_request_cid_entry_new();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate resources for a new PeerRequest
|
||||||
|
* @returns a new PeerRequest struct or NULL if there was a problem
|
||||||
|
*/
|
||||||
|
struct PeerRequest* ipfs_bitswap_peer_request_new();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free resources from a PeerRequest
|
||||||
|
* @param request the request to free
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_peer_request_free(struct PeerRequest* request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate resources for a new queue
|
||||||
|
* @returns a new PeerRequestQueue
|
||||||
|
*/
|
||||||
|
struct PeerRequestQueue* ipfs_bitswap_peer_request_queue_new();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free all resources related to the queue
|
||||||
|
* @param queue the queue
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_peer_request_queue_free(struct PeerRequestQueue* queue);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a peer request to the end of the queue
|
||||||
|
* @param queue the queue
|
||||||
|
* @param request the request
|
||||||
|
* @returns true(1) on success, otherwise false
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_peer_request_queue_add(struct PeerRequestQueue* queue, struct PeerRequest* request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a peer request from the queue, no mather where it is
|
||||||
|
* @param queue the queue
|
||||||
|
* @param request the request
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_peer_request_quque_remove(struct PeerRequestQueue* queue, struct PeerRequest* request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pull a PeerRequest off the queue
|
||||||
|
* @param queue the queue
|
||||||
|
* @returns the PeerRequest that should be handled next.
|
||||||
|
*/
|
||||||
|
struct PeerRequest* ipfs_bitswap_peer_request_queue_pop(struct PeerRequestQueue* queue);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a PeerRequestEntry that contains the specified PeerRequest
|
||||||
|
* @param queue the queue
|
||||||
|
* @param request what we're looking for
|
||||||
|
* @returns the PeerRequestEntry or NULL if not found
|
||||||
|
*/
|
||||||
|
struct PeerRequestEntry* ipfs_bitswap_peer_request_queue_find_entry(struct PeerRequestQueue* queue, struct Libp2pPeer* peer);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Add a block to the appropriate peer's queue
|
||||||
|
* @param queue the queue
|
||||||
|
* @param who the session context that identifies the peer
|
||||||
|
* @param block the block
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_peer_request_queue_fill(struct PeerRequestQueue* queue, struct Libp2pPeer* who, struct Block* block);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Allocate resources for a PeerRequestEntry struct
|
||||||
|
* @returns the allocated struct or NULL if there was a problem
|
||||||
|
*/
|
||||||
|
struct PeerRequestEntry* ipfs_bitswap_peer_request_entry_new();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees resources allocated
|
||||||
|
* @param entry the PeerRequestEntry to free
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_peer_request_entry_free(struct PeerRequestEntry* entry);
|
||||||
|
|
||||||
|
/****
|
||||||
|
* Handle a PeerRequest
|
||||||
|
* @param context the BitswapContext
|
||||||
|
* @param request the request to process
|
||||||
|
* @returns true(1) on succes, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_peer_request_process_entry(const struct BitswapContext* context, struct PeerRequest* request);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Find a PeerRequest related to a peer. If one is not found, it is created.
|
||||||
|
*
|
||||||
|
* @param peer_request_queue the queue to look through
|
||||||
|
* @param peer the peer to look for
|
||||||
|
* @returns a PeerRequestEntry or NULL on error
|
||||||
|
*/
|
||||||
|
struct PeerRequest* ipfs_peer_request_queue_find_peer(struct PeerRequestQueue* queue, struct Libp2pPeer* peer);
|
||||||
|
|
40
include/ipfs/exchange/bitswap/want_manager.h
Normal file
40
include/ipfs/exchange/bitswap/want_manager.h
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ipfs/blocks/block.h"
|
||||||
|
#include "ipfs/cid/cid.h"
|
||||||
|
#include "ipfs/exchange/bitswap/bitswap.h"
|
||||||
|
#include "wantlist_queue.h"
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Add a Cid to the local wantlist
|
||||||
|
* @param context the context
|
||||||
|
* @param cid the Cid
|
||||||
|
* @returns the added WantListQueueEntry
|
||||||
|
*/
|
||||||
|
struct WantListQueueEntry* ipfs_bitswap_want_manager_add(const struct BitswapContext* context, const struct Cid* cid, const struct WantListSession* session);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Checks to see if the requested block has been received
|
||||||
|
* @param context the context
|
||||||
|
* @param cid the Cid
|
||||||
|
* @returns true(1) if it has been received, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_want_manager_received(const struct BitswapContext* context, const struct Cid* cid);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* retrieve a block from the WantManager.
|
||||||
|
* @param context the context
|
||||||
|
* @param cid the Cid to get
|
||||||
|
* @param block a pointer to the block that will be allocated
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_want_manager_get_block(const struct BitswapContext* context, const struct Cid* cid, struct Block** block);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* We no longer are requesting this block, so remove it from the queue
|
||||||
|
* NOTE: This is reference counted, as another process may have asked for it.
|
||||||
|
* @param context the context
|
||||||
|
* @param cid the Cid
|
||||||
|
* @returns true(1) on success, false(0) otherwise.
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_want_manager_remove(const struct BitswapContext* context, const struct Cid* cid);
|
115
include/ipfs/exchange/bitswap/wantlist_queue.h
Normal file
115
include/ipfs/exchange/bitswap/wantlist_queue.h
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
#pragma once
|
||||||
|
/**
|
||||||
|
* This is a list of requests from a peer (including locally).
|
||||||
|
* NOTE: This tracks who wants what. If 2 peers want the same file,
|
||||||
|
* there will be 1 WantListEntry in the WantList. There will be 2 entries in
|
||||||
|
* WantListEntry.sessionsRequesting.
|
||||||
|
*/
|
||||||
|
#include <pthread.h>
|
||||||
|
#include "ipfs/cid/cid.h"
|
||||||
|
#include "ipfs/blocks/block.h"
|
||||||
|
#include "ipfs/exchange/bitswap/bitswap.h"
|
||||||
|
|
||||||
|
enum WantListSessionType { WANTLIST_SESSION_TYPE_LOCAL, WANTLIST_SESSION_TYPE_REMOTE };
|
||||||
|
|
||||||
|
struct WantListSession {
|
||||||
|
enum WantListSessionType type;
|
||||||
|
void* context; // either an IpfsNode (local) or a Libp2pPeer (remote)
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WantListQueueEntry {
|
||||||
|
struct Cid* cid;
|
||||||
|
int priority;
|
||||||
|
// a vector of WantListSessions
|
||||||
|
struct Libp2pVector* sessionsRequesting;
|
||||||
|
struct Block* block;
|
||||||
|
int asked_network;
|
||||||
|
int attempts;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WantListQueue {
|
||||||
|
pthread_mutex_t wantlist_mutex;
|
||||||
|
// a vector of WantListEntries
|
||||||
|
struct Libp2pVector* queue;
|
||||||
|
};
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Initialize a WantListQueueEntry
|
||||||
|
* @returns a new WantListQueueEntry
|
||||||
|
*/
|
||||||
|
struct WantListQueueEntry* ipfs_bitswap_wantlist_queue_entry_new();
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Remove resources, freeing a WantListQueueEntry
|
||||||
|
* @param entry the WantListQueueEntry
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_wantlist_queue_entry_free(struct WantListQueueEntry* entry);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Initialize a new Wantlist (there should only be 1 per instance)
|
||||||
|
* @returns a new WantList
|
||||||
|
*/
|
||||||
|
struct WantListQueue* ipfs_bitswap_wantlist_queue_new();
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Deallocate resources of a WantList
|
||||||
|
* @param wantlist the WantList
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_wantlist_queue_free(struct WantListQueue* wantlist);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Add a Cid to the WantList
|
||||||
|
* @param wantlist the WantList to add to
|
||||||
|
* @param cid the Cid to add
|
||||||
|
* @returns the correct WantListEntry or NULL if error
|
||||||
|
*/
|
||||||
|
struct WantListQueueEntry* ipfs_bitswap_wantlist_queue_add(struct WantListQueue* wantlist, const struct Cid* cid, const struct WantListSession* session);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Remove (decrement the counter) a Cid from the WantList
|
||||||
|
* @param wantlist the WantList
|
||||||
|
* @param cid the Cid
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_wantlist_queue_remove(struct WantListQueue* wantlist, const struct Cid* cid, const struct WantListSession* session);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Find a Cid in the WantList
|
||||||
|
* @param wantlist the list
|
||||||
|
* @param cid the Cid
|
||||||
|
* @returns the WantListQueueEntry
|
||||||
|
*/
|
||||||
|
struct WantListQueueEntry* ipfs_bitswap_wantlist_queue_find(struct WantListQueue* wantlist, const struct Cid* cid);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* compare 2 sessions for equality
|
||||||
|
* @param a side a
|
||||||
|
* @param b side b
|
||||||
|
* @returns 0 if equal, <0 if A wins, >0 if b wins
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_wantlist_session_compare(const struct WantListSession* a, const struct WantListSession* b);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new WantListSession
|
||||||
|
* @returns the newly allocated WantListSession
|
||||||
|
*/
|
||||||
|
struct WantListSession* ipfs_bitswap_wantlist_session_new();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the Bitswap engine, this processes an item on the WantListQueue
|
||||||
|
* @param context the context
|
||||||
|
* @param entry the WantListQueueEntry
|
||||||
|
* @returns true(1) on success, false(0) if not.
|
||||||
|
*/
|
||||||
|
int ipfs_bitswap_wantlist_process_entry(struct BitswapContext* context, struct WantListQueueEntry* entry);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Pops the top one off the queue
|
||||||
|
*
|
||||||
|
* @param wantlist the list
|
||||||
|
* @returns the WantListQueueEntry
|
||||||
|
*/
|
||||||
|
struct WantListQueueEntry* ipfs_bitswap_wantlist_queue_pop(struct WantListQueue* wantlist);
|
||||||
|
|
77
include/ipfs/exchange/exchange.h
Normal file
77
include/ipfs/exchange/exchange.h
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
#pragma once
|
||||||
|
/**
|
||||||
|
* This is the definition of an "Exchange"
|
||||||
|
*
|
||||||
|
* Anything that implements the Exchange interface can be used as
|
||||||
|
* an IPFS block exchange protocol.
|
||||||
|
*/
|
||||||
|
#include "ipfs/blocks/block.h"
|
||||||
|
#include "ipfs/cid/cid.h"
|
||||||
|
#include "libp2p/utils/vector.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These are methods that the local IPFS daemon (or client)
|
||||||
|
* call to communicate with the local repository or network
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct Exchange {
|
||||||
|
/**
|
||||||
|
* Retrieve a block from peers within the deadline enforced
|
||||||
|
* by the context
|
||||||
|
*
|
||||||
|
* NOTE: Shouldn't the block parameter be a callback (function pointer)?
|
||||||
|
* Otherwise, this function is going to block. Is that what we want?
|
||||||
|
*
|
||||||
|
* @param context the context
|
||||||
|
* @param cid the hash of the block to retrieve
|
||||||
|
* @param block a pointer to the block (allocated by this method if return is true)
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int (*GetBlock)(struct Exchange* exchange, struct Cid* cid, struct Block** block);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a block from peers asynchronously
|
||||||
|
*
|
||||||
|
* @param context the context
|
||||||
|
* @param cid the hash of the block to retrieve
|
||||||
|
* @param queue_entry the queue entry to watch
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int (*GetBlockAsync)(struct Exchange* exchange, struct Cid* cid, struct Block** block);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve several blocks
|
||||||
|
* @param context the context
|
||||||
|
* @param Cids a vector of hashes for the blocks to be retrieved
|
||||||
|
* @param blocks a pointer to a vector of retrieved blocks (will be NULL on error)
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int (*GetBlocks)(struct Exchange* exchange, struct Libp2pVector* Cids, struct Libp2pVector** blocks);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Announces the existance of a block to this bitswap service. The service will
|
||||||
|
* potentially notify its peers.
|
||||||
|
* NOTE: This is mainly designed to announce blocks added by non-bitswap methods (i.e. the local user)
|
||||||
|
* @param block the block being announced
|
||||||
|
* @returns true(1) on success, false(0) if not
|
||||||
|
*/
|
||||||
|
int (*HasBlock)(struct Exchange* exchange, struct Block* block);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if we're online
|
||||||
|
* @returns true(1) if we're online
|
||||||
|
*/
|
||||||
|
int (*IsOnline)(struct Exchange*);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close up the exchange, and go offline
|
||||||
|
* @returns true(1);
|
||||||
|
*/
|
||||||
|
int (*Close)(struct Exchange*);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by each implementation to maintain state
|
||||||
|
* (will be cast-ed to an implementation-specific structure)
|
||||||
|
*/
|
||||||
|
void* exchangeContext;
|
||||||
|
};
|
56
include/ipfs/importer/exporter.h
Normal file
56
include/ipfs/importer/exporter.h
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ipfs/cmd/cli.h"
|
||||||
|
#include "ipfs/core/ipfs_node.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pull bytes from the hashtable
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get a file by its hash, and write the data to a file
|
||||||
|
* @param hash the base58 multihash of the cid
|
||||||
|
* @param file_name the file name to write to
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_exporter_to_file(const unsigned char* hash, const char* file_name, struct IpfsNode* local_node);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Retrieve a protobuf'd Node from the router
|
||||||
|
* @param local_node the context
|
||||||
|
* @param hash the hash to retrieve
|
||||||
|
* @param hash_size the length of the hash
|
||||||
|
* @param result a place to store the Node
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_exporter_get_node(struct IpfsNode* local_node, const unsigned char* hash, const size_t hash_size, struct HashtableNode** result);
|
||||||
|
|
||||||
|
int ipfs_exporter_object_get(int argc, char** argv);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Called from the command line with ipfs cat [hash]. Retrieves the object pointed to by hash, and displays its block data (links and data elements)
|
||||||
|
* @param argc number of arguments
|
||||||
|
* @param argv arguments
|
||||||
|
* @param output_file where to stream the results
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_exporter_object_cat(struct CliArguments* args, FILE* output_file);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the object pointed to by hash and displays the raw data
|
||||||
|
* @param local_node the local node
|
||||||
|
* @param hash the hash to use
|
||||||
|
* @param hash_size the length of the hash
|
||||||
|
* @param file the file descrptor to write to
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_exporter_object_cat_to_file(struct IpfsNode *local_node, unsigned char* hash, int hash_size, FILE* file);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rebuild a file based on this HashtableNode, traversing links
|
||||||
|
* @param node the HashtableNode to start with
|
||||||
|
* @param local_node the context
|
||||||
|
* @param file the filestream to fill
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_exporter_cat_node(struct HashtableNode* node, struct IpfsNode* local_node, FILE *file);
|
32
include/ipfs/importer/importer.h
Normal file
32
include/ipfs/importer/importer.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#ifndef __IPFS_IMPORTER_IMPORTER_H__
|
||||||
|
#define __IPFS_IMPORTER_IMPORTER_H__
|
||||||
|
|
||||||
|
#include "ipfs/cmd/cli.h"
|
||||||
|
#include "ipfs/merkledag/node.h"
|
||||||
|
#include "ipfs/core/ipfs_node.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a node based on an incoming file or directory
|
||||||
|
* NOTE: this can be called recursively for directories
|
||||||
|
* NOTE: When this function completes, parent_node will be either:
|
||||||
|
* 1) the complete file, in the case of a small file (<256k-ish)
|
||||||
|
* 2) a node with links to the various pieces of a large file
|
||||||
|
* 3) a node with links to files and directories if 'fileName' is a directory
|
||||||
|
* @param root_dir the directory for where to look for the file
|
||||||
|
* @param file_name the file (or directory) to import
|
||||||
|
* @param parent_node the root node (has links to others in case this is a large file and is split)
|
||||||
|
* @param fs_repo the ipfs repository
|
||||||
|
* @param bytes_written number of bytes written to disk
|
||||||
|
* @param recursive true if we should navigate directories
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_import_file(const char* root, const char* fileName, struct HashtableNode** parent_node, struct IpfsNode *local_node, size_t* bytes_written, int recursive);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* called from the command line
|
||||||
|
* @param argc the number of arguments
|
||||||
|
* @param argv the arguments
|
||||||
|
*/
|
||||||
|
int ipfs_import_files(struct CliArguments* args);
|
||||||
|
|
||||||
|
#endif /* INCLUDE_IPFS_IMPORTER_IMPORTER_H_ */
|
25
include/ipfs/importer/resolver.h
Normal file
25
include/ipfs/importer/resolver.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ipfs/merkledag/node.h"
|
||||||
|
#include "ipfs/core/ipfs_node.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements a resover. EOM
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interogate the path and the current node, looking
|
||||||
|
* for the desired node.
|
||||||
|
* @param path the current path
|
||||||
|
* @param from the current node (or NULL if it is the first call)
|
||||||
|
* @returns what we are looking for, or NULL if it wasn't found
|
||||||
|
*/
|
||||||
|
struct HashtableNode* ipfs_resolver_get(const char* path, struct HashtableNode* from, const struct IpfsNode* ipfs_node);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interrogate the path, looking for the peer
|
||||||
|
* @param path the peer path to search for
|
||||||
|
* @param ipfs_node the context
|
||||||
|
* @returns the MultiAddress that relates to the path, or NULL if not found
|
||||||
|
*/
|
||||||
|
struct Libp2pPeer* ipfs_resolver_find_peer(const char* path, const struct IpfsNode* ipfs_node);
|
50
include/ipfs/journal/journal.h
Normal file
50
include/ipfs/journal/journal.h
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#pragma once
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "libp2p/conn/session.h"
|
||||||
|
#include "ipfs/core/ipfs_node.h"
|
||||||
|
#include "libp2p/net/protocol.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The journal protocol attempts to keep a journal in sync with other (approved) nodes
|
||||||
|
*/
|
||||||
|
|
||||||
|
/***
|
||||||
|
* See if we can handle this message
|
||||||
|
* @param incoming the incoming message
|
||||||
|
* @param incoming_size the size of the incoming message
|
||||||
|
* @returns true(1) if the protocol in incoming is something we can handle. False(0) otherwise.
|
||||||
|
*/
|
||||||
|
int ipfs_journal_can_handle(const struct StreamMessage* msg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean up resources used by this handler
|
||||||
|
* @param context the context to clean up
|
||||||
|
* @returns true(1)
|
||||||
|
*/
|
||||||
|
int ipfs_journal_shutdown_handler(void* context);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Handles a message
|
||||||
|
* @param incoming the message
|
||||||
|
* @param incoming_size the size of the message
|
||||||
|
* @param session_context details of the remote peer
|
||||||
|
* @param protocol_context in this case, an IpfsNode
|
||||||
|
* @returns 0 if the caller should not continue looping, <0 on error, >0 on success
|
||||||
|
*/
|
||||||
|
int ipfs_journal_handle_message(const struct StreamMessage* msg, struct Stream* stream, void* protocol_context) ;
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Build the protocol handler struct for the Journal protocol
|
||||||
|
* @param local_node what to stuff in the context
|
||||||
|
* @returns the protocol handler
|
||||||
|
*/
|
||||||
|
struct Libp2pProtocolHandler* ipfs_journal_build_protocol_handler(const struct IpfsNode* local_node);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Send a journal message to a remote peer
|
||||||
|
* @param replication_peer the peer to send it to
|
||||||
|
* @returns true(1) on success, false(0) otherwise.
|
||||||
|
*/
|
||||||
|
int ipfs_journal_sync(struct IpfsNode* local_node, struct ReplicationPeer* replication_peer);
|
43
include/ipfs/journal/journal_entry.h
Normal file
43
include/ipfs/journal/journal_entry.h
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A journal entry protobuf
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct JournalEntry {
|
||||||
|
unsigned long long timestamp;
|
||||||
|
uint8_t pin;
|
||||||
|
uint8_t *hash;
|
||||||
|
size_t hash_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JournalEntry* ipfs_journal_entry_new();
|
||||||
|
|
||||||
|
int ipfs_journal_entry_free(struct JournalEntry* entry);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the maximum size of a protobuf'd JournalEntry
|
||||||
|
*/
|
||||||
|
int ipfs_journal_entry_encode_size(struct JournalEntry* entry);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Protobuf the journal entry
|
||||||
|
* @param entry the JournalEntry to protobuf
|
||||||
|
* @param buffer where to place the results
|
||||||
|
* @param max_buffer_size the amount of memory allocated for the buffer
|
||||||
|
* @param bytes_used the amount of the buffer used
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_journal_entry_encode(struct JournalEntry* entry, uint8_t *buffer, size_t max_buffer_size, size_t *bytes_used);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Turn a protobuf'd JournalEntry and turn it into a real JournalEntry
|
||||||
|
* @param incoming the incoming bytes
|
||||||
|
* @param incoming_size the size of the incoming buffer
|
||||||
|
* @param results where to put the new JournalEntry
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_journal_entry_decode(uint8_t *incoming, size_t incoming_size, struct JournalEntry **results);
|
42
include/ipfs/journal/journal_message.h
Normal file
42
include/ipfs/journal/journal_message.h
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "libp2p/utils/vector.h"
|
||||||
|
|
||||||
|
struct JournalMessage {
|
||||||
|
unsigned long long current_epoch;
|
||||||
|
unsigned long long start_epoch;
|
||||||
|
unsigned long long end_epoch;
|
||||||
|
struct Libp2pVector* journal_entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JournalMessage* ipfs_journal_message_new();
|
||||||
|
int ipfs_journal_message_free(struct JournalMessage* message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the maximum size of a protobuf'd JournalMessage
|
||||||
|
* @param message the JournalMessage
|
||||||
|
* @returns the maximum size of this message in bytes if it were protobuf'd
|
||||||
|
*/
|
||||||
|
int ipfs_journal_message_encode_size(struct JournalMessage* message);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Protobuf the journal message
|
||||||
|
* @param message the JournalMessage to protobuf
|
||||||
|
* @param buffer where to place the results
|
||||||
|
* @param max_buffer_size the amount of memory allocated for the buffer
|
||||||
|
* @param bytes_used the amount of the buffer used
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_journal_message_encode(struct JournalMessage* entry, uint8_t *buffer, size_t max_buffer_size, size_t *bytes_used);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Turn a protobuf'd JournalMessage and turn it into a real JournalMessage
|
||||||
|
* @param incoming the incoming bytes
|
||||||
|
* @param incoming_size the size of the incoming buffer
|
||||||
|
* @param results where to put the new JournalMessage
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_journal_message_decode(const uint8_t *incoming, size_t incoming_size, struct JournalMessage **results);
|
|
@ -43,15 +43,15 @@ int main(int argc, char** argv)
|
||||||
////Nodes/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////Nodes/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
//N_Create_From_Link
|
//N_Create_From_Link
|
||||||
struct Node * Mynode;
|
struct HashtableNode * Mynode;
|
||||||
Mynode = N_Create_From_Link(mylink);
|
Mynode = N_Create_From_Link(mylink);
|
||||||
mylink->name = "HAHA";//Testing for valid node creation
|
mylink->name = "HAHA";//Testing for valid node creation
|
||||||
printf("Node Link[0] Name: %s\nHash: %s\n",Mynode->links[0]->name, Mynode->links[0]->Lcid->hash);
|
printf("Node Link[0] Name: %s\nHash: %s\n",Mynode->head_link[0]->name, Mynode->head_link[0]->Lcid->hash);
|
||||||
|
|
||||||
//N_Add_Link
|
//N_Add_Link
|
||||||
Mynode = N_Add_Link(&Mynode, mylink2, sizeof(mylink2));
|
Mynode = N_Add_Link(&Mynode, mylink2, sizeof(mylink2));
|
||||||
mylink2->name = "HAHA";//Testing for valid node creation
|
mylink2->name = "HAHA";//Testing for valid node creation
|
||||||
printf("Node Link[1] Name: %s\nHash: %s\n",Mynode->links[1]->name,Mynode->links[1]->Lcid->hash);
|
printf("Node Link[1] Name: %s\nHash: %s\n",Mynode->head_link[1]->name,Mynode->head_link[1]->Lcid->hash);
|
||||||
|
|
||||||
//Node_Get_Link
|
//Node_Get_Link
|
||||||
struct Link * ResultLink = Node_Get_Link(Mynode, "Simo");
|
struct Link * ResultLink = Node_Get_Link(Mynode, "Simo");
|
||||||
|
@ -72,9 +72,9 @@ int main(int argc, char** argv)
|
||||||
printf("Outlinkamt: %d\n", Mynode->link_ammount);
|
printf("Outlinkamt: %d\n", Mynode->link_ammount);
|
||||||
|
|
||||||
//Node Copy
|
//Node Copy
|
||||||
struct Node * Node2;
|
struct HashtableNode * Node2;
|
||||||
Node2 = Node_Copy(Mynode);
|
Node2 = Node_Copy(Mynode);
|
||||||
printf("NODE COPY TEST: [0]: %s\n", Node2->links[0]->Lcid->hash);
|
printf("NODE COPY TEST: [0]: %s\n", Node2->head_link[0]->Lcid->hash);
|
||||||
Node_Delete(Node2);
|
Node_Delete(Node2);
|
||||||
|
|
||||||
//Node_Set_Data
|
//Node_Set_Data
|
45
include/ipfs/merkledag/merkledag.h
Normal file
45
include/ipfs/merkledag/merkledag.h
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/***
|
||||||
|
* Merkledag methods
|
||||||
|
*/
|
||||||
|
#ifndef __IPFS_MERKLEDAG_H__
|
||||||
|
#define __IPFS_MERKLEDAG_H__
|
||||||
|
|
||||||
|
#include "ipfs/merkledag/node.h"
|
||||||
|
#include "ipfs/repo/fsrepo/fs_repo.h"
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Adds a node to the dagService and blockService
|
||||||
|
* @param node the node to add
|
||||||
|
* @param fs_repo the repo to add to
|
||||||
|
* @param bytes_written the number of bytes written
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_merkledag_add(struct HashtableNode* node, struct FSRepo* fs_repo, size_t* bytes_written);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Retrieves a node from the datastore based on the cid
|
||||||
|
* @param cid the key to look for
|
||||||
|
* @param node the node to be created
|
||||||
|
* @param fs_repo the repository
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_merkledag_get(const unsigned char* hash, size_t hash_size, struct HashtableNode** node, const struct FSRepo* fs_repo);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Retrieves a node from the datastore based on the multihash
|
||||||
|
* @param multihash the base58 encoded multihash (should start with Qm) as a null terminated string
|
||||||
|
* @param node the node to be created
|
||||||
|
* @param fs_repo the repository
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_merkledag_get_by_multihash(const unsigned char* multihash, size_t multihash_length, struct HashtableNode** node, const struct FSRepo* fs_repo);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Convert the data within a block to a HashtableNode
|
||||||
|
* @param block the block
|
||||||
|
* @param node_ptr where to put the results
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_merkledag_convert_block_to_node(struct Block* block, struct HashtableNode** node_ptr);
|
||||||
|
|
||||||
|
#endif
|
296
include/ipfs/merkledag/node.h
Normal file
296
include/ipfs/merkledag/node.h
Normal file
|
@ -0,0 +1,296 @@
|
||||||
|
/**
|
||||||
|
* An implementation of an IPFS node
|
||||||
|
* Copying the go-ipfs-node project
|
||||||
|
*/
|
||||||
|
#ifndef IPFS_NODE_H
|
||||||
|
#define IPFS_NODE_H
|
||||||
|
|
||||||
|
#include "ipfs/cid/cid.h"
|
||||||
|
|
||||||
|
/*====================================================================================
|
||||||
|
*
|
||||||
|
* Structures
|
||||||
|
*
|
||||||
|
*===================================================================================*/
|
||||||
|
|
||||||
|
struct NodeLink
|
||||||
|
{
|
||||||
|
size_t hash_size;
|
||||||
|
unsigned char* hash;
|
||||||
|
char* name;
|
||||||
|
size_t t_size;
|
||||||
|
struct NodeLink* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HashtableNode
|
||||||
|
{
|
||||||
|
// saved in protobuf
|
||||||
|
size_t data_size;
|
||||||
|
unsigned char* data;
|
||||||
|
struct NodeLink* head_link;
|
||||||
|
// not saved in protobuf
|
||||||
|
unsigned char* encoded;
|
||||||
|
// a base32 representation of the multihash
|
||||||
|
unsigned char* hash;
|
||||||
|
size_t hash_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*====================================================================================
|
||||||
|
*
|
||||||
|
* Functions
|
||||||
|
*
|
||||||
|
*===================================================================================*/
|
||||||
|
|
||||||
|
/*====================================================================================
|
||||||
|
* Link Functions
|
||||||
|
*===================================================================================*/
|
||||||
|
|
||||||
|
/* Create_Link
|
||||||
|
* @Param name: The name of the link (char *)
|
||||||
|
* @Param ahash: An Qmhash
|
||||||
|
* @param hash_size the size of the hash
|
||||||
|
* @param node_link a pointer to the new struct NodeLink
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_node_link_create(char * name, unsigned char * ahash, size_t hash_size, struct NodeLink** node_link);
|
||||||
|
|
||||||
|
/****
|
||||||
|
* Allocate memory for a new NodeLink
|
||||||
|
* @param node_link a pointer to the newly allocated memory
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_node_link_new(struct NodeLink** node_link);
|
||||||
|
|
||||||
|
/* ipfs_node_link_free
|
||||||
|
* @param L: Free the link you have allocated.
|
||||||
|
*/
|
||||||
|
int ipfs_node_link_free(struct NodeLink * node_link);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Node protobuf functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Get the approximate size needed to protobuf encode this link
|
||||||
|
* @param link the link to examine
|
||||||
|
* @returns the maximum size that should be needed
|
||||||
|
*/
|
||||||
|
size_t ipfs_node_link_protobuf_encode_size(const struct NodeLink* link);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Encode a NodeLink into protobuf format
|
||||||
|
* @param link the link
|
||||||
|
* @param buffer where to put the encoded results
|
||||||
|
* @param max_buffer_length the max size that should be put in buffer
|
||||||
|
* @pram bytes_written the amount of the buffer used
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_node_link_protobuf_encode(const struct NodeLink* link, unsigned char* buffer, size_t max_buffer_length, size_t* bytes_written);
|
||||||
|
|
||||||
|
/****
|
||||||
|
* Decode from a byte array into a NodeLink
|
||||||
|
* @param buffer the byte array
|
||||||
|
* @param buffer_length the length of the byte array
|
||||||
|
* @param link the pointer to the new NodeLink (NOTE: Will be allocated in this function)
|
||||||
|
* @param bytes_read the amount of bytes read by this function
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_node_link_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct NodeLink** link);
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* return an approximate size of the encoded node
|
||||||
|
* @param node the node to examine
|
||||||
|
* @returns the max size of an encoded stream of bytes, if it were encoded
|
||||||
|
*/
|
||||||
|
size_t ipfs_hashtable_node_protobuf_encode_size(const struct HashtableNode* node);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Encode a node into a protobuf byte stream
|
||||||
|
* @param node the node to encode
|
||||||
|
* @param buffer where to put it
|
||||||
|
* @param max_buffer_length the length of buffer
|
||||||
|
* @param bytes_written how much of buffer was used
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_hashtable_node_protobuf_encode(const struct HashtableNode* node, unsigned char* buffer, size_t max_buffer_length, size_t* bytes_written);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Decode a stream of bytes into a Node structure
|
||||||
|
* @param buffer where to get the bytes from
|
||||||
|
* @param buffer_length the length of buffer
|
||||||
|
* @param node pointer to the Node to be created
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_hashtable_node_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct HashtableNode** node);
|
||||||
|
|
||||||
|
/*====================================================================================
|
||||||
|
* Node Functions
|
||||||
|
*===================================================================================*/
|
||||||
|
|
||||||
|
/****
|
||||||
|
* Creates an empty node, allocates the required memory
|
||||||
|
* @param node the pointer to the memory allocated
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_hashtable_node_new(struct HashtableNode** node);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Allocates memory for a node, and sets the data section to indicate
|
||||||
|
* that this node is a directory
|
||||||
|
* @param node the node to initialize
|
||||||
|
* @returns true(1) on success, otherwise false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_hashtable_node_create_directory(struct HashtableNode** node);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Determine if this node is actually a directory
|
||||||
|
* @param node the node to examine
|
||||||
|
* @returns true(1) if this node is a directory. Otherwise, false(0)
|
||||||
|
*/
|
||||||
|
int ipfs_hashtable_node_is_directory(struct HashtableNode* node);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sets the Cid into the struct element titled cached
|
||||||
|
* @param node the node to work with
|
||||||
|
* @param cid the cid
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_hashtable_node_set_hash(struct HashtableNode* node, const unsigned char* hash, size_t hash_size);
|
||||||
|
|
||||||
|
/*ipfs_node_set_data
|
||||||
|
* Sets the data of a node
|
||||||
|
* @param Node: The node which you want to set data in.
|
||||||
|
* @param Data, the data you want to assign to the node
|
||||||
|
* Sets pointers of encoded & cached to NULL /following go method
|
||||||
|
* returns 1 on success 0 on failure
|
||||||
|
*/
|
||||||
|
int ipfs_hashtable_node_set_data(struct HashtableNode * N, unsigned char * Data, size_t data_size);
|
||||||
|
|
||||||
|
/*ipfs_node_set_encoded
|
||||||
|
* @param NODE: the node you wish to alter (struct Node *)
|
||||||
|
* @param Data: The data you wish to set in encoded.(unsigned char *)
|
||||||
|
* returns 1 on success 0 on failure
|
||||||
|
*/
|
||||||
|
int ipfs_hashtable_node_set_encoded(struct HashtableNode * N, unsigned char * Data);
|
||||||
|
|
||||||
|
/*ipfs_node_get_data
|
||||||
|
* Gets data from a node
|
||||||
|
* @param Node: = The node you want to get data from. (unsigned char *)
|
||||||
|
* Returns data of node.
|
||||||
|
*/
|
||||||
|
unsigned char * ipfs_hashtable_node_get_data(struct HashtableNode * N);
|
||||||
|
|
||||||
|
/*ipfs_node_free
|
||||||
|
* Once you are finished using a node, always delete it using this.
|
||||||
|
* It will take care of the links inside it.
|
||||||
|
* @param N: the node you want to free. (struct Node *)
|
||||||
|
*/
|
||||||
|
int ipfs_hashtable_node_free(struct HashtableNode * N);
|
||||||
|
|
||||||
|
/*ipfs_node_get_link_by_name
|
||||||
|
* Returns a copy of the link with given name
|
||||||
|
* @param Name: (char * name) searches for link with this name
|
||||||
|
* Returns the link struct if it's found otherwise returns NULL
|
||||||
|
*/
|
||||||
|
struct NodeLink * ipfs_hashtable_node_get_link_by_name(struct HashtableNode * N, char * Name);
|
||||||
|
|
||||||
|
/*ipfs_node_remove_link_by_name
|
||||||
|
* Removes a link from node if found by name.
|
||||||
|
* @param name: Name of link (char * name)
|
||||||
|
* returns 1 on success, 0 on failure.
|
||||||
|
*/
|
||||||
|
int ipfs_hashtable_node_remove_link_by_name(char * Name, struct HashtableNode * mynode);
|
||||||
|
|
||||||
|
/* ipfs_node_add_link
|
||||||
|
* Adds a link to your node
|
||||||
|
* @param mynode: &yournode
|
||||||
|
* @param mylink: the CID you want to create a node from
|
||||||
|
* @param linksz: sizeof(your cid here)
|
||||||
|
* Returns your node with the newly added link
|
||||||
|
*/
|
||||||
|
int ipfs_hashtable_node_add_link(struct HashtableNode * mynode, struct NodeLink * mylink);
|
||||||
|
|
||||||
|
/*ipfs_node_new_from_link
|
||||||
|
* Create a node from a link
|
||||||
|
* @param mylink: the link you want to create it from. (struct Cid *)
|
||||||
|
* @param node the pointer to the new node
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_hashtable_node_new_from_link(struct NodeLink * mylink, struct HashtableNode** node);
|
||||||
|
|
||||||
|
/*ipfs_node_new_from_data
|
||||||
|
* @param data: bytes buffer you want to create the node from
|
||||||
|
* @param data_size the size of the data
|
||||||
|
* @param node the pointer to the new node
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_hashtable_node_new_from_data(unsigned char * data, size_t data_size, struct HashtableNode** node);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* create a Node struct from encoded data
|
||||||
|
* @param data: encoded bytes buffer you want to create the node from. Note: this copies the pointer, not a memcpy
|
||||||
|
* @param node a pointer to the node that will be created
|
||||||
|
* @returns true(1) on success
|
||||||
|
*/
|
||||||
|
int ipfs_hashtable_node_new_from_encoded(unsigned char * data, struct HashtableNode** node);
|
||||||
|
|
||||||
|
/*Node_Resolve_Max_Size
|
||||||
|
* !!!This shouldn't concern you!
|
||||||
|
*Gets the ammount of words that will be returned by Node_Resolve
|
||||||
|
*@Param1: The string that will be processed (eg: char * sentence = "foo/bar/bin")
|
||||||
|
*Returns either -1 if something went wrong or the ammount of words that would be processed.
|
||||||
|
*/
|
||||||
|
int Node_Resolve_Max_Size(char * input1);
|
||||||
|
|
||||||
|
/*Node_Resolve Basically stores everything in a pointer array eg: char * bla[Max_Words_]
|
||||||
|
* !!!This shouldn't concern you!!!
|
||||||
|
*@param1: Pointer array(char * foo[x], X=Whatever ammount there is. should be used with the helper function Node_Resolve_Max_Size)
|
||||||
|
*@param2: Sentence to gather words/paths from (Eg: char * meh = "foo/bar/bin")
|
||||||
|
*@Returns 1 or 0, 0 if something went wrong, 1 if everything went smoothly.
|
||||||
|
*/
|
||||||
|
int Node_Resolve(char ** result, char * input1);
|
||||||
|
|
||||||
|
/**************************************************************************************************************************************
|
||||||
|
*|||||||||||||||||||||||||||||||||||||||| !!!! IMPORTANT !!! ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||*
|
||||||
|
**************************************************************************************************************************************
|
||||||
|
* Not sure where this is used, I'm making something to easen it up for all of you.
|
||||||
|
* This in itself will get all the links for you in a link[] array inside Link_Proc
|
||||||
|
* the memory allocation for storage will be noted in the ammount of links.
|
||||||
|
* After being done with this, you have to free the array for which you will have a function specially made for you.
|
||||||
|
*
|
||||||
|
* What this does:
|
||||||
|
* It searches for links using a path like /foo/bar/bin/, if links with those names are found in the node you specify, it stores them
|
||||||
|
* in a custom struct, Link_Proc; which is what Node_Resolve_Link returns.
|
||||||
|
* Notes:
|
||||||
|
* Use it, free it, it's all already laid out for you.
|
||||||
|
* There will also be a tutorial demonstrating it in the same folder here so everyone can understand this.
|
||||||
|
*/
|
||||||
|
struct Link_Proc
|
||||||
|
{
|
||||||
|
char * remaining_links; // Not your concern.
|
||||||
|
int ammount; //This will store the ammount of links, so you know what to process.
|
||||||
|
struct NodeLink * links[]; // Link array
|
||||||
|
};
|
||||||
|
|
||||||
|
/*Node_Resolve_Links
|
||||||
|
* Processes a path returning all links.
|
||||||
|
* @param N: The node you want to get links from
|
||||||
|
* @param path: The "foo/bar/bin" path
|
||||||
|
*/
|
||||||
|
struct Link_Proc * Node_Resolve_Links(struct HashtableNode * N, char * path);
|
||||||
|
|
||||||
|
/*Free_link_Proc
|
||||||
|
* frees the Link_Proc struct you created.
|
||||||
|
* @param1: Link_Proc struct (struct Link_Proc *)
|
||||||
|
*/
|
||||||
|
void Free_Link_Proc(struct Link_Proc * LPRC);
|
||||||
|
|
||||||
|
/*Node_Tree() Basically a unix-like ls
|
||||||
|
*@Param1: Result char * foo[strlen(sentence)]
|
||||||
|
*@Param2: char sentence[] = foo/bar/bin/whatever
|
||||||
|
*Return: 0 if failure, 1 if success
|
||||||
|
*/
|
||||||
|
int Node_Tree(char * result, char * input1); //I don't know where you use this but here it is.
|
||||||
|
|
||||||
|
#endif
|
21
include/ipfs/namesys/name.h
Normal file
21
include/ipfs/namesys/name.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ipfs/cmd/cli.h"
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Publish IPNS name
|
||||||
|
*/
|
||||||
|
int ipfs_name_publish(struct IpfsNode* local_node, char* name, char **response, size_t *response_size);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Resolve IPNS name
|
||||||
|
*/
|
||||||
|
int ipfs_name_resolve(struct IpfsNode* local_node, char* name, char **response, size_t *response_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We received a cli command "ipfs name". Do the right thing.
|
||||||
|
* @param argc number of arguments on the command line
|
||||||
|
* @param argv actual command line arguments
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_name(struct CliArguments* args);
|
|
@ -3,40 +3,7 @@
|
||||||
|
|
||||||
#define DefaultDepthLimit 32
|
#define DefaultDepthLimit 32
|
||||||
|
|
||||||
#ifdef NAMESYS_C
|
#include "ipfs/util/errs.h"
|
||||||
char *ErrNamesys[] = {
|
|
||||||
NULL,
|
|
||||||
"ErrAllocFailed",
|
|
||||||
"ErrNULLPointer",
|
|
||||||
"ErrPipe",
|
|
||||||
"ErrPoll",
|
|
||||||
"Could not publish name."
|
|
||||||
"Could not resolve name.",
|
|
||||||
"Could not resolve name (recursion limit exceeded).",
|
|
||||||
"expired record",
|
|
||||||
"unrecognized validity type",
|
|
||||||
"not a valid proquint string",
|
|
||||||
"not a valid domain name",
|
|
||||||
"not a valid dnslink entry"
|
|
||||||
};
|
|
||||||
#else
|
|
||||||
extern char *ErrNamesys;
|
|
||||||
#endif // NAMESYS_C
|
|
||||||
|
|
||||||
enum {
|
|
||||||
ErrAllocFailed = 1,
|
|
||||||
ErrNULLPointer,
|
|
||||||
ErrPipe,
|
|
||||||
ErrPoll,
|
|
||||||
ErrPublishFailed,
|
|
||||||
ErrResolveFailed,
|
|
||||||
ErrResolveRecursion,
|
|
||||||
ErrExpiredRecord,
|
|
||||||
ErrUnrecognizedValidity,
|
|
||||||
ErrInvalidProquint,
|
|
||||||
ErrInvalidDomain,
|
|
||||||
ErrInvalidDNSLink
|
|
||||||
} NamesysErrs;
|
|
||||||
|
|
||||||
typedef struct s_resolvers {
|
typedef struct s_resolvers {
|
||||||
char *protocol;
|
char *protocol;
|
||||||
|
@ -65,7 +32,7 @@
|
||||||
|
|
||||||
typedef struct s_mpns {
|
typedef struct s_mpns {
|
||||||
resolvers *resolver;
|
resolvers *resolver;
|
||||||
publishers *Publisher;
|
publishers *publisher;
|
||||||
} mpns;
|
} mpns;
|
||||||
|
|
||||||
typedef struct s_tlds {
|
typedef struct s_tlds {
|
||||||
|
@ -73,30 +40,30 @@
|
||||||
int condition;
|
int condition;
|
||||||
} tlds;
|
} tlds;
|
||||||
|
|
||||||
int resolve (resolver *r, char **p, char *str, int depth, char **prefixes);
|
int ipfs_namesys_resolve(char **path, char *name);
|
||||||
int Resolve(char **path, char *name);
|
int ipfs_namesys_resolve_n(char **path, char *name, int depth);
|
||||||
int ResolveN(char **path, char *name, int depth);
|
int ipfs_namesys_resolve_once (char **path, char *name);
|
||||||
int resolveOnce (char **path, char *name);
|
int ipfs_namesys_publish (char *proto, ciPrivKey name, char *value);
|
||||||
int Publish (char *proto, ciPrivKey name, char *value);
|
int ipfs_namesys_publish_with_eol (char *proto, ciPrivKey name, char *value, time_t eol);
|
||||||
int PublishWithEOL (char *proto, ciPrivKey name, char *value, time_t eol);
|
|
||||||
|
|
||||||
int ProquintIsProquint(char *str);
|
int ipfs_proquint_is_proquint(char *str);
|
||||||
char *ProquintEncode(char *buf, int size);
|
char *ipfs_proquint_encode(char *buf, int size);
|
||||||
char *ProquintDecode(char *str);
|
char *ipfs_proquint_decode(char *str);
|
||||||
int ProquintResolveOnce (char **p, char *name);
|
int ipfs_proquint_resolve_once (char **p, char *name);
|
||||||
|
|
||||||
int domainMatchString (char *d);
|
int ipfs_isdomain_match_string (char *d);
|
||||||
int IsICANNTLD(char *s);
|
int ipfs_isdomain_is_icann_tld(char *s);
|
||||||
int IsExtendedTLD (char *s);
|
int ipfs_isdomain_is_extended_tld (char *s);
|
||||||
int IsTLD (char *s);
|
int ipfs_isdomain_is_tld (char *s);
|
||||||
int IsDomain (char *s);
|
int ipfs_isdomain_is_domain (char *s);
|
||||||
|
|
||||||
typedef struct s_DNSResolver {
|
typedef struct s_DNSResolver {
|
||||||
// TODO
|
// TODO
|
||||||
|
int (*lookupTXT) (char ***, char *);
|
||||||
} DNSResolver;
|
} DNSResolver;
|
||||||
|
|
||||||
int DNSResolverResolveOnce (DNSResolver *r, char **path, char *name);
|
int ipfs_dns_resolver_resolve_once (char **path, char *name);
|
||||||
int workDomain (int output, DNSResolver *r, char *name);
|
int ipfs_dns_work_domain (int output, DNSResolver *r, char *name);
|
||||||
int parseEntry (char **Path, char *txt);
|
int ipfs_dns_parse_entry (char **path, char *txt);
|
||||||
int tryParseDnsLink (char **Path, char *txt);
|
int ipfs_dns_try_parse_dns_link(char **path, char *txt);
|
||||||
#endif //NAMESYS_H
|
#endif //NAMESYS_H
|
||||||
|
|
38
include/ipfs/namesys/pb.h
Normal file
38
include/ipfs/namesys/pb.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef int32_t IpnsEntry_ValidityType;
|
||||||
|
|
||||||
|
struct ipns_entry {
|
||||||
|
char *value;
|
||||||
|
char *signature;
|
||||||
|
int32_t *validityType;
|
||||||
|
char *validity;
|
||||||
|
uint64_t *sequence;
|
||||||
|
uint64_t *ttl;
|
||||||
|
struct routingResolver *cache; // cache and eol should be the last items.
|
||||||
|
struct timespec *eol;
|
||||||
|
};
|
||||||
|
struct namesys_pb {
|
||||||
|
// TODO
|
||||||
|
struct ipns_entry *IpnsEntry;
|
||||||
|
};
|
||||||
|
|
||||||
|
// setting an EOL says "this record is valid until..."
|
||||||
|
const static IpnsEntry_ValidityType IpnsEntry_EOL = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
static char *IpnsEntry_ValidityType_name[] = {
|
||||||
|
"EOL",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
int IpnsEntry_ValidityType_value (char *s);
|
||||||
|
struct ipns_entry* ipfs_namesys_pb_new_ipns_entry ();
|
||||||
|
char* ipfs_namesys_pb_get_validity (struct ipns_entry*);
|
||||||
|
char* ipns_entry_data_for_sig(struct ipns_entry*);
|
||||||
|
char* ipfs_ipns_entry_get_signature(struct ipns_entry*);
|
||||||
|
int ipfs_namesys_pb_get_value (char**, struct ipns_entry*);
|
||||||
|
IpnsEntry_ValidityType ipfs_namesys_pb_get_validity_type (struct ipns_entry*);
|
||||||
|
void ipfs_namesys_ipnsentry_reset (struct ipns_entry *m);
|
21
include/ipfs/namesys/publisher.h
Normal file
21
include/ipfs/namesys/publisher.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ipfs/cid/cid.h"
|
||||||
|
#include "ipfs/core/ipfs_node.h"
|
||||||
|
#include "ipfs/namesys/pb.h"
|
||||||
|
|
||||||
|
char* ipns_entry_data_for_sig (struct ipns_entry *entry);
|
||||||
|
int ipns_selector_func (int *idx, struct ipns_entry ***recs, char *k, char **vals);
|
||||||
|
int ipns_select_record (int *idx, struct ipns_entry **recs, char **vals);
|
||||||
|
// ipns_validate_ipns_record implements ValidatorFunc and verifies that the
|
||||||
|
// given 'val' is an IpnsEntry and that that entry is valid.
|
||||||
|
int ipns_validate_ipns_record (char *k, char *val);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store the hash locally, and notify the network
|
||||||
|
*
|
||||||
|
* @param local_node the context
|
||||||
|
* @param path the "/ipfs/" or "/ipns" path
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_namesys_publisher_publish(struct IpfsNode* local_node, char* path);
|
14
include/ipfs/namesys/resolver.h
Normal file
14
include/ipfs/namesys/resolver.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ipfs/core/ipfs_node.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve an IPNS name.
|
||||||
|
* NOTE: if recursive is set to false, the result could be another ipns path
|
||||||
|
* @param local_node the context
|
||||||
|
* @param path the ipns path (i.e. "/ipns/Qm12345..")
|
||||||
|
* @param recursive true to resolve until the result is not ipns, false to simply get the next step in the path
|
||||||
|
* @param result the results (i.e. "/ipfs/QmAb12CD...")
|
||||||
|
* @returns true(1) on success, false(0) otherwise
|
||||||
|
*/
|
||||||
|
int ipfs_namesys_resolver_resolve(struct IpfsNode* local_node, const char* path, int recursive, char** results);
|
40
include/ipfs/namesys/routing.h
Normal file
40
include/ipfs/namesys/routing.h
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#ifndef IPNS_NAMESYS_ROUTING_H
|
||||||
|
#define IPNS_NAMESYS_ROUTING_H
|
||||||
|
|
||||||
|
#include "mh/multihash.h"
|
||||||
|
#include "ipfs/util/time.h"
|
||||||
|
#include "ipfs/namesys/pb.h"
|
||||||
|
|
||||||
|
#define DefaultResolverCacheTTL 60 // a minute
|
||||||
|
|
||||||
|
struct cacheEntry {
|
||||||
|
char *key;
|
||||||
|
char *value;
|
||||||
|
struct timespec eol;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct routingResolver {
|
||||||
|
int cachesize;
|
||||||
|
int next;
|
||||||
|
struct cacheEntry **data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct libp2p_routing_value_store { // dummy declaration, not implemented yet.
|
||||||
|
void *missing;
|
||||||
|
};
|
||||||
|
|
||||||
|
char* ipfs_routing_cache_get (char *key, struct ipns_entry *ientry);
|
||||||
|
void ipfs_routing_cache_set (char *key, char *value, struct ipns_entry *ientry);
|
||||||
|
struct routingResolver* ipfs_namesys_new_routing_resolver (struct libp2p_routing_value_store *route, int cachesize);
|
||||||
|
// ipfs_namesys_routing_resolve implements Resolver.
|
||||||
|
int ipfs_namesys_routing_resolve (char **path, char *name, struct namesys_pb *pb);
|
||||||
|
// ipfs_namesys_routing_resolve_n implements Resolver.
|
||||||
|
int ipfs_namesys_routing_resolve_n (char **path, char *name, int depth, struct namesys_pb *pb);
|
||||||
|
// ipfs_namesys_routing_resolve_once implements resolver. Uses the IPFS
|
||||||
|
// routing system to resolve SFS-like names.
|
||||||
|
int ipfs_namesys_routing_resolve_once (char **path, char *name, int depth, char *prefix, struct namesys_pb *pb);
|
||||||
|
int ipfs_namesys_routing_check_EOL (struct timespec *ts, struct namesys_pb *pb);
|
||||||
|
|
||||||
|
int ipfs_namesys_routing_get_value (char*, char*);
|
||||||
|
int ipfs_namesys_routing_getpublic_key (char*, unsigned char* multihash, size_t multihash_size);
|
||||||
|
#endif // IPNS_NAMESYS_ROUTING_H
|
|
@ -1,405 +0,0 @@
|
||||||
/**
|
|
||||||
* An implementation of an IPFS node
|
|
||||||
* Copying the go-ipfs-node project
|
|
||||||
*/
|
|
||||||
#ifndef IPFS_NODE_H
|
|
||||||
#define IPFS_NODE_H
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "inttypes.h"
|
|
||||||
#include "ipfs/cid/cid.h"
|
|
||||||
|
|
||||||
int debug = 0; // Set to 1 to enable debug mode of this program
|
|
||||||
|
|
||||||
/*====================================================================================
|
|
||||||
*
|
|
||||||
* Structures
|
|
||||||
*
|
|
||||||
*===================================================================================*/
|
|
||||||
|
|
||||||
struct Link
|
|
||||||
{
|
|
||||||
char * name;
|
|
||||||
size_t size;
|
|
||||||
struct Cid * Lcid;
|
|
||||||
};
|
|
||||||
struct Node
|
|
||||||
{
|
|
||||||
unsigned char * data;
|
|
||||||
unsigned char * encoded;
|
|
||||||
struct Cid * cached;
|
|
||||||
int link_ammount;
|
|
||||||
struct Link * links[];
|
|
||||||
};
|
|
||||||
/*====================================================================================
|
|
||||||
*
|
|
||||||
* Functions
|
|
||||||
*
|
|
||||||
*===================================================================================*/
|
|
||||||
|
|
||||||
/*====================================================================================
|
|
||||||
* Link Functions
|
|
||||||
*===================================================================================*/
|
|
||||||
/* Create_Link
|
|
||||||
* @Param name: The name of the link (char *)
|
|
||||||
* @Param size: Size of the link (size_t)
|
|
||||||
* @Param ahash: An Qmhash
|
|
||||||
*/
|
|
||||||
struct Link * Create_Link(char * name, unsigned char * ahash)
|
|
||||||
{
|
|
||||||
struct Link * mylink;
|
|
||||||
mylink = malloc(sizeof(struct Link));
|
|
||||||
mylink->name = name;
|
|
||||||
int ver = 0;
|
|
||||||
size_t lenhash = strlen(ahash)-1;
|
|
||||||
ipfs_cid_new(ver, ahash, lenhash*2, CID_PROTOBUF, &mylink->Lcid);
|
|
||||||
mylink->size = sizeof(mylink) + mylink->Lcid->hash_length; //Unsure of this
|
|
||||||
return mylink;
|
|
||||||
}
|
|
||||||
/* Free_Link
|
|
||||||
* @param L: Free the link you have allocated.
|
|
||||||
*/
|
|
||||||
void Free_Link(struct Link * L)
|
|
||||||
{
|
|
||||||
ipfs_cid_free(L->Lcid);
|
|
||||||
free(L);
|
|
||||||
}
|
|
||||||
/*====================================================================================
|
|
||||||
* Node Functions
|
|
||||||
*===================================================================================*/
|
|
||||||
/*Create_Empty_Node
|
|
||||||
* Creates an empty node, allocates the required memory
|
|
||||||
* Returns a fresh new node with no data set in it.
|
|
||||||
*/
|
|
||||||
struct Node * Create_Empty_Node()
|
|
||||||
{
|
|
||||||
struct Node * N;
|
|
||||||
N = (struct Node *)malloc(sizeof(struct Node));
|
|
||||||
return N;
|
|
||||||
}
|
|
||||||
/*Node_Set_Data
|
|
||||||
* Sets the data of a node
|
|
||||||
* @param Node: The node which you want to set data in.
|
|
||||||
* @param Data, the data you want to assign to the node
|
|
||||||
* returns 1 on success 0 on failure
|
|
||||||
*/
|
|
||||||
int Node_Set_Data(struct Node * N, unsigned char * Data)
|
|
||||||
{
|
|
||||||
if(!N || !Data)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
N->encoded = NULL;
|
|
||||||
N->cached = NULL;
|
|
||||||
N->data = Data;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
/*Node_Get_Data
|
|
||||||
* Gets data from a node
|
|
||||||
* @param Node: = The node you want to get data from. (unsigned char *)
|
|
||||||
* Returns data of node.
|
|
||||||
*/
|
|
||||||
unsigned char * Node_Get_Data(struct Node * N)
|
|
||||||
{
|
|
||||||
unsigned char * DATA;
|
|
||||||
DATA = N->data;
|
|
||||||
return DATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Node_Copy: Returns a copy of the node you input
|
|
||||||
* @param Node: The node you want to copy (struct CP_Node *)
|
|
||||||
* Returns a copy of the node you wanted to copy.
|
|
||||||
*/
|
|
||||||
struct Node * Node_Copy(struct Node * CP_Node)
|
|
||||||
{
|
|
||||||
struct Node * CN;
|
|
||||||
CN = (struct Node*) malloc(sizeof(struct Node) + sizeof(struct Link) * 2);
|
|
||||||
if(CP_Node->link_ammount != 0)
|
|
||||||
{
|
|
||||||
for(int i=0; i<CP_Node->link_ammount; i++)
|
|
||||||
{
|
|
||||||
CN->links[i] = malloc(sizeof(struct Link));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
memcpy(CN, CP_Node, sizeof(struct Node));
|
|
||||||
memcpy(CN->links[0],CP_Node->links[0], sizeof(struct Link));
|
|
||||||
return CN;
|
|
||||||
}
|
|
||||||
/*Node_Delete
|
|
||||||
* Once you are finished using a node, always delete it using this.
|
|
||||||
* It will take care of the links inside it.
|
|
||||||
* @param N: the node you want to free. (struct Node *)
|
|
||||||
*/
|
|
||||||
void Node_Delete(struct Node * N)
|
|
||||||
{
|
|
||||||
if(N->link_ammount > 0)
|
|
||||||
{
|
|
||||||
for(int i=0; i<N->link_ammount; i++)
|
|
||||||
{
|
|
||||||
free(N->links[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(N);
|
|
||||||
}
|
|
||||||
/*Node_Get_Link
|
|
||||||
* Returns a copy of the link with given name
|
|
||||||
* @param Name: (char * name) searches for link with this name
|
|
||||||
* Returns the link struct if it's found otherwise returns NULL
|
|
||||||
*/
|
|
||||||
struct Link * Node_Get_Link(struct Node * N, char * Name)
|
|
||||||
{
|
|
||||||
struct Link * L;
|
|
||||||
for(int i=0;i<N->link_ammount;i++)
|
|
||||||
{
|
|
||||||
if(strcmp(N->links[i]->name,Name) == 0)
|
|
||||||
{
|
|
||||||
L = (struct Link *)malloc(sizeof(struct Link));
|
|
||||||
memcpy(L,N->links[i],sizeof(struct Link));
|
|
||||||
int ver = L->Lcid->version;
|
|
||||||
char * ahash = L->Lcid->hash;
|
|
||||||
size_t lenhash = L->Lcid->hash_length;
|
|
||||||
ipfs_cid_new(ver, ahash, lenhash, CID_PROTOBUF, &L->Lcid);
|
|
||||||
return L;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
/*Node_Remove_Link
|
|
||||||
* Removes a link from node if found by name.
|
|
||||||
* @param name: Name of link (char * name)
|
|
||||||
* returns 1 on success, 0 on failure.
|
|
||||||
*/
|
|
||||||
int Node_Remove_Link(char * Name, struct Node * mynode)
|
|
||||||
{
|
|
||||||
for(int i=0; i<mynode->link_ammount; i++)
|
|
||||||
{
|
|
||||||
if(mynode->links[i]->name == Name)
|
|
||||||
{
|
|
||||||
for(int x=i;x<mynode->link_ammount && x+1 != mynode->link_ammount;i++)
|
|
||||||
{
|
|
||||||
memcpy(mynode->links[x],mynode->links[x+1],sizeof(struct Link));
|
|
||||||
}
|
|
||||||
free(mynode->links[mynode->link_ammount-1]);
|
|
||||||
mynode->link_ammount--;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
/* N_Add_Link
|
|
||||||
* Adds a link to your node
|
|
||||||
* @param mynode: &yournode
|
|
||||||
* @param mylink: the CID you want to create a node from
|
|
||||||
* @param linksz: sizeof(your cid here)
|
|
||||||
* Returns your node with the newly added link
|
|
||||||
*/
|
|
||||||
struct Node * N_Add_Link(struct Node ** mynode, struct Link * mylink, size_t linksz)
|
|
||||||
{
|
|
||||||
struct Node * Nl = *mynode;
|
|
||||||
Nl->link_ammount++;
|
|
||||||
size_t calculatesize = 0;
|
|
||||||
if(Nl->link_ammount != 0)
|
|
||||||
{
|
|
||||||
for(int i=0; i<Nl->link_ammount-1;i++)
|
|
||||||
{
|
|
||||||
calculatesize = calculatesize + sizeof(Nl->links[i]);
|
|
||||||
}
|
|
||||||
calculatesize = calculatesize + linksz;
|
|
||||||
Nl = (struct Node *) realloc(Nl, sizeof(struct Node) + calculatesize);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Nl = (struct Node *) malloc(sizeof(struct Node) + linksz);
|
|
||||||
}
|
|
||||||
Nl->links[Nl->link_ammount-1] = malloc(sizeof(struct Link));
|
|
||||||
memcpy(Nl->links[Nl->link_ammount-1],mylink,sizeof(struct Link));
|
|
||||||
return Nl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*N_Create_From_Link
|
|
||||||
* Create a node from a link
|
|
||||||
* @param mylink: the link you want to create it from. (struct Cid *)
|
|
||||||
* @param linksize: sizeof(the link in mylink) (size_T)
|
|
||||||
* Returns a fresh new node with the link you specified. Has to be freed with Node_Free preferably.
|
|
||||||
*/
|
|
||||||
struct Node * N_Create_From_Link(struct Link * mylink)
|
|
||||||
{
|
|
||||||
struct Node * mynode;
|
|
||||||
mynode = (struct Node *) malloc(sizeof(struct Node) + sizeof(struct Link));
|
|
||||||
mynode->link_ammount = 0;
|
|
||||||
mynode->link_ammount++;
|
|
||||||
mynode->links[0] = malloc(sizeof(struct Link));
|
|
||||||
memcpy(mynode->links[0], mylink, sizeof(struct Link));
|
|
||||||
return mynode;
|
|
||||||
}
|
|
||||||
/*N_Create_From_Data
|
|
||||||
* @param data: bytes buffer you want to create the node from
|
|
||||||
* returns a node with the data you inputted.
|
|
||||||
*/
|
|
||||||
struct Node * N_Create_From_Data(unsigned char * data)
|
|
||||||
{
|
|
||||||
struct Node * mynode;
|
|
||||||
mynode = (struct Node *) malloc(sizeof(struct Node));
|
|
||||||
mynode->data = data;
|
|
||||||
return mynode;
|
|
||||||
}
|
|
||||||
/*Node_Resolve_Max_Size
|
|
||||||
* !!!This shouldn't concern you!
|
|
||||||
*Gets the ammount of words that will be returned by Node_Resolve
|
|
||||||
*@Param1: The string that will be processed (eg: char * sentence = "foo/bar/bin")
|
|
||||||
*Returns either -1 if something went wrong or the ammount of words that would be processed.
|
|
||||||
*/
|
|
||||||
int Node_Resolve_Max_Size(char * input1)
|
|
||||||
{
|
|
||||||
if(!input1)
|
|
||||||
{
|
|
||||||
return -1; // Input is null, therefor nothing can be processed.
|
|
||||||
}
|
|
||||||
char input[strlen(input1)];
|
|
||||||
bzero(input, strlen(input1));
|
|
||||||
strcpy(input, input1);
|
|
||||||
int num = 0;
|
|
||||||
char * tr;
|
|
||||||
char * end;
|
|
||||||
tr=strtok_r(input,"/",&end);
|
|
||||||
for(int i = 0;tr;i++)
|
|
||||||
{
|
|
||||||
tr=strtok_r(NULL,"/",&end);
|
|
||||||
num++;
|
|
||||||
}
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Node_Resolve Basically stores everything in a pointer array eg: char * bla[Max_Words_]
|
|
||||||
* !!!This shouldn't concern you!!!
|
|
||||||
*@param1: Pointer array(char * foo[x], X=Whatever ammount there is. should be used with the helper function Node_Resolve_Max_Size)
|
|
||||||
*@param2: Sentence to gather words/paths from (Eg: char * meh = "foo/bar/bin")
|
|
||||||
*@Returns 1 or 0, 0 if something went wrong, 1 if everything went smoothly.
|
|
||||||
*/
|
|
||||||
|
|
||||||
int Node_Resolve(char ** result, char * input1)
|
|
||||||
{
|
|
||||||
if(!input1)
|
|
||||||
{
|
|
||||||
return 0; // Input is null, therefor nothing can be processed.
|
|
||||||
}
|
|
||||||
char input[strlen(input1)];
|
|
||||||
bzero(input, strlen(input1));
|
|
||||||
strcpy(input, input1);
|
|
||||||
char * tr;
|
|
||||||
char * end;
|
|
||||||
tr=strtok_r(input,"/",&end);
|
|
||||||
for(int i = 0;tr;i++)
|
|
||||||
{
|
|
||||||
if(debug == 1)
|
|
||||||
{
|
|
||||||
printf("TR: %s\n", tr);
|
|
||||||
}
|
|
||||||
result[i] = (char *) malloc(strlen(tr)+1);
|
|
||||||
strcpy(result[i], tr);
|
|
||||||
tr=strtok_r(NULL,"/",&end);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
/**************************************************************************************************************************************
|
|
||||||
*|||||||||||||||||||||||||||||||||||||||| !!!! IMPORTANT !!! ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||*
|
|
||||||
**************************************************************************************************************************************
|
|
||||||
* Not sure where this is used, I'm making something to easen it up for all of you.
|
|
||||||
* This in itself will get all the links for you in a link[] array inside Link_Proc
|
|
||||||
* the memory allocation for storage will be noted in the ammount of links.
|
|
||||||
* After being done with this, you have to free the array for which you will have a function specially made for you.
|
|
||||||
*
|
|
||||||
* What this does:
|
|
||||||
* It searches for links using a path like /foo/bar/bin/, if links with those names are found in the node you specify, it stores them
|
|
||||||
* in a custom struct, Link_Proc; which is what Node_Resolve_Link returns.
|
|
||||||
* Notes:
|
|
||||||
* Use it, free it, it's all already laid out for you.
|
|
||||||
* There will also be a tutorial demonstrating it in the same folder here so everyone can understand this.
|
|
||||||
*/
|
|
||||||
struct Link_Proc
|
|
||||||
{
|
|
||||||
char * remaining_links; // Not your concern.
|
|
||||||
int ammount; //This will store the ammount of links, so you know what to process.
|
|
||||||
struct Link * links[]; // Link array
|
|
||||||
};
|
|
||||||
/*Node_Resolve_Links
|
|
||||||
* Processes a path returning all links.
|
|
||||||
* @param N: The node you want to get links from
|
|
||||||
* @param path: The "foo/bar/bin" path
|
|
||||||
*/
|
|
||||||
struct Link_Proc * Node_Resolve_Links(struct Node * N, char * path)
|
|
||||||
{
|
|
||||||
if(!N || !path)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
int expected_link_ammount = Node_Resolve_Max_Size(path);
|
|
||||||
struct Link_Proc * LProc = (struct Link_Proc *) malloc(sizeof(struct Link_Proc) + sizeof(struct Link) * expected_link_ammount);
|
|
||||||
LProc->ammount = 0;
|
|
||||||
char * linknames[expected_link_ammount];
|
|
||||||
Node_Resolve(linknames, path);
|
|
||||||
for(int i=0;i<expected_link_ammount; i++)
|
|
||||||
{
|
|
||||||
struct Link * proclink;
|
|
||||||
proclink = Node_Get_Link(N, linknames[i]);
|
|
||||||
if(proclink)
|
|
||||||
{
|
|
||||||
LProc->links[i] = (struct Link *)malloc(sizeof(struct Link));
|
|
||||||
memcpy(LProc->links[i], proclink, sizeof(struct Link));
|
|
||||||
LProc->ammount++;
|
|
||||||
free(proclink);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//Freeing pointer array
|
|
||||||
for(int i=0;i<expected_link_ammount; i++)
|
|
||||||
{
|
|
||||||
free(linknames[i]);
|
|
||||||
}
|
|
||||||
return LProc;
|
|
||||||
}
|
|
||||||
/*Free_link_Proc
|
|
||||||
* frees the Link_Proc struct you created.
|
|
||||||
* @param1: Link_Proc struct (struct Link_Proc *)
|
|
||||||
*/
|
|
||||||
void Free_Link_Proc(struct Link_Proc * LPRC)
|
|
||||||
{
|
|
||||||
if(LPRC->ammount != 0)
|
|
||||||
{
|
|
||||||
for(int i=0;i<LPRC->ammount;i++)
|
|
||||||
{
|
|
||||||
Free_Link(LPRC->links[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(LPRC);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Node_Tree() Basically a unix-like ls
|
|
||||||
*@Param1: Result char * foo[strlen(sentence)]
|
|
||||||
*@Param2: char sentence[] = foo/bar/bin/whatever
|
|
||||||
*Return: 0 if failure, 1 if success
|
|
||||||
*/
|
|
||||||
int Node_Tree(char * result, char * input1) //I don't know where you use this but here it is.
|
|
||||||
{
|
|
||||||
if(!input1)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
char input[strlen(input1)];
|
|
||||||
bzero(input, strlen(input1));
|
|
||||||
strcpy(input, input1);
|
|
||||||
for(int i=0; i<strlen(input); i++)
|
|
||||||
{
|
|
||||||
if(input[i] == '/')
|
|
||||||
{
|
|
||||||
input[i] = ' ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
strcpy(result, input);
|
|
||||||
if(debug == 1)
|
|
||||||
{
|
|
||||||
printf("Node_Tree Internal Result: %s\n",result);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
#endif
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue