Compare commits

..

380 commits

Author SHA1 Message Date
Agorise
e599dd2f34
Create FUNDING.yml 2019-06-12 16:54:37 -05:00
ee0c287a34
Fix compilation reported in issue #16 2019-01-02 21:36:18 -03:00
662825493d
Dependencies: add c-libp2p and lmdb as submodules 2019-01-02 12:51:29 -03:00
Agorise
0432315d33
Update LICENSE 2019-01-01 10:39:45 -06:00
9491e69d76
Added ipns support for cat/get commands. 2018-10-25 19:14:47 -03:00
94b782cfaa
Implemented simple json parsing reusing jsmn. 2018-10-25 19:00:32 -03:00
35b20c9a2e
Changed some erroneously labeled messages as an error. 2018-10-25 18:44:19 -03:00
Agorise
0037be0594
Update LICENSE 2018-05-31 09:50:52 +03:00
John Jones
a0731525ea Changes for building the identify handler 2017-11-30 15:27:52 -05:00
John Jones
31e6142e68 debugging yamux / identify 2017-11-30 14:33:13 -05:00
John Jones
30a59c950e re-fix from broken merge 2017-11-29 06:45:24 -05:00
John Jones
8644dbbb56 Merge branch 'master' of https://github.com/Agorise/c-ipfs 2017-11-29 06:23:51 -05:00
John Jones
9bdfed92f1 Merge branch 'master' into yamux
# Conflicts:
#	core/http_request.c
2017-11-29 06:21:49 -05:00
John Jones
65a61268a3 Final adjustments before merge back to master 2017-11-29 05:55:03 -05:00
John Jones
14ad68b9bb fix compiler warning 2017-11-28 23:42:17 -05:00
John Jones
48b9647a3c More go compat fixes 2017-11-28 22:44:45 -05:00
John Jones
f3db50c3ba debugging yamux and go 2017-11-27 09:06:57 -05:00
John Jones
f96209fc4b Minor fixes for yamux testing with GO 2017-11-23 07:45:06 -05:00
Agorise
d9f8260c6e
Update LICENSE 2017-11-15 13:11:34 +02:00
John Jones
6160dd841b Big changes for the yamux protocol
The Stream interface changed, so it touches many areas. But this change
will help with memory allocation and a cleaner interface.
2017-11-08 10:54:31 -05:00
John Jones
8b2a8ef3ab Minor changes for yamux 2017-11-06 13:37:48 -05:00
John M. Jones
5e034f14bc
Update README.md 2017-11-02 06:02:09 -05:00
John M. Jones
01e5222ea7
Update README.md 2017-11-02 06:01:12 -05:00
John M. Jones
0b6add5146
Update README.md 2017-11-02 06:00:14 -05:00
John M. Jones
db0a519e66
Update README.md 2017-11-02 05:58:38 -05:00
John M. Jones
df7a3e8761
Update README.md 2017-11-02 05:56:21 -05:00
John M. Jones
1fdd490e89
Update README.md 2017-11-02 05:54:03 -05:00
John M. Jones
eeabd85bc3
Update README.md 2017-11-02 05:52:19 -05:00
John M. Jones
77dc2f1472
Update README.md 2017-11-02 05:50:58 -05:00
jmjatlanta
80f97f4aff Added quick start to readme 2017-11-02 05:43:45 -05:00
jmjatlanta
8ce3c68b72 Added quick start to readme 2017-11-02 05:43:01 -05:00
jmjatlanta
e839163d91 Added quick start to readme 2017-11-02 05:42:17 -05:00
jmjatlanta
711ec1df30 Added quick start to readme 2017-11-02 05:39:03 -05:00
John Jones
0c31ef7331 More changes to swarm negotiation 2017-10-25 12:29:20 -05:00
jmjatlanta
b72f247939 More implementation of dialer 2017-10-23 18:03:55 -05:00
jmjatlanta
9087c58113 more changes for dialer 2017-10-23 16:21:24 -05:00
jmjatlanta
f9bec0ac20 New way of swarm connection 2017-10-23 15:22:12 -05:00
jmjatlanta
91f5c50a71 Network write methods now expect struct StreamMessage 2017-10-23 09:48:19 -05:00
jmjatlanta
9afaf535d6 Reading from stream now uses a StreamMessage struct 2017-10-23 09:03:30 -05:00
Agorise
3d7dd87ef3 Update LICENSE 2017-10-16 13:43:57 +03:00
ae6fe6dc29
Changed some API calls to use POST method. 2017-10-12 20:12:28 -03:00
4e556221bd
Fixed issue when receiving by POST method. 2017-10-12 20:08:21 -03:00
jmjatlanta
a315af8534 Now correctly initializing Cid variable, and checking after decoding 2017-10-12 13:04:23 -05:00
jmjatlanta
e394723fb6 Initializing uninitialized variable. 2017-10-12 12:57:20 -05:00
jmjatlanta
cfde84b15c Beginnings of stream locking 2017-10-12 12:38:00 -05:00
jmjatlanta
b399762d82 Handle api and cli swarm connect 2017-10-12 10:12:54 -05:00
jmjatlanta
8cdaf919fe First cut of yamux protocol 2017-10-11 11:23:49 -05:00
John Jones
575be24be2 memory and bug fixes, plus update of several tests 2017-10-09 15:23:30 -05:00
John Jones
98b1e0fef4 Return a copy of the block from the exchange, not the block itself
Returning the block itself will cause problems when a client deallocates
the block.
2017-10-09 10:01:29 -05:00
jmjatlanta
f69ab92469 Fixed merge conflicts 2017-10-09 09:13:54 -05:00
jmjatlanta
b99a78a4d3 Merge branch 'master' of https://github.com/Agorise/c-ipfs
Conflicts:
	test/scripts/run_tests.sh
	test/testit.c
