Browse Source

auto update script executed

tobychui 1 year ago
parent
commit
1d1e24adb7
5 changed files with 228 additions and 14 deletions
  1. 3 0
      api.go
  2. 11 1
      main.go
  3. 1 7
      mod/upnp/upnp.go
  4. 207 0
      upnp.go
  5. 6 6
      web/components/upnp.html

+ 3 - 0
api.go

@@ -135,5 +135,8 @@ func initAPIs() {
 	authRouter.HandleFunc("/api/blacklist/ip/add", handleIpBlacklistAdd)
 	authRouter.HandleFunc("/api/blacklist/ip/remove", handleIpBlacklistRemove)
 	authRouter.HandleFunc("/api/blacklist/enable", handleBlacklistEnable)
+
+	//Upnp
+	authRouter.HandleFunc("/api/upnp/discover", handleUpnpDiscover)
 	//If you got APIs to add, append them here
 }

+ 11 - 1
main.go

@@ -17,6 +17,7 @@ import (
 	"imuslab.com/Zoarxy/mod/geodb"
 	"imuslab.com/Zoarxy/mod/statistic"
 	"imuslab.com/Zoarxy/mod/tlscert"
+	"imuslab.com/Zoarxy/mod/upnp"
 )
 
 //General flags
@@ -34,6 +35,7 @@ var (
 	redirectTable      *redirection.RuleTable
 	geodbStore         *geodb.Store
 	statisticCollector *statistic.Collector
+	upnpClient         *upnp.UPnPClient
 )
 
 // Kill signal handler. Do something before the system the core terminate.
@@ -43,9 +45,11 @@ func SetupCloseHandler() {
 	go func() {
 		<-c
 		log.Println("\r- Shutting down " + name)
+		geodbStore.Close()
+		statisticCollector.Close()
 
+		//Close database, final
 		sysdb.Close()
-
 		os.Exit(0)
 	}()
 }
@@ -117,6 +121,12 @@ func main() {
 		panic(err)
 	}
 
+	//Create a upnp client
+	err = initUpnp()
+	if err != nil {
+		panic(err)
+	}
+
 	//Initiate management interface APIs
 	initAPIs()
 

+ 1 - 7
mod/upnp/upnp.go

@@ -23,7 +23,7 @@ type UPnPClient struct {
 	PolicyNames   sync.Map  //Name for the required port nubmer
 }
 
