Initial commit
authorRichard Whitehouse <github@richardwhiuk.com>
Sat, 28 Oct 2017 22:09:02 +0000 (23:09 +0100)
committerRichard Whitehouse <github@richardwhiuk.com>
Sat, 28 Oct 2017 22:11:52 +0000 (23:11 +0100)
Gather SIP message

Parse top line

.gitignore [new file with mode: 0644]
Cargo.toml [new file with mode: 0644]
src/codec.rs [new file with mode: 0644]
src/lib.rs [new file with mode: 0644]
src/parser.rs [new file with mode: 0644]
src/types.rs [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..6fe9427
--- /dev/null
@@ -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 (file)
index 0000000..2e97f2f
--- /dev/null
@@ -0,0 +1,12 @@
+[package]
+name = "sip"
+version = "0.1.0"
+authors = ["Richard Whitehouse <github@richardwhiuk.com>"]
+
+[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 (file)
index 0000000..94b1c23
--- /dev/null
@@ -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<Header, std::str::Utf8Error> {
+        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<PartialMessage>,
+    headers: Vec<UnparsedLine>,
+    top_line: Option<UnparsedLine>,
+    latest_header: Option<UnparsedLine>,
+}
+
+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<Option<Message>> {
+        loop {
+            println!("Gathering headers from {:?}", std::str::from_utf8(&buf));
+
+            let mut done = false;
+            let mut total = 0;
+
+            {
+                let mut search: Box<Iterator<Item = &u8>> = 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<Header>,
+    top_line: UnparsedLine,
+}
+
+impl PartialMessage {
+    fn new(top_line: UnparsedLine,
+           headers: &mut Vec<UnparsedLine>)
+           -> Result<PartialMessage, std::str::Utf8Error> {
+        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<Header>,
+    body: Option<BytesMut>,
+}
+
+impl Response {
+    fn as_bytes(&self) -> &[u8] {
+        return self.status_line.1.as_bytes();
+    }
+}
+
+#[derive(Debug)]
+struct Request {
+    request_line: (RequestLine, UnparsedLine),
+    headers: Vec<Header>,
+    body: Option<BytesMut>,
+}
+
+impl Request {
+    fn as_bytes(&self) -> &[u8] {
+        return self.request_line.1.as_bytes();
+    }
+}
+
+impl Message {
+    fn new(message: PartialMessage) -> Option<Message> {
+        Message::parse(message, None)
+    }
+
+    fn parse(message: PartialMessage, body: Option<BytesMut>) -> Option<Message> {
+        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> {
+        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<Option<Message>> {
+        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<TcpListener>,
+}
+
+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<Future<Item = (), Error = std::io::Error>> {
+
+        let mut future: Box<Future<Item = (), Error = std::io::Error>> = 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 (file)
index 0000000..974d78e
--- /dev/null
@@ -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 (file)
index 0000000..13c46ad
--- /dev/null
@@ -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<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<u8>>, many1!(alt!(
+    user_unreserved |
+    escaped)));
+
+named!(password<&[u8], Vec<u8>>, many1!(alt!(
+    password_unreserved |
+    escaped)));
+
+named!(userinfo<&[u8], (Vec<u8>, Option<Vec<u8>>)>, terminated!(
+        pair!(
+        user,
+        opt!(preceded!(tag!(b":"), password))),
+    tag!(b"@")));
+
+named!(domain_label<&[u8], Vec<u8>>, 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<u8>>, 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<u8>>, delimited!(
+    tag!(b"["),
+    many1!(take_1!(is_ipv6_char)),
+    tag!(b"]")));
+
+named!(host<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<Transport>, 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<UserParam>, 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<u8>>, many1!(alt!(
+    take_1!(is_param_unreserved) |
+    escaped)));
+
+fn number<O>(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::<O>() {
+                    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<UriParameter>, 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<UriParameter>>, 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<u8>>, many1!(alt!(
+    take_1!(is_header_unreserved) |
+    escaped)));
+
+named!(hvalue<&[u8], Vec<u8>>, 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<UserInfo>, HostPort, Vec<UriParameter>, Option<Headers>)>, preceded!(
+    tag!("sip:"),
+    tuple!(
+        opt!(userinfo),
+        hostport,
+        uri_parameters,
+        opt!(headers)
+        )));
+
+named!(sips_uri<&[u8], (Option<UserInfo>, HostPort, Vec<UriParameter>, Option<Headers>)>, 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<u8>>, 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<UserInfo>, 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<u8>>, 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<AbsolutePath>)>, preceded!(tag!("//"), pair!(
+    authority,
+    opt!(abs_path))));
+
+named!(query<&[u8], Vec<u8>>, many0!(uric));
+
+named!(hier_part<&[u8], (HierarchicalPath, Option<Vec<u8>>)>, 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<u8>)>, 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<Vec<u8>>, many0!(alt!(
+       escaped |
+       take_1!(is_reason_phrase_char))));
+
+named!(status_line<&[u8], (Version, u16, Vec<u8>)>, tuple!(
+       sip_version,
+       delimited!(
+               tag!(" "),
+               number,
+               tag!(" ")
+       ),
+       reason_phrase));
+
+named!(pub top_line<TopLine>, 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 (file)
index 0000000..b1b58ea
--- /dev/null
@@ -0,0 +1,133 @@
+pub type UserInfo = (Vec<u8>, Option<Vec<u8>>);
+
+pub type Hostname = (Vec<Vec<u8>>, Vec<u8>);
+
+#[derive(Debug)]
+pub enum Host {
+    Hostname(Hostname),
+    Ipv4Address(u8, u8, u8, u8),
+    Ipv6Address(Vec<u8>),
+}
+
+pub type HostPort = (Host, Option<u16>);
+
+#[derive(Debug)]
+pub enum Authority {
+    Server {
+        userinfo: Option<UserInfo>,
+        hostport: HostPort,
+    },
+    Name(Vec<u8>),
+}
+
+pub type PathSegment = (Vec<u8>, Option<Vec<u8>>);
+
+pub type AbsolutePath = Vec<PathSegment>;
+
+#[derive(Debug)]
+pub enum HierarchicalPath {
+    Network {
+        authority: Authority,
+        path: Option<AbsolutePath>,
+    },
+    Absolute(AbsolutePath),
+}
+
+#[derive(Debug)]
+pub enum UriPath {
+    Hierarchical {
+        path: HierarchicalPath,
+        query: Option<Vec<u8>>,
+    },
+    Opaque(u8, Vec<u8>),
+}
+
+pub type Scheme = (u8, Vec<u8>);
+
+pub type Header = (Vec<u8>, Vec<u8>);
+
+pub type Headers = (Header, Vec<Header>);
+
+#[derive(Debug)]
+pub enum Transport {
+    Udp,
+    Tcp,
+    Sctp,
+    Tls,
+    Other(Vec<u8>),
+}
+
+#[derive(Debug)]
+pub enum UserParam {
+    Phone,
+    Ip,
+    Other(Vec<u8>),
+}
+
+#[derive(Debug)]
+pub enum Method {
+    INVITE,
+    ACK,
+    OPTIONS,
+    BYE,
+    CANCEL,
+    REGISTER,
+    MESSAGE,
+    REFER,
+    SUBSCRIBE,
+    NOTIFY,
+    Extension(Vec<u8>),
+}
+
+#[derive(Debug)]
+pub enum UriParameter {
+    Transport(Transport),
+    User(UserParam),
+    Method(Method),
+    Ttl(u32),
+    Maddr(Vec<u8>),
+    Lr,
+    Other {
+        key: Vec<u8>,
+        value: Option<Vec<u8>>,
+    },
+}
+
+#[derive(Debug)]
+pub struct SipUri {
+    pub user_info: Option<UserInfo>,
+    pub host_port: HostPort,
+    pub params: Vec<UriParameter>,
+    pub headers: Option<Headers>,
+}
+
+#[derive(Debug)]
+pub enum Uri {
+    Sip(SipUri),
+    Sips(SipUri),
+    Tel(Vec<u8>),
+    Urn(Vec<u8>),
+    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<u8>,
+}
+
+#[derive(Debug)]
+pub enum TopLine {
+    RequestLine(RequestLine),
+    StatusLine(StatusLine),
+}