1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28#include <ctype.h>
29#include <stdlib.h>
30#include <string.h>
31
32#include "sip_parse_uri.h"
33
34/*
35 * SIP-URI          =  "sip:" [ userinfo ] hostport uri-parameters [ headers ]
36 * SIPS-URI         =  "sips:" [ userinfo ] hostport uri-parameters [ headers ]
37 * userinfo         =  ( user / telephone-subscriber ) [ ":" password ] "@"
38 * user             =  1*( unreserved / escaped / user-unreserved )
39 * user-unreserved  =  "&" / "=" / "+" / "$" / "," / ";" / "?" / "/"
40 * password         =  *( unreserved / escaped / "&" / "=" / "+" / "$" / "," )
41 * hostport         =  host [ ":" port ]
42 * host             =  hostname / IPv4address / IPv6reference
43 * hostname         =  *( domainlabel "." ) toplabel [ "." ]
44 * domainlabel      =  alphanum / alphanum *( alphanum / "-" ) alphanum
45 * toplabel         =  ALPHA / ALPHA *( alphanum / "-" ) alphanum
46 * IPv4address    =  1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
47 * IPv6reference  =  "[" IPv6address "]"
48 * IPv6address    =  hexpart [ ":" IPv4address ]
49 * hexpart        =  hexseq / hexseq "::" [ hexseq ] / "::" [ hexseq ]
50 * hexseq         =  hex4 *( ":" hex4)
51 * hex4           =  1*4HEXDIG
52 * port           =  1*DIGIT
53 *
54 * The BNF for telephone-subscriber can be found in RFC 2806 [9].  Note,
55 * however, that any characters allowed there that are not allowed in
56 * the user part of the SIP URI MUST be escaped.
57 *
58 * uri-parameters    =  *( ";" uri-parameter)
59 * uri-parameter     =  transport-param / user-param / method-param
60 *                      / ttl-param / maddr-param / lr-param / other-param
61 * transport-param   =  "transport="( "udp" / "tcp" / "sctp" / "tls"
62 *                     / other-transport)
63 * other-transport   =  token
64 * user-param        =  "user=" ( "phone" / "ip" / other-user)
65 * other-user        =  token
66 * method-param      =  "method=" Method
67 * ttl-param         =  "ttl=" ttl
68 * maddr-param       =  "maddr=" host
69 * lr-param          =  "lr"
70 * other-param       =  pname [ "=" pvalue ]
71 * pname             =  1*paramchar
72 * pvalue            =  1*paramchar
73 * paramchar         =  param-unreserved / unreserved / escaped
74 * param-unreserved  =  "[" / "]" / "/" / ":" / "&" / "+" / "$"
75 * headers         =  "?" header *( "&" header )
76 * header          =  hname "=" hvalue
77 * hname           =  1*( hnv-unreserved / unreserved / escaped )
78 * hvalue          =  *( hnv-unreserved / unreserved / escaped )
79 * hnv-unreserved  =  "[" / "]" / "/" / "?" / ":" / "+" / "$"
80 *
81 */
82
83#define	SIP_URI_MSG_BUF_SZ	100
84
85#define	SIP_URI_ISHEX(c)					\
86	(((int)(c) >= 0x30 && (int)(c) <= 0x39) || 	\
87	((int)(c) >= 0x41 && (int)(c) <= 0x46) || 	\
88	((int)(c) >= 0x61 && (int)(c) <= 0x66))
89
90#define	SIP_URI_ISURLESCAPE(scan, end)			\
91	((scan) + 2 < (end) && (scan)[0] == '%' && 	\
92	SIP_URI_ISHEX((scan)[1]) && SIP_URI_ISHEX((scan[2])))
93
94/*
95 * URL character classes
96 *  mark	- _ . ! ~ * ' ()
97 *  reserved	; / ? : @ & = + $ ,    also [] for IPv6
98 *  unreserved	alphanum mark
99 *  pchar	: @ & = + $ , unreserved
100 *  userinfo	; : & = + $ , unreserved escaped
101 *  relsegment	; @ & = + $ , unreserved escaped
102 *  reg_name	; : @ & = + $ , unreserved escaped
103 *  token	- _ . ! ~ * ' %  + `
104 *  param-unreserved  [ ] / : + $ &
105 *  hnv-unreserved    [ ] / : + $ ?
106 */
107#define	SIP_URI_ALPHA_BIT		0x0001
108#define	SIP_URI_DIGIT_BIT		0x0002
109#define	SIP_URI_ALNUM_BITS		0x0003
110#define	SIP_URI_SCHEME_BIT		0x0004	/* for - + . */
111#define	SIP_URI_TOKEN_BIT		0x0008	/* for - _ . ! ~ * ' % + ` */
112#define	SIP_URI_QUEST_BIT		0x0010	/* for ? */
113#define	SIP_URI_AT_BIT			0x0020	/* for @ */
114#define	SIP_URI_COLON_BIT		0x0040	/* for : */
115#define	SIP_URI_SEMI_BIT		0x0080	/* for ; */
116#define	SIP_URI_DASH_BIT		0x0100	/* for - */
117#define	SIP_URI_MARK_BIT		0x0200	/* for - _ . ! ~ * ' ( ) */
118#define	SIP_URI_AND_BIT			0x0400	/* for & */
119#define	SIP_URI_PHCOMM_BIT		0x0800	/* for [ ] / : + $ */
120#define	SIP_URI_OTHER_BIT		0x1000	/* for = + $ , */
121#define	SIP_URI_SLASH_BIT		0x2000	/* for / */
122#define	SIP_URI_VISUALSEP_BIT		0x4000	/* for -.() */
123#define	SIP_URI_DTMFURI_DIGIT_BIT	0x8000	/* for *ABCD */
124
125#define	a 			SIP_URI_ALPHA_BIT
126#define	d 			SIP_URI_DIGIT_BIT
127#define	s 			SIP_URI_SCHEME_BIT
128#define	t 			SIP_URI_TOKEN_BIT
129#define	q 			SIP_URI_QUEST_BIT
130#define	m 			SIP_URI_AT_BIT
131#define	c 			SIP_URI_COLON_BIT
132#define	i 			SIP_URI_SEMI_BIT
133#define	h 			SIP_URI_DASH_BIT
134#define	k 			SIP_URI_MARK_BIT
135#define	n 			SIP_URI_AND_BIT
136#define	o 			SIP_URI_PHCOMM_BIT
137#define	r 			SIP_URI_OTHER_BIT
138#define	l 			SIP_URI_SLASH_BIT
139#define	v 			SIP_URI_VISUALSEP_BIT
140#define	f 			SIP_URI_DTMFURI_DIGIT_BIT
141
142static const unsigned short sip_uri_table[256] = {
143	0,	0,	0,	0,	0,	0,	0,	0,
144	0,	0,	0,	0,	0,	0,	0,	0,
145	0,	0,	0,	0,	0,	0,	0,	0,
146	0,	0,	0,	0,	0,	0,	0,	0,
147	0,	t|k,	0,	0,	o|r,	t,	n,	t|k,
148	k|v,	k|v,	t|k|f, s|t|r|o,	r,  h|s|t|k|v, s|t|k|v,	o|l,
149	d,	d,	d,	d,	d,	d,	d,	d,
150	d,	d,	c|o,	i,	0,	r,	0,	q,
151	m,	a|f,	a|f,	a|f,	a|f,	a,	a,	a,
152	a,	a,	a,	a,	a,	a,	a,	a,
153	a,	a,	a,	a,	a,	a,	a,	a,
154	a,	a,	a,	o,	0,	o,	0,	t|k,
155	t,	a,	a,	a,	a,	a,	a,	a,
156	a,	a,	a,	a,	a,	a,	a,	a,
157	a,	a,	a,	a,	a,	a,	a,	a,
158	a,	a,	a,	0,	0,	0,	t|k,	0,
159	0,	0,	0,	0,	0,	0,	0,	0,
160	0,	0,	0,	0,	0,	0,	0,	0,
161	0,	0,	0,	0,	0,	0,	0,	0,
162	0,	0,	0,	0,	0,	0,	0,	0,
163	0,	0,	0,	0,	0,	0,	0,	0,
164	0,	0,	0,	0,	0,	0,	0,	0,
165	0,	0,	0,	0,	0,	0,	0,	0,
166	0,	0,	0,	0,	0,	0,	0,	0,
167	0,	0,	0,	0,	0,	0,	0,	0,
168	0,	0,	0,	0,	0,	0,	0,	0,
169	0,	0,	0,	0,	0,	0,	0,	0,
170	0,	0,	0,	0,	0,	0,	0,	0,
171	0,	0,	0,	0,	0,	0,	0,	0,
172	0,	0,	0,	0,	0,	0,	0,	0,
173	0,	0,	0,	0,	0,	0,	0,	0,
174	0,	0,	0,	0,	0,	0,	0,	0,
175};
176
177#undef	a
178#undef	d
179#undef	s
180#undef	t
181#undef	q
182#undef	m
183#undef	c
184#undef	i
185#undef	h
186#undef	k
187#undef	n
188#undef	o
189#undef	r
190#undef	l
191#undef	v
192#undef	f
193
194#define	SIP_URI_UT(c)			sip_uri_table[(unsigned char)(c)]
195#define	SIP_URI_ISALPHA(c)		(SIP_URI_UT(c) & SIP_URI_ALPHA_BIT)
196#define	SIP_URI_ISDIGIT(c)		(SIP_URI_UT(c) & SIP_URI_DIGIT_BIT)
197#define	SIP_URI_ISALNUM(c)		(SIP_URI_UT(c) & SIP_URI_ALNUM_BITS)
198#define	SIP_URI_ISSCHEME(c)		\
199		(SIP_URI_UT(c) & (SIP_URI_ALNUM_BITS|SIP_URI_SCHEME_BIT))
200#define	SIP_URI_ISTOKEN(c)		\
201		(SIP_URI_UT(c) & (SIP_URI_ALNUM_BITS|SIP_URI_TOKEN_BIT))
202#define	SIP_URI_ISSIPDELIM(c)		\
203		(SIP_URI_UT(c) & (SIP_URI_SEMI_BIT|SIP_URI_QUEST_BIT))
204#define	SIP_URI_ISSIPHDELIM(c)					\
205	(SIP_URI_UT(c) & (SIP_URI_COLON_BIT|SIP_URI_SEMI_BIT|SIP_URI_QUEST_BIT))
206#define	SIP_URI_ISHOST(c)		\
207		(SIP_URI_UT(c) & (SIP_URI_ALNUM_BITS|SIP_URI_DASH_BIT))
208#define	SIP_URI_ISUSER(c)						\
209	(SIP_URI_UT(c) & (SIP_URI_OTHER_BIT|SIP_URI_SEMI_BIT|		\
210	SIP_URI_QUEST_BIT|SIP_URI_SLASH_BIT|SIP_URI_AND_BIT))
211
212#define	SIP_URI_ISABSHDELIM(c)					\
213	(SIP_URI_UT(c) & \
214	(SIP_URI_SLASH_BIT|SIP_URI_COLON_BIT|SIP_URI_QUEST_BIT))
215#define	SIP_URI_ISABSDELIM(c)	\
216	(SIP_URI_UT(c) & (SIP_URI_SLASH_BIT|SIP_URI_QUEST_BIT))
217#define	SIP_URI_ISUNRESERVED(c)	\
218	(SIP_URI_UT(c) & (SIP_URI_ALNUM_BITS|SIP_URI_MARK_BIT))
219#define	SIP_URI_ISPARAM(c)						\
220	(SIP_URI_UT(c) & (SIP_URI_PHCOMM_BIT|SIP_URI_AND_BIT|\
221	SIP_URI_ALNUM_BITS|SIP_URI_MARK_BIT))
222#define	SIP_URI_ISHEADER(c)						\
223	(SIP_URI_UT(c) & (SIP_URI_PHCOMM_BIT|SIP_URI_QUEST_BIT|\
224	SIP_URI_ALNUM_BITS|SIP_URI_MARK_BIT))
225#define	SIP_URI_ISOTHER(c)		(SIP_URI_UT(c) & SIP_URI_OTHER_BIT)
226#define	SIP_URI_ISRESERVED(c)					\
227	(SIP_URI_UT(c) & (SIP_URI_SEMI_BIT|SIP_URI_SLASH_BIT|	\
228	SIP_URI_QUEST_BIT| SIP_URI_COLON_BIT|SIP_URI_AT_BIT|	\
229	SIP_URI_AND_BIT|SIP_URI_OTHER_BIT))
230#define	SIP_URI_ISPCHAR(c)	\
231	(SIP_URI_UT(c) & (SIP_URI_COLON_BIT|SIP_URI_AT_BIT|	\
232	SIP_URI_AND_BIT|SIP_URI_OTHER_BIT))
233#define	SIP_URI_ISREGNAME(c)					\
234	(SIP_URI_UT(c) & 	\
235	(SIP_URI_OTHER_BIT|SIP_URI_SEMI_BIT|SIP_URI_COLON_BIT|	\
236	SIP_URI_AT_BIT|SIP_URI_AND_BIT))
237#define	SIP_URI_ISPHONEDIGIT(c)	\
238	(SIP_URI_UT(c) & (SIP_URI_DIGIT_BIT|SIP_URI_VISUALSEP_BIT))
239#define	SIP_URI_ISDTMFDIGIT(c)	(SIP_URI_UT(c) & SIP_URI_DTMFURI_DIGIT_BIT)
240
241static int  sip_uri_url_casecmp(const char *, const char *, unsigned);
242static void sip_uri_parse_params(_sip_uri_t *, char *, char *);
243static void sip_uri_parse_headers(_sip_uri_t *, char *, char *);
244static void sip_uri_parse_abs_opaque(_sip_uri_t *, char *, char *);
245static void sip_uri_parse_abs_query(_sip_uri_t *, char *, char *);
246static void sip_uri_parse_abs_path(_sip_uri_t *, char *, char *);
247static void sip_uri_parse_abs_regname(_sip_uri_t *, char *, char *);
248static int  sip_uri_parse_scheme(_sip_uri_t *, char *, char *);
249static void sip_uri_parse_password(_sip_uri_t *, char *, char *);
250static void sip_uri_parse_user(_sip_uri_t *, char *, char *);
251static void sip_uri_parse_port(_sip_uri_t *, char *, char *);
252static void sip_uri_parse_netpath(_sip_uri_t *, char **, char *, boolean_t);
253static int  sip_uri_parse_ipv6(char *, char *);
254static int  sip_uri_parse_ipv4(char *, char *);
255static int  sip_uri_parse_hostname(char *, char *);
256static int sip_uri_parse_tel(char *, char *);
257static int sip_uri_parse_tel_areaspe(char *, char *);
258static int sip_uri_parse_tel_servicepro(char *, char *);
259static int sip_uri_parse_tel_futureext(char *, char *);
260static int sip_uri_isTokenchar(char **, char *);
261static int sip_uri_isEscapedPound(char **, char *);
262static int sip_uri_hexVal(char *, char *);
263static int SIP_URI_HEXVAL(int);
264
265/*
266 * get the hex value of a char
267 */
268static int
269SIP_URI_HEXVAL(int c)
270{
271	if (c >= 0x30 && c <= 0x39)
272		return (c - '0');
273	if (c >= 0x41 && c <= 0x46)
274		return (c - 'A' + 10);
275	if (c >= 0x61 && c <= 0x66)
276		return (c - 'a' + 10);
277	return (c);
278}
279
280/*
281 * basic ASCII case-insensitive comparison
282 */
283static int
284sip_uri_url_casecmp(const char *str1, const char *str2, unsigned len)
285{
286	unsigned	j;
287
288	for (j = 0; j < len && tolower(str1[j]) == tolower(str2[j]) &&
289	    str1[j] != '\0'; ++j) {
290		;
291	}
292	return (j == len ? 0 : tolower(str2[j]) - tolower(str1[j]));
293}
294
295/*
296 * telephone-subscriber  = global-phone-number / local-phone-number
297 * Please refer to RFC 2806
298 */
299static int
300sip_uri_parse_tel(char *scan, char *uend)
301{
302	char	*mark = (char *)0;
303	int	ret = 0;
304	int	isGlobal = 0;
305	int	quote = 0;
306
307	if (scan == uend)
308		return (0);
309	if (*scan == '+') {
310		++scan;
311		isGlobal = 1;
312	}
313	mark = scan;
314	if (isGlobal) {
315		while (scan < uend && SIP_URI_ISPHONEDIGIT(*scan))
316			++scan;
317	} else {
318		while (scan < uend &&
319		    (SIP_URI_ISPHONEDIGIT(*scan) ||
320		    SIP_URI_ISDTMFDIGIT(*scan) ||
321		    sip_uri_isEscapedPound(&scan, uend) ||
322		    *scan == 'p' || *scan == 'w')) {
323			++scan;
324		}
325	}
326	if (mark == scan || (scan < uend && *scan != ';'))
327		return (0);
328
329	/*
330	 * parse isdn-subaddress
331	 */
332	if (uend - scan > 6 && !sip_uri_url_casecmp(scan, ";isub=", 6)) {
333		scan += 6;
334		mark = scan;
335		while (scan < uend && SIP_URI_ISPHONEDIGIT(*scan))
336			++scan;
337		if (mark == scan || (scan < uend && *scan != ';'))
338			return (0);
339	}
340
341	/*
342	 * parse post-dial
343	 */
344	if (uend - scan > 7 && !sip_uri_url_casecmp(scan, ";postd=", 7)) {
345		scan += 7;
346		mark = scan;
347		while (scan < uend &&
348		    (SIP_URI_ISPHONEDIGIT(*scan) ||
349		    SIP_URI_ISDTMFDIGIT(*scan) ||
350		    sip_uri_isEscapedPound(&scan, uend) ||
351		    *scan == 'p' || *scan == 'w')) {
352			++scan;
353		}
354		if (mark == scan || (scan < uend && *scan != ';'))
355			return (0);
356	}
357
358	if (!isGlobal) {
359		/*
360		 * parse area-specifier
361		 */
362		if (uend - scan > 15 &&
363		    !sip_uri_url_casecmp(scan, ";phone-context=", 15)) {
364			scan += 15;
365			mark = scan;
366			while (scan < uend && *scan != ';')
367				++scan;
368			ret = sip_uri_parse_tel_areaspe(mark, scan);
369		}
370	} else {
371		ret = 1;
372	}
373
374	/*
375	 * parse area-specifier, service-provider, future-extension
376	 */
377	while (scan < uend && ret) {
378		if (uend - scan > 15 &&
379			!sip_uri_url_casecmp(scan, ";phone-context=", 15)) {
380			scan += 15;
381			mark = scan;
382			while (scan < uend && *scan != ';')
383				++scan;
384			ret = sip_uri_parse_tel_areaspe(mark, scan);
385		} else if (uend - scan > 5 &&
386		    !sip_uri_url_casecmp(scan, ";tsp=", 5)) {
387			scan += 5;
388			mark = scan;
389			while (scan < uend && *scan != ';')
390				++scan;
391			ret = sip_uri_parse_tel_servicepro(mark, scan);
392		} else {
393			++scan;
394			mark = scan;
395			while (scan < uend && (*scan != ';' || quote)) {
396				if (sip_uri_hexVal(scan, uend) == 0x22) {
397					quote = !quote;
398					scan += 3;
399				} else {
400					++scan;
401				}
402			}
403			ret = sip_uri_parse_tel_futureext(mark, scan);
404		}
405	}
406	return (ret && scan == uend);
407}
408
409/*
410 * area-specifier        = ";" phone-context-tag "=" phone-context-ident
411 * phone-context-tag     = "phone-context"
412 * phone-context-ident   = network-prefix / private-prefix
413 * network-prefix        = global-network-prefix / local-network-prefix
414 * global-network-prefix = "+" 1*phonedigit
415 * local-network-prefix  = 1*(phonedigit / dtmf-digit / pause-character)
416 * private-prefix        = (%x21-22 / %x24-27 / %x2C / %x2F / %x3A /
417 *                          %x3C-40 / %x45-4F / %x51-56 / %x58-60 /
418 *                          %x65-6F / %x71-76 / %x78-7E)
419 *                          *(%x21-3A / %x3C-7E)
420 * phonedigit            = DIGIT / visual-separator
421 * visual-separator      = "-" / "." / "(" / ")"
422 * pause-character       = one-second-pause / wait-for-dial-tone
423 * one-second-pause      = "p"
424 * wait-for-dial-tone    = "w"
425 * dtmf-digit            = "*" / "#" / "A" / "B" / "C" / "D"
426 */
427static int
428sip_uri_parse_tel_areaspe(char *scan, char *uend)
429{
430	int	uri_hexValue;
431
432	if (scan == uend)
433		return (0);
434
435	/*
436	 * parse global-network-prefix
437	 */
438	if (*scan == '+') {
439		++scan;
440		if (scan == uend)
441			return (0);
442		while (scan < uend && SIP_URI_ISPHONEDIGIT(*scan))
443			++scan;
444	/*
445	 * parse local-network-prefix
446	 */
447	} else if (SIP_URI_ISPHONEDIGIT(*scan) || SIP_URI_ISDTMFDIGIT(*scan) ||
448	    sip_uri_isEscapedPound(&scan, uend) ||
449	    *scan == 'p' || *scan == 'w') {
450		++scan;
451		while (scan < uend &&
452		    (SIP_URI_ISPHONEDIGIT(*scan) ||
453		    SIP_URI_ISDTMFDIGIT(*scan) ||
454		    sip_uri_isEscapedPound(&scan, uend) ||
455		    *scan == 'p' || *scan == 'w')) {
456			++scan;
457		}
458	} else {
459	/*
460	 * parse private-prefix
461	 *
462	 * any characters allowed in RFC 2806 that are not allowed in
463	 * the user part of the SIP URI MUST be escaped
464	 *
465	 * private-prefix	= (! $ & ', / = ? _
466	 *			EFGHIJKLMNOQRSTUVXYZ efghijklmnoqrstuvxyz
467	 *			{ } | ~ [ ] \ ^  ` " % : < > @)
468	 *			*(%x21-3A / %x3C-7E)
469	 *
470	 * following characters are allowed in RFC 2806 and
471	 * the user part of SIP URI
472	 *  ! $ & ', / = ? _ EFGHIJKLMNOQRSTUVXYZ efghijklmnoqrstuvxyz
473	 */
474		if (*scan == '!' || *scan == '$' || *scan == '&' ||
475		    *scan == '\'' || *scan == ',' || *scan == '/' ||
476		    *scan == '=' || *scan == '?' || *scan == '_' ||
477		    (*scan >= 'E' && *scan <= 'Z' &&
478		    *scan != 'P' && *scan != 'W') ||
479		    (*scan >= 'e' && *scan <= 'z' &&
480		    *scan != 'p' && *scan != 'w')) {
481			++scan;
482		} else {
483			uri_hexValue = sip_uri_hexVal(scan, uend);
484			if (uri_hexValue == 0x21 || uri_hexValue == 0x22 ||
485			    (uri_hexValue >= 0x24 && uri_hexValue <= 0x27) ||
486			    uri_hexValue == 0x2c || uri_hexValue == 0x2f ||
487			    uri_hexValue == 0x3a ||
488			    (uri_hexValue >= 0x3c && uri_hexValue <= 0x40) ||
489			    (uri_hexValue >= 0x45 && uri_hexValue <= 0x4f) ||
490			    (uri_hexValue >= 0x51 && uri_hexValue <= 0x56) ||
491			    (uri_hexValue >= 0x58 && uri_hexValue <= 0x60) ||
492			    (uri_hexValue >= 0x65 && uri_hexValue <= 0x6f) ||
493			    (uri_hexValue >= 0x71 && uri_hexValue <= 0x76) ||
494			    (uri_hexValue >= 0x78 && uri_hexValue <= 0x7e)) {
495				scan += 3;
496			} else {
497				return (0);
498			}
499		}
500		/*
501		 * parse *(%x21-3A / %x3C-7E)
502		 */
503		while (scan < uend) {
504			if (SIP_URI_ISUNRESERVED(*scan) ||
505			    (SIP_URI_ISUSER(*scan) && *scan != ';')) {
506				++scan;
507			} else {
508				uri_hexValue = sip_uri_hexVal(scan, uend);
509				if (uri_hexValue >= 0x21 &&
510				    uri_hexValue <= 0x7e &&
511				    uri_hexValue != 0x3b) {
512					scan += 3;
513				} else {
514					return (0);
515				}
516			}
517		}
518	}
519	if (scan < uend)
520		return (0);
521	return (1);
522}
523
524static int
525sip_uri_hexVal(char *scan, char *uend)
526{
527	int	ret = -1;
528
529	if (SIP_URI_ISURLESCAPE(scan, uend)) {
530		ret = (SIP_URI_ISDIGIT(scan[1]) ? (scan[1] - '0') :
531		    (tolower(scan[1]) - 'a' + 10)) * 16 +
532		    (SIP_URI_ISDIGIT(scan[2]) ? (scan[2] - '0') :
533		    (tolower(scan[2]) - 'a' + 10));
534	}
535	return (ret);
536}
537
538/*
539 * service-provider  = ";" provider-tag "=" provider-hostname
540 * provider-tag      = "tsp"
541 * provider-hostname = domain
542 */
543static int
544sip_uri_parse_tel_servicepro(char *scan, char *uend)
545{
546	char	*mark = (char *)0;
547
548	if (scan == uend)
549		return (0);
550
551	/*
552	 * parse domain=" "
553	 */
554	if (sip_uri_hexVal(scan, uend) == 0x20 && scan + 3 == uend)
555		return (1);
556	while (scan < uend) {
557		mark = scan;
558		while (scan < uend && (*scan == '-'|| SIP_URI_ISALNUM(*scan)))
559			++scan;
560		if ((scan < uend && *scan != '.') ||
561		    !SIP_URI_ISALPHA(*mark) || !SIP_URI_ISALNUM(*(scan - 1))) {
562			return (0);
563		}
564		if (scan < uend)
565			++scan;
566	}
567
568	if (scan < uend)
569		return (0);
570	return (1);
571}
572
573/*
574 * future-extension = ";" 1*(token-char) ["=" ((1*(token-char)
575 *                    ["?" 1*(token-char)]) / quoted-string )]
576 * token-char       = (%x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39
577 *                     / %x41-5A / %x5E-7A / %x7C / %x7E)
578 */
579static int
580sip_uri_parse_tel_futureext(char *scan, char *uend)
581{
582	char	*mark;
583	int	uri_hexValue = 0;
584
585	if (scan == uend)
586		return (0);
587
588	/*
589	 * parse 1*(token-char)
590	 */
591	mark = scan;
592	while (scan < uend && sip_uri_isTokenchar(&scan, uend))
593		;
594	if (mark == scan ||
595	    (scan < uend && (*scan != '=' || scan + 1 == uend))) {
596		return (0);
597	}
598	if (scan == uend)
599		return (1);
600	++scan;
601
602	/*
603	 * parse 1*token-char ["?" 1*token-char]
604	 */
605	if (sip_uri_isTokenchar(&scan, uend)) {
606		while (sip_uri_isTokenchar(&scan, uend))
607			;
608		if (scan < uend) {
609			if (*scan != '?')
610				return (0);
611			++scan;
612			mark = scan;
613			while (sip_uri_isTokenchar(&scan, uend))
614				;
615			if (mark == scan)
616				return (0);
617		}
618	} else { /* parse quoted-string */
619		uri_hexValue = sip_uri_hexVal(scan, uend);
620		if (uri_hexValue != 0x22)
621			return (0);
622		scan += 3;
623		while (scan < uend && sip_uri_hexVal(scan, uend) != 0x22) {
624			/*
625			 * parse "\" CHAR
626			 */
627			if (sip_uri_hexVal(scan, uend) == 0x5c) {
628				scan += 3;
629				if (scan < uend) {
630					if (SIP_URI_ISUNRESERVED(*scan) ||
631					    SIP_URI_ISUSER(*scan)) {
632						++scan;
633					} else if (sip_uri_hexVal(scan, uend) >=
634					    0x00 &&
635					    sip_uri_hexVal(scan, uend) <=
636					    0x7f) {
637						scan += 3;
638					} else {
639						return (0);
640					}
641				} else {
642					return (0);
643				}
644			} else {
645				if (SIP_URI_ISUNRESERVED(*scan) ||
646				    SIP_URI_ISUSER(*scan)) {
647					++scan;
648				} else {
649					uri_hexValue =
650					    sip_uri_hexVal(scan, uend);
651					if ((uri_hexValue >= 0x20 &&
652						uri_hexValue <= 0x21) ||
653						(uri_hexValue >= 0x23 &&
654						uri_hexValue <= 0x7e) ||
655						(uri_hexValue >= 0x80 &&
656						uri_hexValue <= 0xff)) {
657						scan += 3;
658					} else {
659						return (0);
660					}
661				}
662			}
663		}
664		if (scan == uend ||
665		    (scan < uend && sip_uri_hexVal(scan, uend) != 0x22)) {
666			return (0);
667		}
668		scan += 3;
669	}
670
671	if (scan < uend)
672		return (0);
673	return (1);
674}
675
676/*
677 * Any characters allowed in RFC2806 tel URL that are not allowed in
678 * the user part of the SIP URI MUST be escaped.
679 * token-char = - _ . ! ~ * ' $ &  + DIGIT ALPHA #  % ^ ` |
680 */
681static int
682sip_uri_isTokenchar(char **pscan, char *uend)
683{
684	char	*scan = *pscan;
685	int	uri_hexValue = 0;
686
687	if (scan == uend)
688		return (0);
689
690	/*
691	 * for ALPAH DIGIT - _ . ! ~ * ' $ & +
692	 */
693	if ((SIP_URI_ISUNRESERVED(*scan) && *scan != '(' && *scan != ')') ||
694	    *scan == '$' || *scan == '&' || *scan == '+') {
695		++scan;
696		*pscan = scan;
697		return (1);
698	}
699
700	uri_hexValue = sip_uri_hexVal(scan, uend);
701	if (uri_hexValue == 0x21 || uri_hexValue == 0x7c ||
702	    uri_hexValue == 0x7e ||
703	    (uri_hexValue >= 0x23 && uri_hexValue <= 0x27) ||
704	    (uri_hexValue >= 0x2a && uri_hexValue <= 0x2b) ||
705	    (uri_hexValue >= 0x2d && uri_hexValue <= 0x2e) ||
706	    (uri_hexValue >= 0x30 && uri_hexValue <= 0x39) ||
707	    (uri_hexValue >= 0x41 && uri_hexValue <= 0x5a) ||
708	    (uri_hexValue >= 0x5e && uri_hexValue <= 0x7a)) {
709		scan += 3;
710		*pscan = scan;
711		return (1);
712	}
713	return (0);
714}
715
716/*
717 * '#' is not allowed in the telephone-subscriber part of SIP URI
718 * it must be escaped
719 */
720static int
721sip_uri_isEscapedPound(char **pscan, char *uend)
722{
723	char	*scan = *pscan;
724
725	if (scan == uend)
726		return (0);
727	if (*scan == '%' && scan + 2 < uend && scan[1] == '2' &&
728	    scan[2] == '3') {
729		scan += 2;
730		*pscan = scan;
731		return (1);
732	}
733	return (0);
734}
735
736/*
737 * scheme =  ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
738 */
739static int
740sip_uri_parse_scheme(_sip_uri_t *outurl, char *scan, char *uend)
741{
742	if (scan == uend) {
743		outurl->sip_uri_errflags |= SIP_URIERR_SCHEME;
744		return (0);
745	}
746	outurl->sip_uri_scheme.sip_str_ptr = scan;
747	outurl->sip_uri_scheme.sip_str_len = uend - scan;
748
749	if (scan < uend && SIP_URI_ISALPHA(*scan)) {
750		++scan;
751		while (scan < uend && SIP_URI_ISSCHEME(*scan))
752			++scan;
753	}
754	if (scan < uend)
755		outurl->sip_uri_errflags |= SIP_URIERR_SCHEME;
756	return (1);
757}
758
759/*
760 * The format of params is supposed to be;XXX;XXX;XXX
761 * uri-parameters	= *(";" uri-parameter)
762 * uri-parameter	= transport-param / user-param / method-param
763 * 			/ ttl-param / maddr-param / lr-param / other-param
764 * transport-param	=  "transport="
765 *			("udp" / "tcp" / "sctp" / "tls" / other-transport)
766 * other-transport		=  token
767 * user-param		=  "user=" ("phone" / "ip" / other-user)
768 * other-user		=  token
769 * method-param		=  "method=" Method
770 * ttl-param		=  "ttl=" ttl
771 * maddr-param		=  "maddr=" host
772 * lr-param		=  "lr"
773 * other-param		=  pname [ "=" pvalue ]
774 * pname		=  1*paramchar
775 * pvalue		=  1*paramchar
776 * paramchar		=  param-unreserved / unreserved / escaped
777 * param-unreserved	=  "[" / "]" / "/" / ":" / "&" / "+" / "$"
778 */
779static void
780sip_uri_parse_params(_sip_uri_t *outurl, char *scan, char *uend)
781{
782	char		*mark = (char *)0;
783	char		*equal = (char *)0;
784	int		i = 0;
785	int		ttl = 0;
786	int		paramleftlen = 0;
787	int		gothost = 0;
788	sip_param_t	*param = NULL;
789	sip_param_t	*new_param = NULL;
790
791	if (scan == uend || *scan != ';' || scan + 1 == uend) {
792		outurl->sip_uri_errflags |= SIP_URIERR_PARAM;
793		return;
794	}
795
796	while (scan < uend) {
797		mark = ++scan;
798		while (scan < uend && *scan != ';')
799			++scan;
800		if (scan == mark) {
801			outurl->sip_uri_errflags |= SIP_URIERR_PARAM;
802			return;
803		}
804
805		new_param = calloc(1, sizeof (sip_param_t));
806		if (new_param == NULL) {
807			outurl->sip_uri_errflags |= SIP_URIERR_MEMORY;
808			return;
809		}
810
811		if (param == NULL)
812			outurl->sip_uri_params = new_param;
813		else
814			param->param_next = new_param;
815
816		param = new_param;
817
818		param->param_name.sip_str_ptr = mark;
819		equal = memchr(mark, '=', scan - mark);
820		if (equal == (char *)0) {
821			param->param_name.sip_str_len = scan - mark;
822			param->param_value.sip_str_ptr = NULL;
823			param->param_value.sip_str_len = 0;
824			while (mark < scan && (SIP_URI_ISPARAM(*mark) ||
825			    SIP_URI_ISURLESCAPE(mark, scan))) {
826				++mark;
827			}
828		} else {
829			param->param_name.sip_str_len = equal - mark;
830			param->param_value.sip_str_ptr = equal + 1;
831			param->param_value.sip_str_len = scan - equal - 1;
832
833			if (mark == equal || equal + 1 == scan) {
834				outurl->sip_uri_errflags |= SIP_URIERR_PARAM;
835				return;
836			}
837			paramleftlen = equal - mark + 1;
838			if ((paramleftlen == 10 &&
839			    !sip_uri_url_casecmp(mark, "transport=", 10)) ||
840			    (paramleftlen == 5 &&
841			    !sip_uri_url_casecmp(mark, "user=", 5)) ||
842			    (paramleftlen == 7 &&
843			    !sip_uri_url_casecmp(mark, "method=", 7))) {
844				if (scan - equal == 1) {
845					outurl->sip_uri_errflags |=
846					    SIP_URIERR_PARAM;
847					return;
848				}
849				mark = equal + 1;
850				while (mark < scan && SIP_URI_ISTOKEN(*mark))
851					++mark;
852			} else if (paramleftlen == 4 &&
853			    !sip_uri_url_casecmp(mark, "ttl=", 4)) {
854				if (scan - equal == 1) {
855					outurl->sip_uri_errflags |=
856					    SIP_URIERR_PARAM;
857					return;
858				}
859				mark = equal;
860				for (i = 0; i < 3; ++i) {
861					++mark;
862					if (mark < scan &&
863					    SIP_URI_ISDIGIT(*mark)) {
864						ttl = ttl * 10 + (*mark - '0');
865					}
866					if (ttl > 255) {
867						outurl->sip_uri_errflags |=
868							SIP_URIERR_PARAM;
869						return;
870					}
871				}
872			} else if (paramleftlen == 6 &&
873			    !sip_uri_url_casecmp(mark, "maddr=", 6)) {
874				gothost = 0;
875				mark = equal + 1;
876				if (mark < scan && SIP_URI_ISDIGIT(*mark)) {
877					gothost = sip_uri_parse_ipv4(mark,
878					    scan);
879				}
880				/*
881				 * not valid syntax for a host or user name,
882				 * try IPv6 literal
883				 */
884				if (!gothost && mark < scan && *mark == '[') {
885					gothost = sip_uri_parse_ipv6(mark,
886					    scan);
887				}
888				/*
889				 * look for a valid host name:
890				 * *(domainlabel ".") toplabel ["."]
891				 */
892				if (!gothost && mark < scan) {
893					if (!(gothost =
894					    sip_uri_parse_hostname(mark,
895					    scan))) {
896						outurl->sip_uri_errflags |=
897							SIP_URIERR_PARAM;
898					}
899				}
900				if (gothost)
901					mark = scan;
902			} else if (paramleftlen == 3 &&
903			    !sip_uri_url_casecmp(mark, "lr=", 3)) {
904				outurl->sip_uri_errflags |= SIP_URIERR_PARAM;
905				return;
906			} else {
907				while (mark < scan && (SIP_URI_ISPARAM(*mark) ||
908				    SIP_URI_ISURLESCAPE(mark, scan) ||
909				    mark == equal)) {
910					++mark;
911				}
912			}
913		}
914		if (mark < scan) {
915			outurl->sip_uri_errflags |= SIP_URIERR_PARAM;
916			return;
917		}
918	}
919}
920
921/*
922 * The format of headers is supposed to be ?XXX&XXX&XXX
923 * headers         =  "?" header *("&" header
924 * header          =  hname "=" hvalue
925 * hname           =  1*(hnv-unreserved / unreserved / escaped
926 * hvalue          =  *(hnv-unreserved / unreserved / escaped
927 * hnv-unreserved  =  "[" / "]" / "/" / "?" / ":" / "+" / "$"
928 */
929static void
930sip_uri_parse_headers(_sip_uri_t *outurl, char *scan, char *uend)
931{
932	char	*mark = NULL;
933	char	*equal = NULL;
934
935	if (scan == uend || *scan != '?' || scan + 1 == uend) {
936		outurl->sip_uri_errflags |= SIP_URIERR_HEADER;
937		return;
938	}
939	outurl->sip_uri_headers.sip_str_ptr = scan + 1;
940	outurl->sip_uri_headers.sip_str_len = uend - (scan + 1);
941
942	while (scan < uend) {
943		mark = ++scan;
944		while (scan < uend && *scan != '&')
945			++scan;
946		if (scan == mark) {
947			outurl->sip_uri_errflags |= SIP_URIERR_HEADER;
948			return;
949		}
950		equal = memchr(mark, '=', scan - mark);
951		if (equal == mark || equal == (char *)0) {
952			outurl->sip_uri_errflags |= SIP_URIERR_HEADER;
953			return;
954		}
955		while (mark < scan &&
956		    (SIP_URI_ISHEADER(*mark) ||
957		    SIP_URI_ISURLESCAPE(mark, scan) || mark == equal)) {
958			++mark;
959		}
960		if (mark < scan) {
961			outurl->sip_uri_errflags |= SIP_URIERR_HEADER;
962			return;
963		}
964	}
965}
966
967/*
968 * opaque-part   =  uric-no-slash *uric
969 * uric          =  reserved / unreserved / escaped
970 * uric-no-slash =  unreserved / escaped / ";" / "?" / ":" / "@"
971 *                  / "&" / "=" / "+" / "$" / ","
972 */
973static void
974sip_uri_parse_abs_opaque(_sip_uri_t *outurl, char *scan, char *uend)
975{
976	if (scan == uend) {
977		outurl->sip_uri_errflags |= SIP_URIERR_OPAQUE;
978		return;
979	}
980	outurl->sip_uri_opaque.sip_str_ptr = scan;
981	outurl->sip_uri_opaque.sip_str_len = uend - scan;
982
983	if (SIP_URI_ISUNRESERVED(*scan) || SIP_URI_ISURLESCAPE(scan, uend) ||
984	    SIP_URI_ISOTHER(*scan) || *scan == ';' || *scan == '?' ||
985	    *scan == ':' || *scan == '@' || *scan == '&') {
986		++scan;
987	} else {
988		outurl->sip_uri_errflags |= SIP_URIERR_OPAQUE;
989		return;
990	}
991	while (scan < uend && (SIP_URI_ISRESERVED(*scan) ||
992	    SIP_URI_ISUNRESERVED(*scan) || SIP_URI_ISURLESCAPE(scan, uend))) {
993		++scan;
994	}
995	if (scan < uend)
996		outurl->sip_uri_errflags |= SIP_URIERR_OPAQUE;
997}
998
999/*
1000 * format of query is supposed to be ?XXX
1001 * query =  *uric
1002 * uric  =  reserved / unreserved / escaped
1003 */
1004static void
1005sip_uri_parse_abs_query(_sip_uri_t *outurl, char *scan, char *uend)
1006{
1007	if (uend == scan || *scan != '?' || scan + 1 == uend)
1008		return;
1009	++scan;
1010	outurl->sip_uri_query.sip_str_ptr = scan;
1011	outurl->sip_uri_query.sip_str_len = uend - scan;
1012
1013	while (scan < uend && (SIP_URI_ISRESERVED(*scan) ||
1014	    SIP_URI_ISUNRESERVED(*scan) || SIP_URI_ISURLESCAPE(scan, uend))) {
1015		++scan;
1016	}
1017	if (scan < uend)
1018		outurl->sip_uri_errflags |= SIP_URIERR_QUERY;
1019}
1020
1021/*
1022 * the format of path is supposed to be /XXX;XXX/XXX;
1023 * abs-path       =  "/" path-segments
1024 * path-segments  =  segment *( "/" segment )
1025 * segment        =  *pchar *( ";" param )
1026 * param          =  *pchar
1027 * pchar          =  unreserved / escaped /
1028 *                   ":" / "@" / "&" / "=" / "+" / "$" / ","
1029 */
1030static void
1031sip_uri_parse_abs_path(_sip_uri_t *outurl, char *scan, char *uend)
1032{
1033	if (scan == uend || *scan != '/')
1034		return;
1035	outurl->sip_uri_path.sip_str_ptr = scan;
1036	outurl->sip_uri_path.sip_str_len = uend - scan;
1037
1038	++scan;
1039	while (scan < uend && (SIP_URI_ISPCHAR(*scan) ||
1040	    SIP_URI_ISUNRESERVED(*scan) || SIP_URI_ISURLESCAPE(scan, uend) ||
1041	    *scan == '/' || *scan == ';')) {
1042		++scan;
1043	}
1044	if (scan < uend)
1045		outurl->sip_uri_errflags |= SIP_URIERR_PATH;
1046}
1047/*
1048 * reg-name =  1*( unreserved / escaped / "$" / "," / ";"
1049 *             / ":" / "@" / "&" / "=" / "+" )
1050 */
1051static void
1052sip_uri_parse_abs_regname(_sip_uri_t *outurl, char *scan, char *uend)
1053{
1054	if (scan == uend)
1055		return;
1056	outurl->sip_uri_regname.sip_str_ptr = scan;
1057	outurl->sip_uri_regname.sip_str_len = uend - scan;
1058
1059	while (scan < uend && (SIP_URI_ISUNRESERVED(*scan) ||
1060	    SIP_URI_ISURLESCAPE(scan, uend) || SIP_URI_ISREGNAME(*scan))) {
1061		++scan;
1062	}
1063	if (scan < uend)
1064		outurl->sip_uri_errflags |= SIP_URIERR_REGNAME;
1065}
1066
1067/*
1068 * The format of the password is supposed to be :XXX
1069 * password =  *( unreserved / escaped / "&" / "=" / "+" / "$" / "," )
1070 */
1071static void
1072sip_uri_parse_password(_sip_uri_t *outurl, char *scan, char *uend)
1073{
1074	if (scan == uend || *scan != ':' || scan + 1 == uend)
1075		return;
1076	++scan;
1077	outurl->sip_uri_password.sip_str_ptr = scan;
1078	outurl->sip_uri_password.sip_str_len = uend - scan;
1079
1080	while (scan < uend && (SIP_URI_ISUNRESERVED(*scan) ||
1081	    SIP_URI_ISURLESCAPE(scan, uend) || SIP_URI_ISOTHER(*scan) ||
1082	    *scan == '&')) {
1083		++scan;
1084	}
1085	if (scan < uend)
1086		outurl->sip_uri_errflags |= SIP_URIERR_PASS;
1087}
1088
1089/*
1090 * user =  1*( unreserved / escaped / user-unreserved )
1091 * user-unreserved  =  "&" / "=" / "+" / "$" / "," / ";" / "?" / "/"
1092 */
1093static void
1094sip_uri_parse_user(_sip_uri_t *outurl, char *scan, char *uend)
1095{
1096	if (scan == uend) {
1097		outurl->sip_uri_errflags |= SIP_URIERR_USER;
1098		return;
1099	}
1100	outurl->sip_uri_user.sip_str_ptr = scan;
1101	outurl->sip_uri_user.sip_str_len = uend - scan;
1102
1103	if (sip_uri_parse_tel(scan, uend)) {
1104		outurl->sip_uri_isteluser = B_TRUE;
1105	} else {
1106		while (scan < uend && (SIP_URI_ISUNRESERVED(*scan) ||
1107		    SIP_URI_ISURLESCAPE(scan, uend) || SIP_URI_ISUSER(*scan))) {
1108			++scan;
1109		}
1110		if (scan < uend)
1111			outurl->sip_uri_errflags |= SIP_URIERR_USER;
1112	}
1113}
1114
1115/*
1116 * the format of port is supposed to be :XXX
1117 * port =  1*DIGIT
1118 */
1119static void
1120sip_uri_parse_port(_sip_uri_t *outurl, char *scan, char *uend)
1121{
1122	if (scan == uend || *scan != ':' || scan + 1 == uend) {
1123		outurl->sip_uri_errflags |= SIP_URIERR_PORT;
1124		return;
1125	}
1126	++scan;
1127	/*
1128	 * parse numeric port number
1129	 */
1130	if (SIP_URI_ISDIGIT(*scan)) {
1131		outurl->sip_uri_port = *scan - '0';
1132		while (++scan < uend && SIP_URI_ISDIGIT(*scan)) {
1133		    outurl->sip_uri_port =
1134			outurl->sip_uri_port * 10 + (*scan - '0');
1135			if (outurl->sip_uri_port > 0xffff) {
1136				outurl->sip_uri_errflags |= SIP_URIERR_PORT;
1137				outurl->sip_uri_port = 0;
1138				break;
1139			}
1140		}
1141	}
1142	if (scan < uend) {
1143		outurl->sip_uri_errflags |= SIP_URIERR_PORT;
1144		outurl->sip_uri_port = 0;
1145	}
1146}
1147
1148/*
1149 * parse an IPv4 address
1150 *    1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
1151 *  advances pscan to end of IPv4 address, or after last "." that was
1152 *  a valid IPv4 or domain name.
1153 * returns 1 if ipv4 found, 0 otherwise
1154 */
1155static int
1156sip_uri_parse_ipv4(char *scan, char *uend)
1157{
1158	int	j = 0;
1159	int	val = 0;
1160
1161	for (j = 0; j < 4; ++j) {
1162		if (!SIP_URI_ISDIGIT(*scan))
1163			break;
1164		val = *scan - '0';
1165		while (++scan < uend && SIP_URI_ISDIGIT(*scan)) {
1166			val = val * 10 + (*scan - '0');
1167			if (val > 255)
1168				return (0);
1169		}
1170		if (j < 3) {
1171			if (*scan != '.')
1172				break;
1173			++scan;
1174		}
1175	}
1176
1177	if (j == 4 && scan == uend)
1178		return (1);
1179
1180	return (0);
1181}
1182
1183/*
1184 * parse an IPv6 address
1185 *  IPv6address = hexpart [ ":" IPv4address ]
1186 *  IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
1187 *  hexpart = hexseq | hexseq "::" [ hexseq ] | "::" [ hexseq ]
1188 *  hexseq  = hex4 *( ":" hex4)
1189 *  hex4    = 1*4HEXDIG
1190 *  if not found, leaves pscan unchanged, otherwise advances to end
1191 *  returns 1 if valid,
1192 *  0 if invalid
1193 */
1194static int
1195sip_uri_parse_ipv6(char *scan, char *uend)
1196{
1197	char		*mark;
1198	unsigned	j = 0;			/* index for addr */
1199	unsigned	val = 0;		/* hex value */
1200	int		zpad = 0;		/* index of :: delimiter */
1201
1202	if (*scan != '[')
1203		return (0);
1204	++scan;
1205	j = 0;
1206
1207	/*
1208	 * check for leading "::", set zpad to the position of the "::"
1209	 */
1210	if (scan + 1 < uend && scan[0] == ':' && scan[1] == ':') {
1211		zpad = 0;
1212		scan += 2;
1213	} else {
1214		zpad = -1;
1215	}
1216
1217	/*
1218	 * loop through up to 16 bytes of IPv6 address
1219	 */
1220	while (scan < uend && j < 15) {
1221		if (!SIP_URI_ISHEX(*scan))
1222			break;
1223		mark = scan;
1224		val = SIP_URI_HEXVAL(*scan);
1225		while (++scan < uend && SIP_URI_ISHEX(*scan)) {
1226			val = val * 16 + SIP_URI_HEXVAL(*scan);
1227			if (val > 0xffff)
1228				return (0);
1229		}
1230
1231		/*
1232		 * always require a delimiter or ]
1233		 */
1234		if (scan == uend)
1235			return (0);
1236
1237		if (*scan == '.' && (j == 12 || (zpad != -1 && j < 12)) &&
1238		    mark < uend && sip_uri_parse_ipv4(mark, uend - 1) &&
1239		    *(uend - 1) == ']') {
1240			mark = uend - 1;
1241			j += 4;
1242			scan = mark + 1;
1243			break;
1244		}
1245
1246		/*
1247		 * set address
1248		 */
1249		j += 2;
1250
1251		/*
1252		 * check for delimiter or ]
1253		 */
1254		if (*scan == ':') {
1255			/*
1256			 * found ":" delimiter, check for "::"
1257			 */
1258			if (++scan < uend && *scan == ':') {
1259				if (zpad != -1)
1260					return (0);
1261				zpad = j;
1262				if (++scan < uend && *scan == ']') {
1263					++scan;
1264					break;
1265				}
1266			}
1267		} else if (*scan == ']' && (j == 16 || zpad != -1)) {
1268			++scan;
1269			break;
1270		} else {
1271			/*
1272			 * not a valid delimiter
1273			 */
1274			return (0);
1275		}
1276	}
1277	if (zpad == -1 && j < 16)
1278		return (0);
1279	if (zpad != -1) {
1280		if (j > 15)
1281			return (0);
1282	}
1283
1284	if (scan == uend)
1285		return (1);
1286
1287	return (0);
1288}
1289
1290/*
1291 * hostname         =  *( domainlabel "." ) toplabel [ "." ]
1292 * domainlabel      =  alphanum / alphanum *( alphanum / "-" ) alphanum
1293 * toplabel         =  ALPHA / ALPHA *( alphanum / "-" ) alphanum
1294 */
1295static int
1296sip_uri_parse_hostname(char *scan, char *uend)
1297{
1298	int	sawalpha = 0;
1299
1300	if (scan < uend && SIP_URI_ISALNUM(*scan)) {
1301		do {
1302			sawalpha = SIP_URI_ISALPHA(*scan);
1303			while (SIP_URI_ISHOST(*scan))
1304				++scan;
1305			if (*scan != '.')
1306				break;
1307			++scan;
1308		} while (scan < uend && SIP_URI_ISALNUM(*scan));
1309	}
1310
1311	if (sawalpha && scan == uend)
1312		return (1);
1313	return (0);
1314}
1315
1316
1317/*
1318 * parse the network path portion of a full URL
1319 */
1320static void
1321sip_uri_parse_netpath(_sip_uri_t *outurl, char **pscan, char *uend,
1322    boolean_t issip)
1323{
1324	char	*mark = (char *)0;
1325	char	*mark2 = (char *)0;
1326	char	*scan = *pscan;
1327	int	gothost = 0;
1328
1329	/*
1330	 * look for the first high-level delimiter
1331	 */
1332	mark = scan;
1333	while (scan < uend && *scan != '@')
1334		++scan;
1335	/*
1336	 * handle userinfo section of URL
1337	 */
1338	if (scan < uend && *scan == '@') {
1339		/*
1340		 * parse user
1341		 */
1342		mark2 = mark;
1343		while (mark < scan && *mark != ':')
1344			++mark;
1345		sip_uri_parse_user(outurl, mark2, mark);
1346		/*
1347		 * parse password
1348		 */
1349		if (*mark == ':')
1350			sip_uri_parse_password(outurl, mark, scan);
1351		mark = ++scan;
1352	}
1353
1354	scan = mark;
1355	if (scan < uend && *scan == '[') {	/* look for an IPv6 address */
1356		while (scan < uend && *scan != ']')
1357			++scan;
1358		if (scan < uend) {
1359			++scan;
1360			if (sip_uri_parse_ipv6(mark, scan))
1361				gothost = 1;
1362		}
1363	} else {
1364		while (scan < uend && ((issip && !SIP_URI_ISSIPHDELIM(*scan)) ||
1365		    (!issip && !SIP_URI_ISABSHDELIM(*scan)))) {
1366			++scan;
1367		}
1368
1369		/*
1370		 * look for an IPv4 address
1371		 */
1372		if (mark < scan && SIP_URI_ISDIGIT(*mark) &&
1373		    sip_uri_parse_ipv4(mark, scan)) {
1374			gothost = 1;
1375		}
1376
1377		/*
1378		 * look for a valid host name
1379		 */
1380		if (!gothost && mark < scan &&
1381		    sip_uri_parse_hostname(mark, scan)) {
1382			gothost = 1;
1383		}
1384	}
1385	/*
1386	 * handle invalid host name
1387	 */
1388	if (!gothost)
1389		outurl->sip_uri_errflags |= SIP_URIERR_HOST;
1390	/*
1391	 * save host name
1392	 */
1393	outurl->sip_uri_host.sip_str_ptr = mark;
1394	outurl->sip_uri_host.sip_str_len = scan - mark;
1395
1396	mark = scan;
1397	/*
1398	 * parse the port number
1399	 */
1400	if (scan < uend && *scan == ':') {
1401		while (scan < uend && ((issip && !SIP_URI_ISSIPDELIM(*scan)) ||
1402		    (!issip && !SIP_URI_ISABSDELIM(*scan)))) {
1403			++scan;
1404		}
1405		sip_uri_parse_port(outurl, mark, scan);
1406	}
1407
1408	/*
1409	 * set return pointer
1410	 */
1411	*pscan = scan;
1412}
1413
1414/*
1415 * parse a URL
1416 * URL = SIP-URI / SIPS-URI / absoluteURI
1417 */
1418void
1419sip_uri_parse_it(_sip_uri_t *outurl, sip_str_t *uri_str)
1420{
1421	char 		*mark;
1422	char		*scan;
1423	char		*uend;
1424	char		*str = uri_str->sip_str_ptr;
1425	unsigned	urlen = uri_str->sip_str_len;
1426
1427	/*
1428	 * reset output parameters
1429	 */
1430	(void) memset(outurl, 0, sizeof (sip_uri_t));
1431
1432	/*
1433	 * strip enclosing angle brackets
1434	 */
1435	if (urlen > 1 && str[0] == '<' && str[urlen-1] == '>') {
1436		urlen -= 2;
1437		++str;
1438	}
1439	uend = str + urlen;
1440
1441	/*
1442	 * strip off space prefix and trailing spaces
1443	 */
1444	while (str < uend && isspace(*str)) {
1445		++str;
1446		--urlen;
1447	}
1448	while (str < uend && isspace(*(uend - 1))) {
1449		--uend;
1450		--urlen;
1451	}
1452
1453	/*
1454	 * strip off "URL:" prefix
1455	 */
1456	if (urlen > 4 && sip_uri_url_casecmp(str, "URL:", 4) == 0) {
1457		str += 4;
1458		urlen -= 4;
1459	}
1460
1461	/*
1462	 * parse the scheme name
1463	 */
1464	mark = scan = str;
1465	while (scan < uend && *scan != ':')
1466		++scan;
1467	if (scan == uend || !sip_uri_parse_scheme(outurl, mark, scan)) {
1468		outurl->sip_uri_errflags |= SIP_URIERR_SCHEME;
1469		return;
1470	}
1471
1472	if ((outurl->sip_uri_scheme.sip_str_len == SIP_SCHEME_LEN &&
1473	    !memcmp(outurl->sip_uri_scheme.sip_str_ptr, SIP_SCHEME,
1474	    SIP_SCHEME_LEN)) ||
1475	    (outurl->sip_uri_scheme.sip_str_len == SIPS_SCHEME_LEN &&
1476	    !memcmp(outurl->sip_uri_scheme.sip_str_ptr, SIPS_SCHEME,
1477	    SIPS_SCHEME_LEN))) {
1478		outurl->sip_uri_issip = B_TRUE;
1479	} else {
1480		outurl->sip_uri_issip = B_FALSE;
1481	}
1482	++scan; /* skip ':' */
1483
1484	if (outurl->sip_uri_issip) {
1485		/*
1486		 * parse SIP URL
1487		 */
1488		sip_uri_parse_netpath(outurl, &scan, uend, B_TRUE);
1489
1490		/*
1491		 * parse parameters
1492		 */
1493		if (scan < uend && *scan == ';') {
1494			mark = scan;
1495			while (scan < uend && *scan != '?')
1496				++scan;
1497			sip_uri_parse_params(outurl, mark, scan);
1498		}
1499
1500		/*
1501		 * parse headers
1502		 */
1503		if (scan < uend && *scan == '?')
1504			sip_uri_parse_headers(outurl, scan, uend);
1505	} else if (scan < uend && scan[0] == '/') {	 /* parse absoluteURL */
1506		++scan;
1507		/*
1508		 * parse authority
1509		 * authority	= srvr / reg-name
1510		 * srvr		= [ [ userinfo "@" ] hostport ]
1511		 * reg-name	= 1*(unreserved / escaped / "$" / ","
1512		 *			/ ";" / ":" / "@" / "&" / "=" / "+")
1513		 */
1514		if (scan < uend && *scan == '/') {
1515			++scan;
1516			mark = scan;
1517			/*
1518			 * take authority as srvr
1519			 */
1520			sip_uri_parse_netpath(outurl, &scan, uend, B_FALSE);
1521
1522			/*
1523			 * if srvr failed, take it as reg-name
1524			 * parse reg-name
1525			 */
1526			if (outurl->sip_uri_errflags & SIP_URIERR_USER ||
1527			    outurl->sip_uri_errflags & SIP_URIERR_PASS ||
1528			    outurl->sip_uri_errflags & SIP_URIERR_HOST ||
1529			    outurl->sip_uri_errflags & SIP_URIERR_PORT) {
1530				scan = mark;
1531				while (scan < uend && *scan != '/' &&
1532					*scan != '?') {
1533					++scan;
1534				}
1535				sip_uri_parse_abs_regname(outurl, mark, scan);
1536				if (!(outurl->sip_uri_errflags &
1537				    SIP_URIERR_REGNAME)) {
1538					/*
1539					 * remove error info of user,
1540					 * password, host, port
1541					 */
1542					outurl->sip_uri_user.sip_str_ptr = NULL;
1543					outurl->sip_uri_user.sip_str_len = 0;
1544					outurl->sip_uri_errflags &=
1545					    ~SIP_URIERR_USER;
1546					outurl->sip_uri_password.sip_str_ptr =
1547					    NULL;
1548					outurl->sip_uri_password.sip_str_len =
1549					    0;
1550					outurl->sip_uri_errflags &=
1551					    ~SIP_URIERR_PASS;
1552					outurl->sip_uri_host.sip_str_ptr = NULL;
1553					outurl->sip_uri_host.sip_str_len = 0;
1554					outurl->sip_uri_errflags &=
1555					    ~SIP_URIERR_HOST;
1556					outurl->sip_uri_port = 0;
1557					outurl->sip_uri_errflags &=
1558					    ~SIP_URIERR_PORT;
1559				}
1560			}
1561		} else {
1562			/*
1563			 * there is no net-path
1564			 */
1565			--scan;
1566		}
1567		/*
1568		 * parse abs-path
1569		 */
1570		if (scan < uend && *scan == '/') {
1571			mark = scan;
1572			while (scan < uend && *scan != '?')
1573				++scan;
1574			sip_uri_parse_abs_path(outurl, mark, scan);
1575		}
1576
1577		/*
1578		 * parse query
1579		 */
1580		if (scan < uend && *scan == '?')
1581			sip_uri_parse_abs_query(outurl, scan, uend);
1582	} else {
1583		/*
1584		 * parse opaque-part
1585		 */
1586		sip_uri_parse_abs_opaque(outurl, scan, uend);
1587	}
1588}
1589