mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-06-06 07:37:21 +02:00
Updates 2.6.4
+ Added force TLS v1.2 above toggle + Added trace route + Added ICMP ping + Added special routing rules module for up-coming acme integration + Fixed IPv6 check bug in black/whitelist + Optimized UI for TCP Proxy +
This commit is contained in:
parent
a73a7944ec
commit
48dc85ea3e
35
src/acme.go
Normal file
35
src/acme.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"imuslab.com/zoraxy/mod/dynamicproxy"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
acme.go
|
||||||
|
|
||||||
|
This script handle special routing required for acme auto cert renew functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
func acmeRegisterSpecialRoutingRule() {
|
||||||
|
err := dynamicProxyRouter.AddRoutingRules(&dynamicproxy.RoutingRule{
|
||||||
|
ID: "acme-autorenew",
|
||||||
|
MatchRule: func(r *http.Request) bool {
|
||||||
|
if r.RequestURI == "/.well-known/" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
RoutingHandler: func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Write([]byte("HELLO WORLD, THIS IS ACME REQUEST HANDLER"))
|
||||||
|
},
|
||||||
|
Enabled: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println("[Err] " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"imuslab.com/zoraxy/mod/auth"
|
"imuslab.com/zoraxy/mod/auth"
|
||||||
"imuslab.com/zoraxy/mod/netstat"
|
"imuslab.com/zoraxy/mod/netstat"
|
||||||
|
"imuslab.com/zoraxy/mod/netutils"
|
||||||
"imuslab.com/zoraxy/mod/utils"
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -55,6 +56,7 @@ func initAPIs() {
|
|||||||
|
|
||||||
//TLS / SSL config
|
//TLS / SSL config
|
||||||
authRouter.HandleFunc("/api/cert/tls", handleToggleTLSProxy)
|
authRouter.HandleFunc("/api/cert/tls", handleToggleTLSProxy)
|
||||||
|
authRouter.HandleFunc("/api/cert/tlsRequireLatest", handleSetTlsRequireLatest)
|
||||||
authRouter.HandleFunc("/api/cert/upload", handleCertUpload)
|
authRouter.HandleFunc("/api/cert/upload", handleCertUpload)
|
||||||
authRouter.HandleFunc("/api/cert/list", handleListCertificate)
|
authRouter.HandleFunc("/api/cert/list", handleListCertificate)
|
||||||
authRouter.HandleFunc("/api/cert/checkDefault", handleDefaultCertCheck)
|
authRouter.HandleFunc("/api/cert/checkDefault", handleDefaultCertCheck)
|
||||||
@ -81,6 +83,11 @@ func initAPIs() {
|
|||||||
authRouter.HandleFunc("/api/whitelist/ip/remove", handleIpWhitelistRemove)
|
authRouter.HandleFunc("/api/whitelist/ip/remove", handleIpWhitelistRemove)
|
||||||
authRouter.HandleFunc("/api/whitelist/enable", handleWhitelistEnable)
|
authRouter.HandleFunc("/api/whitelist/enable", handleWhitelistEnable)
|
||||||
|
|
||||||
|
//Path Blocker APIs
|
||||||
|
authRouter.HandleFunc("/api/pathrule/add", pathRuleHandler.HandleAddBlockingPath)
|
||||||
|
authRouter.HandleFunc("/api/pathrule/list", pathRuleHandler.HandleListBlockingPath)
|
||||||
|
authRouter.HandleFunc("/api/pathrule/remove", pathRuleHandler.HandleRemoveBlockingPath)
|
||||||
|
|
||||||
//Statistic & uptime monitoring API
|
//Statistic & uptime monitoring API
|
||||||
authRouter.HandleFunc("/api/stats/summary", statisticCollector.HandleTodayStatLoad)
|
authRouter.HandleFunc("/api/stats/summary", statisticCollector.HandleTodayStatLoad)
|
||||||
authRouter.HandleFunc("/api/stats/countries", HandleCountryDistrSummary)
|
authRouter.HandleFunc("/api/stats/countries", HandleCountryDistrSummary)
|
||||||
@ -126,6 +133,8 @@ func initAPIs() {
|
|||||||
|
|
||||||
//Network utilities
|
//Network utilities
|
||||||
authRouter.HandleFunc("/api/tools/ipscan", HandleIpScan)
|
authRouter.HandleFunc("/api/tools/ipscan", HandleIpScan)
|
||||||
|
authRouter.HandleFunc("/api/tools/traceroute", netutils.HandleTraceRoute)
|
||||||
|
authRouter.HandleFunc("/api/tools/ping", netutils.HandlePing)
|
||||||
authRouter.HandleFunc("/api/tools/webssh", HandleCreateProxySession)
|
authRouter.HandleFunc("/api/tools/webssh", HandleCreateProxySession)
|
||||||
authRouter.HandleFunc("/api/tools/websshSupported", HandleWebSshSupportCheck)
|
authRouter.HandleFunc("/api/tools/websshSupported", HandleWebSshSupportCheck)
|
||||||
authRouter.HandleFunc("/api/tools/wol", HandleWakeOnLan)
|
authRouter.HandleFunc("/api/tools/wol", HandleWakeOnLan)
|
||||||
|
27
src/cert.go
27
src/cert.go
@ -130,6 +130,33 @@ func handleToggleTLSProxy(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle the GET and SET of reverse proxy TLS versions
|
||||||
|
func handleSetTlsRequireLatest(w http.ResponseWriter, r *http.Request) {
|
||||||
|
newState, err := utils.PostPara(r, "set")
|
||||||
|
if err != nil {
|
||||||
|
//GET
|
||||||
|
var reqLatestTLS bool = false
|
||||||
|
if sysdb.KeyExists("settings", "forceLatestTLS") {
|
||||||
|
sysdb.Read("settings", "forceLatestTLS", &reqLatestTLS)
|
||||||
|
}
|
||||||
|
|
||||||
|
js, _ := json.Marshal(reqLatestTLS)
|
||||||
|
utils.SendJSONResponse(w, string(js))
|
||||||
|
} else {
|
||||||
|
if newState == "true" {
|
||||||
|
sysdb.Write("settings", "forceLatestTLS", true)
|
||||||
|
log.Println("Updating minimum TLS version to v1.2 or above")
|
||||||
|
dynamicProxyRouter.UpdateTLSVersion(true)
|
||||||
|
} else if newState == "false" {
|
||||||
|
sysdb.Write("settings", "forceLatestTLS", false)
|
||||||
|
log.Println("Updating minimum TLS version to v1.0 or above")
|
||||||
|
dynamicProxyRouter.UpdateTLSVersion(false)
|
||||||
|
} else {
|
||||||
|
utils.SendErrorResponse(w, "invalid state given")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle upload of the certificate
|
// Handle upload of the certificate
|
||||||
func handleCertUpload(w http.ResponseWriter, r *http.Request) {
|
func handleCertUpload(w http.ResponseWriter, r *http.Request) {
|
||||||
// check if request method is POST
|
// check if request method is POST
|
||||||
|
@ -12,5 +12,6 @@ require (
|
|||||||
github.com/microcosm-cc/bluemonday v1.0.24
|
github.com/microcosm-cc/bluemonday v1.0.24
|
||||||
github.com/oschwald/geoip2-golang v1.8.0
|
github.com/oschwald/geoip2-golang v1.8.0
|
||||||
github.com/satori/go.uuid v1.2.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/sys v0.8.0
|
||||||
)
|
)
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"imuslab.com/zoraxy/mod/geodb"
|
"imuslab.com/zoraxy/mod/geodb"
|
||||||
"imuslab.com/zoraxy/mod/mdns"
|
"imuslab.com/zoraxy/mod/mdns"
|
||||||
"imuslab.com/zoraxy/mod/netstat"
|
"imuslab.com/zoraxy/mod/netstat"
|
||||||
|
"imuslab.com/zoraxy/mod/pathrule"
|
||||||
"imuslab.com/zoraxy/mod/sshprox"
|
"imuslab.com/zoraxy/mod/sshprox"
|
||||||
"imuslab.com/zoraxy/mod/statistic"
|
"imuslab.com/zoraxy/mod/statistic"
|
||||||
"imuslab.com/zoraxy/mod/statistic/analytic"
|
"imuslab.com/zoraxy/mod/statistic/analytic"
|
||||||
@ -38,7 +39,7 @@ var ztAuthToken = flag.String("ztauth", "", "ZeroTier authtoken for the local no
|
|||||||
var ztAPIPort = flag.Int("ztport", 9993, "ZeroTier controller API port")
|
var ztAPIPort = flag.Int("ztport", 9993, "ZeroTier controller API port")
|
||||||
var (
|
var (
|
||||||
name = "Zoraxy"
|
name = "Zoraxy"
|
||||||
version = "2.6.3"
|
version = "2.6.4"
|
||||||
nodeUUID = "generic"
|
nodeUUID = "generic"
|
||||||
development = false //Set this to false to use embedded web fs
|
development = false //Set this to false to use embedded web fs
|
||||||
bootTime = time.Now().Unix()
|
bootTime = time.Now().Unix()
|
||||||
@ -57,6 +58,7 @@ var (
|
|||||||
authAgent *auth.AuthAgent //Authentication agent
|
authAgent *auth.AuthAgent //Authentication agent
|
||||||
tlsCertManager *tlscert.Manager //TLS / SSL management
|
tlsCertManager *tlscert.Manager //TLS / SSL management
|
||||||
redirectTable *redirection.RuleTable //Handle special redirection rule sets
|
redirectTable *redirection.RuleTable //Handle special redirection rule sets
|
||||||
|
pathRuleHandler *pathrule.Handler //Handle specific path blocking or custom headers
|
||||||
geodbStore *geodb.Store //GeoIP database, also handle black list and whitelist features
|
geodbStore *geodb.Store //GeoIP database, also handle black list and whitelist features
|
||||||
netstatBuffers *netstat.NetStatBuffers //Realtime graph buffers
|
netstatBuffers *netstat.NetStatBuffers //Realtime graph buffers
|
||||||
statisticCollector *statistic.Collector //Collecting statistic from visitors
|
statisticCollector *statistic.Collector //Collecting statistic from visitors
|
||||||
@ -149,6 +151,9 @@ func main() {
|
|||||||
|
|
||||||
time.Sleep(500 * time.Millisecond)
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
|
||||||
|
//Start the finalize sequences
|
||||||
|
finalSequence()
|
||||||
|
|
||||||
log.Println("Zoraxy started. Visit control panel at http://localhost" + handler.Port)
|
log.Println("Zoraxy started. Visit control panel at http://localhost" + handler.Port)
|
||||||
err = http.ListenAndServe(handler.Port, nil)
|
err = http.ListenAndServe(handler.Port, nil)
|
||||||
|
|
||||||
|
@ -115,7 +115,6 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
h.proxyRequest(w, r, targetProxyEndpoint)
|
h.proxyRequest(w, r, targetProxyEndpoint)
|
||||||
} else if !strings.HasSuffix(proxyingPath, "/") {
|
} else if !strings.HasSuffix(proxyingPath, "/") {
|
||||||
potentialProxtEndpoint := h.Parent.getTargetProxyEndpointFromRequestURI(proxyingPath + "/")
|
potentialProxtEndpoint := h.Parent.getTargetProxyEndpointFromRequestURI(proxyingPath + "/")
|
||||||
|
|
||||||
if potentialProxtEndpoint != nil {
|
if potentialProxtEndpoint != nil {
|
||||||
//Missing tailing slash. Redirect to target proxy endpoint
|
//Missing tailing slash. Redirect to target proxy endpoint
|
||||||
http.Redirect(w, r, r.RequestURI+"/", http.StatusTemporaryRedirect)
|
http.Redirect(w, r, r.RequestURI+"/", http.StatusTemporaryRedirect)
|
||||||
|
@ -45,6 +45,13 @@ func (router *Router) UpdateTLSSetting(tlsEnabled bool) {
|
|||||||
router.Restart()
|
router.Restart()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update TLS Version in runtime. Will restart proxy server if running.
|
||||||
|
// Set this to true to force TLS 1.2 or above
|
||||||
|
func (router *Router) UpdateTLSVersion(requireLatest bool) {
|
||||||
|
router.Option.ForceTLSLatest = requireLatest
|
||||||
|
router.Restart()
|
||||||
|
}
|
||||||
|
|
||||||
// Update https redirect, which will require updates
|
// Update https redirect, which will require updates
|
||||||
func (router *Router) UpdateHttpToHttpsRedirectSetting(useRedirect bool) {
|
func (router *Router) UpdateHttpToHttpsRedirectSetting(useRedirect bool) {
|
||||||
router.Option.ForceHttpsRedirect = useRedirect
|
router.Option.ForceHttpsRedirect = useRedirect
|
||||||
@ -62,8 +69,13 @@ func (router *Router) StartProxyService() error {
|
|||||||
return errors.New("Reverse proxy router root not set")
|
return errors.New("Reverse proxy router root not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
minVersion := tls.VersionTLS10
|
||||||
|
if router.Option.ForceTLSLatest {
|
||||||
|
minVersion = tls.VersionTLS12
|
||||||
|
}
|
||||||
config := &tls.Config{
|
config := &tls.Config{
|
||||||
GetCertificate: router.Option.TlsManager.GetCert,
|
GetCertificate: router.Option.TlsManager.GetCert,
|
||||||
|
MinVersion: uint16(minVersion),
|
||||||
}
|
}
|
||||||
|
|
||||||
if router.Option.UseTls {
|
if router.Option.UseTls {
|
||||||
@ -171,18 +183,22 @@ func (router *Router) StopProxyService() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Restart the current router if it is running.
|
// Restart the current router if it is running.
|
||||||
// Startup the server if it is not running initially
|
|
||||||
func (router *Router) Restart() error {
|
func (router *Router) Restart() error {
|
||||||
//Stop the router if it is already running
|
//Stop the router if it is already running
|
||||||
|
var err error = nil
|
||||||
if router.Running {
|
if router.Running {
|
||||||
err := router.StopProxyService()
|
err := router.StopProxyService()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start the server
|
||||||
|
err = router.StartProxyService()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Start the server
|
|
||||||
err := router.StartProxyService()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,12 +15,12 @@ import (
|
|||||||
type RoutingRule struct {
|
type RoutingRule struct {
|
||||||
ID string
|
ID string
|
||||||
MatchRule func(r *http.Request) bool
|
MatchRule func(r *http.Request) bool
|
||||||
RoutingHandler http.Handler
|
RoutingHandler func(http.ResponseWriter, *http.Request)
|
||||||
Enabled bool
|
Enabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
//Router functions
|
// Router functions
|
||||||
//Check if a routing rule exists given its id
|
// Check if a routing rule exists given its id
|
||||||
func (router *Router) GetRoutingRuleById(rrid string) (*RoutingRule, error) {
|
func (router *Router) GetRoutingRuleById(rrid string) (*RoutingRule, error) {
|
||||||
for _, rr := range router.routingRules {
|
for _, rr := range router.routingRules {
|
||||||
if rr.ID == rrid {
|
if rr.ID == rrid {
|
||||||
@ -31,19 +31,19 @@ func (router *Router) GetRoutingRuleById(rrid string) (*RoutingRule, error) {
|
|||||||
return nil, errors.New("routing rule with given id not found")
|
return nil, errors.New("routing rule with given id not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
//Add a routing rule to the router
|
// Add a routing rule to the router
|
||||||
func (router *Router) AddRoutingRules(rr *RoutingRule) error {
|
func (router *Router) AddRoutingRules(rr *RoutingRule) error {
|
||||||
_, err := router.GetRoutingRuleById(rr.ID)
|
_, err := router.GetRoutingRuleById(rr.ID)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
//routing rule with given id already exists
|
//routing rule with given id already exists
|
||||||
return err
|
return errors.New("routing rule with same id already exists")
|
||||||
}
|
}
|
||||||
|
|
||||||
router.routingRules = append(router.routingRules, rr)
|
router.routingRules = append(router.routingRules, rr)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//Remove a routing rule from the router
|
// Remove a routing rule from the router
|
||||||
func (router *Router) RemoveRoutingRule(rrid string) {
|
func (router *Router) RemoveRoutingRule(rrid string) {
|
||||||
newRoutingRules := []*RoutingRule{}
|
newRoutingRules := []*RoutingRule{}
|
||||||
for _, rr := range router.routingRules {
|
for _, rr := range router.routingRules {
|
||||||
@ -55,13 +55,13 @@ func (router *Router) RemoveRoutingRule(rrid string) {
|
|||||||
router.routingRules = newRoutingRules
|
router.routingRules = newRoutingRules
|
||||||
}
|
}
|
||||||
|
|
||||||
//Get all routing rules
|
// Get all routing rules
|
||||||
func (router *Router) GetAllRoutingRules() []*RoutingRule {
|
func (router *Router) GetAllRoutingRules() []*RoutingRule {
|
||||||
return router.routingRules
|
return router.routingRules
|
||||||
}
|
}
|
||||||
|
|
||||||
//Get the matching routing rule that describe this request.
|
// Get the matching routing rule that describe this request.
|
||||||
//Return nil if no routing rule is match
|
// Return nil if no routing rule is match
|
||||||
func (router *Router) GetMatchingRoutingRule(r *http.Request) *RoutingRule {
|
func (router *Router) GetMatchingRoutingRule(r *http.Request) *RoutingRule {
|
||||||
for _, thisRr := range router.routingRules {
|
for _, thisRr := range router.routingRules {
|
||||||
if thisRr.IsMatch(r) {
|
if thisRr.IsMatch(r) {
|
||||||
@ -71,8 +71,8 @@ func (router *Router) GetMatchingRoutingRule(r *http.Request) *RoutingRule {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//Routing Rule functions
|
// Routing Rule functions
|
||||||
//Check if a request object match the
|
// Check if a request object match the
|
||||||
func (e *RoutingRule) IsMatch(r *http.Request) bool {
|
func (e *RoutingRule) IsMatch(r *http.Request) bool {
|
||||||
if !e.Enabled {
|
if !e.Enabled {
|
||||||
return false
|
return false
|
||||||
@ -81,5 +81,5 @@ func (e *RoutingRule) IsMatch(r *http.Request) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *RoutingRule) Route(w http.ResponseWriter, r *http.Request) {
|
func (e *RoutingRule) Route(w http.ResponseWriter, r *http.Request) {
|
||||||
e.RoutingHandler.ServeHTTP(w, r)
|
e.RoutingHandler(w, r)
|
||||||
}
|
}
|
||||||
|
@ -22,13 +22,14 @@ type ProxyHandler struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RouterOption struct {
|
type RouterOption struct {
|
||||||
HostUUID string
|
HostUUID string //The UUID of Zoraxy, use for heading mod
|
||||||
Port int
|
Port int //Incoming port
|
||||||
UseTls bool
|
UseTls bool //Use TLS to serve incoming requsts
|
||||||
ForceHttpsRedirect bool
|
ForceTLSLatest bool //Force TLS1.2 or above
|
||||||
|
ForceHttpsRedirect bool //Force redirection of http to https endpoint
|
||||||
TlsManager *tlscert.Manager
|
TlsManager *tlscert.Manager
|
||||||
RedirectRuleTable *redirection.RuleTable
|
RedirectRuleTable *redirection.RuleTable
|
||||||
GeodbStore *geodb.Store
|
GeodbStore *geodb.Store //GeoIP blacklist and whitelist
|
||||||
StatisticCollector *statistic.Collector
|
StatisticCollector *statistic.Collector
|
||||||
}
|
}
|
||||||
|
|
||||||
|
16
src/mod/expose/expose.go
Normal file
16
src/mod/expose/expose.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package expose
|
||||||
|
|
||||||
|
/*
|
||||||
|
Service Expose Proxy
|
||||||
|
|
||||||
|
A tunnel for getting your local server online in one line
|
||||||
|
(No, this is not ngrok)
|
||||||
|
*/
|
||||||
|
|
||||||
|
type Router struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
//Create a new service expose router
|
||||||
|
func NewServiceExposeRouter() {
|
||||||
|
|
||||||
|
}
|
111
src/mod/expose/security.go
Normal file
111
src/mod/expose/security.go
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
package expose
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha512"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenerateKeyPair generates a new key pair
|
||||||
|
func GenerateKeyPair(bits int) (*rsa.PrivateKey, *rsa.PublicKey, error) {
|
||||||
|
privkey, err := rsa.GenerateKey(rand.Reader, bits)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return privkey, &privkey.PublicKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivateKeyToBytes private key to bytes
|
||||||
|
func PrivateKeyToBytes(priv *rsa.PrivateKey) []byte {
|
||||||
|
privBytes := pem.EncodeToMemory(
|
||||||
|
&pem.Block{
|
||||||
|
Type: "RSA PRIVATE KEY",
|
||||||
|
Bytes: x509.MarshalPKCS1PrivateKey(priv),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return privBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKeyToBytes public key to bytes
|
||||||
|
func PublicKeyToBytes(pub *rsa.PublicKey) ([]byte, error) {
|
||||||
|
pubASN1, err := x509.MarshalPKIXPublicKey(pub)
|
||||||
|
if err != nil {
|
||||||
|
return []byte(""), err
|
||||||
|
}
|
||||||
|
|
||||||
|
pubBytes := pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: "RSA PUBLIC KEY",
|
||||||
|
Bytes: pubASN1,
|
||||||
|
})
|
||||||
|
|
||||||
|
return pubBytes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesToPrivateKey bytes to private key
|
||||||
|
func BytesToPrivateKey(priv []byte) (*rsa.PrivateKey, error) {
|
||||||
|
block, _ := pem.Decode(priv)
|
||||||
|
enc := x509.IsEncryptedPEMBlock(block)
|
||||||
|
b := block.Bytes
|
||||||
|
var err error
|
||||||
|
if enc {
|
||||||
|
log.Println("is encrypted pem block")
|
||||||
|
b, err = x509.DecryptPEMBlock(block, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
key, err := x509.ParsePKCS1PrivateKey(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesToPublicKey bytes to public key
|
||||||
|
func BytesToPublicKey(pub []byte) (*rsa.PublicKey, error) {
|
||||||
|
block, _ := pem.Decode(pub)
|
||||||
|
enc := x509.IsEncryptedPEMBlock(block)
|
||||||
|
b := block.Bytes
|
||||||
|
var err error
|
||||||
|
if enc {
|
||||||
|
log.Println("is encrypted pem block")
|
||||||
|
b, err = x509.DecryptPEMBlock(block, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ifc, err := x509.ParsePKIXPublicKey(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
key, ok := ifc.(*rsa.PublicKey)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("key not valid")
|
||||||
|
}
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncryptWithPublicKey encrypts data with public key
|
||||||
|
func EncryptWithPublicKey(msg []byte, pub *rsa.PublicKey) ([]byte, error) {
|
||||||
|
hash := sha512.New()
|
||||||
|
ciphertext, err := rsa.EncryptOAEP(hash, rand.Reader, pub, msg, nil)
|
||||||
|
if err != nil {
|
||||||
|
return []byte(""), err
|
||||||
|
}
|
||||||
|
return ciphertext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecryptWithPrivateKey decrypts data with private key
|
||||||
|
func DecryptWithPrivateKey(ciphertext []byte, priv *rsa.PrivateKey) ([]byte, error) {
|
||||||
|
hash := sha512.New()
|
||||||
|
plaintext, err := rsa.DecryptOAEP(hash, rand.Reader, priv, ciphertext, nil)
|
||||||
|
if err != nil {
|
||||||
|
return []byte(""), err
|
||||||
|
}
|
||||||
|
return plaintext, nil
|
||||||
|
}
|
69
src/mod/netutils/netutils.go
Normal file
69
src/mod/netutils/netutils.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package netutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
This script handles basic network utilities like
|
||||||
|
- traceroute
|
||||||
|
- ping
|
||||||
|
*/
|
||||||
|
|
||||||
|
func HandleTraceRoute(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
|
||||||
|
}
|
||||||
|
|
||||||
|
maxhopsString, err := utils.GetPara(r, "maxhops")
|
||||||
|
if err != nil {
|
||||||
|
maxhopsString = "64"
|
||||||
|
}
|
||||||
|
|
||||||
|
maxHops, err := strconv.Atoi(maxhopsString)
|
||||||
|
if err != nil {
|
||||||
|
maxHops = 64
|
||||||
|
}
|
||||||
|
|
||||||
|
results, err := TraceRoute(targetIpOrDomain, maxHops)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
js, _ := json.Marshal(results)
|
||||||
|
utils.SendJSONResponse(w, string(js))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TraceRoute(targetIpOrDomain string, maxHops int) ([]string, error) {
|
||||||
|
return traceroute(targetIpOrDomain, maxHops)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandlePing(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
|
||||||
|
}
|
||||||
|
|
||||||
|
results := []string{}
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
realIP, pingTime, ttl, err := PingIP(targetIpOrDomain)
|
||||||
|
if err != nil {
|
||||||
|
results = append(results, "Reply from "+realIP+": "+err.Error())
|
||||||
|
} else {
|
||||||
|
results = append(results, fmt.Sprintf("Reply from %s: Time=%dms TTL=%d", realIP, pingTime.Milliseconds(), ttl))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
js, _ := json.Marshal(results)
|
||||||
|
utils.SendJSONResponse(w, string(js))
|
||||||
|
|
||||||
|
}
|
28
src/mod/netutils/netutils_test.go
Normal file
28
src/mod/netutils/netutils_test.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package netutils_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"imuslab.com/zoraxy/mod/netutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHandleTraceRoute(t *testing.T) {
|
||||||
|
results, err := netutils.TraceRoute("imuslab.com", 64)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log(results)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandlePing(t *testing.T) {
|
||||||
|
ipOrDomain := "example.com"
|
||||||
|
|
||||||
|
realIP, pingTime, ttl, err := netutils.PingIP(ipOrDomain)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Error:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log(realIP, pingTime, ttl)
|
||||||
|
}
|
48
src/mod/netutils/pingip.go
Normal file
48
src/mod/netutils/pingip.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package netutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PingIP(ipOrDomain string) (string, time.Duration, int, error) {
|
||||||
|
ipAddr, err := net.ResolveIPAddr("ip", ipOrDomain)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, 0, fmt.Errorf("failed to resolve IP address: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ip := ipAddr.IP.String()
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
conn, err := net.Dial("ip:icmp", ip)
|
||||||
|
if err != nil {
|
||||||
|
return ip, 0, 0, fmt.Errorf("failed to establish ICMP connection: %v", err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
icmpMsg := []byte{8, 0, 0, 0, 0, 1, 0, 0}
|
||||||
|
_, err = conn.Write(icmpMsg)
|
||||||
|
if err != nil {
|
||||||
|
return ip, 0, 0, fmt.Errorf("failed to send ICMP message: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
reply := make([]byte, 1500)
|
||||||
|
err = conn.SetReadDeadline(time.Now().Add(3 * time.Second))
|
||||||
|
if err != nil {
|
||||||
|
return ip, 0, 0, fmt.Errorf("failed to set read deadline: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = conn.Read(reply)
|
||||||
|
if err != nil {
|
||||||
|
return ip, 0, 0, fmt.Errorf("failed to read ICMP reply: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
elapsed := time.Since(start)
|
||||||
|
pingTime := elapsed.Round(time.Millisecond)
|
||||||
|
|
||||||
|
ttl := int(reply[8])
|
||||||
|
|
||||||
|
return ip, pingTime, ttl, nil
|
||||||
|
}
|
212
src/mod/netutils/traceroute.go
Normal file
212
src/mod/netutils/traceroute.go
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
package netutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/net/icmp"
|
||||||
|
"golang.org/x/net/ipv4"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
protocolICMP = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// liveTraceRoute return realtime tracing information to live response handler
|
||||||
|
func liveTraceRoute(dst string, maxHops int, liveRespHandler func(string)) error {
|
||||||
|
timeout := time.Second * 3
|
||||||
|
// resolve the host name to an IP address
|
||||||
|
ipAddr, err := net.ResolveIPAddr("ip4", dst)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to resolve IP address for %s: %v", dst, err)
|
||||||
|
}
|
||||||
|
// create a socket to listen for incoming ICMP packets
|
||||||
|
conn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create ICMP listener: %v", err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
id := os.Getpid() & 0xffff
|
||||||
|
seq := 0
|
||||||
|
loop_ttl:
|
||||||
|
for ttl := 1; ttl <= maxHops; ttl++ {
|
||||||
|
// set the TTL on the socket
|
||||||
|
if err := conn.IPv4PacketConn().SetTTL(ttl); err != nil {
|
||||||
|
return fmt.Errorf("failed to set TTL: %v", err)
|
||||||
|
}
|
||||||
|
seq++
|
||||||
|
// create an ICMP message
|
||||||
|
msg := icmp.Message{
|
||||||
|
Type: ipv4.ICMPTypeEcho,
|
||||||
|
Code: 0,
|
||||||
|
Body: &icmp.Echo{
|
||||||
|
ID: id,
|
||||||
|
Seq: seq,
|
||||||
|
Data: []byte("zoraxy_trace"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// serialize the ICMP message
|
||||||
|
msgBytes, err := msg.Marshal(nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to serialize ICMP message: %v", err)
|
||||||
|
}
|
||||||
|
// send the ICMP message
|
||||||
|
start := time.Now()
|
||||||
|
if _, err := conn.WriteTo(msgBytes, ipAddr); err != nil {
|
||||||
|
//log.Printf("%d: %v", ttl, err)
|
||||||
|
liveRespHandler(fmt.Sprintf("%d: %v", ttl, err))
|
||||||
|
continue loop_ttl
|
||||||
|
}
|
||||||
|
// listen for the reply
|
||||||
|
replyBytes := make([]byte, 1500)
|
||||||
|
if err := conn.SetReadDeadline(time.Now().Add(timeout)); err != nil {
|
||||||
|
return fmt.Errorf("failed to set read deadline: %v", err)
|
||||||
|
}
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
n, peer, err := conn.ReadFrom(replyBytes)
|
||||||
|
if err != nil {
|
||||||
|
if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() {
|
||||||
|
//fmt.Printf("%d: *\n", ttl)
|
||||||
|
liveRespHandler(fmt.Sprintf("%d: *\n", ttl))
|
||||||
|
continue loop_ttl
|
||||||
|
} else {
|
||||||
|
liveRespHandler(fmt.Sprintf("%d: Failed to parse ICMP message: %v", ttl, err))
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// parse the ICMP message
|
||||||
|
replyMsg, err := icmp.ParseMessage(protocolICMP, replyBytes[:n])
|
||||||
|
if err != nil {
|
||||||
|
liveRespHandler(fmt.Sprintf("%d: Failed to parse ICMP message: %v", ttl, err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// check if the reply is an echo reply
|
||||||
|
if replyMsg.Type == ipv4.ICMPTypeEchoReply {
|
||||||
|
echoReply, ok := msg.Body.(*icmp.Echo)
|
||||||
|
if !ok || echoReply.ID != id || echoReply.Seq != seq {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
liveRespHandler(fmt.Sprintf("%d: %v %v\n", ttl, peer, time.Since(start)))
|
||||||
|
break loop_ttl
|
||||||
|
}
|
||||||
|
if replyMsg.Type == ipv4.ICMPTypeTimeExceeded {
|
||||||
|
echoReply, ok := msg.Body.(*icmp.Echo)
|
||||||
|
if !ok || echoReply.ID != id || echoReply.Seq != seq {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var raddr = peer.String()
|
||||||
|
names, _ := net.LookupAddr(raddr)
|
||||||
|
if len(names) > 0 {
|
||||||
|
raddr = names[0] + " (" + raddr + ")"
|
||||||
|
} else {
|
||||||
|
raddr = raddr + " (" + raddr + ")"
|
||||||
|
}
|
||||||
|
liveRespHandler(fmt.Sprintf("%d: %v %v\n", ttl, raddr, time.Since(start)))
|
||||||
|
continue loop_ttl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard traceroute, return results after complete
|
||||||
|
func traceroute(dst string, maxHops int) ([]string, error) {
|
||||||
|
results := []string{}
|
||||||
|
timeout := time.Second * 3
|
||||||
|
// resolve the host name to an IP address
|
||||||
|
ipAddr, err := net.ResolveIPAddr("ip4", dst)
|
||||||
|
if err != nil {
|
||||||
|
return results, fmt.Errorf("failed to resolve IP address for %s: %v", dst, err)
|
||||||
|
}
|
||||||
|
// create a socket to listen for incoming ICMP packets
|
||||||
|
conn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0")
|
||||||
|
if err != nil {
|
||||||
|
return results, fmt.Errorf("failed to create ICMP listener: %v", err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
id := os.Getpid() & 0xffff
|
||||||
|
seq := 0
|
||||||
|
loop_ttl:
|
||||||
|
for ttl := 1; ttl <= maxHops; ttl++ {
|
||||||
|
// set the TTL on the socket
|
||||||
|
if err := conn.IPv4PacketConn().SetTTL(ttl); err != nil {
|
||||||
|
return results, fmt.Errorf("failed to set TTL: %v", err)
|
||||||
|
}
|
||||||
|
seq++
|
||||||
|
// create an ICMP message
|
||||||
|
msg := icmp.Message{
|
||||||
|
Type: ipv4.ICMPTypeEcho,
|
||||||
|
Code: 0,
|
||||||
|
Body: &icmp.Echo{
|
||||||
|
ID: id,
|
||||||
|
Seq: seq,
|
||||||
|
Data: []byte("zoraxy_trace"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// serialize the ICMP message
|
||||||
|
msgBytes, err := msg.Marshal(nil)
|
||||||
|
if err != nil {
|
||||||
|
return results, fmt.Errorf("failed to serialize ICMP message: %v", err)
|
||||||
|
}
|
||||||
|
// send the ICMP message
|
||||||
|
start := time.Now()
|
||||||
|
if _, err := conn.WriteTo(msgBytes, ipAddr); err != nil {
|
||||||
|
//log.Printf("%d: %v", ttl, err)
|
||||||
|
results = append(results, fmt.Sprintf("%d: %v", ttl, err))
|
||||||
|
continue loop_ttl
|
||||||
|
}
|
||||||
|
// listen for the reply
|
||||||
|
replyBytes := make([]byte, 1500)
|
||||||
|
if err := conn.SetReadDeadline(time.Now().Add(timeout)); err != nil {
|
||||||
|
return results, fmt.Errorf("failed to set read deadline: %v", err)
|
||||||
|
}
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
n, peer, err := conn.ReadFrom(replyBytes)
|
||||||
|
if err != nil {
|
||||||
|
if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() {
|
||||||
|
//fmt.Printf("%d: *\n", ttl)
|
||||||
|
results = append(results, fmt.Sprintf("%d: *", ttl))
|
||||||
|
continue loop_ttl
|
||||||
|
} else {
|
||||||
|
results = append(results, fmt.Sprintf("%d: Failed to parse ICMP message: %v", ttl, err))
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// parse the ICMP message
|
||||||
|
replyMsg, err := icmp.ParseMessage(protocolICMP, replyBytes[:n])
|
||||||
|
if err != nil {
|
||||||
|
results = append(results, fmt.Sprintf("%d: Failed to parse ICMP message: %v", ttl, err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// check if the reply is an echo reply
|
||||||
|
if replyMsg.Type == ipv4.ICMPTypeEchoReply {
|
||||||
|
echoReply, ok := msg.Body.(*icmp.Echo)
|
||||||
|
if !ok || echoReply.ID != id || echoReply.Seq != seq {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
results = append(results, fmt.Sprintf("%d: %v %v", ttl, peer, time.Since(start)))
|
||||||
|
break loop_ttl
|
||||||
|
}
|
||||||
|
if replyMsg.Type == ipv4.ICMPTypeTimeExceeded {
|
||||||
|
echoReply, ok := msg.Body.(*icmp.Echo)
|
||||||
|
if !ok || echoReply.ID != id || echoReply.Seq != seq {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var raddr = peer.String()
|
||||||
|
names, _ := net.LookupAddr(raddr)
|
||||||
|
if len(names) > 0 {
|
||||||
|
raddr = names[0] + " (" + raddr + ")"
|
||||||
|
} else {
|
||||||
|
raddr = raddr + " (" + raddr + ")"
|
||||||
|
}
|
||||||
|
results = append(results, fmt.Sprintf("%d: %v %v", ttl, raddr, time.Since(start)))
|
||||||
|
continue loop_ttl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return results, nil
|
||||||
|
}
|
100
src/mod/pathrule/handler.go
Normal file
100
src/mod/pathrule/handler.go
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package pathrule
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
uuid "github.com/satori/go.uuid"
|
||||||
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
handler.go
|
||||||
|
|
||||||
|
This script handles pathblock api
|
||||||
|
*/
|
||||||
|
|
||||||
|
func (h *Handler) HandleListBlockingPath(w http.ResponseWriter, r *http.Request) {
|
||||||
|
js, _ := json.Marshal(h.BlockingPaths)
|
||||||
|
utils.SendJSONResponse(w, string(js))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) HandleAddBlockingPath(w http.ResponseWriter, r *http.Request) {
|
||||||
|
matchingPath, err := utils.PostPara(r, "matchingPath")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "invalid matching path given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
exactMatch, err := utils.PostPara(r, "exactMatch")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "invalid exact match value given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
statusCodeString, err := utils.PostPara(r, "statusCode")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "invalid status code given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
statusCode, err := strconv.Atoi(statusCodeString)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "invalid status code given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
enabled, err := utils.PostPara(r, "enabled")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "invalid enabled value given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
caseSensitive, err := utils.PostPara(r, "caseSensitive")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "invalid case sensitive value given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
targetBlockingPath := BlockingPath{
|
||||||
|
UUID: uuid.NewV4().String(),
|
||||||
|
MatchingPath: matchingPath,
|
||||||
|
ExactMatch: exactMatch == "true",
|
||||||
|
StatusCode: statusCode,
|
||||||
|
CustomHeaders: http.Header{},
|
||||||
|
CustomHTML: []byte(""),
|
||||||
|
Enabled: enabled == "true",
|
||||||
|
CaseSenitive: caseSensitive == "true",
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.AddBlockingPath(&targetBlockingPath)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.SendOK(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) HandleRemoveBlockingPath(w http.ResponseWriter, r *http.Request) {
|
||||||
|
blockerUUID, err := utils.PostPara(r, "uuid")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "invalid uuid given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
targetRule := h.GetPathBlockerFromUUID(blockerUUID)
|
||||||
|
if targetRule == nil {
|
||||||
|
//Not found
|
||||||
|
utils.SendErrorResponse(w, "target path blocker not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.RemoveBlockingPathByUUID(blockerUUID)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
utils.SendOK(w)
|
||||||
|
}
|
175
src/mod/pathrule/pathrule.go
Normal file
175
src/mod/pathrule/pathrule.go
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
package pathrule
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Pathblock.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
|
||||||
|
*/
|
||||||
|
|
||||||
|
type Options struct {
|
||||||
|
ConfigFolder string //The folder to store the path blocking config files
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlockingPath struct {
|
||||||
|
UUID string
|
||||||
|
MatchingPath string
|
||||||
|
ExactMatch bool
|
||||||
|
StatusCode int
|
||||||
|
CustomHeaders http.Header
|
||||||
|
CustomHTML []byte
|
||||||
|
Enabled bool
|
||||||
|
CaseSenitive bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Handler struct {
|
||||||
|
Options *Options
|
||||||
|
BlockingPaths []*BlockingPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new path blocker handler
|
||||||
|
func NewPathBlocker(options *Options) *Handler {
|
||||||
|
//Create folder if not exists
|
||||||
|
if !utils.FileExists(options.ConfigFolder) {
|
||||||
|
os.Mkdir(options.ConfigFolder, 0775)
|
||||||
|
}
|
||||||
|
|
||||||
|
//Load the configs from file
|
||||||
|
//TODO
|
||||||
|
|
||||||
|
return &Handler{
|
||||||
|
Options: options,
|
||||||
|
BlockingPaths: []*BlockingPath{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) ListBlockingPath() []*BlockingPath {
|
||||||
|
return h.BlockingPaths
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the blocker from matching path (path match, ignore tailing slash)
|
||||||
|
func (h *Handler) GetPathBlockerFromMatchingPath(matchingPath string) *BlockingPath {
|
||||||
|
for _, blocker := range h.BlockingPaths {
|
||||||
|
if blocker.MatchingPath == matchingPath {
|
||||||
|
return blocker
|
||||||
|
} else if strings.TrimSuffix(blocker.MatchingPath, "/") == strings.TrimSuffix(matchingPath, "/") {
|
||||||
|
return blocker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) GetPathBlockerFromUUID(UUID string) *BlockingPath {
|
||||||
|
for _, blocker := range h.BlockingPaths {
|
||||||
|
if blocker.UUID == UUID {
|
||||||
|
return blocker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) AddBlockingPath(pathBlocker *BlockingPath) error {
|
||||||
|
//Check if the blocker exists
|
||||||
|
blockerPath := pathBlocker.MatchingPath
|
||||||
|
targetBlocker := h.GetPathBlockerFromMatchingPath(blockerPath)
|
||||||
|
if targetBlocker != nil {
|
||||||
|
//Blocker with the same matching path already exists
|
||||||
|
return errors.New("path blocker with the same path already exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
h.BlockingPaths = append(h.BlockingPaths, pathBlocker)
|
||||||
|
|
||||||
|
//Write the new config to file
|
||||||
|
return h.SaveBlockerToFile(pathBlocker)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) RemoveBlockingPathByUUID(uuid string) error {
|
||||||
|
newBlockingList := []*BlockingPath{}
|
||||||
|
for _, thisBlocker := range h.BlockingPaths {
|
||||||
|
if thisBlocker.UUID != uuid {
|
||||||
|
newBlockingList = append(newBlockingList, thisBlocker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(h.BlockingPaths) == len(newBlockingList) {
|
||||||
|
//Nothing is removed
|
||||||
|
return errors.New("given matching path blocker not exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
h.BlockingPaths = newBlockingList
|
||||||
|
|
||||||
|
return h.RemoveBlockerFromFile(uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) SaveBlockerToFile(pathBlocker *BlockingPath) error {
|
||||||
|
saveFilename := filepath.Join(h.Options.ConfigFolder, pathBlocker.UUID)
|
||||||
|
js, _ := json.MarshalIndent(pathBlocker, "", " ")
|
||||||
|
return os.WriteFile(saveFilename, js, 0775)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) RemoveBlockerFromFile(uuid string) error {
|
||||||
|
expectedConfigFile := filepath.Join(h.Options.ConfigFolder, uuid)
|
||||||
|
if !utils.FileExists(expectedConfigFile) {
|
||||||
|
return errors.New("config file not found on disk")
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.Remove(expectedConfigFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all the matching blockers for the given URL path
|
||||||
|
// return all the path blockers and the max length matching rule
|
||||||
|
func (h *Handler) GetMatchingBlockers(urlPath string) ([]*BlockingPath, *BlockingPath) {
|
||||||
|
urlPath = strings.TrimSuffix(urlPath, "/")
|
||||||
|
matchingBlockers := []*BlockingPath{}
|
||||||
|
var longestMatchingPrefix *BlockingPath = nil
|
||||||
|
for _, thisBlocker := range h.BlockingPaths {
|
||||||
|
if thisBlocker.Enabled == false {
|
||||||
|
//This blocker is not enabled. Ignore this
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
incomingURLPath := urlPath
|
||||||
|
matchingPath := strings.TrimSuffix(thisBlocker.MatchingPath, "/")
|
||||||
|
|
||||||
|
if !thisBlocker.CaseSenitive {
|
||||||
|
//This is not case sensitive
|
||||||
|
incomingURLPath = strings.ToLower(incomingURLPath)
|
||||||
|
matchingPath = strings.ToLower(matchingPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if matchingPath == incomingURLPath {
|
||||||
|
//This blocker have exact url path match
|
||||||
|
matchingBlockers = append(matchingBlockers, thisBlocker)
|
||||||
|
if longestMatchingPrefix == nil || len(thisBlocker.MatchingPath) > len(longestMatchingPrefix.MatchingPath) {
|
||||||
|
longestMatchingPrefix = thisBlocker
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !thisBlocker.ExactMatch && strings.HasPrefix(incomingURLPath, matchingPath) {
|
||||||
|
//This blocker have prefix url match
|
||||||
|
matchingBlockers = append(matchingBlockers, thisBlocker)
|
||||||
|
if longestMatchingPrefix == nil || len(thisBlocker.MatchingPath) > len(longestMatchingPrefix.MatchingPath) {
|
||||||
|
longestMatchingPrefix = thisBlocker
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchingBlockers, longestMatchingPrefix
|
||||||
|
}
|
@ -38,6 +38,14 @@ func ReverseProxtInit() {
|
|||||||
log.Println("TLS mode disabled. Serving proxy request with plain http")
|
log.Println("TLS mode disabled. Serving proxy request with plain http")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
forceLatestTLSVersion := false
|
||||||
|
sysdb.Read("settings", "forceLatestTLS", &forceLatestTLSVersion)
|
||||||
|
if forceLatestTLSVersion {
|
||||||
|
log.Println("Force latest TLS mode enabled. Minimum TLS LS version is set to v1.2")
|
||||||
|
} else {
|
||||||
|
log.Println("Force latest TLS mode disabled. Minimum TLS version is set to v1.0")
|
||||||
|
}
|
||||||
|
|
||||||
forceHttpsRedirect := false
|
forceHttpsRedirect := false
|
||||||
sysdb.Read("settings", "redirect", &forceHttpsRedirect)
|
sysdb.Read("settings", "redirect", &forceHttpsRedirect)
|
||||||
if forceHttpsRedirect {
|
if forceHttpsRedirect {
|
||||||
@ -50,6 +58,7 @@ func ReverseProxtInit() {
|
|||||||
HostUUID: nodeUUID,
|
HostUUID: nodeUUID,
|
||||||
Port: inboundPort,
|
Port: inboundPort,
|
||||||
UseTls: useTls,
|
UseTls: useTls,
|
||||||
|
ForceTLSLatest: forceLatestTLSVersion,
|
||||||
ForceHttpsRedirect: forceHttpsRedirect,
|
ForceHttpsRedirect: forceHttpsRedirect,
|
||||||
TlsManager: tlsCertManager,
|
TlsManager: tlsCertManager,
|
||||||
RedirectRuleTable: redirectTable,
|
RedirectRuleTable: redirectTable,
|
||||||
|
20
src/start.go
20
src/start.go
@ -15,6 +15,7 @@ import (
|
|||||||
"imuslab.com/zoraxy/mod/geodb"
|
"imuslab.com/zoraxy/mod/geodb"
|
||||||
"imuslab.com/zoraxy/mod/mdns"
|
"imuslab.com/zoraxy/mod/mdns"
|
||||||
"imuslab.com/zoraxy/mod/netstat"
|
"imuslab.com/zoraxy/mod/netstat"
|
||||||
|
"imuslab.com/zoraxy/mod/pathrule"
|
||||||
"imuslab.com/zoraxy/mod/sshprox"
|
"imuslab.com/zoraxy/mod/sshprox"
|
||||||
"imuslab.com/zoraxy/mod/statistic"
|
"imuslab.com/zoraxy/mod/statistic"
|
||||||
"imuslab.com/zoraxy/mod/statistic/analytic"
|
"imuslab.com/zoraxy/mod/statistic/analytic"
|
||||||
@ -67,7 +68,7 @@ func startupSequence() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Create a redirection rule table
|
//Create a redirection rule table
|
||||||
redirectTable, err = redirection.NewRuleTable("./rules")
|
redirectTable, err = redirection.NewRuleTable("./rules/redirect")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -93,6 +94,17 @@ func startupSequence() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Path Blocker
|
||||||
|
|
||||||
|
This section of starutp script start the pathblocker
|
||||||
|
from file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pathRuleHandler = pathrule.NewPathBlocker(&pathrule.Options{
|
||||||
|
ConfigFolder: "./rules/pathblock",
|
||||||
|
})
|
||||||
|
|
||||||
/*
|
/*
|
||||||
MDNS Discovery Service
|
MDNS Discovery Service
|
||||||
|
|
||||||
@ -177,3 +189,9 @@ func startupSequence() {
|
|||||||
//Create an analytic loader
|
//Create an analytic loader
|
||||||
AnalyticLoader = analytic.NewDataLoader(sysdb, statisticCollector)
|
AnalyticLoader = analytic.NewDataLoader(sysdb, statisticCollector)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This sequence start after everything is initialized
|
||||||
|
func finalSequence() {
|
||||||
|
//Start ACME renew agent
|
||||||
|
acmeRegisterSpecialRoutingRule()
|
||||||
|
}
|
||||||
|
@ -1082,24 +1082,51 @@
|
|||||||
|
|
||||||
//Check if a input is a valid IP address, wildcard of a IP address or a CIDR string
|
//Check if a input is a valid IP address, wildcard of a IP address or a CIDR string
|
||||||
function isValidIpFilter(input) {
|
function isValidIpFilter(input) {
|
||||||
// Check if input is a valid IP address
|
// Check if input is a valid IPv4 address
|
||||||
const isValidIp = /^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$/.test(input);
|
const isValidIPv4 = /^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$/.test(input);
|
||||||
|
|
||||||
if (isValidIp) {
|
if (isValidIPv4) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if input is a wildcard IP address
|
// Check if input is a valid IPv4 wildcard address
|
||||||
const isValidWildcardIp = /^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|\*|\*\/[0-9]|[01]?[0-9]?[0-9]-[01]?[0-9]?[0-9]|\[\d+,\d+\])\.([01]?[0-9]?[0-9]|\*|\*\/[0-9]|[01]?[0-9]?[0-9]-[01]?[0-9]?[0-9]|\[\d+,\d+\])\.([01]?[0-9]?[0-9]|\*|\*\/[0-9]|[01]?[0-9]?[0-9]-[01]?[0-9]?[0-9]|\[\d+,\d+\])$/.test(input);
|
const isValidIPv4Wildcard = /^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|\*|\*\/[0-9]|[01]?[0-9]?[0-9]-[01]?[0-9]?[0-9]|\[\d+,\d+\])\.([01]?[0-9]?[0-9]|\*|\*\/[0-9]|[01]?[0-9]?[0-9]-[01]?[0-9]?[0-9]|\[\d+,\d+\])\.([01]?[0-9]?[0-9]|\*|\*\/[0-9]|[01]?[0-9]?[0-9]-[01]?[0-9]?[0-9]|\[\d+,\d+\])$/.test(input);
|
||||||
|
|
||||||
if (isValidWildcardIp) {
|
if (isValidIPv4Wildcard) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if input is a valid CIDR address string
|
// Check if input is a valid IPv4 CIDR address
|
||||||
const isValidCidr = /^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\/([0-9]|[1-2][0-9]|3[0-2])$/.test(input);
|
const isValidIPv4CIDR = /^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\/([0-9]|[1-2][0-9]|3[0-2])$/.test(input);
|
||||||
|
|
||||||
if (isValidCidr) {
|
if (isValidIPv4CIDR) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Check if input is loopback ipv6
|
||||||
|
if (input == "::1"){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if input is a valid IPv6 address
|
||||||
|
const isValidIPv6 = /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/.test(input);
|
||||||
|
|
||||||
|
if (isValidIPv6) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Pure magic, I have no idea how this works
|
||||||
|
//src: https://stackoverflow.com/questions/70348674/alternate-solution-validate-ipv4-and-ipv6-with-wildcard-characters-using-r
|
||||||
|
function evalIp6(t){var e=t.split(":"),n=t.split("::").length-1;if(8<e.length&&(9!=t.split(":").length||""!=e[e.length-1]||1!=n))return!1;if(1<n)return!1;if(-1!=t.indexOf("::*")||-1!=t.indexOf("*::"))return!1;var r=!1;for(let t=0;t<e.length;t++){if(!isIPV6Group(e[t]))return!1;"*"==e[t]&&(r=!0)}return!(!r&&0==n&&8!=e.length)}function isIPV6Group(t){var e="^(([0-9A-Fa-f]{1,4})|\\*|)$";return(e=new RegExp(e)).test(t)}
|
||||||
|
if (evalIp6(input)){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Check if input is a valid IPv6 CIDR address
|
||||||
|
const isValidIPv6CIDR = /^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$/.test(input);
|
||||||
|
if (isValidIPv6CIDR) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,13 +12,41 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ui bottom attached tab segment nettoolstab active" data-tab="tab1">
|
<div class="ui bottom attached tab segment nettoolstab active" data-tab="tab1">
|
||||||
|
<!-- MDNS Scanner-->
|
||||||
<h2>Multicast DNS (mDNS) Scanner</h2>
|
<h2>Multicast DNS (mDNS) Scanner</h2>
|
||||||
<p>Discover mDNS enabled service in this gateway forwarded network</p>
|
<p>Discover mDNS enabled service in this gateway forwarded network</p>
|
||||||
<button class="ui basic larger circular button" onclick="launchToolWithSize('./tools/mdns.html',1000, 640);">View Discovery</button>
|
<button class="ui basic larger circular button" onclick="launchToolWithSize('./tools/mdns.html',1000, 640);">View Discovery</button>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
|
<!-- IP Scanner-->
|
||||||
<h2>IP Scanner</h2>
|
<h2>IP Scanner</h2>
|
||||||
<p>Discover local area network devices by pinging them one by one</p>
|
<p>Discover local area network devices by pinging them one by one</p>
|
||||||
<button class="ui basic larger circular button" onclick="launchToolWithSize('./tools/ipscan.html',1000, 640);">Start Scanner</button>
|
<button class="ui basic larger circular button" onclick="launchToolWithSize('./tools/ipscan.html',1000, 640);">Start Scanner</button>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<!-- Traceroute-->
|
||||||
|
<h2>Traceroute / Ping</h2>
|
||||||
|
<p>Trace the network nodes that your packets hops through</p>
|
||||||
|
<div class="ui form">
|
||||||
|
<div class="two fields">
|
||||||
|
<div class="field">
|
||||||
|
<label>Target domain or IP</label>
|
||||||
|
<input type="text" id="traceroute_domain" placeholder="1.1.1.1">
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Max Hops</label>
|
||||||
|
<input type="number" min="1" step="1" id="traceroute_maxhops" placeholder="64" value="64">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="ui basic button" onclick="traceroute();"><i class="ui blue location arrow icon"></i> Start Tracing</button>
|
||||||
|
<button class="ui basic button" onclick="ping();"><i class="ui teal circle outline icon"></i> Ping</button>
|
||||||
|
<br><br>
|
||||||
|
<div class="field">
|
||||||
|
<label>Results</label>
|
||||||
|
<textarea id="traceroute_results" rows="10" style=""></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class=""></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ui bottom attached tab segment nettoolstab" data-tab="tab2">
|
<div class="ui bottom attached tab segment nettoolstab" data-tab="tab2">
|
||||||
@ -435,7 +463,32 @@ function updateMDNSListForWoL(){
|
|||||||
}
|
}
|
||||||
updateMDNSListForWoL();
|
updateMDNSListForWoL();
|
||||||
|
|
||||||
|
function traceroute(){
|
||||||
|
let domain = $("#traceroute_domain").val().trim();
|
||||||
|
let maxhops = $("#traceroute_maxhops").val().trim();
|
||||||
|
$("#traceroute_results").val("Loading...");
|
||||||
|
$.get("/api/tools/traceroute?target=" + domain + "&maxhops=" + maxhops, function(data){
|
||||||
|
if (data.error != undefined){
|
||||||
|
$("#traceroute_results").val("");
|
||||||
|
msgbox(data.error, false, 6000);
|
||||||
|
}else{
|
||||||
|
$("#traceroute_results").val(data.join("\n"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function ping(){
|
||||||
|
let domain = $("#traceroute_domain").val().trim();
|
||||||
|
$("#traceroute_results").val("Loading...");
|
||||||
|
$.get("/api/tools/ping?target=" + domain, function(data){
|
||||||
|
if (data.error != undefined){
|
||||||
|
$("#traceroute_results").val("");
|
||||||
|
msgbox(data.error, false, 6000);
|
||||||
|
}else{
|
||||||
|
$("#traceroute_results").val(data.join("\n"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,78 +1,100 @@
|
|||||||
|
|
||||||
<div class="standardContainer">
|
<div class="standardContainer">
|
||||||
<div class="ui basic segment">
|
<div class="ui basic segment">
|
||||||
<h2>Redirection Rules</h2>
|
<h2>Redirection Rules</h2>
|
||||||
<p>Add exception case for redirecting any matching URLs</p>
|
<p>Add exception case for redirecting any matching URLs</p>
|
||||||
</div>
|
</div>
|
||||||
<div style="width: 100%; overflow-x: auto;">
|
<div style="width: 100%; overflow-x: auto;">
|
||||||
<table class="ui sortable unstackable celled table" >
|
<table class="ui sortable unstackable celled table" >
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Redirection URL</th>
|
<th>Redirection URL</th>
|
||||||
<th>Destination URL</th>
|
<th>Destination URL</th>
|
||||||
<th class="no-sort">Copy Pathname</th>
|
<th class="no-sort">Copy Pathname</th>
|
||||||
<th class="no-sort">Status Code</th>
|
<th class="no-sort">Status Code</th>
|
||||||
<th class="no-sort">Remove</th>
|
<th class="no-sort">Remove</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="redirectionRuleList">
|
<tbody id="redirectionRuleList">
|
||||||
<tr>
|
<tr>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui green message" id="delRuleSucc" style="display:none;">
|
<div class="ui green message" id="delRuleSucc" style="display:none;">
|
||||||
<i class="ui green checkmark icon"></i> Redirection Rule Deleted
|
<i class="ui green checkmark icon"></i> Redirection Rule Deleted
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<h4>Add Redirection Rule</h4>
|
<h4>Add Redirection Rule</h4>
|
||||||
<div class="ui form">
|
<div class="ui form">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Redirection URL (From)</label>
|
<label>Redirection URL (From)</label>
|
||||||
<input type="text" id="rurl" name="redirection-url" placeholder="Redirection URL">
|
<input type="text" id="rurl" name="redirection-url" placeholder="Redirection URL">
|
||||||
<small><i class="ui circle info icon"></i> Any matching prefix of the request URL will be redirected to the destination URL, e.g. redirect.example.com</small>
|
<small><i class="ui circle info icon"></i> Any matching prefix of the request URL will be redirected to the destination URL, e.g. redirect.example.com</small>
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label>Destination URL (To)</label>
|
|
||||||
<input type="text" name="destination-url" placeholder="Destination URL">
|
|
||||||
<small><i class="ui circle info icon"></i> The target URL request being redirected to, e.g. dest.example.com/mysite</small>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="ui checkbox">
|
|
||||||
<input type="checkbox" name="forward-childpath" tabindex="0" class="hidden" checked>
|
|
||||||
<label>Forward Pathname</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="ui message">
|
<div class="field">
|
||||||
<p>Append the current pathname after the redirect destination</p>
|
<label>Destination URL (To)</label>
|
||||||
<i class="check square outline icon"></i> old.example.com<b>/blog?post=13</b> <i class="long arrow alternate right icon" style="margin-left: 1em;"></i> new.example.com<b>/blog?post=13</b> <br>
|
<input type="text" name="destination-url" placeholder="Destination URL">
|
||||||
<i class="square outline icon"></i> old.example.com<b>/blog?post=13</b> <i class="long arrow alternate right icon" style="margin-left: 1em;"></i> new.example.com
|
<small><i class="ui circle info icon"></i> The target URL request being redirected to, e.g. dest.example.com/mysite</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="field">
|
||||||
<div class="grouped fields">
|
<div class="ui checkbox">
|
||||||
<label>Redirection Status Code</label>
|
<input type="checkbox" name="forward-childpath" tabindex="0" class="hidden" checked>
|
||||||
<div class="field">
|
<label>Forward Pathname</label>
|
||||||
<div class="ui radio checkbox">
|
</div>
|
||||||
<input type="radio" name="redirect-type" value="307" checked>
|
<div class="ui message">
|
||||||
<label>Temporary Redirect <br><small>Status Code: 307</small></label>
|
<p>Append the current pathname after the redirect destination</p>
|
||||||
|
<i class="check square outline icon"></i> old.example.com<b>/blog?post=13</b> <i class="long arrow alternate right icon" style="margin-left: 1em;"></i> new.example.com<b>/blog?post=13</b> <br>
|
||||||
|
<i class="square outline icon"></i> old.example.com<b>/blog?post=13</b> <i class="long arrow alternate right icon" style="margin-left: 1em;"></i> new.example.com
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="grouped fields">
|
||||||
|
<label>Redirection Status Code</label>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui radio checkbox">
|
||||||
|
<input type="radio" name="redirect-type" value="307" checked>
|
||||||
|
<label>Temporary Redirect <br><small>Status Code: 307</small></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui radio checkbox">
|
||||||
|
<input type="radio" name="redirect-type" value="301">
|
||||||
|
<label>Moved Permanently <br><small>Status Code: 301</small></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="ui basic button" onclick="addRules();"><i class="ui teal plus icon"></i> Add Redirection Rule</button>
|
||||||
|
<div class="ui green message" id="ruleAddSucc" style="display:none;">
|
||||||
|
<i class="ui green checkmark icon"></i> Redirection Rules Added
|
||||||
|
</div>
|
||||||
|
<br><br>
|
||||||
|
<!--
|
||||||
|
<div class="advancezone ui basic segment">
|
||||||
|
<div class="ui accordion advanceSettings">
|
||||||
|
<div class="title">
|
||||||
|
<i class="dropdown icon"></i>
|
||||||
|
Advance Options
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<p>If you need custom header, content or status code other than basic redirects, you can use the advance path rules editor.</p>
|
||||||
|
<button class="ui black basic button" onclick="createAdvanceRules();"><i class="ui black external icon"></i> Open Advance Rules Editor</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
</div>
|
||||||
<div class="ui radio checkbox">
|
-->
|
||||||
<input type="radio" name="redirect-type" value="301">
|
</div>
|
||||||
<label>Moved Permanently <br><small>Status Code: 301</small></label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button class="ui basic button" onclick="addRules();"><i class="ui teal plus icon"></i> Add Redirection Rule</button>
|
|
||||||
<div class="ui green message" id="ruleAddSucc" style="display:none;">
|
|
||||||
<i class="ui green checkmark icon"></i> Redirection Rules Added
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
|
$(".advanceSettings").accordion();
|
||||||
|
|
||||||
|
/*
|
||||||
|
Redirection functions
|
||||||
|
*/
|
||||||
$(".checkbox").checkbox();
|
$(".checkbox").checkbox();
|
||||||
|
|
||||||
function resetForm() {
|
function resetForm() {
|
||||||
@ -129,6 +151,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createAdvanceRules(){
|
||||||
|
showSideWrapper("snippet/advancePathRules.html?t=" + Date.now(), true);
|
||||||
|
}
|
||||||
|
|
||||||
function initRedirectionRuleList(){
|
function initRedirectionRuleList(){
|
||||||
$("#redirectionRuleList").html("");
|
$("#redirectionRuleList").html("");
|
||||||
$.get("/api/redirect/list", function(data){
|
$.get("/api/redirect/list", function(data){
|
||||||
|
@ -72,11 +72,30 @@
|
|||||||
<label>Use TLS to serve proxy request</label>
|
<label>Use TLS to serve proxy request</label>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div id="redirect" class="ui toggle notloopbackOnly checkbox" style="margin-top: 0.6em;">
|
<div id="redirect" class="ui toggle notloopbackOnly tlsEnabledOnly checkbox" style="margin-top: 0.6em;">
|
||||||
<input type="checkbox">
|
<input type="checkbox">
|
||||||
<label>Force redirect HTTP request to HTTPS<br>
|
<label>Force redirect HTTP request to HTTPS<br>
|
||||||
<small>(Only apply when listening port is not 80)</small></label>
|
<small>(Only apply when listening port is not 80)</small></label>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="ui basic segment" style="background-color: #f7f7f7; border-radius: 1em;">
|
||||||
|
<div class="ui accordion advanceSettings">
|
||||||
|
<div class="title">
|
||||||
|
<i class="dropdown icon"></i>
|
||||||
|
Advance Settings
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<p>If you have no idea what are these, you can leave them as default :)</p>
|
||||||
|
<div id="tlsMinVer" class="ui toggle notloopbackOnly tlsEnabledOnly checkbox" style="margin-top: 0.6em;">
|
||||||
|
<input type="checkbox">
|
||||||
|
<label>Force TLS v1.2 or above<br>
|
||||||
|
<small>(Enhance security, but not compatible with legacy browsers)</small></label>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<br><br>
|
<br><br>
|
||||||
<button id="startbtn" class="ui teal button" onclick="startService();">Start Service</button>
|
<button id="startbtn" class="ui teal button" onclick="startService();">Start Service</button>
|
||||||
<button id="stopbtn" class="ui red notloopbackOnly disabled button" onclick="stopService();">Stop Service</button>
|
<button id="stopbtn" class="ui red notloopbackOnly disabled button" onclick="stopService();">Stop Service</button>
|
||||||
@ -128,6 +147,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
let loopbackProxiedInterface = false;
|
let loopbackProxiedInterface = false;
|
||||||
|
$(".advanceSettings").accordion();
|
||||||
|
|
||||||
//Initial the start stop button if this is reverse proxied
|
//Initial the start stop button if this is reverse proxied
|
||||||
$.get("/api/proxy/requestIsProxied", function(data){
|
$.get("/api/proxy/requestIsProxied", function(data){
|
||||||
if (data == true){
|
if (data == true){
|
||||||
@ -340,21 +361,50 @@
|
|||||||
}
|
}
|
||||||
initHTTPtoHTTPSRedirectSetting();
|
initHTTPtoHTTPSRedirectSetting();
|
||||||
|
|
||||||
|
function initTlsVersionSetting(){
|
||||||
|
$.get("/api/cert/tlsRequireLatest", function(data){
|
||||||
|
if (data == true){
|
||||||
|
$("#tlsMinVer").checkbox("set checked");
|
||||||
|
}else{
|
||||||
|
$("#tlsMinVer").checkbox("set unchecked");
|
||||||
|
}
|
||||||
|
|
||||||
|
//Bind events to the checkbox
|
||||||
|
$("#tlsMinVer").find("input").on("change", function(){
|
||||||
|
let thisValue = $("#tlsMinVer").checkbox("is checked");
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/cert/tlsRequireLatest",
|
||||||
|
data: {"set": thisValue},
|
||||||
|
success: function(data){
|
||||||
|
if (data.error != undefined){
|
||||||
|
msgbox(data.error, false, 5000);
|
||||||
|
}else{
|
||||||
|
msgbox("TLS Version Setting Updated");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
initTlsVersionSetting();
|
||||||
|
|
||||||
function initTlsSetting(){
|
function initTlsSetting(){
|
||||||
$.get("/api/cert/tls", function(data){
|
$.get("/api/cert/tls", function(data){
|
||||||
if (data == true){
|
if (data == true){
|
||||||
$("#tls").checkbox("set checked");
|
$("#tls").checkbox("set checked");
|
||||||
}else{
|
}else{
|
||||||
$("#redirect").addClass('disabled');
|
$(".tlsEnabledOnly").addClass('disabled');
|
||||||
|
$(".tlsEnabledOnly").addClass('disabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
//Initiate the input listener on the checkbox
|
//Initiate the input listener on the checkbox
|
||||||
$("#tls").find("input").on("change", function(){
|
$("#tls").find("input").on("change", function(){
|
||||||
let thisValue = $("#tls").checkbox("is checked");
|
let thisValue = $("#tls").checkbox("is checked");
|
||||||
if (thisValue){
|
if (thisValue){
|
||||||
$("#redirect").removeClass('disabled');
|
$(".tlsEnabledOnly").removeClass('disabled');
|
||||||
}else{
|
}else{
|
||||||
$("#redirect").addClass('disabled');
|
$(".tlsEnabledOnly").addClass('disabled');
|
||||||
}
|
}
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/api/cert/tls",
|
url: "/api/cert/tls",
|
||||||
@ -364,7 +414,27 @@
|
|||||||
alert(data.error);
|
alert(data.error);
|
||||||
}else{
|
}else{
|
||||||
//Updated
|
//Updated
|
||||||
msgbox("Setting Updated");
|
|
||||||
|
//Check for case if the port is invalid default ports
|
||||||
|
if ($("#incomingPort").val() == "80" && thisValue == true){
|
||||||
|
confirmBox("Change listen port to :443?", function(choice){
|
||||||
|
if (choice == true){
|
||||||
|
$("#incomingPort").val("443");
|
||||||
|
handlePortChange();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}else if ($("#incomingPort").val() == "443" && thisValue == false){
|
||||||
|
confirmBox("Change listen port to :80?", function(choice){
|
||||||
|
if (choice == true){
|
||||||
|
$("#incomingPort").val("80");
|
||||||
|
handlePortChange();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
msgbox("Setting Updated");
|
||||||
|
}
|
||||||
|
|
||||||
initRPStaste();
|
initRPStaste();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,11 +42,75 @@
|
|||||||
<button id="addTcpProxyButton" class="ui basic button" type="submit"><i class="ui blue add icon"></i> Create</button>
|
<button id="addTcpProxyButton" class="ui basic button" type="submit"><i class="ui blue add icon"></i> Create</button>
|
||||||
<button id="editTcpProxyButton" class="ui basic button" onclick="confirmEditTCPProxyConfig(event);"><i class="ui blue save icon"></i> Update</button>
|
<button id="editTcpProxyButton" class="ui basic button" onclick="confirmEditTCPProxyConfig(event);"><i class="ui blue save icon"></i> Update</button>
|
||||||
<button class="ui basic red button" onclick="event.preventDefault(); cancelTCPProxyEdit(event);"><i class="ui red remove icon"></i> Cancel</button>
|
<button class="ui basic red button" onclick="event.preventDefault(); cancelTCPProxyEdit(event);"><i class="ui red remove icon"></i> Cancel</button>
|
||||||
|
<div class="ui basic inverted segment" style="background-color: #414141; border-radius: 0.6em;">
|
||||||
|
<h3>Proxy Mode Instructions</h3>
|
||||||
|
<p>TCP Proxy support the following TCP sockets proxy modes</p>
|
||||||
|
<table class="ui celled padded inverted basic table">
|
||||||
|
<thead>
|
||||||
|
<tr><th class="single line">Mode</th>
|
||||||
|
<th>Public-IP</th>
|
||||||
|
<th>Concurrent Access</th>
|
||||||
|
<th>Flow Diagram</th>
|
||||||
|
</tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<h4 class="ui center aligned inverted header">Transport</h4>
|
||||||
|
</td>
|
||||||
|
<td class="single line">
|
||||||
|
Server: <i class="ui green check icon"></i><br>
|
||||||
|
A: <i class="ui remove icon"></i><br>
|
||||||
|
B: <i class="ui green check icon"></i> (or same LAN)<br>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<i class="ui green check icon"></i>
|
||||||
|
</td>
|
||||||
|
<td>Port A (e.g. 25565) <i class="arrow right icon"></i> Server<br>
|
||||||
|
Server <i class="arrow right icon"></i> Port B (e.g. 192.168.0.2:25565)<br>
|
||||||
|
<small>Traffic from Port A will be forward to Port B's (IP if provided and) Port</small>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<h4 class="ui center aligned inverted header">Listen</h4>
|
||||||
|
</td>
|
||||||
|
<td class="single line">
|
||||||
|
Server: <i class="ui green check icon"></i><br>
|
||||||
|
A: <i class="ui remove icon"></i><br>
|
||||||
|
B: <i class="ui remove icon"></i><br>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<i class="ui red times icon"></i>
|
||||||
|
</td>
|
||||||
|
<td>Port A (e.g. 8080) <i class="arrow right icon"></i> Server<br>
|
||||||
|
Port B (e.g. 8081) <i class="arrow right icon"></i> Server<br>
|
||||||
|
<small>Server will act as a bridge to proxy traffic between Port A and B</small>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<h4 class="ui center aligned inverted header">Starter</h4>
|
||||||
|
</td>
|
||||||
|
<td class="single line">
|
||||||
|
Server: <i class="ui times icon"></i><br>
|
||||||
|
A: <i class="ui green check icon"></i><br>
|
||||||
|
B: <i class="ui green check icon"></i><br>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<i class="ui red times icon"></i>
|
||||||
|
</td>
|
||||||
|
<td>Server <i class="arrow right icon"></i> Port A (e.g. remote.local.:8080) <br>
|
||||||
|
Server <i class="arrow right icon"></i> Port B (e.g. recv.local.:8081) <br>
|
||||||
|
<small>Port A and B will be actively bridged</small>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui basic segment">
|
<div class="ui basic segment" style="margin-top: 0;">
|
||||||
<h3>TCP Proxy Configs</h3>
|
<h3>TCP Proxy Configs</h3>
|
||||||
<p>A list of TCP proxy configs created on this host. To enable them, use the toggle button on the right.</p>
|
<p>A list of TCP proxy configs created on this host. To enable them, use the toggle button on the right.</p>
|
||||||
<div style="overflow-x: auto; min-height: 400px;">
|
<div style="overflow-x: auto; min-height: 400px;">
|
||||||
@ -67,72 +131,7 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui basic inverted segment" style="background-color: #414141; border-radius: 0.6em;">
|
</div>
|
||||||
<h3>Proxy Mode</h3>
|
|
||||||
<p>TCP Proxy support the following TCP sockets proxy modes</p>
|
|
||||||
<table class="ui celled padded inverted basic table">
|
|
||||||
<thead>
|
|
||||||
<tr><th class="single line">Mode</th>
|
|
||||||
<th>Public-IP</th>
|
|
||||||
<th>Concurrent Access</th>
|
|
||||||
<th>Flow Diagram</th>
|
|
||||||
</tr></thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<h4 class="ui center aligned inverted header">Transport</h4>
|
|
||||||
</td>
|
|
||||||
<td class="single line">
|
|
||||||
Server: <i class="ui green check icon"></i><br>
|
|
||||||
A: <i class="ui remove icon"></i><br>
|
|
||||||
B: <i class="ui green check icon"></i> (or same LAN)<br>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<i class="ui green check icon"></i>
|
|
||||||
</td>
|
|
||||||
<td>Port A (e.g. 25565) <i class="arrow right icon"></i> Server<br>
|
|
||||||
Server <i class="arrow right icon"></i> Port B (e.g. 192.168.0.2:25565)<br>
|
|
||||||
<small>Traffic from Port A will be forward to Port B's (IP if provided and) Port</small>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<h4 class="ui center aligned inverted header">Listen</h4>
|
|
||||||
</td>
|
|
||||||
<td class="single line">
|
|
||||||
Server: <i class="ui green check icon"></i><br>
|
|
||||||
A: <i class="ui remove icon"></i><br>
|
|
||||||
B: <i class="ui remove icon"></i><br>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<i class="ui red times icon"></i>
|
|
||||||
</td>
|
|
||||||
<td>Port A (e.g. 8080) <i class="arrow right icon"></i> Server<br>
|
|
||||||
Port B (e.g. 8081) <i class="arrow right icon"></i> Server<br>
|
|
||||||
<small>Server will act as a bridge to proxy traffic between Port A and B</small>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<h4 class="ui center aligned inverted header">Starter</h4>
|
|
||||||
</td>
|
|
||||||
<td class="single line">
|
|
||||||
Server: <i class="ui times icon"></i><br>
|
|
||||||
A: <i class="ui green check icon"></i><br>
|
|
||||||
B: <i class="ui green check icon"></i><br>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<i class="ui red times icon"></i>
|
|
||||||
</td>
|
|
||||||
<td>Server <i class="arrow right icon"></i> Port A (e.g. remote.local.:8080) <br>
|
|
||||||
Server <i class="arrow right icon"></i> Port B (e.g. recv.local.:8081) <br>
|
|
||||||
<small>Port A and B will be actively bridged</small>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script>
|
<script>
|
||||||
let editingTCPProxyConfigUUID = ""; //The current editing TCP Proxy config UUID
|
let editingTCPProxyConfigUUID = ""; //The current editing TCP Proxy config UUID
|
||||||
|
|
||||||
|
@ -148,6 +148,17 @@
|
|||||||
<p><i class="green check circle icon"></i> There are no message</p>
|
<p><i class="green check circle icon"></i> There are no message</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="confirmBox" style="display:none;">
|
||||||
|
<div class="ui top attached progress">
|
||||||
|
<div class="bar" style="width: 100%; min-width: 0px;"></div>
|
||||||
|
</div>
|
||||||
|
<div class="confirmBoxBody">
|
||||||
|
<button class="ui red basic mini circular icon right floated button" style="margin-left: 0.4em;"><i class="ui times icon"></i></button>
|
||||||
|
<button class="ui green basic mini circular icon right floated button"><i class="ui check icon"></i></button>
|
||||||
|
<div class="questionToConfirm">Confirm Exit?</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<br><br>
|
<br><br>
|
||||||
<script>
|
<script>
|
||||||
$(".year").text(new Date().getFullYear());
|
$(".year").text(new Date().getFullYear());
|
||||||
@ -280,6 +291,64 @@
|
|||||||
$("#messageBox").stop().finish().fadeIn("fast").delay(delayDuration).fadeOut("fast");
|
$("#messageBox").stop().finish().fadeIn("fast").delay(delayDuration).fadeOut("fast");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function confirmBox(question_to_confirm, callback, delaytime=15) {
|
||||||
|
|
||||||
|
var progressBar = $("#confirmBox .bar");
|
||||||
|
var questionElement = $("#confirmBox .questionToConfirm");
|
||||||
|
|
||||||
|
//Just to make sure there are no animation runnings
|
||||||
|
progressBar.stop();
|
||||||
|
|
||||||
|
// Update the question to confirm
|
||||||
|
questionElement.text(question_to_confirm);
|
||||||
|
|
||||||
|
// Start the progress bar animation
|
||||||
|
progressBar.css("width", "100%");
|
||||||
|
progressBar.animate({ width: "0%", easing: "linear" }, delaytime * 1000, function() {
|
||||||
|
// Animation complete, invoke the callback with undefined
|
||||||
|
callback(undefined);
|
||||||
|
|
||||||
|
//Unset the event listener
|
||||||
|
$("#confirmBox .ui.green.button").off("click");
|
||||||
|
|
||||||
|
// Hide the confirm box
|
||||||
|
$("#confirmBox").hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Bind click event to "Yes" button
|
||||||
|
$("#confirmBox .ui.green.button").on("click", function() {
|
||||||
|
// Stop the progress bar animation
|
||||||
|
progressBar.stop();
|
||||||
|
|
||||||
|
// Invoke the callback with true
|
||||||
|
callback(true);
|
||||||
|
|
||||||
|
// Hide the confirm box
|
||||||
|
$("#confirmBox").hide();
|
||||||
|
|
||||||
|
//Unset the event listener
|
||||||
|
$("#confirmBox .ui.green.button").off("click");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Bind click event to "No" button
|
||||||
|
$("#confirmBox .ui.red.button").on("click", function() {
|
||||||
|
// Stop the progress bar animation
|
||||||
|
progressBar.stop();
|
||||||
|
|
||||||
|
// Invoke the callback with false
|
||||||
|
callback(false);
|
||||||
|
|
||||||
|
// Hide the confirm box
|
||||||
|
$("#confirmBox").hide();
|
||||||
|
|
||||||
|
//Unset the event listener
|
||||||
|
$("#confirmBox .ui.red.button").off("click");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Show the confirm box
|
||||||
|
$("#confirmBox").show().transition('jiggle');
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Toggles for side wrapper
|
Toggles for side wrapper
|
||||||
*/
|
*/
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
--theme_lgrey: #f6f6f6;
|
--theme_lgrey: #f6f6f6;
|
||||||
--theme_green: #3c9c63;
|
--theme_green: #3c9c63;
|
||||||
--theme_fcolor: #979797;
|
--theme_fcolor: #979797;
|
||||||
|
--theme_advance: #f8f8f9;
|
||||||
}
|
}
|
||||||
body{
|
body{
|
||||||
background-color:#f6f6f6;
|
background-color:#f6f6f6;
|
||||||
@ -16,6 +17,15 @@ body{
|
|||||||
display:none;
|
display:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.advance{
|
||||||
|
background: var(--theme_advance) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.advancezone{
|
||||||
|
background: var(--theme_advance) !important;
|
||||||
|
border-radius: 1em !important;
|
||||||
|
}
|
||||||
|
|
||||||
.menubar{
|
.menubar{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0.4em;
|
padding: 0.4em;
|
||||||
@ -89,6 +99,34 @@ body{
|
|||||||
z-index: 999;
|
z-index: 999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Confirm Box */
|
||||||
|
#confirmBox{
|
||||||
|
position: fixed;
|
||||||
|
z-index: 999;
|
||||||
|
bottom: 1em;
|
||||||
|
right: 1em;
|
||||||
|
min-width: 300px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: rgb(65, 65, 65);
|
||||||
|
box-shadow: 10px 10px 5px -2px rgba(0,0,0,0.13);
|
||||||
|
}
|
||||||
|
|
||||||
|
#confirmBox .confirmBoxBody{
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#confirmBox .ui.progress .bar{
|
||||||
|
background: #ffe32b !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#confirmBox .confirmBoxBody .button{
|
||||||
|
margin-top: -0.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#confirmBox .questionToConfirm{
|
||||||
|
margin-top: -0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
/* Standard containers */
|
/* Standard containers */
|
||||||
.standardContainer{
|
.standardContainer{
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -553,3 +591,17 @@ body{
|
|||||||
.GANetMember.unauthorized{
|
.GANetMember.unauthorized{
|
||||||
border-left: 6px solid #9c3c3c !important;
|
border-left: 6px solid #9c3c3c !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Network Utilities
|
||||||
|
*/
|
||||||
|
|
||||||
|
#traceroute_results{
|
||||||
|
resize: none;
|
||||||
|
background-color: #202020;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#traceroute_results::selection {
|
||||||
|
background: #a9d1f3;
|
||||||
|
}
|
76
src/web/snippet/advancePathRules.html
Normal file
76
src/web/snippet/advancePathRules.html
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!-- Notes: This should be open in its original path-->
|
||||||
|
<link rel="stylesheet" href="../script/semantic/semantic.min.css">
|
||||||
|
<script src="../script/jquery-3.6.0.min.js"></script>
|
||||||
|
<script src="../script/semantic/semantic.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<br>
|
||||||
|
<div class="ui container">
|
||||||
|
<!-- Path Rules -->
|
||||||
|
<div class="ui header">
|
||||||
|
<div class="content">
|
||||||
|
Special Path Rules
|
||||||
|
<div class="sub header">Advanced customization for response on particular matching path or URL</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h4>Current list of special path rules.</h4>
|
||||||
|
<div style="width: 100%; overflow-x: auto;">
|
||||||
|
<table class="ui sortable unstackable celled table" >
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Matching Path</th>
|
||||||
|
<th>Status Code</th>
|
||||||
|
<th class="no-sort">Exact Match</th>
|
||||||
|
<th class="no-sort">Case Sensitive</th>
|
||||||
|
<th class="no-sort">Enabled</th>
|
||||||
|
<th class="no-sort">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="specialPathRules">
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<h4>Add Special Path Rule</h4>
|
||||||
|
<div class="ui form">
|
||||||
|
<div class="field">
|
||||||
|
<label>Matching URI</label>
|
||||||
|
<input type="text" name="matchingPath" placeholder="Matching URL">
|
||||||
|
<small><i class="ui circle info icon"></i> Any matching prefix of the request URL will be handled by this rule, e.g. example.com/secret</small>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input type="checkbox" name="exactMatch" tabindex="0" class="hidden">
|
||||||
|
<label>Require Exact Match</label>
|
||||||
|
</div>
|
||||||
|
<div class="ui message">
|
||||||
|
<p>Require exactly match but not prefix match (default). Enable this if you only want to block access to a directory but not the resources inside the directory. Assume you have entered a matching URI of <b>example.com/secret/</b> and set it to return 401</p>
|
||||||
|
<i class="check square outline icon"></i> example.com/secret<b>/image.png</b> <i class="long arrow alternate right icon" style="margin-left: 1em;"></i> (content of image.png)<br>
|
||||||
|
<i class="square outline icon"></i> example.com/secret<b>/image.png</b> <i class="long arrow alternate right icon" style="margin-left: 1em;"></i> HTTP 401
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Response Status Code</label>
|
||||||
|
<input type="text"name="statusCode" placeholder="500">
|
||||||
|
<small><i class="ui circle info icon"></i> HTTP Status Code to be served by this rule</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br><br>
|
||||||
|
<button class="ui basic button iframeOnly" style="float: right;" onclick="parent.hideSideWrapper();"><i class="remove icon"></i> Cancel</button>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
x
Reference in New Issue
Block a user