diff --git a/client/client.go b/client/udpclient.go similarity index 78% rename from client/client.go rename to client/udpclient.go index e3e2a00..2959027 100644 --- a/client/client.go +++ b/client/udpclient.go @@ -7,10 +7,11 @@ import ( "github.com/net-byte/vtun/common/cipher" "github.com/net-byte/vtun/common/config" "github.com/net-byte/vtun/tun" + "github.com/songgao/water/waterutil" ) -// Start client -func Start(config config.Config) { +// StartUDPClient start udp client +func StartUDPClient(config config.Config) { config.Init() iface := tun.CreateTun(config.CIDR) serverAddr, err := net.ResolveUDPAddr("udp", config.ServerAddr) @@ -26,7 +27,7 @@ func Start(config config.Config) { log.Fatalln("failed to listen on UDP socket:", err) } defer conn.Close() - log.Printf("vtun client started on %v,CIDR is %v", config.LocalAddr, config.CIDR) + log.Printf("vtun udp client started on %v,CIDR is %v", config.LocalAddr, config.CIDR) // read data from server go func() { buf := make([]byte, 1500) @@ -37,6 +38,9 @@ func Start(config config.Config) { } // decrypt data b := cipher.Decrypt(buf[:n]) + if !waterutil.IsIPv4(b) { + continue + } iface.Write(b) } }() @@ -47,6 +51,9 @@ func Start(config config.Config) { if err != nil || n == 0 { continue } + if !waterutil.IsIPv4(packet) { + continue + } // encrypt data b := cipher.Encrypt(packet[:n]) conn.WriteToUDP(b, serverAddr) diff --git a/client/wsclient.go b/client/wsclient.go new file mode 100644 index 0000000..60a132a --- /dev/null +++ b/client/wsclient.go @@ -0,0 +1,74 @@ +package client + +import ( + "fmt" + "io" + "log" + "time" + + "github.com/gorilla/websocket" + "github.com/net-byte/vtun/common/cipher" + "github.com/net-byte/vtun/common/config" + "github.com/net-byte/vtun/common/netutil" + "github.com/net-byte/vtun/tun" + "github.com/patrickmn/go-cache" + "github.com/songgao/water" + "github.com/songgao/water/waterutil" +) + +// StartWSClient start ws client +func StartWSClient(config config.Config) { + config.Init() + iface := tun.CreateTun(config.CIDR) + c := cache.New(30*time.Minute, 10*time.Minute) + log.Printf("vtun ws client started,CIDR is %v", config.CIDR) + // read data from tun + packet := make([]byte, 1500) + for { + n, err := iface.Read(packet) + if err != nil || n == 0 { + continue + } + b := packet[:n] + if !waterutil.IsIPv4(b) { + continue + } + srcAddr := netutil.SrcAddr(b) + dstAddr := netutil.DstAddr(b) + if srcAddr == "" || dstAddr == "" { + continue + } + key := fmt.Sprintf("%v->%v", dstAddr, srcAddr) + var conn *websocket.Conn + v, ok := c.Get(key) + if ok { + conn = v.(*websocket.Conn) + } else { + conn = netutil.ConnectWS(config) + if conn == nil { + continue + } + c.Set(key, conn, cache.DefaultExpiration) + go wsToTun(c, key, conn, iface) + } + b = cipher.Encrypt(b) + conn.WriteMessage(websocket.BinaryMessage, b) + } +} + +func wsToTun(c *cache.Cache, key string, wsConn *websocket.Conn, iface *water.Interface) { + defer netutil.CloseWS(wsConn) + for { + wsConn.SetReadDeadline(time.Now().Add(time.Duration(30) * time.Second)) + _, b, err := wsConn.ReadMessage() + if err != nil || err == io.EOF { + break + } + b = cipher.Decrypt(b) + if !waterutil.IsIPv4(b) { + continue + } + iface.Write(b[:]) + } + c.Delete(key) +} diff --git a/common/netutil/netutil.go b/common/netutil/netutil.go new file mode 100644 index 0000000..6f81320 --- /dev/null +++ b/common/netutil/netutil.go @@ -0,0 +1,56 @@ +package netutil + +import ( + "fmt" + "log" + "net/http" + "net/url" + "time" + + "github.com/gorilla/websocket" + "github.com/net-byte/vtun/common/config" + "github.com/songgao/water/waterutil" +) + +func SrcAddr(b []byte) string { + if waterutil.IPv4Protocol(b) == waterutil.UDP || waterutil.IPv4Protocol(b) == waterutil.TCP { + ip := waterutil.IPv4Source(b) + port := waterutil.IPv4SourcePort(b) + addr := fmt.Sprintf("%s:%d", ip.To4().String(), port) + return addr + } else if waterutil.IPv4Protocol(b) == waterutil.ICMP { + ip := waterutil.IPv4Source(b) + return ip.To4().String() + } + return "" +} + +func DstAddr(b []byte) string { + if waterutil.IPv4Protocol(b) == waterutil.UDP || waterutil.IPv4Protocol(b) == waterutil.TCP { + ip := waterutil.IPv4Destination(b) + port := waterutil.IPv4DestinationPort(b) + addr := fmt.Sprintf("%s:%d", ip.To4().String(), port) + return addr + } else if waterutil.IPv4Protocol(b) == waterutil.ICMP { + ip := waterutil.IPv4Destination(b) + return ip.To4().String() + } + return "" +} + +func ConnectWS(config config.Config) *websocket.Conn { + u := url.URL{Scheme: "wss", Host: config.ServerAddr, Path: "/way-to-freedom"} + 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") + c, _, err := websocket.DefaultDialer.Dial(u.String(), header) + if err != nil { + log.Printf("[client] failed to dial websocket %v", err) + return nil + } + return c +} + +func CloseWS(wsConn *websocket.Conn) { + wsConn.WriteControl(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""), time.Now().Add(time.Second*5)) + wsConn.Close() +} diff --git a/main.go b/main.go index a0df02b..08e6906 100644 --- a/main.go +++ b/main.go @@ -18,16 +18,19 @@ func main() { flag.BoolVar(&config.ServerMode, "S", false, "server mode") flag.Parse() - if config.ServerMode { - switch config.Protocol { - case "udp": + switch config.Protocol { + case "udp": + if config.ServerMode { server.StartUDPServer(config) - case "ws": - server.StartWSServer(config) - default: + } else { + client.StartUDPClient(config) } - } else { - client.Start(config) + case "ws": + if config.ServerMode { + server.StartWSServer(config) + } else { + client.StartWSClient(config) + } + default: } - } diff --git a/server/udpserver.go b/server/udpserver.go index f3a36a2..fa642b9 100644 --- a/server/udpserver.go +++ b/server/udpserver.go @@ -8,6 +8,7 @@ import ( "github.com/net-byte/vtun/common/cipher" "github.com/net-byte/vtun/common/config" + "github.com/net-byte/vtun/common/netutil" "github.com/net-byte/vtun/tun" "github.com/patrickmn/go-cache" "github.com/songgao/water" @@ -44,8 +45,8 @@ func StartUDPServer(config config.Config) { continue } iface.Write(b) - srcAddr := srcAddr(b) - dstAddr := dstAddr(b) + srcAddr := netutil.SrcAddr(b) + dstAddr := netutil.DstAddr(b) if srcAddr == "" || dstAddr == "" { continue } @@ -70,9 +71,9 @@ func (f *Forwarder) forward(iface *water.Interface, conn *net.UDPConn) { if !waterutil.IsIPv4(b) { continue } - dstAddr := dstAddr(b) - srcAddr := srcAddr(b) - if dstAddr == "" || srcAddr == "" { + srcAddr := netutil.SrcAddr(b) + dstAddr := netutil.DstAddr(b) + if srcAddr == "" || dstAddr == "" { continue } key := fmt.Sprintf("%v->%v", dstAddr, srcAddr) @@ -84,29 +85,3 @@ func (f *Forwarder) forward(iface *water.Interface, conn *net.UDPConn) { } } } - -func srcAddr(b []byte) string { - if waterutil.IPv4Protocol(b) == waterutil.UDP || waterutil.IPv4Protocol(b) == waterutil.TCP { - ip := waterutil.IPv4Source(b) - port := waterutil.IPv4SourcePort(b) - addr := fmt.Sprintf("%s:%d", ip.To4().String(), port) - return addr - } else if waterutil.IPv4Protocol(b) == waterutil.ICMP { - ip := waterutil.IPv4Source(b) - return ip.To4().String() - } - return "" -} - -func dstAddr(b []byte) string { - if waterutil.IPv4Protocol(b) == waterutil.UDP || waterutil.IPv4Protocol(b) == waterutil.TCP { - ip := waterutil.IPv4Destination(b) - port := waterutil.IPv4DestinationPort(b) - addr := fmt.Sprintf("%s:%d", ip.To4().String(), port) - return addr - } else if waterutil.IPv4Protocol(b) == waterutil.ICMP { - ip := waterutil.IPv4Destination(b) - return ip.To4().String() - } - return "" -} diff --git a/server/wsserver.go b/server/wsserver.go index ac78abb..074ad95 100644 --- a/server/wsserver.go +++ b/server/wsserver.go @@ -11,6 +11,7 @@ import ( "github.com/gorilla/websocket" "github.com/net-byte/vtun/common/cipher" "github.com/net-byte/vtun/common/config" + "github.com/net-byte/vtun/common/netutil" "github.com/net-byte/vtun/tun" "github.com/patrickmn/go-cache" "github.com/songgao/water" @@ -57,11 +58,6 @@ func StartWSServer(config config.Config) { http.ListenAndServe(config.ServerAddr, nil) } -func closeWS(wsConn *websocket.Conn) { - wsConn.WriteControl(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""), time.Now().Add(time.Second*5)) - wsConn.Close() -} - func tunToWs(iface *water.Interface, c *cache.Cache) { buffer := make([]byte, 1500) for { @@ -73,8 +69,8 @@ func tunToWs(iface *water.Interface, c *cache.Cache) { if !waterutil.IsIPv4(b) { continue } - srcAddr := srcAddr(b) - dstAddr := dstAddr(b) + srcAddr := netutil.SrcAddr(b) + dstAddr := netutil.DstAddr(b) if srcAddr == "" || dstAddr == "" { continue } @@ -88,7 +84,7 @@ func tunToWs(iface *water.Interface, c *cache.Cache) { } func wsToTun(wsConn *websocket.Conn, iface *water.Interface, c *cache.Cache) { - defer closeWS(wsConn) + defer netutil.CloseWS(wsConn) for { wsConn.SetReadDeadline(time.Now().Add(time.Duration(30) * time.Second)) _, b, err := wsConn.ReadMessage() @@ -99,8 +95,8 @@ func wsToTun(wsConn *websocket.Conn, iface *water.Interface, c *cache.Cache) { if !waterutil.IsIPv4(b) { continue } - srcAddr := srcAddr(b) - dstAddr := dstAddr(b) + srcAddr := netutil.SrcAddr(b) + dstAddr := netutil.DstAddr(b) if srcAddr == "" || dstAddr == "" { continue }