diff --git a/Bootstrap.go b/Bootstrap.go index be6f0e4..4f9a655 100644 --- a/Bootstrap.go +++ b/Bootstrap.go @@ -95,14 +95,7 @@ func parseAddress(Address string) (remote *net.UDPAddr, err error) { // contact tries to contact the root peer on all networks func (peer *rootPeer) contact() { - packets, err := msgEncodeAnnouncement(true, true, nil, nil, nil) - if len(packets) == 0 || err != nil { - return - } - - for _, address := range peer.addresses { - sendAllNetworks(peer.publicKey, &PacketRaw{Command: CommandAnnouncement, Payload: packets[0]}, address) - } + contactArbitraryPeer(peer.publicKey, peer.addresses) } // bootstrap connects to the initial set of peers. It will also start the routine for ongoing sending of multicast/broadcast messages. @@ -198,8 +191,9 @@ func autoMulticastBroadcast() { } } -func contactArbitraryPeer(publicKey *btcec.PublicKey, ip net.IP, port uint16) { - // check first if peer is already in the list! +// contactArbitraryPeer reaches for the first time to an arbitrary peer. +// It does not contact the peer if it is in the peer list, which means that a connection is already established. +func contactArbitraryPeer(publicKey *btcec.PublicKey, addresses []*net.UDPAddr) { if peer := PeerlistLookup(publicKey); peer != nil { return } @@ -209,5 +203,15 @@ func contactArbitraryPeer(publicKey *btcec.PublicKey, ip net.IP, port uint16) { return } - sendAllNetworks(publicKey, &PacketRaw{Command: CommandAnnouncement, Payload: packets[0]}, &net.UDPAddr{IP: ip, Port: int(port)}) + for _, address := range addresses { + sendAllNetworks(publicKey, &PacketRaw{Command: CommandAnnouncement, Payload: packets[0]}, address) + } +} + +// cmdResponseFindSelf processes FIND_SELF responses +func (peer *PeerInfo) cmdResponseFindSelf(msg *MessageResponse, Closest []PeerRecord) { + for _, closePeer := range Closest { + // Initiate contact. Once a response comes back, the peer is actually added to the list. + contactArbitraryPeer(closePeer.PublicKey, []*net.UDPAddr{{IP: closePeer.IP, Port: int(closePeer.Port)}}) + } } diff --git a/Commands.go b/Commands.go index e3a6557..abbde90 100644 --- a/Commands.go +++ b/Commands.go @@ -7,6 +7,7 @@ Author: Peter Kleissner package core import ( + "bytes" "fmt" "time" @@ -14,7 +15,7 @@ import ( ) // respondClosesContactsCount is the number of closest contact to respond. -// Each peer record will take 55 bytes. Overhead is 73 + 15 payload header + UA length + 6 + 34 = 128 bytes without UA. +// Each peer record will take 55 bytes. Overhead is 77 + 15 payload header + UA length + 6 + 34 = 132 bytes without UA. // It makes sense to stay below 508 bytes (no fragmentation). Reporting back 5 contacts for FIND_SELF requests should do the magic. const respondClosesContactsCount = 5 @@ -138,6 +139,10 @@ func (peer *PeerInfo) cmdResponse(msg *MessageResponse) { for _, hash2Peer := range msg.Hash2Peers { info := nodesDHT.IRLookup(peer.NodeID, hash2Peer.ID.Hash) if info == nil { + // Response to FIND_SELF? + if bytes.Equal(info.Key, nodeID) && len(hash2Peer.Closest) > 0 { + peer.cmdResponseFindSelf(msg, hash2Peer.Closest) + } continue } diff --git a/Connection.go b/Connection.go index f16fc59..028afd6 100644 --- a/Connection.go +++ b/Connection.go @@ -45,6 +45,16 @@ func (c *Connection) IsLocal() bool { return IsIPLocal(c.Address.IP) } +// IsIPv4 checks if the connection is using IPv4 +func (c *Connection) IsIPv4() bool { + return IsIPv4(c.Address.IP) +} + +// IsIPv6 checks if the connection is using IPv6 +func (c *Connection) IsIPv6() bool { + return IsIPv6(c.Address.IP) +} + // GetConnections returns the list of connections func (peer *PeerInfo) GetConnections(active bool) (connections []*Connection) { peer.RLock() diff --git a/Packet Encoding.go b/Packet Encoding.go index c0afded..992fe7a 100644 --- a/Packet Encoding.go +++ b/Packet Encoding.go @@ -8,8 +8,9 @@ Offset Size Info 0 4 Nonce 4 1 Protocol version = 0 5 1 Command -6 2 Size of payload data -8 ? Payload +6 4 Sequence +10 2 Size of payload data +12 ? Payload ? Randomized garbage ? 65 Signature, ECDSA secp256k1 512-bit + 1 header byte @@ -34,11 +35,12 @@ import ( type PacketRaw struct { Protocol uint8 // Protocol version = 0 Command uint8 // 0 = Announcement + Sequence uint32 // Sequence number Payload []byte // Payload } -// The minimum packet size is 8 bytes (minimum header size) + 65 bytes (signature) -const packetLengthMin = 8 + signatureSize +// The minimum packet size is 12 bytes (minimum header size) + 65 bytes (signature) +const packetLengthMin = 12 + signatureSize const signatureSize = 65 const maxRandomGarbage = 20 @@ -69,14 +71,15 @@ func PacketDecrypt(raw []byte, receiverPublicKey *btcec.PublicKey) (packet *Pack // copy all fields packet = &PacketRaw{Protocol: bufferDecrypted[0], Command: bufferDecrypted[1]} + packet.Sequence = binary.LittleEndian.Uint32(bufferDecrypted[2:6]) - sizePayload := binary.LittleEndian.Uint16(bufferDecrypted[2:4]) - if int(sizePayload) > len(bufferDecrypted)-4 { // invalid length? + sizePayload := binary.LittleEndian.Uint16(bufferDecrypted[6:8]) + if int(sizePayload) > len(bufferDecrypted)-8 { // invalid length? return nil, nil, errors.New("invalid length field") } if sizePayload > 0 { packet.Payload = make([]byte, int(sizePayload)) - copy(packet.Payload, bufferDecrypted[4:4+int(sizePayload)]) + copy(packet.Payload, bufferDecrypted[8:8+int(sizePayload)]) } return packet, senderPublicKey, nil @@ -96,13 +99,14 @@ func PacketEncrypt(senderPrivateKey *btcec.PrivateKey, receiverPublicKey *btcec. raw[4] = packet.Protocol raw[5] = packet.Command - binary.LittleEndian.PutUint16(raw[6:8], uint16(len(packet.Payload))) - copy(raw[8:], packet.Payload) - copy(raw[8+len(packet.Payload):8+len(packet.Payload)+len(garbage)], garbage) + binary.LittleEndian.PutUint16(raw[6:10], uint16(packet.Sequence)) + binary.LittleEndian.PutUint16(raw[10:12], uint16(len(packet.Payload))) + copy(raw[12:], packet.Payload) + copy(raw[12+len(packet.Payload):12+len(packet.Payload)+len(garbage)], garbage) // encrypt it using Salsa20 keySalsa := publicKeyToSalsa20Key(receiverPublicKey) - salsa20.XORKeyStream(raw[4:8+len(packet.Payload)+len(garbage)], raw[4:8+len(packet.Payload)+len(garbage)], nonce, keySalsa) + salsa20.XORKeyStream(raw[4:12+len(packet.Payload)+len(garbage)], raw[4:12+len(packet.Payload)+len(garbage)], nonce, keySalsa) // add signature signature, err := btcec.SignCompact(btcec.S256(), senderPrivateKey, hashData(raw[:len(raw)-signatureSize]), true)