From 0a49b6b6dce3a756bd8c4d458a34c2d8035ae99d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 20 Nov 2015 17:52:36 +0100 Subject: dns-domain: add code for verifying validity of DNS-SD service names and types --- src/shared/dns-domain.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++ src/shared/dns-domain.h | 4 +++ src/test/test-dns-domain.c | 36 +++++++++++++++++++++++++ 3 files changed, 107 insertions(+) (limited to 'src') diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index 423ccca9cc..014e0bd70d 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 "utf8.h" int dns_label_unescape(const char **name, char *dest, size_t sz) { const char *n; @@ -749,3 +750,69 @@ int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len) { return out - buffer; } + +int dns_srv_type_verify(const char *name) { + unsigned c = 0; + int r; + + if (!name) + return 0; + + for (;;) { + char label[DNS_LABEL_MAX]; + int k; + + /* 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; + if (r == 0) + return c >= 2; /* At least two labels */ + if (r < 2) /* Label needs to be at least 2 chars long */ + return 0; + if (label[0] != '_') /* First label char needs to be underscore */ + return 0; + + /* Second char must be a letter */ + if (!(label[1] >= 'A' && label[1] <= 'Z') && + !(label[1] >= 'a' && label[1] <= 'z')) + return 0; + + /* Third and further chars must be alphanumeric or a hyphen */ + for (k = 2; k < r; k++) { + if (!(label[k] >= 'A' && label[k] <= 'Z') && + !(label[k] >= 'a' && label[k] <= 'z') && + !(label[k] >= '0' && label[k] <= '9') && + label[k] != '-') + return 0; + } + + c++; + } +} + +bool dns_service_name_is_valid(const char *name) { + size_t l; + + /* This more or less implements RFC 6763, Section 4.1.1 */ + + if (!name) + return false; + + if (!utf8_is_valid(name)) + return false; + + if (string_has_cc(name, NULL)) + return false; + + l = strlen(name); + if (l <= 0) + return false; + if (l > 63) + return false; + + return true; +} diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h index b214897440..22c394443b 100644 --- a/src/shared/dns-domain.h +++ b/src/shared/dns-domain.h @@ -69,3 +69,7 @@ int dns_name_root(const char *name); int dns_name_single_label(const char *name); int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len); + +int dns_srv_type_verify(const char *name); + +bool dns_service_name_is_valid(const char *name); diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c index 407c7d8ef7..a23d5dee05 100644 --- a/src/test/test-dns-domain.c +++ b/src/test/test-dns-domain.c @@ -316,6 +316,40 @@ static void test_dns_name_is_valid(void) { test_dns_name_is_valid_one("\n", 0); } +static void test_dns_service_name_is_valid(void) { + assert_se(dns_service_name_is_valid("Lennart's Compüter")); + assert_se(dns_service_name_is_valid("piff.paff")); + + assert_se(!dns_service_name_is_valid(NULL)); + assert_se(!dns_service_name_is_valid("")); + assert_se(!dns_service_name_is_valid("foo\nbar")); + assert_se(!dns_service_name_is_valid("foo\201bar")); + assert_se(!dns_service_name_is_valid("this is an overly long string that is certainly longer than 63 characters")); +} + +static void test_dns_srv_type_verify(void) { + + assert_se(dns_srv_type_verify("_http._tcp") > 0); + assert_se(dns_srv_type_verify("_foo-bar._tcp") > 0); + assert_se(dns_srv_type_verify("_w._udp") > 0); + assert_se(dns_srv_type_verify("_piep._sub._w._udp") > 0); + assert_se(dns_srv_type_verify("_a800._tcp") > 0); + assert_se(dns_srv_type_verify("_a-800._tcp") > 0); + + assert_se(dns_srv_type_verify(NULL) == 0); + assert_se(dns_srv_type_verify("") == 0); + assert_se(dns_srv_type_verify("x") == 0); + assert_se(dns_srv_type_verify("_foo") == 0); + assert_se(dns_srv_type_verify("_tcp") == 0); + assert_se(dns_srv_type_verify("_") == 0); + assert_se(dns_srv_type_verify("_foo.") == 0); + assert_se(dns_srv_type_verify("_föo._tcp") == 0); + assert_se(dns_srv_type_verify("_f\no._tcp") == 0); + assert_se(dns_srv_type_verify("_800._tcp") == 0); + assert_se(dns_srv_type_verify("_-800._tcp") == 0); + assert_se(dns_srv_type_verify("_-foo._tcp") == 0); +} + int main(int argc, char *argv[]) { test_dns_label_unescape(); @@ -331,6 +365,8 @@ int main(int argc, char *argv[]) { test_dns_name_concat(); test_dns_name_is_valid(); test_dns_name_to_wire_format(); + test_dns_service_name_is_valid(); + test_dns_srv_type_verify(); return 0; } -- cgit v1.2.3-54-g00ecf