/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include "sip_parse_uri.h" /* * SIP-URI = "sip:" [ userinfo ] hostport uri-parameters [ headers ] * SIPS-URI = "sips:" [ userinfo ] hostport uri-parameters [ headers ] * userinfo = ( user / telephone-subscriber ) [ ":" password ] "@" * user = 1*( unreserved / escaped / user-unreserved ) * user-unreserved = "&" / "=" / "+" / "$" / "," / ";" / "?" / "/" * password = *( unreserved / escaped / "&" / "=" / "+" / "$" / "," ) * hostport = host [ ":" port ] * host = hostname / IPv4address / IPv6reference * hostname = *( domainlabel "." ) toplabel [ "." ] * domainlabel = alphanum / alphanum *( alphanum / "-" ) alphanum * toplabel = ALPHA / ALPHA *( alphanum / "-" ) alphanum * IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT * IPv6reference = "[" IPv6address "]" * IPv6address = hexpart [ ":" IPv4address ] * hexpart = hexseq / hexseq "::" [ hexseq ] / "::" [ hexseq ] * hexseq = hex4 *( ":" hex4) * hex4 = 1*4HEXDIG * port = 1*DIGIT * * The BNF for telephone-subscriber can be found in RFC 2806 [9]. Note, * however, that any characters allowed there that are not allowed in * the user part of the SIP URI MUST be escaped. * * uri-parameters = *( ";" uri-parameter) * uri-parameter = transport-param / user-param / method-param * / ttl-param / maddr-param / lr-param / other-param * transport-param = "transport="( "udp" / "tcp" / "sctp" / "tls" * / other-transport) * other-transport = token * user-param = "user=" ( "phone" / "ip" / other-user) * other-user = token * method-param = "method=" Method * ttl-param = "ttl=" ttl * maddr-param = "maddr=" host * lr-param = "lr" * other-param = pname [ "=" pvalue ] * pname = 1*paramchar * pvalue = 1*paramchar * paramchar = param-unreserved / unreserved / escaped * param-unreserved = "[" / "]" / "/" / ":" / "&" / "+" / "$" * headers = "?" header *( "&" header ) * header = hname "=" hvalue * hname = 1*( hnv-unreserved / unreserved / escaped ) * hvalue = *( hnv-unreserved / unreserved / escaped ) * hnv-unreserved = "[" / "]" / "/" / "?" / ":" / "+" / "$" * */ #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]))) /* * URL character classes * mark - _ . ! ~ * ' () * reserved ; / ? : @ & = + $ , also [] for IPv6 * unreserved alphanum mark * pchar : @ & = + $ , unreserved * userinfo ; : & = + $ , unreserved escaped * relsegment ; @ & = + $ , unreserved escaped * reg_name ; : @ & = + $ , unreserved escaped * token - _ . ! ~ * ' % + ` * param-unreserved [ ] / : + $ & * hnv-unreserved [ ] / : + $ ? */ #define SIP_URI_ALPHA_BIT 0x0001 #define SIP_URI_DIGIT_BIT 0x0002 #define SIP_URI_ALNUM_BITS 0x0003 #define SIP_URI_SCHEME_BIT 0x0004 /* for - + . */ #define SIP_URI_TOKEN_BIT 0x0008 /* for - _ . ! ~ * ' % + ` */ #define SIP_URI_QUEST_BIT 0x0010 /* for ? */ #define SIP_URI_AT_BIT 0x0020 /* for @ */ #define SIP_URI_COLON_BIT 0x0040 /* for : */ #define SIP_URI_SEMI_BIT 0x0080 /* for ; */ #define SIP_URI_DASH_BIT 0x0100 /* for - */ #define SIP_URI_MARK_BIT 0x0200 /* for - _ . ! ~ * ' ( ) */ #define SIP_URI_AND_BIT 0x0400 /* for & */ #define SIP_URI_PHCOMM_BIT 0x0800 /* for [ ] / : + $ */ #define SIP_URI_OTHER_BIT 0x1000 /* for = + $ , */ #define SIP_URI_SLASH_BIT 0x2000 /* for / */ #define SIP_URI_VISUALSEP_BIT 0x4000 /* for -.() */ #define SIP_URI_DTMFURI_DIGIT_BIT 0x8000 /* for *ABCD */ #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); /* * get the hex value of a char */ 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); } /* * basic ASCII case-insensitive comparison */ 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])); } /* * telephone-subscriber = global-phone-number / local-phone-number * Please refer to RFC 2806 */ 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); /* * parse isdn-subaddress */ 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); } /* * parse post-dial */ 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) { /* * parse area-specifier */ 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; } /* * parse area-specifier, service-provider, future-extension */ 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); } /* * area-specifier = ";" phone-context-tag "=" phone-context-ident * phone-context-tag = "phone-context" * phone-context-ident = network-prefix / private-prefix * network-prefix = global-network-prefix / local-network-prefix * global-network-prefix = "+" 1*phonedigit * local-network-prefix = 1*(phonedigit / dtmf-digit / pause-character) * private-prefix = (%x21-22 / %x24-27 / %x2C / %x2F / %x3A / * %x3C-40 / %x45-4F / %x51-56 / %x58-60 / * %x65-6F / %x71-76 / %x78-7E) * *(%x21-3A / %x3C-7E) * phonedigit = DIGIT / visual-separator * visual-separator = "-" / "." / "(" / ")" * pause-character = one-second-pause / wait-for-dial-tone * one-second-pause = "p" * wait-for-dial-tone = "w" * dtmf-digit = "*" / "#" / "A" / "B" / "C" / "D" */ static int sip_uri_parse_tel_areaspe(char *scan, char *uend) { int uri_hexValue; if (scan == uend) return (0); /* * parse global-network-prefix */ if (*scan == '+') { ++scan; if (scan == uend) return (0); while (scan < uend && SIP_URI_ISPHONEDIGIT(*scan)) ++scan; /* * parse local-network-prefix */ } 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 { /* * parse private-prefix * * any characters allowed in RFC 2806 that are not allowed in * the user part of the SIP URI MUST be escaped * * private-prefix = (! $ & ', / = ? _ * EFGHIJKLMNOQRSTUVXYZ efghijklmnoqrstuvxyz * { } | ~ [ ] \ ^ ` " % : < > @) * *(%x21-3A / %x3C-7E) * * following characters are allowed in RFC 2806 and * the user part of SIP URI * ! $ & ', / = ? _ EFGHIJKLMNOQRSTUVXYZ efghijklmnoqrstuvxyz */ 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); } } /* * parse *(%x21-3A / %x3C-7E) */ 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); } /* * service-provider = ";" provider-tag "=" provider-hostname * provider-tag = "tsp" * provider-hostname = domain */ static int sip_uri_parse_tel_servicepro(char *scan, char *uend) { char *mark = (char *)0; if (scan == uend) return (0); /* * parse domain=" " */ 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); } /* * future-extension = ";" 1*(token-char) ["=" ((1*(token-char) * ["?" 1*(token-char)]) / quoted-string )] * token-char = (%x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39 * / %x41-5A / %x5E-7A / %x7C / %x7E) */ static int sip_uri_parse_tel_futureext(char *scan, char *uend) { char *mark; int uri_hexValue = 0; if (scan == uend) return (0); /* * parse 1*(token-char) */ 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; /* * parse 1*token-char ["?" 1*token-char] */ 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 { /* parse quoted-string */ uri_hexValue = sip_uri_hexVal(scan, uend); if (uri_hexValue != 0x22) return (0); scan += 3; while (scan < uend && sip_uri_hexVal(scan, uend) != 0x22) { /* * parse "\" CHAR */ 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); } /* * Any characters allowed in RFC2806 tel URL that are not allowed in * the user part of the SIP URI MUST be escaped. * token-char = - _ . ! ~ * ' $ & + DIGIT ALPHA # % ^ ` | */ static int sip_uri_isTokenchar(char **pscan, char *uend) { char *scan = *pscan; int uri_hexValue = 0; if (scan == uend) return (0); /* * for ALPAH DIGIT - _ . ! ~ * ' $ & + */ 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); } /* * '#' is not allowed in the telephone-subscriber part of SIP URI * it must be escaped */ 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); } /* * scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */ 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); } /* * The format of params is supposed to be;XXX;XXX;XXX * uri-parameters = *(";" uri-parameter) * uri-parameter = transport-param / user-param / method-param * / ttl-param / maddr-param / lr-param / other-param * transport-param = "transport=" * ("udp" / "tcp" / "sctp" / "tls" / other-transport) * other-transport = token * user-param = "user=" ("phone" / "ip" / other-user) * other-user = token * method-param = "method=" Method * ttl-param = "ttl=" ttl * maddr-param = "maddr=" host * lr-param = "lr" * other-param = pname [ "=" pvalue ] * pname = 1*paramchar * pvalue = 1*paramchar * paramchar = param-unreserved / unreserved / escaped * param-unreserved = "[" / "]" / "/" / ":" / "&" / "+" / "$" */ 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); } /* * not valid syntax for a host or user name, * try IPv6 literal */ if (!gothost && mark < scan && *mark == '[') { gothost = sip_uri_parse_ipv6(mark, scan); } /* * look for a valid host name: * *(domainlabel ".") toplabel ["."] */ 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; } } } /* * The format of headers is supposed to be ?XXX&XXX&XXX * headers = "?" header *("&" header * header = hname "=" hvalue * hname = 1*(hnv-unreserved / unreserved / escaped * hvalue = *(hnv-unreserved / unreserved / escaped * hnv-unreserved = "[" / "]" / "/" / "?" / ":" / "+" / "$" */ 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; } } } /* * opaque-part = uric-no-slash *uric * uric = reserved / unreserved / escaped * uric-no-slash = unreserved / escaped / ";" / "?" / ":" / "@" * / "&" / "=" / "+" / "$" / "," */ 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; } /* * format of query is supposed to be ?XXX * query = *uric * uric = reserved / unreserved / escaped */ 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; } /* * the format of path is supposed to be /XXX;XXX/XXX; * abs-path = "/" path-segments * path-segments = segment *( "/" segment ) * segment = *pchar *( ";" param ) * param = *pchar * pchar = unreserved / escaped / * ":" / "@" / "&" / "=" / "+" / "$" / "," */ 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; } /* * reg-name = 1*( unreserved / escaped / "$" / "," / ";" * / ":" / "@" / "&" / "=" / "+" ) */ 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; } /* * The format of the password is supposed to be :XXX * password = *( unreserved / escaped / "&" / "=" / "+" / "$" / "," ) */ 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; } /* * user = 1*( unreserved / escaped / user-unreserved ) * user-unreserved = "&" / "=" / "+" / "$" / "," / ";" / "?" / "/" */ 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; } } /* * the format of port is supposed to be :XXX * port = 1*DIGIT */ 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; /* * parse numeric port number */ 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; } } /* * parse an IPv4 address * 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT * advances pscan to end of IPv4 address, or after last "." that was * a valid IPv4 or domain name. * returns 1 if ipv4 found, 0 otherwise */ 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); } /* * parse an IPv6 address * IPv6address = hexpart [ ":" IPv4address ] * IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT * hexpart = hexseq | hexseq "::" [ hexseq ] | "::" [ hexseq ] * hexseq = hex4 *( ":" hex4) * hex4 = 1*4HEXDIG * if not found, leaves pscan unchanged, otherwise advances to end * returns 1 if valid, * 0 if invalid */ static int sip_uri_parse_ipv6(char *scan, char *uend) { char *mark; unsigned j = 0; /* index for addr */ unsigned val = 0; /* hex value */ int zpad = 0; /* index of :: delimiter */ if (*scan != '[') return (0); ++scan; j = 0; /* * check for leading "::", set zpad to the position of the "::" */ if (scan + 1 < uend && scan[0] == ':' && scan[1] == ':') { zpad = 0; scan += 2; } else { zpad = -1; } /* * loop through up to 16 bytes of IPv6 address */ 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); } /* * always require a delimiter or ] */ 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; } /* * set address */ j += 2; /* * check for delimiter or ] */ if (*scan == ':') { /* * found ":" delimiter, check for "::" */ 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 { /* * not a valid delimiter */ return (0); } } if (zpad == -1 && j < 16) return (0); if (zpad != -1) { if (j > 15) return (0); } if (scan == uend) return (1); return (0); } /* * hostname = *( domainlabel "." ) toplabel [ "." ] * domainlabel = alphanum / alphanum *( alphanum / "-" ) alphanum * toplabel = ALPHA / ALPHA *( alphanum / "-" ) alphanum */ 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); } /* * parse the network path portion of a full URL */ 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; /* * look for the first high-level delimiter */ mark = scan; while (scan < uend && *scan != '@') ++scan; /* * handle userinfo section of URL */ if (scan < uend && *scan == '@') { /* * parse user */ mark2 = mark; while (mark < scan && *mark != ':') ++mark; sip_uri_parse_user(outurl, mark2, mark); /* * parse password */ if (*mark == ':') sip_uri_parse_password(outurl, mark, scan); mark = ++scan; } scan = mark; if (scan < uend && *scan == '[') { /* look for an IPv6 address */ 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; } /* * look for an IPv4 address */ if (mark < scan && SIP_URI_ISDIGIT(*mark) && sip_uri_parse_ipv4(mark, scan)) { gothost = 1; } /* * look for a valid host name */ if (!gothost && mark < scan && sip_uri_parse_hostname(mark, scan)) { gothost = 1; } } /* * handle invalid host name */ if (!gothost) outurl->sip_uri_errflags |= SIP_URIERR_HOST; /* * save host name */ outurl->sip_uri_host.sip_str_ptr = mark; outurl->sip_uri_host.sip_str_len = scan - mark; mark = scan; /* * parse the port number */ 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); } /* * set return pointer */ *pscan = scan; } /* * parse a URL * URL = SIP-URI / SIPS-URI / absoluteURI */ 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; /* * reset output parameters */ (void) memset(outurl, 0, sizeof (sip_uri_t)); /* * strip enclosing angle brackets */ if (urlen > 1 && str[0] == '<' && str[urlen-1] == '>') { urlen -= 2; ++str; } uend = str + urlen; /* * strip off space prefix and trailing spaces */ while (str < uend && isspace(*str)) { ++str; --urlen; } while (str < uend && isspace(*(uend - 1))) { --uend; --urlen; } /* * strip off "URL:" prefix */ if (urlen > 4 && sip_uri_url_casecmp(str, "URL:", 4) == 0) { str += 4; urlen -= 4; } /* * parse the scheme name */ 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; /* skip ':' */ if (outurl->sip_uri_issip) { /* * parse SIP URL */ sip_uri_parse_netpath(outurl, &scan, uend, B_TRUE); /* * parse parameters */ if (scan < uend && *scan == ';') { mark = scan; while (scan < uend && *scan != '?') ++scan; sip_uri_parse_params(outurl, mark, scan); } /* * parse headers */ if (scan < uend && *scan == '?') sip_uri_parse_headers(outurl, scan, uend); } else if (scan < uend && scan[0] == '/') { /* parse absoluteURL */ ++scan; /* * parse authority * authority = srvr / reg-name * srvr = [ [ userinfo "@" ] hostport ] * reg-name = 1*(unreserved / escaped / "$" / "," * / ";" / ":" / "@" / "&" / "=" / "+") */ if (scan < uend && *scan == '/') { ++scan; mark = scan; /* * take authority as srvr */ sip_uri_parse_netpath(outurl, &scan, uend, B_FALSE); /* * if srvr failed, take it as reg-name * parse reg-name */ 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)) { /* * remove error info of user, * password, host, port */ 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 { /* * there is no net-path */ --scan; } /* * parse abs-path */ if (scan < uend && *scan == '/') { mark = scan; while (scan < uend && *scan != '?') ++scan; sip_uri_parse_abs_path(outurl, mark, scan); } /* * parse query */ if (scan < uend && *scan == '?') sip_uri_parse_abs_query(outurl, scan, uend); } else { /* * parse opaque-part */ sip_uri_parse_abs_opaque(outurl, scan, uend); } }