Files
core/Network IPv4 Broadcast.go
Akilan Selvacoumar ae5f1d2fd7 New features (#110)
* added upload status

* added changes for progress bar with more logs and bug fixes, Documentation yet to be added

* huge changes that need more doucmenting

* added possibility to get profile using NodeID

* added fix profile listing user profile information

* removed profile image from the explore reult struct

* saving current changes

* added filter to search based on NodeID

* Monday bug fixing

* updates to the profile

* changes for tracing the blockchain profile image not shown

* added condition to ensure TAG is not sent and removed debug prints

* updated webapi docs
2023-06-28 00:38:17 +01:00

182 lines
5.8 KiB
Go

/*
File Username: Network IPv4 Broadcast.go
Copyright: 2021 Peernet s.r.o.
Author: Peter Kleissner
IPv4 Multicast just sucks (can't use socket bound to 0.0.0.0:PortMain and send to 224.0.0.1:PortMulticast), so we rely on Broadcast instead.
*/
package core
import (
"encoding/hex"
"errors"
"net"
"strconv"
"time"
"github.com/PeernetOfficial/core/btcec"
"github.com/PeernetOfficial/core/protocol"
"github.com/PeernetOfficial/core/reuseport"
)
const ipv4BroadcastPort = 12912
// special Public-Private Key pair for local discovery
var ipv4BroadcastPrivateKey *btcec.PrivateKey
var ipv4BroadcastPublicKey *btcec.PublicKey
const ipv4BroadcastPrivateKeyH = "5e27ecc8e54a24e71dca9ba84a9bf465400e27b8c46a977d34962d3d88558c8e"
func initBroadcastIPv4() {
if configPK, err := hex.DecodeString(ipv4BroadcastPrivateKeyH); err == nil {
ipv4BroadcastPrivateKey, ipv4BroadcastPublicKey = btcec.PrivKeyFromBytes(btcec.S256(), configPK)
}
}
// BroadcastIPv4 prepares sending Broadcasts
func (network *Network) BroadcastIPv4() (err error) {
if ipv4BroadcastPrivateKey == nil || ipv4BroadcastPublicKey == nil {
return
}
// listen on a special socket
network.broadcastSocket, err = reuseport.ListenPacket("udp4", net.JoinHostPort(network.address.IP.String(), strconv.Itoa(ipv4BroadcastPort)))
if err != nil {
network.backend.LogError("BroadcastIPv4", "broadcast socket listen on IP '%s' port '%d': %v\n", network.address.IP.String(), ipv4BroadcastPort, err)
return err
}
network.broadcastIPv4 = networkToIPv4BroadcastIPs(network.ipnet)
go network.BroadcastIPv4Listen()
return nil
}
// BroadcastIPv4Listen listens for incoming broadcast packets
// Fork from network.Listen! Keep any changes synced.
func (network *Network) BroadcastIPv4Listen() {
for {
// Buffer: Must be created for each packet as it is passed as pointer.
// If the buffer is too small, ReadFromUDP only reads until its length and returns this error: "wsarecvfrom: A message sent on a datagram socket was larger than the internal message buffer or some other network limit, or the buffer used to receive a datagram into was smaller than the datagram itself."
buffer := make([]byte, maxPacketSize)
length, sender, err := network.broadcastSocket.ReadFrom(buffer)
if err != nil {
network.backend.LogError("BroadcastIPv4Listen", "receiving UDP message: %v\n", err) // Only log for debug purposes.
time.Sleep(time.Millisecond * 50) // In case of endless errors, prevent ddos of CPU.
continue
}
if network.networkGroup.ipListen.IsAddressSelf(sender.(*net.UDPAddr)) {
continue
}
// For good network practice (and reducing amount of parallel connections), do not allow link-local to talk to non-link-local addresses.
if sender.(*net.UDPAddr).IP.IsLinkLocalUnicast() != network.address.IP.IsLinkLocalUnicast() {
continue
}
//fmt.Printf("BroadcastIPv4Listen from %s at network %s\n", sender.String(), network.address.String())
if length < protocol.PacketLengthMin {
// Discard packets that do not meet the minimum length.
continue
}
// send the packet to a channel which is processed by multiple workers.
network.networkGroup.rawPacketsIncoming <- networkWire{network: network, sender: sender.(*net.UDPAddr), raw: buffer[:length], receiverPublicKey: ipv4BroadcastPublicKey, unicast: false}
}
}
// BroadcastIPv4Send sends out a single broadcast messages to discover peers
func (network *Network) BroadcastIPv4Send() (err error) {
_, blockchainHeight, blockchainVersion := network.backend.UserBlockchain.Header()
packets := protocol.EncodeAnnouncement(true, true, nil, nil, nil, network.backend.FeatureSupport(), blockchainHeight, blockchainVersion, network.backend.userAgent)
if len(packets) == 0 {
return errors.New("error encoding broadcast announcement")
}
raw, err := protocol.PacketEncrypt(network.backend.PeerPrivateKey, ipv4BroadcastPublicKey, &protocol.PacketRaw{Protocol: protocol.ProtocolVersion, Command: protocol.CommandLocalDiscovery, Payload: packets[0]})
if err != nil {
return err
}
// send out the wire
for _, ip := range network.broadcastIPv4 {
err = network.send(ip, ipv4BroadcastPort, raw)
if err != nil {
network.backend.LogError("BroadcastIPv4Send", "sending UDP packet: %v\n", err)
}
}
return nil
}
// networkToIPv4BroadcastIPs generates the IPv4 addresses to send out the broadcast to
func networkToIPv4BroadcastIPs(ipnet *net.IPNet) (broadcastIPs []net.IP) {
broadcastIPs = append(broadcastIPs, net.IPv4bcast)
if ipnet != nil {
if ip2 := ipv4DirectedBroadcast(ipnet); ip2 != nil {
broadcastIPs = append(broadcastIPs, ip2)
}
} else {
interfaceList, err := net.Interfaces()
if err != nil {
return
}
for _, iface := range interfaceList {
addresses, err := iface.Addrs()
if err != nil {
continue
}
for _, address := range addresses {
net1 := address.(*net.IPNet)
// TODO: Does the rfc3927Net make sense?
if !IsIPv4(net1.IP) || rfc3927Net.Contains(net1.IP) {
continue
}
if ip2 := ipv4DirectedBroadcast(net1); ip2 != nil {
broadcastIPs = append(broadcastIPs, ip2)
}
}
}
}
// TODO: Result could contain duplicates, filter them out
return broadcastIPs
}
func ipv4DirectedBroadcast(n *net.IPNet) net.IP {
ip4 := n.IP.To4()
if ip4 == nil {
return nil
}
last := make(net.IP, len(ip4))
copy(last, ip4)
for i := range ip4 {
last[i] |= ^n.Mask[i]
}
return last
}
var (
// rfc3927Net specifies the IPv4 auto configuration address block as
// defined by RFC3927 (169.254.0.0/16).
rfc3927Net = ipNet("169.254.0.0", 16, 32)
)
// ipNet returns a net.IPNet struct given the passed IP address string, number
// of one bits to include at the start of the mask, and the total number of bits
// for the mask.
func ipNet(ip string, ones, bits int) net.IPNet {
return net.IPNet{IP: net.ParseIP(ip), Mask: net.CIDRMask(ones, bits)}
}