[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[gnunet-go] branch master updated: MS2-RC2: Handle CNAME records with cu
From: |
gnunet |
Subject: |
[gnunet-go] branch master updated: MS2-RC2: Handle CNAME records with custom block handler. |
Date: |
Wed, 18 Dec 2019 22:22:44 +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 65f1f9c MS2-RC2: Handle CNAME records with custom block handler.
65f1f9c is described below
commit 65f1f9c357307b92a7ee3fc1327f34eb24135667
Author: Bernd Fix <address@hidden>
AuthorDate: Wed Dec 18 22:18:28 2019 +0100
MS2-RC2: Handle CNAME records with custom block handler.
---
src/gnunet/service/gns/block_handler.go | 172 +++++++++++++++++++++-----------
src/gnunet/service/gns/dns.go | 49 ++++-----
src/gnunet/service/gns/module.go | 125 ++++++++++++++++-------
src/gnunet/util/misc.go | 33 ++++++
4 files changed, 255 insertions(+), 124 deletions(-)
diff --git a/src/gnunet/service/gns/block_handler.go
b/src/gnunet/service/gns/block_handler.go
index 860b4a7..bf65e95 100644
--- a/src/gnunet/service/gns/block_handler.go
+++ b/src/gnunet/service/gns/block_handler.go
@@ -6,6 +6,7 @@ import (
"gnunet/enums"
"gnunet/message"
+ "gnunet/util"
"github.com/bfix/gospel/crypto/ed25519"
"github.com/bfix/gospel/logger"
@@ -16,17 +17,22 @@ type HdlrInst func(*message.GNSResourceRecord, []string)
(BlockHandler, error)
// Error codes
var (
- ErrInvalidRecordMix = fmt.Errorf("Invalid mix of RR types in block")
- ErrBlockHandler = fmt.Errorf("Internal block handler failure")
+ ErrInvalidRecordType = fmt.Errorf("Invalid resource record type")
+ ErrInvalidRecordBody = fmt.Errorf("Invalid resource record body")
+ ErrInvalidPKEY = fmt.Errorf("Invalid PKEY resource record")
+ ErrInvalidCNAME = fmt.Errorf("Invalid CNAME resource record")
+ ErrInvalidRecordMix = fmt.Errorf("Invalid mix of RR types in block")
+ ErrBlockHandler = fmt.Errorf("Internal block handler failure")
)
// Mapping of RR types to BlockHandler instanciation functions
var (
customHandler = map[int]HdlrInst{
- enums.GNS_TYPE_PKEY: NewPkeyHandler,
- enums.GNS_TYPE_GNS2DNS: NewGns2DnsHandler,
- enums.GNS_TYPE_BOX: NewBoxHandler,
- enums.GNS_TYPE_LEHO: NewLehoHandler,
+ enums.GNS_TYPE_PKEY: NewPkeyHandler,
+ enums.GNS_TYPE_GNS2DNS: NewGns2DnsHandler,
+ enums.GNS_TYPE_BOX: NewBoxHandler,
+ enums.GNS_TYPE_LEHO: NewLehoHandler,
+ enums.GNS_TYPE_DNS_CNAME: NewCnameHandler,
}
)
@@ -39,17 +45,17 @@ var (
// BlockHandler interface.
type BlockHandler interface {
- // AddRecord inserts a RR into the BlockHandler for (later) processing.
- // The handler can inspect the remaining labels in a path if required.
- // It returns an error if a record is not accepted by the block handler.
+ // AddRecord inserts an associated RR into the BlockHandler for (later)
+ // processing. The handler can inspect the remaining labels in a path
+ // if required. The method returns an error if a record is not accepted
+ // by the block handler (RR not of required type).
AddRecord(rr *message.GNSResourceRecord, labels []string) error
- // TypeAction returns a flag indicating how a resource record of a
- // given type is to be treated by a custom block handler:
- // = -1: Record is not allowed
- // = 0: Record is allowed but will be ignored
- // = 1: Record is allowed and will be processed
- TypeAction(t int) int
+ // Coexist checks if a custom block handler can co-exist with other
+ // resource records in the same block. 'cm' maps the resource type
+ // to an integer count (how many records of a type are present in the
+ // GNS block).
+ Coexist(cm util.CounterMap) bool
// Records returns a list of RR of the given types associated with
// the custom handler
@@ -69,7 +75,8 @@ type BlockHandler interface {
// BlockHandlerList is a list of block handlers instantiated.
type BlockHandlerList struct {
- list map[int]BlockHandler // list of handler instances
+ list map[int]BlockHandler // list of handler instances
+ counts util.CounterMap // count number of RRs by type
}
// NewBlockHandlerList instantiates an a list of active block handlers
@@ -77,24 +84,21 @@ type BlockHandlerList struct {
func NewBlockHandlerList(records []*message.GNSResourceRecord, labels
[]string) (*BlockHandlerList, error) {
// initialize block handler list
hl := &BlockHandlerList{
- list: make(map[int]BlockHandler),
+ list: make(map[int]BlockHandler),
+ counts: make(util.CounterMap),
}
- // build a list of record types that are handled by a custom handler.
- rrList := NewRRTypeList(
- enums.GNS_TYPE_PKEY,
- enums.GNS_TYPE_GNS2DNS,
- enums.GNS_TYPE_BOX,
- enums.GNS_TYPE_LEHO)
// Traverse record list and build list of handler instances
for _, rec := range records {
- // check for custom handler type
+ // update counter map
rrType := int(rec.Type)
- if rrList.HasType(rrType) {
+ hl.counts.Add(rrType)
+
+ // check for custom handler type
+ if creat, ok := customHandler[rrType]; ok {
// check if a handler for given type already exists
var (
hdlr BlockHandler
- ok bool
err error
)
if hdlr, ok = hl.list[rrType]; ok {
@@ -105,23 +109,22 @@ func NewBlockHandlerList(records
[]*message.GNSResourceRecord, labels []string)
continue
}
// create a new handler instance
- switch rrType {
- case enums.GNS_TYPE_PKEY:
- hdlr, err = NewPkeyHandler(rec, labels)
- case enums.GNS_TYPE_GNS2DNS:
- hdlr, err = NewGns2DnsHandler(rec, labels)
- case enums.GNS_TYPE_BOX:
- hdlr, err = NewBoxHandler(rec, labels)
- case enums.GNS_TYPE_LEHO:
- hdlr, err = NewLehoHandler(rec, labels)
- }
- if err != nil {
+ if hdlr, err = creat(rec, labels); err != nil {
return nil, err
}
// store handler in list
hl.list[rrType] = hdlr
}
}
+
+ // Check if all registered handlers in list can co-exist with
+ // all the other records of varying type
+ for _, hdlr := range hl.list {
+ if !hdlr.Coexist(hl.counts) {
+ return nil, ErrInvalidRecordMix
+ }
+ }
+ // return assembled handler list
return hl, nil
}
@@ -179,14 +182,11 @@ func (h *PkeyHandler) AddRecord(rec
*message.GNSResourceRecord, labels []string)
return nil
}
-// TypeAction return a flag indicating how a resource record of a given type
+// Coexist return a flag indicating how a resource record of a given type
// is to be treated (see BlockHandler interface)
-func (h *PkeyHandler) TypeAction(t int) int {
- // no other resource record type is not allowed
- if t == enums.GNS_TYPE_PKEY {
- return 1
- }
- return -1
+func (h *PkeyHandler) Coexist(cm util.CounterMap) bool {
+ // only one type (GNS_TYPE_PKEY) is present
+ return len(cm) == 1 && cm.Num(enums.GNS_TYPE_PKEY) == 1
}
// Records returns a list of RR of the given type associated with this handler
@@ -252,11 +252,11 @@ func (h *Gns2DnsHandler) AddRecord(rec
*message.GNSResourceRecord, labels []stri
return nil
}
-// TypeAction return a flag indicating how a resource record of a given type
+// Coexist return a flag indicating how a resource record of a given type
// is to be treated (see BlockHandler interface)
-func (h *Gns2DnsHandler) TypeAction(t int) int {
- // anything goes...
- return 1
+func (h *Gns2DnsHandler) Coexist(cm util.CounterMap) bool {
+ // only one type (GNS_TYPE_GNS2DNS) is present
+ return len(cm) == 1 && cm.Num(enums.GNS_TYPE_GNS2DNS) > 0
}
// Records returns a list of RR of the given type associated with this handler
@@ -317,11 +317,11 @@ func (h *BoxHandler) AddRecord(rec
*message.GNSResourceRecord, labels []string)
return nil
}
-// TypeAction return a flag indicating how a resource record of a given type
+// Coexist return a flag indicating how a resource record of a given type
// is to be treated (see BlockHandler interface)
-func (h *BoxHandler) TypeAction(t int) int {
+func (h *BoxHandler) Coexist(cm util.CounterMap) bool {
// anything goes...
- return 1
+ return true
}
// Records returns a list of RR of the given type associated with this handler
@@ -376,16 +376,17 @@ func (h *LehoHandler) AddRecord(rec
*message.GNSResourceRecord, labels []string)
return nil
}
-// TypeAction return a flag indicating how a resource record of a given type
+// Coexist return a flag indicating how a resource record of a given type
// is to be treated (see BlockHandler interface)
-func (h *LehoHandler) TypeAction(t int) int {
- // only A and AAAA records allowed beside LEHO
- switch t {
- case enums.GNS_TYPE_LEHO, enums.GNS_TYPE_DNS_A, enums.GNS_TYPE_DNS_AAAA:
- return 1
- default:
- return -1
+func (h *LehoHandler) Coexist(cm util.CounterMap) bool {
+ // requires exactly one A/AAAA record alongside single LEHO
+ if len(cm) != 2 {
+ return false
}
+ if cm.Num(enums.GNS_TYPE_LEHO) != 1 {
+ return false
+ }
+ return cm.Num(enums.GNS_TYPE_DNS_A) == 1 ||
cm.Num(enums.GNS_TYPE_DNS_AAAA) == 1
}
// Records returns a list of RR of the given type associated with this handler
@@ -396,3 +397,56 @@ func (h *LehoHandler) Records(kind RRTypeList)
*GNSRecordSet {
}
return rs
}
+
+//----------------------------------------------------------------------
+// CNAME handler
+//----------------------------------------------------------------------
+
+// CnameHandler implementing the BlockHandler interface
+type CnameHandler struct {
+ name string
+ rec *message.GNSResourceRecord
+}
+
+// NewCnameHandler returns a new BlockHandler instance
+func NewCnameHandler(rec *message.GNSResourceRecord, labels []string)
(BlockHandler, error) {
+ if int(rec.Type) != enums.GNS_TYPE_DNS_CNAME {
+ return nil, ErrInvalidRecordType
+ }
+ h := &CnameHandler{
+ name: "",
+ }
+ if err := h.AddRecord(rec, labels); err != nil {
+ return nil, err
+ }
+ return h, nil
+}
+
+// AddRecord inserts a CNAME record into the handler.
+func (h *CnameHandler) AddRecord(rec *message.GNSResourceRecord, labels
[]string) error {
+ if int(rec.Type) != enums.GNS_TYPE_DNS_CNAME {
+ return ErrInvalidRecordType
+ }
+ if h.rec != nil {
+ return ErrInvalidCNAME
+ }
+ _, h.name = DNSNameFromBytes(rec.Data, 0)
+ h.rec = rec
+ return nil
+}
+
+// Coexist return a flag indicating how a resource record of a given type
+// is to be treated (see BlockHandler interface)
+func (h *CnameHandler) Coexist(cm util.CounterMap) bool {
+ // anything goes
+ return true
+}
+
+// Records returns a list of RR of the given type associated with this handler
+func (h *CnameHandler) Records(kind RRTypeList) *GNSRecordSet {
+ rs := NewGNSRecordSet()
+ if kind.HasType(enums.GNS_TYPE_DNS_CNAME) {
+ rs.AddRecord(h.rec)
+ }
+ return rs
+}
diff --git a/src/gnunet/service/gns/dns.go b/src/gnunet/service/gns/dns.go
index fe138b0..18e2b78 100644
--- a/src/gnunet/service/gns/dns.go
+++ b/src/gnunet/service/gns/dns.go
@@ -45,9 +45,11 @@ func DNSNameFromBytes(b []byte, offset int) (int, string) {
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 RRTypeList, res chan
*GNSRecordSet) {
+func QueryDNS(id int, name string, server net.IP, kind RRTypeList)
*GNSRecordSet {
+ // get default nameserver if not defined.
+ if server == nil {
+ server = net.IPv4(8, 8, 8, 8)
+ }
logger.Printf(logger.DBG, "[dns][%d] Starting query for '%s' on
'%s'...\n", id, name, server.String())
// assemble query
@@ -80,14 +82,13 @@ func queryDNS(id int, name string, server net.IP, kind
RRTypeList, res chan *GNS
continue
}
logger.Printf(logger.ERROR, "[dns][%d] Error: %s\n",
id, errMsg)
- res <- nil
+ return 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
+ return nil
}
set := NewGNSRecordSet()
for _, record := range in.Answer {
@@ -118,12 +119,11 @@ func queryDNS(id int, name string, server net.IP, kind
RRTypeList, res chan *GNS
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.INFO, "[dns][%d] %d resource records
extracted from response (%d/5).\n", id, set.Count, retry+1)
+ return set
}
logger.Printf(logger.WARN, "[dns][%d] Resolution failed -- giving
up...\n", id)
- res <- nil
+ return nil
}
// ResolveDNS resolves a name in DNS. Multiple DNS servers are queried in
@@ -135,29 +135,16 @@ func (gns *GNSModule) ResolveDNS(name string, servers
[]string, kind RRTypeList,
// start DNS queries concurrently
res := make(chan *GNSRecordSet)
running := 0
- for idx, srv := range servers {
+ for _, srv := range servers {
// check if srv is an IPv4/IPv6 address
addr := net.ParseIP(srv)
logger.Printf(logger.DBG, "ParseIP('%s', len=%d) --> %v\n",
srv, len(srv), addr)
if addr == nil {
+ // no, it is a name... try to resolve an IP address
from the name
query := NewRRTypeList(enums.GNS_TYPE_DNS_A,
enums.GNS_TYPE_DNS_AAAA)
- // 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, query,
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 (name MUST end in
a PKEY)
- set, err = gns.Resolve(srv, nil, query,
enums.GNS_LO_DEFAULT)
- if err != nil {
- logger.Printf(logger.ERROR, "[dns]
Can't resolve NS server '%s'\n", srv)
- continue
- }
+ if set, err = gns.ResolveUnknown(srv, pkey, query); err
!= nil {
+ logger.Printf(logger.ERROR, "[dns] Can't
resolve NS server '%s': %s\n", srv, err.Error())
+ continue
}
// traverse resource records for 'A' and 'AAAA' records.
rec_loop:
@@ -173,12 +160,14 @@ func (gns *GNSModule) ResolveDNS(name string, servers
[]string, kind RRTypeList,
}
// check if we have an IP address available
if addr == nil {
- logger.Printf(logger.WARN, "[dns] No IP address
for nameserver in GNS")
+ logger.Printf(logger.WARN, "[dns] No IP address
for nameserver '%s'\n", srv)
continue
}
}
// query DNS concurrently
- go queryDNS(idx, name, addr, kind, res)
+ go func() {
+ res <- QueryDNS(util.NextID(), name, addr, kind)
+ }()
running++
}
// check if we started some queries at all.
diff --git a/src/gnunet/service/gns/module.go b/src/gnunet/service/gns/module.go
index 3e7bd18..6732e08 100644
--- a/src/gnunet/service/gns/module.go
+++ b/src/gnunet/service/gns/module.go
@@ -20,10 +20,7 @@ import (
// 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")
+ ErrUnknownTLD = fmt.Errorf("Unknown TLD in name")
)
//----------------------------------------------------------------------
@@ -83,7 +80,7 @@ func NewQuery(pkey *ed25519.PublicKey, label string) *Query {
// (d) if rec is CNAME record:
// if no remaining labels:
// if requested types include CNAME -> (5)
-// if
+// -> Resolve(CNAME)
// resolution failed: name not completely processed and no zone available
//
// (5) return records: it is the responsibility of the caller to assemble
@@ -119,30 +116,23 @@ func (gns *GNSModule) Resolve(path string, pkey
*ed25519.PublicKey, kind RRTypeL
// Resolve a fully qualified GNS absolute name (with multiple labels).
func (gns *GNSModule) ResolveAbsolute(labels []string, kind RRTypeList, 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(labels[0]) == 52 {
- if data, err = util.DecodeStringToBinary(labels[0],
32); err == nil {
- if pkey = ed25519.NewPublicKeyFromBytes(data);
pkey != nil {
- break
- }
- }
- }
+ // get the zone key for the TLD
+ // (1) check if TLD is a PKEY
+ pkey := gns.GetZoneKey(labels[0])
+ if pkey == nil {
// (2) check if TLD is in our local config
- if pkey = config.Cfg.GNS.GetRootZoneKey(labels[0]); pkey != nil
{
- break
- }
+ pkey = config.Cfg.GNS.GetRootZoneKey(labels[0])
+ }
+ if pkey == nil {
// (3) check if TLD is one of our identities
- if pkey, err = gns.GetLocalZone(labels[0]); err == nil {
- break
+ pkey, err = gns.GetLocalZone(labels[0])
+ }
+ if pkey == nil {
+ if err == nil {
+ err = ErrUnknownTLD
}
// (4) we can't resolve this TLD
- return nil, ErrUnknownTLD
+ return
}
// continue with resolution relative to a zone.
return gns.ResolveRelative(labels[1:], pkey, kind, mode)
@@ -156,7 +146,6 @@ func (gns *GNSModule) ResolveRelative(labels []string, pkey
*ed25519.PublicKey,
records []*message.GNSResourceRecord // final resource records
from resolution
hdlrs *BlockHandlerList // list of block handlers
in final step
)
-name_loop:
for ; len(labels) > 0; labels = labels[1:] {
logger.Printf(logger.DBG, "[gns] ResolveRelative '%s' in
'%s'\n", labels[0], util.EncodeBinaryToString(pkey.Bytes()))
@@ -192,16 +181,24 @@ name_loop:
if hdlr := hdlrs.GetHandler(enums.GNS_TYPE_PKEY); hdlr != nil {
// (1) PKEY record:
inst := hdlr.(*PkeyHandler)
- // if labels are pending, set new zone and continue
resolution
- if len(labels) > 1 {
- pkey = inst.pkey
- continue name_loop
+ // if labels are pending, set new zone and continue
resolution;
+ // otherwise resolve "@" label for the zone if no PKEY
record
+ // was requested.
+ pkey = inst.pkey
+ if len(labels) == 1 &&
!kind.HasType(enums.GNS_TYPE_PKEY) {
+ labels = append(labels, "@")
}
} else if hdlr := hdlrs.GetHandler(enums.GNS_TYPE_GNS2DNS);
hdlr != nil {
- // (2) GNS2DNS records: delegate resolution to DNS
+ // (2) GNS2DNS records
inst := hdlr.(*Gns2DnsHandler)
- // we need to handle delegation to DNS: returns a list
of found
- // resource records in DNS (filter by 'kind')
+ // if we are at the end of the path and the requested
type
+ // includes GNS_TYPE_GNS2DNS, the GNS2DNS records are
returned...
+ if len(labels) == 1 &&
kind.HasType(enums.GNS_TYPE_GNS2DNS) {
+ records = inst.recs
+ break
+ }
+ // ... otherwise we need to handle delegation to DNS:
returns a
+ // list of found resource records in DNS (filter by
'kind')
lbls :=
strings.Join(util.ReverseStringList(labels[1:]), ".")
if len(lbls) > 0 {
lbls += "."
@@ -213,15 +210,30 @@ name_loop:
}
// we are done with resolution; pass on records to
caller
records = set.Records
- break name_loop
+ break
} else if hdlr := hdlrs.GetHandler(enums.GNS_TYPE_BOX); hdlr !=
nil {
// (3) BOX records:
inst := hdlr.(*BoxHandler)
new_records := inst.Records(kind).Records
if len(new_records) > 0 {
records = new_records
- break name_loop
+ break
+ }
+ } else if hdlr := hdlrs.GetHandler(enums.GNS_TYPE_DNS_CNAME);
hdlr != nil {
+ // (4) CNAME records:
+ inst := hdlr.(*CnameHandler)
+ // if we are at the end of the path and the requested
type
+ // includes GNS_TYPE_DNS_CNAME, the records are
returned...
+ if len(labels) == 1 &&
kind.HasType(enums.GNS_TYPE_DNS_CNAME) {
+ break
}
+ if set, err = gns.ResolveUnknown(inst.name, pkey,
kind); err != nil {
+ logger.Println(logger.ERROR, "[gns] CNAME
resolution failed.")
+ return
+ }
+ // we are done with resolution; pass on records to
caller
+ records = set.Records
+ break
}
}
// Assemble resulting resource record set by filtering for requested
types.
@@ -237,6 +249,49 @@ name_loop:
return
}
+// ResolveUnknown resolves a name either in GNS (if applicable) or DNS:
+// If the name is a relative GNS path (ending in ".+"), it is resolved in GNS
+// relative to the zone PKEY. If the name is an absolute GNS name (ending in
+// a PKEY TLD), it is also resolved with GNS. All other names are resolved
+// via DNS queries.
+func (gns *GNSModule) ResolveUnknown(name string, pkey *ed25519.PublicKey,
kind RRTypeList) (set *GNSRecordSet, err error) {
+ // relative GNS-based server name?
+ if strings.HasSuffix(name, ".+") {
+ // resolve server name relative to current zone
+ name = strings.TrimSuffix(name, ".+")
+ if set, err = gns.Resolve(name, pkey, kind,
enums.GNS_LO_DEFAULT); err != nil {
+ return
+ }
+ } else {
+ // check for absolute GNS name (with PKEY as TLD)
+ if zk := gns.GetZoneKey(name); zk != nil {
+ // resolve absolute GNS name (name ends in a PKEY)
+ if set, err = gns.Resolve(util.StripPathRight(name),
zk, kind, enums.GNS_LO_DEFAULT); err != nil {
+ return
+ }
+ } else {
+ // resolve the server name via DNS
+ if set = QueryDNS(util.NextID(), name, nil, kind); set
== nil {
+ err = ErrNoDNSResults
+ }
+ }
+ }
+ return
+}
+
+// GetZoneKey returns the PKEY (or nil) from an absolute GNS path.
+func (gns *GNSModule) GetZoneKey(path string) *ed25519.PublicKey {
+ labels := util.ReverseStringList(strings.Split(path, "."))
+ if len(labels[0]) == 52 {
+ if data, err := util.DecodeStringToBinary(labels[0], 32); err
== nil {
+ if pkey := ed25519.NewPublicKeyFromBytes(data); pkey !=
nil {
+ return pkey
+ }
+ }
+ }
+ return nil
+}
+
// Lookup name in GNS.
func (gns *GNSModule) Lookup(pkey *ed25519.PublicKey, label string, remote
bool) (block *GNSBlock, err error) {
diff --git a/src/gnunet/util/misc.go b/src/gnunet/util/misc.go
new file mode 100644
index 0000000..6a8a4a3
--- /dev/null
+++ b/src/gnunet/util/misc.go
@@ -0,0 +1,33 @@
+package util
+
+import (
+ "strings"
+)
+
+type CounterMap map[interface{}]int
+
+func (cm CounterMap) Add(i interface{}) int {
+ count, ok := cm[i]
+ if !ok {
+ count = 1
+ } else {
+ count++
+ }
+ cm[i] = count
+ return count
+}
+
+func (cm CounterMap) Num(i interface{}) int {
+ count, ok := cm[i]
+ if !ok {
+ count = 0
+ }
+ return count
+}
+
+func StripPathRight(s string) string {
+ if idx := strings.LastIndex(s, "."); idx != -1 {
+ return s[:idx]
+ }
+ return s
+}
--
To stop receiving notification emails like this one, please contact
address@hidden.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [gnunet-go] branch master updated: MS2-RC2: Handle CNAME records with custom block handler.,
gnunet <=