vless: spport server mode

This commit is contained in:
nadoo 2020-10-03 20:51:27 +08:00
parent 1ed7fbff65
commit 8b6739f12c
15 changed files with 328 additions and 97 deletions

View File

@ -4,7 +4,7 @@
[![GitHub release](https://img.shields.io/github/v/release/nadoo/glider.svg?style=flat-square&include_prereleases)](https://github.com/nadoo/glider/releases) [![GitHub release](https://img.shields.io/github/v/release/nadoo/glider.svg?style=flat-square&include_prereleases)](https://github.com/nadoo/glider/releases)
[![Actions Status](https://img.shields.io/github/workflow/status/nadoo/glider/Build?style=flat-square)](https://github.com/nadoo/glider/actions) [![Actions Status](https://img.shields.io/github/workflow/status/nadoo/glider/Build?style=flat-square)](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. we can set up local listeners as proxy servers, and forward requests to internet via forwarders.

View File

@ -14,6 +14,7 @@ import (
var flag = conflag.New() var flag = conflag.New()
// Config is global config struct.
type Config struct { type Config struct {
Verbose bool Verbose bool

7
go.mod
View File

@ -10,10 +10,9 @@ require (
github.com/nadoo/ipset v0.3.0 github.com/nadoo/ipset v0.3.0
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/xtaci/kcp-go/v5 v5.5.17 github.com/xtaci/kcp-go/v5 v5.5.17
golang.org/x/crypto v0.0.0-20201002094018-c90954cbb977 golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0
golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 // indirect golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c // indirect
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f // indirect golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d // indirect
golang.org/x/tools v0.0.0-20201002055958-0d28ed0cbe40 // indirect
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
) )

12
go.sum
View File

@ -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-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 h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE=
golang.org/x/crypto v0.0.0-20201002094018-c90954cbb977/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= 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-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 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 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-20201002202402-0a1ea396d57c h1:dk0ukUIHmGHqASjP0iue2261isepFCC6XRCSd1nHgDw=
golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 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-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-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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 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-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-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-20201002184944-ecd9fd270d5d h1:vWQvJ/Z0Lu+9/8oQ/pAYXNzbc7CMnBl+tULGVHOy3oE=
golang.org/x/tools v0.0.0-20201002055958-0d28ed0cbe40/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= 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-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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=

View File

@ -154,12 +154,12 @@ func (s *Socks5) Serve(c net.Conn) {
func (s *Socks5) ListenAndServeUDP() { func (s *Socks5) ListenAndServeUDP() {
lc, err := net.ListenPacket("udp", s.addr) lc, err := net.ListenPacket("udp", s.addr)
if err != nil { 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 return
} }
defer lc.Close() 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 var nm sync.Map
buf := make([]byte, proxy.UDPBufSize) buf := make([]byte, proxy.UDPBufSize)
@ -169,7 +169,7 @@ func (s *Socks5) ListenAndServeUDP() {
n, raddr, err := c.ReadFrom(buf) n, raddr, err := c.ReadFrom(buf)
if err != nil { if err != nil {
log.F("[socks5-udp] remote read error: %v", err) log.F("[socks5u] remote read error: %v", err)
continue continue
} }
@ -177,13 +177,13 @@ func (s *Socks5) ListenAndServeUDP() {
v, ok := nm.Load(raddr.String()) v, ok := nm.Load(raddr.String())
if !ok && v == nil { if !ok && v == nil {
if c.tgtAddr == 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 continue
} }
lpc, nextHop, err := s.proxy.DialUDP("udp", c.tgtAddr.String()) lpc, nextHop, err := s.proxy.DialUDP("udp", c.tgtAddr.String())
if err != nil { if err != nil {
log.F("[socks5-udp] remote dial error: %v", err) log.F("[socks5u] remote dial error: %v", err)
continue continue
} }
@ -196,7 +196,7 @@ func (s *Socks5) ListenAndServeUDP() {
nm.Delete(raddr.String()) nm.Delete(raddr.String())
}() }()
log.F("[socks5-udp] %s <-> %s", raddr, c.tgtAddr) log.F("[socks5u] %s <-> %s", raddr, c.tgtAddr)
} else { } else {
pc = v.(*PktConn) pc = v.(*PktConn)
@ -204,11 +204,11 @@ func (s *Socks5) ListenAndServeUDP() {
_, err = pc.WriteTo(buf[:n], pc.writeAddr) _, err = pc.WriteTo(buf[:n], pc.writeAddr)
if err != nil { if err != nil {
log.F("[socks5-udp] remote write error: %v", err) log.F("[socks5u] remote write error: %v", err)
continue continue
} }
// log.F("[socks5-udp] %s <-> %s", raddr, c.tgtAddr) // log.F("[socks5u] %s <-> %s", raddr, c.tgtAddr)
} }
} }

View File

@ -117,7 +117,7 @@ func (s *SS) Serve(c net.Conn) {
if uot && dialer.Addr() == "DIRECT" { if uot && dialer.Addr() == "DIRECT" {
rc, err := net.ListenPacket("udp", "") rc, err := net.ListenPacket("udp", "")
if err != nil { 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() defer rc.Close()
@ -126,7 +126,7 @@ func (s *SS) Serve(c net.Conn) {
n, err := c.Read(buf) n, err := c.Read(buf)
if err != nil { if err != nil {
log.F("[ss-uottun] error in read: %s\n", err) log.F("[ssuot] error in read: %s\n", err)
return return
} }
@ -135,7 +135,7 @@ func (s *SS) Serve(c net.Conn) {
n, _, err = rc.ReadFrom(buf) n, _, err = rc.ReadFrom(buf)
if err != nil { if err != nil {
log.F("[ss-uottun] read error: %v", err) log.F("[ssuot] read error: %v", err)
} }
c.Write(buf[:n]) c.Write(buf[:n])
@ -172,14 +172,14 @@ func (s *SS) Serve(c net.Conn) {
func (s *SS) ListenAndServeUDP() { func (s *SS) ListenAndServeUDP() {
lc, err := net.ListenPacket("udp", s.addr) lc, err := net.ListenPacket("udp", s.addr)
if err != nil { 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 return
} }
defer lc.Close() defer lc.Close()
lc = s.PacketConn(lc) 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 var nm sync.Map
buf := make([]byte, proxy.UDPBufSize) buf := make([]byte, proxy.UDPBufSize)

View File

@ -11,7 +11,7 @@ import (
"github.com/nadoo/glider/proxy/socks" "github.com/nadoo/glider/proxy/socks"
) )
// PktConn . // PktConn is a udp Packet.Conn.
type PktConn struct { type PktConn struct {
net.Conn net.Conn
@ -58,7 +58,7 @@ func (pc *PktConn) ReadFrom(b []byte) (int, net.Addr, error) {
// Payload // Payload
n, err := io.ReadFull(pc.Conn, b[:length]) n, err := io.ReadFull(pc.Conn, b[:length])
if err != nil { 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 // TODO: check the addr in return value, it's a fake packetConn so the addr is not valid

View File

@ -17,7 +17,7 @@ import (
"github.com/nadoo/glider/proxy/socks" "github.com/nadoo/glider/proxy/socks"
) )
// Trojan is a base trojan struct // Trojan is a base trojan struct.
type Trojan struct { type Trojan struct {
dialer proxy.Dialer dialer proxy.Dialer
proxy proxy.Proxy proxy proxy.Proxy
@ -67,7 +67,7 @@ func NewTrojan(s string, d proxy.Dialer, p proxy.Proxy) (*Trojan, error) {
t.tlsConfig = &tls.Config{ t.tlsConfig = &tls.Config{
ServerName: t.serverName, ServerName: t.serverName,
InsecureSkipVerify: t.skipVerify, InsecureSkipVerify: t.skipVerify,
NextProtos: []string{"http/1.1", "h2"}, NextProtos: []string{"http/1.1"},
ClientSessionCache: tls.NewLRUClientSessionCache(64), ClientSessionCache: tls.NewLRUClientSessionCache(64),
MinVersion: tls.VersionTLS10, MinVersion: tls.VersionTLS10,
} }

View File

@ -1,8 +1,12 @@
package vless package vless
import ( import (
"encoding/binary"
"io"
"net" "net"
"strconv" "strconv"
"github.com/nadoo/glider/pool"
) )
// Atyp is vless addr type. // Atyp is vless addr type.
@ -19,6 +23,9 @@ const (
// Addr is vless addr. // Addr is vless addr.
type Addr []byte type Addr []byte
// MaxHostLen is the maximum size of host in bytes.
const MaxHostLen = 255
// Port is vless addr port. // Port is vless addr port.
type Port uint16 type Port uint16
@ -43,7 +50,7 @@ func ParseAddr(s string) (Atyp, Addr, Port, error) {
copy(addr[:], ip) copy(addr[:], ip)
} }
} else { } else {
if len(host) > 255 { if len(host) > MaxHostLen {
return 0, nil, 0, err return 0, nil, 0, err
} }
addr = make([]byte, 1+len(host)) addr = make([]byte, 1+len(host))
@ -59,3 +66,67 @@ func ParseAddr(s string) (Atyp, Addr, Port, error) {
return atyp, addr, Port(portnum), err 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)))
}

View File

@ -2,33 +2,55 @@ package vless
import ( import (
"encoding/binary" "encoding/binary"
"encoding/hex"
"errors" "errors"
"io" "io"
"io/ioutil" "io/ioutil"
"net" "net"
"strings"
"github.com/nadoo/glider/pool" "github.com/nadoo/glider/pool"
"github.com/nadoo/glider/proxy" "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. // Addr returns forwarder's address.
const ( func (s *VLess) Addr() string {
CmdTCP byte = 1 if s.addr == "" {
CmdUDP byte = 2 return s.dialer.Addr()
) }
return s.addr
}
// Conn is a vless client connection. // Dial connects to the address addr on the network net via the proxy.
type Conn struct { 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 net.Conn
rcved bool rcved bool
} }
// ClientConn returns a new vless client conn. // NewClientConn returns a new vless client conn.
func ClientConn(c net.Conn, uuid [16]byte, network, target string) (*Conn, error) { func NewClientConn(c net.Conn, uuid [16]byte, network, target string) (*ClientConn, error) {
atyp, addr, port, err := ParseAddr(target) atyp, addr, port, err := ParseAddr(target)
if err != nil { if err != nil {
return nil, err return nil, err
@ -45,7 +67,7 @@ func ClientConn(c net.Conn, uuid [16]byte, network, target string) (*Conn, error
if network == "udp" { if network == "udp" {
cmd = CmdUDP cmd = CmdUDP
} }
buf.WriteByte(cmd) // cmd buf.WriteByte(byte(cmd)) // cmd
// target // target
err = binary.Write(buf, binary.BigEndian, uint16(port)) // port 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 buf.Write(addr) //addr
_, err = c.Write(buf.Bytes()) _, 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 { if !c.rcved {
buf := pool.GetBuffer(2) buf := pool.GetBuffer(2)
defer pool.PutBuffer(buf) defer pool.PutBuffer(buf)
@ -81,14 +103,3 @@ func (c *Conn) Read(b []byte) (n int, err error) {
return c.Conn.Read(b) 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
}

View File

@ -9,15 +9,11 @@ import (
"github.com/nadoo/glider/pool" "github.com/nadoo/glider/pool"
) )
// PktConn . // PktConn is a udp Packet.Conn.
type PktConn struct { type PktConn struct{ net.Conn }
net.Conn
}
// NewPktConn returns a PktConn. // NewPktConn returns a PktConn.
func NewPktConn(c net.Conn) *PktConn { func NewPktConn(c net.Conn) *PktConn { return &PktConn{Conn: c} }
return &PktConn{Conn: c}
}
// ReadFrom implements the necessary function of net.PacketConn. // 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. // 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 // Payload
n, err := io.ReadFull(pc.Conn, b[:length]) n, err := io.ReadFull(pc.Conn, b[:length])
if err != nil { if err != nil {
return 0, nil, err return n, nil, err
} }
return n, nil, nil return n, nil, nil

150
proxy/vless/server.go Normal file
View 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)
}

View File

@ -1,25 +1,42 @@
package vless package vless
import ( import (
"net" "encoding/hex"
"errors"
"net/url" "net/url"
"strings"
"github.com/nadoo/glider/proxy" "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. // VLess struct.
type VLess struct { type VLess struct {
dialer proxy.Dialer dialer proxy.Dialer
proxy proxy.Proxy
addr string addr string
uuid [16]byte uuid [16]byte
} }
func init() { func init() {
proxy.RegisterDialer("vless", NewVLessDialer) proxy.RegisterDialer("vless", NewVLessDialer)
proxy.RegisterServer("vless", NewVLessServer)
} }
// NewVLess returns a vless proxy. // 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) u, err := url.Parse(s)
if err != nil { if err != nil {
return nil, err return nil, err
@ -31,43 +48,23 @@ func NewVLess(s string, d proxy.Dialer) (*VLess, error) {
return nil, err return nil, err
} }
p := &VLess{ v := &VLess{
dialer: d, dialer: d,
proxy: p,
addr: addr, addr: addr,
uuid: uuid, uuid: uuid,
} }
return p, nil return v, nil
} }
// NewVLessDialer returns a vless proxy dialer. // StrToUUID converts string to uuid.
func NewVLessDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) { // s fomat: "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
return NewVLess(s, dialer) func StrToUUID(s string) (uuid [16]byte, err error) {
} b := []byte(strings.Replace(s, "-", "", -1))
if len(b) != 32 {
// Addr returns forwarder's address. return uuid, errors.New("invalid UUID: " + s)
func (s *VLess) Addr() string {
if s.addr == "" {
return s.dialer.Addr()
} }
return s.addr _, err = hex.Decode(uuid[:], b)
} return
// 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
} }

View File

@ -76,7 +76,7 @@ func handleDHCP(serverIP net.IP, mask net.IPMask, pool *Pool) server4.Handler {
replyIp, err := pool.AssignIP(m.ClientHWAddr) replyIp, err := pool.AssignIP(m.ClientHWAddr)
if err != nil { if err != nil {
log.F("[dpcpd] can not assign IP error %s", err) log.F("[dpcpd] can not assign IP, error %s", err)
return 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 // RFC 2131, Section 4.3.1. IP lease time: MUST
dhcpv4.WithOption(dhcpv4.OptIPAddressLeaseTime(leaseTime)), 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 { if val := m.Options.Get(dhcpv4.OptionClientIdentifier); len(val) > 0 {
reply.UpdateOption(dhcpv4.OptGeneric(dhcpv4.OptionClientIdentifier, val)) reply.UpdateOption(dhcpv4.OptGeneric(dhcpv4.OptionClientIdentifier, val))

View File

@ -8,6 +8,7 @@ import (
"time" "time"
) )
// Pool is a dhcp pool.
type Pool struct { type Pool struct {
items []*item items []*item
} }
@ -31,6 +32,7 @@ func NewPool(lease time.Duration, start, end net.IP) (*Pool, error) {
return &Pool{items: items}, nil return &Pool{items: items}, nil
} }
// AssignIP assigns an ip to mac from dhco pool.
func (p *Pool) AssignIP(mac net.HardwareAddr) (net.IP, error) { func (p *Pool) AssignIP(mac net.HardwareAddr) (net.IP, error) {
var ip net.IP var ip net.IP
for _, item := range p.items { for _, item := range p.items {