From 89e51820f1e78b77864892936a1171f722ce4ce9 Mon Sep 17 00:00:00 2001 From: satk0 Date: Wed, 26 Feb 2025 21:21:09 +0100 Subject: [PATCH] Add DatabaseLookup and test it --- doc/tests/header.mk | 30 ++- lib/i2np/database_lookup.go | 274 +++++++++++++++++++- lib/i2np/database_lookup_test.go | 430 +++++++++++++++++++++++++++++++ 3 files changed, 720 insertions(+), 14 deletions(-) create mode 100644 lib/i2np/database_lookup_test.go diff --git a/doc/tests/header.mk b/doc/tests/header.mk index c17793c..67d0581 100644 --- a/doc/tests/header.mk +++ b/doc/tests/header.mk @@ -1,5 +1,5 @@ -test-i2np-header-all: test-i2np-type test-i2np-message test-i2np-expiration test-i2np-ntcp-components test-i2np-data test-i2np-regression test-i2np-build-request-record test-i2np-build-response-record +test-i2np-header-all: test-i2np-type test-i2np-message test-i2np-expiration test-i2np-ntcp-components test-i2np-data test-i2np-regression test-i2np-build-request-record test-i2np-build-response-record test-i2np-database-lookup test-i2np-type: $(GO) test -v ./lib/i2np -run TestReadI2NPTypeWith @@ -37,6 +37,31 @@ test-i2np-build-response-record: $(GO) test -v ./lib/i2np -run TestReadBuildResponseRecordTooLittleData $(GO) test -v ./lib/i2np -run TestReadBuildResponseRecordValidData +test-i2np-database-lookup: + $(GO) test -v ./lib/i2np -run TestReadDatabaseLookupKeyTooLittleData + $(GO) test -v ./lib/i2np -run TestReadDatabaseLookupKeyValidData + $(GO) test -v ./lib/i2np -run TestReadDatabaseLookupFromTooLittleData + $(GO) test -v ./lib/i2np -run TestReadDatabaseLookupFromValidData + $(GO) test -v ./lib/i2np -run TestReadDatabaseLookupFlagsTooLittleData + $(GO) test -v ./lib/i2np -run TestReadDatabaseLookupFlagsValidData + $(GO) test -v ./lib/i2np -run TestReadDatabaseLookupReplyTunnelIDTooLittleData + $(GO) test -v ./lib/i2np -run TestReadDatabaseLookupReplyTunnelIDNotIncluded + $(GO) test -v ./lib/i2np -run TestReadDatabaseLookupReplyTunnelIDValidData + $(GO) test -v ./lib/i2np -run TestReadDatabaseLookupSizeTooLittleData + $(GO) test -v ./lib/i2np -run TestReadDatabaseLookupSizeValidData + $(GO) test -v ./lib/i2np -run TestReadDatabaseLookupExcludedPeersTooLittleData + $(GO) test -v ./lib/i2np -run TestReadDatabaseLookupExcludedPeersZeroSize + $(GO) test -v ./lib/i2np -run TestReadDatabaseLookupExcludedPeersValidData + $(GO) test -v ./lib/i2np -run TestReadDatabaseLookupReplyKeyTooLittleData + $(GO) test -v ./lib/i2np -run TestReadDatabaseLookupReplyKeyValidData + $(GO) test -v ./lib/i2np -run TestReadDatabaseLookupTagsTooLittleData + $(GO) test -v ./lib/i2np -run TestReadDatabaseLookupTagsValidData + $(GO) test -v ./lib/i2np -run TestReadDatabaseLookupReplyTagsTooLittleData + $(GO) test -v ./lib/i2np -run TestReadDatabaseLookupReplyTagsZeroTags + $(GO) test -v ./lib/i2np -run TestReadDatabaseLookupReplyTagsValidData + $(GO) test -v ./lib/i2np -run TestReadDatabaseLookupTooLittleData + $(GO) test -v ./lib/i2np -run TestReadDatabaseLookupValidData + .PHONY: test-i2np-header-all \ test-i2np-type \ test-i2np-message \ @@ -45,4 +70,5 @@ test-i2np-build-response-record: test-i2np-data \ test-i2np-regression \ test-i2np-build-request-record \ - test-i2np-build-response-record + test-i2np-build-response-record \ + test-i2np-database-lookup diff --git a/lib/i2np/database_lookup.go b/lib/i2np/database_lookup.go index 88afe61..9ea78b7 100644 --- a/lib/i2np/database_lookup.go +++ b/lib/i2np/database_lookup.go @@ -1,6 +1,10 @@ package i2np import ( + "errors" + + "github.com/sirupsen/logrus" + common "github.com/go-i2p/go-i2p/lib/common/data" "github.com/go-i2p/go-i2p/lib/common/session_key" "github.com/go-i2p/go-i2p/lib/common/session_tag" @@ -8,8 +12,8 @@ import ( /* I2P I2NP DatabaseLookup -https://geti2p.net/spec/i2np -Accurate for version 0.9.28 +https://geti2p.net/spec/i2np#databaselookup +Accurate for version 0.9.65 +----+----+----+----+----+----+----+----+ | SHA256 hash as the key to look up | @@ -21,7 +25,7 @@ Accurate for version 0.9.28 | | +----+----+----+----+----+----+----+----+ | SHA256 hash of the routerInfo | -+ who is asking, or the gateway to + ++ who is asking or the gateway to + | send the reply to | + + | | @@ -30,7 +34,7 @@ Accurate for version 0.9.28 +----+----+----+----+----+----+----+----+ |flag| reply_tunnelId | size | | +----+----+----+----+----+----+----+ + -| SHA256 of $key1 to exclude | +| SHA256 of key1 to exclude | + + | | + + @@ -38,7 +42,7 @@ Accurate for version 0.9.28 + +----+ | | | +----+----+----+----+----+----+----+ + -| SHA256 of $key2 to exclude | +| SHA256 of key2 to exclude | + + ~ ~ + +----+ @@ -95,12 +99,20 @@ flags :: with version 0.9.16 or higher. 01 => LS lookup, return LeaseSet or DatabaseSearchReplyMessage + As of release 0.9.38, may also return a + LeaseSet2, MetaLeaseSet, or EncryptedLeaseSet. 10 => RI lookup, return RouterInfo or DatabaseSearchReplyMessage 11 => exploration lookup, return DatabaseSearchReplyMessage containing non-floodfill routers only (replaces an excludedPeer of all zeroes) - bits 7-4: + bit 4: ECIESFlag + before release 0.9.46 ignored + as of release 0.9.46: + 0 => send unencrypted or ElGamal reply + 1 => send ChaCha/Poly encrypted reply using enclosed key + (whether tag is enclosed depends on bit 1) + bits 7-5: through release 0.9.5, must be set to 0 as of release 0.9.6, ignored, set to 0 for compatibility with future uses and with older routers @@ -108,7 +120,7 @@ flags :: reply_tunnelId :: 4 byte TunnelID only included if deliveryFlag == 1 - tunnelId of the tunnel to send the reply to + tunnelId of the tunnel to send the reply to, nonzero size :: 2 byte Integer @@ -124,18 +136,53 @@ excludedPeers :: to list non-floodfill routers only. reply_key :: - 32 byte SessionKey - only included if encryptionFlag == 1, only as of release 0.9.7 + 32 byte key + see below tags :: 1 byte Integer valid range: 1-32 (typically 1) the number of reply tags that follow - only included if encryptionFlag == 1, only as of release 0.9.7 + see below + +reply_tags :: + one or more 8 or 32 byte session tags (typically one) + see below + + +ElG to ElG + +reply_key :: + 32 byte SessionKey big-endian + only included if encryptionFlag == 1 AND ECIESFlag == 0, only as of release 0.9.7 + +tags :: + 1 byte Integer + valid range: 1-32 (typically 1) + the number of reply tags that follow + only included if encryptionFlag == 1 AND ECIESFlag == 0, only as of release 0.9.7 reply_tags :: one or more 32 byte SessionTags (typically one) - only included if encryptionFlag == 1, only as of release 0.9.7 + only included if encryptionFlag == 1 AND ECIESFlag == 0, only as of release 0.9.7 + + +ECIES to ElG + +reply_key :: + 32 byte ECIES SessionKey big-endian + only included if encryptionFlag == 0 AND ECIESFlag == 1, only as of release 0.9.46 + +tags :: + 1 byte Integer + required value: 1 + the number of reply tags that follow + only included if encryptionFlag == 0 AND ECIESFlag == 1, only as of release 0.9.46 + +reply_tags :: + an 8 byte ECIES SessionTag + only included if encryptionFlag == 0 AND ECIESFlag == 1, only as of release 0.9.46 + */ type DatabaseLookup struct { @@ -146,6 +193,209 @@ type DatabaseLookup struct { Size int ExcludedPeers []common.Hash ReplyKey session_key.SessionKey - tags int + Tags int ReplyTags []session_tag.SessionTag } + +var ERR_DATABASE_LOOKUP_NOT_ENOUGH_DATA = errors.New("not enough i2np database lookup data") + +func ReadDatabaseLookup(data []byte) (DatabaseLookup, error) { + log.Debug("Reading DatabaseLookup") + databaseLookup := DatabaseLookup{} + + length, key, err := readDatabaseLookupKey(data) + if err != nil { + log.WithError(err).Error("Failed to read Key") + return databaseLookup, err + } + databaseLookup.Key = key + + length, from, err := readDatabaseLookupFrom(length, data) + if err != nil { + log.WithError(err).Error("Failed to read From") + return databaseLookup, err + } + databaseLookup.From = from + + length, flags, err := readDatabaseLookupFlags(length, data) + if err != nil { + log.WithError(err).Error("Failed to read Flags") + return databaseLookup, err + } + databaseLookup.Flags = flags + + length, replyTunnelID, err := readDatabaseLookupReplyTunnelID(flags, length, data) + if err != nil { + log.WithError(err).Error("Failed to read ReplyTunnelID") + return databaseLookup, err + } + databaseLookup.ReplyTunnelID = replyTunnelID + + length, size, err := readDatabaseLookupSize(length, data) + if err != nil { + log.WithError(err).Error("Failed to read Size") + return databaseLookup, err + } + databaseLookup.Size = size + + length, excludedPeers, err := readDatabaseLookupExcludedPeers(length, data, size) + if err != nil { + log.WithError(err).Error("Failed to read ExcludedPeers") + return databaseLookup, err + } + databaseLookup.ExcludedPeers = excludedPeers + + length, reply_key, err := readDatabaseLookupReplyKey(length, data) + if err != nil { + log.WithError(err).Error("Failed to read ReplyKey") + return databaseLookup, err + } + databaseLookup.ReplyKey = reply_key + + length, tags, err := readDatabaseLookupTags(length, data) + if err != nil { + log.WithError(err).Error("Failed to read Tags") + return databaseLookup, err + } + databaseLookup.Tags = tags + + length, reply_tags, err := readDatabaseLookupReplyTags(length, data, tags) + if err != nil { + log.WithError(err).Error("Failed to read ReplyTags") + return databaseLookup, err + } + databaseLookup.ReplyTags = reply_tags + + log.Debug("DatabaseLookup read successfully") + return databaseLookup, nil +} + +func readDatabaseLookupKey(data []byte) (int, common.Hash, error) { + if len(data) < 32 { + return 0, common.Hash{}, ERR_DATABASE_LOOKUP_NOT_ENOUGH_DATA + } + + key := common.Hash(data[:32]) + log.WithFields(logrus.Fields{ + "at": "i2np.readDatabaseLookupKey", + "key": key, + }).Debug("parsed_database_lookup_key") + return 32, key, nil +} + +func readDatabaseLookupFrom(length int, data []byte) (int, common.Hash, error) { + if len(data) < length + 32 { + return length, common.Hash{}, ERR_DATABASE_LOOKUP_NOT_ENOUGH_DATA + } + + from := common.Hash(data[length:length + 32]) + log.WithFields(logrus.Fields{ + "at": "i2np.database_lookup.readDatabaseLookupFrom", + "from": from, + }).Debug("parsed_database_lookup_from") + return length + 32, from, nil +} + +func readDatabaseLookupFlags(length int, data []byte) (int, byte, error) { + if len(data) < length + 1 { + return length, byte(0), ERR_DATABASE_LOOKUP_NOT_ENOUGH_DATA + } + flags := data[length] + + log.WithFields(logrus.Fields{ + "at": "i2np.database_lookup.readDatabaseLookupFlags", + "flags": flags, + }).Debug("parsed_database_lookup_flags") + return length + 1, flags, nil +} + +func readDatabaseLookupReplyTunnelID(flags byte, length int, data []byte) (int, [4]byte, error) { + if flags & 1 != 1 { + return length, [4]byte{}, nil + } + if len(data) < length + 4 { + return length, [4]byte{}, ERR_DATABASE_LOOKUP_NOT_ENOUGH_DATA + } + + replyTunnelID := [4]byte(data[length:length + 4]) + log.WithFields(logrus.Fields{ + "at": "i2np.database_lookup.readDatabaseLookupReplyTunnelID", + "reply_tunnel_id": replyTunnelID, + }).Debug("parsed_database_lookup_reply_tunnel_id") + return length + 4, replyTunnelID, nil +} + +func readDatabaseLookupSize(length int, data []byte) (int, int, error) { + if len(data) < length + 2 { + return length, 0, ERR_DATABASE_LOOKUP_NOT_ENOUGH_DATA + } + + size := common.Integer(data[length:length + 2]).Int() + log.WithFields(logrus.Fields{ + "at": "i2np.database_lookup.readDatabaseLookupSize", + "size": size, + }).Debug("parsed_database_lookup_size") + return length + 2, size, nil +} + +func readDatabaseLookupExcludedPeers(length int, data []byte, size int) (int, []common.Hash, error) { + if len(data) < length + size * 32 { + return length, []common.Hash{}, ERR_DATABASE_LOOKUP_NOT_ENOUGH_DATA + } + var excludedPeers []common.Hash + for i := 0; i < size; i++ { + offset := length + i * 32 + peer := common.Hash(data[offset:offset + 32]) + excludedPeers = append(excludedPeers, peer) + } + + log.WithFields(logrus.Fields{ + "at": "i2np.database_lookup.readDatabaseLookupExcludedPeers", + "excluded_peers": excludedPeers, + }).Debug("parsed_database_lookup_excluded_peers") + return length + size * 32, excludedPeers, nil +} + +func readDatabaseLookupReplyKey(length int, data []byte) (int, session_key.SessionKey, error) { + if len(data) < length + 32 { + return length, session_key.SessionKey{}, ERR_DATABASE_LOOKUP_NOT_ENOUGH_DATA + } + replyKey := session_key.SessionKey(data[length:length + 32]) + + log.WithFields(logrus.Fields{ + "at": "i2np.database_lookup.readDatabaseLookupReplyKey", + "reply_key": replyKey, + }).Debug("parsed_database_lookup_reply_key") + return length + 32, replyKey, nil +} + +func readDatabaseLookupTags(length int, data []byte) (int, int, error) { + if len(data) < length + 1 { + return length, 0, ERR_DATABASE_LOOKUP_NOT_ENOUGH_DATA + } + tags := int(data[length]) + + log.WithFields(logrus.Fields{ + "at": "i2np.database_lookup.readDatabaseLookupTags", + "tags": tags, + }).Debug("parsed_database_lookup_tags") + return length + 1, tags, nil +} + +func readDatabaseLookupReplyTags(length int, data []byte, tags int) (int, []session_tag.SessionTag, error) { + if len(data) < length + tags * 32 { + return length, []session_tag.SessionTag{}, ERR_DATABASE_LOOKUP_NOT_ENOUGH_DATA + } + var reply_tags []session_tag.SessionTag + for i := 0; i < tags; i++ { + offset := length + i * 32 + tag := session_tag.SessionTag(data[offset:offset + 32]) + reply_tags = append(reply_tags, tag) + } + + log.WithFields(logrus.Fields{ + "at": "i2np.database_lookup.readDatabaseLookupReplyTags", + "reply_tags": reply_tags, + }).Debug("parsed_database_lookup_reply_tags") + return length + tags * 32, reply_tags, nil +} diff --git a/lib/i2np/database_lookup_test.go b/lib/i2np/database_lookup_test.go new file mode 100644 index 0000000..ee15c14 --- /dev/null +++ b/lib/i2np/database_lookup_test.go @@ -0,0 +1,430 @@ +package i2np + +import ( + "testing" + + common "github.com/go-i2p/go-i2p/lib/common/data" + "github.com/go-i2p/go-i2p/lib/common/session_key" + "github.com/go-i2p/go-i2p/lib/common/session_tag" + "github.com/stretchr/testify/assert" +) + +func TestReadDatabaseLookupKeyTooLittleData(t *testing.T) { + assert := assert.New(t) + + length, key, err := readDatabaseLookupKey([]byte{0x01}) + assert.Equal(0, length) + assert.Equal(common.Hash{}, key) + assert.Equal(ERR_DATABASE_LOOKUP_NOT_ENOUGH_DATA, err) +} + +func TestReadDatabaseLookupKeyValidData(t *testing.T) { + assert := assert.New(t) + + data := make([]byte, 32) + for i := range 31 { + data[i] = 0x31 + } + length, key, err := readDatabaseLookupKey(data) + expected := common.Hash(data) + + assert.Equal(32, length) + assert.Equal(expected, key) + assert.Equal(nil, err) +} + +func TestReadDatabaseLookupFromTooLittleData(t *testing.T) { + assert := assert.New(t) + + length := 32 + prev := make([]byte, length) + data := append(prev, 0x01) + + length, key, err := readDatabaseLookupFrom(length, data) + assert.Equal(32, length) + assert.Equal(common.Hash{}, key) + assert.Equal(ERR_DATABASE_LOOKUP_NOT_ENOUGH_DATA, err) +} + +func TestReadDatabaseLookupFromValidData(t *testing.T) { + assert := assert.New(t) + + length := 32 + prev := make([]byte, length) + expectedFrom := make([]byte, 32) + expectedFrom[23] = 0x21 + expectedFrom[29] = 0x37 + data := append(prev, expectedFrom...) + length, from, err := readDatabaseLookupFrom(length, data) + expected := common.Hash(expectedFrom) + + assert.Equal(64, length) + assert.Equal(from, expected) + assert.Equal(nil, err) +} + +func TestReadDatabaseLookupFlagsTooLittleData(t *testing.T) { + assert := assert.New(t) + + length := 64 + prev := make([]byte, length) + data := prev + + length, flags, err := readDatabaseLookupFlags(length, data) + assert.Equal(64, length) + assert.Equal(byte(0), flags) + assert.Equal(ERR_DATABASE_LOOKUP_NOT_ENOUGH_DATA, err) +} + +func TestReadDatabaseLookupFlagsValidData(t *testing.T) { + assert := assert.New(t) + + length := 64 + prev := make([]byte, length) + expected := byte(0x1) + data := append(prev, expected) + length, flags, err := readDatabaseLookupFlags(length, data) + + assert.Equal(65, length) + assert.Equal(flags, expected) + assert.Equal(nil, err) +} +func TestReadDatabaseLookupReplyTunnelIDTooLittleData(t *testing.T) { + assert := assert.New(t) + + length := 64 + prev := make([]byte, length) + flag := byte(0x1) + data := append(prev, flag) + + excessData := make([]byte, 2) + excessData[1] = 0x32 + data = append(data, excessData...) + length, flags, err := readDatabaseLookupFlags(length, data) + + length, replyTunnelID, err := readDatabaseLookupReplyTunnelID(flags, length, data) + assert.Equal(65, length) + assert.Equal([4]byte{}, replyTunnelID) + assert.Equal(ERR_DATABASE_LOOKUP_NOT_ENOUGH_DATA, err) +} + +func TestReadDatabaseLookupReplyTunnelIDNotIncluded(t *testing.T) { + assert := assert.New(t) + + length := 65 + data := make([]byte, length) + length, flags, err := readDatabaseLookupFlags(length, data) + + length, tunnelID, err := readDatabaseLookupReplyTunnelID(flags, length, data) + assert.Equal(65, length) + assert.Equal([4]byte{}, tunnelID) + assert.Equal(nil, err) +} + +func TestReadDatabaseLookupReplyTunnelIDValidData(t *testing.T) { + assert := assert.New(t) + + length := 64 + prev := make([]byte, length) + flag := byte(0x1) + data := append(prev, flag) + + expected := make([]byte, 4) + expected[1] = 0x32 + expected[3] = 0x34 + data = append(data, expected...) + length, flags, err := readDatabaseLookupFlags(length, data) + + length, replyTunnelID, err := readDatabaseLookupReplyTunnelID(flags, length, data) + assert.Equal(69, length) + assert.Equal([4]byte(expected), replyTunnelID) + assert.Equal(nil, err) +} + +func TestReadDatabaseLookupSizeTooLittleData(t *testing.T) { + assert := assert.New(t) + + length := 65 + data := make([]byte, length) + data = append(data, 0x2) + + length, size, err := readDatabaseLookupSize(length, data) + assert.Equal(65, length) + assert.Equal(0, size) + assert.Equal(ERR_DATABASE_LOOKUP_NOT_ENOUGH_DATA, err) +} + +func TestReadDatabaseLookupSizeValidData(t *testing.T) { + assert := assert.New(t) + + length := 65 + data := make([]byte, length) + expectedSizeData := []byte{0x16,0x9} + data = append(data, expectedSizeData...) + + length, size, err := readDatabaseLookupSize(length, data) + assert.Equal(67, length) + assert.Equal(common.Integer(expectedSizeData).Int(), size) + assert.Equal(nil, err) +} + +func TestReadDatabaseLookupExcludedPeersTooLittleData(t *testing.T) { + assert := assert.New(t) + + length := 65 + data := make([]byte, length) + sizeData := []byte{0x0,0x3} + data = append(data, sizeData...) + data = append(data, 0x23) + + length, size, err := readDatabaseLookupSize(length, data) + length, excludedPeers, err := readDatabaseLookupExcludedPeers(length, data, size) + assert.Equal([]common.Hash{}, excludedPeers) + assert.Equal(67, length) + assert.Equal(ERR_DATABASE_LOOKUP_NOT_ENOUGH_DATA, err) +} + +func TestReadDatabaseLookupExcludedPeersZeroSize(t *testing.T) { + assert := assert.New(t) + + length := 65 + data := make([]byte, length) + sizeData := []byte{0x0,0x0} + data = append(data, sizeData...) + data = append(data, 0x23) + + length, size, err := readDatabaseLookupSize(length, data) + + var expectedExcludedPeers []common.Hash + + length, excludedPeers, err := readDatabaseLookupExcludedPeers(length, data, size) + assert.Equal(expectedExcludedPeers, excludedPeers) + assert.Equal(67, length) + assert.Equal(nil, err) +} + +func TestReadDatabaseLookupExcludedPeersValidData(t *testing.T) { + assert := assert.New(t) + + length := 65 + data := make([]byte, length) + sizeData := []byte{0x0,0x3} + data = append(data, sizeData...) + + length, size, err := readDatabaseLookupSize(length, data) + + var expectedExcludedPeers []common.Hash + for i := range size { + peer := make([]byte, 32) + // random data: + peer[i + 1] = 0x43 + peer[i + 23] = 0x89 + expectedExcludedPeers = append(expectedExcludedPeers, common.Hash(peer)) + data = append(data, peer...) + } + + length, excludedPeers, err := readDatabaseLookupExcludedPeers(length, data, size) + assert.Equal(expectedExcludedPeers, excludedPeers) + assert.Equal(67 + 32 * size, length) + assert.Equal(nil, err) +} + +func TestReadDatabaseLookupReplyKeyTooLittleData(t *testing.T) { + assert := assert.New(t) + + length := 67 + data := make([]byte, length) + data = append(data, 0x2) + + length, replyKey, err := readDatabaseLookupReplyKey(length, data) + assert.Equal(67, length) + assert.Equal(session_key.SessionKey{}, replyKey) + assert.Equal(ERR_DATABASE_LOOKUP_NOT_ENOUGH_DATA, err) +} + +func TestReadDatabaseLookupReplyKeyValidData(t *testing.T) { + assert := assert.New(t) + + length := 67 + data := make([]byte, length) + expectedReplyKeyData := make([]byte, 32) + expectedReplyKeyData[3] = 0x31 + expectedReplyKey := session_key.SessionKey(expectedReplyKeyData) + data = append(data, expectedReplyKeyData...) + + length, replyKey, err := readDatabaseLookupReplyKey(length, data) + assert.Equal(67 + 32, length) + assert.Equal(expectedReplyKey, replyKey) + assert.Equal(nil, err) +} + +func TestReadDatabaseLookupTagsTooLittleData(t *testing.T) { + assert := assert.New(t) + + length := 99 + data := make([]byte, length) + + length, tags, err := readDatabaseLookupTags(length, data) + assert.Equal(99, length) + assert.Equal(0, tags) + assert.Equal(ERR_DATABASE_LOOKUP_NOT_ENOUGH_DATA, err) +} + +func TestReadDatabaseLookupTagsValidData(t *testing.T) { + assert := assert.New(t) + + length := 99 + data := make([]byte, length) + expected := 121 + data = append(data, byte(expected)) + + length, tags, err := readDatabaseLookupTags(length, data) + assert.Equal(100, length) + assert.Equal(expected, tags) + assert.Equal(nil, err) +} + +func TestReadDatabaseLookupReplyTagsTooLittleData(t *testing.T) { + assert := assert.New(t) + + length := 99 + data := make([]byte, length) + tags := 10 + data = append(data, byte(tags)) + data = append(data, 0x34) + + length, tags, err := readDatabaseLookupTags(length, data) + length, replyTags, err := readDatabaseLookupReplyTags(length, data, tags) + assert.Equal(100, length) + assert.Equal([]session_tag.SessionTag{}, replyTags) + assert.Equal(ERR_DATABASE_LOOKUP_NOT_ENOUGH_DATA, err) +} + +func TestReadDatabaseLookupReplyTagsZeroTags(t *testing.T) { + assert := assert.New(t) + + length := 99 + data := make([]byte, length) + tags := 0 + data = append(data, byte(tags)) + data = append(data, 0x23) + + length, tags, err := readDatabaseLookupTags(length, data) + + var expectedReplyTags []session_tag.SessionTag + + length, replyTags, err := readDatabaseLookupReplyTags(length, data, tags) + assert.Equal(expectedReplyTags, replyTags) + assert.Equal(100, length) + assert.Equal(nil, err) +} + +func TestReadDatabaseLookupReplyTagsValidData(t *testing.T) { + assert := assert.New(t) + + length := 99 + data := make([]byte, length) + tags := 10 + data = append(data, byte(tags)) + + length, tags, err := readDatabaseLookupTags(length, data) + + var expectedReplyTags []session_tag.SessionTag + for i := range tags { + tag := make([]byte, 32) + // random data: + tag[i + 1] = 0x43 + tag[i + 5] = 0x89 + expectedReplyTags = append(expectedReplyTags, session_tag.SessionTag(tag)) + data = append(data, tag...) + } + + length, replyTags, err := readDatabaseLookupReplyTags(length, data, tags) + assert.Equal(expectedReplyTags, replyTags) + assert.Equal(100 + 32 * tags, length) + assert.Equal(nil, err) +} + +func TestReadDatabaseLookupTooLittleData(t *testing.T) { + assert := assert.New(t) + + length := 67 + data := make([]byte, length) + expectedReplyKeyData := make([]byte, 32) + expectedReplyKeyData[3] = 0x31 + expectedReplyKey := session_key.SessionKey(expectedReplyKeyData) + data = append(data, expectedReplyKeyData...) + + databaseLookup, err := ReadDatabaseLookup(data) + assert.Equal(expectedReplyKey, databaseLookup.ReplyKey) + assert.Equal(ERR_DATABASE_LOOKUP_NOT_ENOUGH_DATA, err) +} + +func TestReadDatabaseLookupValidData(t *testing.T) { + assert := assert.New(t) + + data := make([]byte, 32) + for i := range 31 { + data[i] = 0x31 + } + expectedKey := common.Hash(data) + + from := make([]byte, 32) + from[14] = 0x69 + from[27] = 0x15 + data = append(data, from...) + expectedFrom := common.Hash(from) + + expectedFlags := byte(0x1) + data = append(data, expectedFlags) + + tunnelIDData := make([]byte, 4) + tunnelIDData[0] = 0xff + tunnelIDData[2] = 0xf2 + data = append(data, tunnelIDData...) + expectedTunnelID := [4]byte(tunnelIDData) + + sizeData := []byte{0x0,0xf} + data = append(data, sizeData...) + expectedSize := common.Integer(sizeData).Int() + + var expectedExcludedPeers []common.Hash + for i := range expectedSize { + peer := make([]byte, 32) + // random data: + peer[i + 5] = 0xdd + peer[i + 13] = 0x35 + expectedExcludedPeers = append(expectedExcludedPeers, common.Hash(peer)) + data = append(data, peer...) + } + + replyKeyData := make([]byte, 32) + replyKeyData[6] = 0x11 + replyKeyData[14] = 0x13 + data = append(data, replyKeyData...) + expectedReplyKey := session_key.SessionKey(replyKeyData) + + expectedTags := 15 + data = append(data, byte(expectedTags)) + + var expectedReplyTags []session_tag.SessionTag + for i := range expectedTags { + tag := make([]byte, 32) + // random data: + tag[i + 3] = 0x22 + tag[i + 13] = 0x11 + expectedReplyTags = append(expectedReplyTags, session_tag.SessionTag(tag)) + data = append(data, tag...) + } + databaseLookup, err := ReadDatabaseLookup(data) + assert.Equal(expectedKey, databaseLookup.Key) + assert.Equal(expectedFrom, databaseLookup.From) + assert.Equal(expectedFlags, databaseLookup.Flags) + assert.Equal(expectedTunnelID, databaseLookup.ReplyTunnelID) + assert.Equal(expectedSize, databaseLookup.Size) + assert.Equal(expectedExcludedPeers, databaseLookup.ExcludedPeers) + assert.Equal(expectedReplyKey, databaseLookup.ReplyKey) + assert.Equal(expectedTags, databaseLookup.Tags) + assert.Equal(expectedReplyTags, databaseLookup.ReplyTags) + assert.Equal(err, nil) +}