From b914745b476926a5bd0d4c254aed8bec51707b5c Mon Sep 17 00:00:00 2001 From: Jose Marcial Vieira Bisneto Date: Tue, 20 Dec 2016 20:38:31 -0300 Subject: [PATCH 1/6] Removed redundant struct stime from util/time --- include/ipfs/util/time.h | 10 ++-------- util/time.c | 25 +++++++------------------ 2 files changed, 9 insertions(+), 26 deletions(-) diff --git a/include/ipfs/util/time.h b/include/ipfs/util/time.h index 3d49f5c..b6eec48 100644 --- a/include/ipfs/util/time.h +++ b/include/ipfs/util/time.h @@ -1,12 +1,6 @@ #ifndef IPFS_TIME_H #define IPFS_TIME_H - struct stime { - time_t t; - struct timespec ts; - }; - - int get_gmttime(struct stime *st); - int ipfs_util_time_parse_RFC3339 (struct stime *st, char *s); - char *ipfs_util_time_format_RFC3339 (struct stime *st); + int ipfs_util_time_parse_RFC3339 (struct timespec *ts, char *s); + char *ipfs_util_time_format_RFC3339 (struct timespec *ts); #endif // IPFS_TIME_H diff --git a/util/time.c b/util/time.c index b438eb8..68707fe 100644 --- a/util/time.c +++ b/util/time.c @@ -13,35 +13,24 @@ #include #include "ipfs/util/time.h" -int get_gmttime(struct stime *st) { - if (!st) { - return 1; - } - if (!timespec_get(&st->ts, TIME_UTC) || - !time(&st->t)) { - return 2; - } - return 0; -} - -int ipfs_util_time_parse_RFC3339 (struct stime *st, char *s) +int ipfs_util_time_parse_RFC3339 (struct timespec *ts, char *s) { char *r; struct tm tm; - if (!st || !s || strlen(s) != 35) { + if (!ts || !s || strlen(s) != 35) { return 1; } r = strptime (s, "%Y-%m-%dT%H:%M:%S", &tm); if (!r || *r != '.') { return 2; } - st->t = mktime(&tm); - st->ts.tv_nsec = atoll(++r); + ts->tv_sec = mktime(&tm); + ts->tv_nsec = atoll(++r); return 0; } -char *ipfs_util_time_format_RFC3339 (struct stime *st) +char *ipfs_util_time_format_RFC3339 (struct timespec *ts) { char buf[31], *ret; @@ -50,8 +39,8 @@ char *ipfs_util_time_format_RFC3339 (struct stime *st) return NULL; } - if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S.%%09dZ00:00", gmtime(&st->t)) != sizeof(buf)-1 || - snprintf(ret, 36, buf, st->ts.tv_nsec) != 35) { + if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S.%%09dZ00:00", gmtime(&(ts->tv_sec))) != sizeof(buf)-1 || + snprintf(ret, 36, buf, ts->tv_nsec) != 35) { free (ret); return NULL; } From a94aa609b968ffe4ad6bad0c7366212c4104a383 Mon Sep 17 00:00:00 2001 From: Jose Marcial Vieira Bisneto Date: Tue, 20 Dec 2016 20:46:50 -0300 Subject: [PATCH 2/6] Initial implementation of namesys/routing --- include/ipfs/namesys/routing.h | 5 +++-- namesys/routing.c | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/include/ipfs/namesys/routing.h b/include/ipfs/namesys/routing.h index 7d6fbdd..bebc872 100644 --- a/include/ipfs/namesys/routing.h +++ b/include/ipfs/namesys/routing.h @@ -10,7 +10,7 @@ struct cacheEntry { char *key; char *value; - struct stime eol; + struct timespec eol; }; struct routingResolver { @@ -29,7 +29,8 @@ // 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 stime *st, 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*, struct MultiHash*); #endif // IPNS_NAMESYS_ROUTING_H diff --git a/namesys/routing.c b/namesys/routing.c index 3842300..924878b 100644 --- a/namesys/routing.c +++ b/namesys/routing.c @@ -13,15 +13,15 @@ char* ipfs_routing_cache_get (char *key, struct ipns_entry *ientry) { int i; struct routingResolver *cache; - struct stime now; + struct timespec now; if (key && ientry) { cache = ientry->cache; if (cache) { - get_gmttime (&now); + timespec_get (&now); for (i = 0 ; i < cache->next ; i++) { - if (((now.t < cache->data[i]->eol.t || - (now.t == cache->data[i]->eol.t && now.ts.tv_nsec < cache->data[i]->eol.ts.tv_nsec))) && + if (((now.tv_sec < cache->data[i]->eol.tv_sec || + (now.tv_sec == cache->data[i]->eol.tv_sec && now.tv_nsec < cache->data[i]->eol.tv_nsec))) && strcmp(cache->data[i]->key, key) == 0) { return cache->data[i]->value; } @@ -43,8 +43,8 @@ void ipfs_routing_cache_set (char *key, char *value, struct ipns_entry *ientry) if (n) { n->key = key; n->value = value; - get_gmttime (&n->eol); // now - n->eol.t += DefaultResolverCacheTTL; // sum TTL seconds to time seconds. + timespec_get (&n->eol); // now + n->eol.tv_sec += DefaultResolverCacheTTL; // sum TTL seconds to time seconds. cache->data[cache->next++] = n; } } @@ -152,7 +152,7 @@ int ipfs_namesys_routing_resolve_once (char **path, char *name, int depth, char } // check sig with pk - err = libp2p_crypto_verify (ipfs_ipns_entry_data_for_sig(pb->IpnsEntry), ipfs_ipns_entry_get_signature(pb->IpnsEntry), &ok); + err = libp2p_crypto_verify (ipns_entry_data_for_sig(pb->IpnsEntry), pb->IpnsEntry->signature, &ok); if (err || !ok) { char buf[500]; snprintf(buf, sizeof(buf), Err[ErrInvalidSignatureFmt], pubkey); @@ -198,12 +198,12 @@ int ipfs_namesys_routing_resolve_once (char **path, char *name, int depth, char return 0; } -int ipfs_namesys_routing_check_EOL (struct stime *st, struct namesys_pb *pb) +int ipfs_namesys_routing_check_EOL (struct timespec *ts, struct namesys_pb *pb) { int err; - if (ipfs_namesys_pb_get_validity_type (pb->IpnsEntry) == IpnsEntry_EOL) { - err = ipfs_util_time_parse_RFC3339 (st, ipfs_namesys_pb_get_validity (pb->IpnsEntry)); + if (*(pb->IpnsEntry->validityType) == IpnsEntry_EOL) { + err = ipfs_util_time_parse_RFC3339 (ts, pb->IpnsEntry->validity); if (!err) { return 1; } From 5b8678b48c7a266d1adcf4b4d726b00f01077c9f Mon Sep 17 00:00:00 2001 From: Jose Marcial Vieira Bisneto Date: Tue, 20 Dec 2016 20:46:50 -0300 Subject: [PATCH 3/6] Some changes at namesys/routing --- include/ipfs/namesys/routing.h | 5 +- namesys/publisher.c | 362 +++++++++++++++++++++++++++++++++ namesys/routing.c | 20 +- 3 files changed, 375 insertions(+), 12 deletions(-) create mode 100644 namesys/publisher.c diff --git a/include/ipfs/namesys/routing.h b/include/ipfs/namesys/routing.h index 7d6fbdd..bebc872 100644 --- a/include/ipfs/namesys/routing.h +++ b/include/ipfs/namesys/routing.h @@ -10,7 +10,7 @@ struct cacheEntry { char *key; char *value; - struct stime eol; + struct timespec eol; }; struct routingResolver { @@ -29,7 +29,8 @@ // 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 stime *st, 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*, struct MultiHash*); #endif // IPNS_NAMESYS_ROUTING_H diff --git a/namesys/publisher.c b/namesys/publisher.c new file mode 100644 index 0000000..a3566e0 --- /dev/null +++ b/namesys/publisher.c @@ -0,0 +1,362 @@ +var PublishPutValTimeout = time.Minute + +// ipnsPublisher is capable of publishing and resolving names to the IPFS +// routing system. +type ipnsPublisher struct { + routing routing.ValueStore + ds ds.Datastore +} + +// NewRoutingPublisher constructs a publisher for the IPFS Routing name system. +func NewRoutingPublisher(route routing.ValueStore, ds ds.Datastore) *ipnsPublisher { + if ds == nil { + panic("nil datastore") + } + return &ipnsPublisher{routing: route, ds: ds} +} + +// Publish implements Publisher. Accepts a keypair and a value, +// and publishes it out to the routing system +func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Path) error { + log.Debugf("Publish %s", value) + return p.PublishWithEOL(ctx, k, value, time.Now().Add(time.Hour*24)) +} + +// PublishWithEOL is a temporary stand in for the ipns records implementation +// see here for more details: https://github.com/ipfs/specs/tree/master/records +func (p *ipnsPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) error { + + id, err := peer.IDFromPrivateKey(k) + if err != nil { + return err + } + + _, ipnskey := IpnsKeysForID(id) + + // get previous records sequence number + seqnum, err := p.getPreviousSeqNo(ctx, ipnskey) + if err != nil { + return err + } + + // increment it + seqnum++ + + return PutRecordToRouting(ctx, k, value, seqnum, eol, p.routing, id) +} + +func (p *ipnsPublisher) getPreviousSeqNo(ctx context.Context, ipnskey string) (uint64, error) { + prevrec, err := p.ds.Get(dshelp.NewKeyFromBinary(ipnskey)) + if err != nil && err != ds.ErrNotFound { + // None found, lets start at zero! + return 0, err + } + var val []byte + if err == nil { + prbytes, ok := prevrec.([]byte) + if !ok { + return 0, fmt.Errorf("unexpected type returned from datastore: %#v", prevrec) + } + dhtrec := new(dhtpb.Record) + err := proto.Unmarshal(prbytes, dhtrec) + if err != nil { + return 0, err + } + + val = dhtrec.GetValue() + } else { + // try and check the dht for a record + ctx, cancel := context.WithTimeout(ctx, time.Second*30) + defer cancel() + + rv, err := p.routing.GetValue(ctx, ipnskey) + if err != nil { + // no such record found, start at zero! + return 0, nil + } + + val = rv + } + + e := new(pb.IpnsEntry) + err = proto.Unmarshal(val, e) + if err != nil { + return 0, err + } + + return e.GetSequence(), nil +} + +// setting the TTL on published records is an experimental feature. +// as such, i'm using the context to wire it through to avoid changing too +// much code along the way. +func checkCtxTTL(ctx context.Context) (time.Duration, bool) { + v := ctx.Value("ipns-publish-ttl") + if v == nil { + return 0, false + } + + d, ok := v.(time.Duration) + return d, ok +} + +func PutRecordToRouting(ctx context.Context, k ci.PrivKey, value path.Path, seqnum uint64, eol time.Time, r routing.ValueStore, id peer.ID) error { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + namekey, ipnskey := IpnsKeysForID(id) + entry, err := CreateRoutingEntryData(k, value, seqnum, eol) + if err != nil { + return err + } + + ttl, ok := checkCtxTTL(ctx) + if ok { + entry.Ttl = proto.Uint64(uint64(ttl.Nanoseconds())) + } + + errs := make(chan error, 2) + + go func() { + errs <- PublishEntry(ctx, r, ipnskey, entry) + }() + + go func() { + errs <- PublishPublicKey(ctx, r, namekey, k.GetPublic()) + }() + + err = waitOnErrChan(ctx, errs) + if err != nil { + return err + } + + err = waitOnErrChan(ctx, errs) + if err != nil { + return err + } + + return nil +} + +func waitOnErrChan(ctx context.Context, errs chan error) error { + select { + case err := <-errs: + return err + case <-ctx.Done(): + return ctx.Err() + } +} + +func PublishPublicKey(ctx context.Context, r routing.ValueStore, k string, pubk ci.PubKey) error { + log.Debugf("Storing pubkey at: %s", k) + pkbytes, err := pubk.Bytes() + if err != nil { + return err + } + + // Store associated public key + timectx, cancel := context.WithTimeout(ctx, PublishPutValTimeout) + defer cancel() + err = r.PutValue(timectx, k, pkbytes) + if err != nil { + return err + } + + return nil +} + +func PublishEntry(ctx context.Context, r routing.ValueStore, ipnskey string, rec *pb.IpnsEntry) error { + timectx, cancel := context.WithTimeout(ctx, PublishPutValTimeout) + defer cancel() + + data, err := proto.Marshal(rec) + if err != nil { + return err + } + + log.Debugf("Storing ipns entry at: %s", ipnskey) + // Store ipns entry at "/ipns/"+b58(h(pubkey)) + if err := r.PutValue(timectx, ipnskey, data); err != nil { + return err + } + + return nil +} + +func CreateRoutingEntryData(pk ci.PrivKey, val path.Path, seq uint64, eol time.Time) (*pb.IpnsEntry, error) { + entry := new(pb.IpnsEntry) + + entry.Value = []byte(val) + typ := pb.IpnsEntry_EOL + entry.ValidityType = &typ + entry.Sequence = proto.Uint64(seq) + entry.Validity = []byte(u.FormatRFC3339(eol)) + + sig, err := pk.Sign(ipnsEntryDataForSig(entry)) + if err != nil { + return nil, err + } + entry.Signature = sig + return entry, nil +} + +func ipnsEntryDataForSig(e *pb.IpnsEntry) []byte { + return bytes.Join([][]byte{ + e.Value, + e.Validity, + []byte(fmt.Sprint(e.GetValidityType())), + }, + []byte{}) +} + +var IpnsRecordValidator = &record.ValidChecker{ + Func: ValidateIpnsRecord, + Sign: true, +} + +func IpnsSelectorFunc(k string, vals [][]byte) (int, error) { + var recs []*pb.IpnsEntry + for _, v := range vals { + e := new(pb.IpnsEntry) + err := proto.Unmarshal(v, e) + if err == nil { + recs = append(recs, e) + } else { + recs = append(recs, nil) + } + } + + return selectRecord(recs, vals) +} + +func selectRecord(recs []*pb.IpnsEntry, vals [][]byte) (int, error) { + var best_seq uint64 + best_i := -1 + + for i, r := range recs { + if r == nil || r.GetSequence() < best_seq { + continue + } + + if best_i == -1 || r.GetSequence() > best_seq { + best_seq = r.GetSequence() + best_i = i + } else if r.GetSequence() == best_seq { + rt, err := u.ParseRFC3339(string(r.GetValidity())) + if err != nil { + continue + } + + bestt, err := u.ParseRFC3339(string(recs[best_i].GetValidity())) + if err != nil { + continue + } + + if rt.After(bestt) { + best_i = i + } else if rt == bestt { + if bytes.Compare(vals[i], vals[best_i]) > 0 { + best_i = i + } + } + } + } + if best_i == -1 { + return 0, errors.New("no usable records in given set") + } + + return best_i, nil +} + +// ValidateIpnsRecord implements ValidatorFunc and verifies that the +// given 'val' is an IpnsEntry and that that entry is valid. +int ValidateIpnsRecord (char *k, char *val) +{ + int err; + +} +func ValidateIpnsRecord(k string, val []byte) error { + entry := new(pb.IpnsEntry) + err := proto.Unmarshal(val, entry) + if err != nil { + return err + } + switch entry.GetValidityType() { + case pb.IpnsEntry_EOL: + t, err := u.ParseRFC3339(string(entry.GetValidity())) + if err != nil { + log.Debug("failed parsing time for ipns record EOL") + return err + } + if time.Now().After(t) { + return ErrExpiredRecord + } + default: + return ErrUnrecognizedValidity + } + return nil +} + +// InitializeKeyspace sets the ipns record for the given key to +// point to an empty directory. +// TODO: this doesnt feel like it belongs here +int InitializeKeyspace (DAGService ds, Publisher pub, Pinner pins, ciPrivKey key) +{ + int err; + Node emptyDir; + Cid nodek; + + err = ipfs_merkledag_add(ds, nodek, emptyDir); + if (err) { + return err; + } + + // pin recursively because this might already be pinned + // and doing a direct pin would throw an error in that case + err = ipfs_pins_pin(emptyDir, TRUE); + if (err) { + return err; + } + + err = ipfs_pins_flush(); + if (err) { + return err; + } + + err = ipfs_pub_publish(key, PathFromCid(nodek)); + if (err) { + return err; + } + + return 0; +} + +int IpnsKeysForID (char **namekey, char **ipnskey, char *id) +{ + char namekey_prefix[] = "/pk/"; + char ipnskey_prefix[] = "/ipns/"; + int i, n; + + n = sizeof(namekey_prefix) + strlen(id); + *namekey = malloc(n); + if (!*namekey) { + return ErrAllocFailed; + } + + *ipnskey = malloc(i); + if (!*ipnskey) { + free (*namekey); + *namekey = NULL; + return ErrAllocFailed; + } + + namekey[--n] = '\0'; + strncpy (*namekey, namekey_prefix, n); + strncat (*namekey, id, n - strlen (namekey)); + + ipnskey[--i] = '\0'; + strncpy (*ipnskey, ipnskey_prefix, i); + strncat (*ipnskey, id, i - strlen (ipnskey)); + + return 0; +} diff --git a/namesys/routing.c b/namesys/routing.c index 3842300..924878b 100644 --- a/namesys/routing.c +++ b/namesys/routing.c @@ -13,15 +13,15 @@ char* ipfs_routing_cache_get (char *key, struct ipns_entry *ientry) { int i; struct routingResolver *cache; - struct stime now; + struct timespec now; if (key && ientry) { cache = ientry->cache; if (cache) { - get_gmttime (&now); + timespec_get (&now); for (i = 0 ; i < cache->next ; i++) { - if (((now.t < cache->data[i]->eol.t || - (now.t == cache->data[i]->eol.t && now.ts.tv_nsec < cache->data[i]->eol.ts.tv_nsec))) && + if (((now.tv_sec < cache->data[i]->eol.tv_sec || + (now.tv_sec == cache->data[i]->eol.tv_sec && now.tv_nsec < cache->data[i]->eol.tv_nsec))) && strcmp(cache->data[i]->key, key) == 0) { return cache->data[i]->value; } @@ -43,8 +43,8 @@ void ipfs_routing_cache_set (char *key, char *value, struct ipns_entry *ientry) if (n) { n->key = key; n->value = value; - get_gmttime (&n->eol); // now - n->eol.t += DefaultResolverCacheTTL; // sum TTL seconds to time seconds. + timespec_get (&n->eol); // now + n->eol.tv_sec += DefaultResolverCacheTTL; // sum TTL seconds to time seconds. cache->data[cache->next++] = n; } } @@ -152,7 +152,7 @@ int ipfs_namesys_routing_resolve_once (char **path, char *name, int depth, char } // check sig with pk - err = libp2p_crypto_verify (ipfs_ipns_entry_data_for_sig(pb->IpnsEntry), ipfs_ipns_entry_get_signature(pb->IpnsEntry), &ok); + err = libp2p_crypto_verify (ipns_entry_data_for_sig(pb->IpnsEntry), pb->IpnsEntry->signature, &ok); if (err || !ok) { char buf[500]; snprintf(buf, sizeof(buf), Err[ErrInvalidSignatureFmt], pubkey); @@ -198,12 +198,12 @@ int ipfs_namesys_routing_resolve_once (char **path, char *name, int depth, char return 0; } -int ipfs_namesys_routing_check_EOL (struct stime *st, struct namesys_pb *pb) +int ipfs_namesys_routing_check_EOL (struct timespec *ts, struct namesys_pb *pb) { int err; - if (ipfs_namesys_pb_get_validity_type (pb->IpnsEntry) == IpnsEntry_EOL) { - err = ipfs_util_time_parse_RFC3339 (st, ipfs_namesys_pb_get_validity (pb->IpnsEntry)); + if (*(pb->IpnsEntry->validityType) == IpnsEntry_EOL) { + err = ipfs_util_time_parse_RFC3339 (ts, pb->IpnsEntry->validity); if (!err) { return 1; } From 41b7579f21b9ccdb10dc407d2055fa6e43fa8335 Mon Sep 17 00:00:00 2001 From: Jose Marcial Vieira Bisneto Date: Tue, 20 Dec 2016 21:08:51 -0300 Subject: [PATCH 4/6] Some changes at namesys/pb --- include/ipfs/namesys/pb.h | 18 ++++++++++++------ namesys/pb.c | 22 ++++++++++++++++++++++ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/include/ipfs/namesys/pb.h b/include/ipfs/namesys/pb.h index 1d47ef6..82468f1 100644 --- a/include/ipfs/namesys/pb.h +++ b/include/ipfs/namesys/pb.h @@ -1,14 +1,19 @@ #ifndef IPNS_NAMESYS_PB_H #define IPNS_NAMESYS_PB_H + #include - typedef int IpnsEntry_ValidityType; + typedef int32_t IpnsEntry_ValidityType; struct ipns_entry { - // TODO - struct routingResolver *cache; - struct stime *eol; + 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; @@ -23,8 +28,9 @@ }; 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* ipfs_ipns_entry_data_for_sig(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*); diff --git a/namesys/pb.c b/namesys/pb.c index 4e7d633..a3bf863 100644 --- a/namesys/pb.c +++ b/namesys/pb.c @@ -1,4 +1,5 @@ #include +#include "ipfs/namesys/routing.h" #include "ipfs/namesys/pb.h" int IpnsEntry_ValidityType_value (char *s) @@ -17,3 +18,24 @@ int IpnsEntry_ValidityType_value (char *s) return -1; // not found. } + +struct ipns_entry* ipfs_namesys_pb_new_ipns_entry () +{ + return calloc(1, sizeof (struct ipns_entry)); +} + +void ipfs_namesys_ipnsentry_reset (struct ipns_entry *m) +{ + if (m) { + // ipns_entry is an struct of pointers, + // so we can access as an array of pointers. + char **a = (char **)m; + int i, l = (sizeof (struct ipns_entry) / sizeof (void*)) - 2; // avoid last 2 pointers, cache and eol. + for (i = 0 ; i < l ; i++) { + if (a[i]) { + free (a[i]); // free allocated pointers, + a[i] = NULL; // and mark as free. + } + } + } +} From 6b9d205ef2c3b09c4e0511d10303d83259fedcc6 Mon Sep 17 00:00:00 2001 From: Jose Marcial Vieira Bisneto Date: Wed, 21 Dec 2016 07:14:21 -0300 Subject: [PATCH 5/6] Some changes at util/time --- include/ipfs/util/time.h | 9 +++++++++ util/time.c | 10 ---------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/include/ipfs/util/time.h b/include/ipfs/util/time.h index b6eec48..ec95eb5 100644 --- a/include/ipfs/util/time.h +++ b/include/ipfs/util/time.h @@ -1,5 +1,14 @@ #ifndef IPFS_TIME_H #define IPFS_TIME_H + #ifndef __USE_XOPEN + #define __USE_XOPEN + #endif // __USE_XOPEN + + #ifndef __USE_ISOC11 + #define __USE_ISOC11 + #endif // __USE_ISOC11 + + #include int ipfs_util_time_parse_RFC3339 (struct timespec *ts, char *s); char *ipfs_util_time_format_RFC3339 (struct timespec *ts); diff --git a/util/time.c b/util/time.c index 68707fe..3eee8c3 100644 --- a/util/time.c +++ b/util/time.c @@ -1,16 +1,6 @@ #include #include #include - -#ifndef __USE_XOPEN - #define __USE_XOPEN -#endif // __USE_XOPEN - -#ifndef __USE_ISOC11 - #define __USE_ISOC11 -#endif // __USE_ISOC11 - -#include #include "ipfs/util/time.h" int ipfs_util_time_parse_RFC3339 (struct timespec *ts, char *s) From b17403b61acbea5cf87a55f560705e30be11a243 Mon Sep 17 00:00:00 2001 From: Jose Marcial Vieira Bisneto Date: Wed, 21 Dec 2016 07:21:40 -0300 Subject: [PATCH 6/6] Initial implementation of namesys/publisher --- include/ipfs/namesys/publisher.h | 9 + namesys/publisher.c | 457 ++++++++----------------------- 2 files changed, 119 insertions(+), 347 deletions(-) create mode 100644 include/ipfs/namesys/publisher.h diff --git a/include/ipfs/namesys/publisher.h b/include/ipfs/namesys/publisher.h new file mode 100644 index 0000000..b68afb0 --- /dev/null +++ b/include/ipfs/namesys/publisher.h @@ -0,0 +1,9 @@ +#ifndef IPFS_PUBLISHER_H + #define IPFS_PUBLISHER_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); +#endif // IPFS_PUBLISHER_H diff --git a/namesys/publisher.c b/namesys/publisher.c index a3566e0..1710b93 100644 --- a/namesys/publisher.c +++ b/namesys/publisher.c @@ -1,362 +1,125 @@ -var PublishPutValTimeout = time.Minute +#include +#include +#include "ipfs/errs.h" +#include "ipfs/util/time.h" +#include "ipfs/namesys/pb.h" +#include "ipfs/namesys/publisher.h" -// ipnsPublisher is capable of publishing and resolving names to the IPFS -// routing system. -type ipnsPublisher struct { - routing routing.ValueStore - ds ds.Datastore +char* ipns_entry_data_for_sig (struct ipns_entry *entry) +{ + char *ret; + + if (!entry || !entry->value || !entry->validity) { + return NULL; + } + ret = calloc (1, strlen(entry->value) + strlen (entry->validity) + sizeof(IpnsEntry_ValidityType) + 1); + if (ret) { + strcpy(ret, entry->value); + strcat(ret, entry->validity); + if (entry->validityType) { + memcpy(ret+strlen(entry->value)+strlen(entry->validity), entry->validityType, sizeof(IpnsEntry_ValidityType)); + } else { + memcpy(ret+strlen(entry->value)+strlen(entry->validity), &IpnsEntry_EOL, sizeof(IpnsEntry_ValidityType)); + } + } + return ret; } -// NewRoutingPublisher constructs a publisher for the IPFS Routing name system. -func NewRoutingPublisher(route routing.ValueStore, ds ds.Datastore) *ipnsPublisher { - if ds == nil { - panic("nil datastore") - } - return &ipnsPublisher{routing: route, ds: ds} +int ipns_selector_func (int *idx, struct ipns_entry ***recs, char *k, char **vals) +{ + int err, i, c; + + if (!idx || !recs || !k || !vals) { + return ErrInvalidParam; + } + + for (c = 0 ; vals[c] ; c++); // count array + + *recs = calloc(c+1, sizeof (void*)); // allocate return array. + if (!*recs) { + return ErrAllocFailed; + } + for (i = 0 ; i < c ; i++) { + *recs[i] = calloc(1, sizeof (struct ipns_entry)); // alloc every record + if (!*recs[i]) { + return ErrAllocFailed; + } + //err = proto.Unmarshal(vals[i], *recs[i]); // and decode. + if (err) { + ipfs_namesys_ipnsentry_reset (*recs[i]); // make sure record is empty. + } + } + return ipns_select_record(idx, *recs, vals); } -// Publish implements Publisher. Accepts a keypair and a value, -// and publishes it out to the routing system -func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Path) error { - log.Debugf("Publish %s", value) - return p.PublishWithEOL(ctx, k, value, time.Now().Add(time.Hour*24)) +int ipns_select_record (int *idx, struct ipns_entry **recs, char **vals) +{ + int err, i, best_i = -1, best_seq = 0; + struct timespec rt, bestt; + + if (!idx || !recs || !vals) { + return ErrInvalidParam; + } + + for (i = 0 ; recs[i] ; i++) { + if (!(recs[i]->sequence) || *(recs[i]->sequence) < best_seq) { + continue; + } + + if (best_i == -1 || *(recs[i]->sequence) > best_seq) { + best_seq = *(recs[i]->sequence); + best_i = i; + } else if (*(recs[i]->sequence) == best_seq) { + err = ipfs_util_time_parse_RFC3339 (&rt, ipfs_namesys_pb_get_validity (recs[i])); + if (err) { + continue; + } + err = ipfs_util_time_parse_RFC3339 (&bestt, ipfs_namesys_pb_get_validity (recs[best_i])); + if (err) { + continue; + } + if (rt.tv_sec > bestt.tv_sec || (rt.tv_sec == bestt.tv_sec && rt.tv_nsec > bestt.tv_nsec)) { + best_i = i; + } else if (rt.tv_sec == bestt.tv_sec && rt.tv_nsec == bestt.tv_nsec) { + if (memcmp(vals[i], vals[best_i], strlen(vals[best_i])) > 0) { // FIXME: strlen? + best_i = i; + } + } + } + } + if (best_i == -1) { + return ErrNoRecord; + } + *idx = best_i; + return 0; } -// PublishWithEOL is a temporary stand in for the ipns records implementation -// see here for more details: https://github.com/ipfs/specs/tree/master/records -func (p *ipnsPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) error { - - id, err := peer.IDFromPrivateKey(k) - if err != nil { - return err - } - - _, ipnskey := IpnsKeysForID(id) - - // get previous records sequence number - seqnum, err := p.getPreviousSeqNo(ctx, ipnskey) - if err != nil { - return err - } - - // increment it - seqnum++ - - return PutRecordToRouting(ctx, k, value, seqnum, eol, p.routing, id) -} - -func (p *ipnsPublisher) getPreviousSeqNo(ctx context.Context, ipnskey string) (uint64, error) { - prevrec, err := p.ds.Get(dshelp.NewKeyFromBinary(ipnskey)) - if err != nil && err != ds.ErrNotFound { - // None found, lets start at zero! - return 0, err - } - var val []byte - if err == nil { - prbytes, ok := prevrec.([]byte) - if !ok { - return 0, fmt.Errorf("unexpected type returned from datastore: %#v", prevrec) - } - dhtrec := new(dhtpb.Record) - err := proto.Unmarshal(prbytes, dhtrec) - if err != nil { - return 0, err - } - - val = dhtrec.GetValue() - } else { - // try and check the dht for a record - ctx, cancel := context.WithTimeout(ctx, time.Second*30) - defer cancel() - - rv, err := p.routing.GetValue(ctx, ipnskey) - if err != nil { - // no such record found, start at zero! - return 0, nil - } - - val = rv - } - - e := new(pb.IpnsEntry) - err = proto.Unmarshal(val, e) - if err != nil { - return 0, err - } - - return e.GetSequence(), nil -} - -// setting the TTL on published records is an experimental feature. -// as such, i'm using the context to wire it through to avoid changing too -// much code along the way. -func checkCtxTTL(ctx context.Context) (time.Duration, bool) { - v := ctx.Value("ipns-publish-ttl") - if v == nil { - return 0, false - } - - d, ok := v.(time.Duration) - return d, ok -} - -func PutRecordToRouting(ctx context.Context, k ci.PrivKey, value path.Path, seqnum uint64, eol time.Time, r routing.ValueStore, id peer.ID) error { - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - namekey, ipnskey := IpnsKeysForID(id) - entry, err := CreateRoutingEntryData(k, value, seqnum, eol) - if err != nil { - return err - } - - ttl, ok := checkCtxTTL(ctx) - if ok { - entry.Ttl = proto.Uint64(uint64(ttl.Nanoseconds())) - } - - errs := make(chan error, 2) - - go func() { - errs <- PublishEntry(ctx, r, ipnskey, entry) - }() - - go func() { - errs <- PublishPublicKey(ctx, r, namekey, k.GetPublic()) - }() - - err = waitOnErrChan(ctx, errs) - if err != nil { - return err - } - - err = waitOnErrChan(ctx, errs) - if err != nil { - return err - } - - return nil -} - -func waitOnErrChan(ctx context.Context, errs chan error) error { - select { - case err := <-errs: - return err - case <-ctx.Done(): - return ctx.Err() - } -} - -func PublishPublicKey(ctx context.Context, r routing.ValueStore, k string, pubk ci.PubKey) error { - log.Debugf("Storing pubkey at: %s", k) - pkbytes, err := pubk.Bytes() - if err != nil { - return err - } - - // Store associated public key - timectx, cancel := context.WithTimeout(ctx, PublishPutValTimeout) - defer cancel() - err = r.PutValue(timectx, k, pkbytes) - if err != nil { - return err - } - - return nil -} - -func PublishEntry(ctx context.Context, r routing.ValueStore, ipnskey string, rec *pb.IpnsEntry) error { - timectx, cancel := context.WithTimeout(ctx, PublishPutValTimeout) - defer cancel() - - data, err := proto.Marshal(rec) - if err != nil { - return err - } - - log.Debugf("Storing ipns entry at: %s", ipnskey) - // Store ipns entry at "/ipns/"+b58(h(pubkey)) - if err := r.PutValue(timectx, ipnskey, data); err != nil { - return err - } - - return nil -} - -func CreateRoutingEntryData(pk ci.PrivKey, val path.Path, seq uint64, eol time.Time) (*pb.IpnsEntry, error) { - entry := new(pb.IpnsEntry) - - entry.Value = []byte(val) - typ := pb.IpnsEntry_EOL - entry.ValidityType = &typ - entry.Sequence = proto.Uint64(seq) - entry.Validity = []byte(u.FormatRFC3339(eol)) - - sig, err := pk.Sign(ipnsEntryDataForSig(entry)) - if err != nil { - return nil, err - } - entry.Signature = sig - return entry, nil -} - -func ipnsEntryDataForSig(e *pb.IpnsEntry) []byte { - return bytes.Join([][]byte{ - e.Value, - e.Validity, - []byte(fmt.Sprint(e.GetValidityType())), - }, - []byte{}) -} - -var IpnsRecordValidator = &record.ValidChecker{ - Func: ValidateIpnsRecord, - Sign: true, -} - -func IpnsSelectorFunc(k string, vals [][]byte) (int, error) { - var recs []*pb.IpnsEntry - for _, v := range vals { - e := new(pb.IpnsEntry) - err := proto.Unmarshal(v, e) - if err == nil { - recs = append(recs, e) - } else { - recs = append(recs, nil) - } - } - - return selectRecord(recs, vals) -} - -func selectRecord(recs []*pb.IpnsEntry, vals [][]byte) (int, error) { - var best_seq uint64 - best_i := -1 - - for i, r := range recs { - if r == nil || r.GetSequence() < best_seq { - continue - } - - if best_i == -1 || r.GetSequence() > best_seq { - best_seq = r.GetSequence() - best_i = i - } else if r.GetSequence() == best_seq { - rt, err := u.ParseRFC3339(string(r.GetValidity())) - if err != nil { - continue - } - - bestt, err := u.ParseRFC3339(string(recs[best_i].GetValidity())) - if err != nil { - continue - } - - if rt.After(bestt) { - best_i = i - } else if rt == bestt { - if bytes.Compare(vals[i], vals[best_i]) > 0 { - best_i = i - } - } - } - } - if best_i == -1 { - return 0, errors.New("no usable records in given set") - } - - return best_i, nil -} - -// ValidateIpnsRecord implements ValidatorFunc and verifies that the +// ipns_validate_ipns_record implements ValidatorFunc and verifies that the // given 'val' is an IpnsEntry and that that entry is valid. -int ValidateIpnsRecord (char *k, char *val) +int ipns_validate_ipns_record (char *k, char *val) { int err; + struct ipns_entry *entry = ipfs_namesys_pb_new_ipns_entry(); + struct timespec ts, now; -} -func ValidateIpnsRecord(k string, val []byte) error { - entry := new(pb.IpnsEntry) - err := proto.Unmarshal(val, entry) - if err != nil { - return err - } - switch entry.GetValidityType() { - case pb.IpnsEntry_EOL: - t, err := u.ParseRFC3339(string(entry.GetValidity())) - if err != nil { - log.Debug("failed parsing time for ipns record EOL") - return err - } - if time.Now().After(t) { - return ErrExpiredRecord - } - default: - return ErrUnrecognizedValidity - } - return nil -} - -// InitializeKeyspace sets the ipns record for the given key to -// point to an empty directory. -// TODO: this doesnt feel like it belongs here -int InitializeKeyspace (DAGService ds, Publisher pub, Pinner pins, ciPrivKey key) -{ - int err; - Node emptyDir; - Cid nodek; - - err = ipfs_merkledag_add(ds, nodek, emptyDir); - if (err) { - return err; - } - - // pin recursively because this might already be pinned - // and doing a direct pin would throw an error in that case - err = ipfs_pins_pin(emptyDir, TRUE); - if (err) { - return err; - } - - err = ipfs_pins_flush(); - if (err) { - return err; - } - - err = ipfs_pub_publish(key, PathFromCid(nodek)); - if (err) { - return err; - } - - return 0; -} - -int IpnsKeysForID (char **namekey, char **ipnskey, char *id) -{ - char namekey_prefix[] = "/pk/"; - char ipnskey_prefix[] = "/ipns/"; - int i, n; - - n = sizeof(namekey_prefix) + strlen(id); - *namekey = malloc(n); - if (!*namekey) { + if (!entry) { return ErrAllocFailed; } - - *ipnskey = malloc(i); - if (!*ipnskey) { - free (*namekey); - *namekey = NULL; - return ErrAllocFailed; + //err = proto.Unmarshal(val, entry); + if (err) { + return err; + } + if (ipfs_namesys_pb_get_validity_type (entry) == IpnsEntry_EOL) { + err = ipfs_util_time_parse_RFC3339 (&ts, ipfs_namesys_pb_get_validity (entry)); + if (err) { + //log.Debug("failed parsing time for ipns record EOL") + return err; + } + timespec_get (&now, TIME_UTC); + if (now.tv_nsec > ts.tv_nsec || (now.tv_nsec == ts.tv_nsec && now.tv_nsec > ts.tv_nsec)) { + return ErrExpiredRecord; + } + } else { + return ErrUnrecognizedValidity; } - - namekey[--n] = '\0'; - strncpy (*namekey, namekey_prefix, n); - strncat (*namekey, id, n - strlen (namekey)); - - ipnskey[--i] = '\0'; - strncpy (*ipnskey, ipnskey_prefix, i); - strncat (*ipnskey, id, i - strlen (ipnskey)); - - return 0; }