dns-domain.c revision 54adabf727fbcf1a4f49b59362e92b79da1082f1
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering This file is part of systemd.
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering Copyright 2014 Lennart Poettering
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering systemd is free software; you can redistribute it and/or modify it
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering under the terms of the GNU Lesser General Public License as published by
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering the Free Software Foundation; either version 2.1 of the License, or
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering (at your option) any later version.
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering systemd is distributed in the hope that it will be useful, but
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering Lesser General Public License for more details.
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering You should have received a copy of the GNU Lesser General Public License
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sieversint dns_label_unescape(const char **name, char *dest, size_t sz) {
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sievers const char *n;
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering if (*n == '.') {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering if (*n == '\\') {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* Escaped character */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* Ending NUL */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* Escaped backslash or dot */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering *(d++) = *(n++);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* Escaped literal ASCII character */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* Don't allow CC characters or anything that doesn't fit in 8bit */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering *(d++) = (char) k;
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering } else if ((uint8_t) *n >= (uint8_t) ' ' && *n != 127) {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* Normal character */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering *(d++) = *(n++);
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek /* Empty label that is not at the end? */
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers if (r == 0 && *n)
ffc06c3513d9a0693c7f810d03b20705127ba55aKay Sievers/* @label_terminal: terminal character of a label, updated to point to the terminal character of
ffc06c3513d9a0693c7f810d03b20705127ba55aKay Sievers * the previous label (always skipping one dot) or to NULL if there are no more
ffc06c3513d9a0693c7f810d03b20705127ba55aKay Sieversint dns_label_unescape_suffix(const char *name, const char **label_terminal, char *dest, size_t sz) {
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sievers /* no more labels */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering assert(**label_terminal == '.' || **label_terminal == 0);
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sievers /* skip current terminal character */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* reached the first label, so indicate that there are no more */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* find the start of the last label */
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sievers const char *y;
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sievers unsigned slashes = 0;
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering for (y = terminal - 1; y >= name && *y == '\\'; y--)
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* the '.' was not escaped */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poetteringint dns_label_escape(const char *p, size_t l, char **ret) {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering while (l > 0) {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* Dot or backslash */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering *(q++) = '\\';
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering } else if (*p == '_' ||
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* Proper character */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering } else if ((uint8_t) *p >= (uint8_t) ' ' && *p != 127) {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* Everything else */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering *(q++) = '\\';
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering *(q++) = '0' + (char) ((uint8_t) *p / 100);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering *(q++) = '0' + (char) (((uint8_t) *p / 10) % 10);
17d33cecaa762f7e43200307328af5e9135e2091Giovanni Campagna *(q++) = '0' + (char) ((uint8_t) *p % 10);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poetteringint dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering const char *p;
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering for (p = encoded; p < encoded + encoded_size; p++)
2f7a4867babd3fd382e5495f21724358f30fa67dMichal Sekletar input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering if (idna_to_ascii_4i(input, input_size, decoded, 0) != 0)
0affed79d2e30013f07cb94e6f07e3fcb81c02faLennart Poetteringint dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* To be invoked after unescaping */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering if (encoded_size < sizeof(IDNA_ACE_PREFIX)-1)
17d33cecaa762f7e43200307328af5e9135e2091Giovanni Campagna if (memcmp(encoded, IDNA_ACE_PREFIX, sizeof(IDNA_ACE_PREFIX) -1) != 0)
17d33cecaa762f7e43200307328af5e9135e2091Giovanni Campagna input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size);
17d33cecaa762f7e43200307328af5e9135e2091Giovanni Campagna idna_to_unicode_44i(input, input_size, output, &output_size, 0);
17d33cecaa762f7e43200307328af5e9135e2091Giovanni Campagna result = stringprep_ucs4_to_utf8(output, output_size, NULL, &w);
17d33cecaa762f7e43200307328af5e9135e2091Giovanni Campagnaint dns_name_concat(const char *a, const char *b, char **_ret) {
bac3c8eefe23a820caac930d41629cebafbfc7b2Zbigniew Jędrzejewski-Szmek const char *p = a;
8333c77edf8fd1654cd96f3f6ee0f078dd64b58bZbigniew Jędrzejewski-Szmek r = dns_label_unescape(&p, label, sizeof(label));
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sievers /* Now continue with the second string, if there is one */
17d33cecaa762f7e43200307328af5e9135e2091Giovanni Campagna k = dns_label_undo_idna(label, r, label, sizeof(label));
bac3c8eefe23a820caac930d41629cebafbfc7b2Zbigniew Jędrzejewski-Szmek r = dns_label_escape(label, r, &t);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1))
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering if (!GREEDY_REALLOC(ret, allocated, n + 1))
e1636421f46db6d06fbd028ef20a3113fa3e11f8Lennart Poetteringvoid dns_name_hash_func(const void *s, struct siphash *state) {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering const char *p = s;
e1636421f46db6d06fbd028ef20a3113fa3e11f8Lennart Poettering r = dns_label_unescape(&p, label, sizeof(label));
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering k = dns_label_undo_idna(label, r, label, sizeof(label));
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* enforce that all names are terminated by the empty label */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poetteringint dns_name_compare_func(const void *a, const void *b) {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering const char *x, *y;
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering int r, q, k, w;
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering x = (const char *) a + strlen(a);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering y = (const char *) b + strlen(b);
ef42202ac8ed27e7ff1fc90ef8bc2590046dff25Zbigniew Jędrzejewski-Szmek char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering r = dns_label_unescape_suffix(a, &x, la, sizeof(la));
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering q = dns_label_unescape_suffix(b, &y, lb, sizeof(lb));
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering if (r < 0 || q < 0)
7fd1b19bc9e9f5574f2877936b8ac267c7706947Harald Hoyer k = dns_label_undo_idna(la, r, la, sizeof(la));
0732ef7acf37473847992888bcb6446726d9d877Zbigniew Jędrzejewski-Szmek w = dns_label_undo_idna(lb, q, lb, sizeof(lb));
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering if (k < 0 || w < 0)
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poetteringconst struct hash_ops dns_name_hash_ops = {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poetteringint dns_name_equal(const char *x, const char *y) {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering int r, q, k, w;
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering if (*x == 0 && *y == 0)
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering k = dns_label_undo_idna(la, r, la, sizeof(la));
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering q = dns_label_unescape(&y, lb, sizeof(lb));
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering w = dns_label_undo_idna(lb, q, lb, sizeof(lb));
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sieversint dns_name_endswith(const char *name, const char *suffix) {
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sievers int r, q, k, w;
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering r = dns_label_unescape(&n, ln, sizeof(ln));
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering k = dns_label_undo_idna(ln, r, ln, sizeof(ln));
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering q = dns_label_unescape(&s, ls, sizeof(ls));
c62e11ce3966c55d23520b9f0785c7e839cf7f37Lennart Poettering w = dns_label_undo_idna(ls, q, ls, sizeof(ls));
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering if (r == 0 && q == 0)
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering if (r == 0 && saved_n == n)
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering /* Not the same, let's jump back, and try with the next label again */
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poetteringint dns_name_between(const char *a, const char *b, const char *c) {
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering /* Determine if b is strictly greater than a and strictly smaller than c.
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering We consider the order of names to be circular, so that if a is
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering strictly greater than c, we consider b to be between them if it is
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering either greater than a or smaller than c. This is how the canonical
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering DNS name order used in NSEC records work. */
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering else if (n < 0)
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering /* a<---b--->c */
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering return dns_name_compare_func(a, b) < 0 &&
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering /* <--b--c a--b--> */
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering return dns_name_compare_func(b, c) < 0 ||
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poetteringint dns_name_reverse(int family, const union in_addr_union *a, char **ret) {
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering p = (const uint8_t*) a;
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering r = asprintf(ret, "%u.%u.%u.%u.in-addr.arpa", p[3], p[2], p[1], p[0]);
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering r = asprintf(ret, "%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.ip6.arpa",
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering hexchar(p[15] & 0xF), hexchar(p[15] >> 4), hexchar(p[14] & 0xF), hexchar(p[14] >> 4),
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering hexchar(p[13] & 0xF), hexchar(p[13] >> 4), hexchar(p[12] & 0xF), hexchar(p[12] >> 4),
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering hexchar(p[11] & 0xF), hexchar(p[11] >> 4), hexchar(p[10] & 0xF), hexchar(p[10] >> 4),
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering hexchar(p[ 9] & 0xF), hexchar(p[ 9] >> 4), hexchar(p[ 8] & 0xF), hexchar(p[ 8] >> 4),
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering hexchar(p[ 7] & 0xF), hexchar(p[ 7] >> 4), hexchar(p[ 6] & 0xF), hexchar(p[ 6] >> 4),
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering hexchar(p[ 5] & 0xF), hexchar(p[ 5] >> 4), hexchar(p[ 4] & 0xF), hexchar(p[ 4] >> 4),
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering hexchar(p[ 3] & 0xF), hexchar(p[ 3] >> 4), hexchar(p[ 2] & 0xF), hexchar(p[ 2] >> 4),
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering hexchar(p[ 1] & 0xF), hexchar(p[ 1] >> 4), hexchar(p[ 0] & 0xF), hexchar(p[ 0] >> 4));
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poetteringint dns_name_address(const char *p, int *family, union in_addr_union *address) {
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering r = dns_name_endswith(p, "in-addr.arpa");
82c1d8f4eb74ddd9be2c9b9b56d9dc564c599effLennart Poettering for (i = 0; i < ELEMENTSOF(a); i++) {
50cfc579280fb42569488079bd2e249e32a27df2Lennart Poettering r = dns_label_unescape(&p, label, sizeof(label));
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering address->in.s_addr = htobe32(((uint32_t) a[3] << 24) |
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sievers r = dns_label_unescape(&p, label, sizeof(label));
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering r = dns_label_unescape(&p, label, sizeof(label));
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering a.s6_addr[ELEMENTSOF(a.s6_addr) - i - 1] = (uint8_t) y << 4 | (uint8_t) x;
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sievers r = dns_label_unescape(&name, label, sizeof(label));
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sievers return r == 0 && *name == 0;
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poetteringint dns_name_single_label(const char *name) {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering r = dns_label_unescape(&name, label, sizeof(label));
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering r = dns_label_unescape(&name, label, sizeof(label));
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering return r == 0 && *name == 0;
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering/* Encode a domain name according to RFC 1035 Section 3.1 */
4d7859d173282e16bb75254c2b4ec14a915ef30bKay Sieversint dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len) {
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* reserve a byte for label length */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* convert and copy a single label */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering r = dns_label_unescape(&domain, (char *) out, len);
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering /* fill label length, move forward */
2087a7aff26ea5d1bc2c7c29add3275328f36baaLennart Poettering } while (r != 0);