xref: /illumos-gate/usr/src/lib/libsip/common/sip_parse_uri.c (revision 40cb5e5daa7b80bb70fcf8dadfb20f9281566331)
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 2006 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 <stdlib.h>
29 #include <string.h>
30 #include <ctype.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 
142 static 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 
241 static int  sip_uri_url_casecmp(const char *, const char *, unsigned);
242 static void sip_uri_parse_params(_sip_uri_t *, char *, char *);
243 static void sip_uri_parse_headers(_sip_uri_t *, char *, char *);
244 static void sip_uri_parse_abs_opaque(_sip_uri_t *, char *, char *);
245 static void sip_uri_parse_abs_query(_sip_uri_t *, char *, char *);
246 static void sip_uri_parse_abs_path(_sip_uri_t *, char *, char *);
247 static void sip_uri_parse_abs_regname(_sip_uri_t *, char *, char *);
248 static int  sip_uri_parse_scheme(_sip_uri_t *, char *, char *);
249 static void sip_uri_parse_password(_sip_uri_t *, char *, char *);
250 static void sip_uri_parse_user(_sip_uri_t *, char *, char *);
251 static void sip_uri_parse_port(_sip_uri_t *, char *, char *);
252 static void sip_uri_parse_netpath(_sip_uri_t *, char **, char *, boolean_t);
253 static int  sip_uri_parse_ipv6(char *, char *);
254 static int  sip_uri_parse_ipv4(char *, char *);
255 static int  sip_uri_parse_hostname(char *, char *);
256 static int sip_uri_parse_tel(char *, char *);
257 static int sip_uri_parse_tel_areaspe(char *, char *);
258 static int sip_uri_parse_tel_servicepro(char *, char *);
259 static int sip_uri_parse_tel_futureext(char *, char *);
260 static int sip_uri_isTokenchar(char **, char *);
261 static int sip_uri_isEscapedPound(char **, char *);
262 static int sip_uri_hexVal(char *, char *);
263 static int SIP_URI_HEXVAL(int);
264 
265 /*
266  * get the hex value of a char
267  */
268 static int
269 SIP_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  */
283 static int
284 sip_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  */
299 static int
300 sip_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  */
427 static int
428 sip_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 
524 static int
525 sip_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  */
543 static int
544 sip_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  */
579 static int
580 sip_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  */
681 static int
682 sip_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  */
720 static int
721 sip_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  */
739 static int
740 sip_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  */
779 static void
780 sip_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  */
929 static void
930 sip_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  */
973 static void
974 sip_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  */
1004 static void
1005 sip_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  */
1030 static void
1031 sip_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  */
1051 static void
1052 sip_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  */
1071 static void
1072 sip_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  */
1093 static void
1094 sip_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  */
1119 static void
1120 sip_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  */
1155 static int
1156 sip_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  */
1194 static int
1195 sip_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  */
1295 static int
1296 sip_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  */
1320 static void
1321 sip_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  */
1418 void
1419 sip_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