mirror of
https://gitclone.com/github.com/MetaCubeX/Clash.Meta
synced 2025-02-22 21:23:14 +08:00
Feature: socks5 udp associate
This commit is contained in:
parent
49f8902961
commit
c92cda6980
@ -122,7 +122,7 @@ Proxy:
|
|||||||
# The types of cipher are consistent with go-shadowsocks2
|
# The types of cipher are consistent with go-shadowsocks2
|
||||||
# support AEAD_AES_128_GCM AEAD_AES_192_GCM AEAD_AES_256_GCM AEAD_CHACHA20_POLY1305 AES-128-CTR AES-192-CTR AES-256-CTR AES-128-CFB AES-192-CFB AES-256-CFB CHACHA20-IETF XCHACHA20
|
# support AEAD_AES_128_GCM AEAD_AES_192_GCM AEAD_AES_256_GCM AEAD_CHACHA20_POLY1305 AES-128-CTR AES-192-CTR AES-256-CTR AES-128-CFB AES-192-CFB AES-256-CFB CHACHA20-IETF XCHACHA20
|
||||||
# In addition to what go-shadowsocks2 supports, it also supports chacha20 rc4-md5 xchacha20-ietf-poly1305
|
# In addition to what go-shadowsocks2 supports, it also supports chacha20 rc4-md5 xchacha20-ietf-poly1305
|
||||||
- { name: "ss1", type: ss, server: server, port: 443, cipher: AEAD_CHACHA20_POLY1305, password: "password" }
|
- { name: "ss1", type: ss, server: server, port: 443, cipher: AEAD_CHACHA20_POLY1305, password: "password", udp: true }
|
||||||
|
|
||||||
# old obfs configuration remove after prerelease
|
# old obfs configuration remove after prerelease
|
||||||
- name: "ss2"
|
- name: "ss2"
|
||||||
@ -226,5 +226,5 @@ https://clash.gitbook.io/
|
|||||||
|
|
||||||
- [x] Complementing the necessary rule operators
|
- [x] Complementing the necessary rule operators
|
||||||
- [x] Redir proxy
|
- [x] Redir proxy
|
||||||
- [ ] UDP support
|
- [ ] UDP support (vmess, outbound socks5)
|
||||||
- [ ] Connection manager
|
- [ ] Connection manager
|
||||||
|
@ -10,26 +10,16 @@ import (
|
|||||||
|
|
||||||
// HTTPAdapter is a adapter for HTTP connection
|
// HTTPAdapter is a adapter for HTTP connection
|
||||||
type HTTPAdapter struct {
|
type HTTPAdapter struct {
|
||||||
|
net.Conn
|
||||||
metadata *C.Metadata
|
metadata *C.Metadata
|
||||||
conn net.Conn
|
|
||||||
R *http.Request
|
R *http.Request
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close HTTP connection
|
|
||||||
func (h *HTTPAdapter) Close() {
|
|
||||||
h.conn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Metadata return destination metadata
|
// Metadata return destination metadata
|
||||||
func (h *HTTPAdapter) Metadata() *C.Metadata {
|
func (h *HTTPAdapter) Metadata() *C.Metadata {
|
||||||
return h.metadata
|
return h.metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
// Conn return raw net.Conn of HTTP
|
|
||||||
func (h *HTTPAdapter) Conn() net.Conn {
|
|
||||||
return h.conn
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewHTTP is HTTPAdapter generator
|
// NewHTTP is HTTPAdapter generator
|
||||||
func NewHTTP(request *http.Request, conn net.Conn) *HTTPAdapter {
|
func NewHTTP(request *http.Request, conn net.Conn) *HTTPAdapter {
|
||||||
metadata := parseHTTPAddr(request)
|
metadata := parseHTTPAddr(request)
|
||||||
@ -37,7 +27,7 @@ func NewHTTP(request *http.Request, conn net.Conn) *HTTPAdapter {
|
|||||||
return &HTTPAdapter{
|
return &HTTPAdapter{
|
||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
R: request,
|
R: request,
|
||||||
conn: conn,
|
Conn: conn,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,6 @@ func NewHTTPS(request *http.Request, conn net.Conn) *SocketAdapter {
|
|||||||
metadata.SourceIP = parseSourceIP(conn)
|
metadata.SourceIP = parseSourceIP(conn)
|
||||||
return &SocketAdapter{
|
return &SocketAdapter{
|
||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
conn: conn,
|
Conn: conn,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,33 +9,24 @@ import (
|
|||||||
|
|
||||||
// SocketAdapter is a adapter for socks and redir connection
|
// SocketAdapter is a adapter for socks and redir connection
|
||||||
type SocketAdapter struct {
|
type SocketAdapter struct {
|
||||||
conn net.Conn
|
net.Conn
|
||||||
metadata *C.Metadata
|
metadata *C.Metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close socks and redir connection
|
|
||||||
func (s *SocketAdapter) Close() {
|
|
||||||
s.conn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Metadata return destination metadata
|
// Metadata return destination metadata
|
||||||
func (s *SocketAdapter) Metadata() *C.Metadata {
|
func (s *SocketAdapter) Metadata() *C.Metadata {
|
||||||
return s.metadata
|
return s.metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
// Conn return raw net.Conn
|
|
||||||
func (s *SocketAdapter) Conn() net.Conn {
|
|
||||||
return s.conn
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSocket is SocketAdapter generator
|
// 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 := parseSocksAddr(target)
|
||||||
|
metadata.NetWork = netType
|
||||||
metadata.Source = source
|
metadata.Source = source
|
||||||
metadata.SourceIP = parseSourceIP(conn)
|
metadata.SourceIP = parseSourceIP(conn)
|
||||||
|
|
||||||
return &SocketAdapter{
|
return &SocketAdapter{
|
||||||
conn: conn,
|
Conn: conn,
|
||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
|
|
||||||
func parseSocksAddr(target socks.Addr) *C.Metadata {
|
func parseSocksAddr(target socks.Addr) *C.Metadata {
|
||||||
metadata := &C.Metadata{
|
metadata := &C.Metadata{
|
||||||
NetWork: C.TCP,
|
|
||||||
AddrType: int(target[0]),
|
AddrType: int(target[0]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package adapters
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
@ -13,6 +14,7 @@ import (
|
|||||||
type Base struct {
|
type Base struct {
|
||||||
name string
|
name string
|
||||||
tp C.AdapterType
|
tp C.AdapterType
|
||||||
|
udp bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Base) Name() string {
|
func (b *Base) Name() string {
|
||||||
@ -23,6 +25,14 @@ func (b *Base) Type() C.AdapterType {
|
|||||||
return b.tp
|
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) Destroy() {}
|
||||||
|
|
||||||
func (b *Base) MarshalJSON() ([]byte, error) {
|
func (b *Base) MarshalJSON() ([]byte, error) {
|
||||||
|
@ -24,11 +24,22 @@ func (d *Direct) Dial(metadata *C.Metadata) (net.Conn, error) {
|
|||||||
return c, nil
|
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 {
|
func NewDirect() *Direct {
|
||||||
return &Direct{
|
return &Direct{
|
||||||
Base: &Base{
|
Base: &Base{
|
||||||
name: "DIRECT",
|
name: "DIRECT",
|
||||||
tp: C.Direct,
|
tp: C.Direct,
|
||||||
|
udp: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,16 @@ func (f *Fallback) Dial(metadata *C.Metadata) (net.Conn, error) {
|
|||||||
return proxy.Dial(metadata)
|
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) {
|
func (f *Fallback) MarshalJSON() ([]byte, error) {
|
||||||
var all []string
|
var all []string
|
||||||
for _, proxy := range f.proxies {
|
for _, proxy := range f.proxies {
|
||||||
|
@ -67,6 +67,24 @@ func (lb *LoadBalance) Dial(metadata *C.Metadata) (net.Conn, error) {
|
|||||||
return lb.proxies[0].Dial(metadata)
|
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() {
|
func (lb *LoadBalance) Destroy() {
|
||||||
lb.done <- struct{}{}
|
lb.done <- struct{}{}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,14 @@ func (s *Selector) Dial(metadata *C.Metadata) (net.Conn, error) {
|
|||||||
return s.selected.Dial(metadata)
|
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) {
|
func (s *Selector) MarshalJSON() ([]byte, error) {
|
||||||
var all []string
|
var all []string
|
||||||
for k := range s.proxies {
|
for k := range s.proxies {
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/pool"
|
||||||
"github.com/Dreamacro/clash/common/structure"
|
"github.com/Dreamacro/clash/common/structure"
|
||||||
obfs "github.com/Dreamacro/clash/component/simple-obfs"
|
obfs "github.com/Dreamacro/clash/component/simple-obfs"
|
||||||
v2rayObfs "github.com/Dreamacro/clash/component/v2ray-plugin"
|
v2rayObfs "github.com/Dreamacro/clash/component/v2ray-plugin"
|
||||||
@ -34,6 +35,7 @@ type ShadowSocksOption struct {
|
|||||||
Port int `proxy:"port"`
|
Port int `proxy:"port"`
|
||||||
Password string `proxy:"password"`
|
Password string `proxy:"password"`
|
||||||
Cipher string `proxy:"cipher"`
|
Cipher string `proxy:"cipher"`
|
||||||
|
UDP bool `proxy:"udp,omitempty"`
|
||||||
Plugin string `proxy:"plugin,omitempty"`
|
Plugin string `proxy:"plugin,omitempty"`
|
||||||
PluginOpts map[string]interface{} `proxy:"plugin-opts,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
|
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) {
|
func (ss *ShadowSocks) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(map[string]string{
|
return json.Marshal(map[string]string{
|
||||||
"type": ss.Type().String(),
|
"type": ss.Type().String(),
|
||||||
@ -144,6 +159,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
|
|||||||
Base: &Base{
|
Base: &Base{
|
||||||
name: option.Name,
|
name: option.Name,
|
||||||
tp: C.Shadowsocks,
|
tp: C.Shadowsocks,
|
||||||
|
udp: option.UDP,
|
||||||
},
|
},
|
||||||
server: server,
|
server: server,
|
||||||
cipher: ciph,
|
cipher: ciph,
|
||||||
@ -173,3 +189,24 @@ func serializesSocksAddr(metadata *C.Metadata) []byte {
|
|||||||
}
|
}
|
||||||
return bytes.Join(buf, nil)
|
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
|
||||||
|
}
|
||||||
|
@ -31,6 +31,7 @@ type Socks5Option struct {
|
|||||||
UserName string `proxy:"username,omitempty"`
|
UserName string `proxy:"username,omitempty"`
|
||||||
Password string `proxy:"password,omitempty"`
|
Password string `proxy:"password,omitempty"`
|
||||||
TLS bool `proxy:"tls,omitempty"`
|
TLS bool `proxy:"tls,omitempty"`
|
||||||
|
UDP bool `proxy:"udp,omitempty"`
|
||||||
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
|
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,6 +127,7 @@ func NewSocks5(option Socks5Option) *Socks5 {
|
|||||||
Base: &Base{
|
Base: &Base{
|
||||||
name: option.Name,
|
name: option.Name,
|
||||||
tp: C.Socks5,
|
tp: C.Socks5,
|
||||||
|
udp: option.UDP,
|
||||||
},
|
},
|
||||||
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
|
addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
|
||||||
user: option.UserName,
|
user: option.UserName,
|
||||||
|
@ -43,6 +43,14 @@ func (u *URLTest) Dial(metadata *C.Metadata) (net.Conn, error) {
|
|||||||
return a, err
|
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) {
|
func (u *URLTest) MarshalJSON() ([]byte, error) {
|
||||||
var all []string
|
var all []string
|
||||||
for _, proxy := range u.proxies {
|
for _, proxy := range u.proxies {
|
||||||
|
15
common/pool/pool.go
Normal file
15
common/pool/pool.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package pool
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// io.Copy default buffer size is 32 KiB
|
||||||
|
// but the maximum packet size of vmess/shadowsocks is about 16 KiB
|
||||||
|
// so define a buffer of 20 KiB to reduce the memory of each TCP relay
|
||||||
|
bufferSize = 20 * 1024
|
||||||
|
)
|
||||||
|
|
||||||
|
// BufPool provide buffer for relay
|
||||||
|
var BufPool = sync.Pool{New: func() interface{} { return make([]byte, bufferSize) }}
|
@ -8,6 +8,8 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/pool"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HTTPObfs is shadowsocks http simple-obfs implementation
|
// HTTPObfs is shadowsocks http simple-obfs implementation
|
||||||
@ -32,15 +34,15 @@ func (ho *HTTPObfs) Read(b []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ho.firstResponse {
|
if ho.firstResponse {
|
||||||
buf := bufPool.Get().([]byte)
|
buf := pool.BufPool.Get().([]byte)
|
||||||
n, err := ho.Conn.Read(buf)
|
n, err := ho.Conn.Read(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bufPool.Put(buf[:cap(buf)])
|
pool.BufPool.Put(buf[:cap(buf)])
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
idx := bytes.Index(buf[:n], []byte("\r\n\r\n"))
|
idx := bytes.Index(buf[:n], []byte("\r\n\r\n"))
|
||||||
if idx == -1 {
|
if idx == -1 {
|
||||||
bufPool.Put(buf[:cap(buf)])
|
pool.BufPool.Put(buf[:cap(buf)])
|
||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
}
|
}
|
||||||
ho.firstResponse = false
|
ho.firstResponse = false
|
||||||
@ -50,7 +52,7 @@ func (ho *HTTPObfs) Read(b []byte) (int, error) {
|
|||||||
ho.buf = buf[:idx+4+length]
|
ho.buf = buf[:idx+4+length]
|
||||||
ho.offset = idx + 4 + n
|
ho.offset = idx + 4 + n
|
||||||
} else {
|
} else {
|
||||||
bufPool.Put(buf[:cap(buf)])
|
pool.BufPool.Put(buf[:cap(buf)])
|
||||||
}
|
}
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,9 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/pool"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -18,8 +19,6 @@ const (
|
|||||||
chunkSize = 1 << 14 // 2 ** 14 == 16 * 1024
|
chunkSize = 1 << 14 // 2 ** 14 == 16 * 1024
|
||||||
)
|
)
|
||||||
|
|
||||||
var bufPool = sync.Pool{New: func() interface{} { return make([]byte, 2048) }}
|
|
||||||
|
|
||||||
// TLSObfs is shadowsocks tls simple-obfs implementation
|
// TLSObfs is shadowsocks tls simple-obfs implementation
|
||||||
type TLSObfs struct {
|
type TLSObfs struct {
|
||||||
net.Conn
|
net.Conn
|
||||||
@ -30,12 +29,12 @@ type TLSObfs struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (to *TLSObfs) read(b []byte, discardN int) (int, error) {
|
func (to *TLSObfs) read(b []byte, discardN int) (int, error) {
|
||||||
buf := bufPool.Get().([]byte)
|
buf := pool.BufPool.Get().([]byte)
|
||||||
_, err := io.ReadFull(to.Conn, buf[:discardN])
|
_, err := io.ReadFull(to.Conn, buf[:discardN])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
bufPool.Put(buf[:cap(buf)])
|
pool.BufPool.Put(buf[:cap(buf)])
|
||||||
|
|
||||||
sizeBuf := make([]byte, 2)
|
sizeBuf := make([]byte, 2)
|
||||||
_, err = io.ReadFull(to.Conn, sizeBuf)
|
_, err = io.ReadFull(to.Conn, sizeBuf)
|
||||||
@ -103,7 +102,7 @@ func (to *TLSObfs) write(b []byte) (int, error) {
|
|||||||
return len(b), err
|
return len(b), err
|
||||||
}
|
}
|
||||||
|
|
||||||
size := bufPool.Get().([]byte)
|
size := pool.BufPool.Get().([]byte)
|
||||||
binary.BigEndian.PutUint16(size[:2], uint16(len(b)))
|
binary.BigEndian.PutUint16(size[:2], uint16(len(b)))
|
||||||
|
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
@ -111,7 +110,7 @@ func (to *TLSObfs) write(b []byte) (int, error) {
|
|||||||
buf.Write(size[:2])
|
buf.Write(size[:2])
|
||||||
buf.Write(b)
|
buf.Write(b)
|
||||||
_, err := to.Conn.Write(buf.Bytes())
|
_, err := to.Conn.Write(buf.Bytes())
|
||||||
bufPool.Put(size[:cap(size)])
|
pool.BufPool.Put(size[:cap(size)])
|
||||||
return len(b), err
|
return len(b), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/pool"
|
||||||
)
|
)
|
||||||
|
|
||||||
type aeadWriter struct {
|
type aeadWriter struct {
|
||||||
@ -20,8 +22,8 @@ func newAEADWriter(w io.Writer, aead cipher.AEAD, iv []byte) *aeadWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *aeadWriter) Write(b []byte) (n int, err error) {
|
func (w *aeadWriter) Write(b []byte) (n int, err error) {
|
||||||
buf := bufPool.Get().([]byte)
|
buf := pool.BufPool.Get().([]byte)
|
||||||
defer bufPool.Put(buf[:cap(buf)])
|
defer pool.BufPool.Put(buf[:cap(buf)])
|
||||||
length := len(b)
|
length := len(b)
|
||||||
for {
|
for {
|
||||||
if length == 0 {
|
if length == 0 {
|
||||||
@ -71,7 +73,7 @@ func (r *aeadReader) Read(b []byte) (int, error) {
|
|||||||
n := copy(b, r.buf[r.offset:])
|
n := copy(b, r.buf[r.offset:])
|
||||||
r.offset += n
|
r.offset += n
|
||||||
if r.offset == len(r.buf) {
|
if r.offset == len(r.buf) {
|
||||||
bufPool.Put(r.buf[:cap(r.buf)])
|
pool.BufPool.Put(r.buf[:cap(r.buf)])
|
||||||
r.buf = nil
|
r.buf = nil
|
||||||
}
|
}
|
||||||
return n, nil
|
return n, nil
|
||||||
@ -87,10 +89,10 @@ func (r *aeadReader) Read(b []byte) (int, error) {
|
|||||||
return 0, errors.New("Buffer is larger than standard")
|
return 0, errors.New("Buffer is larger than standard")
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := bufPool.Get().([]byte)
|
buf := pool.BufPool.Get().([]byte)
|
||||||
_, err = io.ReadFull(r.Reader, buf[:size])
|
_, err = io.ReadFull(r.Reader, buf[:size])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bufPool.Put(buf[:cap(buf)])
|
pool.BufPool.Put(buf[:cap(buf)])
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +107,7 @@ func (r *aeadReader) Read(b []byte) (int, error) {
|
|||||||
realLen := size - r.Overhead()
|
realLen := size - r.Overhead()
|
||||||
n := copy(b, buf[:realLen])
|
n := copy(b, buf[:realLen])
|
||||||
if len(b) >= realLen {
|
if len(b) >= realLen {
|
||||||
bufPool.Put(buf[:cap(buf)])
|
pool.BufPool.Put(buf[:cap(buf)])
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,8 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"sync"
|
|
||||||
|
"github.com/Dreamacro/clash/common/pool"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -13,8 +14,6 @@ const (
|
|||||||
maxSize = 17 * 1024 // 2 + chunkSize + aead.Overhead()
|
maxSize = 17 * 1024 // 2 + chunkSize + aead.Overhead()
|
||||||
)
|
)
|
||||||
|
|
||||||
var bufPool = sync.Pool{New: func() interface{} { return make([]byte, maxSize) }}
|
|
||||||
|
|
||||||
type chunkReader struct {
|
type chunkReader struct {
|
||||||
io.Reader
|
io.Reader
|
||||||
buf []byte
|
buf []byte
|
||||||
@ -35,7 +34,7 @@ func (cr *chunkReader) Read(b []byte) (int, error) {
|
|||||||
n := copy(b, cr.buf[cr.offset:])
|
n := copy(b, cr.buf[cr.offset:])
|
||||||
cr.offset += n
|
cr.offset += n
|
||||||
if cr.offset == len(cr.buf) {
|
if cr.offset == len(cr.buf) {
|
||||||
bufPool.Put(cr.buf[:cap(cr.buf)])
|
pool.BufPool.Put(cr.buf[:cap(cr.buf)])
|
||||||
cr.buf = nil
|
cr.buf = nil
|
||||||
}
|
}
|
||||||
return n, nil
|
return n, nil
|
||||||
@ -60,10 +59,10 @@ func (cr *chunkReader) Read(b []byte) (int, error) {
|
|||||||
return size, nil
|
return size, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := bufPool.Get().([]byte)
|
buf := pool.BufPool.Get().([]byte)
|
||||||
_, err = io.ReadFull(cr.Reader, buf[:size])
|
_, err = io.ReadFull(cr.Reader, buf[:size])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bufPool.Put(buf[:cap(buf)])
|
pool.BufPool.Put(buf[:cap(buf)])
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
n := copy(b, cr.buf[:])
|
n := copy(b, cr.buf[:])
|
||||||
@ -77,8 +76,8 @@ type chunkWriter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cw *chunkWriter) Write(b []byte) (n int, err error) {
|
func (cw *chunkWriter) Write(b []byte) (n int, err error) {
|
||||||
buf := bufPool.Get().([]byte)
|
buf := pool.BufPool.Get().([]byte)
|
||||||
defer bufPool.Put(buf[:cap(buf)])
|
defer pool.BufPool.Put(buf[:cap(buf)])
|
||||||
length := len(b)
|
length := len(b)
|
||||||
for {
|
for {
|
||||||
if length == 0 {
|
if length == 0 {
|
||||||
|
@ -20,14 +20,16 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ServerAdapter interface {
|
type ServerAdapter interface {
|
||||||
|
net.Conn
|
||||||
Metadata() *Metadata
|
Metadata() *Metadata
|
||||||
Close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProxyAdapter interface {
|
type ProxyAdapter interface {
|
||||||
Name() string
|
Name() string
|
||||||
Type() AdapterType
|
Type() AdapterType
|
||||||
Dial(metadata *Metadata) (net.Conn, error)
|
Dial(metadata *Metadata) (net.Conn, error)
|
||||||
|
DialUDP(metadata *Metadata) (net.PacketConn, net.Addr, error)
|
||||||
|
SupportUDP() bool
|
||||||
Destroy()
|
Destroy()
|
||||||
MarshalJSON() ([]byte, error)
|
MarshalJSON() ([]byte, error)
|
||||||
}
|
}
|
||||||
|
2
go.mod
2
go.mod
@ -1,7 +1,7 @@
|
|||||||
module github.com/Dreamacro/clash
|
module github.com/Dreamacro/clash
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Dreamacro/go-shadowsocks2 v0.1.3-0.20190202135136-da4602d8f112
|
github.com/Dreamacro/go-shadowsocks2 v0.1.3-0.20190406142755-9128a199439f
|
||||||
github.com/eapache/queue v1.1.0 // indirect
|
github.com/eapache/queue v1.1.0 // indirect
|
||||||
github.com/go-chi/chi v4.0.1+incompatible
|
github.com/go-chi/chi v4.0.1+incompatible
|
||||||
github.com/go-chi/cors v1.0.0
|
github.com/go-chi/cors v1.0.0
|
||||||
|
4
go.sum
4
go.sum
@ -1,5 +1,5 @@
|
|||||||
github.com/Dreamacro/go-shadowsocks2 v0.1.3-0.20190202135136-da4602d8f112 h1:1axYxE0ZLJy40+ulq46XQt7MaJDJr4iGer1NQz7jmKw=
|
github.com/Dreamacro/go-shadowsocks2 v0.1.3-0.20190406142755-9128a199439f h1:nlImrmI6I2AVjJ2AvE3w3f7fi8rhLQAhZO1Gs31+/nE=
|
||||||
github.com/Dreamacro/go-shadowsocks2 v0.1.3-0.20190202135136-da4602d8f112/go.mod h1:giIuN+TuUudTxHc1jjTOyyQYiJ3VXp1pWOHdJbSCAPo=
|
github.com/Dreamacro/go-shadowsocks2 v0.1.3-0.20190406142755-9128a199439f/go.mod h1:giIuN+TuUudTxHc1jjTOyyQYiJ3VXp1pWOHdJbSCAPo=
|
||||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
|
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
|
||||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
|
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
@ -59,5 +59,5 @@ func handleRedir(conn net.Conn) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
conn.(*net.TCPConn).SetKeepAlive(true)
|
conn.(*net.TCPConn).SetKeepAlive(true)
|
||||||
tun.Add(adapters.NewSocket(target, conn, C.REDIR))
|
tun.Add(adapters.NewSocket(target, conn, C.REDIR, C.TCP))
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package socks
|
package socks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapters/inbound"
|
adapters "github.com/Dreamacro/clash/adapters/inbound"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
"github.com/Dreamacro/clash/tunnel"
|
"github.com/Dreamacro/clash/tunnel"
|
||||||
@ -15,6 +17,41 @@ var (
|
|||||||
tun = tunnel.Instance()
|
tun = tunnel.Instance()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Error represents a SOCKS error
|
||||||
|
type Error byte
|
||||||
|
|
||||||
|
func (err Error) Error() string {
|
||||||
|
return "SOCKS error: " + strconv.Itoa(int(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SOCKS request commands as defined in RFC 1928 section 4.
|
||||||
|
const (
|
||||||
|
CmdConnect = 1
|
||||||
|
CmdBind = 2
|
||||||
|
CmdUDPAssociate = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// SOCKS address types as defined in RFC 1928 section 5.
|
||||||
|
const (
|
||||||
|
AtypIPv4 = 1
|
||||||
|
AtypDomainName = 3
|
||||||
|
AtypIPv6 = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
const MaxAddrLen = 1 + 1 + 255 + 2
|
||||||
|
|
||||||
|
// SOCKS errors as defined in RFC 1928 section 6.
|
||||||
|
const (
|
||||||
|
ErrGeneralFailure = Error(1)
|
||||||
|
ErrConnectionNotAllowed = Error(2)
|
||||||
|
ErrNetworkUnreachable = Error(3)
|
||||||
|
ErrHostUnreachable = Error(4)
|
||||||
|
ErrConnectionRefused = Error(5)
|
||||||
|
ErrTTLExpired = Error(6)
|
||||||
|
ErrCommandNotSupported = Error(7)
|
||||||
|
ErrAddressNotSupported = Error(8)
|
||||||
|
)
|
||||||
|
|
||||||
type SockListener struct {
|
type SockListener struct {
|
||||||
net.Listener
|
net.Listener
|
||||||
address string
|
address string
|
||||||
@ -55,11 +92,78 @@ func (l *SockListener) Address() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleSocks(conn net.Conn) {
|
func handleSocks(conn net.Conn) {
|
||||||
target, err := socks.Handshake(conn)
|
target, command, err := handshake(conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
conn.(*net.TCPConn).SetKeepAlive(true)
|
conn.(*net.TCPConn).SetKeepAlive(true)
|
||||||
tun.Add(adapters.NewSocket(target, conn, C.SOCKS))
|
if command == CmdUDPAssociate {
|
||||||
|
tun.Add(adapters.NewSocket(target, conn, C.SOCKS, C.UDP))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tun.Add(adapters.NewSocket(target, conn, C.SOCKS, C.TCP))
|
||||||
|
}
|
||||||
|
|
||||||
|
// handshake fast-tracks SOCKS initialization to get target address to connect.
|
||||||
|
func handshake(rw io.ReadWriter) (addr socks.Addr, command int, err error) {
|
||||||
|
// Read RFC 1928 for request and reply structure and sizes.
|
||||||
|
buf := make([]byte, MaxAddrLen)
|
||||||
|
// read VER, NMETHODS, METHODS
|
||||||
|
if _, err = io.ReadFull(rw, buf[:2]); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nmethods := buf[1]
|
||||||
|
if _, err = io.ReadFull(rw, buf[:nmethods]); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// write VER METHOD
|
||||||
|
if _, err = rw.Write([]byte{5, 0}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// read VER CMD RSV ATYP DST.ADDR DST.PORT
|
||||||
|
if _, err = io.ReadFull(rw, buf[:3]); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if buf[1] != CmdConnect && buf[1] != CmdUDPAssociate {
|
||||||
|
err = ErrCommandNotSupported
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
command = int(buf[1])
|
||||||
|
addr, err = readAddr(rw, buf)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// write VER REP RSV ATYP BND.ADDR BND.PORT
|
||||||
|
_, err = rw.Write([]byte{5, 0, 0, 1, 0, 0, 0, 0, 0, 0})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func readAddr(r io.Reader, b []byte) (socks.Addr, error) {
|
||||||
|
if len(b) < MaxAddrLen {
|
||||||
|
return nil, io.ErrShortBuffer
|
||||||
|
}
|
||||||
|
_, err := io.ReadFull(r, b[:1]) // read 1st byte for address type
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch b[0] {
|
||||||
|
case AtypDomainName:
|
||||||
|
_, err = io.ReadFull(r, b[1:2]) // read 2nd byte for domain length
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = io.ReadFull(r, b[2:2+b[1]+2])
|
||||||
|
return b[:1+1+b[1]+2], err
|
||||||
|
case AtypIPv4:
|
||||||
|
_, err = io.ReadFull(r, b[1:1+net.IPv4len+2])
|
||||||
|
return b[:1+net.IPv4len+2], err
|
||||||
|
case AtypIPv6:
|
||||||
|
_, err = io.ReadFull(r, b[1:1+net.IPv6len+2])
|
||||||
|
return b[:1+net.IPv6len+2], err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ErrAddressNotSupported
|
||||||
}
|
}
|
||||||
|
@ -6,21 +6,12 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
adapters "github.com/Dreamacro/clash/adapters/inbound"
|
adapters "github.com/Dreamacro/clash/adapters/inbound"
|
||||||
|
"github.com/Dreamacro/clash/common/pool"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
// io.Copy default buffer size is 32 KiB
|
|
||||||
// but the maximum packet size of vmess/shadowsocks is about 16 KiB
|
|
||||||
// so define a buffer of 20 KiB to reduce the memory of each TCP relay
|
|
||||||
bufferSize = 20 * 1024
|
|
||||||
)
|
|
||||||
|
|
||||||
var bufPool = sync.Pool{New: func() interface{} { return make([]byte, bufferSize) }}
|
|
||||||
|
|
||||||
func (t *Tunnel) handleHTTP(request *adapters.HTTPAdapter, outbound net.Conn) {
|
func (t *Tunnel) handleHTTP(request *adapters.HTTPAdapter, outbound net.Conn) {
|
||||||
conn := newTrafficTrack(outbound, t.traffic)
|
conn := newTrafficTrack(outbound, t.traffic)
|
||||||
req := request.R
|
req := request.R
|
||||||
@ -50,7 +41,7 @@ func (t *Tunnel) handleHTTP(request *adapters.HTTPAdapter, outbound net.Conn) {
|
|||||||
} else {
|
} else {
|
||||||
resp.Close = true
|
resp.Close = true
|
||||||
}
|
}
|
||||||
err = resp.Write(request.Conn())
|
err = resp.Write(request)
|
||||||
if err != nil || resp.Close {
|
if err != nil || resp.Close {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -59,7 +50,7 @@ func (t *Tunnel) handleHTTP(request *adapters.HTTPAdapter, outbound net.Conn) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err = http.ReadRequest(bufio.NewReader(request.Conn()))
|
req, err = http.ReadRequest(bufio.NewReader(request))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -72,9 +63,52 @@ func (t *Tunnel) handleHTTP(request *adapters.HTTPAdapter, outbound net.Conn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tunnel) handleSOCKS(request *adapters.SocketAdapter, outbound net.Conn) {
|
func (t *Tunnel) handleSocket(request *adapters.SocketAdapter, outbound net.Conn) {
|
||||||
conn := newTrafficTrack(outbound, t.traffic)
|
conn := newTrafficTrack(outbound, t.traffic)
|
||||||
relay(request.Conn(), conn)
|
relay(request, conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Tunnel) handleUDPOverTCP(conn net.Conn, pc net.PacketConn, addr net.Addr) error {
|
||||||
|
ch := make(chan error, 1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
buf := pool.BufPool.Get().([]byte)
|
||||||
|
defer pool.BufPool.Put(buf)
|
||||||
|
for {
|
||||||
|
n, err := conn.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
ch <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pc.SetReadDeadline(time.Now().Add(120 * time.Second))
|
||||||
|
if _, err = pc.WriteTo(buf[:n], addr); err != nil {
|
||||||
|
ch <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.traffic.Up() <- int64(n)
|
||||||
|
ch <- nil
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
buf := pool.BufPool.Get().([]byte)
|
||||||
|
defer pool.BufPool.Put(buf)
|
||||||
|
|
||||||
|
for {
|
||||||
|
pc.SetReadDeadline(time.Now().Add(120 * time.Second))
|
||||||
|
n, _, err := pc.ReadFrom(buf)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := conn.Write(buf[:n]); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
t.traffic.Down() <- int64(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
<-ch
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// relay copies between left and right bidirectionally.
|
// relay copies between left and right bidirectionally.
|
||||||
@ -82,16 +116,16 @@ func relay(leftConn, rightConn net.Conn) {
|
|||||||
ch := make(chan error)
|
ch := make(chan error)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
buf := bufPool.Get().([]byte)
|
buf := pool.BufPool.Get().([]byte)
|
||||||
_, err := io.CopyBuffer(leftConn, rightConn, buf)
|
_, err := io.CopyBuffer(leftConn, rightConn, buf)
|
||||||
bufPool.Put(buf[:cap(buf)])
|
pool.BufPool.Put(buf[:cap(buf)])
|
||||||
leftConn.SetReadDeadline(time.Now())
|
leftConn.SetReadDeadline(time.Now())
|
||||||
ch <- err
|
ch <- err
|
||||||
}()
|
}()
|
||||||
|
|
||||||
buf := bufPool.Get().([]byte)
|
buf := pool.BufPool.Get().([]byte)
|
||||||
io.CopyBuffer(rightConn, leftConn, buf)
|
io.CopyBuffer(rightConn, leftConn, buf)
|
||||||
bufPool.Put(buf[:cap(buf)])
|
pool.BufPool.Put(buf[:cap(buf)])
|
||||||
rightConn.SetReadDeadline(time.Now())
|
rightConn.SetReadDeadline(time.Now())
|
||||||
<-ch
|
<-ch
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ var (
|
|||||||
once sync.Once
|
once sync.Once
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tunnel handle proxy socket and HTTP/SOCKS socket
|
// Tunnel handle relay inbound proxy and outbound proxy
|
||||||
type Tunnel struct {
|
type Tunnel struct {
|
||||||
queue *channels.InfiniteChannel
|
queue *channels.InfiniteChannel
|
||||||
rules []C.Rule
|
rules []C.Rule
|
||||||
@ -143,6 +143,12 @@ func (t *Tunnel) handleConn(localConn C.ServerAdapter) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if metadata.NetWork == C.UDP {
|
||||||
|
pc, addr, _ := proxy.DialUDP(metadata)
|
||||||
|
t.handleUDPOverTCP(localConn, pc, addr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
remoConn, err := proxy.Dial(metadata)
|
remoConn, err := proxy.Dial(metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnln("Proxy[%s] connect [%s --> %s] error: %s", proxy.Name(), metadata.SourceIP.String(), metadata.String(), err.Error())
|
log.Warnln("Proxy[%s] connect [%s --> %s] error: %s", proxy.Name(), metadata.SourceIP.String(), metadata.String(), err.Error())
|
||||||
@ -154,7 +160,7 @@ func (t *Tunnel) handleConn(localConn C.ServerAdapter) {
|
|||||||
case *InboundAdapter.HTTPAdapter:
|
case *InboundAdapter.HTTPAdapter:
|
||||||
t.handleHTTP(adapter, remoConn)
|
t.handleHTTP(adapter, remoConn)
|
||||||
case *InboundAdapter.SocketAdapter:
|
case *InboundAdapter.SocketAdapter:
|
||||||
t.handleSOCKS(adapter, remoConn)
|
t.handleSocket(adapter, remoConn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,10 +183,17 @@ func (t *Tunnel) match(metadata *C.Metadata) (C.Proxy, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if rule.IsMatch(metadata) {
|
if rule.IsMatch(metadata) {
|
||||||
if a, ok := t.proxies[rule.Adapter()]; ok {
|
adapter, ok := t.proxies[rule.Adapter()]
|
||||||
log.Infoln("%s --> %v match %s using %s", metadata.SourceIP.String(), metadata.String(), rule.RuleType().String(), rule.Adapter())
|
if !ok {
|
||||||
return a, nil
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if metadata.NetWork == C.UDP && !adapter.SupportUDP() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infoln("%s --> %v match %s using %s", metadata.SourceIP.String(), metadata.String(), rule.RuleType().String(), rule.Adapter())
|
||||||
|
return adapter, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Infoln("%s --> %v doesn't match any rule using DIRECT", metadata.SourceIP.String(), metadata.String())
|
log.Infoln("%s --> %v doesn't match any rule using DIRECT", metadata.SourceIP.String(), metadata.String())
|
||||||
|
Loading…
Reference in New Issue
Block a user