mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-08-05 20:58:28 +02:00
v2 init commit
This commit is contained in:
243
src/mod/mdns/mdns.go
Normal file
243
src/mod/mdns/mdns.go
Normal file
@@ -0,0 +1,243 @@
|
||||
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
|
||||
}
|
Reference in New Issue
Block a user