/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "sip_parse_uri.h"
/*
* SIP-URI = "sip:" [ userinfo ] hostport uri-parameters [ headers ]
* SIPS-URI = "sips:" [ userinfo ] hostport uri-parameters [ headers ]
* userinfo = ( user / telephone-subscriber ) [ ":" password ] "@"
* user = 1*( unreserved / escaped / user-unreserved )
* user-unreserved = "&" / "=" / "+" / "$" / "," / ";" / "?" / "/"
* password = *( unreserved / escaped / "&" / "=" / "+" / "$" / "," )
* hostport = host [ ":" port ]
* host = hostname / IPv4address / IPv6reference
* hostname = *( domainlabel "." ) toplabel [ "." ]
* domainlabel = alphanum / alphanum *( alphanum / "-" ) alphanum
* toplabel = ALPHA / ALPHA *( alphanum / "-" ) alphanum
* IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
* IPv6reference = "[" IPv6address "]"
* IPv6address = hexpart [ ":" IPv4address ]
* hexpart = hexseq / hexseq "::" [ hexseq ] / "::" [ hexseq ]
* hexseq = hex4 *( ":" hex4)
* hex4 = 1*4HEXDIG
* port = 1*DIGIT
*
* The BNF for telephone-subscriber can be found in RFC 2806 [9]. Note,
* however, that any characters allowed there that are not allowed in
* the user part of the SIP URI MUST be escaped.
*
* uri-parameters = *( ";" uri-parameter)
* uri-parameter = transport-param / user-param / method-param
* / ttl-param / maddr-param / lr-param / other-param
* transport-param = "transport="( "udp" / "tcp" / "sctp" / "tls"
* / other-transport)
* other-transport = token
* user-param = "user=" ( "phone" / "ip" / other-user)
* other-user = token
* method-param = "method=" Method
* ttl-param = "ttl=" ttl
* maddr-param = "maddr=" host
* lr-param = "lr"
* other-param = pname [ "=" pvalue ]
* pname = 1*paramchar
* pvalue = 1*paramchar
* paramchar = param-unreserved / unreserved / escaped
* param-unreserved = "[" / "]" / "/" / ":" / "&" / "+" / "$"
* headers = "?" header *( "&" header )
* header = hname "=" hvalue
* hname = 1*( hnv-unreserved / unreserved / escaped )
* hvalue = *( hnv-unreserved / unreserved / escaped )
* hnv-unreserved = "[" / "]" / "/" / "?" / ":" / "+" / "$"
*
*/
#define SIP_URI_ISHEX(c) \
(((int)(c) >= 0x30 && (int)(c) <= 0x39) || \
((int)(c) >= 0x41 && (int)(c) <= 0x46) || \
((int)(c) >= 0x61 && (int)(c) <= 0x66))
/*
* URL character classes
* mark - _ . ! ~ * ' ()
* reserved ; / ? : @ & = + $ , also [] for IPv6
* unreserved alphanum mark
* pchar : @ & = + $ , unreserved
* userinfo ; : & = + $ , unreserved escaped
* relsegment ; @ & = + $ , unreserved escaped
* reg_name ; : @ & = + $ , unreserved escaped
* token - _ . ! ~ * ' % + `
* param-unreserved [ ] / : + $ &
* hnv-unreserved [ ] / : + $ ?
*/
#define a SIP_URI_ALPHA_BIT
#define d SIP_URI_DIGIT_BIT
#define s SIP_URI_SCHEME_BIT
#define t SIP_URI_TOKEN_BIT
#define q SIP_URI_QUEST_BIT
#define m SIP_URI_AT_BIT
#define c SIP_URI_COLON_BIT
#define i SIP_URI_SEMI_BIT
#define h SIP_URI_DASH_BIT
#define k SIP_URI_MARK_BIT
#define n SIP_URI_AND_BIT
#define o SIP_URI_PHCOMM_BIT
#define r SIP_URI_OTHER_BIT
#define l SIP_URI_SLASH_BIT
#define v SIP_URI_VISUALSEP_BIT
#define f SIP_URI_DTMFURI_DIGIT_BIT
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, t|k, 0, 0, o|r, t, n, t|k,
k|v, k|v, t|k|f, s|t|r|o, r, h|s|t|k|v, s|t|k|v, o|l,
d, d, d, d, d, d, d, d,
d, d, c|o, i, 0, r, 0, q,
m, a|f, a|f, a|f, a|f, a, a, a,
a, a, a, a, a, a, a, a,
a, a, a, a, a, a, a, a,
a, a, a, o, 0, o, 0, t|k,
t, a, a, a, a, a, a, a,
a, a, a, a, a, a, a, a,
a, a, a, a, a, a, a, a,
a, a, a, 0, 0, 0, t|k, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
};
#undef a
#undef d
#undef s
#undef t
#undef q
#undef m
#undef c
#undef i
#undef h
#undef k
#undef n
#undef o
#undef r
#undef l
#undef v
#undef f
#define SIP_URI_ISSCHEME(c) \
#define SIP_URI_ISTOKEN(c) \
#define SIP_URI_ISSIPDELIM(c) \
#define SIP_URI_ISSIPHDELIM(c) \
#define SIP_URI_ISHOST(c) \
#define SIP_URI_ISUSER(c) \
#define SIP_URI_ISABSHDELIM(c) \
(SIP_URI_UT(c) & \
#define SIP_URI_ISABSDELIM(c) \
#define SIP_URI_ISUNRESERVED(c) \
#define SIP_URI_ISPARAM(c) \
#define SIP_URI_ISHEADER(c) \
#define SIP_URI_ISRESERVED(c) \
#define SIP_URI_ISPCHAR(c) \
#define SIP_URI_ISREGNAME(c) \
(SIP_URI_UT(c) & \
#define SIP_URI_ISPHONEDIGIT(c) \
static int sip_uri_url_casecmp(const char *, const char *, unsigned);
static void sip_uri_parse_params(_sip_uri_t *, char *, char *);
static void sip_uri_parse_headers(_sip_uri_t *, char *, char *);
static void sip_uri_parse_abs_opaque(_sip_uri_t *, char *, char *);
static void sip_uri_parse_abs_query(_sip_uri_t *, char *, char *);
static void sip_uri_parse_abs_path(_sip_uri_t *, char *, char *);
static void sip_uri_parse_abs_regname(_sip_uri_t *, char *, char *);
static int sip_uri_parse_scheme(_sip_uri_t *, char *, char *);
static void sip_uri_parse_password(_sip_uri_t *, char *, char *);
static void sip_uri_parse_user(_sip_uri_t *, char *, char *);
static void sip_uri_parse_port(_sip_uri_t *, char *, char *);
static int sip_uri_parse_ipv6(char *, char *);
static int sip_uri_parse_ipv4(char *, char *);
static int sip_uri_parse_hostname(char *, char *);
static int sip_uri_parse_tel(char *, char *);
static int sip_uri_parse_tel_areaspe(char *, char *);
static int sip_uri_parse_tel_servicepro(char *, char *);
static int sip_uri_parse_tel_futureext(char *, char *);
static int sip_uri_isTokenchar(char **, char *);
static int sip_uri_isEscapedPound(char **, char *);
static int sip_uri_hexVal(char *, char *);
static int SIP_URI_HEXVAL(int);
/*
* get the hex value of a char
*/
static int
SIP_URI_HEXVAL(int c)
{
if (c >= 0x30 && c <= 0x39)
return (c - '0');
if (c >= 0x41 && c <= 0x46)
return (c - 'A' + 10);
if (c >= 0x61 && c <= 0x66)
return (c - 'a' + 10);
return (c);
}
/*
* basic ASCII case-insensitive comparison
*/
static int
{
unsigned j;
str1[j] != '\0'; ++j) {
;
}
}
/*
* telephone-subscriber = global-phone-number / local-phone-number
* Please refer to RFC 2806
*/
static int
{
char *mark = (char *)0;
int ret = 0;
int isGlobal = 0;
int quote = 0;
return (0);
if (*scan == '+') {
++scan;
isGlobal = 1;
}
if (isGlobal) {
++scan;
} else {
(SIP_URI_ISPHONEDIGIT(*scan) ||
SIP_URI_ISDTMFDIGIT(*scan) ||
++scan;
}
}
return (0);
/*
* parse isdn-subaddress
*/
scan += 6;
++scan;
return (0);
}
/*
* parse post-dial
*/
scan += 7;
(SIP_URI_ISPHONEDIGIT(*scan) ||
SIP_URI_ISDTMFDIGIT(*scan) ||
++scan;
}
return (0);
}
if (!isGlobal) {
/*
* parse area-specifier
*/
scan += 15;
++scan;
}
} else {
ret = 1;
}
/*
* parse area-specifier, service-provider, future-extension
*/
scan += 15;
++scan;
scan += 5;
++scan;
} else {
++scan;
scan += 3;
} else {
++scan;
}
}
}
}
}
/*
* area-specifier = ";" phone-context-tag "=" phone-context-ident
* phone-context-tag = "phone-context"
* phone-context-ident = network-prefix / private-prefix
* network-prefix = global-network-prefix / local-network-prefix
* global-network-prefix = "+" 1*phonedigit
* local-network-prefix = 1*(phonedigit / dtmf-digit / pause-character)
* private-prefix = (%x21-22 / %x24-27 / %x2C / %x2F / %x3A /
* %x3C-40 / %x45-4F / %x51-56 / %x58-60 /
* %x65-6F / %x71-76 / %x78-7E)
* *(%x21-3A / %x3C-7E)
* phonedigit = DIGIT / visual-separator
* visual-separator = "-" / "." / "(" / ")"
* pause-character = one-second-pause / wait-for-dial-tone
* one-second-pause = "p"
* wait-for-dial-tone = "w"
* dtmf-digit = "*" / "#" / "A" / "B" / "C" / "D"
*/
static int
{
int uri_hexValue;
return (0);
/*
* parse global-network-prefix
*/
if (*scan == '+') {
++scan;
return (0);
++scan;
/*
* parse local-network-prefix
*/
++scan;
(SIP_URI_ISPHONEDIGIT(*scan) ||
SIP_URI_ISDTMFDIGIT(*scan) ||
++scan;
}
} else {
/*
* parse private-prefix
*
* any characters allowed in RFC 2806 that are not allowed in
* the user part of the SIP URI MUST be escaped
*
* private-prefix = (! $ & ', / = ? _
* EFGHIJKLMNOQRSTUVXYZ efghijklmnoqrstuvxyz
* { } | ~ [ ] \ ^ ` " % : < > @)
* *(%x21-3A / %x3C-7E)
*
* following characters are allowed in RFC 2806 and
* the user part of SIP URI
* ! $ & ', / = ? _ EFGHIJKLMNOQRSTUVXYZ efghijklmnoqrstuvxyz
*/
++scan;
} else {
uri_hexValue == 0x3a ||
scan += 3;
} else {
return (0);
}
}
/*
* parse *(%x21-3A / %x3C-7E)
*/
if (SIP_URI_ISUNRESERVED(*scan) ||
++scan;
} else {
if (uri_hexValue >= 0x21 &&
uri_hexValue <= 0x7e &&
uri_hexValue != 0x3b) {
scan += 3;
} else {
return (0);
}
}
}
}
return (0);
return (1);
}
static int
{
}
return (ret);
}
/*
* service-provider = ";" provider-tag "=" provider-hostname
* provider-tag = "tsp"
* provider-hostname = domain
*/
static int
{
char *mark = (char *)0;
return (0);
/*
* parse domain=" "
*/
return (1);
++scan;
return (0);
}
++scan;
}
return (0);
return (1);
}
/*
* future-extension = ";" 1*(token-char) ["=" ((1*(token-char)
* ["?" 1*(token-char)]) / quoted-string )]
* token-char = (%x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39
* / %x41-5A / %x5E-7A / %x7C / %x7E)
*/
static int
{
char *mark;
int uri_hexValue = 0;
return (0);
/*
* parse 1*(token-char)
*/
;
return (0);
}
return (1);
++scan;
/*
* parse 1*token-char ["?" 1*token-char]
*/
;
if (*scan != '?')
return (0);
++scan;
;
return (0);
}
} else { /* parse quoted-string */
if (uri_hexValue != 0x22)
return (0);
scan += 3;
/*
* parse "\" CHAR
*/
scan += 3;
if (SIP_URI_ISUNRESERVED(*scan) ||
SIP_URI_ISUSER(*scan)) {
++scan;
0x00 &&
0x7f) {
scan += 3;
} else {
return (0);
}
} else {
return (0);
}
} else {
if (SIP_URI_ISUNRESERVED(*scan) ||
SIP_URI_ISUSER(*scan)) {
++scan;
} else {
if ((uri_hexValue >= 0x20 &&
uri_hexValue <= 0x21) ||
(uri_hexValue >= 0x23 &&
uri_hexValue <= 0x7e) ||
(uri_hexValue >= 0x80 &&
uri_hexValue <= 0xff)) {
scan += 3;
} else {
return (0);
}
}
}
}
return (0);
}
scan += 3;
}
return (0);
return (1);
}
/*
* Any characters allowed in RFC2806 tel URL that are not allowed in
* the user part of the SIP URI MUST be escaped.
* token-char = - _ . ! ~ * ' $ & + DIGIT ALPHA # % ^ ` |
*/
static int
{
int uri_hexValue = 0;
return (0);
/*
* for ALPAH DIGIT - _ . ! ~ * ' $ & +
*/
++scan;
return (1);
}
uri_hexValue == 0x7e ||
scan += 3;
return (1);
}
return (0);
}
/*
* '#' is not allowed in the telephone-subscriber part of SIP URI
* it must be escaped
*/
static int
{
return (0);
scan += 2;
return (1);
}
return (0);
}
/*
* scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
*/
static int
{
return (0);
}
++scan;
++scan;
}
return (1);
}
/*
* The format of params is supposed to be;XXX;XXX;XXX
* uri-parameters = *(";" uri-parameter)
* uri-parameter = transport-param / user-param / method-param
* / ttl-param / maddr-param / lr-param / other-param
* transport-param = "transport="
* ("udp" / "tcp" / "sctp" / "tls" / other-transport)
* other-transport = token
* user-param = "user=" ("phone" / "ip" / other-user)
* other-user = token
* method-param = "method=" Method
* ttl-param = "ttl=" ttl
* maddr-param = "maddr=" host
* lr-param = "lr"
* other-param = pname [ "=" pvalue ]
* pname = 1*paramchar
* pvalue = 1*paramchar
* paramchar = param-unreserved / unreserved / escaped
* param-unreserved = "[" / "]" / "/" / ":" / "&" / "+" / "$"
*/
static void
{
char *mark = (char *)0;
char *equal = (char *)0;
int i = 0;
int ttl = 0;
int paramleftlen = 0;
int gothost = 0;
return;
}
++scan;
return;
}
return;
}
else
if (equal == (char *)0) {
++mark;
}
} else {
return;
}
if ((paramleftlen == 10 &&
(paramleftlen == 5 &&
(paramleftlen == 7 &&
return;
}
++mark;
} else if (paramleftlen == 4 &&
return;
}
for (i = 0; i < 3; ++i) {
++mark;
SIP_URI_ISDIGIT(*mark)) {
}
if (ttl > 255) {
return;
}
}
} else if (paramleftlen == 6 &&
gothost = 0;
scan);
}
/*
* not valid syntax for a host or user name,
* try IPv6 literal
*/
scan);
}
/*
* look for a valid host name:
* *(domainlabel ".") toplabel ["."]
*/
if (!(gothost =
scan))) {
}
}
if (gothost)
} else if (paramleftlen == 3 &&
return;
} else {
++mark;
}
}
}
return;
}
}
}
/*
* The format of headers is supposed to be ?XXX&XXX&XXX
* headers = "?" header *("&" header
* header = hname "=" hvalue
* hname = 1*(hnv-unreserved / unreserved / escaped
* hvalue = *(hnv-unreserved / unreserved / escaped
* hnv-unreserved = "[" / "]" / "/" / "?" / ":" / "+" / "$"
*/
static void
{
return;
}
++scan;
return;
}
return;
}
(SIP_URI_ISHEADER(*mark) ||
++mark;
}
return;
}
}
}
/*
* opaque-part = uric-no-slash *uric
* uric = reserved / unreserved / escaped
* uric-no-slash = unreserved / escaped / ";" / "?" / ":" / "@"
* / "&" / "=" / "+" / "$" / ","
*/
static void
{
return;
}
++scan;
} else {
return;
}
++scan;
}
}
/*
* format of query is supposed to be ?XXX
* query = *uric
* uric = reserved / unreserved / escaped
*/
static void
{
return;
++scan;
++scan;
}
}
/*
* abs-path = "/" path-segments
* path-segments = segment *( "/" segment )
* segment = *pchar *( ";" param )
* param = *pchar
* pchar = unreserved / escaped /
* ":" / "@" / "&" / "=" / "+" / "$" / ","
*/
static void
{
return;
++scan;
++scan;
}
}
/*
* reg-name = 1*( unreserved / escaped / "$" / "," / ";"
* / ":" / "@" / "&" / "=" / "+" )
*/
static void
{
return;
++scan;
}
}
/*
* The format of the password is supposed to be :XXX
* password = *( unreserved / escaped / "&" / "=" / "+" / "$" / "," )
*/
static void
{
return;
++scan;
*scan == '&')) {
++scan;
}
}
/*
* user = 1*( unreserved / escaped / user-unreserved )
* user-unreserved = "&" / "=" / "+" / "$" / "," / ";" / "?" / "/"
*/
static void
{
return;
}
} else {
++scan;
}
}
}
/*
* the format of port is supposed to be :XXX
* port = 1*DIGIT
*/
static void
{
return;
}
++scan;
/*
* parse numeric port number
*/
if (SIP_URI_ISDIGIT(*scan)) {
outurl->sip_uri_port = 0;
break;
}
}
}
outurl->sip_uri_port = 0;
}
}
/*
* parse an IPv4 address
* 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
* advances pscan to end of IPv4 address, or after last "." that was
* a valid IPv4 or domain name.
* returns 1 if ipv4 found, 0 otherwise
*/
static int
{
int j = 0;
int val = 0;
for (j = 0; j < 4; ++j) {
if (!SIP_URI_ISDIGIT(*scan))
break;
if (val > 255)
return (0);
}
if (j < 3) {
if (*scan != '.')
break;
++scan;
}
}
return (1);
return (0);
}
/*
* parse an IPv6 address
* IPv6address = hexpart [ ":" IPv4address ]
* IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
* hexpart = hexseq | hexseq "::" [ hexseq ] | "::" [ hexseq ]
* hexseq = hex4 *( ":" hex4)
* hex4 = 1*4HEXDIG
* if not found, leaves pscan unchanged, otherwise advances to end
* returns 1 if valid,
* 0 if invalid
*/
static int
{
char *mark;
unsigned j = 0; /* index for addr */
if (*scan != '[')
return (0);
++scan;
j = 0;
/*
* check for leading "::", set zpad to the position of the "::"
*/
zpad = 0;
scan += 2;
} else {
zpad = -1;
}
/*
* loop through up to 16 bytes of IPv6 address
*/
if (!SIP_URI_ISHEX(*scan))
break;
if (val > 0xffff)
return (0);
}
/*
* always require a delimiter or ]
*/
return (0);
j += 4;
break;
}
/*
* set address
*/
j += 2;
/*
* check for delimiter or ]
*/
if (*scan == ':') {
/*
* found ":" delimiter, check for "::"
*/
if (zpad != -1)
return (0);
zpad = j;
++scan;
break;
}
}
++scan;
break;
} else {
/*
* not a valid delimiter
*/
return (0);
}
}
return (0);
if (zpad != -1) {
if (j > 15)
return (0);
}
return (1);
return (0);
}
/*
* hostname = *( domainlabel "." ) toplabel [ "." ]
* domainlabel = alphanum / alphanum *( alphanum / "-" ) alphanum
* toplabel = ALPHA / ALPHA *( alphanum / "-" ) alphanum
*/
static int
{
int sawalpha = 0;
do {
while (SIP_URI_ISHOST(*scan))
++scan;
if (*scan != '.')
break;
++scan;
}
return (1);
return (0);
}
/*
* parse the network path portion of a full URL
*/
static void
{
char *mark = (char *)0;
char *mark2 = (char *)0;
int gothost = 0;
/*
* look for the first high-level delimiter
*/
++scan;
/*
* handle userinfo section of URL
*/
/*
* parse user
*/
++mark;
/*
* parse password
*/
if (*mark == ':')
}
++scan;
++scan;
gothost = 1;
}
} else {
++scan;
}
/*
* look for an IPv4 address
*/
gothost = 1;
}
/*
* look for a valid host name
*/
gothost = 1;
}
}
/*
* handle invalid host name
*/
if (!gothost)
/*
* save host name
*/
/*
* parse the port number
*/
++scan;
}
}
/*
* set return pointer
*/
}
/*
* parse a URL
* URL = SIP-URI / SIPS-URI / absoluteURI
*/
void
{
char *mark;
char *scan;
char *uend;
/*
* reset output parameters
*/
/*
* strip enclosing angle brackets
*/
urlen -= 2;
++str;
}
/*
* strip off space prefix and trailing spaces
*/
++str;
--urlen;
}
--uend;
--urlen;
}
/*
* strip off "URL:" prefix
*/
str += 4;
urlen -= 4;
}
/*
* parse the scheme name
*/
++scan;
return;
}
SIP_SCHEME_LEN)) ||
SIPS_SCHEME_LEN))) {
} else {
}
++scan; /* skip ':' */
if (outurl->sip_uri_issip) {
/*
* parse SIP URL
*/
/*
* parse parameters
*/
++scan;
}
/*
* parse headers
*/
++scan;
/*
* parse authority
* authority = srvr / reg-name
* srvr = [ [ userinfo "@" ] hostport ]
* reg-name = 1*(unreserved / escaped / "$" / ","
* / ";" / ":" / "@" / "&" / "=" / "+")
*/
++scan;
/*
* take authority as srvr
*/
/*
* if srvr failed, take it as reg-name
* parse reg-name
*/
*scan != '?') {
++scan;
}
if (!(outurl->sip_uri_errflags &
/*
* remove error info of user,
* password, host, port
*/
NULL;
0;
outurl->sip_uri_port = 0;
}
}
} else {
/*
* there is no net-path
*/
--scan;
}
/*
* parse abs-path
*/
++scan;
}
/*
* parse query
*/
} else {
/*
* parse opaque-part
*/
}
}