diff --git a/sip/msg_parse.rl b/sip/msg_parse.rl index 7619a41..1e9908e 100644 --- a/sip/msg_parse.rl +++ b/sip/msg_parse.rl @@ -1,9 +1,10 @@ +// -*-go-*- + package sip import ( -// "bytes" - "errors" - "fmt" + "errors" + "fmt" "github.com/jart/gosip/sdp" ) @@ -12,481 +13,483 @@ import ( // ParseMsg turns a a SIP message into a data structure. func ParseMsg(s string) (msg *Msg, err error) { - if s == "" { - return nil, errors.New("Empty SIP message") - } - return ParseMsgBytes([]byte(s)) + if s == "" { + return nil, errors.New("Empty SIP message") + } + return ParseMsgBytes([]byte(s)) } // ParseMsg turns a a SIP message byte slice into a data structure. func ParseMsgBytes(data []byte) (msg *Msg, err error) { - if data == nil { - return nil, nil - } - msg = new(Msg) - viap := &msg.Via - routep := &msg.Route - rroutep := &msg.RecordRoute - contactp := &msg.Contact - cs := 0 - p := 0 - pe := len(data) - line := 1 - linep := 0 - buf := make([]byte, len(data)) - amt := 0 - mark := 0 - clen := 0 - ctype := "" -// var b1 string - var hex byte - - %%{ - action break { - fbreak; - } - - action mark { - mark = p - } - - action start { - amt = 0 - } - - action append { - buf[amt] = fc - amt++ - } - - action collapse { - amt = appendCollapse(buf, amt, fc) - } - - action hexHi { - hex = unhex(fc) * 16 - } - - action hexLo { - hex += unhex(fc) - buf[amt] = hex - amt++ - } - - action lower { - amt = appendLower(buf, amt, fc) - } - - action Method { - msg.Method = string(data[mark:p]) - } - - action VersionMajor { - msg.VersionMajor = msg.VersionMajor * 10 + (fc - 0x30) - } - - action VersionMinor { - msg.VersionMinor = msg.VersionMinor * 10 + (fc - 0x30) - } - - action RequestURI { - msg.Request, err = ParseURIBytes(data[mark:p]) - if err != nil { return nil, err } - } - - action StatusCode { - msg.Status = msg.Status * 10 + (int(fc) - 0x30) - } - - action ReasonPhrase { - msg.Phrase = string(buf[0:amt]) - } - - action extHeaderName { - b1 = string(bytes.ToLower(data[mark:p])) - } - - action extHeaderValue { - if msg.Headers == nil { - msg.Headers = Headers{} - } - msg.Headers[b1] = string(data[mark:p]) - } - - action Accept { - msg.Accept = string(data[mark:p]) - } - - action AcceptValue { - msg.AcceptContact = string(data[mark:p]) - } - - action AcceptEncoding { - msg.AcceptEncoding = string(data[mark:p]) - } - - action AcceptLanguage { - msg.AcceptLanguage = string(data[mark:p]) - } - - action Allow { - msg.Allow = string(data[mark:p]) - } - - action AllowEvents { - msg.AllowEvents = string(data[mark:p]) - } - - action AlertInfo { - msg.AlertInfo = string(data[mark:p]) - } - - action AuthenticationInfo { - msg.AuthenticationInfo = string(data[mark:p]) - } - - action Authorization { - msg.Authorization = string(data[mark:p]) - } - - action CallID { - msg.CallID = string(data[mark:p]) - } - - action Contact { - *contactp, err = ParseAddr(string(data[mark:p])) - if err != nil { return nil, err } - for *contactp != nil { contactp = &(*contactp).Next } - } - - action ContentDisposition { - msg.ContentDisposition = string(data[mark:p]) - } - - action ContentLanguage { - msg.ContentLanguage = string(data[mark:p]) - } - - action ContentLength { - clen = clen * 10 + (int(fc) - 0x30) - } - - action ContentEncoding { - msg.ContentEncoding = string(data[mark:p]) - } - - action ContentType { - ctype = string(data[mark:p]) - } - - action CSeq { - msg.CSeq = msg.CSeq * 10 + (int(fc) - 0x30) - } - - action CSeqMethod { - msg.CSeqMethod = string(data[mark:p]) - } - - action CallInfo { - msg.CallInfo = string(data[mark:p]) - } - - action Date { - msg.Date = string(data[mark:p]) - } - - action ErrorInfo { - msg.ErrorInfo = string(data[mark:p]) - } - - action Event { - msg.Event = string(data[mark:p]) - } - - action Expires { - msg.Expires = msg.Expires * 10 + (int(fc) - 0x30) - } - - action From { - msg.From, err = ParseAddr(string(data[mark:p])) - if err != nil { return nil, err } - } - - action InReplyTo { - msg.InReplyTo = string(data[mark:p]) - } - - action MaxForwardsZero { - msg.MaxForwards = 0 - } - - action MaxForwards { - msg.MaxForwards = msg.MaxForwards * 10 + (int(fc) - 0x30) - } - - action MinExpires { - msg.MinExpires = msg.MinExpires * 10 + (int(fc) - 0x30) - } - - action ReplyTo { - msg.ReplyTo = string(data[mark:p]) - } - - action MIMEVersion { - msg.MIMEVersion = string(data[mark:p]) - } - - action Organization { - msg.Organization = string(data[mark:p]) - } - - action PAssertedIdentity { - msg.PAssertedIdentity, err = ParseAddr(string(data[mark:p])) - if err != nil { return nil, err } - } - - action Priority { - msg.Priority = string(data[mark:p]) - } - - action ProxyAuthenticate { - msg.ProxyAuthenticate = string(data[mark:p]) - } - - action ProxyAuthorization { - msg.ProxyAuthorization = string(data[mark:p]) - } - - action ProxyRequire { - msg.ProxyRequire = string(data[mark:p]) - } - - action RecordRoute { - *rroutep, err = ParseAddr(string(data[mark:p])) - if err != nil { return nil, err } - for *rroutep != nil { rroutep = &(*rroutep).Next } - } - - action ReferTo { - msg.ReferTo = string(data[mark:p]) - } - - action ReferredBy { - msg.ReferredBy = string(data[mark:p]) - } - - action RemotePartyID { - msg.RemotePartyID, err = ParseAddr(string(data[mark:p])) - if err != nil { return nil, err } - } - - action Require { - msg.Require = string(data[mark:p]) - } - - action RetryAfter { - msg.RetryAfter = string(data[mark:p]) - } - - action Route { - *routep, err = ParseAddr(string(data[mark:p])) - if err != nil { return nil, err } - for *routep != nil { routep = &(*routep).Next } - } - - action Server { - msg.Server = string(data[mark:p]) - } - - action Subject { - msg.Subject = string(data[mark:p]) - } - - action Supported { - msg.Supported = string(data[mark:p]) - } - - action Timestamp { - msg.Timestamp = string(data[mark:p]) - } - - action To { - msg.To, err = ParseAddr(string(data[mark:p])) - if err != nil { return nil, err } - } - - action Unsupported { - msg.Unsupported = string(data[mark:p]) - } - - action UserAgent { - msg.UserAgent = string(data[mark:p]) - } - - action Via { - *viap, err = ParseVia(string(data[mark:p])) - if err != nil { return nil, err } - for *viap != nil { viap = &(*viap).Next } - } - - action Warning { - msg.Warning = string(data[mark:p]) - } - - action WWWAuthenticate { - msg.WWWAuthenticate = string(data[mark:p]) - } - - action lookAheadWSP { p + 2 < pe && (data[p+2] == ' ' || data[p+2] == '\t') } - - # https://tools.ietf.org/html/rfc2234 - SP = " "; - HTAB = "\t"; - CR = "\r"; - LF = "\n" @{ line++; linep = p; }; - CRLF = CR LF; - WSP = SP | HTAB; - LWS = ( WSP* ( CR when lookAheadWSP ) LF )? WSP+; - SWS = LWS?; - UTF8_NONASCII = 0x80..0xFD; - UTF8 = 0x21..0x7F | UTF8_NONASCII; - UTF8_TRIM = ( UTF8+ (LWS* UTF8)* ) >start @collapse; - - # https://tools.ietf.org/html/rfc3261#section-25.1 - reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," ; - mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" ; - unreserved = alpha | digit | mark ; - HCOLON = WSP* ":" SWS ; - tokenc = alpha | digit | "-" | "." | "!" | "%" | "*" | "_" - | "+" | "`" | "'" | "~" ; - token = tokenc+ >mark ; - separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" - | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP - | HTAB ; - wordc = alpha | digit | "-" | "." | "!" | "%" | "*" | "_" - | "+" | "`" | "'" | "~" | "(" | ")" | "<" | ">" | ":" - | "\\" | "\"" | "/" | "[" | "]" | "?" | "{" | "}" ; - word = wordc+ ; - STAR = SWS "*" SWS ; - SLASH = SWS "/" SWS ; - EQUAL = SWS "=" SWS ; - LPAREN = SWS "(" SWS ; - RPAREN = SWS ")" SWS ; - RAQUOT = ">" SWS ; - LAQUOT = SWS "<" ; - COMMA = SWS "," SWS ; - SEMI = SWS ";" SWS ; - COLON = SWS ":" SWS ; - LDQUOT = SWS "\"" ; - RDQUOT = "\"" SWS ; - ctext = 0x21..0x27 | 0x2A..0x5B | 0x5D..0x7E | UTF8_NONASCII | LWS ; - quoted_pair = "\\" ( 0x00..0x09 | 0x0B..0x0C | 0x0E..0x7F ) ; - comment = LPAREN ( ctext | quoted_pair )* <: RPAREN ; # TODO(jart): Nested parens - qdtext = LWS | 0x21 | 0x23..0x5B | 0x5D..0x7E | UTF8_NONASCII ; - quoted_string = SWS "\"" ( qdtext | quoted_pair )* <: "\"" ; - escaped = "%" ( xdigit @hexHi ) ( xdigit @hexLo ) ; - uric = reserved | unreserved | escaped ; - uric_no_slash = unreserved | escaped | ";" | "?" | ":" | "@" | "&" | "=" - | "+" | "$" | "," ; - reasonc = reserved | unreserved | UTF8_NONASCII | SP | HTAB ; - reasonmc = escaped | ( reasonc @append ) ; - cid = word ( "@" word )? ; - - Method = token %Method; - SIPVersionNo = digit+ @VersionMajor "." digit+ @VersionMinor; - RequestURI = ^SP+ >mark %RequestURI; - StatusCode = ( digit @StatusCode ) {3}; - ReasonPhrase = reasonmc+ >start %ReasonPhrase; - hval = ( UTF8 | LWS )* >mark; - extHeader = token %extHeaderName HCOLON hval %extHeaderValue; - - # http://www.iana.org/assignments/sip-parameters/sip-parameters.xhtml - stdHeader = "Accept"i HCOLON hval %Accept - | ("Accept-Contact"i | "a"i) HCOLON hval %AcceptValue - | "Accept-Encoding"i HCOLON hval %AcceptEncoding - | "Accept-Language"i HCOLON hval %AcceptLanguage - | ("Allow"i | "u"i) HCOLON hval %Allow - | ("Allow-Events"i | "u"i) HCOLON hval %AllowEvents - | "Alert-Info"i HCOLON hval %AlertInfo - | "Authentication-Info"i HCOLON hval %AuthenticationInfo - | "Authorization"i HCOLON hval %Authorization - | ("Call-ID"i | "i"i) HCOLON cid >mark %CallID - | ("Contact"i | "m"i) HCOLON hval %Contact - | "Content-Disposition"i HCOLON hval %ContentDisposition - | "Content-Language"i HCOLON hval %ContentLanguage - | ("Content-Length"i | "l"i) HCOLON digit+ @ContentLength - | ("Content-Encoding"i | "e"i) HCOLON hval %ContentEncoding - | ("Content-Type"i | "c"i) HCOLON hval %ContentType - | "CSeq"i HCOLON (digit+ @CSeq) LWS token >mark %CSeqMethod - | "Call-Info"i HCOLON hval %CallInfo - | "Date"i HCOLON hval %Date - | "Error-Info"i HCOLON hval %ErrorInfo - | ("Event"i | "o"i) HCOLON hval %Event - | ("Expires"i | "l"i) HCOLON digit+ @Expires - | ("From"i | "f"i) HCOLON hval %From - | "In-Reply-To"i HCOLON hval %InReplyTo - | ("Max-Forwards"i | "l"i) HCOLON digit+ >MaxForwardsZero @MaxForwards - | ("Min-Expires"i | "l"i) HCOLON digit+ @MinExpires - | "Reply-To"i HCOLON hval %ReplyTo - | "MIME-Version"i HCOLON hval %MIMEVersion - | "Organization"i HCOLON hval %Organization - | "P-Asserted-Identity"i HCOLON hval %PAssertedIdentity - | "Priority"i HCOLON hval %Priority - | "Proxy-Authenticate"i HCOLON hval %ProxyAuthenticate - | "Proxy-Authorization"i HCOLON hval %ProxyAuthorization - | "Proxy-Require"i HCOLON hval %ProxyRequire - | "Record-Route"i HCOLON hval %RecordRoute - | ("Refer-To"i | "r"i) HCOLON hval %ReferTo - | ("Referred-By"i | "b"i) HCOLON hval %ReferredBy - | "Remote-Party-ID"i HCOLON hval %RemotePartyID - | "Require"i HCOLON hval %Require - | "Retry-After"i HCOLON hval %RetryAfter - | "Route"i HCOLON hval %Route - | "Server"i HCOLON hval %Server - | ("Subject"i | "s"i) HCOLON hval %Subject - | ("Supported"i | "k"i) HCOLON hval %Supported - | "Timestamp"i HCOLON hval %Timestamp - | ("To"i | "t"i) HCOLON hval %To - | "Unsupported"i HCOLON hval %Unsupported - | "User-Agent"i HCOLON hval %UserAgent - | ("Via"i | "v"i) HCOLON hval %Via - | "Warning"i HCOLON hval %Warning - | "WWW-Authenticate"i HCOLON hval %WWWAuthenticate - ; - - header = stdHeader CRLF; - headers = header* CR LF @break; - SIPVersion = "SIP/" SIPVersionNo; - RequestLine = Method SP RequestURI SP SIPVersion CRLF; - StatusLine = SIPVersion SP StatusCode SP ReasonPhrase CRLF; - Request = RequestLine headers; - Response = StatusLine headers; - main := Request | Response; - - write init; - write exec; - }%% - - if cs < msg_first_final { - if p == pe { - return nil, errors.New(fmt.Sprintf("Incomplete SIP message: %s", data)) - } else { - return nil, errors.New(fmt.Sprintf("Error in SIP message at line %d offset %d:\n%s", line, p - linep, data)) - } - } - - if clen > 0 { - if clen != len(data) - p { - return nil, errors.New(fmt.Sprintf("Content-Length incorrect: %d != %d", clen, len(data) - p)) - } - if ctype == sdp.ContentType { - ms, err := sdp.Parse(string(data[p:len(data)])) - if err != nil { return nil, err } - msg.Payload = ms - } else { - msg.Payload = &MiscPayload{T: ctype, D: data[p:len(data)]} - } - } - - return msg, nil + if data == nil { + return nil, nil + } + msg = new(Msg) + viap := &msg.Via + routep := &msg.Route + rroutep := &msg.RecordRoute + contactp := &msg.Contact + cs := 0 + p := 0 + pe := len(data) + line := 1 + linep := 0 + buf := make([]byte, len(data)) + amt := 0 + mark := 0 + clen := 0 + ctype := "" + // var b1 string + var hex byte + + %%{ + action break { + fbreak; + } + + action mark { + mark = p + } + + action start { + amt = 0 + } + + action append { + buf[amt] = fc + amt++ + } + + action collapse { + amt = appendCollapse(buf, amt, fc) + } + + action hexHi { + hex = unhex(fc) * 16 + } + + action hexLo { + hex += unhex(fc) + buf[amt] = hex + amt++ + } + + action lower { + amt = appendLower(buf, amt, fc) + } + + action Method { + msg.Method = string(data[mark:p]) + } + + action VersionMajor { + msg.VersionMajor = msg.VersionMajor * 10 + (fc - 0x30) + } + + action VersionMinor { + msg.VersionMinor = msg.VersionMinor * 10 + (fc - 0x30) + } + + action RequestURI { + msg.Request, err = ParseURIBytes(data[mark:p]) + if err != nil { return nil, err } + } + + action StatusCode { + msg.Status = msg.Status * 10 + (int(fc) - 0x30) + } + + action ReasonPhrase { + msg.Phrase = string(buf[0:amt]) + } + + action extHeaderName { + b1 = string(bytes.ToLower(data[mark:p])) + } + + action extHeaderValue { + if msg.Headers == nil { + msg.Headers = Headers{} + } + msg.Headers[b1] = string(data[mark:p]) + } + + action Accept { + msg.Accept = string(data[mark:p]) + } + + action AcceptValue { + msg.AcceptContact = string(data[mark:p]) + } + + action AcceptEncoding { + msg.AcceptEncoding = string(data[mark:p]) + } + + action AcceptLanguage { + msg.AcceptLanguage = string(data[mark:p]) + } + + action Allow { + msg.Allow = string(data[mark:p]) + } + + action AllowEvents { + msg.AllowEvents = string(data[mark:p]) + } + + action AlertInfo { + msg.AlertInfo = string(data[mark:p]) + } + + action AuthenticationInfo { + msg.AuthenticationInfo = string(data[mark:p]) + } + + action Authorization { + msg.Authorization = string(data[mark:p]) + } + + action CallID { + msg.CallID = string(data[mark:p]) + } + + action Contact { + *contactp, err = ParseAddr(string(data[mark:p])) + if err != nil { return nil, err } + for *contactp != nil { contactp = &(*contactp).Next } + } + + action ContentDisposition { + msg.ContentDisposition = string(data[mark:p]) + } + + action ContentLanguage { + msg.ContentLanguage = string(data[mark:p]) + } + + action ContentLength { + clen = clen * 10 + (int(fc) - 0x30) + } + + action ContentEncoding { + msg.ContentEncoding = string(data[mark:p]) + } + + action ContentType { + ctype = string(data[mark:p]) + } + + action CSeq { + msg.CSeq = msg.CSeq * 10 + (int(fc) - 0x30) + } + + action CSeqMethod { + msg.CSeqMethod = string(data[mark:p]) + } + + action CallInfo { + msg.CallInfo = string(data[mark:p]) + } + + action Date { + msg.Date = string(data[mark:p]) + } + + action ErrorInfo { + msg.ErrorInfo = string(data[mark:p]) + } + + action Event { + msg.Event = string(data[mark:p]) + } + + action Expires { + msg.Expires = msg.Expires * 10 + (int(fc) - 0x30) + } + + action From { + msg.From, err = ParseAddr(string(data[mark:p])) + if err != nil { return nil, err } + } + + action InReplyTo { + msg.InReplyTo = string(data[mark:p]) + } + + action MaxForwardsZero { + msg.MaxForwards = 0 + } + + action MaxForwards { + msg.MaxForwards = msg.MaxForwards * 10 + (int(fc) - 0x30) + } + + action MinExpires { + msg.MinExpires = msg.MinExpires * 10 + (int(fc) - 0x30) + } + + action ReplyTo { + msg.ReplyTo = string(data[mark:p]) + } + + action MIMEVersion { + msg.MIMEVersion = string(data[mark:p]) + } + + action Organization { + msg.Organization = string(data[mark:p]) + } + + action PAssertedIdentity { + msg.PAssertedIdentity, err = ParseAddr(string(data[mark:p])) + if err != nil { return nil, err } + } + + action Priority { + msg.Priority = string(data[mark:p]) + } + + action ProxyAuthenticate { + msg.ProxyAuthenticate = string(data[mark:p]) + } + + action ProxyAuthorization { + msg.ProxyAuthorization = string(data[mark:p]) + } + + action ProxyRequire { + msg.ProxyRequire = string(data[mark:p]) + } + + action RecordRoute { + *rroutep, err = ParseAddr(string(data[mark:p])) + if err != nil { return nil, err } + for *rroutep != nil { rroutep = &(*rroutep).Next } + } + + action ReferTo { + msg.ReferTo = string(data[mark:p]) + } + + action ReferredBy { + msg.ReferredBy = string(data[mark:p]) + } + + action RemotePartyID { + msg.RemotePartyID, err = ParseAddr(string(data[mark:p])) + if err != nil { return nil, err } + } + + action Require { + msg.Require = string(data[mark:p]) + } + + action RetryAfter { + msg.RetryAfter = string(data[mark:p]) + } + + action Route { + *routep, err = ParseAddr(string(data[mark:p])) + if err != nil { return nil, err } + for *routep != nil { routep = &(*routep).Next } + } + + action Server { + msg.Server = string(data[mark:p]) + } + + action Subject { + msg.Subject = string(data[mark:p]) + } + + action Supported { + msg.Supported = string(data[mark:p]) + } + + action Timestamp { + msg.Timestamp = string(data[mark:p]) + } + + action To { + msg.To, err = ParseAddr(string(data[mark:p])) + if err != nil { return nil, err } + } + + action Unsupported { + msg.Unsupported = string(data[mark:p]) + } + + action UserAgent { + msg.UserAgent = string(data[mark:p]) + } + + action Via { + *viap, err = ParseVia(string(data[mark:p])) + if err != nil { return nil, err } + for *viap != nil { viap = &(*viap).Next } + } + + action Warning { + msg.Warning = string(data[mark:p]) + } + + action WWWAuthenticate { + msg.WWWAuthenticate = string(data[mark:p]) + } + + action lookAheadWSP { + p + 2 < pe && (data[p+2] == ' ' || data[p+2] == '\t') + } + + # https://tools.ietf.org/html/rfc2234 + SP = " "; + HTAB = "\t"; + CR = "\r"; + LF = "\n" @{ line++; linep = p; }; + CRLF = CR LF; + WSP = SP | HTAB; + LWS = ( WSP* ( CR when lookAheadWSP ) LF )? WSP+; + SWS = LWS?; + UTF8_NONASCII = 0x80..0xFD; + UTF8 = 0x21..0x7F | UTF8_NONASCII; + UTF8_TRIM = ( UTF8+ (LWS* UTF8)* ) >start @collapse; + + # https://tools.ietf.org/html/rfc3261#section-25.1 + reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," ; + mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" ; + unreserved = alpha | digit | mark ; + HCOLON = WSP* ":" SWS ; + tokenc = alpha | digit | "-" | "." | "!" | "%" | "*" | "_" + | "+" | "`" | "'" | "~" ; + token = tokenc+ >mark ; + separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" + | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP + | HTAB ; + wordc = alpha | digit | "-" | "." | "!" | "%" | "*" | "_" + | "+" | "`" | "'" | "~" | "(" | ")" | "<" | ">" | ":" + | "\\" | "\"" | "/" | "[" | "]" | "?" | "{" | "}" ; + word = wordc+ ; + STAR = SWS "*" SWS ; + SLASH = SWS "/" SWS ; + EQUAL = SWS "=" SWS ; + LPAREN = SWS "(" SWS ; + RPAREN = SWS ")" SWS ; + RAQUOT = ">" SWS ; + LAQUOT = SWS "<" ; + COMMA = SWS "," SWS ; + SEMI = SWS ";" SWS ; + COLON = SWS ":" SWS ; + LDQUOT = SWS "\"" ; + RDQUOT = "\"" SWS ; + ctext = 0x21..0x27 | 0x2A..0x5B | 0x5D..0x7E | UTF8_NONASCII | LWS ; + quoted_pair = "\\" ( 0x00..0x09 | 0x0B..0x0C | 0x0E..0x7F ) ; + comment = LPAREN ( ctext | quoted_pair )* <: RPAREN ; # TODO(jart): Nested parens + qdtext = LWS | 0x21 | 0x23..0x5B | 0x5D..0x7E | UTF8_NONASCII ; + quoted_string = SWS "\"" ( qdtext | quoted_pair )* <: "\"" ; + escaped = "%" ( xdigit @hexHi ) ( xdigit @hexLo ) ; + uric = reserved | unreserved | escaped ; + uric_no_slash = unreserved | escaped | ";" | "?" | ":" | "@" | "&" | "=" + | "+" | "$" | "," ; + reasonc = reserved | unreserved | UTF8_NONASCII | SP | HTAB ; + reasonmc = escaped | ( reasonc @append ) ; + cid = word ( "@" word )? ; + + Method = token %Method; + SIPVersionNo = digit+ @VersionMajor "." digit+ @VersionMinor; + RequestURI = ^SP+ >mark %RequestURI; + StatusCode = ( digit @StatusCode ) {3}; + ReasonPhrase = reasonmc+ >start %ReasonPhrase; + hval = ( UTF8 | LWS )* >mark; + extHeader = token %extHeaderName HCOLON hval %extHeaderValue; + + # http://www.iana.org/assignments/sip-parameters/sip-parameters.xhtml + stdHeader = "Accept"i HCOLON hval %Accept + | ("Accept-Contact"i | "a"i) HCOLON hval %AcceptValue + | "Accept-Encoding"i HCOLON hval %AcceptEncoding + | "Accept-Language"i HCOLON hval %AcceptLanguage + | ("Allow"i | "u"i) HCOLON hval %Allow + | ("Allow-Events"i | "u"i) HCOLON hval %AllowEvents + | "Alert-Info"i HCOLON hval %AlertInfo + | "Authentication-Info"i HCOLON hval %AuthenticationInfo + | "Authorization"i HCOLON hval %Authorization + | ("Call-ID"i | "i"i) HCOLON cid >mark %CallID + | ("Contact"i | "m"i) HCOLON hval %Contact + | "Content-Disposition"i HCOLON hval %ContentDisposition + | "Content-Language"i HCOLON hval %ContentLanguage + | ("Content-Length"i | "l"i) HCOLON digit+ @ContentLength + | ("Content-Encoding"i | "e"i) HCOLON hval %ContentEncoding + | ("Content-Type"i | "c"i) HCOLON hval %ContentType + | "CSeq"i HCOLON (digit+ @CSeq) LWS token >mark %CSeqMethod + | "Call-Info"i HCOLON hval %CallInfo + | "Date"i HCOLON hval %Date + | "Error-Info"i HCOLON hval %ErrorInfo + | ("Event"i | "o"i) HCOLON hval %Event + | ("Expires"i | "l"i) HCOLON digit+ @Expires + | ("From"i | "f"i) HCOLON hval %From + | "In-Reply-To"i HCOLON hval %InReplyTo + | ("Max-Forwards"i | "l"i) HCOLON digit+ >MaxForwardsZero @MaxForwards + | ("Min-Expires"i | "l"i) HCOLON digit+ @MinExpires + | "Reply-To"i HCOLON hval %ReplyTo + | "MIME-Version"i HCOLON hval %MIMEVersion + | "Organization"i HCOLON hval %Organization + | "P-Asserted-Identity"i HCOLON hval %PAssertedIdentity + | "Priority"i HCOLON hval %Priority + | "Proxy-Authenticate"i HCOLON hval %ProxyAuthenticate + | "Proxy-Authorization"i HCOLON hval %ProxyAuthorization + | "Proxy-Require"i HCOLON hval %ProxyRequire + | "Record-Route"i HCOLON hval %RecordRoute + | ("Refer-To"i | "r"i) HCOLON hval %ReferTo + | ("Referred-By"i | "b"i) HCOLON hval %ReferredBy + | "Remote-Party-ID"i HCOLON hval %RemotePartyID + | "Require"i HCOLON hval %Require + | "Retry-After"i HCOLON hval %RetryAfter + | "Route"i HCOLON hval %Route + | "Server"i HCOLON hval %Server + | ("Subject"i | "s"i) HCOLON hval %Subject + | ("Supported"i | "k"i) HCOLON hval %Supported + | "Timestamp"i HCOLON hval %Timestamp + | ("To"i | "t"i) HCOLON hval %To + | "Unsupported"i HCOLON hval %Unsupported + | "User-Agent"i HCOLON hval %UserAgent + | ("Via"i | "v"i) HCOLON hval %Via + | "Warning"i HCOLON hval %Warning + | "WWW-Authenticate"i HCOLON hval %WWWAuthenticate + ; + + header = stdHeader CRLF; + headers = header* CR LF @break; + SIPVersion = "SIP/" SIPVersionNo; + RequestLine = Method SP RequestURI SP SIPVersion CRLF; + StatusLine = SIPVersion SP StatusCode SP ReasonPhrase CRLF; + Request = RequestLine headers; + Response = StatusLine headers; + main := Request | Response; + + write init; + write exec; + }%% + + if cs < msg_first_final { + if p == pe { + return nil, errors.New(fmt.Sprintf("Incomplete SIP message: %s", data)) + } else { + return nil, errors.New(fmt.Sprintf("Error in SIP message at line %d offset %d:\n%s", line, p - linep, data)) + } + } + + if clen > 0 { + if clen != len(data) - p { + return nil, errors.New(fmt.Sprintf("Content-Length incorrect: %d != %d", clen, len(data) - p)) + } + if ctype == sdp.ContentType { + ms, err := sdp.Parse(string(data[p:len(data)])) + if err != nil { return nil, err } + msg.Payload = ms + } else { + msg.Payload = &MiscPayload{T: ctype, D: data[p:len(data)]} + } + } + + return msg, nil } diff --git a/sip/uri_parse.rl b/sip/uri_parse.rl index 332b48a..de1e99f 100644 --- a/sip/uri_parse.rl +++ b/sip/uri_parse.rl @@ -1,9 +1,11 @@ +// -*-go-*- + package sip import ( - "bytes" - "errors" - "fmt" + "bytes" + "errors" + "fmt" ) %% machine uri; @@ -11,155 +13,155 @@ import ( // ParseURI turns a a SIP URI into a data structure. func ParseURI(s string) (uri *URI, err error) { - if s == "" { - return nil, errors.New("Empty URI") - } - return ParseURIBytes([]byte(s)) + if s == "" { + return nil, errors.New("Empty URI") + } + return ParseURIBytes([]byte(s)) } // ParseURI turns a a SIP URI byte slice into a data structure. func ParseURIBytes(data []byte) (uri *URI, err error) { - if data == nil { - return nil, nil - } - uri = new(URI) - cs := 0 - p := 0 - pe := len(data) - eof := len(data) - buf := make([]byte, len(data)) - amt := 0 - var b1, b2 string - var hex byte - - %%{ - action start { - amt = 0 - } - - action append { - buf[amt] = fc - amt++ - } - - action hexHi { - hex = unhex(fc) * 16 - } - - action hexLo { - hex += unhex(fc) - buf[amt] = hex - amt++ - } - - action scheme { - uri.Scheme = string(buf[0:amt]) - } - - action user { - uri.User = string(buf[0:amt]) - } - - action pass { - uri.Pass = string(buf[0:amt]) - } - - action host { - uri.Host = string(buf[0:amt]) - } - - action port { - uri.Port = uri.Port * 10 + uint16(fc - 0x30) - } - - action b1 { - b1 = string(buf[0:amt]) - amt = 0 - } - - action b2 { - b2 = string(buf[0:amt]) - amt = 0 - } - - action lower { - if 'A' <= fc && fc <= 'Z' { - buf[amt] = fc + 0x20 - } else { - buf[amt] = fc - } - amt++ - } - - action param { - if uri.Params == nil { - uri.Params = Params{} - } - uri.Params[b1] = b2 - } - - action header { - if uri.Headers == nil { - uri.Headers = URIHeaders{} - } - uri.Headers[b1] = b2 - } - - # Byte character definitions. - mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" ; - reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," ; - unreserved = alpha | digit | mark ; - ipv4 = digit | "." ; - ipv6 = xdigit | "." | ":" ; - hostname = alpha | digit | "-" | "." ; - tel = digit | "+" | "-" ; - schmchars = alpha | digit | "+" | "-" | "." ; - userchars = unreserved | "&" | "=" | "+" | "$" | "," | ";" | "?" | "/" ; - passchars = unreserved | "&" | "=" | "+" | "$" | "," ; - paramchars = unreserved | "[" | "]" | "/" | ":" | "&" | "+" | "$" ; - headerchars = unreserved | "[" | "]" | "/" | "?" | ":" | "+" | "$" ; - - # Multibyte character definitions. - escaped = "%" ( xdigit @hexHi ) ( xdigit @hexLo ) ; - userchar = escaped | ( userchars @append ) ; - passchar = escaped | ( passchars @append ) ; - paramchar = escaped | ( paramchars @lower ) ; - headerchar = escaped | ( headerchars @append ) ; - - # URI component definitions. - scheme = ( alpha schmchars* ) >start @lower %scheme ; - user = userchar+ >start %user ; - pass = passchar+ >start %pass ; - host6 = "[" ( ipv6+ >start @lower %host ) "]" ; - host = host6 | ( ( ipv4 | hostname | tel )+ >start @lower %host ) ; - port = digit+ @port ; - paramkey = paramchar+ >start >b2 %b1 ; - paramval = paramchar+ >start %b2 ; - param = space* ";" paramkey ( "=" paramval )? %param ; - headerkey = headerchar+ >start >b2 %b1 ; - headerval = headerchar+ >start %b2 ; - header = headerkey ( "=" headerval )? %header ; - headers = "?" header ( "&" header )* ; - userpass = user ( ":" pass )? ; - hostport = host ( ":" port )? ; - uriSansUser := space* scheme ":" hostport param* space* headers? space* ; - uriWithUser := space* scheme ":" userpass "@" hostport param* space* headers? space* ; - }%% - - %% write init; - if bytes.IndexByte(data, '@') == -1 { - cs = uri_en_uriSansUser; - } else { - cs = uri_en_uriWithUser; - } - %% write exec; - - if cs < uri_first_final { - if p == pe { - return nil, errors.New(fmt.Sprintf("Incomplete URI: %s", data)) - } else { - return nil, errors.New(fmt.Sprintf("Error in URI at pos %d: %s", p, data)) - } - } - return uri, nil + if data == nil { + return nil, nil + } + uri = new(URI) + cs := 0 + p := 0 + pe := len(data) + eof := len(data) + buf := make([]byte, len(data)) + amt := 0 + var b1, b2 string + var hex byte + + %%{ + action start { + amt = 0 + } + + action append { + buf[amt] = fc + amt++ + } + + action hexHi { + hex = unhex(fc) * 16 + } + + action hexLo { + hex += unhex(fc) + buf[amt] = hex + amt++ + } + + action scheme { + uri.Scheme = string(buf[0:amt]) + } + + action user { + uri.User = string(buf[0:amt]) + } + + action pass { + uri.Pass = string(buf[0:amt]) + } + + action host { + uri.Host = string(buf[0:amt]) + } + + action port { + uri.Port = uri.Port * 10 + uint16(fc - 0x30) + } + + action b1 { + b1 = string(buf[0:amt]) + amt = 0 + } + + action b2 { + b2 = string(buf[0:amt]) + amt = 0 + } + + action lower { + if 'A' <= fc && fc <= 'Z' { + buf[amt] = fc + 0x20 + } else { + buf[amt] = fc + } + amt++ + } + + action param { + if uri.Params == nil { + uri.Params = Params{} + } + uri.Params[b1] = b2 + } + + action header { + if uri.Headers == nil { + uri.Headers = URIHeaders{} + } + uri.Headers[b1] = b2 + } + + # Byte character definitions. + mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" ; + reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," ; + unreserved = alpha | digit | mark ; + ipv4 = digit | "." ; + ipv6 = xdigit | "." | ":" ; + hostname = alpha | digit | "-" | "." ; + tel = digit | "+" | "-" ; + schmchars = alpha | digit | "+" | "-" | "." ; + userchars = unreserved | "&" | "=" | "+" | "$" | "," | ";" | "?" | "/" ; + passchars = unreserved | "&" | "=" | "+" | "$" | "," ; + paramchars = unreserved | "[" | "]" | "/" | ":" | "&" | "+" | "$" ; + headerchars = unreserved | "[" | "]" | "/" | "?" | ":" | "+" | "$" ; + + # Multibyte character definitions. + escaped = "%" ( xdigit @hexHi ) ( xdigit @hexLo ) ; + userchar = escaped | ( userchars @append ) ; + passchar = escaped | ( passchars @append ) ; + paramchar = escaped | ( paramchars @lower ) ; + headerchar = escaped | ( headerchars @append ) ; + + # URI component definitions. + scheme = ( alpha schmchars* ) >start @lower %scheme ; + user = userchar+ >start %user ; + pass = passchar+ >start %pass ; + host6 = "[" ( ipv6+ >start @lower %host ) "]" ; + host = host6 | ( ( ipv4 | hostname | tel )+ >start @lower %host ) ; + port = digit+ @port ; + paramkey = paramchar+ >start >b2 %b1 ; + paramval = paramchar+ >start %b2 ; + param = space* ";" paramkey ( "=" paramval )? %param ; + headerkey = headerchar+ >start >b2 %b1 ; + headerval = headerchar+ >start %b2 ; + header = headerkey ( "=" headerval )? %header ; + headers = "?" header ( "&" header )* ; + userpass = user ( ":" pass )? ; + hostport = host ( ":" port )? ; + uriSansUser := space* scheme ":" hostport param* space* headers? space* ; + uriWithUser := space* scheme ":" userpass "@" hostport param* space* headers? space* ; + }%% + + %% write init; + if bytes.IndexByte(data, '@') == -1 { + cs = uri_en_uriSansUser; + } else { + cs = uri_en_uriWithUser; + } + %% write exec; + + if cs < uri_first_final { + if p == pe { + return nil, errors.New(fmt.Sprintf("Incomplete URI: %s", data)) + } else { + return nil, errors.New(fmt.Sprintf("Error in URI at pos %d: %s", p, data)) + } + } + return uri, nil }