Added support for STUN protocol detection
This commit is contained in:
101
protocol/detect_stun.go
Normal file
101
protocol/detect_stun.go
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
package protocol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/cryptobyte"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registerSTUN()
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerSTUN() {
|
||||||
|
Register(Both, "????\x21\x12\xa4\x42", detectSTUN)
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectSTUN(dir Direction, data []byte, srcPort, dstPort int) (proto *Protocol, confidence float64) {
|
||||||
|
// All STUN messages comprise a 20-byte header followed by zero or more attributes.
|
||||||
|
var (
|
||||||
|
stream = cryptobyte.String(data)
|
||||||
|
messageType uint16
|
||||||
|
messageLength uint16
|
||||||
|
magicCookie uint32
|
||||||
|
transactionID []byte
|
||||||
|
)
|
||||||
|
if !stream.ReadUint16(&messageType) ||
|
||||||
|
!stream.ReadUint16(&messageLength) ||
|
||||||
|
!stream.ReadUint32(&magicCookie) ||
|
||||||
|
!stream.ReadBytes(&transactionID, 12) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(stream) != int(messageLength) {
|
||||||
|
confidence -= .2
|
||||||
|
}
|
||||||
|
|
||||||
|
// The most significant 2 bits of every STUN message MUST be zeroes.
|
||||||
|
if messageType&0b11000000 != 0 {
|
||||||
|
log.Printf("type %#08b", messageType)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Magic Cookie field MUST contain the fixed value 0x2112A442 in network byte order.
|
||||||
|
if magicCookie != 0x2112A442 {
|
||||||
|
log.Printf("cookie %#04x", magicCookie)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
confidence += .5
|
||||||
|
|
||||||
|
// Decode the message class & type.
|
||||||
|
class := (messageType & 0b00000100000000) >> 7
|
||||||
|
class |= (messageType & 0b00000000010000) >> 4
|
||||||
|
method := (messageType & 0b11111000000000) >> 2
|
||||||
|
method |= (messageType & 0b00000011100000) >> 1
|
||||||
|
method |= (messageType & 0b00000000001111) >> 0
|
||||||
|
|
||||||
|
// Validate most common methods.
|
||||||
|
// TODO(maze): check method arguments to improve accuracy
|
||||||
|
switch class {
|
||||||
|
case stunRequest: // request
|
||||||
|
switch method {
|
||||||
|
case stunBinding: // binding
|
||||||
|
confidence += .2
|
||||||
|
}
|
||||||
|
|
||||||
|
case stunSuccesResponse: // success response
|
||||||
|
switch method {
|
||||||
|
case stunBinding: // binding
|
||||||
|
confidence += .2
|
||||||
|
}
|
||||||
|
|
||||||
|
case stunErrorResponse:
|
||||||
|
switch method {
|
||||||
|
case stunBinding: // binding error
|
||||||
|
confidence += .2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if stunPort[srcPort] || stunPort[dstPort] {
|
||||||
|
confidence += .2
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Protocol{
|
||||||
|
Type: TypeSTUN,
|
||||||
|
}, confidence
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
stunRequest = 0b00
|
||||||
|
stunSuccesResponse = 0b10
|
||||||
|
stunErrorResponse = 0b11
|
||||||
|
stunBinding = 0x0001
|
||||||
|
)
|
||||||
|
|
||||||
|
// IANA has updated the reference from RFC 5389 to RFC 8489 for the
|
||||||
|
// following ports in the "Service Name and Transport Protocol Port
|
||||||
|
// Number Registry".
|
||||||
|
var stunPort = map[int]bool{
|
||||||
|
3478: true,
|
||||||
|
5349: true,
|
||||||
|
}
|
85
protocol/detect_stun_test.go
Normal file
85
protocol/detect_stun_test.go
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
package protocol
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestDetectSTUN(t *testing.T) {
|
||||||
|
// A valid STUN binding request.
|
||||||
|
bindingRequest := []byte{
|
||||||
|
0x00, 0x01, // Message Type
|
||||||
|
0x00, 0x00, // Message Length (0 bytes)
|
||||||
|
0x21, 0x12, 0xa4, 0x42, // Magic Cookie
|
||||||
|
0x4b, 0x49, 0x6b, 0x72, 0x7a, 0x6a, 0x56, 0x37, 0x41, 0x61, 0x6e, 0x38, // Transaction ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// A valid STUN binding success response.
|
||||||
|
bindingSuccessResponse := []byte{
|
||||||
|
0x01, 0x01, // Message Type
|
||||||
|
0x00, 0x0c, // Message Length (12 bytes)
|
||||||
|
0x21, 0x12, 0xa4, 0x42, // Magic Cookie
|
||||||
|
0x4b, 0x49, 0x6b, 0x72, 0x7a, 0x6a, 0x56, 0x37, 0x41, 0x61, 0x6e, 0x38, // Transaction ID
|
||||||
|
// --- Attributes (12 bytes) ---
|
||||||
|
0x00, 0x01, // Attribute Type MAPPED-ADDRESS
|
||||||
|
0x00, 0x08, // Attribute Length (8 bytes)
|
||||||
|
0x00, // Reserved (always 0)
|
||||||
|
0x01, // Protocol Family: IPv4 (0)
|
||||||
|
0x11, 0xfc, // Port Number: 4604
|
||||||
|
0x46, 0xc7, 0x80, 0x2e, // IP: 70.199.128.46
|
||||||
|
}
|
||||||
|
|
||||||
|
// A truncated STUN binding success response.
|
||||||
|
truncatedBindingSuccessResponse := []byte{
|
||||||
|
0x01, 0x01, // Message Type
|
||||||
|
0x00, 0x0c, // Message Length (12 bytes)
|
||||||
|
0x21, 0x12, 0xa4, 0x42, // Magic Cookie
|
||||||
|
0x4b, 0x49, 0x6b, 0x72, 0x7a, 0x6a, 0x56, 0x37, 0x41, 0x61, 0x6e, 0x38, // Transaction ID
|
||||||
|
// --- Attributes (12 bytes) ---
|
||||||
|
0x00, 0x01, // Attribute Type MAPPED-ADDRESS
|
||||||
|
0x00, 0x08, // Attribute Length (8 bytes)
|
||||||
|
0x00, // Reserved (always 0)
|
||||||
|
/* truncated */
|
||||||
|
}
|
||||||
|
|
||||||
|
// Am invalid STUN binding request (invalid magic).
|
||||||
|
invalidMagicBindingRequest := []byte{
|
||||||
|
0x00, 0x01, // Message Type
|
||||||
|
0x00, 0x00, // Message Length (0 bytes)
|
||||||
|
0x2a, 0x12, 0xa4, 0x42, // Magic Cookie
|
||||||
|
0x4b, 0x49, 0x6b, 0x72, 0x7a, 0x6a, 0x56, 0x37, 0x41, 0x61, 0x6e, 0x38, // Transaction ID
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []*testCase{
|
||||||
|
{
|
||||||
|
Name: "STUN binding request",
|
||||||
|
Direction: Client,
|
||||||
|
Data: bindingRequest,
|
||||||
|
DstPort: 3478,
|
||||||
|
WantType: TypeSTUN,
|
||||||
|
WantConfidence: .9,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "STUN binding success response",
|
||||||
|
Direction: Server,
|
||||||
|
Data: bindingSuccessResponse,
|
||||||
|
SrcPort: 3478,
|
||||||
|
WantType: TypeSTUN,
|
||||||
|
WantConfidence: .9,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Truncated STUN binding success response",
|
||||||
|
Direction: Server,
|
||||||
|
Data: truncatedBindingSuccessResponse,
|
||||||
|
SrcPort: 3478,
|
||||||
|
WantType: TypeSTUN,
|
||||||
|
WantConfidence: .7,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Invalid magic STUN binding request",
|
||||||
|
Direction: Client,
|
||||||
|
Data: invalidMagicBindingRequest,
|
||||||
|
DstPort: 3478,
|
||||||
|
WantError: ErrUnknown,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testRunner(t, tests)
|
||||||
|
}
|
Reference in New Issue
Block a user