Browse Source

Some addr parsing in Ragel.

pull/2/head
Justine Alexandra Roberts Tunney 11 years ago
parent
commit
38c884720c
3 changed files with 8919 additions and 6416 deletions
  1. +8567
    -6284
      sip/msg_parse.go
  2. +167
    -131
      sip/msg_parse.rl
  3. +185
    -1
      sip/msg_test.go

+ 8567
- 6284
sip/msg_parse.go
File diff suppressed because it is too large
View File


+ 167
- 131
sip/msg_parse.rl View File

@ -3,10 +3,10 @@
package sip
import (
"bytes"
"errors"
"fmt"
"github.com/jart/gosip/sdp"
"strings"
)
%% machine msg;
@ -26,16 +26,18 @@ func ParseMsgBytes(data []byte) (msg *Msg, err error) {
return nil, nil
}
msg = new(Msg)
fromp := &msg.From
top := &msg.To
viap := &msg.Via
routep := &msg.Route
rroutep := &msg.RecordRoute
contactp := &msg.Contact
paidp := &msg.PAssertedIdentity
rpidp := &msg.RemotePartyID
cs := 0
p := 0
pe := len(data)
eof := len(data)
//stack := make([]int, 2)
//top := 0
line := 1
linep := 0
buf := make([]byte, len(data))
@ -43,9 +45,11 @@ func ParseMsgBytes(data []byte) (msg *Msg, err error) {
mark := 0
clen := 0
ctype := ""
var b1 string
var name string
var hex byte
var dest *string
var value *string
var addr *Addr
var addrpp ***Addr
%%{
action break {
@ -65,6 +69,15 @@ func ParseMsgBytes(data []byte) (msg *Msg, err error) {
amt++
}
action space {
buf[amt] = ' '
amt++
}
action erase {
amt--
}
action collapse {
amt = appendCollapse(buf, amt, fc)
}
@ -108,45 +121,78 @@ func ParseMsgBytes(data []byte) (msg *Msg, err error) {
msg.Phrase = string(buf[0:amt])
}
action header {
fgoto header;
action goto_avalue {
fhold;
fgoto avalue;
}
action hname {
b1 = string(bytes.ToLower(data[mark:p]))
action goto_header {
fgoto header;
}
action svalue {
action goto_svalue {
fhold;
fgoto svalue;
}
action svalueDone {{
action goto_xheader {
fhold;
fgoto xheader;
}
# Shorthand notation.
action gxh {
fhold;
fgoto xheader;
}
action store_name {
name = string(data[mark:p])
}
action store_value {{
b := data[mark:p - 1]
if dest != nil {
*dest = string(b)
if value != nil {
*value = string(b)
} else {
if msg.Headers == nil {
msg.Headers = Headers{}
}
msg.Headers[b1] = string(b)
msg.Headers[name] = string(b)
}
}}
action xheader {
dest = nil;
fhold;
fgoto xheader;
action store_addr {
// TODO(jart): Why does this fire multiple times?
if addr != nil {
**addrpp = addr
*addrpp = &addr.Next
addr = nil
}
}
action CallID {
msg.CallID = string(data[mark:p])
action new_addr {
addr = new(Addr)
}
action addr_display {
addr.Display = strings.TrimRight(string(buf[0:amt]), " \t\r\n")
}
action Contact {
*contactp, err = ParseAddr(string(data[mark:p]))
action addr_uri {
addr.Uri, err = ParseURIBytes(data[mark:p])
if err != nil { return nil, err }
for *contactp != nil { contactp = &(*contactp).Next }
}
action addr_param {
if addr.Params == nil {
addr.Params = Params{}
}
addr.Params[name] = string(buf[0:amt])
}
action CallID {
msg.CallID = string(data[mark:p])
}
action ContentLength {
@ -169,11 +215,6 @@ func ParseMsgBytes(data []byte) (msg *Msg, err error) {
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 MaxForwards {
msg.MaxForwards = msg.MaxForwards * 10 + (int(fc) - 0x30)
}
@ -182,33 +223,6 @@ func ParseMsgBytes(data []byte) (msg *Msg, err error) {
msg.MinExpires = msg.MinExpires * 10 + (int(fc) - 0x30)
}
action PAssertedIdentity {
msg.PAssertedIdentity, err = ParseAddr(string(data[mark:p]))
if err != nil { return nil, err }
}
action RecordRoute {
*rroutep, err = ParseAddr(string(data[mark:p]))
if err != nil { return nil, err }
for *rroutep != nil { rroutep = &(*rroutep).Next }
}
action RemotePartyID {
msg.RemotePartyID, err = ParseAddr(string(data[mark:p]))
if err != nil { return nil, err }
}
action Route {
*routep, err = ParseAddr(string(data[mark:p]))
if err != nil { return nil, err }
for *routep != nil { routep = &(*routep).Next }
}
action To {
msg.To, err = ParseAddr(string(data[mark:p]))
if err != nil { return nil, err }
}
action Via {
*viap, err = ParseVia(string(data[mark:p]))
if err != nil { return nil, err }
@ -226,13 +240,13 @@ func ParseMsgBytes(data []byte) (msg *Msg, err error) {
WSP = SP | HTAB;
LWS = ( WSP* ( CR when lookAheadWSP ) LF )? WSP+;
SWS = LWS?;
UTF8_CONT = 0x80..0xBF;
UTF8_NONASCII = 0xC0..0xDF UTF8_CONT {1}
| 0xE0..0xEF UTF8_CONT {2}
| 0xF0..0xF7 UTF8_CONT {3}
| 0xF8..0xFb UTF8_CONT {4}
| 0xFC..0xFD UTF8_CONT {5};
UTF8 = 0x21..0x7F | UTF8_NONASCII;
UTF8_CONT = 0x80..0xBF @append;
UTF8_NONASCII = 0xC0..0xDF @append UTF8_CONT {1}
| 0xE0..0xEF @append UTF8_CONT {2}
| 0xF0..0xF7 @append UTF8_CONT {3}
| 0xF8..0xFb @append UTF8_CONT {4}
| 0xFC..0xFD @append UTF8_CONT {5};
UTF8 = 0x21..0x7F @append | UTF8_NONASCII;
UTF8_TRIM = ( UTF8+ (LWS* UTF8)* ) >start @collapse;
# https://tools.ietf.org/html/rfc3261#section-25.1
@ -247,6 +261,7 @@ func ParseMsgBytes(data []byte) (msg *Msg, err error) {
wordc = alpha | digit | "-" | "." | "!" | "%" | "*" | "_"
| "+" | "`" | "'" | "~" | "(" | ")" | "<" | ">" | ":"
| "\\" | "\"" | "/" | "[" | "]" | "?" | "{" | "}" ;
schmchars = alpha | digit | "+" | "-" | "." ;
word = wordc+ ;
STAR = SWS "*" SWS ;
SLASH = SWS "/" SWS ;
@ -261,92 +276,113 @@ func ParseMsgBytes(data []byte) (msg *Msg, err error) {
HCOLON = WSP* ":" 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 )* <: "\"" ;
ctext = 0x21..0x27 | 0x2A..0x5B | 0x5D..0x7E | UTF8_NONASCII | LWS;
quoted_pair = "\\" ( 0x00..0x09 | 0x0B..0x0C | 0x0E..0x7F ) @append;
comment = LPAREN ( ctext | quoted_pair )* <: RPAREN; # TODO(jart): Nested parens.
qdtext = UTF8_NONASCII | LWS @append | ( 0x21 | 0x23..0x5B | 0x5D..0x7E ) @append;
escaped = "%" ( xdigit @hexHi ) ( xdigit @hexLo ) ;
uric = reserved | unreserved | escaped ;
uric = reserved | unreserved | "%" ;
uric_no_slash = unreserved | escaped | ";" | "?" | ":" | "@" | "&" | "="
| "+" | "$" | "," ;
token = tokenc+ >mark ;
reasonc = reserved | unreserved | UTF8_NONASCII | SP | HTAB ;
reasonmc = escaped | ( reasonc @append ) ;
cid = word ( "@" word )? ;
Method = token %Method;
uri = alpha schmchars* ":" uric+;
token = tokenc+;
tokenhost = ( tokenc | "[" | "]" | ":" )+;
reasonc = UTF8_NONASCII | ( reserved | unreserved | SP | HTAB ) @append;
reasonmc = escaped | reasonc;
cid = word ( "@" word )?;
quoted_content = ( qdtext | quoted_pair )* >start;
quoted_string = LDQUOT quoted_content <: RDQUOT;
param_name = token >mark %store_name;
param_content = tokenhost >start @append;
param_value = param_content | quoted_string;
param = ( param_name ( EQUAL param_value )? ) %addr_param;
display = ( token @append LWS %space )* >start;
display_name = ( display | quoted_string ) %addr_display;
addr_spec = uri >mark %addr_uri;
name_addr = display_name? LAQUOT addr_spec RAQUOT;
addr_omg = ( ( addr_spec -- ";" ) | name_addr ) ( SEMI param )*;
addr = addr_omg >new_addr %store_addr;
Method = token >mark %Method;
SIPVersionNo = digit+ @VersionMajor "." digit+ @VersionMinor;
RequestURI = ^SP+ >mark %RequestURI;
StatusCode = ( digit @StatusCode ) {3};
ReasonPhrase = reasonmc+ >start %ReasonPhrase;
hval = ( UTF8 | LWS )* >mark;
cheader = ("Call-ID"i | "i"i) HCOLON cid >mark %CallID
| ("Contact"i | "m"i) HCOLON <: hval %Contact
| ("Content-Length"i | "l"i) HCOLON digit+ >{clen=0} @ContentLength
| ("Content-Type"i | "c"i) HCOLON <: hval %ContentType
| "CSeq"i HCOLON (digit+ @CSeq) LWS token %CSeqMethod
| ("Expires"i | "l"i) HCOLON digit+ >{msg.Expires=0} @Expires
| ("From"i | "f"i) HCOLON <: hval %From
| ("Max-Forwards"i | "l"i) HCOLON digit+ >{msg.MaxForwards=0} @MaxForwards
| ("Min-Expires"i | "l"i) HCOLON digit+ >{msg.MinExpires=0} @MinExpires
| "P-Asserted-Identity"i HCOLON <: hval %PAssertedIdentity
| "Record-Route"i HCOLON <: hval %RecordRoute
| "Remote-Party-ID"i HCOLON <: hval %RemotePartyID
| "Route"i HCOLON <: hval %Route
| ("To"i | "t"i) HCOLON <: hval %To
| ("Via"i | "v"i) HCOLON <: hval %Via
# Address Headers
aname = ("Contact"i | "m"i) %{addrpp=&contactp}
| ("From"i | "f"i) %{addrpp=&fromp}
| "P-Asserted-Identity"i %{addrpp=&paidp}
| "Record-Route"i %{addrpp=&rroutep}
| "Remote-Party-ID"i %{addrpp=&rpidp}
| "Route"i %{addrpp=&routep}
| ("To"i | "t"i) %{addrpp=&top}
;
# String Headers
sname = "Accept"i %{value=&msg.Accept}
| ("Accept-Contact"i | "a"i) %{value=&msg.AcceptContact}
| "Accept-Encoding"i %{value=&msg.AcceptEncoding}
| "Accept-Language"i %{value=&msg.AcceptLanguage}
| ("Allow"i | "u"i) %{value=&msg.Allow}
| ("Allow-Events"i | "u"i) %{value=&msg.AllowEvents}
| "Alert-Info"i %{value=&msg.AlertInfo}
| "Authentication-Info"i %{value=&msg.AuthenticationInfo}
| "Authorization"i %{value=&msg.Authorization}
| "Content-Disposition"i %{value=&msg.ContentDisposition}
| "Content-Language"i %{value=&msg.ContentLanguage}
| ("Content-Encoding"i | "e"i) %{value=&msg.ContentEncoding}
| "Call-Info"i %{value=&msg.CallInfo}
| "Date"i %{value=&msg.Date}
| "Error-Info"i %{value=&msg.ErrorInfo}
| ("Event"i | "o"i) %{value=&msg.Event}
| "In-Reply-To"i %{value=&msg.InReplyTo}
| "Reply-To"i %{value=&msg.ReplyTo}
| "MIME-Version"i %{value=&msg.MIMEVersion}
| "Organization"i %{value=&msg.Organization}
| "Priority"i %{value=&msg.Priority}
| "Proxy-Authenticate"i %{value=&msg.ProxyAuthenticate}
| "Proxy-Authorization"i %{value=&msg.ProxyAuthorization}
| "Proxy-Require"i %{value=&msg.ProxyRequire}
| ("Refer-To"i | "r"i) %{value=&msg.ReferTo}
| ("Referred-By"i | "b"i) %{value=&msg.ReferredBy}
| "Require"i %{value=&msg.Require}
| "Retry-After"i %{value=&msg.RetryAfter}
| "Server"i %{value=&msg.Server}
| ("Subject"i | "s"i) %{value=&msg.Subject}
| ("Supported"i | "k"i) %{value=&msg.Supported}
| "Timestamp"i %{value=&msg.Timestamp}
| "Unsupported"i %{value=&msg.Unsupported}
| "User-Agent"i %{value=&msg.UserAgent}
| "Warning"i %{value=&msg.Warning}
| "WWW-Authenticate"i %{value=&msg.WWWAuthenticate}
;
sname = "Accept"i %{dest=&msg.Accept}
| ("Accept-Contact"i | "a"i) %{dest=&msg.AcceptContact}
| "Accept-Encoding"i %{dest=&msg.AcceptEncoding}
| "Accept-Language"i %{dest=&msg.AcceptLanguage}
| ("Allow"i | "u"i) %{dest=&msg.Allow}
| ("Allow-Events"i | "u"i) %{dest=&msg.AllowEvents}
| "Alert-Info"i %{dest=&msg.AlertInfo}
| "Authentication-Info"i %{dest=&msg.AuthenticationInfo}
| "Authorization"i %{dest=&msg.Authorization}
| "Content-Disposition"i %{dest=&msg.ContentDisposition}
| "Content-Language"i %{dest=&msg.ContentLanguage}
| ("Content-Encoding"i | "e"i) %{dest=&msg.ContentEncoding}
| "Call-Info"i %{dest=&msg.CallInfo}
| "Date"i %{dest=&msg.Date}
| "Error-Info"i %{dest=&msg.ErrorInfo}
| ("Event"i | "o"i) %{dest=&msg.Event}
| "In-Reply-To"i %{dest=&msg.InReplyTo}
| "Reply-To"i %{dest=&msg.ReplyTo}
| "MIME-Version"i %{dest=&msg.MIMEVersion}
| "Organization"i %{dest=&msg.Organization}
| "Priority"i %{dest=&msg.Priority}
| "Proxy-Authenticate"i %{dest=&msg.ProxyAuthenticate}
| "Proxy-Authorization"i %{dest=&msg.ProxyAuthorization}
| "Proxy-Require"i %{dest=&msg.ProxyRequire}
| ("Refer-To"i | "r"i) %{dest=&msg.ReferTo}
| ("Referred-By"i | "b"i) %{dest=&msg.ReferredBy}
| "Require"i %{dest=&msg.Require}
| "Retry-After"i %{dest=&msg.RetryAfter}
| "Server"i %{dest=&msg.Server}
| ("Subject"i | "s"i) %{dest=&msg.Subject}
| ("Supported"i | "k"i) %{dest=&msg.Supported}
| "Timestamp"i %{dest=&msg.Timestamp}
| "Unsupported"i %{dest=&msg.Unsupported}
| "User-Agent"i %{dest=&msg.UserAgent}
| "Warning"i %{dest=&msg.Warning}
| "WWW-Authenticate"i %{dest=&msg.WWWAuthenticate}
# Custom Headers
cheader = ("Call-ID"i | "i"i) @!gxh HCOLON cid >mark %CallID
| ("Content-Length"i | "l"i) @!gxh HCOLON digit+ >{clen=0} @ContentLength
| ("Content-Type"i | "c"i) @!gxh HCOLON <: hval %ContentType
| "CSeq"i @!gxh HCOLON (digit+ @CSeq) LWS token >mark %CSeqMethod
| ("Expires"i | "l"i) @!gxh HCOLON digit+ >{msg.Expires=0} @Expires
| ("Max-Forwards"i | "l"i) @!gxh HCOLON digit+ >{msg.MaxForwards=0} @MaxForwards
| ("Min-Expires"i | "l"i) @!gxh HCOLON digit+ >{msg.MinExpires=0} @MinExpires
| ("Via"i | "v"i) @!gxh HCOLON <: hval %Via
;
svalue := hval CR LF @svalueDone @header;
xheader := token %hname HCOLON <: any @svalue;
avalue := addr ( COMMA addr )* CR LF @goto_header;
svalue := hval CR LF @store_value @goto_header;
xheader := token >mark %store_name HCOLON <: any @{value=nil} @goto_svalue;
header := CR LF @break
| cheader CR LF @header
| sname >mark @err(xheader) HCOLON <: any @svalue
| cheader CR LF @goto_header
| aname >mark @err(goto_xheader) HCOLON <: any @goto_avalue
| sname >mark @err(goto_xheader) HCOLON <: any @goto_svalue
;
SIPVersion = "SIP/" SIPVersionNo;
Request = Method SP RequestURI SP SIPVersion CR LF @header;
Response = SIPVersion SP StatusCode SP ReasonPhrase CR LF @header;
Request = Method SP RequestURI SP SIPVersion CR LF @goto_header;
Response = SIPVersion SP StatusCode SP ReasonPhrase CR LF @goto_header;
main := Request | Response;
write init;


+ 185
- 1
sip/msg_test.go View File

@ -15,6 +15,17 @@ type msgTest struct {
var msgTests = []msgTest{
msgTest{
name: "UTF8 Phrase",
s: "SIP/2.0 200 ◕◡◕\r\n" +
"\r\n",
msg: sip.Msg{
VersionMajor: 2,
Status: 200,
Phrase: "◕◡◕",
},
},
msgTest{
name: "Left Padding",
s: "SIP/2.0 200 OK\r\n" +
@ -38,7 +49,180 @@ var msgTests = []msgTest{
Status: 200,
Phrase: "OK",
Headers: sip.Headers{
"x-lol": "omfg",
"X-LOL": "omfg",
},
},
},
msgTest{
name: "Multiple Addresses",
s: "SIP/2.0 200 OK\r\n" +
"From: <sip:lol.com> , <sip:bog.com> \r\n" +
"\r\n",
msg: sip.Msg{
VersionMajor: 2,
Status: 200,
Phrase: "OK",
From: &sip.Addr{
Uri: &sip.URI{
Scheme: "sip",
Host: "lol.com",
},
Next: &sip.Addr{
Uri: &sip.URI{
Scheme: "sip",
Host: "bog.com",
},
},
},
},
},
msgTest{
name: "Line Continuations",
s: "SIP/2.0 200 OK\r\n" +
"From:\r\n" +
" <sip:lol.com>,\r\n" +
" <sip:bog.com>\r\n" +
"\r\n",
msg: sip.Msg{
VersionMajor: 2,
Status: 200,
Phrase: "OK",
From: &sip.Addr{
Uri: &sip.URI{
Scheme: "sip",
Host: "lol.com",
},
Next: &sip.Addr{
Uri: &sip.URI{
Scheme: "sip",
Host: "bog.com",
},
},
},
},
},
msgTest{
name: "Address Unquoted Display",
s: "SIP/2.0 200 OK\r\n" +
"From: Kitty <sip:lol.com>\r\n" +
"\r\n",
msg: sip.Msg{
VersionMajor: 2,
Status: 200,
Phrase: "OK",
From: &sip.Addr{
Display: "Kitty",
Uri: &sip.URI{
Scheme: "sip",
Host: "lol.com",
},
},
},
},
msgTest{
name: "Address Quoted Display",
s: "SIP/2.0 200 OK\r\n" +
"From: \"Hello \\\"Kitty\\\" ◕◡◕\" <sip:lol.com>\r\n" +
"\r\n",
msg: sip.Msg{
VersionMajor: 2,
Status: 200,
Phrase: "OK",
From: &sip.Addr{
Display: "Hello \"Kitty\" ◕◡◕",
Uri: &sip.URI{
Scheme: "sip",
Host: "lol.com",
},
},
},
},
msgTest{
name: "Addr Tag",
s: "SIP/2.0 200 OK\r\n" +
"From: <sip:lol.com>;tag=omfg\r\n" +
"\r\n",
msg: sip.Msg{
VersionMajor: 2,
Status: 200,
Phrase: "OK",
From: &sip.Addr{
Uri: &sip.URI{
Scheme: "sip",
Host: "lol.com",
},
Params: sip.Params{
"tag": "omfg",
},
},
},
},
msgTest{
name: "Addr Tag Quoted",
// TODO(jart): Crash when extra spacing in here.
s: "SIP/2.0 200 OK\r\n" +
"From: <sip:lol.com>;tag=\"◕◡◕\"\r\n" +
"\r\n",
msg: sip.Msg{
VersionMajor: 2,
Status: 200,
Phrase: "OK",
From: &sip.Addr{
Uri: &sip.URI{
Scheme: "sip",
Host: "lol.com",
},
Params: sip.Params{
"tag": "◕◡◕",
},
},
},
},
msgTest{
name: "Addr Tag Bare",
s: "SIP/2.0 200 OK\r\n" +
"From: <sip:lol.com>;tag\r\n" +
"\r\n",
msg: sip.Msg{
VersionMajor: 2,
Status: 200,
Phrase: "OK",
From: &sip.Addr{
Uri: &sip.URI{
Scheme: "sip",
Host: "lol.com",
},
Params: sip.Params{
"tag": "",
},
},
},
},
msgTest{
name: "Missing Angle Brackets With Tag Belongs to Addr Not URI",
s: "SIP/2.0 200 OK\r\n" +
"From: sip:lol.com;tag=omfg\r\n" +
"\r\n",
msg: sip.Msg{
VersionMajor: 2,
Status: 200,
Phrase: "OK",
From: &sip.Addr{
Uri: &sip.URI{
Scheme: "sip",
Host: "lol.com",
},
Params: sip.Params{
"tag": "omfg",
},
},
},
},


Loading…
Cancel
Save