2017-10-09 09:12:14 -05:00
jmjatlanta
c0855c9630 Going through tests, verifying functionality 2017-10-09 09:00:48 -05:00
John Jones
a6a54fb69b Made test file generation faster 2017-10-05 15:37:27 -05:00
John Jones
71c216defb Better handling of bad memory allocation 2017-10-05 15:14:47 -05:00
John Jones
996687cfce Better fix for memory allocation issue with unixfs 2017-10-05 13:52:30 -05:00
John Jones
9c63ed1315 Bad alloc was not being checked 2017-10-05 13:49:08 -05:00
John Jones
99ffd120e8 Added test for transferring large files 2017-10-05 13:36:23 -05:00
John Jones
861ca0a332 Conversion from blockstore to node was not happening 2017-10-05 13:08:36 -05:00
John Jones
2ee0a87439 Adjusting test scripts to make it easier to spot failures. 2017-10-05 11:20:12 -05:00
John Jones
42aa1646ab Misc changes to support binary upload 2017-10-04 09:36:38 -05:00
John Jones
8aa7b7ca77 Added more tests, fixed bug with binary file xfer 2017-10-04 08:42:00 -05:00
John Jones
b9c28ceed4 Adjustments to handle retrieval of binary files 2017-10-04 07:33:29 -05:00
John Jones
7259028bd0 small fix to test 2017-10-04 06:38:38 -05:00
John Jones
596a811d5e testing of using api for binary files 2017-10-04 06:34:37 -05:00
3b2e94ebe2
Implemented HTTP Post using libcurl. 2017-10-03 12:33:30 -03:00
495a92f5f8
Fixed binary chunk response in API. 2017-09-28 21:54:09 -03:00
John Jones
cd922633b2 Added 1 more test 2017-09-28 17:17:33 -05:00
John Jones
c65301dc28 memory fixes 2017-09-28 16:32:36 -05:00
John Jones
27d36d8320 Minor memory leak fixes 2017-09-28 15:43:03 -05:00
John Jones
7aa81936ec Minor memory leak fixes 2017-09-28 14:44:55 -05:00
John Jones
87cc96a011 Fix of memory leak 2017-09-28 13:48:57 -05:00
jmjatlanta
7f89e80d7b Debugging 2017-09-28 13:21:34 -05:00
jmjatlanta
296d164e84 now puting result of get_value in the return buffer 2017-09-28 08:24:57 -05:00
jmjatlanta
3d425bb30f Fixes for remote file retrieval 2017-09-28 07:58:51 -05:00
jmjatlanta
f5250a71f3 more changes to implement api for dht provide and get 2017-09-27 11:45:36 -05:00
jmjatlanta
76b860c06f Attempting to bitswap from previously unknown node 2017-09-27 10:05:17 -05:00
jmjatlanta
f0a53f2753 Added test for name publish and name resolve 2017-09-27 08:12:58 -05:00
jmjatlanta
3418ee5435 more test scripts 2017-09-27 07:40:55 -05:00
jmjatlanta
81e103f1e0 Fix for object get when api not running 2017-09-27 07:02:00 -05:00
jmjatlanta
058a1d64ab More test script examples 2017-09-27 06:16:18 -05:00
jmjatlanta
daf715929a beginning of test scripts 2017-09-27 05:52:53 -05:00
jmjatlanta
2cc7f52fbf fixed attempt to free a non allocated string 2017-09-26 09:43:10 -05:00
John Jones
5bcd3a99f2 various fixes for object_get 2017-09-25 17:56:10 -05:00
John Jones
630985c698 Adjustments for testing 2017-09-25 15:11:44 -05:00
8b8a2844bd
Fixed some allocation problems. 2017-09-25 15:55:14 -03:00
jmjatlanta
abb607c905 fixing name publish and resolve 2017-09-25 13:37:16 -05:00
8e56826b8d
Using urlencode. 2017-09-25 15:31:03 -03:00
6fc2614fe7
Fixed chunked size. 2017-09-25 14:40:33 -03:00
John Jones
f0d19bab97 Small fixes for http api connectivity issues 2017-09-25 12:13:32 -05:00
John Jones
88baee62a2 Fixes for http receiving 2017-09-25 12:07:16 -05:00
John Jones
5fc40e51ee solved pthread_mutex being shared across processes 2017-09-25 11:25:34 -05:00
John Jones
982c7e9e6e More debugging of api 2017-09-25 10:00:49 -05:00
jmjatlanta
4b1cd8cb11 small bugfixes, chasing segfault 2017-09-25 09:20:51 -05:00
jmjatlanta
5404fce6ec move api globals to struct 2017-09-25 08:55:42 -05:00
b4e3817d62
Fixed the wrong buffer check. 2017-09-25 09:34:54 -03:00
jmjatlanta
9402f31841 Modified ipfs_core_http_request_free 2017-09-25 06:57:17 -05:00
0b45b25d6e
Changed parameters and arguments. 2017-09-22 11:52:31 -03:00
Agorise
d872fb079d Update README.md 2017-09-22 12:38:45 +03:00
395eb4f031
Improved api_build_http_request implementation. 2017-09-21 23:27:33 -03:00
jmjatlanta
90262ef657 Calling the API from the client for "name resolve" and "name publish" 2017-09-21 18:03:42 -05:00
jmjatlanta
c54cf989c0 Filling in more of http_request 2017-09-21 17:10:41 -05:00
jmjatlanta
b133a703cc fixed memory leak 2017-09-21 15:30:23 -05:00
jmjatlanta
a1b887ba76 Implementation of http_request 2017-09-21 15:27:16 -05:00
jmjatlanta
9425e2fee3 More work on ipns publisher and resolver 2017-09-21 13:59:06 -05:00
jmjatlanta
e094528293 Finally sorted config file directory rules
If we are trying to build a repository in the home directory of
the user, put everything in a .ipfs directory. Otherwise, use
what was given, even if it does not have a .ipfs suffix.
2017-09-21 11:49:47 -05:00
jmjatlanta
0eab9cc3fc Merge branch 'master' of https://github.com/Agorise/c-ipfs 2017-09-21 11:21:12 -05:00
jmjatlanta
378dd7c051 beginning to resolve ipns addresses 2017-09-21 11:21:01 -05:00
e0d5fed53e
HTTP redirection added in the API. 2017-09-21 11:50:15 -03:00
jmjatlanta
b301c7e4d2 Preparing for name resolve and name publish 2017-09-21 09:47:16 -05:00
jmjatlanta
2051f7714a ipfs name command line options processing 2017-09-21 07:51:18 -05:00
a907f1dd2d
The API changed pthread to scope, so it can load multiple instances. 2017-09-20 14:39:26 -03:00
John Jones
c06625a00e Fixing building the paths for tests 2017-09-20 11:30:39 -05:00
John Jones
cfffe36fb2 Fixing test paths 2017-09-20 11:10:54 -05:00
John Jones
07551151da Fixing of test_core_api_object_cat 2017-09-20 10:50:32 -05:00
jmjatlanta
215af9cfce Buildout of test_core_api_object_cat 2017-09-20 10:18:27 -05:00
jmjatlanta
9a49ddd27b Start of client_api.c 2017-09-20 09:11:01 -05:00
jmjatlanta
81d2252229 Now compiling with api changes 2017-09-20 07:53:36 -05:00
jmjatlanta
794217ed6c method signature changes 2017-09-20 07:40:28 -05:00
jmjatlanta
c0419f2424 Adding IpfsNode to api startup 2017-09-20 07:32:12 -05:00
jmjatlanta
262216f6db Merge branch 'master' of https://github.com/Agorise/c-ipfs 2017-09-20 07:02:13 -05:00
jmjatlanta
6db0830c7d Added first cut of get_object 2017-09-20 07:02:00 -05:00
87b0d6b13c
Fix the mutex lock when the memory allocation fails in api_listen_thread.
Thanks to John Jones for pointing out the problem.
2017-09-20 08:29:51 -03:00
64891c9198
Added initial GET calls. 2017-09-20 08:02:13 -03:00
jmjatlanta
acf506296e Updating tests 2017-09-18 19:04:52 -05:00
John Jones
fc8fc582b1 Testing ipns 2017-09-14 16:49:19 -05:00
John Jones
395c7d94cf More implementation of ipns 2017-09-14 14:58:53 -05:00
John Jones
d5c3e01267 Intitial test for ipns resolve 2017-09-14 12:13:07 -05:00
jmjatlanta
3eec8553a6 Began implementing updates to journal timestamp 2017-09-13 12:40:48 -05:00
jmjatlanta
478fa403fd Merge branch 'master' of https://github.com/Agorise/c-ipfs 2017-09-13 05:03:13 -05:00
jmjatlanta
d0eb0acc9d Added test for api 2017-09-13 05:02:59 -05:00
0b113cb95d
Initial implementation of multipart in API. 2017-09-07 23:59:57 -03:00
John Jones
a9481631df Several memory leak fixes for journal code 2017-09-07 18:45:09 -05:00
John Jones
78904ff1b6 refactored datastore/journalstore for readability 2017-09-07 14:58:02 -05:00
John Jones
cb1ea3ceff Sorting a memory leak in datastore/journalstore 2017-09-07 11:05:56 -05:00
jmjatlanta
bf7ba9049c Implementation of timestamp in both datastore and journalstore 2017-09-04 17:10:57 -05:00
jmjatlanta
bf87d93136 Updating cid codes 2017-09-04 13:33:56 -05:00
John Jones
7dbb6fca29 multistream protocol now has a protocol interface 2017-09-04 11:02:48 -05:00
John Jones
407f85bc89 More testing and compat fixes for ipfs 2017-08-31 16:41:10 -05:00
John Jones
f9d836ef6f Removed some test code 2017-08-31 12:52:36 -05:00
jmjatlanta
1eab27fa0e journalio working, needs tuning 2017-08-31 06:41:54 -05:00
jmjatlanta
49bd61feb1 Testing journalio protocol 2017-08-30 11:10:14 -05:00
jmjatlanta
b3af783f4e Building tests for journaling and backup 2017-08-28 13:04:27 -05:00
jmjatlanta
0066670f60 Datastore now returns a DatastoreRecord struct
When retrieving a record, a struct is returned, making it
much more user friendly.
2017-08-28 10:55:44 -05:00
jmjatlanta
5678a14eb3 Merge branch 'master' of https://github.com/Agorise/c-ipfs 2017-08-28 06:56:38 -05:00
jmjatlanta
1b69cdf1e8 Implemented client side of journaling protocol 2017-08-28 06:56:22 -05:00
d66bbdea65 Changed the interpretation of chunked in the API. 2017-08-24 23:13:36 -03:00
jmjatlanta
60c6085469 Code complete for client side of journal protocol 2017-08-24 13:30:44 -05:00
jmjatlanta
0bc975dfcf journaling protocol 2017-08-24 10:08:27 -05:00
jmjatlanta
d13e4b4318 Added journaling when a file is saved - beginning of backup scheme 2017-08-21 14:49:21 -05:00
jmjatlanta
5b242a2d08 An occasional ping to verify connectivity 2017-08-16 08:15:06 -05:00
jmjatlanta
5de67539ef correctly adding request to peer_request_queue 2017-08-16 07:34:28 -05:00
jmjatlanta
c58bfc9b1e Fixed loop 2017-08-16 07:08:39 -05:00
jmjatlanta
8a492c1e2f fixed memory leak 2017-08-16 06:41:48 -05:00
John Jones
5e8683e64d Cleaning up old code 2017-08-09 12:34:02 -05:00
John Jones
0e24b0a1d3 Fix small memory leak. 2017-08-09 12:26:26 -05:00
jmjatlanta
dd69216c75 Shut down handlers when app shuts down 2017-08-09 12:21:03 -05:00
jmjatlanta
d226e480c9 Fixing repeated needless sends 2017-08-09 12:09:44 -05:00
jmjatlanta
8944e407e9 Initialize handlers after peerstore and providerstore 2017-08-09 08:14:15 -05:00
John Jones
ced96dcf81 IPFS protocols now implement an interface to make marshalling easier 2017-08-09 08:04:17 -05:00
John Jones
c58134db1c Fixing memory leaks 2017-08-08 20:40:35 -05:00
jmjatlanta
6754ba77b3 Removing noise 2017-08-03 17:57:01 -05:00
jmjatlanta
46b6921ddf added more debugging messages 2017-08-03 17:51:34 -05:00
John Jones
3cc75058f0 Fix of memory leaks 2017-08-03 17:46:20 -05:00
jmjatlanta
2b24b00324 Cleanup of after free 2017-08-03 15:04:12 -05:00
jmjatlanta
b578e5c13a Fixed tests 2017-08-03 14:35:06 -05:00
jmjatlanta
ef53c886a0 Verify good connection before using it. 2017-08-03 13:36:53 -05:00
jmjatlanta
d1d4d19fa8 Handling details of bitswap 2017-08-03 11:16:58 -05:00
John Jones
e58909b875 Handle entry in WantlistQueue only once if we found provider 2017-08-02 11:43:48 -05:00
John Jones
3fa822aed6 Merge branch 'master' of https://github.com/kenCode-de/c-ipfs 2017-08-02 10:53:28 -05:00
John Jones
5910d63c3d Clean up when we receive a block we wanted 2017-08-02 10:53:14 -05:00
jmjatlanta
17dbad3bce clean up peer request queue after message send 2017-08-02 10:52:35 -05:00
jmjatlanta
e5e565272e Continue to listen to connected peers within bitswap engine 2017-08-02 09:43:27 -05:00
John Jones
9131559a04 correctly handling send of wantlist 2017-08-02 09:04:37 -05:00
jmjatlanta
986d054c6c code cleanup 2017-08-02 08:53:34 -05:00
jmjatlanta
0638996684 processing want list 2017-08-02 07:04:06 -05:00
7632949e30 Implemented chunked transfer encoding. 2017-08-01 21:17:17 -03:00
jmjatlanta
836fb5387b Attempting to fulfill remote requests 2017-07-31 17:59:51 -05:00
jmjatlanta
fa7a6826b1 Change in secio method signature 2017-07-31 16:36:52 -05:00
jmjatlanta
82c8911b71 squelch some warnings 2017-07-31 15:22:49 -05:00
jmjatlanta
bc01f36839 tweaks to bitswap message 2017-07-31 15:19:17 -05:00
John Jones
835b70c97f compare peer id from file with the one generated from the private key 2017-07-31 15:16:54 -05:00
John Jones
ac5a622400 Passing SessionContext instead of Stream 2017-07-31 13:54:09 -05:00
John Jones
3a68619016 Fixed mem leak and more testing 2017-07-31 13:32:09 -05:00
jmjatlanta
e4f1c9b39c Making providerstore smarter 2017-07-31 12:50:12 -05:00
John Jones
e22da601ea bug fixes to client side bitswap 2017-07-31 10:01:06 -05:00
jmjatlanta
9bceade4d8 correctly handle empty list of replication servers 2017-07-31 08:50:20 -05:00
John Jones
d969f48324 implementation of bitswap network receive 2017-07-31 08:16:52 -05:00
John Jones
45c997cd9a Merge branch 'master' of https://github.com/kenCode-de/c-ipfs 2017-07-31 06:43:28 -05:00
John Jones
059a3286c9 More bitswap implementation 2017-07-31 06:43:15 -05:00
5941a3593a Added body transmitted directly in the API. 2017-07-28 00:43:36 -03:00
John Jones
b3bb857f3a Still testing bitswap 2017-07-27 14:33:19 -05:00
John Jones
73d7d5daed More implementation of bitswap 2017-07-27 12:05:41 -05:00
John Jones
e1135fef3b Beginnings of the multithreaded engine
This engine has 2 threads. One to process the request queue, the other
to gather up and build peer messages and send them.
2017-07-27 08:38:57 -05:00
John Jones
10aa932e08 Unit testing and memory fixes 2017-07-26 09:48:04 -05:00
John Jones
692d3406c8 Implementation of a universal bitswap queue
This queue stores both local and remote requests for blocks
2017-07-26 07:38:47 -05:00
John Jones
108792ca44 More changes for bitswap 2017-07-24 17:58:39 -05:00
John Jones
3a8a85e628 More buildout of the Bitswap protocol 2017-07-24 16:03:56 -05:00
John Jones
9924d5dcf7 Connecting bitswap and blockstore 2017-07-24 14:56:30 -05:00
John Jones
4368e052e2 First cut of peer request queue complete 2017-07-24 10:26:07 -05:00
jmjatlanta
5507937bff Merge branch 'master' of https://github.com/kenCode-de/c-ipfs 2017-07-24 09:09:48 -05:00
jmjatlanta
a63910e0d7 Adding bitswap request queue 2017-07-24 09:09:22 -05:00
bde4d4debe Initial handling of http request structure. 2017-07-23 22:24:14 -03:00
f7ddfa0088 Fixed thread-safe in API. 2017-07-23 11:49:40 -03:00
John M. Jones
937504c3f2 Merge pull request #7 from PayasR/master
Fixed broken links
2017-07-22 06:31:30 -05:00
Payas
b26c94ec2d Fixed broken links 2017-07-21 22:39:50 -07:00
cce43e2bce Initial API implementation. 2017-07-21 00:40:43 -03:00
John Jones
2bb70b01be Some tweaks to avoid potential problems found during unit testing 2017-07-20 16:10:31 -05:00
jmjatlanta
f47a6116f0 Adding basic unit tests for Bitswap protobuf 2017-07-20 15:16:59 -05:00
jmjatlanta
2232d03854 Implementing protobuf objects for bitswap messages 2017-07-20 15:03:49 -05:00
jmjatlanta
1fe5be1c5c Reading replication parameters from config file 2017-07-20 09:12:31 -05:00
jmjatlanta
6e19c14bab directory modification 2017-07-20 08:03:34 -05:00
jmjatlanta
250b88601a Beginnings of bitswap 2017-07-20 07:57:20 -05:00
jmjatlanta
cb05b249ba Fixed warnings 2017-07-17 16:14:40 -05:00
John Jones
d038b5d6f7 Broke out the marshaling of incoming requests 2017-07-17 14:38:13 -05:00
John Jones
069379acf4 Fix of a few compiler warnings 2017-07-17 13:05:56 -05:00
John Jones
656b0b50b7 Now we can connect using secio. 2017-07-13 18:32:40 -05:00
f2e7c3c475 Missing null.h 2017-07-07 00:40:39 -03:00
297283168c Make sure don't crash if ipfs_routing_new_kademlia fails. 2017-07-07 00:11:52 -03:00
13b8b8bf27 Null routing code reallocated to facilitate routing protocol swap. 2017-07-06 23:51:53 -03:00
8da685b5cf Implemented timeout to avoid hang due to connection error. 2017-07-06 23:02:11 -03:00
4af0bedba1 Added missing ipfsaddr header. 2017-06-29 21:05:12 -03:00
9f190fb5dc Implemented kademlia bootstrap. 2017-06-29 20:51:24 -03:00
8fb67ec7e1 Changed ping message to use the dialer. 2017-06-22 21:17:43 -03:00
51639b354a Added ping stats output. 2017-06-07 23:20:00 -03:00
a88636ae7e Fixed IPFS_PATH environment var. 2017-06-06 21:42:38 -03:00
a2e31f1edd Fixed some memory allocations. 2017-06-06 21:40:10 -03:00
aa4b433fb0 The sanity check won't attempt to free uninitialized memory. 2017-06-06 19:29:12 -03:00
John Jones
6c936de20e Closing socket descriptor 2017-05-11 14:30:52 -05:00
John Jones
a5e5a71ddd Fixes for various memory leaks 2017-05-11 13:53:52 -05:00
John Jones
d6ee0f7d5d Fixed small memory leak 2017-05-11 07:13:13 -05:00
John Jones
def5331d4c Better handling of locally stored files 2017-05-11 07:04:54 -05:00
John Jones
3de4b757e4 Bugfixes and unit test fixes 2017-04-27 15:52:20 -05:00
John Jones
e756fdf510 Memory and test fixes 2017-04-27 11:35:26 -05:00
John Jones
a991dab1bc Small change to logger cleanup 2017-04-27 00:10:25 -05:00
John Jones
43bf2caeff Tested and made adjustments for large file transfers 2017-04-24 16:33:59 -05:00
John Jones
03696dd6e7 Major changes to support large file transfer 2017-04-20 17:56:03 -05:00
John Jones
a2a08156a7 Added thread pool to aid in clean daemon shutdown 2017-04-17 14:02:33 -05:00
John Jones
427b5c948f Added routines to cleanly shutdown daemon
This still needs more work, but the mechanism now exists
2017-04-17 11:58:47 -05:00
John Jones
2b0a29a06b Implemented find providers from remote peers
Now, you can ask a known node for a key, and it can pass a list of peers
that are perhaps unknownn to the calling peer.
2017-04-16 23:47:53 -05:00
John Jones
62096ffc1c Handling finding of peer via swarm
Also included is a way to pass port and swarm info to methods that build
the config file. This makes testing easier. Multiple peers can be
started on the same machine easily.
2017-04-13 09:31:58 -05:00
John Jones
5d558f5229 Correctly parsing command line parameters for ipfs add 2017-04-06 19:05:30 -05:00
John Jones
794608a7ea Added -c or -config command line parameter
You can now specify the ipfs repository directory from the command line
by using -c [dir] or --config [dir]
2017-04-06 17:46:40 -05:00
John Jones
bc19434490 setting Datastore on SessionContext structure 2017-04-06 09:55:26 -05:00
John Jones
94d6005587 Moved datastore interface from ipfs to libp2p 2017-04-06 09:33:28 -05:00
John Jones
950ad31760 For now, puting bogus, non-zero IP address in due to MultiAddress bug.
The client pays no attention to the IP address anyway. But the
MultiAddress bug needs to be fixed.
2017-04-03 22:02:44 -05:00
John Jones
2efd59cbd5 adding MultiAddress to peer structure sent to remote connection 2017-04-03 21:18:08 -05:00
John Jones
7b61c70639 added logging to daemon 2017-04-03 20:54:03 -05:00
John Jones
089d072736 fixed memory freeing for daemon 2017-04-03 17:31:35 -05:00
John Jones
87cf779704 handling add provider 2017-04-03 17:26:33 -05:00
John Jones
58b5bc8cdd More debugging for segfault 2017-04-03 13:41:26 -05:00
John Jones
624c2280e4 fix of protocol name 2017-04-03 13:26:11 -05:00
John Jones
6f94f7e6c0 Using new logging mechanism 2017-04-03 13:20:35 -05:00
John Jones
396a27d712 Added some debugging info 2017-04-03 12:42:35 -05:00
John Jones
01531693d6 Small adjustment to makefile 2017-04-03 11:58:28 -05:00
John Jones
e90d966e44 Adding network and swarm functionality 2017-04-03 11:55:36 -05:00
John Jones
96b97ad347 Adding network calls to kademlia 2017-03-30 13:59:31 -05:00
John Jones
59af1c0b9e Using config file for bootstrap of kademlia 2017-03-24 16:51:00 -05:00
John Jones
8feb946087 debugging daemon 2017-03-24 14:29:00 -05:00
John Jones
6c64b55176 Added more comments 2017-03-23 15:05:09 -05:00
John Jones
d25e088b7c Built some system tests
Testing the retrieve remote file area using kademlia
2017-03-23 08:28:35 -05:00
John Jones
640e4be5be fix for strtok_r 2017-03-21 13:40:46 -05:00
John Jones
25a2fa0c65 Testing string tokenizer 2017-03-21 12:58:39 -05:00
John Jones
618264c709 Kademlia does not need a separate thread
Kademlia creates its own threads, and does not need a thread to get it
started.
2017-03-21 12:23:54 -05:00
John Jones
83242b0046 minor fixes 2017-03-21 12:11:41 -05:00
John Jones
8edc94509c c99 for centos 2017-03-21 15:20:52 +00:00
John Jones
cfcabaecd0 Finishing NodeIO 2017-03-19 14:40:16 -05:00
John Jones
0b238eb5ac Implementing NodeIO 2017-03-19 08:05:25 -05:00
John Jones
e8b8d06f24 refactoring + beginning of file transfer
Now attempting to use kademlia to find a hash, and NodeIO to transfer
the file
2017-03-19 07:47:19 -05:00
John Jones
93c4988f90 Added utility functions to multiaddress
Parsing of typical IP addresses is now easier
2017-03-09 18:47:27 -05:00
John Jones
f494344b15 Adding kademlia routing to daemon 2017-03-09 18:03:21 -05:00
John Jones
43ca313854 Working with secio streams 2017-03-09 12:50:08 -05:00
John Jones
15a8abff9a Made the stream methods more generic 2017-03-09 10:01:09 -05:00
John Jones
cd5d347e63 Minor fixes to libp2p 2017-03-06 19:04:14 -05:00
John Jones
d4fee344a7 Fixes for connections via secio 2017-03-02 16:18:02 -05:00
3a38623dcc Fixed key length validation. 2017-03-01 14:28:17 -03:00
John Jones
f1aac5d707 Starting to handle get_value request 2017-02-27 12:27:40 -05:00
John Jones
7a6b138444 more ping cleanup and friendly interface to streams 2017-02-23 15:15:33 -05:00
John Jones
ae48e058dd Added ping functionality to multistream 2017-02-23 11:16:23 -05:00
John Jones
daefe7604f Beginning of implementation of smarter connections for daemon 2017-02-22 11:48:42 -05:00
John Jones
f8e4286740 Starting to make the daemon more intelligent 2017-02-22 10:56:11 -05:00
fbd862431c namesys: Incomplete code disabled to not break compilation. 2017-02-14 07:56:07 -03:00
John Jones
9379d0904f Fixed the encoding of the private key in the config file
The old version had a unique way to store the private key in the config
file. Now we are compatible with the GO version.
2017-02-13 17:42:43 -05:00
John Jones
2307309fa2 Adjusted makefile to make the main method near-last 2017-02-13 09:07:40 -05:00
John Jones
ea5f04e27a added namesys to makefile 2017-02-13 08:56:26 -05:00
John Jones
8a2aabc013 Fixed some errors and warnings 2017-02-13 08:53:32 -05:00
John Jones
81fe9305bf Adjusting to use the better multihash implementation 2017-02-13 07:43:18 -05:00
4cd4750f6f namesys: Fixed some compilation errors. 2017-02-13 08:52:22 -03:00
John Jones
c972852c9a Fixed some compiler warnings 2017-02-13 05:34:56 -05:00
0522bedd2a Implemented initial core/ping.
A fake plaintext ping/pong implementation, for now it's just
   to use as a POC for multistream and secio.
