Browse Source

auto update script executed

Toby Chui 1 year ago
parent
commit
8d89076001
12 changed files with 594 additions and 66 deletions
  1. 4 0
      api.go
  2. 4 1
      go.mod
  3. 25 5
      go.sum
  4. 23 0
      helpers.go
  5. 22 46
      main.go
  6. 243 0
      mod/mdns/mdns.go
  7. 0 12
      specialRouter.go
  8. 126 0
      start.go
  9. 1 0
      system/sys.uuid
  10. 37 0
      web/components/networktools.html
  11. 5 2
      web/index.html
  12. 104 0
      web/tools/mdns.html

+ 4 - 0
api.go

@@ -73,6 +73,10 @@ func initAPIs() {
 	authRouter.HandleFunc("/api/stats/countries", HandleCountryDistrSummary)
 	authRouter.HandleFunc("/api/utm/list", HandleUptimeMonitorListing)
 
+	//mDNS APIs
+	authRouter.HandleFunc("/api/mdns/list", HandleMdnsListing)
+	authRouter.HandleFunc("/api/mdns/discover", HandleMdnsScanning)
+
 	//If you got APIs to add, append them here
 }
 

+ 4 - 1
go.mod

@@ -4,9 +4,12 @@ go 1.16
 
 require (
 	github.com/boltdb/bolt v1.3.1
+	github.com/google/uuid v1.3.0 // indirect
 	github.com/gorilla/sessions v1.2.1
 	github.com/gorilla/websocket v1.4.2
+	github.com/grandcat/zeroconf v1.0.0
 	github.com/oschwald/geoip2-golang v1.8.0
-	gitlab.com/NebulousLabs/go-upnp v0.0.0-20211002182029-11da932010b6
+	golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect
+	golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1 // indirect
 	golang.org/x/sys v0.6.0 // indirect
 )

+ 25 - 5
go.sum

@@ -1,18 +1,28 @@
 github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
 github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
+github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
+github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
 github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
 github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
 github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
 github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/grandcat/zeroconf v1.0.0 h1:uHhahLBKqwWBV6WZUDAT71044vwOTL+McW0mBJvo6kE=
+github.com/grandcat/zeroconf v1.0.0/go.mod h1:lTKmG1zh86XyCoUeIHSA4FJMBwCJiQmGfcP2PdzytEs=
+github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM=
+github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
 github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs=
 github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw=
 github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
 github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -21,25 +31,35 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.7.3/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-gitlab.com/NebulousLabs/fastrand v0.0.0-20181126182046-603482d69e40 h1:dizWJqTWjwyD8KGcMOwgrkqu1JIkofYgKkmDeNE7oAs=
-gitlab.com/NebulousLabs/fastrand v0.0.0-20181126182046-603482d69e40/go.mod h1:rOnSnoRyxMI3fe/7KIbVcsHRGxe30OONv8dEgo+vCfA=
-gitlab.com/NebulousLabs/go-upnp v0.0.0-20211002182029-11da932010b6 h1:WKij6HF8ECp9E7K0E44dew9NrRDGiNR5u4EFsXnJUx4=
-gitlab.com/NebulousLabs/go-upnp v0.0.0-20211002182029-11da932010b6/go.mod h1:vhrHTGDh4YR7wK8Z+kRJ+x8SF/6RUM3Vb64Si5FD0L8=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
 golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1 h1:4qWs8cYYH6PoEFy4dfhDFgoMGkwAcETd+MmPdCPMzUc=
 golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

+ 23 - 0
helpers.go

@@ -5,6 +5,7 @@ import (
 	"net/http"
 
 	"imuslab.com/zoraxy/mod/dynamicproxy"
+	"imuslab.com/zoraxy/mod/mdns"
 	"imuslab.com/zoraxy/mod/uptime"
 	"imuslab.com/zoraxy/mod/utils"
 )
