From: Richard Whitehouse Date: Sat, 28 Oct 2017 22:09:02 +0000 (+0100) Subject: Initial commit X-Git-Url: https://git.richardwhiuk.com/?a=commitdiff_plain;h=baf72bb68d87a1318fe99b4a1d4a58009ebf8291;p=rust-sip.git Initial commit Gather SIP message Parse top line --- baf72bb68d87a1318fe99b4a1d4a58009ebf8291 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6fe9427 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Cargo +target +Cargo.lock + +# Ignore .bk files from rustfmt +*.bk diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..2e97f2f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "sip" +version = "0.1.0" +authors = ["Richard Whitehouse "] + +[dependencies] +futures = "0.1.16" +tokio-io = "0.1.3" +tokio-core = "0.1.10" +bytes = "0.4.5" +nom = "3.2.0" + diff --git a/src/codec.rs b/src/codec.rs new file mode 100644 index 0000000..94b1c23 --- /dev/null +++ b/src/codec.rs @@ -0,0 +1,463 @@ +use bytes::BytesMut; + +use futures::future; +use futures::{Future, Stream}; +use tokio_io::AsyncRead; +use tokio_core::reactor::Handle; +use tokio_core::net::TcpListener; +use tokio_io::codec::{Encoder, Decoder}; +use std::io; +use std; +use nom; + +use types::{RequestLine, StatusLine, TopLine, Uri}; +use parser::top_line; + +const SPACE: u8 = b' '; +const TAB: u8 = b'\t'; +const LF: u8 = b'\n'; +const CR: u8 = b'\r'; + +#[derive(Debug)] +struct Header { + key: String, + raw_value: UnparsedLine, + dirty: bool, + parsed: HeaderType, +} + +impl Header { + fn parse(line: UnparsedLine) -> Result { + Ok(Header { + key: "To".to_string(), + raw_value: line, + dirty: false, + parsed: HeaderType::Extension { + name: "To".to_string(), + value: "".to_string(), + }, + }) + } +} + +#[derive(Debug)] +enum HeaderType { + From { uri: Uri }, + To { uri: Uri }, + Extension { name: String, value: String }, +} + +#[derive(Clone, Debug)] +struct UnparsedLine { + value: BytesMut, +} + +impl UnparsedLine { + fn new(value: BytesMut) -> UnparsedLine { + UnparsedLine { value: value } + } + + fn add(&mut self, value: &BytesMut) { + self.value.extend_from_slice(value); + } + + fn as_bytes(&self) -> &[u8] { + &self.value + } +} + +struct SipCodec { + message: Option, + headers: Vec, + top_line: Option, + latest_header: Option, +} + +impl SipCodec { + fn new() -> SipCodec { + SipCodec { + message: None, + headers: Vec::new(), + top_line: None, + latest_header: None, + } + } + + fn parse_message(&mut self, buf: &mut BytesMut) -> io::Result> { + loop { + println!("Gathering headers from {:?}", std::str::from_utf8(&buf)); + + let mut done = false; + let mut total = 0; + + { + let mut search: Box> = Box::new(buf.iter()); + + while !done { + println!("Gathering CRLF"); + + if let Some(cr) = search.position(|&b| b == CR) { + + println!("Found CR at {}", cr); + + total += cr; + + let next = search.next(); + + println!("Got {:?} after CR", next); + + if let Some(&LF) = next { + done = true; + } + } else { + return Ok(None); + } + } + } + + // We've got a line terminating in \r\n. + let line = buf.split_to(total + 2); + + println!("Using line: {:?}", std::str::from_utf8(&line)); + + match (&mut self.top_line, chartype(line.first()), &mut self.latest_header) { + // Ignore empty top lines + (&mut None, CharType::Line, _) => { + + println!("Ignoring empty top line"); + + } + + // Top line beginnning with whitespace - ignore + (&mut None, CharType::Whitespace, _) => { + println!("Got top line beginning with whitespace - ignore: {:?}", + line); + } + + // Top line with no headers! Discard. + (top_line @ &mut None, CharType::Other, _) => { + println!("Got new top line of request: {:?}", line); + + top_line.get_or_insert(UnparsedLine::new(line)); + } + + // Top line with no headers! Discard. + (top_line @ &mut Some(_), CharType::Line, &mut None) => { + println!("Got request with no headers - discarding"); + top_line.take(); + } + + // Whitespace wrapped req line + (&mut Some(ref mut top_line), CharType::Whitespace, &mut None) => { + top_line.add(&line) + } + + // Whitespace wrapped header + (&mut Some(_), CharType::Whitespace, &mut Some(ref mut latest_header)) => { + latest_header.add(&line) + } + + // First header + (&mut Some(_), CharType::Other, latest_header @ &mut None) => { + latest_header.get_or_insert(UnparsedLine::new(line)); + } + + // New header + (&mut Some(_), CharType::Other, latest_header @ &mut Some(_)) => { + self.headers.push(latest_header.take().unwrap()); + latest_header.get_or_insert(UnparsedLine::new(line)); + } + + // End of headers + (top_line @ &mut Some(_), CharType::Line, old_header @ &mut Some(_)) => { + self.headers.push(old_header.take().unwrap()); + + println!("Got end of headers"); + + if let Ok(new_message) = PartialMessage::new(top_line.take() + .unwrap(), + &mut self.headers) { + + println!("Got partial message: {:?}", new_message); + + self.headers.clear(); + + let length = new_message.body_length(); + + if length == 0 { + if let Some(message) = Message::new(new_message) { + println!("Got message without body: {:?}", message); + + return Ok(Some(message)); + } else { + println!("Failed to parse message with no body!"); + } + } else if length > buf.len() { + self.message.get_or_insert(new_message); + return Ok(None); + } else { + let body = buf.split_to(length); + if let Some(message) = Message::new_with_body(new_message, body) { + println!("Got message with body: {:?}", message); + + return Ok(Some(message)); + } else { + println!("Failed to parse message with body!"); + } + } + } else { + println!("Failed to parse partial message!"); + + // Message failed to parse + top_line.take(); + self.headers.clear(); + } + } + } + } + + } +} + +#[derive(Debug)] +struct PartialMessage { + headers: Vec
, + top_line: UnparsedLine, +} + +impl PartialMessage { + fn new(top_line: UnparsedLine, + headers: &mut Vec) + -> Result { + Ok(PartialMessage { + top_line: top_line, + headers: try!(headers.drain(..).map(|line| Header::parse(line)).collect()), + }) + } + + fn body_length(&self) -> usize { + let body_length = 0; + + body_length + } +} + +#[derive(Debug)] +enum Message { + Request(Request), + Response(Response), +} + +#[derive(Debug)] +struct Response { + status_line: (StatusLine, UnparsedLine), + headers: Vec
, + body: Option, +} + +impl Response { + fn as_bytes(&self) -> &[u8] { + return self.status_line.1.as_bytes(); + } +} + +#[derive(Debug)] +struct Request { + request_line: (RequestLine, UnparsedLine), + headers: Vec
, + body: Option, +} + +impl Request { + fn as_bytes(&self) -> &[u8] { + return self.request_line.1.as_bytes(); + } +} + +impl Message { + fn new(message: PartialMessage) -> Option { + Message::parse(message, None) + } + + fn parse(message: PartialMessage, body: Option) -> Option { + match top_line(message.top_line.as_bytes()) { + nom::IResult::Done(_, TopLine::RequestLine(r)) => { + Some(Message::Request(Request { + headers: message.headers, + request_line: (r, message.top_line.clone()), + body: body, + })) + }, + nom::IResult::Done(_, TopLine::StatusLine(s)) => { + Some(Message::Response(Response { + headers: message.headers, + status_line: (s, message.top_line.clone()), + body: body, + })) + }, + result => { println!("Failed to parse top line: {:?}", result); None }, + } + } + + fn new_with_body(message: PartialMessage, body: BytesMut) -> Option { + Message::parse(message, Some(body)) + } + + fn as_bytes<'a>(&'a self) -> &'a [u8] { + match self { + &Message::Request(ref r) => r.as_bytes(), + &Message::Response(ref r) => r.as_bytes(), + } + } +} + +impl Decoder for SipCodec { + type Item = Message; + type Error = io::Error; + + fn decode(&mut self, buf: &mut BytesMut) -> io::Result> { + if let Some(_) = self.message { + + println!("Gathering body"); + + let length = self.message.iter().map(|m| m.body_length()).next().unwrap(); + + if length > buf.len() { + return Ok(None); + } else if let Some(message) = Message::new_with_body(self.message.take().unwrap(), + buf.split_to(length)) { + return Ok(Some(message)); + } else { + println!("Failed to parse message with no body!"); + } + } + + self.parse_message(buf) + } +} + +impl Encoder for SipCodec { + type Item = Message; + type Error = io::Error; + + fn encode(&mut self, msg: Message, buf: &mut BytesMut) -> io::Result<()> { + buf.extend(msg.as_bytes()); + Ok(()) + } +} + +struct Sip { + listeners: Vec, +} + +enum CharType { + Line, + Whitespace, + Other, +} + +fn chartype(char: Option<&u8>) -> CharType { + match char { + None | Some(&LF) | Some(&CR) => CharType::Line, + Some(&SPACE) | Some(&TAB) => CharType::Whitespace, + Some(_) => CharType::Other, + } +} + +impl Sip { + fn new() -> Sip { + Sip { listeners: Vec::new() } + } + + pub fn add_listener(&mut self, listener: TcpListener) { + self.listeners.push(listener); + } + + pub fn run(self, handle: Handle) -> Box> { + + let mut future: Box> = Box::new(future::ok(())); + + for listener in self.listeners { + + let lis_handle = handle.clone(); + + // Iterate incoming connections + future = Box::new(future.join(listener.incoming().for_each(move |(tcp, _)| { + + // Split up the read and write halves + let (_sink, stream) = tcp.framed(SipCodec::new()).split(); + + let future = stream.for_each(|message| { + println!("recevied sip message: {:?}", message); + + Ok(()) + }) + .map_err(|err| println!("Error {:?}", err)); + + // Spawn the future as a concurrent task + lis_handle.spawn(future); + + Ok(()) + })) + .map(|_| ())); + } + + future + } +} + +#[cfg(test)] +mod tests { + + use codec::Sip; + use futures; + use futures::Future; + use futures::future::Either; + use tokio_io::io; + use tokio_core::net::TcpListener; + use tokio_core::net::TcpStream; + use tokio_core::reactor::Core; + use std::net::Shutdown; + + #[test] + fn it_works() { + // Create the event loop that will drive this server + let mut core = Core::new().unwrap(); + let handle = core.handle(); + + let mut sip = Sip::new(); + + // Bind the server's socket + let addr = "127.0.0.1:12345".parse().unwrap(); + sip.add_listener(TcpListener::bind(&addr, &handle).unwrap()); + + let server = sip.run(core.handle()); + + // Send a request + let socket = TcpStream::connect(&addr, &handle); + + let request = socket.and_then(|socket| { + io::write_all(socket, + "MESSAGE sip:test.com SIP/2.0\r\nVia: localhost\r\n\r\n") + }); + + let finished = request.and_then(|(socket, _request)| { + futures::done(socket.shutdown(Shutdown::Write).map(|_| socket)) + }); + + let response = finished.and_then(|socket| io::read_to_end(socket, Vec::new())); + + // Spin up the server on the event loop, and connect the client to it + let res = core.run(server.select2(response)); + + // Response + match res { + Ok(Either::B(((_socket, response), _))) => { + // assert_eq!("echo", String::from_utf8_lossy(&response)); + } + Ok(_) => panic!("Expected client to complete"), + Err(Either::A((err, _))) => panic!("{:?}", err), + Err(Either::B((err, _))) => panic!("{:?}", err), + } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..974d78e --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,11 @@ +extern crate futures; +extern crate tokio_core; +extern crate tokio_io; +extern crate bytes; + +#[macro_use] +extern crate nom; + +mod types; +mod parser; +mod codec; diff --git a/src/parser.rs b/src/parser.rs new file mode 100644 index 0000000..13c46ad --- /dev/null +++ b/src/parser.rs @@ -0,0 +1,436 @@ +use nom; +use std; + +use nom::alpha as alphas; +use nom::digit as digits; +use nom::alphanumeric as alphanumerics; + +use nom::IResult; +use nom::ErrorKind::Custom; + +use types::{PathSegment, HostPort, Host, Hostname, UriParameter, Header, Headers, SipUri, Uri, + HierarchicalPath, Authority, UriPath, UserInfo, AbsolutePath, Scheme, Method, + Transport, UserParam, Version, RequestLine, StatusLine, TopLine}; + +fn is_token(c: u8) -> bool { + nom::is_alphanumeric(c) || c == b'-' || c == b'.' || c == b'!' || c == b'%' || + c == b'*' || c == b'_' || c == b'+' || c == b'`' || c == b'\'' || c == b'~' +} + +named!(token, take_while!(is_token)); + +named!(method, alt!( +// RFC 3261 + tag!("INVITE") => { |_| Method::INVITE } | + tag!("ACK") => { |_| Method::ACK } | + tag!("OPTIONS") => { |_| Method::OPTIONS } | + tag!("BYE") => { |_| Method::BYE } | + tag!("CANCEL") => { |_| Method::CANCEL } | + tag!("REGISTER") => { |_| Method::REGISTER } | +// Extensions + tag!("MESSAGE") => { |_| Method::MESSAGE } | + tag!("REFER") => { |_| Method::REFER } | + tag!("SUBSCRIBE") => { |_| Method::SUBSCRIBE } | + tag!("NOTIFY") => { |_| Method::NOTIFY } | + token => { |method : &[u8]| Method::Extension(method.to_vec()) } +)); + +fn is_mark(c: u8) -> bool { + c == b'-' || c == b'_' || c == b'.' || c == b'!' || c == b'~' || c == b'*' || c == b'\'' || + c == b'(' || c == b')' +} + +fn is_unreserved(c: u8) -> bool { + nom::is_alphanumeric(c) || is_mark(c) +} + +fn is_reserved(c: u8) -> bool { + c == b';' || c == b'/' || c == b'?' || c == b':' || c == b'@' || c == b'&' || c == b'=' || + c == b'+' || c == b'$' || c == b',' +} + +fn is_user_unreserved(c: u8) -> bool { + is_unreserved(c) || c == b'&' || c == b'=' || c == b'+' || c == b'$' || c == b',' || + c == b';' || c == b'?' || c == b'/' +} + +fn is_password_unreserved(c: u8) -> bool { + is_unreserved(c) || c == b'=' || c == b'+' || c == b'$' || c == b',' +} + +fn is_domain_label(c: u8) -> bool { + nom::is_alphanumeric(c) || c == b'-' +} + +macro_rules! take_1 ( + ($input:expr, $submac:ident!( $($args:tt)* )) => ( + { + use nom::Slice; + let input = $input; + + println!("Taking 1 from {:?}", input); + + match input.iter().next() { + Some(c) => { + if $submac!(*c, $($args)*) { + let res:nom::IResult<&[u8],u8> = nom::IResult::Done(input.slice(1..), *c); + res + } + else + { + println!("Failed to take 1 from {:?} - {:?} doesn't match", input, c); + + nom::IResult::Error(error_position!(nom::ErrorKind::TakeWhile1, input)) + } + }, + None => { + nom::IResult::Incomplete(nom::Needed::Size(1)) + } + } + } + ); + ($input:expr, $f:expr) => ( + take_1!($input, call!($f)); + ); +); + +named!(alpha<&[u8], u8>, take_1!(nom::is_alphabetic)); +named!(alphanumeric<&[u8], u8>, take_1!(nom::is_alphanumeric)); +named!(digit<&[u8], u8>, take_1!(nom::is_digit)); +named!(hex<&[u8], u8>, take_1!(nom::is_hex_digit)); + +fn escaped(input: &[u8]) -> IResult<&[u8], u8> { + let result = preceded!(input, tag!(b"%"), pair!(hex, hex)); + + match result { + IResult::Done(left, (a, b)) => { + let value: u8 = ((a - (b'0' as u8)) << 4) + (b - b'0'); + IResult::Done(left, value) + } + IResult::Incomplete(a) => IResult::Incomplete(a), + IResult::Error(a) => IResult::Error(a), + } +} + +named!(unreserved<&[u8], u8>, take_1!(is_unreserved)); +named!(user_unreserved<&[u8], u8>, take_1!(is_user_unreserved)); +named!(password_unreserved<&[u8], u8>, take_1!(is_password_unreserved)); + +// The BNF splits unreserved and user unreserved, but that's just a waste +named!(user<&[u8], Vec>, many1!(alt!( + user_unreserved | + escaped))); + +named!(password<&[u8], Vec>, many1!(alt!( + password_unreserved | + escaped))); + +named!(userinfo<&[u8], (Vec, Option>)>, terminated!( + pair!( + user, + opt!(preceded!(tag!(b":"), password))), + tag!(b"@"))); + +named!(domain_label<&[u8], Vec>, alt!( + alphanumerics => { |a : &[u8]| a.to_vec() } | + tuple!( + alphanumeric, + take_while1!(is_domain_label), + alphanumeric) => { |(a, b, c) : (u8, &[u8], u8)| { + let mut v = Vec::with_capacity(b.len() + 2); + v.push(a); + v.extend_from_slice(b); + v.push(c); + v + }})); + +named!(top_label<&[u8], Vec>, alt!( + alphas => { |a : &[u8]| a.to_vec() } | + tuple!( + alpha, + take_while1!(is_domain_label)) => { |(a, b) : (u8, &[u8])| { + let mut v = Vec::with_capacity(b.len() + 2); + v.push(a); + v.extend_from_slice(b); + v + }})); + +named!(hostname<&[u8], Hostname>, tuple!( + many0!(terminated!(domain_label, tag!(b"."))), + terminated!(top_label, opt!(tag!(b"."))))); + +named!(ipv4_address<&[u8], (u8, u8, u8, u8)>, tuple!( + number, + preceded!(tag!(b"."), number), + preceded!(tag!(b"."), number), + preceded!(tag!(b"."), number))); + +fn is_ipv6_char(c: u8) -> bool { + nom::is_hex_digit(c) || c == b':' +} + +named!(ipv6_address<&[u8], Vec>, delimited!( + tag!(b"["), + many1!(take_1!(is_ipv6_char)), + tag!(b"]"))); + +named!(host, alt!( + hostname => { |h| Host::Hostname(h) } | + ipv4_address => { |(a, b, c, d)| Host::Ipv4Address(a,b,c,d) } | + ipv6_address => { |i| Host::Ipv6Address(i) } +)); + +named!(hostport<&[u8], HostPort>, tuple!( + host, + opt!(preceded!(tag!(":"), number)))); + +named!(transport_param, alt!( + tag!("tcp") => { |_| Transport::Tcp } | + tag!("udp") => { |_| Transport::Udp } | + tag!("sctp") => { |_| Transport::Sctp } | + tag!("tls") => { |_| Transport::Tls } | + token => { |token : &[u8]| Transport::Other(token.to_vec()) } +)); + +named!(user_param, alt!( + tag!("phone") => { |_| UserParam::Phone } | + tag!("ip") => { |_| UserParam::Ip } | + token => { |token : &[u8]| UserParam::Other(token.to_vec()) })); + +fn is_param_unreserved(c: u8) -> bool { + is_unreserved(c) || c == b'[' || c == b']' || c == b'/' || c == b':' || c == b'&' || + c == b'+' || c == b'$' +} + +named!(param<&[u8], Vec>, many1!(alt!( + take_1!(is_param_unreserved) | + escaped))); + +fn number(input: &[u8]) -> IResult<&[u8], O> + where O: std::str::FromStr +{ + match digits(input) { + IResult::Done(left, num_str) => { + if let Ok(num_utf8) = std::str::from_utf8(num_str) { + if let Ok(num_i) = num_utf8.parse::() { + IResult::Done(left, num_i) + } else { + IResult::Error(Custom(1)) + } + } else { + IResult::Error(Custom(2)) + } + } + IResult::Error(e) => IResult::Error(e), + IResult::Incomplete(i) => IResult::Incomplete(i), + } +} + +named!(uri_parameter, alt!( + preceded!(tag!("transport="), transport_param) => { + |transport| UriParameter::Transport(transport) + } | + preceded!(tag!("user="), user_param) => { |user| UriParameter::User(user) } | + preceded!(tag!("method="), method) => { |method| UriParameter::Method(method) } | + preceded!(tag!(b"ttl="), number) => { |ttl| UriParameter::Ttl(ttl) } | + preceded!(tag!("maddr="), recognize!(host)) => { + |host : &[u8]| UriParameter::Maddr(host.to_vec()) + } | + tag!("lr") => { |ttl| UriParameter::Lr } | + pair!(param, opt!(preceded!(tag!(b"="), param))) => { + |(key, value)| + UriParameter::Other { + key: key, + value: value + } + } +)); + +named!(uri_parameters<&[u8], Vec>, many0!( + preceded!(char!(';'), uri_parameter))); + +fn is_header_unreserved(c: u8) -> bool { + is_unreserved(c) || c == b'[' || c == b']' || c == b'/' || c == b'?' || c == b':' || + c == b'+' || c == b'$' +} + +named!(hname<&[u8], Vec>, many1!(alt!( + take_1!(is_header_unreserved) | + escaped))); + +named!(hvalue<&[u8], Vec>, many1!(alt!( + take_1!(is_header_unreserved) | + escaped))); + +named!(header<&[u8], Header>, separated_pair!( + hname, + tag!("="), + hvalue)); + +named!(headers<&[u8], Headers>, preceded!( + char!('?'), + tuple!( + header, + many0!( + preceded!(char!('&'), header))))); + +named!(sip_uri<&[u8], (Option, HostPort, Vec, Option)>, preceded!( + tag!("sip:"), + tuple!( + opt!(userinfo), + hostport, + uri_parameters, + opt!(headers) + ))); + +named!(sips_uri<&[u8], (Option, HostPort, Vec, Option)>, preceded!( + tag!("sips:"), + tuple!( + opt!(userinfo), + hostport, + uri_parameters, + opt!(headers) + ))); + +fn is_scheme_unreserved(c: u8) -> bool { + nom::is_alphanumeric(c) || c == b'+' || c == b'-' || c == b'.' +} + +named!(scheme<&[u8], Scheme>, tuple!( + alpha, + many0!(take_1!(is_scheme_unreserved)))); + +fn is_path_char(c: u8) -> bool { + is_unreserved(c) || c == b':' || c == b'@' || c == b'&' || c == b'=' || c == b'+' || + c == b'$' || c == b',' +} + +named!(path_param<&[u8], Vec>, many0!(alt!( + escaped | + take_1!(is_path_char)))); + +named!(path_segment<&[u8], PathSegment>, pair!( + many0!(take_1!(is_path_char)), + opt!(preceded!(tag!(b";"), path_param)))); + +named!(srvr<&[u8], (Option, HostPort)>, tuple!( + opt!(userinfo), + hostport)); + +fn is_reg_name_char(c: u8) -> bool { + is_unreserved(c) || c == b':' || c == b';' || c == b'$' || c == b'@' || c == b'&' || + c == b'=' || c == b'+' || c == b',' +} + +named!(reg_name<&[u8], Vec>, many1!(alt!( + take_1!(is_reg_name_char) | + escaped))); + +named!(authority<&[u8], Authority>, alt!( + srvr => { |(u, h)| Authority::Server { userinfo: u, hostport: h } } | + reg_name => { |n| Authority::Name(n) } )); + +named!(abs_path<&[u8], AbsolutePath>, many1!( + preceded!(char!('/'), path_segment))); + +named!(net_path<&[u8], (Authority, Option)>, preceded!(tag!("//"), pair!( + authority, + opt!(abs_path)))); + +named!(query<&[u8], Vec>, many0!(uric)); + +named!(hier_part<&[u8], (HierarchicalPath, Option>)>, tuple!( + alt!( + net_path => { |(a, p)| HierarchicalPath::Network{ authority: a, path: p } } | + abs_path => { |p| HierarchicalPath::Absolute(p) } + ), + opt!(preceded!(char!('?'), query)))); + +fn is_uri_char_no_slash(c: u8) -> bool { + is_unreserved(c) || c == b';' || c == b'?' || c == b':' || c == b'@' || c == b'&' || + c == b'=' || c == b'+' || c == b'$' || c == b',' +} + +named!(uric_no_slash<&[u8], u8>, alt!( + take_1!(is_uri_char_no_slash) | + escaped)); + +fn is_uri_char(c: u8) -> bool { + is_unreserved(c) || is_reserved(c) +} + +named!(uric<&[u8], u8>, alt!( + take_1!(is_uri_char) | + escaped)); + +named!(opaque_part<&[u8], (u8, Vec)>, pair!( + uric_no_slash, + many0!(uric))); + +named!(absolute_uri<&[u8], (Scheme, UriPath)>, separated_pair!( + scheme, + char!(':'), + alt!( + hier_part => { |(h,q)| UriPath::Hierarchical { path: h, query: q } } | + opaque_part => { |(b, o)| UriPath::Opaque(b, o) } + ))); + +named!(request_uri<&[u8], Uri>, alt!( + sip_uri => { |(u, hp, p, h)| Uri::Sip(SipUri { + user_info: u, + host_port: hp, + params: p, + headers: h }) } | + sips_uri => { |(u, hp, p, h)| Uri::Sips(SipUri { + user_info: u, + host_port: hp, + params: p, + headers: h }) } | + absolute_uri => { |(s, p)| Uri::Generic { + scheme: s, + path: p } } +)); + +named!(sip_version<&[u8], Version>, preceded!( + tag!("SIP/"), + dbg_dmp!(separated_pair!( + dbg_dmp!(number), + dbg_dmp!(tag!(b".")), + dbg_dmp!(number))))); + +named!(request_line<&[u8], (Method, Uri, Version)>, tuple!( + method, + delimited!( + tag!(" "), + request_uri, + tag!(" ")), + sip_version)); + +fn is_reason_phrase_char(c: u8) -> bool { + is_reserved(c) || is_unreserved(c) || c == b' ' || c == b'\t' +} + +named!(reason_phrase>, many0!(alt!( + escaped | + take_1!(is_reason_phrase_char)))); + +named!(status_line<&[u8], (Version, u16, Vec)>, tuple!( + sip_version, + delimited!( + tag!(" "), + number, + tag!(" ") + ), + reason_phrase)); + +named!(pub top_line, alt!( + request_line => { |(m,u,v)| TopLine::RequestLine(RequestLine { + method: m, + uri: u, + version: v + })} | + status_line => { |(v, c, r)| TopLine::StatusLine(StatusLine { + version: v, + code: c, + reason: r + })})); diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..b1b58ea --- /dev/null +++ b/src/types.rs @@ -0,0 +1,133 @@ +pub type UserInfo = (Vec, Option>); + +pub type Hostname = (Vec>, Vec); + +#[derive(Debug)] +pub enum Host { + Hostname(Hostname), + Ipv4Address(u8, u8, u8, u8), + Ipv6Address(Vec), +} + +pub type HostPort = (Host, Option); + +#[derive(Debug)] +pub enum Authority { + Server { + userinfo: Option, + hostport: HostPort, + }, + Name(Vec), +} + +pub type PathSegment = (Vec, Option>); + +pub type AbsolutePath = Vec; + +#[derive(Debug)] +pub enum HierarchicalPath { + Network { + authority: Authority, + path: Option, + }, + Absolute(AbsolutePath), +} + +#[derive(Debug)] +pub enum UriPath { + Hierarchical { + path: HierarchicalPath, + query: Option>, + }, + Opaque(u8, Vec), +} + +pub type Scheme = (u8, Vec); + +pub type Header = (Vec, Vec); + +pub type Headers = (Header, Vec
); + +#[derive(Debug)] +pub enum Transport { + Udp, + Tcp, + Sctp, + Tls, + Other(Vec), +} + +#[derive(Debug)] +pub enum UserParam { + Phone, + Ip, + Other(Vec), +} + +#[derive(Debug)] +pub enum Method { + INVITE, + ACK, + OPTIONS, + BYE, + CANCEL, + REGISTER, + MESSAGE, + REFER, + SUBSCRIBE, + NOTIFY, + Extension(Vec), +} + +#[derive(Debug)] +pub enum UriParameter { + Transport(Transport), + User(UserParam), + Method(Method), + Ttl(u32), + Maddr(Vec), + Lr, + Other { + key: Vec, + value: Option>, + }, +} + +#[derive(Debug)] +pub struct SipUri { + pub user_info: Option, + pub host_port: HostPort, + pub params: Vec, + pub headers: Option, +} + +#[derive(Debug)] +pub enum Uri { + Sip(SipUri), + Sips(SipUri), + Tel(Vec), + Urn(Vec), + Generic { scheme: Scheme, path: UriPath }, +} + +pub type Version = (u8, u8); + +#[derive(Debug)] +pub struct RequestLine { + pub method: Method, + pub uri: Uri, + pub version: Version, +} + +#[derive(Debug)] +pub struct StatusLine { + pub version: Version, + pub code: u16, + pub reason: Vec, +} + +#[derive(Debug)] +pub enum TopLine { + RequestLine(RequestLine), + StatusLine(StatusLine), +}