Files
stun/attributes.go
2016-05-16 20:56:43 +03:00

331 lines
9.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package stun
import (
"encoding/binary"
"fmt"
"net"
"strconv"
"github.com/pkg/errors"
)
// blank is just blank string and exists just because it is ugly to keep it
// in code.
const blank = ""
// Attributes is list of message attributes.
type Attributes []Attribute
var (
// BlankAttribute is attribute that is returned by
// Attributes.Get if nothing found.
BlankAttribute = Attribute{}
)
// Get returns first attribute from list which match AttrType. If nothing
// found, it returns blank attribute.
func (a Attributes) Get(t AttrType) Attribute {
for _, candidate := range a {
if candidate.Type == t {
return candidate
}
}
return BlankAttribute
}
// AttrType is attribute type.
type AttrType uint16
// Attributes from comprehension-required range (0x0000-0x7FFF).
const (
AttrMappedAddress AttrType = 0x0001 // MAPPED-ADDRESS
AttrUsername AttrType = 0x0006 // USERNAME
AttrMessageIntegrity AttrType = 0x0008 // MESSAGE-INTEGRITY
AttrErrorCode AttrType = 0x0009 // ERROR-CODE
AttrUnknownAttributes AttrType = 0x000A // UNKNOWN-ATTRIBUTES
AttrRealm AttrType = 0x0014 // REALM
AttrNonce AttrType = 0x0015 // NONCE
AttrXORMappedAddress AttrType = 0x0020 // XOR-MAPPED-ADDRESS
)
// Attributes from comprehension-optional range (0x8000-0xFFFF).
const (
AttrSoftware AttrType = 0x8022 // SOFTWARE
AttrAlternateServer AttrType = 0x8023 // ALTERNATE-SERVER
AttrFingerprint AttrType = 0x8028 // FINGERPRINT
)
// Attributes from RFC 5245 ICE.
const (
AttrPriority AttrType = 0x0024 // PRIORITY
AttrUseCandidate AttrType = 0x0025 // USE-CANDIDATE
AttrICEControlled AttrType = 0x8029 // ICE-CONTROLLED
AttrICEControlling AttrType = 0x802A // ICE-CONTROLLING
)
// Attributes from RFC 5766 TURN.
const (
AttrChannelNumber AttrType = 0x000C // CHANNEL-NUMBER
AttrLifetime AttrType = 0x000D // LIFETIME
AttrXORPeerAddress AttrType = 0x0012 // XOR-PEER-ADDRESS
AttrData AttrType = 0x0013 // DATA
AttrXORRelayedAddress AttrType = 0x0016 // XOR-RELAYED-ADDRESS
AttrEvenPort AttrType = 0x0018 // EVEN-PORT
AttrRequestedTransport AttrType = 0x0019 // REQUESTED-TRANSPORT
AttrDontFragment AttrType = 0x001A // DONT-FRAGMENT
AttrReservationToken AttrType = 0x0022 // RESERVATION-TOKEN
)
// Value returns uint16 representation of attribute type.
func (t AttrType) Value() uint16 {
return uint16(t)
}
func (t AttrType) String() string {
switch t {
case AttrMappedAddress:
return "MAPPED-ADDRESS"
case AttrUsername:
return "USERNAME"
case AttrErrorCode:
return "ERROR-CODE"
case AttrMessageIntegrity:
return "MESSAGE-INTEGRITY"
case AttrUnknownAttributes:
return "UNKNOWN-ATTRIBUTES"
case AttrRealm:
return "REALM"
case AttrNonce:
return "NONCE"
case AttrXORMappedAddress:
return "XOR-MAPPED-ADDRESS"
case AttrSoftware:
return "SOFTWARE"
case AttrAlternateServer:
return "ALTERNATE-SERVER"
case AttrFingerprint:
return "FINGERPRINT"
case AttrPriority:
return "PRIORITY"
case AttrUseCandidate:
return "USE-CANDIDATE"
case AttrICEControlled:
return "ICE-CONTROLLED"
case AttrICEControlling:
return "ICE-CONTROLLING"
case AttrChannelNumber:
return "CHANNEL-NUMBER"
case AttrLifetime:
return "LIFETIME"
case AttrXORPeerAddress:
return "XOR-PEER-ADDRESS"
case AttrData:
return "DATA"
case AttrXORRelayedAddress:
return "XOR-RELAYED-ADDRESS"
case AttrEvenPort:
return "EVEN-PORT"
case AttrRequestedTransport:
return "REQUESTED-TRANSPORT"
case AttrDontFragment:
return "DONT-FRAGMENT"
case AttrReservationToken:
return "RESERVATION-TOKEN"
default:
// just return hex representation of unknown attribute type
return "0x" + strconv.FormatUint(uint64(t), 16)
}
}
// Attribute is a Type-Length-Value (TLV) object that
// can be added to a STUN message. Attributes are divided into two
// types: comprehension-required and comprehension-optional. STUN
// agents can safely ignore comprehension-optional attributes they
// don't understand, but cannot successfully process a message if it
// contains comprehension-required attributes that are not
// understood.
type Attribute struct {
Type AttrType
Length uint16
Value []byte
}
// Equal returns true if a == b.
func (a Attribute) Equal(b Attribute) bool {
if a.Type != b.Type {
return false
}
if a.Length != b.Length {
return false
}
if len(b.Value) != len(a.Value) {
return false
}
for i, v := range a.Value {
if b.Value[i] != v {
return false
}
}
return true
}
func (a Attribute) String() string {
return fmt.Sprintf("%s: %x", a.Type, a.Value)
}
// getAttrValue returns byte slice that represents attribute value,
// and if there is no value found, error returned.
func (m *Message) getAttrValue(t AttrType) ([]byte, error) {
v := m.Attributes.Get(t).Value
if len(v) == 0 {
return nil, errors.Wrap(ErrAttributeNotFound, "failed to find")
}
return v, nil
}
// AddSoftwareBytes adds SOFTWARE attribute with value from byte slice.
func (m *Message) AddSoftwareBytes(software []byte) {
m.Add(AttrSoftware, software)
}
// AddSoftware adds SOFTWARE attribute with value from string.
func (m *Message) AddSoftware(software string) {
m.Add(AttrSoftware, []byte(software))
}
// GetSoftwareBytes returns SOFTWARE attribute value in byte slice.
// If not found, returns nil.
func (m *Message) GetSoftwareBytes() []byte {
return m.Attributes.Get(AttrSoftware).Value
}
// GetSoftware returns SOFTWARE attribute value in string.
// If not found, returns blank string.
func (m *Message) GetSoftware() string {
v := m.GetSoftwareBytes()
if len(v) == 0 {
return blank
}
return string(v)
}
// Address family values.
const (
FamilyIPv4 byte = 0x01
FamilyIPv6 byte = 0x02
)
// AddXORMappedAddress adds XOR MAPPED ADDRESS attribute to message.
func (m *Message) AddXORMappedAddress(ip net.IP, port int) {
// X-Port is computed by taking the mapped port in host byte order,
// XORing it with the most significant 16 bits of the magic cookie, and
// then the converting the result to network byte order.
value := make([]byte, 32+128)
value[0] = 0 // first 8 bits are zeroes
family := FamilyIPv4
if len(ip) == net.IPv6len {
family = FamilyIPv6
}
binary.BigEndian.PutUint16(value[0:2], uint16(family))
port ^= magicCookie >> 16
binary.BigEndian.PutUint16(value[2:4], uint16(port))
xorValue := make([]byte, 128)
binary.BigEndian.PutUint32(xorValue[0:4], magicCookie)
copy(xorValue[4:], m.TransactionID[:])
xorBytes(value[4:4+len(ip)], ip, xorValue)
m.Add(AttrXORMappedAddress, value[:4+len(ip)])
}
func (m *Message) allocBuffer(size int) []byte {
capacity := len(m.buf.B) + size
m.grow(capacity)
m.buf.B = m.buf.B[:capacity]
return m.buf.B[len(m.buf.B)-size:]
}
// GetXORMappedAddress returns ip, port from attribute and error if any.
// Value for ip is valid until Message is released or underlying buffer is
// corrupted.
func (m *Message) GetXORMappedAddress() (net.IP, int, error) {
// X-Port is computed by taking the mapped port in host byte order,
// XORing it with the most significant 16 bits of the magic cookie, and
// then the converting the result to network byte order.
v, err := m.getAttrValue(AttrXORMappedAddress)
if len(v) == 0 {
return nil, 0, errors.Wrap(err, "address not found")
}
family := byte(binary.BigEndian.Uint16(v[0:2]))
if family != FamilyIPv6 && family != FamilyIPv4 {
err := errors.Wrapf(ErrAttributeDecodeError, "bad family %d", family)
return nil, 0, err
}
ipLen := net.IPv4len
if family == FamilyIPv6 {
ipLen = net.IPv6len
}
ip := net.IP(m.allocBuffer(ipLen))
port := int(binary.BigEndian.Uint16(v[2:4])) ^ (magicCookie >> 16)
xorValue := make([]byte, 128)
binary.BigEndian.PutUint32(xorValue[0:4], magicCookie)
copy(xorValue[4:], m.TransactionID[:])
xorBytes(ip, v[4:], xorValue)
return ip, port, nil
}
// constants for ERROR-CODE encoding.
const (
errorCodeReasonStart = 4
errorCodeClassByte = 2
errorCodeNumberByte = 3
errorCodeReasonMaxB = 763
errorCodeModulo = 100
)
// AddErrorCode adds ERROR-CODE attribute to message.
//
// The reason phrase MUST be a UTF-8 [RFC 3629] encoded
// sequence of less than 128 characters (which can be as long as 763
// bytes).
func (m *Message) AddErrorCode(code int, reason string) {
value := make([]byte,
errorCodeReasonStart, errorCodeReasonMaxB+errorCodeReasonStart,
)
number := byte(code % errorCodeModulo) // error code modulo 100
class := byte(code / errorCodeModulo) // hundred digit
value[errorCodeClassByte] = class
value[errorCodeNumberByte] = number
value = append(value, reason...)
m.Add(AttrErrorCode, value)
}
// AddErrorCodeDefault is wrapper for AddErrorCode that uses recommended
// reason string from RFC. If error code is unknown, reason will be "Unknown
// Error".
func (m *Message) AddErrorCodeDefault(code int) {
m.AddErrorCode(code, ErrorCode(code).Reason())
}
// GetErrorCode returns ERROR-CODE code, reason and decode error if any.
func (m *Message) GetErrorCode() (int, []byte, error) {
v, err := m.getAttrValue(AttrErrorCode)
if err != nil {
return 0, nil, errors.Wrap(err, "error not found")
}
var (
class = uint16(v[errorCodeClassByte])
number = uint16(v[errorCodeNumberByte])
code = int(class*errorCodeModulo + number)
reason = v[errorCodeReasonStart:]
)
return code, reason, nil
}
var (
// ErrAttributeNotFound means that there is no such attribute.
ErrAttributeNotFound Error = "Attribute not found"
// ErrAttributeDecodeError means that agent is unable to decode value.
ErrAttributeDecodeError Error = "Attribute decode error"
)