Browse Source

auto update script executed

Toby Chui 1 year ago
parent
commit
84bfe8c1f5
5 changed files with 226 additions and 18 deletions
  1. 1 1
      go.mod
  2. 16 17
      go.sum
  3. 133 0
      mod/upnp/upnp.go
  4. 70 0
      web/components/upnp.html
  5. 6 0
      web/index.html

+ 1 - 1
go.mod

@@ -7,6 +7,6 @@ require (
 	github.com/gorilla/sessions v1.2.1
 	github.com/gorilla/websocket v1.4.2
 	github.com/oschwald/geoip2-golang v1.8.0
-	github.com/syndtr/goleveldb v1.0.0 // indirect
+	gitlab.com/NebulousLabs/go-upnp v0.0.0-20211002182029-11da932010b6
 	golang.org/x/sys v0.6.0 // indirect
 )

+ 16 - 17
go.sum

@@ -3,20 +3,12 @@ github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx2
 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/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
-github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 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=
@@ -29,19 +21,26 @@ 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=
-github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
-github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
-golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+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-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
+golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
+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/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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+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=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 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=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 133 - 0
mod/upnp/upnp.go

@@ -0,0 +1,133 @@
+package upnp
+
+import (
+	"errors"
+	"log"
+	"sync"
+	"time"
+
+	"gitlab.com/NebulousLabs/go-upnp"
+)
+
+/*
+	uPNP Module
+
+	This module handles uPNP Connections to the gateway router and create a port forward entry
+	for the host system at the given port (set with -port paramter)
+*/
+
+type UPnPClient struct {
+	Connection    *upnp.IGD //UPnP conenction object
+	ExternalIP    string    //Storage of external IP address
+	RequiredPorts []int     //All the required ports will be recored
+	PolicyNames   sync.Map  //Name for the required port nubmer
+}
+
+func NewUPNPClient(basePort int, hostname string) (*UPnPClient, error) {
+	//Create uPNP forwarding in the NAT router
+	log.Println("Discovering UPnP router in Local Area Network...")
+	d, err := upnp.Discover()
+	if err != nil {
+		return &UPnPClient{}, err
+	}
+
+	// discover external IP
+	ip, err := d.ExternalIP()
+	if err != nil {
+		return &UPnPClient{}, err
+	}
+
+	//Create the final obejcts
+	newUPnPObject := &UPnPClient{
+		Connection:    d,
+		ExternalIP:    ip,
+		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
+}
+
+func (u *UPnPClient) ForwardPort(portNumber int, ruleName string) error {
+	log.Println("UPnP forwarding new port: ", portNumber, "for "+ruleName+" service")
+
+	//Check if port already forwarded
+	_, ok := u.PolicyNames.Load(portNumber)
+	if ok {
+		//Port already forward. Ignore this request
+		return errors.New("Port already forwarded")
+	}
+
+	// forward a port
+	err := u.Connection.Forward(uint16(portNumber), ruleName)
+	if err != nil {
+		return err
+	}
+
+	u.RequiredPorts = append(u.RequiredPorts, portNumber)
+	u.PolicyNames.Store(portNumber, ruleName)
+	return nil
+}
+
+func (u *UPnPClient) ClosePort(portNumber int) error {
+	//Check if port is opened
+	portOpened := false
+	newRequiredPort := []int{}
+	for _, thisPort := range u.RequiredPorts {
+		if thisPort != portNumber {
+			newRequiredPort = append(newRequiredPort, thisPort)
+		} else {
+			portOpened = true
+		}
+	}
+
+	if portOpened {
+		//Update the port list
+		u.RequiredPorts = newRequiredPort
+
+		// Close the port
+		log.Println("Closing UPnP Port Forward: ", portNumber)
+		err := u.Connection.Clear(uint16(portNumber))
+
+		//Delete the name registry
+		u.PolicyNames.Delete(portNumber)
+
+		if err != nil {
+			log.Println(err)
+			return err
+		}
+	}
+	return nil
+}
+
+// Renew forward rules, prevent router lease time from flushing the Upnp config
+func (u *UPnPClient) RenewForwardRules() {
+	portsToRenew := u.RequiredPorts
+	for _, thisPort := range portsToRenew {
+		ruleName, ok := u.PolicyNames.Load(thisPort)
+		if !ok {
+			continue
+		}
+		u.ClosePort(thisPort)
+		time.Sleep(100 * time.Millisecond)
+		u.ForwardPort(thisPort, ruleName.(string))
+	}
+	log.Println("UPnP Port Forward rule renew completed")
+}
+
+func (u *UPnPClient) Close() {
+	//Shutdown the default UPnP Object
+	if u != nil {
+		for _, portNumber := range u.RequiredPorts {
+			err := u.Connection.Clear(uint16(portNumber))
+			if err != nil {
+				log.Println(err)
+			}
+		}
+	}
+}

+ 70 - 0
web/components/upnp.html

@@ -0,0 +1,70 @@
+
+<h3><i class="blue exchange icon"></i> Port Forward</h3>
+<p>Port forward using UPnP protocol</p>
+<div class="ui divider"></div>
+<div class="ui message">
+    <button style="position: absolute; right: 0.6em; top: 0.6em;" class="ui circular basic icon button"><i class="ui green refresh icon"></i></button>
+    <h4><i class="ui loading spinner icon"></i> Checking Upnp State</h4> 
+    <p><i class="ui info circle icon"></i> If you are hosting this server under a home router which you have no access to, you can try port forward your services port and expose them to the internet via Upnp protocol.
+        Note that not all router support this function, sometime this might be disabled by your ISP or administrator.</p>
+</div>
+<div class="ui form">
+    <div class="field">
+      <div class="ui toggle checkbox">
+        <input type="checkbox" name="upnp">
+        <label>Enable UPnP</label>
+      </div>
+    </div>
+    <div class="field">
+      <label>Port to Forward</label>
+      <div class="ui input">
+        <input type="number" min="1" max="65535" name="forwardPort" placeholder="Forwardable Port">
+      </div>
+    </div>
+    <div class="field">
+      <label>Rule Name</label>
+      <div class="ui input">
+        <input type="text" name="ruleName" placeholder="Rule Name">
+      </div>
+    </div>
+    <div class="field">
+      <button class="ui teal button" type="button" name="addRule"><i class="ui add icon"></i> Add Rule</button>
+    </div>
+</div>
+  
+  <div class="ui basic segment">
+    <p>Forwarded Ports</p>
+    <table class="ui basic table">
+      <thead>
+        <tr>
+          <th>Port Forwarded</th>
+          <th>Name of Rule</th>
+          <th></th>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>80</td>
+          <td>HTTP</td>
+          <td><button class="ui button negative" type="button" name="deleteRule">Delete</button></td>
+        </tr>
+        <tr>
+          <td>22</td>
+          <td>SSH</td>
+          <td><button class="ui button negative" type="button" name="deleteRule">Delete</button></td>
+        </tr>
+      </tbody>
+    </table>
+  </div>
+  
+  <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;
+}
+
+  </script>

+ 6 - 0
web/index.html

@@ -117,6 +117,9 @@
                     <a class="item" tag="blacklist">
                         <i class="red ban icon"></i> Blacklist
                     </a>
+                    <a class="item" tag="upnp">
+                        <i class="blue exchange icon"></i> Port Forward
+                    </a>
                     <div class="ui divider menudivider">Bridging</div>
                     <a class="item" tag="">
                         <i class="remove icon"></i> TCP Proxy
@@ -167,6 +170,9 @@
                 <!-- Blacklist -->
                 <div id="blacklist" class="functiontab" target="blacklist.html"></div>
 
+                <!-- UPnP based port fowarding -->
+                <div id="upnp" class="functiontab" target="upnp.html"></div>
+
                 <!-- Utilities -->
                 <div id="utils" class="functiontab" target="utils.html"></div>
             </div>