gnunet-go

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

msg_dht_p2p.go (20798B)


      1 // This file is part of gnunet-go, a GNUnet-implementation in Golang.
      2 // Copyright (C) 2019-2022 Bernd Fix  >Y<
      3 //
      4 // gnunet-go is free software: you can redistribute it and/or modify it
      5 // under the terms of the GNU Affero General Public License as published
      6 // by the Free Software Foundation, either version 3 of the License,
      7 // or (at your option) any later version.
      8 //
      9 // gnunet-go is distributed in the hope that it will be useful, but
     10 // WITHOUT ANY WARRANTY; without even the implied warranty of
     11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12 // Affero General Public License for more details.
     13 //
     14 // You should have received a copy of the GNU Affero General Public License
     15 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
     16 //
     17 // SPDX-License-Identifier: AGPL3.0-or-later
     18 
     19 package message
     20 
     21 import (
     22 	"bytes"
     23 	"crypto/sha512"
     24 	"encoding/binary"
     25 	"errors"
     26 	"fmt"
     27 	"gnunet/config"
     28 	"gnunet/crypto"
     29 	"gnunet/enums"
     30 	"gnunet/service/dht/blocks"
     31 	"gnunet/service/dht/path"
     32 	"gnunet/util"
     33 	"strings"
     34 	"time"
     35 
     36 	"github.com/bfix/gospel/crypto/ed25519"
     37 	"github.com/bfix/gospel/logger"
     38 )
     39 
     40 //======================================================================
     41 // DHT-P2P is a next-generation implementation of the R5N DHT.
     42 //======================================================================
     43 
     44 //----------------------------------------------------------------------
     45 // DHT-P2P-GET messages are used to request information from other
     46 // peers in the DHT.
     47 //----------------------------------------------------------------------
     48 
     49 // DHTP2PGetMsg wire layout
     50 type DHTP2PGetMsg struct {
     51 	MsgHeader
     52 	BType      enums.BlockType    `order:"big"`   // content type of the payload
     53 	Flags      uint16             `order:"big"`   // processing flags
     54 	HopCount   uint16             `order:"big"`   // number of hops so far
     55 	ReplLevel  uint16             `order:"big"`   // Replication level
     56 	RfSize     uint16             `order:"big"`   // size of result filter
     57 	PeerFilter *blocks.PeerFilter ``              // peer filter to prevent loops
     58 	Query      *crypto.HashCode   ``              // query hash
     59 	ResFilter  []byte             `size:"RfSize"` // result filter
     60 	XQuery     []byte             `size:"*"`      // extended query
     61 }
     62 
     63 // NewDHTP2PGetMsg creates an empty DHT-P2P-Get message
     64 func NewDHTP2PGetMsg() *DHTP2PGetMsg {
     65 	return &DHTP2PGetMsg{
     66 		MsgHeader:  MsgHeader{208, enums.MSG_DHT_P2P_GET},
     67 		BType:      enums.BLOCK_TYPE_ANY,    // no block type defined
     68 		Flags:      0,                       // no flags defined
     69 		HopCount:   0,                       // no hops
     70 		ReplLevel:  0,                       // no replication level defined
     71 		RfSize:     0,                       // no result filter
     72 		PeerFilter: blocks.NewPeerFilter(),  // allocate bloom filter
     73 		Query:      crypto.NewHashCode(nil), // empty Query hash
     74 		ResFilter:  nil,                     // empty result filter
     75 		XQuery:     nil,                     // empty XQuery
     76 	}
     77 }
     78 
     79 // Init called after unmarshalling a message to setup internal state
     80 func (m *DHTP2PGetMsg) Init() (err error) { return nil }
     81 
     82 // String returns a human-readable representation of the message.
     83 func (m *DHTP2PGetMsg) String() string {
     84 	return fmt.Sprintf("DHTP2PGetMsg{btype=%s,hops=%d,flags=%s}",
     85 		m.BType, m.HopCount, DHTFlags(m.Flags))
     86 }
     87 
     88 // Update message (forwarding)
     89 func (m *DHTP2PGetMsg) Update(pf *blocks.PeerFilter, rf blocks.ResultFilter, hop uint16) *DHTP2PGetMsg {
     90 	buf := rf.Bytes()
     91 	ns := uint16(len(buf))
     92 	return &DHTP2PGetMsg{
     93 		MsgHeader:  MsgHeader{m.MsgSize - m.RfSize + ns, enums.MSG_DHT_P2P_GET},
     94 		BType:      m.BType,
     95 		Flags:      m.Flags,
     96 		HopCount:   hop,
     97 		ReplLevel:  m.ReplLevel,
     98 		RfSize:     ns,
     99 		PeerFilter: pf.Clone(),
    100 		Query:      m.Query,
    101 		ResFilter:  buf,
    102 		XQuery:     util.Clone(m.XQuery),
    103 	}
    104 }
    105 
    106 //----------------------------------------------------------------------
    107 // DHT-P2P-PUT messages are used by other peers in the DHT to
    108 // request block storage.
    109 //----------------------------------------------------------------------
    110 
    111 // DHTP2PPutMsg wire layout
    112 type DHTP2PPutMsg struct {
    113 	MsgHeader
    114 	BType       enums.BlockType     `order:"big"`    // block type
    115 	Flags       uint16              `order:"big"`    // processing flags
    116 	HopCount    uint16              `order:"big"`    // message hops
    117 	ReplLvl     uint16              `order:"big"`    // replication level
    118 	PathL       uint16              `order:"big"`    // path length
    119 	Expire      util.AbsoluteTime   ``               // expiration date
    120 	PeerFilter  *blocks.PeerFilter  ``               // peer bloomfilter
    121 	Key         *crypto.HashCode    ``               // query key to block
    122 	TruncOrigin *util.PeerID        `opt:"(IsUsed)"` // truncated origin (if TRUNCATED flag set)
    123 	PutPath     []*path.Entry       `size:"PathL"`   // PUT path
    124 	LastSig     *util.PeerSignature `opt:"(IsUsed)"` // signature of last hop (if RECORD_ROUTE flag is set)
    125 	Block       []byte              `size:"*"`       // block data
    126 }
    127 
    128 // NewDHTP2PPutMsg creates an empty new DHTP2PPutMsg
    129 func NewDHTP2PPutMsg(block blocks.Block) *DHTP2PPutMsg {
    130 	// create empty message
    131 	msg := &DHTP2PPutMsg{
    132 		MsgHeader:   MsgHeader{216, enums.MSG_DHT_P2P_PUT},
    133 		BType:       enums.BLOCK_TYPE_ANY,     // block type
    134 		Flags:       0,                        // processing flags
    135 		HopCount:    0,                        // message hops
    136 		ReplLvl:     0,                        // replication level
    137 		PathL:       0,                        // no PUT path
    138 		Expire:      util.AbsoluteTimeNever(), // expiration date
    139 		PeerFilter:  blocks.NewPeerFilter(),   // peer bloom filter
    140 		Key:         crypto.NewHashCode(nil),  // query key
    141 		TruncOrigin: nil,                      // no truncated path
    142 		PutPath:     make([]*path.Entry, 0),   // empty PUT path
    143 		LastSig:     nil,                      // no signature from last hop
    144 		Block:       nil,                      // no block data
    145 	}
    146 	// initialize with block if available
    147 	if block != nil {
    148 		msg.BType = block.Type()
    149 		msg.HopCount = 0
    150 		msg.PeerFilter = blocks.NewPeerFilter()
    151 		msg.ReplLvl = uint16(config.Cfg.GNS.ReplLevel)
    152 		msg.Expire = block.Expire()
    153 		msg.Block = block.Bytes()
    154 		msg.TruncOrigin = nil
    155 		msg.PutPath = nil
    156 		msg.LastSig = nil
    157 		msg.MsgSize += uint16(len(msg.Block))
    158 	}
    159 	return msg
    160 }
    161 
    162 // IsUsed returns true if an optional field is used
    163 func (m *DHTP2PPutMsg) IsUsed(field string) bool {
    164 	switch field {
    165 	case "Origin":
    166 		return m.Flags&enums.DHT_RO_TRUNCATED != 0
    167 	case "LastSig":
    168 		return m.Flags&enums.DHT_RO_RECORD_ROUTE != 0
    169 	}
    170 	return false
    171 }
    172 
    173 // Init called after unmarshalling a message to setup internal state
    174 func (m *DHTP2PPutMsg) Init() (err error) {
    175 	return nil
    176 }
    177 
    178 //----------------------------------------------------------------------
    179 
    180 // Update message (forwarding)
    181 func (m *DHTP2PPutMsg) Update(p *path.Path, pf *blocks.PeerFilter, hop uint16) *DHTP2PPutMsg {
    182 	msg := NewDHTP2PPutMsg(nil)
    183 	msg.Flags = m.Flags
    184 	msg.HopCount = hop
    185 	msg.PathL = p.NumList
    186 	msg.Expire = m.Expire
    187 	msg.PeerFilter = pf
    188 	msg.Key = m.Key.Clone()
    189 	msg.TruncOrigin = p.TruncOrigin
    190 	msg.PutPath = util.Clone(p.List)
    191 	msg.LastSig = p.LastSig
    192 	msg.Block = util.Clone(m.Block)
    193 	msg.SetPath(p)
    194 	return msg
    195 }
    196 
    197 //----------------------------------------------------------------------
    198 // Path handling (get/set path in message)
    199 //----------------------------------------------------------------------
    200 
    201 // Path returns the current path from message
    202 func (m *DHTP2PPutMsg) Path(sender *util.PeerID) *path.Path {
    203 	// create a "real" path list from message data
    204 	pth := path.NewPath(crypto.Hash(m.Block), m.Expire)
    205 	pth.Flags = m.Flags
    206 
    207 	// return empty path if recording is switched off
    208 	if pth.Flags&enums.DHT_RO_RECORD_ROUTE == 0 {
    209 		return pth
    210 	}
    211 
    212 	// handle truncate origin
    213 	if pth.Flags&enums.DHT_RO_TRUNCATED == 1 {
    214 		if m.TruncOrigin == nil {
    215 			logger.Printf(logger.WARN, "[path] truncated but no origin - flag reset")
    216 			pth.Flags &^= enums.DHT_RO_TRUNCATED
    217 		} else {
    218 			pth.TruncOrigin = m.TruncOrigin
    219 		}
    220 	}
    221 
    222 	// copy path elements
    223 	pth.List = util.Clone(m.PutPath)
    224 	pth.NumList = uint16(len(pth.List))
    225 
    226 	// handle last hop signature
    227 	if m.LastSig == nil {
    228 		logger.Printf(logger.WARN, "[path]  - last hop signature missing - path reset")
    229 		return path.NewPath(crypto.Hash(m.Block), m.Expire)
    230 	}
    231 	pth.LastSig = m.LastSig
    232 	pth.LastHop = sender
    233 	return pth
    234 }
    235 
    236 // Set path in message; corrects the message size accordingly
    237 func (m *DHTP2PPutMsg) SetPath(p *path.Path) {
    238 
    239 	// return if recording is switched off (don't touch path)
    240 	if m.Flags&enums.DHT_RO_RECORD_ROUTE == 0 {
    241 		return
    242 	}
    243 	// compute old path size
    244 	var pes uint
    245 	if len(m.PutPath) > 0 {
    246 		pes = m.PutPath[0].Size()
    247 	}
    248 	oldSize := uint(len(m.PutPath)) * pes
    249 	if m.TruncOrigin != nil {
    250 		oldSize += m.TruncOrigin.Size()
    251 	}
    252 	if m.LastSig != nil {
    253 		oldSize += m.LastSig.Size()
    254 	}
    255 	// if no new path is defined,...
    256 	if p == nil {
    257 		// ... remove existing path
    258 		m.TruncOrigin = nil
    259 		m.PutPath = nil
    260 		m.LastSig = nil
    261 		m.PathL = 0
    262 		m.Flags &^= enums.DHT_RO_TRUNCATED
    263 		m.MsgSize -= uint16(oldSize)
    264 		return
    265 	}
    266 	// adjust message size
    267 	m.MsgSize += uint16(p.Size() - oldSize)
    268 
    269 	// transfer path data
    270 	if p.TruncOrigin != nil {
    271 		// truncated path
    272 		m.Flags |= enums.DHT_RO_TRUNCATED
    273 		m.TruncOrigin = p.TruncOrigin
    274 	}
    275 	m.PutPath = util.Clone(p.List)
    276 	m.PathL = uint16(len(m.PutPath))
    277 	if p.LastSig != nil {
    278 		m.LastSig = p.LastSig
    279 	}
    280 }
    281 
    282 //----------------------------------------------------------------------
    283 
    284 // String returns a human-readable representation of the message.
    285 func (m *DHTP2PPutMsg) String() string {
    286 	return fmt.Sprintf("DHTP2PPutMsg{btype=%s,hops=%d,flags=%s}",
    287 		m.BType, m.HopCount, DHTFlags(m.Flags))
    288 }
    289 
    290 //----------------------------------------------------------------------
    291 // DHT-P2P-RESULT messages are used to answer peer requests for
    292 // bock retrieval.
    293 //----------------------------------------------------------------------
    294 
    295 // DHTP2PResultMsg wire layout
    296 type DHTP2PResultMsg struct {
    297 	MsgHeader
    298 	BType       enums.BlockType     `order:"big"`                // Block type of result
    299 	Reserved    uint16              `order:"big"`                // Reserved
    300 	Flags       uint16              `order:"big"`                // Message flags
    301 	PutPathL    uint16              `order:"big"`                // size of PUTPATH field
    302 	GetPathL    uint16              `order:"big"`                // size of GETPATH field
    303 	Expire      util.AbsoluteTime   ``                           // expiration date
    304 	Query       *crypto.HashCode    ``                           // Query key for block
    305 	TruncOrigin *util.PeerID        `opt:"(IsUsed)"`             // truncated origin (if TRUNCATED flag set)
    306 	PathList    []*path.Entry       `size:"(NumPath)"`           // PATH
    307 	LastSig     *util.PeerSignature `opt:"(IsUsed)" init:"Init"` // signature of last hop (if RECORD_ROUTE flag is set)
    308 	Block       []byte              `size:"*"`                   // block data
    309 }
    310 
    311 // NewDHTP2PResultMsg creates a new empty DHTP2PResultMsg
    312 func NewDHTP2PResultMsg() *DHTP2PResultMsg {
    313 	return &DHTP2PResultMsg{
    314 		MsgHeader:   MsgHeader{88, enums.MSG_DHT_P2P_RESULT},
    315 		BType:       enums.BLOCK_TYPE_ANY, // type of returned block
    316 		TruncOrigin: nil,                  // no truncated origin
    317 		PutPathL:    0,                    // empty putpath
    318 		GetPathL:    0,                    // empty getpath
    319 		PathList:    nil,                  // empty path list (put+get)
    320 		LastSig:     nil,                  // no recorded route
    321 		Block:       nil,                  // empty block
    322 	}
    323 }
    324 
    325 // IsUsed returns if an optional field is present
    326 func (m *DHTP2PResultMsg) IsUsed(field string) bool {
    327 	switch field {
    328 	case "Origin":
    329 		return m.Flags&enums.DHT_RO_TRUNCATED != 0
    330 	case "LastSig":
    331 		return m.Flags&enums.DHT_RO_RECORD_ROUTE != 0
    332 	}
    333 	return false
    334 }
    335 
    336 // NumPath returns the total number of entries in path
    337 func (m *DHTP2PResultMsg) NumPath(field string) uint {
    338 	return uint(m.GetPathL + m.PutPathL)
    339 }
    340 
    341 // Init called after unmarshalling a message to setup internal state
    342 func (m *DHTP2PResultMsg) Init() (err error) {
    343 	return nil
    344 }
    345 
    346 //----------------------------------------------------------------------
    347 // Path handling (get/set path in message)
    348 //----------------------------------------------------------------------
    349 
    350 // Path returns the current path from message
    351 func (m *DHTP2PResultMsg) Path(sender *util.PeerID) *path.Path {
    352 	// create a "real" path list from message data
    353 	pth := path.NewPath(crypto.Hash(m.Block), m.Expire)
    354 	pth.Flags = m.Flags
    355 
    356 	// return empty path if recording is switched off
    357 	if pth.Flags&enums.DHT_RO_RECORD_ROUTE == 0 {
    358 		return pth
    359 	}
    360 	// handle truncate origin
    361 	if pth.Flags&enums.DHT_RO_TRUNCATED == 1 {
    362 		if pth.TruncOrigin == nil {
    363 			logger.Printf(logger.WARN, "[path] truncated but no origin - flag reset")
    364 			pth.Flags &^= enums.DHT_RO_TRUNCATED
    365 		} else {
    366 			pth.TruncOrigin = m.TruncOrigin
    367 		}
    368 	}
    369 
    370 	// copy path elements
    371 	pth.List = util.Clone(m.PathList)
    372 	pth.NumList = uint16(len(pth.List))
    373 
    374 	// check consistent length values; adjust if mismatched
    375 	if m.GetPathL+m.PutPathL != pth.NumList {
    376 		logger.Printf(logger.WARN, "[path] Inconsistent PATH length -- adjusting...")
    377 		if sp := pth.NumList - m.PutPathL; sp > 0 {
    378 			pth.SplitPos = sp
    379 		} else {
    380 			pth.SplitPos = 0
    381 		}
    382 	} else {
    383 		pth.SplitPos = pth.NumList - m.PutPathL
    384 	}
    385 	// handle last hop signature
    386 	if m.LastSig == nil {
    387 		logger.Printf(logger.WARN, "[path]  - last hop signature missing - path reset")
    388 		return path.NewPath(crypto.Hash(m.Block), m.Expire)
    389 	}
    390 	pth.LastSig = m.LastSig
    391 	pth.LastHop = sender
    392 	return pth
    393 }
    394 
    395 // Set path in message; corrects the message size accordingly
    396 func (m *DHTP2PResultMsg) SetPath(p *path.Path) {
    397 
    398 	// return if recording is switched off (don't touch path)
    399 	if m.Flags&enums.DHT_RO_RECORD_ROUTE == 0 {
    400 		return
    401 	}
    402 	// compute old path size
    403 	var pes uint
    404 	if len(m.PathList) > 0 {
    405 		pes = m.PathList[0].Size()
    406 	}
    407 	oldSize := uint(len(m.PathList)) * pes
    408 	if m.TruncOrigin != nil {
    409 		oldSize += m.TruncOrigin.Size()
    410 	}
    411 	if m.LastSig != nil {
    412 		oldSize += m.LastSig.Size()
    413 	}
    414 	// if no new path is defined,...
    415 	if p == nil {
    416 		// ... remove existing path
    417 		m.TruncOrigin = nil
    418 		m.PathList = make([]*path.Entry, 0)
    419 		m.LastSig = nil
    420 		m.GetPathL = 0
    421 		m.PutPathL = 0
    422 		m.Flags &^= enums.DHT_RO_TRUNCATED
    423 		m.MsgSize -= uint16(oldSize)
    424 		return
    425 	}
    426 	// adjust message size
    427 	m.MsgSize += uint16(p.Size() - oldSize)
    428 
    429 	// transfer path data
    430 	if p.TruncOrigin != nil {
    431 		// truncated path
    432 		m.Flags |= enums.DHT_RO_TRUNCATED
    433 		m.TruncOrigin = p.TruncOrigin
    434 	}
    435 	m.PathList = util.Clone(p.List)
    436 	m.PutPathL = p.SplitPos
    437 	m.GetPathL = p.NumList - p.SplitPos
    438 	if p.LastSig != nil {
    439 		m.LastSig = p.LastSig
    440 	}
    441 }
    442 
    443 //----------------------------------------------------------------------
    444 
    445 // Update message (forwarding)
    446 func (m *DHTP2PResultMsg) Update(pth *path.Path) *DHTP2PResultMsg {
    447 	// clone old message
    448 	msg := &DHTP2PResultMsg{
    449 		MsgHeader:   MsgHeader{m.MsgSize, m.MsgType},
    450 		BType:       m.BType,
    451 		Flags:       m.Flags,
    452 		PutPathL:    m.PutPathL,
    453 		GetPathL:    m.GetPathL,
    454 		Expire:      m.Expire,
    455 		Query:       m.Query.Clone(),
    456 		TruncOrigin: m.TruncOrigin,
    457 		PathList:    util.Clone(m.PathList),
    458 		LastSig:     m.LastSig,
    459 		Block:       util.Clone(m.Block),
    460 	}
    461 	// set new path
    462 	msg.SetPath(pth)
    463 	return msg
    464 }
    465 
    466 //----------------------------------------------------------------------
    467 
    468 // String returns a human-readable representation of the message.
    469 func (m *DHTP2PResultMsg) String() string {
    470 	return fmt.Sprintf("DHTP2PResultMsg{btype=%s,putl=%d,getl=%d,flags=%s}",
    471 		m.BType, m.PutPathL, m.GetPathL, DHTFlags(m.Flags))
    472 }
    473 
    474 //----------------------------------------------------------------------
    475 // DHT-P2P-HELLO
    476 //
    477 // A DHT-P2P-HELLO message is used to exchange information about transports
    478 // with other DHT nodes. This struct is always followed by the actual
    479 // network addresses of type "HelloAddress"
    480 //----------------------------------------------------------------------
    481 
    482 // DHTP2PHelloMsg is a message send by peers to announce their presence
    483 type DHTP2PHelloMsg struct {
    484 	MsgHeader
    485 	Reserved  uint16              `order:"big"` // Reserved for further use
    486 	NumAddr   uint16              `order:"big"` // Number of addresses in list
    487 	Signature *util.PeerSignature `init:"Init"` // Signature
    488 	Expire    util.AbsoluteTime   ``            // expiration time
    489 	AddrList  []byte              `size:"*"`    // List of end-point addresses (HelloAddress)
    490 
    491 	// transient state
    492 	addresses []*util.Address // list of converted addresses
    493 }
    494 
    495 // NewHelloMsgDHT creates an empty DHT_P2P_HELLO message.
    496 func NewDHTP2PHelloMsg() *DHTP2PHelloMsg {
    497 	// return empty HelloMessage with set expire date
    498 	t := util.NewAbsoluteTime(time.Now().Add(HelloAddressExpiration))
    499 	exp := util.NewAbsoluteTimeEpoch(t.Epoch())
    500 
    501 	return &DHTP2PHelloMsg{
    502 		MsgHeader: MsgHeader{80, enums.MSG_DHT_P2P_HELLO},
    503 		Reserved:  0,                          // not used here
    504 		NumAddr:   0,                          // start with empty address list
    505 		Signature: util.NewPeerSignature(nil), // signature
    506 		Expire:    exp,                        // default expiration
    507 		AddrList:  make([]byte, 0),            // list of addresses
    508 	}
    509 }
    510 
    511 // Init called after unmarshalling a message to setup internal state
    512 func (m *DHTP2PHelloMsg) Init() (err error) {
    513 	if m.addresses != nil {
    514 		return
    515 	}
    516 	m.addresses = make([]*util.Address, 0)
    517 	var addr *util.Address
    518 	var as string
    519 	num, pos := 0, 0
    520 	for {
    521 		// parse address string from stream
    522 		if as, pos = util.ReadCString(m.AddrList, pos); pos == -1 {
    523 			break
    524 		}
    525 		if addr, err = util.ParseAddress(as); err != nil {
    526 			return
    527 		}
    528 		addr.Expire = m.Expire
    529 		m.addresses = append(m.addresses, addr)
    530 		num++
    531 	}
    532 	// check numbers
    533 	if num != int(m.NumAddr) {
    534 		err = errors.New("number of addresses does not match")
    535 	}
    536 	return
    537 }
    538 
    539 // Addresses returns the list of HelloAddress
    540 func (m *DHTP2PHelloMsg) Addresses() (list []*util.Address, err error) {
    541 	if m.addresses == nil {
    542 		err = errors.New("no addresses available")
    543 		return
    544 	}
    545 	return m.addresses, nil
    546 }
    547 
    548 // SetAddresses adds addresses to the HELLO message.
    549 func (m *DHTP2PHelloMsg) SetAddresses(list []*util.Address) {
    550 	// write addresses as blob and track earliest expiration
    551 	t := util.NewAbsoluteTime(time.Now().Add(HelloAddressExpiration))
    552 	exp := util.NewAbsoluteTimeEpoch(t.Epoch())
    553 	wrt := new(bytes.Buffer)
    554 	for _, addr := range list {
    555 		// check if address expires before current expire
    556 		if exp.Compare(addr.Expire) > 0 {
    557 			exp = addr.Expire
    558 		}
    559 		n, _ := wrt.Write([]byte(addr.URI()))
    560 		wrt.WriteByte(0)
    561 		m.MsgSize += uint16(n + 1)
    562 	}
    563 	m.AddrList = wrt.Bytes()
    564 	m.Expire = exp
    565 	m.NumAddr = uint16(len(list))
    566 }
    567 
    568 // String returns a human-readable representation of the message.
    569 func (m *DHTP2PHelloMsg) String() string {
    570 	return fmt.Sprintf("DHTP2PHelloMsg{expire:%s,addrs=[%d]}", m.Expire, m.NumAddr)
    571 }
    572 
    573 // Verify the message signature
    574 func (m *DHTP2PHelloMsg) Verify(peer *util.PeerID) (bool, error) {
    575 	// assemble signed data and public key
    576 	sd := m.SignedData()
    577 	pub := ed25519.NewPublicKeyFromBytes(peer.Data)
    578 	sig, err := ed25519.NewEdSignatureFromBytes(m.Signature.Data)
    579 	if err != nil {
    580 		return false, err
    581 	}
    582 	return pub.EdVerify(sd, sig)
    583 }
    584 
    585 // SetSignature stores a signature in the the HELLO block
    586 func (m *DHTP2PHelloMsg) SetSignature(sig *util.PeerSignature) error {
    587 	m.Signature = sig
    588 	return nil
    589 }
    590 
    591 // SignedData assembles a data block for sign and verify operations.
    592 func (m *DHTP2PHelloMsg) SignedData() []byte {
    593 	// hash address block
    594 	hAddr := sha512.Sum512(m.AddrList)
    595 	var size uint32 = 80
    596 	purpose := uint32(enums.SIG_HELLO)
    597 
    598 	// assemble signed data
    599 	buf := new(bytes.Buffer)
    600 	var n int
    601 	err := binary.Write(buf, binary.BigEndian, size)
    602 	if err == nil {
    603 		if err = binary.Write(buf, binary.BigEndian, purpose); err == nil {
    604 			if err = binary.Write(buf, binary.BigEndian, m.Expire); err == nil {
    605 				if n, err = buf.Write(hAddr[:]); err == nil {
    606 					if n != len(hAddr[:]) {
    607 						err = errors.New("write failed")
    608 					}
    609 				}
    610 			}
    611 		}
    612 	}
    613 	if err != nil {
    614 		logger.Printf(logger.ERROR, "[DHTP2PHelloMsg.SignedData] failed: %s", err.Error())
    615 	}
    616 	return buf.Bytes()
    617 }
    618 
    619 //----------------------------------------------------------------------
    620 // Helper functions
    621 //----------------------------------------------------------------------
    622 
    623 // get human-readable flags
    624 func DHTFlags(flags uint16) string {
    625 	var list []string
    626 	if flags&enums.DHT_RO_DEMULTIPLEX_EVERYWHERE != 0 {
    627 		list = append(list, "DEMUX")
    628 	}
    629 	if flags&enums.DHT_RO_RECORD_ROUTE != 0 {
    630 		list = append(list, "ROUTE")
    631 	}
    632 	if flags&enums.DHT_RO_FIND_APPROXIMATE != 0 {
    633 		list = append(list, "APPROX")
    634 	}
    635 	if flags&enums.DHT_RO_TRUNCATED != 0 {
    636 		list = append(list, "TRUNC")
    637 	}
    638 	s := strings.Join(list, "|")
    639 	return "<" + s + ">"
    640 }