123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- package mdns
- import (
- "context"
- "log"
- "net"
- "strings"
- "time"
- "github.com/grandcat/zeroconf"
- "imuslab.com/zoraxy/mod/utils"
- )
- type MDNSHost struct {
- MDNS *zeroconf.Server
- Host *NetworkHost
- IfaceOverride *net.Interface
- }
- type NetworkHost struct {
- HostName string
- Port int
- IPv4 []net.IP
- Domain string
- Model string
- UUID string
- Vendor string
- BuildVersion string
- MacAddr []string
- Online bool
- }
- // Create a new MDNS discoverer, set MacOverride to empty string for using the first NIC discovered
- func NewMDNS(config NetworkHost, MacOverride string) (*MDNSHost, error) {
- //Get host MAC Address
- macAddress, err := getMacAddr()
- if err != nil {
- return nil, err
- }
- macAddressBoardcast := ""
- if err == nil {
- macAddressBoardcast = strings.Join(macAddress, ",")
- } else {
- log.Println("[mDNS] Unable to get MAC Address: ", err.Error())
- }
- //Register the mds services
- server, err := zeroconf.Register(config.HostName, "_http._tcp", "local.", config.Port, []string{"version_build=" + config.BuildVersion, "vendor=" + config.Vendor, "model=" + config.Model, "uuid=" + config.UUID, "domain=" + config.Domain, "mac_addr=" + macAddressBoardcast}, nil)
- if err != nil {
- log.Println("[mDNS] Error when registering zeroconf broadcast message", err.Error())
- return &MDNSHost{}, err
- }
- //Discover the iface to override if exists
- var overrideIface *net.Interface = nil
- if MacOverride != "" {
- ifaceIp := ""
- ifaces, err := net.Interfaces()
- if err != nil {
- log.Println("[mDNS] Unable to override iface MAC: " + err.Error() + ". Resuming with default iface")
- }
- foundMatching := false
- for _, iface := range ifaces {
- thisIfaceMac := iface.HardwareAddr.String()
- thisIfaceMac = strings.ReplaceAll(thisIfaceMac, ":", "-")
- MacOverride = strings.ReplaceAll(MacOverride, ":", "-")
- if strings.EqualFold(thisIfaceMac, strings.TrimSpace(MacOverride)) {
- //This is the correct iface to use
- overrideIface = &iface
- addrs, err := iface.Addrs()
- if err == nil && len(addrs) > 0 {
- ifaceIp = addrs[0].String()
- }
- for _, addr := range addrs {
- var ip net.IP
- switch v := addr.(type) {
- case *net.IPNet:
- ip = v.IP
- case *net.IPAddr:
- ip = v.IP
- }
- if ip.To4() != nil {
- //This NIC have Ipv4 addr
- ifaceIp = ip.String()
- }
- }
- foundMatching = true
- break
- }
- }
- if !foundMatching {
- log.Println("[mDNS] Unable to find the target iface with MAC address: " + MacOverride + ". Resuming with default iface")
- } else {
- log.Println("[mDNS] Entering force MAC address mode, listening on: " + MacOverride + "(IP address: " + ifaceIp + ")")
- }
- }
- return &MDNSHost{
- MDNS: server,
- Host: &config,
- IfaceOverride: overrideIface,
- }, nil
- }
- func (m *MDNSHost) Close() {
- if m != nil {
- m.MDNS.Shutdown()
- }
- }
- // Scan with given timeout and domain filter. Use m.Host.Domain for scanning similar typed devices
- func (m *MDNSHost) Scan(timeout int, domainFilter string) []*NetworkHost {
- // Discover all services on the network (e.g. _workstation._tcp)
- var zcoption zeroconf.ClientOption = nil
- if m.IfaceOverride != nil {
- zcoption = zeroconf.SelectIfaces([]net.Interface{*m.IfaceOverride})
- }
- resolver, err := zeroconf.NewResolver(zcoption)
- if err != nil {
- log.Fatalln("Failed to initialize resolver:", err.Error())
- }
- entries := make(chan *zeroconf.ServiceEntry)
- //Create go routine to wait for the resolver
- discoveredHost := []*NetworkHost{}
- go func(results <-chan *zeroconf.ServiceEntry) {
- for entry := range results {
- if domainFilter == "" {
- //This is a ArOZ Online Host
- //Split the required information out of the text element
- TEXT := entry.Text
- properties := map[string]string{}
- for _, v := range TEXT {
- kv := strings.Split(v, "=")
- if len(kv) == 2 {
- properties[kv[0]] = kv[1]
- }
- }
- var macAddrs []string
- val, ok := properties["mac_addr"]
- if !ok || val == "" {
- //No MacAddr found. Target node version too old
- macAddrs = []string{}
- } else {
- macAddrs = strings.Split(properties["mac_addr"], ",")
- }
- //log.Println(properties)
- discoveredHost = append(discoveredHost, &NetworkHost{
- HostName: entry.HostName,
- Port: entry.Port,
- IPv4: entry.AddrIPv4,
- Domain: properties["domain"],
- Model: properties["model"],
- UUID: properties["uuid"],
- Vendor: properties["vendor"],
- BuildVersion: properties["version_build"],
- MacAddr: macAddrs,
- Online: true,
- })
- } else {
- if utils.StringInArray(entry.Text, "domain="+domainFilter) {
- //This is generic scan request
- //Split the required information out of the text element
- TEXT := entry.Text
- properties := map[string]string{}
- for _, v := range TEXT {
- kv := strings.Split(v, "=")
- if len(kv) == 2 {
- properties[kv[0]] = kv[1]
- }
- }
- var macAddrs []string
- val, ok := properties["mac_addr"]
- if !ok || val == "" {
- //No MacAddr found. Target node version too old
- macAddrs = []string{}
- } else {
- macAddrs = strings.Split(properties["mac_addr"], ",")
- }
- //log.Println(properties)
- discoveredHost = append(discoveredHost, &NetworkHost{
- HostName: entry.HostName,
- Port: entry.Port,
- IPv4: entry.AddrIPv4,
- Domain: properties["domain"],
- Model: properties["model"],
- UUID: properties["uuid"],
- Vendor: properties["vendor"],
- BuildVersion: properties["version_build"],
- MacAddr: macAddrs,
- Online: true,
- })
- }
- }
- }
- }(entries)
- //Resolve each of the mDNS and pipe it back to the log functions
- ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(timeout))
- defer cancel()
- err = resolver.Browse(ctx, "_http._tcp", "local.", entries)
- if err != nil {
- log.Fatalln("Failed to browse:", err.Error())
- }
- //Update the master scan record
- <-ctx.Done()
- return discoveredHost
- }
- // Get all mac address of all interfaces
- func getMacAddr() ([]string, error) {
- ifas, err := net.Interfaces()
- if err != nil {
- return nil, err
- }
- var as []string
- for _, ifa := range ifas {
- a := ifa.HardwareAddr.String()
- if a != "" {
- as = append(as, a)
- }
- }
- return as, nil
- }
|