From: Stephan Bosch Date: Sun, 8 May 2016 20:59:09 +0000 (+0200) Subject: uri-util: Implemented proper parsing of DNS-based host name for URI schemes that... X-Git-Tag: 2.3.0.rc1~3758 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b0dfc8fc0dd5d08fe6a746e346dca6c737749d89;p=thirdparty%2Fdovecot%2Fcore.git uri-util: Implemented proper parsing of DNS-based host name for URI schemes that use DNS host names, such as http and imap. --- diff --git a/src/lib-http/http-url.c b/src/lib-http/http-url.c index 9b7050cbae..375c63337e 100644 --- a/src/lib-http/http-url.c +++ b/src/lib-http/http-url.c @@ -35,7 +35,7 @@ static bool http_url_parse_authority(struct http_url_parser *url_parser) const char *user = NULL, *password = NULL; int ret; - if ((ret = uri_parse_authority(parser, &auth)) < 0) + if ((ret = uri_parse_authority(parser, &auth, TRUE)) < 0) return FALSE; if (auth.host_literal == NULL || *auth.host_literal == '\0') { /* RFC 7230, Section 2.7.1: http URI Scheme @@ -369,7 +369,7 @@ int http_url_request_target_parse(const char *request_target, parser = &url_parser.parser; uri_parser_init(parser, pool, host_header); - if (uri_parse_authority(parser, &host) <= 0) { + if (uri_parse_authority(parser, &host, TRUE) <= 0) { *error_r = t_strdup_printf("Invalid Host header: %s", parser->error); return -1; } diff --git a/src/lib-http/test-http-url.c b/src/lib-http/test-http-url.c index 87daf07ae5..fc1aa5c03b 100644 --- a/src/lib-http/test-http-url.c +++ b/src/lib-http/test-http-url.c @@ -434,7 +434,6 @@ static const char *parse_create_url_tests[] = { "http://www.example.com:993/", "http://www.example.com/index.html", "http://www.example.com/settings/index.html", - "http://ww.%23example.com/", "http://www.example.com/%23shared/news", "http://www.example.com/query.php?name=Hendrik%20Visser", "http://www.example.com/network.html#IMAP%20Server", diff --git a/src/lib-imap/imap-url.c b/src/lib-imap/imap-url.c index a0ec9648e2..ecd7058e50 100644 --- a/src/lib-imap/imap-url.c +++ b/src/lib-imap/imap-url.c @@ -191,7 +191,8 @@ static int imap_url_parse_iserver(struct imap_url_parser *url_parser) */ /* "//" iserver */ - if ((ret = uri_parse_slashslash_authority(parser, &auth)) <= 0) + if ((ret = uri_parse_slashslash_authority + (parser, &auth, TRUE)) <= 0) return ret; if (auth.host_literal == NULL || *auth.host_literal == '\0') { /* This situation is not documented anywhere, but it is not diff --git a/src/lib-imap/test-imap-url.c b/src/lib-imap/test-imap-url.c index f3bfdc5668..cfc3f1f7b3 100644 --- a/src/lib-imap/test-imap-url.c +++ b/src/lib-imap/test-imap-url.c @@ -976,7 +976,6 @@ static const char *parse_create_url_tests[] = { "imap://user;AUTH=PLAIN@host.example.com/INBOX;UIDVALIDITY=15/;UID=5" "/;SECTION=TEXT/;PARTIAL=1.14;URLAUTH=user+username", "imap://user;AUTH=PLAIN@host.example.com/INBOX?SUBJECT%20%22Frop?%22", - "imap://host.%23example.com/", "imap://user%3ba@host.example.com/", "imap://user%40example.com@host.example.com/", "imap://user%40example.com;AUTH=STR%23ANGE@host.example.com/", diff --git a/src/lib/uri-util.c b/src/lib/uri-util.c index d8d5491a1c..09485c1508 100644 --- a/src/lib/uri-util.c +++ b/src/lib/uri-util.c @@ -372,6 +372,122 @@ uri_parse_reg_name(struct uri_parser *parser, return 0; } +static int uri_parse_host_name_dns(struct uri_parser *parser, + string_t *host_name) ATTR_NULL(2, 3) +{ + const unsigned char *first, *part; + int ret; + + /* RFC 3986, Section 3.2.2: + + A registered name intended for lookup in the DNS uses the syntax + defined in Section 3.5 of [RFC1034] and Section 2.1 of [RFC1123]. + Such a name consists of a sequence of domain labels separated by ".", + each domain label starting and ending with an alphanumeric character + and possibly also containing "-" characters. The rightmost domain + label of a fully qualified domain name in DNS may be followed by a + single "." and should be if it is necessary to distinguish between + the complete domain name and some local domain. + + RFC 2396, Section 3.2.2 (old URI specification): + + hostname = *( domainlabel "." ) toplabel [ "." ] + domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum + toplabel = alpha | alpha *( alphanum | "-" ) alphanum + + The description in RFC 3986 is more liberal, so: + + hostname = *( domainlabel "." ) domainlabel [ "." ] + domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum + + We also support percent encoding in spirit of the generic reg-name, + even though this should explicitly not be used according to the RFC. + It is, however, not strictly forbidden (unlike older RFC), so we + support it. + */ + + first = part = parser->cur; + for (;;) { + const unsigned char *offset; + unsigned char ch, pch; + + /* alphanum */ + offset = parser->cur; + ch = pch = *parser->cur; + if (parser->cur >= parser->end) + break; + if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0) { + return -1; + } else if (ret > 0) { + if (!i_isalnum(ch)) + return -1; + if (host_name != NULL) + str_append_c(host_name, ch); + part = parser->cur; + } else { + if (!i_isalnum(*parser->cur)) + break; + parser->cur++; + } + + if (parser->cur < parser->end) { + /* *( alphanum | "-" ) alphanum */ + do { + offset = parser->cur; + + if ((ret=uri_parse_pct_encoded(parser, &ch)) < 0) { + return -1; + } else if (ret > 0) { + if (!i_isalnum(ch) && ch != '-') + break; + if (host_name != NULL) { + if (offset > part) + str_append_n(host_name, part, offset - part); + str_append_c(host_name, ch); + } + part = parser->cur; + } else { + ch = *parser->cur; + if (!i_isalnum(ch) && ch != '-') + break; + parser->cur++; + } + pch = ch; + } while (parser->cur < parser->end); + + if (!i_isalnum(pch)) { + parser->error = "Invalid domain label in hostname"; + return -1; + } + } + + if (host_name != NULL && parser->cur > part) + str_append_n(host_name, part, parser->cur - part); + + /* "." */ + if (parser->cur >= parser->end || ch != '.') + break; + if (host_name != NULL) + str_append_c(host_name, '.'); + if (parser->cur == offset) + parser->cur++; + part = parser->cur; + } + + if (parser->cur == first) + return 0; + + /* remove trailing '.' */ + if (host_name != NULL) { + const char *name = str_c(host_name); + + i_assert(str_len(host_name) > 0); + if (name[str_len(host_name)-1] == '.') + str_truncate(host_name, str_len(host_name)-1); + } + return 1; +} + static int uri_parse_ip_literal(struct uri_parser *parser, string_t *literal, struct in6_addr *ip6_r) ATTR_NULL(2,3) @@ -425,7 +541,7 @@ uri_parse_ip_literal(struct uri_parser *parser, string_t *literal, static int uri_parse_host(struct uri_parser *parser, - struct uri_authority *auth) ATTR_NULL(2) + struct uri_authority *auth, bool dns_name) ATTR_NULL(2) { const unsigned char *preserve; struct in_addr ip4; @@ -470,7 +586,10 @@ uri_parse_host(struct uri_parser *parser, str_truncate(literal, 0); /* reg-name */ - if (uri_parse_reg_name(parser, literal) < 0) + if (dns_name) { + if (uri_parse_host_name_dns(parser, literal) < 0) + return -1; + } else if (uri_parse_reg_name(parser, literal) < 0) return -1; if (auth != NULL) auth->host_literal = p_strdup(parser->pool, str_c(literal)); @@ -506,7 +625,7 @@ uri_parse_port(struct uri_parser *parser, } int uri_parse_authority(struct uri_parser *parser, - struct uri_authority *auth) + struct uri_authority *auth, bool dns_name) { const unsigned char *p; int ret; @@ -537,7 +656,7 @@ int uri_parse_authority(struct uri_parser *parser, } /* host */ - if (uri_parse_host(parser, auth) < 0) + if (uri_parse_host(parser, auth, dns_name) < 0) return -1; if (parser->cur == parser->end) return 1; @@ -570,7 +689,7 @@ int uri_parse_authority(struct uri_parser *parser, } int uri_parse_slashslash_authority(struct uri_parser *parser, - struct uri_authority *auth) + struct uri_authority *auth, bool dns_name) { /* "//" authority */ @@ -579,7 +698,7 @@ int uri_parse_slashslash_authority(struct uri_parser *parser, return 0; parser->cur += 2; - return uri_parse_authority(parser, auth); + return uri_parse_authority(parser, auth, dns_name); } int uri_parse_path_segment(struct uri_parser *parser, const char **segment_r) diff --git a/src/lib/uri-util.h b/src/lib/uri-util.h index 02e1b39a0c..bfb31f0f95 100644 --- a/src/lib/uri-util.h +++ b/src/lib/uri-util.h @@ -37,9 +37,9 @@ int uri_cut_scheme(const char **uri_p, const char **scheme_r) int uri_parse_scheme(struct uri_parser *parser, const char **scheme_r) ATTR_NULL(2); int uri_parse_authority(struct uri_parser *parser, - struct uri_authority *auth) ATTR_NULL(2); + struct uri_authority *auth, bool dns_name) ATTR_NULL(2); int uri_parse_slashslash_authority(struct uri_parser *parser, - struct uri_authority *auth) ATTR_NULL(2); + struct uri_authority *auth, bool dns_name) ATTR_NULL(2); int uri_parse_path_segment(struct uri_parser *parser, const char **segment_r) ATTR_NULL(2);