aboutsummaryrefslogtreecommitdiff
path: root/src/encoding
diff options
context:
space:
mode:
Diffstat (limited to 'src/encoding')
-rw-r--r--src/encoding/headermap.rs34
-rw-r--r--src/encoding/headers.rs36
-rw-r--r--src/encoding/method.rs23
-rw-r--r--src/encoding/mod.rs7
-rw-r--r--src/encoding/request.rs21
-rw-r--r--src/encoding/response.rs40
-rw-r--r--src/encoding/status.rs63
-rw-r--r--src/encoding/uri.rs12
8 files changed, 236 insertions, 0 deletions
diff --git a/src/encoding/headermap.rs b/src/encoding/headermap.rs
new file mode 100644
index 0000000..313d8b5
--- /dev/null
+++ b/src/encoding/headermap.rs
@@ -0,0 +1,34 @@
+use super::headers::Header;
+use anyhow::Result;
+use std::fmt::Display;
+
+#[derive(Debug)]
+pub struct HeaderMap(pub Vec<(String, String)>);
+
+impl HeaderMap {
+ pub fn new() -> Self {
+ Self(vec![])
+ }
+ pub fn add<H: Header>(mut self, h: H) -> Self {
+ self.0.push((H::NAME.to_string(), format!("{h}")));
+ self
+ }
+ pub fn get<H: Header>(&self) -> Option<Result<H>> {
+ self.0
+ .iter()
+ .find(|(k, _)| k == H::NAME)
+ .map(|(_, v)| H::from_str(v))
+ }
+ pub fn insert_raw(&mut self, key: String, value: String) {
+ self.0.push((key, value))
+ }
+}
+
+impl Display for HeaderMap {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ for (k, v) in &self.0 {
+ write!(f, "{k}: {v}\r\n")?;
+ }
+ Ok(())
+ }
+}
diff --git a/src/encoding/headers.rs b/src/encoding/headers.rs
new file mode 100644
index 0000000..d837ee5
--- /dev/null
+++ b/src/encoding/headers.rs
@@ -0,0 +1,36 @@
+use std::{fmt::Display, str::FromStr};
+
+macro_rules! header {
+ ($hname:literal, struct $name:ident($type:ty)) => {
+ pub struct $name(pub $type);
+ impl Header for $name {
+ const NAME: &'static str = $hname;
+ }
+ impl Display for $name {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.0)
+ }
+ }
+ impl FromStr for $name {
+ type Err = anyhow::Error;
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ Ok($name(<$type>::from_str(s)?))
+ }
+ }
+ };
+}
+
+pub trait Header: FromStr<Err = anyhow::Error> + Display {
+ const NAME: &'static str;
+}
+
+header!("Content-Length", struct ContentLength(usize));
+header!("Call-ID", struct CallID(String));
+header!("CSeq", struct CSeq(String));
+header!("Via", struct Via(String));
+header!("Contact", struct Contact(String));
+header!("Max-Forwards", struct MaxForwards(usize));
+header!("From", struct From(String));
+header!("To", struct To(String));
+header!("User-Agent", struct UserAgent(String));
+header!("Allow", struct Allow(String));
diff --git a/src/encoding/method.rs b/src/encoding/method.rs
new file mode 100644
index 0000000..5f8110a
--- /dev/null
+++ b/src/encoding/method.rs
@@ -0,0 +1,23 @@
+use std::fmt::Display;
+
+pub enum Method {
+ Register,
+ Invite,
+ Ack,
+ Option,
+ Cancel,
+ Bye,
+}
+
+impl Display for Method {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.write_str(match self {
+ Method::Register => "REGISTER",
+ Method::Invite => "INVITE",
+ Method::Ack => "ACK",
+ Method::Option => "OPTION",
+ Method::Cancel => "CANCEL",
+ Method::Bye => "BYE",
+ })
+ }
+}
diff --git a/src/encoding/mod.rs b/src/encoding/mod.rs
new file mode 100644
index 0000000..a7dd227
--- /dev/null
+++ b/src/encoding/mod.rs
@@ -0,0 +1,7 @@
+pub mod headermap;
+pub mod headers;
+pub mod method;
+pub mod request;
+pub mod response;
+pub mod status;
+pub mod uri;
diff --git a/src/encoding/request.rs b/src/encoding/request.rs
new file mode 100644
index 0000000..03b93c9
--- /dev/null
+++ b/src/encoding/request.rs
@@ -0,0 +1,21 @@
+use super::{headermap::HeaderMap, method::Method, uri::Uri};
+use std::fmt::Display;
+
+pub struct Request {
+ pub method: Method,
+ pub uri: Uri,
+ pub headers: HeaderMap,
+}
+
+impl Display for Request {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let Self {
+ headers,
+ method,
+ uri,
+ } = self;
+ write!(f, "{method} {uri} SIP/2.0\r\n")?;
+ write!(f, "{headers}\r\n")?;
+ Ok(())
+ }
+}
diff --git a/src/encoding/response.rs b/src/encoding/response.rs
new file mode 100644
index 0000000..3564b34
--- /dev/null
+++ b/src/encoding/response.rs
@@ -0,0 +1,40 @@
+use super::headermap::HeaderMap;
+use anyhow::{anyhow, bail, Context};
+use std::str::FromStr;
+
+#[derive(Debug)]
+pub struct Response {
+ pub code: u16,
+ pub headers: HeaderMap,
+}
+
+impl FromStr for Response {
+ type Err = anyhow::Error;
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ let mut lines = s.lines();
+ let statusline = lines.next().ok_or(anyhow!("status line missing"))?;
+ let (sipver, rest) = statusline
+ .split_once(" ")
+ .ok_or(anyhow!("status line malformed"))?;
+ let (code, _status_str) = rest
+ .split_once(" ")
+ .ok_or(anyhow!("status line malformed"))?;
+ let code = u16::from_str(code).context("status code")?;
+
+ let Some(ver) = sipver.strip_prefix("SIP/") else {
+ bail!("sip version malformed");
+ };
+ if ver != "2.0" {
+ bail!("sip version {ver:?} is not supported");
+ }
+
+ let mut headers = HeaderMap::new();
+ for line in lines {
+ // TODO multiline values
+ let (key, value) = line.split_once(":").ok_or(anyhow!("header malformed"))?;
+ headers.insert_raw(key.trim().to_string(), value.trim().to_string())
+ }
+
+ Ok(Self { code, headers })
+ }
+}
diff --git a/src/encoding/status.rs b/src/encoding/status.rs
new file mode 100644
index 0000000..de9ea88
--- /dev/null
+++ b/src/encoding/status.rs
@@ -0,0 +1,63 @@
+
+macro_rules! status_enum {
+ ($v:vis enum $name:ident { $($variant:ident = $value:literal),*, }) => {
+ $v enum $name { $($variant),*, Other(u16) }
+ impl $name { pub fn from_code(c: u16) -> Self { match c { $($value => Self::$variant),*, x => Self::Other(x) } } }
+ impl $name { pub fn to_code(&self) -> u16 { match self { $(Self::$variant => $value),*, Self::Other(x) => *x } } }
+ };
+}
+
+status_enum!(
+ pub enum Status {
+ Trying = 100,
+ Ringing = 180,
+ CallIsBeingForwarded = 181,
+ Queued = 182,
+ SessionProgress = 183,
+ Ok = 200,
+ MultipleChoices = 300,
+ MovedPermanently = 301,
+ MovedTemporarily = 302,
+ UseProxy = 305,
+ AlternativeService = 380,
+ BadRequest = 400,
+ Unauthorized = 401,
+ PaymentRequired = 402,
+ Forbidden = 403,
+ NotFound = 404,
+ MethodNotAllowed = 405,
+ NotAcceptable = 406,
+ ProxyAuthenticationRequired = 407,
+ RequestTimeout = 408,
+ Gone = 410,
+ RequestEntityTooLarge = 413,
+ RequestURITooLarge = 414,
+ UnsupportedMediaType = 415,
+ UnsupportedURIScheme = 416,
+ BadExtension = 420,
+ ExtensionRequired = 421,
+ IntervalTooBrief = 423,
+ TemporarilyNotAvailable = 480,
+ CallLegTransactionDoesNotExist = 481,
+ LoopDetected = 482,
+ TooManyHops = 483,
+ AddressIncomplete = 484,
+ Ambiguous = 485,
+ BusyHere = 486,
+ RequestTerminated = 487,
+ NotAcceptableHere = 488,
+ RequestPending = 491,
+ Undecipherable = 493,
+ InternalServerError = 500,
+ NotImplemented = 501,
+ BadGateway = 502,
+ ServiceUnavailable = 503,
+ ServerTimeout = 504,
+ SIPVersionNotSupported = 505,
+ MessageTooLarge = 513,
+ BusyEverywhere = 600,
+ Decline = 603,
+ DoesNotExistAnywhere = 604,
+ GlobalNotAcceptable = 606,
+ }
+);
diff --git a/src/encoding/uri.rs b/src/encoding/uri.rs
new file mode 100644
index 0000000..2d77df2
--- /dev/null
+++ b/src/encoding/uri.rs
@@ -0,0 +1,12 @@
+use std::fmt::Display;
+
+pub struct Uri {
+ pub content: String,
+}
+
+impl Display for Uri {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.content)?;
+ Ok(())
+ }
+}