Feature: socks5 udp associate

This commit is contained in:
Dreamacro
2019-04-23 23:29:36 +08:00
parent 49f8902961
commit c92cda6980
25 changed files with 339 additions and 85 deletions

View File

@ -10,26 +10,16 @@ import (
// HTTPAdapter is a adapter for HTTP connection
type HTTPAdapter struct {
net.Conn
metadata *C.Metadata
conn net.Conn
R *http.Request
}
// Close HTTP connection
func (h *HTTPAdapter) Close() {
h.conn.Close()
}
// Metadata return destination metadata
func (h *HTTPAdapter) Metadata() *C.Metadata {
return h.metadata
}
// Conn return raw net.Conn of HTTP
func (h *HTTPAdapter) Conn() net.Conn {
return h.conn
}
// NewHTTP is HTTPAdapter generator
func NewHTTP(request *http.Request, conn net.Conn) *HTTPAdapter {
metadata := parseHTTPAddr(request)
@ -37,7 +27,7 @@ func NewHTTP(request *http.Request, conn net.Conn) *HTTPAdapter {
return &HTTPAdapter{
metadata: metadata,
R: request,
conn: conn,
Conn: conn,
}
}

View File

@ -11,6 +11,6 @@ func NewHTTPS(request *http.Request, conn net.Conn) *SocketAdapter {
metadata.SourceIP = parseSourceIP(conn)
return &SocketAdapter{
metadata: metadata,
conn: conn,
Conn: conn,
}
}

View File

@ -9,33 +9,24 @@ import (
// SocketAdapter is a adapter for socks and redir connection
type SocketAdapter struct {
conn net.Conn
net.Conn
metadata *C.Metadata
}
// Close socks and redir connection
func (s *SocketAdapter) Close() {
s.conn.Close()
}
// Metadata return destination metadata
func (s *SocketAdapter) Metadata() *C.Metadata {
return s.metadata
}
// Conn return raw net.Conn
func (s *SocketAdapter) Conn() net.Conn {
return s.conn
}
// NewSocket is SocketAdapter generator
func NewSocket(target socks.Addr, conn net.Conn, source C.SourceType) *SocketAdapter {
func NewSocket(target socks.Addr, conn net.Conn, source C.SourceType, netType C.NetWork) *SocketAdapter {
metadata := parseSocksAddr(target)
metadata.NetWork = netType
metadata.Source = source
metadata.SourceIP = parseSourceIP(conn)
return &SocketAdapter{
conn: conn,
Conn: conn,
metadata: metadata,
}
}

View File

@ -11,7 +11,6 @@ import (
func parseSocksAddr(target socks.Addr) *C.Metadata {
metadata := &C.Metadata{
NetWork: C.TCP,
AddrType: int(target[0]),
}

View File

@ -2,6 +2,7 @@ package adapters
import (
"encoding/json"
"errors"
"net"
"net/http"
"time"
@ -13,6 +14,7 @@ import (
type Base struct {
name string
tp C.AdapterType
udp bool
}
func (b *Base) Name() string {
@ -23,6 +25,14 @@ func (b *Base) Type() C.AdapterType {
return b.tp
}
func (b *Base) DialUDP(metadata *C.Metadata) (net.PacketConn, net.Addr, error) {
return nil, nil, errors.New("no support")
}
func (b *Base) SupportUDP() bool {
return b.udp
}
func (b *Base) Destroy() {}
func (b *Base) MarshalJSON() ([]byte, error) {

View File

@ -24,11 +24,22 @@ func (d *Direct) Dial(metadata *C.Metadata) (net.Conn, error) {
return c, nil
}
func (d *Direct) DialUDP(metadata *C.Metadata) (net.PacketConn, net.Addr, error) {
pc, err := net.ListenPacket("udp", "")
if err != nil {
return nil, nil, err
}
addr, _ := net.ResolveUDPAddr("udp", net.JoinHostPort(metadata.String(), metadata.Port))
return pc, addr, nil
}
func NewDirect() *Direct {
return &Direct{
Base: &Base{
name: "DIRECT",
tp: C.Direct,
udp: true,
},
}
}

View File

@ -35,6 +35,16 @@ func (f *Fallback) Dial(metadata *C.Metadata) (net.Conn, error) {
return proxy.Dial(metadata)
}
func (f *Fallback) DialUDP(metadata *C.Metadata) (net.PacketConn, net.Addr, error) {
proxy := f.findAliveProxy()
return proxy.DialUDP(metadata)
}
func (f *Fallback) SupportUDP() bool {
proxy := f.findAliveProxy()
return proxy.SupportUDP()
}
func (f *Fallback) MarshalJSON() ([]byte, error) {
var all []string
for _, proxy := range f.proxies {

View File

@ -67,6 +67,24 @@ func (lb *LoadBalance) Dial(metadata *C.Metadata) (net.Conn, error) {
return lb.proxies[0].Dial(metadata)
}
func (lb *LoadBalance) DialUDP(metadata *C.Metadata) (net.PacketConn, net.Addr, error) {
key := uint64(murmur3.Sum32([]byte(getKey(metadata))))
buckets := int32(len(lb.proxies))
for i := 0; i < lb.maxRetry; i, key = i+1, key+1 {
idx := jumpHash(key, buckets)
proxy := lb.proxies[idx]
if proxy.Alive() {
return proxy.DialUDP(metadata)
}
}
return lb.proxies[0].DialUDP(metadata)
}
func (lb *LoadBalance) SupportUDP() bool {
return true
}
func (lb *LoadBalance) Destroy() {
lb.done <- struct{}{}
}

View File

@ -24,6 +24,14 @@ func (s *Selector) Dial(metadata *C.Metadata) (net.Conn, error) {
return s.selected.Dial(metadata)
}
func (s *Selector) DialUDP(metadata *C.Metadata) (net.PacketConn, net.Addr, error) {
return s.selected.DialUDP(metadata)
}
func (s *Selector) SupportUDP() bool {
return s.selected.SupportUDP()
}
func (s *Selector) MarshalJSON() ([]byte, error) {
var all []string
for k := range s.proxies {

View File

@ -8,6 +8,7 @@ import (
"net"
"strconv"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/common/structure"
obfs "github.com/Dreamacro/clash/component/simple-obfs"
v2rayObfs "github.com/Dreamacro/clash/component/v2ray-plugin"
@ -34,6 +35,7 @@ type ShadowSocksOption struct {
Port int `proxy:"port"`
Password string `proxy:"password"`
Cipher string `proxy:"cipher"`
UDP bool `proxy:"udp,omitempty"`
Plugin string `proxy:"plugin,omitempty"`
PluginOpts map[string]interface{} `proxy:"plugin-opts,omitempty"`
@ -80,6 +82,19 @@ func (ss *ShadowSocks) Dial(metadata *C.Metadata) (net.Conn, error) {
return c, err
}
func (ss *ShadowSocks) DialUDP(metadata *C.Metadata) (net.PacketConn, net.Addr, error) {
pc, err := net.ListenPacket("udp", "")
if err != nil {
return nil, nil, err
}
addr, _ := net.ResolveUDPAddr("udp", ss.server)
remoteAddr, _ := net.ResolveUDPAddr("udp", net.JoinHostPort(metadata.String(), metadata.Port))
pc = ss.cipher.PacketConn(pc)
return &ssUDPConn{PacketConn: pc, rAddr: remoteAddr}, addr, nil
}
func (ss *ShadowSocks) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]string{
"type": ss.Type().String(),
@ -144,6 +159,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
Base: &Base{
name: option.Name,
tp: C.Shadowsocks,
udp: option.UDP,
},
server: server,
cipher: ciph,
@ -173,3 +189,24 @@ func serializesSocksAddr(metadata *C.Metadata) []byte {
}
return bytes.Join(buf, nil)
}
type ssUDPConn struct {
net.PacketConn
rAddr net.Addr
}
func (uc *ssUDPConn) WriteTo(b []byte, addr net.Addr) (int, error) {
buf := pool.BufPool.Get().([]byte)
defer pool.BufPool.Put(buf[:cap(buf)])
rAddr := socks.ParseAddr(uc.rAddr.String())
copy(buf[len(rAddr):], b)
copy(buf, rAddr)
return uc.PacketConn.WriteTo(buf[:len(rAddr)+len(b)], addr)
}
func (uc *ssUDPConn) ReadFrom(b []byte) (int, net.Addr, error) {
n, a, e := uc.PacketConn.ReadFrom(b)
addr := socks.SplitAddr(b[:n])
copy(b, b[len(addr):])
return n - len(addr), a, e
}

View File

@ -31,6 +31,7 @@ type Socks5Option struct {
UserName string `proxy:"username,omitempty"`
Password string `proxy:"password,omitempty"`
TLS bool `proxy:"tls,omitempty"`
UDP bool `proxy:"udp,omitempty"`
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
}
@ -126,6 +127,7 @@ func NewSocks5(option Socks5Option) *Socks5 {
Base: &Base{
name: option.Name,
tp: C.Socks5,
udp: option.UDP,
},
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
user: option.UserName,

View File

@ -43,6 +43,14 @@ func (u *URLTest) Dial(metadata *C.Metadata) (net.Conn, error) {
return a, err
}
func (u *URLTest) DialUDP(metadata *C.Metadata) (net.PacketConn, net.Addr, error) {
return u.fast.DialUDP(metadata)
}
func (u *URLTest) SupportUDP() bool {
return u.fast.SupportUDP()
}
func (u *URLTest) MarshalJSON() ([]byte, error) {
var all []string
for _, proxy := range u.proxies {