2017-02-09 23:53:58 -03:00
de6c4b2495 Initial implementation of core/daemon. 2017-02-09 22:10:21 -03:00
cd09930077 Fixed path/path compilation. 2017-02-09 18:32:12 -03:00
1535273259 Initial implementation of routing/offline. 2017-02-02 22:15:28 -03:00
John Jones
33431a3007 Merge branch 'master' of https://github.com/kenCode-de/c-ipfs 2017-02-02 19:10:10 -05:00
John Jones
8e19636de5 Removed old peer id method 2017-02-02 19:09:50 -05:00
2431aba246 Complement of last commit, same return to mkdir condition. 2017-02-02 19:44:32 -03:00
John Jones
be4bee3119 Upgraded to latest libp2p and fixed some warnings 2017-02-02 14:14:59 -05:00
John Jones
e88f40cad0 Merge branch 'master' of https://github.com/kenCode-de/c-ipfs 2017-02-02 14:11:14 -05:00
John Jones
a3ec7bf41d Small change to provide a little more information for testing 2017-02-02 14:10:55 -05:00
ken Code
631835e23b Update LICENSE 2017-01-31 17:10:12 +01:00
3e39fec066 Merge pull request #5 from squishyhuman/fix-uninitialized-var
Fix use of uninitialized variable
2017-01-20 08:17:33 -03:00
squishyhuman
929bc07168 Fix possible memory leak 2017-01-19 12:25:48 -08:00
squishyhuman
1a13f39cf6 Fix use of uninitialized variable 2017-01-19 12:25:31 -08:00
7d3418e9c7 Added MINGW support for compiling in Windows
* Still need a fix for missing libresolv.
2017-01-12 18:45:44 -03:00
3c3474eacd Fixed bug during compilation. 2017-01-05 21:18:50 -03:00
6448a35a72 Implemented ipfs_pin_has_child 2017-01-05 19:01:24 -03:00
4c330e29be Initial implementation of pin/set 2017-01-05 18:55:39 -03:00
John Jones
9882c28743 Fixed memory leaks
Several tests had memory leaks. As well found a few leaks within the
importer and resolver areas.
2017-01-02 00:38:09 -05:00
jmjatlanta
61d0adc445 misc tweaks and fixes
Now paying attention to the IPFS_PATH environment variable to determine
where the repository is. Fixed some broken tests. Fixed a bug whereby a
subdirectory within a subdirectory was not displaying correctly when
imported.
2017-01-01 23:48:09 -05:00
e1582544b1 Using ns_c_in and ns_t_txt for compatibility with OSX. 2016-12-30 20:52:15 -03:00
f7a029ade3 Added pinned struct. 2016-12-30 02:55:25 -03:00
9d194ad484 Implemented conversion between PinMode and string. 2016-12-30 01:50:45 -03:00
ef380f2a69 Initial implementation of pin/pin 2016-12-30 01:18:03 -03:00
d9774095d3 Removed unnecessary debugging in dnslink/dnslink. 2016-12-30 00:09:36 -03:00
37bab54a5c Fix undefined reference to `Err' 2016-12-30 00:07:44 -03:00
jmjatlanta
addb5ba302 Merge branch 'master' of https://github.com/kenCode-de/c-ipfs 2016-12-29 19:06:34 -05:00
jmjatlanta
c2fe60949e Added the ability to retrieve the file using directories
Files can be refered to directly using their hash, or the hash of their
directory and the file name.
2016-12-29 19:05:44 -05:00
00bf29b0fa Added -r for recursion in dns command. 2016-12-29 20:05:53 -03:00
jmjatlanta
396dfc6abc Directory hashes match 2016-12-29 04:42:01 -05:00
jmjatlanta
fa3dd77e96 Saving directories 2016-12-28 22:45:35 -05:00
jmjatlanta
9d77b2709f Beginnings of handling multiple files 2016-12-27 21:39:58 -05:00
jmjatlanta
8f44c857db Hashes match on large files 2016-12-23 20:12:51 -05:00
John Jones
3004f1411a More memory fixes 2016-12-23 19:08:41 -05:00
jmjatlanta
1dcbf8962e Intermediate save for hash matching 2016-12-23 17:21:04 -05:00
jmjatlanta
15b432c70e Now using correct hashes on small files
Also added ipfs cat command line functionality
2016-12-23 12:53:03 -05:00
John Jones
8d2aeab016 Fixed various memory leaks 2016-12-23 10:49:30 -05:00
jmjatlanta
914d3caaed Intermidiate commit with big changes to storage formats
I am attempting to match the storage format of the reference
implementation, so as to generate the same hashes.
2016-12-23 09:37:43 -05:00
a569159cc2 Added command ipfs dns 2016-12-23 00:51:06 -03:00
5f22be643c Commented missing implementation at path. 2016-12-23 00:46:31 -03:00
7691fe0dc2 Created errs.c and moved to util directory. 2016-12-23 00:45:13 -03:00
049078effc Some changes at namesys. 2016-12-23 00:28:30 -03:00
jmjatlanta
da4b1f86f4 Added command ipfs object get [hash]
This will display the formatted data of a MerkleDag (links and data)
2016-12-22 10:21:18 -05:00
jmjatlanta
7fa0fc6a7b Added a simplistic command line.
From the command line, you can init the repository or add a file.
Directories coming soon...
2016-12-21 08:08:44 -05:00
jmjatlanta
f8cdaf0a97 Merge branch 'master' of https://github.com/kenCode-de/c-ipfs 2016-12-21 06:40:31 -05:00
jmjatlanta
c8fdb084e4 Moving closer to binary compatability with go version of ipfs
The files are now stored in the same format. Now it is necessary to
reverse engineer the directory structure
2016-12-21 06:40:19 -05:00
b17403b61a Initial implementation of namesys/publisher 2016-12-21 07:21:40 -03:00
6b9d205ef2 Some changes at util/time 2016-12-21 07:14:21 -03:00
41b7579f21 Some changes at namesys/pb 2016-12-20 21:08:51 -03:00
a60300d160 Merge branch 'master' of https://github.com/kenCode-de/c-ipfs 2016-12-20 20:58:55 -03:00
5b8678b48c Some changes at namesys/routing 2016-12-20 20:54:37 -03:00
a94aa609b9 Initial implementation of namesys/routing 2016-12-20 20:46:50 -03:00
b914745b47 Removed redundant struct stime from util/time 2016-12-20 20:38:31 -03:00
jmjatlanta
a654022d32 More work on unixfs protobuf 2016-12-19 17:21:21 -05:00
jmjatlanta
57ed4fd5e4 Beginnings of persistence for unixfs 2016-12-19 14:19:43 -05:00
jmjatlanta
bece919c40 Merge branch 'master' of https://github.com/kenCode-de/c-ipfs 2016-12-19 09:03:45 -05:00
jmjatlanta
a1166e840a Move of node to merkledag 2016-12-19 09:03:28 -05:00
fea5d3b8bf Initial implementation of namesys/routing 2016-12-16 00:30:40 -03:00
aa49c7dc35 Initial implementation of namesys/pb 2016-12-16 00:28:12 -03:00
John Jones
87f0cbedbc fixed memory leak for file exporter 2016-12-15 13:06:12 -05:00
jmjatlanta
1d63cdb4a1 Persisting large files in chunks 2016-12-15 12:38:08 -05:00
jmjatlanta
34301c286e Storing large files
Files larger than about 200K are split into smaller files and stored in
the ipfs file system in blocks.
2016-12-15 05:40:24 -05:00
John Jones
5168bc87e0 Fixed memory leak in test 2016-12-14 12:24:44 -05:00
jmjatlanta
033dd767b4 More work on persisting data to disk.
Blockstore now storing the data, whereas datastore is storing the key
and filename. The key should be the multihash (currently the sha256, not
the multihash), and the value is the filename (base32).
2016-12-14 12:07:43 -05:00
John Jones
88d177cf4a Fixed minor leak in test and more compiler warnings 2016-12-14 06:39:06 -05:00
jmjatlanta
bf0cbfb412 Removed compiler warnings, starting to import large files 2016-12-14 06:25:09 -05:00
9ec06749f2 Initial implementation of util/time.c 2016-12-13 22:16:51 -03:00
John Jones
876e2dfcf2 Fixed memory leaks around node and allocations 2016-12-12 18:27:46 -05:00
John Jones
363a773a1e Merge branch 'master' of https://github.com/kenCode-de/c-ipfs 2016-12-12 17:03:56 -05:00
John Jones
4a99078c12 Minor test fix 2016-12-12 17:03:38 -05:00
jmjatlanta
a36ba20557 made node links a single linked list instead of array
This will save some complicated memory allocations
2016-12-12 16:58:27 -05:00
jmjatlanta
4fe768c2c5 Working with nodes to add protobuf to persist to db
adding a link to a node does a realloc. I believe this is causing
problems. I am going to replace this and see if that fixes the issue.
2016-12-12 15:06:17 -05:00
jmjatlanta
e0b0552b39 Pushing Cid into protobuf 2016-12-12 06:27:06 -05:00
786bd5d80b Initial implementation of dnslink 2016-12-08 23:29:50 -03:00
73a7690725 namesys: Invalid prototype removed. 2016-12-08 07:31:17 -03:00
7a3d0c5e0b namesys/path: Changed error messages to a separate file. 2016-12-08 07:08:34 -03:00
84f24797f4 Fixed memory leaks in namesys and path. 2016-12-08 06:48:38 -03:00
John Jones
e69f10a68f Fixed memory leaks in node and test 2016-12-07 11:53:17 -05:00
jmjatlanta
75bee8d1ea Merge branch 'master' of https://github.com/kenCode-de/c-ipfs 2016-12-07 11:07:52 -05:00
jmjatlanta
4a6b88871a Merged in changes to node.h and node.c 2016-12-07 11:07:36 -05:00
f8723eb8c7 path: implemented ipfs_path_clean_path 2016-12-07 09:17:15 -03:00
John Jones
a180a63160 Setting uninitialized values
Uninitialized values should be set to NULL to aid in memory deallocation
at cleanup time.
2016-12-05 18:17:17 -05:00
jmjatlanta
da6490ac7f Implementation of MerkleDag get and put
Now saving and retrieving MerkleDags that contain data. Now need to work
with links and other types.
2016-12-05 17:23:58 -05:00
jmjatlanta
8a80d2afc7 now not permitting duplicate keys in the database 2016-12-05 14:34:10 -05:00
John Jones
a7d8bc82b7 Fixed memory leak in test 2016-12-05 13:36:24 -05:00
jmjatlanta
f9d927f375 Beginnings of testing for MerkleDag 2016-12-05 13:11:22 -05:00
jmjatlanta
0245aa6549 Starting implementation of MerkleDag 2016-12-05 10:50:17 -05:00
jmjatlanta
5f452969fd Merge branch 'master' of https://github.com/kenCode-de/c-ipfs 2016-12-05 07:12:37 -05:00
jmjatlanta
f79d2f9f0c Removal of incorrect copyright notice 2016-12-05 07:12:23 -05:00
496ae3ec6c namesys/path: Renamed the function names to match the rest of the project. 2016-12-05 08:55:17 -03:00
John Jones
bf9ddfd6f6 Turned on warnings for compilation 2016-12-05 06:54:21 -05:00
jmjatlanta
362ef81cb7 Merge branch 'xethyrion-master' 2016-12-05 06:14:11 -05:00
240 changed files with 22952 additions and 1806 deletions

View file

@ -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="&quot;${workspace_loc:/c-libp2p/include}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/c-multiaddr/include}&quot;"/>
</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="&quot;${workspace_loc:/c-libp2p/include}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/c-multihash/include}&quot;"/> <listOptionValue builtIn="false" value="&quot;${workspace_loc:/c-multihash/include}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/c-multiaddr}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/lmdb}&quot;"/> <listOptionValue builtIn="false" value="&quot;${workspace_loc:/lmdb}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/c-ipfs/include}&quot;"/> <listOptionValue builtIn="false" value="&quot;${workspace_loc:/c-ipfs/include}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/c-protobuf}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/c-libp2p/include}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/c-multiaddr/include}&quot;"/>
</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="&quot;${workspace_loc:/c-libp2p/include}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/c-multiaddr/include}&quot;"/>
</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
View 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
View file

@ -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
View 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

View file

@ -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

View file

@ -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

View file

@ -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
```

