1package ice
2
3import (
4	"encoding/binary"
5
6	"github.com/pion/stun"
7)
8
9// tiebreaker is common helper for ICE-{CONTROLLED,CONTROLLING}
10// and represents the so-called tiebreaker number.
11type tiebreaker uint64
12
13const tiebreakerSize = 8 // 64 bit
14
15// AddToAs adds tiebreaker value to m as t attribute.
16func (a tiebreaker) AddToAs(m *stun.Message, t stun.AttrType) error {
17	v := make([]byte, tiebreakerSize)
18	binary.BigEndian.PutUint64(v, uint64(a))
19	m.Add(t, v)
20	return nil
21}
22
23// GetFromAs decodes tiebreaker value in message getting it as for t type.
24func (a *tiebreaker) GetFromAs(m *stun.Message, t stun.AttrType) error {
25	v, err := m.Get(t)
26	if err != nil {
27		return err
28	}
29	if err = stun.CheckSize(t, len(v), tiebreakerSize); err != nil {
30		return err
31	}
32	*a = tiebreaker(binary.BigEndian.Uint64(v))
33	return nil
34}
35
36// AttrControlled represents ICE-CONTROLLED attribute.
37type AttrControlled uint64
38
39// AddTo adds ICE-CONTROLLED to message.
40func (c AttrControlled) AddTo(m *stun.Message) error {
41	return tiebreaker(c).AddToAs(m, stun.AttrICEControlled)
42}
43
44// GetFrom decodes ICE-CONTROLLED from message.
45func (c *AttrControlled) GetFrom(m *stun.Message) error {
46	return (*tiebreaker)(c).GetFromAs(m, stun.AttrICEControlled)
47}
48
49// AttrControlling represents ICE-CONTROLLING attribute.
50type AttrControlling uint64
51
52// AddTo adds ICE-CONTROLLING to message.
53func (c AttrControlling) AddTo(m *stun.Message) error {
54	return tiebreaker(c).AddToAs(m, stun.AttrICEControlling)
55}
56
57// GetFrom decodes ICE-CONTROLLING from message.
58func (c *AttrControlling) GetFrom(m *stun.Message) error {
59	return (*tiebreaker)(c).GetFromAs(m, stun.AttrICEControlling)
60}
61
62// AttrControl is helper that wraps ICE-{CONTROLLED,CONTROLLING}.
63type AttrControl struct {
64	Role       Role
65	Tiebreaker uint64
66}
67
68// AddTo adds ICE-CONTROLLED or ICE-CONTROLLING attribute depending on Role.
69func (c AttrControl) AddTo(m *stun.Message) error {
70	if c.Role == Controlling {
71		return tiebreaker(c.Tiebreaker).AddToAs(m, stun.AttrICEControlling)
72	}
73	return tiebreaker(c.Tiebreaker).AddToAs(m, stun.AttrICEControlled)
74}
75
76// GetFrom decodes Role and Tiebreaker value from message.
77func (c *AttrControl) GetFrom(m *stun.Message) error {
78	if m.Contains(stun.AttrICEControlling) {
79		c.Role = Controlling
80		return (*tiebreaker)(&c.Tiebreaker).GetFromAs(m, stun.AttrICEControlling)
81	}
82	if m.Contains(stun.AttrICEControlled) {
83		c.Role = Controlled
84		return (*tiebreaker)(&c.Tiebreaker).GetFromAs(m, stun.AttrICEControlled)
85	}
86	return stun.ErrAttributeNotFound
87}
88