diff --git a/.cproject b/.cproject index d05172c..a60437a 100644 --- a/.cproject +++ b/.cproject @@ -30,6 +30,7 @@ + diff --git a/blocks/Makefile b/blocks/Makefile index f830509..f64ebc1 100644 --- a/blocks/Makefile +++ b/blocks/Makefile @@ -1,5 +1,5 @@ CC = gcc -CFLAGS = -O0 -I../include -I../../c-libp2p/include -I../../c-multihash/include -I../../c-multiaddr/include -Wall +CFLAGS = -O0 -I../include -I../../c-libp2p/include -I../../c-multihash/include -I../../c-multiaddr/include -I../../c-protobuf -Wall ifdef DEBUG CFLAGS += -g3 diff --git a/cid/Makefile b/cid/Makefile index 08774d9..bb4c1ee 100644 --- a/cid/Makefile +++ b/cid/Makefile @@ -1,5 +1,5 @@ CC = gcc -CFLAGS = -O0 -I../include -I../../c-libp2p/include -I../../c-multihash/include -I../../c-multiaddr/include -Wall +CFLAGS = -O0 -I../include -I../../c-libp2p/include -I../../c-multihash/include -I../../c-multiaddr/include -I../../c-protobuf -Wall ifdef DEBUG CFLAGS += -g3 diff --git a/cid/cid.c b/cid/cid.c index 13a4ca1..3ee934f 100644 --- a/cid/cid.c +++ b/cid/cid.c @@ -10,8 +10,77 @@ #include "libp2p/crypto/encoding/base58.h" #include "ipfs/multibase/multibase.h" #include "mh/multihash.h" -#include "multiaddr/varint.h" +#include "varint.h" +enum WireType ipfs_cid_message_fields[] = { WIRETYPE_VARINT, WIRETYPE_VARINT, WIRETYPE_LENGTH_DELIMITED }; + + +size_t ipfs_cid_protobuf_encode_size(struct Cid* cid) { + if (cid != NULL) + return 11+12+cid->hash_length+11; + return 0; +} + +int ipfs_cid_protobuf_encode(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); + *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); + *bytes_written += bytes_used; + retVal = protobuf_encode_length_delimited(3, ipfs_cid_message_fields[2], cid->hash, cid->hash_length, &buffer[*bytes_written], buffer_length - (*bytes_written), &bytes_used); + *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, &hash, &hash_length, &bytes_read); + if (retVal == 0) + return 0; + pos += bytes_read; + break; + } + + } + + retVal = ipfs_cid_new(version, hash, hash_length, codec, output); + free(hash); + return retVal; +} /** * Create a new CID based on the given hash @@ -118,16 +187,16 @@ int ipfs_cid_cast(unsigned char* incoming, size_t incoming_size, struct Cid* cid // This is not a multihash. Perhaps it is using varints. Try to peel the information out of the bytes. // first the version - int pos = 0, retVal = 0; + int pos = 0; size_t num_bytes = 0; - num_bytes = uvarint_decode32(&incoming[pos], incoming_size - pos, &cid->version); - if (num_bytes < 0 || cid->version > 1 || cid->version < 0) + cid->version = varint_decode(&incoming[pos], incoming_size - pos, &num_bytes); + if (num_bytes == 0 || cid->version > 1 || cid->version < 0) return 0; pos = num_bytes; // now the codec uint32_t codec = 0; - num_bytes = uvarint_decode32(&incoming[pos], incoming_size - pos, &codec); - if (num_bytes < 0) + codec = varint_decode(&incoming[pos], incoming_size - pos, &num_bytes); + if (num_bytes == 0) return 0; cid->codec = codec; pos += num_bytes; diff --git a/cmd/ipfs/Makefile b/cmd/ipfs/Makefile index 9021275..24b9bd5 100644 --- a/cmd/ipfs/Makefile +++ b/cmd/ipfs/Makefile @@ -1,5 +1,5 @@ CC = gcc -CFLAGS = -O0 -I../../include -I../../../c-libp2p/include -Wall +CFLAGS = -O0 -I../../include -I../../../c-libp2p/include -I../../../c-protobuf -Wall ifdef DEBUG CFLAGS += -g3 diff --git a/core/Makefile b/core/Makefile index 943ec08..94a443b 100644 --- a/core/Makefile +++ b/core/Makefile @@ -1,5 +1,5 @@ CC = gcc -CFLAGS = -O0 -I../include -I../../c-libp2p/include -Wall +CFLAGS = -O0 -I../include -I../../c-libp2p/include -I../../c-protobuf -Wall LFLAGS = DEPS = builder.h ipfs_node.h OBJS = builder.o diff --git a/dnslink/dnslink.c b/dnslink/dnslink.c new file mode 100644 index 0000000..d80fe2c --- /dev/null +++ b/dnslink/dnslink.c @@ -0,0 +1,295 @@ +/* +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 +#include +#include +#include +#include +#include "ipfs/namesys/namesys.h" +#define IPFS_DNSLINK_C +#include "ipfs/dnslink/dnslink.h" +#include "ipfs/cid/cid.h" +#include "ipfs/path/path.h" + +// 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; +} + +// 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,C_IN,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 { + 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; +} + +// 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; + } + + 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); + 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= +// 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// +// /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; +} diff --git a/importer/importer.c b/importer/importer.c new file mode 100644 index 0000000..3c5b324 --- /dev/null +++ b/importer/importer.c @@ -0,0 +1,46 @@ +#include + +#include "ipfs/importer/importer.h" + +#define MAX_DATA_SIZE 262144 // 1024 * 256; + +/** + * 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 + */ +int ipfs_import_chunk(FILE* file, struct Node* node) { + unsigned char buffer[MAX_DATA_SIZE]; + size_t bytes_read = fread(buffer, MAX_DATA_SIZE, 1, file); + if (node->data_size == 0) { + Node_Set_Data(node, buffer, bytes_read); + } else { + // create a new node, and link to the parent + struct Node* new_node = N_Create_From_Data(buffer, bytes_read); + // persist + // put link in node + Node_Add_Link(node, Create_Link("", new_node->cached->hash)); + Node_Delete(new_node); + } + return bytes_read; +} + +/** + * Creates a node based on an incoming file + * @param file_name the file to import + * @param node the root node (could have links to others) + * @returns true(1) on success + */ +int ipfs_import_file(const char* fileName, struct Node** node) { + int retVal = 1; + + FILE* file = fopen(fileName, "rb"); + *node = (struct Node)malloc(sizeof(struct Node)); + + // add all nodes + while (ipfs_import_chunk(file, *node) == MAX_DATA_SIZE) {} + fclose(file); + + return 1; +} diff --git a/include/ipfs/cid/cid.h b/include/ipfs/cid/cid.h index e499c41..1ce766b 100644 --- a/include/ipfs/cid/cid.h +++ b/include/ipfs/cid/cid.h @@ -6,6 +6,7 @@ #define __IPFS_CID_CID_H #include +#include "protobuf.h" #define CID_PROTOBUF 0x70 #define CID_CBOR 0x71 @@ -25,6 +26,30 @@ struct Cid { size_t hash_length; }; +/*** + * 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(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(struct Cid* incoming); /** * Create a new CID based on the given hash diff --git a/include/ipfs/dnslink/dnslink.h b/include/ipfs/dnslink/dnslink.h new file mode 100644 index 0000000..f04cb7c --- /dev/null +++ b/include/ipfs/dnslink/dnslink.h @@ -0,0 +1,26 @@ +#ifndef DNSLINK_H + #define DNSLINK_H + + #include "ipfs/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_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 diff --git a/include/ipfs/errs.h b/include/ipfs/errs.h new file mode 100644 index 0000000..c300dac --- /dev/null +++ b/include/ipfs/errs.h @@ -0,0 +1,52 @@ +#ifndef IPFS_ERRS_H + #define IPFS_ERRS_H + + char *Err[] = { + 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", + // ErrBadPath is returned when a given path is incorrectly formatted + "invalid 'ipfs ref' path", + // Paths after a protocol must contain at least one component + "path must contain at least one component", + "TODO: ErrCidDecode", + NULL, + "no link named %s under %s", + "ErrInvalidParam", + // ErrResolveLimit is returned when a recursive resolution goes over + // the limit. + "resolve depth exceeded" + }; + + enum { + ErrAllocFailed = 1, + ErrNULLPointer, + ErrPipe, + ErrPoll, + ErrPublishFailed, + ErrResolveFailed, + ErrResolveRecursion, + ErrExpiredRecord, + ErrUnrecognizedValidity, + ErrInvalidProquint, + ErrInvalidDomain, + ErrInvalidDNSLink, + ErrBadPath, + ErrNoComponents, + ErrCidDecode, + ErrNoLink, + ErrNoLinkFmt, + ErrInvalidParam, + ErrResolveLimit + } ErrsIdx; +#endif // IPFS_ERRS_H diff --git a/include/ipfs/importer/importer.h b/include/ipfs/importer/importer.h new file mode 100644 index 0000000..00c977c --- /dev/null +++ b/include/ipfs/importer/importer.h @@ -0,0 +1,12 @@ +#ifndef __IPFS_IMPORTER_IMPORTER_H__ +#define __IPFS_IMPORTER_IMPORTER_H__ + +/** + * Creates a node based on an incoming file + * @param file_name the file to import + * @param node the root node (could have links to others) + * @returns true(1) on success + */ +int ipfs_import_file(const char* fileName, struct Node** node); + +#endif /* INCLUDE_IPFS_IMPORTER_IMPORTER_H_ */ diff --git a/include/ipfs/merkledag/merkledag.h b/include/ipfs/merkledag/merkledag.h index fb0c682..9817187 100644 --- a/include/ipfs/merkledag/merkledag.h +++ b/include/ipfs/merkledag/merkledag.h @@ -1,3 +1,6 @@ +/*** + * Merkledag methods + */ #ifndef __IPFS_MERKLEDAG_H__ #define __IPFS_MERKLEDAG_H__ diff --git a/include/ipfs/namesys/namesys.h b/include/ipfs/namesys/namesys.h index 9ff92dd..aaa70e5 100644 --- a/include/ipfs/namesys/namesys.h +++ b/include/ipfs/namesys/namesys.h @@ -3,40 +3,7 @@ #define DefaultDepthLimit 32 - #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; + #include "ipfs/errs.h" typedef struct s_resolvers { char *protocol; @@ -73,7 +40,6 @@ int condition; } tlds; - int ipfs_namesys_resolve (resolver *r, char **p, char *str, int depth, char **prefixes); 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); diff --git a/include/ipfs/node/Example for this.c b/include/ipfs/node/Example for this.c index 1cf7d4a..1ec8f98 100644 --- a/include/ipfs/node/Example for this.c +++ b/include/ipfs/node/Example for this.c @@ -46,12 +46,12 @@ int main(int argc, char** argv) 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->links[0]->name, Mynode->links[0]->Lcid->hash); + printf("Node Link[0] Name: %s\nHash: %s\n",Mynode->head_link[0]->name, Mynode->head_link[0]->Lcid->hash); //N_Add_Link 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->links[1]->name,Mynode->links[1]->Lcid->hash); + printf("Node Link[1] Name: %s\nHash: %s\n",Mynode->head_link[1]->name,Mynode->head_link[1]->Lcid->hash); //Node_Get_Link struct Link * ResultLink = Node_Get_Link(Mynode, "Simo"); @@ -74,7 +74,7 @@ int main(int argc, char** argv) //Node Copy struct Node * Node2; Node2 = Node_Copy(Mynode); - printf("NODE COPY TEST: [0]: %s\n", Node2->links[0]->Lcid->hash); + printf("NODE COPY TEST: [0]: %s\n", Node2->head_link[0]->Lcid->hash); Node_Delete(Node2); //Node_Set_Data diff --git a/include/ipfs/node/node.h b/include/ipfs/node/node.h index c3a82f1..56990fe 100644 --- a/include/ipfs/node/node.h +++ b/include/ipfs/node/node.h @@ -13,21 +13,20 @@ * *===================================================================================*/ -struct Link +struct NodeLink { - char * name; - size_t size; - struct Cid * Lcid; + char* name; + struct Cid * cid; + struct NodeLink* next; }; struct Node { - unsigned char * data; + unsigned char* data; size_t data_size; - unsigned char * encoded; - struct Cid * cached; - int link_ammount; - struct Link * links[]; + unsigned char* encoded; + struct Cid* cached; + struct NodeLink* head_link; }; /*==================================================================================== @@ -42,107 +41,141 @@ struct Node /* Create_Link * @Param name: The name of the link (char *) - * @Param size: Size of the link (size_t) * @Param ahash: An Qmhash + * @param node_link a pointer to the new struct NodeLink + * @returns true(1) on success */ -struct Link * Create_Link(char * name, unsigned char * ahash); +int ipfs_node_link_new(char * name, unsigned char * ahash, struct NodeLink** node_link); -/* Free_Link +/* ipfs_node_link_free * @param L: Free the link you have allocated. */ -void Free_Link(struct Link * L); +int ipfs_node_link_free(struct NodeLink * node_link); + +/*** + * Node protobuf functions + */ + +/*** + * 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_node_protobuf_encode_size(struct Node* 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 + */ +ipfs_node_protobuf_encode(struct Node* 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 + */ +ipfs_node_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct Node** node); /*==================================================================================== * Node Functions *===================================================================================*/ -/*Create_Empty_Node +/*ipfs_node_new * Creates an empty node, allocates the required memory * Returns a fresh new node with no data set in it. */ -struct Node * Create_Empty_Node(); +int ipfs_node_new(struct Node** node); -//Node_Set_Cached -int Node_Set_Cached(struct Node * N, struct Cid * TheCid); +/** + * 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_node_set_cached(struct Node* node, struct Cid* cid); -/*Node_Set_Data +/*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 Node_Set_Data(struct Node * N, unsigned char * Data); +int ipfs_node_set_data(struct Node * N, unsigned char * Data, size_t data_size); -/*Node_Set_Encoded +/*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 Node_Set_Encoded(struct Node * N, unsigned char * Data); +int ipfs_node_set_encoded(struct Node * N, unsigned char * Data); -/*Node_Get_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 * Node_Get_Data(struct Node * N); +unsigned char * ipfs_node_get_data(struct Node * N); -/*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); - -/*Node_Delete +/*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 *) */ -void Node_Delete(struct Node * N); +void ipfs_node_free(struct Node * N); -/*Node_Get_Link +/*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 Link * Node_Get_Link(struct Node * N, char * Name); +struct NodeLink * ipfs_node_get_link_by_name(struct Node * N, char * Name); -/*Node_Remove_Link +/*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 Node_Remove_Link(char * Name, struct Node * mynode); +int ipfs_node_remove_link_by_name(char * Name, struct Node * mynode); -/* N_Add_Link +/* 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 */ -struct Node * N_Add_Link(struct Node ** mynode, struct Link * mylink, size_t linksz); +int ipfs_node_add_link(struct Node * mynode, struct NodeLink * mylink); -/*N_Create_From_Link +/*ipfs_node_new_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. + * @param node the pointer to the new node + * @returns true(1) on success */ -struct Node * N_Create_From_Link(struct Link * mylink) ; +int ipfs_node_new_from_link(struct NodeLink * mylink, struct Node** node); -/*N_Create_From_Data +/*ipfs_node_new_from_data * @param data: bytes buffer you want to create the node from - * returns a node with the data you inputted. + * @param data_size the size of the data + * @param node the pointer to the new node + * @returns true(1) on success */ -struct Node * N_Create_From_Data(unsigned char * data, size_t data_size); +int ipfs_node_new_from_data(unsigned char * data, size_t data_size, struct Node** node); -/*N_Create_From_Encoded - * @param data: encoded bytes buffer you want to create the node from - * returns a node with the encoded data you inputted. +/*** + * 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 */ -struct Node * N_Create_From_Encoded(unsigned char * data); +int ipfs_node_new_from_encoded(unsigned char * data, struct Node** node); /*Node_Resolve_Max_Size * !!!This shouldn't concern you! @@ -179,7 +212,7 @@ 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 + struct NodeLink * links[]; // Link array }; /*Node_Resolve_Links diff --git a/include/ipfs/path/path.h b/include/ipfs/path/path.h index 9ad58f4..e588c67 100644 --- a/include/ipfs/path/path.h +++ b/include/ipfs/path/path.h @@ -1,28 +1,7 @@ #ifndef IPFS_PATH_H #define IPFS_PATH_H - #ifdef IPFS_PATH_C - char *ErrPath[] = { - NULL, - // ErrBadPath is returned when a given path is incorrectly formatted - "invalid 'ipfs ref' path", - // Paths after a protocol must contain at least one component - "path must contain at least one component", - "TODO: ErrCidDecode", - NULL, - "no link named %s under %s" - }; - #else - extern char **ErrPath; - #endif // IPFS_PATH_C - - enum { - ErrBadPath = 1, - ErrNoComponents, - ErrCidDecode, - ErrNoLink, - ErrNoLinkFmt - } PathErrs; + #include "ipfs/errs.h" char* ipfs_path_from_cid (struct Cid *c); char** ipfs_path_split_n (char *p, char *delim, int n); diff --git a/merkledag/Makefile b/merkledag/Makefile index a5f0784..aadae27 100644 --- a/merkledag/Makefile +++ b/merkledag/Makefile @@ -1,5 +1,5 @@ CC = gcc -CFLAGS = -O0 -I../include -I../../c-libp2p/include -I../../c-multihash/include -I../../c-multiaddr/include -Wall +CFLAGS = -O0 -I../include -I../../c-libp2p/include -I../../c-multihash/include -I../../c-multiaddr/include -I../../c-protobuf -Wall ifdef DEBUG CFLAGS += -g3 diff --git a/merkledag/merkledag.c b/merkledag/merkledag.c index b118c65..e9a0bc4 100644 --- a/merkledag/merkledag.c +++ b/merkledag/merkledag.c @@ -13,9 +13,15 @@ int ipfs_merkledag_add(struct Node* node, struct FSRepo* fs_repo) { // taken from merkledag.go line 59 + // protobuf the node + size_t protobuf_len = ipfs_node_protobuf_encode_size(node); + size_t bytes_written = 0; + unsigned char protobuf[protobuf_len]; + ipfs_node_protobuf_encode(node, protobuf, protobuf_len, &bytes_written); + // turn the node into a block struct Block* block; - ipfs_blocks_block_new(node->data, node->data_size, &block); + ipfs_blocks_block_new(protobuf, bytes_written, &block); int retVal = fs_repo->config->datastore->datastore_put_block(block, fs_repo->config->datastore); if (retVal == 0) { @@ -23,7 +29,7 @@ int ipfs_merkledag_add(struct Node* node, struct FSRepo* fs_repo) { return 0; } - Node_Set_Cached(node, block->cid); + ipfs_node_set_cached(node, block->cid); ipfs_blocks_block_free(block); // TODO: call HasBlock (unsure why as yet) @@ -47,8 +53,8 @@ int ipfs_merkledag_get(const struct Cid* cid, struct Node** node, const struct F return 0; // we have the block. Fill the node - *node = N_Create_From_Data(block->data, block->data_length); - Node_Set_Cached(*node, cid); + ipfs_node_protobuf_decode(block->data, block->data_length, node); + ipfs_node_set_cached(*node, cid); ipfs_blocks_block_free(block); diff --git a/namesys/dns.c b/namesys/dns.c index 0991a8d..23d7e9a 100644 --- a/namesys/dns.c +++ b/namesys/dns.c @@ -51,7 +51,7 @@ type lookupRes struct { int ipfs_dns_resolver_resolve_once (char **path, char *name) { char **segments, *domain, *dnslink, buf[500], dlprefix[] = "_dnslink."; - int p1[2], p2[2], r, c=2; + int p1[2], p2[2], r, l, c=2; struct pollfd event[2], *e; segments = ipfs_path_split_n(name, "/", 2); @@ -84,12 +84,14 @@ int ipfs_dns_resolver_resolve_once (char **path, char *name) case 0: // child close(p2[STDIN_FILENO]); // we don't need to read at child process. - dnslink = malloc(strlen(domain) + sizeof(dlprefix)); + l = strlen(domain) + sizeof(dlprefix); + dnslink = malloc(l); if (!dnslink) { return ErrAllocFailed; } - strcpy (dnslink, dlprefix); - strcat (dnslink, domain); + dnslink[--l] = '\0'; + strncpy (dnslink, dlprefix, l); + strncat (dnslink, domain, l - strlen(dnslink)); return ipfs_dns_work_domain (p2[STDOUT_FILENO], r, dnslink); } @@ -114,7 +116,8 @@ int ipfs_dns_resolver_resolve_once (char **path, char *name) buf[r] = '\0'; *path = malloc(r+1); if (*path) { - strcpy(*path, buf); + *path[r] = '\0'; + strncpy(*path, buf, r); } } else if (r <= 0) { return ErrPoll; @@ -187,7 +190,7 @@ int ipfs_dns_parse_entry (char **path, char *txt) if (!*path) { return ErrAllocFailed; } - strcpy(*path, buf); + memcpy(*path, buf, strlen(buf) + 1); return 0; } return ipfs_dns_try_parse_dns_link(path, txt); @@ -205,7 +208,7 @@ int ipfs_dns_try_parse_dns_link(char **path, char *txt) if (! *parts) { return ErrAllocFailed; } - strcpy(*parts, buf); + memcpy(*parts, buf, strlen(buf) + 1); return 0; } return err; diff --git a/namesys/isdomain.c b/namesys/isdomain.c index ec9d2ca..5317070 100644 --- a/namesys/isdomain.c +++ b/namesys/isdomain.c @@ -90,10 +90,14 @@ int ipfs_isdomain_is_tld (char *s) // It first checks the TLD, and then uses a regular expression. int ipfs_isdomain_is_domain (char *s) { - char str[strlen(s)]; - char *tld; + int err; + char *str, *tld; - strcpy(str, s); + str = malloc(strlen(s) + 1); + if (!str) { + return ErrAllocFailed; + } + memcpy(str, s, strlen(s) + 1); s = str; // work with local copy. if (ipfs_isdomain_has_suffix (s, ".")) { @@ -112,5 +116,7 @@ int ipfs_isdomain_is_domain (char *s) return 0; } - return ipfs_isdomain_match_string(s); + err = ipfs_isdomain_match_string(s); + free (s); + return err; } diff --git a/namesys/namesys.c b/namesys/namesys.c index fd534a4..9b3abff 100644 --- a/namesys/namesys.c +++ b/namesys/namesys.c @@ -3,7 +3,6 @@ #include #include "ipfs/cid/cid.h" #include "ipfs/path/path.h" -#define NAMESYS_C #include "ipfs/namesys/namesys.h" /* mpns (a multi-protocol NameSystem) implements generic IPFS naming. @@ -55,7 +54,7 @@ int ipfs_namesys_resolve_n(char **path, char *name, int depth) ipfs_path_parse(p, name); *path = malloc(strlen(p) + 1); if (*p) { - strcpy(*path, p); + memcpy(*path, p, strlen(p) + 1); } else { err = ErrAllocFailed; } @@ -63,18 +62,22 @@ int ipfs_namesys_resolve_n(char **path, char *name, int depth) } if (*name == '/') { - int err; - char *str = malloc(sizeof(ipfs_prefix) + strlen(name)); + int err, l; + char *str; + + l = sizeof(ipfs_prefix) + strlen(name); + str = malloc(l); if (!str) { return ErrAllocFailed; } - strcpy(str, ipfs_prefix); - strcat(str, name+1); // ignore inital / from name, because ipfs_prefix already has it. + str[--l] = '\0'; + strncpy(str, ipfs_prefix, l); + strncat(str, name+1, l - strlen (str)); // ignore inital / from name, because ipfs_prefix already has it. err = ipfs_path_parse(p, str); // save return value. free (str); // so we can free allocated memory before return. *path = malloc(strlen(p) + 1); if (*p) { - strcpy(*path, p); + memcpy(*path, p, strlen(p) + 1); } else { err = ErrAllocFailed; } @@ -97,12 +100,14 @@ int ipfs_namesys_resolve_once (char **path, char *name) } if (memcmp (name, ipns_prefix, strlen(ipns_prefix)) == 0) { // prefix missing. - ptr = malloc(strlen(name) + sizeof(ipns_prefix)); + i = strlen(name) + sizeof(ipns_prefix); + ptr = malloc(i); if (!ptr) { // allocation fail. return ErrAllocFailed; } - strcpy(ptr, ipns_prefix); - strcat(ptr, name); + ptr[--i] = '\0'; + strncpy(ptr, ipns_prefix, i); + strncat(ptr, name, i - strlen(ptr)); segs = ipfs_path_split_segments(ptr); free (ptr); } else { diff --git a/namesys/proquint.c b/namesys/proquint.c index ded1995..561b4f0 100644 --- a/namesys/proquint.c +++ b/namesys/proquint.c @@ -176,7 +176,7 @@ int ipfs_proquint_resolve_once (char **p, char *name) if (!err) { *p = malloc (strlen(buf) + 1); if (p) { - strcpy(*p, buf); + memcpy(*p, buf, strlen(buf) + 1); } } } diff --git a/node/Makefile b/node/Makefile index 607fbec..40240eb 100644 --- a/node/Makefile +++ b/node/Makefile @@ -1,5 +1,5 @@ CC = gcc -CFLAGS = -O0 -I../include -I../../c-libp2p/include -I../../c-multihash/include -I../../c-multiaddr/include -Wall +CFLAGS = -O0 -I../include -I../../c-libp2p/include -I../../c-multihash/include -I../../c-multiaddr/include -I../../c-protobuf -Wall ifdef DEBUG CFLAGS += -g3 diff --git a/node/node.c b/node/node.c index 3dc0c51..8d037f2 100644 --- a/node/node.c +++ b/node/node.c @@ -10,74 +10,302 @@ #include "ipfs/node/node.h" +// for protobuf Node data & data_size encoded cid link_amount & links +enum WireType ipfs_node_message_fields[] = { WIRETYPE_LENGTH_DELIMITED, WIRETYPE_LENGTH_DELIMITED, WIRETYPE_LENGTH_DELIMITED, WIRETYPE_LENGTH_DELIMITED }; +// for protobuf NodeLink name cid +enum WireType ipfs_node_link_message_fields[] = { WIRETYPE_LENGTH_DELIMITED, WIRETYPE_LENGTH_DELIMITED }; + /*==================================================================================== * Link Functions *===================================================================================*/ -/* Create_Link +/* ipfs_node_link_new * @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) +int ipfs_node_link_new(char * name, unsigned char * ahash, struct NodeLink** node_link) { - struct Link * mylink; - mylink = malloc(sizeof(struct Link)); - mylink->name = name; + *node_link = malloc(sizeof(struct NodeLink)); + if (*node_link == NULL) + return 0; + (*node_link)->name = name; + (*node_link)->next = NULL; int ver = 0; - size_t lenhash = strlen((char*)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; + size_t lenhash = strlen((char*)ahash); + if (ipfs_cid_new(ver, ahash, lenhash, CID_PROTOBUF, &(*node_link)->cid) == 0) { + free(*node_link); + return 0; + } + return 1; } -/* Free_Link - * @param L: Free the link you have allocated. +/* ipfs_node_link_free + * @param node_link: Free the link you have allocated. */ -void Free_Link(struct Link * L) +int ipfs_node_link_free(struct NodeLink * node_link) { - ipfs_cid_free(L->Lcid); - free(L); + if (node_link != NULL) + ipfs_cid_free(node_link->cid); + free(node_link); + return 1; } + +int ipfs_node_link_protobuf_encode_size(struct NodeLink* link) { + if (link == NULL) + return 0; + + size_t size = 0; + size += 11 + strlen(link->name); + size += ipfs_cid_protobuf_encode_size(link->cid); + return size; +} + +int ipfs_node_link_protobuf_encode(struct NodeLink* link, unsigned char* buffer, size_t max_buffer_length, size_t* bytes_written) { + size_t bytes_used = 0; + int retVal = 0; + *bytes_written = 0; + retVal = protobuf_encode_length_delimited(1, ipfs_node_link_message_fields[0], link->name, strlen(link->name), &buffer[*bytes_written], max_buffer_length - *bytes_written, &bytes_used); + *bytes_written += bytes_used; + // cid + size_t cid_size = ipfs_cid_protobuf_encode_size(link->cid); + unsigned char cid_buffer[cid_size]; + retVal = ipfs_cid_protobuf_encode(link->cid, cid_buffer, cid_size, &bytes_used); + retVal = protobuf_encode_length_delimited(2, ipfs_node_link_message_fields[1], cid_buffer, bytes_used, &buffer[*bytes_written], max_buffer_length - *bytes_written, &bytes_used); + *bytes_written += bytes_used; + return 1; +} + +int ipfs_node_link_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct NodeLink** link, size_t* bytes_read) { + size_t pos = 0; + int retVal = 0; + *link = (struct NodeLink*)malloc(sizeof(struct NodeLink)); + (*link)->cid = NULL; + (*link)->name = NULL; + (*link)->next = NULL; + unsigned char* temp_buffer = NULL; + size_t temp_size; + + if (*link == 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): + if (protobuf_decode_string(&buffer[pos], buffer_length - pos, &((*link)->name), &bytes_read) == 0) + goto exit; + pos += bytes_read; + break; + case (2): + if (protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&temp_buffer, &temp_size, &bytes_read) == 0) + goto exit; + ipfs_cid_protobuf_decode(temp_buffer, temp_size, &((*link)->cid)); + pos += bytes_read; + free(temp_buffer); + temp_buffer = NULL; + break; + } + } + + retVal = 1; + +exit: + if (retVal == 0) { + if (link != NULL) + ipfs_node_link_free(*link); + } + if (temp_buffer != NULL) + free(temp_buffer); + + return retVal; +} + +/*** + * return an approximate size of the encoded node + */ +size_t ipfs_node_protobuf_encode_size(struct Node* node) { + size_t size = 0; + // data + size += 11 + node->data_size; + // encoded + size += 11; + if (node->encoded != NULL) + size += strlen((const char*)node->encoded); + // cid (a.k.a. cached) + size += 11 + ipfs_cid_protobuf_encode_size(node->cached); + // links + size += 11; + struct NodeLink* current = node->head_link; + while(current != NULL) { + size += 11 + strlen(current->name) + ipfs_cid_protobuf_encode_size(current->cid); + current = current->next; + } + return size; +} + +/*** + * 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_node_protobuf_encode(struct Node* node, 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_node_message_fields[0], node->data, node->data_size, &buffer[*bytes_written], max_buffer_length - *bytes_written, &bytes_used); + *bytes_written += bytes_used; + int sz = 0; + if (node->encoded != NULL) + sz = strlen(node->encoded); + retVal = protobuf_encode_length_delimited(2, ipfs_node_message_fields[1], node->encoded, sz, &buffer[*bytes_written], max_buffer_length - *bytes_written, &bytes_used); + *bytes_written += bytes_used; + // cid + size_t cid_size = ipfs_cid_protobuf_encode_size(node->cached); + unsigned char cid[cid_size]; + retVal = ipfs_cid_protobuf_encode(node->cached, cid, cid_size, &cid_size); + retVal = protobuf_encode_length_delimited(3, ipfs_node_message_fields[2], cid, cid_size, &buffer[*bytes_written], max_buffer_length - *bytes_written, &bytes_used); + *bytes_written += bytes_used; + // links + struct NodeLink* current = node->head_link; + while(current != NULL) { + // size + name + cid + size_t link_buffer_size = 11 + ipfs_node_link_protobuf_encode_size(current); + unsigned char link_buffer[link_buffer_size]; + retVal = ipfs_node_link_protobuf_encode(current, link_buffer, link_buffer_size, &link_buffer_size); + protobuf_encode_length_delimited(4, ipfs_node_message_fields[3], link_buffer, link_buffer_size, &buffer[*bytes_written], max_buffer_length - *bytes_written, &bytes_used); + *bytes_written += bytes_used; + current = current->next; + } + + return 1; +} + +/*** + * 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_node_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct Node** node) { + /* + * Field 0: data + * Field 1: encoded + * Field 3: cid + * Field 4: links array + */ + size_t pos = 0; + int retVal = 0; + unsigned char* temp_buffer = NULL; + size_t temp_size; + struct NodeLink* temp_link = NULL; + + if (ipfs_node_new(node) == 0) + 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**)&((*node)->data), &((*node)->data_size), &bytes_read) == 0) + goto exit; + pos += bytes_read; + break; + case (2): // encoded + if (protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&((*node)->encoded), &temp_size, &bytes_read) == 0) + goto exit; + pos += bytes_read; + break; + case (3): // 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, &((*node)->cached)) == 0) + goto exit; + free(temp_buffer); + temp_buffer = NULL; + break; + case (4): // links + 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_node_link_protobuf_decode(temp_buffer, temp_size, &temp_link, &bytes_read) == 0) + goto exit; + free(temp_buffer); + temp_buffer = NULL; + ipfs_node_add_link(*node, temp_link); + break; + } + } + + retVal = 1; + +exit: + if (retVal == 0) { + ipfs_node_free(*node); + } + if (temp_buffer != NULL) + free(temp_buffer); + + return retVal; +} + /*==================================================================================== * Node Functions *===================================================================================*/ -/*Create_Empty_Node +/*ipfs_node_new * Creates an empty node, allocates the required memory * Returns a fresh new node with no data set in it. */ -struct Node * Create_Empty_Node() +int ipfs_node_new(struct Node** node) { - struct Node * N; - N = (struct Node *)malloc(sizeof(struct Node)); - N->cached = NULL; - N->data = NULL; - N->encoded = NULL; - N->link_ammount = 0; - return N; + *node = (struct Node *)malloc(sizeof(struct Node)); + if (*node == NULL) + return 0; + (*node)->cached = NULL; + (*node)->data = NULL; + (*node)->encoded = NULL; + (*node)->head_link = NULL; + return 1; } /** * Set the cached struct element - * @param N the node to be modified - * @param TheCid the Cid to be copied into the Node->cached element + * @param node the node to be modified + * @param cid the Cid to be copied into the Node->cached element * @returns true(1) on success */ -int Node_Set_Cached(struct Node * N, struct Cid * TheCid) +int ipfs_node_set_cached(struct Node* node, struct Cid* cid) { - if (N->cached != NULL) - ipfs_cid_free(N->cached); - return ipfs_cid_new(TheCid->version, TheCid->hash, TheCid->hash_length, TheCid->codec, &(N->cached)); + if (node->cached != NULL) + ipfs_cid_free(node->cached); + return ipfs_cid_new(cid->version, cid->hash, cid->hash_length, cid->codec, &(node->cached)); } -/*Node_Set_Data +/*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 Node_Set_Data(struct Node * N, unsigned char * Data) +int ipfs_node_set_data(struct Node * N, unsigned char * Data, size_t data_size) { if(!N || !Data) { @@ -85,16 +313,21 @@ int Node_Set_Data(struct Node * N, unsigned char * Data) } N->encoded = NULL; N->cached = NULL; - N->data = Data; + N->data = malloc(sizeof(unsigned char) * data_size); + if (N->data == NULL) + return 0; + + memcpy(N->data, Data, data_size); + N->data_size = data_size; return 1; } -/*Node_Set_Encoded +/*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 Node_Set_Encoded(struct Node * N, unsigned char * Data) +int ipfs_node_set_encoded(struct Node * N, unsigned char * Data) { if(!N || !Data) { @@ -106,52 +339,65 @@ int Node_Set_Encoded(struct Node * N, unsigned char * Data) //N->data = NULL; return 1; } -/*Node_Get_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 * Node_Get_Data(struct Node * N) +unsigned char * ipfs_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; ilink_ammount; i++) - { - CN->links[i] = malloc(sizeof(struct Link)); - } +struct NodeLink* ipfs_node_link_last(struct Node* node) { + struct NodeLink* current = node->head_link; + while(current != NULL) { + if (current->next == NULL) + break; + current = current->next; } - memcpy(CN, CP_Node, sizeof(struct Node)); - memcpy(CN->links[0],CP_Node->links[0], sizeof(struct Link)); - return CN; + return current; } -/*Node_Delete + +int ipfs_node_remove_link(struct Node* node, struct NodeLink* toRemove) { + struct NodeLink* current = node->head_link; + struct NodeLink* previous = NULL; + while(current != NULL && current != toRemove) { + previous = current; + current = current->next; + } + if (current != NULL) { + if (previous == NULL) { + // we're trying to delete the head + previous = current->next; + ipfs_node_link_free(current); + node->head_link = previous; + } else { + // we're in the middle or end + previous = current->next; + ipfs_node_link_free(current); + } + return 1; + } + return 0; +} + +/*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 *) */ -void Node_Delete(struct Node * N) +void ipfs_node_free(struct Node * N) { - if(N) + if(N != NULL) { - if(N->link_ammount > 0) - { - for(int i=0; ilink_ammount; i++) - { - free(N->links[i]); - } + struct NodeLink* current = ipfs_node_link_last(N); + while (current != NULL) { + struct NodeLink* toDelete = current; + current = current->next; + ipfs_node_remove_link(N, toDelete); } if(N->cached) { @@ -163,137 +409,132 @@ void Node_Delete(struct Node * N) free(N); } } -/*Node_Get_Link + +/*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 Link * Node_Get_Link(struct Node * N, char * Name) +struct NodeLink * ipfs_node_get_link_by_name(struct Node * N, char * Name) { - struct Link * L; - for(int i=0;ilink_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; - unsigned char * ahash = L->Lcid->hash; - size_t lenhash = L->Lcid->hash_length; - ipfs_cid_new(ver, ahash, lenhash, CID_PROTOBUF, &L->Lcid); - return L; - } + struct NodeLink* current = N->head_link; + while(current != NULL && strcmp(Name, current->name) != 0) { + current = current->next; } - return NULL; + return current; } -/*Node_Remove_Link + +/*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 Node_Remove_Link(char * Name, struct Node * mynode) +int ipfs_node_remove_link_by_name(char * Name, struct Node * mynode) { - for(int i=0; ilink_ammount; i++) - { - if(mynode->links[i]->name == Name) - { - for(int x=i;xlink_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; + struct NodeLink* current = mynode->head_link; + struct NodeLink* previous = NULL; + while( (current != NULL) + && (( Name == NULL && current->name != NULL ) + || ( Name != NULL && current->name == NULL ) + || ( Name != NULL && current->name != NULL && strcmp(Name, current->name) != 0) ) ) { + previous = current; + current = current->next; + } + if (current != NULL) { + // we found it + if (previous == NULL) { + // we're first, use the next one (if there is one) + if (current->next != NULL) + mynode->head_link = current->next; + } else { + // we're somewhere in the middle, remove me from the list + previous->next = current->next; + ipfs_node_link_free(current); } + + return 1; } return 0; } -/* N_Add_Link - * Adds a link to your node + +/* ipfs_node_add_link + * Adds a link to your nodse * @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) +int ipfs_node_add_link(struct Node* Nl, struct NodeLink * mylink) { - struct Node * Nl = *mynode; - Nl->link_ammount++; - size_t calculatesize = 0; - if(Nl->link_ammount != 0) - { - for(int i=0; ilink_ammount-1;i++) - { - calculatesize = calculatesize + sizeof(Nl->links[i]); + if(Nl->head_link != NULL) { + // add to existing by finding last one + struct NodeLink* current_end = Nl->head_link; + while(current_end->next != NULL) { + current_end = current_end->next; } - calculatesize = calculatesize + linksz; - Nl = (struct Node *) realloc(Nl, sizeof(struct Node) + calculatesize); + // now we have the last one, add to it + current_end->next = mylink; } else { - Nl = (struct Node *) malloc(sizeof(struct Node) + linksz); + Nl->head_link = mylink; } - Nl->links[Nl->link_ammount-1] = malloc(sizeof(struct Link)); - memcpy(Nl->links[Nl->link_ammount-1],mylink,sizeof(struct Link)); - return Nl; + return 1; } -/*N_Create_From_Link +/*ipfs_node_new_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) +int ipfs_node_new_from_link(struct NodeLink * mylink, struct Node** node) { - 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)); - mynode->cached = NULL; - mynode->data = NULL; - mynode->encoded = NULL; - return mynode; + *node = (struct Node *) malloc(sizeof(struct Node)); + if (*node == NULL) + return 0; + (*node)->head_link = NULL; + ipfs_node_add_link(*node, mylink); + (*node)->cached = NULL; + (*node)->data = NULL; + (*node)->encoded = NULL; + return 1; } -/*N_Create_From_Data + +/** + * create a new Node struct with data * @param data: bytes buffer you want to create the node from + * @param data_size the size of the data buffer + * @param node a pointer to the node to be created * returns a node with the data you inputted. */ -struct Node * N_Create_From_Data(unsigned char * data, size_t data_size) +int ipfs_node_new_from_data(unsigned char * data, size_t data_size, struct Node** node) { if(data) { - struct Node * mynode; - mynode = (struct Node *) malloc(sizeof(struct Node)); - mynode->data = malloc(sizeof(unsigned char) * data_size); - memcpy(mynode->data, data, data_size); - mynode->data_size = data_size; - mynode->link_ammount=0; - mynode->encoded = NULL; - mynode->cached = NULL; - return mynode; + if (ipfs_node_new(node) == 0) + return 0; + return ipfs_node_set_data(*node, data, data_size); } - return NULL; + return 0; } -/*N_Create_From_Encoded - * @param data: encoded bytes buffer you want to create the node from - * returns a node with the encoded data you inputted. + +/*** + * 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 */ -struct Node * N_Create_From_Encoded(unsigned char * data) +int ipfs_node_new_from_encoded(unsigned char * data, struct Node** node) { if(data) { - struct Node * mynode; - mynode = (struct Node *) malloc(sizeof(struct Node)); - mynode->encoded = data; - mynode->link_ammount = 0; - mynode->data = NULL; - mynode->cached = NULL; - return mynode; + if (ipfs_node_new(node) == 0) + return 0; + (*node)->encoded = data; + return 1; } - return NULL; + return 0; } /*Node_Resolve_Max_Size * !!!This shouldn't concern you! @@ -362,18 +603,18 @@ struct Link_Proc * Node_Resolve_Links(struct Node * N, char * 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); + struct Link_Proc * LProc = (struct Link_Proc *) malloc(sizeof(struct Link_Proc) + sizeof(struct NodeLink) * expected_link_ammount); LProc->ammount = 0; char * linknames[expected_link_ammount]; Node_Resolve(linknames, path); for(int i=0;ilinks[i] = (struct Link *)malloc(sizeof(struct Link)); - memcpy(LProc->links[i], proclink, sizeof(struct Link)); + LProc->links[i] = (struct NodeLink *)malloc(sizeof(struct NodeLink)); + memcpy(LProc->links[i], proclink, sizeof(struct NodeLink)); LProc->ammount++; free(proclink); } @@ -395,7 +636,7 @@ void Free_Link_Proc(struct Link_Proc * LPRC) { for(int i=0;iammount;i++) { - Free_Link(LPRC->links[i]); + ipfs_node_link_free(LPRC->links[i]); } } free(LPRC); diff --git a/path/path.c b/path/path.c index 88c6556..11e6fc8 100644 --- a/path/path.c +++ b/path/path.c @@ -1,7 +1,6 @@ #include #include #include -#define IPFS_PATH_C #include // FromCid safely converts a cid.Cid type to a Path type @@ -9,11 +8,14 @@ char* ipfs_path_from_cid (struct Cid *c) { const char prefix[] = "/ipfs/"; char *rpath, *cidstr = CidString(c); + int l; - rpath = malloc(sizeof(prefix) + strlen(cidstr)); + l = sizeof(prefix) + strlen(cidstr); + rpath = malloc(l); if (!rpath) return NULL; - strcpy(rpath, prefix); - strcat(rpath, cidstr); + rpath[--l] = '\0'; + strncpy(rpath, prefix, l); + strncat(rpath, cidstr, l - strlen(rpath)); return rpath; } @@ -48,7 +50,7 @@ char** ipfs_path_split_n (char *p, char *delim, int n) return NULL; } - strcpy(rbuf, p); // keep original + memcpy(rbuf, p, strlen(p) + 1); // keep original for (c = rbuf, i = 0 ; i < n && c ; i++) { r[i] = c; c = strstr(c, delim); @@ -206,10 +208,11 @@ char *ipfs_path_from_segments(char *prefix, char **seg) ret = malloc(retlen + 1); // allocate final string size + null terminator. if (!ret) return NULL; - strcpy(ret, prefix); + ret[retlen] = '\0'; + strncpy(ret, prefix, retlen); for (i = 0 ; seg[i] ; i++) { - strcat(ret, "/"); - strcat(ret, seg[i]); + strncat(ret, "/", retlen - strlen(ret)); + strncat(ret, seg[i], retlen - strlen(ret)); } return ret; } @@ -232,7 +235,7 @@ int ipfs_path_parse_from_cid (char *dst, char *txt) if (!r) { return ErrCidDecode; } - strcpy (dst, r); + memcpy (dst, r, strlen(r) + 1); free (r); return 0; } @@ -252,7 +255,7 @@ int ipfs_path_parse (char *dst, char *txt) } err = ipfs_path_parse_from_cid (dst+plen, txt); if (err == 0) { // only change dst if ipfs_path_parse_from_cid returned success. - // Use memcpy instead of strcpy to avoid overwriting + // Use memcpy instead of strncpy to avoid overwriting // result of ipfs_path_parse_from_cid with a null terminator. memcpy (dst, prefix, plen); } @@ -264,11 +267,21 @@ int ipfs_path_parse (char *dst, char *txt) if (i < 3) return ErrBadPath; if (strcmp (txt, prefix) == 0) { - char buf[strlen(txt+5)]; - strcpy (buf, txt+6); // copy to temp buffer. + char *buf; + i = strlen(txt+6); + buf = malloc(i + 1); + if (!buf) { + return ErrAllocFailed; + } + buf[i] = '\0'; + strncpy (buf, txt+6, i); // copy to temp buffer. c = strchr(buf, '/'); - if (c) *c = '\0'; - return ipfs_path_parse_from_cid(dst, buf); + if (c) { + *c = '\0'; + } + err = ipfs_path_parse_from_cid(dst, buf); + free (buf); + return err; } else if (strcmp (txt, "/ipns/") != 0) { return ErrBadPath; } diff --git a/path/resolver.c b/path/resolver.c index c4b410f..4d17129 100644 --- a/path/resolver.c +++ b/path/resolver.c @@ -87,7 +87,7 @@ int ipfs_path_resolve_path_components(Node ***nd, Context ctx, Resolver *s, char // would retrieve "baz" in ("bar" in ("foo" in nd.Links).Links).Links int ipfs_path_resolve_links(Node ***result, Context ctx, Node *ndd, char **names) { - int err, idx = 0; + int err, idx = 0, l; NodeLink *lnk; Node *nd; @@ -115,9 +115,10 @@ int ipfs_path_resolve_links(Node ***result, Context ctx, Node *ndd, char **names if (ErrPath[ErrNoLink]) { free(ErrPath[ErrNoLink]); } - ErrPath[ErrNoLink] = malloc(strlen(msg) + 1); + l = strlen(msg) + 1; + ErrPath[ErrNoLink] = malloc(l); if (ErrPath[ErrNoLink]) { - strcpy(ErrPath[ErrNoLink], msg); + memcpy(ErrPath[ErrNoLink], msg, l); } free (*result); return ErrNoLink; diff --git a/repo/config/Makefile b/repo/config/Makefile index c0a1792..cf49611 100644 --- a/repo/config/Makefile +++ b/repo/config/Makefile @@ -1,5 +1,5 @@ CC = gcc -CFLAGS = -O0 -I../../include -I../../../c-libp2p/include -I../../../c-multihash/include -Wall +CFLAGS = -O0 -I../../include -I../../../c-libp2p/include -I../../../c-multihash/include -I../../../c-protobuf -Wall ifdef DEBUG CFLAGS += -g3 diff --git a/repo/fsrepo/Makefile b/repo/fsrepo/Makefile index 48e22d6..a4196c0 100644 --- a/repo/fsrepo/Makefile +++ b/repo/fsrepo/Makefile @@ -1,5 +1,5 @@ CC = gcc -CFLAGS = -O0 -I../../include -I../../../c-libp2p/include -I../../../lmdb/libraries/liblmdb -Wall +CFLAGS = -O0 -I../../include -I../../../c-libp2p/include -I../../../lmdb/libraries/liblmdb -I../../../c-protobuf -Wall ifdef DEBUG CFLAGS += -g3 diff --git a/repo/fsrepo/lmdb_datastore.c b/repo/fsrepo/lmdb_datastore.c index 62b29f5..9ac6e8d 100644 --- a/repo/fsrepo/lmdb_datastore.c +++ b/repo/fsrepo/lmdb_datastore.c @@ -175,10 +175,17 @@ int repo_fsrepo_lmdb_put(unsigned const char* key, size_t key_size, unsigned cha * @returns true(1) on success */ int repo_fsrepo_lmdb_put_block(const struct Block* block, const struct Datastore* datastore) { - return repo_fsrepo_lmdb_put(block->cid->hash, block->cid->hash_length, block->data, block->data_length, datastore); } +/*** + * Save a node in the datastore + */ +int repo_fsrepo_lmdb_put_node(const struct Node* node, const struct Datastore* datastore) { + // first the links, then the data + +} + /** * Open an lmdb database with the given parameters. * Note: for now, the parameters are not used diff --git a/test/Makefile b/test/Makefile index 9a247e6..c241eb0 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,5 +1,5 @@ CC = gcc -CFLAGS = -O0 -I../include -I../../c-libp2p/include -I../../c-multihash/include -I../../c-multiaddr/ -g3 -Wall +CFLAGS = -O0 -I../include -I../../c-libp2p/include -I../../c-multihash/include -I../../c-multiaddr/ -I../../c-protobuf -g3 -Wall LFLAGS = -L../../c-libp2p -L../../c-multihash -L../../c-multiaddr -lp2p -lm -lmultihash -lmultiaddr -lpthread DEPS = cmd/ipfs/test_init.h repo/test_repo_bootstrap_peers.h repo/test_repo_config.h repo/test_repo_identity.h cid/test_cid.h OBJS = testit.o test_helper.o \ @@ -18,7 +18,8 @@ OBJS = testit.o test_helper.o \ ../repo/config/config.o ../repo/config/identity.o \ ../repo/config/bootstrap_peers.o ../repo/config/datastore.o ../repo/config/gateway.o \ ../repo/config/addresses.o ../repo/config/swarm.o ../repo/config/peer.o \ - ../thirdparty/ipfsaddr/ipfs_addr.o + ../thirdparty/ipfsaddr/ipfs_addr.o \ + ../../c-protobuf/protobuf.o ../../c-protobuf/varint.o %.o: %.c $(DEPS) $(CC) -c -o $@ $< $(CFLAGS) diff --git a/test/cid/test_cid.h b/test/cid/test_cid.h index aa28841..8eb0d6f 100644 --- a/test/cid/test_cid.h +++ b/test/cid/test_cid.h @@ -106,3 +106,50 @@ int test_cid_cast_non_multihash() { return 1; } +int test_cid_protobuf_encode_decode() { + struct Cid tester; + tester.version = 1; + tester.codec = CID_ETHEREUM_BLOCK; + tester.hash = "ABC123"; + tester.hash_length = 6; + size_t bytes_written_to_buffer; + + // encode + size_t buffer_length = ipfs_cid_protobuf_encode_size(&tester); + unsigned char buffer[buffer_length]; + ipfs_cid_protobuf_encode(&tester, buffer, buffer_length, &bytes_written_to_buffer); + + // decode + struct Cid* results; + ipfs_cid_protobuf_decode(buffer, bytes_written_to_buffer, &results); + + // compare + if (tester.version != results->version) { + printf("Version %d does not match version %d\n", tester.version, results->version); + ipfs_cid_free(results); + return 0; + } + + if (tester.codec != results->codec) { + printf("Codec %02x does not match %02x\n", tester.codec, results->codec); + ipfs_cid_free(results); + return 0; + } + + if (tester.hash_length != results->hash_length) { + printf("Hash length %d does not match %d\n", tester.hash_length, results->hash_length); + ipfs_cid_free(results); + return 0; + } + + for(int i = 0; i < 6; i++) { + if (tester.hash[i] != results->hash[i]) { + printf("Hash character %c does not match %c at position %d", tester.hash[i], results->hash[i], i); + ipfs_cid_free(results); + return 0; + } + } + + ipfs_cid_free(results); + return 1; +} diff --git a/test/merkledag/test_merkledag.h b/test/merkledag/test_merkledag.h index be26a9d..632be2e 100644 --- a/test/merkledag/test_merkledag.h +++ b/test/merkledag/test_merkledag.h @@ -2,30 +2,35 @@ #include "ipfs/node/node.h" #include "../test_helper.h" -int test_merkledag_get_data() { - int retVal = 0; - +struct FSRepo* createAndOpenRepo(const char* dir) { + int retVal = 1; // create a fresh repo - retVal = drop_and_build_repository("/tmp/.ipfs"); + retVal = drop_and_build_repository(dir); if (retVal == 0) - return 0; + return NULL; // open the fs repo struct RepoConfig* repo_config = NULL; struct FSRepo* fs_repo; - const char* path = "/tmp/.ipfs"; // create the struct - retVal = ipfs_repo_fsrepo_new((char*)path, repo_config, &fs_repo); + retVal = ipfs_repo_fsrepo_new(dir, repo_config, &fs_repo); if (retVal == 0) - return 0; + return NULL; // open the repository and read the config file retVal = ipfs_repo_fsrepo_open(fs_repo); if (retVal == 0) { ipfs_repo_fsrepo_free(fs_repo); - return 0; + return NULL; } + return fs_repo; +} + +int test_merkledag_get_data() { + int retVal = 0; + + struct FSRepo* fs_repo = createAndOpenRepo("/tmp/.ipfs"); // create data for node size_t binary_data_size = 256; @@ -35,11 +40,12 @@ int test_merkledag_get_data() { } // create a node - struct Node* node1 = N_Create_From_Data(binary_data, binary_data_size); + struct Node* node1; + retVal = ipfs_node_new_from_data(binary_data, binary_data_size, &node1); retVal = ipfs_merkledag_add(node1, fs_repo); if (retVal == 0) { - Node_Delete(node1); + ipfs_node_free(node1); ipfs_repo_fsrepo_free(fs_repo); return 0; } @@ -48,15 +54,15 @@ int test_merkledag_get_data() { struct Node* results_node; retVal = ipfs_merkledag_get(node1->cached, &results_node, fs_repo); if (retVal == 0) { - Node_Delete(node1); - Node_Delete(results_node); + ipfs_node_free(node1); + ipfs_node_free(results_node); ipfs_repo_fsrepo_free(fs_repo); return 0; } if (results_node->data_size != 256) { - Node_Delete(node1); - Node_Delete(results_node); + ipfs_node_free(node1); + ipfs_node_free(results_node); ipfs_repo_fsrepo_free(fs_repo); return 0; } @@ -64,15 +70,15 @@ int test_merkledag_get_data() { // the data should be the same for(int i = 0; i < results_node->data_size; i++) { if (results_node->data[i] != node1->data[i]) { - Node_Delete(node1); - Node_Delete(results_node); + ipfs_node_free(node1); + ipfs_node_free(results_node); ipfs_repo_fsrepo_free(fs_repo); return 0; } } - Node_Delete(node1); - Node_Delete(results_node); + ipfs_node_free(node1); + ipfs_node_free(results_node); ipfs_repo_fsrepo_free(fs_repo); return retVal; @@ -81,28 +87,10 @@ int test_merkledag_get_data() { int test_merkledag_add_data() { int retVal = 0; - // create a fresh repo - retVal = drop_and_build_repository("/tmp/.ipfs"); - if (retVal == 0) + struct FSRepo* fs_repo = createAndOpenRepo("/tmp/.ipfs"); + if (fs_repo == NULL) return 0; - // open the fs repo - struct RepoConfig* repo_config = NULL; - struct FSRepo* fs_repo; - const char* path = "/tmp/.ipfs"; - - // create the struct - retVal = ipfs_repo_fsrepo_new((char*)path, repo_config, &fs_repo); - if (retVal == 0) - return 0; - - // open the repository and read the config file - retVal = ipfs_repo_fsrepo_open(fs_repo); - if (retVal == 0) { - ipfs_repo_fsrepo_free(fs_repo); - return 0; - } - // get the size of the database int start_file_size = os_utils_file_size("/tmp/.ipfs/datastore/data.mdb"); @@ -114,11 +102,12 @@ int test_merkledag_add_data() { } // create a node - struct Node* node1 = N_Create_From_Data(binary_data, binary_data_size); + struct Node* node1; + retVal = ipfs_node_new_from_data(binary_data, binary_data_size, &node1); retVal = ipfs_merkledag_add(node1, fs_repo); if (retVal == 0) { - Node_Delete(node1); + ipfs_node_free(node1); return 0; } @@ -128,30 +117,31 @@ int test_merkledag_add_data() { int first_add_size = os_utils_file_size("/tmp/.ipfs/datastore/data.mdb"); if (first_add_size == start_file_size) { // uh oh, database should have increased in size - Node_Delete(node1); + ipfs_node_free(node1); return 0; } // adding the same binary again should do nothing (the hash should be the same) - struct Node* node2 = N_Create_From_Data(binary_data, binary_data_size); + struct Node* node2; + retVal = ipfs_node_new_from_data(binary_data, binary_data_size, &node2); retVal = ipfs_merkledag_add(node2, fs_repo); if (retVal == 0) { - Node_Delete(node1); - Node_Delete(node2); + ipfs_node_free(node1); + ipfs_node_free(node2); return 0; } // make sure everything is correct if (node2->cached == NULL) { - Node_Delete(node1); - Node_Delete(node2); + ipfs_node_free(node1); + ipfs_node_free(node2); return 0; } for(int i = 0; i < node1->cached->hash_length; i++) { if (node1->cached->hash[i] != node2->cached->hash[i]) { printf("hash of node1 does not match node2 at position %d\n", i); - Node_Delete(node1); - Node_Delete(node2); + ipfs_node_free(node1); + ipfs_node_free(node2); return 0; } } @@ -159,35 +149,36 @@ int test_merkledag_add_data() { int second_add_size = os_utils_file_size("/tmp/.ipfs/datastore/data.mdb"); if (first_add_size != second_add_size) { // uh oh, the database shouldn't have changed size printf("looks as if a new record was added when it shouldn't have. Old file size: %d, new file size: %d\n", first_add_size, second_add_size); - Node_Delete(node1); - Node_Delete(node2); + ipfs_node_free(node1); + ipfs_node_free(node2); return 0; } // now change 1 byte, which should change the hash binary_data[10] = 0; // create a node - struct Node* node3 = N_Create_From_Data(binary_data, binary_data_size); + struct Node* node3; + retVal = ipfs_node_new_from_data(binary_data, binary_data_size, &node3); retVal = ipfs_merkledag_add(node3, fs_repo); if (retVal == 0) { - Node_Delete(node1); - Node_Delete(node2); - Node_Delete(node3); + ipfs_node_free(node1); + ipfs_node_free(node2); + ipfs_node_free(node3); return 0; } // make sure everything is correct if (node3->cached == NULL) { - Node_Delete(node1); - Node_Delete(node2); - Node_Delete(node3); + ipfs_node_free(node1); + ipfs_node_free(node2); + ipfs_node_free(node3); return 0; } - Node_Delete(node1); - Node_Delete(node2); - Node_Delete(node3); + ipfs_node_free(node1); + ipfs_node_free(node2); + ipfs_node_free(node3); int third_add_size = os_utils_file_size("/tmp/.ipfs/datastore/data.mdb"); if (third_add_size == second_add_size || third_add_size < second_add_size) {// uh oh, it didn't add it printf("Node 3 should have been added, but the file size did not change from %d.\n", third_add_size); @@ -198,3 +189,61 @@ int test_merkledag_add_data() { return 1; } + +/** + * Should save links + */ +int test_merkledag_add_node_with_links() { + int retVal = 0; + struct Link* link = NULL; + struct Node* node1 = NULL; + + struct FSRepo* fs_repo = createAndOpenRepo("/tmp/.ipfs"); + if (fs_repo == NULL) { + printf("Unable to create repo\n"); + return 0; + } + + // make link + retVal = ipfs_node_link_new("", "abc123", &link); + retVal = ipfs_node_new_from_link(link, &node1); + + retVal = ipfs_merkledag_add(node1, fs_repo); + if (retVal == 0) { + ipfs_repo_fsrepo_free(fs_repo); + ipfs_node_free(node1); + printf("Unable to add node\n"); + return 0; + } + + // now look for it + struct Node* node2 = NULL; + retVal = ipfs_merkledag_get(node1->cached, &node2, fs_repo); + if (retVal == 0) { + ipfs_repo_fsrepo_free(fs_repo); + ipfs_node_free(node1); + return 0; + } + + struct NodeLink* node1_link = node1->head_link; + struct NodeLink* node2_link = node2->head_link; + while(node1_link != NULL) { + for(int i = 0; i < node1_link->cid->hash_length; i++) { + if(node1_link->cid->hash[i] != node2_link->cid->hash[i]) { + printf("Hashes do not match for node %s\n", node1_link->name); + ipfs_repo_fsrepo_free(fs_repo); + ipfs_node_free(node1); + ipfs_node_free(node2); + return 0; + } + } + node1_link = node1_link->next; + node2_link = node2_link->next; + } + + ipfs_node_free(node1); + ipfs_node_free(node2); + ipfs_repo_fsrepo_free(fs_repo); + + return 1; +} diff --git a/test/node/test_node.h b/test/node/test_node.h index bdf89e9..ca0720f 100644 --- a/test/node/test_node.h +++ b/test/node/test_node.h @@ -3,40 +3,157 @@ int test_node() { //Variables of link: char * name = "Alex"; - unsigned char * ahash = "QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG"; - struct Link * mylink; - mylink = Create_Link(name,ahash); - printf("===================================\n" \ - "Node Link:\n" \ - " -Name: %s\n" \ - " -Size: %lu\n" \ - "\n Cid Details:\n\n" \ - " -Version: %d\n" \ - " -Codec: %c\n" \ - " -Hash: %s\n" \ - " -Hash Length: %lu\n" \ - "====================================\n" \ - , mylink->name, mylink->size, mylink->Lcid->version,mylink->Lcid->codec,mylink->Lcid->hash,mylink->Lcid->hash_length); + unsigned char * ahash = (unsigned char*)"QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG"; + struct NodeLink * mylink; + int retVal = ipfs_node_link_new(name,ahash, &mylink); + //Link Two for testing purposes char * name2 = "Simo"; - unsigned char * ahash2 = "QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnSimo"; - struct Link * mylink2; - mylink2 = Create_Link(name2,ahash2); + unsigned char * ahash2 = (unsigned char*)"QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnSimo"; + struct NodeLink * mylink2; + retVal = ipfs_node_link_new(name2, ahash2, &mylink2); + //Nodes 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->links[0]->name, Mynode->links[0]->Lcid->hash); - 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->links[1]->name,Mynode->links[1]->Lcid->hash); - struct Link * ResultLink = Node_Get_Link(Mynode, "Simo"); - printf("\nResultLink: \nName: %s\nHash: %s\n", ResultLink->name, ResultLink->Lcid->hash); - Node_Remove_Link("Simo", Mynode); - printf("Outlinkamt: %d\n", Mynode->link_ammount); - Free_Link(mylink); - Free_Link(mylink2); - Free_Link(ResultLink); - Node_Delete(Mynode); + retVal = ipfs_node_new_from_link(mylink, &Mynode); + //mylink->name = "HAHA";//Testing for valid node creation + retVal = ipfs_node_add_link(Mynode, mylink2); + //mylink2->name = "HAHA";//Testing for valid node creation + struct NodeLink * ResultLink = ipfs_node_get_link_by_name(Mynode, "Simo"); + ipfs_node_remove_link_by_name("Simo", Mynode); + ipfs_node_free(Mynode); return 1; } + +int compare_link(struct NodeLink* link1, struct NodeLink* link2) { + if (strcmp(link1->name, link2->name) != 0) { + printf("Link Names are different %s vs. %s\n", link1->name, link2->name); + return 0; + } + if (link1->cid->codec != link2->cid->codec) { + printf("Link cid codecs are different. Expected %02x but got %02x\n", link1->cid->codec, link2->cid->codec); + return 0; + } + if (link1->cid->hash_length != link2->cid->hash_length) { + printf("Link cid hash lengths are different. Expected %d but got %d\n", link1->cid->hash_length, link2->cid->hash_length); + return 0; + } + if (link1->cid->version != link2->cid->version) { + printf("Link cid versions are different. Expected %d but got %d\n", link1->cid->version, link2->cid->version); + return 0; + } + if (memcmp(link1->cid->hash, link2->cid->hash, link1->cid->hash_length) != 0) { + printf("compare_link: The values of the hashes are different\n"); + return 0; + } + return 1; +} + +int test_node_link_encode_decode() { + struct NodeLink* control = NULL; + struct NodeLink* results = NULL; + size_t nl_size; + unsigned char* buffer = NULL; + int retVal = 0; + + // make a NodeLink + if (ipfs_node_link_new("My Name", "QmMyHash", &control) == 0) + goto exit; + + // encode it + nl_size = ipfs_node_link_protobuf_encode_size(control); + buffer = malloc(nl_size); + if (buffer == NULL) + goto exit; + if (ipfs_node_link_protobuf_encode(control, buffer, nl_size, &nl_size) == 0) { + goto exit; + } + + // decode it + if (ipfs_node_link_protobuf_decode(buffer, nl_size, &results) == 0) { + goto exit; + } + + // verify it + if (compare_link(control, results) == 0) + goto exit; + retVal = 1; +exit: + if (control != NULL) + ipfs_node_link_free(control); + if (results != NULL) + ipfs_node_link_free(results); + return retVal; +} + +/*** + * Test a node with 2 links + */ +int test_node_encode_decode() { + struct Node* control = NULL; + struct Node* results = NULL; + struct NodeLink* link1 = NULL; + struct NodeLink* link2 = NULL; + int retVal = 0; + size_t buffer_length = 0; + unsigned char* buffer = NULL; + + // node + if (ipfs_node_new(&control) == 0) + goto exit; + + // first link + if (ipfs_node_link_new((char*)"Link1", (unsigned char*)"QmLink1", &link1) == 0) + goto exit; + + if ( ipfs_node_add_link(control, link1) == 0) + goto exit; + + // second link + if (ipfs_node_link_new((char*)"Link2", (unsigned char*)"QmLink2", &link2) == 0) + goto exit; + if ( ipfs_node_add_link(control, link2) == 0) + goto exit; + + // encode + buffer_length = ipfs_node_protobuf_encode_size(control); + buffer = (unsigned char*)malloc(buffer_length); + if (ipfs_node_protobuf_encode(control, buffer, buffer_length, &buffer_length) == 0) + goto exit; + + // decode + if (ipfs_node_protobuf_decode(buffer, buffer_length, &results) == 0) + goto exit; + + // compare results + + struct NodeLink* control_link = control->head_link; + struct NodeLink* results_link = results->head_link; + while(control_link != NULL) { + if (compare_link(control_link, results_link) == 0) { + printf("Error was on link %s\n", control_link->name); + goto exit; + } + control_link = control_link->next; + results_link = results_link->next; + } + + if (control->data_size != results->data_size) + goto exit; + + if (memcmp(results->data, control->data, control->data_size) != 0) { + goto exit; + } + + retVal = 1; +exit: + // clean up + if (control != NULL) + ipfs_node_free(control); + if (results != NULL) + ipfs_node_free(results); + if (buffer != NULL) + free(buffer); + + return retVal; +} diff --git a/test/test_helper.c b/test/test_helper.c index fdea41e..592669a 100644 --- a/test/test_helper.c +++ b/test/test_helper.c @@ -69,21 +69,25 @@ int remove_directory(const char *path) int make_ipfs_repository(const char* path) { int retVal; + char currDirectory[1024]; struct RepoConfig* repo_config; - char currDirectory[1024]; - retVal = os_utils_filepath_join(path, "config", currDirectory, 1024); - if (retVal == 0) - return 0; - unlink(currDirectory); - retVal = os_utils_filepath_join(path, "datastore", currDirectory, 1024); - if (retVal == 0) - return 0; - remove_directory(currDirectory); - retVal = os_utils_filepath_join(path, "blockstore", currDirectory, 1024); - if (retVal == 0) - return 0; - remove_directory(currDirectory); + if (os_utils_file_exists(path)) { + retVal = os_utils_filepath_join(path, "config", currDirectory, 1024); + if (retVal == 0) + return 0; + unlink(currDirectory); + retVal = os_utils_filepath_join(path, "datastore", currDirectory, 1024); + if (retVal == 0) + return 0; + remove_directory(currDirectory); + retVal = os_utils_filepath_join(path, "blockstore", currDirectory, 1024); + if (retVal == 0) + return 0; + remove_directory(currDirectory); + } else { + mkdir(path, S_IRWXU); + } // build a default repo config retVal = ipfs_repo_config_new(&repo_config); diff --git a/test/testit.c b/test/testit.c index 8da3a93..1ba6006 100644 --- a/test/testit.c +++ b/test/testit.c @@ -25,7 +25,7 @@ const char* names[] = { "test_cid_new_free", "test_cid_cast_multihash", "test_cid_cast_non_multihash", - //"test_init_new_installation", + "test_cid_protobuf_encode_decode", "test_repo_config_new", "test_repo_config_init", "test_repo_config_write", @@ -41,15 +41,18 @@ const char* names[] = { "test_repo_bootstrap_peers_init", "test_ipfs_datastore_put", "test_node", + "test_node_link_encode_decode", + "test_node_encode_decode", "test_merkledag_add_data", - "test_merkledag_get_data" + "test_merkledag_get_data", + "test_merkledag_add_node_with_links" }; int (*funcs[])(void) = { test_cid_new_free, test_cid_cast_multihash, test_cid_cast_non_multihash, - //test_init_new_installation, + test_cid_protobuf_encode_decode, test_repo_config_new, test_repo_config_init, test_repo_config_write, @@ -65,8 +68,11 @@ int (*funcs[])(void) = { test_repo_bootstrap_peers_init, test_ipfs_datastore_put, test_node, + test_node_link_encode_decode, + test_node_encode_decode, test_merkledag_add_data, - test_merkledag_get_data + test_merkledag_get_data, + test_merkledag_add_node_with_links }; /** @@ -88,9 +94,12 @@ int main(int argc, char** argv) { } int array_length = sizeof(funcs) / sizeof(funcs[0]); for (int i = 0; i < array_length; i++) { - if (only_one && strcmp(names[i], test_wanted) == 0) { - tests_ran++; - counter += testit(names[i], funcs[i]); + if (only_one) { + char* currName = names[i]; + if (strcmp(currName, test_wanted) == 0) { + tests_ran++; + counter += testit(names[i], funcs[i]); + } } else if (!only_one) {