-func NewUPNPClient(basePort int, hostname string) (*UPnPClient, error) {
+func NewUPNPClient() (*UPnPClient, error) {
 	//Create uPNP forwarding in the NAT router
 	log.Println("Discovering UPnP router in Local Area Network...")
 	d, err := upnp.Discover()
@@ -44,12 +44,6 @@ func NewUPNPClient(basePort int, hostname string) (*UPnPClient, error) {
 		RequiredPorts: []int{},
 	}
 
-	//Require the port that is running ArOZ Online Host
-	err = newUPnPObject.ForwardPort(basePort, hostname)
-	if err != nil {
-		return &UPnPClient{}, err
-	}
-
 	return newUPnPObject, nil
 }
 

+ 207 - 0
upnp.go

@@ -0,0 +1,207 @@
+package main
+
+import (
+	"encoding/json"
+	"log"
+	"net/http"
+	"net/url"
+	"regexp"
+	"strconv"
+	"time"
+
+	"imuslab.com/Zoarxy/mod/upnp"
+	"imuslab.com/Zoarxy/mod/utils"
+)
+
+var upnpEnabled = false
+var preforwardMap map[int]string
+
+func initUpnp() error {
+	var err error
+	upnpClient, err = upnp.NewUPNPClient()
+	if err != nil {
+		log.Println("UPnP router discover error: ", err.Error())
+		return err
+	}
+
+	//Check if the upnp was enabled
+	sysdb.NewTable("upnp")
+	sysdb.Read("upnp", "enabled", &upnpEnabled)
+
+	//Load all the ports from database
+	portsMap := map[int]string{}
+	sysdb.Read("upnp", "portmap", &portsMap)
+	preforwardMap = portsMap
+
+	if upnpEnabled {
+		//Forward all the ports
+		for port, policyName := range preforwardMap {
+			upnpClient.ForwardPort(port, policyName)
+			log.Println("Upnp forwarding ", port, " for "+policyName)
+			time.Sleep(300 * time.Millisecond)
+		}
+	}
+
+	return nil
+}
+
+func handleUpnpDiscover(w http.ResponseWriter, r *http.Request) {
+	restart, err := utils.PostPara(r, "restart")
+	if err != nil {
+		type UpnpInfo struct {
+			ExternalIp string
+			RouterIp   string
+		}
+
+		if upnpClient == nil {
+			utils.SendErrorResponse(w, "No UPnP router discovered")
+			return
+		}
+
+		parsedUrl, _ := url.Parse(upnpClient.Connection.Location())
+		ipWithPort := parsedUrl.Host
+
+		result := UpnpInfo{
+			ExternalIp: upnpClient.ExternalIP,
+			RouterIp:   ipWithPort,
+		}
+
+		//Show if there is a upnpclient
+		js, _ := json.Marshal(result)
+		utils.SendJSONResponse(w, string(js))
+	} else {
+		if restart == "true" {
+			//Close the upnp client if exists
+			if upnpClient != nil {
+				saveForwardingPortsToDatabase()
+				upnpClient.Close()
+			}
+
+			//Restart a new one
+			initUpnp()
+
+			utils.SendOK(w)
+		}
+	}
+}
+
+func handleToggleUPnP(w http.ResponseWriter, r *http.Request) {
+	newMode, err := utils.PostPara(r, "mode")
+	if err != nil {
+		//Send the current mode to client side
+		js, _ := json.Marshal(upnpEnabled)
+		utils.SendJSONResponse(w, string(js))
+	} else {
+		if newMode == "true" {
+			upnpEnabled = true
+			sysdb.Read("upnp", "enabled", true)
+
+			log.Println("UPnP Enabled. Forwarding all required ports")
+			//Mount all Upnp requests from preforward Map
+			for port, policyName := range preforwardMap {
+				upnpClient.ForwardPort(port, policyName)
+				log.Println("Upnp forwarding ", port, " for "+policyName)
+				time.Sleep(300 * time.Millisecond)
+			}
+
+			utils.SendOK(w)
+			return
+
+		} else if newMode == "false" {
+			upnpEnabled = false
+			sysdb.Read("upnp", "enabled", false)
+			log.Println("UPnP disabled. Closing all forwarded ports")
+			//Save the current forwarded ports
+			saveForwardingPortsToDatabase()
+
+			//Unmount all Upnp request
+			for _, port := range upnpClient.RequiredPorts {
+				upnpClient.ClosePort(port)
+				log.Println("UPnP port closed: ", port)
+				time.Sleep(300 * time.Millisecond)
+			}
+
+			//done
+			utils.SendOK(w)
+			return
+		}
+	}
+}
+
+func filterRFC2141(input string) string {
+	rfc2141 := regexp.MustCompile(`^[\w\-.!~*'()]*(\%[\da-fA-F]{2}[\w\-.!~*'()]*)*$`)
+	var result []rune
+	for _, char := range input {
+		if char <= 127 && rfc2141.MatchString(string(char)) {
+			result = append(result, char)
+		}
+	}
+	return string(result)
+}
+
+func handleAddUpnpPort(w http.ResponseWriter, r *http.Request) {
+	portString, err := utils.PostPara(r, "port")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid port given")
+		return
+	}
+
+	portNumber, err := strconv.Atoi(portString)
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid port given")
+		return
+	}
+
+	policyName, err := utils.PostPara(r, "name")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid policy name")
+		return
+	}
+
+	policyName = filterRFC2141(policyName)
+
+	err = upnpClient.ForwardPort(portNumber, policyName)
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+
+	saveForwardingPortsToDatabase()
+
+	utils.SendOK(w)
+}
+
+func handleRemoveUpnpPort(w http.ResponseWriter, r *http.Request) {
+	portString, err := utils.PostPara(r, "port")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid port given")
+		return
+	}
+
+	portNumber, err := strconv.Atoi(portString)
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid port given")
+		return
+	}
+
+	saveForwardingPortsToDatabase()
+
+	upnpClient.ClosePort(portNumber)
+}
+
+func saveForwardingPortsToDatabase() {
+	//Move the sync map to map[int]string
+	m := make(map[int]string)
+	upnpClient.PolicyNames.Range(func(key, value interface{}) bool {
+		if k, ok := key.(int); ok {
+			if v, ok := value.(string); ok {
+				m[k] = v
+			}
+		}
+		return true
+	})
+
+	preforwardMap = m
+	sysdb.Write("upnp", "portmap", &preforwardMap)
+
+}

+ 6 - 6
web/components/upnp.html

@@ -61,11 +61,11 @@
   <script>
     //Get value of the form
     function getFormValues() {
-  var formValues = {};
-  formValues.upnp = $('input[name="upnp"]').prop('checked');
-  formValues.forwardPort = $('input[name="forwardPort"]').val();
-  formValues.ruleName = $('input[name="ruleName"]').val();
-  return formValues;
-}
+      var formValues = {};
+      formValues.upnp = $('input[name="upnp"]').prop('checked');
+      formValues.forwardPort = $('input[name="forwardPort"]').val();
+      formValues.ruleName = $('input[name="ruleName"]').val();
+      return formValues;
+    }
 
   </script>