#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "sip_parse_uri.h"
#define SIP_URI_MSG_BUF_SZ 100
#define SIP_URI_ISHEX(c) \
(((int)(c) >= 0x30 && (int)(c) <= 0x39) || \
((int)(c) >= 0x41 && (int)(c) <= 0x46) || \
((int)(c) >= 0x61 && (int)(c) <= 0x66))
#define SIP_URI_ISURLESCAPE(scan, end) \
((scan) + 2 < (end) && (scan)[0] == '%' && \
SIP_URI_ISHEX((scan)[1]) && SIP_URI_ISHEX((scan[2])))
#define SIP_URI_ALPHA_BIT 0x0001
#define SIP_URI_DIGIT_BIT 0x0002
#define SIP_URI_ALNUM_BITS 0x0003
#define SIP_URI_SCHEME_BIT 0x0004
#define SIP_URI_TOKEN_BIT 0x0008
#define SIP_URI_QUEST_BIT 0x0010
#define SIP_URI_AT_BIT 0x0020
#define SIP_URI_COLON_BIT 0x0040
#define SIP_URI_SEMI_BIT 0x0080
#define SIP_URI_DASH_BIT 0x0100
#define SIP_URI_MARK_BIT 0x0200
#define SIP_URI_AND_BIT 0x0400
#define SIP_URI_PHCOMM_BIT 0x0800
#define SIP_URI_OTHER_BIT 0x1000
#define SIP_URI_SLASH_BIT 0x2000
#define SIP_URI_VISUALSEP_BIT 0x4000
#define SIP_URI_DTMFURI_DIGIT_BIT 0x8000
#define a SIP_URI_ALPHA_BIT
#define d SIP_URI_DIGIT_BIT
#define s SIP_URI_SCHEME_BIT
#define t SIP_URI_TOKEN_BIT
#define q SIP_URI_QUEST_BIT
#define m SIP_URI_AT_BIT
#define c SIP_URI_COLON_BIT
#define i SIP_URI_SEMI_BIT
#define h SIP_URI_DASH_BIT
#define k SIP_URI_MARK_BIT
#define n SIP_URI_AND_BIT
#define o SIP_URI_PHCOMM_BIT
#define r SIP_URI_OTHER_BIT
#define l SIP_URI_SLASH_BIT
#define v SIP_URI_VISUALSEP_BIT
#define f SIP_URI_DTMFURI_DIGIT_BIT
static const unsigned short sip_uri_table[256] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, t|k, 0, 0, o|r, t, n, t|k,
k|v, k|v, t|k|f, s|t|r|o, r, h|s|t|k|v, s|t|k|v, o|l,
d, d, d, d, d, d, d, d,
d, d, c|o, i, 0, r, 0, q,
m, a|f, a|f, a|f, a|f, a, a, a,
a, a, a, a, a, a, a, a,
a, a, a, a, a, a, a, a,
a, a, a, o, 0, o, 0, t|k,
t, a, a, a, a, a, a, a,
a, a, a, a, a, a, a, a,
a, a, a, a, a, a, a, a,
a, a, a, 0, 0, 0, t|k, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
};
#undef a
#undef d
#undef s
#undef t
#undef q
#undef m
#undef c
#undef i
#undef h
#undef k
#undef n
#undef o
#undef r
#undef l
#undef v
#undef f
#define SIP_URI_UT(c) sip_uri_table[(unsigned char)(c)]
#define SIP_URI_ISALPHA(c) (SIP_URI_UT(c) & SIP_URI_ALPHA_BIT)
#define SIP_URI_ISDIGIT(c) (SIP_URI_UT(c) & SIP_URI_DIGIT_BIT)
#define SIP_URI_ISALNUM(c) (SIP_URI_UT(c) & SIP_URI_ALNUM_BITS)
#define SIP_URI_ISSCHEME(c) \
(SIP_URI_UT(c) & (SIP_URI_ALNUM_BITS|SIP_URI_SCHEME_BIT))
#define SIP_URI_ISTOKEN(c) \
(SIP_URI_UT(c) & (SIP_URI_ALNUM_BITS|SIP_URI_TOKEN_BIT))
#define SIP_URI_ISSIPDELIM(c) \
(SIP_URI_UT(c) & (SIP_URI_SEMI_BIT|SIP_URI_QUEST_BIT))
#define SIP_URI_ISSIPHDELIM(c) \
(SIP_URI_UT(c) & (SIP_URI_COLON_BIT|SIP_URI_SEMI_BIT|SIP_URI_QUEST_BIT))
#define SIP_URI_ISHOST(c) \
(SIP_URI_UT(c) & (SIP_URI_ALNUM_BITS|SIP_URI_DASH_BIT))
#define SIP_URI_ISUSER(c) \
(SIP_URI_UT(c) & (SIP_URI_OTHER_BIT|SIP_URI_SEMI_BIT| \
SIP_URI_QUEST_BIT|SIP_URI_SLASH_BIT|SIP_URI_AND_BIT))
#define SIP_URI_ISABSHDELIM(c) \
(SIP_URI_UT(c) & \
(SIP_URI_SLASH_BIT|SIP_URI_COLON_BIT|SIP_URI_QUEST_BIT))
#define SIP_URI_ISABSDELIM(c) \
(SIP_URI_UT(c) & (SIP_URI_SLASH_BIT|SIP_URI_QUEST_BIT))
#define SIP_URI_ISUNRESERVED(c) \
(SIP_URI_UT(c) & (SIP_URI_ALNUM_BITS|SIP_URI_MARK_BIT))
#define SIP_URI_ISPARAM(c) \
(SIP_URI_UT(c) & (SIP_URI_PHCOMM_BIT|SIP_URI_AND_BIT|\
SIP_URI_ALNUM_BITS|SIP_URI_MARK_BIT))
#define SIP_URI_ISHEADER(c) \
(SIP_URI_UT(c) & (SIP_URI_PHCOMM_BIT|SIP_URI_QUEST_BIT|\
SIP_URI_ALNUM_BITS|SIP_URI_MARK_BIT))
#define SIP_URI_ISOTHER(c) (SIP_URI_UT(c) & SIP_URI_OTHER_BIT)
#define SIP_URI_ISRESERVED(c) \
(SIP_URI_UT(c) & (SIP_URI_SEMI_BIT|SIP_URI_SLASH_BIT| \
SIP_URI_QUEST_BIT| SIP_URI_COLON_BIT|SIP_URI_AT_BIT| \
SIP_URI_AND_BIT|SIP_URI_OTHER_BIT))
#define SIP_URI_ISPCHAR(c) \
(SIP_URI_UT(c) & (SIP_URI_COLON_BIT|SIP_URI_AT_BIT| \
SIP_URI_AND_BIT|SIP_URI_OTHER_BIT))
#define SIP_URI_ISREGNAME(c) \
(SIP_URI_UT(c) & \
(SIP_URI_OTHER_BIT|SIP_URI_SEMI_BIT|SIP_URI_COLON_BIT| \
SIP_URI_AT_BIT|SIP_URI_AND_BIT))
#define SIP_URI_ISPHONEDIGIT(c) \
(SIP_URI_UT(c) & (SIP_URI_DIGIT_BIT|SIP_URI_VISUALSEP_BIT))
#define SIP_URI_ISDTMFDIGIT(c) (SIP_URI_UT(c) & SIP_URI_DTMFURI_DIGIT_BIT)
static int sip_uri_url_casecmp(const char *, const char *, unsigned);
static void sip_uri_parse_params(_sip_uri_t *, char *, char *);
static void sip_uri_parse_headers(_sip_uri_t *, char *, char *);
static void sip_uri_parse_abs_opaque(_sip_uri_t *, char *, char *);
static void sip_uri_parse_abs_query(_sip_uri_t *, char *, char *);
static void sip_uri_parse_abs_path(_sip_uri_t *, char *, char *);
static void sip_uri_parse_abs_regname(_sip_uri_t *, char *, char *);
static int sip_uri_parse_scheme(_sip_uri_t *, char *, char *);
static void sip_uri_parse_password(_sip_uri_t *, char *, char *);
static void sip_uri_parse_user(_sip_uri_t *, char *, char *);
static void sip_uri_parse_port(_sip_uri_t *, char *, char *);
static void sip_uri_parse_netpath(_sip_uri_t *, char **, char *, boolean_t);
static int sip_uri_parse_ipv6(char *, char *);
static int sip_uri_parse_ipv4(char *, char *);
static int sip_uri_parse_hostname(char *, char *);
static int sip_uri_parse_tel(char *, char *);
static int sip_uri_parse_tel_areaspe(char *, char *);
static int sip_uri_parse_tel_servicepro(char *, char *);
static int sip_uri_parse_tel_futureext(char *, char *);
static int sip_uri_isTokenchar(char **, char *);
static int sip_uri_isEscapedPound(char **, char *);
static int sip_uri_hexVal(char *, char *);
static int SIP_URI_HEXVAL(int);
static int
SIP_URI_HEXVAL(int c)
{
if (c >= 0x30 && c <= 0x39)
return (c - '0');
if (c >= 0x41 && c <= 0x46)
return (c - 'A' + 10);
if (c >= 0x61 && c <= 0x66)
return (c - 'a' + 10);
return (c);
}
static int
sip_uri_url_casecmp(const char *str1, const char *str2, unsigned len)
{
unsigned j;
for (j = 0; j < len && tolower(str1[j]) == tolower(str2[j]) &&
str1[j] != '\0'; ++j) {
;
}
return (j == len ? 0 : tolower(str2[j]) - tolower(str1[j]));
}
static int
sip_uri_parse_tel(char *scan, char *uend)
{
char *mark = (char *)0;
int ret = 0;
int isGlobal = 0;
int quote = 0;
if (scan == uend)
return (0);
if (*scan == '+') {
++scan;
isGlobal = 1;
}
mark = scan;
if (isGlobal) {
while (scan < uend && SIP_URI_ISPHONEDIGIT(*scan))
++scan;
} else {
while (scan < uend &&
(SIP_URI_ISPHONEDIGIT(*scan) ||
SIP_URI_ISDTMFDIGIT(*scan) ||
sip_uri_isEscapedPound(&scan, uend) ||
*scan == 'p' || *scan == 'w')) {
++scan;
}
}
if (mark == scan || (scan < uend && *scan != ';'))
return (0);
if (uend - scan > 6 && !sip_uri_url_casecmp(scan, ";isub=", 6)) {
scan += 6;
mark = scan;
while (scan < uend && SIP_URI_ISPHONEDIGIT(*scan))
++scan;
if (mark == scan || (scan < uend && *scan != ';'))
return (0);
}
if (uend - scan > 7 && !sip_uri_url_casecmp(scan, ";postd=", 7)) {
scan += 7;
mark = scan;
while (scan < uend &&
(SIP_URI_ISPHONEDIGIT(*scan) ||
SIP_URI_ISDTMFDIGIT(*scan) ||
sip_uri_isEscapedPound(&scan, uend) ||
*scan == 'p' || *scan == 'w')) {
++scan;
}
if (mark == scan || (scan < uend && *scan != ';'))
return (0);
}
if (!isGlobal) {
if (uend - scan > 15 &&
!sip_uri_url_casecmp(scan, ";phone-context=", 15)) {
scan += 15;
mark = scan;
while (scan < uend && *scan != ';')
++scan;
ret = sip_uri_parse_tel_areaspe(mark, scan);
}
} else {
ret = 1;
}
while (scan < uend && ret) {
if (uend - scan > 15 &&
!sip_uri_url_casecmp(scan, ";phone-context=", 15)) {
scan += 15;
mark = scan;
while (scan < uend && *scan != ';')
++scan;
ret = sip_uri_parse_tel_areaspe(mark, scan);
} else if (uend - scan > 5 &&
!sip_uri_url_casecmp(scan, ";tsp=", 5)) {
scan += 5;
mark = scan;
while (scan < uend && *scan != ';')
++scan;
ret = sip_uri_parse_tel_servicepro(mark, scan);
} else {
++scan;
mark = scan;
while (scan < uend && (*scan != ';' || quote)) {
if (sip_uri_hexVal(scan, uend) == 0x22) {
quote = !quote;
scan += 3;
} else {
++scan;
}
}
ret = sip_uri_parse_tel_futureext(mark, scan);
}
}
return (ret && scan == uend);
}
static int
sip_uri_parse_tel_areaspe(char *scan, char *uend)
{
int uri_hexValue;
if (scan == uend)
return (0);
if (*scan == '+') {
++scan;
if (scan == uend)
return (0);
while (scan < uend && SIP_URI_ISPHONEDIGIT(*scan))
++scan;
} else if (SIP_URI_ISPHONEDIGIT(*scan) || SIP_URI_ISDTMFDIGIT(*scan) ||
sip_uri_isEscapedPound(&scan, uend) ||
*scan == 'p' || *scan == 'w') {
++scan;
while (scan < uend &&
(SIP_URI_ISPHONEDIGIT(*scan) ||
SIP_URI_ISDTMFDIGIT(*scan) ||
sip_uri_isEscapedPound(&scan, uend) ||
*scan == 'p' || *scan == 'w')) {
++scan;
}
} else {
if (*scan == '!' || *scan == '$' || *scan == '&' ||
*scan == '\'' || *scan == ',' || *scan == '/' ||
*scan == '=' || *scan == '?' || *scan == '_' ||
(*scan >= 'E' && *scan <= 'Z' &&
*scan != 'P' && *scan != 'W') ||
(*scan >= 'e' && *scan <= 'z' &&
*scan != 'p' && *scan != 'w')) {
++scan;
} else {
uri_hexValue = sip_uri_hexVal(scan, uend);
if (uri_hexValue == 0x21 || uri_hexValue == 0x22 ||
(uri_hexValue >= 0x24 && uri_hexValue <= 0x27) ||
uri_hexValue == 0x2c || uri_hexValue == 0x2f ||
uri_hexValue == 0x3a ||
(uri_hexValue >= 0x3c && uri_hexValue <= 0x40) ||
(uri_hexValue >= 0x45 && uri_hexValue <= 0x4f) ||
(uri_hexValue >= 0x51 && uri_hexValue <= 0x56) ||
(uri_hexValue >= 0x58 && uri_hexValue <= 0x60) ||
(uri_hexValue >= 0x65 && uri_hexValue <= 0x6f) ||
(uri_hexValue >= 0x71 && uri_hexValue <= 0x76) ||
(uri_hexValue >= 0x78 && uri_hexValue <= 0x7e)) {
scan += 3;
} else {
return (0);
}
}
while (scan < uend) {
if (SIP_URI_ISUNRESERVED(*scan) ||
(SIP_URI_ISUSER(*scan) && *scan != ';')) {
++scan;
} else {
uri_hexValue = sip_uri_hexVal(scan, uend);
if (uri_hexValue >= 0x21 &&
uri_hexValue <= 0x7e &&
uri_hexValue != 0x3b) {
scan += 3;
} else {
return (0);
}
}
}
}
if (scan < uend)
return (0);
return (1);
}
static int
sip_uri_hexVal(char *scan, char *uend)
{
int ret = -1;
if (SIP_URI_ISURLESCAPE(scan, uend)) {
ret = (SIP_URI_ISDIGIT(scan[1]) ? (scan[1] - '0') :
(tolower(scan[1]) - 'a' + 10)) * 16 +
(SIP_URI_ISDIGIT(scan[2]) ? (scan[2] - '0') :
(tolower(scan[2]) - 'a' + 10));
}
return (ret);
}
static int
sip_uri_parse_tel_servicepro(char *scan, char *uend)
{
char *mark = (char *)0;
if (scan == uend)
return (0);
if (sip_uri_hexVal(scan, uend) == 0x20 && scan + 3 == uend)
return (1);
while (scan < uend) {
mark = scan;
while (scan < uend && (*scan == '-'|| SIP_URI_ISALNUM(*scan)))
++scan;
if ((scan < uend && *scan != '.') ||
!SIP_URI_ISALPHA(*mark) || !SIP_URI_ISALNUM(*(scan - 1))) {
return (0);
}
if (scan < uend)
++scan;
}
if (scan < uend)
return (0);
return (1);
}
static int
sip_uri_parse_tel_futureext(char *scan, char *uend)
{
char *mark;
int uri_hexValue = 0;
if (scan == uend)
return (0);
mark = scan;
while (scan < uend && sip_uri_isTokenchar(&scan, uend))
;
if (mark == scan ||
(scan < uend && (*scan != '=' || scan + 1 == uend))) {
return (0);
}
if (scan == uend)
return (1);
++scan;
if (sip_uri_isTokenchar(&scan, uend)) {
while (sip_uri_isTokenchar(&scan, uend))
;
if (scan < uend) {
if (*scan != '?')
return (0);
++scan;
mark = scan;
while (sip_uri_isTokenchar(&scan, uend))
;
if (mark == scan)
return (0);
}
} else {
uri_hexValue = sip_uri_hexVal(scan, uend);
if (uri_hexValue != 0x22)
return (0);
scan += 3;
while (scan < uend && sip_uri_hexVal(scan, uend) != 0x22) {
if (sip_uri_hexVal(scan, uend) == 0x5c) {
scan += 3;
if (scan < uend) {
if (SIP_URI_ISUNRESERVED(*scan) ||
SIP_URI_ISUSER(*scan)) {
++scan;
} else if (sip_uri_hexVal(scan, uend) >=
0x00 &&
sip_uri_hexVal(scan, uend) <=
0x7f) {
scan += 3;
} else {
return (0);
}
} else {
return (0);
}
} else {
if (SIP_URI_ISUNRESERVED(*scan) ||
SIP_URI_ISUSER(*scan)) {
++scan;
} else {
uri_hexValue =
sip_uri_hexVal(scan, uend);
if ((uri_hexValue >= 0x20 &&
uri_hexValue <= 0x21) ||
(uri_hexValue >= 0x23 &&
uri_hexValue <= 0x7e) ||
(uri_hexValue >= 0x80 &&
uri_hexValue <= 0xff)) {
scan += 3;
} else {
return (0);
}
}
}
}
if (scan == uend ||
(scan < uend && sip_uri_hexVal(scan, uend) != 0x22)) {
return (0);
}
scan += 3;
}
if (scan < uend)
return (0);
return (1);
}
static int
sip_uri_isTokenchar(char **pscan, char *uend)
{
char *scan = *pscan;
int uri_hexValue = 0;
if (scan == uend)
return (0);
if ((SIP_URI_ISUNRESERVED(*scan) && *scan != '(' && *scan != ')') ||
*scan == '$' || *scan == '&' || *scan == '+') {
++scan;
*pscan = scan;
return (1);
}
uri_hexValue = sip_uri_hexVal(scan, uend);
if (uri_hexValue == 0x21 || uri_hexValue == 0x7c ||
uri_hexValue == 0x7e ||
(uri_hexValue >= 0x23 && uri_hexValue <= 0x27) ||
(uri_hexValue >= 0x2a && uri_hexValue <= 0x2b) ||
(uri_hexValue >= 0x2d && uri_hexValue <= 0x2e) ||
(uri_hexValue >= 0x30 && uri_hexValue <= 0x39) ||
(uri_hexValue >= 0x41 && uri_hexValue <= 0x5a) ||
(uri_hexValue >= 0x5e && uri_hexValue <= 0x7a)) {
scan += 3;
*pscan = scan;
return (1);
}
return (0);
}
static int
sip_uri_isEscapedPound(char **pscan, char *uend)
{
char *scan = *pscan;
if (scan == uend)
return (0);
if (*scan == '%' && scan + 2 < uend && scan[1] == '2' &&
scan[2] == '3') {
scan += 2;
*pscan = scan;
return (1);
}
return (0);
}
static int
sip_uri_parse_scheme(_sip_uri_t *outurl, char *scan, char *uend)
{
if (scan == uend) {
outurl->sip_uri_errflags |= SIP_URIERR_SCHEME;
return (0);
}
outurl->sip_uri_scheme.sip_str_ptr = scan;
outurl->sip_uri_scheme.sip_str_len = uend - scan;
if (scan < uend && SIP_URI_ISALPHA(*scan)) {
++scan;
while (scan < uend && SIP_URI_ISSCHEME(*scan))
++scan;
}
if (scan < uend)
outurl->sip_uri_errflags |= SIP_URIERR_SCHEME;
return (1);
}
static void
sip_uri_parse_params(_sip_uri_t *outurl, char *scan, char *uend)
{
char *mark = (char *)0;
char *equal = (char *)0;
int i = 0;
int ttl = 0;
int paramleftlen = 0;
int gothost = 0;
sip_param_t *param = NULL;
sip_param_t *new_param = NULL;
if (scan == uend || *scan != ';' || scan + 1 == uend) {
outurl->sip_uri_errflags |= SIP_URIERR_PARAM;
return;
}
while (scan < uend) {
mark = ++scan;
while (scan < uend && *scan != ';')
++scan;
if (scan == mark) {
outurl->sip_uri_errflags |= SIP_URIERR_PARAM;
return;
}
new_param = calloc(1, sizeof (sip_param_t));
if (new_param == NULL) {
outurl->sip_uri_errflags |= SIP_URIERR_MEMORY;
return;
}
if (param == NULL)
outurl->sip_uri_params = new_param;
else
param->param_next = new_param;
param = new_param;
param->param_name.sip_str_ptr = mark;
equal = memchr(mark, '=', scan - mark);
if (equal == (char *)0) {
param->param_name.sip_str_len = scan - mark;
param->param_value.sip_str_ptr = NULL;
param->param_value.sip_str_len = 0;
while (mark < scan && (SIP_URI_ISPARAM(*mark) ||
SIP_URI_ISURLESCAPE(mark, scan))) {
++mark;
}
} else {
param->param_name.sip_str_len = equal - mark;
param->param_value.sip_str_ptr = equal + 1;
param->param_value.sip_str_len = scan - equal - 1;
if (mark == equal || equal + 1 == scan) {
outurl->sip_uri_errflags |= SIP_URIERR_PARAM;
return;
}
paramleftlen = equal - mark + 1;
if ((paramleftlen == 10 &&
!sip_uri_url_casecmp(mark, "transport=", 10)) ||
(paramleftlen == 5 &&
!sip_uri_url_casecmp(mark, "user=", 5)) ||
(paramleftlen == 7 &&
!sip_uri_url_casecmp(mark, "method=", 7))) {
if (scan - equal == 1) {
outurl->sip_uri_errflags |=
SIP_URIERR_PARAM;
return;
}
mark = equal + 1;
while (mark < scan && SIP_URI_ISTOKEN(*mark))
++mark;
} else if (paramleftlen == 4 &&
!sip_uri_url_casecmp(mark, "ttl=", 4)) {
if (scan - equal == 1) {
outurl->sip_uri_errflags |=
SIP_URIERR_PARAM;
return;
}
mark = equal;
for (i = 0; i < 3; ++i) {
++mark;
if (mark < scan &&
SIP_URI_ISDIGIT(*mark)) {
ttl = ttl * 10 + (*mark - '0');
}
if (ttl > 255) {
outurl->sip_uri_errflags |=
SIP_URIERR_PARAM;
return;
}
}
} else if (paramleftlen == 6 &&
!sip_uri_url_casecmp(mark, "maddr=", 6)) {
gothost = 0;
mark = equal + 1;
if (mark < scan && SIP_URI_ISDIGIT(*mark)) {
gothost = sip_uri_parse_ipv4(mark,
scan);
}
if (!gothost && mark < scan && *mark == '[') {
gothost = sip_uri_parse_ipv6(mark,
scan);
}
if (!gothost && mark < scan) {
if (!(gothost =
sip_uri_parse_hostname(mark,
scan))) {
outurl->sip_uri_errflags |=
SIP_URIERR_PARAM;
}
}
if (gothost)
mark = scan;
} else if (paramleftlen == 3 &&
!sip_uri_url_casecmp(mark, "lr=", 3)) {
outurl->sip_uri_errflags |= SIP_URIERR_PARAM;
return;
} else {
while (mark < scan && (SIP_URI_ISPARAM(*mark) ||
SIP_URI_ISURLESCAPE(mark, scan) ||
mark == equal)) {
++mark;
}
}
}
if (mark < scan) {
outurl->sip_uri_errflags |= SIP_URIERR_PARAM;
return;
}
}
}
static void
sip_uri_parse_headers(_sip_uri_t *outurl, char *scan, char *uend)
{
char *mark = NULL;
char *equal = NULL;
if (scan == uend || *scan != '?' || scan + 1 == uend) {
outurl->sip_uri_errflags |= SIP_URIERR_HEADER;
return;
}
outurl->sip_uri_headers.sip_str_ptr = scan + 1;
outurl->sip_uri_headers.sip_str_len = uend - (scan + 1);
while (scan < uend) {
mark = ++scan;
while (scan < uend && *scan != '&')
++scan;
if (scan == mark) {
outurl->sip_uri_errflags |= SIP_URIERR_HEADER;
return;
}
equal = memchr(mark, '=', scan - mark);
if (equal == mark || equal == (char *)0) {
outurl->sip_uri_errflags |= SIP_URIERR_HEADER;
return;
}
while (mark < scan &&
(SIP_URI_ISHEADER(*mark) ||
SIP_URI_ISURLESCAPE(mark, scan) || mark == equal)) {
++mark;
}
if (mark < scan) {
outurl->sip_uri_errflags |= SIP_URIERR_HEADER;
return;
}
}
}
static void
sip_uri_parse_abs_opaque(_sip_uri_t *outurl, char *scan, char *uend)
{
if (scan == uend) {
outurl->sip_uri_errflags |= SIP_URIERR_OPAQUE;
return;
}
outurl->sip_uri_opaque.sip_str_ptr = scan;
outurl->sip_uri_opaque.sip_str_len = uend - scan;
if (SIP_URI_ISUNRESERVED(*scan) || SIP_URI_ISURLESCAPE(scan, uend) ||
SIP_URI_ISOTHER(*scan) || *scan == ';' || *scan == '?' ||
*scan == ':' || *scan == '@' || *scan == '&') {
++scan;
} else {
outurl->sip_uri_errflags |= SIP_URIERR_OPAQUE;
return;
}
while (scan < uend && (SIP_URI_ISRESERVED(*scan) ||
SIP_URI_ISUNRESERVED(*scan) || SIP_URI_ISURLESCAPE(scan, uend))) {
++scan;
}
if (scan < uend)
outurl->sip_uri_errflags |= SIP_URIERR_OPAQUE;
}
static void
sip_uri_parse_abs_query(_sip_uri_t *outurl, char *scan, char *uend)
{
if (uend == scan || *scan != '?' || scan + 1 == uend)
return;
++scan;
outurl->sip_uri_query.sip_str_ptr = scan;
outurl->sip_uri_query.sip_str_len = uend - scan;
while (scan < uend && (SIP_URI_ISRESERVED(*scan) ||
SIP_URI_ISUNRESERVED(*scan) || SIP_URI_ISURLESCAPE(scan, uend))) {
++scan;
}
if (scan < uend)
outurl->sip_uri_errflags |= SIP_URIERR_QUERY;
}
static void
sip_uri_parse_abs_path(_sip_uri_t *outurl, char *scan, char *uend)
{
if (scan == uend || *scan != '/')
return;
outurl->sip_uri_path.sip_str_ptr = scan;
outurl->sip_uri_path.sip_str_len = uend - scan;
++scan;
while (scan < uend && (SIP_URI_ISPCHAR(*scan) ||
SIP_URI_ISUNRESERVED(*scan) || SIP_URI_ISURLESCAPE(scan, uend) ||
*scan == '/' || *scan == ';')) {
++scan;
}
if (scan < uend)
outurl->sip_uri_errflags |= SIP_URIERR_PATH;
}
static void
sip_uri_parse_abs_regname(_sip_uri_t *outurl, char *scan, char *uend)
{
if (scan == uend)
return;
outurl->sip_uri_regname.sip_str_ptr = scan;
outurl->sip_uri_regname.sip_str_len = uend - scan;
while (scan < uend && (SIP_URI_ISUNRESERVED(*scan) ||
SIP_URI_ISURLESCAPE(scan, uend) || SIP_URI_ISREGNAME(*scan))) {
++scan;
}
if (scan < uend)
outurl->sip_uri_errflags |= SIP_URIERR_REGNAME;
}
static void
sip_uri_parse_password(_sip_uri_t *outurl, char *scan, char *uend)
{
if (scan == uend || *scan != ':' || scan + 1 == uend)
return;
++scan;
outurl->sip_uri_password.sip_str_ptr = scan;
outurl->sip_uri_password.sip_str_len = uend - scan;
while (scan < uend && (SIP_URI_ISUNRESERVED(*scan) ||
SIP_URI_ISURLESCAPE(scan, uend) || SIP_URI_ISOTHER(*scan) ||
*scan == '&')) {
++scan;
}
if (scan < uend)
outurl->sip_uri_errflags |= SIP_URIERR_PASS;
}
static void
sip_uri_parse_user(_sip_uri_t *outurl, char *scan, char *uend)
{
if (scan == uend) {
outurl->sip_uri_errflags |= SIP_URIERR_USER;
return;
}
outurl->sip_uri_user.sip_str_ptr = scan;
outurl->sip_uri_user.sip_str_len = uend - scan;
if (sip_uri_parse_tel(scan, uend)) {
outurl->sip_uri_isteluser = B_TRUE;
} else {
while (scan < uend && (SIP_URI_ISUNRESERVED(*scan) ||
SIP_URI_ISURLESCAPE(scan, uend) || SIP_URI_ISUSER(*scan))) {
++scan;
}
if (scan < uend)
outurl->sip_uri_errflags |= SIP_URIERR_USER;
}
}
static void
sip_uri_parse_port(_sip_uri_t *outurl, char *scan, char *uend)
{
if (scan == uend || *scan != ':' || scan + 1 == uend) {
outurl->sip_uri_errflags |= SIP_URIERR_PORT;
return;
}
++scan;
if (SIP_URI_ISDIGIT(*scan)) {
outurl->sip_uri_port = *scan - '0';
while (++scan < uend && SIP_URI_ISDIGIT(*scan)) {
outurl->sip_uri_port =
outurl->sip_uri_port * 10 + (*scan - '0');
if (outurl->sip_uri_port > 0xffff) {
outurl->sip_uri_errflags |= SIP_URIERR_PORT;
outurl->sip_uri_port = 0;
break;
}
}
}
if (scan < uend) {
outurl->sip_uri_errflags |= SIP_URIERR_PORT;
outurl->sip_uri_port = 0;
}
}
static int
sip_uri_parse_ipv4(char *scan, char *uend)
{
int j = 0;
int val = 0;
for (j = 0; j < 4; ++j) {
if (!SIP_URI_ISDIGIT(*scan))
break;
val = *scan - '0';
while (++scan < uend && SIP_URI_ISDIGIT(*scan)) {
val = val * 10 + (*scan - '0');
if (val > 255)
return (0);
}
if (j < 3) {
if (*scan != '.')
break;
++scan;
}
}
if (j == 4 && scan == uend)
return (1);
return (0);
}
static int
sip_uri_parse_ipv6(char *scan, char *uend)
{
char *mark;
unsigned j = 0;
unsigned val = 0;
int zpad = 0;
if (*scan != '[')
return (0);
++scan;
j = 0;
if (scan + 1 < uend && scan[0] == ':' && scan[1] == ':') {
zpad = 0;
scan += 2;
} else {
zpad = -1;
}
while (scan < uend && j < 15) {
if (!SIP_URI_ISHEX(*scan))
break;
mark = scan;
val = SIP_URI_HEXVAL(*scan);
while (++scan < uend && SIP_URI_ISHEX(*scan)) {
val = val * 16 + SIP_URI_HEXVAL(*scan);
if (val > 0xffff)
return (0);
}
if (scan == uend)
return (0);
if (*scan == '.' && (j == 12 || (zpad != -1 && j < 12)) &&
mark < uend && sip_uri_parse_ipv4(mark, uend - 1) &&
*(uend - 1) == ']') {
mark = uend - 1;
j += 4;
scan = mark + 1;
break;
}
j += 2;
if (*scan == ':') {
if (++scan < uend && *scan == ':') {
if (zpad != -1)
return (0);
zpad = j;
if (++scan < uend && *scan == ']') {
++scan;
break;
}
}
} else if (*scan == ']' && (j == 16 || zpad != -1)) {
++scan;
break;
} else {
return (0);
}
}
if (zpad == -1 && j < 16)
return (0);
if (zpad != -1) {
if (j > 15)
return (0);
}
if (scan == uend)
return (1);
return (0);
}
static int
sip_uri_parse_hostname(char *scan, char *uend)
{
int sawalpha = 0;
if (scan < uend && SIP_URI_ISALNUM(*scan)) {
do {
sawalpha = SIP_URI_ISALPHA(*scan);
while (SIP_URI_ISHOST(*scan))
++scan;
if (*scan != '.')
break;
++scan;
} while (scan < uend && SIP_URI_ISALNUM(*scan));
}
if (sawalpha && scan == uend)
return (1);
return (0);
}
static void
sip_uri_parse_netpath(_sip_uri_t *outurl, char **pscan, char *uend,
boolean_t issip)
{
char *mark = (char *)0;
char *mark2 = (char *)0;
char *scan = *pscan;
int gothost = 0;
mark = scan;
while (scan < uend && *scan != '@')
++scan;
if (scan < uend && *scan == '@') {
mark2 = mark;
while (mark < scan && *mark != ':')
++mark;
sip_uri_parse_user(outurl, mark2, mark);
if (*mark == ':')
sip_uri_parse_password(outurl, mark, scan);
mark = ++scan;
}
scan = mark;
if (scan < uend && *scan == '[') {
while (scan < uend && *scan != ']')
++scan;
if (scan < uend) {
++scan;
if (sip_uri_parse_ipv6(mark, scan))
gothost = 1;
}
} else {
while (scan < uend && ((issip && !SIP_URI_ISSIPHDELIM(*scan)) ||
(!issip && !SIP_URI_ISABSHDELIM(*scan)))) {
++scan;
}
if (mark < scan && SIP_URI_ISDIGIT(*mark) &&
sip_uri_parse_ipv4(mark, scan)) {
gothost = 1;
}
if (!gothost && mark < scan &&
sip_uri_parse_hostname(mark, scan)) {
gothost = 1;
}
}
if (!gothost)
outurl->sip_uri_errflags |= SIP_URIERR_HOST;
outurl->sip_uri_host.sip_str_ptr = mark;
outurl->sip_uri_host.sip_str_len = scan - mark;
mark = scan;
if (scan < uend && *scan == ':') {
while (scan < uend && ((issip && !SIP_URI_ISSIPDELIM(*scan)) ||
(!issip && !SIP_URI_ISABSDELIM(*scan)))) {
++scan;
}
sip_uri_parse_port(outurl, mark, scan);
}
*pscan = scan;
}
void
sip_uri_parse_it(_sip_uri_t *outurl, sip_str_t *uri_str)
{
char *mark;
char *scan;
char *uend;
char *str = uri_str->sip_str_ptr;
unsigned urlen = uri_str->sip_str_len;
(void) memset(outurl, 0, sizeof (sip_uri_t));
if (urlen > 1 && str[0] == '<' && str[urlen-1] == '>') {
urlen -= 2;
++str;
}
uend = str + urlen;
while (str < uend && isspace(*str)) {
++str;
--urlen;
}
while (str < uend && isspace(*(uend - 1))) {
--uend;
--urlen;
}
if (urlen > 4 && sip_uri_url_casecmp(str, "URL:", 4) == 0) {
str += 4;
urlen -= 4;
}
mark = scan = str;
while (scan < uend && *scan != ':')
++scan;
if (scan == uend || !sip_uri_parse_scheme(outurl, mark, scan)) {
outurl->sip_uri_errflags |= SIP_URIERR_SCHEME;
return;
}
if ((outurl->sip_uri_scheme.sip_str_len == SIP_SCHEME_LEN &&
!memcmp(outurl->sip_uri_scheme.sip_str_ptr, SIP_SCHEME,
SIP_SCHEME_LEN)) ||
(outurl->sip_uri_scheme.sip_str_len == SIPS_SCHEME_LEN &&
!memcmp(outurl->sip_uri_scheme.sip_str_ptr, SIPS_SCHEME,
SIPS_SCHEME_LEN))) {
outurl->sip_uri_issip = B_TRUE;
} else {
outurl->sip_uri_issip = B_FALSE;
}
++scan;
if (outurl->sip_uri_issip) {
sip_uri_parse_netpath(outurl, &scan, uend, B_TRUE);
if (scan < uend && *scan == ';') {
mark = scan;
while (scan < uend && *scan != '?')
++scan;
sip_uri_parse_params(outurl, mark, scan);
}
if (scan < uend && *scan == '?')
sip_uri_parse_headers(outurl, scan, uend);
} else if (scan < uend && scan[0] == '/') {
++scan;
if (scan < uend && *scan == '/') {
++scan;
mark = scan;
sip_uri_parse_netpath(outurl, &scan, uend, B_FALSE);
if (outurl->sip_uri_errflags & SIP_URIERR_USER ||
outurl->sip_uri_errflags & SIP_URIERR_PASS ||
outurl->sip_uri_errflags & SIP_URIERR_HOST ||
outurl->sip_uri_errflags & SIP_URIERR_PORT) {
scan = mark;
while (scan < uend && *scan != '/' &&
*scan != '?') {
++scan;
}
sip_uri_parse_abs_regname(outurl, mark, scan);
if (!(outurl->sip_uri_errflags &
SIP_URIERR_REGNAME)) {
outurl->sip_uri_user.sip_str_ptr = NULL;
outurl->sip_uri_user.sip_str_len = 0;
outurl->sip_uri_errflags &=
~SIP_URIERR_USER;
outurl->sip_uri_password.sip_str_ptr =
NULL;
outurl->sip_uri_password.sip_str_len =
0;
outurl->sip_uri_errflags &=
~SIP_URIERR_PASS;
outurl->sip_uri_host.sip_str_ptr = NULL;
outurl->sip_uri_host.sip_str_len = 0;
outurl->sip_uri_errflags &=
~SIP_URIERR_HOST;
outurl->sip_uri_port = 0;
outurl->sip_uri_errflags &=
~SIP_URIERR_PORT;
}
}
} else {
--scan;
}
if (scan < uend && *scan == '/') {
mark = scan;
while (scan < uend && *scan != '?')
++scan;
sip_uri_parse_abs_path(outurl, mark, scan);
}
if (scan < uend && *scan == '?')
sip_uri_parse_abs_query(outurl, scan, uend);
} else {
sip_uri_parse_abs_opaque(outurl, scan, uend);
}
}