-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathxoraddr.go
More file actions
124 lines (113 loc) · 2.89 KB
/
xoraddr.go
File metadata and controls
124 lines (113 loc) · 2.89 KB
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
package stun
import (
"errors"
"fmt"
"io"
"net"
"strconv"
)
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))
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[:])
xorBytes(a.IP, v[4:], xorValue)
return nil
}
func (a *XORMappedAddress) GetFrom(m *Message) error {
return a.GetFromAs(m, AttrXORMappedAddress)
}