add utls support

add dtls support
This commit is contained in:
NNdroid 2023-05-30 14:12:39 +08:00
parent 4c2f9b47f9
commit 7c8048e7c2
10 changed files with 607 additions and 8 deletions

View File

@ -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"
@ -97,6 +99,18 @@ func (app *App) StartApp() {
} 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
View 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
View 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
View File

@ -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
View File

@ -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=

View File

@ -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 {

View File

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