uri-util.c revision af53c056cf1e3d133a78c201e72a678b5431d0fb
2e37d45867d081db150ab78dad303b9077aea24fTimo Sirainen/* Copyright (c) 2010-2016 Dovecot authors, see the included COPYING file */
37847ec8eaec9ad55c9df10ae109efe7b37ac573Timo Sirainen/* [URI-GEN] RFC3986 Appendix A:
37847ec8eaec9ad55c9df10ae109efe7b37ac573Timo Sirainen URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
37847ec8eaec9ad55c9df10ae109efe7b37ac573Timo Sirainen absolute-URI = scheme ":" hier-part [ "?" query ]
37847ec8eaec9ad55c9df10ae109efe7b37ac573Timo Sirainen scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
94d8e51119003d2bc5a100c663f90141f297385dTimo Sirainen URI-reference = URI / relative-ref
ef50336eefcb9ba99f73c6af37420eaf8857a39bTimo Sirainen relative-ref = relative-part [ "?" query ] [ "#" fragment ]
37847ec8eaec9ad55c9df10ae109efe7b37ac573Timo Sirainen relative-part = "//" authority path-abempty
ef50336eefcb9ba99f73c6af37420eaf8857a39bTimo Sirainen / path-absolute
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen / path-noscheme
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen hier-part = "//" authority path-abempty
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen / path-absolute
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen / path-rootless
5694eeb99b69dea8033ca77ad69743c6b4871370Timo Sirainen authority = [ userinfo "@" ] host [ ":" port ]
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen host = IP-literal / IPv4address / reg-name
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen port = *DIGIT
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen IP-literal = "[" ( IPv6address / IPvFuture ) "]"
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen IPv6address = 6( h16 ":" ) ls32
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen / "::" 5( h16 ":" ) ls32
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen / [ h16 ] "::" 4( h16 ":" ) ls32
de486b59018016977015ef42e6071155b60e82e1Timo Sirainen / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
de486b59018016977015ef42e6071155b60e82e1Timo Sirainen / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
de486b59018016977015ef42e6071155b60e82e1Timo Sirainen / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
de486b59018016977015ef42e6071155b60e82e1Timo Sirainen / [ *4( h16 ":" ) h16 ] "::" ls32
de486b59018016977015ef42e6071155b60e82e1Timo Sirainen / [ *5( h16 ":" ) h16 ] "::" h16
de4d9f77d6378a416c9963963eac5ac18b75ec0bTimo Sirainen / [ *6( h16 ":" ) h16 ] "::"
de4d9f77d6378a416c9963963eac5ac18b75ec0bTimo Sirainen h16 = 1*4HEXDIG
de486b59018016977015ef42e6071155b60e82e1Timo Sirainen ls32 = ( h16 ":" h16 ) / IPv4address
de486b59018016977015ef42e6071155b60e82e1Timo Sirainen IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen dec-octet = DIGIT ; 0-9
220e21750948941dc6e33b8f11b552fa21d7f81eTimo Sirainen / %x31-39 DIGIT ; 10-99
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen / "1" 2DIGIT ; 100-199
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen / "2" %x30-34 DIGIT ; 200-249
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen / "25" %x30-35 ; 250-255
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen reg-name = *( unreserved / pct-encoded / sub-delims )
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen path = path-abempty ; begins with "/" or is empty
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen / path-absolute ; begins with "/" but not "//"
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen / path-noscheme ; begins with a non-colon segment
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen / path-rootless ; begins with a segment
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen / path-empty ; zero characters
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen path-abempty = *( "/" segment )
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen path-absolute = "/" [ segment-nz *( "/" segment ) ]
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen path-noscheme = segment-nz-nc *( "/" segment )
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen path-rootless = segment-nz *( "/" segment )
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen path-empty = 0<pchar>
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen segment = *pchar
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen segment-nz = 1*pchar
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen ; non-zero-length segment without any colon ":"
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen query = *( pchar / "/" / "?" )
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen fragment = *( pchar / "/" / "?" )
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen pct-encoded = "%" HEXDIG HEXDIG
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen reserved = gen-delims / sub-delims
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen / "*" / "+" / "," / ";" / "="
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen/* Character lookup table
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" [bit0]
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen * / "*" / "+" / "," / ";" / "=" [bit1]
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen * gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" [bit2]
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen * pchar = unreserved / sub-delims / ":" / "@" [bit0|bit1|bit3]
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen * 'pfchar' = unreserved / sub-delims / ":" / "@" / "/"
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen * [bit0|bit1|bit3|bit5]
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen * 'uchar' = unreserved / sub-delims / ":" [bit0|bit1|bit4]
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen * 'qchar' = pchar / "/" / "?" [bit0|bit1|bit3|bit5|bit6]
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen#define CHAR_MASK_PFCHAR ((1<<0)|(1<<1)|(1<<3)|(1<<5))
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen#define CHAR_MASK_QCHAR ((1<<0)|(1<<1)|(1<<3)|(1<<5)|(1<<6))
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainenstatic unsigned const char _uri_char_lookup[256] = {
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen 0, 2, 0, 4, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 1, 36, // 20
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 28, 2, 0, 2, 0, 68, // 30
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen 12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0, 4, 0, 1, // 50
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, // 70
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenstatic inline int _decode_hex_digit(const unsigned char digit)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen case '0': case '1': case '2': case '3': case '4':
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen case '5': case '6': case '7': case '8': case '9':
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainenuri_parse_pct_encoded_data(struct uri_parser *parser,
6600c05e2ab38e9f662582b63c56b0c980a03748Timo Sirainen const unsigned char **p, const unsigned char *pend,
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen if (**p != '%' || (pend != NULL && *p >= pend))
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen if (**p == 0 || *(*p+1) == 0 || (pend != NULL && *p+1 >= pend)) {
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen parser->error = "Unexpected URI boundary after '%'";
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen "Expecting hex digit after '%%', but found '%c'", **p);
2a6dcd984104fed84bed8795ccdfabb20e41ce52Timo Sirainen "Expecting hex digit after '%%%c', but found '%c'", *((*p)-1), **p);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen "Percent encoding is not allowed to encode NUL character";
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenint uri_parse_pct_encoded(struct uri_parser *parser,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen unsigned char *ch_r)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenuri_parse_unreserved_char(struct uri_parser *parser, unsigned char *ch_r)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if ((_uri_char_lookup[*parser->cur] & CHAR_MASK_UNRESERVED) != 0) {
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volkint uri_parse_unreserved(struct uri_parser *parser, string_t *part)
db8b0a3f74a20528d66a3c4be7df920e5c4554c2Timo Sirainen unsigned char ch = 0;
5666a3d6a7ea89362b8d9e8b39b15424cd9d6388Timo Sirainen if ((ret = uri_parse_unreserved_char(parser, &ch)) < 0)
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainenint uri_parse_unreserved_pct(struct uri_parser *parser, string_t *part)
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen unsigned char ch = 0;
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0)
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen else if (ret == 0 &&
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen (ret=uri_parse_unreserved_char(parser, &ch)) < 0)
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainenbool uri_data_decode(struct uri_parser *parser, const char *data,
5c597df6aa8d81de4053c6986fab7739f3b44b20Timo Sirainen const unsigned char *p = (const unsigned char *)data;
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen const unsigned char *pend = (const unsigned char *)until;
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen /* NULL means unlimited; solely rely on '\0' */
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen unsigned char ch;
c58c12049c883b281c088d47a2a7278c21c390e1Timo Sirainen *decoded_r = p_strdup(parser->pool, str_c(decoded));
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainenint uri_parse_scheme(struct uri_parser *parser, const char **scheme_r)
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen * scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen if (parser->cur >= parser->end || !i_isalpha(*parser->cur))
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen if (parser->cur >= parser->end || *parser->cur != ':') {
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen *scheme_r = t_strndup(first, parser->cur - first);
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainenint uri_cut_scheme(const char **uri_p, const char **scheme_r)
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainenuri_parse_dec_octet(struct uri_parser *parser, string_t *literal,
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen unsigned int octet = 0;
1433bf361ddb0bba8878c8ada5726d0284edad57Timo Sirainen * dec-octet = DIGIT ; 0-9
1433bf361ddb0bba8878c8ada5726d0284edad57Timo Sirainen * / %x31-39 DIGIT ; 10-99
1433bf361ddb0bba8878c8ada5726d0284edad57Timo Sirainen * / "1" 2DIGIT ; 100-199
1433bf361ddb0bba8878c8ada5726d0284edad57Timo Sirainen * / "2" %x30-34 DIGIT ; 200-249
94d8e51119003d2bc5a100c663f90141f297385dTimo Sirainen * / "25" %x30-35 ; 250-255
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen while (parser->cur < parser->end && i_isdigit(*parser->cur)) {
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainenuri_parse_ipv4address(struct uri_parser *parser, string_t *literal,
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen if ((ret = uri_parse_dec_octet(parser, literal, &octet)) <= 0)
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen for (i = 0; i < 3 && parser->cur < parser->end; i++) {
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen if ((ret = uri_parse_dec_octet(parser, literal, &octet)) <= 0)
636f017be100bce67d66fd3ae1544a47681efd33Timo Sirainen * reg-name = *( unreserved / pct-encoded / sub-delims )
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen unsigned char c;
494a5de15db3b2806ab31d5ecc3e1c306ae14d06Timo Sirainen /* unreserved / pct-encoded */
08ed4ab71fd2a4e800d9025a736f0f46b771ea90Timo Sirainen if ((ret=uri_parse_pct_encoded(parser, &c)) < 0)
636f017be100bce67d66fd3ae1544a47681efd33Timo Sirainen else if (ret == 0 &&
636f017be100bce67d66fd3ae1544a47681efd33Timo Sirainen (ret=uri_parse_unreserved_char(parser, &c)) < 0)
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen /* sub-delims */
636f017be100bce67d66fd3ae1544a47681efd33Timo Sirainen if ((c & 0x80) == 0 && (_uri_char_lookup[c] & CHAR_MASK_SUB_DELIMS) != 0) {
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainenstatic int uri_do_parse_host_name_dns(struct uri_parser *parser,
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen /* RFC 3986, Section 3.2.2:
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen A registered name intended for lookup in the DNS uses the syntax
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen defined in Section 3.5 of [RFC1034] and Section 2.1 of [RFC1123].
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen Such a name consists of a sequence of domain labels separated by ".",
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen each domain label starting and ending with an alphanumeric character
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen and possibly also containing "-" characters. The rightmost domain
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen label of a fully qualified domain name in DNS may be followed by a
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen single "." and should be if it is necessary to distinguish between
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen the complete domain name and some local domain.
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen RFC 2396, Section 3.2.2 (old URI specification):
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen hostname = *( domainlabel "." ) toplabel [ "." ]
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen toplabel = alpha | alpha *( alphanum | "-" ) alphanum
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen The description in RFC 3986 is more liberal, so:
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen hostname = *( domainlabel "." ) domainlabel [ "." ]
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen We also support percent encoding in spirit of the generic reg-name,
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen even though this should explicitly not be used according to the RFC.
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen It is, however, not strictly forbidden (unlike older RFC), so we
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen const unsigned char *offset;
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen /* alphanum */
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0) {
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen } else if (ret > 0) {
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen /* *( alphanum | "-" ) alphanum */
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0) {
636f017be100bce67d66fd3ae1544a47681efd33Timo Sirainen } else if (ret > 0) {
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen parser->error = "Invalid domain label in hostname";
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen str_append_n(host_name, part, parser->cur - part);
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen /* remove trailing '.' */
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen str_truncate(host_name, str_len(host_name)-1);
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainenint uri_parse_host_name_dns(struct uri_parser *parser,
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen host_name = uri_parser_get_tmpbuf(parser, 256);
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen if ((ret=uri_do_parse_host_name_dns(parser, host_name)) <= 0)
c58906589cafc32df4c04ffbef933baadd3f2276Timo Sirainenuri_parse_ip_literal(struct uri_parser *parser, string_t *literal,
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen const unsigned char *p;
6dd77763f5451269ace733579cf58f2f3b18bca4Timo Sirainen /* IP-literal = "[" ( IPv6address / IPvFuture ) "]"
6dd77763f5451269ace733579cf58f2f3b18bca4Timo Sirainen * IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
6dd77763f5451269ace733579cf58f2f3b18bca4Timo Sirainen * IPv6address = ; Syntax not relevant: parsed using inet_pton()
6dd77763f5451269ace733579cf58f2f3b18bca4Timo Sirainen /* "[" already verified */
6dd77763f5451269ace733579cf58f2f3b18bca4Timo Sirainen /* Scan for end of address */
6dd77763f5451269ace733579cf58f2f3b18bca4Timo Sirainen for (p = parser->cur+1; p < parser->end; p++) {
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (*p == ']')
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen parser->error = "Expecting ']' at end of IP-literal";
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen str_append_n(literal, parser->cur, p-parser->cur+1);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen "Future IP host address '%s' not supported", address);
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen if ((ret = inet_pton(AF_INET6, address, &ip6)) <= 0) {
d02d34e138e32b4266f5a403d6c51d7803bf322fTimo Sirainen struct uri_host *host, bool dns_name) ATTR_NULL(2)
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen const unsigned char *preserve;
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen * host = IP-literal / IPv4address / reg-name
d02d34e138e32b4266f5a403d6c51d7803bf322fTimo Sirainen /* IP-literal / */
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen if (parser->cur < parser->end && *parser->cur == '[') {
d06c46087e9e6e66bbbbb9df1d5b33154349515cTimo Sirainen if ((ret=uri_parse_ip_literal(parser, literal, &ip6)) <= 0)
d06c46087e9e6e66bbbbb9df1d5b33154349515cTimo Sirainen host->name = p_strdup(parser->pool, str_c(literal));;
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen /* IPv4address /
490f66d6476d51cc02333d6eb398a5cd94b67f48Timo Sirainen * If it fails to parse, we try to parse it as a reg-name
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen if ((ret = uri_parse_ipv4address(parser, literal, &ip4)) > 0) {
490f66d6476d51cc02333d6eb398a5cd94b67f48Timo Sirainen host->name = p_strdup(parser->pool, str_c(literal));
490f66d6476d51cc02333d6eb398a5cd94b67f48Timo Sirainen /* reg-name */
93688bfedcfb2b9c02750b8d4d409123a386de5cTimo Sirainen if (uri_do_parse_host_name_dns(parser, literal) < 0)
490f66d6476d51cc02333d6eb398a5cd94b67f48Timo Sirainen } else if (uri_parse_reg_name(parser, literal) < 0)
490f66d6476d51cc02333d6eb398a5cd94b67f48Timo Sirainen host->name = p_strdup(parser->pool, str_c(literal));
490f66d6476d51cc02333d6eb398a5cd94b67f48Timo Sirainen const unsigned char *first;
d02d34e138e32b4266f5a403d6c51d7803bf322fTimo Sirainen * port = *DIGIT
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen while (parser->cur < parser->end && i_isdigit(*parser->cur))
490f66d6476d51cc02333d6eb398a5cd94b67f48Timo Sirainen if (net_str2port(t_strdup_until(first, parser->cur), &port) < 0) {
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainenint uri_parse_authority(struct uri_parser *parser,
e5aa8d4c9bdda79c2db03391ec45b37bf774df14Timo Sirainen const unsigned char *p;
fea7b8b3fc182e415b1875d79587c0aa1adb09d7Timo Sirainen * authority = [ userinfo "@" ] host [ ":" port ]
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen /* Scan ahead to check whether there is a [userinfo "@"] uri component */
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen /* refuse 8bit characters */
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen if ((*p & 0x80) != 0)
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen /* break at first delimiter */
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen if (*p != '%' && (_uri_char_lookup[*p] & CHAR_MASK_UCHAR) == 0)
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen /* Extract userinfo */
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen auth->enc_userinfo = p_strdup_until(parser->pool, parser->cur, p);
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen (auth == NULL ? NULL : &auth->host), dns_name) < 0)
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen /* [":" port] */
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainenint uri_parse_slashslash_authority(struct uri_parser *parser,
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen /* "//" authority */
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen if ((parser->end - parser->cur) <= 2 || parser->cur[0] != '/' ||
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen return uri_parse_authority(parser, auth, dns_name);
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainenint uri_parse_path_segment(struct uri_parser *parser, const char **segment_r)
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen if (*p == '%') {
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen if ((*p & 0x80) != 0 || (_uri_char_lookup[*p] & CHAR_MASK_PCHAR) == 0)
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen "Path component contains invalid character";
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen *segment_r = p_strdup_until(parser->pool, parser->cur, p);
e5aa8d4c9bdda79c2db03391ec45b37bf774df14Timo Sirainen unsigned int count;
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen /* check for a leading '/' and indicate absolute path
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen when it is present
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen if (parser->cur < parser->end && *parser->cur == '/') {
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen /* parse first segment */
6d931bbce16786df431e9ae8201a78a95084316dTimo Sirainen if ((ret = uri_parse_path_segment(parser, &segment)) < 0)
6d931bbce16786df431e9ae8201a78a95084316dTimo Sirainen /* strip dot segments */
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen /* '..' -> skip and... */
6d931bbce16786df431e9ae8201a78a95084316dTimo Sirainen /* ... pop last segment (if any) */
6d931bbce16786df431e9ae8201a78a95084316dTimo Sirainen } else if ( relative > 0 ) {
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen /* '.' -> skip */
e28fa207d1a097fa6e4a867f74ee0761472ef1ceTimo Sirainen if (parser->cur >= parser->end || *parser->cur != '/')
e28fa207d1a097fa6e4a867f74ee0761472ef1ceTimo Sirainen /* parse next path segment */
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen if ((ret = uri_parse_path_segment(parser, &segment)) < 0)
e0ca8f2484847b57e20798a9f9c7040708696a90Timo Sirainen /* path part of URI is empty */
e0ca8f2484847b57e20798a9f9c7040708696a90Timo Sirainen /* special treatment for a trailing '..' or '.' */
e0ca8f2484847b57e20798a9f9c7040708696a90Timo Sirainen parser->error = "Path component contains invalid character";
e0ca8f2484847b57e20798a9f9c7040708696a90Timo Sirainenint uri_parse_query(struct uri_parser *parser, const char **query_r)
e0ca8f2484847b57e20798a9f9c7040708696a90Timo Sirainen * URI = { ... } [ "?" query ] { ... }
e0ca8f2484847b57e20798a9f9c7040708696a90Timo Sirainen * query = *( pchar / "/" / "?" )
e0ca8f2484847b57e20798a9f9c7040708696a90Timo Sirainen * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
c040ee67d0ac0fb7375bb543965bf67dcae6affaTimo Sirainen if (*p == '%') {
533bfba437e4120aa29dd45bca2aa87e30ee28a2Timo Sirainen if ((*p & 0x80) != 0 || (_uri_char_lookup[*p] & CHAR_MASK_QCHAR) == 0)
5160580b0ec3f3288a320987abdf12a990f09df5Timo Sirainen parser->error = "Query component contains invalid character";
533bfba437e4120aa29dd45bca2aa87e30ee28a2Timo Sirainen *query_r = p_strdup_until(parser->pool, parser->cur+1, p);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenint uri_parse_fragment(struct uri_parser *parser, const char **fragment_r)
d92f33f13830ba23d814342bf3ea8db721a15bb1Timo Sirainen * URI = { ... } [ "#" fragment ]
d92f33f13830ba23d814342bf3ea8db721a15bb1Timo Sirainen * fragment = *( pchar / "/" / "?" )
f97cacf16251b42f530c6a28686cc8c9aa7df3a2Timo Sirainen * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
4ba962c3e78f140facdcfb1e093c4c46de75ae24Timo Sirainen if (*p == '%') {
4ba962c3e78f140facdcfb1e093c4c46de75ae24Timo Sirainen if ((*p & 0x80) != 0 || (_uri_char_lookup[*p] & CHAR_MASK_QCHAR) == 0)
d92f33f13830ba23d814342bf3ea8db721a15bb1Timo Sirainen parser->error = "Fragment component contains invalid character";
b13f738e8eb3f24dc2abf2c804f954b4d864ac6fTimo Sirainen *fragment_r = p_strdup_until(parser->pool, parser->cur+1, p);
4ba962c3e78f140facdcfb1e093c4c46de75ae24Timo Sirainenvoid uri_parser_init_data(struct uri_parser *parser,
b13f738e8eb3f24dc2abf2c804f954b4d864ac6fTimo Sirainen pool_t pool, const unsigned char *data, size_t size)
de4d9f77d6378a416c9963963eac5ac18b75ec0bTimo Sirainenvoid uri_parser_init(struct uri_parser *parser,
de4d9f77d6378a416c9963963eac5ac18b75ec0bTimo Sirainen (parser, pool, (const unsigned char *)uri, strlen(uri));
de4d9f77d6378a416c9963963eac5ac18b75ec0bTimo Sirainenstring_t *uri_parser_get_tmpbuf(struct uri_parser *parser, size_t size)
6b8bcce7a001076605cb2cfdca8507204c88f376Timo Sirainen * Generic URI manipulation
6b8bcce7a001076605cb2cfdca8507204c88f376Timo Sirainenvoid uri_host_copy(pool_t pool, struct uri_host *dest,
de4d9f77d6378a416c9963963eac5ac18b75ec0bTimo Sirainen /* create host name literal if caller is lazy */
de4d9f77d6378a416c9963963eac5ac18b75ec0bTimo Sirainen if (host_name == NULL && src->ip.family != 0) {
de4d9f77d6378a416c9963963eac5ac18b75ec0bTimo Sirainen * Generic URI construction
de4d9f77d6378a416c9963963eac5ac18b75ec0bTimo Sirainenuri_data_encode(string_t *out, const unsigned char esc_table[256],
de4d9f77d6378a416c9963963eac5ac18b75ec0bTimo Sirainen unsigned char esc_mask, const char *esc_extra, const char *data)
de4d9f77d6378a416c9963963eac5ac18b75ec0bTimo Sirainen const unsigned char *pbegin, *p;
de4d9f77d6378a416c9963963eac5ac18b75ec0bTimo Sirainen while (*p != '\0') {
de4d9f77d6378a416c9963963eac5ac18b75ec0bTimo Sirainen if ((*p & 0x80) != 0 || (esc_table[*p] & esc_mask) == 0 ||
de4d9f77d6378a416c9963963eac5ac18b75ec0bTimo Sirainen if ((p - pbegin) > 0)
de4d9f77d6378a416c9963963eac5ac18b75ec0bTimo Sirainen if ((p - pbegin) > 0)
de4d9f77d6378a416c9963963eac5ac18b75ec0bTimo Sirainenvoid uri_append_scheme(string_t *out, const char *scheme)
de4d9f77d6378a416c9963963eac5ac18b75ec0bTimo Sirainenvoid uri_append_user_data(string_t *out, const char *esc,
de4d9f77d6378a416c9963963eac5ac18b75ec0bTimo Sirainen uri_data_encode(out, _uri_char_lookup, CHAR_MASK_UCHAR, esc, data);
de4d9f77d6378a416c9963963eac5ac18b75ec0bTimo Sirainenvoid uri_append_userinfo(string_t *out, const char *userinfo)
de4d9f77d6378a416c9963963eac5ac18b75ec0bTimo Sirainenvoid uri_append_host_name(string_t *out, const char *name)
de4d9f77d6378a416c9963963eac5ac18b75ec0bTimo Sirainen CHAR_MASK_UNRESERVED | CHAR_MASK_SUB_DELIMS, "", name);
de4d9f77d6378a416c9963963eac5ac18b75ec0bTimo Sirainenvoid uri_append_host_ip(string_t *out, const struct ip_addr *host_ip)
c07e35cfc9587fc7589cbc1db0daeeb828456b2bTimo Sirainenvoid uri_append_host(string_t *out, const struct uri_host *host)
c07e35cfc9587fc7589cbc1db0daeeb828456b2bTimo Sirainen /* assume IPv6 literal if starts with '['; avoid encoding */
c07e35cfc9587fc7589cbc1db0daeeb828456b2bTimo Sirainenvoid uri_append_port(string_t *out, in_port_t port)
de4d9f77d6378a416c9963963eac5ac18b75ec0bTimo Sirainenvoid uri_append_path_segment_data(string_t *out, const char *esc,
547847191d4a95cd267fc846a1ca3c2111214719Timo Sirainen uri_data_encode(out, _uri_char_lookup, CHAR_MASK_PCHAR, esc, data);
547847191d4a95cd267fc846a1ca3c2111214719Timo Sirainenvoid uri_append_path_segment(string_t *out, const char *segment)
de486b59018016977015ef42e6071155b60e82e1Timo Sirainenvoid uri_append_path_data(string_t *out, const char *esc,
de486b59018016977015ef42e6071155b60e82e1Timo Sirainen uri_data_encode(out, _uri_char_lookup, CHAR_MASK_PFCHAR, esc, data);
de486b59018016977015ef42e6071155b60e82e1Timo Sirainenvoid uri_append_path(string_t *out, const char *path)
de486b59018016977015ef42e6071155b60e82e1Timo Sirainenvoid uri_append_query_data(string_t *out, const char *esc,
de486b59018016977015ef42e6071155b60e82e1Timo Sirainen uri_data_encode(out, _uri_char_lookup, CHAR_MASK_QCHAR, esc, data);
de486b59018016977015ef42e6071155b60e82e1Timo Sirainenvoid uri_append_query(string_t *out, const char *query)
de4d9f77d6378a416c9963963eac5ac18b75ec0bTimo Sirainenvoid uri_append_fragment_data(string_t *out, const char *esc,
de486b59018016977015ef42e6071155b60e82e1Timo Sirainen uri_data_encode(out, _uri_char_lookup, CHAR_MASK_QCHAR, esc, data);
de486b59018016977015ef42e6071155b60e82e1Timo Sirainenvoid uri_append_fragment(string_t *out, const char *fragment)