dns-domain: rework dns_label_escape() to not imply memory allocation

The new dns_label_escape() call now operates on a buffer passed in,
similar to dns_label_unescape(). This should make decoding a bit faster,
and nicer.
This commit is contained in:
Lennart Poettering 2015-11-25 21:56:48 +01:00
parent 7e8131e9c6
commit 422baca0f2
6 changed files with 85 additions and 50 deletions

View File

@ -360,7 +360,6 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char *
/* End of name */
break;
else if (c <= 63) {
_cleanup_free_ char *t = NULL;
const char *label;
/* Literal label */
@ -369,21 +368,20 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char *
if (pos > optlen)
return -EMSGSIZE;
r = dns_label_escape(label, c, &t);
if (r < 0)
goto fail;
if (!GREEDY_REALLOC0(ret, allocated, n + !first + strlen(t) + 1)) {
if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) {
r = -ENOMEM;
goto fail;
}
if (!first)
ret[n++] = '.';
else
if (first)
first = false;
else
ret[n++] = '.';
r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
if (r < 0)
goto fail;
memcpy(ret + n, t, r);
n += r;
continue;
} else {

View File

@ -1145,7 +1145,6 @@ int dns_packet_read_name(
/* End of name */
break;
else if (c <= 63) {
_cleanup_free_ char *t = NULL;
const char *label;
/* Literal label */
@ -1153,21 +1152,20 @@ int dns_packet_read_name(
if (r < 0)
goto fail;
r = dns_label_escape(label, c, &t);
if (r < 0)
goto fail;
if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1)) {
if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) {
r = -ENOMEM;
goto fail;
}
if (!first)
ret[n++] = '.';
else
if (first)
first = false;
else
ret[n++] = '.';
r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
if (r < 0)
goto fail;
memcpy(ret + n, t, r);
n += r;
continue;
} else if (allow_compression && (c & 0xc0) == 0xc0) {

View File

@ -351,7 +351,7 @@ static int determine_hostname(char **llmnr_hostname, char **mdns_hostname) {
return -EINVAL;
}
r = dns_label_escape(label, r, &n);
r = dns_label_escape_new(label, r, &n);
if (r < 0)
return log_error_errno(r, "Failed to escape host name: %m");

View File

@ -182,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') ||
@ -213,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;
@ -230,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;
@ -351,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;
if (!first)
ret[n++] = '.';
else
first = false;
r = dns_label_escape(label, r, ret + n + !first, DNS_LABEL_ESCAPED_MAX);
if (r < 0)
return r;
memcpy(ret + n, t, r);
if (!first)
ret[n] = '.';
} else {
char escaped[DNS_LABEL_ESCAPED_MAX];
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;
@ -892,7 +929,8 @@ 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);
@ -908,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;

View File

@ -26,11 +26,12 @@
#include "in-addr-util.h"
#define DNS_LABEL_MAX 63
#define DNS_NAME_MAX 255
#define DNS_LABEL_ESCAPED_MAX (DNS_LABEL_MAX*4+1)
int dns_label_unescape(const char **name, char *dest, size_t sz);
int dns_label_unescape_suffix(const char *name, const char **label_end, char *dest, size_t sz);
int dns_label_escape(const char *p, size_t l, char **ret);
int dns_label_escape(const char *p, size_t l, char *dest, size_t sz);
int dns_label_escape_new(const char *p, size_t l, char **ret);
int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);
int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);

View File

@ -126,7 +126,7 @@ static void test_dns_label_escape_one(const char *what, size_t l, const char *ex
_cleanup_free_ char *t = NULL;
int r;
r = dns_label_escape(what, l, &t);
r = dns_label_escape_new(what, l, &t);
assert_se(r == ret);
if (r < 0)