mirror of
https://github.com/net-byte/vtun
synced 2024-03-14 10:50:03 +08:00
add utls support
add dtls support
This commit is contained in:
parent
4c2f9b47f9
commit
7c8048e7c2
16
app/app.go
16
app/app.go
@ -1,8 +1,10 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"github.com/net-byte/vtun/dtls"
|
||||
"github.com/net-byte/vtun/kcp"
|
||||
"github.com/net-byte/vtun/quic"
|
||||
"github.com/net-byte/vtun/utls"
|
||||
"log"
|
||||
|
||||
"github.com/net-byte/vtun/common/cipher"
|
||||
@ -94,9 +96,21 @@ func (app *App) StartApp() {
|
||||
case "kcp":
|
||||
if app.Config.ServerMode {
|
||||
kcp.StartServer(app.Iface, *app.Config)
|
||||
}else {
|
||||
} else {
|
||||
kcp.StartClient(app.Iface, *app.Config)
|
||||
}
|
||||
case "utls":
|
||||
if app.Config.ServerMode {
|
||||
utls.StartServer(app.Iface, *app.Config)
|
||||
} else {
|
||||
utls.StartClient(app.Iface, *app.Config)
|
||||
}
|
||||
case "dtls":
|
||||
if app.Config.ServerMode {
|
||||
dtls.StartServer(app.Iface, *app.Config)
|
||||
} else {
|
||||
dtls.StartClient(app.Iface, *app.Config)
|
||||
}
|
||||
default:
|
||||
if app.Config.ServerMode {
|
||||
udp.StartServer(app.Iface, *app.Config)
|
||||
|
108
dtls/dtlsclient.go
Normal file
108
dtls/dtlsclient.go
Normal file
@ -0,0 +1,108 @@
|
||||
package dtls
|
||||
|
||||
import (
|
||||
"github.com/pion/dtls"
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/golang/snappy"
|
||||
"github.com/net-byte/vtun/common/cache"
|
||||
"github.com/net-byte/vtun/common/cipher"
|
||||
"github.com/net-byte/vtun/common/config"
|
||||
"github.com/net-byte/vtun/common/counter"
|
||||
"github.com/net-byte/vtun/common/netutil"
|
||||
"github.com/net-byte/water"
|
||||
)
|
||||
|
||||
// StartClient starts the tls client
|
||||
func StartClient(iface *water.Interface, config config.Config) {
|
||||
log.Println("vtun dtls client started")
|
||||
go tunToTLS(config, iface)
|
||||
tlsConfig := &dtls.Config{
|
||||
PSK: func(bytes []byte) ([]byte, error) {
|
||||
return []byte{0x09, 0x46, 0x59, 0x02, 0x49}, nil
|
||||
},
|
||||
PSKIdentityHint: []byte(config.Key),
|
||||
CipherSuites: []dtls.CipherSuiteID{dtls.TLS_PSK_WITH_AES_128_GCM_SHA256, dtls.TLS_PSK_WITH_AES_128_CCM_8},
|
||||
ExtendedMasterSecret: dtls.RequireExtendedMasterSecret,
|
||||
}
|
||||
if config.TLSSni != "" {
|
||||
tlsConfig.ServerName = config.TLSSni
|
||||
}
|
||||
for {
|
||||
addr, err := net.ResolveUDPAddr("udp", config.ServerAddr)
|
||||
if err != nil {
|
||||
time.Sleep(3 * time.Second)
|
||||
netutil.PrintErr(err, config.Verbose)
|
||||
continue
|
||||
}
|
||||
conn, err := dtls.Dial("udp", addr, tlsConfig)
|
||||
if err != nil {
|
||||
time.Sleep(3 * time.Second)
|
||||
netutil.PrintErr(err, config.Verbose)
|
||||
continue
|
||||
}
|
||||
cache.GetCache().Set("dtlsconn", conn, 24*time.Hour)
|
||||
tlsToTun(config, conn, iface)
|
||||
cache.GetCache().Delete("dtlsconn")
|
||||
}
|
||||
}
|
||||
|
||||
// tunToTLS sends packets from tun to tls
|
||||
func tunToTLS(config config.Config, iface *water.Interface) {
|
||||
packet := make([]byte, config.BufferSize)
|
||||
for {
|
||||
n, err := iface.Read(packet)
|
||||
if err != nil {
|
||||
netutil.PrintErr(err, config.Verbose)
|
||||
break
|
||||
}
|
||||
if v, ok := cache.GetCache().Get("dtlsconn"); ok {
|
||||
b := packet[:n]
|
||||
if config.Obfs {
|
||||
b = cipher.XOR(b)
|
||||
}
|
||||
if config.Compress {
|
||||
b = snappy.Encode(nil, b)
|
||||
}
|
||||
tlsconn := v.(net.Conn)
|
||||
_, err = tlsconn.Write(b)
|
||||
if err != nil {
|
||||
netutil.PrintErr(err, config.Verbose)
|
||||
continue
|
||||
}
|
||||
counter.IncrWrittenBytes(n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tlsToTun sends packets from tls to tun
|
||||
func tlsToTun(config config.Config, tlsconn net.Conn, iface *water.Interface) {
|
||||
defer tlsconn.Close()
|
||||
packet := make([]byte, config.BufferSize)
|
||||
for {
|
||||
n, err := tlsconn.Read(packet)
|
||||
if err != nil {
|
||||
netutil.PrintErr(err, config.Verbose)
|
||||
break
|
||||
}
|
||||
b := packet[:n]
|
||||
if config.Compress {
|
||||
b, err = snappy.Decode(nil, b)
|
||||
if err != nil {
|
||||
netutil.PrintErr(err, config.Verbose)
|
||||
break
|
||||
}
|
||||
}
|
||||
if config.Obfs {
|
||||
b = cipher.XOR(b)
|
||||
}
|
||||
_, err = iface.Write(b)
|
||||
if err != nil {
|
||||
netutil.PrintErr(err, config.Verbose)
|
||||
break
|
||||
}
|
||||
counter.IncrReadBytes(n)
|
||||
}
|
||||
}
|
105
dtls/dtlsserver.go
Normal file
105
dtls/dtlsserver.go
Normal file
@ -0,0 +1,105 @@
|
||||
package dtls
|
||||
|
||||
import (
|
||||
"github.com/pion/dtls"
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/golang/snappy"
|
||||
"github.com/net-byte/vtun/common/cache"
|
||||
"github.com/net-byte/vtun/common/cipher"
|
||||
"github.com/net-byte/vtun/common/config"
|
||||
"github.com/net-byte/vtun/common/counter"
|
||||
"github.com/net-byte/vtun/common/netutil"
|
||||
"github.com/net-byte/water"
|
||||
)
|
||||
|
||||
// StartServer starts the tls server
|
||||
func StartServer(iface *water.Interface, config config.Config) {
|
||||
log.Printf("vtun dtls server started on %v", config.LocalAddr)
|
||||
tlsConfig := &dtls.Config{
|
||||
PSK: func(bytes []byte) ([]byte, error) {
|
||||
return []byte{0x09, 0x46, 0x59, 0x02, 0x49}, nil
|
||||
},
|
||||
PSKIdentityHint: []byte(config.Key),
|
||||
CipherSuites: []dtls.CipherSuiteID{dtls.TLS_PSK_WITH_AES_128_GCM_SHA256, dtls.TLS_PSK_WITH_AES_128_CCM_8},
|
||||
ExtendedMasterSecret: dtls.RequireExtendedMasterSecret,
|
||||
}
|
||||
addr, err := net.ResolveUDPAddr("udp", config.LocalAddr)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
ln, err := dtls.Listen("udp", addr, tlsConfig)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
// server -> client
|
||||
go toClient(config, iface)
|
||||
// client -> server
|
||||
for {
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
go toServer(config, conn, iface)
|
||||
}
|
||||
}
|
||||
|
||||
// toClient sends packets from iface to tlsconn
|
||||
func toClient(config config.Config, iface *water.Interface) {
|
||||
packet := make([]byte, config.BufferSize)
|
||||
for {
|
||||
n, err := iface.Read(packet)
|
||||
if err != nil {
|
||||
netutil.PrintErr(err, config.Verbose)
|
||||
continue
|
||||
}
|
||||
b := packet[:n]
|
||||
if key := netutil.GetDstKey(b); key != "" {
|
||||
if v, ok := cache.GetCache().Get(key); ok {
|
||||
if config.Obfs {
|
||||
b = cipher.XOR(b)
|
||||
}
|
||||
if config.Compress {
|
||||
b = snappy.Encode(nil, b)
|
||||
}
|
||||
_, err := v.(net.Conn).Write(b)
|
||||
if err != nil {
|
||||
cache.GetCache().Delete(key)
|
||||
continue
|
||||
}
|
||||
counter.IncrWrittenBytes(n)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// toServer sends packets from tlsconn to iface
|
||||
func toServer(config config.Config, tlsconn net.Conn, iface *water.Interface) {
|
||||
defer tlsconn.Close()
|
||||
packet := make([]byte, config.BufferSize)
|
||||
for {
|
||||
n, err := tlsconn.Read(packet)
|
||||
if err != nil {
|
||||
netutil.PrintErr(err, config.Verbose)
|
||||
break
|
||||
}
|
||||
b := packet[:n]
|
||||
if config.Compress {
|
||||
b, err = snappy.Decode(nil, b)
|
||||
if err != nil {
|
||||
netutil.PrintErr(err, config.Verbose)
|
||||
break
|
||||
}
|
||||
}
|
||||
if config.Obfs {
|
||||
b = cipher.XOR(b)
|
||||
}
|
||||
if key := netutil.GetSrcKey(b); key != "" {
|
||||
cache.GetCache().Set(key, tlsconn, 24*time.Hour)
|
||||
iface.Write(b)
|
||||
counter.IncrReadBytes(n)
|
||||
}
|
||||
}
|
||||
}
|
9
go.mod
9
go.mod
@ -10,26 +10,33 @@ require (
|
||||
github.com/net-byte/water v0.0.9
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/quic-go/quic-go v0.32.0
|
||||
github.com/smallnest/chanx v1.1.0
|
||||
github.com/xtaci/kcp-go v5.4.20+incompatible
|
||||
golang.org/x/crypto v0.4.0
|
||||
golang.org/x/crypto v0.5.0
|
||||
google.golang.org/grpc v1.46.2
|
||||
google.golang.org/protobuf v1.28.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/gaukas/godicttls v0.0.3 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||
github.com/klauspost/compress v1.15.15 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.1.1 // indirect
|
||||
github.com/klauspost/reedsolomon v1.11.3 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
|
||||
github.com/pion/dtls v1.5.4 // indirect
|
||||
github.com/pion/logging v0.2.2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
|
||||
github.com/quic-go/qtls-go1-19 v0.2.0 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
|
||||
github.com/refraction-networking/utls v1.3.2 // indirect
|
||||
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect
|
||||
github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b // indirect
|
||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||
|
19
go.sum
19
go.sum
@ -1,6 +1,8 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
@ -23,6 +25,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/gaukas/godicttls v0.0.3 h1:YNDIf0d9adcxOijiLrEzpfZGAkNwLRzPaG6OjU7EITk=
|
||||
github.com/gaukas/godicttls v0.0.3/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
@ -67,6 +71,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFb
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inhies/go-bytesize v0.0.0-20210819104631-275770b98743 h1:X3Xxno5Ji8idrNiUoFc7QyXpqhSYlDRYQmc7mlpMBzU=
|
||||
github.com/inhies/go-bytesize v0.0.0-20210819104631-275770b98743/go.mod h1:KrtyD5PFj++GKkFS/7/RRrfnRhAMGQwy75GLCHWrCNs=
|
||||
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
|
||||
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
|
||||
github.com/klauspost/cpuid/v2 v2.1.1 h1:t0wUqjowdm8ezddV5k0tLWVklVuvLJpoHeb4WBdydm0=
|
||||
github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/klauspost/reedsolomon v1.11.3 h1:rX9UNNvDhJ0Bq45y6uBy/eYehcjyz5faokTuZmu1Q9U=
|
||||
@ -80,6 +86,11 @@ github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7
|
||||
github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pion/dtls v1.5.4 h1:q8pXFMF7T+EAVO4auQU/ds+5yh5yOK6NiTN/4NQ0dB0=
|
||||
github.com/pion/dtls v1.5.4/go.mod h1:eVHevf4AM8R9+Pxa29q4aiI2iIbfMWOW1WgEcSCGpHU=
|
||||
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
||||
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||
github.com/pion/transport v0.8.10/go.mod h1:tBmha/UCjpum5hqTWhfAEs3CO4/tHSg0MYRhSzR+CZ8=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
@ -93,8 +104,13 @@ github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV5
|
||||
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||
github.com/quic-go/quic-go v0.32.0 h1:lY02md31s1JgPiiyfqJijpu/UX/Iun304FI3yUqX7tA=
|
||||
github.com/quic-go/quic-go v0.32.0/go.mod h1:/fCsKANhQIeD5l76c2JFU+07gVE3KaA0FP+0zMWwfwo=
|
||||
github.com/refraction-networking/utls v1.3.2 h1:o+AkWB57mkcoW36ET7uJ002CpBWHu0KPxi6vzxvPnv8=
|
||||
github.com/refraction-networking/utls v1.3.2/go.mod h1:fmoaOww2bxzzEpIKOebIsnBvjQpqP7L2vcm/9KUfm/E=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/smallnest/chanx v1.1.0 h1:2f/anv7jUuFqeOvCRy4ApmVixWcYUz1ya0vpVx6P7vQ=
|
||||
github.com/smallnest/chanx v1.1.0/go.mod h1:zXoLoNTSzdRiD+XNNsfAj6/jY7ZfBnviL96tVr+tQ3E=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
@ -112,10 +128,13 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
|
||||
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
|
||||
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
|
||||
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
|
||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
|
@ -23,13 +23,13 @@ import (
|
||||
func StartClient(iface *water.Interface, config config.Config) {
|
||||
log.Println("vtun grpc client started")
|
||||
go tunToGrpc(config, iface)
|
||||
tlsconfig := &tls.Config{
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: config.TLSInsecureSkipVerify,
|
||||
}
|
||||
if config.TLSSni != "" {
|
||||
tlsconfig.ServerName = config.TLSSni
|
||||
tlsConfig.ServerName = config.TLSSni
|
||||
}
|
||||
creds := credentials.NewTLS(tlsconfig)
|
||||
creds := credentials.NewTLS(tlsConfig)
|
||||
for {
|
||||
conn, err := grpc.Dial(config.ServerAddr, grpc.WithBlock(), grpc.WithTransportCredentials(creds))
|
||||
if err != nil {
|
||||
|
@ -19,7 +19,7 @@ import (
|
||||
func StartClient(iface *water.Interface, config config.Config) {
|
||||
log.Println("vtun tls client started")
|
||||
go tunToTLS(config, iface)
|
||||
tlsconfig := &tls.Config{
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: config.TLSInsecureSkipVerify,
|
||||
MinVersion: tls.VersionTLS13,
|
||||
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
|
||||
@ -34,10 +34,10 @@ func StartClient(iface *water.Interface, config config.Config) {
|
||||
},
|
||||
}
|
||||
if config.TLSSni != "" {
|
||||
tlsconfig.ServerName = config.TLSSni
|
||||
tlsConfig.ServerName = config.TLSSni
|
||||
}
|
||||
for {
|
||||
conn, err := tls.Dial("tcp", config.ServerAddr, tlsconfig)
|
||||
conn, err := tls.Dial("tcp", config.ServerAddr, tlsConfig)
|
||||
if err != nil {
|
||||
time.Sleep(3 * time.Second)
|
||||
netutil.PrintErr(err, config.Verbose)
|
||||
|
131
utls/sniffer.go
Normal file
131
utls/sniffer.go
Normal file
@ -0,0 +1,131 @@
|
||||
package utls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
|
||||
"github.com/net-byte/vtun/common/netutil"
|
||||
)
|
||||
|
||||
type SniffConn struct {
|
||||
net.Conn
|
||||
rout io.Reader
|
||||
peeked, read bool
|
||||
Type int
|
||||
preData []byte
|
||||
path string
|
||||
}
|
||||
|
||||
const (
|
||||
TypeHttp = iota
|
||||
TypeHttp2
|
||||
TypeUnknown
|
||||
)
|
||||
|
||||
var (
|
||||
httpMethods = [...][]byte{
|
||||
[]byte("GET"),
|
||||
[]byte("POST"),
|
||||
[]byte("HEAD"),
|
||||
[]byte("PUT"),
|
||||
[]byte("DELETE"),
|
||||
[]byte("OPTIONS"),
|
||||
[]byte("CONNECT"),
|
||||
}
|
||||
http2Header = []byte("PRI * HTTP/2.0")
|
||||
sep = []byte(" ")
|
||||
)
|
||||
|
||||
func NewPeekPreDataConn(c net.Conn) *SniffConn {
|
||||
s := &SniffConn{Conn: c, rout: c}
|
||||
s.Type = s.sniff()
|
||||
return s
|
||||
}
|
||||
|
||||
func (c *SniffConn) peekPreData(n int) ([]byte, error) {
|
||||
if c.read {
|
||||
return nil, errors.New("pre-data must be peek before read")
|
||||
}
|
||||
if c.peeked {
|
||||
return nil, errors.New("can only peek once")
|
||||
}
|
||||
c.peeked = true
|
||||
preDate := make([]byte, n)
|
||||
n, err := c.Conn.Read(preDate)
|
||||
return preDate[:n], err
|
||||
}
|
||||
|
||||
func (c *SniffConn) Read(p []byte) (int, error) {
|
||||
if !c.read {
|
||||
c.read = true
|
||||
c.rout = io.MultiReader(bytes.NewReader(c.preData), c.Conn)
|
||||
}
|
||||
return c.rout.Read(p)
|
||||
}
|
||||
|
||||
func (c *SniffConn) sniff() int {
|
||||
var err error
|
||||
c.preData, err = c.peekPreData(64)
|
||||
if err != nil && err != io.EOF {
|
||||
return TypeUnknown
|
||||
}
|
||||
|
||||
if c.sniffHttp() {
|
||||
return TypeHttp
|
||||
}
|
||||
|
||||
if c.sniffHttp2() {
|
||||
return TypeHttp2
|
||||
}
|
||||
|
||||
return TypeUnknown
|
||||
}
|
||||
|
||||
func (c *SniffConn) sniffHttp() bool {
|
||||
preDataParts := bytes.Split(c.preData, sep)
|
||||
|
||||
if len(preDataParts) < 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, m := range httpMethods {
|
||||
if bytes.Compare(preDataParts[0], m) == 0 {
|
||||
c.path = string(preDataParts[1])
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *SniffConn) sniffHttp2() bool {
|
||||
return len(c.preData) >= len(http2Header) &&
|
||||
bytes.Compare(c.preData[:len(http2Header)], http2Header) == 0
|
||||
}
|
||||
|
||||
func (c *SniffConn) SetPath(path string) {
|
||||
preDataParts := bytes.Split(c.preData, sep)
|
||||
preDataParts[1] = []byte(path)
|
||||
c.preData = bytes.Join(preDataParts, sep)
|
||||
c.path = path
|
||||
}
|
||||
|
||||
func (c *SniffConn) GetPath() string {
|
||||
return c.path
|
||||
}
|
||||
|
||||
func (c *SniffConn) Handle() bool {
|
||||
write, err := c.Write(netutil.GetDefaultHttpResponse())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer func(c *SniffConn) {
|
||||
err := c.Close()
|
||||
if err != nil {
|
||||
log.Printf("%x\n", err)
|
||||
}
|
||||
}(c)
|
||||
return write > 0
|
||||
}
|
104
utls/utlsclient.go
Normal file
104
utls/utlsclient.go
Normal file
@ -0,0 +1,104 @@
|
||||
package utls
|
||||
|
||||
import (
|
||||
utls "github.com/refraction-networking/utls"
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/golang/snappy"
|
||||
"github.com/net-byte/vtun/common/cache"
|
||||
"github.com/net-byte/vtun/common/cipher"
|
||||
"github.com/net-byte/vtun/common/config"
|
||||
"github.com/net-byte/vtun/common/counter"
|
||||
"github.com/net-byte/vtun/common/netutil"
|
||||
"github.com/net-byte/water"
|
||||
)
|
||||
|
||||
// StartClient starts the tls client
|
||||
func StartClient(iface *water.Interface, config config.Config) {
|
||||
log.Println("vtun utls client started")
|
||||
go tunToTLS(config, iface)
|
||||
tlsconfig := &utls.Config{
|
||||
InsecureSkipVerify: config.TLSInsecureSkipVerify,
|
||||
}
|
||||
if config.TLSSni != "" {
|
||||
tlsconfig.ServerName = config.TLSSni
|
||||
}
|
||||
for {
|
||||
tcpConn, err := net.Dial("tcp", config.ServerAddr)
|
||||
if err != nil {
|
||||
time.Sleep(3 * time.Second)
|
||||
netutil.PrintErr(err, config.Verbose)
|
||||
continue
|
||||
}
|
||||
conn := utls.UClient(tcpConn, tlsconfig, utls.HelloRandomized)
|
||||
err = conn.Handshake()
|
||||
if err != nil {
|
||||
time.Sleep(3 * time.Second)
|
||||
netutil.PrintErr(err, config.Verbose)
|
||||
continue
|
||||
}
|
||||
cache.GetCache().Set("utlsconn", conn, 24*time.Hour)
|
||||
tlsToTun(config, conn, iface)
|
||||
cache.GetCache().Delete("utlsconn")
|
||||
}
|
||||
}
|
||||
|
||||
// tunToTLS sends packets from tun to tls
|
||||
func tunToTLS(config config.Config, iface *water.Interface) {
|
||||
packet := make([]byte, config.BufferSize)
|
||||
for {
|
||||
n, err := iface.Read(packet)
|
||||
if err != nil {
|
||||
netutil.PrintErr(err, config.Verbose)
|
||||
break
|
||||
}
|
||||
if v, ok := cache.GetCache().Get("utlsconn"); ok {
|
||||
b := packet[:n]
|
||||
if config.Obfs {
|
||||
b = cipher.XOR(b)
|
||||
}
|
||||
if config.Compress {
|
||||
b = snappy.Encode(nil, b)
|
||||
}
|
||||
utlsconn := v.(*utls.UConn)
|
||||
_, err = utlsconn.Write(b)
|
||||
if err != nil {
|
||||
netutil.PrintErr(err, config.Verbose)
|
||||
continue
|
||||
}
|
||||
counter.IncrWrittenBytes(n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tlsToTun sends packets from tls to tun
|
||||
func tlsToTun(config config.Config, utlsconn *utls.UConn, iface *water.Interface) {
|
||||
defer utlsconn.Close()
|
||||
packet := make([]byte, config.BufferSize)
|
||||
for {
|
||||
n, err := utlsconn.Read(packet)
|
||||
if err != nil {
|
||||
netutil.PrintErr(err, config.Verbose)
|
||||
break
|
||||
}
|
||||
b := packet[:n]
|
||||
if config.Compress {
|
||||
b, err = snappy.Decode(nil, b)
|
||||
if err != nil {
|
||||
netutil.PrintErr(err, config.Verbose)
|
||||
break
|
||||
}
|
||||
}
|
||||
if config.Obfs {
|
||||
b = cipher.XOR(b)
|
||||
}
|
||||
_, err = iface.Write(b)
|
||||
if err != nil {
|
||||
netutil.PrintErr(err, config.Verbose)
|
||||
break
|
||||
}
|
||||
counter.IncrReadBytes(n)
|
||||
}
|
||||
}
|
111
utls/utlsserver.go
Normal file
111
utls/utlsserver.go
Normal file
@ -0,0 +1,111 @@
|
||||
package utls
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/golang/snappy"
|
||||
"github.com/net-byte/vtun/common/cache"
|
||||
"github.com/net-byte/vtun/common/cipher"
|
||||
"github.com/net-byte/vtun/common/config"
|
||||
"github.com/net-byte/vtun/common/counter"
|
||||
"github.com/net-byte/vtun/common/netutil"
|
||||
"github.com/net-byte/water"
|
||||
utls "github.com/refraction-networking/utls"
|
||||
)
|
||||
|
||||
// StartServer starts the tls server
|
||||
func StartServer(iface *water.Interface, config config.Config) {
|
||||
log.Printf("vtun utls server started on %v", config.LocalAddr)
|
||||
cert, err := utls.LoadX509KeyPair(config.TLSCertificateFilePath, config.TLSCertificateKeyFilePath)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
tlsConfig := &utls.Config{
|
||||
Certificates: []utls.Certificate{cert},
|
||||
}
|
||||
ln, err := utls.Listen("tcp", config.LocalAddr, tlsConfig)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
// server -> client
|
||||
go toClient(config, iface)
|
||||
// client -> server
|
||||
for {
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
sniffConn := NewPeekPreDataConn(conn)
|
||||
switch sniffConn.Type {
|
||||
case TypeHttp:
|
||||
if sniffConn.Handle() {
|
||||
continue
|
||||
}
|
||||
case TypeHttp2:
|
||||
if sniffConn.Handle() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
go toServer(config, sniffConn, iface)
|
||||
}
|
||||
}
|
||||
|
||||
// toClient sends packets from iface to tlsconn
|
||||
func toClient(config config.Config, iface *water.Interface) {
|
||||
packet := make([]byte, config.BufferSize)
|
||||
for {
|
||||
n, err := iface.Read(packet)
|
||||
if err != nil {
|
||||
netutil.PrintErr(err, config.Verbose)
|
||||
continue
|
||||
}
|
||||
b := packet[:n]
|
||||
if key := netutil.GetDstKey(b); key != "" {
|
||||
if v, ok := cache.GetCache().Get(key); ok {
|
||||
if config.Obfs {
|
||||
b = cipher.XOR(b)
|
||||
}
|
||||
if config.Compress {
|
||||
b = snappy.Encode(nil, b)
|
||||
}
|
||||
_, err := v.(net.Conn).Write(b)
|
||||
if err != nil {
|
||||
cache.GetCache().Delete(key)
|
||||
continue
|
||||
}
|
||||
counter.IncrWrittenBytes(n)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// toServer sends packets from tlsconn to iface
|
||||
func toServer(config config.Config, tlsconn net.Conn, iface *water.Interface) {
|
||||
defer tlsconn.Close()
|
||||
packet := make([]byte, config.BufferSize)
|
||||
for {
|
||||
n, err := tlsconn.Read(packet)
|
||||
if err != nil {
|
||||
netutil.PrintErr(err, config.Verbose)
|
||||
break
|
||||
}
|
||||
b := packet[:n]
|
||||
if config.Compress {
|
||||
b, err = snappy.Decode(nil, b)
|
||||
if err != nil {
|
||||
netutil.PrintErr(err, config.Verbose)
|
||||
break
|
||||
}
|
||||
}
|
||||
if config.Obfs {
|
||||
b = cipher.XOR(b)
|
||||
}
|
||||
if key := netutil.GetSrcKey(b); key != "" {
|
||||
cache.GetCache().Set(key, tlsconn, 24*time.Hour)
|
||||
iface.Write(b)
|
||||
counter.IncrReadBytes(n)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user