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,
|
||||
}
|
Reference in New Issue
Block a user