gnunet-go

GNUnet Bindings for Go
Log | Files | Refs | README | LICENSE

commit bcc6ce0be0d9c240dce80c42af5b56e8ee805aff
parent cc143998695474cbe91941b91eaa9c8fa41d6700
Author: Bernd Fix <brf@hoi-polloi.org>
Date:   Wed, 26 Oct 2022 17:59:46 +0200

Identity service added to zone master.

Diffstat:
Msrc/gnunet/cmd/zonemaster-go/main.go | 14++++----------
Msrc/gnunet/config/gnunet-config.json | 2+-
Msrc/gnunet/crypto/gns.go | 158+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/gnunet/crypto/gns_edkey.go | 15++++++++++-----
Msrc/gnunet/crypto/gns_edkey_test.go | 4++++
Msrc/gnunet/crypto/gns_pkey.go | 21+++++++++++++++++----
Msrc/gnunet/crypto/gns_test.go | 51++++++++++++++++-----------------------------------
Asrc/gnunet/enums/results.go | 31+++++++++++++++++++++++++++++++
Msrc/gnunet/go.mod | 4++--
Msrc/gnunet/go.sum | 4++--
Msrc/gnunet/message/factory.go | 32++++++++++++++++++++++++++++++++
Msrc/gnunet/message/message.go | 3+++
Msrc/gnunet/message/msg_core.go | 5++++-
Msrc/gnunet/message/msg_dht.go | 15+++++++++++++++
Msrc/gnunet/message/msg_dht_p2p.go | 61+++++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/gnunet/message/msg_gns.go | 8+++++++-
Msrc/gnunet/message/msg_hello.go | 41+++++++++++++++++++++++++++++------------
Asrc/gnunet/message/msg_identity.go | 473+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/gnunet/message/msg_namecache.go | 18+++++++++++++++---
Msrc/gnunet/message/msg_namestore.go | 57+++++++++++++++++++++++++++++++++++++++++++--------------
Msrc/gnunet/message/msg_revocation.go | 16++++++++++++++--
Msrc/gnunet/message/msg_transport.go | 27+++++++++++++++++++++++++++
Msrc/gnunet/service/connection.go | 3+++
Msrc/gnunet/service/dht/blocks/gns.go | 7++++++-
Msrc/gnunet/service/dht/blocks/gns_test.go | 75+++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/gnunet/service/namecache/module.go | 2+-
Msrc/gnunet/service/revocation/pow_test.go | 8++++++--
Msrc/gnunet/service/store/store_zonemaster.go | 201+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/gnunet/service/store/store_zonemaster.sql | 30++++++++++++++++++++++++++----
Msrc/gnunet/service/zonemaster/gui.go | 46++++++++++++++++++++++++++++++++--------------
Msrc/gnunet/service/zonemaster/gui.htpl | 4++--
Msrc/gnunet/service/zonemaster/gui_css.htpl | 27++++-----------------------
Msrc/gnunet/service/zonemaster/gui_new.htpl | 6+++---
Msrc/gnunet/service/zonemaster/gui_rr.htpl | 40++++++++++++++++++++--------------------
Msrc/gnunet/service/zonemaster/records.go | 25++++++++++++++++++++++++-
Msrc/gnunet/service/zonemaster/rpc.go | 2+-
Msrc/gnunet/service/zonemaster/service.go | 186+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Asrc/gnunet/service/zonemaster/service_identity.go | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/gnunet/service/zonemaster/service_namestore.go | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/gnunet/service/zonemaster/zonemaster.go | 137+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/gnunet/transport/reader_writer.go | 5++++-
41 files changed, 1722 insertions(+), 367 deletions(-)

