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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
use std::{
    net::IpAddr,
    sync::{Arc, Mutex, MutexGuard},
};

use crate::{
    candidate::{CandidateKind, LocalCandidate},
    AgentRole,
};

/// Credentials.
#[derive(Clone)]
pub struct Credentials {
    username: String,
    password: String,
}

impl Credentials {
    /// Create random credentials.
    fn random() -> Self {
        Self {
            username: crate::utils::random_ice_string(4),
            password: crate::utils::random_ice_string(22),
        }
    }

    /// Create new credentials.
    #[inline]
    pub fn new<U, P>(username: U, password: P) -> Self
    where
        U: ToString,
        P: ToString,
    {
        Self {
            username: username.to_string(),
            password: password.to_string(),
        }
    }

    /// Get the username.
    #[inline]
    pub fn username(&self) -> &str {
        &self.username
    }

    /// Get the password.
    #[inline]
    pub fn password(&self) -> &str {
        &self.password
    }
}

/// ICE session.
#[derive(Clone)]
pub struct Session {
    context: Arc<Mutex<SessionContext>>,
}

impl Session {
    /// Create a new ICE session.
    pub fn new(agent_role: AgentRole, channels: usize) -> Self {
        Self {
            context: Arc::new(Mutex::new(SessionContext::new(agent_role, channels))),
        }
    }

    /// Lock the session for exclusive access.
    pub fn lock(&self) -> SessionGuard<'_> {
        SessionGuard {
            inner: self.context.lock().unwrap(),
        }
    }

    /// Get the current agent role.
    pub fn get_agent_role(&self) -> AgentRole {
        self.lock().get_agent_role()
    }

    /// Set the agent role.
    pub fn set_agent_role(&self, role: AgentRole) {
        self.lock().set_agent_role(role)
    }

    /// Get local credentials.
    pub fn get_local_credentials(&self, channel: usize) -> Credentials {
        self.lock().get_local_credentials(channel).clone()
    }

    /// Get remote credentials.
    pub fn get_remote_credentials(&self, channel: usize) -> Option<Credentials> {
        self.lock().get_remote_credentials(channel).cloned()
    }

    /// Set remote credentials.
    pub fn set_remote_credentials(&self, channel: usize, credentials: Credentials) {
        self.lock().set_remote_credentials(channel, credentials)
    }

    /// Assign a session-wide foundation to a given local candidate.
    pub fn assign_foundation(
        &self,
        candidate: &LocalCandidate,
        source_addr: Option<IpAddr>,
    ) -> u32 {
        self.lock().assign_foundation(candidate, source_addr)
    }
}

/// Session lock guard.
pub struct SessionGuard<'a> {
    inner: MutexGuard<'a, SessionContext>,
}

impl<'a> SessionGuard<'a> {
    /// Get the current agent role.
    pub fn get_agent_role(&self) -> AgentRole {
        self.inner.agent_role
    }

    /// Set agent role.
    pub fn set_agent_role(&mut self, role: AgentRole) {
        self.inner.agent_role = role;
    }

    /// Get local credentials.
    pub fn get_local_credentials(&self, channel: usize) -> &Credentials {
        let channel = &self.inner.channels[channel];

        channel.get_local_credentials()
    }

    /// Get remote credentials.
    pub fn get_remote_credentials(&self, channel: usize) -> Option<&Credentials> {
        let channel = &self.inner.channels[channel];

        channel.get_remote_credentials()
    }

    /// Set remote credentials.
    pub fn set_remote_credentials(&mut self, channel: usize, credentials: Credentials) {
        let channel = &mut self.inner.channels[channel];

        channel.set_remote_credentials(credentials);
    }

    /// Get the tie-breaker value.
    pub fn get_tie_breaker(&self) -> u64 {
        self.inner.tie_breaker
    }

    /// Assign a session-wide foundation to a given local candidate.
    ///
    /// NOTE: We expect that the transport protocol used for obtaining
    /// reflexive/relayed candidates is always UDP.
    pub fn assign_foundation(
        &mut self,
        candidate: &LocalCandidate,
        source_addr: Option<IpAddr>,
    ) -> u32 {
        let kind = candidate.kind();
        let base = candidate.base();

        assert!(kind == CandidateKind::Host || source_addr.is_some());

        let entry = FoundationEntry {
            candidate_kind: kind,
            candidate_base: base.ip(),
            source_addr,
        };

        let foundation_idx = self.inner.foundations.iter().position(|e| e == &entry);

        if let Some(index) = foundation_idx {
            index as u32
        } else {
            let index = self.inner.foundations.len();

            self.inner.foundations.push(entry);

            index as u32
        }
    }
}

/// Shared session context.
struct SessionContext {
    agent_role: AgentRole,
    tie_breaker: u64,
    channels: Vec<ChannelContext>,
    foundations: Vec<FoundationEntry>,
}

impl SessionContext {
    /// Create a new session context.
    fn new(agent_role: AgentRole, channels: usize) -> Self {
        let channel_count = channels;

        let mut channels = Vec::with_capacity(channel_count);

        channels.resize_with(channel_count, ChannelContext::new);

        Self {
            agent_role,
            tie_breaker: rand::random(),
            channels,
            foundations: Vec::new(),
        }
    }
}

/// Channel related session context.
struct ChannelContext {
    local_credentials: Credentials,
    remote_credentials: Option<Credentials>,
}

impl ChannelContext {
    /// Create a new channel context.
    fn new() -> Self {
        Self {
            local_credentials: Credentials::random(),
            remote_credentials: None,
        }
    }

    /// Get local credentials.
    fn get_local_credentials(&self) -> &Credentials {
        &self.local_credentials
    }

    /// Get remote credentials.
    fn get_remote_credentials(&self) -> Option<&Credentials> {
        self.remote_credentials.as_ref()
    }

    /// Set remote credentials.
    fn set_remote_credentials(&mut self, credentials: Credentials) {
        self.remote_credentials = Some(credentials);
    }
}

/// Foundation table entry.
#[derive(Eq, PartialEq)]
struct FoundationEntry {
    candidate_kind: CandidateKind,
    candidate_base: IpAddr,
    source_addr: Option<IpAddr>,
}