Created
October 28, 2013 16:12
-
-
Save krpors/7199782 to your computer and use it in GitHub Desktop.
Multicast heartbeats etc.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"flag" | |
"fmt" | |
"net" | |
"os" | |
"time" | |
) | |
var flagAddress *string = flag.String("address", "239.100.100.100:31337", "multicast group and port") | |
var flagId *string = flag.String("id", "some-uuid", "identification of this instance") | |
var flagInterval *int = flag.Int("interval", 5000, "beacon interval in milliseconds") | |
type discoverData struct { | |
Addr string | |
Id string | |
} | |
func (self discoverData) ToPacket() string { | |
return fmt.Sprintf("%s %s", self.Addr, self.Id) | |
} | |
func ParseDiscoverData(buf *[]byte) (discoverData, error) { | |
retdata := discoverData{} | |
return retdata, nil | |
} | |
// Listens for beacons/heartbeats. | |
func listen(strAddress string, recvChan chan discoverData) error { | |
addr, err := net.ResolveUDPAddr("udp", strAddress) | |
if err != nil { | |
return err | |
} | |
conn, err := net.ListenMulticastUDP("udp", nil, addr) | |
if err != nil { | |
return err | |
} | |
fmt.Printf("Joining multicast group %v\n", addr) | |
for { | |
buf := make([]byte, 512) | |
conn.Read(buf) | |
d, err := ParseDiscoverData(&buf) | |
if err != nil { | |
fmt.Printf("Unable to parse data: %s\n", err) | |
continue | |
} | |
recvChan <- d | |
} | |
return nil | |
} | |
// Finds the first interface which support multicasting, and is also up. | |
func findMulticastInterface() (net.Interface, error) { | |
ifs, err := net.Interfaces() | |
if err != nil { | |
return net.Interface{}, err | |
} | |
for i, _ := range ifs { | |
// check if the interface in the current iteration supports multicasting, and is | |
// in fact up. Immediately just return the first one. Not sure if this'll be | |
// a good solution though. | |
if ifs[i].Flags&(net.FlagMulticast|net.FlagUp) == (net.FlagMulticast | net.FlagUp) { | |
return ifs[i], nil | |
} | |
} | |
return net.Interface{}, fmt.Errorf("no suitable interface found which supports multicasting") | |
} | |
// Gets a source IP to send the UDP heartbeats from. This is done by finding the first | |
// address which is found on the given interface, and then parsing that address using | |
// either the CIDR notation (i.e. a.b.c.d/x) or just the 'plain' notation, a.b.c.d. It will | |
// also work for IPv6 addresses. Will return the IP string, or an error when either parsing | |
// failed miserably. | |
func getSrcIP(intf net.Interface) (string, error) { | |
addrs, err := intf.Addrs() | |
if err != nil { | |
return "", err | |
} | |
wut := addrs[0].String() | |
// first try CIDR. This is usually to be found on *nixes. Windows for instance | |
// does not return the network portion. For example, Windows returns just | |
// 192.168.0.30, but Linux returns 192.168.0.30/24. | |
ip, _, err := net.ParseCIDR(wut) | |
if err != nil { | |
// try parsing normally | |
ip = net.ParseIP(wut) | |
if ip == nil { | |
// Well, we tried it. Bail out. | |
return "", fmt.Errorf("unable to parse IP") | |
} | |
} | |
return ip.String(), nil | |
} | |
// Sends a beacon, or a heartbeat. The heartbeat should be sent using the source IP of | |
// an interface that supports multicasting. An IP can be found using the findMulticastInterface() | |
// and the getSrcIP() functions. On Windows for instance, when a Vbox host adapter has also | |
// been installed, the heartbeats are sent on that adapter by default (well, at least on my | |
// box). | |
func startHeartbeat(addr string, srcIP string, interval time.Duration) { | |
remoteAddr, err := net.ResolveUDPAddr("udp", addr) | |
if err != nil { | |
fmt.Println(err) | |
} | |
sourceAddr, err := net.ResolveUDPAddr("udp", srcIP+":0") | |
if err != nil { | |
fmt.Println(err) | |
} | |
fmt.Printf("Announcing ourselves now\n") | |
fmt.Printf("Interface used for beacon is %v\n", sourceAddr) | |
conn, err := net.DialUDP("udp", sourceAddr, remoteAddr) | |
defer conn.Close() | |
if err != nil { | |
fmt.Printf("Error occurred: %v\n", err) | |
fmt.Printf("Not sending a beacon!\n") | |
return | |
} | |
for { | |
conn.Write([]byte("LOL!")) | |
time.Sleep(interval) | |
} | |
} | |
func main() { | |
flag.Parse() | |
if !flag.Parsed() { | |
flag.PrintDefaults() | |
os.Exit(1) | |
} | |
intf, err := findMulticastInterface() | |
if err != nil { | |
fmt.Println(err) | |
os.Exit(1) | |
} | |
srcIP, err := getSrcIP(intf) | |
if err != nil { | |
fmt.Println(err) | |
os.Exit(1) | |
} | |
recvChan := make(chan discoverData) | |
go listen(*flagAddress, recvChan) | |
go startHeartbeat(*flagAddress, srcIP, time.Duration(*flagInterval)*time.Millisecond) | |
// keep listening for beacons, just print them out | |
for a := range recvChan { | |
fmt.Println("Got a heartbeat:", a) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Default on Linux the multicasted data is also looped back to the current interface, which may be undesirable.