Refactoring
Refactored Protocol.Name -> Protocol.Type; added Encapsulation field Refactored TLS parsing; added support for ALPN
This commit is contained in:
@@ -41,5 +41,5 @@ func main() {
|
|||||||
os.Exit(3)
|
os.Exit(3)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Protocol at address %q is %s version %s (confidence %g%%)\n", address, protocol.Name, protocol.Version, confidence*100)
|
fmt.Printf("Protocol at address %q is %s version %s (confidence %g%%)\n", address, protocol.Type, protocol.Version, confidence*100)
|
||||||
}
|
}
|
||||||
|
@@ -42,11 +42,11 @@ func main() {
|
|||||||
if p == nil {
|
if p == nil {
|
||||||
return errors.New("no protocol detected")
|
return errors.New("no protocol detected")
|
||||||
}
|
}
|
||||||
if !accept[p.Name] {
|
if !accept[p.Type] {
|
||||||
return fmt.Errorf("protocol %s is not accepted", p.Name)
|
return fmt.Errorf("protocol %s is not accepted", p.Type)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(os.Stderr, "Accepting protocol %s version %s initiated by %s\n",
|
fmt.Fprintf(os.Stderr, "Accepting protocol %s version %s initiated by %s\n",
|
||||||
p.Name, p.Version, dir)
|
p.Type, p.Version, dir)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
defer func() { _ = c.Close() }()
|
defer func() { _ = c.Close() }()
|
||||||
|
@@ -81,7 +81,7 @@ func proxy(client net.Conn, target string) {
|
|||||||
log.Printf("protocol detection failed: %v", result.Error)
|
log.Printf("protocol detection failed: %v", result.Error)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("detected protocol %s version %s initiated by %s",
|
log.Printf("detected protocol %s version %s initiated by %s",
|
||||||
result.Protocol.Name, result.Protocol.Version, result.Direction)
|
result.Protocol.Type, result.Protocol.Version, result.Direction)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for the multiplexing to finish.
|
// Wait for the multiplexing to finish.
|
||||||
|
@@ -31,7 +31,7 @@ func detectHTTPRequest(dir Direction, data []byte, srcPort, dstPort int) (proto
|
|||||||
)
|
)
|
||||||
if request, err := http.ReadRequest(r); err == nil {
|
if request, err := http.ReadRequest(r); err == nil {
|
||||||
return &Protocol{
|
return &Protocol{
|
||||||
Name: ProtocolHTTP,
|
Type: TypeHTTP,
|
||||||
Version: Version{
|
Version: Version{
|
||||||
Major: request.ProtoMajor,
|
Major: request.ProtoMajor,
|
||||||
Minor: request.ProtoMinor,
|
Minor: request.ProtoMinor,
|
||||||
@@ -66,7 +66,7 @@ func detectHTTPRequest(dir Direction, data []byte, srcPort, dstPort int) (proto
|
|||||||
_, _ = fmt.Sscanf(string(part[2]), "HTTP/%d.%d ", &version.Major, &version.Minor)
|
_, _ = fmt.Sscanf(string(part[2]), "HTTP/%d.%d ", &version.Major, &version.Minor)
|
||||||
|
|
||||||
return &Protocol{
|
return &Protocol{
|
||||||
Name: ProtocolHTTP,
|
Type: TypeHTTP,
|
||||||
Version: version,
|
Version: version,
|
||||||
}, confidence + .75
|
}, confidence + .75
|
||||||
}
|
}
|
||||||
@@ -95,7 +95,7 @@ func detectHTTPResponse(dir Direction, data []byte, srcPort, dstPort int) (proto
|
|||||||
)
|
)
|
||||||
if response, err := http.ReadResponse(r, nil); err == nil {
|
if response, err := http.ReadResponse(r, nil); err == nil {
|
||||||
return &Protocol{
|
return &Protocol{
|
||||||
Name: ProtocolHTTP,
|
Type: TypeHTTP,
|
||||||
Version: Version{
|
Version: Version{
|
||||||
Major: response.ProtoMajor,
|
Major: response.ProtoMajor,
|
||||||
Minor: response.ProtoMinor,
|
Minor: response.ProtoMinor,
|
||||||
@@ -110,7 +110,7 @@ func detectHTTPResponse(dir Direction, data []byte, srcPort, dstPort int) (proto
|
|||||||
_, _ = fmt.Sscanf(string(data), "HTTP/%d.%d ", &version.Major, &version.Minor)
|
_, _ = fmt.Sscanf(string(data), "HTTP/%d.%d ", &version.Major, &version.Minor)
|
||||||
|
|
||||||
return &Protocol{
|
return &Protocol{
|
||||||
Name: ProtocolHTTP,
|
Type: TypeHTTP,
|
||||||
Version: version,
|
Version: version,
|
||||||
}, confidence + .75
|
}, confidence + .75
|
||||||
}
|
}
|
||||||
|
@@ -22,7 +22,7 @@ func TestDetectHTTPRequest(t *testing.T) {
|
|||||||
Direction: Client,
|
Direction: Client,
|
||||||
Data: http10Request,
|
Data: http10Request,
|
||||||
DstPort: 80,
|
DstPort: 80,
|
||||||
WantProto: ProtocolHTTP,
|
WantType: TypeHTTP,
|
||||||
WantConfidence: .95,
|
WantConfidence: .95,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -30,7 +30,7 @@ func TestDetectHTTPRequest(t *testing.T) {
|
|||||||
Direction: Client,
|
Direction: Client,
|
||||||
Data: getRequest,
|
Data: getRequest,
|
||||||
DstPort: 80,
|
DstPort: 80,
|
||||||
WantProto: ProtocolHTTP,
|
WantType: TypeHTTP,
|
||||||
WantConfidence: .95,
|
WantConfidence: .95,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -81,21 +81,21 @@ func TestDetectHTTPResponse(t *testing.T) {
|
|||||||
Direction: Server,
|
Direction: Server,
|
||||||
Data: http10Response,
|
Data: http10Response,
|
||||||
SrcPort: 80,
|
SrcPort: 80,
|
||||||
WantProto: ProtocolHTTP,
|
WantType: TypeHTTP,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "HTTP/1.1 200",
|
Name: "HTTP/1.1 200",
|
||||||
Direction: Server,
|
Direction: Server,
|
||||||
Data: responseOK,
|
Data: responseOK,
|
||||||
SrcPort: 80,
|
SrcPort: 80,
|
||||||
WantProto: ProtocolHTTP,
|
WantType: TypeHTTP,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "HTTP/1.1 404",
|
Name: "HTTP/1.1 404",
|
||||||
Direction: Server,
|
Direction: Server,
|
||||||
Data: responseNotFound,
|
Data: responseNotFound,
|
||||||
SrcPort: 80,
|
SrcPort: 80,
|
||||||
WantProto: ProtocolHTTP,
|
WantType: TypeHTTP,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Invalid HTTP/1.1 GET",
|
Name: "Invalid HTTP/1.1 GET",
|
||||||
|
@@ -32,7 +32,7 @@ func detectMQTT(dir Direction, data []byte, srcPort, dstPort int) (proto *Protoc
|
|||||||
|
|
||||||
// We are reasonabily sure this is MQTT now.
|
// We are reasonabily sure this is MQTT now.
|
||||||
proto = &Protocol{
|
proto = &Protocol{
|
||||||
Name: ProtocolMQTT,
|
Type: TypeMQTT,
|
||||||
}
|
}
|
||||||
confidence = 0.5
|
confidence = 0.5
|
||||||
|
|
||||||
|
@@ -53,7 +53,7 @@ func TestDetectMQTT(t *testing.T) {
|
|||||||
Direction: Client,
|
Direction: Client,
|
||||||
Data: validSimplePacket,
|
Data: validSimplePacket,
|
||||||
DstPort: 1883,
|
DstPort: 1883,
|
||||||
WantProto: ProtocolMQTT,
|
WantType: TypeMQTT,
|
||||||
WantConfidence: .99,
|
WantConfidence: .99,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -61,7 +61,7 @@ func TestDetectMQTT(t *testing.T) {
|
|||||||
Direction: Client,
|
Direction: Client,
|
||||||
Data: validFullPacket,
|
Data: validFullPacket,
|
||||||
DstPort: 1883,
|
DstPort: 1883,
|
||||||
WantProto: ProtocolMQTT,
|
WantType: TypeMQTT,
|
||||||
WantConfidence: .99,
|
WantConfidence: .99,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -69,7 +69,7 @@ func TestDetectMQTT(t *testing.T) {
|
|||||||
Direction: Client,
|
Direction: Client,
|
||||||
Data: partialPacket,
|
Data: partialPacket,
|
||||||
DstPort: 1883,
|
DstPort: 1883,
|
||||||
WantProto: ProtocolMQTT,
|
WantType: TypeMQTT,
|
||||||
WantConfidence: .5,
|
WantConfidence: .5,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -77,7 +77,7 @@ func TestDetectMQTT(t *testing.T) {
|
|||||||
Direction: Client,
|
Direction: Client,
|
||||||
Data: trailingGarbagePacket,
|
Data: trailingGarbagePacket,
|
||||||
DstPort: 1883,
|
DstPort: 1883,
|
||||||
WantProto: ProtocolMQTT,
|
WantType: TypeMQTT,
|
||||||
WantConfidence: .75,
|
WantConfidence: .75,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@@ -49,7 +49,7 @@ func detectMySQL(dir Direction, data []byte, srcPort, dstPort int) (proto *Proto
|
|||||||
_, _ = fmt.Sscanf(string(data[1:serverVersionEndPos]), "%d.%d.%d-%s", &version.Major, &version.Minor, &version.Patch, &version.Extra)
|
_, _ = fmt.Sscanf(string(data[1:serverVersionEndPos]), "%d.%d.%d-%s", &version.Major, &version.Minor, &version.Patch, &version.Extra)
|
||||||
|
|
||||||
return &Protocol{
|
return &Protocol{
|
||||||
Name: ProtocolMySQL,
|
Type: TypeMySQL,
|
||||||
Version: version,
|
Version: version,
|
||||||
}, confidence + .75
|
}, confidence + .75
|
||||||
}
|
}
|
||||||
|
@@ -43,7 +43,7 @@ func TestDetectMySQL(t *testing.T) {
|
|||||||
Direction: Server,
|
Direction: Server,
|
||||||
Data: mysql8Banner,
|
Data: mysql8Banner,
|
||||||
SrcPort: 3306,
|
SrcPort: 3306,
|
||||||
WantProto: ProtocolMySQL,
|
WantType: TypeMySQL,
|
||||||
WantConfidence: .85,
|
WantConfidence: .85,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -51,7 +51,7 @@ func TestDetectMySQL(t *testing.T) {
|
|||||||
Direction: Server,
|
Direction: Server,
|
||||||
Data: mariaDBBanner,
|
Data: mariaDBBanner,
|
||||||
SrcPort: 3306,
|
SrcPort: 3306,
|
||||||
WantProto: ProtocolMySQL,
|
WantType: TypeMySQL,
|
||||||
WantConfidence: .85,
|
WantConfidence: .85,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@@ -40,7 +40,7 @@ func detectPostgreSQLClient(dir Direction, data []byte, srcPort, dstPort int) (p
|
|||||||
minor := int(binary.BigEndian.Uint16(data[6:]))
|
minor := int(binary.BigEndian.Uint16(data[6:]))
|
||||||
if major == 2 || major == 3 {
|
if major == 2 || major == 3 {
|
||||||
return &Protocol{
|
return &Protocol{
|
||||||
Name: ProtocolPostgreSQL,
|
Type: TypePostgreSQL,
|
||||||
Version: Version{
|
Version: Version{
|
||||||
Major: major,
|
Major: major,
|
||||||
Minor: minor,
|
Minor: minor,
|
||||||
@@ -70,7 +70,7 @@ func detectPostgreSQLServer(dir Direction, data []byte, srcPort, dstPort int) (p
|
|||||||
'Z', // ReadyForQuery
|
'Z', // ReadyForQuery
|
||||||
'E', // ErrorResponse
|
'E', // ErrorResponse
|
||||||
'N': // NoticeResponse
|
'N': // NoticeResponse
|
||||||
return &Protocol{Name: ProtocolPostgreSQL}, confidence + .65
|
return &Protocol{Type: TypePostgreSQL}, confidence + .65
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, 0
|
return nil, 0
|
||||||
|
@@ -36,7 +36,7 @@ func TestDetectPostgreSQLClient(t *testing.T) {
|
|||||||
Direction: Client,
|
Direction: Client,
|
||||||
Data: pgClientStartup,
|
Data: pgClientStartup,
|
||||||
DstPort: 5432,
|
DstPort: 5432,
|
||||||
WantProto: ProtocolPostgreSQL,
|
WantType: TypePostgreSQL,
|
||||||
WantConfidence: .85,
|
WantConfidence: .85,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -77,7 +77,7 @@ func TestDetectPostgreSQLServer(t *testing.T) {
|
|||||||
Direction: Server,
|
Direction: Server,
|
||||||
Data: pgServerAuthOK,
|
Data: pgServerAuthOK,
|
||||||
DstPort: 5432,
|
DstPort: 5432,
|
||||||
WantProto: ProtocolPostgreSQL,
|
WantType: TypePostgreSQL,
|
||||||
WantConfidence: .65,
|
WantConfidence: .65,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -95,9 +95,9 @@ func TestDetectPostgreSQLServer(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.Logf("detected %s version %s confidence %g%%", p.Name, p.Version, c*100)
|
t.Logf("detected %s version %s confidence %g%%", p.Type, p.Version, c*100)
|
||||||
if p.Name != ProtocolPostgreSQL {
|
if p.Type != TypePostgreSQL {
|
||||||
t.Fatalf("expected postgres protocol, got %s", p.Name)
|
t.Fatalf("expected postgres protocol, got %s", p.Type)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -108,9 +108,9 @@ func TestDetectPostgreSQLServer(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.Logf("detected %s version %s confidence %g%%", p.Name, p.Version, c*100)
|
t.Logf("detected %s version %s confidence %g%%", p.Type, p.Version, c*100)
|
||||||
if p.Name != ProtocolPostgreSQL {
|
if p.Type != TypePostgreSQL {
|
||||||
t.Fatalf("expected postgres protocol, got %s", p.Name)
|
t.Fatalf("expected postgres protocol, got %s", p.Type)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@@ -58,7 +58,7 @@ func detectSSH(dir Direction, data []byte, srcPort, dstPort int) (proto *Protoco
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &Protocol{
|
return &Protocol{
|
||||||
Name: ProtocolSSH,
|
Type: TypeSSH,
|
||||||
Version: Version{
|
Version: Version{
|
||||||
Major: 2,
|
Major: 2,
|
||||||
Minor: 0,
|
Minor: 0,
|
||||||
@@ -78,7 +78,7 @@ func detectSSH(dir Direction, data []byte, srcPort, dstPort int) (proto *Protoco
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &Protocol{
|
return &Protocol{
|
||||||
Name: ProtocolSSH,
|
Type: TypeSSH,
|
||||||
Version: Version{
|
Version: Version{
|
||||||
Major: 1,
|
Major: 1,
|
||||||
Minor: 99,
|
Minor: 99,
|
||||||
|
@@ -36,7 +36,7 @@ func TestDetectSSH(t *testing.T) {
|
|||||||
Direction: Client,
|
Direction: Client,
|
||||||
Data: openSSHBanner,
|
Data: openSSHBanner,
|
||||||
DstPort: 22,
|
DstPort: 22,
|
||||||
WantProto: ProtocolSSH,
|
WantType: TypeSSH,
|
||||||
WantConfidence: .95,
|
WantConfidence: .95,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -44,7 +44,7 @@ func TestDetectSSH(t *testing.T) {
|
|||||||
Direction: Server,
|
Direction: Server,
|
||||||
Data: openSSHBanner,
|
Data: openSSHBanner,
|
||||||
SrcPort: 22,
|
SrcPort: 22,
|
||||||
WantProto: ProtocolSSH,
|
WantType: TypeSSH,
|
||||||
WantConfidence: .95,
|
WantConfidence: .95,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -52,7 +52,7 @@ func TestDetectSSH(t *testing.T) {
|
|||||||
Direction: Server,
|
Direction: Server,
|
||||||
Data: preBannerSSH,
|
Data: preBannerSSH,
|
||||||
SrcPort: 22,
|
SrcPort: 22,
|
||||||
WantProto: ProtocolSSH,
|
WantType: TypeSSH,
|
||||||
WantConfidence: .95,
|
WantConfidence: .95,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -60,7 +60,7 @@ func TestDetectSSH(t *testing.T) {
|
|||||||
Direction: Server,
|
Direction: Server,
|
||||||
Data: dropbearBanner,
|
Data: dropbearBanner,
|
||||||
SrcPort: 22,
|
SrcPort: 22,
|
||||||
WantProto: ProtocolSSH,
|
WantType: TypeSSH,
|
||||||
WantConfidence: .95,
|
WantConfidence: .95,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
package protocol
|
package protocol
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/crypto/cryptobyte"
|
"golang.org/x/crypto/cryptobyte"
|
||||||
|
|
||||||
"git.maze.io/go/dpi"
|
"git.maze.io/go/dpi"
|
||||||
@@ -11,12 +14,18 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func registerTLS() {
|
func registerTLS() {
|
||||||
Register(Both, "\x16\x03\x00", detectTLS) // SSLv3
|
Register(Both, "\x16\x03\x00", detectTLS) // SSL 3.0
|
||||||
Register(Both, "\x16\x03\x01", detectTLS) // TLSv1.0
|
Register(Both, "\x16\x03\x01", detectTLS) // TLS 1.0
|
||||||
Register(Both, "\x16\x03\x02", detectTLS) // TLSv1.1
|
Register(Both, "\x16\x03\x02", detectTLS) // TLS 1.1
|
||||||
Register(Both, "\x16\x03\x03", detectTLS) // TLSv1.2
|
Register(Both, "\x16\x03\x03", detectTLS) // TLS 1.2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
tlsRecordTypeHandshake uint8 = 22
|
||||||
|
tlsTypeClientHello uint8 = 1
|
||||||
|
tlsTypeServerHello uint8 = 2
|
||||||
|
)
|
||||||
|
|
||||||
func detectTLS(dir Direction, data []byte, _, _ int) (proto *Protocol, confidence float64) {
|
func detectTLS(dir Direction, data []byte, _, _ int) (proto *Protocol, confidence float64) {
|
||||||
stream := cryptobyte.String(data)
|
stream := cryptobyte.String(data)
|
||||||
|
|
||||||
@@ -44,27 +53,53 @@ func detectTLS(dir Direction, data []byte, _, _ int) (proto *Protocol, confidenc
|
|||||||
// Initial confidence
|
// Initial confidence
|
||||||
confidence = 0.5
|
confidence = 0.5
|
||||||
|
|
||||||
// Detected SSL/TLS version
|
// Detected SSL/TLS tlsVersion
|
||||||
var version dpi.TLSVersion
|
var tlsVersion dpi.TLSVersion
|
||||||
|
|
||||||
// Attempt to decode the full TLS Client Hello handshake
|
// Attempt to decode the full TLS Client Hello handshake
|
||||||
if version == 0 {
|
if tlsVersion == 0 {
|
||||||
if hello, err := dpi.DecodeTLSClientHelloHandshake(data); err == nil {
|
if record, err := dpi.DecodeTLSRecord(data); err == nil && record.Type == tlsRecordTypeHandshake && len(record.Data) > 1 {
|
||||||
version = hello.Version
|
tlsVersion = record.Version
|
||||||
confidence += .45
|
confidence += .15
|
||||||
|
|
||||||
|
switch record.Data[0] {
|
||||||
|
case tlsTypeClientHello: // TLS ClientHello
|
||||||
|
if hello, err := dpi.DecodeTLSClientHello(record.Data); err == nil {
|
||||||
|
tlsVersion = hello.Version
|
||||||
|
confidence += .3
|
||||||
|
|
||||||
|
slices.SortStableFunc(hello.ALPNProtocols, func(a, b string) int {
|
||||||
|
return strings.Compare(b, a)
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, id := range hello.ALPNProtocols {
|
||||||
|
if proto = ALPNProtocol[id]; proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to decode the full TLS Server Hello handshake
|
case tlsTypeServerHello: // TLS ServerHello
|
||||||
if version == 0 {
|
if hello, err := dpi.DecodeTLSServerHello(record.Data); err == nil {
|
||||||
if hello, err := dpi.DecodeTLSServerHello(data); err == nil {
|
tlsVersion = hello.Version
|
||||||
version = hello.Version
|
confidence += .3
|
||||||
confidence += .45
|
|
||||||
|
slices.SortStableFunc(hello.ALPNProtocols, func(a, b string) int {
|
||||||
|
return strings.Compare(b, a)
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, id := range hello.ALPNProtocols {
|
||||||
|
if proto = ALPNProtocol[id]; proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to decode at least the handshake protocol and version.
|
// Attempt to decode at least the handshake protocol and version.
|
||||||
if version == 0 && !Strict {
|
if tlsVersion == 0 && !Strict {
|
||||||
var handshakeType uint8
|
var handshakeType uint8
|
||||||
if stream.ReadUint8(&handshakeType) && (handshakeType == 1 || handshakeType == 2) {
|
if stream.ReadUint8(&handshakeType) && (handshakeType == 1 || handshakeType == 2) {
|
||||||
var (
|
var (
|
||||||
@@ -72,33 +107,68 @@ func detectTLS(dir Direction, data []byte, _, _ int) (proto *Protocol, confidenc
|
|||||||
versionWord uint16
|
versionWord uint16
|
||||||
)
|
)
|
||||||
if stream.ReadUint24(&length) && stream.ReadUint16(&versionWord) {
|
if stream.ReadUint24(&length) && stream.ReadUint16(&versionWord) {
|
||||||
version = dpi.TLSVersion(versionWord)
|
tlsVersion = dpi.TLSVersion(versionWord)
|
||||||
confidence += .25
|
confidence += .25
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fall back to the version in the TLS record header, this is less accurate
|
// Fall back to the version in the TLS record header, this is less accurate
|
||||||
if version == 0 && !Strict {
|
if tlsVersion == 0 && !Strict {
|
||||||
version = dpi.TLSVersion(header.Version)
|
tlsVersion = dpi.TLSVersion(header.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're "multi protocol", in that SSL is its own protocol
|
// We're "multi protocol", in that SSL is its own protocol
|
||||||
if version == dpi.VersionSSL30 {
|
if tlsVersion == dpi.VersionSSL30 {
|
||||||
return &Protocol{
|
return &Protocol{
|
||||||
Name: ProtocolSSL,
|
Type: TypeSSL,
|
||||||
Version: Version{Major: 3, Minor: 0, Patch: -1},
|
Version: Version{Major: 3, Minor: 0, Patch: -1},
|
||||||
}, confidence
|
}, confidence
|
||||||
} else if version >= dpi.VersionTLS10 && version <= dpi.VersionTLS13 {
|
} else if tlsVersion >= dpi.VersionTLS10 && tlsVersion <= dpi.VersionTLS13 {
|
||||||
return &Protocol{
|
return &Protocol{
|
||||||
Name: ProtocolTLS,
|
Type: TypeTLS,
|
||||||
Version: Version{Major: 1, Minor: int(uint8(version) - 1), Patch: -1},
|
Version: Version{Major: 1, Minor: int(uint8(tlsVersion) - 1), Patch: -1},
|
||||||
}, confidence
|
}, confidence
|
||||||
} else if version >= dpi.VersionTLS13Draft && version <= dpi.VersionTLS13Draft23 {
|
} else if tlsVersion >= dpi.VersionTLS13Draft && tlsVersion <= dpi.VersionTLS13Draft23 {
|
||||||
return &Protocol{
|
return &Protocol{
|
||||||
Name: ProtocolTLS,
|
Type: TypeTLS,
|
||||||
Version: Version{Major: 1, Minor: 3, Patch: -1},
|
Version: Version{Major: 1, Minor: 3, Patch: -1},
|
||||||
}, confidence
|
}, confidence
|
||||||
}
|
}
|
||||||
return nil, 0
|
return nil, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ALPNProtocol is a map of TLS Application-Layer Protocol Negotiation (ALPN) Protocol identifier to [Protocol].
|
||||||
|
//
|
||||||
|
// See https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids\
|
||||||
|
var ALPNProtocol = map[string]*Protocol{
|
||||||
|
"acme-tls/1": {Type: TypeACME, Encapsulation: TypeTLS, Version: Version{Major: 1}}, // ACME-TLS/1
|
||||||
|
"co": {Type: TypeCoAP, Encapsulation: TypeTLS}, // CoAP (over DTLS)
|
||||||
|
"coap": {Type: TypeCoAP, Encapsulation: TypeTLS}, // CoAP (over TLS)
|
||||||
|
"c-webrtc": {Type: TypeWebRTC, Encapsulation: TypeTLS}, // Confidential WebRTC Media and Data
|
||||||
|
"dot": {Type: TypeDNS, Encapsulation: TypeTLS}, // DNS-over-TLS
|
||||||
|
"ftp": {Type: TypeFTP, Encapsulation: TypeTLS}, // FTP
|
||||||
|
"http/0.9": {Type: TypeHTTP, Encapsulation: TypeTLS, Version: Version{Major: 0, Minor: 9, Patch: -1}}, // HTTP/0.9
|
||||||
|
"http/1.0": {Type: TypeHTTP, Encapsulation: TypeTLS, Version: Version{Major: 1, Minor: 0, Patch: -1}}, // HTTP/1.0
|
||||||
|
"http/1.1": {Type: TypeHTTP, Encapsulation: TypeTLS, Version: Version{Major: 1, Minor: 1, Patch: -1}}, // HTTP/1.1
|
||||||
|
"h2": {Type: TypeHTTP, Encapsulation: TypeTLS, Version: Version{Major: 2, Minor: -1, Patch: -1}}, // HTTP/2 (over TLS)
|
||||||
|
"h2c": {Type: TypeHTTP, Encapsulation: TypeTLS, Version: Version{Major: 2, Minor: -1, Patch: -1}}, // HTTP/2 (over TCP)
|
||||||
|
"h3": {Type: TypeHTTP, Encapsulation: TypeTLS, Version: Version{Major: 3, Minor: -1, Patch: -1}}, // HTTP/3
|
||||||
|
"irc": {Type: TypeIRC, Encapsulation: TypeTLS}, // IRC
|
||||||
|
"imap": {Type: TypeIMAP, Encapsulation: TypeTLS}, // IMAP
|
||||||
|
"managesieve": {Type: TypeManageSieve, Encapsulation: TypeTLS}, // ManageSieve
|
||||||
|
"mqtt": {Type: TypeMQTT, Encapsulation: TypeTLS}, // MQTT
|
||||||
|
"nntp": {Type: TypeNNTP, Encapsulation: TypeTLS}, // NNTP (reading)
|
||||||
|
"nnsp": {Type: TypeNNTP, Encapsulation: TypeTLS}, // NNTP (transit)
|
||||||
|
"postgresql": {Type: TypePostgreSQL, Encapsulation: TypeTLS}, // PostgreSQL
|
||||||
|
"pop3": {Type: TypePOP3, Encapsulation: TypeTLS}, // POP3
|
||||||
|
"radius/1.0": {Type: TypeRADIUS, Encapsulation: TypeTLS, Version: Version{Major: 1, Minor: 0, Patch: -1}}, // RADIUS/1.0
|
||||||
|
"radius/1.1": {Type: TypeRADIUS, Encapsulation: TypeTLS, Version: Version{Major: 1, Minor: 1, Patch: -1}}, // RADIUS/1.1
|
||||||
|
"smb": {Type: TypeSMB, Encapsulation: TypeTLS, Version: Version{Major: 2, Minor: -1, Patch: -1}}, // SMB2
|
||||||
|
"stun.nat-discovery": {Type: TypeSTUN, Encapsulation: TypeTLS}, // NAT discovery using Session Traversal Utilities for NAT (STUN)
|
||||||
|
"stun.turn": {Type: TypeSTUN, Encapsulation: TypeTLS}, // Traversal Using Relays around NAT (TURN)
|
||||||
|
"sunrpc": {Type: TypeSunRPC, Encapsulation: TypeTLS}, // SunRPC
|
||||||
|
"webrtc": {Type: TypeWebRTC, Encapsulation: TypeTLS}, // WebRTC Media and Data
|
||||||
|
"xmpp-client": {Type: TypeXMPP, Encapsulation: TypeTLS}, // XMPP jabber:client namespace
|
||||||
|
"xmpp-server": {Type: TypeXMPP, Encapsulation: TypeTLS}, // XMPP jabber:server namespace
|
||||||
|
}
|
||||||
|
@@ -76,6 +76,45 @@ func TestDetectTLS(t *testing.T) {
|
|||||||
0x03, 0x02, // Client Version: TLS 1.1 (Major=3, Minor=2)
|
0x03, 0x02, // Client Version: TLS 1.1 (Major=3, Minor=2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A valid TLS 1.1 ServerHello
|
||||||
|
tls11ServerHello := []byte{
|
||||||
|
// --- Record Layer (5 bytes) ---
|
||||||
|
0x16, // Content Type: Handshake (22)
|
||||||
|
0x03, 0x02, // Version: TLS 1.1 (for compatibility in a 1.3 hello)
|
||||||
|
0x00, 0x40, // Length of the handshake message below (64 bytes)
|
||||||
|
|
||||||
|
// --- Handshake Protocol: ServerHello (64 bytes) ---
|
||||||
|
0x02, // Handshake Type: ServerHello (2)
|
||||||
|
0x00, 0x00, 0x3c, // Length of the rest of the message (60 bytes)
|
||||||
|
0x03, 0x02, // Server Version: TLS 1.1 (Major=3, Minor=2)
|
||||||
|
|
||||||
|
// Random (32 bytes)
|
||||||
|
0xb7, 0xa8, 0xdf, 0xd5, 0x17, 0xb1, 0x50, 0xb4,
|
||||||
|
0x28, 0xb7, 0xf6, 0xf3, 0xb9, 0x83, 0xcf, 0x9f,
|
||||||
|
0x31, 0x55, 0x79, 0x1f, 0x3b, 0x07, 0x6d, 0x17,
|
||||||
|
0x44, 0x4f, 0x57, 0x4e, 0x47, 0x52, 0x44, 0x00,
|
||||||
|
|
||||||
|
// Session ID
|
||||||
|
0x00, // Session ID Length: 0 (new session)
|
||||||
|
|
||||||
|
0xc0, 0x09, // Cipher Suite
|
||||||
|
0x00, // Compression Method
|
||||||
|
|
||||||
|
// --- Extensions (20 bytes) ---
|
||||||
|
0x00, 0x14, // Extensions Length: 20 bytes
|
||||||
|
0xff, 0x01, // Extension Type: renegotiation_info
|
||||||
|
0x00, 0x01, // Extension Length: 1 byte
|
||||||
|
0x00, // Renegotiation Info Length: 0 bytes
|
||||||
|
0x00, 0x10, // Extension Type: alpn
|
||||||
|
0x00, 0x05, // Extension Length: 5 bytes
|
||||||
|
0x00, 0x03, // ALPN Extension Length: 3 bytes
|
||||||
|
0x02, 'h', '2',
|
||||||
|
0x0, 0x0b, // Extension Type: ec_points_format
|
||||||
|
0x0, 0x02, // Extension Length: 2 bytes
|
||||||
|
0x01, // EC Points Format Length: 1 byte
|
||||||
|
0x00, // EC Point Format: uncompressed
|
||||||
|
}
|
||||||
|
|
||||||
// A synthesized TLSv1.2 ClientHello
|
// A synthesized TLSv1.2 ClientHello
|
||||||
tls12ClientHello := []byte{
|
tls12ClientHello := []byte{
|
||||||
// --- Record Layer (5 bytes) ---
|
// --- Record Layer (5 bytes) ---
|
||||||
@@ -129,35 +168,62 @@ func TestDetectTLS(t *testing.T) {
|
|||||||
|
|
||||||
// A valid TLS 1.3 Client Hello (captured from a real connection)
|
// A valid TLS 1.3 Client Hello (captured from a real connection)
|
||||||
tls13ClientHello := []byte{
|
tls13ClientHello := []byte{
|
||||||
|
// --- Record Layer (5 bytes) ---
|
||||||
0x16, // Content Type: Handshake (22)
|
0x16, // Content Type: Handshake (22)
|
||||||
0x03, 0x01, // Version: TLS 1.0 (for compatibility in a 1.3 hello)
|
0x03, 0x01, // Version: TLS 1.0 (for compatibility in a 1.3 hello)
|
||||||
0x01, 0x3a, // Length
|
0x01, 0x3a, // Length
|
||||||
0x01, 0x00, 0x01, 0x36, 0x03, 0x03, 0xb1,
|
|
||||||
0x40, 0xd3, 0xf1, 0x7d, 0xa3, 0xb8, 0x33, 0xac, 0xad, 0x21, 0x79, 0x9c,
|
// --- Handshake Protocol: ClientHello ---
|
||||||
0xbe, 0x39, 0x96, 0x08, 0x49, 0x3b, 0x53, 0x75, 0xa0, 0x1b, 0xee, 0x6e,
|
0x01, // Handshake Type: ClientHello (1)
|
||||||
0x6a, 0xbe, 0x6c, 0x41, 0xdf, 0x6c, 0xf4, 0x20, 0xa4, 0xaa, 0x0c, 0xca,
|
0x00, 0x01, 0x36, // Length of the rest of the message
|
||||||
0xd4, 0x37, 0x76, 0x5f, 0x49, 0xc6, 0x06, 0x9b, 0xac, 0x90, 0x89, 0x76,
|
0x03, 0x03, // Client Version: TLS 1.2 (Major=3, Minor=3)
|
||||||
0x1c, 0xc7, 0xc4, 0x12, 0xb4, 0x4a, 0xe0, 0x27, 0x72, 0x89, 0x97, 0x85,
|
|
||||||
0x76, 0xf8, 0xc8, 0x83, 0x00, 0x62, 0x13, 0x03, 0x13, 0x02, 0x13, 0x01,
|
// Random (32 bytes)
|
||||||
0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0xaa, 0xc0, 0x30, 0xc0, 0x2c, 0xc0, 0x28,
|
0xb1, 0x40, 0xd3, 0xf1, 0x7d, 0xa3, 0xb8, 0x33,
|
||||||
0xc0, 0x24, 0xc0, 0x14, 0xc0, 0x0a, 0x00, 0x9f, 0x00, 0x6b, 0x00, 0x39,
|
0xac, 0xad, 0x21, 0x79, 0x9c, 0xbe, 0x39, 0x96,
|
||||||
0xff, 0x85, 0x00, 0xc4, 0x00, 0x88, 0x00, 0x81, 0x00, 0x9d, 0x00, 0x3d,
|
0x08, 0x49, 0x3b, 0x53, 0x75, 0xa0, 0x1b, 0xee,
|
||||||
0x00, 0x35, 0x00, 0xc0, 0x00, 0x84, 0xc0, 0x2f, 0xc0, 0x2b, 0xc0, 0x27,
|
0x6e, 0x6a, 0xbe, 0x6c, 0x41, 0xdf, 0x6c, 0xf4,
|
||||||
0xc0, 0x23, 0xc0, 0x13, 0xc0, 0x09, 0x00, 0x9e, 0x00, 0x67, 0x00, 0x33,
|
|
||||||
0x00, 0xbe, 0x00, 0x45, 0x00, 0x9c, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0xba,
|
// Session ID
|
||||||
0x00, 0x41, 0xc0, 0x11, 0xc0, 0x07, 0x00, 0x05, 0x00, 0x04, 0xc0, 0x12,
|
0x20, // Session ID Length: 32
|
||||||
0xc0, 0x08, 0x00, 0x16, 0x00, 0x0a, 0x00, 0xff, 0x01, 0x00, 0x00, 0x8b,
|
0xa4, 0xaa, 0x0c, 0xca, 0xd4, 0x37, 0x76, 0x5f, //
|
||||||
0x00, 0x2b, 0x00, 0x09, 0x08, 0x03, 0x04, 0x03, 0x03, 0x03, 0x02, 0x03,
|
0x49, 0xc6, 0x06, 0x9b, 0xac, 0x90, 0x89, 0x76, //
|
||||||
0x01, 0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0x2c,
|
0x1c, 0xc7, 0xc4, 0x12, 0xb4, 0x4a, 0xe0, 0x27, //
|
||||||
0x4b, 0xaa, 0xb4, 0xb3, 0xc8, 0x93, 0xcd, 0x5c, 0x24, 0xb9, 0x9b, 0xd4,
|
0x72, 0x89, 0x97, 0x85, 0x76, 0xf8, 0xc8, 0x83, //
|
||||||
0x59, 0x04, 0xfe, 0x69, 0xaf, 0x68, 0xb9, 0xa6, 0x36, 0xbb, 0xab, 0x87,
|
0x00, 0x62, 0x13, 0x03, 0x13, 0x02, 0x13, 0x01, //
|
||||||
0xfa, 0x15, 0x59, 0xea, 0xdd, 0x38, 0x68, 0x00, 0x00, 0x00, 0x0e, 0x00,
|
|
||||||
0x0c, 0x00, 0x00, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73,
|
// Cipher Suites
|
||||||
0x74, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00,
|
0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0xaa, 0xc0, 0x30,
|
||||||
0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, 0x0d, 0x00,
|
0xc0, 0x2c, 0xc0, 0x28, 0xc0, 0x24, 0xc0, 0x14,
|
||||||
0x18, 0x00, 0x16, 0x08, 0x06, 0x06, 0x01, 0x06, 0x03, 0x08, 0x05, 0x05,
|
0xc0, 0x0a, 0x00, 0x9f, 0x00, 0x6b, 0x00, 0x39,
|
||||||
0x01, 0x05, 0x03, 0x08, 0x04, 0x04, 0x01, 0x04, 0x03, 0x02, 0x01, 0x02,
|
0xff, 0x85, 0x00, 0xc4, 0x00, 0x88, 0x00, 0x81,
|
||||||
0x03, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68,
|
0x00, 0x9d, 0x00, 0x3d, 0x00, 0x35, 0x00, 0xc0,
|
||||||
|
0x00, 0x84, 0xc0, 0x2f, 0xc0, 0x2b, 0xc0, 0x27,
|
||||||
|
0xc0, 0x23, 0xc0, 0x13, 0xc0, 0x09, 0x00, 0x9e,
|
||||||
|
0x00, 0x67, 0x00, 0x33, 0x00, 0xbe, 0x00, 0x45,
|
||||||
|
0x00, 0x9c, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0xba,
|
||||||
|
0x00, 0x41, 0xc0, 0x11, 0xc0, 0x07, 0x00, 0x05,
|
||||||
|
0x00, 0x04, 0xc0, 0x12, 0xc0, 0x08, 0x00, 0x16,
|
||||||
|
0x00, 0x0a, 0x00, 0xff, 0x01, 0x00, 0x00, 0x8b,
|
||||||
|
0x00, 0x2b, 0x00, 0x09, 0x08, 0x03, 0x04, 0x03,
|
||||||
|
0x03, 0x03, 0x02, 0x03, 0x01, 0x00, 0x33, 0x00,
|
||||||
|
0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0x2c,
|
||||||
|
0x4b, 0xaa, 0xb4, 0xb3, 0xc8, 0x93, 0xcd, 0x5c,
|
||||||
|
0x24, 0xb9, 0x9b, 0xd4, 0x59, 0x04, 0xfe, 0x69,
|
||||||
|
0xaf, 0x68, 0xb9, 0xa6, 0x36, 0xbb, 0xab, 0x87,
|
||||||
|
0xfa, 0x15, 0x59, 0xea, 0xdd, 0x38, 0x68, 0x00,
|
||||||
|
0x00, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09,
|
||||||
|
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73,
|
||||||
|
0x74, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00,
|
||||||
|
0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x1d, 0x00,
|
||||||
|
0x17, 0x00, 0x18, 0x00, 0x19, 0x00, 0x0d, 0x00,
|
||||||
|
0x18, 0x00, 0x16, 0x08, 0x06, 0x06, 0x01, 0x06,
|
||||||
|
0x03, 0x08, 0x05, 0x05,
|
||||||
|
|
||||||
|
// Compression Methods, etc.
|
||||||
|
0x01, 0x05, 0x03, 0x08, 0x04, 0x04, 0x01, 0x04,
|
||||||
|
0x03, 0x02, 0x01, 0x02, 0x03, 0x00, 0x10, 0x00,
|
||||||
|
0x0e, 0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68,
|
||||||
0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31,
|
0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,15 +238,23 @@ func TestDetectTLS(t *testing.T) {
|
|||||||
Direction: Client,
|
Direction: Client,
|
||||||
Data: sslV3ClientHello,
|
Data: sslV3ClientHello,
|
||||||
DstPort: 443,
|
DstPort: 443,
|
||||||
WantProto: ProtocolSSL,
|
WantType: TypeSSL,
|
||||||
WantConfidence: .95,
|
WantConfidence: .95,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "TLS 1.1",
|
Name: "TLS 1.1 ClientHello",
|
||||||
Direction: Client,
|
Direction: Client,
|
||||||
Data: tls11ClientHello,
|
Data: tls11ClientHello,
|
||||||
DstPort: 443,
|
DstPort: 443,
|
||||||
WantProto: ProtocolTLS,
|
WantType: TypeTLS,
|
||||||
|
WantConfidence: .95,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "TLS 1.1 ServerHello",
|
||||||
|
Direction: Server,
|
||||||
|
Data: tls11ServerHello,
|
||||||
|
SrcPort: 443,
|
||||||
|
WantType: TypeHTTP,
|
||||||
WantConfidence: .95,
|
WantConfidence: .95,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -188,7 +262,7 @@ func TestDetectTLS(t *testing.T) {
|
|||||||
Direction: Client,
|
Direction: Client,
|
||||||
Data: tls12ClientHello,
|
Data: tls12ClientHello,
|
||||||
DstPort: 443,
|
DstPort: 443,
|
||||||
WantProto: ProtocolTLS,
|
WantType: TypeHTTP,
|
||||||
WantConfidence: .95,
|
WantConfidence: .95,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -196,7 +270,7 @@ func TestDetectTLS(t *testing.T) {
|
|||||||
Direction: Client,
|
Direction: Client,
|
||||||
Data: tls13ClientHello,
|
Data: tls13ClientHello,
|
||||||
DstPort: 443,
|
DstPort: 443,
|
||||||
WantProto: ProtocolTLS,
|
WantType: TypeHTTP,
|
||||||
WantConfidence: .95,
|
WantConfidence: .95,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -225,7 +299,7 @@ func TestDetectTLS(t *testing.T) {
|
|||||||
Direction: Client,
|
Direction: Client,
|
||||||
Data: tls11ClientHelloPartial,
|
Data: tls11ClientHelloPartial,
|
||||||
DstPort: 443,
|
DstPort: 443,
|
||||||
WantProto: ProtocolTLS,
|
WantType: TypeTLS,
|
||||||
WantConfidence: .50,
|
WantConfidence: .50,
|
||||||
},
|
},
|
||||||
}, tests...))
|
}, tests...))
|
||||||
|
@@ -16,7 +16,7 @@ type testCase struct {
|
|||||||
Data []byte
|
Data []byte
|
||||||
SrcPort int
|
SrcPort int
|
||||||
DstPort int
|
DstPort int
|
||||||
WantProto string
|
WantType string
|
||||||
WantConfidence float64
|
WantConfidence float64
|
||||||
WantError error
|
WantError error
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,7 @@ func testRunner(t *testing.T, tests []*testCase) {
|
|||||||
} else if test.WantError != nil {
|
} else if test.WantError != nil {
|
||||||
t.Fatalf("Detect(%s, %s, %d, %d) returned protocol %q version %s, expected error %q",
|
t.Fatalf("Detect(%s, %s, %d, %d) returned protocol %q version %s, expected error %q",
|
||||||
test.Direction, testBytesSample(test.Data, 8), test.SrcPort, test.DstPort,
|
test.Direction, testBytesSample(test.Data, 8), test.SrcPort, test.DstPort,
|
||||||
proto.Name, proto.Version, test.WantError)
|
proto.Type, proto.Version, test.WantError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,16 +60,16 @@ func testRunner(t *testing.T, tests []*testCase) {
|
|||||||
if proto == nil {
|
if proto == nil {
|
||||||
t.Fatalf("Detect(%s, %s, %d, %d) returned nil, expected protocol %q",
|
t.Fatalf("Detect(%s, %s, %d, %d) returned nil, expected protocol %q",
|
||||||
test.Direction, testBytesSample(test.Data, 8), test.SrcPort, test.DstPort,
|
test.Direction, testBytesSample(test.Data, 8), test.SrcPort, test.DstPort,
|
||||||
test.WantProto)
|
test.WantType)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("Detect(%s, %s, %d, %d) returned protocol %q version %s with confidence %g%%",
|
t.Logf("Detect(%s, %s, %d, %d) returned protocol %s with confidence %g%%",
|
||||||
test.Direction, testBytesSample(test.Data, 4), test.SrcPort, test.DstPort,
|
test.Direction, testBytesSample(test.Data, 4), test.SrcPort, test.DstPort,
|
||||||
proto.Name, proto.Version, confidence*100)
|
proto, confidence*100)
|
||||||
|
|
||||||
if proto.Name != test.WantProto {
|
if proto.Type != test.WantType {
|
||||||
t.Errorf("Expected protocol %q", test.WantProto)
|
t.Errorf("Expected protocol %q, got %q", test.WantType, proto.Type)
|
||||||
}
|
}
|
||||||
if !testAlmostEqual(confidence, test.WantConfidence) {
|
if !testAlmostEqual(confidence, test.WantConfidence) {
|
||||||
t.Errorf("Expected confidence %g%%", test.WantConfidence*100)
|
t.Errorf("Expected confidence %g%%", test.WantConfidence*100)
|
||||||
|
@@ -7,21 +7,63 @@ import (
|
|||||||
|
|
||||||
// Protocols supported by this package.
|
// Protocols supported by this package.
|
||||||
const (
|
const (
|
||||||
ProtocolDNS = "dns"
|
TypeACME = "ACME"
|
||||||
ProtocolHTTP = "http"
|
TypeCoAP = "CoAP"
|
||||||
ProtocolMQTT = "mqtt"
|
TypeDNS = "DNS"
|
||||||
ProtocolMySQL = "mysql"
|
TypeFTP = "FTP"
|
||||||
ProtocolPostgreSQL = "postgresql"
|
TypeHTTP = "HTTP"
|
||||||
ProtocolSSH = "ssh"
|
TypeIRC = "IRC"
|
||||||
ProtocolSSL = "ssl"
|
TypeIMAP = "IMAP"
|
||||||
ProtocolTLS = "tls"
|
TypeJabber = TypeXMPP
|
||||||
|
TypeManageSieve = "ManageSieve"
|
||||||
|
TypeMosquitto = TypeMQTT
|
||||||
|
TypeMQTT = "MQTT"
|
||||||
|
TypeMySQL = "MySQL"
|
||||||
|
TypeNNTP = "NNTP"
|
||||||
|
TypePOP3 = "POP3"
|
||||||
|
TypePgSQL = TypePostgreSQL
|
||||||
|
TypePostgreSQL = "PostgreSQL"
|
||||||
|
TypeRADIUS = "RADIUS"
|
||||||
|
TypeSamba = TypeSMB
|
||||||
|
TypeSIP = "SIP"
|
||||||
|
TypeSMB = "SMB"
|
||||||
|
TypeSSH = "SSH"
|
||||||
|
TypeSSL = "SSL"
|
||||||
|
TypeSTUN = "STUN"
|
||||||
|
TypeSunRPC = "SunRPC"
|
||||||
|
TypeTLS = "TLS"
|
||||||
|
TypeWebRTC = "WebRTC"
|
||||||
|
TypeXMPP = "XMPP"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Protocol description.
|
||||||
type Protocol struct {
|
type Protocol struct {
|
||||||
Name string
|
// Type of protocol, usually one of the constants defined in this package.
|
||||||
|
Type string
|
||||||
|
|
||||||
|
// Encapsulation type, usually one of the constants defined in this package.
|
||||||
|
//
|
||||||
|
// Empty if there is no encapsulation.
|
||||||
|
Encapsulation string
|
||||||
|
|
||||||
|
// Version of the protocol. Unknown versions are marked with [UnknownVersion].
|
||||||
Version Version
|
Version Version
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (proto Protocol) String() string {
|
||||||
|
var s string
|
||||||
|
if proto.Encapsulation != "" {
|
||||||
|
s = proto.Type + " (over " + proto.Encapsulation + ")"
|
||||||
|
} else {
|
||||||
|
s = proto.Type
|
||||||
|
}
|
||||||
|
if proto.Version == UnknownVersion {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return s + " version " + proto.Version.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version of a protocol.
|
||||||
type Version struct {
|
type Version struct {
|
||||||
Major int
|
Major int
|
||||||
Minor int
|
Minor int
|
||||||
@@ -29,9 +71,15 @@ type Version struct {
|
|||||||
Extra string
|
Extra string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnknownVersion
|
||||||
|
var UnknownVersion Version
|
||||||
|
|
||||||
func (v Version) String() string {
|
func (v Version) String() string {
|
||||||
|
if v == UnknownVersion {
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
p := make([]string, 0, 3)
|
p := make([]string, 0, 3)
|
||||||
if v.Major >= 0 {
|
|
||||||
p = append(p, strconv.Itoa(v.Major))
|
p = append(p, strconv.Itoa(v.Major))
|
||||||
if v.Minor >= 0 {
|
if v.Minor >= 0 {
|
||||||
p = append(p, strconv.Itoa(v.Minor))
|
p = append(p, strconv.Itoa(v.Minor))
|
||||||
@@ -39,7 +87,6 @@ func (v Version) String() string {
|
|||||||
p = append(p, strconv.Itoa(v.Patch))
|
p = append(p, strconv.Itoa(v.Patch))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
s := strings.Join(p, ".")
|
s := strings.Join(p, ".")
|
||||||
if v.Extra != "" {
|
if v.Extra != "" {
|
||||||
return s + "-" + v.Extra
|
return s + "-" + v.Extra
|
||||||
|
32
tls.go
32
tls.go
@@ -19,7 +19,7 @@ type TLSExtension struct {
|
|||||||
type TLSRecord struct {
|
type TLSRecord struct {
|
||||||
Raw []byte
|
Raw []byte
|
||||||
Type uint8
|
Type uint8
|
||||||
Version uint16
|
Version TLSVersion
|
||||||
Length uint16
|
Length uint16
|
||||||
Data []byte
|
Data []byte
|
||||||
}
|
}
|
||||||
@@ -30,26 +30,24 @@ func DecodeTLSRecord(data []byte) (*TLSRecord, error) {
|
|||||||
record = &TLSRecord{Raw: data}
|
record = &TLSRecord{Raw: data}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var version uint16
|
||||||
if !stream.ReadUint8(&record.Type) ||
|
if !stream.ReadUint8(&record.Type) ||
|
||||||
!stream.ReadUint16(&record.Version) ||
|
!stream.ReadUint16(&version) ||
|
||||||
!stream.ReadUint16(&record.Length) {
|
!stream.ReadUint16(&record.Length) {
|
||||||
return nil, DecodeError{
|
return nil, DecodeError{
|
||||||
Reason: "invalid TLS record header",
|
Reason: "invalid TLS record header",
|
||||||
Err: io.ErrUnexpectedEOF,
|
Err: io.ErrUnexpectedEOF,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
record.Version = TLSVersion(version)
|
||||||
|
|
||||||
if !stream.ReadBytes(&record.Data, int(record.Length)) {
|
if !stream.ReadBytes(&record.Data, int(record.Length)) {
|
||||||
return nil, DecodeError{
|
return nil, DecodeError{
|
||||||
Reason: "invalid TLS record data",
|
Reason: "invalid TLS record data",
|
||||||
Err: io.ErrUnexpectedEOF,
|
Err: io.ErrUnexpectedEOF,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !stream.Empty() {
|
|
||||||
return nil, DecodeError{
|
|
||||||
Reason: "extraneous data after TLS record",
|
|
||||||
Err: ErrInvalid,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return record, nil
|
return record, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,12 +164,6 @@ func DecodeTLSClientHello(data []byte) (*TLSClientHello, error) {
|
|||||||
Err: io.ErrUnexpectedEOF,
|
Err: io.ErrUnexpectedEOF,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !record.Empty() {
|
|
||||||
return nil, DecodeError{
|
|
||||||
Reason: "extraneous TLS extension data",
|
|
||||||
Err: io.ErrUnexpectedEOF,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for !extensions.Empty() {
|
for !extensions.Empty() {
|
||||||
var (
|
var (
|
||||||
@@ -260,6 +252,7 @@ type TLSServerHello struct {
|
|||||||
CipherSuite uint16
|
CipherSuite uint16
|
||||||
CompressionMethod uint8
|
CompressionMethod uint8
|
||||||
Extensions []TLSExtension
|
Extensions []TLSExtension
|
||||||
|
ALPNProtocols []string // RFC 7301, Section 3.1
|
||||||
}
|
}
|
||||||
|
|
||||||
func DecodeTLSServerHello(data []byte) (*TLSServerHello, error) {
|
func DecodeTLSServerHello(data []byte) (*TLSServerHello, error) {
|
||||||
@@ -347,12 +340,6 @@ func DecodeTLSServerHello(data []byte) (*TLSServerHello, error) {
|
|||||||
Err: io.ErrUnexpectedEOF,
|
Err: io.ErrUnexpectedEOF,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !record.Empty() {
|
|
||||||
return nil, DecodeError{
|
|
||||||
Reason: "extraneous TLS extension data",
|
|
||||||
Err: io.ErrUnexpectedEOF,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for !extensions.Empty() {
|
for !extensions.Empty() {
|
||||||
var (
|
var (
|
||||||
@@ -366,6 +353,11 @@ func DecodeTLSServerHello(data []byte) (*TLSServerHello, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
hello.Extensions = append(hello.Extensions, extension)
|
hello.Extensions = append(hello.Extensions, extension)
|
||||||
|
|
||||||
|
switch extension.Type {
|
||||||
|
case tlsExtensionALPN:
|
||||||
|
_ = readTLSALPN(extensionData, &hello.ALPNProtocols)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return hello, nil
|
return hello, nil
|
||||||
|
42
tls_test.go
42
tls_test.go
@@ -72,6 +72,39 @@ func TestDecodeTLSServerHello(t *testing.T) {
|
|||||||
t.Fatalf("failed to decode test ServerHello: %s", err)
|
t.Fatalf("failed to decode test ServerHello: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tls11ServerHello := []byte{
|
||||||
|
// --- Handshake Protocol: ServerHello (64 bytes) ---
|
||||||
|
0x02, // Handshake Type: ServerHello (2)
|
||||||
|
0x00, 0x00, 0x3c, // Length of the rest of the message (60 bytes)
|
||||||
|
0x03, 0x02, // Server Version: TLS 1.1 (Major=3, Minor=2)
|
||||||
|
|
||||||
|
// Random (32 bytes)
|
||||||
|
0xb7, 0xa8, 0xdf, 0xd5, 0x17, 0xb1, 0x50, 0xb4,
|
||||||
|
0x28, 0xb7, 0xf6, 0xf3, 0xb9, 0x83, 0xcf, 0x9f,
|
||||||
|
0x31, 0x55, 0x79, 0x1f, 0x3b, 0x07, 0x6d, 0x17,
|
||||||
|
0x44, 0x4f, 0x57, 0x4e, 0x47, 0x52, 0x44, 0x00,
|
||||||
|
|
||||||
|
// Session ID
|
||||||
|
0x00, // Session ID Length: 0 (new session)
|
||||||
|
|
||||||
|
0xc0, 0x09, // Cipher Suite
|
||||||
|
0x00, // Compression Method
|
||||||
|
|
||||||
|
// --- Extensions (20 bytes) ---
|
||||||
|
0x00, 0x14, // Extensions Length: 20 bytes
|
||||||
|
0xff, 0x01, // Extension Type: renegotiation_info
|
||||||
|
0x00, 0x01, // Extension Length: 1 byte
|
||||||
|
0x00, // Renegotiation Info Length: 0 bytes
|
||||||
|
0x00, 0x10, // Extension Type: alpn
|
||||||
|
0x00, 0x05, // Extension Length: 5 bytes
|
||||||
|
0x00, 0x03, // ALPN Extension Length: 3 bytes
|
||||||
|
0x02, 'h', '2',
|
||||||
|
0x0, 0x0b, // Extension Type: ec_points_format
|
||||||
|
0x0, 0x02, // Extension Length: 2 bytes
|
||||||
|
0x01, // EC Points Format Length: 1 byte
|
||||||
|
0x00, // EC Point Format: uncompressed
|
||||||
|
}
|
||||||
|
|
||||||
t.Run("Server Hello", func(t *testing.T) {
|
t.Run("Server Hello", func(t *testing.T) {
|
||||||
hello, err := DecodeTLSServerHello(serverHelloBytes)
|
hello, err := DecodeTLSServerHello(serverHelloBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -80,6 +113,15 @@ func TestDecodeTLSServerHello(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Logf("%#+v", hello)
|
t.Logf("%#+v", hello)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("TLS 1.1 Server Hello", func(t *testing.T) {
|
||||||
|
hello, err := DecodeTLSServerHello(tls11ServerHello)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Logf("%#+v", hello)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDecodeHexString(s string) ([]byte, error) {
|
func testDecodeHexString(s string) ([]byte, error) {
|
||||||
|
Reference in New Issue
Block a user