From c5544522f6409095627dc3d1129560195ab4ec40 Mon Sep 17 00:00:00 2001 From: Trent Lloyd Date: Sat, 4 Jun 2005 18:56:52 +0000 Subject: * Split out a fair bit of avahi-core into avahi-common for use by the client library git-svn-id: file:///home/lennart/svn/public/avahi/trunk@98 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe --- avahi-common/Makefile.am | 81 +++++ avahi-common/address.c | 166 +++++++++ avahi-common/address.h | 62 ++++ avahi-common/alternative-test.c | 48 +++ avahi-common/alternative.c | 81 +++++ avahi-common/alternative.h | 39 +++ avahi-common/dns-test.c | 60 ++++ avahi-common/dns.c | 744 ++++++++++++++++++++++++++++++++++++++++ avahi-common/dns.h | 105 ++++++ avahi-common/domain-test.c | 48 +++ avahi-common/rr.c | 551 +++++++++++++++++++++++++++++ avahi-common/rr.h | 140 ++++++++ avahi-common/strlst-test.c | 85 +++++ avahi-common/strlst.c | 245 +++++++++++++ avahi-common/strlst.h | 53 +++ avahi-common/util.c | 428 +++++++++++++++++++++++ avahi-common/util.h | 57 +++ 17 files changed, 2993 insertions(+) create mode 100644 avahi-common/Makefile.am create mode 100644 avahi-common/address.c create mode 100644 avahi-common/address.h create mode 100644 avahi-common/alternative-test.c create mode 100644 avahi-common/alternative.c create mode 100644 avahi-common/alternative.h create mode 100644 avahi-common/dns-test.c create mode 100644 avahi-common/dns.c create mode 100644 avahi-common/dns.h create mode 100644 avahi-common/domain-test.c create mode 100644 avahi-common/rr.c create mode 100644 avahi-common/rr.h create mode 100644 avahi-common/strlst-test.c create mode 100644 avahi-common/strlst.c create mode 100644 avahi-common/strlst.h create mode 100644 avahi-common/util.c create mode 100644 avahi-common/util.h (limited to 'avahi-common') diff --git a/avahi-common/Makefile.am b/avahi-common/Makefile.am new file mode 100644 index 0000000..8c58757 --- /dev/null +++ b/avahi-common/Makefile.am @@ -0,0 +1,81 @@ +# $Id: Makefile.am 90 2005-05-23 16:15:12Z lennart $ +# +# This file is part of avahi. +# +# avahi is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# avahi is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +# License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with avahi; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA. + +AM_CFLAGS=-I$(top_srcdir) -D_GNU_SOURCE + +# GLIB 2.0 +AM_CFLAGS+=$(GLIB20_CFLAGS) +AM_LDADD=$(GLIB20_LIBS) + +# This cool debug trap works on i386/gcc only +AM_CFLAGS+='-DDEBUG_TRAP=__asm__("int $$3")' + +avahi_commonincludedir=$(includedir)/avahi-common + +avahi_commoninclude_HEADERS = + strlst.h \ + address.h \ + alternative.h \ + rr.h \ + util.h \ + dns.h + +noinst_PROGRAMS = \ + strlst-test \ + dns-test \ + domain-test \ + alternative-test + +lib_LTLIBRARIES = \ + libavahi-common.la + +libavahi_common_la_SOURCES = \ + address.c address.h \ + strlst.c strlst.h \ + alternative.c alternative.h \ + rr.c rr.h \ + dns.c dns.h \ + util.c util.h + +strlst_test_SOURCES = \ + strlst.c strlst.h \ + strlst-test.c +strlst_test_CFLAGS = $(AM_CFLAGS) +strlst_test_LDADD = $(AM_LDADD) + +dns_test_SOURCES = \ + util.c util.h \ + dns.c dns.h \ + rr.c rr.h \ + strlst.c strlst \ + dns-test.c +dns_test_CFLAGS = $(AM_CFLAGS) +dns_test_LDADD = $(AM_LDADD) + +alternative_test_SOURCES = \ + alternative.c alternative.h \ + alternative-test.c +alternative_test_CFLAGS = $(AM_CFLAGS) +alternative_test_LDADD = $(AM_LDADD) + +domain_test_SOURCES = \ + util.c util.h \ + domain-test.c +domain_test_CFLAGS = $(AM_CFLAGS) +domain_test_LDADD = $(AM_LDADD) diff --git a/avahi-common/address.c b/avahi-common/address.c new file mode 100644 index 0000000..3484707 --- /dev/null +++ b/avahi-common/address.c @@ -0,0 +1,166 @@ +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "address.h" + +guint avahi_address_get_size(const AvahiAddress *a) { + g_assert(a); + + if (a->family == AF_INET) + return 4; + else if (a->family == AF_INET6) + return 16; + + return 0; +} + +gint avahi_address_cmp(const AvahiAddress *a, const AvahiAddress *b) { + g_assert(a); + g_assert(b); + + if (a->family != b->family) + return -1; + + return memcmp(a->data.data, b->data.data, avahi_address_get_size(a)); +} + +gchar *avahi_address_snprint(char *s, guint length, const AvahiAddress *a) { + g_assert(s); + g_assert(length); + g_assert(a); + return (gchar*) inet_ntop(a->family, a->data.data, s, length); +} + +gchar* avahi_reverse_lookup_name_ipv4(const AvahiIPv4Address *a) { + guint32 n = ntohl(a->address); + g_assert(a); + + return g_strdup_printf("%u.%u.%u.%u.in-addr.arpa", n & 0xFF, (n >> 8) & 0xFF, (n >> 16) & 0xFF, n >> 24); +} + +static gchar *reverse_lookup_name_ipv6(const AvahiIPv6Address *a, const gchar *suffix) { + + return g_strdup_printf("%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%s", + a->address[15] & 0xF, + a->address[15] >> 4, + a->address[14] & 0xF, + a->address[14] >> 4, + a->address[13] & 0xF, + a->address[13] >> 4, + a->address[12] & 0xF, + a->address[12] >> 4, + a->address[11] & 0xF, + a->address[11] >> 4, + a->address[10] & 0xF, + a->address[10] >> 4, + a->address[9] & 0xF, + a->address[9] >> 4, + a->address[8] & 0xF, + a->address[8] >> 4, + a->address[7] & 0xF, + a->address[7] >> 4, + a->address[6] & 0xF, + a->address[6] >> 4, + a->address[5] & 0xF, + a->address[5] >> 4, + a->address[4] & 0xF, + a->address[4] >> 4, + a->address[3] & 0xF, + a->address[3] >> 4, + a->address[2] & 0xF, + a->address[2] >> 4, + a->address[1] & 0xF, + a->address[1] >> 4, + a->address[0] & 0xF, + a->address[0] >> 4, + suffix); +} + +gchar *avahi_reverse_lookup_name_ipv6_arpa(const AvahiIPv6Address *a) { + return reverse_lookup_name_ipv6(a, "ip6.arpa"); +} + +gchar *avahi_reverse_lookup_name_ipv6_int(const AvahiIPv6Address *a) { + return reverse_lookup_name_ipv6(a, "ip6.int"); +} + +AvahiAddress *avahi_address_parse(const char *s, guchar family, AvahiAddress *ret_addr) { + g_assert(ret_addr); + g_assert(s); + + if (inet_pton(family, s, ret_addr->data.data) < 0) + return NULL; + + ret_addr->family = family; + + return ret_addr; +} + +AvahiAddress *avahi_address_from_sockaddr(const struct sockaddr* sa, AvahiAddress *ret_addr) { + g_assert(sa); + g_assert(ret_addr); + + g_assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6); + + ret_addr->family = sa->sa_family; + + if (sa->sa_family == AF_INET) + memcpy(&ret_addr->data.ipv4, &((struct sockaddr_in*) sa)->sin_addr, sizeof(ret_addr->data.ipv4)); + else + memcpy(&ret_addr->data.ipv6, &((struct sockaddr_in6*) sa)->sin6_addr, sizeof(ret_addr->data.ipv6)); + + return ret_addr; +} + +guint16 avahi_port_from_sockaddr(const struct sockaddr* sa) { + g_assert(sa); + + g_assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6); + + if (sa->sa_family == AF_INET) + return ntohs(((struct sockaddr_in*) sa)->sin_port); + else + return ntohs(((struct sockaddr_in6*) sa)->sin6_port); +} + +gboolean avahi_address_is_ipv4_in_ipv6(const AvahiAddress *a) { + static const guint8 ipv4_in_ipv6[] = { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF }; + + g_assert(a); + + if (a->family != AF_INET6) + return FALSE; + + return memcmp(a->data.ipv6.address, ipv4_in_ipv6, sizeof(ipv4_in_ipv6)) == 0; +} + diff --git a/avahi-common/address.h b/avahi-common/address.h new file mode 100644 index 0000000..22cb74b --- /dev/null +++ b/avahi-common/address.h @@ -0,0 +1,62 @@ +#ifndef fooaddresshfoo +#define fooaddresshfoo + +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +typedef struct { + guint32 address; +} AvahiIPv4Address; + +typedef struct { + guint8 address[16]; +} AvahiIPv6Address; + +typedef struct { + guchar family; + + union { + AvahiIPv6Address ipv6; + AvahiIPv4Address ipv4; + guint8 data[1]; + } data; +} AvahiAddress; + +guint avahi_address_get_size(const AvahiAddress *a); +gint avahi_address_cmp(const AvahiAddress *a, const AvahiAddress *b); + +gchar *avahi_address_snprint(char *ret_s, guint length, const AvahiAddress *a); + +AvahiAddress *avahi_address_parse(const char *s, guchar family, AvahiAddress *ret_addr); + +AvahiAddress *avahi_address_from_sockaddr(const struct sockaddr* sa, AvahiAddress *ret_addr); +guint16 avahi_port_from_sockaddr(const struct sockaddr* sa); + +gchar* avahi_reverse_lookup_name_ipv4(const AvahiIPv4Address *a); +gchar* avahi_reverse_lookup_name_ipv6_arpa(const AvahiIPv6Address *a); +gchar* avahi_reverse_lookup_name_ipv6_int(const AvahiIPv6Address *a); + +gboolean avahi_address_is_ipv4_in_ipv6(const AvahiAddress *a); + +#endif diff --git a/avahi-common/alternative-test.c b/avahi-common/alternative-test.c new file mode 100644 index 0000000..9e26d24 --- /dev/null +++ b/avahi-common/alternative-test.c @@ -0,0 +1,48 @@ +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "alternative.h" + +int main(int argc, char *argv[]) { + gchar *r = NULL; + gint i, k; + + for (k = 0; k < 2; k++) { + + for (i = 0; i < 20; i++) { + gchar *n; + + n = i == 0 ? g_strdup("gurke") : (k ? avahi_alternative_service_name(r) : avahi_alternative_host_name(r)); + g_free(r); + r = n; + + printf("%s\n", r); + } + } + + g_free(r); +} diff --git a/avahi-common/alternative.c b/avahi-common/alternative.c new file mode 100644 index 0000000..c9a0f48 --- /dev/null +++ b/avahi-common/alternative.c @@ -0,0 +1,81 @@ +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "alternative.h" + +gchar * avahi_alternative_host_name(const gchar *s) { + const gchar *p, *e = NULL; + gchar *c, *r; + gint n; + + g_assert(s); + + for (p = s; *p; p++) + if (!isdigit(*p)) + e = p+1; + + if (e && *e) + n = atoi(e)+1; + else + n = 2; + + c = e ? g_strndup(s, e-s) : g_strdup(s); + r = g_strdup_printf("%s%i", c, n); + g_free(c); + + return r; + +} + +gchar *avahi_alternative_service_name(const gchar *s) { + const gchar *e; + g_assert(s); + + if ((e = strstr(s, " #"))) { + const gchar *n, *p; + e += 2; + + while ((n = strstr(e, " #"))) + e = n + 2; + + for (p = e; *p; p++) + if (!isdigit(*p)) { + e = NULL; + break; + } + } + + if (e) { + gchar *r, *c = g_strndup(s, e-s); + r = g_strdup_printf("%s%i", c, atoi(e)+1); + g_free(c); + return r; + } else + return g_strdup_printf("%s #2", s); +} diff --git a/avahi-common/alternative.h b/avahi-common/alternative.h new file mode 100644 index 0000000..3aed5b2 --- /dev/null +++ b/avahi-common/alternative.h @@ -0,0 +1,39 @@ +#ifndef fooalternativehfoo +#define fooalternativehfoo + +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +/** Find an alternative for the specified host name. If called with an + * original host name, "2" is appended, Afterwards the number is + * increased on each call. (i.e. "foo" becomes "foo2" becomes "foo3" + * and so on.)*/ +gchar *avahi_alternative_host_name(const gchar *s); + +/** Find an alternative for the specified service name. If called with + an original service name, " #2" is appended. Afterwards the number + is increased on each call (i.e. "foo" becomes "foo #2" becomes + "foo #3" and so on.)*/ +gchar *avahi_alternative_service_name(const gchar *s); + +#endif diff --git a/avahi-common/dns-test.c b/avahi-common/dns-test.c new file mode 100644 index 0000000..aa36f2a --- /dev/null +++ b/avahi-common/dns-test.c @@ -0,0 +1,60 @@ +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "dns.h" +#include "util.h" + +int main(int argc, char *argv[]) { + gchar t[256], *a, *b, *c, *d; + AvahiDnsPacket *p; + + p = avahi_dns_packet_new(0); + + avahi_dns_packet_append_name(p, a = "hello.hello.hello.de."); + avahi_dns_packet_append_name(p, b = "this is a test.hello.de."); + avahi_dns_packet_append_name(p, c = "this\\.is\\.a\\.test\\.with\\.dots.hello.de."); + avahi_dns_packet_append_name(p, d = "this\\\\is another\\ \\test.hello.de."); + + avahi_hexdump(AVAHI_DNS_PACKET_DATA(p), p->size); + + avahi_dns_packet_consume_name(p, t, sizeof(t)); + g_message(">%s<", t); + g_assert(avahi_domain_equal(a, t)); + + avahi_dns_packet_consume_name(p, t, sizeof(t)); + g_message(">%s<", t); + g_assert(avahi_domain_equal(b, t)); + + avahi_dns_packet_consume_name(p, t, sizeof(t)); + g_message(">%s<", t); + g_assert(avahi_domain_equal(c, t)); + + avahi_dns_packet_consume_name(p, t, sizeof(t)); + g_message(">%s<", t); + g_assert(avahi_domain_equal(d, t)); + + avahi_dns_packet_free(p); + return 0; +} diff --git a/avahi-common/dns.c b/avahi-common/dns.c new file mode 100644 index 0000000..9e19c9e --- /dev/null +++ b/avahi-common/dns.c @@ -0,0 +1,744 @@ +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include + +#include "dns.h" +#include "util.h" + +AvahiDnsPacket* avahi_dns_packet_new(guint mtu) { + AvahiDnsPacket *p; + guint max_size; + + if (mtu <= 0) + max_size = AVAHI_DNS_PACKET_MAX_SIZE; + else if (mtu >= AVAHI_DNS_PACKET_EXTRA_SIZE) + max_size = mtu - AVAHI_DNS_PACKET_EXTRA_SIZE; + else + max_size = 0; + + if (max_size < AVAHI_DNS_PACKET_HEADER_SIZE) + max_size = AVAHI_DNS_PACKET_HEADER_SIZE; + + p = g_malloc(sizeof(AvahiDnsPacket) + max_size); + p->size = p->rindex = AVAHI_DNS_PACKET_HEADER_SIZE; + p->max_size = max_size; + p->name_table = NULL; + + memset(AVAHI_DNS_PACKET_DATA(p), 0, p->size); + return p; +} + +AvahiDnsPacket* avahi_dns_packet_new_query(guint mtu) { + AvahiDnsPacket *p; + + p = avahi_dns_packet_new(mtu); + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)); + return p; +} + +AvahiDnsPacket* avahi_dns_packet_new_response(guint mtu, gboolean aa) { + AvahiDnsPacket *p; + + p = avahi_dns_packet_new(mtu); + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(1, 0, aa, 0, 0, 0, 0, 0, 0, 0)); + return p; +} + +AvahiDnsPacket* avahi_dns_packet_new_reply(AvahiDnsPacket* p, guint mtu, gboolean copy_queries, gboolean aa) { + AvahiDnsPacket *r; + g_assert(p); + + r = avahi_dns_packet_new_response(mtu, aa); + + if (copy_queries) { + guint n, saved_rindex; + + saved_rindex = p->rindex; + p->rindex = AVAHI_DNS_PACKET_HEADER_SIZE; + + for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n--) { + AvahiKey *k; + gboolean unicast_response; + + if ((k = avahi_dns_packet_consume_key(p, &unicast_response))) { + avahi_dns_packet_append_key(r, k, unicast_response); + avahi_key_unref(k); + } + } + + p->rindex = saved_rindex; + + avahi_dns_packet_set_field(r, AVAHI_DNS_FIELD_QDCOUNT, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT)); + } + + avahi_dns_packet_set_field(r, AVAHI_DNS_FIELD_ID, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)); + + avahi_dns_packet_set_field(r, AVAHI_DNS_FIELD_FLAGS, + (avahi_dns_packet_get_field(r, AVAHI_DNS_FIELD_FLAGS) & ~AVAHI_DNS_FLAG_OPCODE) | + (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_OPCODE)); + + return r; +} + + +void avahi_dns_packet_free(AvahiDnsPacket *p) { + g_assert(p); + + if (p->name_table) + g_hash_table_destroy(p->name_table); + + g_free(p); +} + +void avahi_dns_packet_set_field(AvahiDnsPacket *p, guint index, guint16 v) { + g_assert(p); + g_assert(index < AVAHI_DNS_PACKET_HEADER_SIZE); + + ((guint16*) AVAHI_DNS_PACKET_DATA(p))[index] = g_htons(v); +} + +guint16 avahi_dns_packet_get_field(AvahiDnsPacket *p, guint index) { + g_assert(p); + g_assert(index < AVAHI_DNS_PACKET_HEADER_SIZE); + + return g_ntohs(((guint16*) AVAHI_DNS_PACKET_DATA(p))[index]); +} + +void avahi_dns_packet_inc_field(AvahiDnsPacket *p, guint index) { + g_assert(p); + + avahi_dns_packet_set_field(p, index, avahi_dns_packet_get_field(p, index) + 1); +} + +guint8* avahi_dns_packet_append_name(AvahiDnsPacket *p, const gchar *name) { + guint8 *d, *saved_ptr = NULL; + guint saved_size; + + g_assert(p); + g_assert(name); + + saved_size = p->size; + saved_ptr = avahi_dns_packet_extend(p, 0); + + while (*name) { + guint8* prev; + const gchar *pname; + gchar label[64]; + + /* Check whether we can compress this name. */ + + if (p->name_table && (prev = g_hash_table_lookup(p->name_table, name))) { + guint index; + + g_assert(prev >= AVAHI_DNS_PACKET_DATA(p)); + index = (guint) (prev - AVAHI_DNS_PACKET_DATA(p)); + + g_assert(index < p->size); + + if (index < 0x4000) { + guint16 *t; + if (!(t = (guint16*) avahi_dns_packet_extend(p, sizeof(guint16)))) + return NULL; + + *t = g_htons((0xC000 | index)); + return saved_ptr; + } + } + + pname = name; + + if (!(avahi_unescape_label(&name, label, sizeof(label)))) + goto fail; + + if (!(d = avahi_dns_packet_append_string(p, label))) + goto fail; + + if (!p->name_table) + /* This works only for normalized domain names */ + p->name_table = g_hash_table_new_full((GHashFunc) g_str_hash, (GEqualFunc) g_str_equal, g_free, NULL); + + g_hash_table_insert(p->name_table, g_strdup(pname), d); + } + + if (!(d = avahi_dns_packet_extend(p, 1))) + goto fail; + + *d = 0; + + return saved_ptr; + +fail: + p->size = saved_size; + return NULL; +} + +guint8* avahi_dns_packet_append_uint16(AvahiDnsPacket *p, guint16 v) { + guint8 *d; + g_assert(p); + + if (!(d = avahi_dns_packet_extend(p, sizeof(guint16)))) + return NULL; + + *((guint16*) d) = g_htons(v); + return d; +} + +guint8 *avahi_dns_packet_append_uint32(AvahiDnsPacket *p, guint32 v) { + guint8 *d; + g_assert(p); + + if (!(d = avahi_dns_packet_extend(p, sizeof(guint32)))) + return NULL; + + *((guint32*) d) = g_htonl(v); + + return d; +} + +guint8 *avahi_dns_packet_append_bytes(AvahiDnsPacket *p, gconstpointer b, guint l) { + guint8* d; + + g_assert(p); + g_assert(b); + g_assert(l); + + if (!(d = avahi_dns_packet_extend(p, l))) + return NULL; + + memcpy(d, b, l); + return d; +} + +guint8* avahi_dns_packet_append_string(AvahiDnsPacket *p, const gchar *s) { + guint8* d; + guint k; + + g_assert(p); + g_assert(s); + + if ((k = strlen(s)) >= 255) + k = 255; + + if (!(d = avahi_dns_packet_extend(p, k+1))) + return NULL; + + *d = (guint8) k; + memcpy(d+1, s, k); + + return d; +} + +guint8 *avahi_dns_packet_extend(AvahiDnsPacket *p, guint l) { + guint8 *d; + + g_assert(p); + + if (p->size+l > p->max_size) + return NULL; + + d = AVAHI_DNS_PACKET_DATA(p) + p->size; + p->size += l; + + return d; +} + +gint avahi_dns_packet_check_valid(AvahiDnsPacket *p) { + guint16 flags; + g_assert(p); + + if (p->size < 12) + return -1; + + flags = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS); + + if (flags & AVAHI_DNS_FLAG_OPCODE || flags & AVAHI_DNS_FLAG_RCODE) + return -1; + + return 0; +} + +gint avahi_dns_packet_is_query(AvahiDnsPacket *p) { + g_assert(p); + + return !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_QR); +} + +static gint consume_labels(AvahiDnsPacket *p, guint index, gchar *ret_name, guint l) { + gint ret = 0; + int compressed = 0; + int first_label = 1; + g_assert(p && ret_name && l); + + for (;;) { + guint8 n; + + if (index+1 > p->size) + return -1; + + n = AVAHI_DNS_PACKET_DATA(p)[index]; + + if (!n) { + index++; + if (!compressed) + ret++; + + if (l < 1) + return -1; + *ret_name = 0; + + return ret; + + } else if (n <= 63) { + /* Uncompressed label */ + index++; + if (!compressed) + ret++; + + if (index + n > p->size) + return -1; + + if ((guint) n + 1 > l) + return -1; + + if (!first_label) { + *(ret_name++) = '.'; + l--; + } else + first_label = 0; + + if (!(avahi_escape_label(AVAHI_DNS_PACKET_DATA(p) + index, n, &ret_name, &l))) + return -1; + + index += n; + + if (!compressed) + ret += n; + } else if ((n & 0xC0) == 0xC0) { + /* Compressed label */ + + if (index+2 > p->size) + return -1; + + index = ((guint) (AVAHI_DNS_PACKET_DATA(p)[index] & ~0xC0)) << 8 | AVAHI_DNS_PACKET_DATA(p)[index+1]; + + if (!compressed) + ret += 2; + + compressed = 1; + } else + return -1; + } +} + +gint avahi_dns_packet_consume_name(AvahiDnsPacket *p, gchar *ret_name, guint l) { + gint r; + + if ((r = consume_labels(p, p->rindex, ret_name, l)) < 0) + return -1; + + p->rindex += r; + return 0; +} + +gint avahi_dns_packet_consume_uint16(AvahiDnsPacket *p, guint16 *ret_v) { + g_assert(p); + g_assert(ret_v); + + if (p->rindex + sizeof(guint16) > p->size) + return -1; + + *ret_v = g_ntohs(*((guint16*) (AVAHI_DNS_PACKET_DATA(p) + p->rindex))); + p->rindex += sizeof(guint16); + + return 0; +} + +gint avahi_dns_packet_consume_uint32(AvahiDnsPacket *p, guint32 *ret_v) { + g_assert(p); + g_assert(ret_v); + + if (p->rindex + sizeof(guint32) > p->size) + return -1; + + *ret_v = g_ntohl(*((guint32*) (AVAHI_DNS_PACKET_DATA(p) + p->rindex))); + p->rindex += sizeof(guint32); + + return 0; +} + +gint avahi_dns_packet_consume_bytes(AvahiDnsPacket *p, gpointer ret_data, guint l) { + g_assert(p); + g_assert(ret_data); + g_assert(l > 0); + + if (p->rindex + l > p->size) + return -1; + + memcpy(ret_data, AVAHI_DNS_PACKET_DATA(p) + p->rindex, l); + p->rindex += l; + + return 0; +} + +gint avahi_dns_packet_consume_string(AvahiDnsPacket *p, gchar *ret_string, guint l) { + guint k; + + g_assert(p); + g_assert(ret_string); + g_assert(l > 0); + + if (p->rindex >= p->size) + return -1; + + k = AVAHI_DNS_PACKET_DATA(p)[p->rindex]; + + if (p->rindex+1+k > p->size) + return -1; + + if (l > k+1) + l = k+1; + + memcpy(ret_string, AVAHI_DNS_PACKET_DATA(p)+p->rindex+1, l-1); + ret_string[l-1] = 0; + + + p->rindex += 1+k; + + return 0; + +} + +gconstpointer avahi_dns_packet_get_rptr(AvahiDnsPacket *p) { + g_assert(p); + + if (p->rindex > p->size) + return NULL; + + return AVAHI_DNS_PACKET_DATA(p) + p->rindex; +} + +gint avahi_dns_packet_skip(AvahiDnsPacket *p, guint length) { + g_assert(p); + + if (p->rindex + length > p->size) + return -1; + + p->rindex += length; + return 0; +} + +AvahiRecord* avahi_dns_packet_consume_record(AvahiDnsPacket *p, gboolean *ret_cache_flush) { + gchar name[257], buf[257]; + guint16 type, class; + guint32 ttl; + guint16 rdlength; + AvahiRecord *r = NULL; + gconstpointer start; + + g_assert(p); + g_assert(ret_cache_flush); + +/* g_message("consume_record()"); */ + + if (avahi_dns_packet_consume_name(p, name, sizeof(name)) < 0 || + avahi_dns_packet_consume_uint16(p, &type) < 0 || + avahi_dns_packet_consume_uint16(p, &class) < 0 || + avahi_dns_packet_consume_uint32(p, &ttl) < 0 || + avahi_dns_packet_consume_uint16(p, &rdlength) < 0 || + p->rindex + rdlength > p->size) + goto fail; + +/* g_message("name = %s, rdlength = %u", name, rdlength); */ + + *ret_cache_flush = !!(class & AVAHI_DNS_CACHE_FLUSH); + class &= ~AVAHI_DNS_CACHE_FLUSH; + + start = avahi_dns_packet_get_rptr(p); + + r = avahi_record_new_full(name, class, type); + + switch (type) { + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + +/* g_message("ptr"); */ + + if (avahi_dns_packet_consume_name(p, buf, sizeof(buf)) < 0) + goto fail; + + r->data.ptr.name = g_strdup(buf); + break; + + + case AVAHI_DNS_TYPE_SRV: + +/* g_message("srv"); */ + + if (avahi_dns_packet_consume_uint16(p, &r->data.srv.priority) < 0 || + avahi_dns_packet_consume_uint16(p, &r->data.srv.weight) < 0 || + avahi_dns_packet_consume_uint16(p, &r->data.srv.port) < 0 || + avahi_dns_packet_consume_name(p, buf, sizeof(buf)) < 0) + goto fail; + + r->data.srv.name = g_strdup(buf); + break; + + case AVAHI_DNS_TYPE_HINFO: + +/* g_message("hinfo"); */ + + if (avahi_dns_packet_consume_string(p, buf, sizeof(buf)) < 0) + goto fail; + + r->data.hinfo.cpu = g_strdup(buf); + + if (avahi_dns_packet_consume_string(p, buf, sizeof(buf)) < 0) + goto fail; + + r->data.hinfo.os = g_strdup(buf); + break; + + case AVAHI_DNS_TYPE_TXT: + +/* g_message("txt"); */ + + if (rdlength > 0) { + r->data.txt.string_list = avahi_string_list_parse(avahi_dns_packet_get_rptr(p), rdlength); + + if (avahi_dns_packet_skip(p, rdlength) < 0) + goto fail; + } else + r->data.txt.string_list = NULL; + + break; + + case AVAHI_DNS_TYPE_A: + +/* g_message("A"); */ + + if (avahi_dns_packet_consume_bytes(p, &r->data.a.address, sizeof(AvahiIPv4Address)) < 0) + goto fail; + + break; + + case AVAHI_DNS_TYPE_AAAA: + +/* g_message("aaaa"); */ + + if (avahi_dns_packet_consume_bytes(p, &r->data.aaaa.address, sizeof(AvahiIPv6Address)) < 0) + goto fail; + + break; + + default: + +/* g_message("generic"); */ + + if (rdlength > 0) { + + r->data.generic.data = g_memdup(avahi_dns_packet_get_rptr(p), rdlength); + + if (avahi_dns_packet_skip(p, rdlength) < 0) + goto fail; + } + + break; + } + +/* g_message("%i == %u ?", (guint8*) avahi_dns_packet_get_rptr(p) - (guint8*) start, rdlength); */ + + /* Check if we read enough data */ + if ((guint8*) avahi_dns_packet_get_rptr(p) - (guint8*) start != rdlength) + goto fail; + + r->ttl = ttl; + + return r; + +fail: + if (r) + avahi_record_unref(r); + + return NULL; +} + +AvahiKey* avahi_dns_packet_consume_key(AvahiDnsPacket *p, gboolean *ret_unicast_response) { + gchar name[256]; + guint16 type, class; + + g_assert(p); + g_assert(ret_unicast_response); + + if (avahi_dns_packet_consume_name(p, name, sizeof(name)) < 0 || + avahi_dns_packet_consume_uint16(p, &type) < 0 || + avahi_dns_packet_consume_uint16(p, &class) < 0) + return NULL; + + *ret_unicast_response = !!(class & AVAHI_DNS_UNICAST_RESPONSE); + class &= ~AVAHI_DNS_UNICAST_RESPONSE; + + return avahi_key_new(name, class, type); +} + +guint8* avahi_dns_packet_append_key(AvahiDnsPacket *p, AvahiKey *k, gboolean unicast_response) { + guint8 *t; + guint size; + + g_assert(p); + g_assert(k); + + size = p->size; + + if (!(t = avahi_dns_packet_append_name(p, k->name)) || + !avahi_dns_packet_append_uint16(p, k->type) || + !avahi_dns_packet_append_uint16(p, k->class | (unicast_response ? AVAHI_DNS_UNICAST_RESPONSE : 0))) { + p->size = size; + return NULL; + } + + return t; +} + +guint8* avahi_dns_packet_append_record(AvahiDnsPacket *p, AvahiRecord *r, gboolean cache_flush, guint max_ttl) { + guint8 *t, *l, *start; + guint size; + + g_assert(p); + g_assert(r); + + size = p->size; + + if (!(t = avahi_dns_packet_append_name(p, r->key->name)) || + !avahi_dns_packet_append_uint16(p, r->key->type) || + !avahi_dns_packet_append_uint16(p, cache_flush ? (r->key->class | AVAHI_DNS_CACHE_FLUSH) : (r->key->class &~ AVAHI_DNS_CACHE_FLUSH)) || + !avahi_dns_packet_append_uint32(p, (max_ttl && r->ttl > max_ttl) ? max_ttl : r->ttl) || + !(l = avahi_dns_packet_append_uint16(p, 0))) + goto fail; + + start = avahi_dns_packet_extend(p, 0); + + switch (r->key->type) { + + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME : + + if (!(avahi_dns_packet_append_name(p, r->data.ptr.name))) + goto fail; + + break; + + case AVAHI_DNS_TYPE_SRV: + + if (!avahi_dns_packet_append_uint16(p, r->data.srv.priority) || + !avahi_dns_packet_append_uint16(p, r->data.srv.weight) || + !avahi_dns_packet_append_uint16(p, r->data.srv.port) || + !avahi_dns_packet_append_name(p, r->data.srv.name)) + goto fail; + + break; + + case AVAHI_DNS_TYPE_HINFO: + if (!avahi_dns_packet_append_string(p, r->data.hinfo.cpu) || + !avahi_dns_packet_append_string(p, r->data.hinfo.os)) + goto fail; + + break; + + case AVAHI_DNS_TYPE_TXT: { + + guint8 *data; + guint size; + + size = avahi_string_list_serialize(r->data.txt.string_list, NULL, 0); + +/* g_message("appending string: %u %p", size, r->data.txt.string_list); */ + + if (!(data = avahi_dns_packet_extend(p, size))) + goto fail; + + avahi_string_list_serialize(r->data.txt.string_list, data, size); + break; + } + + + case AVAHI_DNS_TYPE_A: + + if (!avahi_dns_packet_append_bytes(p, &r->data.a.address, sizeof(r->data.a.address))) + goto fail; + + break; + + case AVAHI_DNS_TYPE_AAAA: + + if (!avahi_dns_packet_append_bytes(p, &r->data.aaaa.address, sizeof(r->data.aaaa.address))) + goto fail; + + break; + + default: + + if (r->data.generic.size && + avahi_dns_packet_append_bytes(p, r->data.generic.data, r->data.generic.size)) + goto fail; + + break; + } + + + + + size = avahi_dns_packet_extend(p, 0) - start; + g_assert(size <= 0xFFFF); + +/* g_message("appended %u", size); */ + + * (guint16*) l = g_htons((guint16) size); + + return t; + + +fail: + p->size = size; + return NULL; +} + +gboolean avahi_dns_packet_is_empty(AvahiDnsPacket *p) { + g_assert(p); + + return p->size <= AVAHI_DNS_PACKET_HEADER_SIZE; +} + +guint avahi_dns_packet_space(AvahiDnsPacket *p) { + g_assert(p); + + g_assert(p->size <= p->max_size); + + return p->max_size - p->size; +} diff --git a/avahi-common/dns.h b/avahi-common/dns.h new file mode 100644 index 0000000..03d9798 --- /dev/null +++ b/avahi-common/dns.h @@ -0,0 +1,105 @@ +#ifndef foodnshfoo +#define foodnshfoo + +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include "rr.h" + +#define AVAHI_DNS_PACKET_MAX_SIZE 9000 +#define AVAHI_DNS_PACKET_HEADER_SIZE 12 +#define AVAHI_DNS_PACKET_EXTRA_SIZE 48 + +typedef struct AvahiDnsPacket { + guint size, rindex, max_size; + GHashTable *name_table; /* for name compression */ +} AvahiDnsPacket; + +#define AVAHI_DNS_PACKET_DATA(p) (((guint8*) p) + sizeof(AvahiDnsPacket)) + +AvahiDnsPacket* avahi_dns_packet_new(guint mtu); +AvahiDnsPacket* avahi_dns_packet_new_query(guint mtu); +AvahiDnsPacket* avahi_dns_packet_new_response(guint mtu, gboolean aa); + +AvahiDnsPacket* avahi_dns_packet_new_reply(AvahiDnsPacket* p, guint mtu, gboolean copy_queries, gboolean aa); + +void avahi_dns_packet_free(AvahiDnsPacket *p); +void avahi_dns_packet_set_field(AvahiDnsPacket *p, guint index, guint16 v); +guint16 avahi_dns_packet_get_field(AvahiDnsPacket *p, guint index); +void avahi_dns_packet_inc_field(AvahiDnsPacket *p, guint index); + +guint8 *avahi_dns_packet_extend(AvahiDnsPacket *p, guint l); + +guint8 *avahi_dns_packet_append_uint16(AvahiDnsPacket *p, guint16 v); +guint8 *avahi_dns_packet_append_uint32(AvahiDnsPacket *p, guint32 v); +guint8 *avahi_dns_packet_append_name(AvahiDnsPacket *p, const gchar *name); +guint8 *avahi_dns_packet_append_bytes(AvahiDnsPacket *p, gconstpointer, guint l); +guint8* avahi_dns_packet_append_key(AvahiDnsPacket *p, AvahiKey *k, gboolean unicast_response); +guint8* avahi_dns_packet_append_record(AvahiDnsPacket *p, AvahiRecord *r, gboolean cache_flush, guint max_ttl); +guint8* avahi_dns_packet_append_string(AvahiDnsPacket *p, const gchar *s); + +gint avahi_dns_packet_is_query(AvahiDnsPacket *p); +gint avahi_dns_packet_check_valid(AvahiDnsPacket *p); + +gint avahi_dns_packet_consume_uint16(AvahiDnsPacket *p, guint16 *ret_v); +gint avahi_dns_packet_consume_uint32(AvahiDnsPacket *p, guint32 *ret_v); +gint avahi_dns_packet_consume_name(AvahiDnsPacket *p, gchar *ret_name, guint l); +gint avahi_dns_packet_consume_bytes(AvahiDnsPacket *p, gpointer ret_data, guint l); +AvahiKey* avahi_dns_packet_consume_key(AvahiDnsPacket *p, gboolean *ret_unicast_response); +AvahiRecord* avahi_dns_packet_consume_record(AvahiDnsPacket *p, gboolean *ret_cache_flush); +gint avahi_dns_packet_consume_string(AvahiDnsPacket *p, gchar *ret_string, guint l); + +gconstpointer avahi_dns_packet_get_rptr(AvahiDnsPacket *p); + +gint avahi_dns_packet_skip(AvahiDnsPacket *p, guint length); + +gboolean avahi_dns_packet_is_empty(AvahiDnsPacket *p); +guint avahi_dns_packet_space(AvahiDnsPacket *p); + +#define AVAHI_DNS_FIELD_ID 0 +#define AVAHI_DNS_FIELD_FLAGS 1 +#define AVAHI_DNS_FIELD_QDCOUNT 2 +#define AVAHI_DNS_FIELD_ANCOUNT 3 +#define AVAHI_DNS_FIELD_NSCOUNT 4 +#define AVAHI_DNS_FIELD_ARCOUNT 5 + +#define AVAHI_DNS_FLAG_QR (1 << 15) +#define AVAHI_DNS_FLAG_OPCODE (15 << 11) +#define AVAHI_DNS_FLAG_RCODE (15) +#define AVAHI_DNS_FLAG_TC (1 << 9) +#define AVAHI_DNS_FLAG_AA (1 << 10) + +#define AVAHI_DNS_FLAGS(qr, opcode, aa, tc, rd, ra, z, ad, cd, rcode) \ + (((guint16) !!qr << 15) | \ + ((guint16) (opcode & 15) << 11) | \ + ((guint16) !!aa << 10) | \ + ((guint16) !!tc << 9) | \ + ((guint16) !!rd << 8) | \ + ((guint16) !!ra << 7) | \ + ((guint16) !!ad << 5) | \ + ((guint16) !!cd << 4) | \ + ((guint16) (rd & 15))) + + +#endif + diff --git a/avahi-common/domain-test.c b/avahi-common/domain-test.c new file mode 100644 index 0000000..240f960 --- /dev/null +++ b/avahi-common/domain-test.c @@ -0,0 +1,48 @@ +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "util.h" + +int main(int argc, char *argv[]) { + gchar *s; + + g_message("host name: %s", s = avahi_get_host_name()); + g_free(s); + + g_message("%s", s = avahi_normalize_name("foo.foo.")); + g_free(s); + + g_message("%s", s = avahi_normalize_name("\\f\\o\\\\o\\..\\f\\ \\o\\o.")); + g_free(s); + + g_message("%i", avahi_domain_equal("\\aaa bbb\\.cccc\\\\.dee.fff.", "aaa\\ bbb\\.cccc\\\\.dee.fff")); + g_message("%i", avahi_domain_equal("\\A", "a")); + + g_message("%i", avahi_domain_equal("a", "aaa")); + + g_message("%u = %u", avahi_domain_hash("\\Aaaab\\\\."), avahi_domain_hash("aaaa\\b\\\\")); + + return 0; +} diff --git a/avahi-common/rr.c b/avahi-common/rr.c new file mode 100644 index 0000000..c7f7d2c --- /dev/null +++ b/avahi-common/rr.c @@ -0,0 +1,551 @@ +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "util.h" +#include "rr.h" +#include "dns.h" + +AvahiKey *avahi_key_new(const gchar *name, guint16 class, guint16 type) { + AvahiKey *k; + g_assert(name); + + k = g_new(AvahiKey, 1); + k->ref = 1; + k->name = avahi_normalize_name(name); + k->class = class; + k->type = type; + +/* g_message("%p %% ref=1", k); */ + + return k; +} + +AvahiKey *avahi_key_ref(AvahiKey *k) { + g_assert(k); + g_assert(k->ref >= 1); + + k->ref++; + +/* g_message("%p ++ ref=%i", k, k->ref); */ + + return k; +} + +void avahi_key_unref(AvahiKey *k) { + g_assert(k); + g_assert(k->ref >= 1); + +/* g_message("%p -- ref=%i", k, k->ref-1); */ + + if ((--k->ref) <= 0) { + g_free(k->name); + g_free(k); + } +} + +AvahiRecord *avahi_record_new(AvahiKey *k) { + AvahiRecord *r; + + g_assert(k); + + r = g_new(AvahiRecord, 1); + r->ref = 1; + r->key = avahi_key_ref(k); + + memset(&r->data, 0, sizeof(r->data)); + + r->ttl = AVAHI_DEFAULT_TTL; + + return r; +} + +AvahiRecord *avahi_record_new_full(const gchar *name, guint16 class, guint16 type) { + AvahiRecord *r; + AvahiKey *k; + + g_assert(name); + + k = avahi_key_new(name, class, type); + r = avahi_record_new(k); + avahi_key_unref(k); + + return r; +} + +AvahiRecord *avahi_record_ref(AvahiRecord *r) { + g_assert(r); + g_assert(r->ref >= 1); + + r->ref++; + return r; +} + +void avahi_record_unref(AvahiRecord *r) { + g_assert(r); + g_assert(r->ref >= 1); + + if ((--r->ref) <= 0) { + switch (r->key->type) { + + case AVAHI_DNS_TYPE_SRV: + g_free(r->data.srv.name); + break; + + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + g_free(r->data.ptr.name); + break; + + case AVAHI_DNS_TYPE_HINFO: + g_free(r->data.hinfo.cpu); + g_free(r->data.hinfo.os); + break; + + case AVAHI_DNS_TYPE_TXT: + avahi_string_list_free(r->data.txt.string_list); + break; + + case AVAHI_DNS_TYPE_A: + case AVAHI_DNS_TYPE_AAAA: + break; + + default: + g_free(r->data.generic.data); + } + + avahi_key_unref(r->key); + g_free(r); + } +} + +const gchar *avahi_dns_class_to_string(guint16 class) { + if (class & AVAHI_DNS_CACHE_FLUSH) + return "FLUSH"; + + if (class == AVAHI_DNS_CLASS_IN) + return "IN"; + + return NULL; +} + +const gchar *avahi_dns_type_to_string(guint16 type) { + switch (type) { + case AVAHI_DNS_TYPE_CNAME: + return "CNAME"; + case AVAHI_DNS_TYPE_A: + return "A"; + case AVAHI_DNS_TYPE_AAAA: + return "AAAA"; + case AVAHI_DNS_TYPE_PTR: + return "PTR"; + case AVAHI_DNS_TYPE_HINFO: + return "HINFO"; + case AVAHI_DNS_TYPE_TXT: + return "TXT"; + case AVAHI_DNS_TYPE_SRV: + return "SRV"; + case AVAHI_DNS_TYPE_ANY: + return "ANY"; + default: + return NULL; + } +} + + +gchar *avahi_key_to_string(const AvahiKey *k) { + g_assert(k); + g_assert(k->ref >= 1); + + return g_strdup_printf("%s\t%s\t%s", + k->name, + avahi_dns_class_to_string(k->class), + avahi_dns_type_to_string(k->type)); +} + +gchar *avahi_record_to_string(const AvahiRecord *r) { + gchar *p, *s; + char buf[257], *t = NULL, *d = NULL; + + g_assert(r); + g_assert(r->ref >= 1); + + switch (r->key->type) { + case AVAHI_DNS_TYPE_A: + inet_ntop(AF_INET, &r->data.a.address.address, t = buf, sizeof(buf)); + break; + + case AVAHI_DNS_TYPE_AAAA: + inet_ntop(AF_INET6, &r->data.aaaa.address.address, t = buf, sizeof(buf)); + break; + + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME : + + t = r->data.ptr.name; + break; + + case AVAHI_DNS_TYPE_TXT: + t = d = avahi_string_list_to_string(r->data.txt.string_list); + break; + + case AVAHI_DNS_TYPE_HINFO: + + snprintf(t = buf, sizeof(buf), "\"%s\" \"%s\"", r->data.hinfo.cpu, r->data.hinfo.os); + break; + + case AVAHI_DNS_TYPE_SRV: + + snprintf(t = buf, sizeof(buf), "%u %u %u %s", + r->data.srv.priority, + r->data.srv.weight, + r->data.srv.port, + r->data.srv.name); + + break; + } + + p = avahi_key_to_string(r->key); + s = g_strdup_printf("%s %s ; ttl=%u", p, t ? t : "", r->ttl); + g_free(p); + g_free(d); + + return s; +} + +gboolean avahi_key_equal(const AvahiKey *a, const AvahiKey *b) { + g_assert(a); + g_assert(b); + + if (a == b) + return TRUE; + +/* g_message("equal: %p %p", a, b); */ + + return avahi_domain_equal(a->name, b->name) && + a->type == b->type && + a->class == b->class; +} + +gboolean avahi_key_pattern_match(const AvahiKey *pattern, const AvahiKey *k) { + g_assert(pattern); + g_assert(k); + +/* g_message("equal: %p %p", a, b); */ + + g_assert(!avahi_key_is_pattern(k)); + + if (pattern == k) + return TRUE; + + return avahi_domain_equal(pattern->name, k->name) && + (pattern->type == k->type || pattern->type == AVAHI_DNS_TYPE_ANY) && + pattern->class == k->class; +} + +gboolean avahi_key_is_pattern(const AvahiKey *k) { + g_assert(k); + + return k->type == AVAHI_DNS_TYPE_ANY; +} + + +guint avahi_key_hash(const AvahiKey *k) { + g_assert(k); + + return + avahi_domain_hash(k->name) + + k->type + + k->class; +} + +static gboolean rdata_equal(const AvahiRecord *a, const AvahiRecord *b) { + g_assert(a); + g_assert(b); + g_assert(a->key->type == b->key->type); + +/* t = avahi_record_to_string(a); */ +/* g_message("comparing %s", t); */ +/* g_free(t); */ + +/* t = avahi_record_to_string(b); */ +/* g_message("and %s", t); */ +/* g_free(t); */ + + + switch (a->key->type) { + case AVAHI_DNS_TYPE_SRV: + return + a->data.srv.priority == b->data.srv.priority && + a->data.srv.weight == b->data.srv.weight && + a->data.srv.port == b->data.srv.port && + avahi_domain_equal(a->data.srv.name, b->data.srv.name); + + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + return avahi_domain_equal(a->data.ptr.name, b->data.ptr.name); + + case AVAHI_DNS_TYPE_HINFO: + return + !g_utf8_collate(a->data.hinfo.cpu, b->data.hinfo.cpu) && + !g_utf8_collate(a->data.hinfo.os, b->data.hinfo.os); + + case AVAHI_DNS_TYPE_TXT: + return avahi_string_list_equal(a->data.txt.string_list, b->data.txt.string_list); + + case AVAHI_DNS_TYPE_A: + return memcmp(&a->data.a.address, &b->data.a.address, sizeof(AvahiIPv4Address)) == 0; + + case AVAHI_DNS_TYPE_AAAA: + return memcmp(&a->data.aaaa.address, &b->data.aaaa.address, sizeof(AvahiIPv6Address)) == 0; + + default: + return a->data.generic.size == b->data.generic.size && + (a->data.generic.size == 0 || memcmp(a->data.generic.data, b->data.generic.data, a->data.generic.size) == 0); + } + +} + +gboolean avahi_record_equal_no_ttl(const AvahiRecord *a, const AvahiRecord *b) { + g_assert(a); + g_assert(b); + + if (a == b) + return TRUE; + + return + avahi_key_equal(a->key, b->key) && + rdata_equal(a, b); +} + + +AvahiRecord *avahi_record_copy(AvahiRecord *r) { + AvahiRecord *copy; + + copy = g_new(AvahiRecord, 1); + copy->ref = 1; + copy->key = avahi_key_ref(r->key); + copy->ttl = r->ttl; + + switch (r->key->type) { + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + copy->data.ptr.name = g_strdup(r->data.ptr.name); + break; + + case AVAHI_DNS_TYPE_SRV: + copy->data.srv.priority = r->data.srv.priority; + copy->data.srv.weight = r->data.srv.weight; + copy->data.srv.port = r->data.srv.port; + copy->data.srv.name = g_strdup(r->data.srv.name); + break; + + case AVAHI_DNS_TYPE_HINFO: + copy->data.hinfo.os = g_strdup(r->data.hinfo.os); + copy->data.hinfo.cpu = g_strdup(r->data.hinfo.cpu); + break; + + case AVAHI_DNS_TYPE_TXT: + copy->data.txt.string_list = avahi_string_list_copy(r->data.txt.string_list); + break; + + case AVAHI_DNS_TYPE_A: + copy->data.a.address = r->data.a.address; + break; + + case AVAHI_DNS_TYPE_AAAA: + copy->data.aaaa.address = r->data.aaaa.address; + break; + + default: + copy->data.generic.data = g_memdup(r->data.generic.data, r->data.generic.size); + copy->data.generic.size = r->data.generic.size; + break; + + } + + return copy; +} + + +guint avahi_key_get_estimate_size(AvahiKey *k) { + g_assert(k); + + return strlen(k->name)+1+4; +} + +guint avahi_record_get_estimate_size(AvahiRecord *r) { + guint n; + g_assert(r); + + n = avahi_key_get_estimate_size(r->key) + 4 + 2; + + switch (r->key->type) { + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + n += strlen(r->data.ptr.name) + 1; + break; + + case AVAHI_DNS_TYPE_SRV: + n += 6 + strlen(r->data.srv.name) + 1; + break; + + case AVAHI_DNS_TYPE_HINFO: + n += strlen(r->data.hinfo.os) + 1 + strlen(r->data.hinfo.cpu) + 1; + break; + + case AVAHI_DNS_TYPE_TXT: + n += avahi_string_list_serialize(r->data.txt.string_list, NULL, 0); + break; + + case AVAHI_DNS_TYPE_A: + n += sizeof(AvahiIPv4Address); + break; + + case AVAHI_DNS_TYPE_AAAA: + n += sizeof(AvahiIPv6Address); + break; + + default: + n += r->data.generic.size; + } + + return n; +} + +static gint lexicographical_memcmp(gconstpointer a, size_t al, gconstpointer b, size_t bl) { + size_t c; + gint ret; + + g_assert(a); + g_assert(b); + + c = al < bl ? al : bl; + if ((ret = memcmp(a, b, c))) + return ret; + + if (al == bl) + return 0; + else + return al == c ? 1 : -1; +} + +static gint uint16_cmp(guint16 a, guint16 b) { + return a == b ? 0 : (a < b ? -1 : 1); +} + +gint avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b) { + gint r; +/* gchar *t1, *t2; */ + + g_assert(a); + g_assert(b); + +/* t1 = avahi_record_to_string(a); */ +/* t2 = avahi_record_to_string(b); */ +/* g_message("lexicocmp: %s %s", t1, t2); */ +/* g_free(t1); */ +/* g_free(t2); */ + + if (a == b) + return 0; + + if ((r = uint16_cmp(a->key->class, b->key->class)) || + (r = uint16_cmp(a->key->type, b->key->type))) + return r; + + switch (a->key->type) { + + case AVAHI_DNS_TYPE_PTR: + case AVAHI_DNS_TYPE_CNAME: + return avahi_binary_domain_cmp(a->data.ptr.name, b->data.ptr.name); + + case AVAHI_DNS_TYPE_SRV: { + if ((r = uint16_cmp(a->data.srv.priority, b->data.srv.priority)) == 0 && + (r = uint16_cmp(a->data.srv.weight, b->data.srv.weight)) == 0 && + (r = uint16_cmp(a->data.srv.port, b->data.srv.port)) == 0) + r = avahi_binary_domain_cmp(a->data.srv.name, b->data.srv.name); + + return r; + } + + case AVAHI_DNS_TYPE_HINFO: { + + if ((r = strcmp(a->data.hinfo.cpu, b->data.hinfo.cpu)) || + (r = strcmp(a->data.hinfo.os, b->data.hinfo.os))) + return r; + + return 0; + + } + + case AVAHI_DNS_TYPE_TXT: { + + guint8 *ma, *mb; + guint asize, bsize; + + ma = g_new(guint8, asize = avahi_string_list_serialize(a->data.txt.string_list, NULL, 0)); + mb = g_new(guint8, bsize = avahi_string_list_serialize(b->data.txt.string_list, NULL, 0)); + avahi_string_list_serialize(a->data.txt.string_list, ma, asize); + avahi_string_list_serialize(b->data.txt.string_list, mb, bsize); + + if (asize && bsize) + r = lexicographical_memcmp(ma, asize, mb, bsize); + else if (asize && !bsize) + r = 1; + else if (!asize && bsize) + r = -1; + else + r = 0; + + g_free(ma); + g_free(mb); + + return r; + } + + case AVAHI_DNS_TYPE_A: + return memcmp(&a->data.a.address, &b->data.a.address, sizeof(AvahiIPv4Address)); + + case AVAHI_DNS_TYPE_AAAA: + return memcmp(&a->data.aaaa.address, &b->data.aaaa.address, sizeof(AvahiIPv6Address)); + + default: + return lexicographical_memcmp(a->data.generic.data, a->data.generic.size, + b->data.generic.data, b->data.generic.size); + } + +} + +gboolean avahi_record_is_goodbye(AvahiRecord *r) { + g_assert(r); + + return r->ttl == 0; +} diff --git a/avahi-common/rr.h b/avahi-common/rr.h new file mode 100644 index 0000000..404cc21 --- /dev/null +++ b/avahi-common/rr.h @@ -0,0 +1,140 @@ +#ifndef foorrhfoo +#define foorrhfoo + +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include +#include + +enum { + AVAHI_DNS_TYPE_A = 0x01, + AVAHI_DNS_TYPE_NS = 0x02, + AVAHI_DNS_TYPE_CNAME = 0x05, + AVAHI_DNS_TYPE_SOA = 0x06, + AVAHI_DNS_TYPE_PTR = 0x0C, + AVAHI_DNS_TYPE_HINFO = 0x0D, + AVAHI_DNS_TYPE_MX = 0x0F, + AVAHI_DNS_TYPE_TXT = 0x10, + AVAHI_DNS_TYPE_AAAA = 0x1C, + AVAHI_DNS_TYPE_SRV = 0x21, + AVAHI_DNS_TYPE_ANY = 0xFF +}; + +enum { + AVAHI_DNS_CLASS_IN = 0x01, + AVAHI_DNS_CACHE_FLUSH = 0x8000, + AVAHI_DNS_UNICAST_RESPONSE = 0x8000 +}; + +#define AVAHI_DEFAULT_TTL (120*60) + +typedef struct { + guint ref; + gchar *name; + guint16 class; + guint16 type; +} AvahiKey; + +typedef struct { + guint ref; + AvahiKey *key; + + guint32 ttl; + + union { + struct { + gpointer data; + guint16 size; + } generic; + + struct { + guint16 priority; + guint16 weight; + guint16 port; + gchar *name; + } srv; + + struct { + gchar *name; + } ptr; /* and cname */ + + struct { + gchar *cpu; + gchar *os; + } hinfo; + + struct { + AvahiStringList *string_list; + } txt; + + struct { + AvahiIPv4Address address; + } a; + + struct { + AvahiIPv6Address address; + } aaaa; + + } data; + +} AvahiRecord; + +AvahiKey *avahi_key_new(const gchar *name, guint16 class, guint16 type); +AvahiKey *avahi_key_ref(AvahiKey *k); +void avahi_key_unref(AvahiKey *k); + +gboolean avahi_key_equal(const AvahiKey *a, const AvahiKey *b); /* Treat AVAHI_DNS_CLASS_ANY like any other type */ +gboolean avahi_key_pattern_match(const AvahiKey *pattern, const AvahiKey *k); /* If pattern.type is AVAHI_DNS_CLASS_ANY, k.type is ignored */ + +gboolean avahi_key_is_pattern(const AvahiKey *k); + +guint avahi_key_hash(const AvahiKey *k); + +AvahiRecord *avahi_record_new(AvahiKey *k); +AvahiRecord *avahi_record_new_full(const gchar *name, guint16 class, guint16 type); +AvahiRecord *avahi_record_ref(AvahiRecord *r); +void avahi_record_unref(AvahiRecord *r); + +const gchar *avahi_dns_class_to_string(guint16 class); +const gchar *avahi_dns_type_to_string(guint16 type); + +gchar *avahi_key_to_string(const AvahiKey *k); /* g_free() the result! */ +gchar *avahi_record_to_string(const AvahiRecord *r); /* g_free() the result! */ + +gboolean avahi_record_equal_no_ttl(const AvahiRecord *a, const AvahiRecord *b); + +AvahiRecord *avahi_record_copy(AvahiRecord *r); + +/* returns a maximum estimate for the space that is needed to store + * this key in a DNS packet */ +guint avahi_key_get_estimate_size(AvahiKey *k); + +/* ditto */ +guint avahi_record_get_estimate_size(AvahiRecord *r); + +gint avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b); + +gboolean avahi_record_is_goodbye(AvahiRecord *r); + +#endif diff --git a/avahi-common/strlst-test.c b/avahi-common/strlst-test.c new file mode 100644 index 0000000..afc13b1 --- /dev/null +++ b/avahi-common/strlst-test.c @@ -0,0 +1,85 @@ +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "strlst.h" + +int main(int argc, char *argv[]) { + gchar *t; + guint8 data[1024]; + AvahiStringList *a = NULL, *b; + guint size, n; + + a = avahi_string_list_new("prefix", "a", "b", NULL); + + a = avahi_string_list_add(a, "start"); + a = avahi_string_list_add(a, "foo"); + a = avahi_string_list_add(a, "bar"); + a = avahi_string_list_add(a, "quux"); + a = avahi_string_list_add_arbitrary(a, (guint8*) "null\0null", 9); + a = avahi_string_list_add(a, "end"); + + t = avahi_string_list_to_string(a); + printf("--%s--\n", t); + g_free(t); + + size = avahi_string_list_serialize(a, data, sizeof(data)); + + printf("%u\n", size); + + for (t = (gchar*) data, n = 0; n < size; n++, t++) { + if (*t <= 32) + printf("(%u)", *t); + else + printf("%c", *t); + } + + printf("\n"); + + b = avahi_string_list_parse(data, size); + + g_assert(avahi_string_list_equal(a, b)); + + t = avahi_string_list_to_string(b); + printf("--%s--\n", t); + g_free(t); + + avahi_string_list_free(b); + + b = avahi_string_list_copy(a); + + g_assert(avahi_string_list_equal(a, b)); + + t = avahi_string_list_to_string(b); + printf("--%s--\n", t); + g_free(t); + + avahi_string_list_free(a); + avahi_string_list_free(b); + + return 0; +} diff --git a/avahi-common/strlst.c b/avahi-common/strlst.c new file mode 100644 index 0000000..866618f --- /dev/null +++ b/avahi-common/strlst.c @@ -0,0 +1,245 @@ +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "strlst.h" + +AvahiStringList *avahi_string_list_add_arbitrary(AvahiStringList *l, const guint8*text, guint size) { + AvahiStringList *n; + + g_assert(text); + + n = g_malloc(sizeof(AvahiStringList) + size); + n->next = l; + memcpy(n->text, text, n->size = size); + + return n; +} + +AvahiStringList *avahi_string_list_add(AvahiStringList *l, const gchar *text) { + g_assert(text); + + return avahi_string_list_add_arbitrary(l, (const guint8*) text, strlen(text)); +} + +AvahiStringList *avahi_string_list_parse(gconstpointer data, guint size) { + AvahiStringList *r = NULL; + const guint8 *c; + g_assert(data); + + c = data; + for (;;) { + guint k; + + if (size < 1) + break; + + k = *(c++); + r = avahi_string_list_add_arbitrary(r, c, k); + c += k; + + size -= 1 + k; + } + + return r; +} + +void avahi_string_list_free(AvahiStringList *l) { + AvahiStringList *n; + + while (l) { + n = l->next; + g_free(l); + l = n; + } +} + +static AvahiStringList* string_list_reverse(AvahiStringList *l) { + AvahiStringList *r = NULL, *n; + + while (l) { + n = l->next; + l->next = r; + r = l; + l = n; + } + + return r; +} + +gchar* avahi_string_list_to_string(AvahiStringList *l) { + AvahiStringList *n; + guint s = 0; + gchar *t, *e; + + l = string_list_reverse(l); + + for (n = l; n; n = n->next) { + if (n != l) + s ++; + + s += n->size+2; + } + + t = e = g_new(gchar, s+1); + + for (n = l; n; n = n->next) { + if (n != l) + *(e++) = ' '; + + *(e++) = '"'; + strncpy(e, (gchar*) n->text, n->size); + e[n->size] = 0; + e = strchr(e, 0); + *(e++) = '"'; + + g_assert(e); + } + + l = string_list_reverse(l); + + *e = 0; + + return t; +} + +guint avahi_string_list_serialize(AvahiStringList *l, gpointer data, guint size) { + guint used = 0; + + if (data) { + guint8 *c; + AvahiStringList *n; + + g_assert(data); + + l = string_list_reverse(l); + c = data; + + for (n = l; n; n = n->next) { + guint k; + if (size < 1) + break; + + k = n->size; + if (k > 255) + k = 255; + + if (k > size-1) + k = size-1; + + *(c++) = k; + memcpy(c, n->text, k); + c += k; + + used += 1+ k; + } + + l = string_list_reverse(l); + } else { + AvahiStringList *n; + + for (n = l; n; n = n->next) { + guint k; + + k = n->size; + if (k > 255) + k = 255; + + used += 1+k; + } + } + + return used; +} + +gboolean avahi_string_list_equal(AvahiStringList *a, AvahiStringList *b) { + + for (;;) { + if (!a && !b) + return TRUE; + + if (!a || !b) + return FALSE; + + if (a->size != b->size) + return FALSE; + + if (a->size != 0 && memcmp(a->text, b->text, a->size) != 0) + return FALSE; + + a = a->next; + b = b->next; + } +} + +AvahiStringList *avahi_string_list_add_many(AvahiStringList *r, ...) { + va_list va; + + va_start(va, r); + r = avahi_string_list_add_many_va(r, va); + va_end(va); + + return r; +} + +AvahiStringList *avahi_string_list_add_many_va(AvahiStringList *r, va_list va) { + const gchar *txt; + + while ((txt = va_arg(va, const gchar*))) + r = avahi_string_list_add(r, txt); + + return r; +} + + +AvahiStringList *avahi_string_list_new(const gchar *txt, ...) { + va_list va; + AvahiStringList *r = NULL; + + if (txt) { + r = avahi_string_list_add(r, txt); + + va_start(va, txt); + r = avahi_string_list_add_many_va(r, va); + va_end(va); + } + + return r; +} + +AvahiStringList *avahi_string_list_new_va(va_list va) { + return avahi_string_list_add_many_va(NULL, va); +} + +AvahiStringList *avahi_string_list_copy(AvahiStringList *l) { + AvahiStringList *r = NULL; + + for (; l; l = l->next) + r = avahi_string_list_add_arbitrary(r, l->text, l->size); + + return string_list_reverse(r); +} diff --git a/avahi-common/strlst.h b/avahi-common/strlst.h new file mode 100644 index 0000000..80dfc2c --- /dev/null +++ b/avahi-common/strlst.h @@ -0,0 +1,53 @@ +#ifndef footxtlisthfoo +#define footxtlisthfoo + +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +typedef struct AvahiStringList { + struct AvahiStringList *next; + guint size; + guint8 text[1]; +} AvahiStringList; + +AvahiStringList *avahi_string_list_new(const gchar *txt, ...); +AvahiStringList *avahi_string_list_new_va(va_list va); + +void avahi_string_list_free(AvahiStringList *l); + +AvahiStringList *avahi_string_list_add(AvahiStringList *l, const gchar *text); +AvahiStringList *avahi_string_list_add_arbitrary(AvahiStringList *l, const guint8 *text, guint size); +AvahiStringList *avahi_string_list_add_many(AvahiStringList *r, ...); +AvahiStringList *avahi_string_list_add_many_va(AvahiStringList *r, va_list va); + +gchar* avahi_string_list_to_string(AvahiStringList *l); + +guint avahi_string_list_serialize(AvahiStringList *l, gpointer data, guint size); +AvahiStringList *avahi_string_list_parse(gconstpointer data, guint size); + +gboolean avahi_string_list_equal(AvahiStringList *a, AvahiStringList *b); + +AvahiStringList *avahi_string_list_copy(AvahiStringList *l); + +#endif + diff --git a/avahi-common/util.c b/avahi-common/util.c new file mode 100644 index 0000000..a41475a --- /dev/null +++ b/avahi-common/util.c @@ -0,0 +1,428 @@ +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" + +gchar *avahi_get_host_name(void) { +#ifdef HOST_NAME_MAX + char t[HOST_NAME_MAX]; +#else + char t[256]; +#endif + gethostname(t, sizeof(t)); + t[sizeof(t)-1] = 0; + return avahi_normalize_name(t); +} + +static gchar *unescape_uneeded(const gchar *src, gchar *ret_dest, size_t size) { + gboolean escaped = FALSE; + + g_assert(src); + g_assert(ret_dest); + g_assert(size > 0); + + for (; *src; src++) { + + if (!escaped && *src == '\\') + escaped = TRUE; + else if (escaped && (*src == '.' || *src == '\\')) { + + if ((size -= 2) <= 1) break; + + *(ret_dest++) = '\\'; + *(ret_dest++) = *src; + escaped = FALSE; + } else { + if (--size <= 1) break; + + *(ret_dest++) = *src; + escaped = FALSE; + } + + } + + *ret_dest = 0; + + return ret_dest; +} + +gchar *avahi_normalize_name(const gchar *s) { + gchar tmp[256]; + gchar *n, *t; + guint l; + g_assert(s); + + unescape_uneeded(s, tmp, sizeof(tmp)); + + n = g_utf8_normalize(tmp, -1, G_NORMALIZE_DEFAULT); + + if ((l = strlen(n)) == 0) { + g_free(n); + return g_strdup("."); + } + + if (n[l-1] == '.') + return n; + + t = g_strdup_printf("%s.", n); + g_free(n); + return t; +} + +gint avahi_timeval_compare(const GTimeVal *a, const GTimeVal *b) { + g_assert(a); + g_assert(b); + + if (a->tv_sec < b->tv_sec) + return -1; + + if (a->tv_sec > b->tv_sec) + return 1; + + if (a->tv_usec < b->tv_usec) + return -1; + + if (a->tv_usec > b->tv_usec) + return 1; + + return 0; +} + +glong avahi_timeval_diff(const GTimeVal *a, const GTimeVal *b) { + g_assert(a); + g_assert(b); + + if (avahi_timeval_compare(a, b) < 0) + return avahi_timeval_diff(b, a); + + return ((glong) a->tv_sec - b->tv_sec)*1000000 + a->tv_usec - b->tv_usec; +} + + +gint avahi_set_cloexec(gint fd) { + gint n; + + g_assert(fd >= 0); + + if ((n = fcntl(fd, F_GETFD)) < 0) + return -1; + + if (n & FD_CLOEXEC) + return 0; + + return fcntl(fd, F_SETFD, n|FD_CLOEXEC); +} + +gint avahi_set_nonblock(gint fd) { + gint n; + + g_assert(fd >= 0); + + if ((n = fcntl(fd, F_GETFL)) < 0) + return -1; + + if (n & O_NONBLOCK) + return 0; + + return fcntl(fd, F_SETFL, n|O_NONBLOCK); +} + +gint avahi_wait_for_write(gint fd) { + fd_set fds; + gint r; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + + if ((r = select(fd+1, NULL, &fds, NULL, NULL)) < 0) { + g_message("select() failed: %s", strerror(errno)); + + return -1; + } + + g_assert(r > 0); + + return 0; +} + +GTimeVal *avahi_elapse_time(GTimeVal *tv, guint msec, guint jitter) { + g_assert(tv); + + g_get_current_time(tv); + + if (msec) + g_time_val_add(tv, msec*1000); + + if (jitter) + g_time_val_add(tv, g_random_int_range(0, jitter) * 1000); + + return tv; +} + +glong avahi_age(const GTimeVal *a) { + GTimeVal now; + + g_assert(a); + + g_get_current_time(&now); + + return avahi_timeval_diff(&now, a); +} + +/* Read the first label from string *name, unescape "\" and write it to dest */ +gchar *avahi_unescape_label(const gchar **name, gchar *dest, guint size) { + guint i = 0; + gchar *d; + + g_assert(dest); + g_assert(size > 0); + g_assert(name); + + if (!**name) + return NULL; + + 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; +} + +/* Escape "\" and ".", append \0 */ +gchar *avahi_escape_label(const guint8* src, guint src_length, gchar **ret_name, guint *ret_size) { + gchar *r; + + g_assert(src); + g_assert(ret_name); + g_assert(*ret_name); + g_assert(ret_size); + g_assert(*ret_size > 0); + + r = *ret_name; + + while (src_length > 0) { + if (*src == '.' || *src == '\\') { + if (*ret_size < 3) + return NULL; + + *((*ret_name) ++) = '\\'; + (*ret_size) --; + } + + if (*ret_size < 2) + return NULL; + + *((*ret_name)++) = *src; + (*ret_size) --; + + src_length --; + src++; + } + + **ret_name = 0; + + return r; +} + +static gint utf8_strcasecmp(const gchar *a, const gchar *b) { + gchar *ta, *tb; + gint r; + + g_assert(a); + g_assert(b); + + ta = g_utf8_casefold(a, -1); + tb = g_utf8_casefold(b, -1); + r = g_utf8_collate(ta, tb); + g_free(ta); + g_free(tb); + + return r; +} + +gboolean avahi_domain_equal(const gchar *a, const gchar *b) { + g_assert(a); + g_assert(b); + + if (a == b) + return TRUE; + + for (;;) { + gchar ca[65], cb[65], *pa, *pb; + + pa = avahi_unescape_label(&a, ca, sizeof(ca)); + pb = avahi_unescape_label(&b, cb, sizeof(cb)); + + if (!pa && !pb) + return TRUE; + else if ((pa && !pb) || (!pa && pb)) + return FALSE; + + if (utf8_strcasecmp(pa, pb)) + return FALSE; + } + + return TRUE; +} + +gint avahi_binary_domain_cmp(const gchar *a, const gchar *b) { + g_assert(a); + g_assert(b); + + if (a == b) + return 0; + + for (;;) { + gchar ca[65], cb[65], *pa, *pb; + gint r; + + pa = avahi_unescape_label(&a, ca, sizeof(ca)); + pb = avahi_unescape_label(&b, cb, sizeof(cb)); + + if (!pa && !pb) + return 0; + else if (pa && !pb) + return 1; + else if (!pa && pb) + return -1; + + if ((r = strcmp(pa, pb))) + return r; + } +} + +void avahi_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; + } +} + +guint avahi_domain_hash(const gchar *s) { + guint hash = 0; + + for (;;) { + gchar c[65], *n, *m; + + if (!avahi_unescape_label(&s, c, sizeof(c))) + return hash; + + if (!c[0]) + continue; + + n = g_utf8_normalize(c, -1, G_NORMALIZE_DEFAULT); + m = g_utf8_strdown(n, -1); + + hash += g_str_hash(m); + + g_free(m); + g_free(n); + } +} + +gchar *avahi_format_mac_address(const guint8* mac, guint size) { + gchar *r, *t; + guint i; + static const gchar hex[] = "0123456789abcdef"; + + t = r = g_new(gchar, size > 0 ? size*3 : 1); + + if (size <= 0) { + *r = 0; + return r; + } + + for (i = 0; i < size; i++) { + *(t++) = hex[*mac >> 4]; + *(t++) = hex[*mac & 0xF]; + *(t++) = ':'; + + mac++; + } + + *(--t) = 0; + return r; +} diff --git a/avahi-common/util.h b/avahi-common/util.h new file mode 100644 index 0000000..68d367c --- /dev/null +++ b/avahi-common/util.h @@ -0,0 +1,57 @@ +#ifndef fooutilhfoo +#define fooutilhfoo + +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +gchar *avahi_normalize_name(const gchar *s); /* g_free() the result! */ +gchar *avahi_get_host_name(void); /* g_free() the result! */ + +gint avahi_timeval_compare(const GTimeVal *a, const GTimeVal *b); +glong avahi_timeval_diff(const GTimeVal *a, const GTimeVal *b); + +gint avahi_set_cloexec(gint fd); +gint avahi_set_nonblock(gint fd); +gint avahi_wait_for_write(gint fd); + +GTimeVal *avahi_elapse_time(GTimeVal *tv, guint msec, guint jitter); + +glong avahi_age(const GTimeVal *a); + +gboolean avahi_domain_equal(const gchar *a, const gchar *b); +gint avahi_binary_domain_cmp(const gchar *a, const gchar *b); + +void avahi_hexdump(gconstpointer p, guint size); + +/* Read the first label from the textual domain name *name, unescape + * it and write it to dest, *name is changed to point to the next label*/ +gchar *avahi_unescape_label(const gchar **name, gchar *dest, guint size); + +/* Escape the domain name in *src and write it to *ret_name */ +gchar *avahi_escape_label(const guint8* src, guint src_length, gchar **ret_name, guint *ret_size); + +guint avahi_domain_hash(const gchar *s); + +gchar *avahi_format_mac_address(const guint8* mac, guint size); + +#endif -- cgit