support ws

This commit is contained in:
Alex Tsai 2021-04-27 16:41:27 +08:00
parent cec4bcab3b
commit f0f60ef070
6 changed files with 164 additions and 53 deletions

View File

@ -7,10 +7,11 @@ import (
"github.com/net-byte/vtun/common/cipher" "github.com/net-byte/vtun/common/cipher"
"github.com/net-byte/vtun/common/config" "github.com/net-byte/vtun/common/config"
"github.com/net-byte/vtun/tun" "github.com/net-byte/vtun/tun"
"github.com/songgao/water/waterutil"
) )
// Start client // StartUDPClient start udp client
func Start(config config.Config) { func StartUDPClient(config config.Config) {
config.Init() config.Init()
iface := tun.CreateTun(config.CIDR) iface := tun.CreateTun(config.CIDR)
serverAddr, err := net.ResolveUDPAddr("udp", config.ServerAddr) 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) log.Fatalln("failed to listen on UDP socket:", err)
} }
defer conn.Close() 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 // read data from server
go func() { go func() {
buf := make([]byte, 1500) buf := make([]byte, 1500)
@ -37,6 +38,9 @@ func Start(config config.Config) {
} }
// decrypt data // decrypt data
b := cipher.Decrypt(buf[:n]) b := cipher.Decrypt(buf[:n])
if !waterutil.IsIPv4(b) {
continue
}
iface.Write(b) iface.Write(b)
} }
}() }()
@ -47,6 +51,9 @@ func Start(config config.Config) {
if err != nil || n == 0 { if err != nil || n == 0 {
continue continue
} }
if !waterutil.IsIPv4(packet) {
continue
}
// encrypt data // encrypt data
b := cipher.Encrypt(packet[:n]) b := cipher.Encrypt(packet[:n])
conn.WriteToUDP(b, serverAddr) conn.WriteToUDP(b, serverAddr)

74
client/wsclient.go Normal file
View File

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

56
common/netutil/netutil.go Normal file
View File

@ -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()
}

21
main.go
View File

@ -18,16 +18,19 @@ func main() {
flag.BoolVar(&config.ServerMode, "S", false, "server mode") flag.BoolVar(&config.ServerMode, "S", false, "server mode")
flag.Parse() flag.Parse()
if config.ServerMode { switch config.Protocol {
switch config.Protocol { case "udp":
case "udp": if config.ServerMode {
server.StartUDPServer(config) server.StartUDPServer(config)
case "ws": } else {
server.StartWSServer(config) client.StartUDPClient(config)
default:
} }
} else { case "ws":
client.Start(config) if config.ServerMode {
server.StartWSServer(config)
} else {
client.StartWSClient(config)
}
default:
} }
} }

View File

@ -8,6 +8,7 @@ import (
"github.com/net-byte/vtun/common/cipher" "github.com/net-byte/vtun/common/cipher"
"github.com/net-byte/vtun/common/config" "github.com/net-byte/vtun/common/config"
"github.com/net-byte/vtun/common/netutil"
"github.com/net-byte/vtun/tun" "github.com/net-byte/vtun/tun"
"github.com/patrickmn/go-cache" "github.com/patrickmn/go-cache"
"github.com/songgao/water" "github.com/songgao/water"
@ -44,8 +45,8 @@ func StartUDPServer(config config.Config) {
continue continue
} }
iface.Write(b) iface.Write(b)
srcAddr := srcAddr(b) srcAddr := netutil.SrcAddr(b)
dstAddr := dstAddr(b) dstAddr := netutil.DstAddr(b)
if srcAddr == "" || dstAddr == "" { if srcAddr == "" || dstAddr == "" {
continue continue
} }
@ -70,9 +71,9 @@ func (f *Forwarder) forward(iface *water.Interface, conn *net.UDPConn) {
if !waterutil.IsIPv4(b) { if !waterutil.IsIPv4(b) {
continue continue
} }
dstAddr := dstAddr(b) srcAddr := netutil.SrcAddr(b)
srcAddr := srcAddr(b) dstAddr := netutil.DstAddr(b)
if dstAddr == "" || srcAddr == "" { if srcAddr == "" || dstAddr == "" {
continue continue
} }
key := fmt.Sprintf("%v->%v", dstAddr, srcAddr) 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 ""
}

View File

@ -11,6 +11,7 @@ import (
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/net-byte/vtun/common/cipher" "github.com/net-byte/vtun/common/cipher"
"github.com/net-byte/vtun/common/config" "github.com/net-byte/vtun/common/config"
"github.com/net-byte/vtun/common/netutil"
"github.com/net-byte/vtun/tun" "github.com/net-byte/vtun/tun"
"github.com/patrickmn/go-cache" "github.com/patrickmn/go-cache"
"github.com/songgao/water" "github.com/songgao/water"
@ -57,11 +58,6 @@ func StartWSServer(config config.Config) {
http.ListenAndServe(config.ServerAddr, nil) 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) { func tunToWs(iface *water.Interface, c *cache.Cache) {
buffer := make([]byte, 1500) buffer := make([]byte, 1500)
for { for {
@ -73,8 +69,8 @@ func tunToWs(iface *water.Interface, c *cache.Cache) {
if !waterutil.IsIPv4(b) { if !waterutil.IsIPv4(b) {
continue continue
} }
srcAddr := srcAddr(b) srcAddr := netutil.SrcAddr(b)
dstAddr := dstAddr(b) dstAddr := netutil.DstAddr(b)
if srcAddr == "" || dstAddr == "" { if srcAddr == "" || dstAddr == "" {
continue continue
} }
@ -88,7 +84,7 @@ func tunToWs(iface *water.Interface, c *cache.Cache) {
} }
func wsToTun(wsConn *websocket.Conn, 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 { for {
wsConn.SetReadDeadline(time.Now().Add(time.Duration(30) * time.Second)) wsConn.SetReadDeadline(time.Now().Add(time.Duration(30) * time.Second))
_, b, err := wsConn.ReadMessage() _, b, err := wsConn.ReadMessage()
@ -99,8 +95,8 @@ func wsToTun(wsConn *websocket.Conn, iface *water.Interface, c *cache.Cache) {
if !waterutil.IsIPv4(b) { if !waterutil.IsIPv4(b) {
continue continue
} }
srcAddr := srcAddr(b) srcAddr := netutil.SrcAddr(b)
dstAddr := dstAddr(b) dstAddr := netutil.DstAddr(b)
if srcAddr == "" || dstAddr == "" { if srcAddr == "" || dstAddr == "" {
continue continue
} }