vtun/common/netutil/netutil.go
2023-07-14 14:46:03 +08:00

250 lines
6.2 KiB
Go

package netutil
import (
"context"
"crypto/tls"
"log"
"net"
"net/http"
"net/url"
"os/exec"
"strings"
"time"
"github.com/gobwas/ws"
"github.com/net-byte/go-gateway"
"github.com/net-byte/vtun/common/config"
"github.com/net-byte/vtun/common/counter"
)
// ConnectServer connects to the server with the given address.
func ConnectServer(config config.Config) net.Conn {
scheme := "ws"
host := config.ServerAddr
if config.Protocol == "wss" {
scheme = "wss"
if config.TLSSni != "" {
host = config.TLSSni
}
}
u := url.URL{Scheme: scheme, Host: host, Path: config.WebSocketPath}
header := make(http.Header)
header.Set("user-agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36")
if config.Key != "" {
header.Set("key", config.Key)
}
tlsConfig := &tls.Config{
InsecureSkipVerify: config.TLSInsecureSkipVerify,
}
if config.TLSSni != "" {
tlsConfig.ServerName = config.TLSSni
}
dialer := ws.Dialer{
Header: ws.HandshakeHeaderHTTP(header),
Timeout: time.Duration(config.Timeout) * time.Second,
TLSConfig: tlsConfig,
NetDial: func(ctx context.Context, network, addr string) (net.Conn, error) {
return net.Dial(network, config.ServerAddr)
},
}
c, _, _, err := dialer.Dial(context.Background(), u.String())
if err != nil {
log.Printf("[client] failed to dial websocket %s %v", u.String(), err)
return nil
}
return c
}
// GetInterfaceName returns the name of interface
func GetInterface() (name string) {
ifaces := getAllInterfaces()
if len(ifaces) == 0 {
return ""
}
netAddrs, _ := ifaces[0].Addrs()
for _, addr := range netAddrs {
ip, ok := addr.(*net.IPNet)
if ok && ip.IP.To4() != nil && !ip.IP.IsLoopback() {
name = ifaces[0].Name
break
}
}
return name
}
// getAllInterfaces returns all interfaces
func getAllInterfaces() []net.Interface {
ifaces, err := net.Interfaces()
if err != nil {
log.Println(err)
return nil
}
var outInterfaces []net.Interface
for _, iface := range ifaces {
if iface.Flags&net.FlagLoopback == 0 && iface.Flags&net.FlagUp == 1 && isPhysicalInterface(iface.Name) {
netAddrs, _ := iface.Addrs()
if len(netAddrs) > 0 {
outInterfaces = append(outInterfaces, iface)
}
}
}
return outInterfaces
}
// isPhysicalInterface returns true if the interface is physical
func isPhysicalInterface(addr string) bool {
prefixArray := []string{"ens", "enp", "enx", "eno", "eth", "en0", "wlan", "wlp", "wlo", "wlx", "wifi0", "lan0"}
for _, pref := range prefixArray {
if strings.HasPrefix(strings.ToLower(addr), pref) {
return true
}
}
return false
}
// Lookup IP address of the given hostname
func LookupIP(domain string) net.IP {
ips, err := net.LookupIP(domain)
if err != nil || len(ips) == 0 {
log.Println(err)
return nil
}
return ips[0]
}
// IsIPv4 returns true if the packet is IPv4s
func IsIPv4(packet []byte) bool {
flag := packet[0] >> 4
return flag == 4
}
// IsIPv6 returns true if the packet is IPv6s
func IsIPv6(packet []byte) bool {
flag := packet[0] >> 4
return flag == 6
}
// GetIPv4Src returns the IPv4 source address of the packet
func GetIPv4Src(packet []byte) net.IP {
return net.IPv4(packet[12], packet[13], packet[14], packet[15])
}
// GEtIPv4Dst returns the IPv4 destination address of the packet
func GetIPv4Dst(packet []byte) net.IP {
return net.IPv4(packet[16], packet[17], packet[18], packet[19])
}
// GetIPv6Src returns the IPv6 source address of the packet
func GetIPv6Src(packet []byte) net.IP {
return net.IP(packet[8:24])
}
// GetIPv6Dst returns the IPv6 destination address of the packet
func GetIPv6Dst(packet []byte) net.IP {
return net.IP(packet[24:40])
}
// GetSrcKey returns the source key of the packet
func GetSrcKey(packet []byte) string {
key := ""
if IsIPv4(packet) && len(packet) >= 20 {
key = GetIPv4Src(packet).To4().String()
} else if IsIPv6(packet) && len(packet) >= 40 {
key = GetIPv6Src(packet).To16().String()
}
return key
}
// GetDstKey returns the destination key of the packets
func GetDstKey(packet []byte) string {
key := ""
if IsIPv4(packet) && len(packet) >= 20 {
key = GetIPv4Dst(packet).To4().String()
} else if IsIPv6(packet) && len(packet) >= 40 {
key = GetIPv6Dst(packet).To16().String()
}
return key
}
// ExecCmd executes the given command
func ExecCmd(c string, args ...string) string {
//log.Printf("exec %v %v", c, args)
cmd := exec.Command(c, args...)
out, err := cmd.Output()
if err != nil {
log.Println("failed to exec cmd:", err)
}
if len(out) == 0 {
return ""
}
s := string(out)
return strings.ReplaceAll(s, "\n", "")
}
// DiscoverGateway returns the local gateway IP address
func DiscoverGateway(ipv4 bool) string {
var ip net.IP
var err error
if ipv4 {
ip, err = gateway.DiscoverGatewayIPv4()
} else {
ip, err = gateway.DiscoverGatewayIPv6()
}
if err != nil {
log.Println(err)
return ""
}
return ip.String()
}
// LookupServerAddrIP returns the IP of server address
func LookupServerAddrIP(serverAddr string) net.IP {
host, _, err := net.SplitHostPort(serverAddr)
if err != nil {
log.Panic("error server address")
return nil
}
ip := LookupIP(host)
return ip
}
// GetDefaultHttpResponse returns the default http response
func GetDefaultHttpResponse() []byte {
return []byte("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 6\r\nConnection: keep-alive\r\nCache-Control: no-cache\r\nCF-Cache-Status: DYNAMIC\r\nServer: cloudflare\r\n\r\nfollow")
}
func GetDefaultHttpHandleFunc() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "text/plain")
w.Header().Set("Content-Length", "6")
w.Header().Set("Connection", "keep-alive")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("CF-Cache-Status", "DYNAMIC")
w.Header().Set("Server", "cloudflare")
w.Write([]byte("follow"))
})
}
// PrintErr returns the error log
func PrintErr(err error, enableVerbose bool) {
if !enableVerbose {
return
}
log.Printf("error:%v", err)
}
// PrintStats returns the stats info
func PrintStats(enableVerbose bool, serverMode bool) {
if !enableVerbose {
return
}
go func() {
for {
time.Sleep(30 * time.Second)
log.Printf("stats:%v", counter.PrintBytes(serverMode))
}
}()
}