summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2009-03-12 11:25:11 +0000
committerBenny Prijono <bennylp@teluu.com>2009-03-12 11:25:11 +0000
commitba9d8ca28eb209571c0bd6a080a8bb03d0fa2d33 (patch)
tree6f32a52e5bb80196bdffa66de1292ea5bda050c4
parent42ee8666dff7f9689ea1ff50409d5170a8417838 (diff)
Initial fixes for ticket #747: bugs in parsing SIP torture messages (RFC 4475):
- SIP version components may be separated by whitespaces (e.g. "SIP / 2.0") - parsing of mangled header when for unknown/generic header - Via parameters were parsed with paramchar rather than token - handling NULL character inside quoted string Some torture messages have been added in the Python test. git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@2505 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjlib-util/src/pjlib-util/scanner.c4
-rw-r--r--pjsip-apps/src/pjsua/pjsua_app.c97
-rw-r--r--pjsip/src/pjsip/sip_parser.c93
-rw-r--r--tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_1.py52
-rw-r--r--tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_2.py25
-rw-r--r--tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_3.py35
-rw-r--r--tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_4.py25
-rw-r--r--tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_5.py25
8 files changed, 328 insertions, 28 deletions
diff --git a/pjlib-util/src/pjlib-util/scanner.c b/pjlib-util/src/pjlib-util/scanner.c
index a5a56160..6e01ba6f 100644
--- a/pjlib-util/src/pjlib-util/scanner.c
+++ b/pjlib-util/src/pjlib-util/scanner.c
@@ -28,7 +28,7 @@
#define PJ_SCAN_IS_SPACE(c) ((c)==' ' || (c)=='\t')
#define PJ_SCAN_IS_NEWLINE(c) ((c)=='\r' || (c)=='\n')
#define PJ_SCAN_IS_PROBABLY_SPACE(c) ((c) <= 32)
-#define PJ_SCAN_CHECK_EOF(s) (*s)
+#define PJ_SCAN_CHECK_EOF(s) (s != scanner->end)
#if defined(PJ_SCANNER_USE_BITWISE) && PJ_SCANNER_USE_BITWISE != 0
@@ -375,7 +375,7 @@ PJ_DEF(void) pj_scan_get_quotes(pj_scanner *scanner,
*/
do {
/* loop until end_quote is found. */
- while (*s && *s != '\n' && *s != end_quote[qpair]) {
+ while (PJ_SCAN_CHECK_EOF(s) && *s != '\n' && *s != end_quote[qpair]) {
++s;
}
diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c
index 8e73c9cb..e7f3390e 100644
--- a/pjsip-apps/src/pjsua/pjsua_app.c
+++ b/pjsip-apps/src/pjsua/pjsua_app.c
@@ -4023,6 +4023,97 @@ on_exit:
;
}
+/*****************************************************************************
+ * A simple module to handle otherwise unhandled request. We will register
+ * this with the lowest priority.
+ */
+
+/* Notification on incoming request */
+static pj_bool_t default_mod_on_rx_request(pjsip_rx_data *rdata)
+{
+ pjsip_tx_data *tdata;
+ pjsip_status_code status_code;
+ pj_status_t status;
+
+ /* Don't respond to ACK! */
+ if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
+ &pjsip_ack_method) == 0)
+ return PJ_TRUE;
+
+ /* Create basic response. */
+ if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
+ &pjsip_notify_method) == 0)
+ {
+ /* Unsolicited NOTIFY's, send with Bad Request */
+ status_code = PJSIP_SC_BAD_REQUEST;
+ } else {
+ /* Probably unknown method */
+ status_code = PJSIP_SC_METHOD_NOT_ALLOWED;
+ }
+ status = pjsip_endpt_create_response(pjsua_get_pjsip_endpt(),
+ rdata, status_code,
+ NULL, &tdata);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to create response", status);
+ return PJ_TRUE;
+ }
+
+ /* Add Allow if we're responding with 405 */
+ if (status_code == PJSIP_SC_METHOD_NOT_ALLOWED) {
+ const pjsip_hdr *cap_hdr;
+ cap_hdr = pjsip_endpt_get_capability(pjsua_get_pjsip_endpt(),
+ PJSIP_H_ALLOW, NULL);
+ if (cap_hdr) {
+ pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_clone(tdata->pool,
+ cap_hdr));
+ }
+ }
+
+ /* Add User-Agent header */
+ {
+ pj_str_t user_agent;
+ char tmp[80];
+ const pj_str_t USER_AGENT = { "User-Agent", 10};
+ pjsip_hdr *h;
+
+ pj_ansi_snprintf(tmp, sizeof(tmp), "PJSUA v%s/%s",
+ pj_get_version(), PJ_OS_NAME);
+ pj_strdup2_with_null(tdata->pool, &user_agent, tmp);
+
+ h = (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool,
+ &USER_AGENT,
+ &user_agent);
+ pjsip_msg_add_hdr(tdata->msg, h);
+ }
+
+ pjsip_endpt_send_response2(pjsua_get_pjsip_endpt(), rdata, tdata,
+ NULL, NULL);
+
+ return PJ_TRUE;
+}
+
+
+/* The module instance. */
+static pjsip_module mod_default_handler =
+{
+ NULL, NULL, /* prev, next. */
+ { "mod-default-handler", 19 }, /* Name. */
+ -1, /* Id */
+ PJSIP_MOD_PRIORITY_APPLICATION+99, /* Priority */
+ NULL, /* load() */
+ NULL, /* start() */
+ NULL, /* stop() */
+ NULL, /* unload() */
+ &default_mod_on_rx_request, /* on_rx_request() */
+ NULL, /* on_rx_response() */
+ NULL, /* on_tx_request. */
+ NULL, /* on_tx_response() */
+ NULL, /* on_tsx_state() */
+
+};
+
+
+
/*****************************************************************************
* Public API
@@ -4073,6 +4164,12 @@ pj_status_t app_init(int argc, char *argv[])
if (status != PJ_SUCCESS)
return status;
+ /* Initialize our module to handle otherwise unhandled request */
+ status = pjsip_endpt_register_module(pjsua_get_pjsip_endpt(),
+ &mod_default_handler);
+ if (status != PJ_SUCCESS)
+ return status;
+
#ifdef STEREO_DEMO
stereo_demo();
#endif
diff --git a/pjsip/src/pjsip/sip_parser.c b/pjsip/src/pjsip/sip_parser.c
index 5a45817f..bb470c72 100644
--- a/pjsip/src/pjsip/sip_parser.c
+++ b/pjsip/src/pjsip/sip_parser.c
@@ -54,8 +54,6 @@
*/
#define GENERIC_URI_CHARS "#?;:@&=+-_.!~*'()%$,/" "%"
-#define PJSIP_VERSION "SIP/2.0"
-
#define UNREACHED(expr)
#define IS_NEWLINE(c) ((c)=='\r' || (c)=='\n')
@@ -893,6 +891,36 @@ PJ_DEF(pjsip_uri*) pjsip_parse_uri( pj_pool_t *pool,
return NULL;
}
+/* SIP version */
+static void parse_sip_version(pj_scanner *scanner)
+{
+ pj_str_t SIP = { "SIP", 3 };
+ pj_str_t V2 = { "2.0", 3 };
+ pj_str_t sip, version;
+
+ pj_scan_get( scanner, &pconst.pjsip_ALPHA_SPEC, &sip);
+ if (pj_scan_get_char(scanner) != '/')
+ on_syntax_error(scanner);
+ pj_scan_get_n( scanner, 3, &version);
+ if (pj_stricmp(&sip, &SIP) || pj_stricmp(&version, &V2))
+ on_syntax_error(scanner);
+}
+
+static pj_bool_t is_next_sip_version(pj_scanner *scanner)
+{
+ pj_str_t SIP = { "SIP", 3 };
+ pj_str_t sip;
+ int c;
+
+ c = pj_scan_peek(scanner, &pconst.pjsip_ALPHA_SPEC, &sip);
+ /* return TRUE if it is "SIP" followed by "/" or space.
+ * we include space since the "/" may be separated by space,
+ * although this would mean it would return TRUE if it is a
+ * request and the method is "SIP"!
+ */
+ return c && (c=='/' || c==' ' || c=='\t') && pj_stricmp(&sip, &SIP)==0;
+}
+
/* Internal function to parse SIP message */
static pjsip_msg *int_parse_msg( pjsip_parse_ctx *ctx,
pjsip_parser_err_report *err_list)
@@ -926,7 +954,7 @@ retry_parse:
return NULL;
/* Parse request or status line */
- if (pj_scan_stricmp_alnum( scanner, PJSIP_VERSION, 7) == 0) {
+ if (is_next_sip_version(scanner)) {
msg = pjsip_msg_create(pool, PJSIP_RESPONSE_MSG);
int_parse_status_line( scanner, &msg->line.status );
} else {
@@ -1125,7 +1153,7 @@ PJ_DEF(void) pjsip_parse_uri_param_imp( pj_scanner *scanner, pj_pool_t *pool,
}
-/* Parse parameter (";" pname ["=" pvalue]) in header. */
+/* Parse parameter (";" pname ["=" pvalue]) in SIP header. */
static void int_parse_param( pj_scanner *scanner, pj_pool_t *pool,
pj_str_t *pname, pj_str_t *pvalue,
unsigned option)
@@ -1513,9 +1541,7 @@ static void int_parse_req_line( pj_scanner *scanner, pj_pool_t *pool,
pjsip_method_init_np( &req_line->method, &token);
req_line->uri = int_parse_uri(scanner, pool, PJ_TRUE);
- if (pj_scan_stricmp_alnum( scanner, PJSIP_VERSION, 7) != 0)
- PJ_THROW( PJSIP_SYN_ERR_EXCEPTION);
- pj_scan_advance_n (scanner, 7, 1);
+ parse_sip_version(scanner);
pj_scan_get_newline( scanner );
}
@@ -1525,10 +1551,7 @@ static void int_parse_status_line( pj_scanner *scanner,
{
pj_str_t token;
- if (pj_scan_stricmp_alnum(scanner, PJSIP_VERSION, 7) != 0)
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- pj_scan_advance_n( scanner, 7, 1);
-
+ parse_sip_version(scanner);
pj_scan_get( scanner, &pconst.pjsip_DIGIT_SPEC, &token);
status_line->code = pj_strtoul(&token);
if (*scanner->curptr != '\r' && *scanner->curptr != '\n')
@@ -1618,12 +1641,32 @@ end:
/* Parse generic string header. */
static void parse_generic_string_hdr( pjsip_generic_string_hdr *hdr,
- pj_scanner *scanner )
+ pjsip_parse_ctx *ctx)
{
- if (pj_cis_match(&pconst.pjsip_NOT_NEWLINE, *scanner->curptr))
+ pj_scanner *scanner = ctx->scanner;
+
+ hdr->hvalue.slen = 0;
+
+ /* header may be mangled hence the loop */
+ while (pj_cis_match(&pconst.pjsip_NOT_NEWLINE, *scanner->curptr)) {
+ pj_str_t next, tmp;
+
pj_scan_get( scanner, &pconst.pjsip_NOT_NEWLINE, &hdr->hvalue);
- else
- hdr->hvalue.slen = 0;
+ if (IS_NEWLINE(*scanner->curptr))
+ break;
+ /* mangled, get next fraction */
+ pj_scan_get( scanner, &pconst.pjsip_NOT_NEWLINE, &next);
+ /* concatenate */
+ tmp.ptr = (char*)pj_pool_alloc(ctx->pool,
+ hdr->hvalue.slen + next.slen + 2);
+ tmp.slen = 0;
+ pj_strcpy(&tmp, &hdr->hvalue);
+ pj_strcat2(&tmp, " ");
+ pj_strcat(&tmp, &next);
+ tmp.ptr[tmp.slen] = '\0';
+
+ hdr->hvalue = tmp;
+ }
parse_hdr_end(scanner);
}
@@ -1934,13 +1977,12 @@ static void int_parse_via_param( pjsip_via_hdr *hdr, pj_scanner *scanner,
pj_str_t pname, pvalue;
//Parse with PARAM_CHAR instead, to allow IPv6
+ //No, back to using int_parse_param() for the "`" character!
//int_parse_param( scanner, pool, &pname, &pvalue, 0);
- /* Get ';' character */
- pj_scan_get_char(scanner);
-
- parse_param_imp(scanner, pool, &pname, &pvalue,
- &pconst.pjsip_PARAM_CHAR_SPEC,
- &pconst.pjsip_PARAM_CHAR_SPEC_ESC, 0);
+ //parse_param_imp(scanner, pool, &pname, &pvalue,
+ // &pconst.pjsip_TOKEN_SPEC,
+ // &pconst.pjsip_TOKEN_SPEC_ESC, 0);
+ int_parse_param(scanner, pool, &pname, &pvalue, 0);
if (!parser_stricmp(pname, pconst.pjsip_BRANCH_STR) && pvalue.slen) {
hdr->branch_param = pvalue;
@@ -2075,10 +2117,9 @@ static pjsip_hdr* parse_hdr_via( pjsip_parse_ctx *ctx )
else
pj_list_insert_before(first, hdr);
- if (pj_scan_stricmp_alnum( scanner, PJSIP_VERSION "/", 8) != 0)
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
-
- pj_scan_advance_n( scanner, 8, 1);
+ parse_sip_version(scanner);
+ if (pj_scan_get_char(scanner) != '/')
+ on_syntax_error(scanner);
pj_scan_get( scanner, &pconst.pjsip_TOKEN_SPEC, &hdr->transport);
int_parse_host(scanner, &hdr->sent_by.host);
@@ -2119,7 +2160,7 @@ static pjsip_hdr* parse_hdr_generic_string( pjsip_parse_ctx *ctx )
pjsip_generic_string_hdr *hdr;
hdr = pjsip_generic_string_hdr_create(ctx->pool, NULL, NULL);
- parse_generic_string_hdr(hdr, ctx->scanner);
+ parse_generic_string_hdr(hdr, ctx);
return (pjsip_hdr*)hdr;
}
diff --git a/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_1.py b/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_1.py
new file mode 100644
index 00000000..35f803c7
--- /dev/null
+++ b/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_1.py
@@ -0,0 +1,52 @@
+# $Id$
+import inc_sip as sip
+import inc_sdp as sdp
+
+# Torture message from RFC 4475
+# 3.1.1. Valid Messages
+# 3.1.1.1. A Short Tortuous INVITE
+complete_msg = \
+"""INVITE sip:vivekg@chair-dnrc.example.com;unknownparam SIP/2.0
+TO :
+ sip:vivekg@chair-dnrc.example.com ; tag = 1918181833n
+from : "J Rosenberg \\\\\\"" <sip:jdrosen@example.com>
+ ;
+ tag = 98asjd8
+MaX-fOrWaRdS: 0068
+Call-ID: wsinv.ndaksdj@192.0.2.1
+Content-Length : 150
+cseq: 0009
+ INVITE
+Via : SIP / 2.0
+ /UDP
+ 192.0.2.2;rport;branch=390skdjuw
+s :
+NewFangledHeader: newfangled value
+ continued newfangled value
+UnknownHeaderWithUnusualValue: ;;,,;;,;
+Content-Type: application/sdp
+Route:
+ <sip:services.example.com;lr;unknownwith=value;unknown-no-value>
+v: SIP / 2.0 / TCP spindle.example.com ;
+ branch = z9hG4bK9ikj8 ,
+ SIP / 2.0 / UDP 192.168.255.111 ; branch=
+ z9hG4bK30239
+m:"Quoted string \\"\\"" <sip:jdrosen@example.com> ; newparam =
+ newvalue ;
+ secondparam ; q = 0.33
+
+v=0
+o=mhandley 29739 7272939 IN IP4 192.0.2.3
+s=-
+c=IN IP4 192.0.2.4
+t=0 0
+m=audio 49217 RTP/AVP 0 12
+m=video 3227 RTP/AVP 31
+a=rtpmap:31 LPC
+"""
+
+
+sendto_cfg = sip.SendtoCfg( "RFC 4475 3.1.1.1",
+ "--null-audio --auto-answer 200",
+ "", 481, complete_msg=complete_msg)
+
diff --git a/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_2.py b/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_2.py
new file mode 100644
index 00000000..88fd249b
--- /dev/null
+++ b/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_2.py
@@ -0,0 +1,25 @@
+# $Id$
+import inc_sip as sip
+import inc_sdp as sdp
+
+# Torture message from RFC 4475
+# 3.1.1. Valid Messages
+# 3.1.1.2. Wide Range of Valid Characters
+complete_msg = \
+"""!interesting-Method0123456789_*+`.%indeed'~ sip:1_unusual.URI~(to-be!sure)&isn't+it$/crazy?,/;;*:&it+has=1,weird!*pas$wo~d_too.(doesn't-it)@example.com SIP/2.0
+Via: SIP/2.0/UDP host1.example.com;rport;branch=z9hG4bK-.!%66*_+`'~
+To: "BEL:\\\x07 NUL:\\\x00 DEL:\\\x7F" <sip:1_unusual.URI~(to-be!sure)&isn't+it$/crazy?,/;;*@example.com>
+From: token1~` token2'+_ token3*%!.- <sip:mundane@example.com> ;fromParam''~+*_!.-%="\xD1\x80\xD0\xB0\xD0\xB1\xD0\xBE\xD1\x82\xD0\xB0\xD1\x8E\xD1\x89\xD0\xB8\xD0\xB9";tag=_token~1'+`*%!-.
+Call-ID: intmeth.word%ZK-!.*_+'@word`~)(><:\\/"][?}{
+CSeq: 139122385 !interesting-Method0123456789_*+`.%indeed'~
+Max-Forwards: 255
+extensionHeader-!.%*+_`'~: \xEF\xBB\xBF\xE5\xA4\xA7\xE5\x81\x9C\xE9\x9B\xBB
+Content-Length: 0
+
+"""
+
+
+sendto_cfg = sip.SendtoCfg( "RFC 4475 3.1.1.2",
+ "--null-audio --auto-answer 200",
+ "", 405, complete_msg=complete_msg)
+
diff --git a/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_3.py b/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_3.py
new file mode 100644
index 00000000..4f32e979
--- /dev/null
+++ b/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_3.py
@@ -0,0 +1,35 @@
+# $Id$
+import inc_sip as sip
+import inc_sdp as sdp
+
+# Torture message from RFC 4475
+# 3.1.1. Valid Messages
+# 3.1.1.3. Valid Use of the % Escaping Mechanism
+complete_msg = \
+"""INVITE sip:sips%3Auser%40example.com@example.net SIP/2.0
+To: sip:%75se%72@example.com
+From: <sip:I%20have%20spaces@example.net>;tag=$FROM_TAG
+Max-Forwards: 87
+i: esc01.239409asdfakjkn23onasd0-3234
+CSeq: 234234 INVITE
+Via: SIP/2.0/UDP host5.example.net;rport;branch=z9hG4bKkdjuw
+C: application/sdp
+Contact:
+ <sip:cal%6Cer@$LOCAL_IP:$LOCAL_PORT;%6C%72;n%61me=v%61lue%25%34%31>
+Content-Length: 150
+
+v=0
+o=mhandley 29739 7272939 IN IP4 192.0.2.1
+s=-
+c=IN IP4 192.0.2.1
+t=0 0
+m=audio 49217 RTP/AVP 0 12
+m=video 3227 RTP/AVP 31
+a=rtpmap:31 LPC
+"""
+
+
+sendto_cfg = sip.SendtoCfg( "RFC 4475 3.1.1.3",
+ "--null-audio --auto-answer 200",
+ "", 200, complete_msg=complete_msg)
+
diff --git a/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_4.py b/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_4.py
new file mode 100644
index 00000000..7a05c144
--- /dev/null
+++ b/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_4.py
@@ -0,0 +1,25 @@
+# $Id$
+import inc_sip as sip
+import inc_sdp as sdp
+
+# Torture message from RFC 4475
+# 3.1.1. Valid Messages
+# 3.1.1.4. Escaped Nulls in URIs
+complete_msg = \
+"""REGISTER sip:example.com SIP/2.0
+To: sip:null-%00-null@example.com
+From: sip:null-%00-null@example.com;tag=839923423
+Max-Forwards: 70
+Call-ID: escnull.39203ndfvkjdasfkq3w4otrq0adsfdfnavd
+CSeq: 14398234 REGISTER
+Via: SIP/2.0/UDP host5.example.com;rport;branch=z9hG4bKkdjuw
+Contact: <sip:%00@host5.example.com>
+Contact: <sip:%00%00@host5.example.com>
+L:0
+"""
+
+
+sendto_cfg = sip.SendtoCfg( "RFC 4475 3.1.1.4",
+ "--null-audio --auto-answer 200",
+ "", 405, complete_msg=complete_msg)
+
diff --git a/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_5.py b/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_5.py
new file mode 100644
index 00000000..2b449d11
--- /dev/null
+++ b/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_5.py
@@ -0,0 +1,25 @@
+# $Id$
+import inc_sip as sip
+import inc_sdp as sdp
+
+# Torture message from RFC 4475
+# 3.1.1. Valid Messages
+# 3.1.1.5. Use of % When It Is Not an Escape
+complete_msg = \
+"""RE%47IST%45R sip:registrar.example.com SIP/2.0
+To: "%Z%45" <sip:resource@example.com>
+From: "%Z%45" <sip:resource@example.com>;tag=f232jadfj23
+Call-ID: esc02.asdfnqwo34rq23i34jrjasdcnl23nrlknsdf
+Via: SIP/2.0/TCP host.example.com;rport;branch=z9hG4bK209%fzsnel234
+CSeq: 29344 RE%47IST%45R
+Max-Forwards: 70
+Contact: <sip:alias1@host1.example.com>
+C%6Fntact: <sip:alias2@host2.example.com>
+Contact: <sip:alias3@host3.example.com>
+l: 0
+"""
+
+sendto_cfg = sip.SendtoCfg( "RFC 4475 3.1.1.5",
+ "--null-audio --auto-answer 200",
+ "", 405, complete_msg=complete_msg)
+