mdns.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. package mdns
  2. import (
  3. "context"
  4. "log"
  5. "net"
  6. "strings"
  7. "time"
  8. "github.com/grandcat/zeroconf"
  9. "imuslab.com/zoraxy/mod/utils"
  10. )
  11. type MDNSHost struct {
  12. MDNS *zeroconf.Server
  13. Host *NetworkHost
  14. IfaceOverride *net.Interface
  15. }
  16. type NetworkHost struct {
  17. HostName string
  18. Port int
  19. IPv4 []net.IP
  20. Domain string
  21. Model string
  22. UUID string
  23. Vendor string
  24. BuildVersion string
  25. MacAddr []string
  26. Online bool
  27. }
  28. // Create a new MDNS discoverer, set MacOverride to empty string for using the first NIC discovered
  29. func NewMDNS(config NetworkHost, MacOverride string) (*MDNSHost, error) {
  30. //Get host MAC Address
  31. macAddress, err := getMacAddr()
  32. if err != nil {
  33. return nil, err
  34. }
  35. macAddressBoardcast := ""
  36. if err == nil {
  37. macAddressBoardcast = strings.Join(macAddress, ",")
  38. } else {
  39. log.Println("[mDNS] Unable to get MAC Address: ", err.Error())
  40. }
  41. //Register the mds services
  42. 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)
  43. if err != nil {
  44. log.Println("[mDNS] Error when registering zeroconf broadcast message", err.Error())
  45. return &MDNSHost{}, err
  46. }
  47. //Discover the iface to override if exists
  48. var overrideIface *net.Interface = nil
  49. if MacOverride != "" {
  50. ifaceIp := ""
  51. ifaces, err := net.Interfaces()
  52. if err != nil {
  53. log.Println("[mDNS] Unable to override iface MAC: " + err.Error() + ". Resuming with default iface")
  54. }
  55. foundMatching := false
  56. for _, iface := range ifaces {
  57. thisIfaceMac := iface.HardwareAddr.String()
  58. thisIfaceMac = strings.ReplaceAll(thisIfaceMac, ":", "-")
  59. MacOverride = strings.ReplaceAll(MacOverride, ":", "-")
  60. if strings.EqualFold(thisIfaceMac, strings.TrimSpace(MacOverride)) {
  61. //This is the correct iface to use
  62. overrideIface = &iface
  63. addrs, err := iface.Addrs()
  64. if err == nil && len(addrs) > 0 {
  65. ifaceIp = addrs[0].String()
  66. }
  67. for _, addr := range addrs {
  68. var ip net.IP
  69. switch v := addr.(type) {
  70. case *net.IPNet:
  71. ip = v.IP
  72. case *net.IPAddr:
  73. ip = v.IP
  74. }
  75. if ip.To4() != nil {
  76. //This NIC have Ipv4 addr
  77. ifaceIp = ip.String()
  78. }
  79. }
  80. foundMatching = true
  81. break
  82. }
  83. }
  84. if !foundMatching {
  85. log.Println("[mDNS] Unable to find the target iface with MAC address: " + MacOverride + ". Resuming with default iface")
  86. } else {
  87. log.Println("[mDNS] Entering force MAC address mode, listening on: " + MacOverride + "(IP address: " + ifaceIp + ")")
  88. }
  89. }
  90. return &MDNSHost{
  91. MDNS: server,
  92. Host: &config,
  93. IfaceOverride: overrideIface,
  94. }, nil
  95. }
  96. func (m *MDNSHost) Close() {
  97. if m != nil {
  98. m.MDNS.Shutdown()
  99. }
  100. }
  101. // Scan with given timeout and domain filter. Use m.Host.Domain for scanning similar typed devices
  102. func (m *MDNSHost) Scan(timeout int, domainFilter string) []*NetworkHost {
  103. // Discover all services on the network (e.g. _workstation._tcp)
  104. var zcoption zeroconf.ClientOption = nil
  105. if m.IfaceOverride != nil {
  106. zcoption = zeroconf.SelectIfaces([]net.Interface{*m.IfaceOverride})
  107. }
  108. resolver, err := zeroconf.NewResolver(zcoption)
  109. if err != nil {
  110. log.Fatalln("Failed to initialize resolver:", err.Error())
  111. }
  112. entries := make(chan *zeroconf.ServiceEntry)
  113. //Create go routine to wait for the resolver
  114. discoveredHost := []*NetworkHost{}
  115. go func(results <-chan *zeroconf.ServiceEntry) {
  116. for entry := range results {
  117. if domainFilter == "" {
  118. //This is a ArOZ Online Host
  119. //Split the required information out of the text element
  120. TEXT := entry.Text
  121. properties := map[string]string{}
  122. for _, v := range TEXT {
  123. kv := strings.Split(v, "=")
  124. if len(kv) == 2 {
  125. properties[kv[0]] = kv[1]
  126. }
  127. }
  128. var macAddrs []string
  129. val, ok := properties["mac_addr"]
  130. if !ok || val == "" {
  131. //No MacAddr found. Target node version too old
  132. macAddrs = []string{}
  133. } else {
  134. macAddrs = strings.Split(properties["mac_addr"], ",")
  135. }
  136. //log.Println(properties)
  137. discoveredHost = append(discoveredHost, &NetworkHost{
  138. HostName: entry.HostName,
  139. Port: entry.Port,
  140. IPv4: entry.AddrIPv4,
  141. Domain: properties["domain"],
  142. Model: properties["model"],
  143. UUID: properties["uuid"],
  144. Vendor: properties["vendor"],
  145. BuildVersion: properties["version_build"],
  146. MacAddr: macAddrs,
  147. Online: true,
  148. })
  149. } else {
  150. if utils.StringInArray(entry.Text, "domain="+domainFilter) {
  151. //This is generic scan request
  152. //Split the required information out of the text element
  153. TEXT := entry.Text
  154. properties := map[string]string{}
  155. for _, v := range TEXT {
  156. kv := strings.Split(v, "=")
  157. if len(kv) == 2 {
  158. properties[kv[0]] = kv[1]
  159. }
  160. }
  161. var macAddrs []string
  162. val, ok := properties["mac_addr"]
  163. if !ok || val == "" {
  164. //No MacAddr found. Target node version too old
  165. macAddrs = []string{}
  166. } else {
  167. macAddrs = strings.Split(properties["mac_addr"], ",")
  168. }
  169. //log.Println(properties)
  170. discoveredHost = append(discoveredHost, &NetworkHost{
  171. HostName: entry.HostName,
  172. Port: entry.Port,
  173. IPv4: entry.AddrIPv4,
  174. Domain: properties["domain"],
  175. Model: properties["model"],
  176. UUID: properties["uuid"],
  177. Vendor: properties["vendor"],
  178. BuildVersion: properties["version_build"],
  179. MacAddr: macAddrs,
  180. Online: true,
  181. })
  182. }
  183. }
  184. }
  185. }(entries)
  186. //Resolve each of the mDNS and pipe it back to the log functions
  187. ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(timeout))
  188. defer cancel()
  189. err = resolver.Browse(ctx, "_http._tcp", "local.", entries)
  190. if err != nil {
  191. log.Fatalln("Failed to browse:", err.Error())
  192. }
  193. //Update the master scan record
  194. <-ctx.Done()
  195. return discoveredHost
  196. }
  197. // Get all mac address of all interfaces
  198. func getMacAddr() ([]string, error) {
  199. ifas, err := net.Interfaces()
  200. if err != nil {
  201. return nil, err
  202. }
  203. var as []string
  204. for _, ifa := range ifas {
  205. a := ifa.HardwareAddr.String()
  206. if a != "" {
  207. as = append(as, a)
  208. }
  209. }
  210. return as, nil
  211. }