mirror of
https://github.com/nadoo/glider.git
synced 2025-02-23 01:15:41 +08:00
vless: spport server mode
This commit is contained in:
parent
1ed7fbff65
commit
8b6739f12c
@ -4,7 +4,7 @@
|
||||
[](https://github.com/nadoo/glider/releases)
|
||||
[](https://github.com/nadoo/glider/actions)
|
||||
|
||||
glider is a forward proxy with multiple protocols support, and also a dns forwarding server with ipset management features(like dnsmasq).
|
||||
glider is a forward proxy with multiple protocols support, and also a dns/dhcp forwarding server with ipset management features(like dnsmasq).
|
||||
|
||||
we can set up local listeners as proxy servers, and forward requests to internet via forwarders.
|
||||
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
|
||||
var flag = conflag.New()
|
||||
|
||||
// Config is global config struct.
|
||||
type Config struct {
|
||||
Verbose bool
|
||||
|
||||
|
7
go.mod
7
go.mod
@ -10,10 +10,9 @@ require (
|
||||
github.com/nadoo/ipset v0.3.0
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/xtaci/kcp-go/v5 v5.5.17
|
||||
golang.org/x/crypto v0.0.0-20201002094018-c90954cbb977
|
||||
golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 // indirect
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f // indirect
|
||||
golang.org/x/tools v0.0.0-20201002055958-0d28ed0cbe40 // indirect
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0
|
||||
golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c // indirect
|
||||
golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
|
||||
)
|
||||
|
||||
|
12
go.sum
12
go.sum
@ -118,8 +118,8 @@ golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
|
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002094018-c90954cbb977 h1:yH6opeNE+0SY+7pXT4gclZUoKHogXeC2EvOSHGOMGPU=
|
||||
golang.org/x/crypto v0.0.0-20201002094018-c90954cbb977/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
@ -141,8 +141,8 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgN
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 h1:YfxMZzv3PjGonQYNUaeU2+DhAdqOxerQ30JFB6WgAXo=
|
||||
golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c h1:dk0ukUIHmGHqASjP0iue2261isepFCC6XRCSd1nHgDw=
|
||||
golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c/go.mod h1:iQL9McJNjoIa5mjH6nYTCTZXUN6RP+XW3eib7Ya3XcI=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -173,8 +173,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20200425043458-8463f397d07c h1:iHhCR0b26amDCiiO+kBguKZom9aMF+NrFxh9zeKR/XU=
|
||||
golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20201002055958-0d28ed0cbe40 h1:ErPN1Z9An7dXc56pRUCKgWJkjYzc3hE+y15ky9E8qxU=
|
||||
golang.org/x/tools v0.0.0-20201002055958-0d28ed0cbe40/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
||||
golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d h1:vWQvJ/Z0Lu+9/8oQ/pAYXNzbc7CMnBl+tULGVHOy3oE=
|
||||
golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
|
@ -154,12 +154,12 @@ func (s *Socks5) Serve(c net.Conn) {
|
||||
func (s *Socks5) ListenAndServeUDP() {
|
||||
lc, err := net.ListenPacket("udp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[socks5-udp] failed to listen on %s: %v", s.addr, err)
|
||||
log.F("[socks5] failed to listen on UDP %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
defer lc.Close()
|
||||
|
||||
log.F("[socks5-udp] listening UDP on %s", s.addr)
|
||||
log.F("[socks5] listening UDP on %s", s.addr)
|
||||
|
||||
var nm sync.Map
|
||||
buf := make([]byte, proxy.UDPBufSize)
|
||||
@ -169,7 +169,7 @@ func (s *Socks5) ListenAndServeUDP() {
|
||||
|
||||
n, raddr, err := c.ReadFrom(buf)
|
||||
if err != nil {
|
||||
log.F("[socks5-udp] remote read error: %v", err)
|
||||
log.F("[socks5u] remote read error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -177,13 +177,13 @@ func (s *Socks5) ListenAndServeUDP() {
|
||||
v, ok := nm.Load(raddr.String())
|
||||
if !ok && v == nil {
|
||||
if c.tgtAddr == nil {
|
||||
log.F("[socks5-udp] can not get target address, not a valid request")
|
||||
log.F("[socks5u] can not get target address, not a valid request")
|
||||
continue
|
||||
}
|
||||
|
||||
lpc, nextHop, err := s.proxy.DialUDP("udp", c.tgtAddr.String())
|
||||
if err != nil {
|
||||
log.F("[socks5-udp] remote dial error: %v", err)
|
||||
log.F("[socks5u] remote dial error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -196,7 +196,7 @@ func (s *Socks5) ListenAndServeUDP() {
|
||||
nm.Delete(raddr.String())
|
||||
}()
|
||||
|
||||
log.F("[socks5-udp] %s <-> %s", raddr, c.tgtAddr)
|
||||
log.F("[socks5u] %s <-> %s", raddr, c.tgtAddr)
|
||||
|
||||
} else {
|
||||
pc = v.(*PktConn)
|
||||
@ -204,11 +204,11 @@ func (s *Socks5) ListenAndServeUDP() {
|
||||
|
||||
_, err = pc.WriteTo(buf[:n], pc.writeAddr)
|
||||
if err != nil {
|
||||
log.F("[socks5-udp] remote write error: %v", err)
|
||||
log.F("[socks5u] remote write error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// log.F("[socks5-udp] %s <-> %s", raddr, c.tgtAddr)
|
||||
// log.F("[socks5u] %s <-> %s", raddr, c.tgtAddr)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ func (s *SS) Serve(c net.Conn) {
|
||||
if uot && dialer.Addr() == "DIRECT" {
|
||||
rc, err := net.ListenPacket("udp", "")
|
||||
if err != nil {
|
||||
log.F("[ss-uottun] UDP remote listen error: %v", err)
|
||||
log.F("[ssuot] UDP remote listen error: %v", err)
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
@ -126,7 +126,7 @@ func (s *SS) Serve(c net.Conn) {
|
||||
|
||||
n, err := c.Read(buf)
|
||||
if err != nil {
|
||||
log.F("[ss-uottun] error in read: %s\n", err)
|
||||
log.F("[ssuot] error in read: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
@ -135,7 +135,7 @@ func (s *SS) Serve(c net.Conn) {
|
||||
|
||||
n, _, err = rc.ReadFrom(buf)
|
||||
if err != nil {
|
||||
log.F("[ss-uottun] read error: %v", err)
|
||||
log.F("[ssuot] read error: %v", err)
|
||||
}
|
||||
|
||||
c.Write(buf[:n])
|
||||
@ -172,14 +172,14 @@ func (s *SS) Serve(c net.Conn) {
|
||||
func (s *SS) ListenAndServeUDP() {
|
||||
lc, err := net.ListenPacket("udp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[ssu] failed to listen on %s: %v", s.addr, err)
|
||||
log.F("[ss] failed to listen on UDP %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
defer lc.Close()
|
||||
|
||||
lc = s.PacketConn(lc)
|
||||
|
||||
log.F("[ssu] listening UDP on %s", s.addr)
|
||||
log.F("[ss] listening UDP on %s", s.addr)
|
||||
|
||||
var nm sync.Map
|
||||
buf := make([]byte, proxy.UDPBufSize)
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
"github.com/nadoo/glider/proxy/socks"
|
||||
)
|
||||
|
||||
// PktConn .
|
||||
// PktConn is a udp Packet.Conn.
|
||||
type PktConn struct {
|
||||
net.Conn
|
||||
|
||||
@ -58,7 +58,7 @@ func (pc *PktConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||
// Payload
|
||||
n, err := io.ReadFull(pc.Conn, b[:length])
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
return n, nil, err
|
||||
}
|
||||
|
||||
// TODO: check the addr in return value, it's a fake packetConn so the addr is not valid
|
||||
|
@ -17,7 +17,7 @@ import (
|
||||
"github.com/nadoo/glider/proxy/socks"
|
||||
)
|
||||
|
||||
// Trojan is a base trojan struct
|
||||
// Trojan is a base trojan struct.
|
||||
type Trojan struct {
|
||||
dialer proxy.Dialer
|
||||
proxy proxy.Proxy
|
||||
@ -67,7 +67,7 @@ func NewTrojan(s string, d proxy.Dialer, p proxy.Proxy) (*Trojan, error) {
|
||||
t.tlsConfig = &tls.Config{
|
||||
ServerName: t.serverName,
|
||||
InsecureSkipVerify: t.skipVerify,
|
||||
NextProtos: []string{"http/1.1", "h2"},
|
||||
NextProtos: []string{"http/1.1"},
|
||||
ClientSessionCache: tls.NewLRUClientSessionCache(64),
|
||||
MinVersion: tls.VersionTLS10,
|
||||
}
|
||||
|
@ -1,8 +1,12 @@
|
||||
package vless
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/nadoo/glider/pool"
|
||||
)
|
||||
|
||||
// Atyp is vless addr type.
|
||||
@ -19,6 +23,9 @@ const (
|
||||
// Addr is vless addr.
|
||||
type Addr []byte
|
||||
|
||||
// MaxHostLen is the maximum size of host in bytes.
|
||||
const MaxHostLen = 255
|
||||
|
||||
// Port is vless addr port.
|
||||
type Port uint16
|
||||
|
||||
@ -43,7 +50,7 @@ func ParseAddr(s string) (Atyp, Addr, Port, error) {
|
||||
copy(addr[:], ip)
|
||||
}
|
||||
} else {
|
||||
if len(host) > 255 {
|
||||
if len(host) > MaxHostLen {
|
||||
return 0, nil, 0, err
|
||||
}
|
||||
addr = make([]byte, 1+len(host))
|
||||
@ -59,3 +66,67 @@ func ParseAddr(s string) (Atyp, Addr, Port, error) {
|
||||
|
||||
return atyp, addr, Port(portnum), err
|
||||
}
|
||||
|
||||
// ReadAddr reads just enough bytes from r to get addr.
|
||||
func ReadAddr(r io.Reader) (atyp Atyp, host Addr, port Port, err error) {
|
||||
buf := pool.GetBuffer(2)
|
||||
defer pool.PutBuffer(buf)
|
||||
|
||||
// port
|
||||
_, err = io.ReadFull(r, buf[:2])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
port = Port(binary.BigEndian.Uint16(buf[:2]))
|
||||
|
||||
// atyp
|
||||
_, err = io.ReadFull(r, buf[:1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
atyp = Atyp(buf[0])
|
||||
|
||||
switch atyp {
|
||||
case AtypIP4:
|
||||
host = make([]byte, net.IPv4len)
|
||||
_, err = io.ReadFull(r, host)
|
||||
return
|
||||
case AtypIP6:
|
||||
host = make([]byte, net.IPv6len)
|
||||
_, err = io.ReadFull(r, host)
|
||||
return
|
||||
case AtypDomain:
|
||||
_, err = io.ReadFull(r, buf[:1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
host = make([]byte, int(buf[0]))
|
||||
_, err = io.ReadFull(r, host)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ReadAddrString reads just enough bytes from r to get addr string.
|
||||
func ReadAddrString(r io.Reader) (string, error) {
|
||||
atyp, host, port, err := ReadAddr(r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return AddrString(atyp, host, port), nil
|
||||
}
|
||||
|
||||
// AddrString returns a addr string inf format "host:port".
|
||||
func AddrString(atyp Atyp, addr Addr, port Port) string {
|
||||
var host string
|
||||
|
||||
switch atyp {
|
||||
case AtypIP4, AtypIP6:
|
||||
host = net.IP(addr).String()
|
||||
case AtypDomain:
|
||||
host = string(addr)
|
||||
}
|
||||
|
||||
return net.JoinHostPort(host, strconv.Itoa(int(port)))
|
||||
}
|
||||
|
@ -2,33 +2,55 @@ package vless
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/nadoo/glider/pool"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
const Version byte = 0
|
||||
// NewVLessDialer returns a vless proxy dialer.
|
||||
func NewVLessDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewVLess(s, dialer, nil)
|
||||
}
|
||||
|
||||
// CMD types.
|
||||
const (
|
||||
CmdTCP byte = 1
|
||||
CmdUDP byte = 2
|
||||
)
|
||||
// Addr returns forwarder's address.
|
||||
func (s *VLess) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// Conn is a vless client connection.
|
||||
type Conn struct {
|
||||
// Dial connects to the address addr on the network net via the proxy.
|
||||
func (s *VLess) Dial(network, addr string) (net.Conn, error) {
|
||||
rc, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewClientConn(rc, s.uuid, network, addr)
|
||||
}
|
||||
|
||||
// DialUDP connects to the given address via the proxy.
|
||||
func (s *VLess) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||
c, err := s.Dial("udp", addr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
pkc := NewPktConn(c)
|
||||
return pkc, nil, nil
|
||||
}
|
||||
|
||||
// ClientConn is a vless client connection.
|
||||
type ClientConn struct {
|
||||
net.Conn
|
||||
rcved bool
|
||||
}
|
||||
|
||||
// ClientConn returns a new vless client conn.
|
||||
func ClientConn(c net.Conn, uuid [16]byte, network, target string) (*Conn, error) {
|
||||
// NewClientConn returns a new vless client conn.
|
||||
func NewClientConn(c net.Conn, uuid [16]byte, network, target string) (*ClientConn, error) {
|
||||
atyp, addr, port, err := ParseAddr(target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -45,7 +67,7 @@ func ClientConn(c net.Conn, uuid [16]byte, network, target string) (*Conn, error
|
||||
if network == "udp" {
|
||||
cmd = CmdUDP
|
||||
}
|
||||
buf.WriteByte(cmd) // cmd
|
||||
buf.WriteByte(byte(cmd)) // cmd
|
||||
|
||||
// target
|
||||
err = binary.Write(buf, binary.BigEndian, uint16(port)) // port
|
||||
@ -56,10 +78,10 @@ func ClientConn(c net.Conn, uuid [16]byte, network, target string) (*Conn, error
|
||||
buf.Write(addr) //addr
|
||||
|
||||
_, err = c.Write(buf.Bytes())
|
||||
return &Conn{Conn: c}, err
|
||||
return &ClientConn{Conn: c}, err
|
||||
}
|
||||
|
||||
func (c *Conn) Read(b []byte) (n int, err error) {
|
||||
func (c *ClientConn) Read(b []byte) (n int, err error) {
|
||||
if !c.rcved {
|
||||
buf := pool.GetBuffer(2)
|
||||
defer pool.PutBuffer(buf)
|
||||
@ -81,14 +103,3 @@ func (c *Conn) Read(b []byte) (n int, err error) {
|
||||
|
||||
return c.Conn.Read(b)
|
||||
}
|
||||
|
||||
// StrToUUID converts string to uuid.
|
||||
// s fomat: "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
|
||||
func StrToUUID(s string) (uuid [16]byte, err error) {
|
||||
b := []byte(strings.Replace(s, "-", "", -1))
|
||||
if len(b) != 32 {
|
||||
return uuid, errors.New("invalid UUID: " + s)
|
||||
}
|
||||
_, err = hex.Decode(uuid[:], b)
|
||||
return
|
||||
}
|
||||
|
@ -9,15 +9,11 @@ import (
|
||||
"github.com/nadoo/glider/pool"
|
||||
)
|
||||
|
||||
// PktConn .
|
||||
type PktConn struct {
|
||||
net.Conn
|
||||
}
|
||||
// PktConn is a udp Packet.Conn.
|
||||
type PktConn struct{ net.Conn }
|
||||
|
||||
// NewPktConn returns a PktConn.
|
||||
func NewPktConn(c net.Conn) *PktConn {
|
||||
return &PktConn{Conn: c}
|
||||
}
|
||||
func NewPktConn(c net.Conn) *PktConn { return &PktConn{Conn: c} }
|
||||
|
||||
// ReadFrom implements the necessary function of net.PacketConn.
|
||||
// TODO: we know that we use it in proxy.RelayUDP and the length of b is enough, check it later.
|
||||
@ -35,7 +31,7 @@ func (pc *PktConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||
// Payload
|
||||
n, err := io.ReadFull(pc.Conn, b[:length])
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
return n, nil, err
|
||||
}
|
||||
|
||||
return n, nil, nil
|
||||
|
150
proxy/vless/server.go
Normal file
150
proxy/vless/server.go
Normal file
@ -0,0 +1,150 @@
|
||||
package vless
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/nadoo/glider/log"
|
||||
"github.com/nadoo/glider/pool"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
// NewVLessServer returns a vless proxy server.
|
||||
func NewVLessServer(s string, p proxy.Proxy) (proxy.Server, error) {
|
||||
return NewVLess(s, nil, p)
|
||||
}
|
||||
|
||||
// ListenAndServe listen and serves connections.
|
||||
func (s *VLess) ListenAndServe() {
|
||||
l, err := net.Listen("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[vless] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
log.F("[vless] listening TCP on %s", s.addr)
|
||||
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
log.F("[vless] failed to accept: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
go s.Serve(c)
|
||||
}
|
||||
}
|
||||
|
||||
// Serve serves a connection.
|
||||
func (s *VLess) Serve(c net.Conn) {
|
||||
defer c.Close()
|
||||
|
||||
if c, ok := c.(*net.TCPConn); ok {
|
||||
c.SetKeepAlive(true)
|
||||
}
|
||||
|
||||
c = NewServerConn(c)
|
||||
_, err := s.readHeader(c)
|
||||
if err != nil {
|
||||
log.F("[vless] verify header error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
tgt, err := ReadAddrString(c)
|
||||
if err != nil {
|
||||
log.F("[vless] get target error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
dialer := s.proxy.NextDialer(tgt)
|
||||
rc, err := dialer.Dial("tcp", tgt)
|
||||
if err != nil {
|
||||
log.F("[vless] %s <-> %s via %s, error in dial: %v", c.RemoteAddr(), tgt, dialer.Addr(), err)
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
log.F("[vless] %s <-> %s via %s", c.RemoteAddr(), tgt, dialer.Addr())
|
||||
|
||||
if err = proxy.Relay(c, rc); err != nil {
|
||||
log.F("[vless] %s <-> %s via %s, relay error: %v", c.RemoteAddr(), tgt, dialer.Addr(), err)
|
||||
// record remote conn failure only
|
||||
if !strings.Contains(err.Error(), s.addr) {
|
||||
s.proxy.Record(dialer, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *VLess) readHeader(r io.Reader) (CmdType, error) {
|
||||
buf := pool.GetBuffer(16)
|
||||
defer pool.PutBuffer(buf)
|
||||
|
||||
// ver
|
||||
if _, err := io.ReadFull(r, buf[:1]); err != nil {
|
||||
return CmdErr, fmt.Errorf("get version error: %v", err)
|
||||
}
|
||||
|
||||
if buf[0] != Version {
|
||||
return CmdErr, fmt.Errorf("version %d not supported", buf[0])
|
||||
}
|
||||
|
||||
// uuid
|
||||
if _, err := io.ReadFull(r, buf[:16]); err != nil {
|
||||
return CmdErr, fmt.Errorf("get uuid error: %v", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(s.uuid[:], buf) {
|
||||
return CmdErr, fmt.Errorf("auth failed, client id: %02x", buf[:16])
|
||||
}
|
||||
|
||||
// addLen
|
||||
if _, err := io.ReadFull(r, buf[:1]); err != nil {
|
||||
return CmdErr, fmt.Errorf("get addon length error: %v", err)
|
||||
}
|
||||
|
||||
// ignore addons
|
||||
if addLen := int64(buf[0]); addLen > 0 {
|
||||
proxy.CopyN(ioutil.Discard, r, addLen)
|
||||
}
|
||||
|
||||
// cmd
|
||||
if _, err := io.ReadFull(r, buf[:1]); err != nil {
|
||||
return CmdErr, fmt.Errorf("get cmd error: %v", err)
|
||||
}
|
||||
|
||||
return CmdType(buf[0]), nil
|
||||
}
|
||||
|
||||
// ServerConn is a vless client connection.
|
||||
type ServerConn struct {
|
||||
net.Conn
|
||||
sent bool
|
||||
}
|
||||
|
||||
// NewServerConn returns a new vless client conn.
|
||||
func NewServerConn(c net.Conn) *ServerConn {
|
||||
return &ServerConn{Conn: c}
|
||||
}
|
||||
|
||||
func (c *ServerConn) Write(b []byte) (int, error) {
|
||||
if !c.sent {
|
||||
buf := pool.GetWriteBuffer()
|
||||
defer pool.PutWriteBuffer(buf)
|
||||
|
||||
buf.WriteByte(Version) // ver
|
||||
buf.WriteByte(0) // addonLen
|
||||
|
||||
buf.Write(b)
|
||||
c.sent = true
|
||||
|
||||
n, err := c.Conn.Write(buf.Bytes())
|
||||
return n - 2, err
|
||||
}
|
||||
|
||||
return c.Conn.Write(b)
|
||||
}
|
@ -1,25 +1,42 @@
|
||||
package vless
|
||||
|
||||
import (
|
||||
"net"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
// Version of vless protocol.
|
||||
const Version byte = 0
|
||||
|
||||
// CmdType is vless cmd type.
|
||||
type CmdType byte
|
||||
|
||||
// CMD types.
|
||||
const (
|
||||
CmdErr CmdType = 0
|
||||
CmdTCP CmdType = 1
|
||||
CmdUDP CmdType = 2
|
||||
)
|
||||
|
||||
// VLess struct.
|
||||
type VLess struct {
|
||||
dialer proxy.Dialer
|
||||
proxy proxy.Proxy
|
||||
addr string
|
||||
uuid [16]byte
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("vless", NewVLessDialer)
|
||||
proxy.RegisterServer("vless", NewVLessServer)
|
||||
}
|
||||
|
||||
// NewVLess returns a vless proxy.
|
||||
func NewVLess(s string, d proxy.Dialer) (*VLess, error) {
|
||||
func NewVLess(s string, d proxy.Dialer, p proxy.Proxy) (*VLess, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -31,43 +48,23 @@ func NewVLess(s string, d proxy.Dialer) (*VLess, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p := &VLess{
|
||||
v := &VLess{
|
||||
dialer: d,
|
||||
proxy: p,
|
||||
addr: addr,
|
||||
uuid: uuid,
|
||||
}
|
||||
|
||||
return p, nil
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// NewVLessDialer returns a vless proxy dialer.
|
||||
func NewVLessDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewVLess(s, dialer)
|
||||
}
|
||||
|
||||
// Addr returns forwarder's address.
|
||||
func (s *VLess) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
// StrToUUID converts string to uuid.
|
||||
// s fomat: "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
|
||||
func StrToUUID(s string) (uuid [16]byte, err error) {
|
||||
b := []byte(strings.Replace(s, "-", "", -1))
|
||||
if len(b) != 32 {
|
||||
return uuid, errors.New("invalid UUID: " + s)
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// Dial connects to the address addr on the network net via the proxy.
|
||||
func (s *VLess) Dial(network, addr string) (net.Conn, error) {
|
||||
rc, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ClientConn(rc, s.uuid, network, addr)
|
||||
}
|
||||
|
||||
// DialUDP connects to the given address via the proxy.
|
||||
func (s *VLess) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||
c, err := s.Dial("udp", addr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
pkc := NewPktConn(c)
|
||||
return pkc, nil, nil
|
||||
_, err = hex.Decode(uuid[:], b)
|
||||
return
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ func handleDHCP(serverIP net.IP, mask net.IPMask, pool *Pool) server4.Handler {
|
||||
|
||||
replyIp, err := pool.AssignIP(m.ClientHWAddr)
|
||||
if err != nil {
|
||||
log.F("[dpcpd] can not assign IP error %s", err)
|
||||
log.F("[dpcpd] can not assign IP, error %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
@ -92,6 +92,10 @@ func handleDHCP(serverIP net.IP, mask net.IPMask, pool *Pool) server4.Handler {
|
||||
// RFC 2131, Section 4.3.1. IP lease time: MUST
|
||||
dhcpv4.WithOption(dhcpv4.OptIPAddressLeaseTime(leaseTime)),
|
||||
)
|
||||
if err != nil {
|
||||
log.F("[dpcpd] can not create reply message, error %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
if val := m.Options.Get(dhcpv4.OptionClientIdentifier); len(val) > 0 {
|
||||
reply.UpdateOption(dhcpv4.OptGeneric(dhcpv4.OptionClientIdentifier, val))
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Pool is a dhcp pool.
|
||||
type Pool struct {
|
||||
items []*item
|
||||
}
|
||||
@ -31,6 +32,7 @@ func NewPool(lease time.Duration, start, end net.IP) (*Pool, error) {
|
||||
return &Pool{items: items}, nil
|
||||
}
|
||||
|
||||
// AssignIP assigns an ip to mac from dhco pool.
|
||||
func (p *Pool) AssignIP(mac net.HardwareAddr) (net.IP, error) {
|
||||
var ip net.IP
|
||||
for _, item := range p.items {
|
||||
|
Loading…
Reference in New Issue
Block a user