From 8d410b9a57147cbb4a1a2eb076c8f6926f415d5a Mon Sep 17 00:00:00 2001 From: Richard Whitehouse Date: Mon, 20 Nov 2017 00:08:04 +0000 Subject: [PATCH] Split up parser --- src/{parser.rs => parser/header.rs} | 542 +--------------------------- src/parser/mod.rs | 503 ++++++++++++++++++++++++++ src/parser/top_line.rs | 45 +++ 3 files changed, 549 insertions(+), 541 deletions(-) rename src/{parser.rs => parser/header.rs} (57%) create mode 100644 src/parser/mod.rs create mode 100644 src/parser/top_line.rs diff --git a/src/parser.rs b/src/parser/header.rs similarity index 57% rename from src/parser.rs rename to src/parser/header.rs index 81a9131..c954219 100644 --- a/src/parser.rs +++ b/src/parser/header.rs @@ -1,526 +1,7 @@ -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, UriHeader, UriHeaders, SipUri, - AbsoluteUri, Uri, HierarchicalPath, Authority, UriPath, UserInfo, AbsolutePath, - Scheme, Method, Transport, UserParam, Version, RequestLine, StatusLine, TopLine, - Header, MediaMainType, MediaType, MediaFullType, MediaParameter, MediaRange, - GenericParam, AcceptParam, AcceptRange, Coding, Encoding, LanguageTag, LanguageRange, - Language, AlertParam, Qop, AuthenticationInfo, AuthParam, Algorithm, DigestResponse, - Credentials, CallId, Purpose, InfoParam, Info, NameAddr, ContactParam, Target, - Contact, DispositionType, Handling, DispositionParam, ContentCoding, Day, Month, Date, - Time, DateTime, ErrorUri, FromParam, From, Priority, Domain, DigestChallenge, - Challenge, OptionTag, Route, ReplyTo, RetryAfterParam, RetryAfter, Server, ToParam, - To, Protocol, SentProtocol, Received, ViaParam, Via, WarningAgent, Warning}; - -pub fn float(input: &[u8]) -> IResult<&[u8], f32> { - flat_map!(input, - recognize!(tuple!(opt!(alt!(tag!("+") | tag!("-"))), - alt!(delimited!(digits, tag!("."), opt!(digits)) | - delimited!(opt!(digits), tag!("."), digits) | - digits), - opt!(complete!(tuple!(alt!(tag!("e") | tag!("E")), - opt!(alt!(tag!("+") | tag!("-"))), - digit))))), - parse_to!(f32)) -} - -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; - - 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 - { - 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)); - ); -); - -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'~' -} - -fn is_word(c: u8) -> bool { - is_token(c) || c == b'(' || c == b')' || c == b'<' || c == b'>' || c == b':' || - c == b'\\' || c == b'"' || c == b'/' || c == b'[' || c == b']' || - c == b'?' || c == b'{' || c == b'}' -} - -fn is_lhex_digit(c: u8) -> bool { - nom::is_digit(c) || (c >= b'a' && c <= b'f') -} - -named!(token>, many1!(take_1!(is_token))); -named!(word>, many1!(take_1!(is_word))); - -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| Method::Extension(method) } -)); - -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)); -named!(lhex<&[u8], u8>, take_1!(is_lhex_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!(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"."))))); - -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!(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| Transport::Other(token) } -)); - -named!(user_param, alt!( - tag!("phone") => { |_| UserParam::Phone } | - tag!("ip") => { |_| UserParam::Ip } | - token => { |token| UserParam::Other(token) })); - -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))); - -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!(uri_header<&[u8], UriHeader>, separated_pair!( - hname, - tag!(b"="), - hvalue)); - -named!(uri_headers<&[u8], UriHeaders>, preceded!( - char!('?'), - separated_nonempty_list!( - tag!(b"&"), - uri_header))); - -type _SipUri = (Option, HostPort, Vec, Option); - -named!(sip_uri<&[u8], _SipUri>, preceded!( - tag!("sip:"), - tuple!( - opt!(userinfo), - hostport, - uri_parameters, - opt!(uri_headers) - ))); - -named!(sips_uri<&[u8], _SipUri>, preceded!( - tag!("sips:"), - tuple!( - opt!(userinfo), - hostport, - uri_parameters, - opt!(uri_headers) - ))); - -type _SipUriNp = (Option, HostPort); - -named!(sip_uri_np<&[u8], _SipUriNp>, preceded!( - tag!("sip:"), - tuple!( - opt!(userinfo), - hostport - ))); - -named!(sips_uri_np<&[u8], _SipUriNp>, preceded!( - tag!("sips:"), - tuple!( - opt!(userinfo), - hostport - ))); - -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], AbsoluteUri>, separated_pair!( - scheme, - char!(':'), - alt!( - hier_part => { |(h,q)| UriPath::Hierarchical { path: h, query: q } } | - opaque_part => { |(b, o)| UriPath::Opaque(b, o) } - ))); - -named!(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 } } -)); - -// Variant without URI parameters or header parameters -named!(uri_np<&[u8], Uri>, alt!( - sip_uri_np => { |(u, hp)| Uri::Sip(SipUri { - user_info: u, - host_port: hp, - params: vec![], - headers: None }) } | - sips_uri_np => { |(u, hp)| Uri::Sips(SipUri { - user_info: u, - host_port: hp, - params: vec![], - headers: None }) } | - 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!(" "), - 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 - })})); +use parser::*; /// Headers -fn is_spacetab(c: u8) -> bool { - c == b' ' || c == b'\t' -} - -fn is_whitespace(c: u8) -> bool { - is_spacetab(c) -} - -named!(spacetab, take_1!(is_spacetab)); - -named!(crlf, tag!(b"\r\n")); - -named!(lws<(Option<(Vec, &[u8])>, Vec)>, pair!( - opt!(pair!(many0!(take_1!(is_whitespace)), crlf)), - many1!(take_1!(is_whitespace)))); - -named!(sws, &[u8])>, Vec)>>, opt!(lws)); - -named!(hcolon, delimited!( - many0!(spacetab), - tag!(b":"), - sws)); - // Accept Header named!(media_main_type, alt!( @@ -541,27 +22,6 @@ use self::token as media_subtype; use self::token as media_attribute; -fn is_qdtext_char(c: u8) -> bool { - c == 0x21 || (c >= 0x23 && c <= 0x5b) || (c >= 0x5d && c <= 0x7e) -} - -named!(qdtext, take_1!(is_qdtext_char)); - -fn is_qdpair_char(c: u8) -> bool { - c <= 0x09 || c == 0x0B || c == 0x0C || (c >= 0x0E && c <= 0x7f) -} - -named!(qdpair, preceded!( - tag!("\\"), - take_1!(is_qdpair_char))); - -named!(quoted_string>, delimited!( - tag!(b"\""), - many0!(alt!( - qdtext | - qdpair)), - tag!(b"\""))); - named!(media_value>, alt!( token | quoted_string)); diff --git a/src/parser/mod.rs b/src/parser/mod.rs new file mode 100644 index 0000000..84dc80a --- /dev/null +++ b/src/parser/mod.rs @@ -0,0 +1,503 @@ +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; + +macro_rules! take_1 ( + ($input:expr, $submac:ident!( $($args:tt)* )) => ( + { + use nom::Slice; + let input = $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 + { + 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)); + ); +); + +mod header; +mod top_line; + +pub use parser::header::header; +pub use parser::top_line::top_line; + +use types::{PathSegment, HostPort, Host, Hostname, UriParameter, UriHeader, UriHeaders, SipUri, + AbsoluteUri, Uri, HierarchicalPath, Authority, UriPath, UserInfo, AbsolutePath, + Scheme, Method, Transport, UserParam, Version, RequestLine, StatusLine, TopLine, + Header, MediaMainType, MediaType, MediaFullType, MediaParameter, MediaRange, + GenericParam, AcceptParam, AcceptRange, Coding, Encoding, LanguageTag, LanguageRange, + Language, AlertParam, Qop, AuthenticationInfo, AuthParam, Algorithm, DigestResponse, + Credentials, CallId, Purpose, InfoParam, Info, NameAddr, ContactParam, Target, + Contact, DispositionType, Handling, DispositionParam, ContentCoding, Day, Month, Date, + Time, DateTime, ErrorUri, FromParam, From, Priority, Domain, DigestChallenge, + Challenge, OptionTag, Route, ReplyTo, RetryAfterParam, RetryAfter, Server, ToParam, + To, Protocol, SentProtocol, Received, ViaParam, Via, WarningAgent, Warning}; + +pub fn float(input: &[u8]) -> IResult<&[u8], f32> { + flat_map!(input, + recognize!(tuple!(opt!(alt!(tag!("+") | tag!("-"))), + alt!(delimited!(digits, tag!("."), opt!(digits)) | + delimited!(opt!(digits), tag!("."), digits) | + digits), + opt!(complete!(tuple!(alt!(tag!("e") | tag!("E")), + opt!(alt!(tag!("+") | tag!("-"))), + digit))))), + parse_to!(f32)) +} + +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'-' +} + +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'~' +} + +fn is_word(c: u8) -> bool { + is_token(c) || c == b'(' || c == b')' || c == b'<' || c == b'>' || c == b':' || + c == b'\\' || c == b'"' || c == b'/' || c == b'[' || c == b']' || + c == b'?' || c == b'{' || c == b'}' +} + +fn is_lhex_digit(c: u8) -> bool { + nom::is_digit(c) || (c >= b'a' && c <= b'f') +} + +named!(token>, many1!(take_1!(is_token))); +named!(word>, many1!(take_1!(is_word))); + +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| Method::Extension(method) } +)); + +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)); +named!(lhex<&[u8], u8>, take_1!(is_lhex_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!(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"."))))); + +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!(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| Transport::Other(token) } +)); + +named!(user_param, alt!( + tag!("phone") => { |_| UserParam::Phone } | + tag!("ip") => { |_| UserParam::Ip } | + token => { |token| UserParam::Other(token) })); + +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))); + +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!(uri_header<&[u8], UriHeader>, separated_pair!( + hname, + tag!(b"="), + hvalue)); + +named!(uri_headers<&[u8], UriHeaders>, preceded!( + char!('?'), + separated_nonempty_list!( + tag!(b"&"), + uri_header))); + +type _SipUri = (Option, HostPort, Vec, Option); + +named!(sip_uri<&[u8], _SipUri>, preceded!( + tag!("sip:"), + tuple!( + opt!(userinfo), + hostport, + uri_parameters, + opt!(uri_headers) + ))); + +named!(sips_uri<&[u8], _SipUri>, preceded!( + tag!("sips:"), + tuple!( + opt!(userinfo), + hostport, + uri_parameters, + opt!(uri_headers) + ))); + +type _SipUriNp = (Option, HostPort); + +named!(sip_uri_np<&[u8], _SipUriNp>, preceded!( + tag!("sip:"), + tuple!( + opt!(userinfo), + hostport + ))); + +named!(sips_uri_np<&[u8], _SipUriNp>, preceded!( + tag!("sips:"), + tuple!( + opt!(userinfo), + hostport + ))); + +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], AbsoluteUri>, separated_pair!( + scheme, + char!(':'), + alt!( + hier_part => { |(h,q)| UriPath::Hierarchical { path: h, query: q } } | + opaque_part => { |(b, o)| UriPath::Opaque(b, o) } + ))); + +named!(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 } } +)); + +// Variant without URI parameters or header parameters +named!(uri_np<&[u8], Uri>, alt!( + sip_uri_np => { |(u, hp)| Uri::Sip(SipUri { + user_info: u, + host_port: hp, + params: vec![], + headers: None }) } | + sips_uri_np => { |(u, hp)| Uri::Sips(SipUri { + user_info: u, + host_port: hp, + params: vec![], + headers: None }) } | + absolute_uri => { |(s, p)| Uri::Generic { + scheme: s, + path: p } } +)); + +fn is_spacetab(c: u8) -> bool { + c == b' ' || c == b'\t' +} + +fn is_whitespace(c: u8) -> bool { + is_spacetab(c) +} + +named!(spacetab, take_1!(is_spacetab)); + +named!(crlf, tag!(b"\r\n")); + +named!(lws<(Option<(Vec, &[u8])>, Vec)>, pair!( + opt!(pair!(many0!(take_1!(is_whitespace)), crlf)), + many1!(take_1!(is_whitespace)))); + +named!(sws, &[u8])>, Vec)>>, opt!(lws)); + +named!(hcolon, delimited!( + many0!(spacetab), + tag!(b":"), + sws)); + +fn is_qdtext_char(c: u8) -> bool { + c == 0x21 || (c >= 0x23 && c <= 0x5b) || (c >= 0x5d && c <= 0x7e) +} + +named!(qdtext, take_1!(is_qdtext_char)); + +fn is_qdpair_char(c: u8) -> bool { + c <= 0x09 || c == 0x0B || c == 0x0C || (c >= 0x0E && c <= 0x7f) +} + +named!(qdpair, preceded!( + tag!("\\"), + take_1!(is_qdpair_char))); + +named!(quoted_string>, delimited!( + tag!(b"\""), + many0!(alt!( + qdtext | + qdpair)), + tag!(b"\""))); diff --git a/src/parser/top_line.rs b/src/parser/top_line.rs new file mode 100644 index 0000000..18b4c01 --- /dev/null +++ b/src/parser/top_line.rs @@ -0,0 +1,45 @@ +use parser::*; + +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!(" "), + 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 + })})); -- 2.34.1