From 4e2d82b18f7e099cbce933b383c45efa40306161 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 24 Apr 2005 20:23:07 +0000 Subject: [PATCH] add support for dots and backslashes in domain names (required for DNS-SD) git-svn-id: file:///home/lennart/svn/public/avahi/trunk@28 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe --- Makefile | 9 +++- dns-test.c | 29 +++++++++++ dns.c | 139 +++++++++++++++++++++++++++++++++----------------- dns.h | 1 - domain-test.c | 19 +++++++ psched.c | 2 +- rr.c | 10 ++-- server.c | 5 +- util.c | 61 ++++++++++++++++++++-- util.h | 2 + 10 files changed, 214 insertions(+), 63 deletions(-) create mode 100644 dns-test.c create mode 100644 domain-test.c diff --git a/Makefile b/Makefile index 5851286..2a3be76 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ CFLAGS=-g -O0 -Wall -W -pipe $(shell pkg-config --cflags glib-2.0) -Wno-unused LIBS=$(shell pkg-config --libs glib-2.0) -all: strlst-test prioq-test flexmdns +all: strlst-test prioq-test domain-test dns-test flexmdns flexmdns: timeeventq.o main.o iface.o netlink.o server.o address.o util.o prioq.o cache.o rr.o dns.o socket.o psched.o announce.o subscribe.o strlst.o $(CC) -o $@ $^ $(LIBS) @@ -13,10 +13,15 @@ flexmdns: timeeventq.o main.o iface.o netlink.o server.o address.o util.o prioq. prioq-test: prioq-test.o prioq.o $(CC) -o $@ $^ $(LIBS) - strlst-test: strlst-test.o strlst.o $(CC) -o $@ $^ $(LIBS) +domain-test: domain-test.o util.o + $(CC) -o $@ $^ $(LIBS) + +dns-test: dns-test.o util.o dns.o rr.o strlst.o + $(CC) -o $@ $^ $(LIBS) + *.o: *.h diff --git a/dns-test.c b/dns-test.c new file mode 100644 index 0000000..5d09b4a --- /dev/null +++ b/dns-test.c @@ -0,0 +1,29 @@ +#include "dns.h" +#include "util.h" + +int main(int argc, char *argv[]) { + gchar t[256]; + flxDnsPacket *p; + + p = flx_dns_packet_new(8000); + + flx_dns_packet_append_name(p, "hello.hello.hello.de."); + flx_dns_packet_append_name(p, "this is a test.hello.de."); + flx_dns_packet_append_name(p, "this\\.is\\.a\\.test\\.with\\.dots.hello.de."); + flx_dns_packet_append_name(p, "this\\\\is another\\ \\test.hello.de."); + + flx_hexdump(FLX_DNS_PACKET_DATA(p), p->size); + + flx_dns_packet_consume_name(p, t, sizeof(t)); + g_message(">%s<", t); + flx_dns_packet_consume_name(p, t, sizeof(t)); + g_message(">%s<", t); + flx_dns_packet_consume_name(p, t, sizeof(t)); + g_message(">%s<", t); + flx_dns_packet_consume_name(p, t, sizeof(t)); + g_message(">%s<", t); + + + flx_dns_packet_free(p); + return 0; +} diff --git a/dns.c b/dns.c index 11eb005..f55ae78 100644 --- a/dns.c +++ b/dns.c @@ -63,6 +63,48 @@ guint16 flx_dns_packet_get_field(flxDnsPacket *p, guint index) { return g_ntohs(((guint16*) FLX_DNS_PACKET_DATA(p))[index]); } +/* Read the first label from string dest, unescape "\" and append it to *name */ +static gchar *unescape_label(gchar *dest, guint size, const gchar **name) { + guint i = 0; + gchar *d; + + g_assert(dest); + g_assert(size > 0); + g_assert(name); + g_assert(*name); + + d = dest; + + for (;;) { + if (i >= size) + return NULL; + + if (**name == '.') { + (*name)++; + break; + } + + if (**name == 0) + break; + + if (**name == '\\') { + (*name) ++; + + if (**name == 0) + break; + } + + *(d++) = *((*name) ++); + i++; + } + + g_assert(i < size); + + *d = 0; + + return dest; +} + guint8* flx_dns_packet_append_name(flxDnsPacket *p, const gchar *name) { guint8 *d, *f = NULL; guint saved_size; @@ -72,9 +114,11 @@ guint8* flx_dns_packet_append_name(flxDnsPacket *p, const gchar *name) { saved_size = p->size; - for (;;) { + while (*name) { guint n; guint8* prev; + const gchar *pname; + char label[64]; /* Check whether we can compress this name. */ @@ -98,41 +142,25 @@ guint8* flx_dns_packet_append_name(flxDnsPacket *p, const gchar *name) { return f; } } + + pname = name; - n = strcspn(name, "."); - if (!n || n > 63) - goto fail; - - if (!(d = flx_dns_packet_extend(p, n+1))) + if (!(unescape_label(label, sizeof(label), &name))) goto fail; - - if (!f) - f = d; - d[0] = n; - memcpy(d+1, name, n); + if (!(d = flx_dns_packet_append_string(p, label))) + goto fail; + if (!p->name_table) p->name_table = g_hash_table_new_full((GHashFunc) flx_domain_hash, (GEqualFunc) flx_domain_equal, g_free, NULL); - g_hash_table_insert(p->name_table, g_strdup(name), d); - - name += n; - - /* no trailing dot */ - if (!*name) - break; - - name ++; - - /* trailing dot */ - if (!*name) - break; + g_hash_table_insert(p->name_table, g_strdup(pname), d); } if (!(d = flx_dns_packet_extend(p, 1))) goto fail; - d[0] = 0; + *d = 0; return f; @@ -141,25 +169,6 @@ fail: return NULL; } -guint8 *flx_dns_packet_append_name_compressed(flxDnsPacket *p, const gchar *name, guint8 *prev) { - guint16 *d; - signed long k; - g_assert(p); - - if (!prev) - return flx_dns_packet_append_name(p, name); - - k = prev - FLX_DNS_PACKET_DATA(p); - if (k < 0 || k >= 0x4000 || (guint) k >= p->size) - return flx_dns_packet_append_name(p, name); - - if (!(d = (guint16*) flx_dns_packet_extend(p, sizeof(guint16)))) - return NULL; - - *d = g_htons((0xC000 | k)); - return prev; -} - guint8* flx_dns_packet_append_uint16(flxDnsPacket *p, guint16 v) { guint8 *d; g_assert(p); @@ -251,6 +260,42 @@ gint flx_dns_packet_is_query(flxDnsPacket *p) { return !(flx_dns_packet_get_field(p, FLX_DNS_FIELD_FLAGS) & FLX_DNS_FLAG_QR); } +/* Read a label from a DNS packet, escape "\" and ".", append \0 */ +static gchar *escape_label(guint8* src, guint src_length, gchar **ret_name, guint *ret_name_length) { + gchar *r; + + g_assert(src); + g_assert(ret_name); + g_assert(*ret_name); + g_assert(ret_name_length); + g_assert(*ret_name_length > 0); + + r = *ret_name; + + while (src_length > 0) { + if (*src == '.' || *src == '\\') { + if (*ret_name_length < 3) + return NULL; + + *((*ret_name) ++) = '\\'; + (*ret_name_length) --; + } + + if (*ret_name_length < 2) + return NULL; + + *((*ret_name)++) = *src; + (*ret_name_length) --; + + src_length --; + src++; + } + + **ret_name = 0; + + return r; +} + static gint consume_labels(flxDnsPacket *p, guint index, gchar *ret_name, guint l) { gint ret = 0; int compressed = 0; @@ -294,10 +339,10 @@ static gint consume_labels(flxDnsPacket *p, guint index, gchar *ret_name, guint } else first_label = 0; - memcpy(ret_name, FLX_DNS_PACKET_DATA(p) + index, n); + if (!(escape_label(FLX_DNS_PACKET_DATA(p) + index, n, &ret_name, &l))) + return -1; + index += n; - ret_name += n; - l -= n; if (!compressed) ret += n; diff --git a/dns.h b/dns.h index 706b8c6..3a50bb0 100644 --- a/dns.h +++ b/dns.h @@ -29,7 +29,6 @@ guint8 *flx_dns_packet_extend(flxDnsPacket *p, guint l); guint8 *flx_dns_packet_append_uint16(flxDnsPacket *p, guint16 v); guint8 *flx_dns_packet_append_uint32(flxDnsPacket *p, guint32 v); guint8 *flx_dns_packet_append_name(flxDnsPacket *p, const gchar *name); -guint8 *flx_dns_packet_append_name_compressed(flxDnsPacket *p, const gchar *name, guint8 *prev); guint8 *flx_dns_packet_append_bytes(flxDnsPacket *p, gconstpointer, guint l); guint8* flx_dns_packet_append_key(flxDnsPacket *p, flxKey *k); guint8* flx_dns_packet_append_record(flxDnsPacket *p, flxRecord *r, gboolean cache_flush); diff --git a/domain-test.c b/domain-test.c new file mode 100644 index 0000000..ab02943 --- /dev/null +++ b/domain-test.c @@ -0,0 +1,19 @@ +#include "util.h" + +int main(int argc, char *argv[]) { + gchar *s; + + g_message("host name: %s", s = flx_get_host_name()); + g_free(s); + + g_message("%s", s = flx_normalize_name("foo.foo.")); + g_free(s); + + g_message("%s", s = flx_normalize_name("foo.foo.")); + g_free(s); + + + g_message("%i", flx_domain_equal("\\aaa bbb\\.cccc\\\\.dee.fff.", "aaa\\ bbb\\.cccc\\\\.dee.fff")); + + return 0; +} diff --git a/psched.c b/psched.c index e9f77b9..661a5f9 100644 --- a/psched.c +++ b/psched.c @@ -612,7 +612,7 @@ static guint8* packet_add_probe_query(flxPacketScheduler *s, flxDnsPacket *p, fl continue; /* Does the record match the probe? */ - if (k->class != pj->record->key->class || strcmp(k->name, pj->record->key->name)) + if (k->class != pj->record->key->class || flx_domain_equal(k->name, pj->record->key->name)) continue; /* This job wouldn't fit in */ diff --git a/rr.c b/rr.c index f72557b..2c5a044 100644 --- a/rr.c +++ b/rr.c @@ -214,7 +214,7 @@ gboolean flx_key_equal(const flxKey *a, const flxKey *b) { /* g_message("equal: %p %p", a, b); */ - return strcmp(a->name, b->name) == 0 && + return flx_domain_equal(a->name, b->name) == 0 && a->type == b->type && a->class == b->class; } @@ -227,7 +227,7 @@ gboolean flx_key_pattern_match(const flxKey *pattern, const flxKey *k) { g_assert(!flx_key_is_pattern(k)); - return strcmp(pattern->name, k->name) == 0 && + return flx_domain_equal(pattern->name, k->name) == 0 && (pattern->type == k->type || pattern->type == FLX_DNS_TYPE_ANY) && pattern->class == k->class; } @@ -242,7 +242,7 @@ gboolean flx_key_is_pattern(const flxKey *k) { guint flx_key_hash(const flxKey *k) { g_assert(k); - return g_str_hash(k->name) + k->type + k->class; + return flx_domain_hash(k->name) + k->type + k->class; } static gboolean rdata_equal(const flxRecord *a, const flxRecord *b) { @@ -265,11 +265,11 @@ static gboolean rdata_equal(const flxRecord *a, const flxRecord *b) { a->data.srv.priority == b->data.srv.priority && a->data.srv.weight == b->data.srv.weight && a->data.srv.port == b->data.srv.port && - !strcmp(a->data.srv.name, b->data.srv.name); + flx_domain_equal(a->data.srv.name, b->data.srv.name); case FLX_DNS_TYPE_PTR: case FLX_DNS_TYPE_CNAME: - return !strcmp(a->data.ptr.name, b->data.ptr.name); + return flx_domain_equal(a->data.ptr.name, b->data.ptr.name); case FLX_DNS_TYPE_HINFO: return diff --git a/server.c b/server.c index 56e4d81..cd97262 100644 --- a/server.c +++ b/server.c @@ -300,10 +300,9 @@ flxServer *flx_server_new(GMainContext *c) { /* Get host name */ hn = flx_get_host_name(); - if ((e = strchr(hn, '.'))) - *e = 0; + hn[strcspn(hn, ".")] = 0; - s->hostname = g_strdup_printf("%s.local.", hn); + s->hostname = g_strdup_printf("%slocal.", hn); g_free(hn); add_default_entries(s); diff --git a/util.c b/util.c index 0fcedc7..2cd0a87 100644 --- a/util.c +++ b/util.c @@ -2,13 +2,20 @@ #include #include #include +#include +#include #include "util.h" gchar *flx_get_host_name(void) { +#ifdef HOST_NAME_MAX + char t[HOST_NAME_MAX]; +#else char t[256]; +#endif gethostname(t, sizeof(t)); - return g_strndup(t, sizeof(t)); + t[sizeof(t)-1] = 0; + return flx_normalize_name(t); } gchar *flx_normalize_name(const gchar *s) { @@ -127,24 +134,37 @@ gint flx_age(const GTimeVal *a) { } gboolean flx_domain_equal(const gchar *a, const gchar *b) { + int escaped_a = 0, escaped_b = 0; g_assert(a); g_assert(b); for (;;) { + /* Check for escape characters "\" */ + if ((escaped_a = *a == '\\')) + a ++; + + if ((escaped_b = *b == '\\')) + b++; + + /* Check for string end */ if (*a == 0 && *b == 0) return TRUE; - if (*a == 0 && *b == '.' && *(b+1) == 0) + if (*a == 0 && !escaped_b && *b == '.' && *(b+1) == 0) return TRUE; - if (*a == '.' && *(a+1) == 0 && *b == 0) + if (!escaped_a && *a == '.' && *(a+1) == 0 && *b == 0) return TRUE; - if (*a != *b) + /* Compare characters */ + if (escaped_a == escaped_b && *a != *b) return FALSE; + + /* Next characters */ a++; b++; + } } @@ -156,3 +176,36 @@ guint flx_domain_hash(const gchar *p) { return g_int_hash(t); } +void flx_hexdump(gconstpointer p, guint size) { + const guint8 *c = p; + g_assert(p); + + printf("Dumping %u bytes from %p:\n", size, p); + + while (size > 0) { + guint i; + + for (i = 0; i < 16; i++) { + if (i < size) + printf("%02x ", c[i]); + else + printf(" "); + } + + for (i = 0; i < 16; i++) { + if (i < size) + printf("%c", c[i] >= 32 && c[i] < 127 ? c[i] : '.'); + else + printf(" "); + } + + printf("\n"); + + c += 16; + + if (size <= 16) + break; + + size -= 16; + } +} diff --git a/util.h b/util.h index 53884cb..335ab82 100644 --- a/util.h +++ b/util.h @@ -20,4 +20,6 @@ gint flx_age(const GTimeVal *a); guint flx_domain_hash(const gchar *p); gboolean flx_domain_equal(const gchar *a, const gchar *b); +void flx_hexdump(gconstpointer p, guint size); + #endif -- 2.39.5