libsip/client/
registration.rs

1use crate::*;
2
3use std::io::{Error as IoError, ErrorKind as IoErrorKind, Result as IoResult};
4
5/// Handle's the SIP registration process.
6/// This structure is designed to handle the authentication
7/// process from a SoftPhone's point of view.
8///
9/// Currently only Digest auth authentication is implemented.
10#[derive(Debug, PartialEq, Clone)]
11pub struct RegistrationManager {
12    /// Uri representing the account to attempt to register.
13    account_uri: Uri,
14    /// Uri representing the local machine used to register.
15    local_uri: Uri,
16    /// Current REGISTER cseq count number.
17    cseq_counter: u32,
18    /// The computed hash nonce count.
19    nonce_c: u32,
20    /// The CNonce value of the computer hash.
21    c_nonce: Option<String>,
22    /// The Finished computed auth header.
23    auth_header: Option<AuthHeader>,
24    /// The branch to use for registration.
25    branch: String,
26    /// The Call Id to use for register requests.
27    call_id: String,
28    // the value of the expires header.
29    expires_header: Option<u32>,
30    /// The username used for login
31    user: Option<String>,
32    /// The password to use for login.
33    pass: Option<String>,
34    /// Authentication realm
35    realm: Option<String>,
36    /// Authentication nonce
37    nonce: Option<String>,
38}
39
40impl RegistrationManager {
41    /// Create a new Registration Manager typically this will happen once in a program's
42    /// lifecycle. `account_uri` is the sip uri used to authenticate with and `local_uri`
43    /// is the sip uri of the listening socket.
44    pub fn new(account_uri: Uri, local_uri: Uri) -> RegistrationManager {
45        RegistrationManager {
46            account_uri,
47            local_uri,
48            cseq_counter: 444,
49            auth_header: None,
50            nonce_c: 1,
51            c_nonce: None,
52            branch: format!("{:x}", md5::compute(rand::random::<[u8; 16]>())),
53            call_id: format!("{:x}", md5::compute(rand::random::<[u8; 16]>())),
54            expires_header: None,
55            user: None,
56            pass: None,
57            realm: None,
58            nonce: None,
59        }
60    }
61
62    /// Set the username used in the authentication process.
63    pub fn username<S: Into<String>>(&mut self, s: S) {
64        self.user = Some(s.into());
65    }
66
67    /// Set the password used in the authentication process.
68    pub fn password<S: Into<String>>(&mut self, p: S) {
69        self.pass = Some(p.into());
70    }
71
72    /// Get the register request. if this method is called before `set_challenge`
73    /// then no authentication header will be set, if called after `set_challenge`
74    /// then the Authorization header will be set.
75    pub fn get_request(&mut self, cfg: &HeaderWriteConfig) -> IoResult<SipMessage> {
76        self.cseq_counter += 1;
77        self.nonce_c += 1;
78        let to_header = self.account_uri.clone();
79        let from_header = self.account_uri.clone();
80        let mut contact_header = self.local_uri.clone();
81        let mut headers = vec![];
82
83        if let Some(name) = &self.user {
84            contact_header = contact_header.auth(UriAuth::new(name));
85            if let Some(auth_header) = &self.auth_header {
86                if let Some(pass) = &self.pass {
87                    let ctx = AuthContext {
88                        user: &name,
89                        pass,
90                        nc: self.nonce_c,
91                        uri: &self.account_uri,
92                    };
93                    headers.push(Header::Authorization(auth_header.authenticate(ctx)?));
94                }
95            }
96        }
97        headers.push(Header::ContentLength(0));
98        headers.push(Header::To(NamedHeader::new(to_header)));
99        headers.push(Header::From(NamedHeader::new(from_header)));
100        headers.push(Header::Contact(NamedHeader::new(contact_header)));
101        headers.push(Header::CSeq(self.cseq_counter, Method::Register));
102        headers.push(Header::CallId(format!(
103            "{}@{}",
104            self.call_id,
105            self.account_uri.host()
106        )));
107        headers.push(self.via_header());
108        cfg.write_headers_vec(&mut headers);
109
110        if let Some(exp) = self.expires_header {
111            headers.push(Header::Expires(exp));
112        }
113        Ok(RequestGenerator::new()
114            .method(Method::Register)
115            .uri(self.account_uri.clone().authless())
116            .headers(headers)
117            .build()?)
118    }
119
120    /// After the first register request is sent. pass the received sip response
121    /// to this function to perform compute the hashed password.
122    pub fn set_challenge(&mut self, msg: SipMessage) -> IoResult<()> {
123        if let SipMessage::Response { headers, .. } = msg {
124            for item in headers.into_iter() {
125                match item {
126                    Header::WwwAuthenticate(auth) => {
127                        self.auth_header = Some(auth);
128                    },
129                    Header::Expires(expire) => {
130                        self.expires_header = Some(expire);
131                    },
132                    _ => {},
133                }
134            }
135            Ok(())
136        } else {
137            Err(IoError::new(
138                IoErrorKind::InvalidInput,
139                "Challenge Response was not a SIP response",
140            ))
141        }
142    }
143
144    /// Retreive the expires header value.
145    pub fn expires(&self) -> u32 {
146        self.expires_header.unwrap_or(60)
147    }
148
149    /// Retreive the current cseq counter.
150    pub fn cseq(&self) -> u32 {
151        self.cseq_counter
152    }
153
154    /// Retreive the via header being used to represent the local
155    /// listening socket.
156    pub fn via_header(&self) -> Header {
157        let via_uri = self
158            .local_uri
159            .clone()
160            .parameter(UriParam::Branch(self.branch.clone()))
161            .authless()
162            .schemaless();
163        Header::Via(ViaHeader {
164            uri: via_uri,
165            version: Default::default(),
166            transport: Transport::Udp,
167        })
168    }
169}