瀏覽代碼

auto update script executed

Toby Chui 1 年之前
父節點
當前提交
1aabe756a0
共有 13 個文件被更改,包括 309 次插入81 次删除
  1. 1 15
      acme.go
  2. 1 0
      api.go
  3. 3 2
      go.mod
  4. 16 0
      go.sum
  5. 4 2
      main.go
  6. 15 1
      mod/acme/acme.go
  7. 79 3
      mod/netutils/netutils.go
  8. 33 0
      mod/netutils/pingip.go
  9. 84 0
      mod/netutils/whois.go
  10. 3 5
      mod/pathrule/pathrule.go
  11. 0 22
      tmp/localhost.crt
  12. 0 28
      tmp/localhost.key
  13. 70 3
      web/components/networktools.html

+ 1 - 15
acme.go

@@ -5,7 +5,6 @@ import (
 	"io/ioutil"
 	"log"
 	"math/rand"
-	"net"
 	"net/http"
 	"regexp"
 	"strconv"
@@ -21,24 +20,11 @@ import (
 	This script handle special routing required for acme auto cert renew functions
 */
 
-var acmeHandler *acme.ACMEHandler
-
 // Helper function to generate a random port above a specified value
 func getRandomPort(minPort int) int {
 	return rand.Intn(65535-minPort) + minPort
 }
 
-// Helper function to check if a port is in use
-func isPortInUse(port int) bool {
-	address := fmt.Sprintf(":%d", port)
-	listener, err := net.Listen("tcp", address)
-	if err != nil {
-		return true // Port is in use
-	}
-	defer listener.Close()
-	return false // Port is not in use
-}
-
 // init the new ACME instance
 func initACME() *acme.ACMEHandler {
 	log.Println("Start initializing ACME")
@@ -47,7 +33,7 @@ func initACME() *acme.ACMEHandler {
 	port := getRandomPort(30000)
 
 	// Check if the port is already in use
-	for isPortInUse(port) {
+	for acme.IsPortInUse(port) {
 		port = getRandomPort(30000)
 	}
 

+ 1 - 0
api.go

@@ -135,6 +135,7 @@ func initAPIs() {
 	authRouter.HandleFunc("/api/tools/ipscan", HandleIpScan)
 	authRouter.HandleFunc("/api/tools/traceroute", netutils.HandleTraceRoute)
 	authRouter.HandleFunc("/api/tools/ping", netutils.HandlePing)
+	authRouter.HandleFunc("/api/tools/whois", netutils.HandleWhois)
 	authRouter.HandleFunc("/api/tools/webssh", HandleCreateProxySession)
 	authRouter.HandleFunc("/api/tools/websshSupported", HandleWebSshSupportCheck)
 	authRouter.HandleFunc("/api/tools/wol", HandleWakeOnLan)

+ 3 - 2
go.mod

@@ -10,9 +10,10 @@ require (
 	github.com/gorilla/sessions v1.2.1
 	github.com/gorilla/websocket v1.4.2
 	github.com/grandcat/zeroconf v1.0.0
+	github.com/likexian/whois v1.15.0 // indirect
 	github.com/microcosm-cc/bluemonday v1.0.24
 	github.com/oschwald/geoip2-golang v1.8.0
 	github.com/satori/go.uuid v1.2.0
-	golang.org/x/net v0.10.0
-	golang.org/x/sys v0.8.0
+	golang.org/x/net v0.11.0
+	golang.org/x/sys v0.9.0
 )

+ 16 - 0
go.sum

@@ -770,6 +770,10 @@ github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++
 github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
 github.com/lestrrat-go/jwx v1.2.7/go.mod h1:bw24IXWbavc0R2RsOtpXL7RtMyP589yZ1+L7kd09ZGA=
 github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
+github.com/likexian/gokit v0.25.13/go.mod h1:qQhEWFBEfqLCO3/vOEo2EDKd+EycekVtUK4tex+l2H4=
+github.com/likexian/whois v1.15.0 h1:AYYJ5bNUo8Qy2T1Z5GgMp1oIcIlCcTDfg1buYz6TdAE=
+github.com/likexian/whois v1.15.0/go.mod h1:456fUTkh+O8F8v09bGdVl7XxBjRaQ4LvYHyVWX5Bxyg=
+github.com/likexian/whois-parser v1.24.8/go.mod h1:b6STMHHDaSKbd4PzGrP50wWE5NzeBUETa/hT9gI0G9I=
 github.com/linode/linodego v1.9.1/go.mod h1:h6AuFR/JpqwwM/vkj7s8KV3iGN8/jxn+zc437F8SZ8w=
 github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
 github.com/liquidweb/go-lwApi v0.0.5/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
@@ -1040,6 +1044,8 @@ golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0
 golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
 golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
+golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
+golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1149,8 +1155,11 @@ golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
+golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
 golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
 golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
+golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
+golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1293,8 +1302,11 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
+golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -1303,7 +1315,9 @@ golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
 golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
+golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
 golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
+golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1321,6 +1335,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
 golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
+golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

+ 4 - 2
main.go

@@ -12,6 +12,7 @@ import (
 	"time"
 
 	"github.com/google/uuid"
+	"imuslab.com/zoraxy/mod/acme"
 	"imuslab.com/zoraxy/mod/aroz"
 	"imuslab.com/zoraxy/mod/auth"
 	"imuslab.com/zoraxy/mod/database"
@@ -39,9 +40,9 @@ var ztAuthToken = flag.String("ztauth", "", "ZeroTier authtoken for the local no
 var ztAPIPort = flag.Int("ztport", 9993, "ZeroTier controller API port")
 var (
 	name        = "Zoraxy"
-	version     = "2.6.4"
+	version     = "2.6.5"
 	nodeUUID    = "generic"
-	development = false //Set this to false to use embedded web fs
+	development = true //Set this to false to use embedded web fs
 	bootTime    = time.Now().Unix()
 
 	/*
@@ -67,6 +68,7 @@ var (
 	ganManager         *ganserv.NetworkManager //Global Area Network Manager
 	webSshManager      *sshprox.Manager        //Web SSH connection service
 	tcpProxyManager    *tcpprox.Manager        //TCP Proxy Manager
+	acmeHandler        *acme.ACMEHandler       //Handler for ACME Certificate renew
 
 	//Helper modules
 	EmailSender    *email.Sender        //Email sender that handle email sending

+ 15 - 1
mod/acme/acme.go

@@ -8,8 +8,10 @@ import (
 	"crypto/x509"
 	"encoding/json"
 	"encoding/pem"
+	"fmt"
 	"io/ioutil"
 	"log"
+	"net"
 	"net/http"
 	"os"
 	"path/filepath"
@@ -242,8 +244,20 @@ func (a *ACMEHandler) HandleRenewCertificate(w http.ResponseWriter, r *http.Requ
 func jsonEscape(i string) string {
 	b, err := json.Marshal(i)
 	if err != nil {
-		panic(err)
+		log.Println("Unable to escape json data: " + err.Error())
+		return i
 	}
 	s := string(b)
 	return s[1 : len(s)-1]
 }
+
+// Helper function to check if a port is in use
+func IsPortInUse(port int) bool {
+	address := fmt.Sprintf(":%d", port)
+	listener, err := net.Listen("tcp", address)
+	if err != nil {
+		return true // Port is in use
+	}
+	defer listener.Close()
+	return false // Port is not in use
+}

+ 79 - 3
mod/netutils/netutils.go

@@ -3,9 +3,11 @@ package netutils
 import (
 	"encoding/json"
 	"fmt"
+	"net"
 	"net/http"
 	"strconv"
 
+	"github.com/likexian/whois"
 	"imuslab.com/zoraxy/mod/utils"
 )
 
@@ -46,6 +48,36 @@ func TraceRoute(targetIpOrDomain string, maxHops int) ([]string, error) {
 	return traceroute(targetIpOrDomain, maxHops)
 }
 
+func HandleWhois(w http.ResponseWriter, r *http.Request) {
+	targetIpOrDomain, err := utils.GetPara(r, "target")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid target (domain or ip) address given")
+		return
+	}
+
+	raw, _ := utils.GetPara(r, "raw")
+
+	result, err := whois.Whois(targetIpOrDomain)
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+
+	if raw == "true" {
+		utils.SendTextResponse(w, result)
+	} else {
+		parsedOutput, err := ParseWHOISResponse(result)
+		if err != nil {
+			utils.SendErrorResponse(w, err.Error())
+			return
+		}
+
+		js, _ := json.Marshal(parsedOutput)
+		utils.SendJSONResponse(w, string(js))
+	}
+
+}
+
 func HandlePing(w http.ResponseWriter, r *http.Request) {
 	targetIpOrDomain, err := utils.GetPara(r, "target")
 	if err != nil {
@@ -53,13 +85,44 @@ func HandlePing(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	results := []string{}
+	type MixedPingResults struct {
+		ICMP []string
+		TCP  []string
+		UDP  []string
+	}
+
+	results := MixedPingResults{
+		ICMP: []string{},
+		TCP:  []string{},
+		UDP:  []string{},
+	}
+
+	//Ping ICMP
 	for i := 0; i < 4; i++ {
 		realIP, pingTime, ttl, err := PingIP(targetIpOrDomain)
 		if err != nil {
-			results = append(results, "Reply from "+realIP+": "+err.Error())
+			results.ICMP = append(results.ICMP, "Reply from "+realIP+": "+err.Error())
 		} else {
-			results = append(results, fmt.Sprintf("Reply from %s: Time=%dms TTL=%d", realIP, pingTime.Milliseconds(), ttl))
+			results.ICMP = append(results.ICMP, fmt.Sprintf("Reply from %s: Time=%dms TTL=%d", realIP, pingTime.Milliseconds(), ttl))
+		}
+	}
+
+	//Ping TCP
+	for i := 0; i < 4; i++ {
+		pingTime, err := TCPPing(targetIpOrDomain)
+		if err != nil {
+			results.TCP = append(results.TCP, "Reply from "+resolveIpFromDomain(targetIpOrDomain)+": "+err.Error())
+		} else {
+			results.TCP = append(results.TCP, fmt.Sprintf("Reply from %s: Time=%dms", resolveIpFromDomain(targetIpOrDomain), pingTime.Milliseconds()))
+		}
+	}
+	//Ping UDP
+	for i := 0; i < 4; i++ {
+		pingTime, err := UDPPing(targetIpOrDomain)
+		if err != nil {
+			results.UDP = append(results.UDP, "Reply from "+resolveIpFromDomain(targetIpOrDomain)+": "+err.Error())
+		} else {
+			results.UDP = append(results.UDP, fmt.Sprintf("Reply from %s: Time=%dms", resolveIpFromDomain(targetIpOrDomain), pingTime.Milliseconds()))
 		}
 	}
 
@@ -67,3 +130,16 @@ func HandlePing(w http.ResponseWriter, r *http.Request) {
 	utils.SendJSONResponse(w, string(js))
 
 }
+
+func resolveIpFromDomain(targetIpOrDomain string) string {
+	//Resolve target ip address
+	targetIpAddrString := ""
+	ipAddr, err := net.ResolveIPAddr("ip", targetIpOrDomain)
+	if err != nil {
+		targetIpAddrString = targetIpOrDomain
+	} else {
+		targetIpAddrString = ipAddr.IP.String()
+	}
+
+	return targetIpAddrString
+}

+ 33 - 0
mod/netutils/pingip.go

@@ -6,6 +6,39 @@ import (
 	"time"
 )
 
+// TCP ping
+func TCPPing(ipOrDomain string) (time.Duration, error) {
+	start := time.Now()
+
+	conn, err := net.DialTimeout("tcp", ipOrDomain+":80", 3*time.Second)
+	if err != nil {
+		return 0, fmt.Errorf("failed to establish TCP connection: %v", err)
+	}
+	defer conn.Close()
+
+	elapsed := time.Since(start)
+	pingTime := elapsed.Round(time.Millisecond)
+
+	return pingTime, nil
+}
+
+// UDP Ping
+func UDPPing(ipOrDomain string) (time.Duration, error) {
+	start := time.Now()
+
+	conn, err := net.DialTimeout("udp", ipOrDomain+":80", 3*time.Second)
+	if err != nil {
+		return 0, fmt.Errorf("failed to establish UDP connection: %v", err)
+	}
+	defer conn.Close()
+
+	elapsed := time.Since(start)
+	pingTime := elapsed.Round(time.Millisecond)
+
+	return pingTime, nil
+}
+
+// Traditional ICMP ping
 func PingIP(ipOrDomain string) (string, time.Duration, int, error) {
 	ipAddr, err := net.ResolveIPAddr("ip", ipOrDomain)
 	if err != nil {

+ 84 - 0
mod/netutils/whois.go

@@ -0,0 +1,84 @@
+package netutils
+
+import (
+	"strings"
+	"time"
+)
+
+type WHOISResult struct {
+	DomainName       string    `json:"domainName"`
+	RegistryDomainID string    `json:"registryDomainID"`
+	Registrar        string    `json:"registrar"`
+	UpdatedDate      time.Time `json:"updatedDate"`
+	CreationDate     time.Time `json:"creationDate"`
+	ExpiryDate       time.Time `json:"expiryDate"`
+	RegistrantID     string    `json:"registrantID"`
+	RegistrantName   string    `json:"registrantName"`
+	RegistrantEmail  string    `json:"registrantEmail"`
+	AdminID          string    `json:"adminID"`
+	AdminName        string    `json:"adminName"`
+	AdminEmail       string    `json:"adminEmail"`
+	TechID           string    `json:"techID"`
+	TechName         string    `json:"techName"`
+	TechEmail        string    `json:"techEmail"`
+	NameServers      []string  `json:"nameServers"`
+	DNSSEC           string    `json:"dnssec"`
+}
+
+func ParseWHOISResponse(response string) (WHOISResult, error) {
+	result := WHOISResult{}
+
+	lines := strings.Split(response, "\n")
+	for _, line := range lines {
+		if strings.HasPrefix(line, "Domain Name:") {
+			result.DomainName = strings.TrimSpace(strings.TrimPrefix(line, "Domain Name:"))
+		} else if strings.HasPrefix(line, "Registry Domain ID:") {
+			result.RegistryDomainID = strings.TrimSpace(strings.TrimPrefix(line, "Registry Domain ID:"))
+		} else if strings.HasPrefix(line, "Registrar:") {
+			result.Registrar = strings.TrimSpace(strings.TrimPrefix(line, "Registrar:"))
+		} else if strings.HasPrefix(line, "Updated Date:") {
+			dateStr := strings.TrimSpace(strings.TrimPrefix(line, "Updated Date:"))
+			updatedDate, err := time.Parse("2006-01-02T15:04:05Z", dateStr)
+			if err == nil {
+				result.UpdatedDate = updatedDate
+			}
+		} else if strings.HasPrefix(line, "Creation Date:") {
+			dateStr := strings.TrimSpace(strings.TrimPrefix(line, "Creation Date:"))
+			creationDate, err := time.Parse("2006-01-02T15:04:05Z", dateStr)
+			if err == nil {
+				result.CreationDate = creationDate
+			}
+		} else if strings.HasPrefix(line, "Registry Expiry Date:") {
+			dateStr := strings.TrimSpace(strings.TrimPrefix(line, "Registry Expiry Date:"))
+			expiryDate, err := time.Parse("2006-01-02T15:04:05Z", dateStr)
+			if err == nil {
+				result.ExpiryDate = expiryDate
+			}
+		} else if strings.HasPrefix(line, "Registry Registrant ID:") {
+			result.RegistrantID = strings.TrimSpace(strings.TrimPrefix(line, "Registry Registrant ID:"))
+		} else if strings.HasPrefix(line, "Registrant Name:") {
+			result.RegistrantName = strings.TrimSpace(strings.TrimPrefix(line, "Registrant Name:"))
+		} else if strings.HasPrefix(line, "Registrant Email:") {
+			result.RegistrantEmail = strings.TrimSpace(strings.TrimPrefix(line, "Registrant Email:"))
+		} else if strings.HasPrefix(line, "Registry Admin ID:") {
+			result.AdminID = strings.TrimSpace(strings.TrimPrefix(line, "Registry Admin ID:"))
+		} else if strings.HasPrefix(line, "Admin Name:") {
+			result.AdminName = strings.TrimSpace(strings.TrimPrefix(line, "Admin Name:"))
+		} else if strings.HasPrefix(line, "Admin Email:") {
+			result.AdminEmail = strings.TrimSpace(strings.TrimPrefix(line, "Admin Email:"))
+		} else if strings.HasPrefix(line, "Registry Tech ID:") {
+			result.TechID = strings.TrimSpace(strings.TrimPrefix(line, "Registry Tech ID:"))
+		} else if strings.HasPrefix(line, "Tech Name:") {
+			result.TechName = strings.TrimSpace(strings.TrimPrefix(line, "Tech Name:"))
+		} else if strings.HasPrefix(line, "Tech Email:") {
+			result.TechEmail = strings.TrimSpace(strings.TrimPrefix(line, "Tech Email:"))
+		} else if strings.HasPrefix(line, "Name Server:") {
+			ns := strings.TrimSpace(strings.TrimPrefix(line, "Name Server:"))
+			result.NameServers = append(result.NameServers, ns)
+		} else if strings.HasPrefix(line, "DNSSEC:") {
+			result.DNSSEC = strings.TrimSpace(strings.TrimPrefix(line, "DNSSEC:"))
+		}
+	}
+
+	return result, nil
+}

+ 3 - 5
mod/pathrule/pathrule.go

@@ -12,12 +12,10 @@ import (
 )
 
 /*
-	Pathblock.go
+	Pathrules.go
 
-	This script block off some of the specific pathname in access
-	For example, this module can help you block request for a particular
-	apache directory or functional endpoints like /.well-known/ when you
-	are not using it
+	This script handle advance path settings and rules on particular
+	paths of the incoming requests
 */
 
 type Options struct {

+ 0 - 22
tmp/localhost.crt

@@ -1,22 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDuTCCAqCgAwIBAgIBADANBgkqhkiG9w0BAQ0FADB2MQswCQYDVQQGEwJoazES
-MBAGA1UECAwJSG9uZyBLb25nMRQwEgYDVQQKDAtpbXVzbGFiLmNvbTEZMBcGA1UE
-AwwQWm9yYXh5IFNlbGYtaG9zdDEQMA4GA1UEBwwHSU1VU0xBQjEQMA4GA1UECwwH
-SU1VU0xBQjAeFw0yMzA1MjcxMDQyNDJaFw0zODA1MjgxMDQyNDJaMHYxCzAJBgNV
-BAYTAmhrMRIwEAYDVQQIDAlIb25nIEtvbmcxFDASBgNVBAoMC2ltdXNsYWIuY29t
-MRkwFwYDVQQDDBBab3JheHkgU2VsZi1ob3N0MRAwDgYDVQQHDAdJTVVTTEFCMRAw
-DgYDVQQLDAdJTVVTTEFCMIIBIzANBgkqhkiG9w0BAQEFAAOCARAAMIIBCwKCAQIA
-xav3Qq4DBooHsGW9m+r0dgjI832grX2c0Z6MJQQoE7B6wfpUI0OyfRugTXyXoiRZ
-gLxuROgiCUmp8FaLbl7RsvbImMbCPo3D/RbCT1aJCNXLZ0a7yvcDYc6woQW4nUyk
-ohHfT2otcu+OYS6aYRZuXGsKTAqPSwEXRMtr89wkPgZPsrCD27LFHBOmIcVABDvF
-KRuiwHWSHhFfU5n1AZLyYeYoLNQ9fZPvzPpkMD+HMKi4MMwr/vLE0DwU5jSfVFq+
-cd68zVihp9N/T77yah5EIH9CYm4m8Acs4bfL8DALxnaSN3KmGw6J35rOXrJvJLdh
-t42PDROmQrXN8uG8wGkBiBkCAwEAAaNQME4wHQYDVR0OBBYEFLhXihE+1K6MoL0P
-Nx5htfuSatpiMB8GA1UdIwQYMBaAFLhXihE+1K6MoL0PNx5htfuSatpiMAwGA1Ud
-EwQFMAMBAf8wDQYJKoZIhvcNAQENBQADggECAMCn0ed1bfLefGvoQJV/q+X9p61U
-HunSFJAAhp0N2Q3tq/zjIu0kJX7N0JBciEw2c0ZmqJIqR8V8Im/h/4XuuOR+53hg
-opOSPo39ww7mpxyBlQm63v1nXcNQcvw4U0JqXQ4Kyv8cgX7DIuyjRWHQpc5+6joy
-L5Nz5hzQbgpnPdHQEMorfnm8q6bWg/291IAV3ZA9Z6T5gn4YuyjeUdDczQtpT6nu
-1iTNPqtO6R3aeTVT+OSJT9sH2MHfDAsf371HBM6MzM/5QBc/62Bgau7NUjNKeSEA
-EtUBil8wBHwT7vOtqbyNk5FHEfoCpYsQtP7AtEo10izKCQpDXPftfiJefkOY
------END CERTIFICATE-----

+ 0 - 28
tmp/localhost.key

@@ -1,28 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEwQIBADANBgkqhkiG9w0BAQEFAASCBKswggSnAgEAAoIBAgDFq/dCrgMGigew
-Zb2b6vR2CMjzfaCtfZzRnowlBCgTsHrB+lQjQ7J9G6BNfJeiJFmAvG5E6CIJSanw
-VotuXtGy9siYxsI+jcP9FsJPVokI1ctnRrvK9wNhzrChBbidTKSiEd9Pai1y745h
-LpphFm5cawpMCo9LARdEy2vz3CQ+Bk+ysIPbssUcE6YhxUAEO8UpG6LAdZIeEV9T
-mfUBkvJh5igs1D19k+/M+mQwP4cwqLgwzCv+8sTQPBTmNJ9UWr5x3rzNWKGn039P
-vvJqHkQgf0JibibwByzht8vwMAvGdpI3cqYbDonfms5esm8kt2G3jY8NE6ZCtc3y
-4bzAaQGIGQIDAQABAoIBARA+w8FdH66H5X3fvqdztceFjU5FgtD/Q8YOa6IXJ1wG
-4u/SLNwBEkgp3xC/Lo8KwbhMxBsxoKp2vVqdIjRd4on8shusKgaODA9esXVnvTdW
-qrLAI2rYxhRhsi5pk/SJefY/1cRnC3koquDdvZ5BA6zgtyXssD4PxuPGfAa8jtXy
-GIPUDj/Na+pFf3u6iKGGFA7xmKA+Jx3xL77zRdiG5bS70uTUJIpbJ9fdFDTEwyb+
-5hy6gmiPZ96bg3LnCl23jBx1RqvZxMxm6nHkEtMStoqczCkDIqypq0GaDD3Op5P9
-TDVnrz37FQn4PWrq2VoqoKNcigcloBd620dL3p8jVcihAoGBD5yXW+uCGWDEufD0
-Dvqd5pWD4pXlIP5E6Br9KFaDV0hHEyiJrXk6BzUL0EB7w+8kFafHW2gnvIpS99Tk
-mI37v/8qGqOerrsru/KtbwXR524LsSFBMXoJ+KtEanlZu+qBvhXsRA2ov/dqO+mb
-XEpJbcXGMcFuzeTRoss1JpFUcOKlAoGBDKlqhldAjzpWigd29hjK8saoN3nO7XW4
-O4QjRfnU2M/4pcOZrvu3DyPRfF5et2KAfp6pyYDwFfYWpSzzwblyZQTYCaGT5MCP
-e3V5ee0dCjdIkB92XGv9xzZLLWClMcoLEEV2knRVY8LdbDAQHxFgOdCkOIWNfw07
-+BmM72YHHhllAoGBDptOqrxQ/3mg1vBxCUrHTiT6PphMx2/f/OKzlnhLbvC7P1ug
-ZWSVPIUPRovuwMYRFwnh5s4uz6MEOclBENNXhq4xMLeCEq4hHzrRtpzVZhl6awJY
-QviSN83Wt2BO6xlgxv8wDgRRrTrKdL//knwW89QlugvnplC/K/fBBRLY1L3ZAoGB
-BOg3r57rF1c9qLrh4NiU9ugE05MynhbscqxwWzNKyUg4jk2zJvzI4mY4TuHoBVx4
-fhoRpVWCNpCsEBHO2np7mij5bSogvhvev7M0hAtgINByH+EBpyn3LZieJBT7kMND
-7GdvX60UVthzpfUumkvKpj11F66yutWvMyT72OAKzCB9AoGBBHixLZSz89STQNNT
-rYcSDW79Lj18Z6/HBhLwbteMfuYun6HUssh2CKR7awFa/UOmYySiCAV97m38hjDB
-JC5eMEskRGGrejddtUGjIhNX1hanAkhlnbRwVZc97XvXjryDGBZtaTN/2x4lD59t
-mKYLZqGfZ+fMnaWoxLrCnn0cjIBK
------END PRIVATE KEY-----

+ 70 - 3
web/components/networktools.html

@@ -45,8 +45,17 @@
             </div>
             
         </div>
-        <div class=""></div>
-
+        <div class="ui divider"></div>
+        <!-- Whois-->
+        <h2>Whois</h2>
+        <p>Check the owner and registration information of a given domain</p>
+        <div class="ui icon input">
+            <input id="whoisdomain" type="text" onkeypress="if(event.keyCode === 13) { performWhoisLookup(); }" placeholder="Domain or IP">
+            <i onclick="performWhoisLookup();" class="circular search link icon"></i>
+        </div><br>
+        <small>Lookup might take a few minutes to complete</small>
+        <br>
+        <div id="whois_table"></div>
     </div>
 
     <div class="ui bottom attached tab segment nettoolstab" data-tab="tab2">
@@ -485,10 +494,68 @@ function ping(){
             $("#traceroute_results").val("");
             msgbox(data.error, false, 6000);
         }else{
-            $("#traceroute_results").val(data.join("\n"));
+            $("#traceroute_results").val(`--------- ICMP Ping -------------
+${data.ICMP.join("\n")}\n
+---------- TCP Ping -------------
+${data.TCP.join("\n")}\n
+---------- UDP Ping -------------
+${data.UDP.join("\n")}`);
         }
     });
 }
+
+function performWhoisLookup(){
+    let whoisDomain = $("#whoisdomain").val().trim();
+    $("#whoisdomain").parent().addClass("disabled");
+    $("#whoisdomain").parent().css({
+        "cursor": "wait"
+    });
+    $.get("/api/tools/whois?target=" + whoisDomain, function(data){
+        $("#whoisdomain").parent().removeClass("disabled");
+        $("#whoisdomain").parent().css({
+            "cursor": "auto"
+        });
+        if (data.error != undefined){
+            msgbox(data.error, false, 6000);
+        }else{
+            renderWhoisDomainTable(data);
+        }
+    })
+}
+
+function renderWhoisDomainTable(jsonData) {
+
+    function formatDate(dateString) {
+        var date = new Date(dateString);
+        return date.toLocaleString('en-US', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' });
+    }
+
+    var table = $('<table>').addClass('ui definition table');
+
+    // Create table body
+    var body = $('<tbody>');
+    for (var key in jsonData) {
+    var value = jsonData[key];
+    var row = $('<tr>');
+    row.append($('<td>').text(key));
+    if (key.endsWith('Date')) {
+        row.append($('<td>').text(formatDate(value)));
+    } else if (Array.isArray(value)) {
+        row.append($('<td>').text(value.join(', ')));
+    } else {
+        row.append($('<td>').text(value));
+    }
+    body.append(row);
+    }
+
+    // Append the table body to the table
+    table.append(body);
+
+    // Append the table to the target element
+    $('#whois_table').empty().append(table);
+}
+
+
 </script>