Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
dff1e8f1ce | |||
995aa7a8fc | |||
3ca5d17c40 | |||
244cb370a4 | |||
c35cb24bda | |||
b6ff08074c | |||
70d53fd45a | |||
f231a63e93 | |||
6091fcdfec | |||
bcfc15e398 | |||
045edc188c | |||
0778591524 | |||
d5e52bed43 | |||
06fdd3abe0 | |||
4e5898197a | |||
f96ebab99f |
@ -40,6 +40,9 @@ Documentations are now moved to [GitHub Wiki](https://github.com/Dreamacro/clash
|
||||
## Premium Release
|
||||
[Release](https://github.com/Dreamacro/clash/releases/tag/premium)
|
||||
|
||||
## Development
|
||||
If you want to build an application that uses clash as a library, check out the the [GitHub Wiki](https://github.com/Dreamacro/clash/wiki/use-clash-as-a-library)
|
||||
|
||||
## Credits
|
||||
|
||||
* [riobard/go-shadowsocks2](https://github.com/riobard/go-shadowsocks2)
|
||||
|
@ -1,11 +1,12 @@
|
||||
package outbound
|
||||
package adapter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/common/queue"
|
||||
@ -14,97 +15,6 @@ import (
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
type Base struct {
|
||||
name string
|
||||
addr string
|
||||
tp C.AdapterType
|
||||
udp bool
|
||||
}
|
||||
|
||||
// Name implements C.ProxyAdapter
|
||||
func (b *Base) Name() string {
|
||||
return b.name
|
||||
}
|
||||
|
||||
// Type implements C.ProxyAdapter
|
||||
func (b *Base) Type() C.AdapterType {
|
||||
return b.tp
|
||||
}
|
||||
|
||||
// StreamConn implements C.ProxyAdapter
|
||||
func (b *Base) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
||||
return c, errors.New("no support")
|
||||
}
|
||||
|
||||
// DialUDP implements C.ProxyAdapter
|
||||
func (b *Base) DialUDP(metadata *C.Metadata) (C.PacketConn, error) {
|
||||
return nil, errors.New("no support")
|
||||
}
|
||||
|
||||
// SupportUDP implements C.ProxyAdapter
|
||||
func (b *Base) SupportUDP() bool {
|
||||
return b.udp
|
||||
}
|
||||
|
||||
// MarshalJSON implements C.ProxyAdapter
|
||||
func (b *Base) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(map[string]string{
|
||||
"type": b.Type().String(),
|
||||
})
|
||||
}
|
||||
|
||||
// Addr implements C.ProxyAdapter
|
||||
func (b *Base) Addr() string {
|
||||
return b.addr
|
||||
}
|
||||
|
||||
// Unwrap implements C.ProxyAdapter
|
||||
func (b *Base) Unwrap(metadata *C.Metadata) C.Proxy {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewBase(name string, addr string, tp C.AdapterType, udp bool) *Base {
|
||||
return &Base{name, addr, tp, udp}
|
||||
}
|
||||
|
||||
type conn struct {
|
||||
net.Conn
|
||||
chain C.Chain
|
||||
}
|
||||
|
||||
// Chains implements C.Connection
|
||||
func (c *conn) Chains() C.Chain {
|
||||
return c.chain
|
||||
}
|
||||
|
||||
// AppendToChains implements C.Connection
|
||||
func (c *conn) AppendToChains(a C.ProxyAdapter) {
|
||||
c.chain = append(c.chain, a.Name())
|
||||
}
|
||||
|
||||
func NewConn(c net.Conn, a C.ProxyAdapter) C.Conn {
|
||||
return &conn{c, []string{a.Name()}}
|
||||
}
|
||||
|
||||
type packetConn struct {
|
||||
net.PacketConn
|
||||
chain C.Chain
|
||||
}
|
||||
|
||||
// Chains implements C.Connection
|
||||
func (c *packetConn) Chains() C.Chain {
|
||||
return c.chain
|
||||
}
|
||||
|
||||
// AppendToChains implements C.Connection
|
||||
func (c *packetConn) AppendToChains(a C.ProxyAdapter) {
|
||||
c.chain = append(c.chain, a.Name())
|
||||
}
|
||||
|
||||
func newPacketConn(pc net.PacketConn, a C.ProxyAdapter) C.PacketConn {
|
||||
return &packetConn{pc, []string{a.Name()}}
|
||||
}
|
||||
|
||||
type Proxy struct {
|
||||
C.ProxyAdapter
|
||||
history *queue.Queue
|
||||
@ -118,7 +28,7 @@ func (p *Proxy) Alive() bool {
|
||||
|
||||
// Dial implements C.Proxy
|
||||
func (p *Proxy) Dial(metadata *C.Metadata) (C.Conn, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), tcpTimeout)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTCPTimeout)
|
||||
defer cancel()
|
||||
return p.DialContext(ctx, metadata)
|
||||
}
|
||||
@ -172,6 +82,7 @@ func (p *Proxy) MarshalJSON() ([]byte, error) {
|
||||
json.Unmarshal(inner, &mapping)
|
||||
mapping["history"] = p.DelayHistory()
|
||||
mapping["name"] = p.Name()
|
||||
mapping["udp"] = p.SupportUDP()
|
||||
return json.Marshal(mapping)
|
||||
}
|
||||
|
||||
@ -237,3 +148,31 @@ func (p *Proxy) URLTest(ctx context.Context, url string) (t uint16, err error) {
|
||||
func NewProxy(adapter C.ProxyAdapter) *Proxy {
|
||||
return &Proxy{adapter, queue.New(10), atomic.NewBool(true)}
|
||||
}
|
||||
|
||||
func urlToMetadata(rawURL string) (addr C.Metadata, err error) {
|
||||
u, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
port := u.Port()
|
||||
if port == "" {
|
||||
switch u.Scheme {
|
||||
case "https":
|
||||
port = "443"
|
||||
case "http":
|
||||
port = "80"
|
||||
default:
|
||||
err = fmt.Errorf("%s scheme not Support", rawURL)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
addr = C.Metadata{
|
||||
AddrType: C.AtypDomainName,
|
||||
Host: u.Hostname(),
|
||||
DstIP: nil,
|
||||
DstPort: port,
|
||||
}
|
||||
return
|
||||
}
|
21
adapter/inbound/http.go
Normal file
21
adapter/inbound/http.go
Normal file
@ -0,0 +1,21 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/context"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
// NewHTTP receive normal http request and return HTTPContext
|
||||
func NewHTTP(target string, source net.Addr, conn net.Conn) *context.ConnContext {
|
||||
metadata := parseSocksAddr(socks5.ParseAddr(target))
|
||||
metadata.NetWork = C.TCP
|
||||
metadata.Type = C.HTTP
|
||||
if ip, port, err := parseAddr(source.String()); err == nil {
|
||||
metadata.SrcIP = ip
|
||||
metadata.SrcPort = port
|
||||
}
|
||||
return context.NewConnContext(conn, metadata)
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"github.com/Dreamacro/clash/component/socks5"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
// PacketAdapter is a UDP Packet adapter for socks/redir/tun
|
@ -3,9 +3,9 @@ package inbound
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/Dreamacro/clash/component/socks5"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/context"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
// NewSocket receive TCP inbound and return ConnContext
|
@ -6,8 +6,8 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Dreamacro/clash/component/socks5"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
func parseSocksAddr(target socks5.Addr) *C.Metadata {
|
100
adapter/outbound/base.go
Normal file
100
adapter/outbound/base.go
Normal file
@ -0,0 +1,100 @@
|
||||
package outbound
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
)
|
||||
|
||||
type Base struct {
|
||||
name string
|
||||
addr string
|
||||
tp C.AdapterType
|
||||
udp bool
|
||||
}
|
||||
|
||||
// Name implements C.ProxyAdapter
|
||||
func (b *Base) Name() string {
|
||||
return b.name
|
||||
}
|
||||
|
||||
// Type implements C.ProxyAdapter
|
||||
func (b *Base) Type() C.AdapterType {
|
||||
return b.tp
|
||||
}
|
||||
|
||||
// StreamConn implements C.ProxyAdapter
|
||||
func (b *Base) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
||||
return c, errors.New("no support")
|
||||
}
|
||||
|
||||
// DialUDP implements C.ProxyAdapter
|
||||
func (b *Base) DialUDP(metadata *C.Metadata) (C.PacketConn, error) {
|
||||
return nil, errors.New("no support")
|
||||
}
|
||||
|
||||
// SupportUDP implements C.ProxyAdapter
|
||||
func (b *Base) SupportUDP() bool {
|
||||
return b.udp
|
||||
}
|
||||
|
||||
// MarshalJSON implements C.ProxyAdapter
|
||||
func (b *Base) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(map[string]string{
|
||||
"type": b.Type().String(),
|
||||
})
|
||||
}
|
||||
|
||||
// Addr implements C.ProxyAdapter
|
||||
func (b *Base) Addr() string {
|
||||
return b.addr
|
||||
}
|
||||
|
||||
// Unwrap implements C.ProxyAdapter
|
||||
func (b *Base) Unwrap(metadata *C.Metadata) C.Proxy {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewBase(name string, addr string, tp C.AdapterType, udp bool) *Base {
|
||||
return &Base{name, addr, tp, udp}
|
||||
}
|
||||
|
||||
type conn struct {
|
||||
net.Conn
|
||||
chain C.Chain
|
||||
}
|
||||
|
||||
// Chains implements C.Connection
|
||||
func (c *conn) Chains() C.Chain {
|
||||
return c.chain
|
||||
}
|
||||
|
||||
// AppendToChains implements C.Connection
|
||||
func (c *conn) AppendToChains(a C.ProxyAdapter) {
|
||||
c.chain = append(c.chain, a.Name())
|
||||
}
|
||||
|
||||
func NewConn(c net.Conn, a C.ProxyAdapter) C.Conn {
|
||||
return &conn{c, []string{a.Name()}}
|
||||
}
|
||||
|
||||
type packetConn struct {
|
||||
net.PacketConn
|
||||
chain C.Chain
|
||||
}
|
||||
|
||||
// Chains implements C.Connection
|
||||
func (c *packetConn) Chains() C.Chain {
|
||||
return c.chain
|
||||
}
|
||||
|
||||
// AppendToChains implements C.Connection
|
||||
func (c *packetConn) AppendToChains(a C.ProxyAdapter) {
|
||||
c.chain = append(c.chain, a.Name())
|
||||
}
|
||||
|
||||
func newPacketConn(pc net.PacketConn, a C.ProxyAdapter) C.PacketConn {
|
||||
return &packetConn{pc, []string{a.Name()}}
|
||||
}
|
@ -125,7 +125,6 @@ func NewHttp(option HttpOption) *Http {
|
||||
}
|
||||
tlsConfig = &tls.Config{
|
||||
InsecureSkipVerify: option.SkipCertVerify,
|
||||
ClientSessionCache: getClientSessionCache(),
|
||||
ServerName: sni,
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@ package outbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
@ -10,10 +9,10 @@ import (
|
||||
|
||||
"github.com/Dreamacro/clash/common/structure"
|
||||
"github.com/Dreamacro/clash/component/dialer"
|
||||
obfs "github.com/Dreamacro/clash/component/simple-obfs"
|
||||
"github.com/Dreamacro/clash/component/socks5"
|
||||
v2rayObfs "github.com/Dreamacro/clash/component/v2ray-plugin"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
obfs "github.com/Dreamacro/clash/transport/simple-obfs"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
v2rayObfs "github.com/Dreamacro/clash/transport/v2ray-plugin"
|
||||
|
||||
"github.com/Dreamacro/go-shadowsocks2/core"
|
||||
)
|
||||
@ -105,13 +104,6 @@ func (ss *ShadowSocks) DialUDP(metadata *C.Metadata) (C.PacketConn, error) {
|
||||
return newPacketConn(&ssPacketConn{PacketConn: pc, rAddr: addr}, ss), nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements C.ProxyAdapter
|
||||
func (ss *ShadowSocks) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(map[string]string{
|
||||
"type": ss.Type().String(),
|
||||
})
|
||||
}
|
||||
|
||||
func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
|
||||
addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
|
||||
cipher := option.Cipher
|
||||
@ -157,7 +149,6 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
|
||||
if opts.TLS {
|
||||
v2rayOption.TLS = true
|
||||
v2rayOption.SkipCertVerify = opts.SkipCertVerify
|
||||
v2rayOption.SessionCache = getClientSessionCache()
|
||||
}
|
||||
}
|
||||
|
@ -2,15 +2,14 @@ package outbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/Dreamacro/clash/component/dialer"
|
||||
"github.com/Dreamacro/clash/component/ssr/obfs"
|
||||
"github.com/Dreamacro/clash/component/ssr/protocol"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/transport/ssr/obfs"
|
||||
"github.com/Dreamacro/clash/transport/ssr/protocol"
|
||||
|
||||
"github.com/Dreamacro/go-shadowsocks2/core"
|
||||
"github.com/Dreamacro/go-shadowsocks2/shadowaead"
|
||||
@ -91,13 +90,6 @@ func (ssr *ShadowSocksR) DialUDP(metadata *C.Metadata) (C.PacketConn, error) {
|
||||
return newPacketConn(&ssPacketConn{PacketConn: pc, rAddr: addr}, ssr), nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements C.ProxyAdapter
|
||||
func (ssr *ShadowSocksR) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(map[string]string{
|
||||
"type": ssr.Type().String(),
|
||||
})
|
||||
}
|
||||
|
||||
func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) {
|
||||
addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
|
||||
cipher := option.Cipher
|
@ -8,9 +8,9 @@ import (
|
||||
|
||||
"github.com/Dreamacro/clash/common/structure"
|
||||
"github.com/Dreamacro/clash/component/dialer"
|
||||
obfs "github.com/Dreamacro/clash/component/simple-obfs"
|
||||
"github.com/Dreamacro/clash/component/snell"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
obfs "github.com/Dreamacro/clash/transport/simple-obfs"
|
||||
"github.com/Dreamacro/clash/transport/snell"
|
||||
)
|
||||
|
||||
type Snell struct {
|
@ -11,8 +11,8 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/Dreamacro/clash/component/dialer"
|
||||
"github.com/Dreamacro/clash/component/socks5"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
type Socks5 struct {
|
||||
@ -79,7 +79,7 @@ func (ss *Socks5) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Co
|
||||
|
||||
// DialUDP implements C.ProxyAdapter
|
||||
func (ss *Socks5) DialUDP(metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), tcpTimeout)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTCPTimeout)
|
||||
defer cancel()
|
||||
c, err := dialer.DialContext(ctx, "tcp", ss.addr)
|
||||
if err != nil {
|
||||
@ -145,7 +145,6 @@ func NewSocks5(option Socks5Option) *Socks5 {
|
||||
if option.TLS {
|
||||
tlsConfig = &tls.Config{
|
||||
InsecureSkipVerify: option.SkipCertVerify,
|
||||
ClientSessionCache: getClientSessionCache(),
|
||||
ServerName: option.Server,
|
||||
}
|
||||
}
|
@ -3,15 +3,14 @@ package outbound
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/Dreamacro/clash/component/dialer"
|
||||
"github.com/Dreamacro/clash/component/gun"
|
||||
"github.com/Dreamacro/clash/component/trojan"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/transport/gun"
|
||||
"github.com/Dreamacro/clash/transport/trojan"
|
||||
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
@ -101,7 +100,7 @@ func (t *Trojan) DialUDP(metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||
}
|
||||
defer safeConnClose(c, err)
|
||||
} else {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), tcpTimeout)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTCPTimeout)
|
||||
defer cancel()
|
||||
c, err = dialer.DialContext(ctx, "tcp", t.addr)
|
||||
if err != nil {
|
||||
@ -124,21 +123,14 @@ func (t *Trojan) DialUDP(metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||
return newPacketConn(pc, t), err
|
||||
}
|
||||
|
||||
func (t *Trojan) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(map[string]string{
|
||||
"type": t.Type().String(),
|
||||
})
|
||||
}
|
||||
|
||||
func NewTrojan(option TrojanOption) (*Trojan, error) {
|
||||
addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
|
||||
|
||||
tOption := &trojan.Option{
|
||||
Password: option.Password,
|
||||
ALPN: option.ALPN,
|
||||
ServerName: option.Server,
|
||||
SkipCertVerify: option.SkipCertVerify,
|
||||
ClientSessionCache: getClientSessionCache(),
|
||||
Password: option.Password,
|
||||
ALPN: option.ALPN,
|
||||
ServerName: option.Server,
|
||||
SkipCertVerify: option.SkipCertVerify,
|
||||
}
|
||||
|
||||
if option.SNI != "" {
|
||||
@ -170,7 +162,6 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
|
||||
MinVersion: tls.VersionTLS12,
|
||||
InsecureSkipVerify: tOption.SkipCertVerify,
|
||||
ServerName: tOption.ServerName,
|
||||
ClientSessionCache: getClientSessionCache(),
|
||||
}
|
||||
|
||||
t.transport = gun.NewHTTP2Client(dialFn, tlsConfig)
|
@ -2,56 +2,15 @@ package outbound
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/component/resolver"
|
||||
"github.com/Dreamacro/clash/component/socks5"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
const (
|
||||
tcpTimeout = 5 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
globalClientSessionCache tls.ClientSessionCache
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
func urlToMetadata(rawURL string) (addr C.Metadata, err error) {
|
||||
u, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
port := u.Port()
|
||||
if port == "" {
|
||||
switch u.Scheme {
|
||||
case "https":
|
||||
port = "443"
|
||||
case "http":
|
||||
port = "80"
|
||||
default:
|
||||
err = fmt.Errorf("%s scheme not Support", rawURL)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
addr = C.Metadata{
|
||||
AddrType: C.AtypDomainName,
|
||||
Host: u.Hostname(),
|
||||
DstIP: nil,
|
||||
DstPort: port,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func tcpKeepAlive(c net.Conn) {
|
||||
if tcp, ok := c.(*net.TCPConn); ok {
|
||||
tcp.SetKeepAlive(true)
|
||||
@ -59,13 +18,6 @@ func tcpKeepAlive(c net.Conn) {
|
||||
}
|
||||
}
|
||||
|
||||
func getClientSessionCache() tls.ClientSessionCache {
|
||||
once.Do(func() {
|
||||
globalClientSessionCache = tls.NewLRUClientSessionCache(128)
|
||||
})
|
||||
return globalClientSessionCache
|
||||
}
|
||||
|
||||
func serializesSocksAddr(metadata *C.Metadata) []byte {
|
||||
var buf [][]byte
|
||||
aType := uint8(metadata.AddrType)
|
@ -11,10 +11,10 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/Dreamacro/clash/component/dialer"
|
||||
"github.com/Dreamacro/clash/component/gun"
|
||||
"github.com/Dreamacro/clash/component/resolver"
|
||||
"github.com/Dreamacro/clash/component/vmess"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/transport/gun"
|
||||
"github.com/Dreamacro/clash/transport/vmess"
|
||||
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
@ -86,7 +86,6 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
||||
|
||||
if v.option.TLS {
|
||||
wsOpts.TLS = true
|
||||
wsOpts.SessionCache = getClientSessionCache()
|
||||
wsOpts.SkipCertVerify = v.option.SkipCertVerify
|
||||
wsOpts.ServerName = v.option.ServerName
|
||||
}
|
||||
@ -98,7 +97,6 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
||||
tlsOpts := &vmess.TLSConfig{
|
||||
Host: host,
|
||||
SkipCertVerify: v.option.SkipCertVerify,
|
||||
SessionCache: getClientSessionCache(),
|
||||
}
|
||||
|
||||
if v.option.ServerName != "" {
|
||||
@ -125,7 +123,6 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
||||
tlsOpts := vmess.TLSConfig{
|
||||
Host: host,
|
||||
SkipCertVerify: v.option.SkipCertVerify,
|
||||
SessionCache: getClientSessionCache(),
|
||||
NextProtos: []string{"h2"},
|
||||
}
|
||||
|
||||
@ -153,7 +150,6 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
||||
tlsOpts := &vmess.TLSConfig{
|
||||
Host: host,
|
||||
SkipCertVerify: v.option.SkipCertVerify,
|
||||
SessionCache: getClientSessionCache(),
|
||||
}
|
||||
|
||||
if v.option.ServerName != "" {
|
||||
@ -222,7 +218,7 @@ func (v *Vmess) DialUDP(metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||
|
||||
c, err = v.client.StreamConn(c, parseVmessAddr(metadata))
|
||||
} else {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), tcpTimeout)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTCPTimeout)
|
||||
defer cancel()
|
||||
c, err = dialer.DialContext(ctx, "tcp", v.addr)
|
||||
if err != nil {
|
@ -3,7 +3,7 @@ package outboundgroup
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/adapters/provider"
|
||||
"github.com/Dreamacro/clash/adapter/provider"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
)
|
||||
|
@ -4,8 +4,8 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/Dreamacro/clash/adapters/outbound"
|
||||
"github.com/Dreamacro/clash/adapters/provider"
|
||||
"github.com/Dreamacro/clash/adapter/outbound"
|
||||
"github.com/Dreamacro/clash/adapter/provider"
|
||||
"github.com/Dreamacro/clash/common/singledo"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
)
|
@ -7,8 +7,8 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/Dreamacro/clash/adapters/outbound"
|
||||
"github.com/Dreamacro/clash/adapters/provider"
|
||||
"github.com/Dreamacro/clash/adapter/outbound"
|
||||
"github.com/Dreamacro/clash/adapter/provider"
|
||||
"github.com/Dreamacro/clash/common/murmur3"
|
||||
"github.com/Dreamacro/clash/common/singledo"
|
||||
C "github.com/Dreamacro/clash/constant"
|
@ -4,7 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/Dreamacro/clash/adapters/provider"
|
||||
"github.com/Dreamacro/clash/adapter/provider"
|
||||
"github.com/Dreamacro/clash/common/structure"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
)
|
@ -6,8 +6,8 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/Dreamacro/clash/adapters/outbound"
|
||||
"github.com/Dreamacro/clash/adapters/provider"
|
||||
"github.com/Dreamacro/clash/adapter/outbound"
|
||||
"github.com/Dreamacro/clash/adapter/provider"
|
||||
"github.com/Dreamacro/clash/common/singledo"
|
||||
"github.com/Dreamacro/clash/component/dialer"
|
||||
C "github.com/Dreamacro/clash/constant"
|
@ -5,8 +5,8 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/Dreamacro/clash/adapters/outbound"
|
||||
"github.com/Dreamacro/clash/adapters/provider"
|
||||
"github.com/Dreamacro/clash/adapter/outbound"
|
||||
"github.com/Dreamacro/clash/adapter/provider"
|
||||
"github.com/Dreamacro/clash/common/singledo"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
)
|
@ -5,8 +5,8 @@ import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/adapters/outbound"
|
||||
"github.com/Dreamacro/clash/adapters/provider"
|
||||
"github.com/Dreamacro/clash/adapter/outbound"
|
||||
"github.com/Dreamacro/clash/adapter/provider"
|
||||
"github.com/Dreamacro/clash/common/singledo"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
)
|
@ -1,8 +1,9 @@
|
||||
package outbound
|
||||
package adapter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Dreamacro/clash/adapter/outbound"
|
||||
"github.com/Dreamacro/clash/common/structure"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
)
|
||||
@ -20,36 +21,36 @@ func ParseProxy(mapping map[string]interface{}) (C.Proxy, error) {
|
||||
)
|
||||
switch proxyType {
|
||||
case "ss":
|
||||
ssOption := &ShadowSocksOption{}
|
||||
ssOption := &outbound.ShadowSocksOption{}
|
||||
err = decoder.Decode(mapping, ssOption)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
proxy, err = NewShadowSocks(*ssOption)
|
||||
proxy, err = outbound.NewShadowSocks(*ssOption)
|
||||
case "ssr":
|
||||
ssrOption := &ShadowSocksROption{}
|
||||
ssrOption := &outbound.ShadowSocksROption{}
|
||||
err = decoder.Decode(mapping, ssrOption)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
proxy, err = NewShadowSocksR(*ssrOption)
|
||||
proxy, err = outbound.NewShadowSocksR(*ssrOption)
|
||||
case "socks5":
|
||||
socksOption := &Socks5Option{}
|
||||
socksOption := &outbound.Socks5Option{}
|
||||
err = decoder.Decode(mapping, socksOption)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
proxy = NewSocks5(*socksOption)
|
||||
proxy = outbound.NewSocks5(*socksOption)
|
||||
case "http":
|
||||
httpOption := &HttpOption{}
|
||||
httpOption := &outbound.HttpOption{}
|
||||
err = decoder.Decode(mapping, httpOption)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
proxy = NewHttp(*httpOption)
|
||||
proxy = outbound.NewHttp(*httpOption)
|
||||
case "vmess":
|
||||
vmessOption := &VmessOption{
|
||||
HTTPOpts: HTTPOptions{
|
||||
vmessOption := &outbound.VmessOption{
|
||||
HTTPOpts: outbound.HTTPOptions{
|
||||
Method: "GET",
|
||||
Path: []string{"/"},
|
||||
},
|
||||
@ -58,21 +59,21 @@ func ParseProxy(mapping map[string]interface{}) (C.Proxy, error) {
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
proxy, err = NewVmess(*vmessOption)
|
||||
proxy, err = outbound.NewVmess(*vmessOption)
|
||||
case "snell":
|
||||
snellOption := &SnellOption{}
|
||||
snellOption := &outbound.SnellOption{}
|
||||
err = decoder.Decode(mapping, snellOption)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
proxy, err = NewSnell(*snellOption)
|
||||
proxy, err = outbound.NewSnell(*snellOption)
|
||||
case "trojan":
|
||||
trojanOption := &TrojanOption{}
|
||||
trojanOption := &outbound.TrojanOption{}
|
||||
err = decoder.Decode(mapping, trojanOption)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
proxy, err = NewTrojan(*trojanOption)
|
||||
proxy, err = outbound.NewTrojan(*trojanOption)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupport proxy type: %s", proxyType)
|
||||
}
|
@ -7,7 +7,7 @@ import (
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/adapters/outbound"
|
||||
"github.com/Dreamacro/clash/adapter"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
@ -133,7 +133,7 @@ func proxiesParse(buf []byte) (interface{}, error) {
|
||||
|
||||
proxies := []C.Proxy{}
|
||||
for idx, mapping := range schema.Proxies {
|
||||
proxy, err := outbound.ParseProxy(mapping)
|
||||
proxy, err := adapter.ParseProxy(mapping)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("proxy %d error: %w", idx, err)
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package mixed
|
||||
package net
|
||||
|
||||
import (
|
||||
"bufio"
|
@ -1,12 +1,13 @@
|
||||
package process
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -94,12 +95,8 @@ func getExecPathFromPID(pid uint32) (string, error) {
|
||||
if errno != 0 {
|
||||
return "", errno
|
||||
}
|
||||
firstZero := bytes.IndexByte(buf, 0)
|
||||
if firstZero <= 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return filepath.Base(string(buf[:firstZero])), nil
|
||||
return filepath.Base(unix.ByteSliceToString(buf)), nil
|
||||
}
|
||||
|
||||
func readNativeUint32(b []byte) uint32 {
|
||||
|
@ -23,7 +23,7 @@ type DomainTrie struct {
|
||||
root *Node
|
||||
}
|
||||
|
||||
func validAndSplitDomain(domain string) ([]string, bool) {
|
||||
func ValidAndSplitDomain(domain string) ([]string, bool) {
|
||||
if domain != "" && domain[len(domain)-1] == '.' {
|
||||
return nil, false
|
||||
}
|
||||
@ -54,7 +54,7 @@ func validAndSplitDomain(domain string) ([]string, bool) {
|
||||
// 4. .example.com
|
||||
// 5. +.example.com
|
||||
func (t *DomainTrie) Insert(domain string, data interface{}) error {
|
||||
parts, valid := validAndSplitDomain(domain)
|
||||
parts, valid := ValidAndSplitDomain(domain)
|
||||
if !valid {
|
||||
return ErrInvalidDomain
|
||||
}
|
||||
@ -91,7 +91,7 @@ func (t *DomainTrie) insert(parts []string, data interface{}) {
|
||||
// 2. wildcard domain
|
||||
// 2. dot wildcard domain
|
||||
func (t *DomainTrie) Search(domain string) *Node {
|
||||
parts, valid := validAndSplitDomain(domain)
|
||||
parts, valid := ValidAndSplitDomain(domain)
|
||||
if !valid || parts[0] == "" {
|
||||
return nil
|
||||
}
|
||||
|
@ -8,16 +8,17 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/Dreamacro/clash/adapters/outbound"
|
||||
"github.com/Dreamacro/clash/adapters/outboundgroup"
|
||||
"github.com/Dreamacro/clash/adapters/provider"
|
||||
"github.com/Dreamacro/clash/adapter"
|
||||
"github.com/Dreamacro/clash/adapter/outbound"
|
||||
"github.com/Dreamacro/clash/adapter/outboundgroup"
|
||||
"github.com/Dreamacro/clash/adapter/provider"
|
||||
"github.com/Dreamacro/clash/component/auth"
|
||||
"github.com/Dreamacro/clash/component/fakeip"
|
||||
"github.com/Dreamacro/clash/component/trie"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/dns"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
R "github.com/Dreamacro/clash/rules"
|
||||
R "github.com/Dreamacro/clash/rule"
|
||||
T "github.com/Dreamacro/clash/tunnel"
|
||||
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
@ -64,6 +65,7 @@ type DNS struct {
|
||||
DefaultNameserver []dns.NameServer `yaml:"default-nameserver"`
|
||||
FakeIPRange *fakeip.Pool
|
||||
Hosts *trie.DomainTrie
|
||||
NameServerPolicy map[string]dns.NameServer
|
||||
}
|
||||
|
||||
// FallbackFilter config
|
||||
@ -106,6 +108,7 @@ type RawDNS struct {
|
||||
FakeIPRange string `yaml:"fake-ip-range"`
|
||||
FakeIPFilter []string `yaml:"fake-ip-filter"`
|
||||
DefaultNameserver []string `yaml:"default-nameserver"`
|
||||
NameServerPolicy map[string]string `yaml:"nameserver-policy"`
|
||||
}
|
||||
|
||||
type RawFallbackFilter struct {
|
||||
@ -272,13 +275,13 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[
|
||||
groupsConfig := cfg.ProxyGroup
|
||||
providersConfig := cfg.ProxyProvider
|
||||
|
||||
proxies["DIRECT"] = outbound.NewProxy(outbound.NewDirect())
|
||||
proxies["REJECT"] = outbound.NewProxy(outbound.NewReject())
|
||||
proxies["DIRECT"] = adapter.NewProxy(outbound.NewDirect())
|
||||
proxies["REJECT"] = adapter.NewProxy(outbound.NewReject())
|
||||
proxyList = append(proxyList, "DIRECT", "REJECT")
|
||||
|
||||
// parse proxy
|
||||
for idx, mapping := range proxiesConfig {
|
||||
proxy, err := outbound.ParseProxy(mapping)
|
||||
proxy, err := adapter.ParseProxy(mapping)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("proxy %d: %w", idx, err)
|
||||
}
|
||||
@ -337,7 +340,7 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[
|
||||
return nil, nil, fmt.Errorf("proxy group %s: the duplicate name", groupName)
|
||||
}
|
||||
|
||||
proxies[groupName] = outbound.NewProxy(group)
|
||||
proxies[groupName] = adapter.NewProxy(group)
|
||||
}
|
||||
|
||||
// initial compatible provider
|
||||
@ -366,7 +369,7 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[
|
||||
},
|
||||
[]provider.ProxyProvider{pd},
|
||||
)
|
||||
proxies["GLOBAL"] = outbound.NewProxy(global)
|
||||
proxies["GLOBAL"] = adapter.NewProxy(global)
|
||||
return proxies, providersMap, nil
|
||||
}
|
||||
|
||||
@ -500,6 +503,23 @@ func parseNameServer(servers []string) ([]dns.NameServer, error) {
|
||||
return nameservers, nil
|
||||
}
|
||||
|
||||
func parseNameServerPolicy(nsPolicy map[string]string) (map[string]dns.NameServer, error) {
|
||||
policy := map[string]dns.NameServer{}
|
||||
|
||||
for domain, server := range nsPolicy {
|
||||
nameservers, err := parseNameServer([]string{server})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, valid := trie.ValidAndSplitDomain(domain); !valid {
|
||||
return nil, fmt.Errorf("DNS ResoverRule invalid domain: %s", domain)
|
||||
}
|
||||
policy[domain] = nameservers[0]
|
||||
}
|
||||
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
func parseFallbackIPCIDR(ips []string) ([]*net.IPNet, error) {
|
||||
ipNets := []*net.IPNet{}
|
||||
|
||||
@ -537,6 +557,10 @@ func parseDNS(cfg RawDNS, hosts *trie.DomainTrie) (*DNS, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(cfg.DefaultNameserver) == 0 {
|
||||
return nil, errors.New("default nameserver should have at least one nameserver")
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/Dreamacro/clash/adapters/outboundgroup"
|
||||
"github.com/Dreamacro/clash/adapter/outboundgroup"
|
||||
"github.com/Dreamacro/clash/common/structure"
|
||||
)
|
||||
|
||||
|
@ -27,6 +27,10 @@ const (
|
||||
LoadBalance
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultTCPTimeout = 5 * time.Second
|
||||
)
|
||||
|
||||
type Connection interface {
|
||||
Chains() Chain
|
||||
AppendToChains(adapter ProxyAdapter)
|
||||
|
@ -1,47 +0,0 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
)
|
||||
|
||||
type HTTPContext struct {
|
||||
id uuid.UUID
|
||||
metadata *C.Metadata
|
||||
conn net.Conn
|
||||
req *http.Request
|
||||
}
|
||||
|
||||
func NewHTTPContext(conn net.Conn, req *http.Request, metadata *C.Metadata) *HTTPContext {
|
||||
id, _ := uuid.NewV4()
|
||||
return &HTTPContext{
|
||||
id: id,
|
||||
metadata: metadata,
|
||||
conn: conn,
|
||||
req: req,
|
||||
}
|
||||
}
|
||||
|
||||
// ID implement C.ConnContext ID
|
||||
func (hc *HTTPContext) ID() uuid.UUID {
|
||||
return hc.id
|
||||
}
|
||||
|
||||
// Metadata implement C.ConnContext Metadata
|
||||
func (hc *HTTPContext) Metadata() *C.Metadata {
|
||||
return hc.metadata
|
||||
}
|
||||
|
||||
// Conn implement C.ConnContext Conn
|
||||
func (hc *HTTPContext) Conn() net.Conn {
|
||||
return hc.conn
|
||||
}
|
||||
|
||||
// Request return the http request struct
|
||||
func (hc *HTTPContext) Request() *http.Request {
|
||||
return hc.req
|
||||
}
|
@ -3,7 +3,6 @@ package dns
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
@ -76,7 +75,6 @@ func newDoHClient(url string, r *Resolver) *dohClient {
|
||||
return &dohClient{
|
||||
url: url,
|
||||
transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{ClientSessionCache: globalSessionCache},
|
||||
ForceAttemptHTTP2: true,
|
||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
host, port, err := net.SplitHostPort(addr)
|
||||
|
@ -2,7 +2,6 @@ package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
@ -20,10 +19,6 @@ import (
|
||||
"golang.org/x/sync/singleflight"
|
||||
)
|
||||
|
||||
var (
|
||||
globalSessionCache = tls.NewLRUClientSessionCache(64)
|
||||
)
|
||||
|
||||
type dnsClient interface {
|
||||
Exchange(m *D.Msg) (msg *D.Msg, err error)
|
||||
ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error)
|
||||
@ -43,6 +38,7 @@ type Resolver struct {
|
||||
fallbackIPFilters []fallbackIPFilter
|
||||
group singleflight.Group
|
||||
lruCache *cache.LruCache
|
||||
policy *trie.DomainTrie
|
||||
}
|
||||
|
||||
// ResolveIP request with TypeA and TypeAAAA, priority return TypeA
|
||||
@ -131,6 +127,9 @@ func (r *Resolver) exchangeWithoutCache(m *D.Msg) (msg *D.Msg, err error) {
|
||||
return r.ipExchange(m)
|
||||
}
|
||||
|
||||
if matched := r.matchPolicy(m); len(matched) != 0 {
|
||||
return r.batchExchange(matched, m)
|
||||
}
|
||||
return r.batchExchange(r.main, m)
|
||||
})
|
||||
|
||||
@ -172,6 +171,24 @@ func (r *Resolver) batchExchange(clients []dnsClient, m *D.Msg) (msg *D.Msg, err
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Resolver) matchPolicy(m *D.Msg) []dnsClient {
|
||||
if r.policy == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
domain := r.msgToDomain(m)
|
||||
if domain == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
record := r.policy.Search(domain)
|
||||
if record == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return record.Data.([]dnsClient)
|
||||
}
|
||||
|
||||
func (r *Resolver) shouldOnlyQueryFallback(m *D.Msg) bool {
|
||||
if r.fallback == nil || len(r.fallbackDomainFilters) == 0 {
|
||||
return false
|
||||
@ -194,6 +211,11 @@ func (r *Resolver) shouldOnlyQueryFallback(m *D.Msg) bool {
|
||||
|
||||
func (r *Resolver) ipExchange(m *D.Msg) (msg *D.Msg, err error) {
|
||||
|
||||
if matched := r.matchPolicy(m); len(matched) != 0 {
|
||||
res := <-r.asyncExchange(matched, m)
|
||||
return res.Msg, res.Error
|
||||
}
|
||||
|
||||
onlyFallback := r.shouldOnlyQueryFallback(m)
|
||||
|
||||
if onlyFallback {
|
||||
@ -293,6 +315,7 @@ type Config struct {
|
||||
FallbackFilter FallbackFilter
|
||||
Pool *fakeip.Pool
|
||||
Hosts *trie.DomainTrie
|
||||
Policy map[string]NameServer
|
||||
}
|
||||
|
||||
func NewResolver(config Config) *Resolver {
|
||||
@ -312,6 +335,13 @@ func NewResolver(config Config) *Resolver {
|
||||
r.fallback = transform(config.Fallback, defaultResolver)
|
||||
}
|
||||
|
||||
if len(config.Policy) != 0 {
|
||||
r.policy = trie.New()
|
||||
for domain, nameserver := range config.Policy {
|
||||
r.policy.Insert(domain, transform([]NameServer{nameserver}, defaultResolver))
|
||||
}
|
||||
}
|
||||
|
||||
fallbackIPFilters := []fallbackIPFilter{}
|
||||
if config.FallbackFilter.GeoIP {
|
||||
fallbackIPFilters = append(fallbackIPFilters, &geoipFilter{})
|
||||
|
@ -30,6 +30,7 @@ func (s *Server) ServeDNS(w D.ResponseWriter, r *D.Msg) {
|
||||
D.HandleFailed(w, r)
|
||||
return
|
||||
}
|
||||
msg.Compress = true
|
||||
w.WriteMsg(msg)
|
||||
}
|
||||
|
||||
|
@ -127,7 +127,6 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient {
|
||||
Client: &D.Client{
|
||||
Net: s.Net,
|
||||
TLSConfig: &tls.Config{
|
||||
ClientSessionCache: globalSessionCache,
|
||||
// alpn identifier, see https://tools.ietf.org/html/draft-hoffman-dprive-dns-tls-alpn-00#page-6
|
||||
NextProtos: []string{"dns"},
|
||||
ServerName: host,
|
||||
|
10
go.mod
10
go.mod
@ -9,14 +9,14 @@ require (
|
||||
github.com/go-chi/render v1.0.1
|
||||
github.com/gofrs/uuid v4.0.0+incompatible
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/miekg/dns v1.1.42
|
||||
github.com/miekg/dns v1.1.43
|
||||
github.com/oschwald/geoip2-golang v1.5.0
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
go.uber.org/atomic v1.7.0
|
||||
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf
|
||||
golang.org/x/net v0.0.0-20210508051633-16afe75a6701
|
||||
go.uber.org/atomic v1.8.0
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
golang.org/x/sys v0.0.0-20210507161434-a76c4d0a0096
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
21
go.sum
21
go.sum
@ -13,8 +13,8 @@ github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPh
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/miekg/dns v1.1.42 h1:gWGe42RGaIqXQZ+r3WUGEKBEtvPHY2SXo4dqixDNxuY=
|
||||
github.com/miekg/dns v1.1.42/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
|
||||
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||
github.com/oschwald/geoip2-golang v1.5.0 h1:igg2yQIrrcRccB1ytFXqBfOHCjXWIoMv85lVJ1ONZzw=
|
||||
github.com/oschwald/geoip2-golang v1.5.0/go.mod h1:xdvYt5xQzB8ORWFqPnqMwZpCpgNagttWdoZLlJQzg7s=
|
||||
github.com/oschwald/maxminddb-golang v1.8.0 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph9p/UMXK/Hk=
|
||||
@ -29,14 +29,14 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.8.0 h1:CUhrE4N1rqSE6FM9ecihEjRkLQu8cDfgDyoOs83mEY4=
|
||||
go.uber.org/atomic v1.8.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf h1:B2n+Zi5QeYRDAEodEu72OS36gmTWjgpXr2+cWcBW90o=
|
||||
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI=
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210508051633-16afe75a6701 h1:lQVgcB3+FoAXOb20Dp6zTzAIrpj1k/yOOBN7s+Zv1rA=
|
||||
golang.org/x/net v0.0.0-20210508051633-16afe75a6701/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -44,8 +44,9 @@ golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210507161434-a76c4d0a0096 h1:5PbJGn5Sp3GEUjJ61aYbUP6RIo3Z3r2E4Tv9y2z8UHo=
|
||||
golang.org/x/sys v0.0.0-20210507161434-a76c4d0a0096/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
|
@ -6,9 +6,9 @@ import (
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/Dreamacro/clash/adapters/outbound"
|
||||
"github.com/Dreamacro/clash/adapters/outboundgroup"
|
||||
"github.com/Dreamacro/clash/adapters/provider"
|
||||
"github.com/Dreamacro/clash/adapter"
|
||||
"github.com/Dreamacro/clash/adapter/outboundgroup"
|
||||
"github.com/Dreamacro/clash/adapter/provider"
|
||||
"github.com/Dreamacro/clash/component/auth"
|
||||
"github.com/Dreamacro/clash/component/dialer"
|
||||
"github.com/Dreamacro/clash/component/profile"
|
||||
@ -18,9 +18,9 @@ import (
|
||||
"github.com/Dreamacro/clash/config"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/dns"
|
||||
P "github.com/Dreamacro/clash/listener"
|
||||
authStore "github.com/Dreamacro/clash/listener/auth"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
P "github.com/Dreamacro/clash/proxy"
|
||||
authStore "github.com/Dreamacro/clash/proxy/auth"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
)
|
||||
|
||||
@ -128,6 +128,7 @@ func updateDNS(c *config.DNS) {
|
||||
Domain: c.FallbackFilter.Domain,
|
||||
},
|
||||
Default: c.DefaultNameserver,
|
||||
Policy: c.NameServerPolicy,
|
||||
}
|
||||
|
||||
r := dns.NewResolver(cfg)
|
||||
@ -186,23 +187,26 @@ func updateGeneral(general *config.General, force bool) {
|
||||
bindAddress := general.BindAddress
|
||||
P.SetBindAddress(bindAddress)
|
||||
|
||||
if err := P.ReCreateHTTP(general.Port); err != nil {
|
||||
tcpIn := tunnel.TCPIn()
|
||||
udpIn := tunnel.UDPIn()
|
||||
|
||||
if err := P.ReCreateHTTP(general.Port, tcpIn); err != nil {
|
||||
log.Errorln("Start HTTP server error: %s", err.Error())
|
||||
}
|
||||
|
||||
if err := P.ReCreateSocks(general.SocksPort); err != nil {
|
||||
if err := P.ReCreateSocks(general.SocksPort, tcpIn, udpIn); err != nil {
|
||||
log.Errorln("Start SOCKS5 server error: %s", err.Error())
|
||||
}
|
||||
|
||||
if err := P.ReCreateRedir(general.RedirPort); err != nil {
|
||||
if err := P.ReCreateRedir(general.RedirPort, tcpIn, udpIn); err != nil {
|
||||
log.Errorln("Start Redir server error: %s", err.Error())
|
||||
}
|
||||
|
||||
if err := P.ReCreateTProxy(general.TProxyPort); err != nil {
|
||||
if err := P.ReCreateTProxy(general.TProxyPort, tcpIn, udpIn); err != nil {
|
||||
log.Errorln("Start TProxy server error: %s", err.Error())
|
||||
}
|
||||
|
||||
if err := P.ReCreateMixed(general.MixedPort); err != nil {
|
||||
if err := P.ReCreateMixed(general.MixedPort, tcpIn, udpIn); err != nil {
|
||||
log.Errorln("Start Mixed(http and socks5) server error: %s", err.Error())
|
||||
}
|
||||
}
|
||||
@ -231,7 +235,7 @@ func patchSelectGroup(proxies map[string]C.Proxy) {
|
||||
}
|
||||
|
||||
for name, proxy := range proxies {
|
||||
outbound, ok := proxy.(*outbound.Proxy)
|
||||
outbound, ok := proxy.(*adapter.Proxy)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
@ -6,9 +6,10 @@ import (
|
||||
|
||||
"github.com/Dreamacro/clash/component/resolver"
|
||||
"github.com/Dreamacro/clash/config"
|
||||
"github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/hub/executor"
|
||||
P "github.com/Dreamacro/clash/listener"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
P "github.com/Dreamacro/clash/proxy"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
@ -66,11 +67,15 @@ func patchConfigs(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
ports := P.GetPorts()
|
||||
P.ReCreateHTTP(pointerOrDefault(general.Port, ports.Port))
|
||||
P.ReCreateSocks(pointerOrDefault(general.SocksPort, ports.SocksPort))
|
||||
P.ReCreateRedir(pointerOrDefault(general.RedirPort, ports.RedirPort))
|
||||
P.ReCreateTProxy(pointerOrDefault(general.TProxyPort, ports.TProxyPort))
|
||||
P.ReCreateMixed(pointerOrDefault(general.MixedPort, ports.MixedPort))
|
||||
|
||||
tcpIn := tunnel.TCPIn()
|
||||
udpIn := tunnel.UDPIn()
|
||||
|
||||
P.ReCreateHTTP(pointerOrDefault(general.Port, ports.Port), tcpIn)
|
||||
P.ReCreateSocks(pointerOrDefault(general.SocksPort, ports.SocksPort), tcpIn, udpIn)
|
||||
P.ReCreateRedir(pointerOrDefault(general.RedirPort, ports.RedirPort), tcpIn, udpIn)
|
||||
P.ReCreateTProxy(pointerOrDefault(general.TProxyPort, ports.TProxyPort), tcpIn, udpIn)
|
||||
P.ReCreateMixed(pointerOrDefault(general.MixedPort, ports.MixedPort), tcpIn, udpIn)
|
||||
|
||||
if general.Mode != nil {
|
||||
tunnel.SetMode(*general.Mode)
|
||||
@ -112,6 +117,9 @@ func updateConfigs(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if req.Path == "" {
|
||||
req.Path = constant.Path.Config()
|
||||
}
|
||||
if !filepath.IsAbs(req.Path) {
|
||||
render.Status(r, http.StatusBadRequest)
|
||||
render.JSON(w, r, newError("path is not a absolute path"))
|
||||
|
@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/Dreamacro/clash/adapters/provider"
|
||||
"github.com/Dreamacro/clash/adapter/provider"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
@ -7,8 +7,8 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/adapters/outbound"
|
||||
"github.com/Dreamacro/clash/adapters/outboundgroup"
|
||||
"github.com/Dreamacro/clash/adapter"
|
||||
"github.com/Dreamacro/clash/adapter/outboundgroup"
|
||||
"github.com/Dreamacro/clash/component/profile/cachefile"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
@ -78,7 +78,7 @@ func updateProxy(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
proxy := r.Context().Value(CtxKeyProxy).(*outbound.Proxy)
|
||||
proxy := r.Context().Value(CtxKeyProxy).(*adapter.Proxy)
|
||||
selector, ok := proxy.ProxyAdapter.(*outboundgroup.Selector)
|
||||
if !ok {
|
||||
render.Status(r, http.StatusBadRequest)
|
||||
|
39
listener/http/client.go
Normal file
39
listener/http/client.go
Normal file
@ -0,0 +1,39 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/adapter/inbound"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
)
|
||||
|
||||
func newClient(source net.Addr, in chan<- C.ConnContext) *http.Client {
|
||||
return &http.Client{
|
||||
Transport: &http.Transport{
|
||||
// from http.DefaultTransport
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ResponseHeaderTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
DialContext: func(context context.Context, network, address string) (net.Conn, error) {
|
||||
if network != "tcp" && network != "tcp4" && network != "tcp6" {
|
||||
return nil, errors.New("unsupported network " + network)
|
||||
}
|
||||
|
||||
left, right := net.Pipe()
|
||||
|
||||
in <- inbound.NewHTTP(address, source, right)
|
||||
|
||||
return left, nil
|
||||
},
|
||||
},
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
}
|
||||
}
|
10
listener/http/hack.go
Normal file
10
listener/http/hack.go
Normal file
@ -0,0 +1,10 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"net/http"
|
||||
_ "unsafe"
|
||||
)
|
||||
|
||||
//go:linkname ReadRequest net/http.readRequest
|
||||
func ReadRequest(b *bufio.Reader, deleteHostHeader bool) (req *http.Request, err error)
|
132
listener/http/proxy.go
Normal file
132
listener/http/proxy.go
Normal file
@ -0,0 +1,132 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/adapter/inbound"
|
||||
"github.com/Dreamacro/clash/common/cache"
|
||||
N "github.com/Dreamacro/clash/common/net"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
authStore "github.com/Dreamacro/clash/listener/auth"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
)
|
||||
|
||||
func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.Cache) {
|
||||
client := newClient(c.RemoteAddr(), in)
|
||||
defer client.CloseIdleConnections()
|
||||
|
||||
conn := N.NewBufferedConn(c)
|
||||
|
||||
keepAlive := true
|
||||
trusted := cache == nil // disable authenticate if cache is nil
|
||||
|
||||
for keepAlive {
|
||||
request, err := ReadRequest(conn.Reader(), false)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
request.RemoteAddr = conn.RemoteAddr().String()
|
||||
|
||||
keepAlive = strings.TrimSpace(strings.ToLower(request.Header.Get("Proxy-Connection"))) == "keep-alive"
|
||||
|
||||
var resp *http.Response
|
||||
|
||||
if !trusted {
|
||||
resp = authenticate(request, cache)
|
||||
|
||||
trusted = resp == nil
|
||||
}
|
||||
|
||||
if trusted {
|
||||
if request.Method == http.MethodConnect {
|
||||
resp = responseWith(200)
|
||||
resp.Status = "Connection established"
|
||||
|
||||
if resp.Write(conn) != nil {
|
||||
break // close connection
|
||||
}
|
||||
|
||||
in <- inbound.NewHTTPS(request, conn)
|
||||
|
||||
return // hijack connection
|
||||
}
|
||||
|
||||
host := request.Header.Get("Host")
|
||||
if host != "" {
|
||||
request.Host = host
|
||||
}
|
||||
|
||||
request.RequestURI = ""
|
||||
|
||||
RemoveHopByHopHeaders(request.Header)
|
||||
RemoveExtraHTTPHostPort(request)
|
||||
|
||||
if request.URL.Scheme == "" || request.URL.Host == "" {
|
||||
resp = responseWith(http.StatusBadRequest)
|
||||
} else {
|
||||
resp, err = client.Do(request)
|
||||
if err != nil {
|
||||
resp = responseWith(http.StatusBadGateway)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RemoveHopByHopHeaders(resp.Header)
|
||||
|
||||
if keepAlive {
|
||||
resp.Header.Set("Proxy-Connection", "keep-alive")
|
||||
resp.Header.Set("Connection", "keep-alive")
|
||||
resp.Header.Set("Keep-Alive", "timeout=4")
|
||||
}
|
||||
|
||||
resp.Close = !keepAlive
|
||||
|
||||
err = resp.Write(conn)
|
||||
if err != nil {
|
||||
break // close connection
|
||||
}
|
||||
}
|
||||
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
func authenticate(request *http.Request, cache *cache.Cache) *http.Response {
|
||||
authenticator := authStore.Authenticator()
|
||||
if authenticator != nil {
|
||||
credential := ParseBasicProxyAuthorization(request)
|
||||
if credential == "" {
|
||||
resp := responseWith(http.StatusProxyAuthRequired)
|
||||
resp.Header.Set("Proxy-Authenticate", "Basic")
|
||||
return resp
|
||||
}
|
||||
|
||||
var authed interface{}
|
||||
if authed = cache.Get(credential); authed == nil {
|
||||
user, pass, err := DecodeBasicProxyAuthorization(credential)
|
||||
authed = err == nil && authenticator.Verify(user, pass)
|
||||
cache.Put(credential, authed, time.Minute)
|
||||
}
|
||||
if !authed.(bool) {
|
||||
log.Infoln("Auth failed from %s", request.RemoteAddr)
|
||||
|
||||
return responseWith(http.StatusForbidden)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func responseWith(statusCode int) *http.Response {
|
||||
return &http.Response{
|
||||
StatusCode: statusCode,
|
||||
Status: http.StatusText(statusCode),
|
||||
Proto: "HTTP/1.1",
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
Header: http.Header{},
|
||||
}
|
||||
}
|
59
listener/http/server.go
Normal file
59
listener/http/server.go
Normal file
@ -0,0 +1,59 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/common/cache"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
)
|
||||
|
||||
type Listener struct {
|
||||
listener net.Listener
|
||||
address string
|
||||
closed bool
|
||||
}
|
||||
|
||||
func New(addr string, in chan<- C.ConnContext) (*Listener, error) {
|
||||
return NewWithAuthenticate(addr, in, true)
|
||||
}
|
||||
|
||||
func NewWithAuthenticate(addr string, in chan<- C.ConnContext, authenticate bool) (*Listener, error) {
|
||||
l, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var c *cache.Cache
|
||||
if authenticate {
|
||||
c = cache.New(time.Second * 30)
|
||||
}
|
||||
|
||||
hl := &Listener{
|
||||
listener: l,
|
||||
address: addr,
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
conn, err := hl.listener.Accept()
|
||||
if err != nil {
|
||||
if hl.closed {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
go HandleConn(conn, in, c)
|
||||
}
|
||||
}()
|
||||
|
||||
return hl, nil
|
||||
}
|
||||
|
||||
func (l *Listener) Close() {
|
||||
l.closed = true
|
||||
l.listener.Close()
|
||||
}
|
||||
|
||||
func (l *Listener) Address() string {
|
||||
return l.address
|
||||
}
|
@ -1,25 +1,13 @@
|
||||
package inbound
|
||||
package http
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/context"
|
||||
)
|
||||
|
||||
// NewHTTP receive normal http request and return HTTPContext
|
||||
func NewHTTP(request *http.Request, conn net.Conn) *context.HTTPContext {
|
||||
metadata := parseHTTPAddr(request)
|
||||
metadata.Type = C.HTTP
|
||||
if ip, port, err := parseAddr(conn.RemoteAddr().String()); err == nil {
|
||||
metadata.SrcIP = ip
|
||||
metadata.SrcPort = port
|
||||
}
|
||||
return context.NewHTTPContext(conn, request, metadata)
|
||||
}
|
||||
|
||||
// RemoveHopByHopHeaders remove hop-by-hop header
|
||||
func RemoveHopByHopHeaders(header http.Header) {
|
||||
// Strip hop-by-hop header based on RFC:
|
||||
@ -59,3 +47,28 @@ func RemoveExtraHTTPHostPort(req *http.Request) {
|
||||
req.Host = host
|
||||
req.URL.Host = host
|
||||
}
|
||||
|
||||
// ParseBasicProxyAuthorization parse header Proxy-Authorization and return base64-encoded credential
|
||||
func ParseBasicProxyAuthorization(request *http.Request) string {
|
||||
value := request.Header.Get("Proxy-Authorization")
|
||||
if !strings.HasPrefix(value, "Basic ") {
|
||||
return ""
|
||||
}
|
||||
|
||||
return value[6:] // value[len("Basic "):]
|
||||
}
|
||||
|
||||
// DecodeBasicProxyAuthorization decode base64-encoded credential
|
||||
func DecodeBasicProxyAuthorization(credential string) (string, string, error) {
|
||||
plain, err := base64.StdEncoding.DecodeString(credential)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
login := strings.Split(string(plain), ":")
|
||||
if len(login) != 2 {
|
||||
return "", "", errors.New("invalid login")
|
||||
}
|
||||
|
||||
return login[0], login[1], nil
|
||||
}
|
@ -6,26 +6,29 @@ import (
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/Dreamacro/clash/adapter/inbound"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/listener/http"
|
||||
"github.com/Dreamacro/clash/listener/mixed"
|
||||
"github.com/Dreamacro/clash/listener/redir"
|
||||
"github.com/Dreamacro/clash/listener/socks"
|
||||
"github.com/Dreamacro/clash/listener/tproxy"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/Dreamacro/clash/proxy/http"
|
||||
"github.com/Dreamacro/clash/proxy/mixed"
|
||||
"github.com/Dreamacro/clash/proxy/redir"
|
||||
"github.com/Dreamacro/clash/proxy/socks"
|
||||
)
|
||||
|
||||
var (
|
||||
allowLan = false
|
||||
bindAddress = "*"
|
||||
|
||||
socksListener *socks.SockListener
|
||||
socksUDPListener *socks.SockUDPListener
|
||||
httpListener *http.HTTPListener
|
||||
redirListener *redir.RedirListener
|
||||
redirUDPListener *redir.RedirUDPListener
|
||||
tproxyListener *redir.TProxyListener
|
||||
tproxyUDPListener *redir.RedirUDPListener
|
||||
mixedListener *mixed.MixedListener
|
||||
mixedUDPLister *socks.SockUDPListener
|
||||
socksListener *socks.Listener
|
||||
socksUDPListener *socks.UDPListener
|
||||
httpListener *http.Listener
|
||||
redirListener *redir.Listener
|
||||
redirUDPListener *tproxy.UDPListener
|
||||
tproxyListener *tproxy.Listener
|
||||
tproxyUDPListener *tproxy.UDPListener
|
||||
mixedListener *mixed.Listener
|
||||
mixedUDPLister *socks.UDPListener
|
||||
|
||||
// lock for recreate function
|
||||
socksMux sync.Mutex
|
||||
@ -59,7 +62,7 @@ func SetBindAddress(host string) {
|
||||
bindAddress = host
|
||||
}
|
||||
|
||||
func ReCreateHTTP(port int) error {
|
||||
func ReCreateHTTP(port int, tcpIn chan<- C.ConnContext) error {
|
||||
httpMux.Lock()
|
||||
defer httpMux.Unlock()
|
||||
|
||||
@ -78,15 +81,16 @@ func ReCreateHTTP(port int) error {
|
||||
}
|
||||
|
||||
var err error
|
||||
httpListener, err = http.NewHTTPProxy(addr)
|
||||
httpListener, err = http.New(addr, tcpIn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infoln("HTTP proxy listening at: %s", httpListener.Address())
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReCreateSocks(port int) error {
|
||||
func ReCreateSocks(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) error {
|
||||
socksMux.Lock()
|
||||
defer socksMux.Unlock()
|
||||
|
||||
@ -121,12 +125,12 @@ func ReCreateSocks(port int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
tcpListener, err := socks.NewSocksProxy(addr)
|
||||
tcpListener, err := socks.New(addr, tcpIn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
udpListener, err := socks.NewSocksUDPProxy(addr)
|
||||
udpListener, err := socks.NewUDP(addr, udpIn)
|
||||
if err != nil {
|
||||
tcpListener.Close()
|
||||
return err
|
||||
@ -135,10 +139,11 @@ func ReCreateSocks(port int) error {
|
||||
socksListener = tcpListener
|
||||
socksUDPListener = udpListener
|
||||
|
||||
log.Infoln("SOCKS5 proxy listening at: %s", socksListener.Address())
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReCreateRedir(port int) error {
|
||||
func ReCreateRedir(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) error {
|
||||
redirMux.Lock()
|
||||
defer redirMux.Unlock()
|
||||
|
||||
@ -165,20 +170,21 @@ func ReCreateRedir(port int) error {
|
||||
}
|
||||
|
||||
var err error
|
||||
redirListener, err = redir.NewRedirProxy(addr)
|
||||
redirListener, err = redir.New(addr, tcpIn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
redirUDPListener, err = redir.NewRedirUDPProxy(addr)
|
||||
redirUDPListener, err = tproxy.NewUDP(addr, udpIn)
|
||||
if err != nil {
|
||||
log.Warnln("Failed to start Redir UDP Listener: %s", err)
|
||||
}
|
||||
|
||||
log.Infoln("Redirect proxy listening at: %s", redirListener.Address())
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReCreateTProxy(port int) error {
|
||||
func ReCreateTProxy(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) error {
|
||||
tproxyMux.Lock()
|
||||
defer tproxyMux.Unlock()
|
||||
|
||||
@ -205,20 +211,21 @@ func ReCreateTProxy(port int) error {
|
||||
}
|
||||
|
||||
var err error
|
||||
tproxyListener, err = redir.NewTProxy(addr)
|
||||
tproxyListener, err = tproxy.New(addr, tcpIn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tproxyUDPListener, err = redir.NewRedirUDPProxy(addr)
|
||||
tproxyUDPListener, err = tproxy.NewUDP(addr, udpIn)
|
||||
if err != nil {
|
||||
log.Warnln("Failed to start TProxy UDP Listener: %s", err)
|
||||
}
|
||||
|
||||
log.Infoln("TProxy server listening at: %s", tproxyListener.Address())
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReCreateMixed(port int) error {
|
||||
func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) error {
|
||||
mixedMux.Lock()
|
||||
defer mixedMux.Unlock()
|
||||
|
||||
@ -253,17 +260,18 @@ func ReCreateMixed(port int) error {
|
||||
}
|
||||
|
||||
var err error
|
||||
mixedListener, err = mixed.NewMixedProxy(addr)
|
||||
mixedListener, err = mixed.New(addr, tcpIn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mixedUDPLister, err = socks.NewSocksUDPProxy(addr)
|
||||
mixedUDPLister, err = socks.NewUDP(addr, udpIn)
|
||||
if err != nil {
|
||||
mixedListener.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infoln("Mixed(http+socks5) proxy listening at: %s", mixedListener.Address())
|
||||
return nil
|
||||
}
|
||||
|
67
listener/mixed/mixed.go
Normal file
67
listener/mixed/mixed.go
Normal file
@ -0,0 +1,67 @@
|
||||
package mixed
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/common/cache"
|
||||
N "github.com/Dreamacro/clash/common/net"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/listener/http"
|
||||
"github.com/Dreamacro/clash/listener/socks"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
type Listener struct {
|
||||
listener net.Listener
|
||||
address string
|
||||
closed bool
|
||||
cache *cache.Cache
|
||||
}
|
||||
|
||||
func New(addr string, in chan<- C.ConnContext) (*Listener, error) {
|
||||
l, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ml := &Listener{l, addr, false, cache.New(30 * time.Second)}
|
||||
go func() {
|
||||
for {
|
||||
c, err := ml.listener.Accept()
|
||||
if err != nil {
|
||||
if ml.closed {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
go handleConn(c, in, ml.cache)
|
||||
}
|
||||
}()
|
||||
|
||||
return ml, nil
|
||||
}
|
||||
|
||||
func (l *Listener) Close() {
|
||||
l.closed = true
|
||||
l.listener.Close()
|
||||
}
|
||||
|
||||
func (l *Listener) Address() string {
|
||||
return l.address
|
||||
}
|
||||
|
||||
func handleConn(conn net.Conn, in chan<- C.ConnContext, cache *cache.Cache) {
|
||||
bufConn := N.NewBufferedConn(conn)
|
||||
head, err := bufConn.Peek(1)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if head[0] == socks5.Version {
|
||||
socks.HandleSocks(bufConn, in)
|
||||
return
|
||||
}
|
||||
|
||||
http.HandleConn(bufConn, in, cache)
|
||||
}
|
56
listener/redir/tcp.go
Normal file
56
listener/redir/tcp.go
Normal file
@ -0,0 +1,56 @@
|
||||
package redir
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/Dreamacro/clash/adapter/inbound"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
)
|
||||
|
||||
type Listener struct {
|
||||
listener net.Listener
|
||||
address string
|
||||
closed bool
|
||||
}
|
||||
|
||||
func New(addr string, in chan<- C.ConnContext) (*Listener, error) {
|
||||
l, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rl := &Listener{l, addr, false}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
if rl.closed {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
go handleRedir(c, in)
|
||||
}
|
||||
}()
|
||||
|
||||
return rl, nil
|
||||
}
|
||||
|
||||
func (l *Listener) Close() {
|
||||
l.closed = true
|
||||
l.listener.Close()
|
||||
}
|
||||
|
||||
func (l *Listener) Address() string {
|
||||
return l.address
|
||||
}
|
||||
|
||||
func handleRedir(conn net.Conn, in chan<- C.ConnContext) {
|
||||
target, err := parserPacket(conn)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
conn.(*net.TCPConn).SetKeepAlive(true)
|
||||
in <- inbound.NewSocket(target, conn, C.REDIR)
|
||||
}
|
@ -5,7 +5,7 @@ import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/Dreamacro/clash/component/socks5"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
func parserPacket(c net.Conn) (socks5.Addr, error) {
|
@ -6,7 +6,7 @@ import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/Dreamacro/clash/component/socks5"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
const (
|
@ -6,7 +6,7 @@ import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/Dreamacro/clash/component/socks5"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
const (
|
@ -6,7 +6,7 @@ import (
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
"github.com/Dreamacro/clash/component/socks5"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
func parserPacket(conn net.Conn) (socks5.Addr, error) {
|
@ -5,29 +5,26 @@ import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
|
||||
adapters "github.com/Dreamacro/clash/adapters/inbound"
|
||||
"github.com/Dreamacro/clash/component/socks5"
|
||||
"github.com/Dreamacro/clash/adapter/inbound"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
authStore "github.com/Dreamacro/clash/proxy/auth"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
authStore "github.com/Dreamacro/clash/listener/auth"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
type SockListener struct {
|
||||
net.Listener
|
||||
address string
|
||||
closed bool
|
||||
type Listener struct {
|
||||
listener net.Listener
|
||||
address string
|
||||
closed bool
|
||||
}
|
||||
|
||||
func NewSocksProxy(addr string) (*SockListener, error) {
|
||||
func New(addr string, in chan<- C.ConnContext) (*Listener, error) {
|
||||
l, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sl := &SockListener{l, addr, false}
|
||||
sl := &Listener{l, addr, false}
|
||||
go func() {
|
||||
log.Infoln("SOCKS proxy listening at: %s", addr)
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
@ -36,23 +33,23 @@ func NewSocksProxy(addr string) (*SockListener, error) {
|
||||
}
|
||||
continue
|
||||
}
|
||||
go HandleSocks(c)
|
||||
go HandleSocks(c, in)
|
||||
}
|
||||
}()
|
||||
|
||||
return sl, nil
|
||||
}
|
||||
|
||||
func (l *SockListener) Close() {
|
||||
func (l *Listener) Close() {
|
||||
l.closed = true
|
||||
l.Listener.Close()
|
||||
l.listener.Close()
|
||||
}
|
||||
|
||||
func (l *SockListener) Address() string {
|
||||
func (l *Listener) Address() string {
|
||||
return l.address
|
||||
}
|
||||
|
||||
func HandleSocks(conn net.Conn) {
|
||||
func HandleSocks(conn net.Conn, in chan<- C.ConnContext) {
|
||||
target, command, err := socks5.ServerHandshake(conn, authStore.Authenticator())
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
@ -66,5 +63,5 @@ func HandleSocks(conn net.Conn) {
|
||||
io.Copy(ioutil.Discard, conn)
|
||||
return
|
||||
}
|
||||
tunnel.Add(adapters.NewSocket(target, conn, C.SOCKS))
|
||||
in <- inbound.NewSocket(target, conn, C.SOCKS)
|
||||
}
|
@ -3,33 +3,31 @@ package socks
|
||||
import (
|
||||
"net"
|
||||
|
||||
adapters "github.com/Dreamacro/clash/adapters/inbound"
|
||||
"github.com/Dreamacro/clash/adapter/inbound"
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
"github.com/Dreamacro/clash/common/sockopt"
|
||||
"github.com/Dreamacro/clash/component/socks5"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
type SockUDPListener struct {
|
||||
net.PacketConn
|
||||
address string
|
||||
closed bool
|
||||
type UDPListener struct {
|
||||
packetConn net.PacketConn
|
||||
address string
|
||||
closed bool
|
||||
}
|
||||
|
||||
func NewSocksUDPProxy(addr string) (*SockUDPListener, error) {
|
||||
func NewUDP(addr string, in chan<- *inbound.PacketAdapter) (*UDPListener, error) {
|
||||
l, err := net.ListenPacket("udp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = sockopt.UDPReuseaddr(l.(*net.UDPConn))
|
||||
if err != nil {
|
||||
if err := sockopt.UDPReuseaddr(l.(*net.UDPConn)); err != nil {
|
||||
log.Warnln("Failed to Reuse UDP Address: %s", err)
|
||||
}
|
||||
|
||||
sl := &SockUDPListener{l, addr, false}
|
||||
sl := &UDPListener{l, addr, false}
|
||||
go func() {
|
||||
for {
|
||||
buf := pool.Get(pool.RelayBufferSize)
|
||||
@ -41,23 +39,23 @@ func NewSocksUDPProxy(addr string) (*SockUDPListener, error) {
|
||||
}
|
||||
continue
|
||||
}
|
||||
handleSocksUDP(l, buf[:n], remoteAddr)
|
||||
handleSocksUDP(l, in, buf[:n], remoteAddr)
|
||||
}
|
||||
}()
|
||||
|
||||
return sl, nil
|
||||
}
|
||||
|
||||
func (l *SockUDPListener) Close() error {
|
||||
func (l *UDPListener) Close() error {
|
||||
l.closed = true
|
||||
return l.PacketConn.Close()
|
||||
return l.packetConn.Close()
|
||||
}
|
||||
|
||||
func (l *SockUDPListener) Address() string {
|
||||
func (l *UDPListener) Address() string {
|
||||
return l.address
|
||||
}
|
||||
|
||||
func handleSocksUDP(pc net.PacketConn, buf []byte, addr net.Addr) {
|
||||
func handleSocksUDP(pc net.PacketConn, in chan<- *inbound.PacketAdapter, buf []byte, addr net.Addr) {
|
||||
target, payload, err := socks5.DecodeUDPPacket(buf)
|
||||
if err != nil {
|
||||
// Unresolved UDP packet, return buffer to the pool
|
||||
@ -70,5 +68,8 @@ func handleSocksUDP(pc net.PacketConn, buf []byte, addr net.Addr) {
|
||||
payload: payload,
|
||||
bufRef: buf,
|
||||
}
|
||||
tunnel.AddPacket(adapters.NewPacket(target, packet, C.SOCKS))
|
||||
select {
|
||||
case in <- inbound.NewPacket(target, packet, C.TPROXY):
|
||||
default:
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ import (
|
||||
"net"
|
||||
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
"github.com/Dreamacro/clash/component/socks5"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
type packet struct {
|
@ -1,4 +1,4 @@
|
||||
package redir
|
||||
package tproxy
|
||||
|
||||
import (
|
||||
"net"
|
@ -1,6 +1,6 @@
|
||||
// +build linux
|
||||
|
||||
package redir
|
||||
package tproxy
|
||||
|
||||
import (
|
||||
"net"
|
@ -1,6 +1,6 @@
|
||||
// +build !linux
|
||||
|
||||
package redir
|
||||
package tproxy
|
||||
|
||||
import (
|
||||
"errors"
|
68
listener/tproxy/tproxy.go
Normal file
68
listener/tproxy/tproxy.go
Normal file
@ -0,0 +1,68 @@
|
||||
package tproxy
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/Dreamacro/clash/adapter/inbound"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
type Listener struct {
|
||||
listener net.Listener
|
||||
address string
|
||||
closed bool
|
||||
}
|
||||
|
||||
func New(addr string, in chan<- C.ConnContext) (*Listener, error) {
|
||||
l, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tl := l.(*net.TCPListener)
|
||||
rc, err := tl.SyscallConn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = setsockopt(rc, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rl := &Listener{
|
||||
listener: l,
|
||||
address: addr,
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
if rl.closed {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
go rl.handleTProxy(c, in)
|
||||
}
|
||||
}()
|
||||
|
||||
return rl, nil
|
||||
}
|
||||
|
||||
func (l *Listener) Close() {
|
||||
l.closed = true
|
||||
l.listener.Close()
|
||||
}
|
||||
|
||||
func (l *Listener) Address() string {
|
||||
return l.address
|
||||
}
|
||||
|
||||
func (l *Listener) handleTProxy(conn net.Conn, in chan<- C.ConnContext) {
|
||||
target := socks5.ParseAddrToSocksAddr(conn.LocalAddr())
|
||||
conn.(*net.TCPConn).SetKeepAlive(true)
|
||||
in <- inbound.NewSocket(target, conn, C.TPROXY)
|
||||
}
|
@ -1,28 +1,27 @@
|
||||
package redir
|
||||
package tproxy
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
adapters "github.com/Dreamacro/clash/adapters/inbound"
|
||||
"github.com/Dreamacro/clash/adapter/inbound"
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
"github.com/Dreamacro/clash/component/socks5"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
type RedirUDPListener struct {
|
||||
net.PacketConn
|
||||
address string
|
||||
closed bool
|
||||
type UDPListener struct {
|
||||
packetConn net.PacketConn
|
||||
address string
|
||||
closed bool
|
||||
}
|
||||
|
||||
func NewRedirUDPProxy(addr string) (*RedirUDPListener, error) {
|
||||
func NewUDP(addr string, in chan<- *inbound.PacketAdapter) (*UDPListener, error) {
|
||||
l, err := net.ListenPacket("udp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rl := &RedirUDPListener{l, addr, false}
|
||||
rl := &UDPListener{l, addr, false}
|
||||
|
||||
c := l.(*net.UDPConn)
|
||||
|
||||
@ -53,27 +52,30 @@ func NewRedirUDPProxy(addr string) (*RedirUDPListener, error) {
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
handleRedirUDP(l, buf[:n], lAddr, rAddr)
|
||||
handlePacketConn(l, in, buf[:n], lAddr, rAddr)
|
||||
}
|
||||
}()
|
||||
|
||||
return rl, nil
|
||||
}
|
||||
|
||||
func (l *RedirUDPListener) Close() error {
|
||||
func (l *UDPListener) Close() error {
|
||||
l.closed = true
|
||||
return l.PacketConn.Close()
|
||||
return l.packetConn.Close()
|
||||
}
|
||||
|
||||
func (l *RedirUDPListener) Address() string {
|
||||
func (l *UDPListener) Address() string {
|
||||
return l.address
|
||||
}
|
||||
|
||||
func handleRedirUDP(pc net.PacketConn, buf []byte, lAddr *net.UDPAddr, rAddr *net.UDPAddr) {
|
||||
func handlePacketConn(pc net.PacketConn, in chan<- *inbound.PacketAdapter, buf []byte, lAddr *net.UDPAddr, rAddr *net.UDPAddr) {
|
||||
target := socks5.ParseAddrToSocksAddr(rAddr)
|
||||
pkt := &packet{
|
||||
lAddr: lAddr,
|
||||
buf: buf,
|
||||
}
|
||||
tunnel.AddPacket(adapters.NewPacket(target, pkt, C.TPROXY))
|
||||
select {
|
||||
case in <- inbound.NewPacket(target, pkt, C.TPROXY):
|
||||
default:
|
||||
}
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
// +build linux
|
||||
|
||||
package redir
|
||||
package tproxy
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
@ -10,6 +12,11 @@ import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
IPV6_TRANSPARENT = 0x4b
|
||||
IPV6_RECVORIGDSTADDR = 0x4a
|
||||
)
|
||||
|
||||
// dialUDP acts like net.DialUDP for transparent proxy.
|
||||
// It binds to a non-local address(`lAddr`).
|
||||
func dialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) {
|
||||
@ -94,3 +101,24 @@ func udpAddrFamily(net string, lAddr, rAddr *net.UDPAddr) int {
|
||||
}
|
||||
return syscall.AF_INET6
|
||||
}
|
||||
|
||||
func getOrigDst(oob []byte, oobn int) (*net.UDPAddr, error) {
|
||||
msgs, err := syscall.ParseSocketControlMessage(oob[:oobn])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, msg := range msgs {
|
||||
if msg.Header.Level == syscall.SOL_IP && msg.Header.Type == syscall.IP_RECVORIGDSTADDR {
|
||||
ip := net.IP(msg.Data[4:8])
|
||||
port := binary.BigEndian.Uint16(msg.Data[2:4])
|
||||
return &net.UDPAddr{IP: ip, Port: int(port)}, nil
|
||||
} else if msg.Header.Level == syscall.SOL_IPV6 && msg.Header.Type == IPV6_RECVORIGDSTADDR {
|
||||
ip := net.IP(msg.Data[8:24])
|
||||
port := binary.BigEndian.Uint16(msg.Data[2:4])
|
||||
return &net.UDPAddr{IP: ip, Port: int(port)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("cannot find origDst")
|
||||
}
|
@ -1,12 +1,16 @@
|
||||
// +build !linux
|
||||
|
||||
package redir
|
||||
package tproxy
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
func getOrigDst(oob []byte, oobn int) (*net.UDPAddr, error) {
|
||||
return nil, errors.New("UDP redir not supported on current platform")
|
||||
}
|
||||
|
||||
func dialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) {
|
||||
return nil, errors.New("UDP redir not supported on current platform")
|
||||
}
|
@ -1,114 +0,0 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/base64"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
adapters "github.com/Dreamacro/clash/adapters/inbound"
|
||||
"github.com/Dreamacro/clash/common/cache"
|
||||
"github.com/Dreamacro/clash/component/auth"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
authStore "github.com/Dreamacro/clash/proxy/auth"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
)
|
||||
|
||||
type HTTPListener struct {
|
||||
net.Listener
|
||||
address string
|
||||
closed bool
|
||||
cache *cache.Cache
|
||||
}
|
||||
|
||||
func NewHTTPProxy(addr string) (*HTTPListener, error) {
|
||||
l, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hl := &HTTPListener{l, addr, false, cache.New(30 * time.Second)}
|
||||
|
||||
go func() {
|
||||
log.Infoln("HTTP proxy listening at: %s", addr)
|
||||
|
||||
for {
|
||||
c, err := hl.Accept()
|
||||
if err != nil {
|
||||
if hl.closed {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
go HandleConn(c, hl.cache)
|
||||
}
|
||||
}()
|
||||
|
||||
return hl, nil
|
||||
}
|
||||
|
||||
func (l *HTTPListener) Close() {
|
||||
l.closed = true
|
||||
l.Listener.Close()
|
||||
}
|
||||
|
||||
func (l *HTTPListener) Address() string {
|
||||
return l.address
|
||||
}
|
||||
|
||||
func canActivate(loginStr string, authenticator auth.Authenticator, cache *cache.Cache) (ret bool) {
|
||||
if result := cache.Get(loginStr); result != nil {
|
||||
ret = result.(bool)
|
||||
return
|
||||
}
|
||||
loginData, err := base64.StdEncoding.DecodeString(loginStr)
|
||||
login := strings.Split(string(loginData), ":")
|
||||
ret = err == nil && len(login) == 2 && authenticator.Verify(login[0], login[1])
|
||||
|
||||
cache.Put(loginStr, ret, time.Minute)
|
||||
return
|
||||
}
|
||||
|
||||
func HandleConn(conn net.Conn, cache *cache.Cache) {
|
||||
br := bufio.NewReader(conn)
|
||||
|
||||
keepAlive:
|
||||
request, err := http.ReadRequest(br)
|
||||
if err != nil || request.URL.Host == "" {
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
|
||||
keepAlive := strings.TrimSpace(strings.ToLower(request.Header.Get("Proxy-Connection"))) == "keep-alive"
|
||||
authenticator := authStore.Authenticator()
|
||||
if authenticator != nil {
|
||||
if authStrings := strings.Split(request.Header.Get("Proxy-Authorization"), " "); len(authStrings) != 2 {
|
||||
conn.Write([]byte("HTTP/1.1 407 Proxy Authentication Required\r\nProxy-Authenticate: Basic\r\n\r\n"))
|
||||
if keepAlive {
|
||||
goto keepAlive
|
||||
}
|
||||
return
|
||||
} else if !canActivate(authStrings[1], authenticator, cache) {
|
||||
conn.Write([]byte("HTTP/1.1 403 Forbidden\r\n\r\n"))
|
||||
log.Infoln("Auth failed from %s", conn.RemoteAddr().String())
|
||||
if keepAlive {
|
||||
goto keepAlive
|
||||
}
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if request.Method == http.MethodConnect {
|
||||
_, err := conn.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n"))
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
tunnel.Add(adapters.NewHTTPS(request, conn))
|
||||
return
|
||||
}
|
||||
|
||||
tunnel.Add(adapters.NewHTTP(request, conn))
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
package mixed
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/common/cache"
|
||||
"github.com/Dreamacro/clash/component/socks5"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
|
||||
"github.com/Dreamacro/clash/proxy/http"
|
||||
"github.com/Dreamacro/clash/proxy/socks"
|
||||
)
|
||||
|
||||
type MixedListener struct {
|
||||
net.Listener
|
||||
address string
|
||||
closed bool
|
||||
cache *cache.Cache
|
||||
}
|
||||
|
||||
func NewMixedProxy(addr string) (*MixedListener, error) {
|
||||
l, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ml := &MixedListener{l, addr, false, cache.New(30 * time.Second)}
|
||||
go func() {
|
||||
log.Infoln("Mixed(http+socks5) proxy listening at: %s", addr)
|
||||
|
||||
for {
|
||||
c, err := ml.Accept()
|
||||
if err != nil {
|
||||
if ml.closed {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
go handleConn(c, ml.cache)
|
||||
}
|
||||
}()
|
||||
|
||||
return ml, nil
|
||||
}
|
||||
|
||||
func (l *MixedListener) Close() {
|
||||
l.closed = true
|
||||
l.Listener.Close()
|
||||
}
|
||||
|
||||
func (l *MixedListener) Address() string {
|
||||
return l.address
|
||||
}
|
||||
|
||||
func handleConn(conn net.Conn, cache *cache.Cache) {
|
||||
bufConn := NewBufferedConn(conn)
|
||||
head, err := bufConn.Peek(1)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if head[0] == socks5.Version {
|
||||
socks.HandleSocks(bufConn)
|
||||
return
|
||||
}
|
||||
|
||||
http.HandleConn(bufConn, cache)
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
package redir
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/Dreamacro/clash/adapters/inbound"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
)
|
||||
|
||||
type RedirListener struct {
|
||||
net.Listener
|
||||
address string
|
||||
closed bool
|
||||
}
|
||||
|
||||
func NewRedirProxy(addr string) (*RedirListener, error) {
|
||||
l, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rl := &RedirListener{l, addr, false}
|
||||
|
||||
go func() {
|
||||
log.Infoln("Redir proxy listening at: %s", addr)
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
if rl.closed {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
go handleRedir(c)
|
||||
}
|
||||
}()
|
||||
|
||||
return rl, nil
|
||||
}
|
||||
|
||||
func (l *RedirListener) Close() {
|
||||
l.closed = true
|
||||
l.Listener.Close()
|
||||
}
|
||||
|
||||
func (l *RedirListener) Address() string {
|
||||
return l.address
|
||||
}
|
||||
|
||||
func handleRedir(conn net.Conn) {
|
||||
target, err := parserPacket(conn)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
conn.(*net.TCPConn).SetKeepAlive(true)
|
||||
tunnel.Add(inbound.NewSocket(target, conn, C.REDIR))
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
package redir
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/Dreamacro/clash/adapters/inbound"
|
||||
"github.com/Dreamacro/clash/component/socks5"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
)
|
||||
|
||||
type TProxyListener struct {
|
||||
net.Listener
|
||||
address string
|
||||
closed bool
|
||||
}
|
||||
|
||||
func NewTProxy(addr string) (*TProxyListener, error) {
|
||||
l, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tl := l.(*net.TCPListener)
|
||||
rc, err := tl.SyscallConn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = setsockopt(rc, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rl := &TProxyListener{
|
||||
Listener: l,
|
||||
address: addr,
|
||||
}
|
||||
|
||||
go func() {
|
||||
log.Infoln("TProxy server listening at: %s", addr)
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
if rl.closed {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
go rl.handleTProxy(c)
|
||||
}
|
||||
}()
|
||||
|
||||
return rl, nil
|
||||
}
|
||||
|
||||
func (l *TProxyListener) Close() {
|
||||
l.closed = true
|
||||
l.Listener.Close()
|
||||
}
|
||||
|
||||
func (l *TProxyListener) Address() string {
|
||||
return l.address
|
||||
}
|
||||
|
||||
func (l *TProxyListener) handleTProxy(conn net.Conn) {
|
||||
target := socks5.ParseAddrToSocksAddr(conn.LocalAddr())
|
||||
conn.(*net.TCPConn).SetKeepAlive(true)
|
||||
tunnel.Add(inbound.NewSocket(target, conn, C.TPROXY))
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package redir
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"net"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
IPV6_TRANSPARENT = 0x4b
|
||||
IPV6_RECVORIGDSTADDR = 0x4a
|
||||
)
|
||||
|
||||
func getOrigDst(oob []byte, oobn int) (*net.UDPAddr, error) {
|
||||
msgs, err := syscall.ParseSocketControlMessage(oob[:oobn])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, msg := range msgs {
|
||||
if msg.Header.Level == syscall.SOL_IP && msg.Header.Type == syscall.IP_RECVORIGDSTADDR {
|
||||
ip := net.IP(msg.Data[4:8])
|
||||
port := binary.BigEndian.Uint16(msg.Data[2:4])
|
||||
return &net.UDPAddr{IP: ip, Port: int(port)}, nil
|
||||
} else if msg.Header.Level == syscall.SOL_IPV6 && msg.Header.Type == IPV6_RECVORIGDSTADDR {
|
||||
ip := net.IP(msg.Data[8:24])
|
||||
port := binary.BigEndian.Uint16(msg.Data[2:4])
|
||||
return &net.UDPAddr{IP: ip, Port: int(port)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("cannot find origDst")
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
// +build !linux
|
||||
|
||||
package redir
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
func getOrigDst(oob []byte, oobn int) (*net.UDPAddr, error) {
|
||||
return nil, errors.New("UDP redir not supported on current platform")
|
||||
}
|
49
test/README.md
Normal file
49
test/README.md
Normal file
@ -0,0 +1,49 @@
|
||||
## Clash testing suit
|
||||
|
||||
### Protocol testing suit
|
||||
|
||||
* TCP pingpong test
|
||||
* UDP pingpong test
|
||||
* TCP large data test
|
||||
* UDP large data test
|
||||
|
||||
### Protocols
|
||||
|
||||
- [x] Shadowsocks
|
||||
- [x] Normal
|
||||
- [x] ObfsHTTP
|
||||
- [x] ObfsTLS
|
||||
- [x] ObfsV2rayPlugin
|
||||
- [x] Vmess
|
||||
- [x] Normal
|
||||
- [x] AEAD
|
||||
- [x] HTTP
|
||||
- [x] HTTP2
|
||||
- [x] TLS
|
||||
- [x] Websocket
|
||||
- [x] Websocket TLS
|
||||
- [x] gRPC
|
||||
- [x] Trojan
|
||||
- [x] Normal
|
||||
- [x] gRPC
|
||||
- [x] Snell
|
||||
- [x] Normal
|
||||
- [x] ObfsHTTP
|
||||
- [x] ObfsTLS
|
||||
|
||||
### Features
|
||||
|
||||
- [ ] DNS
|
||||
- [x] DNS Server
|
||||
- [x] FakeIP
|
||||
- [x] Host
|
||||
|
||||
### Command
|
||||
|
||||
Prerequisite
|
||||
|
||||
* docker (support Linux and macOS)
|
||||
|
||||
```
|
||||
$ go test -p 1 -v
|
||||
```
|
635
test/clash_test.go
Normal file
635
test/clash_test.go
Normal file
@ -0,0 +1,635 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/hub/executor"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
ImageShadowsocks = "mritd/shadowsocks:latest"
|
||||
ImageVmess = "v2fly/v2fly-core:latest"
|
||||
ImageTrojan = "trojangfw/trojan:latest"
|
||||
ImageSnell = "icpz/snell-server:latest"
|
||||
ImageXray = "teddysun/xray:latest"
|
||||
)
|
||||
|
||||
var (
|
||||
waitTime = time.Second
|
||||
localIP = net.ParseIP("127.0.0.1")
|
||||
|
||||
defaultExposedPorts = nat.PortSet{
|
||||
"10002/tcp": struct{}{},
|
||||
"10002/udp": struct{}{},
|
||||
}
|
||||
defaultPortBindings = nat.PortMap{
|
||||
"10002/tcp": []nat.PortBinding{
|
||||
{HostPort: "10002", HostIP: "0.0.0.0"},
|
||||
},
|
||||
"10002/udp": []nat.PortBinding{
|
||||
{HostPort: "10002", HostIP: "0.0.0.0"},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
if runtime.GOOS == "darwin" {
|
||||
isDarwin = true
|
||||
}
|
||||
|
||||
currentDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
homeDir := filepath.Join(currentDir, "config")
|
||||
C.SetHomeDir(homeDir)
|
||||
|
||||
if isDarwin {
|
||||
localIP, err = defaultRouteIP()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
c, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
list, err := c.ImageList(context.Background(), types.ImageListOptions{All: true})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
imageExist := func(image string) bool {
|
||||
for _, item := range list {
|
||||
for _, tag := range item.RepoTags {
|
||||
if image == tag {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
images := []string{
|
||||
ImageShadowsocks,
|
||||
ImageVmess,
|
||||
ImageTrojan,
|
||||
ImageSnell,
|
||||
ImageXray,
|
||||
}
|
||||
|
||||
for _, image := range images {
|
||||
if imageExist(image) {
|
||||
continue
|
||||
}
|
||||
|
||||
imageStream, err := c.ImagePull(context.Background(), image, types.ImagePullOptions{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
io.Copy(io.Discard, imageStream)
|
||||
}
|
||||
}
|
||||
|
||||
var clean = `
|
||||
port: 0
|
||||
socks-port: 0
|
||||
mixed-port: 0
|
||||
redir-port: 0
|
||||
tproxy-port: 0
|
||||
dns:
|
||||
enable: false
|
||||
`
|
||||
|
||||
func cleanup() {
|
||||
parseAndApply(clean)
|
||||
}
|
||||
|
||||
func parseAndApply(cfgStr string) error {
|
||||
cfg, err := executor.ParseWithBytes([]byte(cfgStr))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
executor.ApplyConfig(cfg, true)
|
||||
return nil
|
||||
}
|
||||
|
||||
func newPingPongPair() (chan []byte, chan []byte, func(t *testing.T) error) {
|
||||
pingCh := make(chan []byte)
|
||||
pongCh := make(chan []byte)
|
||||
test := func(t *testing.T) error {
|
||||
defer close(pingCh)
|
||||
defer close(pongCh)
|
||||
pingOpen := false
|
||||
pongOpen := false
|
||||
var recv []byte
|
||||
|
||||
for {
|
||||
if pingOpen && pongOpen {
|
||||
break
|
||||
}
|
||||
|
||||
select {
|
||||
case recv, pingOpen = <-pingCh:
|
||||
assert.True(t, pingOpen)
|
||||
assert.Equal(t, []byte("ping"), recv)
|
||||
case recv, pongOpen = <-pongCh:
|
||||
assert.True(t, pongOpen)
|
||||
assert.Equal(t, []byte("pong"), recv)
|
||||
case <-time.After(10 * time.Second):
|
||||
return errors.New("timeout")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return pingCh, pongCh, test
|
||||
}
|
||||
|
||||
func newLargeDataPair() (chan hashPair, chan hashPair, func(t *testing.T) error) {
|
||||
pingCh := make(chan hashPair)
|
||||
pongCh := make(chan hashPair)
|
||||
test := func(t *testing.T) error {
|
||||
defer close(pingCh)
|
||||
defer close(pongCh)
|
||||
pingOpen := false
|
||||
pongOpen := false
|
||||
var serverPair hashPair
|
||||
var clientPair hashPair
|
||||
|
||||
for {
|
||||
if pingOpen && pongOpen {
|
||||
break
|
||||
}
|
||||
|
||||
select {
|
||||
case serverPair, pingOpen = <-pingCh:
|
||||
assert.True(t, pingOpen)
|
||||
case clientPair, pongOpen = <-pongCh:
|
||||
assert.True(t, pongOpen)
|
||||
case <-time.After(10 * time.Second):
|
||||
return errors.New("timeout")
|
||||
}
|
||||
}
|
||||
|
||||
assert.Equal(t, serverPair.recvHash, clientPair.sendHash)
|
||||
assert.Equal(t, serverPair.sendHash, clientPair.recvHash)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return pingCh, pongCh, test
|
||||
}
|
||||
|
||||
func testPingPongWithSocksPort(t *testing.T, port int) {
|
||||
pingCh, pongCh, test := newPingPongPair()
|
||||
go func() {
|
||||
l, err := net.Listen("tcp", ":10001")
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
buf := make([]byte, 4)
|
||||
if _, err := io.ReadFull(c, buf); err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
pingCh <- buf
|
||||
if _, err := c.Write([]byte("pong")); err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
l.Close()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
c, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", port))
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
if _, err := socks5.ClientHandshake(c, socks5.ParseAddr("127.0.0.1:10001"), socks5.CmdConnect, nil); err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
if _, err := c.Write([]byte("ping")); err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
buf := make([]byte, 4)
|
||||
if _, err := io.ReadFull(c, buf); err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
|
||||
pongCh <- buf
|
||||
c.Close()
|
||||
}()
|
||||
|
||||
test(t)
|
||||
}
|
||||
|
||||
func testPingPongWithConn(t *testing.T, c net.Conn) error {
|
||||
l, err := net.Listen("tcp", ":10001")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
pingCh, pongCh, test := newPingPongPair()
|
||||
go func() {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf := make([]byte, 4)
|
||||
if _, err := io.ReadFull(c, buf); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
pingCh <- buf
|
||||
if _, err := c.Write([]byte("pong")); err != nil {
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
if _, err := c.Write([]byte("ping")); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf := make([]byte, 4)
|
||||
if _, err := io.ReadFull(c, buf); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
pongCh <- buf
|
||||
}()
|
||||
|
||||
return test(t)
|
||||
}
|
||||
|
||||
func testPingPongWithPacketConn(t *testing.T, pc net.PacketConn) error {
|
||||
l, err := net.ListenPacket("udp", ":10001")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
rAddr := &net.UDPAddr{IP: localIP, Port: 10001}
|
||||
|
||||
pingCh, pongCh, test := newPingPongPair()
|
||||
go func() {
|
||||
buf := make([]byte, 1024)
|
||||
n, rAddr, err := l.ReadFrom(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
pingCh <- buf[:n]
|
||||
if _, err := l.WriteTo([]byte("pong"), rAddr); err != nil {
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
if _, err := pc.WriteTo([]byte("ping"), rAddr); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf := make([]byte, 1024)
|
||||
n, _, err := pc.ReadFrom(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
pongCh <- buf[:n]
|
||||
}()
|
||||
|
||||
return test(t)
|
||||
}
|
||||
|
||||
type hashPair struct {
|
||||
sendHash map[int][]byte
|
||||
recvHash map[int][]byte
|
||||
}
|
||||
|
||||
func testLargeDataWithConn(t *testing.T, c net.Conn) error {
|
||||
l, err := net.Listen("tcp", ":10001")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
times := 100
|
||||
chunkSize := int64(64 * 1024)
|
||||
|
||||
pingCh, pongCh, test := newLargeDataPair()
|
||||
writeRandData := func(conn net.Conn) (map[int][]byte, error) {
|
||||
buf := make([]byte, chunkSize)
|
||||
hashMap := map[int][]byte{}
|
||||
for i := 0; i < times; i++ {
|
||||
if _, err := rand.Read(buf[1:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf[0] = byte(i)
|
||||
|
||||
hash := md5.Sum(buf)
|
||||
hashMap[i] = hash[:]
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return hashMap, nil
|
||||
}
|
||||
|
||||
go func() {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
hashMap := map[int][]byte{}
|
||||
buf := make([]byte, chunkSize)
|
||||
|
||||
for i := 0; i < times; i++ {
|
||||
_, err := io.ReadFull(c, buf)
|
||||
if err != nil {
|
||||
t.Log(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
hash := md5.Sum(buf)
|
||||
hashMap[int(buf[0])] = hash[:]
|
||||
}
|
||||
|
||||
sendHash, err := writeRandData(c)
|
||||
if err != nil {
|
||||
t.Log(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
pingCh <- hashPair{
|
||||
sendHash: sendHash,
|
||||
recvHash: hashMap,
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
sendHash, err := writeRandData(c)
|
||||
if err != nil {
|
||||
t.Log(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
hashMap := map[int][]byte{}
|
||||
buf := make([]byte, chunkSize)
|
||||
|
||||
for i := 0; i < times; i++ {
|
||||
_, err := io.ReadFull(c, buf)
|
||||
if err != nil {
|
||||
t.Log(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
hash := md5.Sum(buf)
|
||||
hashMap[int(buf[0])] = hash[:]
|
||||
}
|
||||
|
||||
pongCh <- hashPair{
|
||||
sendHash: sendHash,
|
||||
recvHash: hashMap,
|
||||
}
|
||||
}()
|
||||
|
||||
return test(t)
|
||||
}
|
||||
|
||||
func testLargeDataWithPacketConn(t *testing.T, pc net.PacketConn) error {
|
||||
l, err := net.ListenPacket("udp", ":10001")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
rAddr := &net.UDPAddr{IP: localIP, Port: 10001}
|
||||
|
||||
times := 50
|
||||
chunkSize := int64(1024)
|
||||
|
||||
pingCh, pongCh, test := newLargeDataPair()
|
||||
writeRandData := func(pc net.PacketConn, addr net.Addr) (map[int][]byte, error) {
|
||||
hashMap := map[int][]byte{}
|
||||
mux := sync.Mutex{}
|
||||
for i := 0; i < times; i++ {
|
||||
go func(idx int) {
|
||||
buf := make([]byte, chunkSize)
|
||||
if _, err := rand.Read(buf[1:]); err != nil {
|
||||
t.Log(err.Error())
|
||||
return
|
||||
}
|
||||
buf[0] = byte(idx)
|
||||
|
||||
hash := md5.Sum(buf)
|
||||
mux.Lock()
|
||||
hashMap[idx] = hash[:]
|
||||
mux.Unlock()
|
||||
|
||||
if _, err := pc.WriteTo(buf, addr); err != nil {
|
||||
t.Log(err.Error())
|
||||
return
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
return hashMap, nil
|
||||
}
|
||||
|
||||
go func() {
|
||||
var rAddr net.Addr
|
||||
hashMap := map[int][]byte{}
|
||||
buf := make([]byte, 64*1024)
|
||||
|
||||
for i := 0; i < times; i++ {
|
||||
_, rAddr, err = l.ReadFrom(buf)
|
||||
if err != nil {
|
||||
t.Log(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
hash := md5.Sum(buf[:chunkSize])
|
||||
hashMap[int(buf[0])] = hash[:]
|
||||
}
|
||||
|
||||
sendHash, err := writeRandData(l, rAddr)
|
||||
if err != nil {
|
||||
t.Log(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
pingCh <- hashPair{
|
||||
sendHash: sendHash,
|
||||
recvHash: hashMap,
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
sendHash, err := writeRandData(pc, rAddr)
|
||||
if err != nil {
|
||||
t.Log(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
hashMap := map[int][]byte{}
|
||||
buf := make([]byte, 64*1024)
|
||||
|
||||
for i := 0; i < times; i++ {
|
||||
_, _, err := pc.ReadFrom(buf)
|
||||
if err != nil {
|
||||
t.Log(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
hash := md5.Sum(buf[:chunkSize])
|
||||
hashMap[int(buf[0])] = hash[:]
|
||||
}
|
||||
|
||||
pongCh <- hashPair{
|
||||
sendHash: sendHash,
|
||||
recvHash: hashMap,
|
||||
}
|
||||
}()
|
||||
|
||||
return test(t)
|
||||
}
|
||||
|
||||
func testPacketConnTimeout(t *testing.T, pc net.PacketConn) error {
|
||||
err := pc.SetReadDeadline(time.Now().Add(time.Millisecond * 300))
|
||||
assert.NoError(t, err)
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
buf := make([]byte, 1024)
|
||||
_, _, err := pc.ReadFrom(buf)
|
||||
errCh <- err
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-errCh:
|
||||
return nil
|
||||
case <-time.After(time.Second * 10):
|
||||
return errors.New("timeout")
|
||||
}
|
||||
}
|
||||
|
||||
func testSuit(t *testing.T, proxy C.ProxyAdapter) {
|
||||
conn, err := proxy.DialContext(context.Background(), &C.Metadata{
|
||||
Host: localIP.String(),
|
||||
DstPort: "10001",
|
||||
AddrType: socks5.AtypDomainName,
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
defer conn.Close()
|
||||
assert.NoError(t, testPingPongWithConn(t, conn))
|
||||
|
||||
conn, err = proxy.DialContext(context.Background(), &C.Metadata{
|
||||
Host: localIP.String(),
|
||||
DstPort: "10001",
|
||||
AddrType: socks5.AtypDomainName,
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
defer conn.Close()
|
||||
assert.NoError(t, testLargeDataWithConn(t, conn))
|
||||
|
||||
if !proxy.SupportUDP() {
|
||||
return
|
||||
}
|
||||
|
||||
pc, err := proxy.DialUDP(&C.Metadata{
|
||||
NetWork: C.UDP,
|
||||
DstIP: localIP,
|
||||
DstPort: "10001",
|
||||
AddrType: socks5.AtypIPv4,
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
defer pc.Close()
|
||||
|
||||
assert.NoError(t, testPingPongWithPacketConn(t, pc))
|
||||
|
||||
pc, err = proxy.DialUDP(&C.Metadata{
|
||||
NetWork: C.UDP,
|
||||
DstIP: localIP,
|
||||
DstPort: "10001",
|
||||
AddrType: socks5.AtypIPv4,
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
defer pc.Close()
|
||||
|
||||
assert.NoError(t, testLargeDataWithPacketConn(t, pc))
|
||||
|
||||
pc, err = proxy.DialUDP(&C.Metadata{
|
||||
NetWork: C.UDP,
|
||||
DstIP: localIP,
|
||||
DstPort: "10001",
|
||||
AddrType: socks5.AtypIPv4,
|
||||
})
|
||||
if err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
defer pc.Close()
|
||||
|
||||
assert.NoError(t, testPacketConnTimeout(t, pc))
|
||||
}
|
||||
|
||||
func TestClash_Basic(t *testing.T) {
|
||||
basic := `
|
||||
mixed-port: 10000
|
||||
log-level: silent
|
||||
`
|
||||
|
||||
if err := parseAndApply(basic); err != nil {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
time.Sleep(waitTime)
|
||||
testPingPongWithSocksPort(t, 10000)
|
||||
}
|
28
test/config/example.org-key.pem
Normal file
28
test/config/example.org-key.pem
Normal file
@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDQ+c++LkDTdaw5
|
||||
5spCu9MWMcvVdrYBZZ5qZy7DskphSUSQp25cIu34GJXVPNxtbWx1CQCmdLlwqXvo
|
||||
PfUt5/pz9qsfhdAbzFduZQgGd7GTQOTJBDrAhm2+iVsQyGHHhF68muN+SgT+AtRE
|
||||
sJyZoHNYtjjWEIHQ++FHEDqwUVnj6Ut99LHlyfCjOZ5+WyBiKCjyMNots/gDep7R
|
||||
i4X2kMTqNMIIqPUcAaP5EQk41bJbFhKe915qN9b1dRISKFKmiWeOsxgTB/O/EaL5
|
||||
LsBYwZ/BiIMDk30aZvzRJeloasIR3z4hrKQqBfB0lfeIdiPpJIs5rXJQEiWH89ge
|
||||
gplsLbfrAgMBAAECggEBAKpMGaZzDPMF/v8Ee6lcZM2+cMyZPALxa+JsCakCvyh+
|
||||
y7hSKVY+RM0cQ+YM/djTBkJtvrDniEMuasI803PAitI7nwJGSuyMXmehP6P9oKFO
|
||||
jeLeZn6ETiSqzKJlmYE89vMeCevdqCnT5mW/wy5Smg0eGj0gIJpM2S3PJPSQpv9Z
|
||||
ots0JXkwooJcpGWzlwPkjSouY2gDbE4Coi+jmYLNjA1k5RbggcutnUCZZkJ6yMNv
|
||||
H52VjnkffpAFHRouK/YgF+5nbMyyw5YTLOyTWBq7qfBMsXynkWLU73GC/xDZa3yG
|
||||
o/Ph2knXCjgLmCRessTOObdOXedjnGWIjiqF8fVboDECgYEA6x5CteYiwthDBULZ
|
||||
CG5nE9VKkRHJYdArm+VjmGbzK51tKli112avmU4r3ol907+mEa4tWLkPqdZrrL49
|
||||
aHltuHizZJixJcw0rcI302ot/Ov0gkF9V55gnAQS/Kemvx9FHWm5NHdYvbObzj33
|
||||
bYRLJBtJWzYg9M8Bw9ZrUnegc/MCgYEA44kq5OSYCbyu3eaX8XHTtFhuQHNFjwl7
|
||||
Xk/Oel6PVZzmt+oOlDHnOfGSB/KpR3YXxFRngiiPZzbrOwFyPGe7HIfg03HAXiJh
|
||||
ivEfrPHbQqQUI/4b44GpDy6bhNtz777ivFGYEt21vpwd89rFiye+RkqF8eL/evxO
|
||||
pUayDZYvwikCgYEA07wFoZ/lkAiHmpZPsxsRcrfzFd+pto9splEWtumHdbCo3ajT
|
||||
4W5VFr9iHF8/VFDT8jokFjFaXL1/bCpKTOqFl8oC68XiSkKy8gPkmFyXm5y2LhNi
|
||||
GGTFZdr5alRkgttbN5i9M/WCkhvMZRhC2Xp43MRB9IUzeqNtWHqhXbvjYGcCgYEA
|
||||
vTMOztviLJ6PjYa0K5lp31l0+/SeD21j/y0/VPOSHi9kjeN7EfFZAw6DTkaSShDB
|
||||
fIhutYVCkSHSgfMW6XGb3gKCiW/Z9KyEDYOowicuGgDTmoYu7IOhbzVjLhtJET7Z
|
||||
zJvQZ0eiW4f3RBFTF/4JMuu+6z7FD6ADSV06qx+KQNkCgYBw26iQxmT5e/4kVv8X
|
||||
DzBJ1HuliKBnnzZA1YRjB4H8F6Yrq+9qur1Lurez4YlbkGV8yPFt+Iu82ViUWL28
|
||||
9T7Jgp3TOpf8qOqsWFv8HldpEZbE0Tcib4x6s+zOg/aw0ac/xOPY1sCVFB81VODP
|
||||
XCar+uxMBXI1zbXqd9QdEwy4Ig==
|
||||
-----END PRIVATE KEY-----
|
25
test/config/example.org.pem
Normal file
25
test/config/example.org.pem
Normal file
@ -0,0 +1,25 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIESzCCArOgAwIBAgIQIi5xRZvFZaSweWU9Y5mExjANBgkqhkiG9w0BAQsFADCB
|
||||
hzEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMS4wLAYDVQQLDCVkcmVh
|
||||
bWFjcm9ARHJlYW1hY3JvLmxvY2FsIChEcmVhbWFjcm8pMTUwMwYDVQQDDCxta2Nl
|
||||
cnQgZHJlYW1hY3JvQERyZWFtYWNyby5sb2NhbCAoRHJlYW1hY3JvKTAeFw0yMTAz
|
||||
MTcxNDQwMzZaFw0yMzA2MTcxNDQwMzZaMFkxJzAlBgNVBAoTHm1rY2VydCBkZXZl
|
||||
bG9wbWVudCBjZXJ0aWZpY2F0ZTEuMCwGA1UECwwlZHJlYW1hY3JvQERyZWFtYWNy
|
||||
by5sb2NhbCAoRHJlYW1hY3JvKTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
|
||||
ggEBAND5z74uQNN1rDnmykK70xYxy9V2tgFlnmpnLsOySmFJRJCnblwi7fgYldU8
|
||||
3G1tbHUJAKZ0uXCpe+g99S3n+nP2qx+F0BvMV25lCAZ3sZNA5MkEOsCGbb6JWxDI
|
||||
YceEXrya435KBP4C1ESwnJmgc1i2ONYQgdD74UcQOrBRWePpS330seXJ8KM5nn5b
|
||||
IGIoKPIw2i2z+AN6ntGLhfaQxOo0wgio9RwBo/kRCTjVslsWEp73Xmo31vV1EhIo
|
||||
UqaJZ46zGBMH878RovkuwFjBn8GIgwOTfRpm/NEl6WhqwhHfPiGspCoF8HSV94h2
|
||||
I+kkizmtclASJYfz2B6CmWwtt+sCAwEAAaNgMF4wDgYDVR0PAQH/BAQDAgWgMBMG
|
||||
A1UdJQQMMAoGCCsGAQUFBwMBMB8GA1UdIwQYMBaAFO800LQ6Pa85RH4EbMmFH6ln
|
||||
F150MBYGA1UdEQQPMA2CC2V4YW1wbGUub3JnMA0GCSqGSIb3DQEBCwUAA4IBgQAP
|
||||
TsF53h7bvJcUXT3Y9yZ2vnW6xr9r92tNnM1Gfo3D2Yyn9oLf2YrfJng6WZ04Fhqa
|
||||
Wh0HOvE0n6yPNpm/Q7mh64DrgolZ8Ce5H4RTJDAabHU9XhEzfGSVtzRSFsz+szu1
|
||||
Y30IV+08DxxqMmNPspYdpAET2Lwyk2WhnARGiGw11CRkQCEkVEe6d702vS9UGBUz
|
||||
Du6lmCYCm0SbFrZ0CGgmHSHoTcCtf3EjVam7dPg3yWiPbWjvhXxgip6hz9sCqkhG
|
||||
WA5f+fPgSZ1I9U4i+uYnqjfrzwgC08RwUYordm15F6gPvXw+KVwDO8yUYQoEH0b6
|
||||
AFJtbzoAXDysvBC6kWYFFOr62EaisaEkELTS/NrPD9ux1eKbxcxHCwEtVjgC0CL6
|
||||
gAxEAQ+9maJMbrAFhsOBbGGFC+mMCGg4eEyx6+iMB0oQe0W7QFeRUAFi7Ptc/ocS
|
||||
tZ9lbrfX1/wrcTTWIYWE+xH6oeb4fhs29kxjHcf2l+tQzmpl0aP3Z/bMW4BSB+w=
|
||||
-----END CERTIFICATE-----
|
4
test/config/snell-http.conf
Normal file
4
test/config/snell-http.conf
Normal file
@ -0,0 +1,4 @@
|
||||
[snell-server]
|
||||
listen = 0.0.0.0:10002
|
||||
psk = password
|
||||
obfs = http
|
4
test/config/snell-tls.conf
Normal file
4
test/config/snell-tls.conf
Normal file
@ -0,0 +1,4 @@
|
||||
[snell-server]
|
||||
listen = 0.0.0.0:10002
|
||||
psk = password
|
||||
obfs = tls
|
3
test/config/snell.conf
Normal file
3
test/config/snell.conf
Normal file
@ -0,0 +1,3 @@
|
||||
[snell-server]
|
||||
listen = 0.0.0.0:10002
|
||||
psk = password
|
40
test/config/trojan-grpc.json
Normal file
40
test/config/trojan-grpc.json
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"port": 10002,
|
||||
"listen": "0.0.0.0",
|
||||
"protocol": "trojan",
|
||||
"settings": {
|
||||
"clients": [
|
||||
{
|
||||
"password": "example",
|
||||
"email": "grpc@example.com"
|
||||
}
|
||||
]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "grpc",
|
||||
"security": "tls",
|
||||
"tlsSettings": {
|
||||
"certificates": [
|
||||
{
|
||||
"certificateFile": "/etc/ssl/v2ray/fullchain.pem",
|
||||
"keyFile": "/etc/ssl/v2ray/privkey.pem"
|
||||
}
|
||||
]
|
||||
},
|
||||
"grpcSettings": {
|
||||
"serviceName": "example"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "freedom"
|
||||
}
|
||||
],
|
||||
"log": {
|
||||
"loglevel": "debug"
|
||||
}
|
||||
}
|
40
test/config/trojan.json
Normal file
40
test/config/trojan.json
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
"run_type": "server",
|
||||
"local_addr": "0.0.0.0",
|
||||
"local_port": 10002,
|
||||
"password": [
|
||||
"password"
|
||||
],
|
||||
"log_level": 1,
|
||||
"ssl": {
|
||||
"cert": "/path/to/certificate.crt",
|
||||
"key": "/path/to/private.key",
|
||||
"key_password": "",
|
||||
"cipher": "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384",
|
||||
"cipher_tls13": "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384",
|
||||
"prefer_server_cipher": true,
|
||||
"alpn": [
|
||||
"http/1.1"
|
||||
],
|
||||
"alpn_port_override": {
|
||||
"h2": 81
|
||||
},
|
||||
"reuse_session": true,
|
||||
"session_ticket": false,
|
||||
"session_timeout": 600,
|
||||
"plain_http_response": "",
|
||||
"curves": "",
|
||||
"dhparam": ""
|
||||
},
|
||||
"tcp": {
|
||||
"prefer_ipv4": false,
|
||||
"no_delay": true,
|
||||
"keep_alive": true,
|
||||
"reuse_port": false,
|
||||
"fast_open": false,
|
||||
"fast_open_qlen": 20
|
||||
},
|
||||
"mysql": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
28
test/config/vmess-aead.json
Normal file
28
test/config/vmess-aead.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"inbounds": [
|
||||
{
|
||||
"port": 10002,
|
||||
"listen": "0.0.0.0",
|
||||
"protocol": "vmess",
|
||||
"settings": {
|
||||
"clients": [
|
||||
{
|
||||
"id": "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||
"alterId": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "tcp"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "freedom"
|
||||
}
|
||||
],
|
||||
"log": {
|
||||
"loglevel": "debug"
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user