anytls: support cleartext mode
Some checks failed
Build / Build (push) Has been cancelled

This commit is contained in:
nadoo 2026-06-24 00:07:19 +08:00
parent 73a594aae1
commit 533571f1ec
7 changed files with 173 additions and 101 deletions

1
.gitignore vendored
View File

@ -22,6 +22,7 @@
# dev test only # dev test only
/dev/ /dev/
.gocache/
dev*.go dev*.go
*_test.go *_test.go

View File

@ -14,7 +14,7 @@ builds:
- darwin - darwin
- freebsd - freebsd
goarch: goarch:
- "386" - 386
- amd64 - amd64
- arm - arm
- arm64 - arm64
@ -27,8 +27,8 @@ builds:
- v1 - v1
- v3 - v3
goarm: goarm:
- "6" - 6
- "7" - 7
gomips: gomips:
- hardfloat - hardfloat
- softfloat - softfloat
@ -49,13 +49,13 @@ archives:
- systemd/* - systemd/*
snapshot: snapshot:
version_template: "{{ incpatch .Version }}-dev-{{.ShortCommit}}" version_template: '{{ incpatch .Version }}-dev-{{.ShortCommit}}'
checksum: checksum:
name_template: "{{ .ProjectName }}_{{ .Version }}_checksums.txt" name_template: "{{ .ProjectName }}_{{ .Version }}_checksums.txt"
release: release:
prerelease: "true" prerelease: true
draft: true draft: true
nfpms: nfpms:
@ -73,8 +73,8 @@ nfpms:
dependencies: dependencies:
- libsystemd0 - libsystemd0
bindir: /usr/bin bindir: /usr/bin
release: "1" release: 1
epoch: "1" epoch: 1
version_metadata: git version_metadata: git
section: default section: default
priority: extra priority: extra

View File

@ -55,6 +55,7 @@ we can set up local listeners as proxy servers, and forward requests to internet
|Trojan |√|√|√|√|client & server |Trojan |√|√|√|√|client & server
|Trojanc |√|√|√|√|trojan cleartext(without tls) |Trojanc |√|√|√|√|trojan cleartext(without tls)
|AnyTLS |√| |√| |client & server |AnyTLS |√| |√| |client & server
|AnyTLSc |√| |√| |anytls cleartext(without tls)
|VLESS |√|√|√|√|client & server |VLESS |√|√|√|√|client & server
|VMess | | |√|√|client only |VMess | | |√|√|client only
|SSR | | |√| |client only |SSR | | |√| |client only
@ -198,8 +199,8 @@ URL:
-forward socks5://serverA:1080,socks5://serverB:1080 (proxy chain) -forward socks5://serverA:1080,socks5://serverB:1080 (proxy chain)
SCHEME: SCHEME:
listen : anytls http kcp mixed pxyproto redir redir6 smux sni socks5 ss tcp tls tproxy trojan trojanc udp unix vless vsock ws wss listen : anytls anytlsc http kcp mixed pxyproto redir redir6 smux sni socks5 ss tcp tls tproxy trojan trojanc udp unix vless vsock ws wss
forward: anytls direct http kcp reject simple-obfs smux socks4 socks4a socks5 ss ssh ssr tcp tls trojan trojanc udp unix vless vmess vsock ws wss forward: anytls anytlsc direct http kcp reject simple-obfs smux socks4 socks4a socks5 ss ssh ssr tcp tls trojan trojanc udp unix vless vmess vsock ws wss
Note: use 'glider -scheme all' or 'glider -scheme SCHEME' to see help info for the scheme. Note: use 'glider -scheme all' or 'glider -scheme SCHEME' to see help info for the scheme.
@ -338,9 +339,11 @@ Trojan server scheme:
-- --
AnyTLS client scheme: AnyTLS client scheme:
anytls://password@host:port[?serverName=SERVERNAME][&skipVerify=true][&cert=PATH][&synackTimeout=10s] anytls://password@host:port[?serverName=SERVERNAME][&skipVerify=true][&cert=PATH][&synackTimeout=10s]
anytlsc://password@host:port (cleartext, without TLS)
AnyTLS server scheme: AnyTLS server scheme:
anytls://password@host:port?cert=PATH&key=PATH anytls://password@host:port?cert=PATH&key=PATH[&fallback=127.0.0.1:80]
anytlsc://password@host:port[?fallback=127.0.0.1:80] (cleartext, without TLS)
-- --
Unix domain socket scheme: Unix domain socket scheme:

View File

@ -86,7 +86,10 @@ listen=127.0.0.1:8443
# listen=trojanc://PASSWORD@:1234?fallback=127.0.0.1 # listen=trojanc://PASSWORD@:1234?fallback=127.0.0.1
# anytls server # anytls server
# listen=anytls://PASSWORD@:8443?cert=/path/to/cert&key=/path/to/key # listen=anytls://PASSWORD@:8443?cert=/path/to/cert&key=/path/to/key&fallback=127.0.0.1:80
# anytlsc server (anytls without tls)
# listen=anytlsc://PASSWORD@:8443?fallback=127.0.0.1:80
# FORWARDERS # FORWARDERS
# ---------- # ----------
@ -133,6 +136,9 @@ listen=127.0.0.1:8443
# anytls as forwarder # anytls as forwarder
# forward=anytls://PASSWORD@1.1.1.1:8443[?serverName=SERVERNAME][&skipVerify=true] # forward=anytls://PASSWORD@1.1.1.1:8443[?serverName=SERVERNAME][&skipVerify=true]
# anytlsc as forwarder
# forward=anytlsc://PASSWORD@1.1.1.1:8443
# vless forwarder # vless forwarder
# forward=vless://5a146038-0b56-4e95-b1dc-5c6f5a32cd98@1.1.1.1:443 # forward=vless://5a146038-0b56-4e95-b1dc-5c6f5a32cd98@1.1.1.1:443

View File

@ -20,10 +20,12 @@ type AnyTLS struct {
addr string addr string
password string password string
withTLS bool
serverName string serverName string
skipVerify bool skipVerify bool
certFile string certFile string
keyFile string keyFile string
fallback string
tlsConfig *tls.Config tlsConfig *tls.Config
synackTimeout time.Duration synackTimeout time.Duration
@ -41,10 +43,12 @@ func NewAnyTLS(s string, d proxy.Dialer, p proxy.Proxy) (*AnyTLS, error) {
proxy: p, proxy: p,
addr: u.Host, addr: u.Host,
password: u.User.Username(), password: u.User.Username(),
withTLS: true,
serverName: query.Get("serverName"), serverName: query.Get("serverName"),
skipVerify: query.Get("skipVerify") == "true", skipVerify: query.Get("skipVerify") == "true",
certFile: query.Get("cert"), certFile: query.Get("cert"),
keyFile: query.Get("key"), keyFile: query.Get("key"),
fallback: query.Get("fallback"),
synackTimeout: 10 * time.Second, synackTimeout: 10 * time.Second,
} }
if a.password == "" { if a.password == "" {
@ -103,8 +107,10 @@ func init() {
proxy.AddUsage("anytls", ` proxy.AddUsage("anytls", `
AnyTLS client scheme: AnyTLS client scheme:
anytls://password@host:port[?serverName=SERVERNAME][&skipVerify=true][&cert=PATH][&synackTimeout=10s] anytls://password@host:port[?serverName=SERVERNAME][&skipVerify=true][&cert=PATH][&synackTimeout=10s]
anytlsc://password@host:port (cleartext, without TLS)
AnyTLS server scheme: AnyTLS server scheme:
anytls://password@host:port?cert=PATH&key=PATH anytls://password@host:port?cert=PATH&key=PATH[&fallback=127.0.0.1:80]
anytlsc://password@host:port[?fallback=127.0.0.1:80] (cleartext, without TLS)
`) `)
} }

View File

@ -12,6 +12,7 @@ import (
func init() { func init() {
proxy.RegisterDialer("anytls", NewAnyTLSDialer) proxy.RegisterDialer("anytls", NewAnyTLSDialer)
proxy.RegisterDialer("anytlsc", NewClearTextDialer)
} }
func NewAnyTLSDialer(s string, d proxy.Dialer) (proxy.Dialer, error) { func NewAnyTLSDialer(s string, d proxy.Dialer) (proxy.Dialer, error) {
@ -23,6 +24,15 @@ func NewAnyTLSDialer(s string, d proxy.Dialer) (proxy.Dialer, error) {
return a, err return a, err
} }
func NewClearTextDialer(s string, d proxy.Dialer) (proxy.Dialer, error) {
a, err := NewAnyTLS(s, d, nil)
if err != nil {
return nil, fmt.Errorf("[anytlsc] create instance error: %s", err)
}
a.withTLS = false
return a, nil
}
func (s *AnyTLS) Dial(network, addr string) (net.Conn, error) { func (s *AnyTLS) Dial(network, addr string) (net.Conn, error) {
if network != "tcp" && network != "tcp4" && network != "tcp6" { if network != "tcp" && network != "tcp4" && network != "tcp6" {
return nil, proxy.ErrNotSupported return nil, proxy.ErrNotSupported
@ -63,18 +73,22 @@ func (s *AnyTLS) newClientSession() (*session, error) {
log.F("[anytls] dial to %s error: %s", s.addr, err) log.F("[anytls] dial to %s error: %s", s.addr, err)
return nil, err return nil, err
} }
c := rc
if s.withTLS {
tc := tls.Client(rc, s.tlsConfig) tc := tls.Client(rc, s.tlsConfig)
if err := tc.Handshake(); err != nil { if err := tc.Handshake(); err != nil {
_ = rc.Close() _ = rc.Close()
return nil, err return nil, err
} }
if err := writeAuth(tc, s.password, s.padding.authPaddingLen()); err != nil { c = tc
_ = tc.Close() }
if err := writeAuth(c, s.password, s.padding.authPaddingLen()); err != nil {
_ = c.Close()
return nil, err return nil, err
} }
ss := newSession(tc) ss := newSession(c)
if err := ss.writeFrame(frame{command: cmdSettings, data: clientSettings(s.padding)}); err != nil { if err := ss.writeFrame(frame{command: cmdSettings, data: clientSettings(s.padding)}); err != nil {
_ = tc.Close() _ = c.Close()
return nil, err return nil, err
} }
ss.start() ss.start()

View File

@ -1,9 +1,11 @@
package anytls package anytls
import ( import (
"bytes"
"crypto/tls" "crypto/tls"
"errors" "errors"
"fmt" "fmt"
"io"
"net" "net"
"strings" "strings"
@ -14,6 +16,7 @@ import (
func init() { func init() {
proxy.RegisterServer("anytls", NewAnyTLSServer) proxy.RegisterServer("anytls", NewAnyTLSServer)
proxy.RegisterServer("anytlsc", NewClearTextServer)
} }
func NewAnyTLSServer(s string, p proxy.Proxy) (proxy.Server, error) { func NewAnyTLSServer(s string, p proxy.Proxy) (proxy.Server, error) {
@ -32,6 +35,15 @@ func NewAnyTLSServer(s string, p proxy.Proxy) (proxy.Server, error) {
return a, nil return a, nil
} }
func NewClearTextServer(s string, p proxy.Proxy) (proxy.Server, error) {
a, err := NewAnyTLS(s, nil, p)
if err != nil {
return nil, fmt.Errorf("[anytlsc] create instance error: %s", err)
}
a.withTLS = false
return a, nil
}
func (s *AnyTLS) ListenAndServe() { func (s *AnyTLS) ListenAndServe() {
l, err := net.Listen("tcp", s.addr) l, err := net.Listen("tcp", s.addr)
if err != nil { if err != nil {
@ -40,7 +52,7 @@ func (s *AnyTLS) ListenAndServe() {
} }
defer l.Close() defer l.Close()
log.F("[anytls] listening TCP on %s", s.addr) log.F("[anytls] listening TCP on %s, with TLS: %v", s.addr, s.withTLS)
for { for {
c, err := l.Accept() c, err := l.Accept()
if err != nil { if err != nil {
@ -52,19 +64,27 @@ func (s *AnyTLS) ListenAndServe() {
} }
func (s *AnyTLS) Serve(c net.Conn) { func (s *AnyTLS) Serve(c net.Conn) {
if s.withTLS {
tlsConn := tls.Server(c, s.tlsConfig) tlsConn := tls.Server(c, s.tlsConfig)
if err := tlsConn.Handshake(); err != nil { if err := tlsConn.Handshake(); err != nil {
_ = tlsConn.Close() _ = tlsConn.Close()
log.F("[anytls] error in tls handshake: %s", err) log.F("[anytls] error in tls handshake: %s", err)
return return
} }
if err := readAuth(tlsConn, s.password); err != nil { c = tlsConn
_ = tlsConn.Close() }
headBuf := bytes.NewBuffer(nil)
if err := readAuth(io.TeeReader(c, headBuf), s.password); err != nil {
if s.fallback != "" {
s.serveFallback(c, s.fallback, headBuf)
return
}
_ = c.Close()
log.F("[anytls] auth error from %s: %s", c.RemoteAddr(), err) log.F("[anytls] auth error from %s: %s", c.RemoteAddr(), err)
return return
} }
ss := newSession(tlsConn) ss := newSession(c)
ss.start() ss.start()
for { for {
st, err := ss.acceptStream() st, err := ss.acceptStream()
@ -76,6 +96,28 @@ func (s *AnyTLS) Serve(c net.Conn) {
} }
} }
func (s *AnyTLS) serveFallback(c net.Conn, target string, headBuf *bytes.Buffer) {
defer c.Close()
dialer := s.proxy.NextDialer(target)
rc, err := dialer.Dial("tcp", target)
if err != nil {
log.F("[anytls-fallback] %s <-> %s via %s, error in dial: %v", c.RemoteAddr(), target, dialer.Addr(), err)
return
}
defer rc.Close()
if _, err := rc.Write(headBuf.Bytes()); err != nil {
log.F("[anytls-fallback] write to rc error: %v", err)
return
}
log.F("[anytls-fallback] %s <-> %s via %s", c.RemoteAddr(), target, dialer.Addr())
if err := proxy.Relay(c, rc); err != nil {
log.F("[anytls-fallback] %s <-> %s via %s, relay error: %v", c.RemoteAddr(), target, dialer.Addr(), err)
}
}
func (s *AnyTLS) serveStream(ss *session, st *stream) { func (s *AnyTLS) serveStream(ss *session, st *stream) {
defer st.Close() defer st.Close()