Merge pull request #18 from NNdroid/master

add tls support
This commit is contained in:
net-byte 2022-05-16 17:26:15 +08:00 committed by GitHub
commit 6aa9f33e1f
6 changed files with 218 additions and 17 deletions

View File

@ -13,6 +13,7 @@ A simple VPN written in golang.
* VPN over tcp
* VPN over udp
* VPN over websocket
* VPN over tls
# Usage
@ -23,6 +24,12 @@ Usage of ./vtun:
tun interface cidr (default "172.16.0.10/24")
-c6 string
tun interface ipv6 cidr (default "fced:9999::9999/64")
-certificate string
tls certificate file path
-privatekey string
tls certificate key file path
-sni string
tls handshake sni
-dn string
device name
-g client global mode

View File

@ -13,6 +13,7 @@
* 支持tcp
* 支持udp
* 支持websocket
* 支持tls
# 用法
@ -23,6 +24,12 @@ Usage of ./vtun:
tun interface cidr (default "172.16.0.10/24")
-c6 string
tun interface ipv6 cidr (default "fced:9999::9999/64")
-certificate string
tls certificate file path
-privatekey string
tls certificate key file path
-sni string
tls handshake sni
-dn string
device name
-g client global mode

View File

@ -1,20 +1,23 @@
package config
type Config struct {
DeviceName string
LocalAddr string
ServerAddr string
IntranetServerIP string
IntranetServerIPv6 string
CIDR string
CIDRv6 string
Key string
Protocol string
WebSocketPath string
ServerMode bool
GlobalMode bool
Obfs bool
MTU int
Timeout int
LocalGateway string
DeviceName string
LocalAddr string
ServerAddr string
IntranetServerIP string
IntranetServerIPv6 string
CIDR string
CIDRv6 string
Key string
Protocol string
WebSocketPath string
ServerMode bool
GlobalMode bool
Obfs bool
MTU int
Timeout int
LocalGateway string
TLSCertificateFilePath string
TLSCertificateKeyFilePath string
TLSSni string
}

12
main.go
View File