View file

@ -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

View file

@ -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) {
ipfs_cid_free(block->cid); if (block != NULL) {
if (block->data != NULL) if (block->cid != NULL)
free(block->data); ipfs_cid_free(block->cid);
free(block); if (block->data != NULL)
free(block->data);
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;
}

View file

@ -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);
return 0; if (key == NULL) {
free(key);
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

@ -0,0 +1 @@
Subproject commit d0c319a88cd1f2cb3a219420b5a0b930e72562e2

View file

@ -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)

277
cid/cid.c
View file

@ -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 {
if (cid->hash == NULL) { cid->hash = (unsigned char*) malloc(sizeof(unsigned char) * hash_length);
free(cid); if (cid->hash == NULL) {
return 0; free(cid);
return NULL;
}
memcpy(cid->hash, hash, hash_length);
}
// assign other values
cid->version = version;
cid->codec = codec;
} }
// assign values return cid;
cid->version = version;
cid->codec = codec;
memcpy(cid->hash, hash, hash_length);
cid->hash_length = hash_length;
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) {
free(cid->hash); if (cid->hash != NULL) {
free(cid); free(cid->hash);
cid->hash = NULL;
}
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
View 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;
}

View file

@ -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
View 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);
}

View file

@ -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

View file

@ -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;

View file

@ -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 \

View file

@ -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

View file

@ -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;
} }

View file

@ -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"

View file

@ -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>

View file

@ -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
View 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(&params->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(&params->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
View 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;
}
*/

View file

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;
}

View file

@ -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

View file

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;
}

View 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;
}

View 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);
}

View 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;
}

View file

@ -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

View file

@ -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
View 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
View 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
View 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
View 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, &current_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, &current_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;
}

View file

@ -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

View file

@ -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

View file

@ -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
View 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);

View file

@ -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

View file

@ -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);

View file

@ -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__

View file

@ -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__

View file

@ -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__

View file

@ -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
View 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);

View 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);

View file

@ -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;

View 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);

View 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

View 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);

View file

@ -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
View 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
View 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);

View file

@ -0,0 +1,8 @@
#pragma once
#include "ipfs/cmd/cli.h"
/***
* Handle command line swarm call
*/
int ipfs_swarm (struct CliArguments* args);

View file

@ -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);

View 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

View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

View 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;
};

View 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);

View 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_ */

View 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);

View 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);

View 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);

View 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);

View file

@ -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

View 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

View 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

View 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);

View file

@ -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
View 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);

View 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);

View 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);

View 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

View file

@ -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