Skip to content

Commit 05a351a

Browse files
authored
Three types of ALPN for post-handshake records detection & imitation
XTLS/Xray-core#4778 (comment)
1 parent e62c4ae commit 05a351a

2 files changed

Lines changed: 51 additions & 28 deletions

File tree

record_detect.go

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"encoding/binary"
66
"io"
77
"net"
8+
"strconv"
89
"sync"
910
"time"
1011

@@ -16,36 +17,50 @@ var GlobalPostHandshakeRecordsLens sync.Map
1617

1718
func DetectPostHandshakeRecordsLens(config *Config) {
1819
for sni := range config.ServerNames {
19-
key := config.Dest + " " + sni
20-
if _, loaded := GlobalPostHandshakeRecordsLens.LoadOrStore(key, false); !loaded {
21-
go func() {
22-
defer func() {
23-
val, _ := GlobalPostHandshakeRecordsLens.Load(key)
24-
if _, ok := val.(bool); ok {
25-
GlobalPostHandshakeRecordsLens.Store(key, []int{})
20+
for alpn := range 3 { // 0, 1, 2
21+
key := config.Dest + " " + sni + " " + strconv.Itoa(alpn)
22+
if _, loaded := GlobalPostHandshakeRecordsLens.LoadOrStore(key, false); !loaded {
23+
go func() {
24+
defer func() {
25+
val, _ := GlobalPostHandshakeRecordsLens.Load(key)
26+
if _, ok := val.(bool); ok {
27+
GlobalPostHandshakeRecordsLens.Store(key, []int{})
28+
}
29+
}()
30+
target, err := net.Dial("tcp", config.Dest)
31+
if err != nil {
32+
return
2633
}
27-
}()
28-
target, err := net.Dial("tcp", config.Dest)
29-
if err != nil {
30-
return
31-
}
32-
if config.Xver == 1 || config.Xver == 2 {
33-
if _, err = proxyproto.HeaderProxyFromAddrs(config.Xver, target.LocalAddr(), target.RemoteAddr()).WriteTo(target); err != nil {
34+
if config.Xver == 1 || config.Xver == 2 {
35+
if _, err = proxyproto.HeaderProxyFromAddrs(config.Xver, target.LocalAddr(), target.RemoteAddr()).WriteTo(target); err != nil {
36+
return
37+
}
38+
}
39+
detectConn := &DetectConn{
40+
Conn: target,
41+
Key: key,
42+
}
43+
fingerprint := utls.HelloChrome_Auto
44+
nextProtos := []string{"h2", "http/1.1"}
45+
if alpn != 2 {
46+
fingerprint = utls.HelloGolang
47+
}
48+
if alpn == 1 {
49+
nextProtos = []string{"http/1.1"}
50+
}
51+
if alpn == 0 {
52+
nextProtos = nil
53+
}
54+
uConn := utls.UClient(detectConn, &utls.Config{
55+
ServerName: sni, // needs new loopvar behaviour
56+
NextProtos: nextProtos,
57+
}, fingerprint)
58+
if err = uConn.Handshake(); err != nil {
3459
return
3560
}
36-
}
37-
detectConn := &DetectConn{
38-
Conn: target,
39-
Key: key,
40-
}
41-
uConn := utls.UClient(detectConn, &utls.Config{
42-
ServerName: sni, // needs new loopvar behaviour
43-
}, utls.HelloChrome_Auto)
44-
if err = uConn.Handshake(); err != nil {
45-
return
46-
}
47-
io.Copy(io.Discard, uConn)
48-
}()
61+
io.Copy(io.Discard, uConn)
62+
}()
63+
}
4964
}
5065
}
5166
}

tls.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,15 @@ func Server(ctx context.Context, conn net.Conn, config *Config) (*Conn, error) {
371371
break
372372
}
373373
for {
374-
if val, ok := GlobalPostHandshakeRecordsLens.Load(config.Dest + " " + hs.clientHello.serverName); ok {
374+
key := config.Dest + " " + hs.clientHello.serverName
375+
if len(hs.clientHello.alpnProtocols) == 0 {
376+
key += " 0"
377+
} else if hs.clientHello.alpnProtocols[0] == "h2" {
378+
key += " 2"
379+
} else {
380+
key += " 1"
381+
}
382+
if val, ok := GlobalPostHandshakeRecordsLens.Load(key); ok {
375383
if postHandshakeRecordsLens, ok := val.([]int); ok {
376384
for _, length := range postHandshakeRecordsLens {
377385
plainText := make([]byte, length-16)

0 commit comments

Comments
 (0)