2010-02-03 20:33:32 +00:00
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2010, Digium, Inc.
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*!
* \file
* \brief sip request parsing functions and unit tests
*/
2012-06-15 16:20:16 +00:00
/*** MODULEINFO
2014-08-08 17:53:39 +00:00
<support_level>extended</support_level>
2012-06-15 16:20:16 +00:00
***/
2010-02-03 20:33:32 +00:00
# include "asterisk.h"
# include "include/sip.h"
2010-02-15 15:45:02 +00:00
# include "include/sip_utils.h"
2010-02-03 20:33:32 +00:00
# include "include/reqresp_parser.h"
2010-07-26 16:44:25 +00:00
# ifdef HAVE_XLOCALE_H
2010-07-23 16:33:52 +00:00
locale_t c_locale ;
2010-07-26 16:44:25 +00:00
# endif
2010-07-23 16:33:52 +00:00
2010-02-03 20:33:32 +00:00
/*! \brief * parses a URI in its components.*/
2010-07-08 22:08:07 +00:00
int parse_uri_full ( char * uri , const char * scheme , char * * user , char * * pass ,
2011-11-09 20:10:52 +00:00
char * * hostport , struct uriparams * params , char * * headers ,
2010-07-08 22:08:07 +00:00
char * * residue )
2010-02-03 20:33:32 +00:00
{
2010-04-09 16:04:16 +00:00
char * userinfo = NULL ;
char * parameters = NULL ;
char * endparams = NULL ;
char * c = NULL ;
2010-02-03 20:33:32 +00:00
int error = 0 ;
2014-04-12 02:27:43 +00:00
int teluri_scheme = 0 ;
2010-02-03 20:33:32 +00:00
2011-05-27 16:35:49 +00:00
/*
* Initialize requested strings - some functions don't care if parse_uri fails
* and will attempt to use string pointers passed into parse_uri even after a
* parse_uri failure
*/
if ( user ) {
* user = " " ;
}
if ( pass ) {
* pass = " " ;
}
2011-11-09 20:10:52 +00:00
if ( hostport ) {
* hostport = " " ;
2011-05-27 16:35:49 +00:00
}
if ( headers ) {
* headers = " " ;
}
if ( residue ) {
* residue = " " ;
}
2010-02-03 20:33:32 +00:00
/* check for valid input */
if ( ast_strlen_zero ( uri ) ) {
return - 1 ;
}
if ( scheme ) {
int l ;
char * scheme2 = ast_strdupa ( scheme ) ;
char * cur = strsep ( & scheme2 , " , " ) ;
for ( ; ! ast_strlen_zero ( cur ) ; cur = strsep ( & scheme2 , " , " ) ) {
l = strlen ( cur ) ;
if ( ! strncasecmp ( uri , cur , l ) ) {
2014-04-12 02:27:43 +00:00
teluri_scheme = ! strncasecmp ( uri , " tel: " , 4 ) ; /* TEL URI */
2010-02-03 20:33:32 +00:00
uri + = l ;
break ;
}
}
if ( ast_strlen_zero ( cur ) ) {
ast_debug ( 1 , " No supported scheme found in '%s' using the scheme[s] %s \n " , uri , scheme ) ;
error = - 1 ;
}
}
2010-04-09 16:04:16 +00:00
2011-11-09 20:10:52 +00:00
if ( ! hostport ) {
/* if we don't want to split around hostport, keep everything as a
2010-07-08 22:08:07 +00:00
* userinfo - cos thats how old parse_uri operated*/
2010-04-09 16:04:16 +00:00
userinfo = uri ;
2014-04-12 02:27:43 +00:00
} else if ( teluri_scheme ) {
/*
* tel: TEL URI INVITE RFC 3966 patch
* See http://www.ietf.org/rfc/rfc3966.txt
*
* Once the full RFC 3966 parsing is implemented,
* the ext= or isub= parameters would be extracted from userinfo.
* When this kind of subaddressing would be implemented, the userinfo must be further parsed.
* Those parameters would be used for ISDN or PSTN local extensions.
*
* Current restrictions:
* We currently consider the ";isub=" or the ";ext=" as part of the userinfo (unparsed).
*/
if ( ( c = strstr ( uri , " ;phone-context= " ) ) ) {
/*
* Local number with context or domain.
* ext= or isub= TEL URI parameters should be upfront.
* All other parameters should come after the ";phone-context=" parameter.
* If other parameters would occur before ";phone-context=" they will be ignored.
*/
* c = ' \0 ' ;
userinfo = uri ;
uri = c + 15 ;
* hostport = uri ;
} else if ( ' + ' = = uri [ 0 ] ) {
/* Global number without context or domain; possibly followed by RFC 3966 and optional other parameters. */
userinfo = uri ;
* hostport = uri ;
} else {
ast_debug ( 1 , " No RFC 3966 global number or context found in '%s'; returning local number anyway \n " , uri ) ;
userinfo = uri ; /* Return local number anyway */
error = - 1 ;
}
2010-04-09 16:04:16 +00:00
} else {
2010-07-08 22:08:07 +00:00
char * dom = " " ;
2010-04-09 16:04:16 +00:00
if ( ( c = strchr ( uri , ' @ ' ) ) ) {
* c + + = ' \0 ' ;
2010-07-08 22:08:07 +00:00
dom = c ;
2010-04-09 16:04:16 +00:00
userinfo = uri ;
2010-07-08 22:08:07 +00:00
uri = c ; /* userinfo can contain ? and ; chars so step forward before looking for params and headers */
2010-04-09 16:04:16 +00:00
} else {
/* domain-only URI, according to the SIP RFC. */
2010-07-08 22:08:07 +00:00
dom = uri ;
2010-04-09 16:04:16 +00:00
userinfo = " " ;
2010-07-16 16:25:01 +00:00
}
2010-04-09 16:04:16 +00:00
2011-11-09 20:10:52 +00:00
* hostport = dom ;
2010-02-03 20:33:32 +00:00
}
2010-04-09 16:04:16 +00:00
if ( pass & & ( c = strchr ( userinfo , ' : ' ) ) ) { /* user:password */
* c + + = ' \0 ' ;
* pass = c ;
} else if ( pass ) {
* pass = " " ;
}
2010-02-03 20:33:32 +00:00
2010-04-09 16:04:16 +00:00
if ( user ) {
* user = userinfo ;
}
parameters = uri ;
/* strip [?headers] from end of uri - even if no header pointer exists*/
if ( ( c = strrchr ( uri , ' ? ' ) ) ) {
2010-07-16 16:25:01 +00:00
* c + + = ' \0 ' ;
2010-04-09 16:04:16 +00:00
uri = c ;
if ( headers ) {
* headers = c ;
2010-02-03 20:33:32 +00:00
}
2010-04-09 16:04:16 +00:00
if ( ( c = strrchr ( uri , ' ; ' ) ) ) {
* c + + = ' \0 ' ;
} else {
c = strrchr ( uri , ' \0 ' ) ;
}
uri = c ; /* residue */
2010-02-03 20:33:32 +00:00
2010-04-09 16:04:16 +00:00
} else if ( headers ) {
* headers = " " ;
}
2010-02-03 20:33:32 +00:00
2010-04-09 16:04:16 +00:00
/* parse parameters */
endparams = strchr ( parameters , ' \0 ' ) ;
if ( ( c = strchr ( parameters , ' ; ' ) ) ) {
2010-07-16 16:25:01 +00:00
* c + + = ' \0 ' ;
2010-04-09 16:04:16 +00:00
parameters = c ;
} else {
parameters = endparams ;
2010-07-16 16:25:01 +00:00
}
2010-04-09 16:04:16 +00:00
if ( params ) {
char * rem = parameters ; /* unparsed or unrecognised remainder */
char * label ;
char * value ;
int lr = 0 ;
params - > transport = " " ;
params - > user = " " ;
params - > method = " " ;
params - > ttl = " " ;
params - > maddr = " " ;
params - > lr = 0 ;
rem = parameters ;
while ( ( value = strchr ( parameters , ' = ' ) ) | | ( lr = ! strncmp ( parameters , " lr " , 2 ) ) ) {
/* The while condition will not continue evaluation to set lr if it matches "lr=" */
if ( lr ) {
value = parameters ;
} else {
* value + + = ' \0 ' ;
}
label = parameters ;
if ( ( c = strchr ( value , ' ; ' ) ) ) {
2010-07-16 16:25:01 +00:00
* c + + = ' \0 ' ;
2010-04-09 16:04:16 +00:00
parameters = c ;
} else {
parameters = endparams ;
2010-07-16 16:25:01 +00:00
}
2010-04-09 16:04:16 +00:00
if ( ! strcmp ( label , " transport " ) ) {
2011-11-11 21:37:53 +00:00
params - > transport = value ;
2010-04-09 16:04:16 +00:00
rem = parameters ;
} else if ( ! strcmp ( label , " user " ) ) {
2011-11-11 21:37:53 +00:00
params - > user = value ;
2010-04-09 16:04:16 +00:00
rem = parameters ;
} else if ( ! strcmp ( label , " method " ) ) {
2011-11-11 21:37:53 +00:00
params - > method = value ;
2010-04-09 16:04:16 +00:00
rem = parameters ;
} else if ( ! strcmp ( label , " ttl " ) ) {
2011-11-11 21:37:53 +00:00
params - > ttl = value ;
2010-04-09 16:04:16 +00:00
rem = parameters ;
} else if ( ! strcmp ( label , " maddr " ) ) {
2011-11-11 21:37:53 +00:00
params - > maddr = value ;
2010-04-09 16:04:16 +00:00
rem = parameters ;
/* Treat "lr", "lr=yes", "lr=on", "lr=1", "lr=almostanything" as lr enabled and "", "lr=no", "lr=off", "lr=0", "lr=" and "lranything" as lr disabled */
} else if ( ( ! strcmp ( label , " lr " ) & & strcmp ( value , " no " ) & & strcmp ( value , " off " ) & & strcmp ( value , " 0 " ) & & strcmp ( value , " " ) ) | | ( ( lr ) & & strcmp ( value , " lr " ) ) ) {
2011-11-11 21:37:53 +00:00
params - > lr = 1 ;
2010-04-09 16:04:16 +00:00
rem = parameters ;
} else {
value - - ;
* value = ' = ' ;
2010-07-16 16:25:01 +00:00
if ( c ) {
c - - ;
* c = ' ; ' ;
}
2010-04-09 16:04:16 +00:00
}
}
if ( rem > uri ) { /* no headers */
uri = rem ;
}
}
if ( residue ) {
* residue = uri ;
2010-02-03 20:33:32 +00:00
}
return error ;
}
2016-07-18 19:40:22 -04:00
# ifdef TEST_FRAMEWORK
2012-05-10 20:56:09 +00:00
AST_TEST_DEFINE ( sip_parse_uri_full_test )
2010-04-09 16:04:16 +00:00
{
int res = AST_TEST_PASS ;
char uri [ 1024 ] ;
2011-11-09 20:10:52 +00:00
char * user , * pass , * hostport , * headers , * residue ;
2010-04-09 16:04:16 +00:00
struct uriparams params ;
struct testdata {
char * desc ;
char * uri ;
char * user ;
char * pass ;
2011-11-09 20:10:52 +00:00
char * hostport ;
2010-04-09 16:04:16 +00:00
char * headers ;
char * residue ;
struct uriparams params ;
AST_LIST_ENTRY ( testdata ) list ;
} ;
struct testdata * testdataptr ;
static AST_LIST_HEAD_NOLOCK ( testdataliststruct , testdata ) testdatalist ;
struct testdata td1 = {
. desc = " no headers " ,
. uri = " sip:user:secret@host:5060;param=discard;transport=tcp;param2=residue " ,
. user = " user " ,
. pass = " secret " ,
2011-11-09 20:10:52 +00:00
. hostport = " host:5060 " ,
2010-04-09 16:04:16 +00:00
. headers = " " ,
2010-07-08 22:08:07 +00:00
. residue = " param2=residue " ,
2010-04-09 16:04:16 +00:00
. params . transport = " tcp " ,
. params . lr = 0 ,
. params . user = " "
} ;
struct testdata td2 = {
. desc = " with headers " ,
. uri = " sip:user:secret@host:5060;param=discard;transport=tcp;param2=discard2?header=blah&header2=blah2;param3=residue " ,
. user = " user " ,
. pass = " secret " ,
2011-11-09 20:10:52 +00:00
. hostport = " host:5060 " ,
2010-04-09 16:04:16 +00:00
. headers = " header=blah&header2=blah2 " ,
. residue = " param3=residue " ,
. params . transport = " tcp " ,
. params . lr = 0 ,
. params . user = " "
} ;
struct testdata td3 = {
. desc = " difficult user " ,
. uri = " sip:-_.!~*'()&=+$,;?/:secret@host:5060;transport=tcp " ,
. user = " -_.!~*'()&=+$,;?/ " ,
. pass = " secret " ,
2011-11-09 20:10:52 +00:00
. hostport = " host:5060 " ,
2010-04-09 16:04:16 +00:00
. headers = " " ,
. residue = " " ,
. params . transport = " tcp " ,
. params . lr = 0 ,
. params . user = " "
} ;
struct testdata td4 = {
. desc = " difficult pass " ,
. uri = " sip:user:-_.!~*'()&=+$,@host:5060;transport=tcp " ,
. user = " user " ,
. pass = " -_.!~*'()&=+$, " ,
2011-11-09 20:10:52 +00:00
. hostport = " host:5060 " ,
2010-04-09 16:04:16 +00:00
. headers = " " ,
. residue = " " ,
. params . transport = " tcp " ,
. params . lr = 0 ,
. params . user = " "
} ;
struct testdata td5 = {
. desc = " difficult host " ,
. uri = " sip:user:secret@1-1.a-1.:5060;transport=tcp " ,
. user = " user " ,
. pass = " secret " ,
2011-11-09 20:10:52 +00:00
. hostport = " 1-1.a-1.:5060 " ,
2010-04-09 16:04:16 +00:00
. headers = " " ,
. residue = " " ,
. params . transport = " tcp " ,
. params . lr = 0 ,
. params . user = " "
} ;
struct testdata td6 = {
. desc = " difficult params near transport " ,
. uri = " sip:user:secret@host:5060;-_.!~*'()[]/:&+$=-_.!~*'()[]/:&+$;transport=tcp " ,
. user = " user " ,
. pass = " secret " ,
2011-11-09 20:10:52 +00:00
. hostport = " host:5060 " ,
2010-04-09 16:04:16 +00:00
. headers = " " ,
. residue = " " ,
. params . transport = " tcp " ,
. params . lr = 0 ,
. params . user = " "
} ;
struct testdata td7 = {
. desc = " difficult params near headers " ,
. uri = " sip:user:secret@host:5060;-_.!~*'()[]/:&+$=-_.!~*'()[]/:&+$?header=blah&header2=blah2;-_.!~*'()[]/:&+$=residue " ,
. user = " user " ,
. pass = " secret " ,
2011-11-09 20:10:52 +00:00
. hostport = " host:5060 " ,
2010-04-09 16:04:16 +00:00
. headers = " header=blah&header2=blah2 " ,
. residue = " -_.!~*'()[]/:&+$=residue " ,
. params . transport = " " ,
. params . lr = 0 ,
. params . user = " "
} ;
struct testdata td8 = {
. desc = " lr parameter " ,
. uri = " sip:user:secret@host:5060;param=discard;lr?header=blah " ,
. user = " user " ,
. pass = " secret " ,
2011-11-09 20:10:52 +00:00
. hostport = " host:5060 " ,
2010-04-09 16:04:16 +00:00
. headers = " header=blah " ,
. residue = " " ,
. params . transport = " " ,
. params . lr = 1 ,
. params . user = " "
} ;
struct testdata td9 = {
. desc = " alternative lr parameter " ,
. uri = " sip:user:secret@host:5060;param=discard;lr=yes?header=blah " ,
. user = " user " ,
. pass = " secret " ,
2011-11-09 20:10:52 +00:00
. hostport = " host:5060 " ,
2010-04-09 16:04:16 +00:00
. headers = " header=blah " ,
. residue = " " ,
. params . transport = " " ,
. params . lr = 1 ,
. params . user = " "
} ;
struct testdata td10 = {
. desc = " no lr parameter " ,
. uri = " sip:user:secret@host:5060;paramlr=lr;lr=no;lr=off;lr=0;lr=;=lr;lrextra;lrparam2=lr?header=blah " ,
. user = " user " ,
. pass = " secret " ,
2011-11-09 20:10:52 +00:00
. hostport = " host:5060 " ,
2010-04-09 16:04:16 +00:00
. headers = " header=blah " ,
. residue = " " ,
. params . transport = " " ,
. params . lr = 0 ,
. params . user = " "
} ;
2014-04-12 02:27:43 +00:00
/* RFC 3966 TEL URI INVITE */
struct testdata td11 = {
. desc = " tel local number " ,
. uri = " tel:0987654321;phone-context=+32987654321 " ,
. user = " 0987654321 " ,
. pass = " " ,
. hostport = " +32987654321 " ,
. headers = " " ,
. residue = " " ,
. params . transport = " " ,
. params . lr = 0 ,
. params . user = " "
} ;
struct testdata td12 = {
. desc = " tel global number " ,
. uri = " tel:+32987654321 " ,
. user = " +32987654321 " ,
. pass = " " ,
. hostport = " +32987654321 " ,
. headers = " " ,
. residue = " " ,
. params . transport = " " ,
. params . lr = 0 ,
. params . user = " "
} ;
/*
* Once the full RFC 3966 parsing is implemented,
* only the ext= or isub= parameters would be extracted from .user
* Then the ;param=discard would be ignored,
* and the .user would only contain "0987654321"
*/
struct testdata td13 = {
. desc = " tel local number " ,
. uri = " tel:0987654321;ext=1234;param=discard;phone-context=+32987654321;transport=udp;param2=discard2?header=blah&header2=blah2;param3=residue " ,
. user = " 0987654321;ext=1234;param=discard " ,
. pass = " " ,
. hostport = " +32987654321 " ,
. headers = " header=blah&header2=blah2 " ,
. residue = " param3=residue " ,
. params . transport = " udp " ,
. params . lr = 0 ,
. params . user = " "
} ;
2010-04-09 16:04:16 +00:00
AST_LIST_HEAD_SET_NOLOCK ( & testdatalist , & td1 ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & td2 , list ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & td3 , list ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & td4 , list ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & td5 , list ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & td6 , list ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & td7 , list ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & td8 , list ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & td9 , list ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & td10 , list ) ;
2014-04-12 02:27:43 +00:00
AST_LIST_INSERT_TAIL ( & testdatalist , & td11 , list ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & td12 , list ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & td13 , list ) ;
2010-04-09 16:04:16 +00:00
switch ( cmd ) {
case TEST_INIT :
info - > name = " sip_uri_full_parse_test " ;
2010-07-09 17:00:22 +00:00
info - > category = " /channels/chan_sip/ " ;
2010-04-09 16:04:16 +00:00
info - > summary = " tests sip full uri parsing " ;
info - > description =
" Tests full parsing of various URIs "
" Verifies output matches expected behavior. " ;
return AST_TEST_NOT_RUN ;
case TEST_EXECUTE :
break ;
}
AST_LIST_TRAVERSE ( & testdatalist , testdataptr , list ) {
2011-11-09 20:10:52 +00:00
user = pass = hostport = headers = residue = NULL ;
2010-04-09 16:04:16 +00:00
params . transport = params . user = params . method = params . ttl = params . maddr = NULL ;
params . lr = 0 ;
ast_copy_string ( uri , testdataptr - > uri , sizeof ( uri ) ) ;
2014-04-12 02:27:43 +00:00
if ( parse_uri_full ( uri , " sip:,sips:,tel: " , & user ,
2012-05-10 20:56:09 +00:00
& pass , & hostport ,
& params ,
& headers ,
& residue ) | |
( user & & strcmp ( testdataptr - > user , user ) ) | |
( pass & & strcmp ( testdataptr - > pass , pass ) ) | |
( hostport & & strcmp ( testdataptr - > hostport , hostport ) ) | |
( headers & & strcmp ( testdataptr - > headers , headers ) ) | |
( residue & & strcmp ( testdataptr - > residue , residue ) ) | |
( strcmp ( testdataptr - > params . transport , params . transport ) ) | |
( testdataptr - > params . lr ! = params . lr ) | |
( strcmp ( testdataptr - > params . user , params . user ) )
2010-04-09 16:04:16 +00:00
) {
ast_test_status_update ( test , " Sub-Test: %s, failed. \n " , testdataptr - > desc ) ;
res = AST_TEST_FAIL ;
}
}
return res ;
}
2016-07-18 19:40:22 -04:00
# endif
2010-04-09 16:04:16 +00:00
2010-07-08 22:08:07 +00:00
int parse_uri ( char * uri , const char * scheme , char * * user , char * * pass ,
2011-11-09 20:10:52 +00:00
char * * hostport , char * * transport ) {
2010-04-09 16:04:16 +00:00
int ret ;
char * headers ;
struct uriparams params ;
headers = NULL ;
2011-11-09 20:10:52 +00:00
ret = parse_uri_full ( uri , scheme , user , pass , hostport , & params , & headers , NULL ) ;
2010-04-09 16:04:16 +00:00
if ( transport ) {
* transport = params . transport ;
}
return ret ;
}
2016-07-18 19:40:22 -04:00
# ifdef TEST_FRAMEWORK
2010-02-03 20:33:32 +00:00
AST_TEST_DEFINE ( sip_parse_uri_test )
{
int res = AST_TEST_PASS ;
2011-11-09 20:10:52 +00:00
char * name , * pass , * hostport , * transport ;
2010-02-03 20:33:32 +00:00
char uri1 [ ] = " sip:name@host " ;
char uri2 [ ] = " sip:name@host;transport=tcp " ;
char uri3 [ ] = " sip:name:secret@host;transport=tcp " ;
char uri4 [ ] = " sip:name:secret@host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah " ;
2010-02-10 23:13:49 +00:00
/* test 5 is for NULL input */
char uri6 [ ] = " sip:name:secret@host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah " ;
char uri7 [ ] = " sip:name:secret@host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah " ;
2010-06-16 22:37:45 +00:00
char uri8 [ ] = " sip:host " ;
char uri9 [ ] = " sip:host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah " ;
char uri10 [ ] = " host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah " ;
char uri11 [ ] = " host " ;
2014-04-12 02:27:43 +00:00
char uri12 [ ] = " tel:911 " ; /* TEL URI Local number without context or global number */
2010-02-10 23:13:49 +00:00
2010-02-03 20:33:32 +00:00
switch ( cmd ) {
case TEST_INIT :
info - > name = " sip_uri_parse_test " ;
2010-07-09 17:00:22 +00:00
info - > category = " /channels/chan_sip/ " ;
2010-02-03 20:33:32 +00:00
info - > summary = " tests sip uri parsing " ;
info - > description =
2010-02-11 21:57:37 +00:00
" Tests parsing of various URIs "
" Verifies output matches expected behavior. " ;
2010-02-03 20:33:32 +00:00
return AST_TEST_NOT_RUN ;
case TEST_EXECUTE :
break ;
}
/* Test 1, simple URI */
2011-11-09 20:10:52 +00:00
name = pass = hostport = transport = NULL ;
if ( parse_uri ( uri1 , " sip:,sips: " , & name , & pass , & hostport , & transport ) | |
2010-02-09 23:32:14 +00:00
strcmp ( name , " name " ) | |
! ast_strlen_zero ( pass ) | |
2011-11-09 20:10:52 +00:00
strcmp ( hostport , " host " ) | |
2010-02-09 23:32:14 +00:00
! ast_strlen_zero ( transport ) ) {
ast_test_status_update ( test , " Test 1: simple uri failed. \n " ) ;
2010-02-03 20:33:32 +00:00
res = AST_TEST_FAIL ;
}
/* Test 2, add tcp transport */
2011-11-09 20:10:52 +00:00
name = pass = hostport = transport = NULL ;
if ( parse_uri ( uri2 , " sip:,sips: " , & name , & pass , & hostport , & transport ) | |
2010-02-09 23:32:14 +00:00
strcmp ( name , " name " ) | |
! ast_strlen_zero ( pass ) | |
2011-11-09 20:10:52 +00:00
strcmp ( hostport , " host " ) | |
2010-02-09 23:32:14 +00:00
strcmp ( transport , " tcp " ) ) {
ast_test_status_update ( test , " Test 2: uri with addtion of tcp transport failed. \n " ) ;
2010-02-03 20:33:32 +00:00
res = AST_TEST_FAIL ;
}
/* Test 3, add secret */
2011-11-09 20:10:52 +00:00
name = pass = hostport = transport = NULL ;
if ( parse_uri ( uri3 , " sip:,sips: " , & name , & pass , & hostport , & transport ) | |
2010-02-09 23:32:14 +00:00
strcmp ( name , " name " ) | |
strcmp ( pass , " secret " ) | |
2011-11-09 20:10:52 +00:00
strcmp ( hostport , " host " ) | |
2010-02-09 23:32:14 +00:00
strcmp ( transport , " tcp " ) ) {
ast_test_status_update ( test , " Test 3: uri with addition of secret failed. \n " ) ;
2010-02-03 20:33:32 +00:00
res = AST_TEST_FAIL ;
}
/* Test 4, add port and unparsed header field*/
2011-11-09 20:10:52 +00:00
name = pass = hostport = transport = NULL ;
if ( parse_uri ( uri4 , " sip:,sips: " , & name , & pass , & hostport , & transport ) | |
2010-02-09 23:32:14 +00:00
strcmp ( name , " name " ) | |
strcmp ( pass , " secret " ) | |
2011-11-09 20:10:52 +00:00
strcmp ( hostport , " host:port " ) | |
2010-02-09 23:32:14 +00:00
strcmp ( transport , " tcp " ) ) {
ast_test_status_update ( test , " Test 4: add port and unparsed header field failed. \n " ) ;
2010-02-03 20:33:32 +00:00
res = AST_TEST_FAIL ;
}
/* Test 5, verify parse_uri does not crash when given a NULL uri */
2011-11-09 20:10:52 +00:00
name = pass = hostport = transport = NULL ;
if ( ! parse_uri ( NULL , " sip:,sips: " , & name , & pass , & hostport , & transport ) ) {
2010-02-09 23:32:14 +00:00
ast_test_status_update ( test , " Test 5: passing a NULL uri failed. \n " ) ;
2010-02-03 20:33:32 +00:00
res = AST_TEST_FAIL ;
}
/* Test 6, verify parse_uri does not crash when given a NULL output parameters */
2011-11-09 20:10:52 +00:00
name = pass = hostport = transport = NULL ;
2010-07-08 22:08:07 +00:00
if ( parse_uri ( uri6 , " sip:,sips: " , NULL , NULL , NULL , NULL ) ) {
2010-02-09 23:32:14 +00:00
ast_test_status_update ( test , " Test 6: passing NULL output parameters failed. \n " ) ;
2010-02-03 20:33:32 +00:00
res = AST_TEST_FAIL ;
}
2011-11-09 20:10:52 +00:00
/* Test 7, verify parse_uri returns user:secret and hostport when no port or secret output parameters are supplied. */
name = pass = hostport = transport = NULL ;
if ( parse_uri ( uri7 , " sip:,sips: " , & name , NULL , & hostport , NULL ) | |
2010-02-10 23:13:49 +00:00
strcmp ( name , " name:secret " ) | |
2011-11-09 20:10:52 +00:00
strcmp ( hostport , " host:port " ) ) {
2010-02-10 23:13:49 +00:00
ast_test_status_update ( test , " Test 7: providing no port and secret output parameters failed. \n " ) ;
res = AST_TEST_FAIL ;
}
2010-06-16 22:37:45 +00:00
2011-11-09 20:10:52 +00:00
/* Test 8, verify parse_uri can handle a hostport only uri */
name = pass = hostport = transport = NULL ;
if ( parse_uri ( uri8 , " sip:,sips: " , & name , & pass , & hostport , & transport ) | |
strcmp ( hostport , " host " ) | |
2010-06-16 22:37:45 +00:00
! ast_strlen_zero ( name ) ) {
ast_test_status_update ( test , " Test 8: add port and unparsed header field failed. \n " ) ;
res = AST_TEST_FAIL ;
}
2011-11-09 20:10:52 +00:00
/* Test 9, add port and unparsed header field with hostport only uri*/
name = pass = hostport = transport = NULL ;
if ( parse_uri ( uri9 , " sip:,sips: " , & name , & pass , & hostport , & transport ) | |
2010-06-16 22:37:45 +00:00
! ast_strlen_zero ( name ) | |
! ast_strlen_zero ( pass ) | |
2011-11-09 20:10:52 +00:00
strcmp ( hostport , " host:port " ) | |
2010-06-16 22:37:45 +00:00
strcmp ( transport , " tcp " ) ) {
2011-11-09 20:10:52 +00:00
ast_test_status_update ( test , " Test 9: hostport only uri failed \n " ) ;
2010-06-16 22:37:45 +00:00
res = AST_TEST_FAIL ;
}
/* Test 10, handle invalid/missing "sip:,sips:" scheme
* we expect parse_uri to return an error, but still parse
* the results correctly here */
2011-11-09 20:10:52 +00:00
name = pass = hostport = transport = NULL ;
if ( ! parse_uri ( uri10 , " sip:,sips: " , & name , & pass , & hostport , & transport ) | |
2010-06-16 22:37:45 +00:00
! ast_strlen_zero ( name ) | |
! ast_strlen_zero ( pass ) | |
2011-11-09 20:10:52 +00:00
strcmp ( hostport , " host:port " ) | |
2010-06-16 22:37:45 +00:00
strcmp ( transport , " tcp " ) ) {
ast_test_status_update ( test , " Test 10: missing \" sip:sips: \" scheme failed \n " ) ;
res = AST_TEST_FAIL ;
}
2011-11-09 20:10:52 +00:00
/* Test 11, simple hostport only URI with missing scheme
2010-06-16 22:37:45 +00:00
* we expect parse_uri to return an error, but still parse
* the results correctly here */
2011-11-09 20:10:52 +00:00
name = pass = hostport = transport = NULL ;
if ( ! parse_uri ( uri11 , " sip:,sips: " , & name , & pass , & hostport , & transport ) | |
2010-06-16 22:37:45 +00:00
! ast_strlen_zero ( name ) | |
! ast_strlen_zero ( pass ) | |
2011-11-09 20:10:52 +00:00
strcmp ( hostport , " host " ) | |
2010-06-16 22:37:45 +00:00
! ast_strlen_zero ( transport ) ) {
ast_test_status_update ( test , " Test 11: simple uri with missing scheme failed. \n " ) ;
res = AST_TEST_FAIL ;
}
2014-04-12 02:27:43 +00:00
/* Test 12, simple URI */
name = pass = hostport = transport = NULL ;
if ( ! parse_uri ( uri12 , " sip:,sips:,tel: " , & name , & pass , & hostport , & transport ) | |
strcmp ( name , " 911 " ) | | /* We return local number anyway */
! ast_strlen_zero ( pass ) | |
! ast_strlen_zero ( hostport ) | | /* No global number nor context */
! ast_strlen_zero ( transport ) ) {
ast_test_status_update ( test , " Test 12: TEL URI INVITE failed. \n " ) ;
res = AST_TEST_FAIL ;
}
2010-02-03 20:33:32 +00:00
return res ;
}
2016-07-18 19:40:22 -04:00
# endif
2010-02-03 20:33:32 +00:00
/*! \brief Get caller id name from SIP headers, copy into output buffer
*
* \retval input string pointer placed after display-name field if possible
*/
const char * get_calleridname ( const char * input , char * output , size_t outputsize )
{
/* From RFC3261:
2010-07-23 16:33:52 +00:00
*
2010-02-03 20:33:32 +00:00
* From = ( "From" / "f" ) HCOLON from-spec
* from-spec = ( name-addr / addr-spec ) *( SEMI from-param )
* name-addr = [ display-name ] LAQUOT addr-spec RAQUOT
* display-name = *(token LWS)/ quoted-string
* token = 1*(alphanum / "-" / "." / "!" / "%" / "*"
* / "_" / "+" / "`" / "'" / "~" )
* quoted-string = SWS DQUOTE *(qdtext / quoted-pair ) DQUOTE
* qdtext = LWS / %x21 / %x23-5B / %x5D-7E
* / UTF8-NONASCII
* quoted-pair = "\" (%x00-09 / %x0B-0C / %x0E-7F)
*
* HCOLON = *WSP ":" SWS
* SWS = [LWS]
* LWS = *[*WSP CRLF] 1*WSP
* WSP = (SP / HTAB)
*
* Deviations from it:
* - following CRLF's in LWS is not done (here at least)
* - ascii NUL is never legal as it terminates the C-string
* - utf8-nonascii is not checked for validity
*/
char * orig_output = output ;
const char * orig_input = input ;
2012-01-19 23:31:17 +00:00
if ( ! output | | ! outputsize ) {
/* Bad output parameters. Should never happen. */
return input ;
}
2010-02-03 20:33:32 +00:00
/* clear any empty characters in the beginning */
input = ast_skip_blanks ( input ) ;
/* make sure the output buffer is initilized */
* orig_output = ' \0 ' ;
/* make room for '\0' at the end of the output buffer */
2012-01-19 23:31:17 +00:00
- - outputsize ;
/* no data at all or no display name? */
if ( ! input | | * input = = ' < ' ) {
return input ;
}
2010-02-03 20:33:32 +00:00
/* quoted-string rules */
if ( input [ 0 ] = = ' " ' ) {
input + + ; /* skip the first " */
2012-01-19 23:31:17 +00:00
for ( ; * input ; + + input ) {
2010-02-03 20:33:32 +00:00
if ( * input = = ' " ' ) { /* end of quoted-string */
break ;
} else if ( * input = = 0x5c ) { /* quoted-pair = "\" (%x00-09 / %x0B-0C / %x0E-7F) */
2012-01-19 23:31:17 +00:00
+ + input ;
if ( ! * input ) {
break ;
}
if ( ( unsigned char ) * input > 0x7f | | * input = = 0xa | | * input = = 0xd ) {
2010-02-03 20:33:32 +00:00
continue ; /* not a valid quoted-pair, so skip it */
}
2012-01-19 23:31:17 +00:00
} else if ( ( * input ! = 0x9 & & ( unsigned char ) * input < 0x20 )
| | * input = = 0x7f ) {
2010-02-03 20:33:32 +00:00
continue ; /* skip this invalid character. */
}
2012-01-19 23:31:17 +00:00
if ( 0 < outputsize ) {
/* We still have room for the output display-name. */
* output + + = * input ;
- - outputsize ;
}
2010-02-03 20:33:32 +00:00
}
/* if this is successful, input should be at the ending quote */
2012-01-19 23:31:17 +00:00
if ( * input ! = ' " ' ) {
2010-02-03 20:33:32 +00:00
ast_log ( LOG_WARNING , " No ending quote for display-name was found \n " ) ;
* orig_output = ' \0 ' ;
return orig_input ;
}
/* make sure input is past the last quote */
2012-01-19 23:31:17 +00:00
+ + input ;
2010-02-03 20:33:32 +00:00
2012-01-19 23:31:17 +00:00
/* terminate output */
2010-02-03 20:33:32 +00:00
* output = ' \0 ' ;
} else { /* either an addr-spec or tokenLWS-combo */
2012-01-19 23:31:17 +00:00
for ( ; * input ; + + input ) {
2010-02-03 20:33:32 +00:00
/* token or WSP (without LWS) */
if ( ( * input > = ' 0 ' & & * input < = ' 9 ' ) | | ( * input > = ' A ' & & * input < = ' Z ' )
| | ( * input > = ' a ' & & * input < = ' z ' ) | | * input = = ' - ' | | * input = = ' . '
| | * input = = ' ! ' | | * input = = ' % ' | | * input = = ' * ' | | * input = = ' _ '
| | * input = = ' + ' | | * input = = ' ` ' | | * input = = ' \' ' | | * input = = ' ~ '
| | * input = = 0x9 | | * input = = ' ' ) {
2012-01-19 23:31:17 +00:00
if ( 0 < outputsize ) {
/* We still have room for the output display-name. */
* output + + = * input ;
- - outputsize ;
}
2010-02-03 20:33:32 +00:00
} else if ( * input = = ' < ' ) { /* end of tokenLWS-combo */
/* we could assert that the previous char is LWS, but we don't care */
break ;
} else if ( * input = = ' : ' ) {
/* This invalid character which indicates this is addr-spec rather than display-name. */
* orig_output = ' \0 ' ;
return orig_input ;
} else { /* else, invalid character we can skip. */
continue ; /* skip this character */
}
}
2010-06-21 20:46:22 +00:00
if ( * input ! = ' < ' ) { /* if we never found the start of addr-spec then this is invalid */
* orig_output = ' \0 ' ;
return orig_input ;
}
2012-01-19 23:31:17 +00:00
/* terminate output while trimming any trailing whitespace */
2010-02-03 20:33:32 +00:00
do {
* output - - = ' \0 ' ;
2012-01-19 23:31:17 +00:00
} while ( orig_output < = output & & ( * output = = 0x9 | | * output = = ' ' ) ) ;
2010-02-03 20:33:32 +00:00
}
return input ;
}
2016-07-18 19:40:22 -04:00
# ifdef TEST_FRAMEWORK
2010-02-03 20:33:32 +00:00
AST_TEST_DEFINE ( get_calleridname_test )
{
int res = AST_TEST_PASS ;
2012-01-19 23:31:17 +00:00
const char * in1 = " \" quoted-text internal \\ \" quote \" <stuff> " ;
2010-02-03 20:33:32 +00:00
const char * in2 = " token text with no quotes <stuff> " ;
const char * overflow1 = " \" quoted-text overflow 1234567890123456789012345678901234567890 \" <stuff> " ;
2012-01-19 23:31:17 +00:00
const char * overflow2 = " non-quoted text overflow 1234567890123456789012345678901234567890 <stuff> " ;
2010-02-03 20:33:32 +00:00
const char * noendquote = " \" quoted-text no end <stuff> " ;
2012-01-19 23:31:17 +00:00
const char * addrspec = " sip:blah@blah " ;
2010-06-21 20:46:22 +00:00
const char * no_quotes_no_brackets = " blah@blah " ;
2010-02-03 20:33:32 +00:00
const char * after_dname ;
char dname [ 40 ] ;
switch ( cmd ) {
case TEST_INIT :
info - > name = " sip_get_calleridname_test " ;
2010-07-09 17:00:22 +00:00
info - > category = " /channels/chan_sip/ " ;
2010-02-03 20:33:32 +00:00
info - > summary = " decodes callerid name from sip header " ;
info - > description = " Decodes display-name field of sip header. Checks for valid output and expected failure cases. " ;
return AST_TEST_NOT_RUN ;
case TEST_EXECUTE :
break ;
}
/* quoted-text with backslash escaped quote */
after_dname = get_calleridname ( in1 , dname , sizeof ( dname ) ) ;
2010-02-09 23:32:14 +00:00
ast_test_status_update ( test , " display-name1: %s \n after: %s \n " , dname , after_dname ) ;
2010-02-03 20:33:32 +00:00
if ( strcmp ( dname , " quoted-text internal \" quote " ) ) {
2010-02-09 23:32:14 +00:00
ast_test_status_update ( test , " display-name1 test failed \n " ) ;
2010-02-03 20:33:32 +00:00
res = AST_TEST_FAIL ;
}
/* token text */
after_dname = get_calleridname ( in2 , dname , sizeof ( dname ) ) ;
2010-02-09 23:32:14 +00:00
ast_test_status_update ( test , " display-name2: %s \n after: %s \n " , dname , after_dname ) ;
2010-02-03 20:33:32 +00:00
if ( strcmp ( dname , " token text with no quotes " ) ) {
2010-02-09 23:32:14 +00:00
ast_test_status_update ( test , " display-name2 test failed \n " ) ;
2010-02-03 20:33:32 +00:00
res = AST_TEST_FAIL ;
}
/* quoted-text buffer overflow */
after_dname = get_calleridname ( overflow1 , dname , sizeof ( dname ) ) ;
2010-02-09 23:32:14 +00:00
ast_test_status_update ( test , " overflow display-name1: %s \n after: %s \n " , dname , after_dname ) ;
2012-01-19 23:31:17 +00:00
if ( strcmp ( dname , " quoted-text overflow 123456789012345678 " ) ) {
2010-02-09 23:32:14 +00:00
ast_test_status_update ( test , " overflow display-name1 test failed \n " ) ;
2010-02-03 20:33:32 +00:00
res = AST_TEST_FAIL ;
}
2012-01-19 23:31:17 +00:00
/* non-quoted-text buffer overflow */
after_dname = get_calleridname ( overflow2 , dname , sizeof ( dname ) ) ;
ast_test_status_update ( test , " overflow display-name2: %s \n after: %s \n " , dname , after_dname ) ;
if ( strcmp ( dname , " non-quoted text overflow 12345678901234 " ) ) {
ast_test_status_update ( test , " overflow display-name2 test failed \n " ) ;
res = AST_TEST_FAIL ;
}
2010-02-03 20:33:32 +00:00
/* quoted-text buffer with no terminating end quote */
after_dname = get_calleridname ( noendquote , dname , sizeof ( dname ) ) ;
2010-02-09 23:32:14 +00:00
ast_test_status_update ( test , " noendquote display-name1: %s \n after: %s \n " , dname , after_dname ) ;
2010-02-03 20:33:32 +00:00
if ( * dname ! = ' \0 ' & & after_dname ! = noendquote ) {
2010-02-09 23:32:14 +00:00
ast_test_status_update ( test , " no end quote for quoted-text display-name failed \n " ) ;
2010-02-03 20:33:32 +00:00
res = AST_TEST_FAIL ;
}
/* addr-spec rather than display-name. */
after_dname = get_calleridname ( addrspec , dname , sizeof ( dname ) ) ;
2012-01-19 23:31:17 +00:00
ast_test_status_update ( test , " addr-spec display-name1: %s \n after: %s \n " , dname , after_dname ) ;
2010-02-03 20:33:32 +00:00
if ( * dname ! = ' \0 ' & & after_dname ! = addrspec ) {
2010-02-09 23:32:14 +00:00
ast_test_status_update ( test , " detection of addr-spec failed \n " ) ;
2010-02-03 20:33:32 +00:00
res = AST_TEST_FAIL ;
}
2010-06-21 20:46:22 +00:00
/* no quotes, no brackets */
after_dname = get_calleridname ( no_quotes_no_brackets , dname , sizeof ( dname ) ) ;
ast_test_status_update ( test , " no_quotes_no_brackets display-name1: %s \n after: %s \n " , dname , after_dname ) ;
if ( * dname ! = ' \0 ' & & after_dname ! = no_quotes_no_brackets ) {
ast_test_status_update ( test , " detection of addr-spec failed \n " ) ;
res = AST_TEST_FAIL ;
}
2010-02-03 20:33:32 +00:00
return res ;
}
2016-07-18 19:40:22 -04:00
# endif
2010-02-03 20:33:32 +00:00
2010-02-15 15:45:02 +00:00
int get_name_and_number ( const char * hdr , char * * name , char * * number )
{
char header [ 256 ] ;
2013-09-27 21:44:42 +00:00
char tmp_name [ 256 ] ;
2010-02-15 15:45:02 +00:00
char * tmp_number = NULL ;
2011-11-09 20:10:52 +00:00
char * hostport = NULL ;
2010-02-15 15:45:02 +00:00
char * dummy = NULL ;
if ( ! name | | ! number | | ast_strlen_zero ( hdr ) ) {
return - 1 ;
}
* number = NULL ;
* name = NULL ;
ast_copy_string ( header , hdr , sizeof ( header ) ) ;
/* strip the display-name portion off the beginning of the header. */
get_calleridname ( header , tmp_name , sizeof ( tmp_name ) ) ;
/* get uri within < > brackets */
tmp_number = get_in_brackets ( header ) ;
/* parse out the number here */
2011-11-09 20:10:52 +00:00
if ( parse_uri ( tmp_number , " sip:,sips: " , & tmp_number , & dummy , & hostport , NULL ) | | ast_strlen_zero ( tmp_number ) ) {
2010-02-15 15:45:02 +00:00
ast_log ( LOG_ERROR , " can not parse name and number from sip header. \n " ) ;
return - 1 ;
}
/* number is not option, and must be present at this point */
* number = ast_strdup ( tmp_number ) ;
2011-01-24 18:59:22 +00:00
ast_uri_decode ( * number , ast_uri_sip_user ) ;
2010-02-15 15:45:02 +00:00
/* name is optional and may not be present at this point */
if ( ! ast_strlen_zero ( tmp_name ) ) {
* name = ast_strdup ( tmp_name ) ;
}
return 0 ;
}
2016-07-18 19:40:22 -04:00
# ifdef TEST_FRAMEWORK
2010-02-15 15:45:02 +00:00
AST_TEST_DEFINE ( get_name_and_number_test )
{
int res = AST_TEST_PASS ;
char * name = NULL ;
char * number = NULL ;
const char * in1 = " NAME <sip:NUMBER@place> " ;
const char * in2 = " \" NA><ME \" <sip:NUMBER@place> " ;
const char * in3 = " NAME " ;
const char * in4 = " <sip:NUMBER@place> " ;
const char * in5 = " This is a screwed up string <sip:LOLCLOWNS<sip:>@place> " ;
switch ( cmd ) {
case TEST_INIT :
info - > name = " sip_get_name_and_number_test " ;
2010-07-09 17:00:22 +00:00
info - > category = " /channels/chan_sip/ " ;
2010-02-15 15:45:02 +00:00
info - > summary = " Tests getting name and number from sip header " ;
info - > description =
" Runs through various test situations in which a name and "
" and number can be retrieved from a sip header. " ;
return AST_TEST_NOT_RUN ;
case TEST_EXECUTE :
break ;
}
/* Test 1. get name and number */
number = name = NULL ;
if ( ( get_name_and_number ( in1 , & name , & number ) ) | |
strcmp ( name , " NAME " ) | |
strcmp ( number , " NUMBER " ) ) {
ast_test_status_update ( test , " Test 1, simple get name and number failed. \n " ) ;
res = AST_TEST_FAIL ;
}
ast_free ( name ) ;
ast_free ( number ) ;
/* Test 2. get quoted name and number */
number = name = NULL ;
if ( ( get_name_and_number ( in2 , & name , & number ) ) | |
strcmp ( name , " NA><ME " ) | |
strcmp ( number , " NUMBER " ) ) {
ast_test_status_update ( test , " Test 2, get quoted name and number failed. \n " ) ;
res = AST_TEST_FAIL ;
}
ast_free ( name ) ;
ast_free ( number ) ;
/* Test 3. name only */
number = name = NULL ;
if ( ! ( get_name_and_number ( in3 , & name , & number ) ) ) {
ast_test_status_update ( test , " Test 3, get name only was expected to fail but did not. \n " ) ;
res = AST_TEST_FAIL ;
}
ast_free ( name ) ;
ast_free ( number ) ;
/* Test 4. number only */
number = name = NULL ;
if ( ( get_name_and_number ( in4 , & name , & number ) ) | |
! ast_strlen_zero ( name ) | |
strcmp ( number , " NUMBER " ) ) {
ast_test_status_update ( test , " Test 4, get number with no name present failed. \n " ) ;
res = AST_TEST_FAIL ;
}
ast_free ( name ) ;
ast_free ( number ) ;
/* Test 5. malformed string, since number can not be parsed, this should return an error. */
number = name = NULL ;
if ( ! ( get_name_and_number ( in5 , & name , & number ) ) | |
! ast_strlen_zero ( name ) | |
! ast_strlen_zero ( number ) ) {
ast_test_status_update ( test , " Test 5, processing malformed string failed. \n " ) ;
res = AST_TEST_FAIL ;
}
ast_free ( name ) ;
ast_free ( number ) ;
/* Test 6. NULL output parameters */
number = name = NULL ;
if ( ! ( get_name_and_number ( in5 , NULL , NULL ) ) ) {
ast_test_status_update ( test , " Test 6, NULL output parameters failed. \n " ) ;
res = AST_TEST_FAIL ;
}
/* Test 7. NULL input parameter */
number = name = NULL ;
if ( ! ( get_name_and_number ( NULL , & name , & number ) ) | |
! ast_strlen_zero ( name ) | |
! ast_strlen_zero ( number ) ) {
ast_test_status_update ( test , " Test 7, NULL input parameter failed. \n " ) ;
res = AST_TEST_FAIL ;
}
ast_free ( name ) ;
ast_free ( number ) ;
return res ;
}
2016-07-18 19:40:22 -04:00
# endif
2010-02-15 15:45:02 +00:00
2013-01-18 05:31:23 +00:00
int get_in_brackets_const ( const char * src , const char * * start , int * length )
{
const char * parse = src ;
const char * first_bracket ;
const char * second_bracket ;
if ( start = = NULL ) {
return - 1 ;
}
if ( length = = NULL ) {
return - 1 ;
}
* start = NULL ;
* length = - 1 ;
if ( ast_strlen_zero ( src ) ) {
return 1 ;
}
/*
* Skip any quoted text until we find the part in brackets.
* On any error give up and return -1
*/
while ( ( first_bracket = strchr ( parse , ' < ' ) ) ) {
const char * first_quote = strchr ( parse , ' " ' ) ;
first_bracket + + ;
if ( ! first_quote | | first_quote > = first_bracket ) {
break ; /* no need to look at quoted part */
}
/* the bracket is within quotes, so ignore it */
parse = find_closing_quote ( first_quote + 1 , NULL ) ;
if ( ! * parse ) {
ast_log ( LOG_WARNING , " No closing quote found in '%s' \n " , src ) ;
return - 1 ;
}
parse + + ;
}
/* Require a first bracket. Unlike get_in_brackets_full, this procedure is passed a const,
* so it can expect a pointer to an original value */
if ( ! first_bracket ) {
ast_log ( LOG_WARNING , " No opening bracket found in '%s' \n " , src ) ;
return 1 ;
}
if ( ( second_bracket = strchr ( first_bracket , ' > ' ) ) ) {
* start = first_bracket ;
* length = second_bracket - first_bracket ;
return 0 ;
}
ast_log ( LOG_WARNING , " No closing bracket found in '%s' \n " , src ) ;
return - 1 ;
}
2010-04-09 16:04:16 +00:00
int get_in_brackets_full ( char * tmp , char * * out , char * * residue )
2010-02-15 15:45:02 +00:00
{
const char * parse = tmp ;
char * first_bracket ;
2010-04-09 16:04:16 +00:00
char * second_bracket ;
if ( out ) {
* out = " " ;
}
if ( residue ) {
* residue = " " ;
}
2010-02-15 15:45:02 +00:00
if ( ast_strlen_zero ( tmp ) ) {
2010-04-09 16:04:16 +00:00
return 1 ;
2010-02-15 15:45:02 +00:00
}
/*
* Skip any quoted text until we find the part in brackets.
2010-04-09 16:04:16 +00:00
* On any error give up and return -1
2010-02-15 15:45:02 +00:00
*/
while ( ( first_bracket = strchr ( parse , ' < ' ) ) ) {
char * first_quote = strchr ( parse , ' " ' ) ;
2010-04-09 16:04:16 +00:00
first_bracket + + ;
if ( ! first_quote | | first_quote > = first_bracket ) {
2010-02-15 15:45:02 +00:00
break ; /* no need to look at quoted part */
2010-04-09 16:04:16 +00:00
}
2010-02-15 15:45:02 +00:00
/* the bracket is within quotes, so ignore it */
parse = find_closing_quote ( first_quote + 1 , NULL ) ;
if ( ! * parse ) {
ast_log ( LOG_WARNING , " No closing quote found in '%s' \n " , tmp ) ;
2010-04-09 16:04:16 +00:00
return - 1 ;
2010-02-15 15:45:02 +00:00
}
parse + + ;
}
2010-04-09 16:04:16 +00:00
/* If no first bracket then still look for a second bracket as some other parsing functions
may overwrite first bracket with NULL when terminating a token based display-name. As this
only affects token based display-names there is no danger of brackets being in quotes */
2010-02-15 15:45:02 +00:00
if ( first_bracket ) {
2010-04-09 16:04:16 +00:00
parse = first_bracket ;
2011-06-23 18:31:42 +00:00
} else {
2010-04-09 16:04:16 +00:00
parse = tmp ;
}
if ( ( second_bracket = strchr ( parse , ' > ' ) ) ) {
* second_bracket + + = ' \0 ' ;
if ( out ) {
2011-06-23 18:31:42 +00:00
* out = ( char * ) parse ;
2010-04-09 16:04:16 +00:00
}
if ( residue ) {
* residue = second_bracket ;
}
return 0 ;
}
if ( ( first_bracket ) ) {
2011-06-23 18:31:42 +00:00
ast_log ( LOG_WARNING , " No closing bracket found in '%s' \n " , tmp ) ;
2010-04-09 16:04:16 +00:00
return - 1 ;
2011-06-23 18:31:42 +00:00
}
2010-04-09 16:04:16 +00:00
if ( out ) {
* out = tmp ;
2010-02-15 15:45:02 +00:00
}
2010-06-17 21:23:41 +00:00
2010-04-09 16:04:16 +00:00
return 1 ;
}
2010-02-15 15:45:02 +00:00
2010-04-09 16:04:16 +00:00
char * get_in_brackets ( char * tmp )
{
char * out ;
2010-06-17 21:23:41 +00:00
if ( ( get_in_brackets_full ( tmp , & out , NULL ) ) ) {
return tmp ;
}
return out ;
2010-02-15 15:45:02 +00:00
}
2016-07-18 19:40:22 -04:00
# ifdef TEST_FRAMEWORK
2010-02-15 15:45:02 +00:00
AST_TEST_DEFINE ( get_in_brackets_test )
{
int res = AST_TEST_PASS ;
2012-01-19 23:31:17 +00:00
char in_brackets [ ] = " sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah " ;
2010-02-15 15:45:02 +00:00
char no_name [ ] = " <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah> " ;
char quoted_string [ ] = " \" I'm a quote stri><ng \" <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah> " ;
char missing_end_quote [ ] = " \" I'm a quote string <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah> " ;
char name_no_quotes [ ] = " name not in quotes <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah> " ;
char no_end_bracket [ ] = " name not in quotes <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah " ;
2010-06-21 20:46:22 +00:00
char no_name_no_brackets [ ] = " sip:name@host " ;
2012-01-19 23:31:17 +00:00
char missing_start_bracket [ ] = " sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah> " ;
2010-02-15 15:45:02 +00:00
char * uri = NULL ;
switch ( cmd ) {
case TEST_INIT :
info - > name = " sip_get_in_brackets_test " ;
2010-07-09 17:00:22 +00:00
info - > category = " /channels/chan_sip/ " ;
2010-02-15 15:45:02 +00:00
info - > summary = " Tests getting a sip uri in <> brackets within a sip header. " ;
info - > description =
" Runs through various test situations in which a sip uri "
" in angle brackets needs to be retrieved " ;
return AST_TEST_NOT_RUN ;
case TEST_EXECUTE :
break ;
}
/* Test 1, simple get in brackets */
2012-01-19 23:31:17 +00:00
if ( ! ( uri = get_in_brackets ( no_name ) ) | | strcmp ( uri , in_brackets ) ) {
ast_test_status_update ( test , " Test 1, simple get in brackets failed. %s \n " , uri ) ;
2010-02-15 15:45:02 +00:00
res = AST_TEST_FAIL ;
}
/* Test 2, starts with quoted string */
2012-01-19 23:31:17 +00:00
if ( ! ( uri = get_in_brackets ( quoted_string ) ) | | strcmp ( uri , in_brackets ) ) {
ast_test_status_update ( test , " Test 2, get in brackets with quoted string in front failed. %s \n " , uri ) ;
2010-02-15 15:45:02 +00:00
res = AST_TEST_FAIL ;
}
/* Test 3, missing end quote */
2012-01-19 23:31:17 +00:00
if ( ! ( uri = get_in_brackets ( missing_end_quote ) ) | | ! strcmp ( uri , in_brackets ) ) {
ast_test_status_update ( test , " Test 3, missing end quote failed. %s \n " , uri ) ;
2010-02-15 15:45:02 +00:00
res = AST_TEST_FAIL ;
}
/* Test 4, starts with a name not in quotes */
2012-01-19 23:31:17 +00:00
if ( ! ( uri = get_in_brackets ( name_no_quotes ) ) | | strcmp ( uri , in_brackets ) ) {
ast_test_status_update ( test , " Test 4, passing name not in quotes failed. %s \n " , uri ) ;
2010-02-15 15:45:02 +00:00
res = AST_TEST_FAIL ;
}
/* Test 5, no end bracket, should just return everything after the first '<' */
2012-01-19 23:31:17 +00:00
if ( ! ( uri = get_in_brackets ( no_end_bracket ) ) | | ! strcmp ( uri , in_brackets ) ) {
ast_test_status_update ( test , " Test 5, no end bracket failed. %s \n " , uri ) ;
2010-02-15 15:45:02 +00:00
res = AST_TEST_FAIL ;
}
/* Test 6, NULL input */
2012-06-11 15:23:30 +00:00
if ( get_in_brackets ( NULL ) ) {
2010-02-15 15:45:02 +00:00
ast_test_status_update ( test , " Test 6, NULL input failed. \n " ) ;
res = AST_TEST_FAIL ;
}
2010-06-21 20:46:22 +00:00
/* Test 7, no name, and no brackets. */
2012-01-19 23:31:17 +00:00
if ( ! ( uri = get_in_brackets ( no_name_no_brackets ) ) | | strcmp ( uri , " sip:name@host " ) ) {
2010-06-21 20:46:22 +00:00
ast_test_status_update ( test , " Test 7 failed. %s \n " , uri ) ;
res = AST_TEST_FAIL ;
}
2010-02-15 15:45:02 +00:00
2011-06-23 18:31:42 +00:00
/* Test 8, no start bracket, but with ending bracket. */
2012-01-19 23:31:17 +00:00
if ( ! ( uri = get_in_brackets ( missing_start_bracket ) ) | | strcmp ( uri , in_brackets ) ) {
2011-06-23 18:31:42 +00:00
ast_test_status_update ( test , " Test 8 failed. %s \n " , uri ) ;
res = AST_TEST_FAIL ;
}
2010-02-15 15:45:02 +00:00
return res ;
}
2016-07-18 19:40:22 -04:00
# endif
2010-04-09 16:04:16 +00:00
2010-07-08 22:08:07 +00:00
int parse_name_andor_addr ( char * uri , const char * scheme , char * * name ,
2011-11-09 20:10:52 +00:00
char * * user , char * * pass , char * * hostport ,
2010-07-08 22:08:07 +00:00
struct uriparams * params , char * * headers ,
char * * residue )
2010-04-09 16:04:16 +00:00
{
2011-03-23 02:51:09 +00:00
char buf [ 1024 ] ;
2012-01-19 23:31:17 +00:00
char * * residue2 = residue ;
char * orig_uri = uri ;
2010-04-09 16:04:16 +00:00
int ret ;
2012-01-19 23:31:17 +00:00
buf [ 0 ] = ' \0 ' ;
2010-04-09 16:04:16 +00:00
if ( name ) {
2012-01-19 23:31:17 +00:00
uri = ( char * ) get_calleridname ( uri , buf , sizeof ( buf ) ) ;
}
ret = get_in_brackets_full ( uri , & uri , residue ) ;
if ( ret = = 0 ) {
/*
* The uri is in brackets so do not treat unknown trailing uri
* parameters as potential message header parameters.
*/
if ( residue & & * * residue ) {
/* step over the first semicolon as per parse_uri_full residue */
* residue = * residue + 1 ;
}
2010-04-09 16:04:16 +00:00
residue2 = NULL ;
}
2012-01-19 23:31:17 +00:00
if ( name ) {
if ( buf [ 0 ] ) {
/*
* There is always room at orig_uri for the display-name because
* at least one character has always been removed. A '"' or '<'
* has been removed.
*/
strcpy ( orig_uri , buf ) ;
* name = orig_uri ;
} else {
* name = " " ;
}
}
return parse_uri_full ( uri , scheme , user , pass , hostport , params , headers , residue2 ) ;
2010-04-09 16:04:16 +00:00
}
2016-07-18 19:40:22 -04:00
# ifdef TEST_FRAMEWORK
2010-04-09 16:04:16 +00:00
AST_TEST_DEFINE ( parse_name_andor_addr_test )
{
int res = AST_TEST_PASS ;
char uri [ 1024 ] ;
2011-11-09 20:10:52 +00:00
char * name , * user , * pass , * hostport , * headers , * residue ;
2010-04-09 16:04:16 +00:00
struct uriparams params ;
struct testdata {
char * desc ;
char * uri ;
char * name ;
char * user ;
char * pass ;
2011-11-09 20:10:52 +00:00
char * hostport ;
2010-04-09 16:04:16 +00:00
char * headers ;
char * residue ;
struct uriparams params ;
AST_LIST_ENTRY ( testdata ) list ;
} ;
struct testdata * testdataptr ;
static AST_LIST_HEAD_NOLOCK ( testdataliststruct , testdata ) testdatalist ;
struct testdata td1 = {
. desc = " quotes and brackets " ,
. uri = " \" name :@ \" <sip:user:secret@host:5060;param=discard;transport=tcp>;tag=tag " ,
. name = " name :@ " ,
. user = " user " ,
. pass = " secret " ,
2011-11-09 20:10:52 +00:00
. hostport = " host:5060 " ,
2010-04-09 16:04:16 +00:00
. headers = " " ,
. residue = " tag=tag " ,
. params . transport = " tcp " ,
. params . lr = 0 ,
. params . user = " "
} ;
struct testdata td2 = {
. desc = " no quotes " ,
. uri = " givenname familyname <sip:user:secret@host:5060;param=discard;transport=tcp>;expires=3600 " ,
. name = " givenname familyname " ,
. user = " user " ,
. pass = " secret " ,
2011-11-09 20:10:52 +00:00
. hostport = " host:5060 " ,
2010-04-09 16:04:16 +00:00
. headers = " " ,
. residue = " expires=3600 " ,
. params . transport = " tcp " ,
. params . lr = 0 ,
. params . user = " "
} ;
struct testdata td3 = {
. desc = " no brackets " ,
. uri = " sip:user:secret@host:5060;param=discard;transport=tcp;q=1 " ,
. name = " " ,
. user = " user " ,
. pass = " secret " ,
2011-11-09 20:10:52 +00:00
. hostport = " host:5060 " ,
2010-04-09 16:04:16 +00:00
. headers = " " ,
. residue = " q=1 " ,
. params . transport = " tcp " ,
. params . lr = 0 ,
. params . user = " "
} ;
struct testdata td4 = {
. desc = " just host " ,
. uri = " sips:host " ,
. name = " " ,
. user = " " ,
. pass = " " ,
2011-11-09 20:10:52 +00:00
. hostport = " host " ,
2010-04-09 16:04:16 +00:00
. headers = " " ,
. residue = " " ,
. params . transport = " " ,
. params . lr = 0 ,
. params . user = " "
} ;
AST_LIST_HEAD_SET_NOLOCK ( & testdatalist , & td1 ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & td2 , list ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & td3 , list ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & td4 , list ) ;
switch ( cmd ) {
case TEST_INIT :
info - > name = " parse_name_andor_addr_test " ;
2010-07-09 17:00:22 +00:00
info - > category = " /channels/chan_sip/ " ;
2010-04-09 16:04:16 +00:00
info - > summary = " tests parsing of name_andor_addr abnf structure " ;
info - > description =
" Tests parsing of abnf name-andor-addr = name-addr / addr-spec "
" Verifies output matches expected behavior. " ;
return AST_TEST_NOT_RUN ;
case TEST_EXECUTE :
break ;
}
AST_LIST_TRAVERSE ( & testdatalist , testdataptr , list ) {
2011-11-09 20:10:52 +00:00
name = user = pass = hostport = headers = residue = NULL ;
2010-04-09 16:04:16 +00:00
params . transport = params . user = params . method = params . ttl = params . maddr = NULL ;
params . lr = 0 ;
2012-01-19 23:31:17 +00:00
ast_copy_string ( uri , testdataptr - > uri , sizeof ( uri ) ) ;
2010-07-08 22:08:07 +00:00
if ( parse_name_andor_addr ( uri , " sip:,sips: " ,
2012-05-10 20:56:09 +00:00
& name ,
& user ,
& pass ,
& hostport ,
& params ,
& headers ,
& residue ) | |
( name & & strcmp ( testdataptr - > name , name ) ) | |
( user & & strcmp ( testdataptr - > user , user ) ) | |
( pass & & strcmp ( testdataptr - > pass , pass ) ) | |
( hostport & & strcmp ( testdataptr - > hostport , hostport ) ) | |
( headers & & strcmp ( testdataptr - > headers , headers ) ) | |
( residue & & strcmp ( testdataptr - > residue , residue ) ) | |
( strcmp ( testdataptr - > params . transport , params . transport ) ) | |
( strcmp ( testdataptr - > params . user , params . user ) )
2012-01-19 23:31:17 +00:00
) {
ast_test_status_update ( test , " Sub-Test: %s,failed. \n " , testdataptr - > desc ) ;
res = AST_TEST_FAIL ;
2010-04-09 16:04:16 +00:00
}
}
return res ;
}
2016-07-18 19:40:22 -04:00
# endif
2010-04-09 16:04:16 +00:00
2012-01-19 23:31:17 +00:00
int get_comma ( char * in , char * * out )
{
2010-04-09 16:04:16 +00:00
char * c ;
char * parse = in ;
if ( out ) {
* out = in ;
}
/* Skip any quoted text */
while ( * parse ) {
if ( ( c = strchr ( parse , ' " ' ) ) ) {
in = ( char * ) find_closing_quote ( ( const char * ) c + 1 , NULL ) ;
if ( ! * in ) {
ast_log ( LOG_WARNING , " No closing quote found in '%s' \n " , c ) ;
return - 1 ;
} else {
break ;
}
} else {
break ;
}
parse + + ;
}
parse = in ;
/* Skip any userinfo components of a uri as they may contain commas */
if ( ( c = strchr ( parse , ' @ ' ) ) ) {
parse = c + 1 ;
}
if ( ( out ) & & ( c = strchr ( parse , ' , ' ) ) ) {
* c + + = ' \0 ' ;
* out = c ;
return 0 ;
}
return 1 ;
}
2012-01-19 23:31:17 +00:00
int parse_contact_header ( char * contactheader , struct contactliststruct * contactlist )
{
2010-04-09 16:04:16 +00:00
int res ;
int last ;
char * comma ;
char * residue ;
char * param ;
char * value ;
2012-01-19 23:31:17 +00:00
struct contact * split_contact = NULL ;
2010-04-09 16:04:16 +00:00
if ( * contactheader = = ' * ' ) {
return 1 ;
}
2012-01-19 23:31:17 +00:00
split_contact = ast_calloc ( 1 , sizeof ( * split_contact ) ) ;
2010-04-09 16:04:16 +00:00
2012-01-19 23:31:17 +00:00
AST_LIST_HEAD_SET_NOLOCK ( contactlist , split_contact ) ;
while ( ( last = get_comma ( contactheader , & comma ) ) ! = - 1 ) {
2010-07-08 22:08:07 +00:00
res = parse_name_andor_addr ( contactheader , " sip:,sips: " ,
2012-01-19 23:31:17 +00:00
& split_contact - > name , & split_contact - > user ,
& split_contact - > pass , & split_contact - > hostport ,
& split_contact - > params , & split_contact - > headers ,
& residue ) ;
2010-04-09 16:04:16 +00:00
if ( res = = - 1 ) {
return res ;
}
/* parse contact params */
2012-01-19 23:31:17 +00:00
split_contact - > expires = split_contact - > q = " " ;
2010-04-09 16:04:16 +00:00
while ( ( value = strchr ( residue , ' = ' ) ) ) {
* value + + = ' \0 ' ;
param = residue ;
if ( ( residue = strchr ( value , ' ; ' ) ) ) {
* residue + + = ' \0 ' ;
} else {
residue = " " ;
}
if ( ! strcmp ( param , " expires " ) ) {
2012-01-19 23:31:17 +00:00
split_contact - > expires = value ;
2010-04-09 16:04:16 +00:00
} else if ( ! strcmp ( param , " q " ) ) {
2012-01-19 23:31:17 +00:00
split_contact - > q = value ;
2010-04-09 16:04:16 +00:00
}
}
2012-01-19 23:31:17 +00:00
if ( last ) {
2010-04-09 16:04:16 +00:00
return 0 ;
}
contactheader = comma ;
2012-01-19 23:31:17 +00:00
split_contact = ast_calloc ( 1 , sizeof ( * split_contact ) ) ;
AST_LIST_INSERT_TAIL ( contactlist , split_contact , list ) ;
2010-04-09 16:04:16 +00:00
}
return last ;
}
2016-07-18 19:40:22 -04:00
# ifdef TEST_FRAMEWORK
2010-04-09 16:04:16 +00:00
AST_TEST_DEFINE ( parse_contact_header_test )
{
int res = AST_TEST_PASS ;
char contactheader [ 1024 ] ;
int star ;
struct contactliststruct contactlist ;
struct contactliststruct * contactlistptr = & contactlist ;
struct testdata {
char * desc ;
char * contactheader ;
int star ;
struct contactliststruct * contactlist ;
AST_LIST_ENTRY ( testdata ) list ;
} ;
struct testdata * testdataptr ;
struct contact * tdcontactptr ;
struct contact * contactptr ;
static AST_LIST_HEAD_NOLOCK ( testdataliststruct , testdata ) testdatalist ;
struct contactliststruct contactlist1 , contactlist2 ;
struct testdata td1 = {
. desc = " single contact " ,
. contactheader = " \" name :@;?&, \" <sip:user:secret@host:5082;param=discard;transport=tcp>;expires=3600 " ,
. contactlist = & contactlist1 ,
. star = 0
} ;
struct contact contact11 = {
. name = " name :@;?&, " ,
. user = " user " ,
. pass = " secret " ,
2011-11-09 20:10:52 +00:00
. hostport = " host:5082 " ,
2010-04-09 16:04:16 +00:00
. params . transport = " tcp " ,
. params . ttl = " " ,
. params . lr = 0 ,
. headers = " " ,
. expires = " 3600 " ,
. q = " "
} ;
struct testdata td2 = {
. desc = " multiple contacts " ,
. contactheader = " sip:,user1,:,secret1,@host1;ttl=7;q=1;expires=3600,sips:host2 " ,
. contactlist = & contactlist2 ,
. star = 0 ,
} ;
struct contact contact21 = {
. name = " " ,
. user = " ,user1, " ,
. pass = " ,secret1, " ,
2011-11-09 20:10:52 +00:00
. hostport = " host1 " ,
2010-04-09 16:04:16 +00:00
. params . transport = " " ,
. params . ttl = " 7 " ,
. params . lr = 0 ,
. headers = " " ,
. expires = " 3600 " ,
. q = " 1 "
} ;
struct contact contact22 = {
. name = " " ,
. user = " " ,
. pass = " " ,
2011-11-09 20:10:52 +00:00
. hostport = " host2 " ,
2010-04-09 16:04:16 +00:00
. params . transport = " " ,
. params . ttl = " " ,
. params . lr = 0 ,
. headers = " " ,
. expires = " " ,
. q = " "
} ;
struct testdata td3 = {
. desc = " star - all contacts " ,
. contactheader = " * " ,
. star = 1 ,
. contactlist = NULL
} ;
AST_LIST_HEAD_SET_NOLOCK ( & testdatalist , & td1 ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & td2 , list ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & td3 , list ) ;
AST_LIST_HEAD_SET_NOLOCK ( & contactlist1 , & contact11 ) ;
AST_LIST_HEAD_SET_NOLOCK ( & contactlist2 , & contact21 ) ;
AST_LIST_INSERT_TAIL ( & contactlist2 , & contact22 , list ) ;
switch ( cmd ) {
case TEST_INIT :
info - > name = " parse_contact_header_test " ;
2010-07-09 17:00:22 +00:00
info - > category = " /channels/chan_sip/ " ;
2010-04-09 16:04:16 +00:00
info - > summary = " tests parsing of sip contact header " ;
info - > description =
" Tests parsing of a contact header including those with multiple contacts "
" Verifies output matches expected behavior. " ;
return AST_TEST_NOT_RUN ;
case TEST_EXECUTE :
break ;
}
AST_LIST_TRAVERSE ( & testdatalist , testdataptr , list ) {
ast_copy_string ( contactheader , testdataptr - > contactheader , sizeof ( contactheader ) ) ;
star = parse_contact_header ( contactheader , contactlistptr ) ;
if ( testdataptr - > star ) {
/* expecting star rather than list of contacts */
if ( ! star ) {
ast_test_status_update ( test , " Sub-Test: %s,failed. \n " , testdataptr - > desc ) ;
res = AST_TEST_FAIL ;
break ;
}
} else {
contactptr = AST_LIST_FIRST ( contactlistptr ) ;
AST_LIST_TRAVERSE ( testdataptr - > contactlist , tdcontactptr , list ) {
if ( ! contactptr | |
strcmp ( tdcontactptr - > name , contactptr - > name ) | |
strcmp ( tdcontactptr - > user , contactptr - > user ) | |
strcmp ( tdcontactptr - > pass , contactptr - > pass ) | |
2011-11-09 20:10:52 +00:00
strcmp ( tdcontactptr - > hostport , contactptr - > hostport ) | |
2010-04-09 16:04:16 +00:00
strcmp ( tdcontactptr - > headers , contactptr - > headers ) | |
strcmp ( tdcontactptr - > expires , contactptr - > expires ) | |
strcmp ( tdcontactptr - > q , contactptr - > q ) | |
strcmp ( tdcontactptr - > params . transport , contactptr - > params . transport ) | |
strcmp ( tdcontactptr - > params . ttl , contactptr - > params . ttl ) | |
( tdcontactptr - > params . lr ! = contactptr - > params . lr )
2012-01-19 23:31:17 +00:00
) {
2010-04-09 16:04:16 +00:00
ast_test_status_update ( test , " Sub-Test: %s,failed. \n " , testdataptr - > desc ) ;
res = AST_TEST_FAIL ;
break ;
}
2012-01-19 23:31:17 +00:00
contactptr = AST_LIST_NEXT ( contactptr , list ) ;
2010-04-09 16:04:16 +00:00
}
2013-10-23 20:10:30 +00:00
while ( ( contactptr = AST_LIST_REMOVE_HEAD ( contactlistptr , list ) ) ) {
ast_free ( contactptr ) ;
}
2010-04-09 16:04:16 +00:00
}
}
return res ;
}
2016-07-18 19:40:22 -04:00
# endif
2010-04-09 16:04:16 +00:00
2010-06-28 18:38:47 +00:00
/*!
* \brief Parse supported header in incoming packet
*
* \details This function parses through the options parameters and
* builds a bit field representing all the SIP options in that field. When an
* item is found that is not supported, it is copied to the unsupported
* out buffer.
*
2012-09-21 17:14:59 +00:00
* \param options list
2010-06-28 18:38:47 +00:00
* \param unsupported out buffer (optional)
2012-09-21 17:14:59 +00:00
* \param unsupported_len out buffer length (optional)
2010-06-28 18:38:47 +00:00
*/
unsigned int parse_sip_options ( const char * options , char * unsupported , size_t unsupported_len )
{
char * next , * sep ;
char * temp ;
int i , found , supported ;
unsigned int profile = 0 ;
char * out = unsupported ;
size_t outlen = unsupported_len ;
char * cur_out = out ;
if ( ast_strlen_zero ( options ) )
return 0 ;
temp = ast_strdupa ( options ) ;
ast_debug ( 3 , " Begin: parsing SIP \" Supported: %s \" \n " , options ) ;
for ( next = temp ; next ; next = sep ) {
found = FALSE ;
supported = FALSE ;
if ( ( sep = strchr ( next , ' , ' ) ) ! = NULL ) {
* sep + + = ' \0 ' ;
}
/* trim leading and trailing whitespace */
next = ast_strip ( next ) ;
if ( ast_strlen_zero ( next ) ) {
continue ; /* if there is a blank argument in there just skip it */
}
ast_debug ( 3 , " Found SIP option: -%s- \n " , next ) ;
for ( i = 0 ; i < ARRAY_LEN ( sip_options ) ; i + + ) {
if ( ! strcasecmp ( next , sip_options [ i ] . text ) ) {
profile | = sip_options [ i ] . id ;
if ( sip_options [ i ] . supported = = SUPPORTED ) {
supported = TRUE ;
}
found = TRUE ;
ast_debug ( 3 , " Matched SIP option: %s \n " , next ) ;
break ;
}
}
/* If option is not supported, add to unsupported out buffer */
if ( ! supported & & out & & outlen ) {
size_t copylen = strlen ( next ) ;
size_t cur_outlen = strlen ( out ) ;
/* Check to see if there is enough room to store this option.
* Copy length is string length plus 2 for the ',' and '\0' */
if ( ( cur_outlen + copylen + 2 ) < outlen ) {
/* if this isn't the first item, add the ',' */
if ( cur_outlen ) {
* cur_out = ' , ' ;
cur_out + + ;
cur_outlen + + ;
}
ast_copy_string ( cur_out , next , ( outlen - cur_outlen ) ) ;
cur_out + = copylen ;
}
}
if ( ! found ) {
profile | = SIP_OPT_UNKNOWN ;
if ( ! strncasecmp ( next , " x- " , 2 ) )
ast_debug ( 3 , " Found private SIP option, not supported: %s \n " , next ) ;
else
ast_debug ( 3 , " Found no match for SIP option: %s (Please file bug report!) \ n " , next) ;
}
}
return profile ;
}
2016-07-18 19:40:22 -04:00
# ifdef TEST_FRAMEWORK
2010-06-28 18:38:47 +00:00
AST_TEST_DEFINE ( sip_parse_options_test )
{
int res = AST_TEST_PASS ;
char unsupported [ 64 ] ;
unsigned int option_profile = 0 ;
struct testdata {
char * name ;
char * input_options ;
char * expected_unsupported ;
unsigned int expected_profile ;
AST_LIST_ENTRY ( testdata ) list ;
} ;
struct testdata * testdataptr ;
static AST_LIST_HEAD_NOLOCK ( testdataliststruct , testdata ) testdatalist ;
struct testdata test1 = {
. name = " test_all_unsupported " ,
. input_options = " unsupported1,,, ,unsupported2,unsupported3,unsupported4 " ,
. expected_unsupported = " unsupported1,unsupported2,unsupported3,unsupported4 " ,
. expected_profile = SIP_OPT_UNKNOWN ,
} ;
struct testdata test2 = {
. name = " test_all_unsupported_one_supported " ,
. input_options = " unsupported1, replaces, unsupported3 , , , ,unsupported4 " ,
. expected_unsupported = " unsupported1,unsupported3,unsupported4 " ,
. expected_profile = SIP_OPT_UNKNOWN | SIP_OPT_REPLACES
} ;
struct testdata test3 = {
. name = " test_two_supported_two_unsupported " ,
. input_options = " ,, timer ,replaces ,unsupported3,unsupported4 " ,
. expected_unsupported = " unsupported3,unsupported4 " ,
. expected_profile = SIP_OPT_UNKNOWN | SIP_OPT_REPLACES | SIP_OPT_TIMER ,
} ;
struct testdata test4 = {
. name = " test_all_supported " ,
. input_options = " timer,replaces " ,
. expected_unsupported = " " ,
. expected_profile = SIP_OPT_REPLACES | SIP_OPT_TIMER ,
} ;
struct testdata test5 = {
. name = " test_all_supported_redundant " ,
. input_options = " timer,replaces,timer,replace,timer,replaces " ,
. expected_unsupported = " " ,
. expected_profile = SIP_OPT_REPLACES | SIP_OPT_TIMER ,
} ;
struct testdata test6 = {
. name = " test_buffer_overflow " ,
. input_options = " unsupported1,replaces,timer,unsupported4,unsupported_huge____ "
" ____________________________________,__________________________________________ "
" ________________________________________________ " ,
. expected_unsupported = " unsupported1,unsupported4 " ,
. expected_profile = SIP_OPT_UNKNOWN | SIP_OPT_REPLACES | SIP_OPT_TIMER ,
} ;
struct testdata test7 = {
. name = " test_null_input " ,
. input_options = NULL ,
. expected_unsupported = " " ,
. expected_profile = 0 ,
} ;
struct testdata test8 = {
. name = " test_whitespace_input " ,
. input_options = " " ,
. expected_unsupported = " " ,
. expected_profile = 0 ,
} ;
struct testdata test9 = {
. name = " test_whitespace_plus_option_input " ,
. input_options = " , , ,timer , , , , , " ,
. expected_unsupported = " " ,
. expected_profile = SIP_OPT_TIMER ,
} ;
switch ( cmd ) {
case TEST_INIT :
info - > name = " sip_parse_options_test " ;
2010-07-09 17:00:22 +00:00
info - > category = " /channels/chan_sip/ " ;
2010-06-28 18:38:47 +00:00
info - > summary = " Tests parsing of sip options " ;
info - > description =
" Tests parsing of SIP options from supported and required "
" header fields. Verifies when unsupported options are encountered "
" that they are appended to the unsupported out buffer and that the "
" correct bit field representnig the option profile is returned. " ;
return AST_TEST_NOT_RUN ;
case TEST_EXECUTE :
break ;
}
AST_LIST_HEAD_SET_NOLOCK ( & testdatalist , & test1 ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & test2 , list ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & test3 , list ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & test4 , list ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & test5 , list ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & test6 , list ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & test7 , list ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & test8 , list ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & test9 , list ) ;
/* Test with unsupported char buffer */
AST_LIST_TRAVERSE ( & testdatalist , testdataptr , list ) {
2014-10-31 03:26:28 +00:00
memset ( unsupported , 0 , sizeof ( unsupported ) ) ;
2010-06-28 18:38:47 +00:00
option_profile = parse_sip_options ( testdataptr - > input_options , unsupported , ARRAY_LEN ( unsupported ) ) ;
if ( option_profile ! = testdataptr - > expected_profile | |
strcmp ( unsupported , testdataptr - > expected_unsupported ) ) {
ast_test_status_update ( test , " Test with output buffer \" %s \" , expected unsupported: %s actual unsupported: "
" %s expected bit profile: %x actual bit profile: %x \n " ,
testdataptr - > name ,
testdataptr - > expected_unsupported ,
unsupported ,
testdataptr - > expected_profile ,
option_profile ) ;
res = AST_TEST_FAIL ;
} else {
ast_test_status_update ( test , " \" %s \" passed got expected unsupported: %s and bit profile: %x \n " ,
testdataptr - > name ,
unsupported ,
option_profile ) ;
}
option_profile = parse_sip_options ( testdataptr - > input_options , NULL , 0 ) ;
if ( option_profile ! = testdataptr - > expected_profile ) {
ast_test_status_update ( test , " NULL output test \" %s \" , expected bit profile: %x actual bit profile: %x \n " ,
testdataptr - > name ,
testdataptr - > expected_profile ,
option_profile ) ;
res = AST_TEST_FAIL ;
} else {
ast_test_status_update ( test , " \" %s \" with NULL output buf passed, bit profile: %x \n " ,
testdataptr - > name ,
option_profile ) ;
}
}
return res ;
}
2016-07-18 19:40:22 -04:00
# endif
2010-06-28 18:38:47 +00:00
2010-07-23 16:33:52 +00:00
/*! \brief helper routine for sip_uri_cmp to compare URI parameters
*
* This takes the parameters from two SIP URIs and determines
* if the URIs match. The rules for parameters *suck*. Here's a breakdown
* 1. If a parameter appears in both URIs, then they must have the same value
* in order for the URIs to match
* 2. If one URI has a user, maddr, ttl, or method parameter, then the other
* URI must also have that parameter and must have the same value
* in order for the URIs to match
* 3. All other headers appearing in only one URI are not considered when
* determining if URIs match
*
* \param input1 Parameters from URI 1
* \param input2 Parameters from URI 2
* \retval 0 URIs' parameters match
* \retval nonzero URIs' parameters do not match
*/
static int sip_uri_params_cmp ( const char * input1 , const char * input2 )
{
char * params1 = NULL ;
char * params2 = NULL ;
char * pos1 ;
char * pos2 ;
int zerolength1 = 0 ;
int zerolength2 = 0 ;
int maddrmatch = 0 ;
int ttlmatch = 0 ;
int usermatch = 0 ;
int methodmatch = 0 ;
if ( ast_strlen_zero ( input1 ) ) {
zerolength1 = 1 ;
} else {
params1 = ast_strdupa ( input1 ) ;
}
if ( ast_strlen_zero ( input2 ) ) {
zerolength2 = 1 ;
} else {
params2 = ast_strdupa ( input2 ) ;
}
/* Quick optimization. If both params are zero-length, then
* they match
*/
if ( zerolength1 & & zerolength2 ) {
return 0 ;
}
for ( pos1 = strsep ( & params1 , " ; " ) ; pos1 ; pos1 = strsep ( & params1 , " ; " ) ) {
char * value1 = pos1 ;
char * name1 = strsep ( & value1 , " = " ) ;
char * params2dup = NULL ;
int matched = 0 ;
if ( ! value1 ) {
value1 = " " ;
}
/* Checkpoint reached. We have the name and value parsed for param1
* We have to duplicate params2 each time through this loop
* or else the inner loop below will not work properly.
*/
if ( ! zerolength2 ) {
params2dup = ast_strdupa ( params2 ) ;
}
for ( pos2 = strsep ( & params2dup , " ; " ) ; pos2 ; pos2 = strsep ( & params2dup , " ; " ) ) {
char * name2 = pos2 ;
char * value2 = strchr ( pos2 , ' = ' ) ;
if ( ! value2 ) {
value2 = " " ;
} else {
* value2 + + = ' \0 ' ;
}
if ( ! strcasecmp ( name1 , name2 ) ) {
if ( strcasecmp ( value1 , value2 ) ) {
goto fail ;
} else {
matched = 1 ;
break ;
}
}
}
/* Check to see if the parameter is one of the 'must-match' parameters */
if ( ! strcasecmp ( name1 , " maddr " ) ) {
if ( matched ) {
maddrmatch = 1 ;
} else {
goto fail ;
}
} else if ( ! strcasecmp ( name1 , " ttl " ) ) {
if ( matched ) {
ttlmatch = 1 ;
} else {
goto fail ;
}
} else if ( ! strcasecmp ( name1 , " user " ) ) {
if ( matched ) {
usermatch = 1 ;
} else {
goto fail ;
}
} else if ( ! strcasecmp ( name1 , " method " ) ) {
if ( matched ) {
methodmatch = 1 ;
} else {
goto fail ;
}
}
}
/* We've made it out of that horrible O(m*n) construct and there are no
* failures yet. We're not done yet, though, because params2 could have
* an maddr, ttl, user, or method header and params1 did not.
*/
for ( pos2 = strsep ( & params2 , " ; " ) ; pos2 ; pos2 = strsep ( & params2 , " ; " ) ) {
char * value2 = pos2 ;
char * name2 = strsep ( & value2 , " = " ) ;
if ( ! value2 ) {
value2 = " " ;
}
if ( ( ! strcasecmp ( name2 , " maddr " ) & & ! maddrmatch ) | |
( ! strcasecmp ( name2 , " ttl " ) & & ! ttlmatch ) | |
( ! strcasecmp ( name2 , " user " ) & & ! usermatch ) | |
( ! strcasecmp ( name2 , " method " ) & & ! methodmatch ) ) {
goto fail ;
}
}
return 0 ;
fail :
return 1 ;
}
/*! \brief helper routine for sip_uri_cmp to compare URI headers
*
* This takes the headers from two SIP URIs and determines
* if the URIs match. The rules for headers is simple. If a header
* appears in one URI, then it must also appear in the other URI. The
* order in which the headers appear does not matter.
*
* \param input1 Headers from URI 1
* \param input2 Headers from URI 2
* \retval 0 URI headers match
* \retval nonzero URI headers do not match
*/
static int sip_uri_headers_cmp ( const char * input1 , const char * input2 )
{
char * headers1 = NULL ;
char * headers2 = NULL ;
int zerolength1 = 0 ;
int zerolength2 = 0 ;
int different = 0 ;
char * header1 ;
if ( ast_strlen_zero ( input1 ) ) {
zerolength1 = 1 ;
} else {
headers1 = ast_strdupa ( input1 ) ;
}
if ( ast_strlen_zero ( input2 ) ) {
zerolength2 = 1 ;
} else {
headers2 = ast_strdupa ( input2 ) ;
}
/* If one URI contains no headers and the other
* does, then they cannot possibly match
*/
if ( zerolength1 ! = zerolength2 ) {
return 1 ;
}
if ( zerolength1 & & zerolength2 )
return 0 ;
/* At this point, we can definitively state that both inputs are
* not zero-length. First, one more optimization. If the length
* of the headers is not equal, then we definitely have no match
*/
if ( strlen ( headers1 ) ! = strlen ( headers2 ) ) {
return 1 ;
}
for ( header1 = strsep ( & headers1 , " & " ) ; header1 ; header1 = strsep ( & headers1 , " & " ) ) {
if ( ! strcasestr ( headers2 , header1 ) ) {
different = 1 ;
break ;
}
}
return different ;
}
/*!
* \brief Compare domain sections of SIP URIs
*
* For hostnames, a case insensitive string comparison is
* used. For IP addresses, a binary comparison is used. This
* is mainly because IPv6 addresses have many ways of writing
* the same address.
*
* For specifics about IP address comparison, see the following
* document: http://tools.ietf.org/html/draft-ietf-sip-ipv6-abnf-fix-05
*
* \param host1 The domain from the first URI
* \param host2 THe domain from the second URI
* \retval 0 The domains match
* \retval nonzero The domains do not match
*/
static int sip_uri_domain_cmp ( const char * host1 , const char * host2 )
{
struct ast_sockaddr addr1 ;
struct ast_sockaddr addr2 ;
int addr1_parsed ;
int addr2_parsed ;
addr1_parsed = ast_sockaddr_parse ( & addr1 , host1 , 0 ) ;
addr2_parsed = ast_sockaddr_parse ( & addr2 , host2 , 0 ) ;
if ( addr1_parsed ! = addr2_parsed ) {
/* One domain was an IP address and the other had
* a host name. FAIL!
*/
return 1 ;
}
/* Both are host names. A string comparison will work
* perfectly here. Specifying the "C" locale ensures that
* The LC_CTYPE conventions use those defined in ANSI C,
* i.e. ASCII.
*/
if ( ! addr1_parsed ) {
2010-07-26 16:44:25 +00:00
# ifdef HAVE_XLOCALE_H
2011-03-23 21:46:59 +00:00
if ( ! c_locale ) {
return strcasecmp ( host1 , host2 ) ;
} else {
return strcasecmp_l ( host1 , host2 , c_locale ) ;
}
2010-07-26 16:44:25 +00:00
# else
return strcasecmp ( host1 , host2 ) ;
# endif
2010-07-23 16:33:52 +00:00
}
/* Both contain IP addresses */
return ast_sockaddr_cmp ( & addr1 , & addr2 ) ;
}
int sip_uri_cmp ( const char * input1 , const char * input2 )
{
char * uri1 ;
char * uri2 ;
char * uri_scheme1 ;
char * uri_scheme2 ;
char * host1 ;
char * host2 ;
char * params1 ;
char * params2 ;
char * headers1 ;
char * headers2 ;
/* XXX It would be really nice if we could just use parse_uri_full() here
* to separate the components of the URI, but unfortunately it is written
* in a way that can cause URI parameters to be discarded.
*/
if ( ! input1 | | ! input2 ) {
return 1 ;
}
uri1 = ast_strdupa ( input1 ) ;
uri2 = ast_strdupa ( input2 ) ;
2011-01-24 18:59:22 +00:00
ast_uri_decode ( uri1 , ast_uri_sip_user ) ;
ast_uri_decode ( uri2 , ast_uri_sip_user ) ;
2010-07-23 16:33:52 +00:00
uri_scheme1 = strsep ( & uri1 , " : " ) ;
uri_scheme2 = strsep ( & uri2 , " : " ) ;
if ( strcmp ( uri_scheme1 , uri_scheme2 ) ) {
return 1 ;
}
/* This function is tailored for SIP and SIPS URIs. There's no
* need to check uri_scheme2 since we have determined uri_scheme1
* and uri_scheme2 are equivalent already.
*/
if ( strcmp ( uri_scheme1 , " sip " ) & & strcmp ( uri_scheme1 , " sips " ) ) {
return 1 ;
}
if ( ast_strlen_zero ( uri1 ) | | ast_strlen_zero ( uri2 ) ) {
return 1 ;
}
if ( ( host1 = strchr ( uri1 , ' @ ' ) ) ) {
* host1 + + = ' \0 ' ;
}
if ( ( host2 = strchr ( uri2 , ' @ ' ) ) ) {
* host2 + + = ' \0 ' ;
}
/* Check for mismatched username and passwords. This is the
* only case-sensitive comparison of a SIP URI
*/
if ( ( host1 & & ! host2 ) | |
( host2 & & ! host1 ) | |
( host1 & & host2 & & strcmp ( uri1 , uri2 ) ) ) {
return 1 ;
}
if ( ! host1 ) {
host1 = uri1 ;
}
if ( ! host2 ) {
host2 = uri2 ;
}
/* Strip off the parameters and headers so we can compare
* host and port
*/
if ( ( params1 = strchr ( host1 , ' ; ' ) ) ) {
* params1 + + = ' \0 ' ;
}
if ( ( params2 = strchr ( host2 , ' ; ' ) ) ) {
* params2 + + = ' \0 ' ;
}
/* Headers come after parameters, but there may be headers without
* parameters, thus the S_OR
*/
if ( ( headers1 = strchr ( S_OR ( params1 , host1 ) , ' ? ' ) ) ) {
* headers1 + + = ' \0 ' ;
}
if ( ( headers2 = strchr ( S_OR ( params2 , host2 ) , ' ? ' ) ) ) {
* headers2 + + = ' \0 ' ;
}
if ( sip_uri_domain_cmp ( host1 , host2 ) ) {
return 1 ;
}
/* Headers have easier rules to follow, so do those first */
if ( sip_uri_headers_cmp ( headers1 , headers2 ) ) {
return 1 ;
}
/* And now the parameters. Ugh */
return sip_uri_params_cmp ( params1 , params2 ) ;
}
# define URI_CMP_MATCH 0
# define URI_CMP_NOMATCH 1
2016-07-18 19:40:22 -04:00
# ifdef TEST_FRAMEWORK
2010-07-23 16:33:52 +00:00
AST_TEST_DEFINE ( sip_uri_cmp_test )
{
static const struct {
const char * uri1 ;
const char * uri2 ;
int expected_result ;
} uri_cmp_tests [ ] = {
/* These are identical, so they match */
{ " sip:bob@example.com " , " sip:bob@example.com " , URI_CMP_MATCH } ,
/* Different usernames. No match */
{ " sip:alice@example.com " , " sip:bob@example.com " , URI_CMP_NOMATCH } ,
/* Different hosts. No match */
{ " sip:bob@example.com " , " sip:bob@examplez.com " , URI_CMP_NOMATCH } ,
/* Now start using IP addresses. Identical, so they match */
{ " sip:bob@1.2.3.4 " , " sip:bob@1.2.3.4 " , URI_CMP_MATCH } ,
/* Two identical IPv4 addresses represented differently. Match */
{ " sip:bob@1.2.3.4 " , " sip:bob@001.002.003.004 " , URI_CMP_MATCH } ,
/* Logically equivalent IPv4 Address and hostname. No Match */
{ " sip:bob@127.0.0.1 " , " sip:bob@localhost " , URI_CMP_NOMATCH } ,
/* Logically equivalent IPv6 address and hostname. No Match */
{ " sip:bob@[::1] " , " sip:bob@localhost " , URI_CMP_NOMATCH } ,
/* Try an IPv6 one as well */
{ " sip:bob@[2001:db8::1234] " , " sip:bob@[2001:db8::1234] " , URI_CMP_MATCH } ,
/* Two identical IPv6 addresses represented differently. Match */
{ " sip:bob@[2001:db8::1234] " , " sip:bob@[2001:0db8::1234] " , URI_CMP_MATCH } ,
/* Different ports. No match */
{ " sip:bob@1.2.3.4:5060 " , " sip:bob@1.2.3.4:5061 " , URI_CMP_NOMATCH } ,
/* Same port logically, but only one address specifies it. No match */
{ " sip:bob@1.2.3.4:5060 " , " sip:bob@1.2.3.4 " , URI_CMP_NOMATCH } ,
/* And for safety, try with IPv6 */
{ " sip:bob@[2001:db8:1234]:5060 " , " sip:bob@[2001:db8:1234] " , URI_CMP_NOMATCH } ,
/* User comparison is case sensitive. No match */
{ " sip:bob@example.com " , " sip:BOB@example.com " , URI_CMP_NOMATCH } ,
/* Host comparison is case insensitive. Match */
{ " sip:bob@example.com " , " sip:bob@EXAMPLE.COM " , URI_CMP_MATCH } ,
/* Add headers to the URI. Identical, so they match */
{ " sip:bob@example.com?header1=value1&header2=value2 " , " sip:bob@example.com?header1=value1&header2=value2 " , URI_CMP_MATCH } ,
/* Headers in URI 1 are not in URI 2. No Match */
{ " sip:bob@example.com?header1=value1&header2=value2 " , " sip:bob@example.com " , URI_CMP_NOMATCH } ,
/* Header present in both URIs does not have matching values. No match */
{ " sip:bob@example.com?header1=value1&header2=value2 " , " sip:bob@example.com?header1=value1&header2=value3 " , URI_CMP_NOMATCH } ,
/* Add parameters to the URI. Identical so they match */
{ " sip:bob@example.com;param1=value1;param2=value2 " , " sip:bob@example.com;param1=value1;param2=value2 " , URI_CMP_MATCH } ,
/* Same parameters in both URIs but appear in different order. Match */
{ " sip:bob@example.com;param2=value2;param1=value1 " , " sip:bob@example.com;param1=value1;param2=value2 " , URI_CMP_MATCH } ,
/* params in URI 1 are not in URI 2. Match */
{ " sip:bob@example.com;param1=value1;param2=value2 " , " sip:bob@example.com " , URI_CMP_MATCH } ,
/* param present in both URIs does not have matching values. No match */
{ " sip:bob@example.com;param1=value1;param2=value2 " , " sip:bob@example.com;param1=value1;param2=value3 " , URI_CMP_NOMATCH } ,
/* URI 1 has a maddr param but URI 2 does not. No match */
{ " sip:bob@example.com;param1=value1;maddr=192.168.0.1 " , " sip:bob@example.com;param1=value1 " , URI_CMP_NOMATCH } ,
/* URI 1 and URI 2 both have identical maddr params. Match */
{ " sip:bob@example.com;param1=value1;maddr=192.168.0.1 " , " sip:bob@example.com;param1=value1;maddr=192.168.0.1 " , URI_CMP_MATCH } ,
/* URI 1 is a SIPS URI and URI 2 is a SIP URI. No Match */
{ " sips:bob@example.com " , " sip:bob@example.com " , URI_CMP_NOMATCH } ,
/* No URI schemes. No match */
{ " bob@example.com " , " bob@example.com " , URI_CMP_NOMATCH } ,
/* Crashiness tests. Just an address scheme. No match */
{ " sip " , " sips " , URI_CMP_NOMATCH } ,
/* Still just an address scheme. Even though they're the same, No match */
{ " sip " , " sip " , URI_CMP_NOMATCH } ,
/* Empty strings. No match */
{ " " , " " , URI_CMP_NOMATCH } ,
/* An empty string and a NULL. No match */
{ " " , NULL , URI_CMP_NOMATCH } ,
} ;
int i ;
int test_res = AST_TEST_PASS ;
switch ( cmd ) {
case TEST_INIT :
info - > name = " sip_uri_cmp_test " ;
info - > category = " /channels/chan_sip/ " ;
info - > summary = " Tests comparison of SIP URIs " ;
info - > description = " Several would-be tricky URI comparisons are performed " ;
return AST_TEST_NOT_RUN ;
case TEST_EXECUTE :
break ;
}
for ( i = 0 ; i < ARRAY_LEN ( uri_cmp_tests ) ; + + i ) {
int cmp_res1 ;
int cmp_res2 ;
if ( ( cmp_res1 = sip_uri_cmp ( uri_cmp_tests [ i ] . uri1 , uri_cmp_tests [ i ] . uri2 ) ) ) {
/* URI comparison may return -1 or +1 depending on the failure. Standardize
* the return value to be URI_CMP_NOMATCH on any failure
*/
cmp_res1 = URI_CMP_NOMATCH ;
}
if ( cmp_res1 ! = uri_cmp_tests [ i ] . expected_result ) {
ast_test_status_update ( test , " Unexpected comparison result for URIs %s and %s. "
" Expected %s but got %s \n " , uri_cmp_tests [ i ] . uri1 , uri_cmp_tests [ i ] . uri2 ,
uri_cmp_tests [ i ] . expected_result = = URI_CMP_MATCH ? " Match " : " No Match " ,
cmp_res1 = = URI_CMP_MATCH ? " Match " : " No Match " ) ;
test_res = AST_TEST_FAIL ;
}
/* All URI comparisons are commutative, so for the sake of being thorough, we'll
* rerun the comparison with the parameters reversed
*/
if ( ( cmp_res2 = sip_uri_cmp ( uri_cmp_tests [ i ] . uri2 , uri_cmp_tests [ i ] . uri1 ) ) ) {
/* URI comparison may return -1 or +1 depending on the failure. Standardize
* the return value to be URI_CMP_NOMATCH on any failure
*/
cmp_res2 = URI_CMP_NOMATCH ;
}
if ( cmp_res2 ! = uri_cmp_tests [ i ] . expected_result ) {
ast_test_status_update ( test , " Unexpected comparison result for URIs %s and %s. "
" Expected %s but got %s \n " , uri_cmp_tests [ i ] . uri2 , uri_cmp_tests [ i ] . uri1 ,
uri_cmp_tests [ i ] . expected_result = = URI_CMP_MATCH ? " Match " : " No Match " ,
cmp_res2 = = URI_CMP_MATCH ? " Match " : " No Match " ) ;
test_res = AST_TEST_FAIL ;
}
}
return test_res ;
}
2016-07-18 19:40:22 -04:00
# endif
2010-06-28 18:38:47 +00:00
2011-01-26 20:44:47 +00:00
void free_via ( struct sip_via * v )
2010-07-26 20:00:52 +00:00
{
2011-01-26 20:44:47 +00:00
if ( ! v ) {
2010-07-26 20:00:52 +00:00
return ;
}
2011-04-27 19:15:49 +00:00
ast_free ( v - > via ) ;
2011-01-26 20:44:47 +00:00
ast_free ( v ) ;
}
struct sip_via * parse_via ( const char * header )
{
struct sip_via * v = ast_calloc ( 1 , sizeof ( * v ) ) ;
char * via , * parm ;
if ( ! v ) {
return NULL ;
2010-07-26 20:00:52 +00:00
}
2011-01-26 20:44:47 +00:00
v - > via = ast_strdup ( header ) ;
v - > ttl = 1 ;
via = v - > via ;
2010-07-26 20:00:52 +00:00
if ( ast_strlen_zero ( via ) ) {
2011-01-26 20:44:47 +00:00
ast_log ( LOG_ERROR , " received request without a Via header \n " ) ;
free_via ( v ) ;
return NULL ;
2010-07-26 20:00:52 +00:00
}
2011-01-26 20:44:47 +00:00
/* seperate the first via-parm */
via = strsep ( & via , " , " ) ;
/* chop off sent-protocol */
v - > protocol = strsep ( & via , " \t \r \n " ) ;
if ( ast_strlen_zero ( v - > protocol ) ) {
ast_log ( LOG_ERROR , " missing sent-protocol in Via header \n " ) ;
free_via ( v ) ;
return NULL ;
2010-07-26 20:00:52 +00:00
}
2011-01-26 20:44:47 +00:00
v - > protocol = ast_skip_blanks ( v - > protocol ) ;
2010-07-26 20:00:52 +00:00
2011-01-26 20:44:47 +00:00
if ( via ) {
via = ast_skip_blanks ( via ) ;
}
2010-07-26 20:00:52 +00:00
2011-01-26 20:44:47 +00:00
/* chop off sent-by */
v - > sent_by = strsep ( & via , " ; \t \r \n " ) ;
if ( ast_strlen_zero ( v - > sent_by ) ) {
ast_log ( LOG_ERROR , " missing sent-by in Via header \n " ) ;
free_via ( v ) ;
return NULL ;
}
v - > sent_by = ast_skip_blanks ( v - > sent_by ) ;
2011-05-13 01:55:38 +00:00
/* store the port, we have to handle ipv6 addresses containing ':'
* characters gracefully */
2015-10-06 19:40:22 +02:00
if ( ( ( parm = strchr ( v - > sent_by , ' ] ' ) ) & & * ( + + parm ) = = ' : ' ) | | ( ! ( parm = strchr ( v - > sent_by , ' ] ' ) ) & & ( parm = strchr ( v - > sent_by , ' : ' ) ) ) ) {
2011-01-26 20:44:47 +00:00
char * endptr ;
v - > port = strtol ( + + parm , & endptr , 10 ) ;
}
/* evaluate any via-parms */
while ( ( parm = strsep ( & via , " ; \t \r \n " ) ) ) {
char * c ;
if ( ( c = strstr ( parm , " maddr= " ) ) ) {
v - > maddr = ast_skip_blanks ( c + sizeof ( " maddr= " ) - 1 ) ;
} else if ( ( c = strstr ( parm , " branch= " ) ) ) {
v - > branch = ast_skip_blanks ( c + sizeof ( " branch= " ) - 1 ) ;
} else if ( ( c = strstr ( parm , " ttl= " ) ) ) {
char * endptr ;
c = ast_skip_blanks ( c + sizeof ( " ttl= " ) - 1 ) ;
v - > ttl = strtol ( c , & endptr , 10 ) ;
/* make sure we got a valid ttl value */
if ( c = = endptr ) {
v - > ttl = 1 ;
}
}
2010-07-26 20:00:52 +00:00
}
2011-01-26 20:44:47 +00:00
return v ;
2010-07-26 20:00:52 +00:00
}
2016-07-18 19:40:22 -04:00
# ifdef TEST_FRAMEWORK
2011-01-26 20:44:47 +00:00
AST_TEST_DEFINE ( parse_via_test )
2010-07-26 20:00:52 +00:00
{
int res = AST_TEST_PASS ;
int i = 1 ;
2011-01-26 20:44:47 +00:00
struct sip_via * via ;
2010-07-26 20:00:52 +00:00
struct testdata {
char * in ;
2011-01-26 20:44:47 +00:00
char * expected_protocol ;
2010-07-26 20:00:52 +00:00
char * expected_branch ;
char * expected_sent_by ;
2011-01-26 20:44:47 +00:00
char * expected_maddr ;
unsigned int expected_port ;
unsigned char expected_ttl ;
int expected_null ;
2010-07-26 20:00:52 +00:00
AST_LIST_ENTRY ( testdata ) list ;
} ;
struct testdata * testdataptr ;
static AST_LIST_HEAD_NOLOCK ( testdataliststruct , testdata ) testdatalist ;
struct testdata t1 = {
. in = " SIP/2.0/UDP host:port;branch=thebranch " ,
2011-01-26 20:44:47 +00:00
. expected_protocol = " SIP/2.0/UDP " ,
. expected_sent_by = " host:port " ,
2010-07-26 20:00:52 +00:00
. expected_branch = " thebranch " ,
} ;
2011-01-26 20:44:47 +00:00
struct testdata t2 = {
2010-07-26 20:00:52 +00:00
. in = " SIP/2.0/UDP host:port " ,
2011-01-26 20:44:47 +00:00
. expected_protocol = " SIP/2.0/UDP " ,
. expected_sent_by = " host:port " ,
2010-07-26 20:00:52 +00:00
. expected_branch = " " ,
2011-01-26 20:44:47 +00:00
} ;
struct testdata t3 = {
. in = " SIP/2.0/UDP " ,
. expected_null = 1 ,
2010-07-26 20:00:52 +00:00
} ;
struct testdata t4 = {
2011-01-26 20:44:47 +00:00
. in = " BLAH/BLAH/BLAH host:port;branch= " ,
. expected_protocol = " BLAH/BLAH/BLAH " ,
. expected_sent_by = " host:port " ,
. expected_branch = " " ,
2010-07-26 20:00:52 +00:00
} ;
struct testdata t5 = {
2011-01-26 20:44:47 +00:00
. in = " SIP/2.0/UDP host:5060;branch=thebranch;maddr=224.0.0.1;ttl=1 " ,
. expected_protocol = " SIP/2.0/UDP " ,
. expected_sent_by = " host:5060 " ,
. expected_port = 5060 ,
. expected_branch = " thebranch " ,
. expected_maddr = " 224.0.0.1 " ,
. expected_ttl = 1 ,
2010-07-26 20:00:52 +00:00
} ;
struct testdata t6 = {
2011-01-26 20:44:47 +00:00
. in = " SIP/2.0/UDP host:5060; \n branch=thebranch; \r \n maddr=224.0.0.1; ttl=1 " ,
. expected_protocol = " SIP/2.0/UDP " ,
. expected_sent_by = " host:5060 " ,
. expected_port = 5060 ,
. expected_branch = " thebranch " ,
. expected_maddr = " 224.0.0.1 " ,
. expected_ttl = 1 ,
2010-07-26 20:00:52 +00:00
} ;
2011-05-13 01:55:38 +00:00
struct testdata t7 = {
. in = " SIP/2.0/UDP [::1]:5060 " ,
. expected_protocol = " SIP/2.0/UDP " ,
. expected_sent_by = " [::1]:5060 " ,
. expected_port = 5060 ,
. expected_branch = " " ,
} ;
2010-07-26 20:00:52 +00:00
switch ( cmd ) {
case TEST_INIT :
2011-01-26 20:44:47 +00:00
info - > name = " parse_via_test " ;
2010-07-28 20:50:02 +00:00
info - > category = " /channels/chan_sip/ " ;
2011-01-26 20:44:47 +00:00
info - > summary = " Tests parsing the Via header " ;
2010-07-26 20:00:52 +00:00
info - > description =
2011-01-26 20:44:47 +00:00
" Runs through various test situations in which various "
" parameters parameter must be extracted from a VIA header " ;
2010-07-26 20:00:52 +00:00
return AST_TEST_NOT_RUN ;
case TEST_EXECUTE :
break ;
}
AST_LIST_HEAD_SET_NOLOCK ( & testdatalist , & t1 ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & t2 , list ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & t3 , list ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & t4 , list ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & t5 , list ) ;
AST_LIST_INSERT_TAIL ( & testdatalist , & t6 , list ) ;
2011-05-13 01:55:38 +00:00
AST_LIST_INSERT_TAIL ( & testdatalist , & t7 , list ) ;
2010-07-26 20:00:52 +00:00
AST_LIST_TRAVERSE ( & testdatalist , testdataptr , list ) {
2011-01-26 20:44:47 +00:00
via = parse_via ( testdataptr - > in ) ;
if ( ! via ) {
if ( ! testdataptr - > expected_null ) {
ast_test_status_update ( test , " TEST#%d FAILED: VIA = \" %s \" \n "
" failed to parse header \n " ,
i , testdataptr - > in ) ;
res = AST_TEST_FAIL ;
}
i + + ;
continue ;
}
if ( testdataptr - > expected_null ) {
ast_test_status_update ( test , " TEST#%d FAILED: VIA = \" %s \" \n "
" successfully parased invalid via header \n " ,
i , testdataptr - > in ) ;
res = AST_TEST_FAIL ;
free_via ( via ) ;
i + + ;
continue ;
}
if ( ( ast_strlen_zero ( via - > protocol ) & & ! ast_strlen_zero ( testdataptr - > expected_protocol ) )
| | ( ! ast_strlen_zero ( via - > protocol ) & & strcmp ( via - > protocol , testdataptr - > expected_protocol ) ) ) {
ast_test_status_update ( test , " TEST#%d FAILED: VIA = \" %s \" \n "
" parsed protocol = \" %s \" \n "
" expected = \" %s \" \n "
" failed to parse protocol \n " ,
i , testdataptr - > in , via - > protocol , testdataptr - > expected_protocol ) ;
res = AST_TEST_FAIL ;
}
if ( ( ast_strlen_zero ( via - > sent_by ) & & ! ast_strlen_zero ( testdataptr - > expected_sent_by ) )
| | ( ! ast_strlen_zero ( via - > sent_by ) & & strcmp ( via - > sent_by , testdataptr - > expected_sent_by ) ) ) {
ast_test_status_update ( test , " TEST#%d FAILED: VIA = \" %s \" \n "
" parsed sent_by = \" %s \" \n "
" expected = \" %s \" \n "
" failed to parse sent-by \n " ,
i , testdataptr - > in , via - > sent_by , testdataptr - > expected_sent_by ) ;
res = AST_TEST_FAIL ;
}
if ( testdataptr - > expected_port & & testdataptr - > expected_port ! = via - > port ) {
ast_test_status_update ( test , " TEST#%d FAILED: VIA = \" %s \" \n "
2014-06-19 19:40:45 +00:00
" parsed port = \" %u \" \n "
" expected = \" %u \" \n "
2011-01-26 20:44:47 +00:00
" failed to parse port \n " ,
i , testdataptr - > in , via - > port , testdataptr - > expected_port ) ;
res = AST_TEST_FAIL ;
}
if ( ( ast_strlen_zero ( via - > branch ) & & ! ast_strlen_zero ( testdataptr - > expected_branch ) )
| | ( ! ast_strlen_zero ( via - > branch ) & & strcmp ( via - > branch , testdataptr - > expected_branch ) ) ) {
ast_test_status_update ( test , " TEST#%d FAILED: VIA = \" %s \" \n "
" parsed branch = \" %s \" \n "
" expected = \" %s \" \n "
" failed to parse branch \n " ,
i , testdataptr - > in , via - > branch , testdataptr - > expected_branch ) ;
2010-07-26 20:00:52 +00:00
res = AST_TEST_FAIL ;
}
2011-01-26 20:44:47 +00:00
if ( ( ast_strlen_zero ( via - > maddr ) & & ! ast_strlen_zero ( testdataptr - > expected_maddr ) )
| | ( ! ast_strlen_zero ( via - > maddr ) & & strcmp ( via - > maddr , testdataptr - > expected_maddr ) ) ) {
ast_test_status_update ( test , " TEST#%d FAILED: VIA = \" %s \" \n "
" parsed maddr = \" %s \" \n "
" expected = \" %s \" \n "
" failed to parse maddr \n " ,
i , testdataptr - > in , via - > maddr , testdataptr - > expected_maddr ) ;
res = AST_TEST_FAIL ;
}
if ( testdataptr - > expected_ttl & & testdataptr - > expected_ttl ! = via - > ttl ) {
ast_test_status_update ( test , " TEST#%d FAILED: VIA = \" %s \" \n "
" parsed ttl = \" %d \" \n "
" expected = \" %d \" \n "
" failed to parse ttl \n " ,
i , testdataptr - > in , via - > ttl , testdataptr - > expected_ttl ) ;
res = AST_TEST_FAIL ;
}
free_via ( via ) ;
2010-07-26 20:00:52 +00:00
i + + ;
}
return res ;
}
2016-07-18 19:40:22 -04:00
# endif
2010-07-26 20:00:52 +00:00
2010-02-03 20:33:32 +00:00
void sip_request_parser_register_tests ( void )
{
AST_TEST_REGISTER ( get_calleridname_test ) ;
AST_TEST_REGISTER ( sip_parse_uri_test ) ;
2010-02-15 15:45:02 +00:00
AST_TEST_REGISTER ( get_in_brackets_test ) ;
AST_TEST_REGISTER ( get_name_and_number_test ) ;
2012-05-10 20:56:09 +00:00
AST_TEST_REGISTER ( sip_parse_uri_full_test ) ;
2010-04-09 16:04:16 +00:00
AST_TEST_REGISTER ( parse_name_andor_addr_test ) ;
AST_TEST_REGISTER ( parse_contact_header_test ) ;
2010-06-28 18:38:47 +00:00
AST_TEST_REGISTER ( sip_parse_options_test ) ;
2010-07-23 16:33:52 +00:00
AST_TEST_REGISTER ( sip_uri_cmp_test ) ;
2011-01-26 20:44:47 +00:00
AST_TEST_REGISTER ( parse_via_test ) ;
2010-02-03 20:33:32 +00:00
}
void sip_request_parser_unregister_tests ( void )
{
AST_TEST_UNREGISTER ( sip_parse_uri_test ) ;
AST_TEST_UNREGISTER ( get_calleridname_test ) ;
2010-02-15 15:45:02 +00:00
AST_TEST_UNREGISTER ( get_in_brackets_test ) ;
AST_TEST_UNREGISTER ( get_name_and_number_test ) ;
2012-05-10 20:56:09 +00:00
AST_TEST_UNREGISTER ( sip_parse_uri_full_test ) ;
2010-04-09 16:04:16 +00:00
AST_TEST_UNREGISTER ( parse_name_andor_addr_test ) ;
AST_TEST_UNREGISTER ( parse_contact_header_test ) ;
2010-06-28 18:38:47 +00:00
AST_TEST_UNREGISTER ( sip_parse_options_test ) ;
2010-07-23 16:33:52 +00:00
AST_TEST_UNREGISTER ( sip_uri_cmp_test ) ;
2011-01-26 20:44:47 +00:00
AST_TEST_UNREGISTER ( parse_via_test ) ;
2010-07-23 16:33:52 +00:00
}
int sip_reqresp_parser_init ( void )
{
2010-07-26 16:44:25 +00:00
# ifdef HAVE_XLOCALE_H
2010-07-23 16:33:52 +00:00
c_locale = newlocale ( LC_CTYPE_MASK , " C " , NULL ) ;
if ( ! c_locale ) {
return - 1 ;
}
2010-07-26 16:44:25 +00:00
# endif
2010-07-23 16:33:52 +00:00
return 0 ;
}
void sip_reqresp_parser_exit ( void )
{
2010-07-26 16:44:25 +00:00
# ifdef HAVE_XLOCALE_H
2010-07-23 16:33:52 +00:00
if ( c_locale ) {
freelocale ( c_locale ) ;
c_locale = NULL ;
}
2010-07-26 16:44:25 +00:00
# endif
2010-02-03 20:33:32 +00:00
}