@@ -95,3 +96,25 @@ func HandleUptimeMonitorListing(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 }
+
+//Handle listing current registered mdns nodes
+func HandleMdnsListing(w http.ResponseWriter, r *http.Request) {
+	js, _ := json.Marshal(previousmdnsScanResults)
+	utils.SendJSONResponse(w, string(js))
+}
+
+func HandleMdnsScanning(w http.ResponseWriter, r *http.Request) {
+	domain, err := utils.PostPara(r, "domain")
+	var hosts []*mdns.NetworkHost
+	if err != nil {
+		//Search for arozos node
+		hosts = mdnsScanner.Scan(30, "")
+		previousmdnsScanResults = hosts
+	} else {
+		//Search for other nodes
+		hosts = mdnsScanner.Scan(30, domain)
+	}
+
+	js, _ := json.Marshal(hosts)
+	utils.SendJSONResponse(w, string(js))
+}

+ 22 - 46
main.go

@@ -10,14 +10,17 @@ import (
 	"syscall"
 	"time"
 
+	"github.com/google/uuid"
 	"imuslab.com/zoraxy/mod/aroz"
 	"imuslab.com/zoraxy/mod/auth"
 	"imuslab.com/zoraxy/mod/database"
 	"imuslab.com/zoraxy/mod/dynamicproxy/redirection"
 	"imuslab.com/zoraxy/mod/geodb"
+	"imuslab.com/zoraxy/mod/mdns"
 	"imuslab.com/zoraxy/mod/statistic"
 	"imuslab.com/zoraxy/mod/tlscert"
 	"imuslab.com/zoraxy/mod/uptime"
+	"imuslab.com/zoraxy/mod/utils"
 )
 
 // General flags