diff --git a/src/gnunet/cmd/zonemaster-go/main.go b/src/gnunet/cmd/zonemaster-go/main.go @@ -73,13 +73,11 @@ func main() { config.Cfg.ZoneMaster.GUI = gui } - // start a new namestore service under zonemaster umbrella + // start services under zonemaster umbrella ctx, cancel := context.WithCancel(context.Background()) - srv, ok := zonemaster.NewService(ctx, nil).(*zonemaster.Service) - if !ok { - logger.Println(logger.ERROR, "[zonemaster] Failed to create service") - return - } + srv := zonemaster.NewService(ctx, nil) + go srv.Run(ctx) + // start UDS listener if service is specified if config.Cfg.ZoneMaster.Service != nil { sockHdlr := service.NewSocketHandler("zonemaster", srv) @@ -89,10 +87,6 @@ func main() { } } - // start a new ZONEMASTER (background service with HTTPS backend) - zm := zonemaster.NewZoneMaster(config.Cfg, srv) - go zm.Run(ctx) - // handle command-line arguments for RPC if len(rpcEndp) > 0 { parts := strings.Split(rpcEndp, ":") diff --git a/src/gnunet/config/gnunet-config.json b/src/gnunet/config/gnunet-config.json @@ -91,7 +91,7 @@ }, "gui": "127.0.0.1:8100", "service": { - "socket": "${RT_USER}/gnunet-service-namestore-go.sock", + "socket": "${RT_USER}/gnunet-service-zonemaster-go.sock", "params": { "perm": "0770" } diff --git a/src/gnunet/crypto/gns.go b/src/gnunet/crypto/gns.go @@ -129,6 +129,10 @@ type ZoneKeyImpl interface { type ZonePrivateImpl interface { ZoneAbstractImpl + // Prepare a random byte array to be used as a random + // private key of given type. + Prepare(rnd []byte) []byte + // Derive a private key from this zone key based on a big integer // (key blinding). Returns the derived key and the blinding value. Derive(h *math.Int) (ZonePrivateImpl, *math.Int, error) @@ -211,46 +215,80 @@ func GetImplementation(ztype enums.GNSType) *ZoneImplementation { // ZonePrivate represents the possible types of private zone keys (PKEY, EDKEY,...) type ZonePrivate struct { - ZoneKey + Type enums.GNSType `json:"type" order:"big"` + KeyData []byte `json:"key" size:"(KeySize)"` impl ZonePrivateImpl // reference to implementation } -// NewZonePrivate returns a new initialized ZonePrivate instance. If no data is -// provided, a new random key is created -func NewZonePrivate(ztype enums.GNSType, d []byte) (zp *ZonePrivate, err error) { +// NewZonePrivate returns a new initialized ZonePrivate instance of given ztype. +// If no data is provided, a new random key is created. If data is provided, it +// must be in the correct format specified by a ZonePrivate implementation. +func NewZonePrivate(ztype enums.GNSType, zdata []byte) (zp *ZonePrivate, err error) { // get factory for given zone type impl, ok := zoneImpl[ztype] if !ok { return nil, ErrNoImplementation } + prvImpl := impl.NewPrivate() + // init data available? - if d == nil { + if zdata == nil { // no: create random seed - d = make([]byte, impl.PrivateSize) - if _, err = rand.Read(d); err != nil { + zdata = make([]byte, impl.PrivateSize) + if _, err = rand.Read(zdata); err != nil { return } + zdata = prvImpl.Prepare(zdata) } // assemble private zone key zp = &ZonePrivate{ - ZoneKey{ - ztype, - nil, - nil, - }, - nil, + Type: ztype, + KeyData: zdata, + impl: prvImpl, } - zp.impl = impl.NewPrivate() - if err = zp.impl.Init(d); err != nil { - return + err = zp.impl.Init(zdata) + return +} + +// Init is called to setup internal state after unmarshalling object +func (zp *ZonePrivate) Init() (err error) { + // check for initialized key + if zp.impl == nil { + impl, ok := zoneImpl[zp.Type] + if !ok { + return ErrNoImplementation + } + zp.impl = impl.NewPrivate() + err = zp.impl.Init(zp.KeyData) } - zp.ZoneKey.KeyData = zp.impl.Public().Bytes() - zp.ZoneKey.impl = impl.NewPublic() - err = zp.ZoneKey.impl.Init(zp.ZoneKey.KeyData) return } +// Null returns a "NULL" ZonePrivate for a given key +func NullZonePrivate(ztype enums.GNSType) (*ZonePrivate, uint16) { + // get factory for given zone type + impl, ok := zoneImpl[ztype] + if !ok { + return nil, 0 + } + kd := make([]byte, impl.PrivateSize) + zp := &ZonePrivate{ + Type: ztype, // need key type for length calculation + KeyData: kd, // untyped key data (all 0) + impl: nil, // no implementation! + } + return zp, uint16(len(zp.KeyData) + 4) +} + +// Bytes returns the binary representation +func (zp *ZonePrivate) Bytes() []byte { + buf := new(bytes.Buffer) + _ = binary.Write(buf, binary.BigEndian, zp.Type) + _, _ = buf.Write(zp.KeyData) + return buf.Bytes() +} + // KeySize returns the number of bytes of a key representation. // This method is used during serialization (Unmarshal). func (zp *ZonePrivate) KeySize() uint { @@ -263,7 +301,7 @@ func (zp *ZonePrivate) KeySize() uint { // Derive key (key blinding) func (zp *ZonePrivate) Derive(label, context string) (dzp *ZonePrivate, h *math.Int, err error) { // calculate derived key - key := zp.Public().Bytes() + key := zp.Public().KeyData h = deriveH(key, label, context) var derived ZonePrivateImpl if derived, h, err = zp.impl.Derive(h); err != nil { @@ -271,15 +309,10 @@ func (zp *ZonePrivate) Derive(label, context string) (dzp *ZonePrivate, h *math. } // assemble derived pivate key dzp = &ZonePrivate{ - ZoneKey{ - zp.Type, - nil, - nil, - }, - derived, + Type: zp.Type, + KeyData: derived.Bytes(), + impl: derived, } - dzp.ZoneKey.KeyData = derived.Public().Bytes() - err = dzp.Init() return } @@ -290,7 +323,12 @@ func (zp *ZonePrivate) Sign(data []byte) (sig *ZoneSignature, err error) { // Public returns the associated public key func (zp *ZonePrivate) Public() *ZoneKey { - return &zp.ZoneKey + impl := zp.impl.Public() + return &ZoneKey{ + Type: zp.Type, + KeyData: impl.Bytes(), + impl: impl, + } } // ID returns the human-readable zone private key. @@ -310,7 +348,7 @@ type ZoneKey struct { impl ZoneKeyImpl // reference to implementation } -// Init a zone key where only the attributes have been read/deserialized. +// Init is called to setup internal state after unmarshalling object func (zk *ZoneKey) Init() (err error) { if zk.impl == nil { // initialize implementation @@ -335,6 +373,14 @@ func NewZoneKey(d []byte) (zk *ZoneKey, err error) { return } +// Bytes returns the binary representation (can be used with 'init()') +func (zk *ZoneKey) Bytes() []byte { + buf := new(bytes.Buffer) + _ = binary.Write(buf, binary.BigEndian, zk.Type) + _, _ = buf.Write(zk.KeyData) + return buf.Bytes() +} + // KeySize returns the number of bytes of a key representation. // This method is used during serialization (Unmarshal). func (zk *ZoneKey) KeySize() uint { @@ -346,7 +392,7 @@ func (zk *ZoneKey) KeySize() uint { // Derive key (key blinding) func (zk *ZoneKey) Derive(label, context string) (dzk *ZoneKey, h *math.Int, err error) { - key := zk.Bytes() + key := zk.KeyData h = deriveH(key, label, context) var derived ZoneKeyImpl if derived, h, err = zk.impl.Derive(h); err != nil { @@ -388,12 +434,6 @@ func (zk *ZoneKey) ID() string { return zk.impl.ID() } -// Bytes returns all bytes of a zone key -func (zk *ZoneKey) Bytes() []byte { - data, _ := data.Marshal(zk) - return data -} - // Equal checks if two zone keys are equal func (zk *ZoneKey) Equal(k *ZoneKey) bool { return bytes.Equal(zk.KeyData, k.KeyData) @@ -420,23 +460,26 @@ type ZoneSignature struct { impl ZoneSigImpl // reference to implementation } +// Init is called to setup internal state after unmarshalling object func (zs *ZoneSignature) Init() (err error) { - // initialize implementations - impl, ok := zoneImpl[zs.Type] - if !ok { - err = ErrUnknownZoneType - return - } - // set signature implementation - sig := impl.NewSignature() - if err = sig.Init(zs.Signature); err != nil { - return + if zs.impl == nil { + // initialize implementations + impl, ok := zoneImpl[zs.Type] + if !ok { + err = ErrUnknownZoneType + return + } + // set signature implementation + sig := impl.NewSignature() + if err = sig.Init(zs.Signature); err != nil { + return + } + zs.impl = sig + // set public key implementation + zk := impl.NewPublic() + err = zk.Init(zs.KeyData) + zs.ZoneKey.impl = zk } - zs.impl = sig - // set public key implementation - zk := impl.NewPublic() - err = zk.Init(zs.KeyData) - zs.ZoneKey.impl = zk return } @@ -460,6 +503,11 @@ func (zs *ZoneSignature) SigSize() uint { return 0 } +// Bytes returns the binary representation (can be used with 'init()') +func (zs *ZoneSignature) Bytes() []byte { + return zs.impl.Bytes() +} + // Key returns the associated zone key object func (zs *ZoneSignature) Key() *ZoneKey { return &zs.ZoneKey @@ -486,10 +534,10 @@ func deriveH(key []byte, label, context string) *math.Int { return math.NewIntFromBytes(b) } -// convert (type|data) to GNUnet identifier -func asID(t enums.GNSType, data []byte) string { +// convert (type|data) to bytes +func asBytes(t enums.GNSType, data []byte) []byte { buf := new(bytes.Buffer) _ = binary.Write(buf, binary.BigEndian, t) _, _ = buf.Write(data) - return util.EncodeBinaryToString(buf.Bytes()) + return buf.Bytes() } diff --git a/src/gnunet/crypto/gns_edkey.go b/src/gnunet/crypto/gns_edkey.go @@ -79,8 +79,8 @@ func (pk *EDKEYPublicImpl) Bytes() []byte { // Derive a public key from this key based on a big integer // (key blinding). Returns the derived key and the blinding value. func (pk *EDKEYPublicImpl) Derive(h *math.Int) (dPk ZoneKeyImpl, hOut *math.Int, err error) { - // limit to allowed value range - hOut = h.Mod(ed25519.GetCurve().N) + // limit to allowed value range (see LSD0001 spec) + hOut = h.SetBit(255, 0) derived := pk.pub.Mult(hOut) dPk = &EDKEYPublicImpl{ pk.ztype, @@ -162,7 +162,7 @@ func (pk *EDKEYPublicImpl) BlockKey(label string, expire util.AbsoluteTime) (ske // ID returns the GNUnet identifier for a public zone key func (pk *EDKEYPublicImpl) ID() string { - return asID(enums.GNS_TYPE_EDKEY, pk.pub.Bytes()) + return util.EncodeBinaryToString(asBytes(enums.GNS_TYPE_EDKEY, pk.pub.Bytes())) } //---------------------------------------------------------------------- @@ -187,6 +187,11 @@ func (pk *EDKEYPrivateImpl) Init(data []byte) error { return nil } +// Prepare a random byte array to be used as a random private EDKEY +func (pk *EDKEYPrivateImpl) Prepare(rnd []byte) []byte { + return rnd +} + // Bytes returns a binary representation of the instance suitable for // consumption in 'Init()'. func (pk *EDKEYPrivateImpl) Bytes() []byte { @@ -201,7 +206,7 @@ func (pk *EDKEYPrivateImpl) Public() ZoneKeyImpl { // Derive a public key from this key based on a big integer // (key blinding). Returns the derived key and the blinding value. func (pk *EDKEYPrivateImpl) Derive(h *math.Int) (dPk ZonePrivateImpl, hOut *math.Int, err error) { - // limit to allowed value range + // limit to allowed value range (see LSD0001 spec) hOut = h.SetBit(255, 0) // derive private key derived := pk.prv.Mult(hOut) @@ -243,7 +248,7 @@ func (pk *EDKEYPrivateImpl) Sign(data []byte) (sig *ZoneSignature, err error) { // ID returns the GNUnet identifier for a private zone key func (pk *EDKEYPrivateImpl) ID() string { - return asID(enums.GNS_TYPE_EDKEY, pk.seed) + return util.EncodeBinaryToString(asBytes(enums.GNS_TYPE_EDKEY, pk.seed)) } //---------------------------------------------------------------------- diff --git a/src/gnunet/crypto/gns_edkey_test.go b/src/gnunet/crypto/gns_edkey_test.go @@ -20,6 +20,7 @@ package crypto import ( "bytes" + "encoding/hex" "gnunet/enums" "testing" ) @@ -50,7 +51,10 @@ func TestDeriveEDKEY(t *testing.T) { if err != nil { t.Fatal(err) } + // check resuts if !bytes.Equal(dzp.Public().Bytes(), dzk.Bytes()) { + t.Logf("dzp.Public = %s", hex.EncodeToString(dzp.Public().Bytes())) + t.Logf("dzk = %s", hex.EncodeToString(dzk.Bytes())) t.Fatal("derive mismatch") } } diff --git a/src/gnunet/crypto/gns_pkey.go b/src/gnunet/crypto/gns_pkey.go @@ -158,7 +158,7 @@ func (pk *PKEYPublicImpl) cipher(encrypt bool, data []byte, label string, expire // ID returns the GNUnet identifier for a public zone key func (pk *PKEYPublicImpl) ID() string { - return asID(enums.GNS_TYPE_PKEY, pk.pub.Bytes()) + return util.EncodeBinaryToString(asBytes(enums.GNS_TYPE_PKEY, pk.pub.Bytes())) } //---------------------------------------------------------------------- @@ -173,15 +173,25 @@ type PKEYPrivateImpl struct { } // Init instance from binary data. The data represents a big integer -// (in big-endian notation) for the private scalar d. +// (in little-endian notation) for the private scalar d (clamped). func (pk *PKEYPrivateImpl) Init(data []byte) error { - d := math.NewIntFromBytes(data) + // generate key material + d := math.NewIntFromBytes(util.Reverse(data)) pk.prv = ed25519.NewPrivateKeyFromD(d) pk.ztype = enums.GNS_TYPE_PKEY pk.pub = pk.prv.Public() return nil } +// Prepare a random byte array to be used as a random private PKEY +func (pk *PKEYPrivateImpl) Prepare(rnd []byte) []byte { + // clamp little-endian skalar + d := util.Clone(rnd) + d[31] = (d[31] & 0x3f) | 0x40 + d[0] &= 0xf8 + return d +} + // Bytes returns a binary representation of the instance suitable for // consumption in 'Init()'. func (pk *PKEYPrivateImpl) Bytes() []byte { @@ -232,8 +242,11 @@ func (pk *PKEYPrivateImpl) Sign(data []byte) (sig *ZoneSignature, err error) { } // ID returns the GNUnet identifier for a private zone key +// (little-endian big integer) func (pk *PKEYPrivateImpl) ID() string { - return asID(enums.GNS_TYPE_PKEY, pk.prv.D.Bytes()) + return util.EncodeBinaryToString(asBytes( + enums.GNS_TYPE_PKEY, + util.Reverse(pk.prv.D.Bytes()))) } //---------------------------------------------------------------------- diff --git a/src/gnunet/crypto/gns_test.go b/src/gnunet/crypto/gns_test.go @@ -202,12 +202,13 @@ func TestVerifyBlock(t *testing.T) { func TestDeriveH(t *testing.T) { var ( D = []byte{ - // private scalar (big-endian) - 0x74, 0x50, 0xf7, 0x1d, 0xef, 0x64, 0x11, 0xe0, - 0xab, 0x0e, 0x6a, 0x1d, 0xfd, 0x1d, 0x9c, 0xcd, - 0x0e, 0xaf, 0x71, 0x95, 0x24, 0x94, 0xcc, 0xf5, - 0x1b, 0x85, 0xff, 0xac, 0x5d, 0xb0, 0x93, 0xc8, + // private scalar (clamped little-endian) + 0xc8, 0x93, 0xb0, 0x5d, 0xac, 0xff, 0x85, 0x1b, + 0xf5, 0xcc, 0x94, 0x24, 0x95, 0x71, 0xaf, 0x0e, + 0xcd, 0x9c, 0x1d, 0xfd, 0x1d, 0x6a, 0x0e, 0xab, + 0xe0, 0x11, 0x64, 0xef, 0x1d, 0xf7, 0x50, 0x74, } + PUB = []byte{ // zone type 0x00, 0x01, 0x00, 0x00, @@ -223,29 +224,19 @@ func TestDeriveH(t *testing.T) { CONTEXT = "gns" H = []byte{ - 0x06, 0x5b, 0xb7, 0x42, 0x12, 0xa1, 0xae, 0xc3, - 0x59, 0x68, 0xdd, 0xdb, 0xca, 0xa3, 0x48, 0xfc, - 0xb0, 0xcd, 0x89, 0xd4, 0xcf, 0x9a, 0xe0, 0xfe, - 0xd1, 0xf9, 0xab, 0x6b, 0xd4, 0x28, 0xf4, 0x95, + 0x07, 0x1e, 0xfc, 0xa7, 0xdb, 0x28, 0x50, 0xbd, + 0x6f, 0x35, 0x4e, 0xbf, 0xe3, 0x8c, 0x5b, 0xbf, + 0xd6, 0xba, 0x2f, 0x80, 0x5c, 0xd8, 0xd3, 0xb5, + 0x4e, 0xdd, 0x7f, 0x3d, 0xd0, 0x73, 0x0d, 0x1a, } Q = []byte{ - // zone type + // zone type (PKEY) 0x00, 0x01, 0x00, 0x00, // derived public key data - 0xb1, 0x0e, 0x88, 0xd5, 0x17, 0x02, 0xf3, 0x3d, - 0xc9, 0xcb, 0xa1, 0xe9, 0x16, 0x65, 0x9c, 0x44, - 0x47, 0x9c, 0xc8, 0xdb, 0x83, 0x32, 0xd1, 0xd1, - 0xc5, 0x03, 0xdb, 0x50, 0x0e, 0xbd, 0x2d, 0x67, - } - QUERY = []byte{ - 0xa9, 0x47, 0x81, 0x8a, 0xaf, 0x45, 0x94, 0xda, - 0x89, 0x41, 0xfa, 0x29, 0x77, 0x53, 0x94, 0x9d, - 0xcb, 0xc5, 0xfb, 0x41, 0xea, 0x77, 0xc6, 0x25, - 0x11, 0x3a, 0x59, 0x09, 0x32, 0xfe, 0xeb, 0xb4, - 0x59, 0x98, 0x69, 0xe2, 0x83, 0xe9, 0xdb, 0xd9, - 0xc7, 0x24, 0xeb, 0xf2, 0xd5, 0x30, 0x3b, 0x73, - 0xd7, 0xda, 0x9a, 0x2c, 0xd1, 0xd7, 0x95, 0x70, - 0xc5, 0x9d, 0x71, 0xb8, 0x32, 0x68, 0xc9, 0xd1, + 0x9f, 0x27, 0xad, 0x25, 0xb5, 0x95, 0x4a, 0x46, + 0x7b, 0xc6, 0x5a, 0x67, 0x6b, 0x7a, 0x6d, 0x23, + 0xb2, 0xef, 0x30, 0x0f, 0x7f, 0xc7, 0x00, 0x58, + 0x05, 0x9e, 0x7f, 0x29, 0xe5, 0x94, 0xb5, 0xc1, } ) @@ -255,7 +246,7 @@ func TestDeriveH(t *testing.T) { t.Fatal(err) } - // derive and checkpublic key + // derive and check public key pub := prv.Public() if !bytes.Equal(pub.Bytes(), PUB) { t.Fatal("wrong public key") @@ -290,16 +281,6 @@ func TestDeriveH(t *testing.T) { } t.Fatal("x-coordinate mismatch") } - - // test query - out := sha512.Sum512(dpub.Bytes()) - if !bytes.Equal(out[:], QUERY) { - if testing.Verbose() { - t.Log("query(computed) = " + hex.EncodeToString(out[:])) - t.Log("query(expected) = " + hex.EncodeToString(QUERY)) - } - t.Fatal("Query mismatch") - } } func TestHKDF_gnunet(t *testing.T) { diff --git a/src/gnunet/enums/results.go b/src/gnunet/enums/results.go @@ -0,0 +1,31 @@ +// This file is part of gnunet-go, a GNUnet-implementation in Golang. +// Copyright (C) 2019-2022 Bernd Fix >Y< +// +// gnunet-go is free software: you can redistribute it and/or modify it +// under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// gnunet-go is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// SPDX-License-Identifier: AGPL3.0-or-later + +//nolint:stylecheck // allow non-camel-case for constants +package enums + +// ResultCode type +type ResultCode int32 + +// ResultCode values +const ( + RC_SYSERR ResultCode = -1 + RC_NO ResultCode = 0 + RC_YES ResultCode = 1 + RC_OK ResultCode = 1 +) diff --git a/src/gnunet/go.mod b/src/gnunet/go.mod @@ -3,7 +3,7 @@ module gnunet go 1.18 require ( - github.com/bfix/gospel v1.2.20 + github.com/bfix/gospel v1.2.21 github.com/go-redis/redis/v8 v8.11.5 github.com/go-sql-driver/mysql v1.6.0 github.com/gorilla/mux v1.8.0 @@ -24,4 +24,4 @@ require ( golang.org/x/tools v0.1.11 // indirect ) -// replace github.com/bfix/gospel v1.2.20 => ../gospel +// replace github.com/bfix/gospel v1.2.21 => ../gospel diff --git a/src/gnunet/go.sum b/src/gnunet/go.sum @@ -1,5 +1,5 @@ -github.com/bfix/gospel v1.2.20 h1:e/IxmTiC579jIQlIxpMzCX/MIKHNsBzJ1WdMKheCgBw= -github.com/bfix/gospel v1.2.20/go.mod h1:cdu63bA9ZdfeDoqZ+vnWOcbY9Puwdzmf5DMxMGMznRI= +github.com/bfix/gospel v1.2.21 h1:rgllMlR+2AZt6+x0uaBF67a+pM7fJxHiO93amhKXZNU= +github.com/bfix/gospel v1.2.21/go.mod h1:cdu63bA9ZdfeDoqZ+vnWOcbY9Puwdzmf5DMxMGMznRI= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= diff --git a/src/gnunet/message/factory.go b/src/gnunet/message/factory.go @@ -31,6 +31,7 @@ func NewEmptyMessage(msgType enums.MsgType) (Message, error) { //------------------------------------------------------------------ // Transport //------------------------------------------------------------------ + case enums.MSG_TRANSPORT_TCP_WELCOME: return NewTransportTCPWelcomeMsg(nil), nil case enums.MSG_HELLO: @@ -53,12 +54,14 @@ func NewEmptyMessage(msgType enums.MsgType) (Message, error) { //------------------------------------------------------------------ // Core //------------------------------------------------------------------ + case enums.MSG_CORE_EPHEMERAL_KEY: return NewEphemeralKeyMsg(), nil //------------------------------------------------------------------ // DHT //------------------------------------------------------------------ + case enums.MSG_DHT_CLIENT_PUT: return NewDHTClientPutMsg(nil, 0, nil), nil case enums.MSG_DHT_CLIENT_GET: @@ -73,6 +76,7 @@ func NewEmptyMessage(msgType enums.MsgType) (Message, error) { //------------------------------------------------------------------ // DHT-P2P //------------------------------------------------------------------ + case enums.MSG_DHT_P2P_HELLO: return NewDHTP2PHelloMsg(), nil case enums.MSG_DHT_P2P_GET: @@ -85,6 +89,7 @@ func NewEmptyMessage(msgType enums.MsgType) (Message, error) { //------------------------------------------------------------------ // GNS //------------------------------------------------------------------ + case enums.MSG_GNS_LOOKUP: return NewGNSLookupMsg(), nil case enums.MSG_GNS_LOOKUP_RESULT: @@ -93,6 +98,7 @@ func NewEmptyMessage(msgType enums.MsgType) (Message, error) { //------------------------------------------------------------------ // Namecache //------------------------------------------------------------------ + case enums.MSG_NAMECACHE_LOOKUP_BLOCK: return NewNamecacheLookupMsg(nil), nil case enums.MSG_NAMECACHE_LOOKUP_BLOCK_RESPONSE: @@ -105,6 +111,7 @@ func NewEmptyMessage(msgType enums.MsgType) (Message, error) { //------------------------------------------------------------------ // Revocation //------------------------------------------------------------------ + case enums.MSG_REVOCATION_QUERY: return NewRevocationQueryMsg(nil), nil case enums.MSG_REVOCATION_QUERY_RESPONSE: @@ -115,8 +122,32 @@ func NewEmptyMessage(msgType enums.MsgType) (Message, error) { return NewRevocationRevokeResponseMsg(false), nil //------------------------------------------------------------------ + // Identity service + //------------------------------------------------------------------ + + case enums.MSG_IDENTITY_START: + return NewIdentityStartMsg(), nil + case enums.MSG_IDENTITY_RESULT_CODE: + return NewIdentityResultCodeMsg(enums.RC_OK, ""), nil + case enums.MSG_IDENTITY_UPDATE: + return NewIdentityUpdateMsg("", nil), nil + case enums.MSG_IDENTITY_CREATE: + return NewIdentityCreateMsg(nil, ""), nil + case enums.MSG_IDENTITY_RENAME: + return NewIdentityRenameMsg("", ""), nil + case enums.MSG_IDENTITY_DELETE: + return NewIdentityDeleteMsg(""), nil + case enums.MSG_IDENTITY_LOOKUP: + return NewIdentityLookupMsg(""), nil + case enums.MSG_IDENTITY_GET_DEFAULT: + return NewIdentityGetDefaultMsg(""), nil + case enums.MSG_IDENTITY_SET_DEFAULT: + return NewIdentitySetDefaultMsg(nil, ""), nil + + //------------------------------------------------------------------ // Namestore service //------------------------------------------------------------------ + case enums.MSG_NAMESTORE_ZONE_ITERATION_START: return NewNamestoreZoneIterStartMsg(nil), nil case enums.MSG_NAMESTORE_ZONE_ITERATION_NEXT: @@ -130,6 +161,7 @@ func NewEmptyMessage(msgType enums.MsgType) (Message, error) { case enums.MSG_NAMESTORE_MONITOR_START: case enums.MSG_NAMESTORE_MONITOR_SYNC: case enums.MSG_NAMESTORE_RECORD_RESULT: + return NewNamestoreRecordResultMsg(nil, ""), nil case enums.MSG_NAMESTORE_MONITOR_NEXT: } return nil, fmt.Errorf("unknown message type %d", msgType) diff --git a/src/gnunet/message/message.go b/src/gnunet/message/message.go @@ -43,6 +43,9 @@ type Message interface { // String returns a human-readable message String() string + + // Init called after unmarshalling a message to setup internal state + Init() error } //---------------------------------------------------------------------- diff --git a/src/gnunet/message/msg_core.go b/src/gnunet/message/msg_core.go @@ -45,7 +45,7 @@ type EphKeyBlock struct { type EphemeralKeyMsg struct { MsgHeader SenderStatus uint32 `order:"big"` // enum PeerStateMachine - Signature *util.PeerSignature `` // EdDSA signature + Signature *util.PeerSignature `init:"Init"` // EdDSA signature SignedBlock *EphKeyBlock } @@ -68,6 +68,9 @@ func NewEphemeralKeyMsg() *EphemeralKeyMsg { } } +// Init called after unmarshalling a message to setup internal state +func (m *EphemeralKeyMsg) Init() error { return nil } + // String returns a human-readable representation of the message. func (m *EphemeralKeyMsg) String() string { return fmt.Sprintf("EphKeyMsg{peer=%s,ephkey=%s,create=%s,expire=%s,status=%d}", diff --git a/src/gnunet/message/msg_dht.go b/src/gnunet/message/msg_dht.go @@ -67,6 +67,9 @@ func (m *DHTClientPutMsg) String() string { m.BType, m.Expire, m.Options, m.ReplLevel, m.Key) } +// Init called after unmarshalling a message to setup internal state +func (m *DHTClientPutMsg) Init() error { return nil } + //---------------------------------------------------------------------- // DHT_CLIENT_GET //---------------------------------------------------------------------- @@ -113,6 +116,9 @@ func (m *DHTClientGetMsg) String() string { m.ID, m.BType, m.Options, m.ReplLevel, m.Key) } +// Init called after unmarshalling a message to setup internal state +func (m *DHTClientGetMsg) Init() error { return nil } + //---------------------------------------------------------------------- // DHT_CLIENT_RESULT //---------------------------------------------------------------------- @@ -153,6 +159,9 @@ func (m *DHTClientResultMsg) String() string { return fmt.Sprintf("DHTClientResultMsg{id:%d,type=%s,expire=%s}", m.ID, m.BType, m.Expire) } +// Init called after unmarshalling a message to setup internal state +func (m *DHTClientResultMsg) Init() error { return nil } + //---------------------------------------------------------------------- // DHT_CLIENT_GET_STOP //---------------------------------------------------------------------- @@ -183,6 +192,9 @@ func (m *DHTClientGetStopMsg) String() string { return fmt.Sprintf("DHTClientGetStopMsg{Id:%d,Key=%s}", m.ID, m.Key) } +// Init called after unmarshalling a message to setup internal state +func (m *DHTClientGetStopMsg) Init() error { return nil } + //---------------------------------------------------------------------- // DHT_CLIENT_GET_RESULTS_KNOWN //---------------------------------------------------------------------- @@ -220,3 +232,6 @@ func (m *DHTClientGetResultsKnownMsg) String() string { return fmt.Sprintf("DHTClientGetResultsKnownMsg{Id:%d,Key=%s,Num=%d}", m.ID, m.Key.Data, len(m.Known)) } + +// Init called after unmarshalling a message to setup internal state +func (m *DHTClientGetResultsKnownMsg) Init() error { return nil } diff --git a/src/gnunet/message/msg_dht_p2p.go b/src/gnunet/message/msg_dht_p2p.go @@ -76,6 +76,9 @@ func NewDHTP2PGetMsg() *DHTP2PGetMsg { } } +// Init called after unmarshalling a message to setup internal state +func (m *DHTP2PGetMsg) Init() (err error) { return nil } + // String returns a human-readable representation of the message. func (m *DHTP2PGetMsg) String() string { return fmt.Sprintf("DHTP2PGetMsg{btype=%s,hops=%d,flags=%s}", @@ -167,6 +170,11 @@ func (m *DHTP2PPutMsg) IsUsed(field string) bool { return false } +// Init called after unmarshalling a message to setup internal state +func (m *DHTP2PPutMsg) Init() (err error) { + return nil +} + //---------------------------------------------------------------------- // Update message (forwarding) @@ -287,17 +295,17 @@ func (m *DHTP2PPutMsg) String() string { // DHTP2PResultMsg wire layout type DHTP2PResultMsg struct { MsgHeader - BType enums.BlockType `order:"big"` // Block type of result - Reserved uint16 `order:"big"` // Reserved - Flags uint16 `order:"big"` // Message flags - PutPathL uint16 `order:"big"` // size of PUTPATH field - GetPathL uint16 `order:"big"` // size of GETPATH field - Expire util.AbsoluteTime `` // expiration date - Query *crypto.HashCode `` // Query key for block - TruncOrigin *util.PeerID `opt:"(IsUsed)"` // truncated origin (if TRUNCATED flag set) - PathList []*path.Entry `size:"(NumPath)"` // PATH - LastSig *util.PeerSignature `opt:"(IsUsed)"` // signature of last hop (if RECORD_ROUTE flag is set) - Block []byte `size:"*"` // block data + BType enums.BlockType `order:"big"` // Block type of result + Reserved uint16 `order:"big"` // Reserved + Flags uint16 `order:"big"` // Message flags + PutPathL uint16 `order:"big"` // size of PUTPATH field + GetPathL uint16 `order:"big"` // size of GETPATH field + Expire util.AbsoluteTime `` // expiration date + Query *crypto.HashCode `` // Query key for block + TruncOrigin *util.PeerID `opt:"(IsUsed)"` // truncated origin (if TRUNCATED flag set) + PathList []*path.Entry `size:"(NumPath)"` // PATH + LastSig *util.PeerSignature `opt:"(IsUsed)" init:"Init"` // signature of last hop (if RECORD_ROUTE flag is set) + Block []byte `size:"*"` // block data } // NewDHTP2PResultMsg creates a new empty DHTP2PResultMsg @@ -330,6 +338,11 @@ func (m *DHTP2PResultMsg) NumPath(field string) uint { return uint(m.GetPathL + m.PutPathL) } +// Init called after unmarshalling a message to setup internal state +func (m *DHTP2PResultMsg) Init() (err error) { + return nil +} + //---------------------------------------------------------------------- // Path handling (get/set path in message) //---------------------------------------------------------------------- @@ -471,9 +484,12 @@ type DHTP2PHelloMsg struct { MsgHeader Reserved uint16 `order:"big"` // Reserved for further use NumAddr uint16 `order:"big"` // Number of addresses in list - Signature *util.PeerSignature `` // Signature + Signature *util.PeerSignature `init:"Init"` // Signature Expire util.AbsoluteTime `` // expiration time AddrList []byte `size:"*"` // List of end-point addresses (HelloAddress) + + // transient state + addresses []*util.Address // list of converted addresses } // NewHelloMsgDHT creates an empty DHT_P2P_HELLO message. @@ -492,8 +508,12 @@ func NewDHTP2PHelloMsg() *DHTP2PHelloMsg { } } -// Addresses returns the list of HelloAddress -func (m *DHTP2PHelloMsg) Addresses() (list []*util.Address, err error) { +// Init called after unmarshalling a message to setup internal state +func (m *DHTP2PHelloMsg) Init() (err error) { + if m.addresses != nil { + return + } + m.addresses = make([]*util.Address, 0) var addr *util.Address var as string num, pos := 0, 0 @@ -506,16 +526,25 @@ func (m *DHTP2PHelloMsg) Addresses() (list []*util.Address, err error) { return } addr.Expire = m.Expire - list = append(list, addr) + m.addresses = append(m.addresses, addr) num++ } // check numbers if num != int(m.NumAddr) { - logger.Printf(logger.WARN, "[DHTP2PHelloMsg] Number of addresses does not match (got %d, expected %d)", num, m.NumAddr) + err = errors.New("number of addresses does not match") } return } +// Addresses returns the list of HelloAddress +func (m *DHTP2PHelloMsg) Addresses() (list []*util.Address, err error) { + if m.addresses == nil { + err = errors.New("no addresses available") + return + } + return m.addresses, nil +} + // SetAddresses adds addresses to the HELLO message. func (m *DHTP2PHelloMsg) SetAddresses(list []*util.Address) { // write addresses as blob and track earliest expiration diff --git a/src/gnunet/message/msg_gns.go b/src/gnunet/message/msg_gns.go @@ -37,7 +37,7 @@ import ( type LookupMsg struct { MsgHeader ID uint32 `order:"big"` // Unique identifier for this request (for key collisions). - Zone *crypto.ZoneKey `` // Zone that is to be used for lookup + Zone *crypto.ZoneKey `init:"Init"` // Zone that is to be used for lookup Options uint16 `order:"big"` // Local options for where to look for results Reserved uint16 `order:"big"` // Always 0 RType enums.GNSType `order:"big"` // the type of record to look up @@ -81,6 +81,9 @@ func (m *LookupMsg) String() string { m.ID, m.Zone.ID(), m.Options, m.RType, m.GetName()) } +// Init called after unmarshalling a message to setup internal state +func (m *LookupMsg) Init() error { return nil } + //---------------------------------------------------------------------- // GNS_LOOKUP_RESULT //---------------------------------------------------------------------- @@ -124,3 +127,6 @@ func (m *LookupResultMsg) String() string { func (m *LookupResultMsg) Header() *MsgHeader { return &MsgHeader{m.MsgSize, m.MsgType} } + +// Init called after unmarshalling a message to setup internal state +func (m *LookupResultMsg) Init() error { return nil } diff --git a/src/gnunet/message/msg_hello.go b/src/gnunet/message/msg_hello.go @@ -145,6 +145,32 @@ type HelloMsg struct { FriendsOnly uint32 `order:"big"` // Do not gossip this HELLO message Peer *util.PeerID `` // peer identifier for addresses AddrList []byte `size:"*"` // List of end-point addresses (HelloAddress) + + // transient state + addresses []*HelloAddress // list of converted addresses +} + +// Init called after unmarshalling a message to setup internal state +func (m *HelloMsg) Init() (err error) { + // check for initialized state + if m.addresses != nil { + return nil + } + // convert addresses + m.addresses = make([]*HelloAddress, 0) + rdr := bytes.NewReader(m.AddrList) + var addr *HelloAddress + for { + // parse address from stream + if addr, err = ParseHelloAddr(rdr); err != nil { + // end of stream: no more addresses + if err == io.EOF { + err = nil + } + return + } + m.addresses = append(m.addresses, addr) + } } // NewHelloMsg creates a new HELLO msg for a given peer. @@ -164,19 +190,10 @@ func NewHelloMsg(peer *util.PeerID) *HelloMsg { // Addresses returns the list of HelloAddress func (m *HelloMsg) Addresses() (list []*HelloAddress, err error) { - rdr := bytes.NewReader(m.AddrList) - var addr *HelloAddress - for { - // parse address from stream - if addr, err = ParseHelloAddr(rdr); err != nil { - // end of stream: no more addresses - if err == io.EOF { - err = nil - } - return - } - list = append(list, addr) + if m.addresses == nil { + return } + return m.addresses, nil } // String returns a human-readable representation of the message. diff --git a/src/gnunet/message/msg_identity.go b/src/gnunet/message/msg_identity.go @@ -0,0 +1,473 @@ +// This file is part of gnunet-go, a GNUnet-implementation in Golang. +// Copyright (C) 2019-2022 Bernd Fix >Y< +// +// gnunet-go is free software: you can redistribute it and/or modify it +// under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// gnunet-go is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// SPDX-License-Identifier: AGPL3.0-or-later + +package message + +import ( + "fmt" + "gnunet/crypto" + "gnunet/enums" + "gnunet/util" +) + +//---------------------------------------------------------------------- +// MSG_IDENTITY_START +// +// Start client connection for update notification. Triggers sending +// all identities as update messages to client. +//---------------------------------------------------------------------- + +// IdentityStartMsg to initiate session. +type IdentityStartMsg struct { + MsgHeader +} + +// NewIdentityStartMsg creates an empty message +func NewIdentityStartMsg() *IdentityStartMsg { + return &IdentityStartMsg{ + MsgHeader: MsgHeader{4, enums.MSG_IDENTITY_START}, + } +} + +// Init called after unmarshalling a message to setup internal state +func (msg *IdentityStartMsg) Init() error { return nil } + +// String returns a human-readable representation of the message. +func (msg *IdentityStartMsg) String() string { + return "IdentityStartMsg{}" +} + +//---------------------------------------------------------------------- +// MSG_IDENTITY_UPDATE +// +// IdentityStore changed (send to all clients with started session) +//---------------------------------------------------------------------- + +// IdentityUpdateMsg notifies about changes in identity store +type IdentityUpdateMsg struct { + MsgHeader + + NameLen uint16 `order:"big"` + EOL uint16 `order:"big"` + ZoneKey *crypto.ZonePrivate `init:"Init"` + Name_ []byte `size:"NameLen"` + + // transient state + name string +} + +// NewIdentityUpdateMsg creates an update message. If the zone key is nil, +// a End-Of-List is triggered so the client knows we are done. +func NewIdentityUpdateMsg(name string, zk *crypto.ZonePrivate) *IdentityUpdateMsg { + msg := &IdentityUpdateMsg{ + MsgHeader: MsgHeader{8, enums.MSG_IDENTITY_UPDATE}, + } + if zk == nil { + // tag end-of-list + msg.EOL = uint16(enums.RC_YES) + var size uint16 + // assemble an empty zonekey + msg.ZoneKey, size = crypto.NullZonePrivate(enums.GNS_TYPE_PKEY) + msg.MsgSize += size + } else { + msg.name = name + msg.Name_ = util.WriteCString(name) + msg.NameLen = uint16(len(msg.Name_)) + msg.MsgSize += msg.NameLen + msg.ZoneKey = zk + msg.MsgSize += uint16(zk.KeySize() + 4) + } + return msg +} + +// Init called after unmarshalling a message to setup internal state +func (msg *IdentityUpdateMsg) Init() error { + msg.name, _ = util.ReadCString(msg.Name_, 0) + return nil +} + +// String returns a human-readable representation of the message. +func (msg *IdentityUpdateMsg) String() string { + if msg.EOL == uint16(enums.RC_OK) { + return "IdentityUpdateMsg{end-of-list}" + } + return fmt.Sprintf("IdentityUpdateMsg{'%s'@%s}", msg.Name(), msg.ZoneKey.ID()) +} + +// Name of the new identity +func (msg *IdentityUpdateMsg) Name() string { + return msg.name +} + +//---------------------------------------------------------------------- +// MSG_IDENTITY_RESULT_CODE +// +// Returned by CREATE and RENAME (and by GET_DEFAULT on failure). +//---------------------------------------------------------------------- + +// IdentityResultCodeMsg is a response message +type IdentityResultCodeMsg struct { + MsgHeader + + ResultCode enums.ResultCode `order:"big"` + Error string `opt:"(OnError)"` +} + +// OnError returns true if an error message is attached +func (msg *IdentityResultCodeMsg) OnError() bool { + return msg.ResultCode != enums.RC_OK +} + +// Init called after unmarshalling a message to setup internal state +func (msg *IdentityResultCodeMsg) Init() error { return nil } + +// NewIdentityResultCodeMsg creates a new default message. +func NewIdentityResultCodeMsg(rc enums.ResultCode, err string) *IdentityResultCodeMsg { + msg := &IdentityResultCodeMsg{ + MsgHeader: MsgHeader{ + MsgSize: 8, + MsgType: enums.MSG_IDENTITY_RESULT_CODE, + }, + ResultCode: rc, + } + if rc != enums.RC_OK { + msg.Error = err + msg.MsgSize += uint16(len(err) + 1) + } + return msg +} + +// String returns a human-readable representation of the message. +func (msg *IdentityResultCodeMsg) String() string { + return fmt.Sprintf("IdentityResultCodeMsg{rc=%d,err='%s'}", msg.ResultCode, msg.Error) +} + +//---------------------------------------------------------------------- +// MSG_IDENTITY_CREATE +// +// Create new identity with service association +//---------------------------------------------------------------------- + +// IdentityCreateMsg to create a new identity for given service +type IdentityCreateMsg struct { + MsgHeader + + NameLen uint16 `order:"big"` + Reserved uint16 `order:"big"` + ZoneKey *crypto.ZonePrivate `init:"Init"` + Name_ []byte `size:"NameLen"` + + // transient state + name string +} + +// Init called after unmarshalling a message to setup internal state +func (msg *IdentityCreateMsg) Init() error { + msg.name, _ = util.ReadCString(msg.Name_, 0) + return nil +} + +// NewIdentityCreateMsg creates a new default message. +func NewIdentityCreateMsg(zk *crypto.ZonePrivate, name string) *IdentityCreateMsg { + var size uint16 + if zk == nil { + zk, size = crypto.NullZonePrivate(enums.GNS_TYPE_PKEY) + } else { + size = uint16(zk.KeySize() + 4) + } + msg := &IdentityCreateMsg{ + MsgHeader: MsgHeader{ + MsgSize: size + 8, + MsgType: enums.MSG_IDENTITY_CREATE, + }, + ZoneKey: zk, + } + if len(name) > 0 { + msg.Name_ = util.WriteCString(name) + msg.MsgSize += uint16(len(msg.Name_)) + msg.name = name + } + return msg +} + +// String returns a human-readable representation of the message. +func (msg *IdentityCreateMsg) String() string { + return fmt.Sprintf("IdentityCreateMsg{name='%s',key=%s}", msg.name, msg.ZoneKey.ID()) +} + +// Name of the new identity +func (msg *IdentityCreateMsg) Name() string { + return msg.name +} + +//---------------------------------------------------------------------- +// MSG_IDENTITY_RENAME +// +// Rename identity +//---------------------------------------------------------------------- + +// IdentitRenameMsg to rename an identity +type IdentityRenameMsg struct { + MsgHeader + + OldNameLen uint16 `order:"big"` + NewNameLen uint16 `order:"big"` + OldName_ []byte `size:"OldNameLen"` + NewName_ []byte `size:"NewNameLen"` + + // transient state + oldName string + newName string +} + +// Init called after unmarshalling a message to setup internal state +func (msg *IdentityRenameMsg) Init() error { + msg.oldName, _ = util.ReadCString(msg.OldName_, 0) + msg.newName, _ = util.ReadCString(msg.NewName_, 0) + return nil +} + +// NewIdentityRenameMsg renames an identity +func NewIdentityRenameMsg(oldName, newName string) *IdentityRenameMsg { + msg := &IdentityRenameMsg{ + MsgHeader: MsgHeader{ + MsgSize: 8, + MsgType: enums.MSG_IDENTITY_RENAME, + }, + } + if len(oldName) > 0 { + msg.OldName_ = util.WriteCString(oldName) + msg.MsgSize += uint16(len(msg.OldName_)) + msg.oldName = oldName + } + if len(newName) > 0 { + msg.NewName_ = util.WriteCString(newName) + msg.MsgSize += uint16(len(msg.NewName_)) + msg.newName = newName + } + return msg +} + +// String returns a human-readable representation of the message. +func (msg *IdentityRenameMsg) String() string { + return fmt.Sprintf("IdentityRenameMsg{'%s'->'%s'}", msg.oldName, msg.newName) +} + +// OldName of the identity +func (msg *IdentityRenameMsg) OldName() string { + return msg.oldName +} + +// NewName of the identity +func (msg *IdentityRenameMsg) NewName() string { + return msg.newName +} + +//---------------------------------------------------------------------- +// MSG_IDENTITY_DELETE +// +// Remove named identity +//---------------------------------------------------------------------- + +// IdentityDeleteMsg requests the deletion of an identity +type IdentityDeleteMsg struct { + MsgHeader + + NameLen uint16 `order:"big"` + Reserved uint16 `order:"big"` + Name_ []byte `size:"NameLen"` + + // transient state + name string +} + +// Init called after unmarshalling a message to setup internal state +func (msg *IdentityDeleteMsg) Init() error { + msg.name, _ = util.ReadCString(msg.Name_, 0) + return nil +} + +// NewIdentityDeleteMsg renames an identity +func NewIdentityDeleteMsg(name string) *IdentityDeleteMsg { + msg := &IdentityDeleteMsg{ + MsgHeader: MsgHeader{ + MsgSize: 8, + MsgType: enums.MSG_IDENTITY_DELETE, + }, + } + if len(name) > 0 { + msg.Name_ = util.WriteCString(name) + msg.MsgSize += uint16(len(msg.Name_)) + msg.name = name + } + return msg +} + +// String returns a human-readable representation of the message. +func (msg *IdentityDeleteMsg) String() string { + return fmt.Sprintf("IdentityDeleteMsg{name='%s'}", msg.name) +} + +// Name of the removed identity +func (msg *IdentityDeleteMsg) Name() string { + return msg.name +} + +//---------------------------------------------------------------------- +// MSG_IDENTITY_LOOKUP +// +// Return default identity +//---------------------------------------------------------------------- + +// IdentityLookupMsg to lookup named identity +type IdentityLookupMsg struct { + MsgHeader + + Name string +} + +// Init called after unmarshalling a message to setup internal state +func (msg *IdentityLookupMsg) Init() error { + return nil +} + +// NewIdentityLookupMsg renames an identity +func NewIdentityLookupMsg(name string) *IdentityLookupMsg { + return &IdentityLookupMsg{ + MsgHeader: MsgHeader{ + MsgSize: uint16(len(name) + 9), + MsgType: enums.MSG_IDENTITY_DELETE, + }, + Name: name, + } +} + +// String returns a human-readable representation of the message. +func (msg *IdentityLookupMsg) String() string { + return fmt.Sprintf("IdentityLookupMsg{name='%s'}", msg.Name) +} + +//---------------------------------------------------------------------- +// MSG_IDENTITY_GET_DEFAULT +// +// Get the default identity for named subsystem +//---------------------------------------------------------------------- + +// IdentityGetDefault to retrieve the default identity for a service +type IdentityGetDefaultMsg struct { + MsgHeader + + SrvLen uint16 `order:"big"` + Reserved uint16 `order:"big"` + Service_ []byte `size:"SrvLen"` + + // transient state + service string +} + +// Init called after unmarshalling a message to setup internal state +func (msg *IdentityGetDefaultMsg) Init() error { + msg.service, _ = util.ReadCString(msg.Service_, 0) + return nil +} + +// NewIdentityGetDefaultMsg creates a new message +func NewIdentityGetDefaultMsg(svc string) *IdentityGetDefaultMsg { + msg := &IdentityGetDefaultMsg{ + MsgHeader: MsgHeader{ + MsgSize: 8, + MsgType: enums.MSG_IDENTITY_DELETE, + }, + } + if len(svc) > 0 { + msg.Service_ = util.WriteCString(svc) + msg.MsgSize += uint16(len(msg.Service_)) + msg.service = svc + } + return msg +} + +// String returns a human-readable representation of the message. +func (msg *IdentityGetDefaultMsg) String() string { + return fmt.Sprintf("IdentityGetDefaultMsg{svc='%s'}", msg.service) +} + +// Service name +func (msg *IdentityGetDefaultMsg) Service() string { + return msg.service +} + +//---------------------------------------------------------------------- +// MSG_IDENTITY_SET_DEFAULT +// +// Set default identity for named subsystem +//---------------------------------------------------------------------- + +// IdentitySetDefaultMsg sets a default identity (key) for a service +type IdentitySetDefaultMsg struct { + MsgHeader + + SrvLen uint16 `order:"big"` + Reserved uint16 `order:"big"` + ZoneKey *crypto.ZonePrivate `init:"Init"` + Service_ []byte `size:"SrvLen"` + + // transient state + service string +} + +// Init called after unmarshalling a message to setup internal state +func (msg *IdentitySetDefaultMsg) Init() error { + msg.service, _ = util.ReadCString(msg.Service_, 0) + return nil +} + +// NewIdentitySetDefaultMsg renames an identity +func NewIdentitySetDefaultMsg(zk *crypto.ZonePrivate, svc string) *IdentitySetDefaultMsg { + msg := &IdentitySetDefaultMsg{ + MsgHeader: MsgHeader{ + MsgSize: 8, + MsgType: enums.MSG_IDENTITY_DELETE, + }, + } + if zk == nil { + // assemble an empty zonekey + var size uint16 + msg.ZoneKey, size = crypto.NullZonePrivate(enums.GNS_TYPE_PKEY) + msg.MsgSize += size + } else { + msg.ZoneKey = zk + msg.MsgSize += uint16(zk.KeySize() + 4) + } + if len(svc) > 0 { + msg.Service_ = util.WriteCString(svc) + msg.MsgSize += uint16(len(msg.Service_)) + msg.service = svc + } + return msg +} + +// String returns a human-readable representation of the message. +func (msg *IdentitySetDefaultMsg) String() string { + return fmt.Sprintf("IdentitySetDefaultMsg{key=%s,svc='%s'}", msg.ZoneKey.ID(), msg.service) +} + +// Service name +func (msg *IdentitySetDefaultMsg) Service() string { + return msg.service +} diff --git a/src/gnunet/message/msg_namecache.go b/src/gnunet/message/msg_namecache.go @@ -67,6 +67,9 @@ func NewNamecacheLookupMsg(query *crypto.HashCode) *NamecacheLookupMsg { } } +// Init called after unmarshalling a message to setup internal state +func (m *NamecacheLookupMsg) Init() error { return nil } + // String returns a human-readable representation of the message. func (m *NamecacheLookupMsg) String() string { return fmt.Sprintf("NamecacheLookupMsg{Id=%d,Query=%s}", @@ -81,11 +84,14 @@ func (m *NamecacheLookupMsg) String() string { type NamecacheLookupResultMsg struct { GenericNamecacheMsg - Expire util.AbsoluteTime `` // Expiration time - DerivedKeySig *crypto.ZoneSignature `` // Derived public key - EncData []byte `size:"*"` // Encrypted block data + Expire util.AbsoluteTime `` // Expiration time + DerivedKeySig *crypto.ZoneSignature `init:"Init"` // Derived public key + EncData []byte `size:"*"` // Encrypted block data } +// Init called after unmarshalling a message to setup internal state +func (m *NamecacheLookupResultMsg) Init() error { return nil } + // NewNamecacheLookupResultMsg creates a new default message. func NewNamecacheLookupResultMsg() *NamecacheLookupResultMsg { return &NamecacheLookupResultMsg{ @@ -116,6 +122,9 @@ type NamecacheCacheMsg struct { EncData []byte `size:"*"` // Encrypted block data } +// Init called after unmarshalling a message to setup internal state +func (m *NamecacheCacheMsg) Init() error { return nil } + // Size returns buffer sizes for fields func (m *NamecacheCacheMsg) FldSize(field string) uint { switch field { @@ -174,6 +183,9 @@ func NewNamecacheCacheResponseMsg() *NamecacheCacheResponseMsg { } } +// Init called after unmarshalling a message to setup internal state +func (m *NamecacheCacheResponseMsg) Init() error { return nil } + // String returns a human-readable representation of the message. func (m *NamecacheCacheResponseMsg) String() string { return fmt.Sprintf("NamecacheCacheResponseMsg{id=%d,result=%d}", diff --git a/src/gnunet/message/msg_namestore.go b/src/gnunet/message/msg_namestore.go @@ -52,7 +52,7 @@ func newGenericNamestoreMsg(size uint16, mtype enums.MsgType) GenericNamestoreMs type NamestoreZoneIterStartMsg struct { GenericNamestoreMsg - ZoneKey *crypto.ZonePrivate // private zone key + ZoneKey *crypto.ZonePrivate `init:"Init"` // private zone key } // NewNamecacheCacheMsg creates a new default message. @@ -63,6 +63,9 @@ func NewNamestoreZoneIterStartMsg(zone *crypto.ZonePrivate) *NamestoreZoneIterSt } } +// Init called after unmarshalling a message to setup internal state +func (m *NamestoreZoneIterStartMsg) Init() error { return nil } + // String returns a human-readable representation of the message. func (m *NamestoreZoneIterStartMsg) String() string { return fmt.Sprintf("NamestoreZoneIterStartMsg{id=%d,zone=%s}", m.ID, m.ZoneKey.ID()) @@ -82,6 +85,9 @@ func NewNamestoreZoneIterNextMsg() *NamestoreZoneIterNextMsg { return &NamestoreZoneIterNextMsg{} } +// Init called after unmarshalling a message to setup internal state +func (m *NamestoreZoneIterNextMsg) Init() error { return nil } + // String returns a human-readable representation of the message. func (m *NamestoreZoneIterNextMsg) String() string { return fmt.Sprintf("NamestoreZoneIterNextMsg{id=%d,limit=%d}", m.ID, m.Limit) @@ -96,6 +102,42 @@ type NamestoreZoneIterStopMsg struct { } //---------------------------------------------------------------------- +// MSG_NAMESTORE_RECORD_RESULT +//---------------------------------------------------------------------- + +type NamestoreRecordResultMsg struct { + GenericNamestoreMsg + + Expire util.AbsoluteTime `` // expiration date + NameLen uint16 `order:"big"` // length of name + RdLen uint16 `order:"big"` // size of record data + RdCount uint16 `order:"big"` // number of records + Reserved uint16 `order:"big"` // alignment + ZoneKey *crypto.ZonePrivate `init:"Init"` // private zone key + Name []byte `size:"NameLen"` // name string + Records []byte `size:"RdLen"` // serialized record data +} + +func NewNamestoreRecordResultMsg(zk *crypto.ZonePrivate, label string) *NamestoreRecordResultMsg { + return &NamestoreRecordResultMsg{ + Expire: util.AbsoluteTimeNever(), + ZoneKey: zk, + NameLen: uint16(len(label)), + Name: []byte(label), + RdLen: 0, + RdCount: 0, + } +} + +// Init called after unmarshalling a message to setup internal state +func (m *NamestoreRecordResultMsg) Init() error { return nil } + +// String returns a human-readable representation of the message. +func (m *NamestoreRecordResultMsg) String() string { + return fmt.Sprintf("NamestoreRecordResultMsg{id=%d,zone=%s,label='%s'}", m.ID, m.ZoneKey.ID(), string(m.Name)) +} + +//---------------------------------------------------------------------- //---------------------------------------------------------------------- type NamestoreRecordStoreMsg struct { @@ -154,19 +196,6 @@ type NamestoreZoneToNameRespMsg struct { Records []byte `size:"RdLen"` // serialized record data } -type NamestoreRecordResultMsg struct { - GenericNamestoreMsg - - Expire util.AbsoluteTime `` // expiration date - NameLen uint16 `order:"big"` // length of name - RdLen uint16 `order:"big"` // size of record data - RdCount uint16 `order:"big"` // number of records - Reserved uint16 `order:"big"` // alignment - ZoneKey *crypto.ZonePrivate // private zone key - Name []byte `size:"NameLen"` // name string - Records []byte `size:"RdLen"` // serialized record data -} - type NamestoreTxControlMsg struct { GenericNamestoreMsg diff --git a/src/gnunet/message/msg_revocation.go b/src/gnunet/message/msg_revocation.go @@ -34,7 +34,7 @@ import ( type RevocationQueryMsg struct { MsgHeader Reserved uint32 `order:"big"` // Reserved for future use - Zone *crypto.ZoneKey // Zone that is to be checked for revocation + Zone *crypto.ZoneKey `init:"Init"` // Zone that is to be checked for revocation } // NewRevocationQueryMsg creates a new message for a given zone. @@ -46,6 +46,9 @@ func NewRevocationQueryMsg(zkey *crypto.ZoneKey) *RevocationQueryMsg { } } +// Init called after unmarshalling a message to setup internal state +func (m *RevocationQueryMsg) Init() error { return nil } + // String returns a human-readable representation of the message. func (m *RevocationQueryMsg) String() string { return fmt.Sprintf("RevocationQueryMsg{zone=%s}", m.Zone.ID()) @@ -73,6 +76,9 @@ func NewRevocationQueryResponseMsg(revoked bool) *RevocationQueryResponseMsg { } } +// Init called after unmarshalling a message to setup internal state +func (m *RevocationQueryResponseMsg) Init() error { return nil } + // String returns a human-readable representation of the message. func (m *RevocationQueryResponseMsg) String() string { return fmt.Sprintf("RevocationQueryResponseMsg{valid=%d}", m.Valid) @@ -88,7 +94,7 @@ type RevocationRevokeMsg struct { Timestamp util.AbsoluteTime `` // Timestamp of revocation creation TTL util.RelativeTime `` // TTL of revocation PoWs []uint64 `size:"32" order:"big"` // (Sorted) list of PoW values - ZoneKeySig *crypto.ZoneSignature `` // public zone key (with signature) to be revoked + ZoneKeySig *crypto.ZoneSignature `init:"Init"` // public zone key (with signature) to be revoked } // NewRevocationRevokeMsg creates a new message for a given zone. @@ -102,6 +108,9 @@ func NewRevocationRevokeMsg(zsig *crypto.ZoneSignature) *RevocationRevokeMsg { } } +// Init called after unmarshalling a message to setup internal state +func (m *RevocationRevokeMsg) Init() error { return nil } + // String returns a human-readable representation of the message. func (m *RevocationRevokeMsg) String() string { return fmt.Sprintf("RevocationRevokeMsg{zone=%s,expire=%s}", @@ -130,6 +139,9 @@ func NewRevocationRevokeResponseMsg(success bool) *RevocationRevokeResponseMsg { } } +// Init called after unmarshalling a message to setup internal state +func (m *RevocationRevokeResponseMsg) Init() error { return nil } + // String returns a human-readable representation of the message. func (m *RevocationRevokeResponseMsg) String() string { return fmt.Sprintf("RevocationRevokeResponseMsg{success=%v}", m.Success == 1) diff --git a/src/gnunet/message/msg_transport.go b/src/gnunet/message/msg_transport.go @@ -57,6 +57,9 @@ func (m *TransportTCPWelcomeMsg) String() string { return fmt.Sprintf("TransportTcpWelcomeMsg{peer=%s}", m.PeerID) } +// Init called after unmarshalling a message to setup internal state +func (m *TransportTCPWelcomeMsg) Init() error { return nil } + //---------------------------------------------------------------------- // TRANSPORT_PING // @@ -105,6 +108,9 @@ func (m *TransportPingMsg) String() string { m.Target, a, m.Challenge) } +// Init called after unmarshalling a message to setup internal state +func (m *TransportPingMsg) Init() error { return nil } + //---------------------------------------------------------------------- // TRANSPORT_PONG // @@ -206,6 +212,9 @@ func (m *TransportPongMsg) Verify(pub *ed25519.PublicKey) (bool, error) { return pub.EdVerify(data, sig) } +// Init called after unmarshalling a message to setup internal state +func (m *TransportPongMsg) Init() error { return nil } + //---------------------------------------------------------------------- // TRANSPORT_SESSION_ACK //---------------------------------------------------------------------- @@ -227,6 +236,9 @@ func (m *SessionAckMsg) String() string { return "SessionAck{}" } +// Init called after unmarshalling a message to setup internal state +func (m *SessionAckMsg) Init() error { return nil } + //---------------------------------------------------------------------- // TRANSPORT_SESSION_SYN //---------------------------------------------------------------------- @@ -252,6 +264,9 @@ func (m *SessionSynMsg) String() string { return fmt.Sprintf("SessionSyn{timestamp=%s}", m.Timestamp) } +// Init called after unmarshalling a message to setup internal state +func (m *SessionSynMsg) Init() error { return nil } + //---------------------------------------------------------------------- // TRANSPORT_SESSION_SYN_ACK //---------------------------------------------------------------------- @@ -277,6 +292,9 @@ func (m *SessionSynAckMsg) String() string { return fmt.Sprintf("SessionSynAck{timestamp=%s}", m.Timestamp) } +// Init called after unmarshalling a message to setup internal state +func (m *SessionSynAckMsg) Init() error { return nil } + //---------------------------------------------------------------------- // TRANSPORT_SESSION_QUOTA //---------------------------------------------------------------------- @@ -303,6 +321,9 @@ func (m *SessionQuotaMsg) String() string { return fmt.Sprintf("SessionQuotaMsg{%sB/s}", util.Scale1024(uint64(m.Quota))) } +// Init called after unmarshalling a message to setup internal state +func (m *SessionQuotaMsg) Init() error { return nil } + //---------------------------------------------------------------------- // TRANSPORT_SESSION_KEEPALIVE //---------------------------------------------------------------------- @@ -327,6 +348,9 @@ func (m *SessionKeepAliveMsg) String() string { return fmt.Sprintf("SessionKeepAliveMsg{%d}", m.Nonce) } +// Init called after unmarshalling a message to setup internal state +func (m *SessionKeepAliveMsg) Init() error { return nil } + //---------------------------------------------------------------------- // TRANSPORT_SESSION_KEEPALIVE_RESPONSE //---------------------------------------------------------------------- @@ -350,3 +374,6 @@ func NewSessionKeepAliveRespMsg(nonce uint32) *SessionKeepAliveRespMsg { func (m *SessionKeepAliveRespMsg) String() string { return fmt.Sprintf("SessionKeepAliveRespMsg{%d}", m.Nonce) } + +// Init called after unmarshalling a message to setup internal state +func (m *SessionKeepAliveRespMsg) Init() error { return nil } diff --git a/src/gnunet/service/connection.go b/src/gnunet/service/connection.go @@ -134,6 +134,9 @@ func (s *Connection) Receive(ctx context.Context) (message.Message, error) { if err = data.Unmarshal(msg, s.buf[:mh.MsgSize]); err != nil { return nil, err } + if err = msg.Init(); err != nil { + return nil, err + } return msg, nil } diff --git a/src/gnunet/service/dht/blocks/gns.go b/src/gnunet/service/dht/blocks/gns.go @@ -235,7 +235,7 @@ func NewRecordSet() *RecordSet { return &RecordSet{ Count: 0, Records: make([]*ResourceRecord, 0), - Padding: make([]byte, 0), + Padding: nil, } } @@ -273,6 +273,11 @@ func (rs *RecordSet) Expire() util.AbsoluteTime { // Bytes returns the binary representation func (rs *RecordSet) Bytes() []byte { + // make sure padding exists + if rs.Padding == nil { + rs.SetPadding() + } + // unmarshal record set buf, err := data.Marshal(rs) if err != nil { return nil diff --git a/src/gnunet/service/dht/blocks/gns_test.go b/src/gnunet/service/dht/blocks/gns_test.go @@ -31,41 +31,41 @@ import ( func TestGNSBlock(t *testing.T) { var ( - ZONEKEY = "000G054G4G3HWZP2WFNVS1XJ4VXWY85G49AVYBZ7TV4EWP5J5V59H5QN40" + ZONEKEY = "000G051J6AZ48NAJP94HD6CNBXNN9C85YT8GYVVXFKGY2516YGF7HRXKR4" LABEL = "@" QKEY = []byte{ - 0xb6, 0x48, 0xfd, 0x0c, 0x4a, 0x6c, 0xaa, 0x87, - 0x33, 0x2f, 0xf5, 0x12, 0x90, 0xe4, 0xbd, 0x55, - 0x0f, 0x8c, 0xe7, 0x9b, 0xc9, 0x5b, 0x3a, 0xfb, - 0xbb, 0xe2, 0xd7, 0x33, 0xbc, 0x32, 0xc9, 0x7d, - 0xc5, 0x4a, 0x56, 0x22, 0xbf, 0xfa, 0x49, 0x1a, - 0x60, 0xd6, 0xdb, 0x77, 0x5d, 0x3d, 0x18, 0x99, - 0x5b, 0x4f, 0xc3, 0x7d, 0x86, 0x00, 0x15, 0x76, - 0x42, 0x03, 0x98, 0xcc, 0xdf, 0x83, 0x4d, 0x21, + 0xb5, 0xbb, 0xf8, 0x43, 0xfa, 0x6f, 0x7d, 0x53, + 0xb9, 0x84, 0x9a, 0xa1, 0x61, 0xc9, 0x44, 0x4f, + 0x29, 0x68, 0x98, 0x02, 0x36, 0x26, 0xc5, 0xd2, + 0xd3, 0x06, 0x21, 0x99, 0xf7, 0x39, 0x06, 0x46, + 0xdb, 0x32, 0x07, 0xa6, 0x0d, 0xd2, 0xea, 0x03, + 0xce, 0x74, 0x93, 0x86, 0x78, 0x3a, 0x70, 0xaa, + 0xa0, 0x75, 0xf1, 0x18, 0xf7, 0xb3, 0xc6, 0x0b, + 0x12, 0x98, 0xbb, 0x1b, 0x58, 0x53, 0xe6, 0x87, } BLK = []byte{ - 0x00, 0x01, 0x00, 0x14, 0xe0, 0x6b, 0xea, 0x2b, - 0x1b, 0xd6, 0xc6, 0x9a, 0xd4, 0x30, 0xa5, 0x0f, - 0x81, 0x16, 0x89, 0xe1, 0x9f, 0xca, 0x1f, 0x86, - 0x3f, 0x83, 0x6e, 0xe6, 0xa7, 0x54, 0x97, 0xde, - 0xf2, 0xc4, 0x2a, 0x84, 0xb6, 0x89, 0xe6, 0x7e, - 0xff, 0x0c, 0xae, 0x84, 0xe6, 0xb1, 0x6c, 0x72, - 0x83, 0x09, 0x68, 0x5b, 0x2f, 0xa2, 0x9f, 0xbe, - 0xfa, 0xef, 0x43, 0x52, 0x20, 0x48, 0xe5, 0x57, - 0x1e, 0x65, 0x21, 0x86, 0xd4, 0x9f, 0x96, 0x51, - 0x4f, 0xa9, 0x6d, 0xa9, 0x98, 0xaa, 0x2d, 0xf6, - 0x92, 0xd7, 0x86, 0x36, 0xc0, 0x84, 0x90, 0x00, - 0x42, 0x2e, 0x4e, 0xc1, 0xaf, 0x6f, 0xe0, 0x7e, - 0x71, 0xe3, 0xc4, 0x0d, 0x00, 0x00, 0x00, 0x10, - 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x08, 0x00, - 0xb5, 0x99, 0x2a, 0x00, 0xd0, 0x7a, 0x2b, 0x9e, - 0x02, 0x45, 0x54, 0x0d, 0x65, 0x26, 0xa1, 0x05, - 0x80, 0x26, 0xce, 0xc2, 0x70, 0xd5, 0x22, 0x38, - 0x80, 0x9a, 0xed, 0x63, 0x2f, 0x96, 0x60, 0x4d, - 0x02, 0x59, 0xd0, 0x9a, 0x4e, 0x71, 0xfa, 0x30, - 0xd6, 0xf9, 0xf4, 0x84, 0x5d, 0xb8, 0x60, 0xa4, - 0xdf, 0xea, 0x34, 0x06, 0x3f, 0x6f, 0x76, 0x9e, + 0x00, 0x01, 0x00, 0x14, 0x64, 0x0e, 0x2f, 0x4b, + 0x8e, 0x7e, 0x7f, 0x54, 0x43, 0xb4, 0x7c, 0xdc, + 0x84, 0xd4, 0x89, 0xfc, 0x87, 0x6e, 0x08, 0x6c, + 0xd9, 0x55, 0xab, 0xc6, 0x0a, 0x7d, 0xfe, 0x1b, + 0xfe, 0x88, 0xa7, 0x13, 0x72, 0x2b, 0xfa, 0xb1, + 0x70, 0xc4, 0x8c, 0xeb, 0xe3, 0x2b, 0x08, 0x9f, + 0x07, 0xea, 0x77, 0x51, 0x2d, 0xf1, 0x02, 0x89, + 0x73, 0xa8, 0xd0, 0xe0, 0x94, 0x2a, 0x16, 0x05, + 0xdb, 0x06, 0x0e, 0xd4, 0x0f, 0xdc, 0xac, 0x48, + 0xde, 0x05, 0x2c, 0x0f, 0x29, 0x81, 0xd6, 0x70, + 0x17, 0x95, 0x76, 0x79, 0x75, 0xce, 0x0d, 0x4b, + 0x98, 0xd7, 0xe5, 0x44, 0x70, 0x3b, 0xbd, 0x50, + 0xba, 0xfa, 0x5d, 0x01, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x09, 0x70, + 0x48, 0xa4, 0xa2, 0x00, 0x45, 0xb0, 0xd0, 0xdc, + 0x1d, 0x60, 0xd8, 0x1c, 0x4f, 0xd3, 0x50, 0xc4, + 0x73, 0x20, 0xc6, 0xd5, 0x28, 0x19, 0x8e, 0xa6, + 0x89, 0x56, 0x9f, 0x8c, 0x48, 0xf4, 0xd6, 0x76, + 0xf4, 0xb9, 0x70, 0xbd, 0x01, 0x2e, 0xd1, 0x4f, + 0x84, 0x96, 0x1b, 0xbf, 0x6c, 0xe4, 0xdb, 0x7a, + 0x32, 0xc1, 0x50, 0xc4, 0xb1, 0x6b, 0x08, 0x59, } ) // unmarshal block @@ -89,7 +89,10 @@ func TestGNSBlock(t *testing.T) { query := NewGNSQuery(zk, LABEL) // check query key - if !bytes.Equal(QKEY, query.Key().Data) { + qkey := query.Key().Data + if !bytes.Equal(QKEY, qkey) { + t.Logf("expected: %s", hex.EncodeToString(QKEY)) + t.Logf("got: %s", hex.EncodeToString(qkey)) t.Fatal("query key mismatch") } @@ -125,11 +128,11 @@ func TestGNSBlock(t *testing.T) { func TestRecordsetPKEY(t *testing.T) { var ( D = []byte{ - // PKEY private scalar - 0x50, 0xd7, 0xb6, 0x52, 0xa4, 0xef, 0xea, 0xdf, - 0xf3, 0x73, 0x96, 0x90, 0x97, 0x85, 0xe5, 0x95, - 0x21, 0x71, 0xa0, 0x21, 0x78, 0xc8, 0xe7, 0xd4, - 0x50, 0xfa, 0x90, 0x79, 0x25, 0xfa, 0xfd, 0x98, + // PKEY private scalar (clamped little-endian) + 0x98, 0xfd, 0xfa, 0x25, 0x79, 0x90, 0xfa, 0x50, + 0xd4, 0xe7, 0xc8, 0x78, 0x21, 0xa0, 0x71, 0x21, + 0x95, 0xe5, 0x85, 0x97, 0x90, 0x96, 0x73, 0xf3, + 0xdf, 0xea, 0xef, 0xa4, 0x52, 0xb6, 0xd7, 0x50, } ZKEY = []byte{ // zone type diff --git a/src/gnunet/service/namecache/module.go b/src/gnunet/service/namecache/module.go @@ -77,7 +77,7 @@ func (m *Module) Get(ctx context.Context, query *blocks.GNSQuery) (block *blocks return } if len(e) != 1 { - err = errors.New("only one DHT entry exppected") + err = errors.New("only one namecache entry expected") } else { err = blocks.Unwrap(e[0].Blk, block) } diff --git a/src/gnunet/service/revocation/pow_test.go b/src/gnunet/service/revocation/pow_test.go @@ -13,7 +13,7 @@ import ( // Test revocation with test vector defined in the RFC draft. func TestRevocationRFC(t *testing.T) { var ( - D = "6fea32c05af58bfa979553d188605fd57d8bf9cc263b78d5f7478c07b998ed70" + D = "70ed98b9078c47f7d5783b26ccf98b7dd55f6088d1539597fa8bf55ac032ea6f" ZKEY = "000100002ca223e879ecc4bbdeb5da17319281d63b2e3b6955f1c3775c804a98d5f8ddaa" PROOF = "" + "0005d66da3598127" + @@ -86,7 +86,11 @@ func TestRevocationRFC(t *testing.T) { if err = data.Unmarshal(revData, revD); err != nil { t.Fatal(err) } - if !bytes.Equal(revData.ZoneKeySig.Bytes(), zkey) { + if err = revData.ZoneKeySig.Init(); err != nil { + t.Fatal(err) + } + // check sigature + if !bytes.Equal(revData.ZoneKeySig.ZoneKey.Bytes(), zkey) { t.Logf("zkey = %s\n", hex.EncodeToString(revData.ZoneKeySig.Bytes())) t.Logf("ZKEY = %s\n", hex.EncodeToString(zkey)) t.Fatal("Wrong zone key in test revocation") diff --git a/src/gnunet/service/store/store_zonemaster.go b/src/gnunet/service/store/store_zonemaster.go @@ -32,11 +32,15 @@ import ( ) //============================================================ -// Local zone records stored in SQLite3 database +// Local identities and zone records (SQLite3 database) +// Identities are named ZonePrivate keys that are associated +// with a GNUnet subsystem (like GNS, CADET and others). +// Identities for the subsystem "gns" are called zones and +// are collections of labeled resource record sets. All other +// identities are usuall called "egos". //============================================================ // Zone is the definition of a local GNS zone -// and is stored in a SQL database for faster access. type Zone struct { ID int64 // database identifier Name string // zone name @@ -59,6 +63,23 @@ func NewZone(name string, sk *crypto.ZonePrivate) *Zone { //---------------------------------------------------------------------- +// Identity is a Zone associated with a service +type Identity struct { + Zone + + Svc string // associated service +} + +// NewIdentity creates an initialize instance for database access +func NewIdentity(name string, sk *crypto.ZonePrivate, svc string) *Identity { + return &Identity{ + Zone: *NewZone(name, sk), + Svc: svc, + } +} + +//---------------------------------------------------------------------- + type Label struct { ID int64 // database id of label Zone int64 // database ID of parent zone @@ -136,7 +157,7 @@ func OpenZoneDB(fname string) (db *ZoneDB, err error) { return } // check for initialized database - res := db.conn.QueryRow("select name from sqlite_master where type='table' and name='zones'") + res := db.conn.QueryRow("select name from sqlite_master where type='table' and name='identities'") var s string if res.Scan(&s) != nil { // initialize database @@ -153,6 +174,152 @@ func (db *ZoneDB) Close() error { } //---------------------------------------------------------------------- +// Identity handling +//---------------------------------------------------------------------- + +// SetIdentity inserts, updates or deletes a zone in the database. +// The function does not change timestamps which are in the +// responsibility of the caller. +// - insert: Identity.ID is nil (0) +// - update: Identity.Name is set +// - remove: otherwise +func (db *ZoneDB) SetIdentity(id *Identity) error { + // GNS zones are handled by Zone instances + if id.Svc == "gns" { + return db.SetZone(&id.Zone) + } + // check for identity insert + if id.ID == 0 { + stmt := "insert into identities(svc,name,created,modified,ztype,zdata) values(?,?,?,?,?,?)" + result, err := db.conn.Exec(stmt, + id.Svc, id.Name, id.Created.Val, id.Modified.Val, id.Key.Type, id.Key.KeyData) + if err != nil { + return err + } + id.ID, err = result.LastInsertId() + return err + } + // check for identity update (name and service only only) + if len(id.Name) > 0 { + stmt := "update identities set svc=?,name=?,modified=? where id=?" + result, err := db.conn.Exec(stmt, id.Svc, id.Name, id.Modified.Val, id.ID) + if err != nil { + return err + } + var num int64 + if num, err = result.RowsAffected(); err == nil { + if num != 1 { + err = errors.New("update identity failed") + } + } + return err + } + // remove identity from database + _, err := db.conn.Exec("delete from identities where id=?", id.ID) + return err +} + +// GetIdentity gets an identifier with given database id +func (db *ZoneDB) GetIdentity(id int64) (ident *Identity, err error) { + // assemble identity from database row + stmt := "select svc,name,created,modified,ztype,zdata from identities where id=?" + ident = new(Identity) + ident.ID = id + row := db.conn.QueryRow(stmt, id) + var ztype enums.GNSType + var zdata []byte + if err = row.Scan(&ident.Svc, &ident.Name, &ident.Created.Val, &ident.Modified.Val, &ztype, &zdata); err == nil { + // reconstruct private zone key + ident.Key, err = crypto.NewZonePrivate(ztype, zdata) + } + return +} + +// GetIdentity gets an identifier with given (name,svc) +func (db *ZoneDB) GetIdentityByName(name, svc string) (ident *Identity, err error) { + // assemble identity from database row + var row *sql.Row + stmt := "select id,created,modified,ztype,zdata from identities where name=?" + if len(svc) > 0 { + stmt += " and svc=?" + row = db.conn.QueryRow(stmt, name, svc) + } else { + row = db.conn.QueryRow(stmt, name) + } + ident = new(Identity) + ident.Name = name + ident.Svc = svc + var ztype enums.GNSType + var zdata []byte + if err = row.Scan(&ident.ID, &ident.Created.Val, &ident.Modified.Val, &ztype, &zdata); err == nil { + // reconstruct private zone key + ident.Key, err = crypto.NewZonePrivate(ztype, zdata) + } + return +} + +func (db *ZoneDB) GetIdentities(filter string, args ...any) (list []*Identity, err error) { + // assemble query + stmt := "select id,name,svc,created,modified,ztype,zdata from identities" + if len(filter) > 0 { + stmt += " where " + fmt.Sprintf(filter, args...) + } + // select zones + var rows *sql.Rows + if rows, err = db.conn.Query(stmt); err != nil { + return + } + // process zones + defer rows.Close() + for rows.Next() { + // assemble identity from database row + i := new(Identity) + var ztype enums.GNSType + var zdata []byte + if err = rows.Scan(&i.ID, &i.Name, &i.Svc, &i.Created.Val, &i.Modified.Val, &ztype, &zdata); err != nil { + // terminate on error; return list so far + return + } + // reconstruct private key + if i.Key, err = crypto.NewZonePrivate(ztype, zdata); err != nil { + return + } + // append to result list + list = append(list, i) + } + return +} + +func (db *ZoneDB) GetDefaultIdentity(svc string) (ident *Identity, err error) { + // assemble identity from database row + stmt := "select id,name,created,modified,ztype,zdata from v_defaults where svc=?" + row := db.conn.QueryRow(stmt, svc) + ident = new(Identity) + ident.Svc = svc + var ztype enums.GNSType + var zdata []byte + if err = row.Scan(&ident.ID, &ident.Name, &ident.Created.Val, &ident.Modified.Val, &ztype, &zdata); err == nil { + // reconstruct private zone key + ident.Key, err = crypto.NewZonePrivate(ztype, zdata) + } + return +} + +func (db *ZoneDB) SetDefaultIdentity(zk *crypto.ZonePrivate, svc string) (err error) { + // get database id of identity + stmt := "select id from identities where zdata=?" + row := db.conn.QueryRow(stmt, zk.KeyData) + var id int64 + if err = row.Scan(&id); err != nil { + return + } + // set default + stmt = "insert into defaults(svc,ident) values(?,?) on conflict(svc) do update set ident=?" + _, err = db.conn.Exec(stmt, svc, id, id) + return +} + +//---------------------------------------------------------------------- // Zone handling //---------------------------------------------------------------------- @@ -165,7 +332,7 @@ func (db *ZoneDB) Close() error { func (db *ZoneDB) SetZone(z *Zone) error { // check for zone insert if z.ID == 0 { - stmt := "insert into zones(name,created,modified,ztype,zdata) values(?,?,?,?,?)" + stmt := "insert into identities(svc,name,created,modified,ztype,zdata) values('gns',?,?,?,?,?)" result, err := db.conn.Exec(stmt, z.Name, z.Created.Val, z.Modified.Val, z.Key.Type, z.Key.KeyData) if err != nil { return err @@ -175,7 +342,7 @@ func (db *ZoneDB) SetZone(z *Zone) error { } // check for zone update (name only) if len(z.Name) > 0 { - stmt := "update zones set name=?,modified=? where id=?" + stmt := "update identities set name=?,modified=? where id=? and svc='gns'" result, err := db.conn.Exec(stmt, z.Name, z.Modified.Val, z.ID) if err != nil { return err @@ -202,6 +369,7 @@ func (db *ZoneDB) GetZone(id int64) (zone *Zone, err error) { // assemble zone from database row stmt := "select name,created,modified,ztype,zdata from zones where id=?" zone = new(Zone) + zone.ID = id var ztype enums.GNSType var zdata []byte row := db.conn.QueryRow(stmt, id) @@ -329,6 +497,29 @@ func (db *ZoneDB) GetLabels(filter string, args ...any) (list []*Label, err erro return } +func (db *ZoneDB) GetLabelIDs(zk *crypto.ZonePrivate) (list []int64, err error) { + // get zone database id + row := db.conn.QueryRow("select id from zones where ztype=? and zdata=?", zk.Type, zk.KeyData) + var zid int64 + if err = row.Scan(&zid); err != nil { + return + } + // select all labels for zone + var rows *sql.Rows + if rows, err = db.conn.Query("select id from labels where zid=?", zid); err != nil { + return + } + defer rows.Close() + var id int64 + for rows.Next() { + if err = rows.Scan(&id); err != nil { + return + } + list = append(list, id) + } + return +} + //---------------------------------------------------------------------- // Record handling //---------------------------------------------------------------------- diff --git a/src/gnunet/service/store/store_zonemaster.sql b/src/gnunet/service/store/store_zonemaster.sql @@ -16,15 +16,37 @@ -- -- SPDX-License-Identifier: AGPL3.0-or-later -create table zones ( +create table identities ( id integer primary key autoincrement, - name text unique, + svc text, + name text, created integer, modified integer, - ztype integer, - zdata blob + ztype integer, + zdata blob, + unique (svc,name) ); +create table defaults ( + svc text unique, + ident integer references identities(id) +); + +create view v_defaults as select + i.id as id, + d.svc as svc, + i.name as name, + i.created as created, + i.modified as modified, + i.ztype as ztype, + i.zdata as zdata +from identities i, defaults d +where i.id = d.ident; + +create view zones as select + id, name, created, modified, ztype, zdata +from identities +where svc = 'gns'; create table labels ( id integer primary key autoincrement, diff --git a/src/gnunet/service/zonemaster/gui.go b/src/gnunet/service/zonemaster/gui.go @@ -21,10 +21,10 @@ package zonemaster import ( "bytes" "context" - "crypto/rand" "embed" "errors" "fmt" + "gnunet/config" "gnunet/crypto" "gnunet/enums" "gnunet/service/gns/rr" @@ -122,6 +122,13 @@ func (zm *ZoneMaster) startGUI(ctx context.Context) { "rrdata": func(t enums.GNSType, buf []byte) string { return guiRRdata(t, buf) }, + "tabSetList": func(num int) (list map[int]int) { + list = make(map[int]int) + for i := 0; i < num; i++ { + list[i+1] = 2*i + 1 + } + return + }, }) if _, err := tpl.ParseFS(fsys, "*.htpl"); err != nil { logger.Println(logger.ERROR, "[zonemaster] GUI templates failed: "+err.Error()) @@ -136,7 +143,7 @@ func (zm *ZoneMaster) startGUI(ctx context.Context) { router.HandleFunc("/action/{cmd}/{mode}/{id}", zm.action) router.HandleFunc("/", zm.dashboard) srv := &http.Server{ - Addr: zm.cfg.ZoneMaster.GUI, + Addr: config.Cfg.ZoneMaster.GUI, ReadTimeout: 10 * time.Second, ReadHeaderTimeout: 5 * time.Second, Handler: router, @@ -205,17 +212,17 @@ func (zm *ZoneMaster) actionNew(w http.ResponseWriter, r *http.Request, mode str // new zone case "zone": name := r.FormValue("name") - // create private key - seed := make([]byte, 32) - if _, err = rand.Read(seed); err != nil { - return - } - var zp *crypto.ZonePrivate - kt := enums.GNS_TYPE_PKEY - if r.FormValue("keytype") == "EDKEY" { + // get key type + var kt enums.GNSType + switch r.FormValue("keytype") { + case "EDKEY": kt = enums.GNS_TYPE_EDKEY + case "PKEY": + kt = enums.GNS_TYPE_PKEY } - zp, err = crypto.NewZonePrivate(kt, seed) + // cretae private key + var zp *crypto.ZonePrivate + zp, err = crypto.NewZonePrivate(kt, nil) if err != nil { return } @@ -366,7 +373,7 @@ func (zm *ZoneMaster) updRec(w http.ResponseWriter, r *http.Request, id int64) e } //---------------------------------------------------------------------- -// Create new zone. label or resource record +// Create new zone, label or resource record //---------------------------------------------------------------------- type NewEditData struct { @@ -630,8 +637,15 @@ func (zm *ZoneMaster) remove(w http.ResponseWriter, r *http.Request) { // Helper methods //====================================================================== +// MainData for the template "main" +type MainData struct { + Content string // Page content + Params any // reference to parameters + NumRR int // number of RR types supported +} + // render a webpage with given data and template reference -func renderPage(w io.Writer, data interface{}, page string) { +func renderPage(w io.Writer, data any, page string) { // create content section t := tpl.Lookup(page) if t == nil { @@ -649,7 +663,11 @@ func renderPage(w io.Writer, data interface{}, page string) { _, _ = io.WriteString(w, "No main template found") return } - if err := t.Execute(w, content.String()); err != nil { + md := new(MainData) + md.Params = data + md.Content = content.String() + md.NumRR = len(rrtypes) + if err := t.Execute(w, md); err != nil { _, _ = io.WriteString(w, err.Error()) } } diff --git a/src/gnunet/service/zonemaster/gui.htpl b/src/gnunet/service/zonemaster/gui.htpl @@ -3,12 +3,12 @@ <html lang="en"> <head> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> - {{template "css"}} + {{template "css" .NumRR}} </head> <body> <h1>GNUnet Zone Master</h1> <hr/> - {{.}} + {{.Content}} <script> function notify(msg) { if ('Notification' in window) { diff --git a/src/gnunet/service/zonemaster/gui_css.htpl b/src/gnunet/service/zonemaster/gui_css.htpl @@ -165,9 +165,6 @@ .nested { display: none; } - .active { - display: block; - } .tabset > input[type="radio"] { position: absolute; left: -200vw; @@ -175,26 +172,10 @@ .tabset .tab-panel { display: none; } - .tabset > input:first-child:checked ~ .tab-panels > .tab-panel:first-child, - .tabset > input:nth-child(3):checked ~ .tab-panels > .tab-panel:nth-child(2), - .tabset > input:nth-child(5):checked ~ .tab-panels > .tab-panel:nth-child(3), - .tabset > input:nth-child(7):checked ~ .tab-panels > .tab-panel:nth-child(4), - .tabset > input:nth-child(9):checked ~ .tab-panels > .tab-panel:nth-child(5), - .tabset > input:nth-child(11):checked ~ .tab-panels > .tab-panel:nth-child(6), - .tabset > input:nth-child(13):checked ~ .tab-panels > .tab-panel:nth-child(7), - .tabset > input:nth-child(15):checked ~ .tab-panels > .tab-panel:nth-child(8), - .tabset > input:nth-child(17):checked ~ .tab-panels > .tab-panel:nth-child(9), - .tabset > input:nth-child(19):checked ~ .tab-panels > .tab-panel:nth-child(10), - .tabset > input:nth-child(21):checked ~ .tab-panels > .tab-panel:nth-child(11), - .tabset > input:nth-child(23):checked ~ .tab-panels > .tab-panel:nth-child(12), - .tabset > input:nth-child(25):checked ~ .tab-panels > .tab-panel:nth-child(13), - .tabset > input:nth-child(27):checked ~ .tab-panels > .tab-panel:nth-child(14), - .tabset > input:nth-child(29):checked ~ .tab-panels > .tab-panel:nth-child(15), - .tabset > input:nth-child(31):checked ~ .tab-panels > .tab-panel:nth-child(16), - .tabset > input:nth-child(33):checked ~ .tab-panels > .tab-panel:nth-child(17), - .tabset > input:nth-child(35):checked ~ .tab-panels > .tab-panel:nth-child(18), - .tabset > input:nth-child(37):checked ~ .tab-panels > .tab-panel:nth-child(19), - .tabset > input:nth-child(39):checked ~ .tab-panels > .tab-panel:nth-child(20) { + {{range $i,$j := tabSetList .}} + .tabset > input:nth-child({{$j}}):checked ~ .tab-panels > .tab-panel:nth-child({{$i}}), + {{end}} + .active { display: block; } .tabset > label { diff --git a/src/gnunet/service/zonemaster/gui_new.htpl b/src/gnunet/service/zonemaster/gui_new.htpl @@ -31,7 +31,7 @@ alert("Empty zone name not allowed"); return false; } - for (var i = 0; i < names.length; i++) { + for (var i = 0; i < zone_names.length; i++) { if (zone_names[i] == name) { alert("Zone name already used"); return false; @@ -45,7 +45,7 @@ {{define "new_label"}} <div> <h3>Creating a new GNS label for zone "{{index .Params "zone"}}":</h3> - <form action="/action/new/label/{{.Ref}}" onsubmit="return(label_validate());"> + <form action="/action/new/label/{{.Ref}}" method="post" onsubmit="return(label_validate());"> <table> <tr> <td align="right">Name:</td> @@ -68,7 +68,7 @@ alert("Empty labels not allowed"); return false; } - for (var i = 0; i < names.length; i++) { + for (var i = 0; i < label_names.length; i++) { if (label_names[i] == name) { alert("Label already used"); return false; diff --git a/src/gnunet/service/zonemaster/gui_rr.htpl b/src/gnunet/service/zonemaster/gui_rr.htpl @@ -1,19 +1,19 @@ {{define "RRCommon"}} - <input type="hidden" name="lid" value="{{index . "lid"}}"> - {{range $k, $v := .}} + <input type="hidden" name="lid" value="{{index .Params "lid"}}"> + {{range $k, $v := .Params}} <input type="hidden" name="old_{{$k}}" value="{{$v}}"> {{end}} - {{$pf := index . "prefix"}} + {{$pf := index .Params "prefix"}} <tr> <td align="right" valign="top"><b>Expires:</b></td> <td> Never <input type="checkbox" class="alternate" name="{{$pf}}never" - {{if eq "on" (index . (print $pf "never"))}}checked="checked"{{end}} + {{if eq "on" (index .Params (print $pf "never"))}}checked="checked"{{end}} > <div class="alternate"> At given date and time: <input type="datetime-local" id="{{$pf}}expires" name="{{$pf}}expires" required - value="{{index . (print $pf "expires")}}" + value="{{index .Params (print $pf "expires")}}" > </div> </td> @@ -22,13 +22,13 @@ <td align="right" valign="top"><b>Flags:</b></td> <td> <input type="checkbox" name="{{$pf}}private" - {{if eq "on" (index . (print $pf "private"))}}checked="checked" class="disabled"{{end}} + {{if eq "on" (index .Params (print $pf "private"))}}checked="checked" class="disabled"{{end}} > Private<br> <input type="checkbox" name="{{$pf}}shadow" - {{if eq "on" (index . (print $pf "shadow"))}}checked="checked" class="disabled"{{end}} + {{if eq "on" (index .Params (print $pf "shadow"))}}checked="checked" class="disabled"{{end}} > Shadow<br> <input type="checkbox" name="{{$pf}}suppl" - {{if eq "on" (index . (print $pf "suppl"))}}checked="checked" class="disabled"{{end}} + {{if eq "on" (index .Params (print $pf "suppl"))}}checked="checked" class="disabled"{{end}} > Supplemental<br> </td> </tr> @@ -68,7 +68,7 @@ > </td> </tr> - {{template "RRCommon" .Params}} + {{template "RRCommon" .}} <tr><td/><td><button id="submit">{{.Button}} record</button></td></tr> </table> </form> @@ -98,7 +98,7 @@ > </td> </tr> - {{template "RRCommon" .Params}} + {{template "RRCommon" .}} <tr><td/><td><button id="submit">{{.Button}} record</button></td></tr> </table> </form> @@ -125,7 +125,7 @@ > </td> </tr> - {{template "RRCommon" .Params}} + {{template "RRCommon" .}} <tr><td/><td><button id="submit">{{.Button}} record</button></td></tr> </table> </form> @@ -145,7 +145,7 @@ > </td> </tr> - {{template "RRCommon" .Params}} + {{template "RRCommon" .}} <tr><td/><td><button id="submit">{{.Button}} record</button></td></tr> </table> </form> @@ -165,7 +165,7 @@ > </td> </tr> - {{template "RRCommon" .Params}} + {{template "RRCommon" .}} <tr><td/><td><button id="submit">{{.Button}} record</button></td></tr> </table> </form> @@ -200,7 +200,7 @@ > </td> </tr> - {{template "RRCommon" .Params}} + {{template "RRCommon" .}} <tr><td/><td><button id="submit">{{.Button}} record</button></td></tr> </table> </form> @@ -302,7 +302,7 @@ </div> </td> </tr> - {{template "RRCommon" .Params}} + {{template "RRCommon" .}} <tr><td/><td><button id="submit">{{.Button}} record</button></td></tr> </table> </form> @@ -323,7 +323,7 @@ > </td> </tr> - {{template "RRCommon" .Params}} + {{template "RRCommon" .}} <tr><td/><td><button id="submit">{{.Button}} record</button></td></tr> </table> </form> @@ -344,7 +344,7 @@ > </td> </tr> - {{template "RRCommon" .Params}} + {{template "RRCommon" .}} <tr><td/><td><button id="submit">{{.Button}} record</button></td></tr> </table> </form> @@ -364,7 +364,7 @@ > </td> </tr> - {{template "RRCommon" .Params}} + {{template "RRCommon" .}} <tr><td/><td><button id="submit">{{.Button}} record</button></td></tr> </table> </form> @@ -384,7 +384,7 @@ > </td> </tr> - {{template "RRCommon" .Params}} + {{template "RRCommon" .}} <tr><td/><td><button id="submit">{{.Button}} record</button></td></tr> </table> </form> @@ -413,7 +413,7 @@ > </td> </tr> - {{template "RRCommon" .Params}} + {{template "RRCommon" .}} <tr><td/><td><button id="submit">{{.Button}} record</button></td></tr> </table> </form> diff --git a/src/gnunet/service/zonemaster/records.go b/src/gnunet/service/zonemaster/records.go @@ -22,8 +22,11 @@ import ( "encoding/hex" "errors" "fmt" + "gnunet/crypto" "gnunet/enums" + "gnunet/service/dht/blocks" "gnunet/service/gns/rr" + "gnunet/service/store" "gnunet/util" "net" "time" @@ -330,7 +333,7 @@ func Map2RRData(t enums.GNSType, set map[string]string) (buf []byte, err error) } //====================================================================== -// Get list of allowed new RRs given a set of existing RRs. +// ResourceRecord helpers //====================================================================== // Create a list of compatible record types from list of @@ -346,3 +349,23 @@ func compatibleRR(in []*enums.GNSSpec, label string) (out []*enums.GNSSpec) { } return } + +// get a list of resource records for a given label in a zone. +func (zm *ZoneMaster) getRecords(zk *crypto.ZoneKey, label int64) (rs *blocks.RecordSet, expire util.AbsoluteTime, err error) { + // collect records for zone label + var recs []*store.Record + if recs, err = zm.zdb.GetRecords("lid=%d", label); err != nil { + return + } + // assemble record set and find earliest expiration + expire = util.AbsoluteTimeNever() + rs = blocks.NewRecordSet() + for _, r := range recs { + if r.Expire.Compare(expire) < 0 { + expire = r.Expire + } + rs.AddRecord(&r.ResourceRecord) + } + // do not add padding yet as record set may be filtered before use. + return +} diff --git a/src/gnunet/service/zonemaster/rpc.go b/src/gnunet/service/zonemaster/rpc.go @@ -20,5 +20,5 @@ package zonemaster import "gnunet/service" -func (s *Service) InitRPC(rpc *service.JRPCServer) { +func (zm *ZoneMaster) InitRPC(rpc *service.JRPCServer) { } diff --git a/src/gnunet/service/zonemaster/service.go b/src/gnunet/service/zonemaster/service.go @@ -25,7 +25,7 @@ import ( "gnunet/config" "gnunet/core" - "gnunet/crypto" + "gnunet/enums" "gnunet/message" "gnunet/service" "gnunet/service/dht/blocks" @@ -35,43 +35,20 @@ import ( "github.com/bfix/gospel/logger" ) -type ZoneIterator struct { - zk *crypto.ZonePrivate -} - //---------------------------------------------------------------------- -// "GNUnet Zonemaster" service implementation: -// The zonemaster service handles Namestore messages +// "GNUnet Zonemaster" socket service implementation: +// Zonemaster handles Namestore and Identity messages. //---------------------------------------------------------------------- -// Service implements a GNS service -type Service struct { - Module - - ZoneIters *util.Map[uint32, *ZoneIterator] -} - -// NewService creates a new GNS service instance -func NewService(ctx context.Context, c *core.Core) service.Service { - // instantiate service - mod := NewModule(ctx, c) - srv := &Service{ - Module: *mod, - ZoneIters: util.NewMap[uint32, *ZoneIterator](), - } - // set external function references (external services) - srv.StoreLocal = srv.StoreNamecache - srv.StoreRemote = srv.StoreDHT - - return srv -} - // ServeClient processes a client channel. -func (s *Service) ServeClient(ctx context.Context, id int, mc *service.Connection) { +func (zm *ZoneMaster) ServeClient(ctx context.Context, id int, mc *service.Connection) { reqID := 0 var cancel context.CancelFunc ctx, cancel = context.WithCancel(ctx) + // inform sub-service about new session + zm.identity.NewSession(id, mc) + for { // receive next message from client reqID++ @@ -89,33 +66,158 @@ func (s *Service) ServeClient(ctx context.Context, id int, mc *service.Connectio } logger.Printf(logger.INFO, "[zonemaster:%d:%d] Received request: %v\n", id, reqID, msg) + // context with values + values := make(util.ParameterSet) + values["id"] = id + values["label"] = fmt.Sprintf(":%d:%d", id, reqID) + valueCtx := context.WithValue(ctx, core.CtxKey("params"), values) + // handle message - valueCtx := context.WithValue(ctx, core.CtxKey("label"), fmt.Sprintf(":%d:%d", id, reqID)) - s.HandleMessage(valueCtx, nil, msg, mc) + zm.HandleMessage(valueCtx, nil, msg, mc) } + // inform sub.services about closed session + zm.identity.CloseSession(id) + // close client connection mc.Close() // cancel all tasks running for this session/connection - logger.Printf(logger.INFO, "[zonemaster:%d] Start closing session...\n", id) + logger.Printf(logger.INFO, "[zonemaster:%d] Closing session...\n", id) cancel() } // Handle a single incoming message -func (s *Service) HandleMessage(ctx context.Context, sender *util.PeerID, msg message.Message, back transport.Responder) bool { +func (zm *ZoneMaster) HandleMessage(ctx context.Context, sender *util.PeerID, msg message.Message, back transport.Responder) bool { // assemble log label - label := "" - if v := ctx.Value("label"); v != nil { - label, _ = v.(string) + var id int + var label string + if v := ctx.Value(core.CtxKey("params")); v != nil { + if ps, ok := v.(util.ParameterSet); ok { + label, _ = util.GetParam[string](ps, "label") + id, _ = util.GetParam[int](ps, "id") + } } // perform lookup switch m := msg.(type) { + //------------------------------------------------------------------ + // Identity service + //------------------------------------------------------------------ + + // start identity update listener + case *message.IdentityStartMsg: + if err := zm.identity.Start(ctx, id); err != nil { + logger.Printf(logger.ERROR, "[zonemaster%s] Identity session for %d failed: %v\n", label, id, err) + return false + } + + // create a new identity with given private key + case *message.IdentityCreateMsg: + if err := zm.identity.Create(ctx, id, m.ZoneKey, m.Name()); err != nil { + logger.Printf(logger.ERROR, "[zonemaster%s] Identity create failed: %v\n", label, err) + return false + } + + // rename identity + case *message.IdentityRenameMsg: + id, err := zm.zdb.GetIdentityByName(m.OldName(), IDENT_DEFAULT_SERVICE) + if err != nil { + logger.Printf(logger.ERROR, "[zonemaster%s] Identity lookup failed: %v\n", label, err) + return false + } + // change name + id.Name = m.NewName() + err = zm.zdb.SetIdentity(id) + + // send response + rc := enums.RC_OK + msg := "" + if err != nil { + rc = enums.RC_NO + msg = err.Error() + } + resp := message.NewIdentityResultCodeMsg(rc, msg) + if err = back.Send(ctx, resp); err != nil { + logger.Printf(logger.ERROR, "[identity:%s] Can't send response (%v): %v\n", label, resp, err) + } + + // delete identity + case *message.IdentityDeleteMsg: + id, err := zm.zdb.GetIdentityByName(m.Name(), IDENT_DEFAULT_SERVICE) + if err != nil { + logger.Printf(logger.ERROR, "[zonemaster%s] Identity lookup failed: %v\n", label, err) + return false + } + // delete in database + id.Name = "" + err = zm.zdb.SetIdentity(id) + + // send response + rc := enums.RC_OK + msg := "" + if err != nil { + rc = enums.RC_NO + msg = err.Error() + } + resp := message.NewIdentityResultCodeMsg(rc, msg) + if err = back.Send(ctx, resp); err != nil { + logger.Printf(logger.ERROR, "[identity:%s] Can't send response (%v): %v\n", label, resp, err) + } + + // lookup identity + case *message.IdentityLookupMsg: + id, err := zm.zdb.GetIdentityByName(m.Name, IDENT_DEFAULT_SERVICE) + if err != nil { + logger.Printf(logger.ERROR, "[zonemaster%s] Identity lookup failed: %v\n", label, err) + return false + } + resp := message.NewIdentityUpdateMsg(id.Name, id.Key) + logger.Printf(logger.DBG, "[identity:%s] Sending %v", label, resp) + if err = back.Send(ctx, resp); err != nil { + logger.Printf(logger.ERROR, "[identity:%s] Can't send response (%v): %v\n", label, resp, err) + } + + // get default identity for service + case *message.IdentityGetDefaultMsg: + id, err := zm.zdb.GetDefaultIdentity(m.Service()) + if err != nil { + logger.Printf(logger.ERROR, "[zonemaster%s] Identity lookup failed: %v\n", label, err) + return false + } + resp := message.NewIdentityUpdateMsg(id.Name, id.Key) + logger.Printf(logger.DBG, "[identity:%s] Sending %v", label, resp) + if err = back.Send(ctx, resp); err != nil { + logger.Printf(logger.ERROR, "[identity:%s] Can't send response (%v): %v\n", label, resp, err) + } + + // set default identity for service + case *message.IdentitySetDefaultMsg: + err := zm.zdb.SetDefaultIdentity(m.ZoneKey, m.Service()) + + // send response + rc := enums.RC_OK + msg := "" + if err != nil { + rc = enums.RC_NO + msg = err.Error() + } + resp := message.NewIdentityResultCodeMsg(rc, msg) + if err = back.Send(ctx, resp); err != nil { + logger.Printf(logger.ERROR, "[identity:%s] Can't send response (%v): %v\n", label, resp, err) + } + + //------------------------------------------------------------------ + // Namestore service + //------------------------------------------------------------------ + // start new zone iteration case *message.NamestoreZoneIterStartMsg: - zi := new(ZoneIterator) - zi.zk = m.ZoneKey - s.ZoneIters.Put(m.ID, zi, 0) + iter := zm.namestore.NewIterator(m.ID, m.ZoneKey) + resp := iter.Next() + if err := back.Send(ctx, resp); err != nil { + logger.Printf(logger.ERROR, "[zonemaster%s] Can't send response (%v)\n", label, resp) + return false + } default: //---------------------------------------------------------- @@ -128,7 +230,7 @@ func (s *Service) HandleMessage(ctx context.Context, sender *util.PeerID, msg me } // storeDHT stores a GNS block in the DHT. -func (s *Service) StoreDHT(ctx context.Context, query blocks.Query, block blocks.Block) (err error) { +func (zm *ZoneMaster) StoreDHT(ctx context.Context, query blocks.Query, block blocks.Block) (err error) { // assemble DHT request req := message.NewDHTP2PPutMsg(block) req.Flags = query.Flags() @@ -140,7 +242,7 @@ func (s *Service) StoreDHT(ctx context.Context, query blocks.Query, block blocks } // storeNamecache stores a GNS block in the local namecache. -func (s *Service) StoreNamecache(ctx context.Context, query *blocks.GNSQuery, block *blocks.GNSBlock) (err error) { +func (zm *ZoneMaster) StoreNamecache(ctx context.Context, query *blocks.GNSQuery, block *blocks.GNSBlock) (err error) { // assemble Namecache request req := message.NewNamecacheCacheMsg(block) diff --git a/src/gnunet/service/zonemaster/service_identity.go b/src/gnunet/service/zonemaster/service_identity.go @@ -0,0 +1,135 @@ +// This file is part of gnunet-go, a GNUnet-implementation in Golang. +// Copyright (C) 2019-2022 Bernd Fix >Y< +// +// gnunet-go is free software: you can redistribute it and/or modify it +// under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// gnunet-go is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// SPDX-License-Identifier: AGPL3.0-or-later + +package zonemaster + +import ( + "context" + "fmt" + "gnunet/crypto" + "gnunet/enums" + "gnunet/message" + "gnunet/service/store" + "gnunet/transport" + "gnunet/util" + + "github.com/bfix/gospel/logger" +) + +//nolint:stylecheck // my style is my style... +const ( + IDENT_DEFAULT_SERVICE = "ego" +) + +//---------------------------------------------------------------------- +// "GNUnet Identity" service implementation: +//---------------------------------------------------------------------- + +type IdentitySession struct { + id int + updates bool + back transport.Responder +} + +type Identity struct{} + +type IdentityService struct { + zm *ZoneMaster // reference to main service + clients *util.Map[int, *IdentitySession] // client sessions +} + +func NewIdentityService(zm *ZoneMaster) *IdentityService { + srv := new(IdentityService) + srv.zm = zm + srv.clients = util.NewMap[int, *IdentitySession]() + return srv +} + +func (ident *IdentityService) NewSession(id int, back transport.Responder) { + sess := &IdentitySession{ + id: id, + updates: false, + back: back, + } + ident.clients.Put(id, sess, 0) +} + +func (ident *IdentityService) CloseSession(id int) { + ident.clients.Delete(id, 0) +} + +func (ident *IdentityService) FollowUpdates(id int) *IdentitySession { + if sess, ok := ident.clients.Get(id, 0); ok { + sess.updates = true + return sess + } + return nil +} + +func (ident *IdentityService) Start(ctx context.Context, id int) (err error) { + // flag client as update receiver + sess := ident.FollowUpdates(id) + if sess == nil { + err = fmt.Errorf("no session available for client %d", id) + return + } + // initial update is to send all existing identites + var list []*store.Identity + if list, err = ident.zm.zdb.GetIdentities(""); err != nil { + return + } + for _, ident := range list { + resp := message.NewIdentityUpdateMsg(ident.Name, ident.Key) + logger.Printf(logger.DBG, "[identity:%d] Sending %v", id, resp) + if err = sess.back.Send(ctx, resp); err != nil { + logger.Printf(logger.ERROR, "[identity:%d] Can't send response (%v): %v\n", id, resp, err) + return + } + } + // terminate with EOL + resp := message.NewIdentityUpdateMsg("", nil) + if err = sess.back.Send(ctx, resp); err != nil { + logger.Printf(logger.ERROR, "[identity:%d] Can't send response (%v): %v\n", id, resp, err) + return + } + return +} + +func (ident *IdentityService) Create(ctx context.Context, cid int, zk *crypto.ZonePrivate, name string) (err error) { + // get client session + sess, ok := ident.clients.Get(cid, 0) + if !ok { + err = fmt.Errorf("no session available for client %d", cid) + return + } + // add identity + id := store.NewIdentity(name, zk, IDENT_DEFAULT_SERVICE) + err = ident.zm.zdb.SetIdentity(id) + rc := enums.RC_OK + msg := "" + if err != nil { + rc = enums.RC_NO + msg = err.Error() + } + resp := message.NewIdentityResultCodeMsg(rc, msg) + if err = sess.back.Send(ctx, resp); err != nil { + logger.Printf(logger.ERROR, "[identity:%d] Can't send response (%v): %v\n", cid, resp, err) + return + } + return +} diff --git a/src/gnunet/service/zonemaster/service_namestore.go b/src/gnunet/service/zonemaster/service_namestore.go @@ -0,0 +1,90 @@ +// This file is part of gnunet-go, a GNUnet-implementation in Golang. +// Copyright (C) 2019-2022 Bernd Fix >Y< +// +// gnunet-go is free software: you can redistribute it and/or modify it +// under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// gnunet-go is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// SPDX-License-Identifier: AGPL3.0-or-later + +package zonemaster + +import ( + "gnunet/crypto" + "gnunet/message" + "gnunet/service/store" + "gnunet/util" +) + +//---------------------------------------------------------------------- +// "GNUnet Namestore" service implementation: +//---------------------------------------------------------------------- + +type ZoneIterator struct { + id uint32 + zk *crypto.ZonePrivate + lastUsed util.AbsoluteTime + db *store.ZoneDB + + labels []int64 + pos int +} + +func NewZoneIterator(id uint32, zk *crypto.ZonePrivate, db *store.ZoneDB) (zi *ZoneIterator, err error) { + // get list of labels to handle + var labels []int64 + if labels, err = db.GetLabelIDs(zk); err != nil { + return + } + // assemble zone iterator + zi = &ZoneIterator{ + id: id, + zk: zk, + lastUsed: util.AbsoluteTimeNow(), + db: db, + pos: 0, + labels: labels, + } + return +} + +func (zi *ZoneIterator) Next() *message.NamestoreRecordResultMsg { + if zi.pos == len(zi.labels)-1 { + // end of list reached + return nil + } + + return nil +} + +// NamestoreService to handle namestore requests +type NamestoreService struct { + zm *ZoneMaster + iters *util.Map[uint32, *ZoneIterator] +} + +func NewNamestoreService(zm *ZoneMaster) *NamestoreService { + return &NamestoreService{ + zm: zm, + iters: util.NewMap[uint32, *ZoneIterator](), + } +} + +func (s *NamestoreService) NewIterator(id uint32, zk *crypto.ZonePrivate) *ZoneIterator { + zi := &ZoneIterator{ + id: id, + zk: zk, + lastUsed: util.AbsoluteTimeNow(), + } + s.iters.Put(id, zi, 0) + return zi +} diff --git a/src/gnunet/service/zonemaster/zonemaster.go b/src/gnunet/service/zonemaster/zonemaster.go @@ -21,6 +21,7 @@ package zonemaster import ( "context" "gnunet/config" + "gnunet/core" "gnunet/enums" "gnunet/service/dht/blocks" "gnunet/service/store" @@ -31,22 +32,36 @@ import ( ) //====================================================================== -// "GNS ZoneMaster" implementation: -// Manage and publish local zone records +// "GNS ZoneMaster" implementation (extended): +// Manage local identities for subsystems. Manage and publish +// local GNS zone records. //====================================================================== -// ZoneMaster instance +// ZoneMaster implements type ZoneMaster struct { - cfg *config.Config // Zonemaster configuration - zdb *store.ZoneDB // ZoneDB connection - srv *Service // NameStore service + Module + + zdb *store.ZoneDB // ZoneDB connection + namestore *NamestoreService // namestore subservice + identity *IdentityService // identity subservice } -// NewZoneMaster initializes a new zone master instance. -func NewZoneMaster(cfg *config.Config, srv *Service) *ZoneMaster { - zm := new(ZoneMaster) - zm.cfg = cfg - return zm +// NewService initializes a new zone master service. +func NewService(ctx context.Context, c *core.Core) *ZoneMaster { + mod := NewModule(ctx, c) + srv := &ZoneMaster{ + Module: *mod, + } + + // set external function references (external services) + srv.StoreLocal = srv.StoreNamecache + srv.StoreRemote = srv.StoreDHT + + // instantiate sub-services + srv.namestore = NewNamestoreService(srv) + srv.identity = NewIdentityService(srv) + + return srv } // Run zone master: connect to zone database and start the RPC/HTTP @@ -55,12 +70,8 @@ func NewZoneMaster(cfg *config.Config, srv *Service) *ZoneMaster { func (zm *ZoneMaster) Run(ctx context.Context) { // connect to database logger.Println(logger.INFO, "[zonemaster] Connecting to zone database...") - dbFile, ok := util.GetParam[string](zm.cfg.ZoneMaster.Storage, "file") - if !ok { - logger.Printf(logger.ERROR, "[zonemaster] missing database file specification") - return - } var err error + dbFile, _ := util.GetParam[string](config.Cfg.ZoneMaster.Storage, "file") if zm.zdb, err = store.OpenZoneDB(dbFile); err != nil { logger.Printf(logger.ERROR, "[zonemaster] open database: %v", err) return @@ -69,15 +80,15 @@ func (zm *ZoneMaster) Run(ctx context.Context) { // start HTTP GUI zm.startGUI(ctx) - /* - // publish on start-up - if err = zm.Publish(ctx); err != nil { - logger.Printf(logger.ERROR, "[zonemaster] initial publish failed: %s", err.Error()) - return - } - */ + + // publish on start-up + if err = zm.Publish(ctx); err != nil { + logger.Printf(logger.ERROR, "[zonemaster] initial publish failed: %s", err.Error()) + return + } + // periodically publish GNS blocks to the DHT - tick := time.NewTicker(time.Duration(zm.cfg.ZoneMaster.Period) * time.Second) + tick := time.NewTicker(time.Duration(config.Cfg.ZoneMaster.Period) * time.Second) loop: for { select { @@ -125,34 +136,44 @@ func (zm *ZoneMaster) PublishZoneLabel(ctx context.Context, zone *store.Zone, la zk := zone.Key.Public() logger.Printf(logger.INFO, "[zonemaster] Publishing label '%s' of zone %s", label.Name, zk.ID()) - // collect public records for zone label - recs, err := zm.zdb.GetRecords("lid=%d and flags&%d = 0", label.ID, enums.GNS_FLAG_PRIVATE) + // collect all records for label + rrSet, expire, err := zm.getRecords(zk, label.ID) if err != nil { return err } - // assemble record set and find earliest expiration - expire := util.AbsoluteTimeNever() - rrSet := blocks.NewRecordSet() - for _, r := range recs { - if r.Expire.Compare(expire) < 0 { - expire = r.Expire - } - rrSet.AddRecord(&r.ResourceRecord) - } - rrSet.SetPadding() if rrSet.Count == 0 { logger.Println(logger.INFO, "[zonemaster] No resource records -- skipped") return nil } - // assemble GNS query + // assemble GNS query (common for DHT and Namecache) query := blocks.NewGNSQuery(zk, label.Name) - // assemble, encrypt and sign GNS block - blk, _ := blocks.NewGNSBlock().(*blocks.GNSBlock) + //------------------------------------------------------------------ + // Publish to DHT + //------------------------------------------------------------------ + + // filter out private resource records. + recsDHT := util.Clone(rrSet.Records) + num := uint32(len(recsDHT)) + for i, rec := range recsDHT { + if rec.Flags&enums.GNS_FLAG_PRIVATE != 0 { + copy(recsDHT[i:], recsDHT[i+1:]) + num-- + recsDHT = recsDHT[:num] + } + } + rrsDHT := &blocks.RecordSet{ + Count: num, + Records: recsDHT, + Padding: nil, + } + rrsDHT.SetPadding() - blk.Body.Expire = expire - blk.Body.Data, err = zk.Encrypt(rrSet.Bytes(), label.Name, expire) + // build block for DHT + blkDHT, _ := blocks.NewGNSBlock().(*blocks.GNSBlock) + blkDHT.Body.Expire = expire + blkDHT.Body.Data, err = zk.Encrypt(rrSet.Bytes(), label.Name, expire) if err != nil { return err } @@ -160,19 +181,39 @@ func (zm *ZoneMaster) PublishZoneLabel(ctx context.Context, zone *store.Zone, la if err != nil { return err } - if err = blk.Sign(dzk); err != nil { + if err = blkDHT.Sign(dzk); err != nil { + return err + } + // publish GNS block to DHT + if err = zm.StoreDHT(ctx, query, blkDHT); err != nil { return err } - // DEBUG: - // logger.Printf(logger.DBG, "[zonemaster] Query key = %s", hex.EncodeToString(query.Key().Data)) - // logger.Printf(logger.DBG, "[zonemaster] Block data = %s", hex.EncodeToString(blk.Bytes())) + // DEBUG + /* + logger.Printf(logger.DBG, "[zonemaster] pub = %s", util.EncodeBinaryToString(zk.Bytes())) + logger.Printf(logger.DBG, "[zonemaster] query = %s", hex.EncodeToString(query.Key().Data)) + logger.Printf(logger.DBG, "[zonemaster] blk = %s", hex.EncodeToString(blkDHT.Bytes())) + */ + + //------------------------------------------------------------------ + // Publish to Namecache + //------------------------------------------------------------------ - // publish GNS block to DHT and Namecache - if err = zm.srv.StoreDHT(ctx, query, blk); err != nil { + // build block for Namecache + blkNC, _ := blocks.NewGNSBlock().(*blocks.GNSBlock) + blkNC.Body.Expire = expire + blkNC.Body.Data = rrSet.Bytes() + // sign block + if dzk, _, err = zone.Key.Derive(label.Name, "gns"); err != nil { return err } - if err = zm.srv.StoreNamecache(ctx, query, blk); err != nil { + if err = blkNC.Sign(dzk); err != nil { + return err + } + + // publish GNS block to namecache + if err = zm.StoreNamecache(ctx, query, blkNC); err != nil { return err } return nil diff --git a/src/gnunet/transport/reader_writer.go b/src/gnunet/transport/reader_writer.go @@ -116,7 +116,10 @@ func ReadMessage(ctx context.Context, rdr io.ReadCloser, buf []byte) (msg messag err = fmt.Errorf("message{%d} is nil", mh.MsgType) return } - err = data.Unmarshal(msg, buf[:mh.MsgSize]) + if err = data.Unmarshal(msg, buf[:mh.MsgSize]); err != nil { + return + } + err = msg.Init() /* // DEBUG: incoming messages if mh.MsgType == enums.MSG_DHT_P2P_RESULT {