mirror of
https://github.com/xjasonlyu/tun2socks.git
synced 2024-12-30 02:37:01 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8fcd8fee85 | ||
|
|
e06cce1dd4 | ||
|
|
867cfefa76 | ||
|
|
176324412b | ||
|
|
66e9c6d4e9 | ||
|
|
3928350e57 | ||
|
|
f6ba31f121 | ||
|
|
bf3f4599cc | ||
|
|
dc8e4f1ba1 | ||
|
|
ad53cc7ffe | ||
|
|
10022388a0 | ||
|
|
377aae12c7 | ||
|
|
3f5b3c9eb3 | ||
|
|
07ce5e6422 | ||
|
|
e1ca5069e0 | ||
|
|
05f4938053 | ||
|
|
a86608e31a | ||
|
|
c478adee34 | ||
|
|
a3f1713d14 | ||
|
|
525d424fca | ||
|
|
f1420b9592 | ||
|
|
4601b4ef74 | ||
|
|
85cf694b8f |
@@ -2,6 +2,13 @@ name: Go Static Check
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
tags-ignore:
|
||||
- '*'
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'docs/**'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
@@ -17,7 +24,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16.x
|
||||
go-version: 1.17.x
|
||||
|
||||
- name: Cache go module
|
||||
uses: actions/cache@v2
|
||||
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16.x
|
||||
go-version: 1.17.x
|
||||
|
||||
- name: Build
|
||||
run: make -j releases
|
||||
|
||||
@@ -45,6 +45,7 @@ UNIX_ARCH_LIST = \
|
||||
WINDOWS_ARCH_LIST = \
|
||||
windows-386 \
|
||||
windows-amd64 \
|
||||
windows-arm64 \
|
||||
windows-arm32v7
|
||||
|
||||
all: linux-amd64 darwin-amd64 windows-amd64
|
||||
@@ -127,6 +128,9 @@ windows-386:
|
||||
windows-amd64:
|
||||
GOARCH=amd64 GOOS=windows $(GO_BUILD) -o $(BUILD_DIR)/$(BINARY)-$@.exe
|
||||
|
||||
windows-arm64:
|
||||
GOARCH=arm64 GOOS=windows $(GO_BUILD) -o $(BUILD_DIR)/$(BINARY)-$@.exe
|
||||
|
||||
windows-arm32v7:
|
||||
GOARCH=arm GOARM=7 GOOS=windows $(GO_BUILD) -o $(BUILD_DIR)/$(BINARY)-$@.exe
|
||||
|
||||
|
||||
@@ -16,25 +16,30 @@ English | [简体中文](README_ZH.md)
|
||||
|
||||
## Features
|
||||
|
||||
- **Fully support:** IPv4/IPv6/ICMP/TCP/UDP
|
||||
- **Proxy protocol:** HTTP/Socks4/Socks5/Shadowsocks
|
||||
- **Game ready:** optimized UDP transmission
|
||||
- **Pure Go:** no CGO required, stability improved
|
||||
- **Router mode:** forwarding packets in LAN
|
||||
- **TCP/IP stack:** powered by **[gVisor](https://github.com/google/gvisor)**
|
||||
- **High performance:** >2.5Gbps throughput
|
||||
|
||||
## Requirements
|
||||
|
||||
| Target | Minimum | Recommended |
|
||||
| :----- | :-----: | :---------: |
|
||||
| System | Linux MacOS Freebsd OpenBSD Windows | Linux or MacOS |
|
||||
| Memory | >20MB | >128MB |
|
||||
| CPU | ANY | AMD64 or ARM64 |
|
||||
- **Network Support**
|
||||
- Dualstack: `IPv4/IPv6`
|
||||
- Forwarder: `TCP/UDP`
|
||||
- Ping Echo: `ICMP`
|
||||
- **Platform Support**
|
||||
- Linux
|
||||
- MacOS
|
||||
- Windows
|
||||
- FreeBSD
|
||||
- OpenBSD
|
||||
- **Proxy Protocol**
|
||||
- HTTP
|
||||
- Socks4
|
||||
- Socks5
|
||||
- Shadowsocks
|
||||
- **Extra Feature**
|
||||
- Improved stability without CGO
|
||||
- Optimized UDP transmission for game
|
||||
- Performed with >2.5Gbps throughput
|
||||
- TCP/IP stack powered by **[gVisor](https://github.com/google/gvisor)**
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentations and quick start guides can be found at [Github Wiki](https://github.com/xjasonlyu/tun2socks/wiki).
|
||||
Docs and quick start guides can be found at [Github Wiki](https://github.com/xjasonlyu/tun2socks/wiki).
|
||||
|
||||
## Community
|
||||
|
||||
@@ -51,3 +56,8 @@ Welcome and feel free to ask any questions at [Github Discussions](https://githu
|
||||
[GPL-3.0](https://github.com/xjasonlyu/tun2socks/blob/main/LICENSE)
|
||||
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2Fxjasonlyu%2Ftun2socks?ref=badge_large)
|
||||
|
||||
|
||||
## Stargazers over time
|
||||
|
||||
[](https://starchart.cc/xjasonlyu/tun2socks)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build !linux,!darwin
|
||||
//go:build !linux && !darwin
|
||||
|
||||
package dialer
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build !linux
|
||||
//go:build !linux
|
||||
|
||||
package dialer
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
package fd
|
||||
|
||||
const Driver = "fd"
|
||||
@@ -0,0 +1,41 @@
|
||||
//go:build !windows
|
||||
|
||||
package fd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/xjasonlyu/tun2socks/core/device"
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
type FD struct {
|
||||
stack.LinkEndpoint
|
||||
|
||||
fd int
|
||||
mtu uint32
|
||||
}
|
||||
|
||||
func Open(name string, mtu uint32) (device.Device, error) {
|
||||
fd, err := strconv.Atoi(name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot open fd: %s", name)
|
||||
}
|
||||
return open(fd, mtu)
|
||||
}
|
||||
|
||||
func (f *FD) Type() string {
|
||||
return Driver
|
||||
}
|
||||
|
||||
func (f *FD) Name() string {
|
||||
return strconv.Itoa(f.fd)
|
||||
}
|
||||
|
||||
func (f *FD) Close() error {
|
||||
return unix.Close(f.fd)
|
||||
}
|
||||
|
||||
var _ device.Device = (*FD)(nil)
|
||||
@@ -0,0 +1,11 @@
|
||||
package fd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/xjasonlyu/tun2socks/core/device"
|
||||
)
|
||||
|
||||
func Open(name string, mtu uint32) (device.Device, error) {
|
||||
return nil, errors.New("not supported")
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package fd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/xjasonlyu/tun2socks/core/device"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/link/fdbased"
|
||||
)
|
||||
|
||||
func open(fd int, mtu uint32) (device.Device, error) {
|
||||
f := &FD{fd: fd, mtu: mtu}
|
||||
|
||||
ep, err := fdbased.New(&fdbased.Options{
|
||||
MTU: mtu,
|
||||
FDs: []int{fd},
|
||||
EthernetHeader: false,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create endpoint: %w", err)
|
||||
}
|
||||
f.LinkEndpoint = ep
|
||||
|
||||
return f, nil
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
//go:build !linux && !windows
|
||||
|
||||
package fd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/xjasonlyu/tun2socks/core/device"
|
||||
"github.com/xjasonlyu/tun2socks/core/device/rwbased"
|
||||
)
|
||||
|
||||
func open(fd int, mtu uint32) (device.Device, error) {
|
||||
f := &FD{fd: fd, mtu: mtu}
|
||||
|
||||
ep, err := rwbased.New(os.NewFile(uintptr(fd), f.Name()), mtu)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create endpoint: %w", err)
|
||||
}
|
||||
f.LinkEndpoint = ep
|
||||
|
||||
return f, nil
|
||||
}
|
||||
@@ -106,6 +106,10 @@ func (e *Endpoint) WritePackets(_ stack.RouteInfo, pkts stack.PacketBufferList,
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (e *Endpoint) WriteRawPacket(packetBuffer *stack.PacketBuffer) tcpip.Error {
|
||||
return &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
||||
// MTU implements stack.LinkEndpoint.MTU.
|
||||
func (e *Endpoint) MTU() uint32 {
|
||||
return e.mtu
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build darwin freebsd openbsd
|
||||
//go:build darwin || freebsd || openbsd
|
||||
|
||||
package tun
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
package tun
|
||||
|
||||
type Option func(*TUN)
|
||||
|
||||
func WithName(name string) Option {
|
||||
return func(t *TUN) {
|
||||
t.name = name
|
||||
}
|
||||
}
|
||||
|
||||
func WithMTU(mtu uint32) Option {
|
||||
return func(t *TUN) {
|
||||
t.mtu = mtu
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build linux
|
||||
//go:build linux
|
||||
|
||||
package tun
|
||||
|
||||
@@ -23,12 +23,8 @@ type TUN struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func Open(opts ...Option) (device.Device, error) {
|
||||
t := &TUN{}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(t)
|
||||
}
|
||||
func Open(name string, mtu uint32) (device.Device, error) {
|
||||
t := &TUN{name: name, mtu: mtu}
|
||||
|
||||
if len(t.name) >= unix.IFNAMSIZ {
|
||||
return nil, fmt.Errorf("interface name too long: %s", t.name)
|
||||
@@ -46,11 +42,11 @@ func Open(opts ...Option) (device.Device, error) {
|
||||
}
|
||||
}
|
||||
|
||||
mtu, err := rawfile.GetMTU(t.name)
|
||||
_mtu, err := rawfile.GetMTU(t.name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get mtu: %w", err)
|
||||
}
|
||||
t.mtu = mtu
|
||||
t.mtu = _mtu
|
||||
|
||||
ep, err := fdbased.New(&fdbased.Options{
|
||||
MTU: t.mtu,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build !linux
|
||||
//go:build !linux
|
||||
|
||||
package tun
|
||||
|
||||
@@ -19,12 +19,8 @@ type TUN struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func Open(opts ...Option) (device.Device, error) {
|
||||
t := &TUN{}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(t)
|
||||
}
|
||||
func Open(name string, mtu uint32) (device.Device, error) {
|
||||
t := &TUN{name: name, mtu: mtu}
|
||||
|
||||
forcedMTU := defaultMTU
|
||||
if t.mtu > 0 {
|
||||
@@ -37,11 +33,11 @@ func Open(opts ...Option) (device.Device, error) {
|
||||
}
|
||||
t.nt = nt.(*tun.NativeTun)
|
||||
|
||||
mtu, err := nt.MTU()
|
||||
_mtu, err := nt.MTU()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get mtu: %w", err)
|
||||
}
|
||||
t.mtu = uint32(mtu)
|
||||
t.mtu = uint32(_mtu)
|
||||
|
||||
ep, err := rwbased.New(t, t.mtu)
|
||||
if err != nil {
|
||||
|
||||
+23
-9
@@ -7,6 +7,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/xjasonlyu/tun2socks/core/device"
|
||||
"github.com/xjasonlyu/tun2socks/core/device/fd"
|
||||
"github.com/xjasonlyu/tun2socks/core/device/tun"
|
||||
"github.com/xjasonlyu/tun2socks/proxy"
|
||||
"github.com/xjasonlyu/tun2socks/proxy/proto"
|
||||
@@ -14,7 +15,7 @@ import (
|
||||
|
||||
func parseDevice(s string, mtu uint32) (device.Device, error) {
|
||||
if !strings.Contains(s, "://") {
|
||||
s = tun.Driver + "://" + s /* default driver */
|
||||
s = fmt.Sprintf("%s://%s", tun.Driver /* default driver */, s)
|
||||
}
|
||||
|
||||
u, err := url.Parse(s)
|
||||
@@ -26,8 +27,10 @@ func parseDevice(s string, mtu uint32) (device.Device, error) {
|
||||
driver := strings.ToLower(u.Scheme)
|
||||
|
||||
switch driver {
|
||||
case fd.Driver:
|
||||
return fd.Open(name, mtu)
|
||||
case tun.Driver:
|
||||
return tun.Open(tun.WithName(name), tun.WithMTU(mtu))
|
||||
return tun.Open(name, mtu)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported driver: %s", driver)
|
||||
}
|
||||
@@ -35,7 +38,7 @@ func parseDevice(s string, mtu uint32) (device.Device, error) {
|
||||
|
||||
func parseProxy(s string) (proxy.Proxy, error) {
|
||||
if !strings.Contains(s, "://") {
|
||||
s = proto.Socks5.String() + "://" + s /* default protocol */
|
||||
s = fmt.Sprintf("%s://%s", proto.Socks5 /* default protocol */, s)
|
||||
}
|
||||
|
||||
u, err := url.Parse(s)
|
||||
@@ -51,11 +54,11 @@ func parseProxy(s string) (proxy.Proxy, error) {
|
||||
case proto.Reject.String():
|
||||
return proxy.NewReject(), nil
|
||||
case proto.HTTP.String():
|
||||
return proxy.NewHTTP(parseAddrUserPass(u))
|
||||
return proxy.NewHTTP(parseHTTP(u))
|
||||
case proto.Socks4.String():
|
||||
return proxy.NewSocks4(parseAddrUser(u))
|
||||
return proxy.NewSocks4(parseSocks4(u))
|
||||
case proto.Socks5.String():
|
||||
return proxy.NewSocks5(parseAddrUserPass(u))
|
||||
return proxy.NewSocks5(parseSocks5(u))
|
||||
case proto.Shadowsocks.String():
|
||||
return proxy.NewShadowsocks(parseShadowsocks(u))
|
||||
default:
|
||||
@@ -63,14 +66,25 @@ func parseProxy(s string) (proxy.Proxy, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func parseAddrUser(u *url.URL) (address, username string) {
|
||||
func parseHTTP(u *url.URL) (address, username, password string) {
|
||||
address, username = u.Host, u.User.Username()
|
||||
password, _ = u.User.Password()
|
||||
return
|
||||
}
|
||||
|
||||
func parseSocks4(u *url.URL) (address, username string) {
|
||||
address, username = u.Host, u.User.Username()
|
||||
return
|
||||
}
|
||||
|
||||
func parseAddrUserPass(u *url.URL) (address, username, password string) {
|
||||
address, username = parseAddrUser(u)
|
||||
func parseSocks5(u *url.URL) (address, username, password string) {
|
||||
address, username = u.Host, u.User.Username()
|
||||
password, _ = u.User.Password()
|
||||
|
||||
// Socks5 over UDS
|
||||
if address == "" {
|
||||
address = u.Path
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +1,28 @@
|
||||
module github.com/xjasonlyu/tun2socks
|
||||
|
||||
go 1.16
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/Dreamacro/go-shadowsocks2 v0.1.7
|
||||
github.com/go-chi/chi v4.1.2+incompatible
|
||||
github.com/go-chi/cors v1.2.0
|
||||
github.com/go-chi/render v1.0.1
|
||||
github.com/gofrs/uuid v4.0.0+incompatible
|
||||
github.com/gofrs/uuid v4.1.0+incompatible
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
go.uber.org/atomic v1.9.0
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
|
||||
golang.org/x/net v0.0.0-20210716203947-853a461950ff // indirect
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
|
||||
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6
|
||||
golang.zx2c4.com/wireguard v0.0.0-20210624150102-15b24b6179e0
|
||||
gvisor.dev/gvisor v0.0.0-20210716193733-566c23a60eea
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
|
||||
golang.org/x/net v0.0.0-20211029224645-99673261e6eb // indirect
|
||||
golang.org/x/sys v0.0.0-20211031064116-611d5d643895
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac
|
||||
golang.zx2c4.com/wireguard v0.0.0-20211030003956-52704c4b9288
|
||||
gvisor.dev/gvisor v0.0.0-20211029210705-806fa5c3235c
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||
)
|
||||
|
||||
+16
-2
@@ -2,6 +2,7 @@ package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
@@ -19,6 +20,9 @@ type Socks5 struct {
|
||||
|
||||
user string
|
||||
pass string
|
||||
|
||||
// unix indicates if socks5 over UDS is enabled.
|
||||
unix bool
|
||||
}
|
||||
|
||||
func NewSocks5(addr, user, pass string) (*Socks5, error) {
|
||||
@@ -29,11 +33,17 @@ func NewSocks5(addr, user, pass string) (*Socks5, error) {
|
||||
},
|
||||
user: user,
|
||||
pass: pass,
|
||||
unix: len(addr) > 0 && addr[0] == '/',
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ss *Socks5) DialContext(ctx context.Context, metadata *M.Metadata) (c net.Conn, err error) {
|
||||
c, err = dialer.DialContext(ctx, "tcp", ss.Addr())
|
||||
var network = "tcp"
|
||||
if ss.unix {
|
||||
network = "unix"
|
||||
}
|
||||
|
||||
c, err = dialer.DialContext(ctx, network, ss.Addr())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("connect to %s: %w", ss.Addr(), err)
|
||||
}
|
||||
@@ -54,6 +64,10 @@ func (ss *Socks5) DialContext(ctx context.Context, metadata *M.Metadata) (c net.
|
||||
}
|
||||
|
||||
func (ss *Socks5) DialUDP(*M.Metadata) (_ net.PacketConn, err error) {
|
||||
if ss.unix {
|
||||
return nil, errors.New("not supported when unix domain socket is enabled")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), tcpConnectTimeout)
|
||||
defer cancel()
|
||||
|
||||
@@ -90,7 +104,7 @@ func (ss *Socks5) DialUDP(*M.Metadata) (_ net.PacketConn, err error) {
|
||||
|
||||
addr, err := socks5.ClientHandshake(c, targetAddr, socks5.CmdUDPAssociate, user)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("client hanshake: %w", err)
|
||||
return nil, fmt.Errorf("client handshake: %w", err)
|
||||
}
|
||||
|
||||
pc, err := dialer.ListenPacket("udp", "")
|
||||
|
||||
@@ -148,15 +148,16 @@ type User struct {
|
||||
// ClientHandshake fast-tracks SOCKS initialization to get target address to connect on client side.
|
||||
func ClientHandshake(rw io.ReadWriter, addr Addr, command Command, user *User) (Addr, error) {
|
||||
buf := make([]byte, MaxAddrLen)
|
||||
var err error
|
||||
|
||||
var method uint8
|
||||
if user != nil {
|
||||
method = 0x02 /* USERNAME/PASSWORD */
|
||||
} else {
|
||||
method = 0x00 /* NO AUTHENTICATION REQUIRED */
|
||||
}
|
||||
|
||||
// VER, NMETHODS, METHODS
|
||||
if user != nil {
|
||||
_, err = rw.Write([]byte{Version, 0x01 /* NMETHODS */, 0x02 /* USERNAME/PASSWORD */})
|
||||
} else {
|
||||
_, err = rw.Write([]byte{Version, 0x01 /* NMETHODS */, 0x00 /* NO AUTHENTICATION REQUIRED */})
|
||||
}
|
||||
if err != nil {
|
||||
if _, err := rw.Write([]byte{Version, 0x01 /* NMETHODS */, method}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -213,7 +214,7 @@ func ClientHandshake(rw io.ReadWriter, addr Addr, command Command, user *User) (
|
||||
}
|
||||
|
||||
if rep := Reply(buf[1]); rep != 0x00 /* SUCCEEDED */ {
|
||||
return nil, fmt.Errorf("%#X: %s", rep, rep.String())
|
||||
return nil, fmt.Errorf("%#02x: %s", uint8(rep), rep)
|
||||
}
|
||||
|
||||
return ReadAddr(rw, buf)
|
||||
|
||||
Reference in New Issue
Block a user