Compare commits
No commits in common. "master" and "xethyrion-master" have entirely different histories.
master
...
xethyrion-
240 changed files with 1802 additions and 22948 deletions
42
.cproject
42
.cproject
|
@ -21,20 +21,15 @@
|
|||
<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.cpp.compiler.base.1701788660" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.base">
|
||||
<option id="gnu.cpp.compiler.option.include.paths.776431500" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath">
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/c-libp2p/include}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/c-multiaddr/include}""/>
|
||||
</option>
|
||||
<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1773271782" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
|
||||
</tool>
|
||||
<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">
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/c-libp2p/include}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/c-multihash/include}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/c-multiaddr}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/lmdb}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/c-ipfs/include}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/c-protobuf}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/c-libp2p/include}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/c-multiaddr/include}""/>
|
||||
</option>
|
||||
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.581176638" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
|
||||
</tool>
|
||||
|
@ -46,10 +41,6 @@
|
|||
</inputType>
|
||||
</tool>
|
||||
<tool id="cdt.managedbuild.tool.gnu.assembler.base.451195806" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.base">
|
||||
<option id="gnu.both.asm.option.include.paths.1115343131" name="Include paths (-I)" superClass="gnu.both.asm.option.include.paths" valueType="includePath">
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/c-libp2p/include}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/c-multiaddr/include}""/>
|
||||
</option>
|
||||
<inputType id="cdt.managedbuild.tool.gnu.assembler.input.1339940304" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
|
||||
</tool>
|
||||
</toolChain>
|
||||
|
@ -81,6 +72,7 @@
|
|||
<buildTargets>
|
||||
<target name="all" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
||||
<buildCommand>make</buildCommand>
|
||||
<buildArguments/>
|
||||
<buildTarget>all</buildTarget>
|
||||
<stopOnError>true</stopOnError>
|
||||
<useDefaultCommand>true</useDefaultCommand>
|
||||
|
@ -88,6 +80,7 @@
|
|||
</target>
|
||||
<target name="clean" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
||||
<buildCommand>make</buildCommand>
|
||||
<buildArguments/>
|
||||
<buildTarget>clean</buildTarget>
|
||||
<stopOnError>true</stopOnError>
|
||||
<useDefaultCommand>true</useDefaultCommand>
|
||||
|
@ -95,6 +88,7 @@
|
|||
</target>
|
||||
<target name="rebuild" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
||||
<buildCommand>make</buildCommand>
|
||||
<buildArguments/>
|
||||
<buildTarget>rebuild</buildTarget>
|
||||
<stopOnError>true</stopOnError>
|
||||
<useDefaultCommand>true</useDefaultCommand>
|
||||
|
@ -102,39 +96,13 @@
|
|||
</target>
|
||||
<target name="clean" path="thirdparty" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
|
||||
<buildCommand>make</buildCommand>
|
||||
<buildArguments/>
|
||||
<buildTarget>clean</buildTarget>
|
||||
<stopOnError>true</stopOnError>
|
||||
<useDefaultCommand>false</useDefaultCommand>
|
||||
<runAllBuilders>true</runAllBuilders>
|
||||
</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">
|
||||
<buildCommand>make</buildCommand>
|
||||
<buildArguments/>
|
||||
<buildTarget>clean</buildTarget>
|
||||
<stopOnError>true</stopOnError>
|
||||
<useDefaultCommand>false</useDefaultCommand>
|
||||
|
|
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
|
@ -1,3 +0,0 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
custom: https://www.blockchain.com/btc/payment_request?address=1AFGT5gVj7xhfjgHTuwEoaV56WTCh7Gjf1#BITCOIN_ONLY
|
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -7,10 +7,3 @@
|
|||
*.o
|
||||
.settings/language.settings.xml
|
||||
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
7
.gitmodules
vendored
|
@ -1,7 +0,0 @@
|
|||
[submodule "c-libp2p"]
|
||||
path = c-libp2p
|
||||
url = https://github.com/Agorise/c-libp2p.git
|
||||
[submodule "lmdb"]
|
||||
path = lmdb
|
||||
url = https://github.com/jmjatlanta/lmdb.git
|
||||
branch = mdb.master
|
5
LICENSE
5
LICENSE
|
@ -1,9 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2019 AGORISE, LTD.
|
||||
An International Business Company, Cyprus Reg# ΗΕ375959
|
||||
|
||||
Contains works from BitShares Munich IVS
|
||||
Copyright (c) 2016 BitShares Munich IVS
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
32
Makefile
32
Makefile
|
@ -3,57 +3,31 @@ DEBUG = true
|
|||
export DEBUG
|
||||
|
||||
all:
|
||||
cd c-libp2p; make all;
|
||||
cd lmdb/libraries/liblmdb; make all;
|
||||
cd blocks; make all;
|
||||
cd cid; make all;
|
||||
cd cmd; make all;
|
||||
cd commands; 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 pin; make all;
|
||||
cd os; make all;
|
||||
cd repo; make all;
|
||||
cd flatfs; make all;
|
||||
cd datastore; 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;
|
||||
|
||||
|
||||
clean:
|
||||
cd c-libp2p; make clean;
|
||||
cd lmdb/libraries/liblmdb; make clean;
|
||||
cd blocks; make clean;
|
||||
cd cid; make clean;
|
||||
cd cmd; make clean;
|
||||
cd commands; 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 pin; make clean;
|
||||
cd os; make clean;
|
||||
cd repo; make clean;
|
||||
cd flatfs; make clean;
|
||||
cd datastore; 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;
|
||||
|
||||
rebuild: clean all
|
||||
|
|
35
README.md
35
README.md
|
@ -1,28 +1,7 @@
|
|||
# C-IPFS
|
||||
IPFS implementation in C, (not just an API client library).
|
||||
|
||||
## Quick start for users:
|
||||
* **ipfs init** to create an ipfs repository on your machine
|
||||
* **ipfs add MyFile.txt** to add a file to the repository, will return with a hash that can be used to retrieve the file.
|
||||
* **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
|
||||
```
|
||||
# c-ipfs
|
||||
IPFS implementation in C, (not just an API client library).<br>
|
||||
<br>
|
||||
getting started: https://github.com/ipfs/specs/blob/master/overviews/implement-ipfs.md <br>
|
||||
specifications: https://github.com/ipfs/specs <br>
|
||||
getting started: https://github.com/ipfs/community/issues/177 <br>
|
||||
libp2p: https://github.com/libp2p/specs <br>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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
|
||||
CFLAGS = -O0 -I../include -I../../c-libp2p/include -I../../c-multihash/include -I../../c-multiaddr/include
|
||||
|
||||
ifdef DEBUG
|
||||
CFLAGS += -g3
|
||||
|
|
165
blocks/block.c
165
blocks/block.c
|
@ -9,105 +9,6 @@
|
|||
#include "ipfs/blocks/block.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
|
||||
* @param data the data to base the block on
|
||||
|
@ -115,40 +16,35 @@ exit:
|
|||
* @param block a pointer to the struct Block that will be created
|
||||
* @returns true(1) on success
|
||||
*/
|
||||
struct Block* ipfs_block_new() {
|
||||
int ipfs_blocks_block_new(const unsigned char* data, size_t data_size, struct Block** block) {
|
||||
|
||||
// allocate memory for structure
|
||||
struct Block* block = (struct Block*)malloc(sizeof(struct Block));
|
||||
if ( block == NULL)
|
||||
(*block) = (struct Block*)malloc(sizeof(struct Block));
|
||||
if ((*block) == NULL)
|
||||
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
|
||||
unsigned char hash[32];
|
||||
if (libp2p_crypto_hashing_sha256(data, data_size, &hash[0]) == 0) {
|
||||
free(*block);
|
||||
return 0;
|
||||
}
|
||||
|
||||
block->cid = ipfs_cid_new(0, hash, 32, CID_DAG_PROTOBUF);
|
||||
if (block->cid == NULL) {
|
||||
if (ipfs_cid_new(0, hash, 32, CID_PROTOBUF, &((*block)->cid)) == 0) {
|
||||
free(*block);
|
||||
return 0;
|
||||
}
|
||||
|
||||
block->data_length = data_size;
|
||||
(*block)->data_length = data_size;
|
||||
|
||||
block->data = malloc(sizeof(unsigned char) * data_size);
|
||||
if ( block->data == NULL) {
|
||||
ipfs_cid_free(block->cid);
|
||||
(*block)->data = malloc(sizeof(unsigned char) * data_size);
|
||||
if ( (*block)->data == NULL) {
|
||||
ipfs_cid_free((*block)->cid);
|
||||
free(*block);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy( block->data, data, data_size);
|
||||
memcpy( (*block)->data, data, data_size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -157,37 +53,10 @@ int ipfs_blocks_block_add_data(const unsigned char* data, size_t data_size, stru
|
|||
* @param block the block to free
|
||||
* @returns true(1) on success
|
||||
*/
|
||||
int ipfs_block_free(struct Block* block) {
|
||||
if (block != NULL) {
|
||||
if (block->cid != NULL)
|
||||
ipfs_cid_free(block->cid);
|
||||
if (block->data != NULL)
|
||||
free(block->data);
|
||||
free(block);
|
||||
}
|
||||
int ipfs_blocks_block_free(struct Block* block) {
|
||||
ipfs_cid_free(block->cid);
|
||||
if (block->data != NULL)
|
||||
free(block->data);
|
||||
free(block);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/***
|
||||
* Make a copy of a block
|
||||
* @param original the original
|
||||
* @returns a new Block that is a copy
|
||||
*/
|
||||
struct Block* ipfs_block_copy(struct Block* original) {
|
||||
struct Block* copy = ipfs_block_new();
|
||||
if (copy != NULL) {
|
||||
copy->data_length = original->data_length;
|
||||
copy->data = (unsigned char*) malloc(original->data_length);
|
||||
if (copy->data == NULL) {
|
||||
ipfs_block_free(copy);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(copy->data, original->data, original->data_length);
|
||||
copy->cid = ipfs_cid_copy(original->cid);
|
||||
if (copy->cid == NULL) {
|
||||
ipfs_block_free(copy);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
|
|
@ -4,54 +4,15 @@
|
|||
#include "libp2p/crypto/encoding/base32.h"
|
||||
#include "ipfs/cid/cid.h"
|
||||
#include "ipfs/blocks/block.h"
|
||||
#include "ipfs/blocks/blockstore.h"
|
||||
#include "ipfs/datastore/ds_helper.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
|
||||
* @param cid the Cid to look for
|
||||
* @param returns true(1) on success
|
||||
*/
|
||||
int ipfs_blockstore_delete(const struct BlockstoreContext* context, struct Cid* cid) {
|
||||
int ipfs_blockstore_delete(struct Cid* cid, struct FSRepo* fs_repo) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -60,88 +21,18 @@ int ipfs_blockstore_delete(const struct BlockstoreContext* context, struct Cid*
|
|||
* @param cid the Cid to look for
|
||||
* @returns true(1) if found
|
||||
*/
|
||||
int ipfs_blockstore_has(const struct BlockstoreContext* context, struct Cid* cid) {
|
||||
int ipfs_blockstore_has(struct Cid* cid, struct FSRepo* fs_repo) {
|
||||
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
|
||||
* @param cid the Cid to look for
|
||||
* @param block where to put the data to be returned
|
||||
* @returns true(1) on success
|
||||
*/
|
||||
int ipfs_blockstore_get(const struct BlockstoreContext* context, struct Cid* cid, struct Block** block) {
|
||||
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;
|
||||
int ipfs_blockstore_get(struct Cid* cid, struct Block* block, struct FSRepo* fs_repo) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
|
@ -149,217 +40,16 @@ int ipfs_blockstore_get(const struct BlockstoreContext* context, struct Cid* cid
|
|||
* @param block the block to store
|
||||
* @returns true(1) on success
|
||||
*/
|
||||
int ipfs_blockstore_put(const struct BlockstoreContext* context, struct Block* block, size_t* bytes_written) {
|
||||
int ipfs_blockstore_put(struct Block* block, struct FSRepo* fs_repo) {
|
||||
// from blockstore.go line 118
|
||||
int retVal = 0;
|
||||
|
||||
// Get Datastore key, which is a base32 key of the multihash,
|
||||
unsigned char* key = ipfs_blockstore_cid_to_base32(block->cid);
|
||||
if (key == NULL) {
|
||||
free(key);
|
||||
// Get Datastore key, which is a base32 key of the binary,
|
||||
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;
|
||||
}
|
||||
|
||||
//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;
|
||||
// send to Put with key
|
||||
fs_repo->config->datastore->datastore_put(key, key_length, block, fs_repo->config->datastore);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
* 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
1
c-libp2p
|
@ -1 +0,0 @@
|
|||
Subproject commit d0c319a88cd1f2cb3a219420b5a0b930e72562e2
|
|
@ -1,5 +1,5 @@
|
|||
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
|
||||
CFLAGS = -O0 -I../include -I../../c-libp2p/include -I../../c-multihash/include -I../../c-multiaddr/include
|
||||
|
||||
ifdef DEBUG
|
||||
CFLAGS += -g3
|
||||
|
@ -7,7 +7,7 @@ endif
|
|||
|
||||
LFLAGS =
|
||||
DEPS =
|
||||
OBJS = cid.o set.o
|
||||
OBJS = cid.o
|
||||
|
||||
%.o: %.c $(DEPS)
|
||||
$(CC) -c -o $@ $< $(CFLAGS)
|
||||
|
|
277
cid/cid.c
277
cid/cid.c
|
@ -9,114 +9,38 @@
|
|||
#include "ipfs/cid/cid.h"
|
||||
#include "libp2p/crypto/encoding/base58.h"
|
||||
#include "ipfs/multibase/multibase.h"
|
||||
#include "mh/hashes.h"
|
||||
#include "mh/multihash.h"
|
||||
#include "varint.h"
|
||||
#include "multiaddr/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
|
||||
* @param version the version
|
||||
* @param hash the multihash
|
||||
* @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_DAG_PROTOBUF)
|
||||
* @returns the new Cid or NULL if there was a problem
|
||||
* @param codec the codec to be used (NOTE: For version 0, this should be CID_PROTOBUF)
|
||||
* @param cid where to put the results
|
||||
* @returns true(1) on success
|
||||
*/
|
||||
struct Cid* ipfs_cid_new(int version, const unsigned char* hash, size_t hash_length, const char codec) {
|
||||
struct Cid* cid = (struct Cid*) malloc(sizeof(struct Cid));
|
||||
if (cid != NULL) {
|
||||
cid->hash_length = hash_length;
|
||||
if (hash_length == 0 || hash == NULL) {
|
||||
cid->hash = NULL;
|
||||
} else {
|
||||
cid->hash = (unsigned char*) malloc(sizeof(unsigned char) * hash_length);
|
||||
if (cid->hash == NULL) {
|
||||
free(cid);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(cid->hash, hash, hash_length);
|
||||
}
|
||||
// assign other values
|
||||
cid->version = version;
|
||||
cid->codec = codec;
|
||||
int ipfs_cid_new(int version, unsigned char* hash, size_t hash_length, const char codec, struct Cid** ptrToCid) {
|
||||
// allocate memory
|
||||
*ptrToCid = (struct Cid*)malloc(sizeof(struct Cid));
|
||||
struct Cid* cid = *ptrToCid;
|
||||
if (cid == NULL)
|
||||
return 0;
|
||||
cid->hash = malloc(sizeof(unsigned char) * hash_length);
|
||||
if (cid->hash == NULL) {
|
||||
free(cid);
|
||||
return 0;
|
||||
}
|
||||
return cid;
|
||||
// assign values
|
||||
cid->version = version;
|
||||
cid->codec = codec;
|
||||
memcpy(cid->hash, hash, hash_length);
|
||||
cid->hash_length = hash_length;
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
/***
|
||||
|
@ -125,63 +49,20 @@ struct Cid* ipfs_cid_new(int version, const unsigned char* hash, size_t hash_len
|
|||
* @returns 1
|
||||
*/
|
||||
int ipfs_cid_free(struct Cid* cid) {
|
||||
if (cid != NULL) {
|
||||
if (cid->hash != NULL) {
|
||||
free(cid->hash);
|
||||
cid->hash = NULL;
|
||||
}
|
||||
free(cid);
|
||||
}
|
||||
if (cid->hash != NULL)
|
||||
free(cid->hash);
|
||||
free(cid);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/***
|
||||
* 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
|
||||
* Fill a Cid struct based on a base 58 encoded string
|
||||
* @param incoming the string
|
||||
* @param incoming_size the size of the string
|
||||
* @cid the Cid struct to fill
|
||||
* @return true(1) on success
|
||||
*/
|
||||
int ipfs_cid_decode_hash_from_base58(const unsigned char* incoming, size_t incoming_length, struct Cid** cid) {
|
||||
int ipfs_cid_decode_from_string(const unsigned char* incoming, size_t incoming_length, struct Cid** cid) {
|
||||
int retVal = 0;
|
||||
|
||||
if (incoming_length < 2)
|
||||
|
@ -196,8 +77,7 @@ int ipfs_cid_decode_hash_from_base58(const unsigned char* incoming, size_t incom
|
|||
if (retVal == 0)
|
||||
return 0;
|
||||
// now we have the hash, build the object
|
||||
*cid = ipfs_cid_new(0, &hash[2], hash_length - 2, CID_DAG_PROTOBUF);
|
||||
return *cid != NULL;
|
||||
return ipfs_cid_new(0, hash, hash_length, CID_PROTOBUF, cid);
|
||||
}
|
||||
|
||||
// TODO: finish this
|
||||
|
@ -219,65 +99,17 @@ int ipfs_cid_decode_hash_from_base58(const unsigned char* incoming, size_t incom
|
|||
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
|
||||
* @param incoming the multibase decoded array
|
||||
* @param incoming_size the size of the array
|
||||
* @param cid the Cid structure to fill
|
||||
*/
|
||||
int ipfs_cid_cast(const unsigned char* incoming, size_t incoming_size, struct Cid* cid) {
|
||||
int ipfs_cid_cast(unsigned char* incoming, size_t incoming_size, struct Cid* cid) {
|
||||
// this is a multihash
|
||||
if (incoming_size == 34 && incoming[0] == 18 && incoming[1] == 32) {
|
||||
// this is a multihash
|
||||
cid->hash_length = mh_multihash_length(incoming, incoming_size);
|
||||
cid->codec = CID_DAG_PROTOBUF;
|
||||
cid->codec = CID_PROTOBUF;
|
||||
cid->version = 0;
|
||||
|
||||
mh_multihash_digest(incoming, incoming_size, &cid->hash, &cid->hash_length);
|
||||
|
@ -286,57 +118,22 @@ int ipfs_cid_cast(const unsigned char* incoming, size_t incoming_size, struct Ci
|
|||
|
||||
// This is not a multihash. Perhaps it is using varints. Try to peel the information out of the bytes.
|
||||
// first the version
|
||||
int pos = 0;
|
||||
int pos = 0, retVal = 0;
|
||||
size_t num_bytes = 0;
|
||||
cid->version = varint_decode(&incoming[pos], incoming_size - pos, &num_bytes);
|
||||
if (num_bytes == 0 || cid->version > 1 || cid->version < 0)
|
||||
num_bytes = uvarint_decode32(&incoming[pos], incoming_size - pos, &cid->version);
|
||||
if (num_bytes < 0 || cid->version > 1 || cid->version < 0)
|
||||
return 0;
|
||||
pos = num_bytes;
|
||||
// now the codec
|
||||
uint32_t codec = 0;
|
||||
codec = varint_decode(&incoming[pos], incoming_size - pos, &num_bytes);
|
||||
if (num_bytes == 0)
|
||||
num_bytes = uvarint_decode32(&incoming[pos], incoming_size - pos, &codec);
|
||||
if (num_bytes < 0)
|
||||
return 0;
|
||||
cid->codec = codec;
|
||||
pos += num_bytes;
|
||||
// now what is left
|
||||
cid->hash_length = incoming_size - pos;
|
||||
cid->hash = (unsigned char*)(&incoming[pos]);
|
||||
cid->hash = &incoming[pos];
|
||||
|
||||
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
185
cid/set.c
|
@ -1,185 +0,0 @@
|
|||
#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;
|
||||
}
|
22
cmd/Makefile
22
cmd/Makefile
|
@ -1,23 +1,5 @@
|
|||
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)
|
||||
all:
|
||||
cd ipfs; make all;
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
cd ipfs; make clean;
|
||||
cd ipfs; make clean;
|
59
cmd/cli.c
59
cmd/cli.c
|
@ -1,59 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "ipfs/cmd/cli.h"
|
||||
|
||||
int cli_is_switch(int argc, char** argv, int index) {
|
||||
char* to_test = argv[index];
|
||||
if (to_test[0] == '-') {
|
||||
if (strcmp(to_test, "-c") == 0 || strcmp(to_test, "--config") == 0) {
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int cli_get_verb_index(struct CliArguments* args) {
|
||||
for(int i = 1; i < args->argc; i++) {
|
||||
int advance_by_more_than_one = cli_is_switch(args->argc, args->argv, i);
|
||||
if (advance_by_more_than_one == 0) {
|
||||
// this is the verb
|
||||
return i;
|
||||
} else {
|
||||
if (advance_by_more_than_one == 2) {
|
||||
// skip the next one
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* cli_get_config_dir(struct CliArguments* args) {
|
||||
for (int i = 1; i < args->argc; i++) {
|
||||
if (args->argv[i][0] == '-') {
|
||||
char* param = args->argv[i];
|
||||
if ((strcmp(param, "-c") == 0 || strcmp(param, "--config") == 0) && args->argc > i + 1) {
|
||||
return args->argv[i+1];
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
struct CliArguments* cli_arguments_new(int argc, char** argv) {
|
||||
struct CliArguments* args = (struct CliArguments*) malloc(sizeof(struct CliArguments));
|
||||
if (args != NULL) {
|
||||
args->argc = argc;
|
||||
args->argv = argv;
|
||||
args->verb_index = cli_get_verb_index(args);
|
||||
args->config_dir = cli_get_config_dir(args);
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
void cli_arguments_free(struct CliArguments* args) {
|
||||
free(args);
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
CC = gcc
|
||||
CFLAGS = -O0 -I../../include -I../../c-libp2p/include -I../../c-libp2p/c-multiaddr/include -I../../c-libp2p/c-protobuf -Wall
|
||||
CFLAGS = -O0 -I../../include -I../../../c-libp2p/include
|
||||
|
||||
ifdef DEBUG
|
||||
CFLAGS += -g3
|
||||
|
@ -19,4 +19,4 @@ all: $(OBJS)
|
|||
|
||||
clean:
|
||||
rm -f *.o
|
||||
rm -f ipfs
|
||||
rm -f ipfs
|
|
@ -1,11 +1,10 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "ipfs/cmd/ipfs/init.h"
|
||||
#include "ipfs/commands/request.h"
|
||||
#include "ipfs/commands/command_option.h"
|
||||
#include "libp2p/os/utils.h"
|
||||
#include "ipfs/os/utils.h"
|
||||
#include "ipfs/core/ipfs_node.h"
|
||||
#include "ipfs/core/builder.h"
|
||||
#include "ipfs/repo/config/config.h"
|
||||
|
@ -35,9 +34,9 @@ int initialize_ipns_keyspace(struct FSRepo* repo) {
|
|||
return 0;
|
||||
//TODO: make a new node, then close it
|
||||
//TODO: setup offline routing on new node
|
||||
struct IpfsNode* ipfs_node = NULL;
|
||||
struct Context* ctx = NULL;
|
||||
struct BuildCfg* bld_cfg = NULL;
|
||||
struct IpfsNode* ipfs_node;
|
||||
struct Context* ctx;
|
||||
struct BuildCfg* bld_cfg;
|
||||
//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);
|
||||
//return namesys_initialize_keyspace(ctx, ipfs_node->DAG, ipfs_node->Namesys, ipfs_node->pinning, ipfs_node->private_key);
|
||||
|
@ -61,8 +60,8 @@ int do_init(FILE* out_file, char* repo_root, int empty, int num_bits_for_keypair
|
|||
if (fs_repo_is_initialized(repo_root))
|
||||
return 0;
|
||||
//TODO: If the conf is null, make one
|
||||
if ( conf->identity->peer == NULL || conf->identity->peer->id == NULL) {
|
||||
int retVal = ipfs_repo_config_init(conf, num_bits_for_keypair, repo_root, 4001, NULL);
|
||||
if ( conf->identity->peer_id == NULL) {
|
||||
int retVal = ipfs_repo_config_init(conf, num_bits_for_keypair, repo_root);
|
||||
if (retVal == 0)
|
||||
return 0;
|
||||
}
|
||||
|
@ -89,8 +88,7 @@ int init_run(struct Request* request) {
|
|||
// TODO: check parameters for logic errors
|
||||
// TODO: Initialize
|
||||
struct RepoConfig* conf;
|
||||
if (ipfs_repo_config_new(&conf) == 0)
|
||||
return 0;
|
||||
int retVal = ipfs_repo_config_new(&conf);
|
||||
// TODO: handle files in request
|
||||
// do the heavy lifting
|
||||
int num_bits_for_key_pair = request->cmd.options[0]->default_int_val;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
CC = gcc
|
||||
CFLAGS = -O0 -I../include -I../c-libp2p/include -Wall -std=gnu99
|
||||
CFLAGS = -O0 -I../include -I../../c-libp2p/include
|
||||
LFLAGS =
|
||||
DEPS = ../include/ipfs/commands/argument.h ../include/ipfs/commands/command_option.h \
|
||||
../include/ipfs/commands/command.h ../include/ipfs/commands/context.h \
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
CC = gcc
|
||||
CFLAGS = -O0 -I../../include -I../../c-libp2p/include -Wall
|
||||
CFLAGS = -O0 -I../../include -I../../../c-libp2p/include
|
||||
LFLAGS =
|
||||
DEPS = parse.h
|
||||
OBJS = parse.o
|
||||
|
|
|
@ -1,23 +1,5 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "libp2p/utils/logger.h"
|
||||
#include "ipfs/commands/cli/parse.h"
|
||||
|
||||
|
||||
/***
|
||||
* 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;
|
||||
}
|
||||
int cli_parse(char** params, FILE* inStream, struct Command* cmd, struct Request* request) {
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
//
|
||||
// command.c
|
||||
// c-ipfs
|
||||
//
|
||||
// Created by John Jones on 10/27/16.
|
||||
// Copyright © 2016 JMJAtlanta. All rights reserved.
|
||||
//
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "ipfs/commands/command.h"
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
//
|
||||
// option.c
|
||||
// c-ipfs
|
||||
//
|
||||
// Created by John Jones on 10/26/16.
|
||||
// Copyright © 2016 JMJAtlanta. All rights reserved.
|
||||
//
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
|
|
@ -1,13 +1,8 @@
|
|||
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
|
||||
|
||||
CFLAGS = -O0 -I../include -I../../c-libp2p/include
|
||||
LFLAGS =
|
||||
DEPS = builder.h ipfs_node.h
|
||||
OBJS = builder.o daemon.o null.o ping.o bootstrap.o ipfs_node.o api.o client_api.o http_request.o swarm.o
|
||||
OBJS = builder.o
|
||||
|
||||
%.o: %.c $(DEPS)
|
||||
$(CC) -c -o $@ $< $(CFLAGS)
|
||||
|
|
772
core/api.c
772
core/api.c
|
@ -1,772 +0,0 @@
|
|||
/**
|
||||
* Methods for lightweight/specific HTTP for API communication.
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#define __USE_GNU
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "libp2p/net/p2pnet.h"
|
||||
#include "libp2p/os/memstream.h"
|
||||
#include "libp2p/utils/logger.h"
|
||||
#include "libp2p/utils/urlencode.h"
|
||||
#include "ipfs/core/api.h"
|
||||
#include "ipfs/importer/exporter.h"
|
||||
#include "ipfs/core/http_request.h"
|
||||
|
||||
//pthread_mutex_t conns_lock;
|
||||
//int conns_count;
|
||||
|
||||
struct ApiContext api_list;
|
||||
|
||||
/**
|
||||
* Write two strings on one write.
|
||||
* @param fd file descriptor to write.
|
||||
* @param str1 first string to write.
|
||||
* @param str2 second string to write.
|
||||
*/
|
||||
size_t write_dual(int fd, char *str1, char *str2)
|
||||
{
|
||||
struct iovec iov[2];
|
||||
|
||||
iov[0].iov_base = str1;
|
||||
iov[0].iov_len = strlen(str1);
|
||||
iov[1].iov_base = str2;
|
||||
iov[1].iov_len = strlen(str2);
|
||||
|
||||
return writev(fd, iov, 2);
|
||||
}
|
||||
|
||||
int find_chunk(char *buf, const size_t buf_size, size_t *pos, size_t *size)
|
||||
{
|
||||
char *p = NULL;
|
||||
|
||||
*size = strtol(buf, &p, 16);
|
||||
if (!p || p < buf || p > (buf + 10)) {
|
||||
return 0;
|
||||
}
|
||||
*pos = (int)(p - buf);
|
||||
if (p[0] == '\r' && p[1] == '\n') {
|
||||
*pos += 2;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int read_chunked(int fd, struct s_request *req, char *already, size_t already_size)
|
||||
{
|
||||
char buf[MAX_READ], *p;
|
||||
size_t pos, nsize, buf_size = 0, r;
|
||||
|
||||
if (already_size > 0) {
|
||||
if (already_size <= sizeof(buf)) {
|
||||
memcpy(buf, already, already_size);
|
||||
buf_size += already_size;
|
||||
already_size = 0;
|
||||
} else {
|
||||
memcpy(buf, already, sizeof(buf));
|
||||
already += sizeof(buf);
|
||||
buf_size += sizeof(buf);
|
||||
already_size -= sizeof(buf);
|
||||
}
|
||||
}
|
||||
|
||||
while(buf_size) {
|
||||
if (!find_chunk(buf, buf_size, &pos, &nsize)) {
|
||||
libp2p_logger_error("api", "fail find_chunk.\n");
|
||||
libp2p_logger_error("api", "nsize = %d.\n", nsize);
|
||||
return 0;
|
||||
}
|
||||
if (nsize == 0) {
|
||||
break;
|
||||
}
|
||||
p = realloc(req->buf, req->size + nsize);
|
||||
if (!p) {
|
||||
libp2p_logger_error("api", "fail realloc.\n");
|
||||
return 0;
|
||||
}
|
||||
req->buf = p;
|
||||
req->size += nsize;
|
||||
|
||||
CPCHUNK:
|
||||
r = nsize;
|
||||
buf_size -= pos;
|
||||
if (r > buf_size) {
|
||||
r = buf_size;
|
||||
}
|
||||
memcpy(req->buf + req->body + req->body_size, buf + pos, r);
|
||||
req->body_size += r;
|
||||
nsize -= r;
|
||||
buf_size -= r;
|
||||
if (buf_size > 0) {
|
||||
memmove(buf, buf + pos + r, buf_size);
|
||||
}
|
||||
pos = 0;
|
||||
if (already_size > 0) {
|
||||
r = sizeof(buf) - buf_size;
|
||||
if (already_size <= r) {
|
||||
memcpy(buf, already, already_size);
|
||||
buf_size += already_size;
|
||||
already_size = 0;
|
||||
} else {
|
||||
memcpy(buf, already, r);
|
||||
already += r;
|
||||
buf_size += r;
|
||||
already_size -= r;
|
||||
}
|
||||
}
|
||||
|
||||
if (socket_read_select4(fd, 5) > 0) {
|
||||
r = sizeof(buf) - buf_size;
|
||||
r = read(fd, buf+buf_size, r);
|
||||
buf_size += r;
|
||||
if (r == 0 && nsize == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (r <= 0) {
|
||||
libp2p_logger_error("api", "read fail.\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (nsize > 0)
|
||||
goto CPCHUNK; // still have data to transfer on current chunk.
|
||||
|
||||
if (memcmp (buf, "\r\n", 2)!=0) {
|
||||
libp2p_logger_error("api", "fail CRLF.\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int read_all(int fd, struct s_request *req, char *already, size_t alread_size)
|
||||
{
|
||||
char buf[MAX_READ], *p;
|
||||
size_t size = 0;
|
||||
|
||||
if (alread_size > 0) {
|
||||
p = realloc(req->buf, req->size + alread_size);
|
||||
if (!p) {
|
||||
return 0;
|
||||
}
|
||||
req->buf = p;
|
||||
req->size += alread_size;
|
||||
memcpy(req->buf + req->body + req->body_size, already, alread_size);
|
||||
req->body_size += alread_size;
|
||||
}
|
||||
for(;;) {
|
||||
if (socket_read_select4(fd, 5) <= 0) {
|
||||
break;
|
||||
}
|
||||
size = read(fd, buf, sizeof buf);
|
||||
if (size <= 0) {
|
||||
break;
|
||||
}
|
||||
p = realloc(req->buf, req->size + size);
|
||||
if (!p) {
|
||||
return 0;
|
||||
}
|
||||
req->buf = p;
|
||||
req->size += size;
|
||||
memcpy(req->buf + req->body + req->body_size, buf, size);
|
||||
req->body_size += size;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a token in a string array.
|
||||
* @param string array and token string.
|
||||
* @returns the pointer after where the token was found or NULL if it fails.
|
||||
*/
|
||||
char *str_tok(char *str, char *tok)
|
||||
{
|
||||
char *p = strstr(str, tok);
|
||||
if (p) {
|
||||
p += strlen(tok);
|
||||
while(*p == ' ') p++;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a token in a binary array.
|
||||
* @param array, size of array, token and size of token.
|
||||
* @returns the pointer after where the token was found or NULL if it fails.
|
||||
*/
|
||||
char *bin_tok(char *bin, size_t limit, char *tok, size_t tok_size)
|
||||
{
|
||||
char *p = memmem(bin, limit, tok, tok_size);
|
||||
if (p) {
|
||||
p += tok_size;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if header contain a especific value.
|
||||
* @param request structure, header name and value to check.
|
||||
* @returns the pointer where the value was found or NULL if it fails.
|
||||
*/
|
||||
char *header_value_cmp(struct s_request *req, char *header, char *value)
|
||||
{
|
||||
char *p = str_tok(req->buf + req->header, header);
|
||||
if (p) {
|
||||
if (strstart(p, value)) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup for boundary at buffer string.
|
||||
* @param body buffer string, boundary id, filename and content-type string.
|
||||
* @returns the pointer where the multipart start.
|
||||
*/
|
||||
char *boundary_find(char *str, char *boundary, char **filename, char **contenttype)
|
||||
{
|
||||
char *p = str_tok(str, "--");
|
||||
while (p) {
|
||||
if (strstart(p, boundary)) {
|
||||
// skip to the beginning, ignoring the header for now, if there is.
|
||||
// TODO: return filename and content-type
|
||||
p = strstr(p, "\r\n\r\n");
|
||||
if (p) {
|
||||
return p + 4; // ignore 4 bytes CRLF 2x
|
||||
}
|
||||
break;
|
||||
}
|
||||
p = str_tok(str, "--");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the size of boundary.
|
||||
* @param boundary buffer, boundary id.
|
||||
* @returns the size of boundary or 0 if fails.
|
||||
*/
|
||||
size_t boundary_size(char *str, char *boundary, size_t limit)
|
||||
{
|
||||
char *p = bin_tok(str, limit, "\r\n--", 4);
|
||||
while (p) {
|
||||
if (strstart(p, boundary)) {
|
||||
if (cstrstart(p + strlen(boundary), "--\r\n")) {
|
||||
p -= 4;
|
||||
return (size_t)(p - str);
|
||||
}
|
||||
}
|
||||
p = bin_tok(p, limit, "\r\n--", 4);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ApiConnectionParam {
|
||||
int index;
|
||||
struct IpfsNode* this_node;
|
||||
};
|
||||
|
||||
/***
|
||||
* Take an s_request and turn it into an HttpRequest
|
||||
* @param req the incoming s_request
|
||||
* @returns the resultant HttpRequest or NULL on error
|
||||
*/
|
||||
struct HttpRequest* api_build_http_request(struct s_request* req) {
|
||||
struct HttpRequest* request = ipfs_core_http_request_new();
|
||||
if (request != NULL) {
|
||||
char *segs = malloc (strlen(req->buf + req->request) + 1);
|
||||
if (segs) {
|
||||
strcpy(segs, req->buf + req->request);
|
||||
request->command = segs;
|
||||
segs = strchr(segs, '/');
|
||||
if (segs) {
|
||||
*segs++ = '\0';
|
||||
request->sub_command = segs; // sub_command can contain another level as filters/add
|
||||
}
|
||||
if (req->query) {
|
||||
segs = libp2p_utils_url_decode(req->buf + req->query);
|
||||
if (segs) {
|
||||
while (segs) {
|
||||
char *value, *name = segs;
|
||||
segs = strchr(segs, '&');
|
||||
if (segs) { // calc next to split before search for = on another parameter.
|
||||
*segs++ = '\0';
|
||||
}
|
||||
|
||||
value = strchr(name, '=');
|
||||
if (value) {
|
||||
*value++ = '\0';
|
||||
}
|
||||
if (value && (strcmp(name, "arg")==0)) {
|
||||
libp2p_utils_vector_add(request->arguments, strdup(value));
|
||||
} else {
|
||||
struct HttpParam *hp = ipfs_core_http_param_new();
|
||||
if (hp) {
|
||||
hp->name = strdup(name);
|
||||
hp->value = strdup(value); // maybe null ?
|
||||
libp2p_utils_vector_add(request->params, hp);
|
||||
}
|
||||
}
|
||||
}
|
||||
free(segs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write bytes into chunks.
|
||||
* @param socket, buffer array, length
|
||||
* @returns 1 when success or 0 if it fails.
|
||||
*/
|
||||
int api_send_resp_chunks(int fd, void *buf, size_t size)
|
||||
{
|
||||
char head[20];
|
||||
size_t s;
|
||||
int l;
|
||||
struct iovec iov[3];
|
||||
|
||||
// will be reused in each write, so defined only once.
|
||||
iov[2].iov_base = "\r\n";
|
||||
iov[2].iov_len = 2;
|
||||
|
||||
while (size > 0) {
|
||||
s = size > MAX_CHUNK ? MAX_CHUNK : size; // write only MAX_CHUNK at once
|
||||
l = snprintf(head, sizeof head, "%x\r\n", (unsigned int)s);
|
||||
if (l <= 0)
|
||||
return 0; // fail at snprintf
|
||||
|
||||
iov[0].iov_base = head;
|
||||
iov[0].iov_len = l; // head length.
|
||||
iov[1].iov_base = buf;
|
||||
iov[1].iov_len = s;
|
||||
|
||||
buf += s;
|
||||
size -= s;
|
||||
|
||||
if (size == 0) { // last chunk
|
||||
iov[2].iov_base = "\r\n0\r\n\r\n";
|
||||
iov[2].iov_len = 7;
|
||||
}
|
||||
libp2p_logger_debug("api", "writing chunk block of %d bytes\n", s);
|
||||
if (writev(fd, iov, 3) == -1)
|
||||
return 0; // fail writing.
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pthread to take care of each client connection.
|
||||
* @param ptr an ApiConnectionParam
|
||||
* @returns nothing
|
||||
*/
|
||||
void *api_connection_thread (void *ptr)
|
||||
{
|
||||
int timeout, s, r;
|
||||
struct ApiConnectionParam* params = (struct ApiConnectionParam*)ptr;
|
||||
char resp[MAX_READ+1], buf[MAX_READ+1], *p, *body;
|
||||
char client[INET_ADDRSTRLEN];
|
||||
struct s_request req;
|
||||
int (*read_func)(int, struct s_request*, char*, size_t) = read_all;
|
||||
|
||||
req.buf = NULL; // sanity.
|
||||
|
||||
buf[MAX_READ] = '\0';
|
||||
|
||||
s = params->this_node->api_context->conns[params->index]->socket;
|
||||
timeout = params->this_node->api_context->timeout;
|
||||
|
||||
if (socket_read_select4(s, timeout) <= 0) {
|
||||
libp2p_logger_error("api", "Client connection timeout.\n");
|
||||
goto quit;
|
||||
}
|
||||
r = read(s, buf, sizeof buf);
|
||||
if (r <= 0) {
|
||||
// this is a common occurrence, so moved from error to debug
|
||||
libp2p_logger_debug("api", "Read from client fail.\n");
|
||||
goto quit;
|
||||
}
|
||||
buf[r] = '\0';
|
||||
|
||||
p = strstr(buf, "\r\n\r\n");
|
||||
|
||||
if (p) {
|
||||
body = p + 4;
|
||||
|
||||
req.size = p - buf + 1;
|
||||
req.buf = malloc(req.size);
|
||||
if (!req.buf) {
|
||||
// memory allocation fail.
|
||||
libp2p_logger_error("api", "malloc fail.\n");
|
||||
write_cstr (s, HTTP_500);
|
||||
goto quit;
|
||||
}
|
||||
memcpy(req.buf, buf, req.size - 1);
|
||||
req.buf[req.size-1] = '\0';
|
||||
|
||||
req.method = 0;
|
||||
p = strchr(req.buf + req.method, ' ');
|
||||
if (!p) {
|
||||
libp2p_logger_error("api", "fail looking for space on method '%s'.\n", req.buf + req.method);
|
||||
write_cstr (s, HTTP_400);
|
||||
goto quit;
|
||||
}
|
||||
*p++ = '\0'; // End of method.
|
||||
req.path = p - req.buf;
|
||||
if (strchr(p, '?')) {
|
||||
p = strchr(p, '?');
|
||||
*p++ = '\0';
|
||||
req.query = p - req.buf;
|
||||
} else {
|
||||
req.query = 0;
|
||||
}
|
||||
p = strchr(p, ' ');
|
||||
if (!p) {
|
||||
libp2p_logger_error("api", "fail looking for space on path '%s'.\n", req.buf + req.path);
|
||||
write_cstr (s, HTTP_400);
|
||||
goto quit;
|
||||
}
|
||||
*p++ = '\0'; // End of path.
|
||||
req.http_ver = p - req.buf;
|
||||
p = strchr(req.buf + req.http_ver, '\r');
|
||||
if (!p) {
|
||||
libp2p_logger_error("api", "fail looking for CR on http_ver '%s'.\n", req.buf + req.http_ver);
|
||||
write_cstr (s, HTTP_400);
|
||||
goto quit;
|
||||
}
|
||||
*p++ = '\0'; // End of http version.
|
||||
while (*p == '\r' || *p == '\n') p++;
|
||||
req.header = p - req.buf;
|
||||
req.body = req.size;
|
||||
req.body_size = 0;
|
||||
|
||||
if (header_value_cmp(&req, "Transfer-Encoding:", "chunked")) {
|
||||
read_func = read_chunked;
|
||||
}
|
||||
|
||||
if (!read_func(s, &req, body, r - (body - buf))) {
|
||||
libp2p_logger_error("api", "fail read_func.\n");
|
||||
write_cstr (s, HTTP_500);
|
||||
goto quit;
|
||||
}
|
||||
|
||||
if (strncmp(req.buf + req.method, "GET", 3)==0) {
|
||||
if (strcmp (req.buf + req.path, "/")==0 ||
|
||||
strcmp (req.buf + req.path, "/webui")==0 ||
|
||||
strcmp (req.buf + req.path, "/webui/")==0) {
|
||||
char *redir;
|
||||
size_t size = sizeof(HTTP_301) + (sizeof(WEBUI_ADDR)*2);
|
||||
|
||||
redir = malloc(size);
|
||||
if (redir) {
|
||||
snprintf(redir, size, HTTP_301, WEBUI_ADDR, WEBUI_ADDR);
|
||||
redir[size-1] = '\0'; // just in case
|
||||
write_dual (s, req.buf + req.http_ver, strchr (redir, ' '));
|
||||
free (redir);
|
||||
} else {
|
||||
write_cstr (s, HTTP_500);
|
||||
}
|
||||
} else if (!cstrstart(req.buf + req.path, API_V0_START)) {
|
||||
// TODO: handle download file here.
|
||||
// move out of the if to do further processing
|
||||
}
|
||||
// end of GET
|
||||
} else if (strncmp(req.buf + req.method, "POST", 4)==0) {
|
||||
// TODO: Handle gzip/json POST requests.
|
||||
|
||||
p = header_value_cmp(&req, "Content-Type:", "multipart/form-data;");
|
||||
if (p) {
|
||||
p = str_tok(p, "boundary=");
|
||||
if (p) {
|
||||
char *boundary, *l;
|
||||
int len;
|
||||
if (*p == '"') {
|
||||
p++;
|
||||
l = strchr(p, '"');
|
||||
} else {
|
||||
l = p;
|
||||
while (*l != '\r' && *l != '\0') l++;
|
||||
}
|
||||
len = l - p;
|
||||
boundary = malloc (len+1);
|
||||
if (boundary) {
|
||||
memcpy(boundary, p, len);
|
||||
boundary[len] = '\0';
|
||||
|
||||
p = boundary_find(req.buf + req.body, boundary, NULL, NULL);
|
||||
if (p) {
|
||||
req.boundary_size = boundary_size(p, boundary, req.size - (p - buf));
|
||||
if (req.boundary_size > 0) {
|
||||
req.boundary = p - req.buf;
|
||||
}
|
||||
}
|
||||
|
||||
free (boundary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (req.boundary > 0) {
|
||||
libp2p_logger_error("api", "boundary index = %d, size = %d\n", req.boundary, req.boundary_size);
|
||||
}
|
||||
|
||||
libp2p_logger_debug("api", "method = '%s'\n"
|
||||
"path = '%s'\n"
|
||||
"http_ver = '%s'\n"
|
||||
"header {\n%s\n}\n"
|
||||
"body_size = %d\n",
|
||||
req.buf+req.method, req.buf+req.path, req.buf+req.http_ver,
|
||||
req.buf+req.header, req.body_size);
|
||||
// end of POST
|
||||
} else {
|
||||
// Unexpected???
|
||||
libp2p_logger_error("api", "fail unexpected '%s'.\n", req.buf + req.method);
|
||||
write_cstr (s, HTTP_500);
|
||||
}
|
||||
|
||||
if (cstrstart(req.buf + req.path, API_V0_START)) {
|
||||
req.request = req.path + sizeof(API_V0_START) - 1;
|
||||
// now do something with the request we have built
|
||||
struct HttpRequest* http_request = api_build_http_request(&req);
|
||||
if (http_request != NULL) {
|
||||
struct HttpResponse* http_response = NULL;
|
||||
if (!ipfs_core_http_request_process(params->this_node, http_request, &http_response)) {
|
||||
libp2p_logger_error("api", "ipfs_core_http_request_process returned false.\n");
|
||||
// 404
|
||||
write_str(s, HTTP_404);
|
||||
} else {
|
||||
snprintf(resp, MAX_READ+1, "%s 200 OK\r\n" \
|
||||
"Content-Type: %s\r\n"
|
||||
"Server: c-ipfs/0.0.0-dev\r\n"
|
||||
"X-Chunked-Output: 1\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Transfer-Encoding: chunked\r\n"
|
||||
"\r\n"
|
||||
,req.buf + req.http_ver, http_response->content_type);
|
||||
write_str (s, resp);
|
||||
api_send_resp_chunks(s, http_response->bytes, http_response->bytes_size);
|
||||
libp2p_logger_debug("api", "resp = {\n%s\n}\n", resp);
|
||||
}
|
||||
ipfs_core_http_request_free(http_request);
|
||||
ipfs_core_http_response_free(http_response);
|
||||
} else {
|
||||
// uh oh... something went wrong converting to the HttpRequest struct
|
||||
libp2p_logger_error("api", "Unable to build HttpRequest struct.\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
libp2p_logger_error("api", "fail looking for body.\n");
|
||||
write_cstr (s, HTTP_400);
|
||||
}
|
||||
|
||||
quit:
|
||||
if (req.buf)
|
||||
free(req.buf);
|
||||
if (inet_ntop(AF_INET, &( params->this_node->api_context->conns[params->index]->ipv4), client, INET_ADDRSTRLEN) == NULL)
|
||||
strcpy(client, "UNKNOW");
|
||||
libp2p_logger_debug("api", "Closing client connection %s:%d (%d).\n", client, params->this_node->api_context->conns[params->index]->port, params->index+1);
|
||||
pthread_mutex_lock(¶ms->this_node->api_context->conns_lock);
|
||||
close(s);
|
||||
free ( params->this_node->api_context->conns[params->index]);
|
||||
params->this_node->api_context->conns[params->index] = NULL;
|
||||
params->this_node->api_context->conns_count--;
|
||||
pthread_mutex_unlock(¶ms->this_node->api_context->conns_lock);
|
||||
free(params);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all connections stopping respectives pthreads and free allocated memory.
|
||||
*/
|
||||
void api_connections_cleanup (struct IpfsNode* local_node)
|
||||
{
|
||||
int i;
|
||||
|
||||
pthread_mutex_lock(&local_node->api_context->conns_lock);
|
||||
if (local_node->api_context->conns_count > 0 && local_node->api_context->conns) {
|
||||
for (i = 0 ; i < local_node->api_context->max_conns ; i++) {
|
||||
if (local_node->api_context->conns[i]->pthread) {
|
||||
pthread_cancel (local_node->api_context->conns[i]->pthread);
|
||||
close (local_node->api_context->conns[i]->socket);
|
||||
free (local_node->api_context->conns[i]);
|
||||
local_node->api_context->conns[i] = NULL;
|
||||
}
|
||||
}
|
||||
local_node->api_context->conns_count = 0;
|
||||
}
|
||||
if (local_node->api_context->conns) {
|
||||
free (local_node->api_context->conns);
|
||||
local_node->api_context->conns = NULL;
|
||||
}
|
||||
pthread_mutex_unlock(&local_node->api_context->conns_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pthread to keep in background dealing with client connections.
|
||||
* @param ptr is not used.
|
||||
* @returns nothing
|
||||
*/
|
||||
void *api_listen_thread (void *ptr)
|
||||
{
|
||||
int s;
|
||||
INT_TYPE i;
|
||||
uint32_t ipv4;
|
||||
uint16_t port;
|
||||
char client[INET_ADDRSTRLEN];
|
||||
struct IpfsNode* local_node = (struct IpfsNode*)ptr;
|
||||
|
||||
local_node->api_context->conns_count = 0;
|
||||
|
||||
for (;;) {
|
||||
s = socket_accept4(local_node->api_context->socket, &ipv4, &port);
|
||||
if (s <= 0) {
|
||||
break;
|
||||
}
|
||||
if (local_node->api_context->conns_count >= local_node->api_context->max_conns) { // limit reached.
|
||||
libp2p_logger_error("api", "Limit of connections reached (%d).\n", local_node->api_context->max_conns);
|
||||
close (s);
|
||||
continue;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&local_node->api_context->conns_lock);
|
||||
for (i = 0 ; i < local_node->api_context->max_conns && local_node->api_context->conns[i] ; i++);
|
||||
local_node->api_context->conns[i] = malloc (sizeof (struct s_conns));
|
||||
if (!local_node->api_context->conns[i]) {
|
||||
libp2p_logger_error("api", "Fail to allocate memory to accept connection.\n");
|
||||
pthread_mutex_unlock(&local_node->api_context->conns_lock);
|
||||
close (s);
|
||||
continue;
|
||||
}
|
||||
if (inet_ntop(AF_INET, &ipv4, client, INET_ADDRSTRLEN) == NULL)
|
||||
strcpy(client, "UNKNOW");
|
||||
local_node->api_context->conns[i]->socket = s;
|
||||
local_node->api_context->conns[i]->ipv4 = ipv4;
|
||||
local_node->api_context->conns[i]->port = port;
|
||||
// create a struct, which the thread is responsible to destroy
|
||||
struct ApiConnectionParam* connection_param = (struct ApiConnectionParam*) malloc(sizeof(struct ApiConnectionParam));
|
||||
if (connection_param == NULL) {
|
||||
libp2p_logger_error("api", "api_listen_thread: Unable to allocate memory.\n");
|
||||
pthread_mutex_unlock(&local_node->api_context->conns_lock);
|
||||
close (s);
|
||||
continue;
|
||||
}
|
||||
connection_param->index = i;
|
||||
connection_param->this_node = local_node;
|
||||
if (pthread_create(&(local_node->api_context->conns[i]->pthread), NULL, api_connection_thread, (void*)connection_param)) {
|
||||
libp2p_logger_error("api", "Create pthread fail.\n");
|
||||
free (local_node->api_context->conns[i]);
|
||||
local_node->api_context->conns[i] = NULL;
|
||||
local_node->api_context->conns_count--;
|
||||
close(s);
|
||||
} else {
|
||||
local_node->api_context->conns_count++;
|
||||
}
|
||||
libp2p_logger_debug("api", "API for %s: Accept connection %s:%d (%d/%d), pthread %d.\n", local_node->identity->peer->id, client, port, local_node->api_context->conns_count, local_node->api_context->max_conns, i+1);
|
||||
pthread_mutex_unlock(&local_node->api_context->conns_lock);
|
||||
}
|
||||
api_connections_cleanup (local_node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ApiContext* api_context_new() {
|
||||
struct ApiContext* context = (struct ApiContext*) malloc(sizeof(struct ApiContext));
|
||||
if (context != NULL) {
|
||||
context->conns = NULL;
|
||||
context->conns_count = 0;
|
||||
context->ipv4 = 0;
|
||||
context->max_conns = 0;
|
||||
context->port = 0;
|
||||
context->socket = 0;
|
||||
context->timeout = 0;
|
||||
pthread_mutex_init(&context->conns_lock, NULL);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start API interface daemon.
|
||||
* @param local_node the context
|
||||
* @param max_conns.
|
||||
* @param timeout time out of client connection.
|
||||
* @returns 0 when failure or 1 if success.
|
||||
*/
|
||||
int api_start (struct IpfsNode* local_node, int max_conns, int timeout)
|
||||
{
|
||||
int s;
|
||||
size_t alloc_size = sizeof(void*) * max_conns;
|
||||
|
||||
struct MultiAddress* my_address = multiaddress_new_from_string(local_node->repo->config->addresses->api);
|
||||
|
||||
char* ip = NULL;
|
||||
multiaddress_get_ip_address(my_address, &ip);
|
||||
int port = multiaddress_get_ip_port(my_address);
|
||||
|
||||
local_node->api_context = api_context_new();
|
||||
if (local_node->api_context == NULL) {
|
||||
multiaddress_free(my_address);
|
||||
return 0;
|
||||
}
|
||||
|
||||
local_node->api_context->ipv4 = hostname_to_ip(ip); // api is listening only on loopback.
|
||||
if (ip != NULL)
|
||||
free(ip);
|
||||
local_node->api_context->port = port;
|
||||
|
||||
if ((s = socket_listen(socket_tcp4(), &(local_node->api_context->ipv4), &(local_node->api_context->port))) <= 0) {
|
||||
libp2p_logger_error("api", "Failed to init API. port: %d\n", port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
local_node->api_context->socket = s;
|
||||
local_node->api_context->max_conns = max_conns;
|
||||
local_node->api_context->timeout = timeout;
|
||||
|
||||
local_node->api_context->conns = malloc (alloc_size);
|
||||
if (!local_node->api_context->conns) {
|
||||
close (s);
|
||||
libp2p_logger_error("api", "Error allocating memory.\n");
|
||||
return 0;
|
||||
}
|
||||
memset(local_node->api_context->conns, 0, alloc_size);
|
||||
|
||||
if (pthread_create(&local_node->api_context->api_thread, NULL, api_listen_thread, (void*)local_node)) {
|
||||
close (s);
|
||||
free (local_node->api_context->conns);
|
||||
local_node->api_context->conns = NULL;
|
||||
local_node->api_context->api_thread = 0;
|
||||
libp2p_logger_error("api", "Error creating thread for API.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
libp2p_logger_info("api", "API server listening on %d.\n", port);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop API.
|
||||
* @returns 0 when failure or 1 if success.
|
||||
*/
|
||||
int api_stop (struct IpfsNode *local_node)
|
||||
{
|
||||
if (local_node->api_context->api_thread == 0) return 0;
|
||||
shutdown(local_node->api_context->socket, SHUT_RDWR);
|
||||
pthread_cancel(local_node->api_context->api_thread);
|
||||
|
||||
api_connections_cleanup (local_node);
|
||||
|
||||
local_node->api_context->api_thread = 0;
|
||||
|
||||
return 1;
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
#include <pthread.h>
|
||||
|
||||
#include "libp2p/peer/peer.h"
|
||||
#include "libp2p/utils/logger.h"
|
||||
#include "ipfs/routing/routing.h"
|
||||
#include "ipfs/core/ipfs_node.h"
|
||||
#include "ipfs/thirdparty/ipfsaddr/ipfs_addr.h"
|
||||
#include "multiaddr/multiaddr.h"
|
||||
|
||||
/***
|
||||
* Begin to connect to the swarm
|
||||
*/
|
||||
/*
|
||||
void *ipfs_bootstrap_swarm(void* param) {
|
||||
//TODO:
|
||||
struct IpfsNode* local_node = (struct IpfsNode*)param;
|
||||
// read the config file and get the bootstrap peers
|
||||
for(int i = 0; i < local_node->repo->config->peer_addresses.num_peers; i++) { // loop through the peers
|
||||
struct IPFSAddr* ipfs_addr = local_node->repo->config->peer_addresses.peers[i];
|
||||
struct MultiAddress* ma = multiaddress_new_from_string(ipfs_addr->entire_string);
|
||||
// get the id
|
||||
char* ptr;
|
||||
if ( (ptr = strstr(ipfs_addr->entire_string, "/ipfs/")) != NULL) { // look for the peer id
|
||||
ptr += 6;
|
||||
if (ptr[0] == 'Q' && ptr[1] == 'm') { // things look good
|
||||
struct Libp2pPeer* peer = libp2p_peer_new_from_data(ptr, strlen(ptr), ma);
|
||||
libp2p_peerstore_add_peer(local_node->peerstore, peer);
|
||||
}
|
||||
// TODO: attempt to connect to the peer
|
||||
|
||||
} // we have a good peer ID
|
||||
|
||||
}
|
||||
return (void*)1;
|
||||
}
|
||||
*/
|
||||
|
||||
/***
|
||||
* Announce to the network all of the files that I have in storage
|
||||
* @param local_node the context
|
||||
*/
|
||||
/*
|
||||
void ipfs_bootstrap_announce_files(struct IpfsNode* local_node) {
|
||||
struct Datastore* db = local_node->repo->config->datastore;
|
||||
if (!db->datastore_cursor_open(db))
|
||||
return;
|
||||
unsigned char* key = NULL;
|
||||
int key_size = 0;
|
||||
enum DatastoreCursorOp op = CURSOR_FIRST;
|
||||
while (db->datastore_cursor_get(&key, &key_size, NULL, 0, op, db)) {
|
||||
libp2p_logger_debug("bootstrap", "Announcing a file to the world.\n");
|
||||
local_node->routing->Provide(local_node->routing, key, key_size);
|
||||
op = CURSOR_NEXT;
|
||||
free(key);
|
||||
}
|
||||
|
||||
// close cursor
|
||||
db->datastore_cursor_close(db);
|
||||
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
/***
|
||||
* connect to the swarm
|
||||
* NOTE: This fills in the IpfsNode->routing struct
|
||||
*
|
||||
* @param param the IpfsNode information
|
||||
* @returns nothing useful
|
||||
*/
|
||||
/*
|
||||
void *ipfs_bootstrap_routing(void* param) {
|
||||
struct IpfsNode* local_node = (struct IpfsNode*)param;
|
||||
local_node->routing = ipfs_routing_new_online(local_node, &local_node->identity->private_key, NULL);
|
||||
local_node->routing->Bootstrap(local_node->routing);
|
||||
ipfs_bootstrap_announce_files(local_node);
|
||||
return (void*)2;
|
||||
}
|
||||
*/
|
|
@ -1,4 +1,11 @@
|
|||
#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"
|
||||
|
||||
int ipfs_core_builder_new_node(struct Context* context, struct BuildCfg* build_cfg, struct IpfsNode* buildConfig) {
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
#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;
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
#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);
|
||||
}
|
|
@ -1,628 +0,0 @@
|
|||
#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
191
core/ipfs_node.c
|
@ -1,191 +0,0 @@
|
|||
#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
42
core/net.c
|
@ -1,42 +0,0 @@
|
|||
|
||||
#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
238
core/null.c
|
@ -1,238 +0,0 @@
|
|||
#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
124
core/ping.c
|
@ -1,124 +0,0 @@
|
|||
#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
74
core/swarm.c
|
@ -1,74 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "libp2p/utils/logger.h"
|
||||
#include "ipfs/core/ipfs_node.h"
|
||||
#include "ipfs/core/swarm.h"
|
||||
#include "ipfs/core/http_request.h"
|
||||
|
||||
/***
|
||||
* Connect to a swarm
|
||||
* @param local_node the local node
|
||||
* @param address the address of the remote
|
||||
* @returns true(1) on success, false(0) otherwise
|
||||
*/
|
||||
int ipfs_swarm_connect(struct IpfsNode* local_node, const char* address) {
|
||||
char* response = NULL;
|
||||
size_t response_size;
|
||||
// use the API to connect
|
||||
struct HttpRequest* request = ipfs_core_http_request_new();
|
||||
if (request == NULL)
|
||||
return 0;
|
||||
request->command = "swarm";
|
||||
request->sub_command = "connect";
|
||||
libp2p_utils_vector_add(request->arguments, address);
|
||||
int retVal = ipfs_core_http_request_get(local_node, request, &response, &response_size);
|
||||
if (response != NULL && response_size > 0) {
|
||||
fwrite(response, 1, response_size, stdout);
|
||||
free(response);
|
||||
}
|
||||
ipfs_core_http_request_free(request);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/***
|
||||
* Handle command line swarm call
|
||||
*/
|
||||
int ipfs_swarm (struct CliArguments* args) {
|
||||
int retVal = 0;
|
||||
struct IpfsNode* client_node = NULL;
|
||||
|
||||
if (args->argc < (args->verb_index + 2)) {
|
||||
libp2p_logger_error("swarm", "Not enough command line arguments. Should be \"swarm connect\" or \"swarm disconnect\" etc.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// make sure API is running
|
||||
if (!ipfs_node_offline_new(args->config_dir, &client_node)) {
|
||||
libp2p_logger_error("swarm", "Unable to create offline node.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (client_node->mode != MODE_API_AVAILABLE) {
|
||||
libp2p_logger_error("swarm", "API must be running.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
const char* which = args->argv[args->verb_index + 1];
|
||||
const char* path = args->argv[args->verb_index + 2];
|
||||
// determine what we're doing
|
||||
if (strcmp(which, "connect") == 0) {
|
||||
retVal = ipfs_swarm_connect(client_node, path);
|
||||
} else if (strcmp(which, "disconnect") == 0) {
|
||||
libp2p_logger_error("swarm", "Swarm disconnect not implemented yet.\n");
|
||||
retVal = 0;
|
||||
} else {
|
||||
libp2p_logger_error("swarm", "Nothing useful found on command line. Should be \"swarm connect\" or \"swarm disconnect\".\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
// shut everything down
|
||||
ipfs_node_free(client_node);
|
||||
|
||||
return retVal;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
CC = gcc
|
||||
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
|
||||
CFLAGS = -O0 -I../include -I../../c-libp2p/include -I../../c-multihash/include -I../../c-multiaddr/include -I../../lmdb/libraries/liblmdb
|
||||
|
||||
ifdef DEBUG
|
||||
CFLAGS += -g3
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
/**
|
||||
* 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 "ipfs/datastore/ds_helper.h"
|
||||
/**
|
||||
* Generate a base32 key based on the passed in binary_array (which is normally a multihash)
|
||||
* Generate a key based on the passed in binary_array
|
||||
* @param binary_array what to base the key on
|
||||
* @param array_length the size of the binary array
|
||||
* @param results where the key will be put
|
||||
|
@ -14,8 +12,8 @@
|
|||
* @param results_length the length of the generated key
|
||||
* @returns true(1) on success
|
||||
*/
|
||||
int ipfs_datastore_helper_ds_key_from_binary(const unsigned char* binary_array, size_t array_length,
|
||||
unsigned char* results, size_t max_results_length, size_t* results_length) {
|
||||
int ipfs_datastore_helper_ds_key_from_binary(unsigned char* binary_array, size_t array_length,
|
||||
char* results, size_t max_results_length, size_t* results_length) {
|
||||
|
||||
size_t encoded_length = libp2p_crypto_encoding_base32_encode_size(array_length);
|
||||
if (encoded_length > max_results_length)
|
||||
|
@ -32,7 +30,7 @@ int ipfs_datastore_helper_ds_key_from_binary(const unsigned char* binary_array,
|
|||
}
|
||||
|
||||
/**
|
||||
* Generate a binary array (normally a multihash) based on the passed in datastore key
|
||||
* Generate a binary array based on the passed in datastore key
|
||||
* @param ds_key the base32 encoded key
|
||||
* @param key_length the length of the base32 "string"
|
||||
* @param binary_array where to put the decoded value
|
||||
|
@ -40,7 +38,7 @@ int ipfs_datastore_helper_ds_key_from_binary(const unsigned char* binary_array,
|
|||
* @param completed_binary_array_length the length of what was written to the binary_array
|
||||
* @returns true(1) on success
|
||||
*/
|
||||
int ipfs_datastore_helper_binary_from_ds_key(const unsigned char* ds_key, size_t key_length, unsigned char* binary_array,
|
||||
int ipfs_datastore_helper_binary_from_ds_key(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 decoded_length = libp2p_crypto_encoding_base32_decode_size(key_length);
|
||||
|
@ -55,41 +53,3 @@ int ipfs_datastore_helper_binary_from_ds_key(const unsigned char* ds_key, size_t
|
|||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
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
|
|
@ -1,407 +0,0 @@
|
|||
/*
|
||||
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;
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
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;
|
|
@ -1,18 +0,0 @@
|
|||
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
|
|
@ -1,249 +0,0 @@
|
|||
/**
|
||||
* 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;
|
||||
}
|
|
@ -1,198 +0,0 @@
|
|||
#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;
|
||||
}
|
|
@ -1,742 +0,0 @@
|
|||
#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;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,146 +0,0 @@
|
|||
/***
|
||||
* 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;
|
||||
}
|
|
@ -1,447 +0,0 @@
|
|||
/***
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
#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);
|
||||
}
|
|
@ -1,335 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include "libp2p/conn/session.h"
|
||||
#include "libp2p/utils/vector.h"
|
||||
#include "ipfs/exchange/bitswap/wantlist_queue.h"
|
||||
#include "ipfs/exchange/bitswap/peer_request_queue.h"
|
||||
|
||||
/**
|
||||
* Implementation of the WantlistQueue
|
||||
*/
|
||||
|
||||
/**
|
||||
* remove this session from the lists of sessions that are looking for this WantListQueueEntry
|
||||
* @param entry the entry
|
||||
* @param session who was looking for it
|
||||
* @returns true(1) on success, false(0) otherwise
|
||||
*/
|
||||
int ipfs_bitswap_wantlist_queue_entry_decrement(struct WantListQueueEntry* entry, const struct WantListSession* session) {
|
||||
for(size_t i = 0; i < entry->sessionsRequesting->total; i++) {
|
||||
const struct WantListSession* current = (const struct WantListSession*)libp2p_utils_vector_get(entry->sessionsRequesting, i);
|
||||
if (ipfs_bitswap_wantlist_session_compare(session, current) == 0) {
|
||||
libp2p_utils_vector_delete(entry->sessionsRequesting, i);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
* Initialize a new Wantlist (there should only be 1 per instance)
|
||||
* @returns a new WantList
|
||||
*/
|
||||
struct WantListQueue* ipfs_bitswap_wantlist_queue_new() {
|
||||
struct WantListQueue* wantlist = (struct WantListQueue*) malloc(sizeof(struct WantListQueue));
|
||||
if (wantlist != NULL) {
|
||||
pthread_mutex_init(&wantlist->wantlist_mutex, NULL);
|
||||
wantlist->queue = NULL;
|
||||
}
|
||||
return wantlist;
|
||||
}
|
||||
|
||||
/***
|
||||
* Deallocate resources of a WantList
|
||||
* @param wantlist the WantList
|
||||
* @returns true(1)
|
||||
*/
|
||||
int ipfs_bitswap_wantlist_queue_free(struct WantListQueue* wantlist) {
|
||||
if (wantlist != NULL) {
|
||||
if (wantlist->queue != NULL) {
|
||||
for(int i = 0; i < wantlist->queue->total; i++) {
|
||||
struct WantListQueueEntry* entry = (struct WantListQueueEntry*)libp2p_utils_vector_get(wantlist->queue, i);
|
||||
ipfs_bitswap_wantlist_queue_entry_free(entry);
|
||||
}
|
||||
libp2p_utils_vector_free(wantlist->queue);
|
||||
wantlist->queue = NULL;
|
||||
}
|
||||
free(wantlist);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/***
|
||||
* Add a Cid to the WantList
|
||||
* @param wantlist the WantList to add to
|
||||
* @param cid the Cid to add
|
||||
* @returns the correct WantListEntry or NULL if error
|
||||
*/
|
||||
struct WantListQueueEntry* ipfs_bitswap_wantlist_queue_add(struct WantListQueue* wantlist, const struct Cid* cid, const struct WantListSession* session) {
|
||||
struct WantListQueueEntry* entry = NULL;
|
||||
if (wantlist != NULL) {
|
||||
pthread_mutex_lock(&wantlist->wantlist_mutex);
|
||||
if (wantlist->queue == NULL) {
|
||||
wantlist->queue = libp2p_utils_vector_new(1);
|
||||
}
|
||||
entry = ipfs_bitswap_wantlist_queue_find(wantlist, cid);
|
||||
if (entry == NULL) {
|
||||
// create a new one
|
||||
entry = ipfs_bitswap_wantlist_queue_entry_new();
|
||||
entry->cid = ipfs_cid_copy(cid);
|
||||
entry->priority = 1;
|
||||
libp2p_utils_vector_add(entry->sessionsRequesting, session);
|
||||
libp2p_utils_vector_add(wantlist->queue, entry);
|
||||
}
|
||||
libp2p_utils_vector_add(entry->sessionsRequesting, session);
|
||||
pthread_mutex_unlock(&wantlist->wantlist_mutex);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
/***
|
||||
* Remove (decrement the counter) a Cid from the WantList
|
||||
* @param wantlist the WantList
|
||||
* @param cid the Cid
|
||||
* @returns true(1) on success, otherwise false(0)
|
||||
*/
|
||||
int ipfs_bitswap_wantlist_queue_remove(struct WantListQueue* wantlist, const struct Cid* cid, const struct WantListSession* session) {
|
||||
//TODO: remove if counter is <= 0
|
||||
if (wantlist != NULL) {
|
||||
struct WantListQueueEntry* entry = ipfs_bitswap_wantlist_queue_find(wantlist, cid);
|
||||
if (entry != NULL) {
|
||||
ipfs_bitswap_wantlist_queue_entry_decrement(entry, session);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
* Find a Cid in the WantList
|
||||
* @param wantlist the list
|
||||
* @param cid the Cid
|
||||
* @returns the WantListQueueEntry
|
||||
*/
|
||||
struct WantListQueueEntry* ipfs_bitswap_wantlist_queue_find(struct WantListQueue* wantlist, const struct Cid* cid) {
|
||||
for (size_t i = 0; i < wantlist->queue->total; i++) {
|
||||
struct WantListQueueEntry* entry = (struct WantListQueueEntry*) libp2p_utils_vector_get(wantlist->queue, i);
|
||||
if (entry == NULL) {
|
||||
//TODO: something went wrong. This should be logged.
|
||||
return NULL;
|
||||
}
|
||||
if (ipfs_cid_compare(cid, entry->cid) == 0) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/***
|
||||
* Pops the top one off the queue
|
||||
*
|
||||
* @param wantlist the list
|
||||
* @returns the WantListQueueEntry
|
||||
*/
|
||||
struct WantListQueueEntry* ipfs_bitswap_wantlist_queue_pop(struct WantListQueue* wantlist) {
|
||||
struct WantListQueueEntry* entry = NULL;
|
||||
|
||||
if (wantlist == NULL || wantlist->queue == NULL || wantlist->queue->total == 0)
|
||||
return entry;
|
||||
|
||||
//TODO: This should be a linked list, not an array
|
||||
pthread_mutex_lock(&wantlist->wantlist_mutex);
|
||||
for(int i = 0; i < wantlist->queue->total; i++) {
|
||||
struct WantListQueueEntry* current = (struct WantListQueueEntry*)libp2p_utils_vector_get(wantlist->queue, i);
|
||||
if (current->block == NULL && !current->asked_network) {
|
||||
entry = current;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//libp2p_utils_vector_delete(wantlist->queue, 0);
|
||||
pthread_mutex_unlock(&wantlist->wantlist_mutex);
|
||||
return entry;
|
||||
}
|
||||
|
||||
/***
|
||||
* Initialize a WantListQueueEntry
|
||||
* @returns a new WantListQueueEntry
|
||||
*/
|
||||
struct WantListQueueEntry* ipfs_bitswap_wantlist_queue_entry_new() {
|
||||
struct WantListQueueEntry* entry = (struct WantListQueueEntry*) malloc(sizeof(struct WantListQueueEntry));
|
||||
if (entry != NULL) {
|
||||
entry->sessionsRequesting = libp2p_utils_vector_new(1);
|
||||
if (entry->sessionsRequesting == NULL) {
|
||||
free(entry);
|
||||
return NULL;
|
||||
}
|
||||
entry->block = NULL;
|
||||
entry->cid = NULL;
|
||||
entry->priority = 0;
|
||||
entry->attempts = 0;
|
||||
entry->asked_network = 0;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
/***
|
||||
* Free a WantListQueueENtry struct
|
||||
* @param entry the struct
|
||||
* @returns true(1)
|
||||
*/
|
||||
int ipfs_bitswap_wantlist_queue_entry_free(struct WantListQueueEntry* entry) {
|
||||
if (entry != NULL) {
|
||||
if (entry->block != NULL) {
|
||||
ipfs_block_free(entry->block);
|
||||
entry->block = NULL;
|
||||
}
|
||||
if (entry->cid != NULL) {
|
||||
ipfs_cid_free(entry->cid);
|
||||
entry->cid = NULL;
|
||||
}
|
||||
if (entry->sessionsRequesting != NULL) {
|
||||
libp2p_utils_vector_free(entry->sessionsRequesting);
|
||||
entry->sessionsRequesting = NULL;
|
||||
}
|
||||
free(entry);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ipfs_bitswap_wantlist_session_compare(const struct WantListSession* a, const struct WantListSession* b) {
|
||||
if (a == NULL && b == NULL)
|
||||
return 0;
|
||||
if (a == NULL && b != NULL)
|
||||
return -1;
|
||||
if (a != NULL && b == NULL)
|
||||
return 1;
|
||||
if (a->type != b->type)
|
||||
return b->type - a->type;
|
||||
if (a->type == WANTLIST_SESSION_TYPE_LOCAL) {
|
||||
// it's local, there should be only 1
|
||||
return 0;
|
||||
} else {
|
||||
struct Libp2pPeer* contextA = (struct Libp2pPeer*)a->context;
|
||||
struct Libp2pPeer* contextB = (struct Libp2pPeer*)b->context;
|
||||
return libp2p_peer_compare(contextA, contextB);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new WantListSession
|
||||
* @returns the newly allocated WantListSession
|
||||
*/
|
||||
struct WantListSession* ipfs_bitswap_wantlist_session_new() {
|
||||
struct WantListSession* ret = (struct WantListSession*) malloc(sizeof(struct WantListSession));
|
||||
if (ret != NULL) {
|
||||
ret->context = NULL;
|
||||
ret->type = WANTLIST_SESSION_TYPE_LOCAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* determine if any of the sessions are referring to the local node
|
||||
* @param sessions a vector of WantlistSession
|
||||
* @returns true(1) if any of the sessions are local, false otherwise
|
||||
*/
|
||||
int ipfs_bitswap_wantlist_local_request(struct Libp2pVector* sessions)
|
||||
{
|
||||
for(int i = 0; i < sessions->total; i++) {
|
||||
struct WantListSession* curr = (struct WantListSession*) libp2p_utils_vector_get(sessions, i);
|
||||
if (curr != NULL && curr->type == WANTLIST_SESSION_TYPE_LOCAL)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
* Attempt to retrieve a block from the local blockstore
|
||||
*
|
||||
* @param context the BitswapContext
|
||||
* @param cid the id to look for
|
||||
* @param block where to put the results
|
||||
* @returns true(1) if found, false(0) otherwise
|
||||
*/
|
||||
int ipfs_bitswap_wantlist_get_block_locally(struct BitswapContext* context, struct Cid* cid, struct Block** block) {
|
||||
return context->ipfsNode->blockstore->Get(context->ipfsNode->blockstore->blockstoreContext, cid, block);
|
||||
}
|
||||
|
||||
/***
|
||||
* Retrieve a block. The only information we have is the cid
|
||||
*
|
||||
* This will ask the network for who has the file, using the router.
|
||||
* It will then ask the specific nodes for the file. This method
|
||||
* does not queue anything. It actually does the work. The remotes
|
||||
* will queue the file, but we'll return before they respond.
|
||||
*
|
||||
* @param context the BitswapContext
|
||||
* @param cid the id of the file
|
||||
* @returns true(1) if we found some providers to ask, false(0) otherwise
|
||||
*/
|
||||
int ipfs_bitswap_wantlist_get_block_remote(struct BitswapContext* context, struct Cid* cid) {
|
||||
// find out who may have the file
|
||||
struct Libp2pVector* providers = NULL;
|
||||
if (context->ipfsNode->routing->FindProviders(context->ipfsNode->routing, cid->hash, cid->hash_length, &providers)) {
|
||||
for(int i = 0; i < providers->total; i++) {
|
||||
struct Libp2pPeer* current = (struct Libp2pPeer*) libp2p_utils_vector_get(providers, i);
|
||||
// add this to their queue
|
||||
struct PeerRequest* queueEntry = ipfs_peer_request_queue_find_peer(context->peerRequestQueue, current);
|
||||
struct CidEntry* entry = ipfs_bitswap_peer_request_cid_entry_new();
|
||||
entry->cid = ipfs_cid_copy(cid);
|
||||
libp2p_utils_vector_add(queueEntry->cids_we_want, entry);
|
||||
// process this queue via bitswap protocol
|
||||
ipfs_bitswap_peer_request_process_entry(context, queueEntry);
|
||||
//libp2p_peer_free(current);
|
||||
}
|
||||
libp2p_utils_vector_free(providers);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the Bitswap engine, this processes an item on the WantListQueue. This is called when
|
||||
* we want a file locally from a remote source. Send a message immediately, adding in stuff that
|
||||
* perhaps the remote source wanted.
|
||||
*
|
||||
* @param context the context
|
||||
* @param entry the WantListQueueEntry
|
||||
* @returns true(1) on success, false(0) if not.
|
||||
*/
|
||||
int ipfs_bitswap_wantlist_process_entry(struct BitswapContext* context, struct WantListQueueEntry* entry) {
|
||||
int local_request = ipfs_bitswap_wantlist_local_request(entry->sessionsRequesting);
|
||||
int have_local = ipfs_bitswap_wantlist_get_block_locally(context, entry->cid, &entry->block);
|
||||
// should we go get it?
|
||||
if (!local_request && !have_local) {
|
||||
return 0;
|
||||
}
|
||||
if (local_request && !have_local) {
|
||||
if (!ipfs_bitswap_wantlist_get_block_remote(context, entry->cid)) {
|
||||
// if we were unsuccessful in retrieving it, put it back in the queue?
|
||||
// I don't think so. But I'm keeping this counter here until we have
|
||||
// a final decision. Maybe lower the priority?
|
||||
entry->attempts++;
|
||||
return 0;
|
||||
} else {
|
||||
entry->asked_network = 1;
|
||||
}
|
||||
}
|
||||
if (entry->block != NULL) {
|
||||
// okay we have the block.
|
||||
// fulfill the requests
|
||||
for(size_t i = 0; i < entry->sessionsRequesting->total; i++) {
|
||||
// TODO: Review this code.
|
||||
struct WantListSession* session = (struct WantListSession*) libp2p_utils_vector_get(entry->sessionsRequesting, i);
|
||||
if (session->type == WANTLIST_SESSION_TYPE_LOCAL) {
|
||||
//context->ipfsNode->exchange->HasBlock(context->ipfsNode->exchange, entry->block);
|
||||
} else {
|
||||
struct Libp2pPeer* peer = (struct Libp2pPeer*) session->context;
|
||||
ipfs_bitswap_peer_request_queue_fill(context->peerRequestQueue, peer, entry->block);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
CC = gcc
|
||||
|
||||
CFLAGS = -O0 -I../include -I../c-libp2p/include -Wall -std=gnu99
|
||||
CFLAGS = -O0 -I../include -I../../c-libp2p/include
|
||||
|
||||
ifdef DEBUG
|
||||
CFLAGS += -g3
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "libp2p/os/utils.h"
|
||||
#include "ipfs/os/utils.h"
|
||||
|
||||
#define FLATFS_MAX_PREFIX_LENGTH 16
|
||||
|
||||
|
@ -50,13 +50,8 @@ int ipfs_flatfs_create_directory(const char* full_directory) {
|
|||
return 0;
|
||||
}
|
||||
// it is not there, create it
|
||||
#ifdef __MINGW32__
|
||||
if (mkdir(full_directory) == -1)
|
||||
return 0;
|
||||
#else
|
||||
if (mkdir(full_directory, S_IRWXU) == -1)
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
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
|
|
@ -1,340 +0,0 @@
|
|||
#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;
|
||||
|
||||
}
|
|
@ -1,473 +0,0 @@
|
|||
// 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;
|
||||
}
|
||||
|
|
@ -1,321 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ipfs/importer/resolver.h"
|
||||
#include "libp2p/utils/logger.h"
|
||||
#include "libp2p/crypto/encoding/base58.h"
|
||||
#include "libp2p/conn/session.h"
|
||||
#include "libp2p/routing/dht_protocol.h"
|
||||
#include "ipfs/merkledag/node.h"
|
||||
#include "ipfs/merkledag/merkledag.h"
|
||||
#include "ipfs/repo/fsrepo/fs_repo.h"
|
||||
#include "libp2p/net/multistream.h"
|
||||
#include "libp2p/record/message.h"
|
||||
#include "multiaddr/multiaddr.h"
|
||||
#include "libp2p/record/message.h"
|
||||
#include "libp2p/conn/dialer.h"
|
||||
|
||||
/**
|
||||
* return the next chunk of a path
|
||||
* @param path the path
|
||||
* @param next_part a pointer to a string NOTE: don't forget to free
|
||||
* @returns true(1) on success, false(0) on error, or no more parts
|
||||
*/
|
||||
int ipfs_resolver_next_path(const char* path, char** next_part) {
|
||||
for (int i = 0; i < strlen(path); i++) {
|
||||
if (path[i] != '/') { // we have the next section
|
||||
char* pos = strchr(&path[i+1], '/');
|
||||
if (pos == NULL) {
|
||||
*next_part = (char*)malloc(strlen(path) + 1);
|
||||
if ( *next_part == NULL) {
|
||||
// memory issue
|
||||
return 0;
|
||||
}
|
||||
strcpy(*next_part, path);
|
||||
} else {
|
||||
*next_part = (char*)malloc(pos - &path[i] + 1);
|
||||
if (*next_part == NULL) {
|
||||
return 0;
|
||||
}
|
||||
strncpy(*next_part, &path[i], pos-&path[i]);
|
||||
(*next_part)[pos-&path[i]] = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove preceding slash and "/ipfs/" or "/ipns/" as well as the local multihash (if it is local)
|
||||
* @param path the path from the command line
|
||||
* @param fs_repo the local repo
|
||||
* @returns the modified path
|
||||
*/
|
||||
const char* ipfs_resolver_remove_path_prefix(const char* path, const struct FSRepo* fs_repo) {
|
||||
int pos = 0;
|
||||
int first_non_slash = -1;
|
||||
while(&path[pos] != NULL) {
|
||||
if (path[pos] == '/') {
|
||||
pos++;
|
||||
continue;
|
||||
} else {
|
||||
if (first_non_slash == -1)
|
||||
first_non_slash = pos;
|
||||
if (pos == first_non_slash && (strncmp(&path[pos], "ipfs", 4) == 0 || strncmp(&path[pos], "ipns", 4) == 0) ) {
|
||||
// ipfs or ipns should be up front. Otherwise, it could be part of the path
|
||||
pos += 4;
|
||||
} else if (strncmp(&path[pos], fs_repo->config->identity->peer->id, fs_repo->config->identity->peer->id_size) == 0) {
|
||||
pos += fs_repo->config->identity->peer->id_size + 1; // the slash
|
||||
} else {
|
||||
return &path[pos];
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this path is a remote path
|
||||
* @param path the path to examine
|
||||
* @param fs_repo the local repo
|
||||
* @returns true(1) if this path is a remote path
|
||||
*/
|
||||
int ipfs_resolver_is_remote(const char* path, const struct FSRepo* fs_repo) {
|
||||
int pos = 0;
|
||||
|
||||
// skip the first slash
|
||||
while (&path[pos] != NULL && path[pos] == '/') {
|
||||
pos++;
|
||||
}
|
||||
if (&path[pos] == NULL)
|
||||
return 0;
|
||||
|
||||
// skip the ipfs prefix
|
||||
if (strncmp(&path[pos], "ipfs/", 5) == 0 || strncmp(&path[pos], "ipns/", 5) == 0) {
|
||||
pos += 5; //the word plus the slash
|
||||
} else
|
||||
return 0;
|
||||
|
||||
// if this is a Qm code, see if it is a local Qm code
|
||||
if (path[pos] == 'Q' && path[pos+1] == 'm') {
|
||||
if (strncmp(&path[pos], fs_repo->config->identity->peer->id, fs_repo->config->identity->peer->id_size) != 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a node from a remote source
|
||||
* @param path the path to retrieve
|
||||
* @param from where to start
|
||||
* @param fs_repo the local repo
|
||||
* @returns the node, or NULL if not found
|
||||
*/
|
||||
struct HashtableNode* ipfs_resolver_remote_get(const char* path, struct HashtableNode* from, const struct IpfsNode* ipfs_node) {
|
||||
// parse the path
|
||||
const char* temp = ipfs_resolver_remove_path_prefix(path, ipfs_node->repo);
|
||||
if (temp == NULL)
|
||||
return NULL;
|
||||
char* pos = strchr(temp, '/');
|
||||
if (pos == NULL || pos - temp > 254)
|
||||
return NULL;
|
||||
char id[255];
|
||||
size_t id_size = pos - temp;
|
||||
strncpy(id, temp, id_size);
|
||||
id[id_size] = 0;
|
||||
char* key = &pos[1];
|
||||
pos = strchr(key, '/');
|
||||
if (pos == NULL || pos - key > 254)
|
||||
return NULL;
|
||||
pos[0] = '\0';
|
||||
// get the multiaddress for this
|
||||
struct Libp2pPeer* peer = libp2p_peerstore_get_peer(ipfs_node->peerstore, (unsigned char*)id, id_size);
|
||||
if (peer == NULL) {
|
||||
//TODO: We don't have the peer address. Ask the swarm for the data related to the hash
|
||||
return NULL;
|
||||
}
|
||||
if (!libp2p_peer_connect(ipfs_node->dialer, peer, ipfs_node->peerstore, ipfs_node->repo->config->datastore, 10))
|
||||
return NULL;
|
||||
struct Stream* kademlia_stream = libp2p_conn_dialer_get_stream(ipfs_node->dialer, peer, "kademlia");
|
||||
if (kademlia_stream == NULL)
|
||||
return NULL;
|
||||
// build the request
|
||||
struct KademliaMessage* message = libp2p_message_new();
|
||||
message->message_type = MESSAGE_TYPE_GET_VALUE;
|
||||
message->key = key;
|
||||
message->key_size = strlen(key);
|
||||
size_t b58size = 100;
|
||||
uint8_t *b58key = (uint8_t*) malloc(b58size);
|
||||
if (b58key == NULL) {
|
||||
libp2p_crypto_encoding_base58_encode((unsigned char*)message->key, message->key_size, (unsigned char**) &b58key, &b58size);
|
||||
libp2p_logger_debug("resolver", "Attempting to use kademlia to get key %s.\n", b58key);
|
||||
free(b58key);
|
||||
}
|
||||
size_t message_protobuf_size = libp2p_message_protobuf_encode_size(message);
|
||||
unsigned char message_protobuf[message_protobuf_size];
|
||||
libp2p_message_protobuf_encode(message, message_protobuf, message_protobuf_size, &message_protobuf_size);
|
||||
libp2p_message_free(message);
|
||||
struct StreamMessage outgoing;
|
||||
outgoing.data = message_protobuf;
|
||||
outgoing.data_size = message_protobuf_size;
|
||||
kademlia_stream->write(kademlia_stream->stream_context, &outgoing);
|
||||
struct StreamMessage* response;
|
||||
// we should get back a protobuf'd record
|
||||
kademlia_stream->read(kademlia_stream->stream_context, &response, 5);
|
||||
if (response->data_size == 1)
|
||||
return NULL;
|
||||
// turn the protobuf into a Node
|
||||
struct HashtableNode* node;
|
||||
ipfs_hashtable_node_protobuf_decode(response->data, response->data_size, &node);
|
||||
libp2p_stream_message_free(response);
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interogate the path and the current node, looking
|
||||
* for the desired node.
|
||||
* @param path the current path
|
||||
* @param from the current node (or NULL if it is the first call)
|
||||
* @returns what we are looking for, or NULL if it wasn't found
|
||||
*/
|
||||
struct HashtableNode* ipfs_resolver_get(const char* path, struct HashtableNode* from, const struct IpfsNode* ipfs_node) {
|
||||
|
||||
struct FSRepo* fs_repo = ipfs_node->repo;
|
||||
|
||||
// shortcut for remote files
|
||||
if (from == NULL && ipfs_resolver_is_remote(path, fs_repo)) {
|
||||
return ipfs_resolver_remote_get(path, from, ipfs_node);
|
||||
}
|
||||
/**
|
||||
* Memory management notes:
|
||||
* If we find what we're looking for, we clean up "from" and return the object
|
||||
* If we don't find what we're looking for, but we can continue the search, we clean up "from"
|
||||
* If we don't find what we're looking for, and we cannot continue, we do not clean up "from"
|
||||
*/
|
||||
// remove unnecessary stuff
|
||||
if (from == NULL)
|
||||
path = ipfs_resolver_remove_path_prefix(path, fs_repo);
|
||||
// grab the portion of the path to work with
|
||||
char* path_section;
|
||||
if (ipfs_resolver_next_path(path, &path_section) == 0)
|
||||
return NULL;
|
||||
struct HashtableNode* current_node = NULL;
|
||||
if (from == NULL) {
|
||||
// this is the first time around. Grab the root node
|
||||
if (path_section[0] == 'Q' && path_section[1] == 'm') {
|
||||
// we have a hash. Convert to a real hash, and find the node
|
||||
size_t hash_length = libp2p_crypto_encoding_base58_decode_size(strlen(path_section));
|
||||
unsigned char hash[hash_length];
|
||||
unsigned char* ptr = &hash[0];
|
||||
if (libp2p_crypto_encoding_base58_decode((unsigned char*)path_section, strlen(path_section), &ptr, &hash_length) == 0) {
|
||||
free(path_section);
|
||||
return NULL;
|
||||
}
|
||||
if (ipfs_merkledag_get_by_multihash(hash, hash_length, ¤t_node, fs_repo) == 0) {
|
||||
free(path_section);
|
||||
return NULL;
|
||||
}
|
||||
// we have the root node, now see if we want this or something further down
|
||||
int pos = strlen(path_section);
|
||||
if (pos == strlen(path)) {
|
||||
free(path_section);
|
||||
return current_node;
|
||||
} else {
|
||||
// look on...
|
||||
free(path_section);
|
||||
struct HashtableNode* newNode = ipfs_resolver_get(&path[pos+1], current_node, ipfs_node); // the +1 is the slash
|
||||
return newNode;
|
||||
}
|
||||
} else {
|
||||
// we don't have a current node, and we don't have a hash. Something is wrong
|
||||
free(path_section);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
// we were passed a node. If it is a directory, see if what we're looking for is in it
|
||||
if (ipfs_hashtable_node_is_directory(from)) {
|
||||
struct NodeLink* curr_link = from->head_link;
|
||||
while (curr_link != NULL) {
|
||||
// if it matches the name, we found what we're looking for.
|
||||
// If so, load up the node by its hash
|
||||
if (strcmp(curr_link->name, path_section) == 0) {
|
||||
if (ipfs_merkledag_get(curr_link->hash, curr_link->hash_size, ¤t_node, fs_repo) == 0) {
|
||||
free(path_section);
|
||||
return NULL;
|
||||
}
|
||||
if (strlen(path_section) == strlen(path)) {
|
||||
// we are at the end of our search
|
||||
ipfs_hashtable_node_free(from);
|
||||
from = NULL;
|
||||
free(path_section);
|
||||
return current_node;
|
||||
} else {
|
||||
char* next_path_section;
|
||||
ipfs_resolver_next_path(&path[strlen(path_section)], &next_path_section);
|
||||
free(path_section);
|
||||
// if we're at the end of the path, return the node
|
||||
// continue looking for the next part of the path
|
||||
ipfs_hashtable_node_free(from);
|
||||
from = NULL;
|
||||
struct HashtableNode* newNode = ipfs_resolver_get(next_path_section, current_node, ipfs_node);
|
||||
return newNode;
|
||||
}
|
||||
}
|
||||
curr_link = curr_link->next;
|
||||
}
|
||||
} else {
|
||||
// we're asking for a file from an object that is not a directory. Bail.
|
||||
free(path_section);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
// it should never get here
|
||||
free(path_section);
|
||||
if (from != NULL)
|
||||
ipfs_hashtable_node_free(from);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interrogate the path, looking for the peer.
|
||||
* NOTE: only called locally. Not for remote callers
|
||||
* @param path the peer path to search for in the form like "/ipfs/QmKioji..."
|
||||
* @param ipfs_node the context
|
||||
* @returns a peer struct, or NULL if not found
|
||||
*/
|
||||
struct Libp2pPeer* ipfs_resolver_find_peer(const char* path, const struct IpfsNode* ipfs_node) {
|
||||
struct FSRepo* fs_repo = ipfs_node->repo;
|
||||
struct Libp2pLinkedList *addresses = NULL;
|
||||
struct Libp2pPeer* peer = NULL;
|
||||
|
||||
// shortcut for if this node is the node we're looking for
|
||||
if (!ipfs_resolver_is_remote(path, fs_repo)) {
|
||||
// turn the string list into a multiaddress list
|
||||
struct Libp2pLinkedList* current_list_string = fs_repo->config->addresses->swarm_head;
|
||||
struct Libp2pLinkedList* current_list_ma = addresses;
|
||||
while(current_list_string != NULL) {
|
||||
struct Libp2pLinkedList* item = libp2p_utils_linked_list_new();
|
||||
item->item = multiaddress_new_from_string(current_list_string->item);
|
||||
if (addresses == NULL) {
|
||||
addresses = item;
|
||||
} else {
|
||||
current_list_ma->next = item;
|
||||
}
|
||||
current_list_ma = item;
|
||||
current_list_string = current_list_string->next;
|
||||
}
|
||||
}
|
||||
|
||||
// ask the swarm for the peer
|
||||
const char* address_string = ipfs_resolver_remove_path_prefix(path, fs_repo);
|
||||
ipfs_node->routing->FindPeer(ipfs_node->routing, (const unsigned char*)address_string, strlen(address_string), &peer);
|
||||
|
||||
return peer;
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
/***
|
||||
* IPFS has the notion of storage blocks.
|
||||
* Raw data with a multihash key (the Cid)
|
||||
*/
|
||||
|
||||
#ifndef __IPFS_BLOCKS_BLOCK_H__
|
||||
|
@ -15,51 +14,19 @@ struct Block {
|
|||
};
|
||||
|
||||
/***
|
||||
* Create a new block
|
||||
* @returns a new allocated Block struct
|
||||
* Create a new block based on the incoming data.
|
||||
* @param data the data to base the block on
|
||||
* @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
|
||||
*/
|
||||
struct Block* ipfs_block_new();
|
||||
|
||||
int ipfs_blocks_block_add_data(const unsigned char* data, size_t data_size, struct Block* block);
|
||||
int ipfs_blocks_block_new(const unsigned char* data, size_t data_size, struct Block** block);
|
||||
|
||||
/***
|
||||
* Free resources used by the creation of a block
|
||||
* @param block the block to free
|
||||
* @returns true(1) on success
|
||||
*/
|
||||
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);
|
||||
int ipfs_blocks_block_free(struct Block* block);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -3,95 +3,36 @@
|
|||
*/
|
||||
|
||||
#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);
|
||||
#ifndef __IPFS_BLOCKS_BLOCKSTORE_H__
|
||||
|
||||
/**
|
||||
* Delete a block based on its Cid
|
||||
* @param context the context
|
||||
* @param cid the Cid to look for
|
||||
* @param returns true(1) on success
|
||||
*/
|
||||
int ipfs_blockstore_delete(const struct BlockstoreContext* context, struct Cid* cid);
|
||||
int ipfs_blockstore_delete(struct Cid* cid, struct FSRepo* fs_repo);
|
||||
|
||||
/***
|
||||
* Determine if the Cid can be found
|
||||
* @param context the context
|
||||
* @param cid the Cid to look for
|
||||
* @returns true(1) if found
|
||||
*/
|
||||
int ipfs_blockstore_has(const struct BlockstoreContext* context, struct Cid* cid);
|
||||
int ipfs_blockstore_has(struct Cid* cid, struct FSRepo* fs_repo);
|
||||
|
||||
/***
|
||||
* Find a block based on its Cid
|
||||
* @param context the context
|
||||
* @param cid the Cid to look for
|
||||
* @param block where to put the data to be returned
|
||||
* @returns true(1) on success
|
||||
*/
|
||||
int ipfs_blockstore_get(const struct BlockstoreContext* context, struct Cid* cid, struct Block** block);
|
||||
int ipfs_blockstore_get(struct Cid* cid, struct Block* block, struct FSRepo* fs_repo);
|
||||
|
||||
/***
|
||||
* Put a block in the blockstore
|
||||
* @param block the block to store
|
||||
* @returns true(1) on success
|
||||
*/
|
||||
int ipfs_blockstore_put(const struct BlockstoreContext* context, struct Block* block, size_t* bytes_written);
|
||||
int ipfs_blockstore_put(struct Block* block, struct FSRepo* fs_repo);
|
||||
|
||||
/***
|
||||
* 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
|
||||
|
|
|
@ -6,82 +6,36 @@
|
|||
#define __IPFS_CID_CID_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include "protobuf.h"
|
||||
|
||||
// these are multicodec packed content types. They should match
|
||||
// the codes described in the authoratative document:
|
||||
// https://github.com/multiformats/multicodec/blob/master/table.csv
|
||||
#define CID_RAW 0x55
|
||||
#define CID_DAG_PROTOBUF 0x70
|
||||
#define CID_DAG_CBOR 0x71
|
||||
#define CID_GIT_RAW 0x78
|
||||
#define CID_PROTOBUF 0x70
|
||||
#define CID_CBOR 0x71
|
||||
#define CID_RAW 0x72
|
||||
#define CID_JSON 0x73
|
||||
#define CID_ETHEREUM_BLOCK 0x90
|
||||
#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_ETHEREUM_TX 0x91
|
||||
#define CID_BITCOIN_BLOCK 0xb0
|
||||
#define CID_BITCOIN_TX 0xb1
|
||||
#define CID_ZCASH_BLOCK 0xc0
|
||||
#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 {
|
||||
int version; // CID version
|
||||
int codec; // codec used (i.e. CID_RAW, CID_PROTOBUF
|
||||
unsigned char* hash; // the multihash
|
||||
size_t hash_length; // the length of hash
|
||||
int version;
|
||||
char codec;
|
||||
unsigned char* hash; // a multihash
|
||||
size_t hash_length;
|
||||
};
|
||||
|
||||
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
|
||||
* @param version the version
|
||||
* @param hash the multihash
|
||||
* @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_DAG_PROTOBUF)
|
||||
* @returns the Cid, or NULL if there was a problem
|
||||
* @param codec the codec to be used (NOTE: For version 0, this should be CID_PROTOBUF)
|
||||
* @param cid where to put the results
|
||||
* @returns true(1) on success
|
||||
*/
|
||||
struct Cid* ipfs_cid_new(int version, const unsigned char* hash, size_t hash_length, const char codec);
|
||||
int ipfs_cid_new(int version, unsigned char* hash, size_t hash_length, const char codec, struct Cid** cid);
|
||||
|
||||
/***
|
||||
* Free the resources from a Cid
|
||||
|
@ -90,13 +44,6 @@ struct Cid* ipfs_cid_new(int version, const unsigned char* hash, size_t hash_len
|
|||
*/
|
||||
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
|
||||
* @param incoming the string
|
||||
|
@ -104,32 +51,7 @@ struct Cid* ipfs_cid_copy(const struct Cid* original);
|
|||
* @cid the Cid struct to fill
|
||||
* @return true(1) on success
|
||||
*/
|
||||
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);
|
||||
int ipfs_cid_decode_from_string(const unsigned char* incoming, size_t incoming_length, struct Cid** cid);
|
||||
|
||||
/***
|
||||
* Turn a multibase decoded string of bytes into a Cid struct
|
||||
|
@ -137,23 +59,6 @@ char* ipfs_cid_to_string(const struct Cid* cid, char **result);
|
|||
* @param incoming_size the size of the array
|
||||
* @param cid the Cid structure to fill
|
||||
*/
|
||||
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);
|
||||
int ipfs_cid_cast(unsigned char* incoming, size_t incoming_size, struct Cid* cid);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
/**
|
||||
* Helps parse the command line.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A structure to hold the command line arguments
|
||||
*/
|
||||
struct CliArguments {
|
||||
int argc;
|
||||
char** argv;
|
||||
int verb_index;
|
||||
char* config_dir;
|
||||
};
|
||||
|
||||
struct CliArguments* cli_arguments_new(int argc, char** argv);
|
||||
|
||||
void cli_arguments_free(struct CliArguments* args);
|
|
@ -20,12 +20,4 @@ int ipfs_cmd_ipfs_init_command_new(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
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* @param request the end result, something that can be passed on that actually does something
|
||||
* @returns 0 if something bad happens, otherwise 1
|
||||
*/
|
||||
int cli_parse(int argc, char** params, FILE* inStream, struct Command** cmd, struct Request* request);
|
||||
int cli_parse(char** params, FILE* inStream, struct Command* cmd, struct Request* request);
|
||||
|
||||
int cli_parse_opts(char** params, struct Command* cmd, char* path, char** stringVals);
|
||||
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
//
|
||||
// option.h
|
||||
// c-ipfs
|
||||
//
|
||||
// Created by John Jones on 10/26/16.
|
||||
// Copyright © 2016 JMJAtlanta. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __COMMANDS_COMMAND_OPTION_H__
|
||||
#define __COMMANDS_COMMAND_OPTION_H__
|
||||
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
//
|
||||
// context.h
|
||||
// c-ipfs
|
||||
//
|
||||
// Created by John Jones on 10/27/16.
|
||||
// Copyright © 2016 JMJAtlanta. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __COMMANDS_CONTEXT_H__
|
||||
#define __COMMANDS_CONTEXT_H__
|
||||
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
//
|
||||
// req_log.h
|
||||
// c-ipfs
|
||||
//
|
||||
// Created by John Jones on 10/27/16.
|
||||
// Copyright © 2016 JMJAtlanta. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __COMMANDS_REQ_LOG_H__
|
||||
#define __COMMANDS_REQ_LOG_H__
|
||||
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
//
|
||||
// request.h
|
||||
// c-ipfs
|
||||
//
|
||||
// Created by John Jones on 10/26/16.
|
||||
// Copyright © 2016 JMJAtlanta. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __COMMANDS_REQUEST_H__
|
||||
#define __COMMANDS_REQUEST_H__
|
||||
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
#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);
|
|
@ -1,14 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
/**
|
||||
* Fires up the connection to peers
|
||||
*/
|
||||
|
||||
/**
|
||||
* Connect to the peers in the config file
|
||||
* @param param a IpfsNode object
|
||||
* @returns nothing useful
|
||||
*/
|
||||
void *ipfs_bootstrap_swarm(void* param);
|
||||
|
||||
void *ipfs_bootstrap_routing(void* param);
|
|
@ -1,3 +1,11 @@
|
|||
//
|
||||
// builder.h
|
||||
// c-ipfs
|
||||
//
|
||||
// Created by John Jones on 10/27/16.
|
||||
// Copyright © 2016 JMJAtlanta. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __CORE_BUILDER_H__
|
||||
#define __CORE_BUILDER_H__
|
||||
|
||||
|
@ -5,7 +13,6 @@
|
|||
|
||||
#include "ipfs/commands/context.h"
|
||||
#include "ipfs/repo/config/config.h"
|
||||
#include "ipfs/core/ipfs_node.h"
|
||||
|
||||
struct BuildCfg {
|
||||
int online;
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
#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);
|
|
@ -1,34 +0,0 @@
|
|||
#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
|
|
@ -1,89 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "ipfs/core/ipfs_node.h"
|
||||
|
||||
/***
|
||||
* A name/value pair of http parameters
|
||||
*/
|
||||
struct HttpParam {
|
||||
char* name; // the name of the parameter
|
||||
char* value; // the value of the parameter
|
||||
};
|
||||
|
||||
/**
|
||||
* A struct to help with incoming http requests
|
||||
*/
|
||||
struct HttpRequest {
|
||||
char* command; // the command
|
||||
char* sub_command; // the sub command
|
||||
struct Libp2pVector* params; // a collection of HttpParam structs
|
||||
struct Libp2pVector* arguments; // a collection of chars that are arguments
|
||||
};
|
||||
|
||||
/***
|
||||
* A struct to hold the response to be sent via http
|
||||
*/
|
||||
struct HttpResponse {
|
||||
char* content_type; // a const char, not dynamically allocated
|
||||
uint8_t* bytes; // dynamically allocated
|
||||
size_t bytes_size;
|
||||
};
|
||||
|
||||
/***
|
||||
* Build a new HttpRequest
|
||||
* @returns the newly allocated HttpRequest struct
|
||||
*/
|
||||
struct HttpRequest* ipfs_core_http_request_new();
|
||||
|
||||
/***
|
||||
* Clean up resources of a HttpRequest struct
|
||||
* @param request the struct to destroy
|
||||
*/
|
||||
void ipfs_core_http_request_free(struct HttpRequest* request);
|
||||
|
||||
struct HttpResponse* ipfs_core_http_response_new();
|
||||
|
||||
void ipfs_core_http_response_free(struct HttpResponse* response);
|
||||
|
||||
/***
|
||||
* Build a new HttpParam
|
||||
* @returns a newly allocated HttpParam struct
|
||||
*/
|
||||
struct HttpParam* ipfs_core_http_param_new();
|
||||
|
||||
/***
|
||||
* Clean up resources allocated by a HttpParam struct
|
||||
* @param param the struct to destroy
|
||||
*/
|
||||
void ipfs_core_http_param_free(struct HttpParam* param);
|
||||
|
||||
/***
|
||||
* Process the parameters passed in from an http request
|
||||
* @param local_node the context
|
||||
* @param request the request
|
||||
* @param response the response
|
||||
* @returns true(1) on success, false(0) otherwise.
|
||||
*/
|
||||
int ipfs_core_http_request_process(struct IpfsNode* local_node, struct HttpRequest* request, struct HttpResponse** response);
|
||||
|
||||
/**
|
||||
* Do an HTTP Get to the local API
|
||||
* @param local_node the context
|
||||
* @param request the request
|
||||
* @param result the results
|
||||
* @param result_size the size of the results
|
||||
* @returns true(1) on success, false(0) on error
|
||||
*/
|
||||
int ipfs_core_http_request_get(struct IpfsNode* local_node, struct HttpRequest* request, char** result, size_t* result_size);
|
||||
|
||||
/**
|
||||
* Do an HTTP Post to the local API
|
||||
* @param local_node the context
|
||||
* @param request the request
|
||||
* @param result the results
|
||||
* @param result_size the size of the results
|
||||
* @param data the array with post data
|
||||
* @param data_size the data length
|
||||
* @returns true(1) on success, false(0) on error
|
||||
*/
|
||||
int ipfs_core_http_request_post(struct IpfsNode* local_node, struct HttpRequest* request, char** result, size_t* result_size, char *data, size_t data_size);
|
|
@ -1,70 +1,21 @@
|
|||
#pragma once
|
||||
//
|
||||
// ipfs_node.h
|
||||
// c-ipfs
|
||||
//
|
||||
// Created by John Jones on 10/27/16.
|
||||
// Copyright © 2016 JMJAtlanta. All rights reserved.
|
||||
//
|
||||
|
||||
#include <pthread.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 };
|
||||
#ifndef __CORE_IPFS_NODE_H__
|
||||
#define __CORE_IPFS_NODE_H__
|
||||
|
||||
struct IpfsNode {
|
||||
/***
|
||||
* 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 PeerId identity;
|
||||
//struct Repo repo;
|
||||
//struct Pinner pinning; // an interface
|
||||
//struct Mount** mounts;
|
||||
//struct PrivKey* private_key;
|
||||
// TODO: Add more here
|
||||
};
|
||||
|
||||
/***
|
||||
* 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);
|
||||
#endif /* ipfs_node_h */
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
#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);
|
|
@ -1,19 +0,0 @@
|
|||
#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);
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "ipfs/cmd/cli.h"
|
||||
|
||||
/***
|
||||
* Handle command line swarm call
|
||||
*/
|
||||
int ipfs_swarm (struct CliArguments* args);
|
|
@ -1,12 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
/**
|
||||
* 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 "ipfs/blocks/block.h"
|
||||
#include "libp2p/db/datastore.h"
|
||||
|
||||
/**
|
||||
* Generate a key based on the passed in binary_array
|
||||
|
@ -17,8 +15,8 @@
|
|||
* @param results_length the length of the generated key
|
||||
* @returns true(1) on success
|
||||
*/
|
||||
int ipfs_datastore_helper_ds_key_from_binary(const unsigned char* binary_array, size_t array_length,
|
||||
unsigned char* results, size_t max_results_length, size_t* results_length);
|
||||
int ipfs_datastore_helper_ds_key_from_binary(unsigned char* binary_array, size_t array_length,
|
||||
char* results, size_t max_results_length, size_t* results_length);
|
||||
|
||||
/**
|
||||
* Generate a binary array based on the passed in datastore key
|
||||
|
@ -29,13 +27,7 @@ int ipfs_datastore_helper_ds_key_from_binary(const unsigned char* binary_array,
|
|||
* @param completed_binary_array_length the length of what was written to the binary_array
|
||||
* @returns true(1) on success
|
||||
*/
|
||||
int ipfs_datastore_helper_binary_from_ds_key(const unsigned char* ds_key, size_t key_length, unsigned char* binary_array,
|
||||
int ipfs_datastore_helper_binary_from_ds_key(unsigned char* ds_key, size_t key_length, unsigned char* binary_array,
|
||||
size_t max_binary_array_length, size_t* completed_binary_array_length);
|
||||
|
||||
/***
|
||||
* 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);
|
||||
#endif
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
#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
|
|
@ -1,89 +0,0 @@
|
|||
#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);
|
|
@ -1,42 +0,0 @@
|
|||
#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);
|
|
@ -1,218 +0,0 @@
|
|||
#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);
|
|
@ -1,83 +0,0 @@
|
|||
/***
|
||||
* 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);
|
|
@ -1,145 +0,0 @@
|
|||
#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);
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
#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);
|
|
@ -1,115 +0,0 @@
|
|||
#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);
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
#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;
|
||||
};
|
|
@ -1,56 +0,0 @@
|
|||
#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);
|
|
@ -1,32 +0,0 @@
|
|||
#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_ */
|
|
@ -1,25 +0,0 @@
|
|||
#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);
|
|
@ -1,50 +0,0 @@
|
|||
#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);
|
|
@ -1,43 +0,0 @@
|
|||
#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);
|
|
@ -1,42 +0,0 @@
|
|||
#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);
|
|
@ -1,45 +0,0 @@
|
|||
/***
|
||||
* 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
|
|
@ -1,296 +0,0 @@
|
|||
/**
|
||||
* 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
|
|
@ -1,21 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "ipfs/cmd/cli.h"
|
||||
|
||||
/***
|
||||
* Publish IPNS name
|
||||
*/
|
||||
int ipfs_name_publish(struct IpfsNode* local_node, char* name, char **response, size_t *response_size);
|
||||
|
||||
/***
|
||||
* Resolve IPNS name
|
||||
*/
|
||||
int ipfs_name_resolve(struct IpfsNode* local_node, char* name, char **response, size_t *response_size);
|
||||
|
||||
/**
|
||||
* We received a cli command "ipfs name". Do the right thing.
|
||||
* @param argc number of arguments on the command line
|
||||
* @param argv actual command line arguments
|
||||
* @returns true(1) on success, false(0) otherwise
|
||||
*/
|
||||
int ipfs_name(struct CliArguments* args);
|
|
@ -3,7 +3,40 @@
|
|||
|
||||
#define DefaultDepthLimit 32
|
||||
|
||||
#include "ipfs/util/errs.h"
|
||||
#ifdef NAMESYS_C
|
||||
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 {
|
||||
char *protocol;
|
||||
|
@ -32,7 +65,7 @@
|
|||
|
||||
typedef struct s_mpns {
|
||||
resolvers *resolver;
|
||||
publishers *publisher;
|
||||
publishers *Publisher;
|
||||
} mpns;
|
||||
|
||||
typedef struct s_tlds {
|
||||
|
@ -40,30 +73,30 @@
|
|||
int condition;
|
||||
} tlds;
|
||||
|
||||
int ipfs_namesys_resolve(char **path, char *name);
|
||||
int ipfs_namesys_resolve_n(char **path, char *name, int depth);
|
||||
int ipfs_namesys_resolve_once (char **path, char *name);
|
||||
int ipfs_namesys_publish (char *proto, ciPrivKey name, char *value);
|
||||
int ipfs_namesys_publish_with_eol (char *proto, ciPrivKey name, char *value, time_t eol);
|
||||
int resolve (resolver *r, char **p, char *str, int depth, char **prefixes);
|
||||
int Resolve(char **path, char *name);
|
||||
int ResolveN(char **path, char *name, int depth);
|
||||
int resolveOnce (char **path, char *name);
|
||||
int Publish (char *proto, ciPrivKey name, char *value);
|
||||
int PublishWithEOL (char *proto, ciPrivKey name, char *value, time_t eol);
|
||||
|
||||
int ipfs_proquint_is_proquint(char *str);
|
||||
char *ipfs_proquint_encode(char *buf, int size);
|
||||
char *ipfs_proquint_decode(char *str);
|
||||
int ipfs_proquint_resolve_once (char **p, char *name);
|
||||
int ProquintIsProquint(char *str);
|
||||
char *ProquintEncode(char *buf, int size);
|
||||
char *ProquintDecode(char *str);
|
||||
int ProquintResolveOnce (char **p, char *name);
|
||||
|
||||
int ipfs_isdomain_match_string (char *d);
|
||||
int ipfs_isdomain_is_icann_tld(char *s);
|
||||
int ipfs_isdomain_is_extended_tld (char *s);
|
||||
int ipfs_isdomain_is_tld (char *s);
|
||||
int ipfs_isdomain_is_domain (char *s);
|
||||
int domainMatchString (char *d);
|
||||
int IsICANNTLD(char *s);
|
||||
int IsExtendedTLD (char *s);
|
||||
int IsTLD (char *s);
|
||||
int IsDomain (char *s);
|
||||
|
||||
typedef struct s_DNSResolver {
|
||||
// TODO
|
||||
int (*lookupTXT) (char ***, char *);
|
||||
} DNSResolver;
|
||||
|
||||
int ipfs_dns_resolver_resolve_once (char **path, char *name);
|
||||
int ipfs_dns_work_domain (int output, DNSResolver *r, char *name);
|
||||
int ipfs_dns_parse_entry (char **path, char *txt);
|
||||
int ipfs_dns_try_parse_dns_link(char **path, char *txt);
|
||||
int DNSResolverResolveOnce (DNSResolver *r, char **path, char *name);
|
||||
int workDomain (int output, DNSResolver *r, char *name);
|
||||
int parseEntry (char **Path, char *txt);
|
||||
int tryParseDnsLink (char **Path, char *txt);
|
||||
#endif //NAMESYS_H
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
#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);
|
|
@ -1,21 +0,0 @@
|
|||
#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);
|
|
@ -1,14 +0,0 @@
|
|||
#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);
|
|
@ -1,40 +0,0 @@
|
|||
#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
|
|
@ -43,15 +43,15 @@ int main(int argc, char** argv)
|
|||
////Nodes/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//N_Create_From_Link
|
||||
struct HashtableNode * Mynode;
|
||||
struct Node * Mynode;
|
||||
Mynode = N_Create_From_Link(mylink);
|
||||
mylink->name = "HAHA";//Testing for valid node creation
|
||||
printf("Node Link[0] Name: %s\nHash: %s\n",Mynode->head_link[0]->name, Mynode->head_link[0]->Lcid->hash);
|
||||
printf("Node Link[0] Name: %s\nHash: %s\n",Mynode->links[0]->name, Mynode->links[0]->Lcid->hash);
|
||||
|
||||
//N_Add_Link
|
||||
Mynode = N_Add_Link(&Mynode, mylink2, sizeof(mylink2));
|
||||
mylink2->name = "HAHA";//Testing for valid node creation
|
||||
printf("Node Link[1] Name: %s\nHash: %s\n",Mynode->head_link[1]->name,Mynode->head_link[1]->Lcid->hash);
|
||||
printf("Node Link[1] Name: %s\nHash: %s\n",Mynode->links[1]->name,Mynode->links[1]->Lcid->hash);
|
||||
|
||||
//Node_Get_Link
|
||||
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);
|
||||
|
||||
//Node Copy
|
||||
struct HashtableNode * Node2;
|
||||
struct Node * Node2;
|
||||
Node2 = Node_Copy(Mynode);
|
||||
printf("NODE COPY TEST: [0]: %s\n", Node2->head_link[0]->Lcid->hash);
|
||||
printf("NODE COPY TEST: [0]: %s\n", Node2->links[0]->Lcid->hash);
|
||||
Node_Delete(Node2);
|
||||
|
||||
//Node_Set_Data
|
405
include/ipfs/node/node.h
Normal file
405
include/ipfs/node/node.h
Normal file
|
@ -0,0 +1,405 @@
|
|||
/**
|
||||
* An implementation of an IPFS node
|
||||
* Copying the go-ipfs-node project
|
||||
*/
|
||||
#ifndef IPFS_NODE_H
|
||||
#define IPFS_NODE_H
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "inttypes.h"
|
||||
#include "ipfs/cid/cid.h"
|
||||
|
||||
int debug = 0; // Set to 1 to enable debug mode of this program
|
||||
|
||||
/*====================================================================================
|
||||
*
|
||||
* Structures
|
||||
*
|
||||
*===================================================================================*/
|
||||
|
||||
struct Link
|
||||
{
|
||||
char * name;
|
||||
size_t size;
|
||||
struct Cid * Lcid;
|
||||
};
|
||||
struct Node
|
||||
{
|
||||
unsigned char * data;
|
||||
unsigned char * encoded;
|
||||
struct Cid * cached;
|
||||
int link_ammount;
|
||||
struct Link * links[];
|
||||
};
|
||||
/*====================================================================================
|
||||
*
|
||||
* Functions
|
||||
*
|
||||
*===================================================================================*/
|
||||
|
||||
/*====================================================================================
|
||||
* Link Functions
|
||||
*===================================================================================*/
|
||||
/* Create_Link
|
||||
* @Param name: The name of the link (char *)
|
||||
* @Param size: Size of the link (size_t)
|
||||
* @Param ahash: An Qmhash
|
||||
*/
|
||||
struct Link * Create_Link(char * name, unsigned char * ahash)
|
||||
{
|
||||
struct Link * mylink;
|
||||
mylink = malloc(sizeof(struct Link));
|
||||
mylink->name = name;
|
||||
int ver = 0;
|
||||
size_t lenhash = strlen(ahash)-1;
|
||||
ipfs_cid_new(ver, ahash, lenhash*2, CID_PROTOBUF, &mylink->Lcid);
|
||||
mylink->size = sizeof(mylink) + mylink->Lcid->hash_length; //Unsure of this
|
||||
return mylink;
|
||||
}
|
||||
/* Free_Link
|
||||
* @param L: Free the link you have allocated.
|
||||
*/
|
||||
void Free_Link(struct Link * L)
|
||||
{
|
||||
ipfs_cid_free(L->Lcid);
|
||||
free(L);
|
||||
}
|
||||
/*====================================================================================
|
||||
* Node Functions
|
||||
*===================================================================================*/
|
||||
/*Create_Empty_Node
|
||||
* Creates an empty node, allocates the required memory
|
||||
* Returns a fresh new node with no data set in it.
|
||||
*/
|
||||
struct Node * Create_Empty_Node()
|
||||
{
|
||||
struct Node * N;
|
||||
N = (struct Node *)malloc(sizeof(struct Node));
|
||||
return N;
|
||||
}
|
||||
/*Node_Set_Data
|
||||
* Sets the data of a node
|
||||
* @param Node: The node which you want to set data in.
|
||||
* @param Data, the data you want to assign to the node
|
||||
* returns 1 on success 0 on failure
|
||||
*/
|
||||
int Node_Set_Data(struct Node * N, unsigned char * Data)
|
||||
{
|
||||
if(!N || !Data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
N->encoded = NULL;
|
||||
N->cached = NULL;
|
||||
N->data = Data;
|
||||
return 1;
|
||||
}
|
||||
/*Node_Get_Data
|
||||
* Gets data from a node
|
||||
* @param Node: = The node you want to get data from. (unsigned char *)
|
||||
* Returns data of node.
|
||||
*/
|
||||
unsigned char * Node_Get_Data(struct Node * N)
|
||||
{
|
||||
unsigned char * DATA;
|
||||
DATA = N->data;
|
||||
return DATA;
|
||||
}
|
||||
|
||||
/*Node_Copy: Returns a copy of the node you input
|
||||
* @param Node: The node you want to copy (struct CP_Node *)
|
||||
* Returns a copy of the node you wanted to copy.
|
||||
*/
|
||||
struct Node * Node_Copy(struct Node * CP_Node)
|
||||
{
|
||||
struct Node * CN;
|
||||
CN = (struct Node*) malloc(sizeof(struct Node) + sizeof(struct Link) * 2);
|
||||
if(CP_Node->link_ammount != 0)
|
||||
{
|
||||
for(int i=0; i<CP_Node->link_ammount; i++)
|
||||
{
|
||||
CN->links[i] = malloc(sizeof(struct Link));
|
||||
}
|
||||
}
|
||||
memcpy(CN, CP_Node, sizeof(struct Node));
|
||||
memcpy(CN->links[0],CP_Node->links[0], sizeof(struct Link));
|
||||
return CN;
|
||||
}
|
||||
/*Node_Delete
|
||||
* Once you are finished using a node, always delete it using this.
|
||||
* It will take care of the links inside it.
|
||||
* @param N: the node you want to free. (struct Node *)
|
||||
*/
|
||||
void Node_Delete(struct Node * N)
|
||||
{
|
||||
if(N->link_ammount > 0)
|
||||
{
|
||||
for(int i=0; i<N->link_ammount; i++)
|
||||
{
|
||||
free(N->links[i]);
|
||||
}
|
||||
}
|
||||
free(N);
|
||||
}
|
||||
/*Node_Get_Link
|
||||
* Returns a copy of the link with given name
|
||||
* @param Name: (char * name) searches for link with this name
|
||||
* Returns the link struct if it's found otherwise returns NULL
|
||||
*/
|
||||
struct Link * Node_Get_Link(struct Node * N, char * Name)
|
||||
{
|
||||
struct Link * L;
|
||||
for(int i=0;i<N->link_ammount;i++)
|
||||
{
|
||||
if(strcmp(N->links[i]->name,Name) == 0)
|
||||
{
|
||||
L = (struct Link *)malloc(sizeof(struct Link));
|
||||
memcpy(L,N->links[i],sizeof(struct Link));
|
||||
int ver = L->Lcid->version;
|
||||
char * ahash = L->Lcid->hash;
|
||||
size_t lenhash = L->Lcid->hash_length;
|
||||
ipfs_cid_new(ver, ahash, lenhash, CID_PROTOBUF, &L->Lcid);
|
||||
return L;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
/*Node_Remove_Link
|
||||
* Removes a link from node if found by name.
|
||||
* @param name: Name of link (char * name)
|
||||
* returns 1 on success, 0 on failure.
|
||||
*/
|
||||
int Node_Remove_Link(char * Name, struct Node * mynode)
|
||||
{
|
||||
for(int i=0; i<mynode->link_ammount; i++)
|
||||
{
|
||||
if(mynode->links[i]->name == Name)
|
||||
{
|
||||
for(int x=i;x<mynode->link_ammount && x+1 != mynode->link_ammount;i++)
|
||||
{
|
||||
memcpy(mynode->links[x],mynode->links[x+1],sizeof(struct Link));
|
||||
}
|
||||
free(mynode->links[mynode->link_ammount-1]);
|
||||
mynode->link_ammount--;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* N_Add_Link
|
||||
* Adds a link to your node
|
||||
* @param mynode: &yournode
|
||||
* @param mylink: the CID you want to create a node from
|
||||
* @param linksz: sizeof(your cid here)
|
||||
* Returns your node with the newly added link
|
||||
*/
|
||||
struct Node * N_Add_Link(struct Node ** mynode, struct Link * mylink, size_t linksz)
|
||||
{
|
||||
struct Node * Nl = *mynode;
|
||||
Nl->link_ammount++;
|
||||
size_t calculatesize = 0;
|
||||
if(Nl->link_ammount != 0)
|
||||
{
|
||||
for(int i=0; i<Nl->link_ammount-1;i++)
|
||||
{
|
||||
calculatesize = calculatesize + sizeof(Nl->links[i]);
|
||||
}
|
||||
calculatesize = calculatesize + linksz;
|
||||
Nl = (struct Node *) realloc(Nl, sizeof(struct Node) + calculatesize);
|
||||
}
|
||||
else
|
||||
{
|
||||
Nl = (struct Node *) malloc(sizeof(struct Node) + linksz);
|
||||
}
|
||||
Nl->links[Nl->link_ammount-1] = malloc(sizeof(struct Link));
|
||||
memcpy(Nl->links[Nl->link_ammount-1],mylink,sizeof(struct Link));
|
||||
return Nl;
|
||||
}
|
||||
|
||||
/*N_Create_From_Link
|
||||
* Create a node from a link
|
||||
* @param mylink: the link you want to create it from. (struct Cid *)
|
||||
* @param linksize: sizeof(the link in mylink) (size_T)
|
||||
* Returns a fresh new node with the link you specified. Has to be freed with Node_Free preferably.
|
||||
*/
|
||||
struct Node * N_Create_From_Link(struct Link * mylink)
|
||||
{
|
||||
struct Node * mynode;
|
||||
mynode = (struct Node *) malloc(sizeof(struct Node) + sizeof(struct Link));
|
||||
mynode->link_ammount = 0;
|
||||
mynode->link_ammount++;
|
||||
mynode->links[0] = malloc(sizeof(struct Link));
|
||||
memcpy(mynode->links[0], mylink, sizeof(struct Link));
|
||||
return mynode;
|
||||
}
|
||||
/*N_Create_From_Data
|
||||
* @param data: bytes buffer you want to create the node from
|
||||
* returns a node with the data you inputted.
|
||||
*/
|
||||
struct Node * N_Create_From_Data(unsigned char * data)
|
||||
{
|
||||
struct Node * mynode;
|
||||
mynode = (struct Node *) malloc(sizeof(struct Node));
|
||||
mynode->data = data;
|
||||
return mynode;
|
||||
}
|
||||
/*Node_Resolve_Max_Size
|
||||
* !!!This shouldn't concern you!
|
||||
*Gets the ammount of words that will be returned by Node_Resolve
|
||||
*@Param1: The string that will be processed (eg: char * sentence = "foo/bar/bin")
|
||||
*Returns either -1 if something went wrong or the ammount of words that would be processed.
|
||||
*/
|
||||
int Node_Resolve_Max_Size(char * input1)
|
||||
{
|
||||
if(!input1)
|
||||
{
|
||||
return -1; // Input is null, therefor nothing can be processed.
|
||||
}
|
||||
char input[strlen(input1)];
|
||||
bzero(input, strlen(input1));
|
||||
strcpy(input, input1);
|
||||
int num = 0;
|
||||
char * tr;
|
||||
char * end;
|
||||
tr=strtok_r(input,"/",&end);
|
||||
for(int i = 0;tr;i++)
|
||||
{
|
||||
tr=strtok_r(NULL,"/",&end);
|
||||
num++;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/*Node_Resolve Basically stores everything in a pointer array eg: char * bla[Max_Words_]
|
||||
* !!!This shouldn't concern you!!!
|
||||
*@param1: Pointer array(char * foo[x], X=Whatever ammount there is. should be used with the helper function Node_Resolve_Max_Size)
|
||||
*@param2: Sentence to gather words/paths from (Eg: char * meh = "foo/bar/bin")
|
||||
*@Returns 1 or 0, 0 if something went wrong, 1 if everything went smoothly.
|
||||
*/
|
||||
|
||||
int Node_Resolve(char ** result, char * input1)
|
||||
{
|
||||
if(!input1)
|
||||
{
|
||||
return 0; // Input is null, therefor nothing can be processed.
|
||||
}
|
||||
char input[strlen(input1)];
|
||||
bzero(input, strlen(input1));
|
||||
strcpy(input, input1);
|
||||
char * tr;
|
||||
char * end;
|
||||
tr=strtok_r(input,"/",&end);
|
||||
for(int i = 0;tr;i++)
|
||||
{
|
||||
if(debug == 1)
|
||||
{
|
||||
printf("TR: %s\n", tr);
|
||||
}
|
||||
result[i] = (char *) malloc(strlen(tr)+1);
|
||||
strcpy(result[i], tr);
|
||||
tr=strtok_r(NULL,"/",&end);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
/**************************************************************************************************************************************
|
||||
*|||||||||||||||||||||||||||||||||||||||| !!!! IMPORTANT !!! ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||*
|
||||
**************************************************************************************************************************************
|
||||
* Not sure where this is used, I'm making something to easen it up for all of you.
|
||||
* This in itself will get all the links for you in a link[] array inside Link_Proc
|
||||
* the memory allocation for storage will be noted in the ammount of links.
|
||||
* After being done with this, you have to free the array for which you will have a function specially made for you.
|
||||
*
|
||||
* What this does:
|
||||
* It searches for links using a path like /foo/bar/bin/, if links with those names are found in the node you specify, it stores them
|
||||
* in a custom struct, Link_Proc; which is what Node_Resolve_Link returns.
|
||||
* Notes:
|
||||
* Use it, free it, it's all already laid out for you.
|
||||
* There will also be a tutorial demonstrating it in the same folder here so everyone can understand this.
|
||||
*/
|
||||
struct Link_Proc
|
||||
{
|
||||
char * remaining_links; // Not your concern.
|
||||
int ammount; //This will store the ammount of links, so you know what to process.
|
||||
struct Link * links[]; // Link array
|
||||
};
|
||||
/*Node_Resolve_Links
|
||||
* Processes a path returning all links.
|
||||
* @param N: The node you want to get links from
|
||||
* @param path: The "foo/bar/bin" path
|
||||
*/
|
||||
struct Link_Proc * Node_Resolve_Links(struct Node * N, char * path)
|
||||
{
|
||||
if(!N || !path)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
int expected_link_ammount = Node_Resolve_Max_Size(path);
|
||||
struct Link_Proc * LProc = (struct Link_Proc *) malloc(sizeof(struct Link_Proc) + sizeof(struct Link) * expected_link_ammount);
|
||||
LProc->ammount = 0;
|
||||
char * linknames[expected_link_ammount];
|
||||
Node_Resolve(linknames, path);
|
||||
for(int i=0;i<expected_link_ammount; i++)
|
||||
{
|
||||
struct Link * proclink;
|
||||
proclink = Node_Get_Link(N, linknames[i]);
|
||||
if(proclink)
|
||||
{
|
||||
LProc->links[i] = (struct Link *)malloc(sizeof(struct Link));
|
||||
memcpy(LProc->links[i], proclink, sizeof(struct Link));
|
||||
LProc->ammount++;
|
||||
free(proclink);
|
||||
}
|
||||
}
|
||||
//Freeing pointer array
|
||||
for(int i=0;i<expected_link_ammount; i++)
|
||||
{
|
||||
free(linknames[i]);
|
||||
}
|
||||
return LProc;
|
||||
}
|
||||
/*Free_link_Proc
|
||||
* frees the Link_Proc struct you created.
|
||||
* @param1: Link_Proc struct (struct Link_Proc *)
|
||||
*/
|
||||
void Free_Link_Proc(struct Link_Proc * LPRC)
|
||||
{
|
||||
if(LPRC->ammount != 0)
|
||||
{
|
||||
for(int i=0;i<LPRC->ammount;i++)
|
||||
{
|
||||
Free_Link(LPRC->links[i]);
|
||||
}
|
||||
}
|
||||
free(LPRC);
|
||||
}
|
||||
|
||||
/*Node_Tree() Basically a unix-like ls
|
||||
*@Param1: Result char * foo[strlen(sentence)]
|
||||
*@Param2: char sentence[] = foo/bar/bin/whatever
|
||||
*Return: 0 if failure, 1 if success
|
||||
*/
|
||||
int Node_Tree(char * result, char * input1) //I don't know where you use this but here it is.
|
||||
{
|
||||
if(!input1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
char input[strlen(input1)];
|
||||
bzero(input, strlen(input1));
|
||||
strcpy(input, input1);
|
||||
for(int i=0; i<strlen(input); i++)
|
||||
{
|
||||
if(input[i] == '/')
|
||||
{
|
||||
input[i] = ' ';
|
||||
}
|
||||
}
|
||||
strcpy(result, input);
|
||||
if(debug == 1)
|
||||
{
|
||||
printf("Node_Tree Internal Result: %s\n",result);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue