gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnunet-go] branch master updated: Started recursive resolution; GNS2DNS


From: gnunet
Subject: [gnunet-go] branch master updated: Started recursive resolution; GNS2DNS implemented.
Date: Tue, 12 Nov 2019 12:35:32 +0100

This is an automated email from the git hooks/post-receive script.

bernd-fix pushed a commit to branch master
in repository gnunet-go.

The following commit(s) were added to refs/heads/master by this push:
     new fef7570  Started recursive resolution; GNS2DNS implemented.
fef7570 is described below

commit fef7570713f8ec064b7689f28f201deb0b5030d4
Author: Bernd Fix <address@hidden>
AuthorDate: Tue Nov 12 12:31:44 2019 +0100

    Started recursive resolution; GNS2DNS implemented.
---
 README.md                              |  34 +++-
 src/cmd/vanityid/main.go               |  11 +-
 src/gnunet/config/config.go            |  19 +-
 src/gnunet/config/gnunet-config.json   |   5 +-
 src/gnunet/crypto/symmetric.go         |  20 ++
 src/gnunet/modules.go                  |  40 ++++
 src/gnunet/service/gns/crypto.go       |  27 +--
 src/gnunet/service/gns/dns.go          | 198 +++++++++++++++++++
 src/gnunet/service/gns/module.go       | 342 +++++++++++++++++++++++++++++++++
 src/gnunet/service/gns/record.go       |   5 +
 src/gnunet/service/gns/service.go      | 257 +++++++++++++++++++++++++
 src/gnunet/service/namecache/module.go |  25 +++
 src/gnunet/util/array.go               |  39 ++++
 src/gnunet/util/time.go                |  17 ++
 test.sh                                |   3 +
 15 files changed, 1015 insertions(+), 27 deletions(-)

diff --git a/README.md b/README.md
index d0f1b9c..1875501 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,7 @@ DOCUMENTATION OR COMPILABLE, RUNNABLE OR EVEN OPERATIONAL 
SOURCE CODE.
 
 ## Source code
 
-All source code is written in Golang (version 1.11+).
+All source code is written in Golang (version 1.13+).
 
 ### Dependencies
 
@@ -38,19 +38,43 @@ $ go get -u github.com/bfix/gospel/...
 
 ### ./src/cmd folder
 
-* `vanityid`: Compute GNUnet vanity peer id for a given regexp pattern.
+
+#### `gnunet-service-gns-go`: Implementation of the GNS service.
+
+#### `peer_mockup`: test message exchange on the lowest level (transport).
+
+#### `vanityid`: Compute GNUnet vanity peer and ego id for a given regexp 
pattern.
+
+N.B.: Key generation is slow at the moment, so be patient! To generate a single
+matching key some 1,000,000 keys need to be generated for a four letter prefix;
+this can take more than 30 minutes on average (depending on your CPU).
 
 ```bash
 $ vanityid "^TST[0-9]"
 ```
 
-* `gnunet-service-gns-go`: Implementation of the GNS service.
+Keys matching the pattern are printed to the console in the following format:
 
-* `peer_mockup`: test message exchange on the lowest level (transport).
+```bash
+<vanity_id> [<hex.seed>][<hex.scalar>] (<count> tries, <time> elapsed)
+```
+The value of `count` tells how many key had been generated before a match was
+found; `time` is the time needed to find a match.
+
+To generate the key files, make sure GNUnet **is not running** and do: 
 
+```bash
+$ # use a vanity peer id:
+$ echo "<hex.seed>" | xxd -r -p > 
/var/lib/gnunet/.local/share/gnunet/private_key.ecc
+$ sudo chown gnunet:gnunet /var/lib/gnunet/.local/share/gnunet/private_key.ecc
+$ sudo chmod 600 /var/lib/gnunet/.local/share/gnunet/private_key.ecc
+$ # use a vanity ego id:
+$ echo "<hex.scalar>" | xxd -r -p > 
~/.local/share/gnunet/identity/egos/<vanity_ego>
+$ chmod 600 ~/.local/share/gnunet/identity/egos/<vanity_ego>
+```
 ### ./src/gnunet folder
 
-Packages used to implement GNUnet protocols (currently only TRANSPORT
+Packages used to implement GNUnet protocols (currently only some of TRANSPORT
 and GNS).
 
 ## Documentation
diff --git a/src/cmd/vanityid/main.go b/src/cmd/vanityid/main.go
index 0b9fb18..938df61 100644
--- a/src/cmd/vanityid/main.go
+++ b/src/cmd/vanityid/main.go
@@ -6,6 +6,7 @@ import (
        "flag"
        "fmt"
        "regexp"
+       "time"
 
        "github.com/bfix/gospel/crypto/ed25519"
        "gnunet/util"
@@ -29,7 +30,8 @@ func main() {
 
        // generate new keys in a loop
        seed := make([]byte, 32)
-       for {
+       start := time.Now()
+       for i := 0; ; i++ {
                n, err := rand.Read(seed)
                if err != nil || n != 32 {
                        panic(err)
@@ -39,7 +41,12 @@ func main() {
                id := util.EncodeBinaryToString(pub)
                for _, r := range reg {
                        if r.MatchString(id) {
-                               fmt.Printf("%s [%s]\n", id, 
hex.EncodeToString(seed))
+                               elapsed := time.Now().Sub(start)
+                               s1 := hex.EncodeToString(seed)
+                               s2 := hex.EncodeToString(prv.D.Bytes())
+                               fmt.Printf("%s [%s][%s] (%d tries, %s 
elapsed)\n", id, s1, s2, i, elapsed)
+                               i = 0
+                               start = time.Now()
                        }
                }
        }
diff --git a/src/gnunet/config/config.go b/src/gnunet/config/config.go
index 74732ab..1a53941 100644
--- a/src/gnunet/config/config.go
+++ b/src/gnunet/config/config.go
@@ -7,7 +7,9 @@ import (
        "regexp"
        "strings"
 
+       "github.com/bfix/gospel/crypto/ed25519"
        "github.com/bfix/gospel/logger"
+       "gnunet/util"
 )
 
 ///////////////////////////////////////////////////////////////////////
@@ -15,8 +17,21 @@ import (
 
 // GNSConfig
 type GNSConfig struct {
-       Endpoint     string `json:"endpoint"`     // end-point of GNS service
-       DHTReplLevel int    `json:"dhtReplLevel"` // DHT replication level
+       Endpoint     string            `json:"endpoint"`     // end-point of 
GNS service
+       DHTReplLevel int               `json:"dhtReplLevel"` // DHT replication 
level
+       RootZones    map[string]string `json:"rootZones"`    // pre-configured 
root zones
+}
+
+// GetRootZoneKey returns the zone key (PKEY) for a pre-configured root with 
given name.
+func (gns *GNSConfig) GetRootZoneKey(name string) *ed25519.PublicKey {
+       // lookup key in the dictionary
+       if dStr, ok := gns.RootZones[name]; ok {
+               if data, err := util.DecodeStringToBinary(dStr, 32); err == nil 
{
+                       return ed25519.NewPublicKeyFromBytes(data)
+               }
+       }
+       // no pkey found.
+       return nil
 }
 
 ///////////////////////////////////////////////////////////////////////
diff --git a/src/gnunet/config/gnunet-config.json 
b/src/gnunet/config/gnunet-config.json
index 7ea854c..13bbe7c 100644
--- a/src/gnunet/config/gnunet-config.json
+++ b/src/gnunet/config/gnunet-config.json
@@ -8,7 +8,10 @@
        },
        "gns": {
                "endpoint": 
"unix+${RT_SYS}/gnunet-service-gns-go.sock+perm=0770",
-               "dhtReplLevel": 10
+               "dhtReplLevel": 10,
+               "rootZones": {
+                       "home": 
"ACAB23DC3SEECJORPHQNVRH965A6N74B1M37S721IG4RBQ15PJLL"
+               }
        },
        "namecache": {
                "endpoint": "unix+${RT_SYS}/gnunet-service-namecache.sock"
diff --git a/src/gnunet/crypto/symmetric.go b/src/gnunet/crypto/symmetric.go
index 5fb2c72..82116a4 100644
--- a/src/gnunet/crypto/symmetric.go
+++ b/src/gnunet/crypto/symmetric.go
@@ -57,3 +57,23 @@ func SymmetricDecrypt(data []byte, skey *SymmetricKey, iv 
*SymmetricIV) ([]byte,
        stream.XORKeyStream(out, out)
        return out, nil
 }
+
+func SymmetricEncrypt(data []byte, skey *SymmetricKey, iv *SymmetricIV) 
([]byte, error) {
+       // Encrypt with AES CFB stream cipher
+       aes, err := aes.NewCipher(skey.AESKey)
+       if err != nil {
+               return nil, err
+       }
+       stream := cipher.NewCFBEncrypter(aes, iv.AESIv)
+       out := make([]byte, len(data))
+       stream.XORKeyStream(out, data)
+
+       // Encrypt with Twofish CFB stream cipher
+       tf, err := twofish.NewCipher(skey.TwofishKey)
+       if err != nil {
+               return nil, err
+       }
+       stream = cipher.NewCFBEncrypter(tf, iv.TwofishIv)
+       stream.XORKeyStream(out, out)
+       return out, nil
+}
diff --git a/src/gnunet/modules.go b/src/gnunet/modules.go
new file mode 100644
index 0000000..bbb2563
--- /dev/null
+++ b/src/gnunet/modules.go
@@ -0,0 +1,40 @@
+//======================================================================
+// Standalone (all-in-one) implementation of GNUnet:
+// -------------------------------------------------
+// Instead of running GNUnet services like GNS or DHT in separate
+// processes communicating (exchanging messages) with each other over
+// Unix Domain Sockets, the standalone implementation combines all
+// service modules into a single binary running go-routines to
+// concurrently performing their tasks.
+//======================================================================
+
+package gnunet
+
+import (
+       "gnunet/service/gns"
+       "gnunet/service/namecache"
+)
+
+// List of all GNUnet service module instances
+type Instances struct {
+       GNS       *gns.GNSModule
+       Namecache *namecache.NamecacheModule
+}
+
+// Local reference to instance list
+var (
+       Modules Instances
+)
+
+// Initialize instance list and link module functions as required.
+func init() {
+
+       // Namecache (no calls to other modules)
+       Modules.Namecache = new(namecache.NamecacheModule)
+
+       // GNS (calls Namecache, DHT and Identity)
+       Modules.GNS = &gns.GNSModule{
+               LookupLocal: Modules.Namecache.Get,
+               StoreLocal:  Modules.Namecache.Put,
+       }
+}
diff --git a/src/gnunet/service/gns/crypto.go b/src/gnunet/service/gns/crypto.go
index f7ef111..38f25fb 100644
--- a/src/gnunet/service/gns/crypto.go
+++ b/src/gnunet/service/gns/crypto.go
@@ -9,23 +9,8 @@ import (
        "golang.org/x/crypto/hkdf"
 )
 
-// QueryFromPublickeyDerive calculates the DHT query for a given label in a
-// given zone (identified by PKEY).
-func QueryFromPublickeyDerive(pkey *ed25519.PublicKey, label string) 
*crypto.HashCode {
-       pd := crypto.DerivePublicKey(pkey, label, "gns")
-       return crypto.Hash(pd.Bytes())
-}
-
-// DecryptBlock
-func DecryptBlock(data []byte, zoneKey *ed25519.PublicKey, label string) (out 
[]byte, err error) {
-       // derive key material for decryption
-       iv, skey := deriveBlockKey(label, zoneKey)
-       // perform decryption
-       return crypto.SymmetricDecrypt(data, skey, iv)
-}
-
-// Derive a symmetric key to decipher a GNS block
-func deriveBlockKey(label string, pub *ed25519.PublicKey) (iv 
*crypto.SymmetricIV, skey *crypto.SymmetricKey) {
+// DeriveBlockKey returns a symmetric key to decipher a GNS block
+func DeriveBlockKey(label string, pub *ed25519.PublicKey) (iv 
*crypto.SymmetricIV, skey *crypto.SymmetricKey) {
        // generate symmetric key
        prk := hkdf.Extract(sha512.New, []byte(label), pub.Bytes())
        rdr := hkdf.Expand(sha256.New, prk, []byte("gns-aes-ctx-key"))
@@ -39,3 +24,11 @@ func deriveBlockKey(label string, pub *ed25519.PublicKey) 
(iv *crypto.SymmetricI
        rdr.Read(iv.TwofishIv)
        return
 }
+
+// DecryptBlock for a given zone and label.
+func DecryptBlock(data []byte, zoneKey *ed25519.PublicKey, label string) (out 
[]byte, err error) {
+       // derive key material for decryption
+       iv, skey := DeriveBlockKey(label, zoneKey)
+       // perform decryption
+       return crypto.SymmetricDecrypt(data, skey, iv)
+}
diff --git a/src/gnunet/service/gns/dns.go b/src/gnunet/service/gns/dns.go
new file mode 100644
index 0000000..2ebe331
--- /dev/null
+++ b/src/gnunet/service/gns/dns.go
@@ -0,0 +1,198 @@
+package gns
+
+import (
+       "fmt"
+       "net"
+       "strings"
+       "time"
+
+       "gnunet/enums"
+       "gnunet/message"
+       "gnunet/util"
+
+       "github.com/bfix/gospel/crypto/ed25519"
+       "github.com/bfix/gospel/logger"
+       "github.com/miekg/dns"
+)
+
+// Error codes
+var (
+       ErrDNSTimedOut  = fmt.Errorf("DNS query timed out")
+       ErrNoDNSQueries = fmt.Errorf("No valid DNS queries")
+       ErrNoDNSResults = fmt.Errorf("No valid DNS results")
+)
+
+// Convert DNS name from its binary representation [RFC1034]:
+// A string is a sequence of a (len,chars...) tupels terminated by a (len=0,).
+// The name parts are concatenated with "." as separator.
+// The parsing starts at offset in the byte array; the function returns the
+// offset after the parsed name as well as the name itself.
+func DNSNameFromBytes(b []byte, offset int) (int, string) {
+       if offset >= len(b) {
+               return offset, ""
+       }
+       str := ""
+       pos := offset
+       for b[pos] != 0 {
+               if len(str) > 0 {
+                       str += "."
+               }
+               count := int(b[pos])
+               pos++
+               str += string(b[pos : pos+count])
+               pos += count
+       }
+       return pos + 1, str
+}
+
+// queryDNS resolves a name on a given nameserver and delivers all matching
+// resource record (of type 'kind') to the result channel.
+func queryDNS(id int, name string, server net.IP, kind int, res chan 
*GNSRecordSet) {
+       logger.Printf(logger.DBG, "[dns][%d] Starting query for '%s' on 
'%s'...\n", id, name, server.String())
+
+       // assemble query
+       m := &dns.Msg{
+               MsgHdr: dns.MsgHdr{
+                       Authoritative:     true,
+                       AuthenticatedData: false,
+                       CheckingDisabled:  false,
+                       RecursionDesired:  true,
+                       Opcode:            dns.OpcodeQuery,
+               },
+               Question: make([]dns.Question, 1),
+       }
+       m.Question[0] = dns.Question{
+               dns.Fqdn(name),
+               dns.TypeANY,
+               dns.ClassINET,
+       }
+
+       // perform query in retry-loop
+       for retry := 0; retry < 5; retry++ {
+               // send query with new ID when retrying
+               m.Id = dns.Id()
+               in, err := dns.Exchange(m, net.JoinHostPort(server.String(), 
"53"))
+               // handle DNS fails
+               if err != nil {
+                       errMsg := err.Error()
+                       if strings.HasSuffix(errMsg, "i/o timeout") {
+                               logger.Printf(logger.WARN, "[dns][%d] Query 
timed-out -- retrying (%d/5)\n", id, retry+1)
+                               continue
+                       }
+                       logger.Printf(logger.ERROR, "[dns][%d] Error: %s\n", 
id, errMsg)
+                       res <- nil
+               }
+               // process results
+               logger.Printf(logger.WARN, "[dns][%d] Response from DNS server 
received (%d/5).\n", id, retry+1)
+               if in == nil {
+                       logger.Printf(logger.ERROR, "[dns][%d] No results\n", 
id)
+                       res <- nil
+                       return
+               }
+               set := NewGNSRecordSet()
+               for _, record := range in.Answer {
+                       // create a new GNS resource record
+                       rr := new(message.GNSResourceRecord)
+                       rr.Expires = util.AbsoluteTimeNever()
+                       rr.Flags = 0
+                       rr.Type = uint32(record.Header().Rrtype)
+                       rr.Size = uint32(record.Header().Rdlength)
+                       rr.Data = make([]byte, rr.Size)
+
+                       // get wire-format of resource record
+                       buf := make([]byte, 2048)
+                       n, err := dns.PackRR(record, buf, 0, nil, false)
+                       if err != nil {
+                               logger.Printf(logger.WARN, "[dns][%d] Failed to 
get RR data for %s\n", id, err.Error())
+                               continue
+                       }
+                       if n < int(rr.Size) {
+                               logger.Printf(logger.WARN, "[dns][%d] Nit 
enough data in RR (%d != %d)\n", id, n, rr.Size)
+                               continue
+                       }
+                       copy(rr.Data, buf[n-int(rr.Size):])
+                       set.AddRecord(rr)
+               }
+               logger.Printf(logger.WARN, "[dns][%d] %d resource records 
extracted from response (%d/5).\n", id, set.Count, retry+1)
+               res <- set
+               return
+       }
+       logger.Printf(logger.WARN, "[dns][%d] Resolution failed -- giving 
up...\n", id)
+       res <- nil
+}
+
+// ResolveDNS resolves a name in DNS. Multiple DNS servers are queried in
+// parallel; the first result delivered by any of the servers is returned
+// as the result list of matching resource records.
+func (gns *GNSModule) ResolveDNS(name string, servers []string, kind int, pkey 
*ed25519.PublicKey) (set *GNSRecordSet, err error) {
+       logger.Printf(logger.DBG, "[dns] Resolution of '%s' starting...\n", 
name)
+
+       // start DNS queries concurrently
+       res := make(chan *GNSRecordSet)
+       running := 0
+       for idx, srv := range servers {
+               // check if srv is an IPv4/IPv6 address
+               addr := net.ParseIP(srv)
+               if addr == nil {
+                       // no; resolve server name in GNS
+                       if strings.HasSuffix(srv, ".+") {
+                               // resolve server name relative to current zone
+                               zone := util.EncodeBinaryToString(pkey.Bytes())
+                               srv = strings.TrimSuffix(srv, ".+")
+                               set, err = gns.Resolve(srv, pkey, 
enums.GNS_TYPE_ANY, enums.GNS_LO_DEFAULT)
+                               if err != nil {
+                                       logger.Printf(logger.ERROR, "[dns] 
Can't resolve NS server '%s' in '%s'\n", srv, zone)
+                                       continue
+                               }
+                       } else {
+                               // resolve absolute GNS name (MUST end in a 
PKEY)
+                               set, err = gns.Resolve(srv, nil, 
enums.GNS_TYPE_ANY, enums.GNS_LO_DEFAULT)
+                               if err != nil {
+                                       logger.Printf(logger.ERROR, "[dns] 
Can't resolve NS server '%s'\n", srv)
+                                       continue
+                               }
+                       }
+                       // traverse resource records for 'A' and 'AAAA' records.
+               rec_loop:
+                       for _, rec := range set.Records {
+                               switch int(rec.Type) {
+                               case enums.GNS_TYPE_DNS_AAAA:
+                                       addr = net.IP(rec.Data)
+                                       break rec_loop
+                               case enums.GNS_TYPE_DNS_A:
+                                       addr = net.IP(rec.Data)
+                               }
+                       }
+               }
+               // query DNS concurrently
+               go queryDNS(idx, name, addr, kind, res)
+               running++
+       }
+       // check if we started some queries at all.
+       if running == 0 {
+               return nil, ErrNoDNSQueries
+       }
+       // wait for query results
+       timeout := time.Tick(10 * time.Second)
+       for {
+               select {
+               case set = <-res:
+                       running--
+                       if set != nil {
+                               // we have a result.
+                               logger.Println(logger.DBG, "[dns] Query result 
available.")
+                               return
+                       }
+                       if running == 0 {
+                               // no results
+                               logger.Println(logger.WARN, "[dns] No results 
received from queries.")
+                               return nil, ErrNoDNSResults
+                       }
+
+               case <-timeout:
+                       // no results
+                       logger.Println(logger.WARN, "[dns] Queries timed out.")
+                       return nil, ErrNoDNSResults
+               }
+       }
+}
diff --git a/src/gnunet/service/gns/module.go b/src/gnunet/service/gns/module.go
new file mode 100644
index 0000000..e4dc5fb
--- /dev/null
+++ b/src/gnunet/service/gns/module.go
@@ -0,0 +1,342 @@
+package gns
+
+import (
+       "encoding/hex"
+       "fmt"
+       "strings"
+
+       "gnunet/config"
+       "gnunet/crypto"
+       "gnunet/enums"
+       "gnunet/message"
+       "gnunet/util"
+
+       "github.com/bfix/gospel/crypto/ed25519"
+       "github.com/bfix/gospel/logger"
+)
+
+//======================================================================
+// "GNUnet Name System" implementation
+//======================================================================
+
+// Error codes
+var (
+       ErrUnknownTLD        = fmt.Errorf("Unknown TLD in name")
+       ErrInvalidRecordType = fmt.Errorf("Invalid resource record type")
+       ErrInvalidRecordBody = fmt.Errorf("Invalid resource record body")
+       ErrInvalidPKEY       = fmt.Errorf("Invalid PKEY resource record")
+)
+
+//----------------------------------------------------------------------
+// Query for simple GNS lookups
+//----------------------------------------------------------------------
+
+// Query specifies the context for a basic GNS name lookup of an (atomic)
+// label in a given zone identified by its public key.
+type Query struct {
+       Zone    *ed25519.PublicKey // Public zone key
+       Label   string             // Atomic label
+       Derived *ed25519.PublicKey // Derived key from (pkey,label)
+       Key     *crypto.HashCode   // Key for repository queries (local/remote)
+}
+
+// NewQuery assembles a new Query object for the given zone and label.
+func NewQuery(pkey *ed25519.PublicKey, label string) *Query {
+       // derive a public key from (pkey,label) and set the repository
+       // key as the SHA512 hash of the binary key representation.
+       pd := crypto.DerivePublicKey(pkey, label, "gns")
+       key := crypto.Hash(pd.Bytes())
+       return &Query{
+               Zone:    pkey,
+               Label:   label,
+               Derived: pd,
+               Key:     key,
+       }
+}
+
+//----------------------------------------------------------------------
+// GNS blocks with special types (PKEY, GNS2DNS) require special
+// treatment with respect to other resource records with different types
+// in the same block. Usually only certain other types (or not at all)
+// are allowed and the allowed ones are required to deliver a consistent
+// list of resulting resource records passed back to the caller.
+//----------------------------------------------------------------------
+
+// BlockHandler interface.
+type BlockHandler interface {
+       // TypeAction returns a flag indicating how a resource record of a
+       // given type is to be treated:
+       //   = -1: Record is not allowed (terminates lookup with an error)
+       //   =  0: Record is allowed but will be ignored
+       //   =  1: Record is allowed and will be processed
+       TypeAction(int) int
+}
+
+// Gns2DnsHandler implementing the BlockHandler interface
+type Gns2DnsHandler struct {
+       Name    string
+       Servers []string
+}
+
+// NewGns2DnsHandler returns a new BlockHandler instance
+func NewGns2DnsHandler() *Gns2DnsHandler {
+       return &Gns2DnsHandler{
+               Name:    "",
+               Servers: make([]string, 0),
+       }
+}
+
+// TypeAction return a flag indicating how a resource record of a given type
+// is to be treated (see RecordMaster interface)
+func (m *Gns2DnsHandler) TypeAction(t int) int {
+       // only process other GNS2DNS records
+       if t == enums.GNS_TYPE_GNS2DNS {
+               return 1
+       }
+       // skip everything else
+       return 0
+}
+
+// AddRequest adds the DNS request for "name" at "server" to the list
+// of requests. All GNS2DNS records must query for the same name
+func (m *Gns2DnsHandler) AddRequest(name, server string) bool {
+       if len(m.Servers) == 0 {
+               m.Name = name
+       }
+       if name != m.Name {
+               return false
+       }
+       m.Servers = append(m.Servers, server)
+       return true
+}
+
+//----------------------------------------------------------------------
+// The GNS module (recursively) resolves GNS names:
+// Resolves DNS-like names (e.g. "minecraft.servers.bob.games") to the
+// requested resource records (RRs). In short, the resolution process
+// works as follows:
+//
+//  Resolve(name):
+//  --------------
+//  (1) split the full name into elements in reverse order: names[]
+//  (2) Resolve first element (root zone, right-most name part, name[0]) to
+//      a zone public key PKEY:
+//      (a) the name is a string representation of a public key -> (3)
+//      (b) the zone key for the name is stored in the config file -> (3)
+//      (c) a local zone with that given name -> (3)
+//      (d) ERROR: "Unknown root zone"
+//  (3) names = names[1:] // remove first element
+//      block = Lookup (PKEY, names[0]):
+//      (a) If last element of namess: -> (4)
+//      (b) block is PKEY record:
+//          PKEY <- block, --> (3)
+//  (4) return block: it is the responsibility of the caller to assemble
+//      the desired result from block data (e.g. filter for requested
+//      resource record types).
+//----------------------------------------------------------------------
+
+// GNSModule handles the resolution of GNS names to RRs bundled in a block.
+type GNSModule struct {
+       // Use function references for calls to methods in other modules:
+       //
+       LookupLocal  func(query *Query) (*GNSBlock, error)
+       StoreLocal   func(query *Query, block *GNSBlock) error
+       LookupRemote func(query *Query) (*GNSBlock, error)
+       GetLocalZone func(name string) (*ed25519.PublicKey, error)
+}
+
+// Resolve a GNS name with multiple elements, If pkey is not nil, the name
+// is interpreted as "relative to current zone".
+func (gns *GNSModule) Resolve(path string, pkey *ed25519.PublicKey, kind int, 
mode int) (set *GNSRecordSet, err error) {
+       // get the name elements in reverse order
+       names := util.ReverseStringList(strings.Split(path, "."))
+       logger.Printf(logger.DBG, "[gns] Resolver called for %v\n", names)
+
+       // check for relative path
+       if pkey != nil {
+               //resolve relative path
+               return gns.ResolveRelative(names, pkey, kind, mode)
+       }
+       // resolve absolute path
+       return gns.ResolveAbsolute(names, kind, mode)
+}
+
+// Resolve a fully qualified GNS absolute name (with multiple levels).
+func (gns *GNSModule) ResolveAbsolute(names []string, kind int, mode int) (set 
*GNSRecordSet, err error) {
+       // get the root zone key for the TLD
+       var (
+               pkey *ed25519.PublicKey
+               data []byte
+       )
+       for {
+               // (1) check if TLD is a public key string
+               if len(names[0]) == 52 {
+                       if data, err = util.DecodeStringToBinary(names[0], 32); 
err == nil {
+                               if pkey = ed25519.NewPublicKeyFromBytes(data); 
pkey != nil {
+                                       break
+                               }
+                       }
+               }
+               // (2) check if TLD is in our local config
+               if pkey = config.Cfg.GNS.GetRootZoneKey(names[0]); pkey != nil {
+                       break
+               }
+               // (3) check if TLD is one of our identities
+               if pkey, err = gns.GetLocalZone(names[0]); err == nil {
+                       break
+               }
+               // (4) we can't resolve this TLD
+               return nil, ErrUnknownTLD
+       }
+       // continue with resolution relative to a zone.
+       return gns.ResolveRelative(names[1:], pkey, kind, mode)
+}
+
+// Resolve relative path (to a given zone) recursively by processing simple
+// (PKEY,Label) lookups in sequence and handle intermediate GNS record types
+func (gns *GNSModule) ResolveRelative(names []string, pkey *ed25519.PublicKey, 
kind int, mode int) (set *GNSRecordSet, err error) {
+       // Process all names in sequence
+       var records []*message.GNSResourceRecord
+name_loop:
+       for ; len(names) > 0; names = names[1:] {
+               logger.Printf(logger.DBG, "[gns] ResolveRelative '%s' in 
'%s'\n", names[0], util.EncodeBinaryToString(pkey.Bytes()))
+
+               // resolve next level
+               var block *GNSBlock
+               if block, err = gns.Lookup(pkey, names[0], mode == 
enums.GNS_LO_DEFAULT); err != nil {
+                       // failed to resolve name
+                       return
+               }
+               // set new mode after processing right-most label in 
LOCAL_MASTER mode
+               if mode == enums.GNS_LO_LOCAL_MASTER {
+                       mode = enums.GNS_LO_DEFAULT
+               }
+               // post-process block by inspecting contained resource records 
for
+               // special GNS types
+               var hdlr BlockHandler
+               if records, err = block.Records(); err != nil {
+                       return
+               }
+               for _, rec := range records {
+                       // let a block handler decide how to handle records
+                       if hdlr != nil {
+                               switch hdlr.TypeAction(int(rec.Type)) {
+                               case -1:
+                                       // No records of this type allowed in 
block
+                                       err = ErrInvalidRecordType
+                                       return
+                               case 0:
+                                       // records of this type are simply 
ignored
+                                       continue
+                               case 1:
+                                       // process record of this type
+                               }
+                       }
+                       switch int(rec.Type) {
+                       
//----------------------------------------------------------
+                       case enums.GNS_TYPE_PKEY:
+                               // check for single RR and sane key data
+                               if len(rec.Data) != 32 || len(records) > 1 {
+                                       err = ErrInvalidPKEY
+                                       return
+                               }
+                               // set new PKEY and continue resolution
+                               pkey = ed25519.NewPublicKeyFromBytes(rec.Data)
+                               continue name_loop
+
+                       
//----------------------------------------------------------
+                       case enums.GNS_TYPE_GNS2DNS:
+                               // get the master controlling this block; 
create a new
+                               // one if necessary
+                               var inst *Gns2DnsHandler
+                               if hdlr == nil {
+                                       inst = NewGns2DnsHandler()
+                                       hdlr = inst
+                               } else {
+                                       inst = hdlr.(*Gns2DnsHandler)
+                               }
+                               // extract list of names in DATA block:
+                               logger.Printf(logger.DBG, "[gns] GNS2DNS data: 
%s\n", hex.EncodeToString(rec.Data))
+                               var dnsNames []string
+                               for pos := 0; ; {
+                                       next, name := 
DNSNameFromBytes(rec.Data, pos)
+                                       if len(name) == 0 {
+                                               break
+                                       }
+                                       dnsNames = append(dnsNames, name)
+                                       pos = next
+                               }
+                               logger.Printf(logger.DBG, "[gns] GNS2DNS 
params: %v\n", dnsNames)
+                               if len(dnsNames) != 2 {
+                                       err = ErrInvalidRecordBody
+                                       return
+                               }
+                               // Add to collection of requests
+                               logger.Printf(logger.DBG, "[gns] GNS2DNS: query 
for '%s' on '%s'\n", dnsNames[0], dnsNames[1])
+                               if !inst.AddRequest(dnsNames[0], dnsNames[1]) {
+                                       err = ErrInvalidRecordBody
+                                       return
+                               }
+                       }
+               }
+               // handle special block cases
+               if hdlr != nil {
+                       switch inst := hdlr.(type) {
+                       case *Gns2DnsHandler:
+                               // we need to handle delegation to DNS: returns 
a list of found
+                               // resource records in DNS (filter by 'kind')
+                               fqdn := 
strings.Join(util.ReverseStringList(names[1:]), ".") + "." + inst.Name
+                               if set, err = gns.ResolveDNS(fqdn, 
inst.Servers, kind, pkey); err != nil {
+                                       logger.Println(logger.ERROR, "[gns] 
GNS2DNS resilution failed.")
+                                       return
+                               }
+                               // we are done with resolution; pass on records 
to caller
+                               records = set.Records
+                               break name_loop
+                       }
+               }
+       }
+       // Assemble resulting resource record set
+       set = NewGNSRecordSet()
+       for _, rec := range records {
+               // is this the record type we are looking for?
+               if kind == enums.GNS_TYPE_ANY || int(rec.Type) == kind {
+                       // add it to the result
+                       set.AddRecord(rec)
+               }
+       }
+       return
+}
+
+// Lookup name in GNS.
+func (gns *GNSModule) Lookup(pkey *ed25519.PublicKey, label string, remote 
bool) (block *GNSBlock, err error) {
+
+       // create query (lookup key)
+       query := NewQuery(pkey, label)
+
+       // try local lookup first
+       if block, err = gns.LookupLocal(query); err != nil {
+               logger.Printf(logger.ERROR, "[gns] local Lookup: %s\n", 
err.Error())
+               block = nil
+               return
+       }
+       if block == nil {
+               logger.Println(logger.DBG, "[gns] local Lookup: no block found")
+               if remote {
+                       // get the block from a remote lookup
+                       if block, err = gns.LookupRemote(query); err != nil || 
block == nil {
+                               if err != nil {
+                                       logger.Printf(logger.ERROR, "[gns] 
remote Lookup: %s\n", err.Error())
+                                       block = nil
+                               } else {
+                                       logger.Println(logger.DBG, "[gns] 
remote Lookup: no block found")
+                               }
+                               // lookup fails completely -- no result
+                               return
+                       }
+                       // store RRs from remote locally.
+                       gns.StoreLocal(query, block)
+               }
+       }
+       return
+}
diff --git a/src/gnunet/service/gns/record.go b/src/gnunet/service/gns/record.go
index 0e6315b..68ce4a6 100644
--- a/src/gnunet/service/gns/record.go
+++ b/src/gnunet/service/gns/record.go
@@ -28,6 +28,11 @@ func NewGNSRecordSet() *GNSRecordSet {
        }
 }
 
+func (rs *GNSRecordSet) AddRecord(rec *message.GNSResourceRecord) {
+       rs.Count++
+       rs.Records = append(rs.Records, rec)
+}
+
 type SignedBlockData struct {
        Purpose *crypto.SignaturePurpose // Size and purpose of signature (8 
bytes)
        Expire  util.AbsoluteTime        // Expiration time of the block.
diff --git a/src/gnunet/service/gns/service.go 
b/src/gnunet/service/gns/service.go
new file mode 100644
index 0000000..852f513
--- /dev/null
+++ b/src/gnunet/service/gns/service.go
@@ -0,0 +1,257 @@
+package gns
+
+import (
+       "encoding/hex"
+       "io"
+
+       "github.com/bfix/gospel/crypto/ed25519"
+       "github.com/bfix/gospel/data"
+       "github.com/bfix/gospel/logger"
+       "gnunet/config"
+       "gnunet/crypto"
+       "gnunet/enums"
+       "gnunet/message"
+       "gnunet/service"
+       "gnunet/transport"
+       "gnunet/util"
+)
+
+//----------------------------------------------------------------------
+// "GNUnet Name System" service implementation
+//----------------------------------------------------------------------
+
+// GNSService
+type GNSService struct {
+       GNSModule
+}
+
+// NewGNSService
+func NewGNSService() service.Service {
+       // instantiate service and assemble a new GNS handler.
+       inst := new(GNSService)
+       inst.LookupLocal = inst.LookupNamecache
+       inst.StoreLocal = inst.StoreNamecache
+       inst.LookupRemote = inst.LookupDHT
+       inst.GetLocalZone = inst.GetPrivateZone
+       return inst
+}
+
+// Start the GNS service
+func (s *GNSService) Start(spec string) error {
+       return nil
+}
+
+// Stop the GNS service
+func (s *GNSService) Stop() error {
+       return nil
+}
+
+// Serve a client channel.
+func (s *GNSService) ServeClient(mc *transport.MsgChannel) {
+       for {
+               // receive next message from client
+               msg, err := mc.Receive()
+               if err != nil {
+                       if err == io.EOF {
+                               logger.Println(logger.INFO, "[gns] Client 
channel closed.")
+                       } else {
+                               logger.Printf(logger.ERROR, "[gns] 
Message-receive failed: %s\n", err.Error())
+                       }
+                       break
+               }
+               logger.Printf(logger.INFO, "[gns] Received msg: %v\n", msg)
+
+               // perform lookup
+               var resp message.Message
+               switch m := msg.(type) {
+               case *message.GNSLookupMsg:
+                       
//----------------------------------------------------------
+                       // GNS_LOOKUP
+                       
//----------------------------------------------------------
+                       logger.Println(logger.INFO, "[gns] Lookup request 
received.")
+                       respX := message.NewGNSLookupResultMsg(m.Id)
+                       resp = respX
+
+                       // perform lookup on block (locally and remote)
+                       // TODO: run code in a go routine concurrently (would 
need
+                       //       access to the message channel to send 
responses)
+                       pkey := ed25519.NewPublicKeyFromBytes(m.Zone)
+                       label := m.GetName()
+                       recset, err := s.Resolve(label, pkey, int(m.Type), 
int(m.Options))
+                       if err != nil {
+                               logger.Printf(logger.ERROR, "[gns] Failed to 
lookup block: %s\n", err.Error())
+                               break
+                       }
+                       // handle records
+                       if recset != nil {
+                               logger.Printf(logger.DBG, "[gns] Received 
record set with %d entries\n", recset.Count)
+
+                               // get records from block
+                               if recset.Count == 0 {
+                                       logger.Println(logger.WARN, "[gns] No 
records in block")
+                                       break
+                               }
+                               // process records
+                               for i, rec := range recset.Records {
+                                       logger.Printf(logger.DBG, "[gns] Record 
#%d: %v\n", i, rec)
+
+                                       // is this the record type we are 
looking for?
+                                       if rec.Type == m.Type || int(m.Type) == 
enums.GNS_TYPE_ANY {
+                                               // add it to the response 
message
+                                               respX.AddRecord(rec)
+                                       }
+                               }
+                       }
+
+               default:
+                       
//----------------------------------------------------------
+                       // UNKNOWN message type received
+                       
//----------------------------------------------------------
+                       logger.Printf(logger.ERROR, "[gns] Unhandled message of 
type (%d)\n", msg.Header().MsgType)
+                       continue
+               }
+
+               // send response
+               if err := mc.Send(resp); err != nil {
+                       logger.Printf(logger.ERROR, "[gns] Failed to send 
response: %s\n", err.Error())
+               }
+
+       }
+       // close client connection
+       mc.Close()
+}
+
+// LookupNamecache
+func (s *GNSService) LookupNamecache(query *Query) (block *GNSBlock, err 
error) {
+       logger.Printf(logger.DBG, "[gns] LookupNamecache(%s)...\n", 
hex.EncodeToString(query.Key.Bits))
+
+       // assemble Namecache request
+       req := message.NewNamecacheLookupMsg(query.Key)
+       req.Id = uint32(util.NextID())
+       block = nil
+
+       // get response from Namecache service
+       var resp message.Message
+       if resp, err = service.ServiceRequestResponse("gns", "Namecache", 
config.Cfg.Namecache.Endpoint, req); err != nil {
+               return
+       }
+
+       // handle message depending on its type
+       logger.Println(logger.DBG, "[gns] Handling response from Namecache 
service")
+       switch m := resp.(type) {
+       case *message.NamecacheLookupResultMsg:
+               // check for matching IDs
+               if m.Id != req.Id {
+                       logger.Println(logger.ERROR, "[gns] Got response for 
unknown ID")
+                       break
+               }
+               // check if block was found
+               if len(m.EncData) == 0 {
+                       logger.Println(logger.DBG, "[gns] block not found in 
namecache")
+                       break
+               }
+               // check if record has expired
+               if m.Expire.Expired() {
+                       logger.Printf(logger.ERROR, "[gns] block expired at 
%s\n", m.Expire)
+                       break
+               }
+
+               // assemble GNSBlock from message
+               block = new(GNSBlock)
+               block.Signature = m.Signature
+               block.DerivedKey = m.DerivedKey
+               sb := new(SignedBlockData)
+               sb.Purpose = new(crypto.SignaturePurpose)
+               sb.Purpose.Purpose = enums.SIG_GNS_RECORD_SIGN
+               sb.Purpose.Size = uint32(16 + len(m.EncData))
+               sb.Expire = m.Expire
+               sb.EncData = m.EncData
+               block.Block = sb
+
+               // verify and decrypt block
+               if err = block.Verify(query.Zone, query.Label); err != nil {
+                       break
+               }
+               if err = block.Decrypt(query.Zone, query.Label); err != nil {
+                       break
+               }
+       }
+       return
+}
+
+// StoreNamecache
+func (s *GNSService) StoreNamecache(query *Query, block *GNSBlock) error {
+       logger.Println(logger.WARN, "[gns] StoreNamecache() not implemented 
yet!")
+       return nil
+}
+
+// LookupDHT
+func (s *GNSService) LookupDHT(query *Query) (block *GNSBlock, err error) {
+       logger.Printf(logger.DBG, "[gns] LookupDHT(%s)...\n", 
hex.EncodeToString(query.Key.Bits))
+
+       // assemble DHT request
+       req := message.NewDHTClientGetMsg(query.Key)
+       req.Id = uint64(util.NextID())
+       req.ReplLevel = uint32(enums.DHT_GNS_REPLICATION_LEVEL)
+       req.Type = uint32(enums.BLOCK_TYPE_GNS_NAMERECORD)
+       req.Options = uint32(enums.DHT_RO_DEMULTIPLEX_EVERYWHERE)
+       block = nil
+
+       // get response from DHT service
+       var resp message.Message
+       if resp, err = service.ServiceRequestResponse("gns", "DHT", 
config.Cfg.DHT.Endpoint, req); err != nil {
+               return
+       }
+
+       // handle message depending on its type
+       logger.Println(logger.DBG, "[gns] Handling response from DHT service")
+       switch m := resp.(type) {
+       case *message.DHTClientResultMsg:
+               // check for matching IDs
+               if m.Id != req.Id {
+                       logger.Println(logger.ERROR, "[gns] Got response for 
unknown ID")
+                       break
+               }
+               // check if block was found
+               if len(m.Data) == 0 {
+                       logger.Println(logger.DBG, "[gns] block not found in 
DHT")
+                       break
+               }
+               // check if record has expired
+               if m.Expire.Expired() {
+                       logger.Printf(logger.ERROR, "[gns] block expired at 
%s\n", m.Expire)
+                       break
+               }
+               // check if result is of requested type
+               if int(m.Type) != enums.BLOCK_TYPE_GNS_NAMERECORD {
+                       logger.Println(logger.ERROR, "[gns] DHT response has 
wrong type")
+                       break
+               }
+
+               // get GNSBlock from message
+               block = NewGNSBlock()
+               if err = data.Unmarshal(block, m.Data); err != nil {
+                       logger.Printf(logger.ERROR, "[gns] can't read GNS 
block: %s\n", err.Error())
+                       break
+               }
+               // verify and decrypt block
+               if err = block.Verify(query.Zone, query.Label); err != nil {
+                       break
+               }
+               if err = block.Decrypt(query.Zone, query.Label); err != nil {
+                       break
+               }
+
+               // we got a result from DHT that was not in the namecache,
+               // so store it there now.
+               if err = s.StoreNamecache(query, block); err != nil {
+                       logger.Printf(logger.ERROR, "[gns] can't store block in 
Namecache: %s\n", err.Error())
+               }
+       }
+       return
+}
+
+// GetPrivateZone
+func (s *GNSService) GetPrivateZone(name string) (*ed25519.PublicKey, error) {
+       return nil, nil
+}
diff --git a/src/gnunet/service/namecache/module.go 
b/src/gnunet/service/namecache/module.go
new file mode 100644
index 0000000..85f7601
--- /dev/null
+++ b/src/gnunet/service/namecache/module.go
@@ -0,0 +1,25 @@
+package namecache
+
+import (
+       "gnunet/service/gns"
+)
+
+//======================================================================
+// "GNS name cache" implementation
+//======================================================================
+
+//----------------------------------------------------------------------
+// Put and get GNS blocks into/from a cache (transient storage)
+//----------------------------------------------------------------------
+
+// Namecache handles the transient storage of GNS blocks under the query key.
+type NamecacheModule struct {
+}
+
+func (nc *NamecacheModule) Get(query *gns.Query) (*gns.GNSBlock, error) {
+       return nil, nil
+}
+
+func (nc *NamecacheModule) Put(query *gns.Query, block *gns.GNSBlock) error {
+       return nil
+}
diff --git a/src/gnunet/util/array.go b/src/gnunet/util/array.go
index 2ffb618..9076516 100644
--- a/src/gnunet/util/array.go
+++ b/src/gnunet/util/array.go
@@ -4,16 +4,23 @@ import (
        "fmt"
 )
 
+// Error variables
 var (
        ErrUtilArrayTooSmall = fmt.Errorf("Array to small")
 )
 
+//----------------------------------------------------------------------
+// Byte array helpers
+//----------------------------------------------------------------------
+
+// Clone creates a new array of same content as the argument.
 func Clone(d []byte) []byte {
        r := make([]byte, len(d))
        copy(r, d)
        return r
 }
 
+// Reverse the content of a byte array
 func Reverse(b []byte) []byte {
        bl := len(b)
        r := make([]byte, bl)
@@ -41,8 +48,40 @@ func CopyBlock(out, in []byte) {
        copy(out[to:], in[from:])
 }
 
+// Fill an array with a value
 func Fill(b []byte, val byte) {
        for i := 0; i < len(b); i++ {
                b[i] = val
        }
 }
+
+//----------------------------------------------------------------------
+// String list helpers
+//----------------------------------------------------------------------
+
+// Reverse StringList reverse an array of strings
+func ReverseStringList(s []string) []string {
+       sl := len(s)
+       r := make([]string, sl)
+       for i := 0; i < sl; i++ {
+               r[sl-i-1] = s[i]
+       }
+       return r
+}
+
+// Convert a binary representation of a string list. Each string is '\0'-
+// terminated. The whole byte array is parsed; if the final string is not
+// terminated, it is skipped.
+func StringList(b []byte) []string {
+       res := make([]string, 0)
+       str := ""
+       for _, ch := range b {
+               if ch == 0 {
+                       res = append(res, str)
+                       str = ""
+                       continue
+               }
+               str += string(ch)
+       }
+       return res
+}
diff --git a/src/gnunet/util/time.go b/src/gnunet/util/time.go
index b513d5e..0dd51a0 100644
--- a/src/gnunet/util/time.go
+++ b/src/gnunet/util/time.go
@@ -9,10 +9,14 @@ import (
 // Absolute time
 //----------------------------------------------------------------------
 
+// AbsoluteTime refers to a unique point in time.
+// The value is the elapsed time in milliseconds (Unix epoch), so no timestamp
+// before January 1st, 1970 is possible (not a restriction for GNUnet).
 type AbsoluteTime struct {
        Val uint64 `order:"big"`
 }
 
+// NewAbsoluteTime set the point in time to the given time value
 func NewAbsoluteTime(t time.Time) AbsoluteTime {
        secs := t.Unix()
        usecs := t.Nanosecond() / 1000
@@ -21,10 +25,17 @@ func NewAbsoluteTime(t time.Time) AbsoluteTime {
        }
 }
 
+// AbsoluteTimeNow returns the current point in time.
 func AbsoluteTimeNow() AbsoluteTime {
        return NewAbsoluteTime(time.Now())
 }
 
+// AbsoluteTimeNever returns the time defined as "never"
+func AbsoluteTimeNever() AbsoluteTime {
+       return AbsoluteTime{math.MaxUint64}
+}
+
+// String returns a human-readable notation of an absolute time.
 func (t AbsoluteTime) String() string {
        if t.Val == math.MaxUint64 {
                return "Never"
@@ -33,12 +44,14 @@ func (t AbsoluteTime) String() string {
        return ts.Format(time.RFC3339Nano)
 }
 
+// Add a duration to an absolute time yielding a new absolute time.
 func (t AbsoluteTime) Add(d time.Duration) AbsoluteTime {
        return AbsoluteTime{
                Val: t.Val + uint64(d.Milliseconds()),
        }
 }
 
+// Expired returns true if the timestamp is in the past.
 func (t AbsoluteTime) Expired() bool {
        // check for "never"
        if t.Val == math.MaxUint64 {
@@ -51,16 +64,20 @@ func (t AbsoluteTime) Expired() bool {
 // Relative time
 //----------------------------------------------------------------------
 
+// Relative time is a timestamp defined relative to the current time.
+// It actually is more like a duration than a time...
 type RelativeTime struct {
        Val uint64 `order:"big"`
 }
 
+// NewRelativeTime is initialized with a given duration.
 func NewRelativeTime(d time.Duration) RelativeTime {
        return RelativeTime{
                Val: uint64(d.Milliseconds()),
        }
 }
 
+// String returns a human-readble representation of a relative time (duration).
 func (t RelativeTime) String() string {
        if t.Val == math.MaxUint64 {
                return "Forever"
diff --git a/test.sh b/test.sh
new file mode 100755
index 0000000..5761694
--- /dev/null
+++ b/test.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+GOPATH=$(pwd):${GOPATH} go test -gcflags "-N -l" ./...

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]