diff --git a/Bootstrap.go b/Bootstrap.go index 3d0ce4b..2b696b5 100644 --- a/Bootstrap.go +++ b/Bootstrap.go @@ -152,6 +152,9 @@ func bootstrap() { func autoMulticastBroadcast() { sendMulticastBroadcast := func() { + networksMutex.RLock() + defer networksMutex.RUnlock() + for _, network := range networks6 { if err := network.MulticastIPv6Send(); err != nil { log.Printf("bootstrap error multicast from network address '%s': %v", network.address.IP.String(), err.Error()) diff --git a/Commands.go b/Commands.go index c7fd45e..65659f7 100644 --- a/Commands.go +++ b/Commands.go @@ -70,6 +70,11 @@ func (peer *PeerInfo) cmdResponse(msg *packet2) { // cmdPing handles an incoming ping message func (peer *PeerInfo) cmdPing(msg *packet2) { + if peer == nil { + // Unexpected incoming ping, reply with announce message + // TODO + return + } peer.send(&PacketRaw{Command: CommandPong}) //fmt.Printf("Incoming ping from %s on %s\n", msg.connection.Address.String(), msg.connection.Address.String()) } diff --git a/Connection.go b/Connection.go index 1d02f9c..8c74935 100644 --- a/Connection.go +++ b/Connection.go @@ -241,6 +241,9 @@ func sendAllNetworks(receiverPublicKey *btcec.PublicKey, packet *PacketRaw, remo successCount := 0 + networksMutex.RLock() + defer networksMutex.RUnlock() + if IsIPv6(remote.IP.To16()) { for _, network := range networks6 { // Do not mix link-local unicast targets with non link-local networks (only when iface is known, i.e. not catch all local) diff --git a/Network Detection.go b/Network Detection.go index 6f308c1..3aa8618 100644 --- a/Network Detection.go +++ b/Network Detection.go @@ -7,8 +7,11 @@ Author: Peter Kleissner package core import ( + "fmt" + "log" "net" "strings" + "time" ) // FindInterfaceByIP finds an interface based on the IP. The IP must be available at the interface. @@ -87,3 +90,111 @@ func IsNetworkErrorFatal(err error) bool { return false } + +// changeMonitorFrequency is the frequency in seconds to check for a network change +const changeMonitorFrequency = 10 + +// networkChangeMonitor() monitors for network changes to act accordingly +func networkChangeMonitor() { + // If manual IPs are entered, no need for monitoring for any network changes. + if len(config.Listen) > 0 { + return + } + + for { + time.Sleep(time.Second * changeMonitorFrequency) + + interfaceList, err := net.Interfaces() + if err != nil { + log.Printf("networkChangeMonitor enumerating network adapters failed: %s\n", err.Error()) + continue + } + + ifacesNew := make(map[string][]net.Addr) + + for _, iface := range interfaceList { + addressesNew, err := iface.Addrs() + if err != nil { + log.Printf("initNetwork error enumerating IPs for network adapter '%s': %s\n", iface.Name, err.Error()) + continue + } + ifacesNew[iface.Name] = addressesNew + + // was the interface added? + addressesExist, ok := ifacesExist[iface.Name] + if !ok { + networkChangeInterfaceNew(iface, addressesNew) + } else { + // new IPs added for this interface? + for _, addr := range addressesNew { + exists := false + for _, exist := range addressesExist { + if exist.String() == addr.String() { + exists = true + break + } + } + + if !exists { + networkChangeIPNew(iface, addr) + } + } + + // were IPs removed from this interface + for _, exist := range addressesExist { + removed := true + for _, addr := range addressesNew { + if exist.String() == addr.String() { + removed = false + break + } + } + + if removed { + networkChangeIPRemove(iface, exist) + } + } + } + } + + // was an existing interface removed? + for ifaceExist, addressesExist := range ifacesExist { + if _, ok := ifacesNew[ifaceExist]; !ok { + networkChangeInterfaceRemove(ifaceExist, addressesExist) + } + } + + ifacesExist = ifacesNew + } +} + +// networkChangeInterfaceNew is called when a new interface is detected +func networkChangeInterfaceNew(iface net.Interface, addresses []net.Addr) { + fmt.Printf("Interface new: %s\n", iface.Name) + + for _, addr := range addresses { + fmt.Printf(" IP: %s\n", addr.String()) + } +} + +// networkChangeInterfaceRemove is called when an existing interface is removed +func networkChangeInterfaceRemove(iface string, addresses []net.Addr) { + //networksMutex.RLock() + //defer networksMutex.RUnlock() + + fmt.Printf("Interface removed: %s\n", iface) + + for _, addr := range addresses { + fmt.Printf(" IP: %s\n", addr.String()) + } +} + +// networkChangeIPNew is called when an existing interface lists a new IP +func networkChangeIPNew(iface net.Interface, address net.Addr) { + fmt.Printf("IP new on iface: %s address %s\n", iface.Name, address.String()) +} + +// networkChangeIPRemove is called when an existing interface removes an IP +func networkChangeIPRemove(iface net.Interface, address net.Addr) { + fmt.Printf("IP removed on iface: %s address %s\n", iface.Name, address.String()) +} diff --git a/Network Init.go b/Network Init.go index 84d20c0..76e8e9c 100644 --- a/Network Init.go +++ b/Network Init.go @@ -31,14 +31,16 @@ type networkWire struct { } var ( - rawPacketsIncoming chan networkWire // channel for processing incoming decoded packets by workers - ipsListen map[string]struct{} // list of IPs currently listening on + rawPacketsIncoming chan networkWire // channel for processing incoming decoded packets by workers + ipsListen map[string]struct{} // list of IPs currently listening on + ifacesExist map[string][]net.Addr // list of currently known interfaces with list of IP addresses ) // initNetwork sets up the network configuration and starts listening. func initNetwork() { rawPacketsIncoming = make(chan networkWire, 1000) // buffer up to 1000 UDP packets before they get buffered by the OS network stack and eventually dropped ipsListen = make(map[string]struct{}) + ifacesExist = make(map[string][]net.Addr) rand.Seed(time.Now().UnixNano()) // we are not using "crypto/rand" for speed tradeoff if config.ListenWorkers == 0 { @@ -96,6 +98,8 @@ func initNetwork() { continue } + ifacesExist[iface.Name] = addresses + for _, address := range addresses { net1 := address.(*net.IPNet) diff --git a/Network.go b/Network.go index c79e98c..fb3add6 100644 --- a/Network.go +++ b/Network.go @@ -10,6 +10,7 @@ import ( "errors" "log" "net" + "sync" "sync/atomic" "time" ) @@ -30,6 +31,9 @@ type Network struct { // networks is a list of all connected networks var networks4, networks6 []*Network +// single mutex for both network lists. Higher granularity currently not needed. +var networksMutex sync.RWMutex + // Default ports to use. This may be randomized in the future to prevent fingerprinting (and subsequent blocking) by corporate and ISP firewalls. const defaultPort = 'p' // 112 @@ -115,7 +119,7 @@ func packetWorker(packets <-chan networkWire) { for packet := range packets { decoded, senderPublicKey, err := PacketDecrypt(packet.raw, packet.receiverPublicKey) if err != nil { - log.Printf("packetWorker Error decrypting packet from '%s': %s\n", packet.sender.String(), err.Error()) + //log.Printf("packetWorker Error decrypting packet from '%s': %s\n", packet.sender.String(), err.Error()) continue } diff --git a/Peernet.go b/Peernet.go index 13fc43c..cacf42d 100644 --- a/Peernet.go +++ b/Peernet.go @@ -20,4 +20,5 @@ func Connect() { go bootstrap() go autoMulticastBroadcast() go autoPingAll() + go networkChangeMonitor() }