Browse Source

auto update script executed

Toby Chui 1 year ago
parent
commit
08a7ebfa58

+ 1 - 0
main.go

@@ -40,6 +40,7 @@ var allowMdnsScanning = flag.Bool("mdns", true, "Enable mDNS scanner and transpo
 var ztAuthToken = flag.String("ztauth", "", "ZeroTier authtoken for the local node")
 var ztAPIPort = flag.Int("ztport", 9993, "ZeroTier controller API port")
 var acmeAutoRenewInterval = flag.Int("autorenew", 86400, "ACME auto TLS/SSL certificate renew check interval (seconds)")
+var enableHighSpeedGeoIPLookup = flag.Bool("fastgeoip", false, "Enable high speed geoip lookup, require 1GB extra memory (Not recommend for low end devices)")
 var (
 	name        = "Zoraxy"
 	version     = "2.6.6"

+ 2 - 20
mod/dynamicproxy/dynamicproxy.go

@@ -8,14 +8,12 @@ import (
 	"log"
 	"net/http"
 	"net/url"
-	"os"
 	"strconv"
 	"strings"
 	"sync"
 	"time"
 
 	"imuslab.com/zoraxy/mod/dynamicproxy/dpcore"
-	"imuslab.com/zoraxy/mod/utils"
 )
 
 /*
@@ -81,27 +79,11 @@ func (router *Router) StartProxyService() error {
 	}
 
 	//Load root options from file
-	rootConfigFilepath := "conf/root_config.json"
-	if !utils.FileExists(rootConfigFilepath) {
-		//Not found. Create a root option
-		js, _ := json.MarshalIndent(RootRoutingOptions{}, "", " ")
-		err := os.WriteFile(rootConfigFilepath, js, 0775)
-		if err != nil {
-			return errors.New("Unable to write root config to file: " + err.Error())
-		}
-	}
-	newRootOption := RootRoutingOptions{}
-	rootOptionsBytes, err := os.ReadFile(rootConfigFilepath)
-	if err != nil {
-		log.Println("[Error] Unable to read root config file at " + rootConfigFilepath + ": " + err.Error())
-		return err
-	}
-	err = json.Unmarshal(rootOptionsBytes, &newRootOption)
+	loadedRootOption, err := loadRootRoutingOptionsFromFile()
 	if err != nil {
-		log.Println("[Error] Unable to parse root config file: " + err.Error())
 		return err
 	}
-	router.RootRoutingOptions = &newRootOption
+	router.RootRoutingOptions = loadedRootOption
 
 	minVersion := tls.VersionTLS10
 	if router.Option.ForceTLSLatest {

+ 48 - 0
mod/dynamicproxy/rootRoute.go

@@ -0,0 +1,48 @@
+package dynamicproxy
+
+import (
+	"encoding/json"
+	"errors"
+	"log"
+	"os"
+
+	"imuslab.com/zoraxy/mod/utils"
+)
+
+/*
+	rootRoute.go
+
+	This script handle special case in routing where the root proxy
+	entity is involved. This also include its setting object
+	RootRoutingOptions
+*/
+
+var rootConfigFilepath string = "conf/root_config.json"
+
+func loadRootRoutingOptionsFromFile() (*RootRoutingOptions, error) {
+	if !utils.FileExists(rootConfigFilepath) {
+		//Not found. Create a root option
+		js, _ := json.MarshalIndent(RootRoutingOptions{}, "", " ")
+		err := os.WriteFile(rootConfigFilepath, js, 0775)
+		if err != nil {
+			return nil, errors.New("Unable to write root config to file: " + err.Error())
+		}
+	}
+	newRootOption := RootRoutingOptions{}
+	rootOptionsBytes, err := os.ReadFile(rootConfigFilepath)
+	if err != nil {
+		log.Println("[Error] Unable to read root config file at " + rootConfigFilepath + ": " + err.Error())
+		return nil, err
+	}
+	err = json.Unmarshal(rootOptionsBytes, &newRootOption)
+	if err != nil {
+		log.Println("[Error] Unable to parse root config file: " + err.Error())
+		return nil, err
+	}
+
+	return &newRootOption, nil
+}
+
+func (opt *RootRoutingOptions) SaveToFile() {
+
+}

+ 27 - 13
mod/geodb/geodb.go

@@ -18,15 +18,18 @@ var geoipv6 []byte //Geodb dataset for ipv6
 type Store struct {
 	BlacklistEnabled bool
 	WhitelistEnabled bool
-	//geodb            [][]string //Parsed geodb list
-	//geodbIpv6        [][]string //Parsed geodb list for ipv6
-
-	geotrie     *trie
-	geotrieIpv6 *trie
-
+	geodb            [][]string //Parsed geodb list
+	geodbIpv6        [][]string //Parsed geodb list for ipv6
+	geotrie          *trie
+	geotrieIpv6      *trie
 	//geoipCache sync.Map
+	sysdb  *database.Database
+	option *StoreOptions
+}
 
-	sysdb *database.Database
+type StoreOptions struct {
+	AllowSlowIpv4LookUp bool
+	AllowSloeIpv6Lookup bool
 }
 
 type CountryInfo struct {
@@ -34,7 +37,7 @@ type CountryInfo struct {
 	ContinetCode   string
 }
 
-func NewGeoDb(sysdb *database.Database) (*Store, error) {
+func NewGeoDb(sysdb *database.Database, option *StoreOptions) (*Store, error) {
 	parsedGeoData, err := parseCSV(geoipv4)
 	if err != nil {
 		return nil, err
@@ -79,14 +82,25 @@ func NewGeoDb(sysdb *database.Database) (*Store, error) {
 		log.Println("Database pointer set to nil: Entering debug mode")
 	}
 
+	var ipv4Trie *trie
+	if !option.AllowSlowIpv4LookUp {
+		ipv4Trie = constrctTrieTree(parsedGeoData)
+	}
+
+	var ipv6Trie *trie
+	if !option.AllowSloeIpv6Lookup {
+		ipv6Trie = constrctTrieTree(parsedGeoDataIpv6)
+	}
+
 	return &Store{
 		BlacklistEnabled: blacklistEnabled,
 		WhitelistEnabled: whitelistEnabled,
-		//geodb:            parsedGeoData,
-		geotrie: constrctTrieTree(parsedGeoData),
-		//geodbIpv6:        parsedGeoDataIpv6,
-		geotrieIpv6: constrctTrieTree(parsedGeoDataIpv6),
-		sysdb:       sysdb,
+		geodb:            parsedGeoData,
+		geotrie:          ipv4Trie,
+		geodbIpv6:        parsedGeoDataIpv6,
+		geotrieIpv6:      ipv6Trie,
+		sysdb:            sysdb,
+		option:           option,
 	}, nil
 }
 

+ 10 - 2
mod/geodb/geoloader.go

@@ -25,9 +25,17 @@ func (s *Store) search(ip string) string {
 	//Search in geotrie tree
 	cc := ""
 	if IsIPv6(ip) {
-		cc = s.geotrieIpv6.search(ip)
+		if s.geotrieIpv6 == nil {
+			cc = s.slowSearchIpv6(ip)
+		} else {
+			cc = s.geotrieIpv6.search(ip)
+		}
 	} else {
-		cc = s.geotrie.search(ip)
+		if s.geotrie == nil {
+			cc = s.slowSearchIpv4(ip)
+		} else {
+			cc = s.geotrie.search(ip)
+		}
 	}
 
 	/*

+ 81 - 0
mod/geodb/slowSearch.go

@@ -0,0 +1,81 @@
+package geodb
+
+import (
+	"errors"
+	"math/big"
+	"net"
+)
+
+/*
+	slowSearch.go
+
+	This script implement the slow search method for ip to country code
+	lookup. If you have the memory allocation for near O(1) lookup,
+	you should not be using slow search mode.
+*/
+
+func ipv4ToUInt32(ip net.IP) uint32 {
+	ip = ip.To4()
+	return uint32(ip[0])<<24 | uint32(ip[1])<<16 | uint32(ip[2])<<8 | uint32(ip[3])
+}
+
+func isIPv4InRange(startIP, endIP, testIP string) (bool, error) {
+	start := net.ParseIP(startIP)
+	end := net.ParseIP(endIP)
+	test := net.ParseIP(testIP)
+
+	if start == nil || end == nil || test == nil {
+		return false, errors.New("invalid IP address format")
+	}
+
+	startUint := ipv4ToUInt32(start)
+	endUint := ipv4ToUInt32(end)
+	testUint := ipv4ToUInt32(test)
+
+	return testUint >= startUint && testUint <= endUint, nil
+}
+
+func isIPv6InRange(startIP, endIP, testIP string) (bool, error) {
+	start := net.ParseIP(startIP)
+	end := net.ParseIP(endIP)
+	test := net.ParseIP(testIP)
+
+	if start == nil || end == nil || test == nil {
+		return false, errors.New("invalid IP address format")
+	}
+
+	startInt := new(big.Int).SetBytes(start.To16())
+	endInt := new(big.Int).SetBytes(end.To16())
+	testInt := new(big.Int).SetBytes(test.To16())
+
+	return testInt.Cmp(startInt) >= 0 && testInt.Cmp(endInt) <= 0, nil
+}
+
+// Slow country code lookup for
+func (s *Store) slowSearchIpv4(ipAddr string) string {
+	for _, ipRange := range s.geodb {
+		startIp := ipRange[0]
+		endIp := ipRange[1]
+		cc := ipRange[2]
+
+		inRange, _ := isIPv4InRange(startIp, endIp, ipAddr)
+		if inRange {
+			return cc
+		}
+	}
+	return ""
+}
+
+func (s *Store) slowSearchIpv6(ipAddr string) string {
+	for _, ipRange := range s.geodbIpv6 {
+		startIp := ipRange[0]
+		endIp := ipRange[1]
+		cc := ipRange[2]
+
+		inRange, _ := isIPv6InRange(startIp, endIp, ipAddr)
+		if inRange {
+			return cc
+		}
+	}
+	return ""
+}

+ 55 - 24
mod/geodb/trie.go

@@ -1,6 +1,7 @@
 package geodb
 
 import (
+	"math"
 	"net"
 )
 
@@ -14,7 +15,7 @@ type trie struct {
 	root *trie_Node
 }
 
-func ipToInt64(ip string) int64 {
+func ipToBytes(ip string) []byte {
 	// Parse the IP address string into a net.IP object
 	parsedIP := net.ParseIP(ip)
 
@@ -25,15 +26,7 @@ func ipToInt64(ip string) int64 {
 		ipBytes = parsedIP.To16()
 	}
 
-	// Convert each byte in the IP address to its 8-bit binary representation
-	var ipInt64 int64
-	for _, b := range ipBytes {
-		ipInt64 <<= 8
-		ipInt64 |= int64(b)
-	}
-
-	// Join the binary representation of each byte with dots to form the final bit string
-	return ipInt64
+	return ipBytes
 }
 
 // inititlaizing a new trie
@@ -45,18 +38,39 @@ func newTrie() *trie {
 
 // Passing words to trie
 func (t *trie) insert(ipAddr string, cc string) {
-	ipInt64 := ipToInt64(ipAddr)
+	ipBytes := ipToBytes(ipAddr)
 	current := t.root
-	for i := 63; i >= 0; i-- {
-		bit := (ipInt64 >> uint(i)) & 1
-		if current.childrens[bit] == nil {
-			current.childrens[bit] = &trie_Node{
-				childrens: [2]*trie_Node{},
-				cc:        cc,
+	for _, b := range ipBytes {
+		//For each byte in the ip address
+		//each byte is 8 bit
+		for j := 0; j < 8; j++ {
+			bitwise := (b&uint8(math.Pow(float64(2), float64(j))) > 0)
+			bit := 0b0000
+			if bitwise {
+				bit = 0b0001
 			}
+			if current.childrens[bit] == nil {
+				current.childrens[bit] = &trie_Node{
+					childrens: [2]*trie_Node{},
+					cc:        cc,
+				}
+			}
+			current = current.childrens[bit]
 		}
-		current = current.childrens[bit]
 	}
+
+	/*
+		for i := 63; i >= 0; i-- {
+			bit := (ipInt64 >> uint(i)) & 1
+			if current.childrens[bit] == nil {
+				current.childrens[bit] = &trie_Node{
+					childrens: [2]*trie_Node{},
+					cc:        cc,
+				}
+			}
+			current = current.childrens[bit]
+		}
+	*/
 }
 
 func isReservedIP(ip string) bool {
@@ -87,15 +101,32 @@ func (t *trie) search(ipAddr string) string {
 		return ""
 	}
 
-	ipInt64 := ipToInt64(ipAddr)
+	ipBytes := ipToBytes(ipAddr)
 	current := t.root
-	for i := 63; i >= 0; i-- {
-		bit := (ipInt64 >> uint(i)) & 1
-		if current.childrens[bit] == nil {
-			return current.cc
+	for _, b := range ipBytes {
+		//For each byte in the ip address
+		//each byte is 8 bit
+		for j := 0; j < 8; j++ {
+			bitwise := (b&uint8(math.Pow(float64(2), float64(j))) > 0)
+			bit := 0b0000
+			if bitwise {
+				bit = 0b0001
+			}
+			if current.childrens[bit] == nil {
+				return current.cc
+			}
+			current = current.childrens[bit]
 		}
-		current = current.childrens[bit]
 	}
+	/*
+		for i := 63; i >= 0; i-- {
+			bit := (ipInt64 >> uint(i)) & 1
+			if current.childrens[bit] == nil {
+				return current.cc
+			}
+			current = current.childrens[bit]
+		}
+	*/
 	if len(current.childrens) == 0 {
 		return current.cc
 	}

+ 18 - 53
mod/utils/utils.go

@@ -37,46 +37,6 @@ func SendOK(w http.ResponseWriter) {
 	w.Write([]byte("\"OK\""))
 }
 
-/*
-	The paramter move function (mv)
-
-	You can find similar things in the PHP version of ArOZ Online Beta. You need to pass in
-	r (HTTP Request Object)
-	getParamter (string, aka $_GET['This string])
-
-	Will return
-	Paramter string (if any)
-	Error (if error)
-
-*/
-/*
-func Mv(r *http.Request, getParamter string, postMode bool) (string, error) {
-	if postMode == false {
-		//Access the paramter via GET
-		keys, ok := r.URL.Query()[getParamter]
-
-		if !ok || len(keys[0]) < 1 {
-			//log.Println("Url Param " + getParamter +" is missing")
-			return "", errors.New("GET paramter " + getParamter + " not found or it is empty")
-		}
-
-		// Query()["key"] will return an array of items,
-		// we only want the single item.
-		key := keys[0]
-		return string(key), nil
-	} else {
-		//Access the parameter via POST
-		r.ParseForm()
-		x := r.Form.Get(getParamter)
-		if len(x) == 0 || x == "" {
-			return "", errors.New("POST paramter " + getParamter + " not found or it is empty")
-		}
-		return string(x), nil
-	}
-
-}
-*/
-
 // Get GET parameter
 func GetPara(r *http.Request, key string) (string, error) {
 	keys, ok := r.URL.Query()[key]
@@ -98,6 +58,24 @@ func PostPara(r *http.Request, key string) (string, error) {
 	}
 }
 
+// Get POST paramter as boolean, accept 1 or true
+func PostBool(r *http.Request, key string) (bool, error) {
+	x, err := PostPara(r, key)
+	if err != nil {
+		return false, err
+	}
+
+	x = strings.TrimSpace(x)
+
+	if x == "1" || strings.ToLower(x) == "true" {
+		return true, nil
+	} else if x == "0" || strings.ToLower(x) == "false" {
+		return false, nil
+	}
+
+	return false, errors.New("invalid boolean given")
+}
+
 func FileExists(filename string) bool {
 	_, err := os.Stat(filename)
 	if os.IsNotExist(err) {
@@ -128,19 +106,6 @@ func TimeToString(targetTime time.Time) string {
 	return targetTime.Format("2006-01-02 15:04:05")
 }
 
-// Use for redirections
-func ConstructRelativePathFromRequestURL(requestURI string, redirectionLocation string) string {
-	if strings.Count(requestURI, "/") == 1 {
-		//Already root level
-		return redirectionLocation
-	}
-	for i := 0; i < strings.Count(requestURI, "/")-1; i++ {
-		redirectionLocation = "../" + redirectionLocation
-	}
-
-	return redirectionLocation
-}
-
 // Check if given string in a given slice
 func StringInArray(arr []string, str string) bool {
 	for _, a := range arr {

+ 22 - 3
reverseproxy.go

@@ -2,7 +2,6 @@ package main
 
 import (
 	"encoding/json"
-	"fmt"
 	"log"
 	"net/http"
 	"path/filepath"
@@ -817,6 +816,26 @@ func HandleIncomingPortSet(w http.ResponseWriter, r *http.Request) {
 }
 
 func HandleRootOptionsUpdate(w http.ResponseWriter, r *http.Request) {
-	newRootOption := dynamicProxyRouter.RootRoutingOptions
-	fmt.Println(newRootOption)
+	enableUnsetSubdomainRedirect, err := utils.PostBool(r, "unsetRedirect")
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+
+	unsetRedirectTarget, _ := utils.PostPara(r, "unsetRedirectTarget")
+
+	disableRootHttps, err := utils.PostBool(r, "disableRootHttps")
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+
+	newRootOption := dynamicproxy.RootRoutingOptions{
+		EnableRedirectForUnsetRules: enableUnsetSubdomainRedirect,
+		UnsetRuleRedirectTarget:     unsetRedirectTarget,
+		DisableHTTPSOnProxyRoot:     disableRootHttps,
+	}
+
+	dynamicProxyRouter.RootRoutingOptions = &newRootOption
+
 }

+ 4 - 1
start.go

@@ -76,7 +76,10 @@ func startupSequence() {
 	}
 
 	//Create a geodb store
-	geodbStore, err = geodb.NewGeoDb(sysdb)
+	geodbStore, err = geodb.NewGeoDb(sysdb, &geodb.StoreOptions{
+		AllowSlowIpv4LookUp: !*enableHighSpeedGeoIPLookup,
+		AllowSloeIpv6Lookup: !*enableHighSpeedGeoIPLookup,
+	})
 	if err != nil {
 		panic(err)
 	}

+ 2 - 1
web/components/rproot.html

@@ -44,7 +44,8 @@
                     <div class="ui input">
                         <input type="text" placeholder="http://example.com">
                     </div>
-                    <p>Unset subdomain will be redirected to the link above. Remember to include the protocol (e.g. http:// or https://)</p>
+                    <small>Unset subdomain will be redirected to the link above. Remember to include the protocol (e.g. http:// or https://)<br>
+                    Leave empty for redirecting to upper level domain (e.g. notfound.example.com <i class="right arrow icon"></i> example.com)</small>
                 </div>
             </div>
             <div class="field">