1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package stun
import (
"errors"
"fmt"
"io"
"net"
"strconv"
"github.com/pion/transport/v2/utils/xor"
)
const (
familyIPv4 uint16 = 0x01
familyIPv6 uint16 = 0x02
)
// XORMappedAddress implements XOR-MAPPED-ADDRESS attribute.
//
// RFC 5389 Section 15.2
type XORMappedAddress struct {
IP net.IP
Port int
}
func (a XORMappedAddress) String() string {
return net.JoinHostPort(a.IP.String(), strconv.Itoa(a.Port))
}
// isIPv4 returns true if ip with len of net.IPv6Len seems to be ipv4.
func isIPv4(ip net.IP) bool {
// Optimized for performance. Copied from net.IP.To4.
return isZeros(ip[0:10]) && ip[10] == 0xff && ip[11] == 0xff
}
// Is p all zeros?
func isZeros(p net.IP) bool {
for i := 0; i < len(p); i++ {
if p[i] != 0 {
return false
}
}
return true
}
// ErrBadIPLength means that len(IP) is not net.{IPv6len,IPv4len}.
var ErrBadIPLength = errors.New("invalid length of IP value")
// AddToAs adds XOR-MAPPED-ADDRESS value to m as t attribute.
func (a XORMappedAddress) AddToAs(m *Message, t AttrType) error {
var (
family = familyIPv4
ip = a.IP
)
if len(a.IP) == net.IPv6len {
if isIPv4(ip) {
ip = ip[12:16] // like in ip.To4()
} else {
family = familyIPv6
}
} else if len(ip) != net.IPv4len {
return ErrBadIPLength
}
value := make([]byte, 32+128)
value[0] = 0 // first 8 bits are zeroes
xorValue := make([]byte, net.IPv6len)
copy(xorValue[4:], m.TransactionID[:])
bin.PutUint32(xorValue[0:4], magicCookie)
bin.PutUint16(value[0:2], family)
bin.PutUint16(value[2:4], uint16(a.Port^magicCookie>>16))
xor.XorBytes(value[4:4+len(ip)], ip, xorValue)
m.Add(t, value[:4+len(ip)])
return nil
}
// AddTo adds XOR-MAPPED-ADDRESS to m. Can return ErrBadIPLength
// if len(a.IP) is invalid.
func (a XORMappedAddress) AddTo(m *Message) error {
return a.AddToAs(m, AttrXORMappedAddress)
}
// GetFromAs decodes XOR-MAPPED-ADDRESS attribute value in message
// getting it as for t type.
func (a *XORMappedAddress) GetFromAs(m *Message, t AttrType) error {
v, err := m.Get(t)
if err != nil {
return err
}
family := bin.Uint16(v[0:2])
if family != familyIPv6 && family != familyIPv4 {
return newDecodeErr("xor-mapped address", "family",
fmt.Sprintf("bad value %d", family),
)
}
ipLen := net.IPv4len
if family == familyIPv6 {
ipLen = net.IPv6len
}
// Ensuring len(a.IP) == ipLen and reusing a.IP.
if len(a.IP) < ipLen {
a.IP = a.IP[:cap(a.IP)]
for len(a.IP) < ipLen {
a.IP = append(a.IP, 0)
}
}
a.IP = a.IP[:ipLen]
for i := range a.IP {
a.IP[i] = 0
}
if len(v) <= 4 {
return io.ErrUnexpectedEOF
}
if err := CheckOverflow(t, len(v[4:]), len(a.IP)); err != nil {
return err
}
a.Port = int(bin.Uint16(v[2:4])) ^ (magicCookie >> 16)
xorValue := make([]byte, 4+TransactionIDSize)
bin.PutUint32(xorValue[0:4], magicCookie)
copy(xorValue[4:], m.TransactionID[:])
xor.XorBytes(a.IP, v[4:], xorValue)
return nil
}
// GetFrom decodes XOR-MAPPED-ADDRESS attribute in message and returns
// error if any. While decoding, a.IP is reused if possible and can be
// rendered to invalid state (e.g. if a.IP was set to IPv6 and then
// IPv4 value were decoded into it), be careful.
//
// Example:
//
// expectedIP := net.ParseIP("213.141.156.236")
// expectedIP.String() // 213.141.156.236, 16 bytes, first 12 of them are zeroes
// expectedPort := 21254
// addr := &XORMappedAddress{
// IP: expectedIP,
// Port: expectedPort,
// }
// // addr were added to message that is decoded as newMessage
// // ...
//
// addr.GetFrom(newMessage)
// addr.IP.String() // 213.141.156.236, net.IPv4Len
// expectedIP.String() // d58d:9cec::ffff:d58d:9cec, 16 bytes, first 4 are IPv4
// // now we have len(expectedIP) = 16 and len(addr.IP) = 4.
func (a *XORMappedAddress) GetFrom(m *Message) error {
return a.GetFromAs(m, AttrXORMappedAddress)
}
|