1package stun
2
3import (
4	"fmt"
5	"io"
6	"net"
7	"strconv"
8)
9
10// MappedAddress represents MAPPED-ADDRESS attribute.
11//
12// This attribute is used only by servers for achieving backwards
13// compatibility with RFC 3489 clients.
14//
15// RFC 5389 Section 15.1
16type MappedAddress struct {
17	IP   net.IP
18	Port int
19}
20
21// AlternateServer represents ALTERNATE-SERVER attribute.
22//
23// RFC 5389 Section 15.11
24type AlternateServer struct {
25	IP   net.IP
26	Port int
27}
28
29// OtherAddress represents OTHER-ADDRESS attribute.
30//
31// RFC 5780 Section 7.4
32type OtherAddress struct {
33	IP   net.IP
34	Port int
35}
36
37// AddTo adds ALTERNATE-SERVER attribute to message.
38func (s *AlternateServer) AddTo(m *Message) error {
39	a := (*MappedAddress)(s)
40	return a.addAs(m, AttrAlternateServer)
41}
42
43// GetFrom decodes ALTERNATE-SERVER from message.
44func (s *AlternateServer) GetFrom(m *Message) error {
45	a := (*MappedAddress)(s)
46	return a.getAs(m, AttrAlternateServer)
47}
48
49func (a MappedAddress) String() string {
50	return net.JoinHostPort(a.IP.String(), strconv.Itoa(a.Port))
51}
52
53func (a *MappedAddress) getAs(m *Message, t AttrType) error {
54	v, err := m.Get(t)
55	if err != nil {
56		return err
57	}
58	if len(v) <= 4 {
59		return io.ErrUnexpectedEOF
60	}
61	family := bin.Uint16(v[0:2])
62	if family != familyIPv6 && family != familyIPv4 {
63		return newDecodeErr("xor-mapped address", "family",
64			fmt.Sprintf("bad value %d", family),
65		)
66	}
67	ipLen := net.IPv4len
68	if family == familyIPv6 {
69		ipLen = net.IPv6len
70	}
71	// Ensuring len(a.IP) == ipLen and reusing a.IP.
72	if len(a.IP) < ipLen {
73		a.IP = a.IP[:cap(a.IP)]
74		for len(a.IP) < ipLen {
75			a.IP = append(a.IP, 0)
76		}
77	}
78	a.IP = a.IP[:ipLen]
79	for i := range a.IP {
80		a.IP[i] = 0
81	}
82	a.Port = int(bin.Uint16(v[2:4]))
83	copy(a.IP, v[4:])
84	return nil
85}
86
87func (a *MappedAddress) addAs(m *Message, t AttrType) error {
88	var (
89		family = familyIPv4
90		ip     = a.IP
91	)
92	if len(a.IP) == net.IPv6len {
93		if isIPv4(ip) {
94			ip = ip[12:16] // like in ip.To4()
95		} else {
96			family = familyIPv6
97		}
98	} else if len(ip) != net.IPv4len {
99		return ErrBadIPLength
100	}
101	value := make([]byte, 128)
102	value[0] = 0 // first 8 bits are zeroes
103	bin.PutUint16(value[0:2], family)
104	bin.PutUint16(value[2:4], uint16(a.Port))
105	copy(value[4:], ip)
106	m.Add(t, value[:4+len(ip)])
107	return nil
108}
109
110// AddTo adds MAPPED-ADDRESS to message.
111func (a *MappedAddress) AddTo(m *Message) error {
112	return a.addAs(m, AttrMappedAddress)
113}
114
115// GetFrom decodes MAPPED-ADDRESS from message.
116func (a *MappedAddress) GetFrom(m *Message) error {
117	return a.getAs(m, AttrMappedAddress)
118}
119
120// AddTo adds OTHER-ADDRESS attribute to message.
121func (o *OtherAddress) AddTo(m *Message) error {
122	a := (*MappedAddress)(o)
123	return a.addAs(m, AttrOtherAddress)
124}
125
126// GetFrom decodes OTHER-ADDRESS from message.
127func (o *OtherAddress) GetFrom(m *Message) error {
128	a := (*MappedAddress)(o)
129	return a.getAs(m, AttrOtherAddress)
130}
131
132func (o OtherAddress) String() string {
133	return net.JoinHostPort(o.IP.String(), strconv.Itoa(o.Port))
134}
135