mirror of
https://github.com/nadoo/glider.git
synced 2025-02-22 17:05:42 +08:00
proxy: added vsock support (#295)
This commit is contained in:
parent
ed7ee6bcd4
commit
730e9c765e
@ -193,8 +193,8 @@ URL:
|
||||
-forward socks5://serverA:1080,socks5://serverB:1080 (proxy chain)
|
||||
|
||||
SCHEME:
|
||||
listen : http kcp mixed pxyproto redir redir6 smux sni socks5 ss tcp tls tproxy trojan trojanc udp unix vless ws wss
|
||||
forward: direct http kcp reject simple-obfs smux socks4 socks4a socks5 ss ssh ssr tcp tls trojan trojanc udp unix vless vmess ws wss
|
||||
listen : http kcp mixed pxyproto redir redir6 smux sni socks5 ss tcp tls tproxy trojan trojanc udp unix vless vsock ws wss
|
||||
forward: direct http kcp reject simple-obfs smux socks4 socks4a socks5 ss ssh ssr tcp tls trojan trojanc udp unix vless vmess vsock ws wss
|
||||
|
||||
Note: use 'glider -scheme all' or 'glider -scheme SCHEME' to see help info for the scheme.
|
||||
|
||||
@ -348,6 +348,10 @@ TLS and Websocket with a specified proxy protocol:
|
||||
tls://host:port[?skipVerify=true],ws://[@/path[?host=HOST]],http://[user:pass@]
|
||||
tls://host:port[?skipVerify=true],ws://[@/path[?host=HOST]],socks5://[user:pass@]
|
||||
tls://host:port[?skipVerify=true],ws://[@/path[?host=HOST]],vmess://[security:]uuid@?alterID=num
|
||||
|
||||
--
|
||||
VM socket scheme:
|
||||
vsock://[contextID]:port
|
||||
```
|
||||
|
||||
</details>
|
||||
|
@ -8,4 +8,5 @@ import (
|
||||
_ "github.com/nadoo/glider/proxy/redir"
|
||||
_ "github.com/nadoo/glider/proxy/tproxy"
|
||||
_ "github.com/nadoo/glider/proxy/unix"
|
||||
_ "github.com/nadoo/glider/proxy/vsock"
|
||||
)
|
||||
|
4
go.mod
4
go.mod
@ -7,12 +7,12 @@ require (
|
||||
github.com/dgryski/go-camellia v0.0.0-20191119043421-69a8a13fb23d
|
||||
github.com/dgryski/go-idea v0.0.0-20170306091226-d2fb45a411fb
|
||||
github.com/dgryski/go-rc2 v0.0.0-20150621095337-8a9021637152
|
||||
github.com/insomniacslk/dhcp v0.0.0-20220119180841-3c283ff8b7dd
|
||||
github.com/insomniacslk/dhcp v0.0.0-20220405050111-12fbdcb11b41
|
||||
github.com/nadoo/conflag v0.3.1
|
||||
github.com/nadoo/ipset v0.5.0
|
||||
github.com/xtaci/kcp-go/v5 v5.6.1
|
||||
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29
|
||||
golang.org/x/sys v0.0.0-20220403205710-6acee93ad0eb
|
||||
golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64
|
||||
)
|
||||
|
||||
require (
|
||||
|
8
go.sum
8
go.sum
@ -39,8 +39,8 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20220119180841-3c283ff8b7dd h1:efcJu2Vzz6DoSq245deWNzTz6l/gsqdphm3FjmI88/g=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20220119180841-3c283ff8b7dd/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20220405050111-12fbdcb11b41 h1:Yg3n3AI7GoHnWt7dyjsLPU+TEuZfPAg0OdiA3MJUV6I=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20220405050111-12fbdcb11b41/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E=
|
||||
github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk=
|
||||
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
|
||||
@ -164,8 +164,8 @@ golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220403205710-6acee93ad0eb h1:PVGECzEo9Y3uOidtkHGdd347NjLtITfJFO9BxFpmRoo=
|
||||
golang.org/x/sys v0.0.0-20220403205710-6acee93ad0eb/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64 h1:D1v9ucDTYBtbz5vNuBbAhIMAGhQhJ6Ym5ah3maMVNX4=
|
||||
golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
|
27
proxy/vsock/client.go
Normal file
27
proxy/vsock/client.go
Normal file
@ -0,0 +1,27 @@
|
||||
package vsock
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("vsock", NewVSockDialer)
|
||||
}
|
||||
|
||||
// NewVSockDialer returns a vm socket dialer.
|
||||
func NewVSockDialer(s string, d proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewVSock(s, d, nil)
|
||||
}
|
||||
|
||||
// Dial connects to the address addr on the network net via the proxy.
|
||||
// NOTE: must be the first dialer in a chain
|
||||
func (s *vsock) Dial(network, addr string) (net.Conn, error) {
|
||||
return Dial(s.cid, s.port)
|
||||
}
|
||||
|
||||
// DialUDP connects to the given address via the proxy.
|
||||
func (s *vsock) DialUDP(network, addr string) (net.PacketConn, error) {
|
||||
return nil, proxy.ErrNotSupported
|
||||
}
|
90
proxy/vsock/server.go
Normal file
90
proxy/vsock/server.go
Normal file
@ -0,0 +1,90 @@
|
||||
package vsock
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/nadoo/glider/pkg/log"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("vsock", NewVSockServer)
|
||||
}
|
||||
|
||||
// NewVSockServer returns a vm socket server.
|
||||
func NewVSockServer(s string, p proxy.Proxy) (proxy.Server, error) {
|
||||
schemes := strings.SplitN(s, ",", 2)
|
||||
vsock, err := NewVSock(schemes[0], nil, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(schemes) > 1 {
|
||||
vsock.server, err = proxy.ServerFromURL(schemes[1], p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if vsock.cid == 0 {
|
||||
cid, err := ContextID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vsock.cid = cid
|
||||
}
|
||||
|
||||
return vsock, nil
|
||||
}
|
||||
|
||||
// ListenAndServe serves requests.
|
||||
func (s *vsock) ListenAndServe() {
|
||||
l, err := Listen(s.cid, s.port)
|
||||
if err != nil {
|
||||
log.Fatalf("[vsock] failed to listen: %v", err)
|
||||
return
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
log.F("[vsock] Listening on %s", l.Addr())
|
||||
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
log.F("[vsock] failed to accept: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
go s.Serve(c)
|
||||
}
|
||||
}
|
||||
|
||||
// Serve serves requests.
|
||||
func (s *vsock) Serve(c net.Conn) {
|
||||
if s.server != nil {
|
||||
s.server.Serve(c)
|
||||
return
|
||||
}
|
||||
|
||||
defer c.Close()
|
||||
|
||||
rc, dialer, err := s.proxy.Dial("tcp", "")
|
||||
if err != nil {
|
||||
log.F("[vsock] %s <-> %s via %s, error in dial: %v", c.RemoteAddr(), s.addr, dialer.Addr(), err)
|
||||
s.proxy.Record(dialer, false)
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
log.F("[vsock] %s <-> %s", c.RemoteAddr(), dialer.Addr())
|
||||
|
||||
if err = proxy.Relay(c, rc); err != nil {
|
||||
log.F("[vsock] %s <-> %s, relay error: %v", c.RemoteAddr(), dialer.Addr(), err)
|
||||
// record remote conn failure only
|
||||
if !strings.Contains(err.Error(), s.addr) {
|
||||
s.proxy.Record(dialer, false)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
214
proxy/vsock/socket.go
Normal file
214
proxy/vsock/socket.go
Normal file
@ -0,0 +1,214 @@
|
||||
package vsock
|
||||
|
||||
// Source code from:
|
||||
// https://github.com/linuxkit/virtsock/tree/master/pkg/vsock
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Addr represents the address of a vsock end point.
|
||||
type Addr struct {
|
||||
CID uint32
|
||||
Port uint32
|
||||
}
|
||||
|
||||
// Network returns the network type for a Addr
|
||||
func (a Addr) Network() string {
|
||||
return "vsock"
|
||||
}
|
||||
|
||||
// String returns a string representation of a Addr
|
||||
func (a Addr) String() string {
|
||||
return fmt.Sprintf("%d:%d", a.CID, a.Port)
|
||||
}
|
||||
|
||||
// Conn is a vsock connection which supports half-close.
|
||||
type Conn interface {
|
||||
net.Conn
|
||||
CloseRead() error
|
||||
CloseWrite() error
|
||||
File() (*os.File, error)
|
||||
}
|
||||
|
||||
// SocketMode is a NOOP on Linux.
|
||||
func SocketMode(m string) {}
|
||||
|
||||
// Convert a generic unix.Sockaddr to a Addr.
|
||||
func sockaddrToVsock(sa unix.Sockaddr) *Addr {
|
||||
switch sa := sa.(type) {
|
||||
case *unix.SockaddrVM:
|
||||
return &Addr{CID: sa.CID, Port: sa.Port}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Closes fd, retrying EINTR
|
||||
func closeFD(fd int) error {
|
||||
for {
|
||||
if err := unix.Close(fd); err != nil {
|
||||
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EINTR {
|
||||
continue
|
||||
}
|
||||
return fmt.Errorf("failed to close() fd %d: %w", fd, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Dial connects to the CID.Port via virtio sockets.
|
||||
func Dial(cid, port uint32) (Conn, error) {
|
||||
fd, err := syscall.Socket(unix.AF_VSOCK, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to create AF_VSOCK socket: %w", err)
|
||||
}
|
||||
sa := &unix.SockaddrVM{CID: cid, Port: port}
|
||||
// Retry connect in a loop if EINTR is encountered.
|
||||
for {
|
||||
if err := unix.Connect(fd, sa); err != nil {
|
||||
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EINTR {
|
||||
continue
|
||||
}
|
||||
// Trying not to leak fd here
|
||||
_ = closeFD(fd)
|
||||
return nil, fmt.Errorf("failed connect() to %d:%d: %w", cid, port, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
return newVsockConn(uintptr(fd), nil, &Addr{cid, port}), nil
|
||||
}
|
||||
|
||||
// Listen returns a net.Listener which can accept connections on the given cid and port.
|
||||
func Listen(cid, port uint32) (net.Listener, error) {
|
||||
fd, err := syscall.Socket(unix.AF_VSOCK, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sa := &unix.SockaddrVM{CID: cid, Port: port}
|
||||
if err = unix.Bind(fd, sa); err != nil {
|
||||
return nil, fmt.Errorf("bind() to %d:%d failed: %w", cid, port, err)
|
||||
}
|
||||
|
||||
err = syscall.Listen(fd, syscall.SOMAXCONN)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listen() on %d:%d failed: %w", cid, port, err)
|
||||
}
|
||||
return &vsockListener{fd, Addr{cid, port}}, nil
|
||||
}
|
||||
|
||||
// ContextID retrieves the local context ID for this system.
|
||||
func ContextID() (uint32, error) {
|
||||
f, err := os.Open("/dev/vsock")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return unix.IoctlGetUint32(int(f.Fd()), unix.IOCTL_VM_SOCKETS_GET_LOCAL_CID)
|
||||
}
|
||||
|
||||
type vsockListener struct {
|
||||
fd int
|
||||
local Addr
|
||||
}
|
||||
|
||||
// Accept accepts an incoming call and returns the new connection.
|
||||
func (v *vsockListener) Accept() (net.Conn, error) {
|
||||
fd, sa, err := unix.Accept(v.fd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newVsockConn(uintptr(fd), &v.local, sockaddrToVsock(sa)), nil
|
||||
}
|
||||
|
||||
// Close closes the listening connection
|
||||
func (v *vsockListener) Close() error {
|
||||
// Note this won't cause the Accept to unblock.
|
||||
return unix.Close(v.fd)
|
||||
}
|
||||
|
||||
// Addr returns the address the Listener is listening on
|
||||
func (v *vsockListener) Addr() net.Addr {
|
||||
return v.local
|
||||
}
|
||||
|
||||
// a wrapper around FileConn which supports CloseRead and CloseWrite
|
||||
type vsockConn struct {
|
||||
vsock *os.File
|
||||
fd uintptr
|
||||
local *Addr
|
||||
remote *Addr
|
||||
}
|
||||
|
||||
func newVsockConn(fd uintptr, local, remote *Addr) *vsockConn {
|
||||
vsock := os.NewFile(fd, fmt.Sprintf("vsock:%d", fd))
|
||||
return &vsockConn{vsock: vsock, fd: fd, local: local, remote: remote}
|
||||
}
|
||||
|
||||
// LocalAddr returns the local address of a connection
|
||||
func (v *vsockConn) LocalAddr() net.Addr {
|
||||
return v.local
|
||||
}
|
||||
|
||||
// RemoteAddr returns the remote address of a connection
|
||||
func (v *vsockConn) RemoteAddr() net.Addr {
|
||||
return v.remote
|
||||
}
|
||||
|
||||
// Close closes the connection
|
||||
func (v *vsockConn) Close() error {
|
||||
return v.vsock.Close()
|
||||
}
|
||||
|
||||
// CloseRead shuts down the reading side of a vsock connection
|
||||
func (v *vsockConn) CloseRead() error {
|
||||
return syscall.Shutdown(int(v.fd), syscall.SHUT_RD)
|
||||
}
|
||||
|
||||
// CloseWrite shuts down the writing side of a vsock connection
|
||||
func (v *vsockConn) CloseWrite() error {
|
||||
return syscall.Shutdown(int(v.fd), syscall.SHUT_WR)
|
||||
}
|
||||
|
||||
// Read reads data from the connection
|
||||
func (v *vsockConn) Read(buf []byte) (int, error) {
|
||||
return v.vsock.Read(buf)
|
||||
}
|
||||
|
||||
// Write writes data over the connection
|
||||
func (v *vsockConn) Write(buf []byte) (int, error) {
|
||||
return v.vsock.Write(buf)
|
||||
}
|
||||
|
||||
// SetDeadline sets the read and write deadlines associated with the connection
|
||||
func (v *vsockConn) SetDeadline(t time.Time) error {
|
||||
return nil // FIXME
|
||||
}
|
||||
|
||||
// SetReadDeadline sets the deadline for future Read calls.
|
||||
func (v *vsockConn) SetReadDeadline(t time.Time) error {
|
||||
return nil // FIXME
|
||||
}
|
||||
|
||||
// SetWriteDeadline sets the deadline for future Write calls
|
||||
func (v *vsockConn) SetWriteDeadline(t time.Time) error {
|
||||
return nil // FIXME
|
||||
}
|
||||
|
||||
// File duplicates the underlying socket descriptor and returns it.
|
||||
func (v *vsockConn) File() (*os.File, error) {
|
||||
// This is equivalent to dup(2) but creates the new fd with CLOEXEC already set.
|
||||
r0, _, e1 := syscall.Syscall(syscall.SYS_FCNTL, uintptr(v.vsock.Fd()), syscall.F_DUPFD_CLOEXEC, 0)
|
||||
if e1 != 0 {
|
||||
return nil, os.NewSyscallError("fcntl", e1)
|
||||
}
|
||||
return os.NewFile(r0, v.vsock.Name()), nil
|
||||
}
|
63
proxy/vsock/vsock.go
Normal file
63
proxy/vsock/vsock.go
Normal file
@ -0,0 +1,63 @@
|
||||
package vsock
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/nadoo/glider/pkg/log"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
type vsock struct {
|
||||
dialer proxy.Dialer
|
||||
proxy proxy.Proxy
|
||||
server proxy.Server
|
||||
addr string
|
||||
cid, port uint32
|
||||
}
|
||||
|
||||
// NewVSock returns vm socket proxy.
|
||||
func NewVSock(s string, d proxy.Dialer, p proxy.Proxy) (*vsock, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
log.F("[vsock] parse url err: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v := &vsock{dialer: d, proxy: p, addr: u.Host}
|
||||
if hostStr, portStr, _ := net.SplitHostPort(v.addr); portStr != "" {
|
||||
if hostStr != "" {
|
||||
host, err := strconv.ParseUint(hostStr, 10, 32)
|
||||
if err != nil {
|
||||
log.F("[vsock] parse cid err: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
v.cid = uint32(host)
|
||||
}
|
||||
|
||||
port, err := strconv.ParseUint(portStr, 10, 32)
|
||||
if err != nil {
|
||||
log.F("[vsock] parse port err: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
v.port = uint32(port)
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Addr returns forwarder's address.
|
||||
func (s *vsock) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.AddUsage("vsock", `
|
||||
VM socket scheme:
|
||||
vsock://[contextID]:port
|
||||
`)
|
||||
}
|
Loading…
Reference in New Issue
Block a user