@@ -25,8 +28,9 @@ var noauth = flag.Bool("noauth", false, "Disable authentication for management i
 var showver = flag.Bool("version", false, "Show version of this server")
 
 var (
-	name    = "Zoraxy"
-	version = "2.11"
+	name     = "Zoraxy"
+	version  = "2.11"
+	nodeUUID = "generic"
 
 	handler            *aroz.ArozHandler      //Handle arozos managed permission system
 	sysdb              *database.Database     //System database
@@ -36,6 +40,8 @@ var (
 	geodbStore         *geodb.Store           //GeoIP database
 	statisticCollector *statistic.Collector   //Collecting statistic from visitors
 	uptimeMonitor      *uptime.Monitor        //Uptime monitor service worker
+	mdnsScanner        *mdns.MDNSHost         //mDNS discovery services
+
 )
 
 // Kill signal handler. Do something before the system the core terminate.
@@ -47,6 +53,9 @@ func SetupCloseHandler() {
 		log.Println("\r- Shutting down " + name)
 		geodbStore.Close()
 		statisticCollector.Close()
+		//Stop the mdns service
+		mdnsTickerStop <- true
+		mdnsScanner.Close()
 
 		//Close database, final
 		sysdb.Close()
@@ -76,54 +85,21 @@ func main() {
 
 	SetupCloseHandler()
 
-	//Create database
-	db, err := database.NewDatabase("sys.db", false)
-	if err != nil {
-		log.Fatal(err)
-	}
-	sysdb = db
-	//Create tables for the database
-	sysdb.NewTable("settings")
-
-	//Create an auth agent
-	sessionKey, err := auth.GetSessionKey(sysdb)
-	if err != nil {
-		log.Fatal(err)
-	}
-	authAgent = auth.NewAuthenticationAgent(name, []byte(sessionKey), sysdb, true, func(w http.ResponseWriter, r *http.Request) {
-		//Not logged in. Redirecting to login page
-		http.Redirect(w, r, "/login.html", http.StatusTemporaryRedirect)
-	})
-
-	//Create a TLS certificate manager
-	tlsCertManager, err = tlscert.NewManager("./certs")
-	if err != nil {
-		panic(err)
-	}
-
-	//Create a redirection rule table
-	redirectTable, err = redirection.NewRuleTable("./rules")
-	if err != nil {
-		panic(err)
+	//Read or create the system uuid
+	uuidRecord := "./system/sys.uuid"
+	if !utils.FileExists(uuidRecord) {
+		newSystemUUID := uuid.New().String()
+		os.WriteFile(uuidRecord, []byte(newSystemUUID), 0775)
 	}
-
-	//Create a geodb store
-	geodbStore, err = geodb.NewGeoDb(sysdb, "./system/GeoLite2-Country.mmdb")
+	uuidBytes, err := os.ReadFile(uuidRecord)
 	if err != nil {
+		log.Println("Unable to read system uuid from file system")
 		panic(err)
 	}
+	nodeUUID = string(uuidBytes)
 
-	//Create a statistic collector
-	statisticCollector, err = statistic.NewStatisticCollector(statistic.CollectorOption{
-		Database: sysdb,
-	})
-	if err != nil {
-		panic(err)
-	}
-
-	if err != nil {
-		panic(err)
-	}
+	//Startup all modules
+	startupSequence()
 
 	//Initiate management interface APIs
 	initAPIs()
@@ -134,7 +110,7 @@ func main() {
 	}()
 
 	time.Sleep(500 * time.Millisecond)
-	//Any log println will be shown in the core system via STDOUT redirection. But not STDIN.
+
 	log.Println("Zoraxy started. Visit control panel at http://localhost" + handler.Port)
 	err = http.ListenAndServe(handler.Port, nil)
 

+ 243 - 0
mod/mdns/mdns.go

@@ -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
+}

+ 0 - 12
specialRouter.go

@@ -1,12 +0,0 @@
-package main
-
-/*
-	specialRouter.go
-
-	This script holds special routers
-	that handle special features in sub modules
-*/
-
-func injectSpecialRoutingRules() {
-
-}

+ 126 - 0
start.go

@@ -0,0 +1,126 @@
+package main
+
+import (
+	"log"
+	"net/http"
+	"strconv"
+	"strings"
+	"time"
+
+	"imuslab.com/zoraxy/mod/auth"
+	"imuslab.com/zoraxy/mod/database"
+	"imuslab.com/zoraxy/mod/dynamicproxy/redirection"
+	"imuslab.com/zoraxy/mod/geodb"
+	"imuslab.com/zoraxy/mod/mdns"
+	"imuslab.com/zoraxy/mod/statistic"
+	"imuslab.com/zoraxy/mod/tlscert"
+)
+
+/*
+	Startup Sequence
+
+	This function starts the startup sequence of all
+	required modules
+*/
+
+var (
+	/*
+		MDNS related
+	*/
+	previousmdnsScanResults = []*mdns.NetworkHost{}
+	mdnsTickerStop          chan bool
+)
+
+func startupSequence() {
+	//Create database
+	db, err := database.NewDatabase("sys.db", false)
+	if err != nil {
+		log.Fatal(err)
+	}
+	sysdb = db
+	//Create tables for the database
+	sysdb.NewTable("settings")
+
+	//Create an auth agent
+	sessionKey, err := auth.GetSessionKey(sysdb)
+	if err != nil {
+		log.Fatal(err)
+	}
+	authAgent = auth.NewAuthenticationAgent(name, []byte(sessionKey), sysdb, true, func(w http.ResponseWriter, r *http.Request) {
+		//Not logged in. Redirecting to login page
+		http.Redirect(w, r, "/login.html", http.StatusTemporaryRedirect)
+	})
+
+	//Create a TLS certificate manager
+	tlsCertManager, err = tlscert.NewManager("./certs")
+	if err != nil {
+		panic(err)
+	}
+
+	//Create a redirection rule table
+	redirectTable, err = redirection.NewRuleTable("./rules")
+	if err != nil {
+		panic(err)
+	}
+
+	//Create a geodb store
+	geodbStore, err = geodb.NewGeoDb(sysdb, "./system/GeoLite2-Country.mmdb")
+	if err != nil {
+		panic(err)
+	}
+
+	//Create a statistic collector
+	statisticCollector, err = statistic.NewStatisticCollector(statistic.CollectorOption{
+		Database: sysdb,
+	})
+	if err != nil {
+		panic(err)
+	}
+
+	if err != nil {
+		panic(err)
+	}
+
+	//Create a mdns discoverer
+	portInt, err := strconv.Atoi(strings.Split(handler.Port, ":")[1])
+	if err != nil {
+		portInt = 8000
+	}
+	mdnsScanner, err = mdns.NewMDNS(mdns.NetworkHost{
+		HostName:     "zoraxy_" + nodeUUID,
+		Port:         portInt,
+		Domain:       "zoraxy.imuslab.com",
+		Model:        "Network Gateway",
+		UUID:         nodeUUID,
+		Vendor:       "imuslab.com",
+		BuildVersion: version,
+	}, "")
+	if err != nil {
+		panic(err)
+	}
+
+	//Start initial scanning
+	go func() {
+		hosts := mdnsScanner.Scan(30, "")
+		previousmdnsScanResults = hosts
+		log.Println("mDNS Startup scan completed")
+	}()
+
+	//Create a ticker to update mDNS results every 5 minutes
+	ticker := time.NewTicker(15 * time.Minute)
+	stopChan := make(chan bool)
+	go func() {
+		for {
+			select {
+			case <-stopChan:
+				ticker.Stop()
+			case <-ticker.C:
+				hosts := mdnsScanner.Scan(30, "")
+				previousmdnsScanResults = hosts
+				log.Println("mDNS scan result updated")
+			}
+		}
+	}()
+	mdnsTickerStop = stopChan
+
+}

+ 1 - 0
system/sys.uuid

@@ -0,0 +1 @@
+ae4a7901-104d-4ad2-bbaa-a54ea25dc086

+ 37 - 0
web/components/networktools.html

@@ -0,0 +1,37 @@
+<h3><i class="terminal icon"></i> Network Tools</h3>
+<p>Network tools to help manage your cluster nodes</p>
+<div class="ui divider"></div>
+<div class="ui basic segment">
+    <table class="ui celled table">
+        <thead>
+            <tr>
+            <th>Tool</th>
+            <th>Description</th>
+            <th class="right aligned">Action</th>
+            </tr>
+        </thead>
+        <tbody>
+            <tr class="left aligned">
+            <td>Multicast DNS (mDNS) Scanner</td>
+            <td>Discover mDNS enabled service in this gateway forwarded network</td>
+            <td class="right aligned"><a style="cursor: pointer;" onclick="launchToolWithSize('./tools/mdns.html',1000, 640);">Launch <i class="external alternate icon"></i></a></td>
+            </tr>
+        </tbody>
+    </table>
+</div>
+
+<script>
+function launchToolWithSize(url, width, height){
+    window.open(url,'targetWindow',
+    `toolbar=no,
+    location=no,
+    status=no,
+    menubar=no,
+    scrollbars=yes,
+    resizable=yes,
+    width=${width},
+    height=${height}`);
+}
+</script>
+
+

+ 5 - 2
web/index.html

@@ -69,8 +69,8 @@
                     <a class="item" tag="utm">
                         <i class="green time icon"></i> Uptime Monitor
                     </a>
-                    <a class="item" tag="">
-                        <i class="remove icon"></i> Network Tools
+                    <a class="item" tag="networktool">
+                        <i class="terminal icon"></i> Network Tools
                     </a>
                     <a class="item" tag="utils">
                         <i class="grey paperclip icon"></i> Utilities
@@ -111,6 +111,9 @@
                 <!-- Up Time Monitor -->
                 <div id="utm" class="functiontab" target="uptime.html"></div>
 
+                <!-- Network Tools -->
+                <div id="networktool" class="functiontab" target="networktools.html"></div>
+
                 <!-- Utilities -->
                 <div id="utils" class="functiontab" target="utils.html"></div>
             </div>

+ 104 - 0
web/tools/mdns.html

@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta name="apple-mobile-web-app-capable" content="yes" />
+        <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1"/>
+        <meta charset="UTF-8">
+        <meta name="theme-color" content="#4b75ff">
+        <link rel="icon" type="image/png" href="./favicon.png" />
+        <title>mDNS Discovery | Zoraxy</title>
+        <link rel="stylesheet" href="../script/semantic/semantic.min.css">
+        <script src="../script/jquery-3.6.0.min.js"></script>
+        <script src="../../script/ao_module.js"></script>
+        <script src="../script/semantic/semantic.min.js"></script>
+        <script src="../script/tablesort.js"></script>
+        <link rel="stylesheet" href="../main.css">
+    </head>
+    <body>
+        <div id="mdns-hosts">
+
+        </div>
+        <div class="ui divider"></div>
+        <div class="ui container">
+            <h4>Scan with custom domain filter</h4>
+            <div class="ui form">
+                <div class="field">
+                    <label for="domain">Domain</label>
+                    <input type="text" id="domain" name="domain" placeholder="domain.example.com"/>
+                </div>
+                <button id="discover" class="ui basic button">Discover</button>
+            </div>
+            <br>
+        </div>
+        
+        <div style="float: right;">
+            <button class="ui green basic button"  onclick="initMDNSScan()"><i class="ui refresh icon"></i> Refresh</button>
+            <button class="ui basic button" style="margin-right: 1em;" onclick="window.open('', '_self', ''); window.close();"><i class="ui red remove icon"></i> Close</button>
+        </div>
+        <br><br><br>
+        <script>
+            function initMDNSScan(){
+                $.get("/api/mdns/list", function(data){
+                    renderMDNSHosts(data);
+                });
+            }
+            initMDNSScan();
+            
+            $("#discover").on("click", function() {
+                var domain = $("#domain").val();
+                $("#discover").addClass("loading").addClass('disabled');
+                $.ajax({
+                    type: "POST",
+                    url: "/api/mdns/discover",
+                    data: { domain: domain },
+                    success: function(data) {
+                        $("#discover").removeClass("loading").removeClass('disabled');
+                        renderMDNSHosts(data);
+                    },
+                    error: function(xhr, status, error) {
+                        console.error(error);
+                        // Handle error response here
+                    }
+                });
+            });
+
+            function renderMDNSHosts(data) {
+                // Create table header
+                var tableHeader = $('<thead>').append(
+                    $('<tr>').append(
+                    $('<th>').text('Host Name'),
+                    $('<th>').text('IP Address'),
+                    $('<th>').text('MAC Address'),
+                    $('<th>').text('Model'),
+                    $('<th>').text('Vendor')
+                    )
+                );
+                
+                // Create table body
+                var tableBody = $('<tbody>');
+                for (var i = 0; i < data.length; i++) {
+                    var host = data[i];
+                    var ipAddresses = host.IPv4.join('<br> ');
+                    var macAddresses = host.MacAddr.join('<br> ');
+                    if (macAddresses.trim() == ""){
+                        macAddresses = '<i class="ui red remove icon"></i> Not Supported'
+                    }
+                    var row = $('<tr>').append(
+                    $('<td>').text(host.HostName),
+                    $('<td>').html(ipAddresses),
+                    $('<td>').html(macAddresses),
+                    $('<td>').text(host.Model),
+                    $('<td>').text(host.Vendor)
+                    );
+                    tableBody.append(row);
+                }
+                
+                // Create table with header and body
+                var table = $('<table>').addClass('ui celled unstackable table').append(tableHeader, tableBody);
+                
+                // Render table in HTML element with ID 'mdns-hosts'
+                    $('#mdns-hosts').html(table);
+                }
+        </script>
+    </body>
+</html>