@ -15,6 +15,7 @@ import (
"github.com/net-byte/vtun/tcp"
"github.com/net-byte/vtun/tun"
"github.com/net-byte/vtun/udp"
"github.com/net-byte/vtun/tls"
"github.com/net-byte/vtun/ws"
)
@ -29,12 +30,15 @@ func main() {
flag.StringVar(&config.IntranetServerIP, "sip", "172.16.0.1", "intranet server ip")
flag.StringVar(&config.IntranetServerIPv6, "sip6", "fced:9999::1", "intranet server ipv6")
flag.StringVar(&config.Key, "k", "freedom@2022", "key")
flag.StringVar(&config.Protocol, "p", "wss", "protocol tcp/udp/ws/wss")
flag.StringVar(&config.Protocol, "p", "wss", "protocol tcp/udp/ws/tls/wss")
flag.StringVar(&config.WebSocketPath, "path", "/freedom", "websocket path")
flag.BoolVar(&config.ServerMode, "S", false, "server mode")
flag.BoolVar(&config.GlobalMode, "g", false, "client global mode")
flag.BoolVar(&config.Obfs, "obfs", false, "enable data obfuscation")
flag.IntVar(&config.Timeout, "t", 30, "dial timeout in seconds")
flag.StringVar(&config.TLSCertificateFilePath, "certificate", "", "tls certificate file path")
flag.StringVar(&config.TLSCertificateKeyFilePath, "privatekey", "", "tls certificate key file path")
flag.StringVar(&config.TLSSni, "sni", "", "tls handshake sni")
flag.Parse()
initConfig(&config)
go startApp(config)
@ -78,6 +82,12 @@ func startApp(config config.Config) {
} else {
ws.StartClient(config)
}
case "tls":
if config.ServerMode {
tls.StartServer(config)
} else {
tls.StartClient(config)
}
default:
if config.ServerMode {
ws.StartServer(config)

80
tls/tlsclient.go Normal file
View File

@ -0,0 +1,80 @@
package tls
import (
"crypto/tls"
"io"
"log"
"net"
"time"
"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/tun"
"github.com/songgao/water"
)
// Start tls client
func StartClient(config config.Config) {
log.Printf("vtun tls client started on %v", config.LocalAddr)
iface := tun.CreateTun(config)
go tunToTLS(config, iface)
for {
tlsconfig := &tls.Config{
//InsecureSkipVerify: true,
}
if config.TLSSni != "" {
tlsconfig.ServerName = config.TLSSni
}
conn, err := tls.Dial("tcp", config.ServerAddr, tlsconfig)
if err != nil {
time.Sleep(3 * time.Second)
continue
}
cache.GetCache().Set("tlsconn", conn, 24 * time.Hour)
tlsToTun(config, conn, iface)
cache.GetCache().Delete("tlsconn")
}
}
func tunToTLS(config config.Config, iface *water.Interface) {
packet := make([]byte, config.MTU)
for {
n, err := iface.Read(packet)
if err != nil || n == 0 {
continue
}
if v, ok := cache.GetCache().Get("tlsconn"); ok {
b := packet[:n]
if config.Obfs {
packet = cipher.XOR(packet)
}
tlsconn := v.(net.Conn)
tlsconn.SetWriteDeadline(time.Now().Add(time.Duration(config.Timeout) * time.Second))
_, err = tlsconn.Write(b)
if err != nil {
continue
}
}
}
}
func tlsToTun(config config.Config, tlsconn net.Conn, iface *water.Interface) {
defer tlsconn.Close()
packet := make([]byte, config.MTU)
for {
tlsconn.SetReadDeadline(time.Now().Add(time.Duration(config.Timeout) * time.Second))
n, err := tlsconn.Read(packet)
if err != nil || err == io.EOF {
break
}
b := packet[:n]
if config.Obfs {
b = cipher.XOR(b)
}
_, err = iface.Write(b)
if err != nil {
break
}
}
}

94
tls/tlsserver.go Normal file
View File

@ -0,0 +1,94 @@
package tls
import (
"crypto/tls"
"log"
"net"
"io"
"time"
"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/netutil"
"github.com/net-byte/vtun/tun"
"github.com/songgao/water"
)
//Start tls server
func StartServer(config config.Config) {
log.Printf("vtun tls server started on %v", config.LocalAddr)
iface := tun.CreateTun(config)
cert, err := tls.LoadX509KeyPair(config.TLSCertificateFilePath, config.TLSCertificateKeyFilePath)
if err != nil {
log.Println(err)
return
}
tlsconfig := &tls.Config{
Certificates: []tls.Certificate{cert},
MinVersion: tls.VersionTLS12,
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
PreferServerCipherSuites: true,
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
},
}
ln, err := tls.Listen("tcp", config.LocalAddr, tlsconfig)
if err != nil {
log.Println(err)
return
}
// server -> client
go toClient(config, iface)
// client -> server
for {
conn, err := ln.Accept()
if err != nil {
continue
}
go toServer(config, conn, iface)
}
}
func toClient(config config.Config, iface *water.Interface) {
packet := make([]byte, config.MTU)
for {
n, err := iface.Read(packet)
if err != nil || err == io.EOF || n == 0 {
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)
}
v.(net.Conn).Write(b)
}
}
}
}
// todo fallback to http
func toServer(config config.Config, tlsconn net.Conn, iface *water.Interface) {
defer tlsconn.Close()
packet := make([]byte, config.MTU)
for {
tlsconn.SetReadDeadline(time.Now().Add(time.Duration(config.Timeout) * time.Second))
n, err := tlsconn.Read(packet)
if err != nil || err == io.EOF {
break
}
b := packet[:n]
if config.Obfs {
b = cipher.XOR(b)
}
if key := netutil.GetSrcKey(b); key != "" {
cache.GetCache().Set(key, tlsconn, 10*time.Minute)
iface.Write(b)
}
}
}