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