From caf624535c2089e01ecc6d4c74f056472b3e54e0 Mon Sep 17 00:00:00 2001 From: Bernd Fix Date: Wed, 18 Dec 2019 17:19:32 +0100 Subject: MS2-RC1: Recursive resolution and handling of GNS-specific RRs. --- src/gnunet/config/config.go | 13 +- src/gnunet/core/peer.go | 36 +-- src/gnunet/crypto/gns.go | 35 +++ src/gnunet/crypto/gns_test.go | 150 ++++++++++++ src/gnunet/crypto/hash.go | 3 + src/gnunet/crypto/key_derivation.go | 1 + src/gnunet/crypto/key_exchange.go | 4 +- src/gnunet/crypto/signature.go | 1 + src/gnunet/crypto/symmetric.go | 19 +- src/gnunet/enums/dht.go | 23 ++ src/gnunet/enums/gns.go | 1 + src/gnunet/message/message.go | 8 + src/gnunet/message/msg_core.go | 33 ++- src/gnunet/message/msg_dht.go | 2 + src/gnunet/message/msg_gns.go | 16 +- src/gnunet/message/msg_namecache.go | 4 +- src/gnunet/message/msg_transport.go | 207 ++++++++++------- src/gnunet/service/client.go | 3 +- src/gnunet/service/gns/block.go | 185 +++++++++++++++ src/gnunet/service/gns/block_handler.go | 398 ++++++++++++++++++++++++++++++++ src/gnunet/service/gns/box.go | 151 ++++++++++++ src/gnunet/service/gns/dns.go | 61 +++-- src/gnunet/service/gns/module.go | 257 ++++++++------------- src/gnunet/service/gns/service.go | 12 +- src/gnunet/service/service.go | 13 +- src/gnunet/transport/channel.go | 1 + src/gnunet/transport/channel_netw.go | 39 +++- src/gnunet/transport/connection.go | 9 +- src/gnunet/transport/session.go | 3 +- src/gnunet/util/address.go | 55 +++-- src/gnunet/util/array.go | 10 + src/gnunet/util/format.go | 13 +- src/gnunet/util/id.go | 3 +- src/gnunet/util/peer_id.go | 3 + src/gnunet/util/rnd.go | 8 + 35 files changed, 1414 insertions(+), 366 deletions(-) create mode 100644 src/gnunet/crypto/gns.go create mode 100644 src/gnunet/crypto/gns_test.go create mode 100644 src/gnunet/service/gns/block.go create mode 100644 src/gnunet/service/gns/block_handler.go create mode 100644 src/gnunet/service/gns/box.go (limited to 'src') diff --git a/src/gnunet/config/config.go b/src/gnunet/config/config.go index 1a53941..eaadd98 100644 --- a/src/gnunet/config/config.go +++ b/src/gnunet/config/config.go @@ -7,9 +7,10 @@ import ( "regexp" "strings" + "gnunet/util" + "github.com/bfix/gospel/crypto/ed25519" "github.com/bfix/gospel/logger" - "gnunet/util" ) /////////////////////////////////////////////////////////////////////// @@ -64,6 +65,7 @@ type Config struct { } var ( + // Cfg is the global configuration Cfg *Config ) @@ -88,6 +90,8 @@ var ( rx = regexp.MustCompile("\\$\\{([^\\}]*)\\}") ) +// substString is a helper function to substitute environment variables +// with actual values. func substString(s string, env map[string]string) string { matches := rx.FindAllStringSubmatch(s, -1) for _, m := range matches { @@ -102,7 +106,8 @@ func substString(s string, env map[string]string) string { return s } -// applySubstitutions +// applySubstitutions traverses the configuration data structure +// and applies string substitutions to all string values. func applySubstitutions(x interface{}, env map[string]string) { var process func(v reflect.Value) @@ -140,10 +145,11 @@ func applySubstitutions(x interface{}, env map[string]string) { } } } - + // start processing at the top-level structure v := reflect.ValueOf(x) switch v.Kind() { case reflect.Ptr: + // indirect top-level e := v.Elem() if e.IsValid() { process(e) @@ -151,6 +157,7 @@ func applySubstitutions(x interface{}, env map[string]string) { logger.Printf(logger.ERROR, "[config] 'nil' pointer encountered") } case reflect.Struct: + // direct top-level process(v) } } diff --git a/src/gnunet/core/peer.go b/src/gnunet/core/peer.go index ac2d980..d0b3a4c 100644 --- a/src/gnunet/core/peer.go +++ b/src/gnunet/core/peer.go @@ -3,30 +3,25 @@ package core import ( "fmt" - "github.com/bfix/gospel/crypto/ed25519" "gnunet/message" "gnunet/util" -) -/* -type Peer interface { - GetID() []byte - GetIDString() string - GetAddressList() []*util.Address - Sign(msg []byte) ([]byte, error) - Verify(msg, sig []byte) bool -} -*/ + "github.com/bfix/gospel/crypto/ed25519" +) +// Peer represents a node in the GNUnet P2P network. type Peer struct { - pub *ed25519.PublicKey - idString string - addrList []*util.Address - prv *ed25519.PrivateKey // long-term signing key + prv *ed25519.PrivateKey // node private key (long-term signing key) + pub *ed25519.PublicKey // node public key (=identifier) + idString string // node identifier as string + addrList []*util.Address // list of addresses associated with node ephPrv *ed25519.PrivateKey // ephemeral signing key ephMsg *message.EphemeralKeyMsg // ephemeral signing key message } +// NewPeer instantiates a new peer object from given data. If a local peer +// is created, the data is the seed for generating the private key of the node; +// for a remote peer the data is the binary representation of its public key. func NewPeer(data []byte, local bool) (p *Peer, err error) { p = new(Peer) if local { @@ -45,42 +40,52 @@ func NewPeer(data []byte, local bool) (p *Peer, err error) { return } +// EphKeyMsg returns a new initialized message to negotiate session keys. func (p *Peer) EphKeyMsg() *message.EphemeralKeyMsg { return p.ephMsg } +// SetEphKeyMsg saves a template for new key negotiation messages. func (p *Peer) SetEphKeyMsg(msg *message.EphemeralKeyMsg) { p.ephMsg = msg } +// EphPrvKey returns the current ephemeral private key. func (p *Peer) EphPrvKey() *ed25519.PrivateKey { return p.ephPrv } +// PrvKey return the private key of the node. func (p *Peer) PrvKey() *ed25519.PrivateKey { return p.prv } +// PubKey return the public key of the node. func (p *Peer) PubKey() *ed25519.PublicKey { return p.pub } +// GetID returns the node ID (public key) in binary format func (p *Peer) GetID() []byte { return p.pub.Bytes() } +// GetIDString returns the string representation of the public key of the node. func (p *Peer) GetIDString() string { return p.idString } +// GetAddressList returns a list of addresses associated with this peer. func (p *Peer) GetAddressList() []*util.Address { return p.addrList } +// AddAddress adds a new address for a node. func (p *Peer) AddAddress(a *util.Address) { p.addrList = append(p.addrList, a) } +// Sign a message with the (long-term) private key. func (p *Peer) Sign(msg []byte) (*ed25519.EdSignature, error) { if p.prv == nil { return nil, fmt.Errorf("No private key") @@ -88,6 +93,7 @@ func (p *Peer) Sign(msg []byte) (*ed25519.EdSignature, error) { return p.prv.EdSign(msg) } +// Verify a message signature with the public key of a peer. func (p *Peer) Verify(msg []byte, sig *ed25519.EdSignature) (bool, error) { return p.pub.EdVerify(msg, sig) } diff --git a/src/gnunet/crypto/gns.go b/src/gnunet/crypto/gns.go new file mode 100644 index 0000000..c6a8cd6 --- /dev/null +++ b/src/gnunet/crypto/gns.go @@ -0,0 +1,35 @@ +package crypto + +import ( + "crypto/sha256" + "crypto/sha512" + + "github.com/bfix/gospel/crypto/ed25519" + "golang.org/x/crypto/hkdf" +) + +// DeriveBlockKey returns a symmetric key and initialization vector to decipher a GNS block. +func DeriveBlockKey(label string, pub *ed25519.PublicKey) (iv *SymmetricIV, skey *SymmetricKey) { + // generate symmetric key + prk := hkdf.Extract(sha512.New, pub.Bytes(), []byte("gns-aes-ctx-key")) + rdr := hkdf.Expand(sha256.New, prk, []byte(label)) + skey = NewSymmetricKey() + rdr.Read(skey.AESKey) + rdr.Read(skey.TwofishKey) + + // generate initialization vector + prk = hkdf.Extract(sha512.New, pub.Bytes(), []byte("gns-aes-ctx-iv")) + rdr = hkdf.Expand(sha256.New, prk, []byte(label)) + iv = NewSymmetricIV() + rdr.Read(iv.AESIv) + rdr.Read(iv.TwofishIv) + return +} + +// DecryptBlock for a given zone and label. +func DecryptBlock(data []byte, zoneKey *ed25519.PublicKey, label string) (out []byte, err error) { + // derive key material for decryption + iv, skey := DeriveBlockKey(label, zoneKey) + // perform decryption + return SymmetricDecrypt(data, skey, iv) +} diff --git a/src/gnunet/crypto/gns_test.go b/src/gnunet/crypto/gns_test.go new file mode 100644 index 0000000..7f3cdb8 --- /dev/null +++ b/src/gnunet/crypto/gns_test.go @@ -0,0 +1,150 @@ +package crypto + +import ( + "bytes" + "encoding/hex" + "testing" + + "github.com/bfix/gospel/crypto/ed25519" +) + +var ( + PUB = []byte{ + 0x93, 0x34, 0x71, 0xF6, 0x99, 0x19, 0x0C, 0x62, + 0x85, 0xC7, 0x9B, 0x83, 0x9D, 0xCA, 0x83, 0x91, + 0x38, 0xFA, 0x87, 0xFB, 0xB8, 0xD4, 0xF6, 0xF0, + 0xF0, 0x4B, 0x7F, 0x0A, 0x48, 0xBF, 0x95, 0xF7, + } + LABEL = "home" +) + +func TestDeriveBlockKey(t *testing.T) { + var ( + SKEY = []byte{ + 0x1D, 0x86, 0x8E, 0xF7, 0x30, 0x96, 0x3B, 0x39, + 0x66, 0xE6, 0x49, 0xD8, 0xF1, 0x13, 0x18, 0x39, + 0x8A, 0x7A, 0xB0, 0xF3, 0xDC, 0xF6, 0xE7, 0x2A, + 0xF6, 0x65, 0xDE, 0x86, 0x47, 0x7B, 0x20, 0x1B, + + 0x21, 0xA6, 0xFA, 0x55, 0x7C, 0x29, 0xF5, 0x94, + 0x8E, 0x9A, 0x80, 0xB0, 0xB6, 0xD5, 0x4D, 0x38, + 0x0E, 0x6A, 0x0F, 0x42, 0x4B, 0x27, 0xBB, 0x6A, + 0x1E, 0xD1, 0x33, 0x08, 0xD6, 0x2E, 0x21, 0x8C, + } + IV = []byte{ + 0xAC, 0x18, 0x03, 0xB7, 0x8B, 0x1E, 0x09, 0xA9, + 0xD0, 0x20, 0x47, 0x2B, 0x1B, 0x23, 0xE8, 0x24, + + 0xC9, 0x23, 0x9E, 0x61, 0x3A, 0x8D, 0x95, 0xA9, + 0x3F, 0x6C, 0x1C, 0xC8, 0xCB, 0xD1, 0xBD, 0x6B, + } + ) + + iv, skey := DeriveBlockKey(LABEL, ed25519.NewPublicKeyFromBytes(PUB)) + + if bytes.Compare(IV[:16], iv.AESIv) != 0 { + t.Logf("AES_IV(computed) = %s\n", hex.EncodeToString(iv.AESIv)) + t.Logf("AES_IV(expected) = %s\n", hex.EncodeToString(IV[:16])) + t.Fatal("AES IV mismatch") + } + if bytes.Compare(IV[16:], iv.TwofishIv) != 0 { + t.Logf("Twofish_IV(computed) = %s\n", hex.EncodeToString(iv.TwofishIv)) + t.Logf("Twofish_IV(expected) = %s\n", hex.EncodeToString(IV[16:])) + t.Fatal("Twofish IV mismatch") + } + + if bytes.Compare(SKEY[:32], skey.AESKey) != 0 { + t.Logf("AES_KEY(computed) = %s\n", hex.EncodeToString(skey.AESKey)) + t.Logf("AES_KEY(expected) = %s\n", hex.EncodeToString(SKEY[:32])) + t.Fatal("AES KEY mismatch") + } + if bytes.Compare(SKEY[32:], skey.TwofishKey) != 0 { + t.Logf("Twofish_KEY(computed) = %s\n", hex.EncodeToString(skey.TwofishKey)) + t.Logf("Twofish_KEY(expected) = %s\n", hex.EncodeToString(SKEY[32:])) + t.Fatal("Twofish KEY mismatch") + } +} + +func TestDecryptBlock(t *testing.T) { + var ( + DATA = []byte{ + 0xAC, 0xA5, 0x3C, 0x55, 0x63, 0x21, 0x31, 0x1F, + 0x11, 0x6E, 0xEF, 0x48, 0xED, 0x53, 0x46, 0x31, + 0x7C, 0x50, 0xFB, 0x6B, 0xA6, 0xC8, 0x6C, 0x46, + 0x1E, 0xE3, 0xCA, 0x45, 0xCD, 0x5B, 0xD6, 0x86, + 0x42, 0x87, 0xEF, 0x18, 0xCE, 0x8E, 0x83, 0x21, + 0x04, 0xCB, 0xCF, 0x40, 0x7E, 0x0F, 0x51, 0x54, + 0xE2, 0x3C, 0xDE, 0xE9, 0x22, 0x00, 0xFF, 0x40, + 0xBB, 0x53, 0xE3, 0x69, 0x99, 0x92, 0x47, 0x97, + 0xF0, 0x4E, 0x3B, 0x70, + } + OUT = []byte{ + 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0xAD, 0x0E, + 0x60, 0x28, 0xFE, 0x80, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, + 0x0A, 0x68, 0x6F, 0x69, 0x2D, 0x70, 0x6F, 0x6C, + 0x6C, 0x6F, 0x69, 0x03, 0x6F, 0x72, 0x67, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + } + ) + + out, err := DecryptBlock(DATA, ed25519.NewPublicKeyFromBytes(PUB), LABEL) + if err != nil { + t.Fatal(err) + } + if bytes.Compare(out, OUT) != 0 { + t.Logf("Decrypt(computed) = %s\n", hex.EncodeToString(out)) + t.Logf("Decrypt(expected) = %s\n", hex.EncodeToString(OUT)) + t.Fatal("Decryptions failed") + } +} + +func TestVerifyBlock(t *testing.T) { + var ( + SIGNED = []byte{ + 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x0f, + 0x00, 0x05, 0xad, 0x0e, 0x60, 0x28, 0xfe, 0x80, + 0xac, 0xa5, 0x3c, 0x55, 0x63, 0x21, 0x31, 0x1f, + 0x11, 0x6e, 0xef, 0x48, 0xed, 0x53, 0x46, 0x31, + 0x7c, 0x50, 0xfb, 0x6b, 0xa6, 0xc8, 0x6c, 0x46, + 0x1e, 0xe3, 0xca, 0x45, 0xcd, 0x5b, 0xd6, 0x86, + 0x42, 0x87, 0xef, 0x18, 0xce, 0x8e, 0x83, 0x21, + 0x04, 0xcb, 0xcf, 0x40, 0x7e, 0x0f, 0x51, 0x54, + 0xe2, 0x3c, 0xde, 0xe9, 0x22, 0x00, 0xff, 0x40, + 0xbb, 0x53, 0xe3, 0x69, 0x99, 0x92, 0x47, 0x97, + 0xf0, 0x4e, 0x3b, 0x70, + } + SIG = []byte{ + 0x09, 0xc9, 0x6a, 0xda, 0x69, 0xce, 0x7c, 0x91, + 0xbd, 0xa4, 0x59, 0xdc, 0xc9, 0x76, 0xf4, 0x6c, + 0x62, 0xb7, 0x79, 0x3f, 0x94, 0xb2, 0xf6, 0xf0, + 0x90, 0x17, 0x4e, 0x2f, 0x68, 0x49, 0xf8, 0xcc, + + 0x0b, 0x77, 0x32, 0x32, 0x28, 0x77, 0x2d, 0x2a, + 0x31, 0x31, 0xc1, 0x2c, 0x44, 0x18, 0xf2, 0x5f, + 0x1a, 0xe9, 0x8b, 0x2e, 0x65, 0xca, 0x1d, 0xe8, + 0x22, 0x82, 0x6a, 0x06, 0xe0, 0x6a, 0x5a, 0xe5, + } + PUB = []byte{ + 0x26, 0x84, 0x1b, 0x24, 0x35, 0xa4, 0x63, 0xe9, + 0xf0, 0x48, 0xae, 0x3e, 0xf7, 0xe8, 0x1b, 0xca, + 0x55, 0x9f, 0x4c, 0x1e, 0x16, 0x18, 0xa6, 0xd3, + 0x5b, 0x91, 0x0d, 0x54, 0x31, 0x6e, 0xbf, 0x97, + } + ) + sig, err := ed25519.NewEcSignatureFromBytes(SIG) + if err != nil { + t.Fatal(err) + } + dkey := ed25519.NewPublicKeyFromBytes(PUB) + ok, err := dkey.EcVerify(SIGNED, sig) + if err != nil { + t.Fatal(err) + } + if !ok { + t.Fatal("EcDSA verify failed") + } +} diff --git a/src/gnunet/crypto/hash.go b/src/gnunet/crypto/hash.go index 87e3941..0fe7c7e 100644 --- a/src/gnunet/crypto/hash.go +++ b/src/gnunet/crypto/hash.go @@ -6,16 +6,19 @@ import ( "gnunet/util" ) +// HashCode is the result of a 512-bit hash function (SHA-512) type HashCode struct { Bits []byte `size:"64"` } +// NewHashCode creates a new, uninitalized hash value func NewHashCode() *HashCode { return &HashCode{ Bits: make([]byte, 64), } } +// Hash returns the SHA-512 hash value of a given blob func Hash(data []byte) *HashCode { val := sha512.Sum512(data) return &HashCode{ diff --git a/src/gnunet/crypto/key_derivation.go b/src/gnunet/crypto/key_derivation.go index 575f511..976db2a 100644 --- a/src/gnunet/crypto/key_derivation.go +++ b/src/gnunet/crypto/key_derivation.go @@ -9,6 +9,7 @@ import ( "golang.org/x/crypto/hkdf" ) +// Curve parameters var ( ED25519_N = ed25519.GetCurve().N ) diff --git a/src/gnunet/crypto/key_exchange.go b/src/gnunet/crypto/key_exchange.go index 0efa441..1403249 100644 --- a/src/gnunet/crypto/key_exchange.go +++ b/src/gnunet/crypto/key_exchange.go @@ -4,8 +4,8 @@ import ( "github.com/bfix/gospel/crypto/ed25519" ) -// SharedSecret computes a 64 byte shared secret -// between (prvA,pubB) and (prvB,pubA). +// SharedSecret computes a 64 byte shared secret between (prvA,pubB) +// and (prvB,pubA) by a Diffie-Hellman-like scheme. func SharedSecret(prv *ed25519.PrivateKey, pub *ed25519.PublicKey) *HashCode { ss := pub.Mult(prv.D).Q.X().Bytes() return Hash(ss) diff --git a/src/gnunet/crypto/signature.go b/src/gnunet/crypto/signature.go index 9a69b56..ba8e535 100644 --- a/src/gnunet/crypto/signature.go +++ b/src/gnunet/crypto/signature.go @@ -1,5 +1,6 @@ package crypto +// SignaturePurpose is the GNUnet data structure used as header for signed data. type SignaturePurpose struct { Size uint32 `order:"big"` // How many bytes are signed? Purpose uint32 `order:"big"` // Signature purpose diff --git a/src/gnunet/crypto/symmetric.go b/src/gnunet/crypto/symmetric.go index 82116a4..5607d3a 100644 --- a/src/gnunet/crypto/symmetric.go +++ b/src/gnunet/crypto/symmetric.go @@ -8,11 +8,17 @@ import ( "golang.org/x/crypto/twofish" ) +// Symmetric encryption in GNUnet uses a two-layer scheme: +// * Encryption: OUT = twofish_cfb(aes_cfb(IN)) +// * Decryption: OUT = aes_cfb(twofish_cfb(IN)) + +// SymmetricKey is a key for the GNUnet-specific two-layer encryption scheme. type SymmetricKey struct { - AESKey []byte `size:"32"` - TwofishKey []byte `size:"32"` + AESKey []byte `size:"32"` // key for AES-CFB + TwofishKey []byte `size:"32"` // key for Twofish-CFB } +// NewSymmetricKey generates a new (random) symmetric key. func NewSymmetricKey() *SymmetricKey { skey := &SymmetricKey{ AESKey: make([]byte, 32), @@ -23,11 +29,14 @@ func NewSymmetricKey() *SymmetricKey { return skey } +// SymmetricIV is an initialization vector for the GNUnet-specific two-layer +// encryption scheme. type SymmetricIV struct { - AESIv []byte `size:"16"` - TwofishIv []byte `size:"16"` + AESIv []byte `size:"16"` // IV for AES-CFB + TwofishIv []byte `size:"16"` // IV for Twofish-CFB } +// NewSymmetricIV generates a new (random) initialization vector. func NewSymmetricIV() *SymmetricIV { iv := &SymmetricIV{ AESIv: make([]byte, 16), @@ -38,6 +47,7 @@ func NewSymmetricIV() *SymmetricIV { return iv } +// SymmetricDecrypt decrypts the data with given key and initialization vector. func SymmetricDecrypt(data []byte, skey *SymmetricKey, iv *SymmetricIV) ([]byte, error) { // Decrypt with Twofish CFB stream cipher tf, err := twofish.NewCipher(skey.TwofishKey) @@ -58,6 +68,7 @@ func SymmetricDecrypt(data []byte, skey *SymmetricKey, iv *SymmetricIV) ([]byte, return out, nil } +// SymmetricEncrypt encrypts the data with given key and initialization vector. func SymmetricEncrypt(data []byte, skey *SymmetricKey, iv *SymmetricIV) ([]byte, error) { // Encrypt with AES CFB stream cipher aes, err := aes.NewCipher(skey.AESKey) diff --git a/src/gnunet/enums/dht.go b/src/gnunet/enums/dht.go index 87c36a3..8f004d7 100644 --- a/src/gnunet/enums/dht.go +++ b/src/gnunet/enums/dht.go @@ -1,5 +1,6 @@ package enums +// DHT flags and settings var ( DHT_RO_NONE = 0 // Default. Do nothing special. DHT_RO_DEMULTIPLEX_EVERYWHERE = 1 // Each peer along the way should look at 'enc' @@ -10,3 +11,25 @@ var ( DHT_GNS_REPLICATION_LEVEL = 10 ) + +// DHT block types +var ( + BLOCK_TYPE_ANY = 0 // Any type of block, used as a wildcard when searching. + BLOCK_TYPE_FS_DBLOCK = 1 // Data block (leaf) in the CHK tree. + BLOCK_TYPE_FS_IBLOCK = 2 // Inner block in the CHK tree. + BLOCK_TYPE_FS_KBLOCK = 3 // Legacy type, no longer in use. + BLOCK_TYPE_FS_SBLOCK = 4 // Legacy type, no longer in use. + BLOCK_TYPE_FS_NBLOCK = 5 // Legacy type, no longer in use. + BLOCK_TYPE_FS_ONDEMAND = 6 // Type of a block representing a block to be encoded on demand from disk. + BLOCK_TYPE_DHT_HELLO = 7 // Type of a block that contains a HELLO for a peer + BLOCK_TYPE_TEST = 8 // Block for testing. + BLOCK_TYPE_FS_UBLOCK = 9 // Type of a block representing any type of search result (universal). + BLOCK_TYPE_DNS = 10 // Block for storing DNS exit service advertisements. + BLOCK_TYPE_GNS_NAMERECORD = 11 // Block for storing record data + BLOCK_TYPE_REVOCATION = 12 // Block type for a revocation message by which a key is revoked. + + BLOCK_TYPE_REGEX = 22 // Block to store a cadet regex state + BLOCK_TYPE_REGEX_ACCEPT = 23 // Block to store a cadet regex accepting state + BLOCK_TYPE_SET_TEST = 24 // Block for testing set/consensus. + BLOCK_TYPE_CONSENSUS_ELEMENT = 25 // Block type for consensus elements. +) diff --git a/src/gnunet/enums/gns.go b/src/gnunet/enums/gns.go index 2de5048..d5b1f7a 100644 --- a/src/gnunet/enums/gns.go +++ b/src/gnunet/enums/gns.go @@ -1,5 +1,6 @@ package enums +// GNS constants var ( GNS_MAX_BLOCK_SIZE = (63 * 1024) // Maximum size of a value that can be stored in a GNS block. diff --git a/src/gnunet/message/message.go b/src/gnunet/message/message.go index 6b49a55..e792ae8 100644 --- a/src/gnunet/message/message.go +++ b/src/gnunet/message/message.go @@ -6,27 +6,35 @@ import ( "github.com/bfix/gospel/data" ) +// Error codes var ( ErrMsgHeaderTooSmall = errors.New("Message header too small") ) +// Message is an interface for all GNUnet-specific messages. type Message interface { Header() *MessageHeader } +// MessageHeader encapsulates the common part of all GNUnet messages (at the +// beginning of the data). type MessageHeader struct { MsgSize uint16 `order:"big"` MsgType uint16 `order:"big"` } +// Size returns the total size of the message (header + body) func (mh *MessageHeader) Size() uint16 { return mh.MsgSize } +// Type returns the message type (defines the layout of the body data) func (mh *MessageHeader) Type() uint16 { return mh.MsgType } +// GetMsgHeader returns the header of a message from a byte array (as the +// serialized form). func GetMsgHeader(b []byte) (mh *MessageHeader, err error) { if b == nil || len(b) < 4 { return nil, ErrMsgHeaderTooSmall diff --git a/src/gnunet/message/msg_core.go b/src/gnunet/message/msg_core.go index 0ddbd8b..538fcea 100644 --- a/src/gnunet/message/msg_core.go +++ b/src/gnunet/message/msg_core.go @@ -5,21 +5,25 @@ import ( "fmt" "time" - "github.com/bfix/gospel/crypto/ed25519" - "github.com/bfix/gospel/data" + "gnunet/crypto" "gnunet/enums" "gnunet/util" + + "github.com/bfix/gospel/crypto/ed25519" + "github.com/bfix/gospel/data" ) +// EphKeyBlock defines the layout of signed ephemeral key with attributes. type EphKeyBlock struct { - SignSize uint32 `order:"big"` // length of signed block - SigPurpose uint32 `order:"big"` // signature purpose: SIG_ECC_KEY - CreateTime util.AbsoluteTime // Time of key creation - ExpireTime util.RelativeTime // Time to live for key - EphemeralKey []byte `size:"32"` // Ephemeral EdDSA public key - PeerID *util.PeerID // Peer identity (EdDSA public key) + Purpose *crypto.SignaturePurpose // signature purpose: SIG_ECC_KEY + CreateTime util.AbsoluteTime // Time of key creation + ExpireTime util.RelativeTime // Time to live for key + EphemeralKey []byte `size:"32"` // Ephemeral EdDSA public key + PeerID *util.PeerID // Peer identity (EdDSA public key) } +// EphemeralKeyMsg announces a new transient key for a peer. The key is signed +// by the issuing peer. type EphemeralKeyMsg struct { MsgSize uint16 `order:"big"` // total size of message MsgType uint16 `order:"big"` // CORE_EPHEMERAL_KEY (88) @@ -28,6 +32,7 @@ type EphemeralKeyMsg struct { SignedBlock *EphKeyBlock } +// NewEphemeralKeyMsg creates an empty message for key announcement. func NewEphemeralKeyMsg() *EphemeralKeyMsg { return &EphemeralKeyMsg{ MsgSize: 160, @@ -35,8 +40,10 @@ func NewEphemeralKeyMsg() *EphemeralKeyMsg { SenderStatus: 1, Signature: make([]byte, 64), SignedBlock: &EphKeyBlock{ - SignSize: 88, - SigPurpose: enums.SIG_ECC_KEY, + Purpose: &crypto.SignaturePurpose{ + Size: 88, + Purpose: enums.SIG_ECC_KEY, + }, CreateTime: util.AbsoluteTimeNow(), ExpireTime: util.NewRelativeTime(12 * time.Hour), EphemeralKey: make([]byte, 32), @@ -45,6 +52,7 @@ func NewEphemeralKeyMsg() *EphemeralKeyMsg { } } +// 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}", util.EncodeBinaryToString(m.SignedBlock.PeerID.Key), @@ -58,10 +66,13 @@ func (msg *EphemeralKeyMsg) Header() *MessageHeader { return &MessageHeader{msg.MsgSize, msg.MsgType} } +// Public extracts the public key of an announcing peer. func (m *EphemeralKeyMsg) Public() *ed25519.PublicKey { return ed25519.NewPublicKeyFromBytes(m.SignedBlock.PeerID.Key) } +// Verify the integrity of the message data using the public key of the +// announcing peer. func (m *EphemeralKeyMsg) Verify(pub *ed25519.PublicKey) (bool, error) { data, err := data.Marshal(m.SignedBlock) if err != nil { @@ -74,6 +85,8 @@ func (m *EphemeralKeyMsg) Verify(pub *ed25519.PublicKey) (bool, error) { return pub.EdVerify(data, sig) } +// NewEphemeralKey creates a new ephemeral key signed by a long-term private +// key and the corresponding GNUnet message to announce the new key. func NewEphemeralKey(peerId []byte, ltPrv *ed25519.PrivateKey) (*ed25519.PrivateKey, *EphemeralKeyMsg, error) { msg := NewEphemeralKeyMsg() copy(msg.SignedBlock.PeerID.Key, peerId) diff --git a/src/gnunet/message/msg_dht.go b/src/gnunet/message/msg_dht.go index bd3475a..b1d28ba 100644 --- a/src/gnunet/message/msg_dht.go +++ b/src/gnunet/message/msg_dht.go @@ -51,6 +51,7 @@ func (m *DHTClientGetMsg) SetXQuery(xq []byte) []byte { return prev } +// String returns a human-readable representation of the message. func (m *DHTClientGetMsg) String() string { return fmt.Sprintf("DHTClientGetMsg{Id:%d,Type=%d,Options=%d,Repl=%d,Key=%s}", m.Id, m.Type, m.Options, m.ReplLevel, hex.EncodeToString(m.Key.Bits)) @@ -98,6 +99,7 @@ func NewDHTClientResultMsg(key *crypto.HashCode) *DHTClientResultMsg { } } +// String returns a human-readable representation of the message. func (m *DHTClientResultMsg) String() string { return fmt.Sprintf("DHTClientResultMsg{id:%d,expire=%s}", m.Id, m.Expire) } diff --git a/src/gnunet/message/msg_gns.go b/src/gnunet/message/msg_gns.go index 0ff34be..b01c410 100644 --- a/src/gnunet/message/msg_gns.go +++ b/src/gnunet/message/msg_gns.go @@ -3,9 +3,10 @@ package message import ( "fmt" - "github.com/bfix/gospel/logger" "gnunet/enums" "gnunet/util" + + "github.com/bfix/gospel/logger" ) //---------------------------------------------------------------------- @@ -38,13 +39,13 @@ func NewGNSLookupMsg() *GNSLookupMsg { } } -// SetName +// SetName appends the name to lookup to the message func (m *GNSLookupMsg) SetName(name string) { m.Name = util.Clone(append([]byte(name), 0)) m.MsgSize = uint16(48 + len(m.Name)) } -// GetName +// GetName returns the name to lookup from the message func (m *GNSLookupMsg) GetName() string { size := len(m.Name) if m.Name[size-1] != 0 { @@ -55,7 +56,7 @@ func (m *GNSLookupMsg) GetName() string { return string(m.Name[:size]) } -// String +// String returns a human-readable representation of the message. func (m *GNSLookupMsg) String() string { return fmt.Sprintf( "GNSLookupMsg{Id=%d,Zone=%s,Options=%d,Type=%d,Name=%s}", @@ -72,6 +73,8 @@ func (msg *GNSLookupMsg) Header() *MessageHeader { // GNS_LOOKUP_RESULT //---------------------------------------------------------------------- +// GNSResourceRecord is the GNUnet-specific representation of resource +// records (not to be confused with DNS resource records). type GNSResourceRecord struct { Expires util.AbsoluteTime // Expiration time for the record Size uint32 `order:"big"` // Number of bytes in 'Data' @@ -80,6 +83,7 @@ type GNSResourceRecord struct { Data []byte `size:"Size"` // Record data } +// String returns a human-readable representation of the message. func (r *GNSResourceRecord) String() string { return fmt.Sprintf("GNSResourceRecord{type=%s,expire=%s,flags=%d,size=%d}", enums.GNS_TYPE[int(r.Type)], r.Expires, r.Flags, r.Size) @@ -105,7 +109,7 @@ func NewGNSLookupResultMsg(id uint32) *GNSLookupResultMsg { } } -// AddRecord +// AddRecord adds a GNS resource recordto the response message. func (m *GNSLookupResultMsg) AddRecord(rec *GNSResourceRecord) error { recSize := 20 + int(rec.Size) if int(m.MsgSize)+recSize > enums.GNS_MAX_BLOCK_SIZE { @@ -117,7 +121,7 @@ func (m *GNSLookupResultMsg) AddRecord(rec *GNSResourceRecord) error { return nil } -// String +// String returns a human-readable representation of the message. func (m *GNSLookupResultMsg) String() string { return fmt.Sprintf("GNSLookupResultMsg{Id=%d,Count=%d}", m.Id, m.Count) } diff --git a/src/gnunet/message/msg_namecache.go b/src/gnunet/message/msg_namecache.go index 8f82622..9e3312d 100644 --- a/src/gnunet/message/msg_namecache.go +++ b/src/gnunet/message/msg_namecache.go @@ -33,7 +33,7 @@ func NewNamecacheLookupMsg(query *crypto.HashCode) *NamecacheLookupMsg { } } -// String +// String returns a human-readable representation of the message. func (m *NamecacheLookupMsg) String() string { return fmt.Sprintf("NamecacheLookupMsg{Id=%d,Query=%s}", m.Id, hex.EncodeToString(m.Query.Bits)) @@ -72,7 +72,7 @@ func NewNamecacheLookupResultMsg() *NamecacheLookupResultMsg { } } -// String +// String returns a human-readable representation of the message. func (m *NamecacheLookupResultMsg) String() string { return fmt.Sprintf("NamecacheLookupResultMsg{id=%d,expire=%s}", m.Id, m.Expire) diff --git a/src/gnunet/message/msg_transport.go b/src/gnunet/message/msg_transport.go index 1459d6a..54e63f2 100644 --- a/src/gnunet/message/msg_transport.go +++ b/src/gnunet/message/msg_transport.go @@ -4,22 +4,26 @@ import ( "fmt" "time" - "github.com/bfix/gospel/crypto/ed25519" - "github.com/bfix/gospel/data" + "gnunet/crypto" "gnunet/enums" "gnunet/util" + + "github.com/bfix/gospel/crypto/ed25519" + "github.com/bfix/gospel/data" ) //---------------------------------------------------------------------- // TRANSPORT_TCP_WELCOME //---------------------------------------------------------------------- +// TransportTcpWelcomeMsg type TransportTcpWelcomeMsg struct { MsgSize uint16 `order:"big"` // total size of message MsgType uint16 `order:"big"` // TRANSPORT_TCP_WELCOME (61) PeerID *util.PeerID // Peer identity (EdDSA public key) } +// NewTransportTcpWelcomeMsg creates a new message for a given peer. func NewTransportTcpWelcomeMsg(peerid *util.PeerID) *TransportTcpWelcomeMsg { if peerid == nil { peerid = util.NewPeerID(nil) @@ -31,6 +35,7 @@ func NewTransportTcpWelcomeMsg(peerid *util.PeerID) *TransportTcpWelcomeMsg { } } +// String returns a human-readable representation of the message. func (m *TransportTcpWelcomeMsg) String() string { return fmt.Sprintf("TransportTcpWelcomeMsg{peer=%s}", m.PeerID) } @@ -40,6 +45,59 @@ func (msg *TransportTcpWelcomeMsg) Header() *MessageHeader { return &MessageHeader{msg.MsgSize, msg.MsgType} } +//---------------------------------------------------------------------- +// TRANSPORT_PING +// +// Message used to ask a peer to validate receipt (to check an address +// from a HELLO). Followed by the address we are trying to validate, +// or an empty address if we are just sending a PING to confirm that a +// connection which the receiver (of the PING) initiated is still valid. +//---------------------------------------------------------------------- + +// TransportPingMsg +type TransportPingMsg struct { + MsgSize uint16 `order:"big"` // total size of message + MsgType uint16 `order:"big"` // TRANSPORT_PING (372) + Challenge uint32 // Challenge code (to ensure fresh reply) + Target *util.PeerID // EdDSA public key (long-term) of target peer + Address []byte `size:"*"` // encoded address +} + +// TransportPingMsg creates a new message for given peer with an address to +// be validated. +func NewTransportPingMsg(target *util.PeerID, a *util.Address) *TransportPingMsg { + if target == nil { + target = util.NewPeerID(nil) + } + m := &TransportPingMsg{ + MsgSize: uint16(40), + MsgType: TRANSPORT_PING, + Challenge: util.RndUInt32(), + Target: target, + Address: nil, + } + if a != nil { + if addrData, err := data.Marshal(a); err == nil { + m.Address = addrData + m.MsgSize += uint16(len(addrData)) + } + } + return m +} + +// String returns a human-readable representation of the message. +func (m *TransportPingMsg) String() string { + a := new(util.Address) + data.Unmarshal(a, m.Address) + return fmt.Sprintf("TransportPingMsg{target=%s,addr=%s,challenge=%d}", + m.Target, a, m.Challenge) +} + +// Header returns the message header in a separate instance. +func (msg *TransportPingMsg) Header() *MessageHeader { + return &MessageHeader{msg.MsgSize, msg.MsgType} +} + //---------------------------------------------------------------------- // TRANSPORT_PONG // @@ -53,29 +111,33 @@ func (msg *TransportTcpWelcomeMsg) Header() *MessageHeader { // a connection that we initiated). //---------------------------------------------------------------------- +// SignedAddress is the signed block of data representing a node address type SignedAddress struct { - SignLength uint32 `order:"big"` // Length of signed block - Purpose uint32 `order:"big"` // SIG_TRANSPORT_PONG_OWN - ExpireOn util.AbsoluteTime // usec epoch - AddrSize uint32 `order:"big"` // size of address - Address []byte `size:"AddrSize"` // address + Purpose *crypto.SignaturePurpose // SIG_TRANSPORT_PONG_OWN + ExpireOn util.AbsoluteTime // usec epoch + AddrSize uint32 `order:"big"` // size of address + Address []byte `size:"AddrSize"` // address } +// NewSignedAddress creates a new (signable) data block from an address. func NewSignedAddress(a *util.Address) *SignedAddress { // serialize address addrData, _ := data.Marshal(a) alen := len(addrData) addr := &SignedAddress{ - SignLength: uint32(alen + 20), - Purpose: enums.SIG_TRANSPORT_PONG_OWN, - ExpireOn: util.AbsoluteTimeNow().Add(12 * time.Hour), - AddrSize: uint32(alen), - Address: make([]byte, alen), + Purpose: &crypto.SignaturePurpose{ + Size: uint32(alen + 20), + Purpose: enums.SIG_TRANSPORT_PONG_OWN, + }, + ExpireOn: util.AbsoluteTimeNow().Add(12 * time.Hour), + AddrSize: uint32(alen), + Address: make([]byte, alen), } copy(addr.Address, addrData) return addr } +// TransportPongMsg type TransportPongMsg struct { MsgSize uint16 `order:"big"` // total size of message MsgType uint16 `order:"big"` // TRANSPORT_PING (372) @@ -84,6 +146,8 @@ type TransportPongMsg struct { SignedBlock *SignedAddress // signed block of data } +// NewTransportPongMsg creates a reponse message with an address the replying +// peer wants to be reached. func NewTransportPongMsg(challenge uint32, a *util.Address) *TransportPongMsg { m := &TransportPongMsg{ MsgSize: 72, @@ -94,12 +158,13 @@ func NewTransportPongMsg(challenge uint32, a *util.Address) *TransportPongMsg { } if a != nil { sa := NewSignedAddress(a) - m.MsgSize += uint16(sa.SignLength) + m.MsgSize += uint16(sa.Purpose.Size) m.SignedBlock = sa } return m } +// String returns a human-readable representation of the message. func (m *TransportPongMsg) String() string { a := new(util.Address) if err := data.Unmarshal(a, m.SignedBlock.Address); err == nil { @@ -114,6 +179,7 @@ func (msg *TransportPongMsg) Header() *MessageHeader { return &MessageHeader{msg.MsgSize, msg.MsgType} } +// Sign the address block of a pong message. func (m *TransportPongMsg) Sign(prv *ed25519.PrivateKey) error { data, err := data.Marshal(m.SignedBlock) if err != nil { @@ -127,6 +193,7 @@ func (m *TransportPongMsg) Sign(prv *ed25519.PrivateKey) error { return nil } +// Verify the address block of a pong message func (m *TransportPongMsg) Verify(pub *ed25519.PublicKey) (bool, error) { data, err := data.Marshal(m.SignedBlock) if err != nil { @@ -139,55 +206,6 @@ func (m *TransportPongMsg) Verify(pub *ed25519.PublicKey) (bool, error) { return pub.EdVerify(data, sig) } -//---------------------------------------------------------------------- -// TRANSPORT_PING -// -// Message used to ask a peer to validate receipt (to check an address -// from a HELLO). Followed by the address we are trying to validate, -// or an empty address if we are just sending a PING to confirm that a -// connection which the receiver (of the PING) initiated is still valid. -//---------------------------------------------------------------------- - -type TransportPingMsg struct { - MsgSize uint16 `order:"big"` // total size of message - MsgType uint16 `order:"big"` // TRANSPORT_PING (372) - Challenge uint32 // Challenge code (to ensure fresh reply) - Target *util.PeerID // EdDSA public key (long-term) of target peer - Address []byte `size:"*"` // encoded address -} - -func NewTransportPingMsg(target *util.PeerID, a *util.Address) *TransportPingMsg { - if target == nil { - target = util.NewPeerID(nil) - } - m := &TransportPingMsg{ - MsgSize: uint16(40), - MsgType: TRANSPORT_PING, - Challenge: util.RndUInt32(), - Target: target, - Address: nil, - } - if a != nil { - if addrData, err := data.Marshal(a); err == nil { - m.Address = addrData - m.MsgSize += uint16(len(addrData)) - } - } - return m -} - -func (m *TransportPingMsg) String() string { - a := new(util.Address) - data.Unmarshal(a, m.Address) - return fmt.Sprintf("TransportPingMsg{target=%s,addr=%s,challenge=%d}", - m.Target, a, m.Challenge) -} - -// Header returns the message header in a separate instance. -func (msg *TransportPingMsg) Header() *MessageHeader { - return &MessageHeader{msg.MsgSize, msg.MsgType} -} - //---------------------------------------------------------------------- // HELLO // @@ -202,6 +220,7 @@ func (msg *TransportPingMsg) Header() *MessageHeader { // 4) address (address-length bytes) //---------------------------------------------------------------------- +// HelloAddress type HelloAddress struct { Transport string // Name of transport AddrSize uint16 `order:"big"` // Size of address entry @@ -209,6 +228,7 @@ type HelloAddress struct { Address []byte `size:"AddrSize"` // Address specification } +// NewHelloAddress create a new HELLO address from the given address func NewAddress(a *util.Address) *HelloAddress { addr := &HelloAddress{ Transport: a.Transport, @@ -220,11 +240,13 @@ func NewAddress(a *util.Address) *HelloAddress { return addr } +// String returns a human-readable representation of the message. func (a *HelloAddress) String() string { return fmt.Sprintf("Address{%s,expire=%s}", util.AddressString(a.Transport, a.Address), a.ExpireOn) } +// HelloMsg type HelloMsg struct { MsgSize uint16 `order:"big"` // total size of message MsgType uint16 `order:"big"` // HELLO (17) @@ -233,6 +255,7 @@ type HelloMsg struct { Addresses []*HelloAddress `size:"*"` // List of end-point addressess } +// NewHelloMsg creates a new HELLO msg for a given peer. func NewHelloMsg(peerid *util.PeerID) *HelloMsg { if peerid == nil { peerid = util.NewPeerID(nil) @@ -246,11 +269,13 @@ func NewHelloMsg(peerid *util.PeerID) *HelloMsg { } } +// String returns a human-readable representation of the message. func (m *HelloMsg) String() string { return fmt.Sprintf("HelloMsg{peer=%s,friendsonly=%d,addr=%v}", m.PeerID, m.FriendOnly, m.Addresses) } +// AddAddress adds a new address to the HELLO message. func (m *HelloMsg) AddAddress(a *HelloAddress) { m.Addresses = append(m.Addresses, a) m.MsgSize += uint16(len(a.Transport)) + a.AddrSize + 11 @@ -265,11 +290,13 @@ func (msg *HelloMsg) Header() *MessageHeader { // TRANSPORT_SESSION_ACK //---------------------------------------------------------------------- +// SessionAckMsg type SessionAckMsg struct { MsgSize uint16 `order:"big"` // total size of message MsgType uint16 `order:"big"` // TRANSPORT_SESSION_ACK (377) } +// NewSessionAckMsg creates an new message (no body required). func NewSessionAckMsg() *SessionAckMsg { return &SessionAckMsg{ MsgSize: 16, @@ -277,6 +304,7 @@ func NewSessionAckMsg() *SessionAckMsg { } } +// String returns a human-readable representation of the message. func (m *SessionAckMsg) String() string { return "SessionAck{}" } @@ -290,6 +318,7 @@ func (msg *SessionAckMsg) Header() *MessageHeader { // TRANSPORT_SESSION_SYN //---------------------------------------------------------------------- +// SessionSynMsg type SessionSynMsg struct { MsgSize uint16 `order:"big"` // total size of message MsgType uint16 `order:"big"` // TRANSPORT_SESSION_SYN (375) @@ -297,6 +326,7 @@ type SessionSynMsg struct { Timestamp util.AbsoluteTime // usec epoch } +// NewSessionSynMsg creates a SYN request for the a session func NewSessionSynMsg() *SessionSynMsg { return &SessionSynMsg{ MsgSize: 16, @@ -306,6 +336,7 @@ func NewSessionSynMsg() *SessionSynMsg { } } +// String returns a human-readable representation of the message. func (m *SessionSynMsg) String() string { return fmt.Sprintf("SessionSyn{timestamp=%s}", m.Timestamp) } @@ -319,6 +350,7 @@ func (msg *SessionSynMsg) Header() *MessageHeader { // TRANSPORT_SESSION_SYN_ACK //---------------------------------------------------------------------- +// SessionSynAckMsg type SessionSynAckMsg struct { MsgSize uint16 `order:"big"` // total size of message MsgType uint16 `order:"big"` // TRANSPORT_SESSION_SYN_ACK (376) @@ -326,6 +358,7 @@ type SessionSynAckMsg struct { Timestamp util.AbsoluteTime // usec epoch } +// NewSessionSynAckMsg is an ACK for a SYN request func NewSessionSynAckMsg() *SessionSynAckMsg { return &SessionSynAckMsg{ MsgSize: 16, @@ -335,6 +368,7 @@ func NewSessionSynAckMsg() *SessionSynAckMsg { } } +// String returns a human-readable representation of the message. func (m *SessionSynAckMsg) String() string { return fmt.Sprintf("SessionSynAck{timestamp=%s}", m.Timestamp) } @@ -348,12 +382,14 @@ func (msg *SessionSynAckMsg) Header() *MessageHeader { // TRANSPORT_SESSION_QUOTA //---------------------------------------------------------------------- +// SessionQuotaMsg type SessionQuotaMsg struct { MsgSize uint16 `order:"big"` // total size of message MsgType uint16 `order:"big"` // TRANSPORT_SESSION_QUOTA (379) Quota uint32 `order:"big"` // Quota in bytes per second } +// NewSessionQuotaMsg announces a session quota to the other end of the session. func NewSessionQuotaMsg(quota uint32) *SessionQuotaMsg { m := new(SessionQuotaMsg) if quota > 0 { @@ -364,6 +400,7 @@ func NewSessionQuotaMsg(quota uint32) *SessionQuotaMsg { return m } +// String returns a human-readable representation of the message. func (m *SessionQuotaMsg) String() string { return fmt.Sprintf("SessionQuotaMsg{%sB/s}", util.Scale1024(uint64(m.Quota))) } @@ -374,57 +411,63 @@ func (msg *SessionQuotaMsg) Header() *MessageHeader { } //---------------------------------------------------------------------- -// TRANSPORT_SESSION_KEEPALIVE_RESPONSE +// TRANSPORT_SESSION_KEEPALIVE //---------------------------------------------------------------------- -type SessionKeepAliveRespMsg struct { +// SessionKeepAliveMsg +type SessionKeepAliveMsg struct { MsgSize uint16 `order:"big"` // total size of message - MsgType uint16 `order:"big"` // TRANSPORT_SESSION_KEEPALIVE_RESPONSE (382) + MsgType uint16 `order:"big"` // TRANSPORT_SESSION_KEEPALIVE (381) Nonce uint32 } -func NewSessionKeepAliveRespMsg(nonce uint32) *SessionKeepAliveRespMsg { - m := &SessionKeepAliveRespMsg{ +// NewSessionKeepAliveMsg creates a new request to keep a session. +func NewSessionKeepAliveMsg() *SessionKeepAliveMsg { + m := &SessionKeepAliveMsg{ MsgSize: 8, - MsgType: TRANSPORT_SESSION_KEEPALIVE_RESPONSE, - Nonce: nonce, + MsgType: TRANSPORT_SESSION_KEEPALIVE, + Nonce: util.RndUInt32(), } return m } -func (m *SessionKeepAliveRespMsg) String() string { - return fmt.Sprintf("SessionKeepAliveRespMsg{%d}", m.Nonce) +// String returns a human-readable representation of the message. +func (m *SessionKeepAliveMsg) String() string { + return fmt.Sprintf("SessionKeepAliveMsg{%d}", m.Nonce) } // Header returns the message header in a separate instance. -func (msg *SessionKeepAliveRespMsg) Header() *MessageHeader { +func (msg *SessionKeepAliveMsg) Header() *MessageHeader { return &MessageHeader{msg.MsgSize, msg.MsgType} } //---------------------------------------------------------------------- -// TRANSPORT_SESSION_KEEPALIVE +// TRANSPORT_SESSION_KEEPALIVE_RESPONSE //---------------------------------------------------------------------- -type SessionKeepAliveMsg struct { +// SessionKeepAliveRespMsg +type SessionKeepAliveRespMsg struct { MsgSize uint16 `order:"big"` // total size of message - MsgType uint16 `order:"big"` // TRANSPORT_SESSION_KEEPALIVE (381) + MsgType uint16 `order:"big"` // TRANSPORT_SESSION_KEEPALIVE_RESPONSE (382) Nonce uint32 } -func NewSessionKeepAliveMsg() *SessionKeepAliveMsg { - m := &SessionKeepAliveMsg{ +// NewSessionKeepAliveRespMsg is a response message for a "keep session" request. +func NewSessionKeepAliveRespMsg(nonce uint32) *SessionKeepAliveRespMsg { + m := &SessionKeepAliveRespMsg{ MsgSize: 8, - MsgType: TRANSPORT_SESSION_KEEPALIVE, - Nonce: util.RndUInt32(), + MsgType: TRANSPORT_SESSION_KEEPALIVE_RESPONSE, + Nonce: nonce, } return m } -func (m *SessionKeepAliveMsg) String() string { - return fmt.Sprintf("SessionKeepAliveMsg{%d}", m.Nonce) +// String returns a human-readable representation of the message. +func (m *SessionKeepAliveRespMsg) String() string { + return fmt.Sprintf("SessionKeepAliveRespMsg{%d}", m.Nonce) } // Header returns the message header in a separate instance. -func (msg *SessionKeepAliveMsg) Header() *MessageHeader { +func (msg *SessionKeepAliveRespMsg) Header() *MessageHeader { return &MessageHeader{msg.MsgSize, msg.MsgType} } diff --git a/src/gnunet/service/client.go b/src/gnunet/service/client.go index 2d1c3dd..3bae7da 100644 --- a/src/gnunet/service/client.go +++ b/src/gnunet/service/client.go @@ -1,9 +1,10 @@ package service import ( - "github.com/bfix/gospel/logger" "gnunet/message" "gnunet/transport" + + "github.com/bfix/gospel/logger" ) // Client diff --git a/src/gnunet/service/gns/block.go b/src/gnunet/service/gns/block.go new file mode 100644 index 0000000..9d83f99 --- /dev/null +++ b/src/gnunet/service/gns/block.go @@ -0,0 +1,185 @@ +package gns + +import ( + "fmt" + + "gnunet/crypto" + "gnunet/enums" + "gnunet/message" + "gnunet/util" + + "github.com/bfix/gospel/crypto/ed25519" + "github.com/bfix/gospel/data" +) + +var ( + ErrBlockNotDecrypted = fmt.Errorf("GNS block not decrypted") +) + +//====================================================================== +// GNS block: An encrypted and signed container for GNS resource records +// that represents the "atomic" data structure associated with a GNS +// label in a given zone. +//====================================================================== + +// SignedBlockData: signed and encrypted list of resource records stored +// in a GNSRecordSet +type SignedBlockData struct { + Purpose *crypto.SignaturePurpose // Size and purpose of signature (8 bytes) + Expire util.AbsoluteTime // Expiration time of the block. + EncData []byte `size:"*"` // encrypted GNSRecordSet + + // transient data (not serialized) + data []byte // unencrypted GNSRecord set +} + +// GNSBlock is the result of GNS lookups for a given label in a zone. +type GNSBlock struct { + Signature []byte `size:"64"` // Signature of the block. + DerivedKey []byte `size:"32"` // Derived key used for signing + Block *SignedBlockData + + // transient data (not serialized) + checked bool // block integrity checked + verified bool // block signature verified (internal) + decrypted bool // block data decrypted (internal) +} + +// String returns the human-readable representation of a GNSBlock +func (b *GNSBlock) String() string { + return fmt.Sprintf("GNSBlock{Verified=%v,Decrypted=%v,data=[%d]}", + b.verified, b.decrypted, len(b.Block.EncData)) +} + +// Records returns the list of resource records in a block. +func (b *GNSBlock) Records() ([]*message.GNSResourceRecord, error) { + // check if block is decrypted + if !b.decrypted { + return nil, ErrBlockNotDecrypted + } + // parse block data into record set + rs := NewGNSRecordSet() + if err := data.Unmarshal(rs, b.Block.data); err != nil { + return nil, err + } + return rs.Records, nil +} + +// Verify the integrity of the block data from a signature. +func (b *GNSBlock) Verify(zoneKey *ed25519.PublicKey, label string) (err error) { + // Integrity check performed + b.checked = true + + // verify derived key + dkey := ed25519.NewPublicKeyFromBytes(b.DerivedKey) + dkey2 := crypto.DerivePublicKey(zoneKey, label, "gns") + if !dkey.Q.Equals(dkey2.Q) { + return fmt.Errorf("Invalid signature key for GNS Block") + } + // verify signature + var ( + sig *ed25519.EcSignature + buf []byte + ok bool + ) + if sig, err = ed25519.NewEcSignatureFromBytes(b.Signature); err != nil { + return + } + if buf, err = data.Marshal(b.Block); err != nil { + return + } + if ok, err = dkey.EcVerify(buf, sig); err == nil && !ok { + err = fmt.Errorf("Signature verification failed for GNS block") + } + b.verified = true + return +} + +// Decrypt block data with a key/iv combination derived from (PKEY,label) +func (b *GNSBlock) Decrypt(zoneKey *ed25519.PublicKey, label string) (err error) { + // decrypt payload + b.Block.data, err = crypto.DecryptBlock(b.Block.EncData, zoneKey, label) + b.decrypted = true + return +} + +// NewGNSBlock instantiates an empty GNS block +func NewGNSBlock() *GNSBlock { + return &GNSBlock{ + Signature: make([]byte, 64), + DerivedKey: make([]byte, 32), + Block: &SignedBlockData{ + Purpose: new(crypto.SignaturePurpose), + Expire: *new(util.AbsoluteTime), + EncData: nil, + data: nil, + }, + checked: false, + verified: false, + decrypted: false, + } +} + +//---------------------------------------------------------------------- +// GNSRecordSet +//---------------------------------------------------------------------- + +// GNSRecordSet ist the GNUnet data structure for a list of resource records +// in a GNSBlock. As part of GNUnet messages, the record set is padded so that +// the binary size of (records||padding) is the smallest power of two. +type GNSRecordSet struct { + Count uint32 `order:"big"` // number of resource records + Records []*message.GNSResourceRecord `size:"Count"` // list of resource records + Padding []byte `size:"*"` // padding +} + +// NewGNSRecordSet returns an empty resource record set. +func NewGNSRecordSet() *GNSRecordSet { + return &GNSRecordSet{ + Count: 0, + Records: make([]*message.GNSResourceRecord, 0), + Padding: make([]byte, 0), + } +} + +// AddRecord to append a resource record to the set. +func (rs *GNSRecordSet) AddRecord(rec *message.GNSResourceRecord) { + rs.Count++ + rs.Records = append(rs.Records, rec) +} + +//====================================================================== +// List of resource records types (for GNS/DNS queries) +//====================================================================== + +// RRTypeList is a list of integers representing RR types. +type RRTypeList []int + +// Initialize a new type list with given type values +func NewRRTypeList(args ...int) (res RRTypeList) { + for _, val := range args { + // if GNS_TYPE_ANY is encountered, it becomes the sole type + if val == enums.GNS_TYPE_ANY { + res = make(RRTypeList, 1) + res[0] = val + return + } + res = append(res, val) + } + return +} + +// HasType returns true if the type is included in the list +func (tl RRTypeList) HasType(t int) bool { + // return true if type is GNS_TYPE_ANY + if tl[0] == enums.GNS_TYPE_ANY { + return true + } + // check for type in list + for _, val := range tl { + if val == t { + return true + } + } + return false +} diff --git a/src/gnunet/service/gns/block_handler.go b/src/gnunet/service/gns/block_handler.go new file mode 100644 index 0000000..860b4a7 --- /dev/null +++ b/src/gnunet/service/gns/block_handler.go @@ -0,0 +1,398 @@ +package gns + +import ( + "encoding/hex" + "fmt" + + "gnunet/enums" + "gnunet/message" + + "github.com/bfix/gospel/crypto/ed25519" + "github.com/bfix/gospel/logger" +) + +// HdlrInst is the type for functions that instanciate custom block handlers. +type HdlrInst func(*message.GNSResourceRecord, []string) (BlockHandler, error) + +// Error codes +var ( + ErrInvalidRecordMix = fmt.Errorf("Invalid mix of RR types in block") + ErrBlockHandler = fmt.Errorf("Internal block handler failure") +) + +// Mapping of RR types to BlockHandler instanciation functions +var ( + customHandler = map[int]HdlrInst{ + enums.GNS_TYPE_PKEY: NewPkeyHandler, + enums.GNS_TYPE_GNS2DNS: NewGns2DnsHandler, + enums.GNS_TYPE_BOX: NewBoxHandler, + enums.GNS_TYPE_LEHO: NewLehoHandler, + } +) + +//====================================================================== +// GNS blocks that contain special records (PKEY, GNS2DNS, BOX, LEHO...) +// require special treatment with respect to other resource records with +// different types in the same block. Usually only certain other types +// (or none at all) are allowed. +//====================================================================== + +// BlockHandler interface. +type BlockHandler interface { + // AddRecord inserts a RR into the BlockHandler for (later) processing. + // The handler can inspect the remaining labels in a path if required. + // It returns an error if a record is not accepted by the block handler. + AddRecord(rr *message.GNSResourceRecord, labels []string) error + + // TypeAction returns a flag indicating how a resource record of a + // given type is to be treated by a custom block handler: + // = -1: Record is not allowed + // = 0: Record is allowed but will be ignored + // = 1: Record is allowed and will be processed + TypeAction(t int) int + + // Records returns a list of RR of the given types associated with + // the custom handler + Records(kind RRTypeList) *GNSRecordSet +} + +//---------------------------------------------------------------------- +// Manage list of block handlers +// Under normal circumstances there is only one (or none) block handler +// per block, but future constructs may allow multiple block handlers +// to be present. The block handler list implements the BlockHandler +// interface. +// The BlockHandlerList maintains a map of actually instantiated handlers +// (indexed by record type) and a list of record types (with occurrence +// count) in the block. +//---------------------------------------------------------------------- + +// BlockHandlerList is a list of block handlers instantiated. +type BlockHandlerList struct { + list map[int]BlockHandler // list of handler instances +} + +// NewBlockHandlerList instantiates an a list of active block handlers +// for a given set of records (GNS block). +func NewBlockHandlerList(records []*message.GNSResourceRecord, labels []string) (*BlockHandlerList, error) { + // initialize block handler list + hl := &BlockHandlerList{ + list: make(map[int]BlockHandler), + } + // build a list of record types that are handled by a custom handler. + rrList := NewRRTypeList( + enums.GNS_TYPE_PKEY, + enums.GNS_TYPE_GNS2DNS, + enums.GNS_TYPE_BOX, + enums.GNS_TYPE_LEHO) + + // Traverse record list and build list of handler instances + for _, rec := range records { + // check for custom handler type + rrType := int(rec.Type) + if rrList.HasType(rrType) { + // check if a handler for given type already exists + var ( + hdlr BlockHandler + ok bool + err error + ) + if hdlr, ok = hl.list[rrType]; ok { + // add record to existing handler + if err = hdlr.AddRecord(rec, labels); err != nil { + return nil, err + } + continue + } + // create a new handler instance + switch rrType { + case enums.GNS_TYPE_PKEY: + hdlr, err = NewPkeyHandler(rec, labels) + case enums.GNS_TYPE_GNS2DNS: + hdlr, err = NewGns2DnsHandler(rec, labels) + case enums.GNS_TYPE_BOX: + hdlr, err = NewBoxHandler(rec, labels) + case enums.GNS_TYPE_LEHO: + hdlr, err = NewLehoHandler(rec, labels) + } + if err != nil { + return nil, err + } + // store handler in list + hl.list[rrType] = hdlr + } + } + return hl, nil +} + +// GetHandler returns a BlockHandler for the given key. If no block handler exists +// under the given name, a new one is created and stored in the list. The type of +// the new block handler is derived from the key value. +func (hl *BlockHandlerList) GetHandler(t int) BlockHandler { + // return handler for given key if it exists + if hdlr, ok := hl.list[t]; ok { + return hdlr + } + return nil +} + +//---------------------------------------------------------------------- +// PKEY handler: Only one PKEY as sole record in a block +//---------------------------------------------------------------------- + +// PkeyHandler implementing the BlockHandler interface +type PkeyHandler struct { + pkey *ed25519.PublicKey // Zone key + rec *message.GNSResourceRecord // associated recource record +} + +// NewPkeyHandler returns a new BlockHandler instance +func NewPkeyHandler(rec *message.GNSResourceRecord, labels []string) (BlockHandler, error) { + if int(rec.Type) != enums.GNS_TYPE_PKEY { + return nil, ErrInvalidRecordType + } + h := &PkeyHandler{ + pkey: nil, + } + if err := h.AddRecord(rec, labels); err != nil { + return nil, err + } + return h, nil +} + +// AddRecord inserts a PKEY record into the handler. +func (h *PkeyHandler) AddRecord(rec *message.GNSResourceRecord, labels []string) error { + if int(rec.Type) != enums.GNS_TYPE_PKEY { + return ErrInvalidRecordType + } + // check for sole PKEY record in block + if h.pkey != nil { + return ErrInvalidPKEY + } + // check for sane key data + if len(rec.Data) != 32 { + return ErrInvalidPKEY + } + // set a PKEY handler + h.pkey = ed25519.NewPublicKeyFromBytes(rec.Data) + h.rec = rec + return nil +} + +// TypeAction return a flag indicating how a resource record of a given type +// is to be treated (see BlockHandler interface) +func (h *PkeyHandler) TypeAction(t int) int { + // no other resource record type is not allowed + if t == enums.GNS_TYPE_PKEY { + return 1 + } + return -1 +} + +// Records returns a list of RR of the given type associated with this handler +func (h *PkeyHandler) Records(kind RRTypeList) *GNSRecordSet { + rs := NewGNSRecordSet() + if kind.HasType(enums.GNS_TYPE_PKEY) { + rs.AddRecord(h.rec) + } + return rs +} + +//---------------------------------------------------------------------- +// GNS2DNS handler +//---------------------------------------------------------------------- + +// Gns2DnsHandler implementing the BlockHandler interface +type Gns2DnsHandler struct { + Name string // DNS query name + Servers []string // DNS servers to ask + recs []*message.GNSResourceRecord // list of rersource records +} + +// NewGns2DnsHandler returns a new BlockHandler instance +func NewGns2DnsHandler(rec *message.GNSResourceRecord, labels []string) (BlockHandler, error) { + if int(rec.Type) != enums.GNS_TYPE_GNS2DNS { + return nil, ErrInvalidRecordType + } + h := &Gns2DnsHandler{ + Name: "", + Servers: make([]string, 0), + recs: make([]*message.GNSResourceRecord, 0), + } + if err := h.AddRecord(rec, labels); err != nil { + return nil, err + } + return h, nil +} + +// AddRecord inserts a GNS2DNS record into the handler. +func (h *Gns2DnsHandler) AddRecord(rec *message.GNSResourceRecord, labels []string) error { + if int(rec.Type) != enums.GNS_TYPE_GNS2DNS { + return ErrInvalidRecordType + } + logger.Printf(logger.DBG, "[gns] GNS2DNS data: %s\n", hex.EncodeToString(rec.Data)) + + // extract list of names in DATA block: + next, dnsQuery := DNSNameFromBytes(rec.Data, 0) + dnsServer := string(rec.Data[next : len(rec.Data)-1]) + logger.Printf(logger.DBG, "[gns] GNS2DNS query '%s'@'%s'\n", dnsQuery, dnsServer) + if len(dnsServer) == 0 || len(dnsQuery) == 0 { + return ErrInvalidRecordBody + } + + // check if all GNS2DNS records refer to the same query name + if len(h.Servers) == 0 { + h.Name = dnsQuery + } + if dnsQuery != h.Name { + return ErrInvalidRecordBody + } + h.Servers = append(h.Servers, dnsServer) + h.recs = append(h.recs, rec) + return nil +} + +// TypeAction return a flag indicating how a resource record of a given type +// is to be treated (see BlockHandler interface) +func (h *Gns2DnsHandler) TypeAction(t int) int { + // anything goes... + return 1 +} + +// Records returns a list of RR of the given type associated with this handler +func (h *Gns2DnsHandler) Records(kind RRTypeList) *GNSRecordSet { + rs := NewGNSRecordSet() + if kind.HasType(enums.GNS_TYPE_GNS2DNS) { + for _, rec := range h.recs { + rs.AddRecord(rec) + } + } + return rs +} + +//---------------------------------------------------------------------- +// BOX handler +//---------------------------------------------------------------------- + +// BoxHandler implementing the BlockHandler interface +type BoxHandler struct { + boxes map[string]*Box // map of found boxes +} + +// NewBoxHandler returns a new BlockHandler instance +func NewBoxHandler(rec *message.GNSResourceRecord, labels []string) (BlockHandler, error) { + if int(rec.Type) != enums.GNS_TYPE_BOX { + return nil, ErrInvalidRecordType + } + h := &BoxHandler{ + boxes: make(map[string]*Box), + } + if err := h.AddRecord(rec, labels); err != nil { + return nil, err + } + return h, nil +} + +// AddRecord inserts a BOX record into the handler. +func (h *BoxHandler) AddRecord(rec *message.GNSResourceRecord, labels []string) error { + if int(rec.Type) != enums.GNS_TYPE_BOX { + return ErrInvalidRecordType + } + logger.Printf(logger.DBG, "[box-rr] for labels %v\n", labels) + // check if we need to process the BOX record: + // (1) only two remaining labels + if len(labels) != 2 { + return nil + } + // (2) remaining labels must start with '_' + if labels[0][0] != '_' || labels[1][0] != '_' { + return nil + } + // (3) check of "svc" and "proto" match values in the BOX + box := NewBox(rec) + if box.Matches(labels) { + logger.Println(logger.DBG, "[box-rr] MATCH -- adding record") + h.boxes[box.key] = box + } + return nil +} + +// TypeAction return a flag indicating how a resource record of a given type +// is to be treated (see BlockHandler interface) +func (h *BoxHandler) TypeAction(t int) int { + // anything goes... + return 1 +} + +// Records returns a list of RR of the given type associated with this handler +func (h *BoxHandler) Records(kind RRTypeList) *GNSRecordSet { + rs := NewGNSRecordSet() + for _, box := range h.boxes { + if kind.HasType(int(box.Type)) { + // valid box found: assemble new resource record. + rr := new(message.GNSResourceRecord) + rr.Expires = box.rec.Expires + rr.Flags = box.rec.Flags + rr.Type = box.Type + rr.Size = uint32(len(box.RR)) + rr.Data = box.RR + rs.AddRecord(rr) + } + } + return rs +} + +//---------------------------------------------------------------------- +// LEHO handler +//---------------------------------------------------------------------- + +// LehoHandler implementing the BlockHandler interface +type LehoHandler struct { + name string + rec *message.GNSResourceRecord +} + +// NewLehoHandler returns a new BlockHandler instance +func NewLehoHandler(rec *message.GNSResourceRecord, labels []string) (BlockHandler, error) { + if int(rec.Type) != enums.GNS_TYPE_LEHO { + return nil, ErrInvalidRecordType + } + h := &LehoHandler{ + name: "", + } + if err := h.AddRecord(rec, labels); err != nil { + return nil, err + } + return h, nil +} + +// AddRecord inserts a LEHO record into the handler. +func (h *LehoHandler) AddRecord(rec *message.GNSResourceRecord, labels []string) error { + if int(rec.Type) != enums.GNS_TYPE_LEHO { + return ErrInvalidRecordType + } + h.name = string(rec.Data) + h.rec = rec + return nil +} + +// TypeAction return a flag indicating how a resource record of a given type +// is to be treated (see BlockHandler interface) +func (h *LehoHandler) TypeAction(t int) int { + // only A and AAAA records allowed beside LEHO + switch t { + case enums.GNS_TYPE_LEHO, enums.GNS_TYPE_DNS_A, enums.GNS_TYPE_DNS_AAAA: + return 1 + default: + return -1 + } +} + +// Records returns a list of RR of the given type associated with this handler +func (h *LehoHandler) Records(kind RRTypeList) *GNSRecordSet { + rs := NewGNSRecordSet() + if kind.HasType(enums.GNS_TYPE_LEHO) { + rs.AddRecord(h.rec) + } + return rs +} diff --git a/src/gnunet/service/gns/box.go b/src/gnunet/service/gns/box.go new file mode 100644 index 0000000..e2d3609 --- /dev/null +++ b/src/gnunet/service/gns/box.go @@ -0,0 +1,151 @@ +package gns + +import ( + "encoding/hex" + "strconv" + "strings" + + "gnunet/message" + + "github.com/bfix/gospel/data" + "github.com/bfix/gospel/logger" +) + +// Box is an encapsulated RR for special names +type Box struct { + Proto uint16 `order:"big"` // Protcol identifier + Svc uint16 `order:"big"` // Service identifier + Type uint32 `order:"big"` // Type of embedded RR + RR []byte `size:"*"` // embedded RR + + // transient attributes (not serialized) + key string // map key for box instance + rec *message.GNSResourceRecord // originating RR +} + +// NewBox creates a new box instance from a BOX resource record. +func NewBox(rec *message.GNSResourceRecord) *Box { + b := new(Box) + if err := data.Unmarshal(b, rec.Data); err != nil { + logger.Printf(logger.ERROR, "[gns] Can't unmarshal BOX") + return nil + } + b.key = hex.EncodeToString(rec.Data[:8]) + b.rec = rec + return b +} + +// Matches verifies that the remaining labels comply with the values +// in the BOX record. +func (b *Box) Matches(labels []string) bool { + // resolve protocol and service names + proto, protoName := GetProtocol(labels[0]) + svc, _ := GetService(labels[1], protoName) + // no match on invalid resolution + if proto == 0 || svc == 0 { + return false + } + // check for matching values in box + return proto == b.Proto && svc == b.Svc +} + +//---------------------------------------------------------------------- +// helper functions + +// list of handled protocols in BOX records +var protocols = map[string]int{ + "icmp": 1, + "igmp": 2, + "tcp": 6, + "udp": 17, + "ipv6-icmp": 58, +} + +// GetProtocol returns the protocol number and name for a given name. The +// name can be an integer value (e.g. "_6" for "tcp") or a mnemonic name +// (e.g. like "_tcp"). +func GetProtocol(name string) (uint16, string) { + // check for required prefix + if name[0] != '_' { + return 0, "" + } + name = strings.ToLower(name[1:]) + + // if label is an integer value it is the protocol number + if val, err := strconv.Atoi(name); err == nil { + // check for valid number (reverse protocol lookup) + for label, id := range protocols { + if id == val { + // return found entry + return uint16(val), label + } + } + // number out of range + return 0, "" + } + // try to resolve via protocol map + if id, ok := protocols[name]; ok { + return uint16(id), name + } + // resolution failed + return 0, "" +} + +// list of services (per protocol) handled in BOX records +var services = map[string]map[string]int{ + "udp": { + "domain": 53, + }, + "tcp": { + "ftp": 21, + "ftps": 990, + "gopher": 70, + "http": 80, + "https": 443, + "imap2": 143, + "imap3": 220, + "imaps": 993, + "pop3": 110, + "pop3s": 995, + "smtp": 25, + "ssh": 22, + "telnet": 23, + }, +} + +// GetService returns the port number and the name of a service (with given +// protocol). The name can be an integer value (e.g. "_443" for "https") or +// a mnemonic name (e.g. like "_https"). +func GetService(name, proto string) (uint16, string) { + // check for required prefix + if name[0] != '_' { + return 0, "" + } + name = strings.ToLower(name[1:]) + + // get list of services for given protocol + svcs, ok := services[proto] + if !ok { + // no services available for this protocol + return 0, "" + } + + // if label is an integer value it is the port number + if val, err := strconv.Atoi(name); err == nil { + // check for valid number (reverse service lookup) + for label, id := range svcs { + if id == val { + // return found entry + return uint16(val), label + } + } + // number out of range + return 0, "" + } + // try to resolve via services map + if id, ok := svcs[name]; ok { + return uint16(id), name + } + // resolution failed + return 0, "" +} diff --git a/src/gnunet/service/gns/dns.go b/src/gnunet/service/gns/dns.go index 2ebe331..fe138b0 100644 --- a/src/gnunet/service/gns/dns.go +++ b/src/gnunet/service/gns/dns.go @@ -47,7 +47,7 @@ func DNSNameFromBytes(b []byte, offset int) (int, string) { // queryDNS resolves a name on a given nameserver and delivers all matching // resource record (of type 'kind') to the result channel. -func queryDNS(id int, name string, server net.IP, kind int, res chan *GNSRecordSet) { +func queryDNS(id int, name string, server net.IP, kind RRTypeList, res chan *GNSRecordSet) { logger.Printf(logger.DBG, "[dns][%d] Starting query for '%s' on '%s'...\n", id, name, server.String()) // assemble query @@ -91,27 +91,32 @@ func queryDNS(id int, name string, server net.IP, kind int, res chan *GNSRecordS } set := NewGNSRecordSet() for _, record := range in.Answer { - // create a new GNS resource record - rr := new(message.GNSResourceRecord) - rr.Expires = util.AbsoluteTimeNever() - rr.Flags = 0 - rr.Type = uint32(record.Header().Rrtype) - rr.Size = uint32(record.Header().Rdlength) - rr.Data = make([]byte, rr.Size) + // check if answer record is of requested type + if kind.HasType(int(record.Header().Rrtype)) { + // get wire-format of resource record + buf := make([]byte, 2048) + n, err := dns.PackRR(record, buf, 0, nil, false) + if err != nil { + logger.Printf(logger.WARN, "[dns][%d] Failed to get RR data for %s\n", id, err.Error()) + continue + } - // get wire-format of resource record - buf := make([]byte, 2048) - n, err := dns.PackRR(record, buf, 0, nil, false) - if err != nil { - logger.Printf(logger.WARN, "[dns][%d] Failed to get RR data for %s\n", id, err.Error()) - continue - } - if n < int(rr.Size) { - logger.Printf(logger.WARN, "[dns][%d] Nit enough data in RR (%d != %d)\n", id, n, rr.Size) - continue + // create a new GNS resource record + rr := new(message.GNSResourceRecord) + expires := time.Now().Add(time.Duration(record.Header().Ttl) * time.Second) + rr.Expires = util.NewAbsoluteTime(expires) + rr.Flags = 0 + rr.Type = uint32(record.Header().Rrtype) + rr.Size = uint32(record.Header().Rdlength) + rr.Data = make([]byte, rr.Size) + + if n < int(rr.Size) { + logger.Printf(logger.WARN, "[dns][%d] Not enough data in RR (%d != %d)\n", id, n, rr.Size) + continue + } + copy(rr.Data, buf[n-int(rr.Size):]) + set.AddRecord(rr) } - copy(rr.Data, buf[n-int(rr.Size):]) - set.AddRecord(rr) } logger.Printf(logger.WARN, "[dns][%d] %d resource records extracted from response (%d/5).\n", id, set.Count, retry+1) res <- set @@ -124,7 +129,7 @@ func queryDNS(id int, name string, server net.IP, kind int, res chan *GNSRecordS // ResolveDNS resolves a name in DNS. Multiple DNS servers are queried in // parallel; the first result delivered by any of the servers is returned // as the result list of matching resource records. -func (gns *GNSModule) ResolveDNS(name string, servers []string, kind int, pkey *ed25519.PublicKey) (set *GNSRecordSet, err error) { +func (gns *GNSModule) ResolveDNS(name string, servers []string, kind RRTypeList, pkey *ed25519.PublicKey) (set *GNSRecordSet, err error) { logger.Printf(logger.DBG, "[dns] Resolution of '%s' starting...\n", name) // start DNS queries concurrently @@ -133,20 +138,22 @@ func (gns *GNSModule) ResolveDNS(name string, servers []string, kind int, pkey * for idx, srv := range servers { // check if srv is an IPv4/IPv6 address addr := net.ParseIP(srv) + logger.Printf(logger.DBG, "ParseIP('%s', len=%d) --> %v\n", srv, len(srv), addr) if addr == nil { + query := NewRRTypeList(enums.GNS_TYPE_DNS_A, enums.GNS_TYPE_DNS_AAAA) // no; resolve server name in GNS if strings.HasSuffix(srv, ".+") { // resolve server name relative to current zone zone := util.EncodeBinaryToString(pkey.Bytes()) srv = strings.TrimSuffix(srv, ".+") - set, err = gns.Resolve(srv, pkey, enums.GNS_TYPE_ANY, enums.GNS_LO_DEFAULT) + set, err = gns.Resolve(srv, pkey, query, enums.GNS_LO_DEFAULT) if err != nil { logger.Printf(logger.ERROR, "[dns] Can't resolve NS server '%s' in '%s'\n", srv, zone) continue } } else { - // resolve absolute GNS name (MUST end in a PKEY) - set, err = gns.Resolve(srv, nil, enums.GNS_TYPE_ANY, enums.GNS_LO_DEFAULT) + // resolve absolute GNS name (name MUST end in a PKEY) + set, err = gns.Resolve(srv, nil, query, enums.GNS_LO_DEFAULT) if err != nil { logger.Printf(logger.ERROR, "[dns] Can't resolve NS server '%s'\n", srv) continue @@ -158,11 +165,17 @@ func (gns *GNSModule) ResolveDNS(name string, servers []string, kind int, pkey * switch int(rec.Type) { case enums.GNS_TYPE_DNS_AAAA: addr = net.IP(rec.Data) + // we prefer IPv6 break rec_loop case enums.GNS_TYPE_DNS_A: addr = net.IP(rec.Data) } } + // check if we have an IP address available + if addr == nil { + logger.Printf(logger.WARN, "[dns] No IP address for nameserver in GNS") + continue + } } // query DNS concurrently go queryDNS(idx, name, addr, kind, res) diff --git a/src/gnunet/service/gns/module.go b/src/gnunet/service/gns/module.go index e4dc5fb..3e7bd18 100644 --- a/src/gnunet/service/gns/module.go +++ b/src/gnunet/service/gns/module.go @@ -1,7 +1,6 @@ package gns import ( - "encoding/hex" "fmt" "strings" @@ -54,83 +53,40 @@ func NewQuery(pkey *ed25519.PublicKey, label string) *Query { } } -//---------------------------------------------------------------------- -// GNS blocks with special types (PKEY, GNS2DNS) require special -// treatment with respect to other resource records with different types -// in the same block. Usually only certain other types (or not at all) -// are allowed and the allowed ones are required to deliver a consistent -// list of resulting resource records passed back to the caller. -//---------------------------------------------------------------------- - -// BlockHandler interface. -type BlockHandler interface { - // TypeAction returns a flag indicating how a resource record of a - // given type is to be treated: - // = -1: Record is not allowed (terminates lookup with an error) - // = 0: Record is allowed but will be ignored - // = 1: Record is allowed and will be processed - TypeAction(int) int -} - -// Gns2DnsHandler implementing the BlockHandler interface -type Gns2DnsHandler struct { - Name string - Servers []string -} - -// NewGns2DnsHandler returns a new BlockHandler instance -func NewGns2DnsHandler() *Gns2DnsHandler { - return &Gns2DnsHandler{ - Name: "", - Servers: make([]string, 0), - } -} - -// TypeAction return a flag indicating how a resource record of a given type -// is to be treated (see RecordMaster interface) -func (m *Gns2DnsHandler) TypeAction(t int) int { - // only process other GNS2DNS records - if t == enums.GNS_TYPE_GNS2DNS { - return 1 - } - // skip everything else - return 0 -} - -// AddRequest adds the DNS request for "name" at "server" to the list -// of requests. All GNS2DNS records must query for the same name -func (m *Gns2DnsHandler) AddRequest(name, server string) bool { - if len(m.Servers) == 0 { - m.Name = name - } - if name != m.Name { - return false - } - m.Servers = append(m.Servers, server) - return true -} - //---------------------------------------------------------------------- // The GNS module (recursively) resolves GNS names: -// Resolves DNS-like names (e.g. "minecraft.servers.bob.games") to the -// requested resource records (RRs). In short, the resolution process -// works as follows: +// Resolves DNS-like names (e.g. "minecraft.servers.bob.games"; a name is +// a list of labels with '.' as separator) to the requested resource +// records (RRs). In short, the resolution process works as follows: // // Resolve(name): // -------------- -// (1) split the full name into elements in reverse order: names[] -// (2) Resolve first element (root zone, right-most name part, name[0]) to +// (1) split the name ('.' as separator) into labels in reverse order: labels[] +// (2) Resolve first label (= root zone, right-most name part, labels[0]) to // a zone public key PKEY: -// (a) the name is a string representation of a public key -> (3) -// (b) the zone key for the name is stored in the config file -> (3) -// (c) a local zone with that given name -> (3) +// (a) the label is a string representation of a public key -> (3) +// (b) the zone key for the label is stored in the config file -> (3) +// (c) a local zone with that given label -> (3) // (d) ERROR: "Unknown root zone" -// (3) names = names[1:] // remove first element -// block = Lookup (PKEY, names[0]): -// (a) If last element of namess: -> (4) -// (b) block is PKEY record: -// PKEY <- block, --> (3) -// (4) return block: it is the responsibility of the caller to assemble +// (3) labels = labels[1:] +// records = Resolve (labels[0], PKEY) +// If last label in name: -> (5) +// (4) for all rec in records: +// (a) if rec is a PKEY record: +// PKEY <- record, --> (3) +// (b) if rec is a GNS2DNS record: +// delegate to DNS to resolve rest of name -> (5) +// (c) if rec is BOX record: +// if rest of name is pattern "_service._proto" and matches +// the values in the BOX: +// Replace records with resource record from BOX -> (5) +// (d) if rec is CNAME record: +// if no remaining labels: +// if requested types include CNAME -> (5) +// if +// resolution failed: name not completely processed and no zone available +// +// (5) return records: it is the responsibility of the caller to assemble // the desired result from block data (e.g. filter for requested // resource record types). //---------------------------------------------------------------------- @@ -145,10 +101,10 @@ type GNSModule struct { GetLocalZone func(name string) (*ed25519.PublicKey, error) } -// Resolve a GNS name with multiple elements, If pkey is not nil, the name +// Resolve a GNS name with multiple labels. If pkey is not nil, the name // is interpreted as "relative to current zone". -func (gns *GNSModule) Resolve(path string, pkey *ed25519.PublicKey, kind int, mode int) (set *GNSRecordSet, err error) { - // get the name elements in reverse order +func (gns *GNSModule) Resolve(path string, pkey *ed25519.PublicKey, kind RRTypeList, mode int) (set *GNSRecordSet, err error) { + // get the labels in reverse order names := util.ReverseStringList(strings.Split(path, ".")) logger.Printf(logger.DBG, "[gns] Resolver called for %v\n", names) @@ -161,8 +117,8 @@ func (gns *GNSModule) Resolve(path string, pkey *ed25519.PublicKey, kind int, mo return gns.ResolveAbsolute(names, kind, mode) } -// Resolve a fully qualified GNS absolute name (with multiple levels). -func (gns *GNSModule) ResolveAbsolute(names []string, kind int, mode int) (set *GNSRecordSet, err error) { +// Resolve a fully qualified GNS absolute name (with multiple labels). +func (gns *GNSModule) ResolveAbsolute(labels []string, kind RRTypeList, mode int) (set *GNSRecordSet, err error) { // get the root zone key for the TLD var ( pkey *ed25519.PublicKey @@ -170,40 +126,43 @@ func (gns *GNSModule) ResolveAbsolute(names []string, kind int, mode int) (set * ) for { // (1) check if TLD is a public key string - if len(names[0]) == 52 { - if data, err = util.DecodeStringToBinary(names[0], 32); err == nil { + if len(labels[0]) == 52 { + if data, err = util.DecodeStringToBinary(labels[0], 32); err == nil { if pkey = ed25519.NewPublicKeyFromBytes(data); pkey != nil { break } } } // (2) check if TLD is in our local config - if pkey = config.Cfg.GNS.GetRootZoneKey(names[0]); pkey != nil { + if pkey = config.Cfg.GNS.GetRootZoneKey(labels[0]); pkey != nil { break } // (3) check if TLD is one of our identities - if pkey, err = gns.GetLocalZone(names[0]); err == nil { + if pkey, err = gns.GetLocalZone(labels[0]); err == nil { break } // (4) we can't resolve this TLD return nil, ErrUnknownTLD } // continue with resolution relative to a zone. - return gns.ResolveRelative(names[1:], pkey, kind, mode) + return gns.ResolveRelative(labels[1:], pkey, kind, mode) } // Resolve relative path (to a given zone) recursively by processing simple // (PKEY,Label) lookups in sequence and handle intermediate GNS record types -func (gns *GNSModule) ResolveRelative(names []string, pkey *ed25519.PublicKey, kind int, mode int) (set *GNSRecordSet, err error) { +func (gns *GNSModule) ResolveRelative(labels []string, pkey *ed25519.PublicKey, kind RRTypeList, mode int) (set *GNSRecordSet, err error) { // Process all names in sequence - var records []*message.GNSResourceRecord + var ( + records []*message.GNSResourceRecord // final resource records from resolution + hdlrs *BlockHandlerList // list of block handlers in final step + ) name_loop: - for ; len(names) > 0; names = names[1:] { - logger.Printf(logger.DBG, "[gns] ResolveRelative '%s' in '%s'\n", names[0], util.EncodeBinaryToString(pkey.Bytes())) + for ; len(labels) > 0; labels = labels[1:] { + logger.Printf(logger.DBG, "[gns] ResolveRelative '%s' in '%s'\n", labels[0], util.EncodeBinaryToString(pkey.Bytes())) // resolve next level var block *GNSBlock - if block, err = gns.Lookup(pkey, names[0], mode == enums.GNS_LO_DEFAULT); err != nil { + if block, err = gns.Lookup(pkey, labels[0], mode == enums.GNS_LO_DEFAULT); err != nil { // failed to resolve name return } @@ -213,94 +172,64 @@ name_loop: } // post-process block by inspecting contained resource records for // special GNS types - var hdlr BlockHandler if records, err = block.Records(); err != nil { return } - for _, rec := range records { - // let a block handler decide how to handle records - if hdlr != nil { - switch hdlr.TypeAction(int(rec.Type)) { - case -1: - // No records of this type allowed in block - err = ErrInvalidRecordType - return - case 0: - // records of this type are simply ignored - continue - case 1: - // process record of this type - } - } - switch int(rec.Type) { - //---------------------------------------------------------- - case enums.GNS_TYPE_PKEY: - // check for single RR and sane key data - if len(rec.Data) != 32 || len(records) > 1 { - err = ErrInvalidPKEY - return - } - // set new PKEY and continue resolution - pkey = ed25519.NewPublicKeyFromBytes(rec.Data) - continue name_loop + // assemble a list of block handlers for this block: if multiple + // block handlers are present, they are consistent with all block + // records. + if hdlrs, err = NewBlockHandlerList(records, labels[1:]); err != nil { + // conflicting block handler records found: terminate with error. + // (N.B.: The BlockHandlerList class executes the logic which mix + // of resource records in a single block is considered valid.) + return + } - //---------------------------------------------------------- - case enums.GNS_TYPE_GNS2DNS: - // get the master controlling this block; create a new - // one if necessary - var inst *Gns2DnsHandler - if hdlr == nil { - inst = NewGns2DnsHandler() - hdlr = inst - } else { - inst = hdlr.(*Gns2DnsHandler) - } - // extract list of names in DATA block: - logger.Printf(logger.DBG, "[gns] GNS2DNS data: %s\n", hex.EncodeToString(rec.Data)) - var dnsNames []string - for pos := 0; ; { - next, name := DNSNameFromBytes(rec.Data, pos) - if len(name) == 0 { - break - } - dnsNames = append(dnsNames, name) - pos = next - } - logger.Printf(logger.DBG, "[gns] GNS2DNS params: %v\n", dnsNames) - if len(dnsNames) != 2 { - err = ErrInvalidRecordBody - return - } - // Add to collection of requests - logger.Printf(logger.DBG, "[gns] GNS2DNS: query for '%s' on '%s'\n", dnsNames[0], dnsNames[1]) - if !inst.AddRequest(dnsNames[0], dnsNames[1]) { - err = ErrInvalidRecordBody - return - } + //-------------------------------------------------------------- + // handle special block cases in priority order: + //-------------------------------------------------------------- + + if hdlr := hdlrs.GetHandler(enums.GNS_TYPE_PKEY); hdlr != nil { + // (1) PKEY record: + inst := hdlr.(*PkeyHandler) + // if labels are pending, set new zone and continue resolution + if len(labels) > 1 { + pkey = inst.pkey + continue name_loop } - } - // handle special block cases - if hdlr != nil { - switch inst := hdlr.(type) { - case *Gns2DnsHandler: - // we need to handle delegation to DNS: returns a list of found - // resource records in DNS (filter by 'kind') - fqdn := strings.Join(util.ReverseStringList(names[1:]), ".") + "." + inst.Name - if set, err = gns.ResolveDNS(fqdn, inst.Servers, kind, pkey); err != nil { - logger.Println(logger.ERROR, "[gns] GNS2DNS resilution failed.") - return - } - // we are done with resolution; pass on records to caller - records = set.Records + } else if hdlr := hdlrs.GetHandler(enums.GNS_TYPE_GNS2DNS); hdlr != nil { + // (2) GNS2DNS records: delegate resolution to DNS + inst := hdlr.(*Gns2DnsHandler) + // we need to handle delegation to DNS: returns a list of found + // resource records in DNS (filter by 'kind') + lbls := strings.Join(util.ReverseStringList(labels[1:]), ".") + if len(lbls) > 0 { + lbls += "." + } + fqdn := lbls + inst.Name + if set, err = gns.ResolveDNS(fqdn, inst.Servers, kind, pkey); err != nil { + logger.Println(logger.ERROR, "[gns] GNS2DNS resolution failed.") + return + } + // we are done with resolution; pass on records to caller + records = set.Records + break name_loop + } else if hdlr := hdlrs.GetHandler(enums.GNS_TYPE_BOX); hdlr != nil { + // (3) BOX records: + inst := hdlr.(*BoxHandler) + new_records := inst.Records(kind).Records + if len(new_records) > 0 { + records = new_records break name_loop } } } - // Assemble resulting resource record set + // Assemble resulting resource record set by filtering for requested types. + // Records might get transformed by active block handlers. set = NewGNSRecordSet() for _, rec := range records { // is this the record type we are looking for? - if kind == enums.GNS_TYPE_ANY || int(rec.Type) == kind { + if kind.HasType(int(rec.Type)) { // add it to the result set.AddRecord(rec) } @@ -321,7 +250,6 @@ func (gns *GNSModule) Lookup(pkey *ed25519.PublicKey, label string, remote bool) return } if block == nil { - logger.Println(logger.DBG, "[gns] local Lookup: no block found") if remote { // get the block from a remote lookup if block, err = gns.LookupRemote(query); err != nil || block == nil { @@ -330,12 +258,15 @@ func (gns *GNSModule) Lookup(pkey *ed25519.PublicKey, label string, remote bool) block = nil } else { logger.Println(logger.DBG, "[gns] remote Lookup: no block found") + err = fmt.Errorf("No block found") } // lookup fails completely -- no result return } // store RRs from remote locally. gns.StoreLocal(query, block) + } else { + err = fmt.Errorf("No block found") } } return diff --git a/src/gnunet/service/gns/service.go b/src/gnunet/service/gns/service.go index 852f513..dd516ff 100644 --- a/src/gnunet/service/gns/service.go +++ b/src/gnunet/service/gns/service.go @@ -4,9 +4,6 @@ import ( "encoding/hex" "io" - "github.com/bfix/gospel/crypto/ed25519" - "github.com/bfix/gospel/data" - "github.com/bfix/gospel/logger" "gnunet/config" "gnunet/crypto" "gnunet/enums" @@ -14,6 +11,10 @@ import ( "gnunet/service" "gnunet/transport" "gnunet/util" + + "github.com/bfix/gospel/crypto/ed25519" + "github.com/bfix/gospel/data" + "github.com/bfix/gospel/logger" ) //---------------------------------------------------------------------- @@ -77,7 +78,8 @@ func (s *GNSService) ServeClient(mc *transport.MsgChannel) { // access to the message channel to send responses) pkey := ed25519.NewPublicKeyFromBytes(m.Zone) label := m.GetName() - recset, err := s.Resolve(label, pkey, int(m.Type), int(m.Options)) + kind := NewRRTypeList(int(m.Type)) + recset, err := s.Resolve(label, pkey, kind, int(m.Options)) if err != nil { logger.Printf(logger.ERROR, "[gns] Failed to lookup block: %s\n", err.Error()) break @@ -146,7 +148,7 @@ func (s *GNSService) LookupNamecache(query *Query) (block *GNSBlock, err error) break } // check if block was found - if len(m.EncData) == 0 { + if len(m.EncData) == 0 || util.IsNull(m.EncData) { logger.Println(logger.DBG, "[gns] block not found in namecache") break } diff --git a/src/gnunet/service/service.go b/src/gnunet/service/service.go index 5b44d47..6aab226 100644 --- a/src/gnunet/service/service.go +++ b/src/gnunet/service/service.go @@ -3,8 +3,9 @@ package service import ( "fmt" - "github.com/bfix/gospel/logger" "gnunet/transport" + + "github.com/bfix/gospel/logger" ) // Service is an interface for GNUnet services. Every service has one channel @@ -48,7 +49,7 @@ func (si *ServiceImpl) Start(spec string) (err error) { } // start channel server - logger.Printf(logger.DBG, "[%s] Service starting.\n", si.name) + logger.Printf(logger.INFO, "[%s] Service starting.\n", si.name) if si.srvc, err = transport.NewChannelServer(spec, si.hdlr); err != nil { return } @@ -61,19 +62,19 @@ func (si *ServiceImpl) Start(spec string) (err error) { select { case in := <-si.hdlr: if in == nil { - logger.Printf(logger.DBG, "[%s] Listener terminated.\n", si.name) + logger.Printf(logger.INFO, "[%s] Listener terminated.\n", si.name) break loop } switch ch := in.(type) { case transport.Channel: - logger.Printf(logger.DBG, "[%s] Client connected.\n", si.name) + logger.Printf(logger.INFO, "[%s] Client connected.\n", si.name) go si.impl.ServeClient(transport.NewMsgChannel(ch)) } case <-si.ctrl: break loop } } - logger.Printf(logger.DBG, "[%s] Service closing.\n", si.name) + logger.Printf(logger.INFO, "[%s] Service closing.\n", si.name) si.srvc.Close() si.running = false }() @@ -89,7 +90,7 @@ func (si *ServiceImpl) Stop() error { } si.running = false si.ctrl <- true - logger.Printf(logger.DBG, "[%s] Service terminating.\n", si.name) + logger.Printf(logger.INFO, "[%s] Service terminating.\n", si.name) return si.impl.Stop() } diff --git a/src/gnunet/transport/channel.go b/src/gnunet/transport/channel.go index 8502d8f..4005759 100644 --- a/src/gnunet/transport/channel.go +++ b/src/gnunet/transport/channel.go @@ -11,6 +11,7 @@ import ( "gnunet/message" ) +// Error codes var ( ErrChannelNotImplemented = fmt.Errorf("Protocol not implemented") ErrChannelNotOpened = fmt.Errorf("Channel not opened") diff --git a/src/gnunet/transport/channel_netw.go b/src/gnunet/transport/channel_netw.go index 69ddda5..ecfd8e2 100644 --- a/src/gnunet/transport/channel_netw.go +++ b/src/gnunet/transport/channel_netw.go @@ -14,11 +14,12 @@ import ( // NetworkChannel type NetworkChannel struct { - network string - conn net.Conn + network string // network protocol identifier ("tcp", "unix", ...) + conn net.Conn // associated connection } -// NewNetworkChannel +// NewNetworkChannel creates a new channel for a given network protocol. +// The channel is in pending state and need to be opened before use. func NewNetworkChannel(netw string) Channel { return &NetworkChannel{ network: netw, @@ -26,7 +27,11 @@ func NewNetworkChannel(netw string) Channel { } } -// Open +// Open a network channel based on specification: +// The specification is a string separated into parts by the '+' delimiter +// (e.g. "unix+/tmp/gnunet-service-gns-go.sock+perm=0770"). The network +// identifier (first part) must match the network specification of the +// underlaying NetworkChannel instance. func (c *NetworkChannel) Open(spec string) (err error) { parts := strings.Split(spec, "+") // check for correct protocol @@ -38,7 +43,7 @@ func (c *NetworkChannel) Open(spec string) (err error) { return } -// Close +// Close a network channel func (c *NetworkChannel) Close() error { if c.conn != nil { return c.conn.Close() @@ -46,7 +51,8 @@ func (c *NetworkChannel) Close() error { return ErrChannelNotOpened } -// Read +// Read bytes from a network channel into buffer: Returns the number of read +// bytes and an error code. Only works on open channels ;) func (c *NetworkChannel) Read(buf []byte) (int, error) { if c.conn == nil { return 0, ErrChannelNotOpened @@ -54,7 +60,8 @@ func (c *NetworkChannel) Read(buf []byte) (int, error) { return c.conn.Read(buf) } -// Write +// Write buffer to a network channel: Returns the number of written bytes and +// an error code. func (c *NetworkChannel) Write(buf []byte) (int, error) { if c.conn == nil { return 0, ErrChannelNotOpened @@ -67,8 +74,8 @@ func (c *NetworkChannel) Write(buf []byte) (int, error) { // NetworkChannelServer type NetworkChannelServer struct { - network string - listener net.Listener + network string // network protocol to listen on + listener net.Listener // reference to listener object } // NewNetworkChannelServer @@ -79,7 +86,9 @@ func NewNetworkChannelServer(netw string) ChannelServer { } } -// Open +// Open a network channel server (= start running it) based on the given +// specification. For every client connection to the server, the associated +// network channel for the connection is send via the hdlr channel. func (s *NetworkChannelServer) Open(spec string, hdlr chan<- Channel) (err error) { parts := strings.Split(spec, "+") // check for correct protocol @@ -136,7 +145,7 @@ func (s *NetworkChannelServer) Open(spec string, hdlr chan<- Channel) (err error return nil } -// Close +// Close a network channel server (= stop the server) func (s *NetworkChannelServer) Close() error { if s.listener != nil { err := s.listener.Close() @@ -147,27 +156,35 @@ func (s *NetworkChannelServer) Close() error { } //////////////////////////////////////////////////////////////////////// +// helper functions to instantiate network channels and servers for +// common network protocols +// NewSocketChannel: Unix Domain Socket connection func NewSocketChannel() Channel { return NewNetworkChannel("unix") } +// NewTCPChannel: TCP connection func NewTCPChannel() Channel { return NewNetworkChannel("tcp") } +// NewUDPChannel: UDP connection func NewUDPChannel() Channel { return NewNetworkChannel("udp") } +// NewSocketChannelServer: Unix Domain Socket listener func NewSocketChannelServer() ChannelServer { return NewNetworkChannelServer("unix") } +// NewTCPChannelServer: TCP listener func NewTCPChannelServer() ChannelServer { return NewNetworkChannelServer("tcp") } +// NewUDPChannelServer: UDP listener func NewUDPChannelServer() ChannelServer { return NewNetworkChannelServer("udp") } diff --git a/src/gnunet/transport/connection.go b/src/gnunet/transport/connection.go index e66bec1..1cf0317 100644 --- a/src/gnunet/transport/connection.go +++ b/src/gnunet/transport/connection.go @@ -5,7 +5,6 @@ import ( "gnunet/message" ) -//////////////////////////////////////////////////////////////////////// // Connection for communicating peers type Connection struct { from, to *core.Peer @@ -17,6 +16,8 @@ type Connection struct { shared []byte } +// NewConnection instanciates a new connection between peers communicating +// over a message channel (Connections are authenticated and secured). func NewConnection(ch *MsgChannel, from, to *core.Peer) *Connection { return &Connection{ from: from, @@ -26,27 +27,33 @@ func NewConnection(ch *MsgChannel, from, to *core.Peer) *Connection { } } +// SharedSecret computes the shared secret the two endpoints of a connection. func (c *Connection) SharedSecret(secret []byte) { c.shared = make([]byte, len(secret)) copy(c.shared, secret) } +// GetState returns the current state of the connection. func (c *Connection) GetState() int { return c.state } +// SetBandwidth to control transfer rates on the connection func (c *Connection) SetBandwidth(bw uint32) { c.bandwidth = bw } +// Close connection between two peers. func (c *Connection) Close() error { return c.ch.Close() } +// Send a message on the connection func (c *Connection) Send(msg message.Message) error { return c.ch.Send(msg) } +// Receive a message on the connection func (c *Connection) Receive() (message.Message, error) { return c.ch.Receive() } diff --git a/src/gnunet/transport/session.go b/src/gnunet/transport/session.go index 90e4016..7d33ea2 100644 --- a/src/gnunet/transport/session.go +++ b/src/gnunet/transport/session.go @@ -1,7 +1,6 @@ package transport -import () - +// Session states const ( KX_STATE_DOWN = iota // No handshake yet. KX_STATE_KEY_SENT // We've sent our session key. diff --git a/src/gnunet/util/address.go b/src/gnunet/util/address.go index 04e2254..37fb102 100644 --- a/src/gnunet/util/address.go +++ b/src/gnunet/util/address.go @@ -1,29 +1,19 @@ package util import ( + "encoding/hex" "fmt" + "net" ) -type IPAddress struct { - Host []byte `size:"*-2"` - Port uint16 `order:"big"` -} - -func NewIPAddress(host []byte, port uint16) *IPAddress { - ip := &IPAddress{ - Host: make([]byte, len(host)), - Port: port, - } - copy(ip.Host, host) - return ip -} - +// Address specifies how a peer is reachable on the network. type Address struct { - Transport string - Options uint32 `order:"big"` - Address []byte `size:"*"` + Transport string // transport protocol + Options uint32 `order:"big"` // address options + Address []byte `size:"*"` // address data (protocol-dependent) } +// NewAddress returns a new Address for the given transport and specs func NewAddress(transport string, addr []byte) *Address { a := &Address{ Transport: transport, @@ -34,6 +24,37 @@ func NewAddress(transport string, addr []byte) *Address { return a } +// String returns a human-readable representation of an address. func (a *Address) String() string { return fmt.Sprintf("Address{%s}", AddressString(a.Transport, a.Address)) } + +//---------------------------------------------------------------------- + +// AddressString returns a string representaion of an address. +func AddressString(transport string, addr []byte) string { + if transport == "tcp" || transport == "udp" { + alen := len(addr) + port := uint(addr[alen-2])*256 + uint(addr[alen-1]) + return fmt.Sprintf("%s:%s:%d", transport, net.IP(addr[:alen-2]).String(), port) + } + return fmt.Sprintf("%s:%s", transport, hex.EncodeToString(addr)) +} + +//---------------------------------------------------------------------- + +// IP address (can be IPv4 or IPv6 or a DNS name) +type IPAddress struct { + Host []byte `size:"*-2"` + Port uint16 `order:"big"` +} + +// NewIPAddress creates a new instance for a given host and port. +func NewIPAddress(host []byte, port uint16) *IPAddress { + ip := &IPAddress{ + Host: make([]byte, len(host)), + Port: port, + } + copy(ip.Host, host) + return ip +} diff --git a/src/gnunet/util/array.go b/src/gnunet/util/array.go index 9076516..f6213bf 100644 --- a/src/gnunet/util/array.go +++ b/src/gnunet/util/array.go @@ -30,6 +30,16 @@ func Reverse(b []byte) []byte { return r } +// IsNull returns true if all bytes in an array are set to 0. +func IsNull(b []byte) bool { + for _, v := range b { + if v != 0 { + return false + } + } + return true +} + // CopyBlock copies 'in' to 'out' so that 'out' is filled completely. // - If 'in' is larger than 'out', it is left-truncated before copy // - If 'in' is smaller than 'out', it is left-padded with 0 before copy diff --git a/src/gnunet/util/format.go b/src/gnunet/util/format.go index 722b9a7..780c814 100644 --- a/src/gnunet/util/format.go +++ b/src/gnunet/util/format.go @@ -1,22 +1,13 @@ package util import ( - "encoding/hex" "fmt" - "net" ) -func AddressString(transport string, addr []byte) string { - if transport == "tcp" || transport == "udp" { - alen := len(addr) - port := uint(addr[alen-2])*256 + uint(addr[alen-1]) - return fmt.Sprintf("%s:%s:%d", transport, net.IP(addr[:alen-2]).String(), port) - } - return fmt.Sprintf("%s:%s", transport, hex.EncodeToString(addr)) -} - var scale = " kMGTPEO" +// Scale1024 returns an integer value (e.g. a size) as a human-readable +// string with scales: a size of 183467245 would result in "174,967M" func Scale1024(n uint64) string { v := float64(n) var i int diff --git a/src/gnunet/util/id.go b/src/gnunet/util/id.go index 41bd30e..ab9b98c 100644 --- a/src/gnunet/util/id.go +++ b/src/gnunet/util/id.go @@ -4,7 +4,8 @@ var ( _id = 0 ) +// generate next unique identifier (unique in the running process/application) func NextID() int { - _id += 1 + _id++ return _id } diff --git a/src/gnunet/util/peer_id.go b/src/gnunet/util/peer_id.go index 03bc73e..6549d75 100644 --- a/src/gnunet/util/peer_id.go +++ b/src/gnunet/util/peer_id.go @@ -1,9 +1,11 @@ package util +// PeerID is the 32-byte binary representation od a Ed25519 key type PeerID struct { Key []byte `size:"32"` } +// NewPeerID creates a new object from the data. func NewPeerID(data []byte) *PeerID { if data == nil { data = make([]byte, 32) @@ -22,6 +24,7 @@ func NewPeerID(data []byte) *PeerID { } } +// String returns a human-readable representation of a peer id. func (p *PeerID) String() string { return EncodeBinaryToString(p.Key) } diff --git a/src/gnunet/util/rnd.go b/src/gnunet/util/rnd.go index d3c8b2e..a9f247f 100644 --- a/src/gnunet/util/rnd.go +++ b/src/gnunet/util/rnd.go @@ -6,16 +6,19 @@ import ( "encoding/binary" ) +// RndArray fills a buffer with random content func RndArray(b []byte) { rand.Read(b) } +// NewRndArray creates a new buffer of given size; filled with random content. func NewRndArray(size int) []byte { b := make([]byte, size) rand.Read(b) return b } +// RndUInt64 returns a new 64-bit unsigned random integer. func RndUInt64() uint64 { b := make([]byte, 8) RndArray(b) @@ -25,22 +28,27 @@ func RndUInt64() uint64 { return v } +// RndInt64 returns a new 64-bit signed random integer. func RndInt64() int64 { return int64(RndUInt64()) } +// RndUInt32 returns a new 32-bit unsigned random integer. func RndUInt32() uint32 { return uint32(RndUInt64()) } +// RndInt32 returns a new 32-bit signed random integer. func RndInt32() int32 { return int32(RndUInt64()) } +// RndUInt16 returns a new 16-bit unsigned random integer. func RndUInt16() uint16 { return uint16(RndUInt64()) } +// RndInt16 returns a new 16-bit signed random integer. func RndInt16() int16 { return int16(RndUInt64()) } -- cgit v1.2.3