mirror of
https://github.com/go-i2p/go-i2p.git
synced 2025-07-06 22:38:30 -04:00
mapping completed
This commit is contained in:
@ -1,17 +1,44 @@
|
|||||||
package common
|
package common
|
||||||
|
|
||||||
|
/*
|
||||||
|
I2P Mapping
|
||||||
|
https://geti2p.net/en/docs/spec/common-structures#type_Mapping
|
||||||
|
Accurate for version 0.9.24
|
||||||
|
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| size |key_string (len + data) | = |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| val_string (len + data) | ; | ...
|
||||||
|
+----+----+----+----+----+----+----+
|
||||||
|
size :: Integer
|
||||||
|
length -> 2 bytes
|
||||||
|
Total number of bytes that follow
|
||||||
|
|
||||||
|
key_string :: String
|
||||||
|
A string (one byte length followed by UTF-8 encoded characters)
|
||||||
|
|
||||||
|
= :: A single byte containing '='
|
||||||
|
|
||||||
|
val_string :: String
|
||||||
|
A string (one byte length followed by UTF-8 encoded characters)
|
||||||
|
|
||||||
|
; :: A single byte containing ';'
|
||||||
|
*/
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
"sort"
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Mapping []byte
|
type Mapping []byte
|
||||||
|
|
||||||
|
// Parsed key-values pairs inside a Mapping.
|
||||||
type MappingValues [][2]String
|
type MappingValues [][2]String
|
||||||
|
|
||||||
//
|
//
|
||||||
// Returns the values contained in a Mapping in
|
// Returns the values contained in a Mapping in the form of a MappingValues.
|
||||||
// the form of a MappingValues.
|
|
||||||
//
|
//
|
||||||
func (mapping Mapping) Values() (map_values MappingValues, errs []error) {
|
func (mapping Mapping) Values() (map_values MappingValues, errs []error) {
|
||||||
var str String
|
var str String
|
||||||
@ -19,11 +46,24 @@ func (mapping Mapping) Values() (map_values MappingValues, errs []error) {
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
length := Integer(remainder[:2])
|
length := Integer(remainder[:2])
|
||||||
|
inferred_length := length + 2
|
||||||
remainder = remainder[2:]
|
remainder = remainder[2:]
|
||||||
mapping_len := len(mapping)
|
mapping_len := len(mapping)
|
||||||
if mapping_len > length+2 {
|
if mapping_len > inferred_length {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"mappnig_bytes_length": mapping_len,
|
||||||
|
"mapping_length_field": length,
|
||||||
|
"expected_bytes_length": inferred_length,
|
||||||
|
"reason": "data longer than expected",
|
||||||
|
}).Warn("mapping format warning")
|
||||||
errs = append(errs, errors.New("warning parsing mapping: data exists beyond length of mapping"))
|
errs = append(errs, errors.New("warning parsing mapping: data exists beyond length of mapping"))
|
||||||
} else if length+2 > mapping_len {
|
} else if inferred_length > mapping_len {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"mappnig_bytes_length": mapping_len,
|
||||||
|
"mapping_length_field": length,
|
||||||
|
"expected_bytes_length": inferred_length,
|
||||||
|
"reason": "data shorter than expected",
|
||||||
|
}).Warn("mapping format warning")
|
||||||
errs = append(errs, errors.New("warning parsing mapping: mapping length exceeds provided data"))
|
errs = append(errs, errors.New("warning parsing mapping: mapping length exceeds provided data"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,6 +79,9 @@ func (mapping Mapping) Values() (map_values MappingValues, errs []error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !beginsWith(remainder, 0x3d) {
|
if !beginsWith(remainder, 0x3d) {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"reason": "expected =",
|
||||||
|
}).Warn("mapping format violation")
|
||||||
errs = append(errs, errors.New("mapping format violation, expected ="))
|
errs = append(errs, errors.New("mapping format violation, expected ="))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -55,13 +98,15 @@ func (mapping Mapping) Values() (map_values MappingValues, errs []error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !beginsWith(remainder, 0x3b) {
|
if !beginsWith(remainder, 0x3b) {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"reason": "expected ;",
|
||||||
|
}).Warn("mapping format violation")
|
||||||
errs = append(errs, errors.New("mapping format violation, expected ;"))
|
errs = append(errs, errors.New("mapping format violation, expected ;"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
remainder = remainder[1:]
|
remainder = remainder[1:]
|
||||||
|
|
||||||
// Append the key-value pair and break
|
// Append the key-value pair and break if there is no more data to read
|
||||||
// if there is no more data to read
|
|
||||||
map_values = append(map_values, [2]String{key_str, val_str})
|
map_values = append(map_values, [2]String{key_str, val_str})
|
||||||
if len(remainder) == 0 {
|
if len(remainder) == 0 {
|
||||||
break
|
break
|
||||||
@ -71,7 +116,7 @@ func (mapping Mapping) Values() (map_values MappingValues, errs []error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Returns true if two keys in a mapping are identical
|
// Return true if two keys in a mapping are identical.
|
||||||
//
|
//
|
||||||
func (mapping Mapping) HasDuplicateKeys() bool {
|
func (mapping Mapping) HasDuplicateKeys() bool {
|
||||||
seen_values := make(map[string]bool)
|
seen_values := make(map[string]bool)
|
||||||
@ -88,12 +133,10 @@ func (mapping Mapping) HasDuplicateKeys() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// ValuesToMapping takes a MappingValue struct and
|
// Convert a MappingValue struct to a Mapping. The values are first
|
||||||
// returns a Mapping. The values are sorted in the
|
// sorted in the order defined in mappingOrder.
|
||||||
// order defined in mappingOrder.
|
|
||||||
//
|
//
|
||||||
func ValuesToMapping(values MappingValues) Mapping {
|
func ValuesToMapping(values MappingValues) (mapping Mapping) {
|
||||||
var mapping Mapping
|
|
||||||
mappingOrder(values)
|
mappingOrder(values)
|
||||||
for _, kv_pair := range values {
|
for _, kv_pair := range values {
|
||||||
key_string := kv_pair[0]
|
key_string := kv_pair[0]
|
||||||
@ -106,12 +149,11 @@ func ValuesToMapping(values MappingValues) Mapping {
|
|||||||
len_bytes := make([]byte, 2)
|
len_bytes := make([]byte, 2)
|
||||||
binary.BigEndian.PutUint16(len_bytes, uint16(map_len))
|
binary.BigEndian.PutUint16(len_bytes, uint16(map_len))
|
||||||
mapping = append(len_bytes, mapping...)
|
mapping = append(len_bytes, mapping...)
|
||||||
return mapping
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// This function takes a map of unformatted strings
|
// Convert a Go map of unformatted strings to a sorted Mapping.
|
||||||
// and returns a Mapping.
|
|
||||||
//
|
//
|
||||||
func GoMapToMapping(gomap map[string]string) (mapping Mapping, err error) {
|
func GoMapToMapping(gomap map[string]string) (mapping Mapping, err error) {
|
||||||
map_vals := MappingValues{}
|
map_vals := MappingValues{}
|
||||||
@ -156,10 +198,8 @@ func (set ByKey) Less(i, j int) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// I2P Mappings require consistent order for
|
// I2P Mappings require consistent order for for cryptographic signing, and sorting
|
||||||
// for cryptographic signing, and sorting
|
// by keys. When new Mappings are created, they are stable sorted first by values
|
||||||
// by keys. When new Mappings are created,
|
|
||||||
// they are stable sorted first by values
|
|
||||||
// than by keys to ensure a consistent order.
|
// than by keys to ensure a consistent order.
|
||||||
//
|
//
|
||||||
func mappingOrder(values MappingValues) {
|
func mappingOrder(values MappingValues) {
|
||||||
@ -167,10 +207,17 @@ func mappingOrder(values MappingValues) {
|
|||||||
sort.Stable(ByKey(values))
|
sort.Stable(ByKey(values))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Check if the string parsing error indicates that the Mapping
|
||||||
|
// should no longer be parsed.
|
||||||
|
//
|
||||||
func stopValueRead(err error) bool {
|
func stopValueRead(err error) bool {
|
||||||
return err.Error() == "error parsing string: zero length"
|
return err.Error() == "error parsing string: zero length"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Determine if the first byte in a slice of bytes is the provided byte.
|
||||||
|
//
|
||||||
func beginsWith(bytes []byte, chr byte) bool {
|
func beginsWith(bytes []byte, chr byte) bool {
|
||||||
return len(bytes) != 0 &&
|
return len(bytes) != 0 &&
|
||||||
bytes[0] == chr
|
bytes[0] == chr
|
||||||
|
Reference in New Issue
Block a user