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