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:
Toby Chui
2023-06-16 00:48:39 +08:00
parent a73a7944ec
commit 48dc85ea3e
27 changed files with 1425 additions and 174 deletions

View 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))
}

View 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)
}

View 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
}

View 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
}