diff options
Diffstat (limited to 'src/shared/dns-domain.c')
-rw-r--r-- | src/shared/dns-domain.c | 128 |
1 files changed, 78 insertions, 50 deletions
diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index e6aad39c74..4cf6355b71 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -29,6 +29,7 @@ #include "hexdecoct.h" #include "parse-util.h" #include "string-util.h" +#include "strv.h" #include "utf8.h" int dns_label_unescape(const char **name, char *dest, size_t sz) { @@ -181,30 +182,31 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha return r; } -int dns_label_escape(const char *p, size_t l, char **ret) { - _cleanup_free_ char *s = NULL; +int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) { char *q; - int r; - - assert(p); - assert(ret); if (l > DNS_LABEL_MAX) return -EINVAL; + if (sz < 1) + return -ENOSPC; - s = malloc(l * 4 + 1); - if (!s) - return -ENOMEM; + assert(p); + assert(dest); - q = s; + q = dest; while (l > 0) { if (*p == '.' || *p == '\\') { + if (sz < 3) + return -ENOSPC; + /* Dot or backslash */ *(q++) = '\\'; *(q++) = *p; + sz -= 2; + } else if (*p == '_' || *p == '-' || (*p >= '0' && *p <= '9') || @@ -212,15 +214,27 @@ int dns_label_escape(const char *p, size_t l, char **ret) { (*p >= 'A' && *p <= 'Z')) { /* Proper character */ + + if (sz < 2) + return -ENOSPC; + *(q++) = *p; + sz -= 1; + } else if ((uint8_t) *p >= (uint8_t) ' ' && *p != 127) { /* Everything else */ + + if (sz < 5) + return -ENOSPC; + *(q++) = '\\'; *(q++) = '0' + (char) ((uint8_t) *p / 100); *(q++) = '0' + (char) (((uint8_t) *p / 10) % 10); *(q++) = '0' + (char) ((uint8_t) *p % 10); + sz -= 4; + } else return -EINVAL; @@ -229,8 +243,28 @@ int dns_label_escape(const char *p, size_t l, char **ret) { } *q = 0; + return (int) (q - dest); +} + +int dns_label_escape_new(const char *p, size_t l, char **ret) { + _cleanup_free_ char *s = NULL; + int r; + + assert(p); + assert(ret); + + if (l > DNS_LABEL_MAX) + return -EINVAL; + + s = new(char, DNS_LABEL_ESCAPED_MAX); + if (!s) + return -ENOMEM; + + r = dns_label_escape(p, l, s, DNS_LABEL_ESCAPED_MAX); + if (r < 0) + return r; + *ret = s; - r = q - s; s = NULL; return r; @@ -350,28 +384,32 @@ int dns_name_concat(const char *a, const char *b, char **_ret) { if (k > 0) r = k; - r = dns_label_escape(label, r, &t); - if (r < 0) - return r; - if (_ret) { - if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1)) + if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) return -ENOMEM; + r = dns_label_escape(label, r, ret + n + !first, DNS_LABEL_ESCAPED_MAX); + if (r < 0) + return r; + if (!first) - ret[n++] = '.'; - else - first = false; + ret[n] = '.'; + } else { + char escaped[DNS_LABEL_ESCAPED_MAX]; - memcpy(ret + n, t, r); + r = dns_label_escape(label, r, escaped, sizeof(escaped)); + if (r < 0) + return r; } + if (!first) + n++; + else + first = false; + n += r; } - if (n > DNS_NAME_MAX) - return -EINVAL; - if (_ret) { if (!GREEDY_REALLOC(ret, allocated, n + 1)) return -ENOMEM; @@ -752,36 +790,27 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) { return 0; } -int dns_name_root(const char *name) { - char label[DNS_LABEL_MAX+1]; - int r; +bool dns_name_is_root(const char *name) { assert(name); - r = dns_label_unescape(&name, label, sizeof(label)); - if (r < 0) - return r; + /* There are exactly two ways to encode the root domain name: + * as empty string, or with a single dot. */ - return r == 0 && *name == 0; + return STR_IN_SET(name, "", "."); } -int dns_name_single_label(const char *name) { +bool dns_name_is_single_label(const char *name) { char label[DNS_LABEL_MAX+1]; int r; assert(name); r = dns_label_unescape(&name, label, sizeof(label)); - if (r < 0) - return r; - if (r == 0) - return 0; - - r = dns_label_unescape(&name, label, sizeof(label)); - if (r < 0) - return r; + if (r <= 0) + return false; - return r == 0 && *name == 0; + return dns_name_is_root(name); } /* Encode a domain name according to RFC 1035 Section 3.1 */ @@ -846,12 +875,12 @@ static bool srv_type_label_is_valid(const char *label, size_t n) { return true; } -int dns_srv_type_verify(const char *name) { +bool dns_srv_type_is_valid(const char *name) { unsigned c = 0; int r; if (!name) - return 0; + return false; for (;;) { char label[DNS_LABEL_MAX]; @@ -859,18 +888,16 @@ int dns_srv_type_verify(const char *name) { /* This more or less implements RFC 6335, Section 5.1 */ r = dns_label_unescape(&name, label, sizeof(label)); - if (r == -EINVAL) - return 0; if (r < 0) - return r; + return false; if (r == 0) break; if (c >= 2) - return 0; + return false; if (!srv_type_label_is_valid(label, r)) - return 0; + return false; c++; } @@ -902,14 +929,15 @@ bool dns_service_name_is_valid(const char *name) { } int dns_service_join(const char *name, const char *type, const char *domain, char **ret) { - _cleanup_free_ char *escaped = NULL, *n = NULL; + char escaped[DNS_LABEL_ESCAPED_MAX]; + _cleanup_free_ char *n = NULL; int r; assert(type); assert(domain); assert(ret); - if (!dns_srv_type_verify(type)) + if (!dns_srv_type_is_valid(type)) return -EINVAL; if (!name) @@ -918,7 +946,7 @@ int dns_service_join(const char *name, const char *type, const char *domain, cha if (!dns_service_name_is_valid(name)) return -EINVAL; - r = dns_label_escape(name, strlen(name), &escaped); + r = dns_label_escape(name, strlen(name), escaped, sizeof(escaped)); if (r < 0) return r; |