From: Lennart Poettering Date: Sun, 25 Sep 2005 20:12:40 +0000 (+0000) Subject: * split off lookup.h and publish.h from core.h X-Git-Url: https://git.meshlink.io/?a=commitdiff_plain;h=1ffedb586bd2fb6daa3970304fac7c5b415cd38f;p=catta * split off lookup.h and publish.h from core.h * implement wide-area DNS-SD * if multiple clients query the same records, only start the query packet sequence once * implement recursive CNAME queries * add support for resolving services without TXT or A/AAAA records * enlarge resolving timeouts to 5s * implement new browse/resolving events CACHE_EXHAUSTED/ALL_FOR_NOW * add support for resolving services without name. (i.e. for normal SRV records) git-svn-id: file:///home/lennart/svn/public/avahi/trunk@608 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe --- diff --git a/avahi-common/defs.h b/avahi-common/defs.h index b973b0c..6578e2c 100644 --- a/avahi-common/defs.h +++ b/avahi-common/defs.h @@ -152,16 +152,39 @@ typedef enum { AVAHI_DOMAIN_BROWSER_MAX } AvahiDomainBrowserType; +/** Some flags for lookup functions */ +typedef enum { + AVAHI_LOOKUP_NULL = 0, + AVAHI_LOOKUP_USE_WIDE_AREA = 1, /**< Force lookup via wide area DNS */ + AVAHI_LOOKUP_USE_MULTICAST = 2, /**< Force lookup via multicast DNS */ + AVAHI_LOOKUP_NO_TXT = 4, /**< When doing service resolving, don't lookup TXT record */ + AVAHI_LOOKUP_NO_ADDRESS = 8 /**< When doing service resolving, don't lookup A/AAAA record */ +} AvahiLookupFlags; + +/** Some flags for lookup callback functions */ +typedef enum { + AVAHI_LOOKUP_CALLBACK_NULL = 0, + AVAHI_LOOKUP_CALLBACK_CACHED = 1, /**< This response originates from the cache */ + AVAHI_LOOKUP_CALLBACK_WIDE_AREA = 2, /**< This response originates from wide area DNS */ + AVAHI_LOOKUP_CALLBACK_MULTICAST = 4, /**< This response originates from multicast DNS */ +} AvahiLookupResultFlags; + /** Type of callback event when browsing */ typedef enum { - AVAHI_BROWSER_NEW, /**< The object is new on the network */ - AVAHI_BROWSER_REMOVE /**< The object has been removed from the network */ + AVAHI_BROWSER_NEW, /**< The object is new on the network */ + AVAHI_BROWSER_REMOVE, /**< The object has been removed from the network */ + AVAHI_BROWSER_CACHE_EXHAUSTED, /**< One-time event, to notify the user that all entries from the caches have been send */ + AVAHI_BROWSER_ALL_FOR_NOW, /**< One-time event, to notify the user that more records will probably not show up in the near future, i.e. all cache entries have been read and all static servers been queried */ + AVAHI_BROWSER_NOT_FOUND, /**< Issued when using wide area DNS-SD to inform that a record is not existing */ + AVAHI_BROWSER_FAILURE /**< Issued when using wide area DNS-SD to inform about server failures */ } AvahiBrowserEvent; /** Type of callback event when resolving */ typedef enum { - AVAHI_RESOLVER_FOUND, /**< RR found, resolving successful */ - AVAHI_RESOLVER_TIMEOUT /**< Noone responded within the timeout, resolving failed */ + AVAHI_RESOLVER_FOUND, /**< RR found, resolving successful */ + AVAHI_RESOLVER_TIMEOUT, /**< Noone responded within the timeout, resolving failed */ + AVAHI_RESOLVER_NOT_FOUND, /**< Query was done using wide area DNS-SD and the server told us that the entry is nto available */ + AVAHI_RESOLVER_FAILURE, /**< Query was done using wide area DNS-SD and the server failed */ } AvahiResolverEvent; /** States of a server object */ diff --git a/avahi-core/Makefile.am b/avahi-core/Makefile.am index 59b6215..9a2c098 100644 --- a/avahi-core/Makefile.am +++ b/avahi-core/Makefile.am @@ -27,7 +27,9 @@ avahiincludedir=$(includedir)/avahi-core avahiinclude_HEADERS = \ core.h \ log.h \ - rr.h + rr.h \ + publish.h \ + lookup.h lib_LTLIBRARIES = \ libavahi-core.la @@ -39,7 +41,8 @@ noinst_PROGRAMS = \ avahi-reflector \ dns-test \ timeeventq-test \ - hashmap-test + hashmap-test \ + querier-test libavahi_core_la_SOURCES = \ timeeventq.c timeeventq.h\ @@ -63,12 +66,15 @@ libavahi_core_la_SOURCES = \ resolve-service.c \ dns.c dns.h \ rr.c rr.h \ - core.h \ + core.h lookup.h publish.h \ log.c log.h \ browse-dns-server.c \ fdutil.h fdutil.c \ util.c util.h \ - hashmap.c hashmap.h + hashmap.c hashmap.h \ + wide-area.c wide-area.h \ + multicast-lookup.c multicast-lookup.h \ + querier.c querier.h libavahi_core_la_CFLAGS = $(AM_CFLAGS) libavahi_core_la_LIBADD = $(AM_LDADD) ../avahi-common/libavahi-common.la @@ -85,6 +91,12 @@ avahi_test_SOURCES = \ avahi_test_CFLAGS = $(AM_CFLAGS) avahi_test_LDADD = $(AM_LDADD) ../avahi-common/libavahi-common.la libavahi-core.la + +querier_test_SOURCES = \ + querier-test.c +querier_test_CFLAGS = $(AM_CFLAGS) +querier_test_LDADD = $(AM_LDADD) ../avahi-common/libavahi-common.la libavahi-core.la + conformance_test_SOURCES = \ conformance-test.c conformance_test_CFLAGS = $(AM_CFLAGS) diff --git a/avahi-core/announce.c b/avahi-core/announce.c index c6edd83..c21b147 100644 --- a/avahi-core/announce.c +++ b/avahi-core/announce.c @@ -443,7 +443,7 @@ void avahi_goodbye_entry(AvahiServer *s, AvahiEntry *e, int goodbye) { /* avahi_log_debug("goodbye entry: %p", e); */ if (goodbye && !e->dead) - avahi_interface_monitor_walk(s->monitor, 0, AF_UNSPEC, send_goodbye_callback, e); + avahi_interface_monitor_walk(s->monitor, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, send_goodbye_callback, e); while (e->announcements) remove_announcement(s, e->announcements); diff --git a/avahi-core/announce.h b/avahi-core/announce.h index 9c01d26..9183258 100644 --- a/avahi-core/announce.h +++ b/avahi-core/announce.h @@ -28,6 +28,7 @@ typedef struct AvahiAnnouncement AvahiAnnouncement; #include "iface.h" #include "server.h" #include "timeeventq.h" +#include "publish.h" typedef enum { AVAHI_PROBING, diff --git a/avahi-core/avahi-test.c b/avahi-core/avahi-test.c index db0d685..0490c84 100644 --- a/avahi-core/avahi-test.c +++ b/avahi-core/avahi-test.c @@ -35,6 +35,8 @@ #include #include #include +#include +#include static AvahiSEntryGroup *group = NULL; static AvahiServer *server = NULL; @@ -62,18 +64,47 @@ static void dump_timeout_callback(AvahiTimeout *timeout, void* userdata) { poll_api->timeout_update(timeout, &tv); } -static void record_browser_callback(AvahiSRecordBrowser *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, AvahiRecord *record, void* userdata) { +static const char *browser_event_to_string(AvahiBrowserEvent event) { + switch (event) { + case AVAHI_BROWSER_NEW : return "NEW"; + case AVAHI_BROWSER_REMOVE : return "REMOVE"; + case AVAHI_BROWSER_CACHE_EXHAUSTED : return "CACHE_EXHAUSTED"; + case AVAHI_BROWSER_ALL_FOR_NOW : return "ALL_FOR_NOW"; + case AVAHI_BROWSER_FAILURE : return "FAILURE"; + case AVAHI_BROWSER_NOT_FOUND : return "NOT_FOUND"; + } + + abort(); +} + +static const char *resolver_event_to_string(AvahiResolverEvent event) { + switch (event) { + case AVAHI_RESOLVER_FOUND: return "FOUND"; + case AVAHI_RESOLVER_TIMEOUT: return "TIMEOUT"; + case AVAHI_RESOLVER_NOT_FOUND: return "NOT_FOUND"; + case AVAHI_RESOLVER_FAILURE: return "FAILURE"; + } + abort(); +} + +static void record_browser_callback( + AvahiSRecordBrowser *r, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiRecord *record, + AvahiLookupResultFlags flags, + void* userdata) { char *t; assert(r); - assert(record); - assert(interface > 0); - assert(protocol != AVAHI_PROTO_UNSPEC); - avahi_log_debug("SUBSCRIPTION: record [%s] on %i.%i is %s", t = avahi_record_to_string(record), interface, protocol, - event == AVAHI_BROWSER_NEW ? "new" : "remove"); + if (record) { + avahi_log_debug("RB: record [%s] on %i.%i is %s", t = avahi_record_to_string(record), interface, protocol, browser_event_to_string(event)); + avahi_free(t); + } else + avahi_log_debug("RB: [%s]", browser_event_to_string(event)); - avahi_free(t); } static void remove_entries(void); @@ -96,7 +127,7 @@ static void server_callback(AvahiServer *s, AvahiServerState state, void* userda avahi_log_debug("server state: %i", state); if (state == AVAHI_SERVER_RUNNING) { - avahi_log_debug("Server startup complete. Host name is <%s>", avahi_server_get_host_name_fqdn(s)); + avahi_log_debug("Server startup complete. Host name is <%s>. Service cookie is %u", avahi_server_get_host_name_fqdn(s), avahi_server_get_local_service_cookie(s)); create_entries(0); } else if (state == AVAHI_SERVER_COLLISION) { char *n; @@ -163,56 +194,122 @@ fail: group = NULL; } -static void hnr_callback(AvahiSHostNameResolver *r, AvahiIfIndex iface, AvahiProtocol protocol, AvahiResolverEvent event, const char *hostname, const AvahiAddress *a, void* userdata) { +static void hnr_callback( + AvahiSHostNameResolver *r, + AvahiIfIndex iface, + AvahiProtocol protocol, + AvahiResolverEvent event, + const char *hostname, + const AvahiAddress *a, + AvahiLookupResultFlags flags, + void* userdata) { char t[64]; if (a) avahi_address_snprint(t, sizeof(t), a); - avahi_log_debug("HNR: (%i.%i) <%s> -> %s [%s]", iface, protocol, hostname, a ? t : "n/a", event == AVAHI_RESOLVER_FOUND ? "found" : "timeout"); + avahi_log_debug("HNR: (%i.%i) <%s> -> %s [%s]", iface, protocol, hostname, a ? t : "n/a", resolver_event_to_string(event)); } -static void ar_callback(AvahiSAddressResolver *r, AvahiIfIndex iface, AvahiProtocol protocol, AvahiResolverEvent event, const AvahiAddress *a, const char *hostname, void* userdata) { +static void ar_callback( + AvahiSAddressResolver *r, + AvahiIfIndex iface, + AvahiProtocol protocol, + AvahiResolverEvent event, + const AvahiAddress *a, + const char *hostname, + AvahiLookupResultFlags flags, + void* userdata) { char t[64]; avahi_address_snprint(t, sizeof(t), a); - avahi_log_debug("AR: (%i.%i) %s -> <%s> [%s]", iface, protocol, t, hostname ? hostname : "n/a", event == AVAHI_RESOLVER_FOUND ? "found" : "timeout"); + avahi_log_debug("AR: (%i.%i) %s -> <%s> [%s]", iface, protocol, t, hostname ? hostname : "n/a", resolver_event_to_string(event)); } -static void db_callback(AvahiSDomainBrowser *b, AvahiIfIndex iface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *domain, void* userdata) { +static void db_callback( + AvahiSDomainBrowser *b, + AvahiIfIndex iface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *domain, + AvahiLookupResultFlags flags, + void* userdata) { - avahi_log_debug("DB: (%i.%i) <%s> [%s]", iface, protocol, domain, event == AVAHI_BROWSER_NEW ? "new" : "remove"); + avahi_log_debug("DB: (%i.%i) <%s> [%s]", iface, protocol, domain, browser_event_to_string(event)); } -static void stb_callback(AvahiSServiceTypeBrowser *b, AvahiIfIndex iface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *service_type, const char *domain, void* userdata) { - - avahi_log_debug("STB: (%i.%i) %s in <%s> [%s]", iface, protocol, service_type, domain, event == AVAHI_BROWSER_NEW ? "new" : "remove"); +static void stb_callback( + AvahiSServiceTypeBrowser *b, + AvahiIfIndex iface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *service_type, + const char *domain, + AvahiLookupResultFlags flags, + void* userdata) { + + avahi_log_debug("STB: (%i.%i) %s in <%s> [%s]", iface, protocol, service_type, domain, browser_event_to_string(event)); } -static void sb_callback(AvahiSServiceBrowser *b, AvahiIfIndex iface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *service_type, const char *domain, void* userdata) { - avahi_log_debug("SB: (%i.%i) <%s> as %s in <%s> [%s]", iface, protocol, name, service_type, domain, event == AVAHI_BROWSER_NEW ? "new" : "remove"); +static void sb_callback( + AvahiSServiceBrowser *b, + AvahiIfIndex iface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, + const char *service_type, + const char *domain, + AvahiLookupResultFlags flags, + void* userdata) { + avahi_log_debug("SB: (%i.%i) <%s> as %s in <%s> [%s]", iface, protocol, name, service_type, domain, browser_event_to_string(event)); } -static void sr_callback(AvahiSServiceResolver *r, AvahiIfIndex iface, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char*service_type, const char*domain_name, const char*hostname, const AvahiAddress *a, uint16_t port, AvahiStringList *txt, void* userdata) { - - if (event == AVAHI_RESOLVER_TIMEOUT) - avahi_log_debug("SR: (%i.%i) <%s> as %s in <%s> [timeout]", iface, protocol, name, service_type, domain_name); +static void sr_callback( + AvahiSServiceResolver *r, + AvahiIfIndex iface, + AvahiProtocol protocol, + AvahiResolverEvent event, + const char *name, + const char*service_type, + const char*domain_name, + const char*hostname, + const AvahiAddress *a, + uint16_t port, + AvahiStringList *txt, + AvahiLookupResultFlags flags, + void* userdata) { + + if (event != AVAHI_RESOLVER_FOUND) + avahi_log_debug("SR: (%i.%i) <%s> as %s in <%s> [%s]", iface, protocol, name, service_type, domain_name, resolver_event_to_string(event)); else { char t[64], *s; avahi_address_snprint(t, sizeof(t), a); s = avahi_string_list_to_string(txt); - avahi_log_debug("SR: (%i.%i) <%s> as %s in <%s>: %s/%s:%i (%s) [found]", iface, protocol, name, service_type, domain_name, hostname, t, port, s); + avahi_log_debug("SR: (%i.%i) <%s> as %s in <%s>: %s/%s:%i (%s) [%s]", iface, protocol, name, service_type, domain_name, hostname, t, port, s, resolver_event_to_string(event)); avahi_free(s); } } -static void dsb_callback(AvahiSDNSServerBrowser *b, AvahiIfIndex iface, AvahiProtocol protocol, AvahiBrowserEvent event, const char*hostname, const AvahiAddress *a, uint16_t port, void* userdata) { - char t[64]; - avahi_address_snprint(t, sizeof(t), a); - avahi_log_debug("DSB: (%i.%i): %s/%s:%i [%s]", iface, protocol, hostname, t, port, event == AVAHI_BROWSER_NEW ? "new" : "remove"); +static void dsb_callback( + AvahiSDNSServerBrowser *b, + AvahiIfIndex iface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char*hostname, + const AvahiAddress *a, + uint16_t port, + AvahiLookupResultFlags flags, + void* userdata) { + + char t[64] = "n/a"; + + if (a) + avahi_address_snprint(t, sizeof(t), a); + + avahi_log_debug("DSB: (%i.%i): %s/%s:%i [%s]", iface, protocol, hostname, t, port, browser_event_to_string(event)); } int main(int argc, char *argv[]) { @@ -235,27 +332,31 @@ int main(int argc, char *argv[]) { poll_api = avahi_simple_poll_get(simple_poll); avahi_server_config_init(&config); -/* config.host_name = g_strdup("test"); */ + + avahi_address_parse("192.168.50.1", AVAHI_PROTO_UNSPEC, &config.wide_area_servers[0]); + config.n_wide_area_servers = 1; + config.enable_wide_area = 1; + server = avahi_server_new(poll_api, &config, server_callback, NULL, &error); avahi_server_config_free(&config); - - k = avahi_key_new("_http._tcp.local", AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR); - r = avahi_s_record_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, k, record_browser_callback, NULL); + + k = avahi_key_new("_http._tcp.0pointer.de", AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR); + r = avahi_s_record_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, k, 0, record_browser_callback, NULL); avahi_key_unref(k); - hnr = avahi_s_host_name_resolver_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "codes-CompUTER.local", AVAHI_PROTO_UNSPEC, hnr_callback, NULL); + hnr = avahi_s_host_name_resolver_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "cocaine.local", AVAHI_PROTO_UNSPEC, 0, hnr_callback, NULL); - ar = avahi_s_address_resolver_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, avahi_address_parse("192.168.50.15", AVAHI_PROTO_INET, &a), ar_callback, NULL); + ar = avahi_s_address_resolver_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, avahi_address_parse("192.168.50.1", AVAHI_PROTO_INET, &a), 0, ar_callback, NULL); - db = avahi_s_domain_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, NULL, AVAHI_DOMAIN_BROWSER_BROWSE, db_callback, NULL); + db = avahi_s_domain_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, NULL, AVAHI_DOMAIN_BROWSER_BROWSE, 0, db_callback, NULL); - stb = avahi_s_service_type_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, NULL, stb_callback, NULL); + stb = avahi_s_service_type_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, NULL, 0, stb_callback, NULL); - sb = avahi_s_service_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_http._tcp", NULL, sb_callback, NULL); + sb = avahi_s_service_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_http._tcp", NULL, 0, sb_callback, NULL); - sr = avahi_s_service_resolver_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "Ecstasy HTTP", "_http._tcp", "local", AVAHI_PROTO_UNSPEC, sr_callback, NULL); + sr = avahi_s_service_resolver_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "Ecstasy HTTP", "_http._tcp", "local", AVAHI_PROTO_UNSPEC, 0, sr_callback, NULL); - dsb = avahi_s_dns_server_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "local", AVAHI_DNS_SERVER_RESOLVE, AVAHI_PROTO_UNSPEC, dsb_callback, NULL); + dsb = avahi_s_dns_server_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "local", AVAHI_DNS_SERVER_RESOLVE, AVAHI_PROTO_UNSPEC, 0, dsb_callback, NULL); avahi_elapse_time(&tv, 1000*5, 0); poll_api->timeout_new(poll_api, &tv, dump_timeout_callback, server); diff --git a/avahi-core/browse-dns-server.c b/avahi-core/browse-dns-server.c index 9121150..675000b 100644 --- a/avahi-core/browse-dns-server.c +++ b/avahi-core/browse-dns-server.c @@ -43,6 +43,7 @@ struct AvahiDNSServerInfo { AvahiRecord *srv_record; AvahiSHostNameResolver *host_name_resolver; AvahiAddress address; + AvahiLookupResultFlags flags; AVAHI_LLIST_FIELDS(AvahiDNSServerInfo, info); }; @@ -55,6 +56,7 @@ struct AvahiSDNSServerBrowser { AvahiSDNSServerBrowserCallback callback; void* userdata; AvahiProtocol aprotocol; + AvahiLookupFlags user_flags; unsigned n_info; @@ -93,67 +95,157 @@ static void server_info_free(AvahiSDNSServerBrowser *b, AvahiDNSServerInfo *i) { avahi_free(i); } -static void host_name_resolver_callback(AvahiSHostNameResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const char *host_name, const AvahiAddress *a, void* userdata) { +static void host_name_resolver_callback( + AvahiSHostNameResolver *r, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiResolverEvent event, + const char *host_name, + const AvahiAddress *a, + AvahiLookupResultFlags flags, + void* userdata) { + AvahiDNSServerInfo *i = userdata; assert(r); assert(host_name); assert(i); - if (event == AVAHI_RESOLVER_FOUND) { - i->address = *a; - - i->browser->callback(i->browser, i->interface, i->protocol, AVAHI_BROWSER_NEW, i->srv_record->data.srv.name, &i->address, i->srv_record->data.srv.port, i->browser->userdata); + switch (event) { + case AVAHI_RESOLVER_FOUND: { + i->address = *a; + + i->browser->callback( + i->browser, + i->interface, + i->protocol, + AVAHI_BROWSER_NEW, + i->srv_record->data.srv.name, + &i->address, + i->srv_record->data.srv.port, + i->flags | flags, + i->browser->userdata); + + break; + } + + case AVAHI_RESOLVER_NOT_FOUND: + case AVAHI_RESOLVER_FAILURE: + case AVAHI_RESOLVER_TIMEOUT: + /* Ignore */ + break; } avahi_s_host_name_resolver_free(i->host_name_resolver); i->host_name_resolver = NULL; } -static void record_browser_callback(AvahiSRecordBrowser*rr, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, AvahiRecord *record, void* userdata) { +static void record_browser_callback( + AvahiSRecordBrowser*rr, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiRecord *record, + AvahiLookupResultFlags flags, + void* userdata) { + AvahiSDNSServerBrowser *b = userdata; assert(rr); - assert(record); assert(b); - assert(record->key->type == AVAHI_DNS_TYPE_SRV); - - if (event == AVAHI_BROWSER_NEW) { - AvahiDNSServerInfo *i; - - if (get_server_info(b, interface, protocol, record)) - return; - - if (b->n_info >= 10) - return; - - if (!(i = avahi_new(AvahiDNSServerInfo, 1))) - return; /* OOM */ - - i->browser = b; - i->interface = interface; - i->protocol = protocol; - i->srv_record = avahi_record_ref(record); - i->host_name_resolver = avahi_s_host_name_resolver_new(b->server, interface, protocol, record->data.srv.name, b->aprotocol, host_name_resolver_callback, i); - - AVAHI_LLIST_PREPEND(AvahiDNSServerInfo, info, b->info, i); - - b->n_info++; - } else if (event == AVAHI_BROWSER_REMOVE) { - AvahiDNSServerInfo *i; - - if (!(i = get_server_info(b, interface, protocol, record))) - return; - if (!i->host_name_resolver) - b->callback(b, interface, protocol, event, i->srv_record->data.srv.name, &i->address, i->srv_record->data.srv.port, b->userdata); - - server_info_free(b, i); + switch (event) { + case AVAHI_BROWSER_NEW: { + AvahiDNSServerInfo *i; + + assert(record); + assert(record->key->type == AVAHI_DNS_TYPE_SRV); + + if (get_server_info(b, interface, protocol, record)) + return; + + if (b->n_info >= 10) + return; + + if (!(i = avahi_new(AvahiDNSServerInfo, 1))) + return; /* OOM */ + + i->browser = b; + i->interface = interface; + i->protocol = protocol; + i->srv_record = avahi_record_ref(record); + i->host_name_resolver = avahi_s_host_name_resolver_new( + b->server, + interface, protocol, + record->data.srv.name, + b->aprotocol, + b->user_flags, + host_name_resolver_callback, i); + i->flags = flags; + + AVAHI_LLIST_PREPEND(AvahiDNSServerInfo, info, b->info, i); + + b->n_info++; + break; + } + + case AVAHI_BROWSER_REMOVE: { + AvahiDNSServerInfo *i; + + assert(record); + assert(record->key->type == AVAHI_DNS_TYPE_SRV); + + if (!(i = get_server_info(b, interface, protocol, record))) + return; + + if (!i->host_name_resolver) + b->callback( + b, + interface, + protocol, + event, + i->srv_record->data.srv.name, + &i->address, + i->srv_record->data.srv.port, + i->flags | flags, + b->userdata); + + server_info_free(b, i); + break; + } + + case AVAHI_BROWSER_FAILURE: + case AVAHI_BROWSER_NOT_FOUND: + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + + b->callback( + b, + interface, + protocol, + event, + NULL, + NULL, + 0, + flags, + b->userdata); + + break; } } -AvahiSDNSServerBrowser *avahi_s_dns_server_browser_new(AvahiServer *server, AvahiIfIndex interface, AvahiProtocol protocol, const char *domain, AvahiDNSServerType type, AvahiProtocol aprotocol, AvahiSDNSServerBrowserCallback callback, void* userdata) { +AvahiSDNSServerBrowser *avahi_s_dns_server_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *domain, + AvahiDNSServerType type, + AvahiProtocol aprotocol, + AvahiLookupFlags flags, + AvahiSDNSServerBrowserCallback callback, + void* userdata) { + AvahiSDNSServerBrowser *b; AvahiKey *k; char *n = NULL; @@ -166,18 +258,27 @@ AvahiSDNSServerBrowser *avahi_s_dns_server_browser_new(AvahiServer *server, Avah avahi_server_set_errno(server, AVAHI_ERR_INVALID_DOMAIN_NAME); return NULL; } - + + if (!domain) + domain = server->domain_name; + + if (!AVAHI_VALID_FLAGS(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST)) { + avahi_server_set_errno(server, AVAHI_ERR_INVALID_FLAGS); + return NULL; + } + if (!(b = avahi_new(AvahiSDNSServerBrowser, 1))) { avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); return NULL; } b->server = server; - b->domain_name = avahi_normalize_name(domain ? domain : "local"); + b->domain_name = avahi_normalize_name(domain); b->callback = callback; b->userdata = userdata; b->aprotocol = aprotocol; b->n_info = 0; + b->user_flags = flags; AVAHI_LLIST_HEAD_INIT(AvahiDNSServerInfo, b->info); AVAHI_LLIST_PREPEND(AvahiSDNSServerBrowser, browser, server->dns_server_browsers, b); @@ -186,7 +287,7 @@ AvahiSDNSServerBrowser *avahi_s_dns_server_browser_new(AvahiServer *server, Avah k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV); avahi_free(n); - b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, record_browser_callback, b); + b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b); avahi_key_unref(k); if (!b->record_browser) { diff --git a/avahi-core/browse-domain.c b/avahi-core/browse-domain.c index 03219e4..c5d2ba8 100644 --- a/avahi-core/browse-domain.c +++ b/avahi-core/browse-domain.c @@ -34,29 +34,48 @@ struct AvahiSDomainBrowser { char *domain_name; AvahiSRecordBrowser *record_browser; - + AvahiLookupResultFlags flags; + AvahiSDomainBrowserCallback callback; void* userdata; AVAHI_LLIST_FIELDS(AvahiSDomainBrowser, browser); }; -static void record_browser_callback(AvahiSRecordBrowser*rr, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, AvahiRecord *record, void* userdata) { +static void record_browser_callback( + AvahiSRecordBrowser*rr, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiRecord *record, + AvahiLookupResultFlags flags, + void* userdata) { + AvahiSDomainBrowser *b = userdata; - char *n; + char *n = NULL; assert(rr); - assert(record); assert(b); - - assert(record->key->type == AVAHI_DNS_TYPE_PTR); - - n = avahi_normalize_name(record->data.ptr.name); - b->callback(b, interface, protocol, event, n, b->userdata); + + if (record) { + assert(record->key->type == AVAHI_DNS_TYPE_PTR); + n = avahi_normalize_name(record->data.ptr.name); + } + + b->callback(b, interface, protocol, event, n, flags, b->userdata); avahi_free(n); } -AvahiSDomainBrowser *avahi_s_domain_browser_new(AvahiServer *server, AvahiIfIndex interface, AvahiProtocol protocol, const char *domain, AvahiDomainBrowserType type, AvahiSDomainBrowserCallback callback, void* userdata) { +AvahiSDomainBrowser *avahi_s_domain_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *domain, + AvahiDomainBrowserType type, + AvahiLookupFlags flags, + AvahiSDomainBrowserCallback callback, + void* userdata) { + AvahiSDomainBrowser *b; AvahiKey *k; char *n = NULL; @@ -65,18 +84,31 @@ AvahiSDomainBrowser *avahi_s_domain_browser_new(AvahiServer *server, AvahiIfInde assert(callback); assert(type >= AVAHI_DOMAIN_BROWSER_BROWSE && type <= AVAHI_DOMAIN_BROWSER_BROWSE_LEGACY); + if (!AVAHI_IF_VALID(interface)) { + avahi_server_set_errno(server, AVAHI_ERR_INVALID_INTERFACE); + return NULL; + } + if (domain && !avahi_is_valid_domain_name(domain)) { avahi_server_set_errno(server, AVAHI_ERR_INVALID_DOMAIN_NAME); return NULL; } + if (!domain) + domain = server->domain_name; + + if (!AVAHI_VALID_FLAGS(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST)) { + avahi_server_set_errno(server, AVAHI_ERR_INVALID_FLAGS); + return NULL; + } + if (!(b = avahi_new(AvahiSDomainBrowser, 1))) { avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); return NULL; } b->server = server; - b->domain_name = avahi_normalize_name(domain ? domain : "local"); + b->domain_name = avahi_normalize_name(domain); b->callback = callback; b->userdata = userdata; @@ -109,7 +141,7 @@ AvahiSDomainBrowser *avahi_s_domain_browser_new(AvahiServer *server, AvahiIfInde k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR); avahi_free(n); - b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, record_browser_callback, b); + b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b); avahi_key_unref(k); if (!b->record_browser) { diff --git a/avahi-core/browse-service-type.c b/avahi-core/browse-service-type.c index 896e46b..3ea28c0 100644 --- a/avahi-core/browse-service-type.c +++ b/avahi-core/browse-service-type.c @@ -44,40 +44,51 @@ struct AvahiSServiceTypeBrowser { AVAHI_LLIST_FIELDS(AvahiSServiceTypeBrowser, browser); }; -static void record_browser_callback(AvahiSRecordBrowser*rr, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, AvahiRecord *record, void* userdata) { +static void record_browser_callback( + AvahiSRecordBrowser*rr, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiRecord *record, + AvahiLookupResultFlags flags, + void* userdata) { + AvahiSServiceTypeBrowser *b = userdata; - char *n, *e, *c; + char *n = NULL, *c = NULL; assert(rr); - assert(record); assert(b); - assert(record->key->type == AVAHI_DNS_TYPE_PTR); - - n = avahi_normalize_name(record->data.ptr.name); - - if (*n != '_') - goto fail; - - for (c = e = n; *c == '_';) { - c += strcspn(c, "."); - - if (*c == 0) + if (record) { + char *e; + + assert(record->key->type == AVAHI_DNS_TYPE_PTR); + + n = avahi_normalize_name(record->data.ptr.name); + + if (*n != '_') + goto fail; + + for (c = e = n; *c == '_';) { + c += strcspn(c, "."); + + if (*c == 0) + goto fail; + + assert(*c == '.'); + e = c; + c++; + } + + *e = 0; + + if (!avahi_domain_equal(c, b->domain_name)) goto fail; - - assert(*c == '.'); - e = c; - c++; } - - *e = 0; - - if (!avahi_domain_equal(c, b->domain_name)) - goto fail; - b->callback(b, interface, protocol, event, n, c, b->userdata); + b->callback(b, interface, protocol, event, n, c, flags, b->userdata); avahi_free(n); - + return; fail: @@ -85,7 +96,15 @@ fail: avahi_free(n); } -AvahiSServiceTypeBrowser *avahi_s_service_type_browser_new(AvahiServer *server, AvahiIfIndex interface, AvahiProtocol protocol, const char *domain, AvahiSServiceTypeBrowserCallback callback, void* userdata) { +AvahiSServiceTypeBrowser *avahi_s_service_type_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *domain, + AvahiLookupFlags flags, + AvahiSServiceTypeBrowserCallback callback, + void* userdata) { + AvahiSServiceTypeBrowser *b; AvahiKey *k; char *n = NULL; @@ -98,13 +117,21 @@ AvahiSServiceTypeBrowser *avahi_s_service_type_browser_new(AvahiServer *server, return NULL; } + if (!domain) + domain = server->domain_name; + + if (!AVAHI_VALID_FLAGS(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST)) { + avahi_server_set_errno(server, AVAHI_ERR_INVALID_FLAGS); + return NULL; + } + if (!(b = avahi_new(AvahiSServiceTypeBrowser, 1))) { avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); return NULL; } b->server = server; - b->domain_name = avahi_normalize_name(domain ? domain : "local"); + b->domain_name = avahi_normalize_name(domain); b->callback = callback; b->userdata = userdata; @@ -114,7 +141,7 @@ AvahiSServiceTypeBrowser *avahi_s_service_type_browser_new(AvahiServer *server, k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR); avahi_free(n); - b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, record_browser_callback, b); + b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b); avahi_key_unref(k); if (!b->record_browser) diff --git a/avahi-core/browse-service.c b/avahi-core/browse-service.c index 9419122..55fba6a 100644 --- a/avahi-core/browse-service.c +++ b/avahi-core/browse-service.c @@ -45,39 +45,49 @@ struct AvahiSServiceBrowser { AVAHI_LLIST_FIELDS(AvahiSServiceBrowser, browser); }; -static void record_browser_callback(AvahiSRecordBrowser*rr, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, AvahiRecord *record, void* userdata) { +static void record_browser_callback( + AvahiSRecordBrowser*rr, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiRecord *record, + AvahiLookupResultFlags flags, + void* userdata) { + AvahiSServiceBrowser *b = userdata; - char *n, *e, *c, *s; + char *n = NULL, *c = NULL, *s = NULL; char service[128]; assert(rr); - assert(record); assert(b); - assert(record->key->type == AVAHI_DNS_TYPE_PTR); - - c = n = avahi_normalize_name(record->data.ptr.name); - - if (!(avahi_unescape_label((const char**) &c, service, sizeof(service)))) - goto fail; - - for (s = e = c; *c == '_';) { - c += strcspn(c, "."); - - if (*c == 0) + if (record) { + char *e; + assert(record->key->type == AVAHI_DNS_TYPE_PTR); + + c = n = avahi_normalize_name(record->data.ptr.name); + + if (!(avahi_unescape_label((const char**) &c, service, sizeof(service)))) + goto fail; + + for (s = e = c; *c == '_';) { + c += strcspn(c, "."); + + if (*c == 0) + goto fail; + + assert(*c == '.'); + e = c; + c++; + } + + *e = 0; + + if (!avahi_domain_equal(c, b->domain_name)) goto fail; - - assert(*c == '.'); - e = c; - c++; } - - *e = 0; - - if (!avahi_domain_equal(c, b->domain_name)) - goto fail; - b->callback(b, interface, protocol, event, service, s, c, b->userdata); + b->callback(b, interface, protocol, event, record ? service : NULL, s, c, flags, b->userdata); avahi_free(n); return; @@ -87,7 +97,16 @@ fail: avahi_free(n); } -AvahiSServiceBrowser *avahi_s_service_browser_new(AvahiServer *server, AvahiIfIndex interface, AvahiProtocol protocol, const char *service_type, const char *domain, AvahiSServiceBrowserCallback callback, void* userdata) { +AvahiSServiceBrowser *avahi_s_service_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *service_type, + const char *domain, + AvahiLookupFlags flags, + AvahiSServiceBrowserCallback callback, + void* userdata) { + AvahiSServiceBrowser *b; AvahiKey *k; char *n = NULL; @@ -106,13 +125,21 @@ AvahiSServiceBrowser *avahi_s_service_browser_new(AvahiServer *server, AvahiIfIn return NULL; } + if (!domain) + domain = server->domain_name; + + if (!AVAHI_VALID_FLAGS(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST)) { + avahi_server_set_errno(server, AVAHI_ERR_INVALID_FLAGS); + return NULL; + } + if (!(b = avahi_new(AvahiSServiceBrowser, 1))) { avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); return NULL; } b->server = server; - b->domain_name = avahi_normalize_name(domain ? domain : "local"); + b->domain_name = avahi_normalize_name(domain); b->service_type = avahi_normalize_name(service_type); b->callback = callback; b->userdata = userdata; @@ -122,7 +149,7 @@ AvahiSServiceBrowser *avahi_s_service_browser_new(AvahiServer *server, AvahiIfIn k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR); avahi_free(n); - b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, record_browser_callback, b); + b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b); avahi_key_unref(k); diff --git a/avahi-core/browse.c b/avahi-core/browse.c index fa97c35..a6ba7ca 100644 --- a/avahi-core/browse.c +++ b/avahi-core/browse.c @@ -23,137 +23,512 @@ #include #endif +#include + #include #include #include +#include +#include +#include #include "browse.h" #include "log.h" +#include "querier.h" + +#define AVAHI_MAX_LOOKUPS_PER_BROWSER 15 -struct AvahiSRecordBrowser { - int dead; +struct AvahiSRBLookup { + AvahiSRecordBrowser *record_browser; - AvahiServer *server; - AvahiKey *key; + unsigned ref; + AvahiIfIndex interface; AvahiProtocol protocol; - unsigned sec_delay; - - AvahiTimeEvent *query_time_event; - AvahiTimeEvent *scan_time_event; + AvahiLookupFlags flags; + + AvahiKey *key; - AvahiSRecordBrowserCallback callback; - void* userdata; + AvahiWideAreaLookup *wide_area; + AvahiMulticastLookup *multicast; - AVAHI_LLIST_FIELDS(AvahiSRecordBrowser, browser); - AVAHI_LLIST_FIELDS(AvahiSRecordBrowser, by_key); + AvahiRList *cname_lookups; + + AVAHI_LLIST_FIELDS(AvahiSRBLookup, lookups); }; -static void elapse_callback(AvahiTimeEvent *e, void *userdata) { - AvahiSRecordBrowser *s = userdata; - struct timeval tv; -/* char *t; */ +static void lookup_handle_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r); +static void lookup_drop_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r); + +static void transport_flags_from_domain(AvahiServer *s, AvahiLookupFlags *flags, const char *domain) { + assert(flags); + assert(domain); + + assert(!((*flags & AVAHI_LOOKUP_USE_MULTICAST) && (*flags & AVAHI_LOOKUP_USE_WIDE_AREA))); + + if (*flags & (AVAHI_LOOKUP_USE_MULTICAST|AVAHI_LOOKUP_USE_WIDE_AREA)) + return; + + if (!s->wide_area_lookup_engine || + !avahi_wide_area_has_servers(s->wide_area_lookup_engine) || + avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_LOCAL) || + avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV4) || + avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV6)) + *flags |= AVAHI_LOOKUP_USE_MULTICAST; + else + *flags |= AVAHI_LOOKUP_USE_WIDE_AREA; +} + + +static AvahiSRBLookup* lookup_new( + AvahiSRecordBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiLookupFlags flags, + AvahiKey *key) { + + AvahiSRBLookup *l; - assert(s); + assert(b); + assert(AVAHI_IF_VALID(interface)); + assert(AVAHI_PROTO_VALID(protocol)); - avahi_server_post_query(s->server, s->interface, s->protocol, s->key); + if (b->n_lookups >= AVAHI_MAX_LOOKUPS_PER_BROWSER) + /* We don't like cyclic CNAMEs */ + return NULL; + + if (!(l = avahi_new(AvahiSRBLookup, 1))) + return NULL; + + l->ref = 1; + l->record_browser = b; + l->interface = interface; + l->protocol = protocol; + l->key = avahi_key_ref(key); + l->wide_area = NULL; + l->multicast = NULL; + l->cname_lookups = NULL; + l->flags = flags; + + transport_flags_from_domain(b->server, &l->flags, key->name); + + AVAHI_LLIST_PREPEND(AvahiSRBLookup, lookups, b->lookups, l); - s->sec_delay *= 2; + b->n_lookups ++; - if (s->sec_delay >= 60*60) /* 1h */ - s->sec_delay = 60*60; + return l; +} + +static void lookup_unref(AvahiSRBLookup *l) { + assert(l); + assert(l->ref >= 1); + + if (--l->ref >= 1) + return; + + AVAHI_LLIST_REMOVE(AvahiSRBLookup, lookups, l->record_browser->lookups, l); + l->record_browser->n_lookups --; + + if (l->wide_area) { + avahi_wide_area_lookup_free(l->wide_area); + l->wide_area = NULL; + } -/* avahi_log_debug("Continuous querying for %s (%i)", t = avahi_key_to_string(s->key), s->sec_delay); */ -/* avahi_free(t); */ + if (l->multicast) { + avahi_multicast_lookup_free(l->multicast); + l->multicast = NULL; + } + + while (l->cname_lookups) { + lookup_unref(l->cname_lookups->data); + l->cname_lookups = avahi_rlist_remove_by_link(l->cname_lookups, l->cname_lookups); + } - avahi_elapse_time(&tv, s->sec_delay*1000, 0); - avahi_time_event_update(s->query_time_event, &tv); + avahi_key_unref(l->key); + avahi_free(l); } -struct cbdata { - AvahiSRecordBrowser *record_browser; - AvahiInterface *interface; -}; +static AvahiSRBLookup* lookup_ref(AvahiSRBLookup *l) { + assert(l); + assert(l->ref >= 1); -static void* scan_cache_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) { - struct cbdata *cbdata = userdata; + l->ref++; + return l; +} - assert(c); - assert(pattern); - assert(e); - assert(cbdata); +static AvahiSRBLookup *lookup_find( + AvahiSRecordBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiLookupFlags flags, + AvahiKey *key) { + + AvahiSRBLookup *l; + + assert(b); - if (cbdata->record_browser->dead) - return NULL; + for (l = b->lookups; l; l = l->lookups_next) { - cbdata->record_browser->callback( - cbdata->record_browser, - cbdata->interface->hardware->index, - cbdata->interface->protocol, - AVAHI_BROWSER_NEW, - e->record, - cbdata->record_browser->userdata); + if ((l->interface == AVAHI_IF_UNSPEC || l->interface == interface) && + (l->interface == AVAHI_PROTO_UNSPEC || l->protocol == protocol) && + l->flags == flags && + avahi_key_equal(l->key, key)) + + return l; + } return NULL; } -static void scan_interface_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { - AvahiSRecordBrowser *b = userdata; - struct cbdata cbdata; +static void lookup_wide_area_callback( + AvahiWideAreaLookupEngine *e, + AvahiBrowserEvent event, + AvahiLookupResultFlags flags, + AvahiRecord *r, + void *userdata) { + + AvahiSRBLookup *l = userdata; + AvahiSRecordBrowser *b; + + assert(e); + assert(l); + + b = l->record_browser; + + if (b->dead) + return; + + lookup_ref(l); + + switch (event) { + case AVAHI_BROWSER_NEW: + assert(r); + + if (r->key->clazz == AVAHI_DNS_CLASS_IN && + r->key->type == AVAHI_DNS_TYPE_CNAME) + /* It's a CNAME record, so let's follow it. We only follow it on wide area DNS! */ + lookup_handle_cname(l, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_LOOKUP_USE_WIDE_AREA, r); + else { + /* It's a normal record, so let's call the user callback */ + assert(avahi_key_equal(r->key, l->key)); + + b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, event, r, flags, b->userdata); + } + break; + + case AVAHI_BROWSER_REMOVE: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + /* Not defined for wide area DNS */ + abort(); + + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_NOT_FOUND: + case AVAHI_BROWSER_FAILURE: + + b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, event, NULL, flags, b->userdata); + break; + } + + lookup_unref(l); + +} + +static void lookup_multicast_callback( + AvahiMulticastLookupEngine *e, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiLookupResultFlags flags, + AvahiRecord *r, + void *userdata) { + + AvahiSRBLookup *l = userdata; + AvahiSRecordBrowser *b; + + assert(e); + assert(l); + + b = l->record_browser; + + if (b->dead) + return; + + lookup_ref(l); + + switch (event) { + case AVAHI_BROWSER_NEW: + assert(r); + + if (r->key->clazz == AVAHI_DNS_CLASS_IN && + r->key->type == AVAHI_DNS_TYPE_CNAME) + /* It's a CNAME record, so let's follow it. We allow browsing on both multicast and wide area. */ + lookup_handle_cname(l, interface, protocol, b->flags, r); + else { + /* It's a normal record, so let's call the user callback */ + assert(avahi_key_equal(b->key, l->key)); + + b->callback(b, interface, protocol, event, r, flags, b->userdata); + } + break; + + case AVAHI_BROWSER_REMOVE: + assert(r); + + if (r->key->clazz == AVAHI_DNS_CLASS_IN && + r->key->type == AVAHI_DNS_TYPE_CNAME) + /* It's a CNAME record, so let's drop that query! */ + lookup_drop_cname(l, interface, protocol, 0, r); + else { + /* It's a normal record, so let's call the user callback */ + assert(avahi_key_equal(b->key, l->key)); + + b->callback(b, interface, protocol, event, r, flags, b->userdata); + } + break; + + case AVAHI_BROWSER_ALL_FOR_NOW: + + b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, event, NULL, flags, b->userdata); + break; + + case AVAHI_BROWSER_CACHE_EXHAUSTED: + case AVAHI_BROWSER_NOT_FOUND: + case AVAHI_BROWSER_FAILURE: + /* Not defined for multicast DNS */ + abort(); - cbdata.record_browser = b; - cbdata.interface = i; + } + + lookup_unref(l); +} + +static int lookup_start(AvahiSRBLookup *l) { + assert(l); + + assert(!(l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) != !(l->flags & AVAHI_LOOKUP_USE_MULTICAST)); + assert(!l->wide_area && !l->multicast); + + if (l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) { - assert(m); - assert(i); + if (!(l->wide_area = avahi_wide_area_lookup_new(l->record_browser->server->wide_area_lookup_engine, l->key, lookup_wide_area_callback, l))) + return -1; + + } else { + assert(l->flags & AVAHI_LOOKUP_USE_MULTICAST); + + if (!(l->multicast = avahi_multicast_lookup_new(l->record_browser->server->multicast_lookup_engine, l->interface, l->protocol, l->key, lookup_multicast_callback, l))) + return -1; + } + + return 0; +} + +static int lookup_scan_cache(AvahiSRBLookup *l) { + int n = 0; + + assert(l); + + assert(!(l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) != !(l->flags & AVAHI_LOOKUP_USE_MULTICAST)); + + + if (l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) { + n = (int) avahi_wide_area_scan_cache(l->record_browser->server->wide_area_lookup_engine, l->key, lookup_wide_area_callback, l); + + } else { + assert(l->flags & AVAHI_LOOKUP_USE_MULTICAST); + n = (int) avahi_multicast_lookup_engine_scan_cache(l->record_browser->server->multicast_lookup_engine, l->interface, l->protocol, l->key, lookup_multicast_callback, l); + } + + return n; +} + +static AvahiSRBLookup* lookup_add(AvahiSRecordBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiKey *key) { + AvahiSRBLookup *l; + assert(b); + assert(!b->dead); - if (!b->dead) - avahi_cache_walk(i->cache, b->key, scan_cache_callback, &cbdata); + if ((l = lookup_find(b, interface, protocol, flags, key))) + return lookup_ref(l); + + if (!(l = lookup_new(b, interface, protocol, flags, key))) + return NULL; + + return l; } -static void scan_callback(AvahiTimeEvent *e, void *userdata) { +static int lookup_go(AvahiSRBLookup *l) { + int n = 0; + assert(l); + + if (l->record_browser->dead) + return 0; + + lookup_ref(l); + + /* Browse the cache for the root request */ + n = lookup_scan_cache(l); + + /* Start the lookup */ + if (!l->record_browser->dead && l->ref > 1) { + + if ((l->flags & AVAHI_LOOKUP_USE_MULTICAST) || n == 0) + /* We do no start a query if the cache contained entries and we're on wide area */ + + if (lookup_start(l) < 0) + n = -1; + } + + lookup_unref(l); + + return n; +} + +static void lookup_handle_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r) { + AvahiKey *k; + AvahiSRBLookup *n; + + assert(l); + assert(r); + + assert(r->key->clazz == AVAHI_DNS_CLASS_IN); + assert(r->key->type == AVAHI_DNS_TYPE_CNAME); + + k = avahi_key_new(r->data.ptr.name, l->record_browser->key->clazz, l->record_browser->key->type); + n = lookup_add(l->record_browser, interface, protocol, flags, k); + avahi_key_unref(k); + + if (!n) { + avahi_log_debug(__FILE__": Failed to create SRBLookup."); + return; + } + + l->cname_lookups = avahi_rlist_prepend(l->cname_lookups, lookup_ref(n)); + + lookup_go(n); +} + +static void lookup_drop_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r) { + AvahiKey *k; + AvahiSRBLookup *n; + AvahiRList *rl; + + assert(r->key->clazz == AVAHI_DNS_CLASS_IN); + assert(r->key->type == AVAHI_DNS_TYPE_CNAME); + + k = avahi_key_new(r->data.ptr.name, l->record_browser->key->clazz, l->record_browser->key->type); + + for (rl = l->cname_lookups; rl; rl = rl->rlist_next) { + n = rl->data; + + assert(n); + + if ((n->interface == AVAHI_IF_UNSPEC || n->interface == interface) && + (n->interface == AVAHI_PROTO_UNSPEC || n->protocol == protocol) && + n->flags == flags && + avahi_key_equal(n->key, k)) + break; + } + + avahi_key_unref(k); + + if (rl) { + l->cname_lookups = avahi_rlist_remove_by_link(l->cname_lookups, rl); + lookup_unref(n); + } +} + +static void defer_callback(AvahiTimeEvent *e, void *userdata) { AvahiSRecordBrowser *b = userdata; + int n; + assert(b); + assert(!b->dead); - /* Scan the caches */ - if (!b->dead) - avahi_interface_monitor_walk(b->server->monitor, b->interface, b->protocol, scan_interface_callback, b); + /* Remove the defer timeout */ + if (b->defer_time_event) { + avahi_time_event_free(b->defer_time_event); + b->defer_time_event = NULL; + } + + /* Create initial query */ + assert(!b->root_lookup); + b->root_lookup = lookup_add(b, b->interface, b->protocol, b->flags, b->key); + assert(b->root_lookup); + + n = lookup_go(b->root_lookup); - if (b->scan_time_event) { - avahi_time_event_free(b->scan_time_event); - b->scan_time_event = NULL; + if (b->dead) + return; + + if (n < 0) { + /* sending of the initial query failed */ + + b->callback( + b, b->interface, b->protocol, AVAHI_BROWSER_FAILURE, NULL, + b->flags & AVAHI_LOOKUP_USE_WIDE_AREA ? AVAHI_LOOKUP_CALLBACK_WIDE_AREA : AVAHI_LOOKUP_CALLBACK_MULTICAST, + b->userdata); + + avahi_s_record_browser_cancel(b); + return; + } + + /* Tell the client that we're done with the cache */ + b->callback( + b, b->interface, b->protocol, AVAHI_BROWSER_CACHE_EXHAUSTED, NULL, + b->flags & AVAHI_LOOKUP_USE_WIDE_AREA ? AVAHI_LOOKUP_CALLBACK_WIDE_AREA : AVAHI_LOOKUP_CALLBACK_MULTICAST, + b->userdata); + + if (!b->dead && b->root_lookup && b->root_lookup->flags & AVAHI_LOOKUP_USE_WIDE_AREA && n > 0) { + + /* If we do wide area lookups and the the cache contained + * entries, we assume that it is complete, and tell the user + * so by firing ALL_FOR_NOW. */ + + b->callback(b, b->interface, b->protocol, AVAHI_BROWSER_ALL_FOR_NOW, NULL, AVAHI_LOOKUP_CALLBACK_WIDE_AREA, b->userdata); } } void avahi_s_record_browser_restart(AvahiSRecordBrowser *b) { assert(b); + assert(!b->dead); - if (!b->scan_time_event) { - b->scan_time_event = avahi_time_event_new(b->server->time_event_queue, NULL, scan_callback, b); - assert(b->scan_time_event); + avahi_s_record_browser_cancel(b); + + /* Request a new iteration of the cache scanning */ + if (!b->defer_time_event) { + b->defer_time_event = avahi_time_event_new(b->server->time_event_queue, NULL, defer_callback, b); + assert(b->defer_time_event); } +} - avahi_server_post_query(b->server, b->interface, b->protocol, b->key); +#define CHECK_VALIDITY_RETURN_NULL(server, expression, error) { \ + if (!(expression)) { \ + avahi_server_set_errno((server), (error)); \ + return NULL; \ + } \ } -AvahiSRecordBrowser *avahi_s_record_browser_new(AvahiServer *server, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key, AvahiSRecordBrowserCallback callback, void* userdata) { - AvahiSRecordBrowser *b, *t; - struct timeval tv; +AvahiSRecordBrowser *avahi_s_record_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiKey *key, + AvahiLookupFlags flags, + AvahiSRecordBrowserCallback callback, + void* userdata) { + + AvahiSRecordBrowser *b; assert(server); assert(key); assert(callback); - if (avahi_key_is_pattern(key)) { - avahi_server_set_errno(server, AVAHI_ERR_IS_PATTERN); - return NULL; - } - - if (!avahi_key_is_valid(key)) { - avahi_server_set_errno(server, AVAHI_ERR_INVALID_KEY); - return NULL; - } + CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); + CHECK_VALIDITY_RETURN_NULL(server, !avahi_key_is_pattern(key), AVAHI_ERR_IS_PATTERN); + CHECK_VALIDITY_RETURN_NULL(server, avahi_key_is_valid(key), AVAHI_ERR_INVALID_KEY); + CHECK_VALIDITY_RETURN_NULL(server, AVAHI_VALID_FLAGS(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); + CHECK_VALIDITY_RETURN_NULL(server, !(flags & AVAHI_LOOKUP_USE_WIDE_AREA) || !(flags & AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); if (!(b = avahi_new(AvahiSRecordBrowser, 1))) { avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); @@ -162,31 +537,43 @@ AvahiSRecordBrowser *avahi_s_record_browser_new(AvahiServer *server, AvahiIfInde b->dead = 0; b->server = server; - b->key = avahi_key_ref(key); b->interface = interface; b->protocol = protocol; + b->key = avahi_key_ref(key); + b->flags = flags; b->callback = callback; b->userdata = userdata; - b->sec_delay = 1; - avahi_server_post_query(b->server, b->interface, b->protocol, b->key); + b->n_lookups = 0; + AVAHI_LLIST_HEAD_INIT(AvahiSRBLookup, b->lookups); + b->root_lookup = NULL; - avahi_elapse_time(&tv, b->sec_delay*1000, 0); - b->query_time_event = avahi_time_event_new(server->time_event_queue, &tv, elapse_callback, b); - AVAHI_LLIST_PREPEND(AvahiSRecordBrowser, browser, server->record_browsers, b); - /* Add the new entry to the record_browser hash table */ - t = avahi_hashmap_lookup(server->record_browser_hashmap, key); - AVAHI_LLIST_PREPEND(AvahiSRecordBrowser, by_key, t, b); - avahi_hashmap_replace(server->record_browser_hashmap, key, t); - - /* The currenlty cached entries are scanned a bit later */ - b->scan_time_event = avahi_time_event_new(server->time_event_queue, NULL, scan_callback, b); - assert(b->scan_time_event); + /* The currently cached entries are scanned a bit later, and than we will start querying, too */ + b->defer_time_event = avahi_time_event_new(server->time_event_queue, NULL, defer_callback, b); + assert(b->defer_time_event); + return b; } +void avahi_s_record_browser_cancel(AvahiSRecordBrowser *b) { + assert(b); + + if (b->root_lookup) { + lookup_unref(b->root_lookup); + b->root_lookup = NULL; + } + + if (b->defer_time_event) { + avahi_time_event_free(b->defer_time_event); + b->defer_time_event = NULL; + } + + + +} + void avahi_s_record_browser_free(AvahiSRecordBrowser *b) { assert(b); assert(!b->dead); @@ -194,36 +581,16 @@ void avahi_s_record_browser_free(AvahiSRecordBrowser *b) { b->dead = 1; b->server->need_browser_cleanup = 1; - if (b->query_time_event) { - avahi_time_event_free(b->query_time_event); - b->query_time_event = NULL; - } - - if (b->scan_time_event) { - avahi_time_event_free(b->scan_time_event); - b->scan_time_event = NULL; - } + avahi_s_record_browser_cancel(b); } void avahi_s_record_browser_destroy(AvahiSRecordBrowser *b) { - AvahiSRecordBrowser *t; - assert(b); + + avahi_s_record_browser_cancel(b); AVAHI_LLIST_REMOVE(AvahiSRecordBrowser, browser, b->server->record_browsers, b); - t = avahi_hashmap_lookup(b->server->record_browser_hashmap, b->key); - AVAHI_LLIST_REMOVE(AvahiSRecordBrowser, by_key, t, b); - if (t) - avahi_hashmap_replace(b->server->record_browser_hashmap, t->key, t); - else - avahi_hashmap_remove(b->server->record_browser_hashmap, b->key); - - if (b->query_time_event) - avahi_time_event_free(b->query_time_event); - if (b->scan_time_event) - avahi_time_event_free(b->scan_time_event); - avahi_key_unref(b->key); avahi_free(b); @@ -235,46 +602,19 @@ void avahi_browser_cleanup(AvahiServer *server) { assert(server); - for (b = server->record_browsers; b; b = n) { - n = b->browser_next; + while (server->need_browser_cleanup) { + server->need_browser_cleanup = 0; - if (b->dead) - avahi_s_record_browser_destroy(b); - } - - server->need_browser_cleanup = 0; -} - -void avahi_browser_notify(AvahiServer *server, AvahiInterface *i, AvahiRecord *record, AvahiBrowserEvent event) { - AvahiSRecordBrowser *b; - - assert(server); - assert(record); - - for (b = avahi_hashmap_lookup(server->record_browser_hashmap, record->key); b; b = b->by_key_next) - if (!b->dead && avahi_interface_match(i, b->interface, b->protocol)) - b->callback(b, i->hardware->index, i->protocol, event, record, b->userdata); + for (b = server->record_browsers; b; b = n) { + n = b->browser_next; + + if (b->dead) + avahi_s_record_browser_destroy(b); + } + } + + if (server->wide_area_lookup_engine) + avahi_wide_area_cleanup(server->wide_area_lookup_engine); + avahi_multicast_lookup_engine_cleanup(server->multicast_lookup_engine); } -int avahi_is_subscribed(AvahiServer *server, AvahiInterface *i, AvahiKey *k) { - AvahiSRecordBrowser *b; - assert(server); - assert(k); - - for (b = avahi_hashmap_lookup(server->record_browser_hashmap, k); b; b = b->by_key_next) - if (!b->dead && avahi_interface_match(i, b->interface, b->protocol)) - return 1; - - return 0; -} - -void avahi_browser_new_interface(AvahiServer*s, AvahiInterface *i) { - AvahiSRecordBrowser *b; - - assert(s); - assert(i); - - for (b = s->record_browsers; b; b = b->browser_next) - if (avahi_interface_match(i, b->interface, b->protocol)) - avahi_interface_post_query(i, b->key, 0); -} diff --git a/avahi-core/browse.h b/avahi-core/browse.h index 0d1cdb8..70e724f 100644 --- a/avahi-core/browse.h +++ b/avahi-core/browse.h @@ -23,19 +23,43 @@ ***/ #include + #include "core.h" #include "timeeventq.h" #include "server.h" +#include "dns.h" +#include "lookup.h" -void avahi_browser_cleanup(AvahiServer *server); -void avahi_browser_notify(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, AvahiBrowserEvent event); +typedef struct AvahiSRBLookup AvahiSRBLookup; -int avahi_is_subscribed(AvahiServer *s, AvahiInterface *i, AvahiKey *k); +struct AvahiSRecordBrowser { + AVAHI_LLIST_FIELDS(AvahiSRecordBrowser, browser); + int dead; + AvahiServer *server; -void avahi_s_record_browser_destroy(AvahiSRecordBrowser *b); + AvahiKey *key; + AvahiIfIndex interface; + AvahiProtocol protocol; + AvahiLookupFlags flags; + + AvahiTimeEvent *defer_time_event; + + AvahiSRecordBrowserCallback callback; + void* userdata; + + /* Lookup data */ + AVAHI_LLIST_HEAD(AvahiSRBLookup, lookups); + unsigned n_lookups; + AvahiSRBLookup *root_lookup; +}; + +void avahi_browser_cleanup(AvahiServer *server); + +void avahi_s_record_browser_destroy(AvahiSRecordBrowser *b); void avahi_s_record_browser_restart(AvahiSRecordBrowser *b); +void avahi_s_record_browser_cancel(AvahiSRecordBrowser *b); -void avahi_browser_new_interface(AvahiServer*s, AvahiInterface *i); +#define AVAHI_VALID_FLAGS(flags, max) (!((flags) & ~(max))) #endif diff --git a/avahi-core/cache.c b/avahi-core/cache.c index 924b8c9..6332b12 100644 --- a/avahi-core/cache.c +++ b/avahi-core/cache.c @@ -32,7 +32,7 @@ #include "cache.h" #include "log.h" -#define AVAHI_MAX_CACHE_ENTRIES 200 +#define AVAHI_MAX_CACHE_ENTRIES 500 static void remove_entry(AvahiCache *c, AvahiCacheEntry *e) { AvahiCacheEntry *t; @@ -56,8 +56,8 @@ static void remove_entry(AvahiCache *c, AvahiCacheEntry *e) { if (e->time_event) avahi_time_event_free(e->time_event); - avahi_browser_notify(c->server, c->interface, e->record, AVAHI_BROWSER_REMOVE); - + avahi_multicast_lookup_engine_notify(c->server->multicast_lookup_engine, c->interface, e->record, AVAHI_BROWSER_REMOVE); + avahi_record_unref(e->record); avahi_free(e); @@ -204,7 +204,7 @@ static void elapse_func(AvahiTimeEvent *t, void *userdata) { assert(percent > 0); /* Request a cache update, if we are subscribed to this entry */ - if (avahi_is_subscribed(e->cache->server, e->cache->interface, e->record->key)) { + if (avahi_querier_exists(e->cache->interface, e->record->key)) { /* avahi_log_debug("Requesting cache entry update at %i%% for %s.", percent, txt); */ avahi_interface_post_query(e->cache->interface, e->record->key, 1); } @@ -345,7 +345,7 @@ void avahi_cache_update(AvahiCache *c, AvahiRecord *r, int cache_flush, const Av c->n_entries++; /* Notify subscribers */ - avahi_browser_notify(c->server, c->interface, e->record, AVAHI_BROWSER_NEW); + avahi_multicast_lookup_engine_notify(c->server->multicast_lookup_engine, c->interface, e->record, AVAHI_BROWSER_NEW); } e->origin = *a; diff --git a/avahi-core/conformance-test.c b/avahi-core/conformance-test.c index 3702372..392dddc 100644 --- a/avahi-core/conformance-test.c +++ b/avahi-core/conformance-test.c @@ -37,6 +37,7 @@ #include "core.h" #include "log.h" +#include "lookup.h" static char *name = NULL; static AvahiSEntryGroup *group = NULL; @@ -73,7 +74,7 @@ static void create_service(const char *t) { else group = avahi_s_entry_group_new(avahi, entry_group_callback, NULL); - avahi_server_add_service(avahi, group, 0, AF_UNSPEC, name, "_http._tcp", NULL, NULL, 80, "foo", NULL); + avahi_server_add_service(avahi, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, name, "_http._tcp", NULL, NULL, 80, "foo", NULL); avahi_s_entry_group_commit(group); try++; diff --git a/avahi-core/core.h b/avahi-core/core.h index abc30c0..705fa31 100644 --- a/avahi-core/core.h +++ b/avahi-core/core.h @@ -22,14 +22,8 @@ USA. ***/ -/** \file core.h The Avahi Multicast DNS and DNS Service Discovery implmentation. */ +/** \file core.h The Avahi Multicast DNS and DNS Service Discovery implementation. */ -/** \example core-publish-service.c Example how to register a DNS-SD - * service using an embedded mDNS stack. It behaves like a network - * printer registering both an IPP and a BSD LPR service. */ - -/** \example core-browse-services.c Example how to browse for DNS-SD - * services using an embedded mDNS stack. */ #include @@ -40,9 +34,6 @@ AVAHI_C_DECL_BEGIN /** An mDNS responder object */ typedef struct AvahiServer AvahiServer; -/** A group of locally registered DNS RRs */ -typedef struct AvahiSEntryGroup AvahiSEntryGroup; - #ifndef DOXYGEN_SHOULD_SKIP_THIS AVAHI_C_DECL_END #endif @@ -56,6 +47,9 @@ AVAHI_C_DECL_END AVAHI_C_DECL_BEGIN #endif +/** Maximum number of defined DNS servers for wide area DNS */ +#define AVAHI_MAX_WIDE_AREA_SERVERS 4 + /** Flags for server entries */ typedef enum { AVAHI_ENTRY_NULL = 0, /**< No special flags */ @@ -68,13 +62,10 @@ typedef enum { /** Prototype for callback functions which are called whenever the state of an AvahiServer object changes */ typedef void (*AvahiServerCallback) (AvahiServer *s, AvahiServerState state, void* userdata); -/** Prototype for callback functions which are called whenever the state of an AvahiSEntryGroup object changes */ -typedef void (*AvahiSEntryGroupCallback) (AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void* userdata); - /** Stores configuration options for a server instance */ typedef struct AvahiServerConfig { - char *host_name; /**< Default host name. If left empty defaults to the result of gethostname(2) of the libc */ - char *domain_name; /**< Default domain name. If left empty defaults to .local */ + char *host_name; /**< Default host name. If left empty defaults to the result of gethostname(2) of the libc */ + char *domain_name; /**< Default domain name. If left empty defaults to .local */ int use_ipv4; /**< Enable IPv4 support */ int use_ipv6; /**< Enable IPv6 support */ int publish_hinfo; /**< Register a HINFO record for the host containing the local OS and CPU type */ @@ -86,6 +77,9 @@ typedef struct AvahiServerConfig { int enable_reflector; /**< Reflect incoming mDNS traffic to all local networks. This allows mDNS based network browsing beyond ethernet borders */ int reflect_ipv; /**< if enable_reflector is 1, enable/disable reflecting between IPv4 and IPv6 */ int add_service_cookie; /**< Add magic service cookie to all locally generated records implicitly */ + int enable_wide_area; /**< Enable wide area support */ + AvahiAddress wide_area_servers[AVAHI_MAX_WIDE_AREA_SERVERS]; /** Unicast DNS server to use for wide area lookup */ + unsigned n_wide_area_servers; /**< Number of servers in wide_area_servers[] */ } AvahiServerConfig; /** Allocate a new mDNS responder object. */ @@ -156,412 +150,12 @@ void avahi_server_set_data(AvahiServer *s, void* userdata); /** Return the current state of the server object */ AvahiServerState avahi_server_get_state(AvahiServer *s); -/** Iterate through all local entries of the server. (when g is NULL) - * or of a specified entry group. At the first call state should point - * to a NULL initialized void pointer, That pointer is used to track - * the current iteration. It is not safe to call any other - * avahi_server_xxx() function during the iteration. If the last entry - * has been read, NULL is returned. */ -const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state); - /** Callback prototype for avahi_server_dump() */ typedef void (*AvahiDumpCallback)(const char *text, void* userdata); /** Dump the current server status by calling "callback" for each line. */ int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata); -/** Create a new entry group. The specified callback function is - * called whenever the state of the group changes. Use entry group - * objects to keep track of you RRs. Add new RRs to a group using - * avahi_server_add_xxx(). Make sure to call avahi_s_entry_group_commit() - * to start the registration process for your RRs */ -AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata); - -/** Free an entry group. All RRs assigned to the group are removed from the server */ -void avahi_s_entry_group_free(AvahiSEntryGroup *g); - -/** Commit an entry group. This starts the probing and registration process for all RRs in the group */ -int avahi_s_entry_group_commit(AvahiSEntryGroup *g); - -/** Remove all entries from the entry group and reset the state to AVAHI_ENTRY_GROUP_UNCOMMITED. */ -void avahi_s_entry_group_reset(AvahiSEntryGroup *g); - -/** Return 1 if the entry group is empty, i.e. has no records attached. */ -int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g); - -/** Return the current state of the specified entry group */ -AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g); - -/** Change the opaque user data pointer attached to an entry group object */ -void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata); - -/** Return the opaque user data pointer currently set for the entry group object */ -void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g); - -/** Add a new resource record to the server. Returns 0 on success, negative otherwise. */ -int avahi_server_add( - AvahiServer *s, /**< The server object to add this record to */ - AvahiSEntryGroup *g, /**< An entry group object if this new record shall be attached to one, or NULL. If you plan to remove the record sometime later you a required to pass an entry group object here. */ - AvahiIfIndex interface, /**< A numeric index of a network interface to attach this record to, or AVAHI_IF_UNSPEC to attach this record to all interfaces */ - AvahiProtocol protocol, /**< A protocol family to attach this record to. One of the AVAHI_PROTO_xxx constants. Use AVAHI_PROTO_UNSPEC to make this record available on all protocols (wich means on both IPv4 and IPv6). */ - AvahiEntryFlags flags, /**< Special flags for this record */ - AvahiRecord *r /**< The record to add. This function increases the reference counter of this object. */ ); - -/** Add a PTR RR to the server. See avahi_server_add() for more information. */ -int avahi_server_add_ptr( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiEntryFlags flags, - uint32_t ttl, /**< DNS TTL for this record */ - const char *name, /**< PTR record name */ - const char *dest /**< pointer destination */ ); - -/** Add a PTR RR to the server. See avahi_server_add() for more information. */ -int avahi_server_add_txt( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiEntryFlags flags, - uint32_t ttl, /**< DNS TTL for this record */ - const char *name, /**< TXT record name */ - ... /**< Text record data, terminated by NULL */) AVAHI_GCC_SENTINEL; - -/** Add a PTR RR to the server. Mostly identical to - * avahi_server_add_text but takes a va_list instead of a variable - * number of arguments */ -int avahi_server_add_txt_va( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiEntryFlags flags, - uint32_t ttl, - const char *name, - va_list va); - -/** Add a PTR RR to the server. Mostly identical to - * avahi_server_add_text but takes an AvahiStringList record instead of a variable - * number of arguments. */ -int avahi_server_add_txt_strlst( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiEntryFlags flags, - uint32_t ttl, - const char *name, - AvahiStringList *strlst /**< TXT decord data as a AvahiString. This routine makes a deep copy of this object. */ ); - -/** Add an IP address mapping to the server. This will add both the - * host-name-to-address and the reverse mapping to the server. See - * avahi_server_add() for more information. If adding one of the RRs - * fails, the function returns with an error, but it is not defined if - * the other RR is deleted from the server or not. Therefore, you have - * to free the AvahiSEntryGroup and create a new one before - * proceeding. */ -int avahi_server_add_address( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiEntryFlags flags, - const char *name, - AvahiAddress *a); - -/** Add an DNS-SD service to the Server. This will add all required - * RRs to the server. See avahi_server_add() for more information. If - * adding one of the RRs fails, the function returns with an error, - * but it is not defined if the other RR is deleted from the server or - * not. Therefore, you have to free the AvahiSEntryGroup and create a - * new one before proceeding. */ -int avahi_server_add_service( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - const char *name, /**< Service name, e.g. "Lennart's Files" */ - const char *type, /**< DNS-SD type, e.g. "_http._tcp" */ - const char *domain, - const char *host, /**< Host name where this servcie resides, or NULL if on the local host */ - uint16_t port, /**< Port number of the service */ - ... /**< Text records, terminated by NULL */) AVAHI_GCC_SENTINEL; - -/** Mostly identical to avahi_server_add_service(), but takes an va_list for the TXT records. */ -int avahi_server_add_service_va( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - const char *name, - const char *type, - const char *domain, - const char *host, - uint16_t port, - va_list va); - -/** Mostly identical to avahi_server_add_service(), but takes an AvahiStringList object for the TXT records. The AvahiStringList object is copied. */ -int avahi_server_add_service_strlst( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - const char *name, - const char *type, - const char *domain, - const char *host, - uint16_t port, - AvahiStringList *strlst); - -/** The type of DNS server */ -typedef enum { - AVAHI_DNS_SERVER_RESOLVE, /**< Unicast DNS servers for normal resolves (_domain._udp)*/ - AVAHI_DNS_SERVER_UPDATE /**< Unicast DNS servers for updates (_dns-update._udp)*/ -} AvahiDNSServerType; - -/** Publish the specified unicast DNS server address via mDNS. You may - * browse for records create this way wit - * avahi_s_dns_server_browser_new(). */ -int avahi_server_add_dns_server_address( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - const char *domain, - AvahiDNSServerType type, - const AvahiAddress *address, - uint16_t port /** should be 53 */); - -/** Similar to avahi_server_add_dns_server_address(), but specify a -host name instead of an address. The specified host name should be -resolvable via mDNS */ -int avahi_server_add_dns_server_name( - AvahiServer *s, - AvahiSEntryGroup *g, - AvahiIfIndex interface, - AvahiProtocol protocol, - const char *domain, - AvahiDNSServerType type, - const char *name, - uint16_t port /** should be 53 */); - -/** A browsing object for arbitrary RRs */ -typedef struct AvahiSRecordBrowser AvahiSRecordBrowser; - -/** Callback prototype for AvahiSRecordBrowser events */ -typedef void (*AvahiSRecordBrowserCallback)( - AvahiSRecordBrowser *b, /**< The AvahiSRecordBrowser object that is emitting this callback */ - AvahiIfIndex interface, /**< Logical OS network interface number the record was found on */ - AvahiProtocol protocol, /**< Protocol number the record was found. */ - AvahiBrowserEvent event, /**< Browsing event, either AVAHI_BROWSER_NEW or AVAHI_BROWSER_REMOVE */ - AvahiRecord *record, /**< The record that was found */ - void* userdata /**< Arbitrary user data passed to avahi_s_record_browser_new() */ ); - -/** Create a new browsing object for arbitrary RRs */ -AvahiSRecordBrowser *avahi_s_record_browser_new( - AvahiServer *server, /**< The server object to which attach this query */ - AvahiIfIndex interface, /**< Logical OS interface number where to look for the records, or AVAHI_IF_UNSPEC to look on interfaces */ - AvahiProtocol protocol, /**< Protocol number to use when looking for the record, or AVAHI_PROTO_UNSPEC to look on all protocols */ - AvahiKey *key, /**< The search key */ - AvahiSRecordBrowserCallback callback, /**< The callback to call on browsing events */ - void* userdata /**< Arbitrary use suppliable data which is passed to the callback */); - -/** Free an AvahiSRecordBrowser object */ -void avahi_s_record_browser_free(AvahiSRecordBrowser *b); - -/** A host name to IP adddress resolver object */ -typedef struct AvahiSHostNameResolver AvahiSHostNameResolver; - -/** Callback prototype for AvahiSHostNameResolver events */ -typedef void (*AvahiSHostNameResolverCallback)( - AvahiSHostNameResolver *r, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiResolverEvent event, /**< Resolving event */ - const char *host_name, /**< Host name which should be resolved. May differ in case from the query */ - const AvahiAddress *a, /**< The address, or NULL if the host name couldn't be resolved. */ - void* userdata); - -/** Create an AvahiSHostNameResolver object for resolving a host name to an adddress. See AvahiSRecordBrowser for more info on the paramters. */ -AvahiSHostNameResolver *avahi_s_host_name_resolver_new( - AvahiServer *server, - AvahiIfIndex interface, - AvahiProtocol protocol, - const char *host_name, /**< The host name to look for */ - AvahiProtocol aprotocol, /**< The address family of the desired address or AVAHI_PROTO_UNSPEC if doesn't matter. */ - AvahiSHostNameResolverCallback calback, - void* userdata); - -/** Free a AvahiSHostNameResolver object */ -void avahi_s_host_name_resolver_free(AvahiSHostNameResolver *r); - -/** An IP address to host name resolver object ("reverse lookup") */ -typedef struct AvahiSAddressResolver AvahiSAddressResolver; - -/** Callback prototype for AvahiSAddressResolver events */ -typedef void (*AvahiSAddressResolverCallback)( - AvahiSAddressResolver *r, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiResolverEvent event, - const AvahiAddress *a, - const char *host_name, /**< A host name for the specified address, if one was found, i.e. event == AVAHI_RESOLVER_FOUND */ - void* userdata); - -/** Create an AvahiSAddressResolver object. See AvahiSRecordBrowser for more info on the paramters. */ -AvahiSAddressResolver *avahi_s_address_resolver_new( - AvahiServer *server, - AvahiIfIndex interface, - AvahiProtocol protocol, - const AvahiAddress *address, - AvahiSAddressResolverCallback calback, - void* userdata); - -/** Free an AvahiSAddressResolver object */ -void avahi_s_address_resolver_free(AvahiSAddressResolver *r); - -/** A local domain browsing object. May be used to enumerate domains used on the local LAN */ -typedef struct AvahiSDomainBrowser AvahiSDomainBrowser; - -/** Callback prototype for AvahiSDomainBrowser events */ -typedef void (*AvahiSDomainBrowserCallback)( - AvahiSDomainBrowser *b, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - const char *domain, - void* userdata); - -/** Create a new AvahiSDomainBrowser object */ -AvahiSDomainBrowser *avahi_s_domain_browser_new( - AvahiServer *server, - AvahiIfIndex interface, - AvahiProtocol protocol, - const char *domain, - AvahiDomainBrowserType type, - AvahiSDomainBrowserCallback callback, - void* userdata); - -/** Free an AvahiSDomainBrowser object */ -void avahi_s_domain_browser_free(AvahiSDomainBrowser *b); - -/** A DNS-SD service type browsing object. May be used to enumerate the service types of all available services on the local LAN */ -typedef struct AvahiSServiceTypeBrowser AvahiSServiceTypeBrowser; - -/** Callback prototype for AvahiSServiceTypeBrowser events */ -typedef void (*AvahiSServiceTypeBrowserCallback)( - AvahiSServiceTypeBrowser *b, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - const char *type, - const char *domain, - void* userdata); - -/** Create a new AvahiSServiceTypeBrowser object. */ -AvahiSServiceTypeBrowser *avahi_s_service_type_browser_new( - AvahiServer *server, - AvahiIfIndex interface, - AvahiProtocol protocol, - const char *domain, - AvahiSServiceTypeBrowserCallback callback, - void* userdata); - -/** Free an AvahiSServiceTypeBrowser object */ -void avahi_s_service_type_browser_free(AvahiSServiceTypeBrowser *b); - -/** A DNS-SD service browser. Use this to enumerate available services of a certain kind on the local LAN. Use AvahiSServiceResolver to get specific service data like address and port for a service. */ -typedef struct AvahiSServiceBrowser AvahiSServiceBrowser; - -/** Callback prototype for AvahiSServiceBrowser events */ -typedef void (*AvahiSServiceBrowserCallback)( - AvahiSServiceBrowser *b, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - const char *name /**< Service name, e.g. "Lennart's Files" */, - const char *type /**< DNS-SD type, e.g. "_http._tcp" */, - const char *domain /**< Domain of this service, e.g. "local" */, - void* userdata); - -/** Create a new AvahiSServiceBrowser object. */ -AvahiSServiceBrowser *avahi_s_service_browser_new( - AvahiServer *server, - AvahiIfIndex interface, - AvahiProtocol protocol, - const char *service_type /** DNS-SD service type, e.g. "_http._tcp" */, - const char *domain, - AvahiSServiceBrowserCallback callback, - void* userdata); - -/** Free an AvahiSServiceBrowser object */ -void avahi_s_service_browser_free(AvahiSServiceBrowser *b); - -/** A DNS-SD service resolver. Use this to retrieve addres, port and TXT data for a DNS-SD service */ -typedef struct AvahiSServiceResolver AvahiSServiceResolver; - -/** Callback prototype for AvahiSServiceResolver events */ -typedef void (*AvahiSServiceResolverCallback)( - AvahiSServiceResolver *r, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiResolverEvent event, /**< Is AVAHI_RESOLVER_FOUND when the service was resolved successfully, and everytime it changes. Is AVAHI_RESOLVER_TIMOUT when the service failed to resolve or disappeared. */ - const char *name, /**< Service name */ - const char *type, /**< Service Type */ - const char *domain, - const char *host_name, /**< Host name of the service */ - const AvahiAddress *a, /**< The resolved host name */ - uint16_t port, /**< Service name */ - AvahiStringList *txt, /**< TXT record data */ - void* userdata); - -/** Create a new AvahiSServiceResolver object. The specified callback function will be called with the resolved service data. */ -AvahiSServiceResolver *avahi_s_service_resolver_new( - AvahiServer *server, - AvahiIfIndex interface, - AvahiProtocol protocol, - const char *name, - const char *type, - const char *domain, - AvahiProtocol aprotocol, /**< Address family of the desired service address. Use AVAHI_PROTO_UNSPEC if you don't care */ - AvahiSServiceResolverCallback calback, - void* userdata); - -/** Free an AvahiSServiceResolver object */ -void avahi_s_service_resolver_free(AvahiSServiceResolver *r); - -/** A domain service browser object. Use this to browse for - * conventional unicast DNS servers which may be used to resolve - * conventional domain names */ -typedef struct AvahiSDNSServerBrowser AvahiSDNSServerBrowser; - -/** Callback prototype for AvahiSDNSServerBrowser events */ -typedef void (*AvahiSDNSServerBrowserCallback)( - AvahiSDNSServerBrowser *b, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - const char *host_name, /**< Host name of the DNS server, probably useless */ - const AvahiAddress *a, /**< Address of the DNS server */ - uint16_t port, /**< Port number of the DNS servers, probably 53 */ - void* userdata); - -/** Create a new AvahiSDNSServerBrowser object */ -AvahiSDNSServerBrowser *avahi_s_dns_server_browser_new( - AvahiServer *server, - AvahiIfIndex interface, - AvahiProtocol protocol, - const char *domain, - AvahiDNSServerType type, - AvahiProtocol aprotocol, /**< Address protocol for the DNS server */ - AvahiSDNSServerBrowserCallback callback, - void* userdata); - -/** Free an AvahiSDNSServerBrowser object */ -void avahi_s_dns_server_browser_free(AvahiSDNSServerBrowser *b); - /** Return the last error code */ int avahi_server_errno(AvahiServer *s); @@ -571,6 +165,9 @@ uint32_t avahi_server_get_local_service_cookie(AvahiServer *s); /** Return 1 if there is a local service with the specified credentials registeresd. Return 0 if not, negative on failure */ int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex, AvahiProtocol protocol, const char *name, const char *type, const char*domain); +/** Set the wide area DNS servers */ +int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n); + #ifndef DOXYGEN_SHOULD_SKIP_THIS AVAHI_C_DECL_END #endif diff --git a/avahi-core/dns.c b/avahi-core/dns.c index 9bc7aec..9412470 100644 --- a/avahi-core/dns.c +++ b/avahi-core/dns.c @@ -286,21 +286,37 @@ uint8_t *avahi_dns_packet_extend(AvahiDnsPacket *p, size_t l) { return d; } -int avahi_dns_packet_is_valid(AvahiDnsPacket *p) { +int avahi_dns_packet_check_valid(AvahiDnsPacket *p) { uint16_t flags; assert(p); - if (p->size < 12) + if (p->size < AVAHI_DNS_PACKET_HEADER_SIZE) return -1; flags = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS); + + if (flags & AVAHI_DNS_FLAG_OPCODE) + return -1; + + return 0; +} + +int avahi_dns_packet_check_valid_multicast(AvahiDnsPacket *p) { + uint16_t flags; + assert(p); - if (flags & AVAHI_DNS_FLAG_OPCODE || flags & AVAHI_DNS_FLAG_RCODE) + if (avahi_dns_packet_check_valid(p) < 0) + return -1; + + flags = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS); + + if (flags & AVAHI_DNS_FLAG_RCODE) return -1; return 0; } + int avahi_dns_packet_is_query(AvahiDnsPacket *p) { assert(p); @@ -478,7 +494,6 @@ AvahiRecord* avahi_dns_packet_consume_record(AvahiDnsPacket *p, int *ret_cache_f const void* start; assert(p); - assert(ret_cache_flush); /* avahi_log_debug("consume_record()"); */ @@ -492,7 +507,8 @@ AvahiRecord* avahi_dns_packet_consume_record(AvahiDnsPacket *p, int *ret_cache_f /* avahi_log_debug("name = %s, rdlength = %u", name, rdlength); */ - *ret_cache_flush = !!(class & AVAHI_DNS_CACHE_FLUSH); + if (ret_cache_flush) + *ret_cache_flush = !!(class & AVAHI_DNS_CACHE_FLUSH); class &= ~AVAHI_DNS_CACHE_FLUSH; start = avahi_dns_packet_get_rptr(p); @@ -608,16 +624,17 @@ AvahiKey* avahi_dns_packet_consume_key(AvahiDnsPacket *p, int *ret_unicast_respo uint16_t type, class; assert(p); - 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; + if (ret_unicast_response) + *ret_unicast_response = !!(class & AVAHI_DNS_UNICAST_RESPONSE); + class &= ~AVAHI_DNS_UNICAST_RESPONSE; + return avahi_key_new(name, class, type); } diff --git a/avahi-core/dns.h b/avahi-core/dns.h index 4988994..743ad4e 100644 --- a/avahi-core/dns.h +++ b/avahi-core/dns.h @@ -58,7 +58,8 @@ uint8_t* avahi_dns_packet_append_record(AvahiDnsPacket *p, AvahiRecord *r, int c uint8_t* avahi_dns_packet_append_string(AvahiDnsPacket *p, const char *s); int avahi_dns_packet_is_query(AvahiDnsPacket *p); -int avahi_dns_packet_is_valid(AvahiDnsPacket *p); +int avahi_dns_packet_check_valid(AvahiDnsPacket *p); +int avahi_dns_packet_check_valid_multicast(AvahiDnsPacket *p); int avahi_dns_packet_consume_uint16(AvahiDnsPacket *p, uint16_t *ret_v); int avahi_dns_packet_consume_uint32(AvahiDnsPacket *p, uint32_t *ret_v); @@ -97,8 +98,11 @@ size_t avahi_dns_packet_space(AvahiDnsPacket *p); ((uint16_t) !!ra << 7) | \ ((uint16_t) !!ad << 5) | \ ((uint16_t) !!cd << 4) | \ - ((uint16_t) (rd & 15))) - + ((uint16_t) (rcode & 15))) + +#define AVAHI_MDNS_SUFFIX_LOCAL "local" +#define AVAHI_MDNS_SUFFIX_ADDR_IPV4 "254.169.in-addr.arpa" +#define AVAHI_MDNS_SUFFIX_ADDR_IPV6 "0.8.e.f.ip6.arpa" #endif diff --git a/avahi-core/hashmap.c b/avahi-core/hashmap.c index 4d2fa3c..24a1249 100644 --- a/avahi-core/hashmap.c +++ b/avahi-core/hashmap.c @@ -57,7 +57,7 @@ struct AvahiHashmap { static Entry* entry_get(AvahiHashmap *m, const void *key) { unsigned idx; Entry *e; - + idx = m->hash_func(key) % HASH_MAP_SIZE; for (e = m->entries[idx]; e; e = e->bucket_next) @@ -91,7 +91,7 @@ AvahiHashmap* avahi_hashmap_new(AvahiHashFunc hash_func, AvahiEqualFunc equal_fu assert(hash_func); assert(equal_func); - if (!(m = avahi_new(AvahiHashmap, 1))) + if (!(m = avahi_new0(AvahiHashmap, 1))) return NULL; m->hash_func = hash_func; @@ -99,8 +99,6 @@ AvahiHashmap* avahi_hashmap_new(AvahiHashFunc hash_func, AvahiEqualFunc equal_fu m->key_free_func = key_free_func; m->value_free_func = value_free_func; - memset(m->entries, 0, sizeof(m->entries)); - AVAHI_LLIST_HEAD_INIT(Entry, m->entries_list); return m; @@ -231,6 +229,8 @@ unsigned avahi_string_hash(const void *data) { const char *p = data; unsigned hash = 0; + assert(p); + for (; *p; p++) hash = 31 * hash + *p; @@ -240,17 +240,25 @@ unsigned avahi_string_hash(const void *data) { int avahi_string_equal(const void *a, const void *b) { const char *p = a, *q = b; + assert(p); + assert(q); + return strcmp(p, q) == 0; } unsigned avahi_int_hash(const void *data) { const int *i = data; + assert(i); + return (unsigned) *i; } int avahi_int_equal(const void *a, const void *b) { const int *_a = a, *_b = b; + assert(_a); + assert(_b); + return *_a == *_b; } diff --git a/avahi-core/iface.c b/avahi-core/iface.c index 1692938..f882988 100644 --- a/avahi-core/iface.c +++ b/avahi-core/iface.c @@ -42,6 +42,8 @@ #include "announce.h" #include "util.h" #include "log.h" +#include "multicast-lookup.h" +#include "querier.h" static void update_address_rr(AvahiInterfaceMonitor *m, AvahiInterfaceAddress *a, int remove_rrs) { assert(m); @@ -180,11 +182,16 @@ static void free_interface(AvahiInterfaceMonitor *m, AvahiInterface *i, int send assert(m); assert(i); + /* Handle goodbyes and remove announcers */ avahi_goodbye_interface(m->server, i, send_goodbye); avahi_response_scheduler_force(i->response_scheduler); - assert(!i->announcements); + /* Remove queriers */ + avahi_querier_free_all(i); + avahi_hashmap_free(i->queriers_by_key); + + /* Remove local RRs */ update_interface_rr(m, i, 1); while (i->addresses) @@ -271,6 +278,9 @@ static void new_interface(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, AvahiP AVAHI_LLIST_HEAD_INIT(AvahiInterfaceAddress, i->addresses); AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, i->announcements); + AVAHI_LLIST_HEAD_INIT(AvahiQuerier, i->queriers); + i->queriers_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL); + i->cache = avahi_cache_new(m->server, i); i->response_scheduler = avahi_response_scheduler_new(i); i->query_scheduler = avahi_query_scheduler_new(i); @@ -316,7 +326,7 @@ static void check_interface_relevant(AvahiInterfaceMonitor *m, AvahiInterface *i i->announcing = 1; avahi_announce_interface(m->server, i); - avahi_browser_new_interface(m->server, i); + avahi_multicast_lookup_engine_new_interface(m->server->multicast_lookup_engine, i); } else if (!b && i->announcing) { avahi_log_info("Interface %s.%s no longer relevant.", i->hardware->name, avahi_proto_to_string(i->protocol)); @@ -326,6 +336,8 @@ static void check_interface_relevant(AvahiInterfaceMonitor *m, AvahiInterface *i avahi_mdns_mcast_leave_ipv6(m->server->fd_ipv6, i->hardware->index); avahi_goodbye_interface(m->server, i, 0); + avahi_querier_free_all(i); + avahi_response_scheduler_clear(i->response_scheduler); avahi_query_scheduler_clear(i->query_scheduler); avahi_probe_scheduler_clear(i->probe_scheduler); @@ -585,7 +597,6 @@ void avahi_interface_monitor_free(AvahiInterfaceMonitor *m) { free_hw_interface(m, m->hw_interfaces, 1); assert(!m->interfaces); - if (m->netlink) avahi_netlink_free(m->netlink); @@ -602,7 +613,7 @@ AvahiInterface* avahi_interface_monitor_get_interface(AvahiInterfaceMonitor *m, AvahiInterface *i; assert(m); - assert(idx > 0); + assert(idx >= 0); assert(protocol != AVAHI_PROTO_UNSPEC); if (!(hw = avahi_interface_monitor_get_hw_interface(m, idx))) @@ -757,7 +768,7 @@ int avahi_interface_address_relevant(AvahiInterfaceAddress *a) { int avahi_interface_match(AvahiInterface *i, AvahiIfIndex idx, AvahiProtocol protocol) { assert(i); - if (idx > 0 && idx != i->hardware->index) + if (idx != AVAHI_IF_UNSPEC && idx != i->hardware->index) return 0; if (protocol != AVAHI_PROTO_UNSPEC && protocol != i->protocol) @@ -770,7 +781,7 @@ void avahi_interface_monitor_walk(AvahiInterfaceMonitor *m, AvahiIfIndex interfa assert(m); assert(callback); - if (interface > 0) { + if (interface != AVAHI_IF_UNSPEC) { if (protocol != AVAHI_PROTO_UNSPEC) { AvahiInterface *i; diff --git a/avahi-core/iface.h b/avahi-core/iface.h index b78c76a..25eb324 100644 --- a/avahi-core/iface.h +++ b/avahi-core/iface.h @@ -38,6 +38,8 @@ typedef struct AvahiHwInterface AvahiHwInterface; #include "probe-sched.h" #include "dns.h" #include "announce.h" +#include "browse.h" +#include "querier.h" #define AVAHI_MAX_MAC_ADDRESS 32 @@ -85,12 +87,16 @@ struct AvahiInterface { int announcing; AvahiCache *cache; + AvahiQueryScheduler *query_scheduler; AvahiResponseScheduler * response_scheduler; AvahiProbeScheduler *probe_scheduler; AVAHI_LLIST_HEAD(AvahiInterfaceAddress, addresses); AVAHI_LLIST_HEAD(AvahiAnnouncement, announcements); + + AvahiHashmap *queriers_by_key; + AVAHI_LLIST_HEAD(AvahiQuerier, queriers); }; struct AvahiInterfaceAddress { diff --git a/avahi-core/lookup.h b/avahi-core/lookup.h new file mode 100644 index 0000000..518a4c9 --- /dev/null +++ b/avahi-core/lookup.h @@ -0,0 +1,284 @@ +#ifndef foolookuphfoo +#define foolookuphfoo + +/* $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. +***/ + +/** \file lookup.h Functions for browsing/resolving services and other RRs */ + +/** \example core-browse-services.c Example how to browse for DNS-SD + * services using an embedded mDNS stack. */ + +#include +#include + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +AVAHI_C_DECL_BEGIN +#endif + +/** A browsing object for arbitrary RRs */ +typedef struct AvahiSRecordBrowser AvahiSRecordBrowser; + +/** A host name to IP adddress resolver object */ +typedef struct AvahiSHostNameResolver AvahiSHostNameResolver; + +/** An IP address to host name resolver object ("reverse lookup") */ +typedef struct AvahiSAddressResolver AvahiSAddressResolver; + +/** A local domain browsing object. May be used to enumerate domains used on the local LAN */ +typedef struct AvahiSDomainBrowser AvahiSDomainBrowser; + +/** A DNS-SD service type browsing object. May be used to enumerate the service types of all available services on the local LAN */ +typedef struct AvahiSServiceTypeBrowser AvahiSServiceTypeBrowser; + +/** A DNS-SD service browser. Use this to enumerate available services of a certain kind on the local LAN. Use AvahiSServiceResolver to get specific service data like address and port for a service. */ +typedef struct AvahiSServiceBrowser AvahiSServiceBrowser; + +/** A DNS-SD service resolver. Use this to retrieve addres, port and TXT data for a DNS-SD service */ +typedef struct AvahiSServiceResolver AvahiSServiceResolver; + +/** A domain service browser object. Use this to browse for + * conventional unicast DNS servers which may be used to resolve + * conventional domain names */ +typedef struct AvahiSDNSServerBrowser AvahiSDNSServerBrowser; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +AVAHI_C_DECL_END +#endif + +#include "core.h" +#include "publish.h" + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +AVAHI_C_DECL_BEGIN +#endif + + +/** Callback prototype for AvahiSRecordBrowser events */ +typedef void (*AvahiSRecordBrowserCallback)( + AvahiSRecordBrowser *b, /**< The AvahiSRecordBrowser object that is emitting this callback */ + AvahiIfIndex interface, /**< Logical OS network interface number the record was found on */ + AvahiProtocol protocol, /**< Protocol number the record was found. */ + AvahiBrowserEvent event, /**< Browsing event, either AVAHI_BROWSER_NEW or AVAHI_BROWSER_REMOVE */ + AvahiRecord *record, /**< The record that was found */ + AvahiLookupResultFlags flags, /**< Lookup flags */ + void* userdata /**< Arbitrary user data passed to avahi_s_record_browser_new() */ ); + +/** Create a new browsing object for arbitrary RRs */ +AvahiSRecordBrowser *avahi_s_record_browser_new( + AvahiServer *server, /**< The server object to which attach this query */ + AvahiIfIndex interface, /**< Logical OS interface number where to look for the records, or AVAHI_IF_UNSPEC to look on interfaces */ + AvahiProtocol protocol, /**< Protocol number to use when looking for the record, or AVAHI_PROTO_UNSPEC to look on all protocols */ + AvahiKey *key, /**< The search key */ + AvahiLookupFlags flags, /**< Lookup flags. Must have set either AVAHI_LOOKUP_FORCE_WIDE_AREA or AVAHI_LOOKUP_FORCE_MULTICAST, since domain based detection is not available here. */ + AvahiSRecordBrowserCallback callback, /**< The callback to call on browsing events */ + void* userdata /**< Arbitrary use suppliable data which is passed to the callback */); + +/** Free an AvahiSRecordBrowser object */ +void avahi_s_record_browser_free(AvahiSRecordBrowser *b); + +/** Callback prototype for AvahiSHostNameResolver events */ +typedef void (*AvahiSHostNameResolverCallback)( + AvahiSHostNameResolver *r, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiResolverEvent event, /**< Resolving event */ + const char *host_name, /**< Host name which should be resolved. May differ in case from the query */ + const AvahiAddress *a, /**< The address, or NULL if the host name couldn't be resolved. */ + AvahiLookupResultFlags flags, /**< Lookup flags */ + void* userdata); + +/** Create an AvahiSHostNameResolver object for resolving a host name to an adddress. See AvahiSRecordBrowser for more info on the paramters. */ +AvahiSHostNameResolver *avahi_s_host_name_resolver_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *host_name, /**< The host name to look for */ + AvahiProtocol aprotocol, /**< The address family of the desired address or AVAHI_PROTO_UNSPEC if doesn't matter. */ + AvahiLookupFlags flags, /**< Lookup flags. */ + AvahiSHostNameResolverCallback calback, + void* userdata); + +/** Free a AvahiSHostNameResolver object */ +void avahi_s_host_name_resolver_free(AvahiSHostNameResolver *r); + +/** Callback prototype for AvahiSAddressResolver events */ +typedef void (*AvahiSAddressResolverCallback)( + AvahiSAddressResolver *r, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiResolverEvent event, + const AvahiAddress *a, + const char *host_name, /**< A host name for the specified address, if one was found, i.e. event == AVAHI_RESOLVER_FOUND */ + AvahiLookupResultFlags flags, /**< Lookup flags */ + void* userdata); + +/** Create an AvahiSAddressResolver object. See AvahiSRecordBrowser for more info on the paramters. */ +AvahiSAddressResolver *avahi_s_address_resolver_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const AvahiAddress *address, + AvahiLookupFlags flags, /**< Lookup flags. */ + AvahiSAddressResolverCallback calback, + void* userdata); + +/** Free an AvahiSAddressResolver object */ +void avahi_s_address_resolver_free(AvahiSAddressResolver *r); + +/** Callback prototype for AvahiSDomainBrowser events */ +typedef void (*AvahiSDomainBrowserCallback)( + AvahiSDomainBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *domain, + AvahiLookupResultFlags flags, /**< Lookup flags */ + void* userdata); + +/** Create a new AvahiSDomainBrowser object */ +AvahiSDomainBrowser *avahi_s_domain_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *domain, + AvahiDomainBrowserType type, + AvahiLookupFlags flags, /**< Lookup flags. */ + AvahiSDomainBrowserCallback callback, + void* userdata); + +/** Free an AvahiSDomainBrowser object */ +void avahi_s_domain_browser_free(AvahiSDomainBrowser *b); + +/** Callback prototype for AvahiSServiceTypeBrowser events */ +typedef void (*AvahiSServiceTypeBrowserCallback)( + AvahiSServiceTypeBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *type, + const char *domain, + AvahiLookupResultFlags flags, /**< Lookup flags */ + void* userdata); + +/** Create a new AvahiSServiceTypeBrowser object. */ +AvahiSServiceTypeBrowser *avahi_s_service_type_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *domain, + AvahiLookupFlags flags, /**< Lookup flags. */ + AvahiSServiceTypeBrowserCallback callback, + void* userdata); + +/** Free an AvahiSServiceTypeBrowser object */ +void avahi_s_service_type_browser_free(AvahiSServiceTypeBrowser *b); + +/** Callback prototype for AvahiSServiceBrowser events */ +typedef void (*AvahiSServiceBrowserCallback)( + AvahiSServiceBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name /**< Service name, e.g. "Lennart's Files" */, + const char *type /**< DNS-SD type, e.g. "_http._tcp" */, + const char *domain /**< Domain of this service, e.g. "local" */, + AvahiLookupResultFlags flags, /**< Lookup flags */ + void* userdata); + +/** Create a new AvahiSServiceBrowser object. */ +AvahiSServiceBrowser *avahi_s_service_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *service_type /** DNS-SD service type, e.g. "_http._tcp" */, + const char *domain, + AvahiLookupFlags flags, /**< Lookup flags. */ + AvahiSServiceBrowserCallback callback, + void* userdata); + +/** Free an AvahiSServiceBrowser object */ +void avahi_s_service_browser_free(AvahiSServiceBrowser *b); + +/** Callback prototype for AvahiSServiceResolver events */ +typedef void (*AvahiSServiceResolverCallback)( + AvahiSServiceResolver *r, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiResolverEvent event, /**< Is AVAHI_RESOLVER_FOUND when the service was resolved successfully, and everytime it changes. Is AVAHI_RESOLVER_TIMOUT when the service failed to resolve or disappeared. */ + const char *name, /**< Service name */ + const char *type, /**< Service Type */ + const char *domain, + const char *host_name, /**< Host name of the service */ + const AvahiAddress *a, /**< The resolved host name */ + uint16_t port, /**< Service name */ + AvahiStringList *txt, /**< TXT record data */ + AvahiLookupResultFlags flags, /**< Lookup flags */ + void* userdata); + +/** Create a new AvahiSServiceResolver object. The specified callback function will be called with the resolved service data. */ +AvahiSServiceResolver *avahi_s_service_resolver_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *name, + const char *type, + const char *domain, + AvahiProtocol aprotocol, /**< Address family of the desired service address. Use AVAHI_PROTO_UNSPEC if you don't care */ + AvahiLookupFlags flags, /**< Lookup flags. */ + AvahiSServiceResolverCallback calback, + void* userdata); + +/** Free an AvahiSServiceResolver object */ +void avahi_s_service_resolver_free(AvahiSServiceResolver *r); + +/** Callback prototype for AvahiSDNSServerBrowser events */ +typedef void (*AvahiSDNSServerBrowserCallback)( + AvahiSDNSServerBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *host_name, /**< Host name of the DNS server, probably useless */ + const AvahiAddress *a, /**< Address of the DNS server */ + uint16_t port, /**< Port number of the DNS servers, probably 53 */ + AvahiLookupResultFlags flags, /**< Lookup flags */ + void* userdata); + +/** Create a new AvahiSDNSServerBrowser object */ +AvahiSDNSServerBrowser *avahi_s_dns_server_browser_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *domain, + AvahiDNSServerType type, + AvahiProtocol aprotocol, /**< Address protocol for the DNS server */ + AvahiLookupFlags flags, /**< Lookup flags. */ + AvahiSDNSServerBrowserCallback callback, + void* userdata); + +/** Free an AvahiSDNSServerBrowser object */ +void avahi_s_dns_server_browser_free(AvahiSDNSServerBrowser *b); + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +AVAHI_C_DECL_END +#endif + +#endif diff --git a/avahi-core/multicast-lookup.c b/avahi-core/multicast-lookup.c new file mode 100644 index 0000000..4f04ab8 --- /dev/null +++ b/avahi-core/multicast-lookup.c @@ -0,0 +1,346 @@ +/* $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 "server.h" +#include "browse.h" +#include "socket.h" +#include "log.h" +#include "hashmap.h" +#include "multicast-lookup.h" + +struct AvahiMulticastLookup { + AvahiMulticastLookupEngine *engine; + int dead; + + AvahiKey *key, *cname_key; + + AvahiMulticastLookupCallback callback; + void *userdata; + + AvahiIfIndex interface; + AvahiProtocol protocol; + + int queriers_added; + + AvahiTimeEvent *all_for_now_event; + + AVAHI_LLIST_FIELDS(AvahiMulticastLookup, lookups); + AVAHI_LLIST_FIELDS(AvahiMulticastLookup, by_key); +}; + +struct AvahiMulticastLookupEngine { + AvahiServer *server; + + /* Lookups */ + AVAHI_LLIST_HEAD(AvahiMulticastLookup, lookups); + AvahiHashmap *lookups_by_key; + + int cleanup_dead; +}; + +static void all_for_now_callback(AvahiTimeEvent *e, void* userdata) { + AvahiMulticastLookup *l = userdata; + + assert(e); + assert(l); + + avahi_time_event_free(l->all_for_now_event); + l->all_for_now_event = NULL; + + l->callback(l->engine, l->interface, l->protocol, AVAHI_BROWSER_ALL_FOR_NOW, AVAHI_LOOKUP_CALLBACK_MULTICAST, NULL, l->userdata); +} + +AvahiMulticastLookup *avahi_multicast_lookup_new( + AvahiMulticastLookupEngine *e, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiKey *key, + AvahiMulticastLookupCallback callback, + void *userdata) { + + AvahiMulticastLookup *l, *t; + struct timeval ctime; + + assert(e); + assert(AVAHI_IF_VALID(interface)); + assert(AVAHI_PROTO_VALID(protocol)); + assert(key); + assert(callback); + + l = avahi_new(AvahiMulticastLookup, 1); + l->engine = e; + l->dead = 0; + l->key = avahi_key_ref(key); + l->cname_key = avahi_key_new_cname(l->key); + l->callback = callback; + l->userdata = userdata; + l->interface = interface; + l->protocol = protocol; + l->all_for_now_event = NULL; + l->queriers_added = 0; + + t = avahi_hashmap_lookup(e->lookups_by_key, l->key); + AVAHI_LLIST_PREPEND(AvahiMulticastLookup, by_key, t, l); + avahi_hashmap_replace(e->lookups_by_key, avahi_key_ref(l->key), t); + + AVAHI_LLIST_PREPEND(AvahiMulticastLookup, lookups, e->lookups, l); + + avahi_querier_add_for_all(e->server, interface, protocol, l->key, &ctime); + + /* add a second */ + avahi_timeval_add(&ctime, 1000000); + + l->all_for_now_event = avahi_time_event_new(e->server->time_event_queue, &ctime, all_for_now_callback, l); + + return l; +} + +static void lookup_stop(AvahiMulticastLookup *l) { + assert(l); + + l->callback = NULL; + + if (l->queriers_added) { + avahi_querier_remove_for_all(l->engine->server, l->interface, l->protocol, l->key); + l->queriers_added = 0; + } + + if (l->all_for_now_event) { + avahi_time_event_free(l->all_for_now_event); + l->all_for_now_event = NULL; + } +} + +static void lookup_destroy(AvahiMulticastLookup *l) { + AvahiMulticastLookup *t; + assert(l); + + lookup_stop(l); + + t = avahi_hashmap_lookup(l->engine->lookups_by_key, l->key); + AVAHI_LLIST_REMOVE(AvahiMulticastLookup, by_key, t, l); + if (t) + avahi_hashmap_replace(l->engine->lookups_by_key, avahi_key_ref(l->key), t); + else + avahi_hashmap_remove(l->engine->lookups_by_key, l->key); + + AVAHI_LLIST_REMOVE(AvahiMulticastLookup, lookups, l->engine->lookups, l); + + if (l->key) + avahi_key_unref(l->key); + + if (l->cname_key) + avahi_key_unref(l->cname_key); + + avahi_free(l); +} + +void avahi_multicast_lookup_free(AvahiMulticastLookup *l) { + assert(l); + + if (l->dead) + return; + + l->dead = 1; + l->engine->cleanup_dead = 1; + lookup_stop(l); +} + +void avahi_multicast_lookup_engine_cleanup(AvahiMulticastLookupEngine *e) { + AvahiMulticastLookup *l, *n; + assert(e); + + while (e->cleanup_dead) { + e->cleanup_dead = 0; + + for (l = e->lookups; l; l = n) { + n = l->lookups_next; + + if (l->dead) + lookup_destroy(l); + } + } +} + +struct cbdata { + AvahiMulticastLookupEngine *engine; + AvahiMulticastLookupCallback callback; + void *userdata; + AvahiKey *key, *cname_key; + AvahiInterface *interface; + unsigned n_found; +}; + +static void* scan_cache_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) { + struct cbdata *cbdata = userdata; + + assert(c); + assert(pattern); + assert(e); + assert(cbdata); + + cbdata->callback( + cbdata->engine, + cbdata->interface->hardware->index, + cbdata->interface->protocol, + AVAHI_BROWSER_NEW, + AVAHI_LOOKUP_CALLBACK_CACHED|AVAHI_LOOKUP_CALLBACK_MULTICAST, + e->record, + cbdata->userdata); + + cbdata->n_found ++; + + return NULL; +} + +static void scan_interface_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { + struct cbdata *cbdata = userdata; + + assert(m); + assert(i); + assert(cbdata); + + cbdata->interface = i; + + avahi_cache_walk(i->cache, cbdata->key, scan_cache_callback, cbdata); + + if (cbdata->cname_key) + avahi_cache_walk(i->cache, cbdata->cname_key, scan_cache_callback, cbdata); + + cbdata->interface = NULL; +} + +unsigned avahi_multicast_lookup_engine_scan_cache( + AvahiMulticastLookupEngine *e, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiKey *key, + AvahiMulticastLookupCallback callback, + void *userdata) { + + struct cbdata cbdata; + + assert(e); + assert(key); + assert(callback); + + assert(AVAHI_IF_VALID(interface)); + assert(AVAHI_PROTO_VALID(protocol)); + + cbdata.engine = e; + cbdata.key = key; + cbdata.cname_key = avahi_key_new_cname(key); + cbdata.callback = callback; + cbdata.userdata = userdata; + cbdata.interface = NULL; + cbdata.n_found = 0; + + avahi_interface_monitor_walk(e->server->monitor, interface, protocol, scan_interface_callback, &cbdata); + + if (cbdata.cname_key) + avahi_key_unref(cbdata.cname_key); + + return cbdata.n_found; +} + +void avahi_multicast_lookup_engine_new_interface(AvahiMulticastLookupEngine *e, AvahiInterface *i) { + AvahiMulticastLookup *l; + + assert(e); + assert(i); + + for (l = e->lookups; l; l = l->lookups_next) { + + if (l->dead || !l->callback) + continue; + + if (l->queriers_added && avahi_interface_match(i, l->interface, l->protocol)) + avahi_querier_add(i, l->key, NULL); + } +} + +void avahi_multicast_lookup_engine_notify(AvahiMulticastLookupEngine *e, AvahiInterface *i, AvahiRecord *record, AvahiBrowserEvent event) { + AvahiMulticastLookup *l; + + assert(e); + assert(record); + assert(i); + + for (l = avahi_hashmap_lookup(e->lookups_by_key, record->key); l; l = l->by_key_next) { + if (l->dead || !l->callback) + continue; + + if (avahi_interface_match(i, l->interface, l->protocol)) + l->callback(e, i->hardware->index, i->protocol, event, AVAHI_LOOKUP_CALLBACK_MULTICAST, record, l->userdata); + } + + + if (record->key->clazz == AVAHI_DNS_CLASS_IN && record->key->type == AVAHI_DNS_TYPE_CNAME) { + /* It's a CNAME record, so we have to scan the all lookups to see if one matches */ + + for (l = e->lookups; l; l = l->lookups_next) { + AvahiKey *key; + + if (l->dead || !l->callback) + continue; + + if ((key = avahi_key_new_cname(l->key))) { + if (avahi_key_equal(record->key, key)) + l->callback(e, i->hardware->index, i->protocol, event, AVAHI_LOOKUP_CALLBACK_MULTICAST, record, l->userdata); + + avahi_key_unref(key); + } + } + } +} + +AvahiMulticastLookupEngine *avahi_multicast_lookup_engine_new(AvahiServer *s) { + AvahiMulticastLookupEngine *e; + + assert(s); + + e = avahi_new(AvahiMulticastLookupEngine, 1); + e->server = s; + e->cleanup_dead = 0; + + /* Initialize lookup list */ + e->lookups_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, (AvahiFreeFunc) avahi_key_unref, NULL); + AVAHI_LLIST_HEAD_INIT(AvahiWideAreaLookup, e->lookups); + + return e; +} + +void avahi_multicast_lookup_engine_free(AvahiMulticastLookupEngine *e) { + assert(e); + + while (e->lookups) + lookup_destroy(e->lookups); + + avahi_hashmap_free(e->lookups_by_key); + avahi_free(e); +} + diff --git a/avahi-core/multicast-lookup.h b/avahi-core/multicast-lookup.h new file mode 100644 index 0000000..43e240d --- /dev/null +++ b/avahi-core/multicast-lookup.h @@ -0,0 +1,53 @@ +#ifndef foomulticastlookuphfoo +#define foomulticastlookuphfoo + +/* $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 "lookup.h" +#include "browse.h" + +typedef struct AvahiMulticastLookupEngine AvahiMulticastLookupEngine; +typedef struct AvahiMulticastLookup AvahiMulticastLookup; + +typedef void (*AvahiMulticastLookupCallback)( + AvahiMulticastLookupEngine *e, + AvahiIfIndex idx, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiLookupResultFlags flags, + AvahiRecord *r, + void *userdata); + +AvahiMulticastLookupEngine *avahi_multicast_lookup_engine_new(AvahiServer *s); +void avahi_multicast_lookup_engine_free(AvahiMulticastLookupEngine *e); + +unsigned avahi_multicast_lookup_engine_scan_cache(AvahiMulticastLookupEngine *e, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key, AvahiMulticastLookupCallback callback, void *userdata); +void avahi_multicast_lookup_engine_new_interface(AvahiMulticastLookupEngine *e, AvahiInterface *i); +void avahi_multicast_lookup_engine_cleanup(AvahiMulticastLookupEngine *e); +void avahi_multicast_lookup_engine_notify(AvahiMulticastLookupEngine *e, AvahiInterface *i, AvahiRecord *record, AvahiBrowserEvent event); + +AvahiMulticastLookup *avahi_multicast_lookup_new(AvahiMulticastLookupEngine *e, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key, AvahiMulticastLookupCallback callback, void *userdata); +void avahi_multicast_lookup_free(AvahiMulticastLookup *q); + + +#endif + diff --git a/avahi-core/publish.h b/avahi-core/publish.h new file mode 100644 index 0000000..4e632f8 --- /dev/null +++ b/avahi-core/publish.h @@ -0,0 +1,244 @@ +#ifndef foopublishhfoo +#define foopublishhfoo + +/* $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. +***/ + +/** \file publish.h Functions for publising local services and RRs */ + +/** \example core-publish-service.c Example how to register a DNS-SD + * service using an embedded mDNS stack. It behaves like a network + * printer registering both an IPP and a BSD LPR service. */ + + +#include + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +AVAHI_C_DECL_BEGIN +#endif + +/** A group of locally registered DNS RRs */ +typedef struct AvahiSEntryGroup AvahiSEntryGroup; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +AVAHI_C_DECL_END +#endif + +#include "core.h" + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +AVAHI_C_DECL_BEGIN +#endif + +/** Prototype for callback functions which are called whenever the state of an AvahiSEntryGroup object changes */ +typedef void (*AvahiSEntryGroupCallback) (AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void* userdata); + +/** Iterate through all local entries of the server. (when g is NULL) + * or of a specified entry group. At the first call state should point + * to a NULL initialized void pointer, That pointer is used to track + * the current iteration. It is not safe to call any other + * avahi_server_xxx() function during the iteration. If the last entry + * has been read, NULL is returned. */ +const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state); + +/** Create a new entry group. The specified callback function is + * called whenever the state of the group changes. Use entry group + * objects to keep track of you RRs. Add new RRs to a group using + * avahi_server_add_xxx(). Make sure to call avahi_s_entry_group_commit() + * to start the registration process for your RRs */ +AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata); + +/** Free an entry group. All RRs assigned to the group are removed from the server */ +void avahi_s_entry_group_free(AvahiSEntryGroup *g); + +/** Commit an entry group. This starts the probing and registration process for all RRs in the group */ +int avahi_s_entry_group_commit(AvahiSEntryGroup *g); + +/** Remove all entries from the entry group and reset the state to AVAHI_ENTRY_GROUP_UNCOMMITED. */ +void avahi_s_entry_group_reset(AvahiSEntryGroup *g); + +/** Return 1 if the entry group is empty, i.e. has no records attached. */ +int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g); + +/** Return the current state of the specified entry group */ +AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g); + +/** Change the opaque user data pointer attached to an entry group object */ +void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata); + +/** Return the opaque user data pointer currently set for the entry group object */ +void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g); + +/** Add a new resource record to the server. Returns 0 on success, negative otherwise. */ +int avahi_server_add( + AvahiServer *s, /**< The server object to add this record to */ + AvahiSEntryGroup *g, /**< An entry group object if this new record shall be attached to one, or NULL. If you plan to remove the record sometime later you a required to pass an entry group object here. */ + AvahiIfIndex interface, /**< A numeric index of a network interface to attach this record to, or AVAHI_IF_UNSPEC to attach this record to all interfaces */ + AvahiProtocol protocol, /**< A protocol family to attach this record to. One of the AVAHI_PROTO_xxx constants. Use AVAHI_PROTO_UNSPEC to make this record available on all protocols (wich means on both IPv4 and IPv6). */ + AvahiEntryFlags flags, /**< Special flags for this record */ + AvahiRecord *r /**< The record to add. This function increases the reference counter of this object. */ ); + +/** Add a PTR RR to the server. See avahi_server_add() for more information. */ +int avahi_server_add_ptr( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiEntryFlags flags, + uint32_t ttl, /**< DNS TTL for this record */ + const char *name, /**< PTR record name */ + const char *dest /**< pointer destination */ ); + +/** Add a PTR RR to the server. See avahi_server_add() for more information. */ +int avahi_server_add_txt( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiEntryFlags flags, + uint32_t ttl, /**< DNS TTL for this record */ + const char *name, /**< TXT record name */ + ... /**< Text record data, terminated by NULL */) AVAHI_GCC_SENTINEL; + +/** Add a PTR RR to the server. Mostly identical to + * avahi_server_add_text but takes a va_list instead of a variable + * number of arguments */ +int avahi_server_add_txt_va( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiEntryFlags flags, + uint32_t ttl, + const char *name, + va_list va); + +/** Add a PTR RR to the server. Mostly identical to + * avahi_server_add_text but takes an AvahiStringList record instead of a variable + * number of arguments. */ +int avahi_server_add_txt_strlst( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiEntryFlags flags, + uint32_t ttl, + const char *name, + AvahiStringList *strlst /**< TXT decord data as a AvahiString. This routine makes a deep copy of this object. */ ); + +/** Add an IP address mapping to the server. This will add both the + * host-name-to-address and the reverse mapping to the server. See + * avahi_server_add() for more information. If adding one of the RRs + * fails, the function returns with an error, but it is not defined if + * the other RR is deleted from the server or not. Therefore, you have + * to free the AvahiSEntryGroup and create a new one before + * proceeding. */ +int avahi_server_add_address( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiEntryFlags flags, + const char *name, + AvahiAddress *a); + +/** Add an DNS-SD service to the Server. This will add all required + * RRs to the server. See avahi_server_add() for more information. If + * adding one of the RRs fails, the function returns with an error, + * but it is not defined if the other RR is deleted from the server or + * not. Therefore, you have to free the AvahiSEntryGroup and create a + * new one before proceeding. */ +int avahi_server_add_service( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *name, /**< Service name, e.g. "Lennart's Files" */ + const char *type, /**< DNS-SD type, e.g. "_http._tcp" */ + const char *domain, + const char *host, /**< Host name where this servcie resides, or NULL if on the local host */ + uint16_t port, /**< Port number of the service */ + ... /**< Text records, terminated by NULL */) AVAHI_GCC_SENTINEL; + +/** Mostly identical to avahi_server_add_service(), but takes an va_list for the TXT records. */ +int avahi_server_add_service_va( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *name, + const char *type, + const char *domain, + const char *host, + uint16_t port, + va_list va); + +/** Mostly identical to avahi_server_add_service(), but takes an AvahiStringList object for the TXT records. The AvahiStringList object is copied. */ +int avahi_server_add_service_strlst( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *name, + const char *type, + const char *domain, + const char *host, + uint16_t port, + AvahiStringList *strlst); + +/** The type of DNS server */ +typedef enum { + AVAHI_DNS_SERVER_RESOLVE, /**< Unicast DNS servers for normal resolves (_domain._udp)*/ + AVAHI_DNS_SERVER_UPDATE /**< Unicast DNS servers for updates (_dns-update._udp)*/ +} AvahiDNSServerType; + +/** Publish the specified unicast DNS server address via mDNS. You may + * browse for records create this way wit + * avahi_s_dns_server_browser_new(). */ +int avahi_server_add_dns_server_address( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *domain, + AvahiDNSServerType type, + const AvahiAddress *address, + uint16_t port /** should be 53 */); + +/** Similar to avahi_server_add_dns_server_address(), but specify a +host name instead of an address. The specified host name should be +resolvable via mDNS */ +int avahi_server_add_dns_server_name( + AvahiServer *s, + AvahiSEntryGroup *g, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *domain, + AvahiDNSServerType type, + const char *name, + uint16_t port /** should be 53 */); + + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +AVAHI_C_DECL_END +#endif + +#endif diff --git a/avahi-core/querier-test.c b/avahi-core/querier-test.c new file mode 100644 index 0000000..f410e66 --- /dev/null +++ b/avahi-core/querier-test.c @@ -0,0 +1,123 @@ +/* $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 + +#define DOMAIN NULL +#define SERVICE_TYPE "_http._tcp" + +static AvahiSServiceBrowser *service_browser1 = NULL, *service_browser2 = NULL; +static const AvahiPoll * poll_api = NULL; +static AvahiServer *server = NULL; +static AvahiSimplePoll *simple_poll; + +static const char *browser_event_to_string(AvahiBrowserEvent event) { + switch (event) { + case AVAHI_BROWSER_NEW : return "NEW"; + case AVAHI_BROWSER_REMOVE : return "REMOVE"; + case AVAHI_BROWSER_CACHE_EXHAUSTED : return "CACHE_EXHAUSTED"; + case AVAHI_BROWSER_ALL_FOR_NOW : return "ALL_FOR_NOW"; + case AVAHI_BROWSER_FAILURE : return "FAILURE"; + case AVAHI_BROWSER_NOT_FOUND : return "NOT_FOUND"; + } + + abort(); +} + +static void sb_callback( + AvahiSServiceBrowser *b, + AvahiIfIndex iface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, + const char *service_type, + const char *domain, + AvahiLookupResultFlags flags, + void* userdata) { + avahi_log_debug("SB%i: (%i.%s) <%s> as <%s> in <%s> [%s] cached=%i", b == service_browser1 ? 1 : 2, iface, avahi_proto_to_string(protocol), name, service_type, domain, browser_event_to_string(event), !!(flags & AVAHI_LOOKUP_CALLBACK_CACHED)); +} + +static void create_second_service_browser(AvahiTimeout *timeout, void* userdata) { + + service_browser2 = avahi_s_service_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, SERVICE_TYPE, DOMAIN, 0, sb_callback, NULL); + assert(service_browser2); + + poll_api->timeout_free(timeout); +} + +static void quit(AvahiTimeout *timeout, void *userdata) { + avahi_simple_poll_quit(simple_poll); +} + +int main(int argc, char *argv[]) { + struct timeval tv; + AvahiServerConfig config; + + simple_poll = avahi_simple_poll_new(); + assert(simple_poll); + + poll_api = avahi_simple_poll_get(simple_poll); + assert(poll_api); + + avahi_server_config_init(&config); + config.publish_hinfo = 0; + config.publish_addresses = 0; + config.publish_workstation = 0; + config.publish_domain = 0; + + avahi_address_parse("192.168.50.1", AVAHI_PROTO_UNSPEC, &config.wide_area_servers[0]); + config.n_wide_area_servers = 1; + config.enable_wide_area = 1; + + server = avahi_server_new(poll_api, &config, NULL, NULL, NULL); + assert(server); + avahi_server_config_free(&config); + + service_browser1 = avahi_s_service_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, SERVICE_TYPE, DOMAIN, 0, sb_callback, NULL); + assert(service_browser1); + + poll_api->timeout_new(poll_api, avahi_elapse_time(&tv, 10000, 0), create_second_service_browser, NULL); + + poll_api->timeout_new(poll_api, avahi_elapse_time(&tv, 60000, 0), quit, NULL); + + + for (;;) + if (avahi_simple_poll_iterate(simple_poll, -1) != 0) + break; + + avahi_server_free(server); + avahi_simple_poll_free(simple_poll); + + return 0; +} diff --git a/avahi-core/querier.c b/avahi-core/querier.c new file mode 100644 index 0000000..51b2ce6 --- /dev/null +++ b/avahi-core/querier.c @@ -0,0 +1,200 @@ +/* $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 "querier.h" +#include "log.h" + +struct AvahiQuerier { + AvahiInterface *interface; + + AvahiKey *key; + int n_used; + + unsigned sec_delay; + + AvahiTimeEvent *time_event; + + struct timeval creation_time; + + AVAHI_LLIST_FIELDS(AvahiQuerier, queriers); +}; + +void avahi_querier_free(AvahiQuerier *q) { + assert(q); + + AVAHI_LLIST_REMOVE(AvahiQuerier, queriers, q->interface->queriers, q); + avahi_hashmap_remove(q->interface->queriers_by_key, q->key); + + avahi_key_unref(q->key); + avahi_time_event_free(q->time_event); + + avahi_free(q); +} + +static void querier_elapse_callback(AvahiTimeEvent *e, void *userdata) { + AvahiQuerier *q = userdata; + struct timeval tv; + + assert(q); + + avahi_interface_post_query(q->interface, q->key, 0); + + q->sec_delay *= 2; + + if (q->sec_delay >= 60*60) /* 1h */ + q->sec_delay = 60*60; + + avahi_elapse_time(&tv, q->sec_delay*1000, 0); + avahi_time_event_update(q->time_event, &tv); +} + +void avahi_querier_add(AvahiInterface *i, AvahiKey *key, struct timeval *ret_ctime) { + AvahiQuerier *q; + struct timeval tv; + + assert(i); + assert(key); + + if ((q = avahi_hashmap_lookup(i->queriers_by_key, key))) { + /* Someone is already browsing for records of this RR key */ + q->n_used++; + + /* Return the creation time */ + if (ret_ctime) + *ret_ctime = q->creation_time; + return; + } + + /* No one is browsing for this RR key, so we add a new querier */ + if (!(q = avahi_new(AvahiQuerier, 1))) + return; /* OOM */ + + q->key = avahi_key_ref(key); + q->interface = i; + q->n_used = 1; + q->sec_delay = 1; + gettimeofday(&q->creation_time, NULL); + + /* Do the initial query */ + avahi_interface_post_query(i, key, 0); + + /* Schedule next queries */ + q->time_event = avahi_time_event_new(i->monitor->server->time_event_queue, avahi_elapse_time(&tv, q->sec_delay*1000, 0), querier_elapse_callback, q); + + AVAHI_LLIST_PREPEND(AvahiQuerier, queriers, i->queriers, q); + avahi_hashmap_insert(i->queriers_by_key, q->key, q); + + /* Return the creation time */ + if (ret_ctime) + *ret_ctime = q->creation_time; +} + +void avahi_querier_remove(AvahiInterface *i, AvahiKey *key) { + AvahiQuerier *q; + + if (!(q = avahi_hashmap_lookup(i->queriers_by_key, key))) { + /* The was no querier for this RR key */ + avahi_log_warn(__FILE__": querier_remove() called but no querier to remove"); + return; + } + + assert(q->n_used >= 1); + if ((--q->n_used) <= 0) + avahi_querier_free(q); +} + +static void remove_querier_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { + assert(m); + assert(i); + assert(userdata); + + if (i->announcing) + avahi_querier_remove(i, (AvahiKey*) userdata); +} + +void avahi_querier_remove_for_all(AvahiServer *s, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key) { + assert(s); + assert(key); + + avahi_interface_monitor_walk(s->monitor, idx, protocol, remove_querier_callback, key); +} + +struct cbdata { + AvahiKey *key; + struct timeval *ret_ctime; +}; + +static void add_querier_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { + struct cbdata *cbdata = userdata; + + assert(m); + assert(i); + assert(cbdata); + + if (i->announcing) { + struct timeval tv; + avahi_querier_add(i, cbdata->key, &tv); + + if (cbdata->ret_ctime && avahi_timeval_compare(&tv, cbdata->ret_ctime) > 0) + *cbdata->ret_ctime = tv; + } +} + +void avahi_querier_add_for_all(AvahiServer *s, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key, struct timeval *ret_ctime) { + struct cbdata cbdata; + + assert(s); + assert(key); + + cbdata.key = key; + cbdata.ret_ctime = ret_ctime; + + if (ret_ctime) + ret_ctime->tv_sec = ret_ctime->tv_usec = 0; + + avahi_interface_monitor_walk(s->monitor, idx, protocol, add_querier_callback, &cbdata); +} + +int avahi_querier_exists(AvahiInterface *i, AvahiKey *key) { + assert(i); + assert(key); + + if (avahi_hashmap_lookup(i->queriers_by_key, key)) + return 1; + + return 0; +} + +void avahi_querier_free_all(AvahiInterface *i) { + assert(i); + + while (i->queriers) + avahi_querier_free(i->queriers); +} diff --git a/avahi-core/querier.h b/avahi-core/querier.h new file mode 100644 index 0000000..3ec4564 --- /dev/null +++ b/avahi-core/querier.h @@ -0,0 +1,50 @@ +#ifndef fooquerierhfoo +#define fooquerierhfoo + +/* $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. +***/ + +typedef struct AvahiQuerier AvahiQuerier; + +#include "iface.h" + +/** Add querier for the specified key to the specified interface */ +void avahi_querier_add(AvahiInterface *i, AvahiKey *key, struct timeval *ret_ctime); + +/** Remove a querier for the specified key from the specified interface */ +void avahi_querier_remove(AvahiInterface *i, AvahiKey *key); + +/** Add a querier for the specified key on all interfaces that mach */ +void avahi_querier_add_for_all(AvahiServer *s, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key, struct timeval *ret_ctime); + +/** Remove a querier for the specified key on all interfaces that mach */ +void avahi_querier_remove_for_all(AvahiServer *s, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key); + +/** Free all queriers */ +void avahi_querier_free(AvahiQuerier *q); + +/** Free all queriers on the specified interface */ +void avahi_querier_free_all(AvahiInterface *i); + +/** Return 1 if there is a querier for the specified key on the specified interface */ +int avahi_querier_exists(AvahiInterface *i, AvahiKey *key); + +#endif diff --git a/avahi-core/resolve-address.c b/avahi-core/resolve-address.c index d5fe743..4fb588e 100644 --- a/avahi-core/resolve-address.c +++ b/avahi-core/resolve-address.c @@ -29,7 +29,7 @@ #include "browse.h" -#define TIMEOUT_MSEC 1000 +#define TIMEOUT_MSEC 5000 struct AvahiSAddressResolver { AvahiServer *server; @@ -43,6 +43,10 @@ struct AvahiSAddressResolver { AvahiRecord *ptr_record; AvahiIfIndex interface; AvahiProtocol protocol; + AvahiLookupResultFlags flags; + + int retry_with_multicast; + AvahiKey *key; AvahiTimeEvent *time_event; @@ -57,14 +61,17 @@ static void finish(AvahiSAddressResolver *r, AvahiResolverEvent event) { r->time_event = NULL; } - if (event == AVAHI_RESOLVER_TIMEOUT) - r->callback(r, r->interface, r->protocol, AVAHI_RESOLVER_TIMEOUT, &r->address, NULL, r->userdata); - else { - - assert(event == AVAHI_RESOLVER_FOUND); - assert(r->ptr_record); - - r->callback(r, r->interface, r->protocol, AVAHI_RESOLVER_FOUND, &r->address, r->ptr_record->data.ptr.name, r->userdata); + switch (event) { + case AVAHI_RESOLVER_NOT_FOUND: + case AVAHI_RESOLVER_TIMEOUT: + case AVAHI_RESOLVER_FAILURE: + r->callback(r, r->interface, r->protocol, event, &r->address, NULL, r->flags, r->userdata); + break; + + case AVAHI_RESOLVER_FOUND: + assert(r->ptr_record); + r->callback(r, r->interface, r->protocol, event, &r->address, r->ptr_record->data.ptr.name, r->flags, r->userdata); + break; } } @@ -88,51 +95,98 @@ static void start_timeout(AvahiSAddressResolver *r) { r->time_event = avahi_time_event_new(r->server->time_event_queue, &tv, time_event_callback, r); } -static void record_browser_callback(AvahiSRecordBrowser*rr, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, AvahiRecord *record, void* userdata) { +static void record_browser_callback( + AvahiSRecordBrowser*rr, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiRecord *record, + AvahiLookupResultFlags flags, + void* userdata) { + AvahiSAddressResolver *r = userdata; assert(rr); - assert(record); assert(r); - assert(record->key->type == AVAHI_DNS_TYPE_PTR); - - if (event == AVAHI_BROWSER_NEW) { - - if (r->interface > 0 && interface != r->interface) - return; - - if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol) - return; - - if (r->interface <= 0) - r->interface = interface; - - if (r->protocol == AVAHI_PROTO_UNSPEC) - r->protocol = protocol; - - if (!r->ptr_record) { - r->ptr_record = avahi_record_ref(record); - - finish(r, AVAHI_RESOLVER_FOUND); - } - } else { - - assert(event == AVAHI_BROWSER_REMOVE); - - if (r->ptr_record && avahi_record_equal_no_ttl(record, r->ptr_record)) { - avahi_record_unref(r->ptr_record); - r->ptr_record = NULL; - - /** Look for a replacement */ - avahi_s_record_browser_restart(r->record_browser); - start_timeout(r); - } + switch (event) { + case AVAHI_BROWSER_NEW: + assert(record); + assert(record->key->type == AVAHI_DNS_TYPE_PTR); + + if (r->interface > 0 && interface != r->interface) + return; + + if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol) + return; + + if (r->interface <= 0) + r->interface = interface; + + if (r->protocol == AVAHI_PROTO_UNSPEC) + r->protocol = protocol; + + if (!r->ptr_record) { + r->ptr_record = avahi_record_ref(record); + r->flags = flags; + + finish(r, AVAHI_RESOLVER_FOUND); + } + break; + + case AVAHI_BROWSER_REMOVE: + assert(record); + assert(record->key->type == AVAHI_DNS_TYPE_PTR); + + if (r->ptr_record && avahi_record_equal_no_ttl(record, r->ptr_record)) { + avahi_record_unref(r->ptr_record); + r->ptr_record = NULL; + r->flags = flags; + + /** Look for a replacement */ + avahi_s_record_browser_restart(r->record_browser); + start_timeout(r); + } + + break; + + case AVAHI_BROWSER_CACHE_EXHAUSTED: + case AVAHI_BROWSER_ALL_FOR_NOW: + break; + + case AVAHI_BROWSER_NOT_FOUND: + + if (r->retry_with_multicast) { + r->retry_with_multicast = 0; + + avahi_s_record_browser_free(r->record_browser); + r->record_browser = avahi_s_record_browser_new(r->server, r->interface, r->protocol, r->key, AVAHI_LOOKUP_USE_MULTICAST, record_browser_callback, r); + + if (r->record_browser) { + start_timeout(r); + break; + } + } + + /* Fallthrough */ + + case AVAHI_BROWSER_FAILURE: + r->flags = flags; + finish(r, event == AVAHI_BROWSER_NOT_FOUND ? AVAHI_RESOLVER_NOT_FOUND : AVAHI_RESOLVER_FAILURE); + } } -AvahiSAddressResolver *avahi_s_address_resolver_new(AvahiServer *server, AvahiIfIndex interface, AvahiProtocol protocol, const AvahiAddress *address, AvahiSAddressResolverCallback callback, void* userdata) { +AvahiSAddressResolver *avahi_s_address_resolver_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const AvahiAddress *address, + AvahiLookupFlags flags, + AvahiSAddressResolverCallback callback, + void* userdata) { + AvahiSAddressResolver *r; AvahiKey *k; char *n; @@ -143,6 +197,16 @@ AvahiSAddressResolver *avahi_s_address_resolver_new(AvahiServer *server, AvahiIf assert(address->proto == AVAHI_PROTO_INET || address->proto == AVAHI_PROTO_INET6); + if (!AVAHI_IF_VALID(interface)) { + avahi_server_set_errno(server, AVAHI_ERR_INVALID_INTERFACE); + return NULL; + } + + if (!AVAHI_VALID_FLAGS(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST)) { + avahi_server_set_errno(server, AVAHI_ERR_INVALID_FLAGS); + return NULL; + } + if (address->proto == AVAHI_PROTO_INET) n = avahi_reverse_lookup_name_ipv4(&address->data.ipv4); else @@ -174,20 +238,33 @@ AvahiSAddressResolver *avahi_s_address_resolver_new(AvahiServer *server, AvahiIf r->ptr_record = NULL; r->interface = interface; r->protocol = protocol; + r->flags = 0; + r->retry_with_multicast = 0; + r->key = k; r->record_browser = NULL; AVAHI_LLIST_PREPEND(AvahiSAddressResolver, resolver, server->address_resolvers, r); r->time_event = NULL; - start_timeout(r); + + if (!(flags & (AVAHI_LOOKUP_USE_MULTICAST|AVAHI_LOOKUP_USE_WIDE_AREA))) { + + if (!server->wide_area_lookup_engine || !avahi_wide_area_has_servers(server->wide_area_lookup_engine)) + flags |= AVAHI_LOOKUP_USE_MULTICAST; + else { + flags |= AVAHI_LOOKUP_USE_WIDE_AREA; + r->retry_with_multicast = 1; + } + } - r->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, record_browser_callback, r); - avahi_key_unref(k); + r->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, r); if (!r->record_browser) { avahi_s_address_resolver_free(r); return NULL; } + + start_timeout(r); return r; } @@ -205,6 +282,9 @@ void avahi_s_address_resolver_free(AvahiSAddressResolver *r) { if (r->ptr_record) avahi_record_unref(r->ptr_record); + + if (r->key) + avahi_key_unref(r->key); avahi_free(r); } diff --git a/avahi-core/resolve-host-name.c b/avahi-core/resolve-host-name.c index 7bb5727..622ece1 100644 --- a/avahi-core/resolve-host-name.c +++ b/avahi-core/resolve-host-name.c @@ -33,7 +33,7 @@ #include "browse.h" #include "log.h" -#define TIMEOUT_MSEC 1000 +#define TIMEOUT_MSEC 5000 struct AvahiSHostNameResolver { AvahiServer *server; @@ -48,6 +48,7 @@ struct AvahiSHostNameResolver { AvahiRecord *address_record; AvahiIfIndex interface; AvahiProtocol protocol; + AvahiLookupResultFlags flags; AvahiTimeEvent *time_event; @@ -62,30 +63,38 @@ static void finish(AvahiSHostNameResolver *r, AvahiResolverEvent event) { r->time_event = NULL; } - if (event == AVAHI_RESOLVER_TIMEOUT) - r->callback(r, r->interface, r->protocol, AVAHI_RESOLVER_TIMEOUT, r->host_name, NULL, r->userdata); - else { - AvahiAddress a; - - assert(event == AVAHI_RESOLVER_FOUND); - assert(r->address_record); - - switch (r->address_record->key->type) { - case AVAHI_DNS_TYPE_A: - a.proto = AVAHI_PROTO_INET; - a.data.ipv4 = r->address_record->data.a.address; - break; - - case AVAHI_DNS_TYPE_AAAA: - a.proto = AVAHI_PROTO_INET6; - a.data.ipv6 = r->address_record->data.aaaa.address; - break; - - default: - abort(); - } + switch (event) { + case AVAHI_RESOLVER_FOUND: { + AvahiAddress a; + + assert(r->address_record); + + switch (r->address_record->key->type) { + case AVAHI_DNS_TYPE_A: + a.proto = AVAHI_PROTO_INET; + a.data.ipv4 = r->address_record->data.a.address; + break; + + case AVAHI_DNS_TYPE_AAAA: + a.proto = AVAHI_PROTO_INET6; + a.data.ipv6 = r->address_record->data.aaaa.address; + break; + + default: + abort(); + } + + r->callback(r, r->interface, r->protocol, AVAHI_RESOLVER_FOUND, r->address_record->key->name, &a, r->flags, r->userdata); + break; - r->callback(r, r->interface, r->protocol, AVAHI_RESOLVER_FOUND, r->address_record->key->name, &a, r->userdata); + } + + case AVAHI_RESOLVER_TIMEOUT: + case AVAHI_RESOLVER_NOT_FOUND: + case AVAHI_RESOLVER_FAILURE: + + r->callback(r, r->interface, r->protocol, event, r->host_name, NULL, r->flags, r->userdata); + break; } } @@ -106,71 +115,130 @@ static void start_timeout(AvahiSHostNameResolver *r) { return; avahi_elapse_time(&tv, TIMEOUT_MSEC, 0); + r->time_event = avahi_time_event_new(r->server->time_event_queue, &tv, time_event_callback, r); } -static void record_browser_callback(AvahiSRecordBrowser*rr, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, AvahiRecord *record, void* userdata) { +static void record_browser_callback( + AvahiSRecordBrowser*rr, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiRecord *record, + AvahiLookupResultFlags flags, + void* userdata) { + AvahiSHostNameResolver *r = userdata; - + assert(rr); - assert(record); assert(r); - assert(record->key->type == AVAHI_DNS_TYPE_A || record->key->type == AVAHI_DNS_TYPE_AAAA); - - if (event == AVAHI_BROWSER_NEW) { - - if (r->interface > 0 && interface != r->interface) - return; - - if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol) - return; - - if (r->interface <= 0) - r->interface = interface; - - if (r->protocol == AVAHI_PROTO_UNSPEC) - r->protocol = protocol; - - if (!r->address_record) { - r->address_record = avahi_record_ref(record); + + switch (event) { + case AVAHI_BROWSER_NEW: + assert(record); + assert(record->key->type == AVAHI_DNS_TYPE_A || record->key->type == AVAHI_DNS_TYPE_AAAA); + + if (r->interface > 0 && interface != r->interface) + return; - finish(r, AVAHI_RESOLVER_FOUND); - } - } else { + if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol) + return; + + if (r->interface <= 0) + r->interface = interface; + + if (r->protocol == AVAHI_PROTO_UNSPEC) + r->protocol = protocol; + + if (!r->address_record) { + r->address_record = avahi_record_ref(record); + r->flags = flags; + + finish(r, AVAHI_RESOLVER_FOUND); + } - assert(event == AVAHI_BROWSER_REMOVE); + break; - if (r->address_record && avahi_record_equal_no_ttl(record, r->address_record)) { - avahi_record_unref(r->address_record); - r->address_record = NULL; + case AVAHI_BROWSER_REMOVE: + assert(record); + assert(record->key->type == AVAHI_DNS_TYPE_A || record->key->type == AVAHI_DNS_TYPE_AAAA); - /** Look for a replacement */ + if (r->address_record && avahi_record_equal_no_ttl(record, r->address_record)) { + avahi_record_unref(r->address_record); + r->address_record = NULL; + + r->flags = flags; + + + /** Look for a replacement */ + if (r->record_browser_aaaa) + avahi_s_record_browser_restart(r->record_browser_aaaa); + if (r->record_browser_a) + avahi_s_record_browser_restart(r->record_browser_a); + + start_timeout(r); + } + + break; + + case AVAHI_BROWSER_CACHE_EXHAUSTED: + case AVAHI_BROWSER_ALL_FOR_NOW: + /* Ignore */ + break; + + case AVAHI_BROWSER_FAILURE: + case AVAHI_BROWSER_NOT_FOUND: + + /* Stop browsers */ + if (r->record_browser_aaaa) - avahi_s_record_browser_restart(r->record_browser_aaaa); + avahi_s_record_browser_free(r->record_browser_aaaa); if (r->record_browser_a) - avahi_s_record_browser_restart(r->record_browser_a); + avahi_s_record_browser_free(r->record_browser_a); - start_timeout(r); - } + r->record_browser_a = r->record_browser_aaaa = NULL; + r->flags = flags; + + finish(r, event == AVAHI_BROWSER_FAILURE ? AVAHI_RESOLVER_FAILURE : AVAHI_RESOLVER_NOT_FOUND); + break; } } -AvahiSHostNameResolver *avahi_s_host_name_resolver_new(AvahiServer *server, AvahiIfIndex interface, AvahiProtocol protocol, const char *host_name, AvahiProtocol aprotocol, AvahiSHostNameResolverCallback callback, void* userdata) { +AvahiSHostNameResolver *avahi_s_host_name_resolver_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *host_name, + AvahiProtocol aprotocol, + AvahiLookupFlags flags, + AvahiSHostNameResolverCallback callback, + void* userdata) { + AvahiSHostNameResolver *r; AvahiKey *k; - + assert(server); assert(host_name); assert(callback); assert(aprotocol == AVAHI_PROTO_UNSPEC || aprotocol == AVAHI_PROTO_INET || aprotocol == AVAHI_PROTO_INET6); + if (!AVAHI_IF_VALID(interface)) { + avahi_server_set_errno(server, AVAHI_ERR_INVALID_INTERFACE); + return NULL; + } + if (!avahi_is_valid_domain_name(host_name)) { avahi_server_set_errno(server, AVAHI_ERR_INVALID_HOST_NAME); return NULL; } - + + if (!AVAHI_VALID_FLAGS(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST)) { + avahi_server_set_errno(server, AVAHI_ERR_INVALID_FLAGS); + return NULL; + } + if (!(r = avahi_new(AvahiSHostNameResolver, 1))) { avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); return NULL; @@ -183,19 +251,20 @@ AvahiSHostNameResolver *avahi_s_host_name_resolver_new(AvahiServer *server, Avah r->address_record = NULL; r->interface = interface; r->protocol = protocol; + r->flags = 0; r->record_browser_a = r->record_browser_aaaa = NULL; r->time_event = NULL; - start_timeout(r); AVAHI_LLIST_PREPEND(AvahiSHostNameResolver, resolver, server->host_name_resolvers, r); r->record_browser_aaaa = r->record_browser_a = NULL; - + + if (aprotocol == AVAHI_PROTO_INET || aprotocol == AVAHI_PROTO_UNSPEC) { k = avahi_key_new(host_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A); - r->record_browser_a = avahi_s_record_browser_new(server, interface, protocol, k, record_browser_callback, r); + r->record_browser_a = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, r); avahi_key_unref(k); if (!r->record_browser_a) @@ -204,7 +273,7 @@ AvahiSHostNameResolver *avahi_s_host_name_resolver_new(AvahiServer *server, Avah if (aprotocol == AVAHI_PROTO_INET6 || aprotocol == AVAHI_PROTO_UNSPEC) { k = avahi_key_new(host_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA); - r->record_browser_aaaa = avahi_s_record_browser_new(server, interface, protocol, k, record_browser_callback, r); + r->record_browser_aaaa = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, r); avahi_key_unref(k); if (!r->record_browser_aaaa) @@ -213,6 +282,8 @@ AvahiSHostNameResolver *avahi_s_host_name_resolver_new(AvahiServer *server, Avah assert(r->record_browser_aaaa || r->record_browser_a); + start_timeout(r); + return r; fail: diff --git a/avahi-core/resolve-service.c b/avahi-core/resolve-service.c index 0ecd388..31b0081 100644 --- a/avahi-core/resolve-service.c +++ b/avahi-core/resolve-service.c @@ -33,8 +33,9 @@ #include #include "browse.h" +#include "log.h" -#define TIMEOUT_MSEC 1000 +#define TIMEOUT_MSEC 5000 struct AvahiSServiceResolver { AvahiServer *server; @@ -52,9 +53,11 @@ struct AvahiSServiceResolver { AvahiSRecordBrowser *record_browser_aaaa; AvahiRecord *srv_record, *txt_record, *address_record; + AvahiLookupResultFlags srv_flags, txt_flags, address_flags; AvahiSServiceResolverCallback callback; void* userdata; + AvahiLookupFlags user_flags; AvahiTimeEvent *time_event; @@ -62,6 +65,8 @@ struct AvahiSServiceResolver { }; static void finish(AvahiSServiceResolver *r, AvahiResolverEvent event) { + AvahiLookupResultFlags flags; + assert(r); if (r->time_event) { @@ -69,44 +74,74 @@ static void finish(AvahiSServiceResolver *r, AvahiResolverEvent event) { r->time_event = NULL; } - if (event == AVAHI_RESOLVER_TIMEOUT) - r->callback(r, r->interface, r->protocol, event, r->service_name, r->service_type, r->domain_name, NULL, NULL, 0, NULL, r->userdata); - else { - AvahiAddress a; - char sn[256], st[256]; - size_t i; - - assert(event == AVAHI_RESOLVER_FOUND); - - assert(r->srv_record); - assert(r->txt_record); - assert(r->address_record); - - switch (r->address_record->key->type) { - case AVAHI_DNS_TYPE_A: - a.proto = AVAHI_PROTO_INET; - a.data.ipv4 = r->address_record->data.a.address; - break; - - case AVAHI_DNS_TYPE_AAAA: - a.proto = AVAHI_PROTO_INET6; - a.data.ipv6 = r->address_record->data.aaaa.address; - break; - - default: - assert(0); + flags = + r->txt_flags | + r->srv_flags | + r->address_flags; + + switch (event) { + case AVAHI_RESOLVER_FAILURE: + case AVAHI_RESOLVER_NOT_FOUND: + case AVAHI_RESOLVER_TIMEOUT: + + r->callback( + r, + r->interface, + r->protocol, + event, + r->service_name, + r->service_type, + r->domain_name, + NULL, + NULL, + 0, + NULL, + flags, + r->userdata); + + break; + + case AVAHI_RESOLVER_FOUND: { + AvahiAddress a; + + assert(event == AVAHI_RESOLVER_FOUND); + + assert(r->srv_record); + + if (r->address_record) { + switch (r->address_record->key->type) { + case AVAHI_DNS_TYPE_A: + a.proto = AVAHI_PROTO_INET; + a.data.ipv4 = r->address_record->data.a.address; + break; + + case AVAHI_DNS_TYPE_AAAA: + a.proto = AVAHI_PROTO_INET6; + a.data.ipv6 = r->address_record->data.aaaa.address; + break; + + default: + assert(0); + } + } + + r->callback( + r, + r->interface, + r->protocol, + event, + r->service_name, + r->service_type, + r->domain_name, + r->address_record ? r->address_record->key->name : r->srv_record->data.srv.name, + r->address_record ? &a : NULL, + r->srv_record->data.srv.port, + r->txt_record ? r->txt_record->data.txt.string_list : NULL, + flags, + r->userdata); + + break; } - - snprintf(sn, sizeof(sn), r->service_name); - snprintf(st, sizeof(st), r->service_type); - - if ((i = strlen(sn)) > 0 && sn[i-1] == '.') - sn[i-1] = 0; - - if ((i = strlen(st)) > 0 && st[i-1] == '.') - st[i-1] = 0; - - r->callback(r, r->interface, r->protocol, event, sn, st, r->domain_name, r->srv_record->data.srv.name, &a, r->srv_record->data.srv.port, r->txt_record->data.txt.string_list, r->userdata); } } @@ -116,6 +151,8 @@ static void time_event_callback(AvahiTimeEvent *e, void *userdata) { assert(e); assert(r); + avahi_log_debug("timeout"); + finish(r, AVAHI_RESOLVER_TIMEOUT); } @@ -125,152 +162,237 @@ static void start_timeout(AvahiSServiceResolver *r) { if (r->time_event) return; - + avahi_elapse_time(&tv, TIMEOUT_MSEC, 0); + r->time_event = avahi_time_event_new(r->server->time_event_queue, &tv, time_event_callback, r); } -static void record_browser_callback(AvahiSRecordBrowser*rr, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, AvahiRecord *record, void* userdata) { +static void record_browser_callback( + AvahiSRecordBrowser*rr, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + AvahiRecord *record, + AvahiLookupResultFlags flags, + void* userdata) { + AvahiSServiceResolver *r = userdata; assert(rr); - assert(record); assert(r); - if (event == AVAHI_BROWSER_NEW) { - int changed = 0; - - if (r->interface > 0 && interface != r->interface) - return; - - if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol) - return; - - if (r->interface <= 0) - r->interface = interface; - - if (r->protocol == AVAHI_PROTO_UNSPEC) - r->protocol = protocol; - - switch (record->key->type) { - case AVAHI_DNS_TYPE_SRV: - if (!r->srv_record) { - r->srv_record = avahi_record_ref(record); - changed = 1; - - if (r->record_browser_a) { - avahi_s_record_browser_free(r->record_browser_a); - r->record_browser_a = NULL; + if (rr == r->record_browser_aaaa || rr == r->record_browser_a) + r->address_flags = flags; + else if (rr == r->record_browser_srv) + r->srv_flags = flags; + else if (rr == r->record_browser_txt) + r->txt_flags = flags; + + switch (event) { + + case AVAHI_BROWSER_NEW: { + int changed = 0; + assert(record); + + if (r->interface > 0 && interface != r->interface) + return; + + if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol) + return; + + if (r->interface <= 0) + r->interface = interface; + + if (r->protocol == AVAHI_PROTO_UNSPEC) + r->protocol = protocol; + + switch (record->key->type) { + case AVAHI_DNS_TYPE_SRV: + if (!r->srv_record) { + r->srv_record = avahi_record_ref(record); + changed = 1; + + if (r->record_browser_a) { + avahi_s_record_browser_free(r->record_browser_a); + r->record_browser_a = NULL; + } + + if (r->record_browser_aaaa) { + avahi_s_record_browser_free(r->record_browser_aaaa); + r->record_browser_aaaa = NULL; + } + + if (!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS)) { + + if (r->address_protocol == AVAHI_PROTO_INET || r->address_protocol == AVAHI_PROTO_UNSPEC) { + AvahiKey *k = avahi_key_new(r->srv_record->data.srv.name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A); + r->record_browser_a = avahi_s_record_browser_new(r->server, r->interface, r->protocol, k, r->user_flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r); + avahi_key_unref(k); + } + + if (r->address_protocol == AVAHI_PROTO_INET6 || r->address_protocol == AVAHI_PROTO_UNSPEC) { + AvahiKey *k = avahi_key_new(r->srv_record->data.srv.name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA); + r->record_browser_aaaa = avahi_s_record_browser_new(r->server, r->interface, r->protocol, k, r->user_flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r); + avahi_key_unref(k); + } + } } + break; + + case AVAHI_DNS_TYPE_TXT: - if (r->record_browser_aaaa) { - avahi_s_record_browser_free(r->record_browser_aaaa); - r->record_browser_aaaa = NULL; - } - - if (r->address_protocol == AVAHI_PROTO_INET || r->address_protocol == AVAHI_PROTO_UNSPEC) { - AvahiKey *k = avahi_key_new(r->srv_record->data.srv.name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A); - r->record_browser_a = avahi_s_record_browser_new(r->server, r->interface, r->protocol, k, record_browser_callback, r); - avahi_key_unref(k); - } - - if (r->address_protocol == AVAHI_PROTO_INET6 || r->address_protocol == AVAHI_PROTO_UNSPEC) { - AvahiKey *k = avahi_key_new(r->srv_record->data.srv.name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA); - r->record_browser_aaaa = avahi_s_record_browser_new(r->server, r->interface, r->protocol, k, record_browser_callback, r); - avahi_key_unref(k); + assert(!(r->user_flags & AVAHI_LOOKUP_NO_TXT)); + + if (!r->txt_record) { + r->txt_record = avahi_record_ref(record); + changed = 1; } - } - break; + break; - case AVAHI_DNS_TYPE_TXT: - if (!r->txt_record) { - r->txt_record = avahi_record_ref(record); - changed = 1; - } - break; - - case AVAHI_DNS_TYPE_A: - case AVAHI_DNS_TYPE_AAAA: - if (!r->address_record) { - r->address_record = avahi_record_ref(record); - changed = 1; - } - break; - - default: - abort(); - } + case AVAHI_DNS_TYPE_A: + case AVAHI_DNS_TYPE_AAAA: + assert(!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS)); - if (changed && r->txt_record && r->srv_record && r->address_record) - finish(r, AVAHI_RESOLVER_FOUND); + if (!r->address_record) { + r->address_record = avahi_record_ref(record); + changed = 1; + } + break; + + default: + abort(); + } - } else { - assert(event == AVAHI_BROWSER_REMOVE); - - switch (record->key->type) { - case AVAHI_DNS_TYPE_SRV: + if (changed && + r->srv_record && + (r->txt_record || (r->user_flags & AVAHI_LOOKUP_NO_TXT)) && + (r->address_record || (r->user_flags & AVAHI_LOOKUP_NO_ADDRESS))) + finish(r, AVAHI_RESOLVER_FOUND); - if (r->srv_record && avahi_record_equal_no_ttl(record, r->srv_record)) { - avahi_record_unref(r->srv_record); - r->srv_record = NULL; + break; - /** Look for a replacement */ - avahi_s_record_browser_restart(r->record_browser_srv); - start_timeout(r); - } - - break; + } + + case AVAHI_BROWSER_REMOVE: - case AVAHI_DNS_TYPE_TXT: + assert(record); - if (r->txt_record && avahi_record_equal_no_ttl(record, r->txt_record)) { - avahi_record_unref(r->txt_record); - r->txt_record = NULL; + switch (record->key->type) { + case AVAHI_DNS_TYPE_SRV: + + if (r->srv_record && avahi_record_equal_no_ttl(record, r->srv_record)) { + avahi_record_unref(r->srv_record); + r->srv_record = NULL; + + if (r->record_browser_a) { + avahi_s_record_browser_free(r->record_browser_a); + r->record_browser_a = NULL; + } + + if (r->record_browser_aaaa) { + avahi_s_record_browser_free(r->record_browser_aaaa); + r->record_browser_aaaa = NULL; + } + + /** Look for a replacement */ + avahi_s_record_browser_restart(r->record_browser_srv); + start_timeout(r); + } + + break; + + case AVAHI_DNS_TYPE_TXT: + + assert(!(r->user_flags & AVAHI_LOOKUP_NO_TXT)); + + if (r->txt_record && avahi_record_equal_no_ttl(record, r->txt_record)) { + avahi_record_unref(r->txt_record); + r->txt_record = NULL; + + /** Look for a replacement */ + avahi_s_record_browser_restart(r->record_browser_txt); + start_timeout(r); + } + break; + + case AVAHI_DNS_TYPE_A: + case AVAHI_DNS_TYPE_AAAA: + + assert(!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS)); + + if (r->address_record && avahi_record_equal_no_ttl(record, r->address_record)) { + avahi_record_unref(r->address_record); + r->address_record = NULL; + + /** Look for a replacement */ + if (r->record_browser_aaaa) + avahi_s_record_browser_restart(r->record_browser_aaaa); + if (r->record_browser_a) + avahi_s_record_browser_restart(r->record_browser_a); + start_timeout(r); + } + break; + + default: + abort(); + } - /** Look for a replacement */ - avahi_s_record_browser_restart(r->record_browser_txt); - start_timeout(r); - } - break; + break; - case AVAHI_DNS_TYPE_A: - case AVAHI_DNS_TYPE_AAAA: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + case AVAHI_BROWSER_ALL_FOR_NOW: + break; - if (r->address_record && avahi_record_equal_no_ttl(record, r->address_record)) { - avahi_record_unref(r->address_record); - r->address_record = NULL; + case AVAHI_BROWSER_NOT_FOUND: + case AVAHI_BROWSER_FAILURE: - /** Look for a replacement */ - if (r->record_browser_aaaa) - avahi_s_record_browser_restart(r->record_browser_aaaa); - if (r->record_browser_a) - avahi_s_record_browser_restart(r->record_browser_a); - start_timeout(r); - } - break; + if (r->record_browser_srv) + avahi_s_record_browser_free(r->record_browser_srv); + if (r->record_browser_txt) + avahi_s_record_browser_free(r->record_browser_txt); + if (r->record_browser_a) + avahi_s_record_browser_free(r->record_browser_a); + if (r->record_browser_aaaa) + avahi_s_record_browser_free(r->record_browser_aaaa); - default: - abort(); - } + r->record_browser_srv = r->record_browser_txt = r->record_browser_a = r->record_browser_aaaa = NULL; + + finish(r, event == AVAHI_BROWSER_FAILURE ? AVAHI_RESOLVER_FAILURE : AVAHI_RESOLVER_NOT_FOUND); + break; } } -AvahiSServiceResolver *avahi_s_service_resolver_new(AvahiServer *server, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain, AvahiProtocol aprotocol, AvahiSServiceResolverCallback callback, void* userdata) { +AvahiSServiceResolver *avahi_s_service_resolver_new( + AvahiServer *server, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *name, + const char *type, + const char *domain, + AvahiProtocol aprotocol, + AvahiLookupFlags flags, + AvahiSServiceResolverCallback callback, + void* userdata) { + AvahiSServiceResolver *r; AvahiKey *k; - char t[256], *n; - size_t l; + char t[256]; assert(server); - assert(name); assert(type); assert(callback); assert(aprotocol == AVAHI_PROTO_UNSPEC || aprotocol == AVAHI_PROTO_INET || aprotocol == AVAHI_PROTO_INET6); - if (!avahi_is_valid_service_name(name)) { + if (!AVAHI_IF_VALID(interface)) { + avahi_server_set_errno(server, AVAHI_ERR_INVALID_INTERFACE); + return NULL; + } + + if (name && !avahi_is_valid_service_name(name)) { avahi_server_set_errno(server, AVAHI_ERR_INVALID_SERVICE_NAME); return NULL; } @@ -280,10 +402,18 @@ AvahiSServiceResolver *avahi_s_service_resolver_new(AvahiServer *server, AvahiIf return NULL; } - if (!avahi_is_valid_domain_name(domain)) { + if (domain && !avahi_is_valid_domain_name(domain)) { avahi_server_set_errno(server, AVAHI_ERR_INVALID_DOMAIN_NAME); return NULL; } + + if (!domain) + domain = server->domain_name; + + if (!AVAHI_VALID_FLAGS(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST|AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS)) { + avahi_server_set_errno(server, AVAHI_ERR_INVALID_FLAGS); + return NULL; + } if (!(r = avahi_new(AvahiSServiceResolver, 1))) { avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); @@ -298,39 +428,50 @@ AvahiSServiceResolver *avahi_s_service_resolver_new(AvahiServer *server, AvahiIf r->userdata = userdata; r->address_protocol = aprotocol; r->srv_record = r->txt_record = r->address_record = NULL; + r->srv_flags = r->txt_flags = r->address_flags = 0; r->interface = interface; r->protocol = protocol; - - n = t; - l = sizeof(t); - avahi_escape_label((const uint8_t*) name, strlen(name), &n, &l); - snprintf(n, l, ".%s.%s", r->service_type, r->domain_name); + r->user_flags = flags; + + if (name) { + char *n; + size_t l; + + n = t; + l = sizeof(t); + avahi_escape_label((const uint8_t*) name, strlen(name), &n, &l); + snprintf(n, l, ".%s.%s", r->service_type, r->domain_name); + } else + snprintf(t, sizeof(t), "%s.%s", r->service_type, r->domain_name); r->time_event = NULL; - start_timeout(r); AVAHI_LLIST_PREPEND(AvahiSServiceResolver, resolver, server->service_resolvers, r); r->record_browser_a = r->record_browser_aaaa = r->record_browser_srv = r->record_browser_txt = NULL; k = avahi_key_new(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV); - r->record_browser_srv = avahi_s_record_browser_new(server, interface, protocol, k, record_browser_callback, r); + r->record_browser_srv = avahi_s_record_browser_new(server, interface, protocol, k, flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r); avahi_key_unref(k); if (!r->record_browser_srv) { avahi_s_service_resolver_free(r); return NULL; } - - k = avahi_key_new(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT); - r->record_browser_txt = avahi_s_record_browser_new(server, interface, protocol, k, record_browser_callback, r); - avahi_key_unref(k); - if (!r->record_browser_txt) { - avahi_s_service_resolver_free(r); - return NULL; + if (!(flags & AVAHI_LOOKUP_NO_TXT)) { + k = avahi_key_new(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT); + r->record_browser_txt = avahi_s_record_browser_new(server, interface, protocol, k, flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r); + avahi_key_unref(k); + + if (!r->record_browser_txt) { + avahi_s_service_resolver_free(r); + return NULL; + } } + start_timeout(r); + return r; } diff --git a/avahi-core/rr.c b/avahi-core/rr.c index 704f5be..90c6236 100644 --- a/avahi-core/rr.c +++ b/avahi-core/rr.c @@ -60,6 +60,19 @@ AvahiKey *avahi_key_new(const char *name, uint16_t class, uint16_t type) { return k; } +AvahiKey *avahi_key_new_cname(AvahiKey *key) { + assert(key); + + if (key->clazz != AVAHI_DNS_CLASS_IN) + return NULL; + + if (key->type == AVAHI_DNS_TYPE_CNAME) + return NULL; + + return avahi_key_new(key->name, key->clazz, AVAHI_DNS_TYPE_CNAME); +} + + AvahiKey *avahi_key_ref(AvahiKey *k) { assert(k); assert(k->ref >= 1); diff --git a/avahi-core/rr.h b/avahi-core/rr.h index 40134bf..6824d2a 100644 --- a/avahi-core/rr.h +++ b/avahi-core/rr.h @@ -124,6 +124,9 @@ typedef struct { /** Create a new AvahiKey object. The reference counter will be set to 1. */ AvahiKey *avahi_key_new(const char *name, uint16_t clazz, uint16_t type); +/** Creaze new AvahiKey object based on an existing key but replaceing the type by CNAME */ +AvahiKey *avahi_key_new_cname(AvahiKey *key); + /** Increase the reference counter of an AvahiKey object by one */ AvahiKey *avahi_key_ref(AvahiKey *k); diff --git a/avahi-core/server.c b/avahi-core/server.c index c746287..2714298 100644 --- a/avahi-core/server.c +++ b/avahi-core/server.c @@ -953,7 +953,7 @@ static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sock if (s->config.enable_reflector) from_local_iface = originates_from_local_iface(s, iface, &a, port); - if (avahi_dns_packet_is_valid(p) < 0) { + if (avahi_dns_packet_check_valid_multicast(p) < 0) { avahi_log_warn("Recieved invalid packet."); return; } @@ -1013,10 +1013,9 @@ static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sock } } -static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface, int ttl) { +static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface) { AvahiInterface *i, *j; AvahiAddress a; - uint16_t port; AvahiLegacyUnicastReflectSlot *slot; assert(s); @@ -1032,14 +1031,13 @@ static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, co /* avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */ - port = avahi_port_from_sockaddr(sa); avahi_address_from_sockaddr(sa, &a); if (avahi_address_is_ipv4_in_ipv6(&a)) /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */ return; - if (avahi_dns_packet_is_valid(p) < 0 || avahi_dns_packet_is_query(p)) { + if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) { avahi_log_warn("Recieved invalid packet."); return; } @@ -1094,22 +1092,21 @@ static void socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *us dest.proto = AVAHI_PROTO_INET; if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) { - dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface, ttl); + dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface); avahi_dns_packet_free(p); } } else if (fd == s->fd_legacy_unicast_ipv6) { dest.proto = AVAHI_PROTO_INET6; if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) { - dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface, ttl); + dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface); avahi_dns_packet_free(p); } } cleanup_dead(s); - } else { - assert(0); - } + } else + abort(); } static void server_set_state(AvahiServer *s, AvahiServerState state) { @@ -1335,20 +1332,24 @@ static int setup_sockets(AvahiServer *s) { else if (s->fd_ipv6 < 0 && s->config.use_ipv6) avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode"); - s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1; - s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1; - - s->watch_ipv4 = s->watch_ipv6 = s->watch_legacy_unicast_ipv4 = s->watch_legacy_unicast_ipv6 = NULL; + s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1; + s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1; + + s->watch_ipv4 = + s->watch_ipv6 = + s->watch_legacy_unicast_ipv4 = + s->watch_legacy_unicast_ipv6 = NULL; if (s->fd_ipv4 >= 0) s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, socket_event, s); if (s->fd_ipv6 >= 0) s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, socket_event, s); + if (s->fd_legacy_unicast_ipv4 >= 0) s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, socket_event, s); if (s->fd_legacy_unicast_ipv6 >= 0) s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, socket_event, s); - + return 0; } @@ -1386,6 +1387,7 @@ AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig return NULL; } + s->n_host_rr_pending = 0; s->need_entry_cleanup = 0; s->need_group_cleanup = 0; @@ -1413,6 +1415,15 @@ AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig s->legacy_unicast_reflect_slots = NULL; s->legacy_unicast_reflect_id = 0; + + if (s->config.enable_wide_area) { + s->wide_area_lookup_engine = avahi_wide_area_engine_new(s); + avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers); + } else + s->wide_area_lookup_engine = NULL; + + s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s); + do { s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand(); } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID); @@ -1445,15 +1456,7 @@ AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig void avahi_server_free(AvahiServer* s) { assert(s); - while(s->entries) - free_entry(s, s->entries); - - avahi_interface_monitor_free(s->monitor); - - while (s->groups) - free_group(s, s->groups); - - free_slots(s); + /* Remove all browsers */ while (s->dns_server_browsers) avahi_s_dns_server_browser_free(s->dns_server_browsers); @@ -1472,31 +1475,54 @@ void avahi_server_free(AvahiServer* s) { while (s->record_browsers) avahi_s_record_browser_destroy(s->record_browsers); - avahi_hashmap_free(s->record_browser_hashmap); - avahi_hashmap_free(s->entries_by_key); + /* Remove all locally rgeistered stuff */ - avahi_time_event_queue_free(s->time_event_queue); + while(s->entries) + free_entry(s, s->entries); + + avahi_interface_monitor_free(s->monitor); + + while (s->groups) + free_group(s, s->groups); + + free_slots(s); + avahi_hashmap_free(s->entries_by_key); avahi_record_list_free(s->record_list); + avahi_hashmap_free(s->record_browser_hashmap); + + if (s->wide_area_lookup_engine) + avahi_wide_area_engine_free(s->wide_area_lookup_engine); + avahi_multicast_lookup_engine_free(s->multicast_lookup_engine); + avahi_time_event_queue_free(s->time_event_queue); + + /* Free watches */ + if (s->watch_ipv4) s->poll_api->watch_free(s->watch_ipv4); if (s->watch_ipv6) s->poll_api->watch_free(s->watch_ipv6); + if (s->watch_legacy_unicast_ipv4) s->poll_api->watch_free(s->watch_legacy_unicast_ipv4); if (s->watch_legacy_unicast_ipv6) s->poll_api->watch_free(s->watch_legacy_unicast_ipv6); + + /* Free sockets */ if (s->fd_ipv4 >= 0) close(s->fd_ipv4); if (s->fd_ipv6 >= 0) close(s->fd_ipv6); + if (s->fd_legacy_unicast_ipv4 >= 0) close(s->fd_legacy_unicast_ipv4); if (s->fd_legacy_unicast_ipv6 >= 0) close(s->fd_legacy_unicast_ipv6); - + + /* Free other stuff */ + avahi_free(s->host_name); avahi_free(s->domain_name); avahi_free(s->host_name_fqdn); @@ -1631,6 +1657,9 @@ int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata } avahi_dump_caches(s->monitor, callback, userdata); + + if (s->wide_area_lookup_engine) + avahi_wide_area_cache_dump(s->wide_area_lookup_engine, callback, userdata); return AVAHI_OK; } @@ -2059,7 +2088,7 @@ int avahi_server_add_dns_server_address( AvahiRecord *r; int ret; - char n[64] = "ip-"; + char n[64], h[64]; assert(s); assert(address); @@ -2073,11 +2102,13 @@ int avahi_server_add_dns_server_address( return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME); if (address->proto == AVAHI_PROTO_INET) { - hexstring(n+3, sizeof(n)-3, &address->data, 4); + hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv4Address)); + snprintf(n, sizeof(n), "ip-%s.%s", h, s->domain_name); r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME); r->data.a.address = address->data.ipv4; } else { - hexstring(n+3, sizeof(n)-3, &address->data, 6); + hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv6Address)); + snprintf(n, sizeof(n), "ip6-%s.%s", h, s->domain_name); r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME); r->data.aaaa.address = address->data.ipv6; } @@ -2413,6 +2444,8 @@ AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) { c->enable_reflector = 0; c->reflect_ipv = 0; c->add_service_cookie = 1; + c->enable_wide_area = 0; + c->n_wide_area_servers = 0; return c; } @@ -2530,3 +2563,14 @@ fail: return ret; } + +/** Set the wide area DNS servers */ +int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) { + assert(s); + + if (!s->wide_area_lookup_engine) + return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG); + + avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n); + return AVAHI_OK; +} diff --git a/avahi-core/server.h b/avahi-core/server.h index 7384e98..600e442 100644 --- a/avahi-core/server.h +++ b/avahi-core/server.h @@ -37,6 +37,8 @@ typedef struct AvahiEntry AvahiEntry; #include "dns.h" #include "rrlist.h" #include "hashmap.h" +#include "wide-area.h" +#include "multicast-lookup.h" #define AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS 100 @@ -138,9 +140,14 @@ struct AvahiServer { AvahiLegacyUnicastReflectSlot **legacy_unicast_reflect_slots; uint16_t legacy_unicast_reflect_id; + /* The last error code */ int error; + /* The local service cookie */ uint32_t local_service_cookie; + + AvahiMulticastLookupEngine *multicast_lookup_engine; + AvahiWideAreaLookupEngine *wide_area_lookup_engine; }; int avahi_server_entry_match_interface(AvahiEntry *e, AvahiInterface *i); diff --git a/avahi-core/socket.c b/avahi-core/socket.c index 91cbf40..ecc137b 100644 --- a/avahi-core/socket.c +++ b/avahi-core/socket.c @@ -362,12 +362,11 @@ int avahi_send_dns_packet_ipv4(int fd, int interface, AvahiDnsPacket *p, const A struct msghdr msg; struct iovec io; struct cmsghdr *cmsg; - struct in_pktinfo *pkti; uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in_pktinfo)]; assert(fd >= 0); assert(p); - assert(avahi_dns_packet_is_valid(p) >= 0); + assert(avahi_dns_packet_check_valid(p) >= 0); assert(!a || port > 0); if (!a) @@ -379,24 +378,32 @@ int avahi_send_dns_packet_ipv4(int fd, int interface, AvahiDnsPacket *p, const A io.iov_base = AVAHI_DNS_PACKET_DATA(p); io.iov_len = p->size; - memset(cmsg_data, 0, sizeof(cmsg_data)); - cmsg = (struct cmsghdr*) cmsg_data; - cmsg->cmsg_len = sizeof(cmsg_data); - cmsg->cmsg_level = IPPROTO_IP; - cmsg->cmsg_type = IP_PKTINFO; - - pkti = (struct in_pktinfo*) (cmsg_data + sizeof(struct cmsghdr)); - pkti->ipi_ifindex = interface; - memset(&msg, 0, sizeof(msg)); msg.msg_name = &sa; msg.msg_namelen = sizeof(sa); msg.msg_iov = &io; msg.msg_iovlen = 1; - msg.msg_control = cmsg_data; - msg.msg_controllen = sizeof(cmsg_data); msg.msg_flags = 0; + if (interface >= 0) { + struct in_pktinfo *pkti; + + memset(cmsg_data, 0, sizeof(cmsg_data)); + cmsg = (struct cmsghdr*) cmsg_data; + cmsg->cmsg_len = sizeof(cmsg_data); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + + pkti = (struct in_pktinfo*) (cmsg_data + sizeof(struct cmsghdr)); + pkti->ipi_ifindex = interface; + + msg.msg_control = cmsg_data; + msg.msg_controllen = sizeof(cmsg_data); + } else { + msg.msg_control = NULL; + msg.msg_controllen = 0; + } + return sendmsg_loop(fd, &msg, 0); } @@ -405,12 +412,11 @@ int avahi_send_dns_packet_ipv6(int fd, int interface, AvahiDnsPacket *p, const A struct msghdr msg; struct iovec io; struct cmsghdr *cmsg; - struct in6_pktinfo *pkti; uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in6_pktinfo)]; assert(fd >= 0); assert(p); - assert(avahi_dns_packet_is_valid(p) >= 0); + assert(avahi_dns_packet_check_valid(p) >= 0); if (!a) mdns_mcast_group_ipv6(&sa); @@ -421,25 +427,34 @@ int avahi_send_dns_packet_ipv6(int fd, int interface, AvahiDnsPacket *p, const A io.iov_base = AVAHI_DNS_PACKET_DATA(p); io.iov_len = p->size; - memset(cmsg_data, 0, sizeof(cmsg_data)); - cmsg = (struct cmsghdr*) cmsg_data; - cmsg->cmsg_len = sizeof(cmsg_data); - cmsg->cmsg_level = IPPROTO_IPV6; - cmsg->cmsg_type = IPV6_PKTINFO; - - pkti = (struct in6_pktinfo*) (cmsg_data + sizeof(struct cmsghdr)); - pkti->ipi6_ifindex = interface; memset(&msg, 0, sizeof(msg)); msg.msg_name = &sa; msg.msg_namelen = sizeof(sa); msg.msg_iov = &io; msg.msg_iovlen = 1; - msg.msg_control = cmsg_data; - msg.msg_controllen = sizeof(cmsg_data); msg.msg_flags = 0; - return sendmsg_loop(fd, &msg, 0 /*MSG_DONTROUTE*/); + if (interface >= 0) { + struct in6_pktinfo *pkti; + + memset(cmsg_data, 0, sizeof(cmsg_data)); + cmsg = (struct cmsghdr*) cmsg_data; + cmsg->cmsg_len = sizeof(cmsg_data); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + + pkti = (struct in6_pktinfo*) (cmsg_data + sizeof(struct cmsghdr)); + pkti->ipi6_ifindex = interface; + + msg.msg_control = cmsg_data; + msg.msg_controllen = sizeof(cmsg_data); + } else { + msg.msg_control = NULL; + msg.msg_controllen = 0; + } + + return sendmsg_loop(fd, &msg, 0); } AvahiDnsPacket* avahi_recv_dns_packet_ipv4(int fd, struct sockaddr_in *ret_sa, AvahiIPv4Address *ret_dest_address, int *ret_iface, uint8_t* ret_ttl) { @@ -453,10 +468,6 @@ AvahiDnsPacket* avahi_recv_dns_packet_ipv4(int fd, struct sockaddr_in *ret_sa, A int ms; assert(fd >= 0); - assert(ret_sa); - assert(ret_dest_address); - assert(ret_iface); - assert(ret_ttl); if (ioctl(fd, FIONREAD, &ms) < 0) { avahi_log_warn("ioctl(): %s", strerror(errno)); @@ -469,8 +480,13 @@ AvahiDnsPacket* avahi_recv_dns_packet_ipv4(int fd, struct sockaddr_in *ret_sa, A io.iov_len = p->max_size; memset(&msg, 0, sizeof(msg)); - msg.msg_name = ret_sa; - msg.msg_namelen = sizeof(struct sockaddr_in); + if (ret_sa) { + msg.msg_name = ret_sa; + msg.msg_namelen = sizeof(struct sockaddr_in); + } else { + msg.msg_name = NULL; + msg.msg_namelen = 0; + } msg.msg_iov = &io; msg.msg_iovlen = 1; msg.msg_control = aux; @@ -482,7 +498,7 @@ AvahiDnsPacket* avahi_recv_dns_packet_ipv4(int fd, struct sockaddr_in *ret_sa, A goto fail; } - if (ret_sa->sin_addr.s_addr == INADDR_ANY) { + if (ret_sa && ret_sa->sin_addr.s_addr == INADDR_ANY) { /* Linux 2.4 behaves very strangely sometimes! */ /*avahi_hexdump(AVAHI_DNS_PACKET_DATA(p), l); */ @@ -492,31 +508,31 @@ AvahiDnsPacket* avahi_recv_dns_packet_ipv4(int fd, struct sockaddr_in *ret_sa, A assert(!(msg.msg_flags & MSG_CTRUNC)); assert(!(msg.msg_flags & MSG_TRUNC)); p->size = (size_t) l; - - *ret_ttl = 0; -/* avahi_hexdump(msg.msg_control, msg.msg_controllen); */ - + if (ret_ttl) + *ret_ttl = 0; + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { -/* avahi_hexdump(CMSG_DATA(cmsg), cmsg->cmsg_len - sizeof(struct cmsghdr)); */ - if (cmsg->cmsg_level == SOL_IP) { if (cmsg->cmsg_type == IP_TTL) { - *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg)); + if (ret_ttl) + *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg)); found_ttl = 1; } else if (cmsg->cmsg_type == IP_PKTINFO) { struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg); - *ret_iface = (int) i->ipi_ifindex; - ret_dest_address->address = i->ipi_addr.s_addr; + + if (ret_iface) + *ret_iface = (int) i->ipi_ifindex; + + if (ret_dest_address) + ret_dest_address->address = i->ipi_addr.s_addr; found_iface = 1; } } } -/* avahi_log_debug("ttl=%u iface=%i", *ret_ttl, *ret_iface); */ - assert(found_iface); assert(found_ttl); @@ -541,10 +557,6 @@ AvahiDnsPacket* avahi_recv_dns_packet_ipv6(int fd, struct sockaddr_in6 *ret_sa, int found_ttl = 0, found_iface = 0; assert(fd >= 0); - assert(ret_sa); - assert(ret_dest_address); - assert(ret_iface); - assert(ret_ttl); if (ioctl(fd, FIONREAD, &ms) < 0) { avahi_log_warn("ioctl(): %s", strerror(errno)); @@ -557,8 +569,14 @@ AvahiDnsPacket* avahi_recv_dns_packet_ipv6(int fd, struct sockaddr_in6 *ret_sa, io.iov_len = p->max_size; memset(&msg, 0, sizeof(msg)); - msg.msg_name = ret_sa; - msg.msg_namelen = sizeof(struct sockaddr_in6); + if (ret_sa) { + msg.msg_name = ret_sa; + msg.msg_namelen = sizeof(struct sockaddr_in6); + } else { + msg.msg_name = NULL; + msg.msg_namelen = 0; + } + msg.msg_iov = &io; msg.msg_iovlen = 1; msg.msg_control = aux; @@ -571,19 +589,27 @@ AvahiDnsPacket* avahi_recv_dns_packet_ipv6(int fd, struct sockaddr_in6 *ret_sa, } p->size = (size_t) l; - - *ret_ttl = 0; + + if (ret_ttl) + *ret_ttl = 0; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) { - *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg)); + + if (ret_ttl) + *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg)); + found_ttl = 1; } if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg); - *ret_iface = i->ipi6_ifindex; - memcpy(ret_dest_address->address, i->ipi6_addr.s6_addr, 16); + + if (ret_iface) + *ret_iface = i->ipi6_ifindex; + + if (ret_dest_address) + memcpy(ret_dest_address->address, i->ipi6_addr.s6_addr, 16); found_iface = 1; } } @@ -600,7 +626,7 @@ fail: return NULL; } -int avahi_open_legacy_unicast_socket_ipv4(void) { +int avahi_open_unicast_socket_ipv4(void) { struct sockaddr_in local; int fd = -1, yes; @@ -648,8 +674,8 @@ fail: return -1; } -int avahi_open_legacy_unicast_socket_ipv6(void) { - struct sockaddr_in local; +int avahi_open_unicast_socket_ipv6(void) { + struct sockaddr_in6 local; int fd = -1, yes; if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { @@ -658,7 +684,7 @@ int avahi_open_legacy_unicast_socket_ipv6(void) { } memset(&local, 0, sizeof(local)); - local.sin_family = AF_INET; + local.sin6_family = AF_INET6; if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) { avahi_log_warn("bind() failed: %s\n", strerror(errno)); diff --git a/avahi-core/socket.h b/avahi-core/socket.h index d795156..971e7e8 100644 --- a/avahi-core/socket.h +++ b/avahi-core/socket.h @@ -28,14 +28,15 @@ #include "dns.h" #define AVAHI_MDNS_PORT 5353 +#define AVAHI_DNS_PORT 53 #define AVAHI_IPV4_MCAST_GROUP "224.0.0.251" #define AVAHI_IPV6_MCAST_GROUP "ff02::fb" int avahi_open_socket_ipv4(void); int avahi_open_socket_ipv6(void); -int avahi_open_legacy_unicast_socket_ipv4(void); -int avahi_open_legacy_unicast_socket_ipv6(void); +int avahi_open_unicast_socket_ipv4(void); +int avahi_open_unicast_socket_ipv6(void); int avahi_send_dns_packet_ipv4(int fd, int iface, AvahiDnsPacket *p, const AvahiIPv4Address *a, uint16_t port); int avahi_send_dns_packet_ipv6(int fd, int iface, AvahiDnsPacket *p, const AvahiIPv6Address *a, uint16_t port); diff --git a/avahi-core/wide-area.c b/avahi-core/wide-area.c new file mode 100644 index 0000000..2f8661d --- /dev/null +++ b/avahi-core/wide-area.c @@ -0,0 +1,693 @@ +/* $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 "server.h" +#include "browse.h" +#include "socket.h" +#include "log.h" +#include "hashmap.h" +#include "wide-area.h" + +#define MAX_CACHE_ENTRIES 500 + +typedef struct AvahiWideAreaCacheEntry AvahiWideAreaCacheEntry; + +struct AvahiWideAreaCacheEntry { + AvahiWideAreaLookupEngine *engine; + + AvahiRecord *record; + struct timeval timestamp; + struct timeval expiry; + + AvahiTimeEvent *time_event; + + AVAHI_LLIST_FIELDS(AvahiWideAreaCacheEntry, by_key); + AVAHI_LLIST_FIELDS(AvahiWideAreaCacheEntry, cache); +}; + +struct AvahiWideAreaLookup { + AvahiWideAreaLookupEngine *engine; + int dead; + + uint32_t id; /* effectively just an uint16_t, but we need it as an index for a hash table */ + AvahiTimeEvent *time_event; + + AvahiKey *key, *cname_key; + + int n_send; + AvahiDnsPacket *packet; + + AvahiWideAreaLookupCallback callback; + void *userdata; + + AvahiAddress dns_server_used; + + AVAHI_LLIST_FIELDS(AvahiWideAreaLookup, lookups); + AVAHI_LLIST_FIELDS(AvahiWideAreaLookup, by_key); +}; + +struct AvahiWideAreaLookupEngine { + AvahiServer *server; + + int fd_ipv4, fd_ipv6; + AvahiWatch *watch_ipv4, *watch_ipv6; + + uint16_t next_id; + + /* Cache */ + AVAHI_LLIST_HEAD(AvahiWideAreaCacheEntry, cache); + AvahiHashmap *cache_by_key; + unsigned cache_n_entries; + + /* Lookups */ + AVAHI_LLIST_HEAD(AvahiWideAreaLookup, lookups); + AvahiHashmap *lookups_by_id; + AvahiHashmap *lookups_by_key; + + int cleanup_dead; + + AvahiAddress dns_servers[AVAHI_MAX_WIDE_AREA_SERVERS]; + unsigned n_dns_servers; + unsigned current_dns_server; +}; + +static AvahiWideAreaLookup* find_lookup(AvahiWideAreaLookupEngine *e, uint16_t id) { + AvahiWideAreaLookup *l; + int i = (int) id; + + assert(e); + + if (!(l = avahi_hashmap_lookup(e->lookups_by_id, &i))) + return NULL; + + assert(l->id == id); + + if (l->dead) + return NULL; + + return l; +} + +static int send_to_dns_server(AvahiWideAreaLookupEngine *e, AvahiDnsPacket *p) { + AvahiAddress *a; + + assert(e); + assert(p); + + if (e->n_dns_servers <= 0) + return -1; + + assert(e->current_dns_server < e->n_dns_servers); + + a = &e->dns_servers[e->current_dns_server]; + + if (a->proto == AVAHI_PROTO_INET) { + + if (e->fd_ipv4 < 0) + return -1; + + return avahi_send_dns_packet_ipv4(e->fd_ipv4, AVAHI_IF_UNSPEC, p, &a->data.ipv4, AVAHI_DNS_PORT); + + } else { + assert(a->proto == AVAHI_PROTO_INET6); + + if (e->fd_ipv6 < 0) + return -1; + + return avahi_send_dns_packet_ipv6(e->fd_ipv6, AVAHI_IF_UNSPEC, p, &a->data.ipv6, AVAHI_DNS_PORT); + } +} + +static void next_dns_server(AvahiWideAreaLookupEngine *e) { + assert(e); + + e->current_dns_server++; + + if (e->current_dns_server >= e->n_dns_servers) + e->current_dns_server = 0; +} + +static void sender_timeout_callback(AvahiTimeEvent *e, void *userdata) { + AvahiWideAreaLookup *l = userdata; + struct timeval tv; + + assert(l); + + /* Try another DNS server after three retries */ + if (l->n_send >= 3 && avahi_address_cmp(&l->engine->dns_servers[l->engine->current_dns_server], &l->dns_server_used) == 0) { + next_dns_server(l->engine); + + if (avahi_address_cmp(&l->engine->dns_servers[l->engine->current_dns_server], &l->dns_server_used) == 0) + /* There is no other DNS server, fail */ + l->n_send = 1000; + } + + if (l->n_send >= 6) { + avahi_log_warn(__FILE__": Query timed out."); + l->callback(l->engine, AVAHI_BROWSER_FAILURE, AVAHI_LOOKUP_CALLBACK_WIDE_AREA, NULL, l->userdata); + avahi_wide_area_lookup_free(l); + return; + } + + assert(l->packet); + send_to_dns_server(l->engine, l->packet); + l->n_send++; + + avahi_time_event_update(e, avahi_elapse_time(&tv, 1000, 0)); +} + +AvahiWideAreaLookup *avahi_wide_area_lookup_new( + AvahiWideAreaLookupEngine *e, + AvahiKey *key, + AvahiWideAreaLookupCallback callback, + void *userdata) { + + struct timeval tv; + AvahiWideAreaLookup *l, *t; + uint8_t *p; + + assert(e); + assert(key); + assert(callback); + assert(userdata); + + l = avahi_new(AvahiWideAreaLookup, 1); + l->engine = e; + l->dead = 0; + l->key = avahi_key_ref(key); + l->cname_key = avahi_key_new_cname(l->key); + l->callback = callback; + l->userdata = userdata; + + /* If more than 65K wide area quries are issued simultaneously, + * this will break. This should be limited by some higher level */ + + for (;; e->next_id++) + if (!find_lookup(e, e->next_id)) + break; /* This ID is not yet used. */ + + l->id = e->next_id++; + + /* We keep the packet around in case we need to repeat our query */ + l->packet = avahi_dns_packet_new(0); + + avahi_dns_packet_set_field(l->packet, AVAHI_DNS_FIELD_ID, (uint16_t) l->id); + avahi_dns_packet_set_field(l->packet, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(0, 0, 0, 0, 1, 0, 0, 0, 0, 0)); + + p = avahi_dns_packet_append_key(l->packet, key, 0); + assert(p); + + avahi_dns_packet_set_field(l->packet, AVAHI_DNS_FIELD_QDCOUNT, 1); + + if (send_to_dns_server(e, l->packet) < 0) { + avahi_log_error(__FILE__": Failed to send packet."); + avahi_dns_packet_free(l->packet); + avahi_key_unref(l->key); + if (l->cname_key) + avahi_key_unref(l->cname_key); + avahi_free(l); + return NULL; + } + + l->n_send = 1; + + l->time_event = avahi_time_event_new(e->server->time_event_queue, avahi_elapse_time(&tv, 500, 0), sender_timeout_callback, l); + + avahi_hashmap_insert(e->lookups_by_id, &l->id, l); + + t = avahi_hashmap_lookup(e->lookups_by_key, l->key); + AVAHI_LLIST_PREPEND(AvahiWideAreaLookup, by_key, t, l); + avahi_hashmap_replace(e->lookups_by_key, avahi_key_ref(l->key), t); + + AVAHI_LLIST_PREPEND(AvahiWideAreaLookup, lookups, e->lookups, l); + + return l; +} + +static void lookup_stop(AvahiWideAreaLookup *l) { + assert(l); + + l->callback = NULL; + + if (l->time_event) { + avahi_time_event_free(l->time_event); + l->time_event = NULL; + } +} + +static void lookup_destroy(AvahiWideAreaLookup *l) { + AvahiWideAreaLookup *t; + assert(l); + + lookup_stop(l); + + t = avahi_hashmap_lookup(l->engine->lookups_by_key, l->key); + AVAHI_LLIST_REMOVE(AvahiWideAreaLookup, by_key, t, l); + if (t) + avahi_hashmap_replace(l->engine->lookups_by_key, avahi_key_ref(l->key), t); + else + avahi_hashmap_remove(l->engine->lookups_by_key, l->key); + + AVAHI_LLIST_REMOVE(AvahiWideAreaLookup, lookups, l->engine->lookups, l); + + avahi_hashmap_remove(l->engine->lookups_by_id, &l->id); + avahi_dns_packet_free(l->packet); + + if (l->key) + avahi_key_unref(l->key); + + if (l->cname_key) + avahi_key_unref(l->cname_key); + + avahi_free(l); +} + +void avahi_wide_area_lookup_free(AvahiWideAreaLookup *l) { + assert(l); + + if (l->dead) + return; + + l->dead = 1; + l->engine->cleanup_dead = 1; + lookup_stop(l); +} + +void avahi_wide_area_cleanup(AvahiWideAreaLookupEngine *e) { + AvahiWideAreaLookup *l, *n; + assert(e); + + while (e->cleanup_dead) { + e->cleanup_dead = 0; + + for (l = e->lookups; l; l = n) { + n = l->lookups_next; + + if (l->dead) + lookup_destroy(l); + } + } +} + +static void cache_entry_free(AvahiWideAreaCacheEntry *c) { + AvahiWideAreaCacheEntry *t; + assert(c); + + if (c->time_event) + avahi_time_event_free(c->time_event); + + AVAHI_LLIST_REMOVE(AvahiWideAreaCacheEntry, cache, c->engine->cache, c); + + t = avahi_hashmap_lookup(c->engine->cache_by_key, c->record->key); + AVAHI_LLIST_REMOVE(AvahiWideAreaCacheEntry, by_key, t, c); + if (t) + avahi_hashmap_replace(c->engine->cache_by_key, avahi_key_ref(c->record->key), t); + else + avahi_hashmap_remove(c->engine->cache_by_key, c->record->key); + + c->engine->cache_n_entries --; + + avahi_record_unref(c->record); + avahi_free(c); +} + +static void expiry_event(AvahiTimeEvent *te, void *userdata) { + AvahiWideAreaCacheEntry *e = userdata; + + assert(te); + assert(e); + + cache_entry_free(e); +} + +static AvahiWideAreaCacheEntry* find_record_in_cache(AvahiWideAreaLookupEngine *e, AvahiRecord *r) { + AvahiWideAreaCacheEntry *c; + + assert(e); + assert(r); + + for (c = avahi_hashmap_lookup(e->cache_by_key, r->key); c; c = c->by_key_next) + if (avahi_record_equal_no_ttl(r, c->record)) + return c; + + return NULL; +} + +static void run_callbacks(AvahiWideAreaLookupEngine *e, AvahiRecord *r) { + AvahiWideAreaLookup *l; + + assert(e); + assert(r); + + for (l = avahi_hashmap_lookup(e->lookups_by_key, r->key); l; l = l->by_key_next) { + if (l->dead || !l->callback) + continue; + + l->callback(e, AVAHI_BROWSER_NEW, AVAHI_LOOKUP_CALLBACK_WIDE_AREA, r, l->userdata); + } + + if (r->key->clazz == AVAHI_DNS_CLASS_IN && r->key->type == AVAHI_DNS_TYPE_CNAME) { + /* It's a CNAME record, so we have to scan the all lookups to see if one matches */ + + for (l = e->lookups; l; l = l->lookups_next) { + AvahiKey *key; + + if (l->dead || !l->callback) + continue; + + if ((key = avahi_key_new_cname(l->key))) { + if (avahi_key_equal(r->key, key)) + l->callback(e, AVAHI_BROWSER_NEW, AVAHI_LOOKUP_CALLBACK_WIDE_AREA, r, l->userdata); + + avahi_key_unref(key); + } + } + } +} + +static void add_to_cache(AvahiWideAreaLookupEngine *e, AvahiRecord *r) { + AvahiWideAreaCacheEntry *c; + int is_new; + + assert(e); + assert(r); + + if ((c = find_record_in_cache(e, r))) { + is_new = 0; + + /* Update the existing entry */ + avahi_record_unref(c->record); + } else { + AvahiWideAreaCacheEntry *t; + + is_new = 1; + + /* Enforce cache size */ + if (e->cache_n_entries >= MAX_CACHE_ENTRIES) + /* Eventually we should improve the caching algorithm here */ + goto finish; + + c = avahi_new(AvahiWideAreaCacheEntry, 1); + c->engine = e; + c->time_event = NULL; + + AVAHI_LLIST_PREPEND(AvahiWideAreaCacheEntry, cache, e->cache, c); + + /* Add the new entry to the cache entry hash table */ + t = avahi_hashmap_lookup(e->cache_by_key, r->key); + AVAHI_LLIST_PREPEND(AvahiWideAreaCacheEntry, by_key, t, c); + avahi_hashmap_replace(e->cache_by_key, avahi_key_ref(r->key), t); + + e->cache_n_entries ++; + } + + c->record = avahi_record_ref(r); + + gettimeofday(&c->timestamp, NULL); + c->expiry = c->timestamp; + avahi_timeval_add(&c->expiry, r->ttl * 1000000); + + if (c->time_event) + avahi_time_event_update(c->time_event, &c->expiry); + else + c->time_event = avahi_time_event_new(e->server->time_event_queue, &c->expiry, expiry_event, c); + +finish: + + if (is_new) + run_callbacks(e, r); +} + +static void handle_packet(AvahiWideAreaLookupEngine *e, AvahiDnsPacket *p, AvahiAddress *a) { + AvahiWideAreaLookup *l = NULL; + int i, r; + + assert(e); + assert(p); + + if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) { + avahi_log_warn(__FILE__": Ignoring invalid response for wide area datagram."); + goto finish; + } + +/* avahi_log_debug(__FILE__": Recieving unicast packet %u", avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)); */ + + if (!(l = find_lookup(e, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) { +/* avahi_log_warn(__FILE__": Got response for query we didn't send."); */ + goto finish; + } + + + if ((r = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & 15) != 0) { +/* avahi_log_debug(__FILE__": Response failed for wide area datagram: %i", r); */ + + /* Tell the user about the failure */ + if (l->callback) + l->callback(e, r == 3 ? AVAHI_BROWSER_NOT_FOUND : AVAHI_BROWSER_FAILURE, AVAHI_LOOKUP_CALLBACK_WIDE_AREA, NULL, l->userdata); + goto finish; + } + + /* Skip over the question */ + for (i = (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); i > 0; i--) { + AvahiKey *k; + + if (!(k = avahi_dns_packet_consume_key(p, NULL))) { + avahi_log_warn(__FILE__": Wide area response packet too short."); + goto finish; + } + + avahi_key_unref(k); + } + + for (i = (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) + + (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) + + (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); i > 0; i--) { + + AvahiRecord *rr; + + if (!(rr = avahi_dns_packet_consume_record(p, NULL))) { + avahi_log_warn(__FILE__": Wide area response packet too short (2)."); + goto finish; + } + + add_to_cache(e, rr); + + avahi_record_unref(rr); + } + + /** Inform the user that this is the last reply */ + if (l->callback && !l->dead) + l->callback(e, AVAHI_BROWSER_ALL_FOR_NOW, AVAHI_LOOKUP_CALLBACK_WIDE_AREA, NULL, l->userdata); + +finish: + if (l) + lookup_stop(l); +} + +static void socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) { + AvahiWideAreaLookupEngine *e = userdata; + AvahiAddress a; + AvahiDnsPacket *p = NULL; + + if (fd == e->fd_ipv4) { + struct sockaddr_in sa; + + if ((p = avahi_recv_dns_packet_ipv4(e->fd_ipv4, &sa, NULL, NULL, NULL))) + avahi_address_from_sockaddr((struct sockaddr*) &sa, &a); + + } else if (fd == e->fd_ipv6) { + struct sockaddr_in6 sa6; + + if ((p = avahi_recv_dns_packet_ipv6(e->fd_ipv6, &sa6, NULL, NULL, NULL))) + avahi_address_from_sockaddr((struct sockaddr*) &sa6, &a); + + } + + if (p) { + handle_packet(e, p, &a); + avahi_dns_packet_free(p); + } +} + +AvahiWideAreaLookupEngine *avahi_wide_area_engine_new(AvahiServer *s) { + AvahiWideAreaLookupEngine *e; + + assert(s); + + e = avahi_new(AvahiWideAreaLookupEngine, 1); + e->server = s; + e->cleanup_dead = 0; + + /* Create sockets */ + e->fd_ipv4 = avahi_open_unicast_socket_ipv4(); + e->fd_ipv6 = avahi_open_unicast_socket_ipv6(); + + if (e->fd_ipv4 < 0 && e->fd_ipv6 < 0) { + avahi_log_error(__FILE__": Failed to create wide area sockets: %s\n", strerror(errno)); + + if (e->fd_ipv6 >= 0) + close(e->fd_ipv6); + + if (e->fd_ipv4 >= 0) + close(e->fd_ipv4); + + avahi_free(e); + return NULL; + } + + /* Create watches */ + if (e->fd_ipv4 >= 0) + e->watch_ipv4 = s->poll_api->watch_new(e->server->poll_api, e->fd_ipv4, AVAHI_WATCH_IN, socket_event, e); + if (e->fd_ipv6 >= 0) + e->watch_ipv6 = s->poll_api->watch_new(e->server->poll_api, e->fd_ipv6, AVAHI_WATCH_IN, socket_event, e); + + e->n_dns_servers = e->current_dns_server = 0; + e->next_id = (uint16_t) rand(); + + /* Initialize cache */ + AVAHI_LLIST_HEAD_INIT(AvahiWideAreaCacheEntry, e->cache); + e->cache_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, (AvahiFreeFunc) avahi_key_unref, NULL); + e->cache_n_entries = 0; + + /* Initialize lookup list */ + e->lookups_by_id = avahi_hashmap_new((AvahiHashFunc) avahi_int_hash, (AvahiEqualFunc) avahi_int_equal, NULL, NULL); + e->lookups_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, (AvahiFreeFunc) avahi_key_unref, NULL); + AVAHI_LLIST_HEAD_INIT(AvahiWideAreaLookup, e->lookups); + + return e; +} + +void avahi_wide_area_engine_free(AvahiWideAreaLookupEngine *e) { + assert(e); + + avahi_wide_area_clear_cache(e); + + while (e->lookups) + lookup_destroy(e->lookups); + + avahi_hashmap_free(e->cache_by_key); + avahi_hashmap_free(e->lookups_by_id); + avahi_hashmap_free(e->lookups_by_key); + + if (e->watch_ipv4) + e->server->poll_api->watch_free(e->watch_ipv4); + + if (e->watch_ipv6) + e->server->poll_api->watch_free(e->watch_ipv6); + + if (e->fd_ipv6 >= 0) + close(e->fd_ipv6); + + if (e->fd_ipv4 >= 0) + close(e->fd_ipv4); + + avahi_free(e); +} + +void avahi_wide_area_clear_cache(AvahiWideAreaLookupEngine *e) { + assert(e); + + while (e->cache) + cache_entry_free(e->cache); + + assert(e->cache_n_entries == 0); +} + + +void avahi_wide_area_set_servers(AvahiWideAreaLookupEngine *e, const AvahiAddress *a, unsigned n) { + assert(e); + + + if (a) { + for (e->n_dns_servers = 0; n > 0 && e->n_dns_servers < AVAHI_MAX_WIDE_AREA_SERVERS; a++, n--) + if ((a->proto == AVAHI_PROTO_INET && e->fd_ipv4 >= 0) || (a->proto == AVAHI_PROTO_INET6 && e->fd_ipv6 >= 0)) + e->dns_servers[e->n_dns_servers++] = *a; + } else { + assert(n == 0); + e->n_dns_servers = 0; + } + + e->current_dns_server = 0; + + avahi_wide_area_clear_cache(e); +} + +void avahi_wide_area_cache_dump(AvahiWideAreaLookupEngine *e, AvahiDumpCallback callback, void* userdata) { + AvahiWideAreaCacheEntry *c; + + assert(e); + assert(callback); + + callback(";; WIDE AREA CACHE ;;; ", userdata); + + for (c = e->cache; c; c = c->cache_next) { + char *t = avahi_record_to_string(c->record); + callback(t, userdata); + avahi_free(t); + } +} + +unsigned avahi_wide_area_scan_cache(AvahiWideAreaLookupEngine *e, AvahiKey *key, AvahiWideAreaLookupCallback callback, void *userdata) { + AvahiWideAreaCacheEntry *c; + AvahiKey *cname_key; + unsigned n = 0; + + assert(e); + assert(key); + assert(callback); + + for (c = avahi_hashmap_lookup(e->cache_by_key, key); c; c = c->by_key_next) { + callback(e, AVAHI_BROWSER_NEW, AVAHI_LOOKUP_CALLBACK_WIDE_AREA|AVAHI_LOOKUP_CALLBACK_CACHED, c->record, userdata); + n++; + } + + if ((cname_key = avahi_key_new_cname(key))) { + + for (c = avahi_hashmap_lookup(e->cache_by_key, cname_key); c; c = c->by_key_next) { + callback(e, AVAHI_BROWSER_NEW, AVAHI_LOOKUP_CALLBACK_WIDE_AREA|AVAHI_LOOKUP_CALLBACK_CACHED, c->record, userdata); + n++; + } + + avahi_key_unref(cname_key); + } + + return n; +} + +int avahi_wide_area_has_servers(AvahiWideAreaLookupEngine *e) { + assert(e); + + return e->n_dns_servers > 0; +} + + + diff --git a/avahi-core/wide-area.h b/avahi-core/wide-area.h new file mode 100644 index 0000000..1af613b --- /dev/null +++ b/avahi-core/wide-area.h @@ -0,0 +1,54 @@ +#ifndef foowideareahfoo +#define foowideareahfoo + +/* $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 "lookup.h" +#include "browse.h" + +typedef struct AvahiWideAreaLookupEngine AvahiWideAreaLookupEngine; +typedef struct AvahiWideAreaLookup AvahiWideAreaLookup; + +typedef void (*AvahiWideAreaLookupCallback)( + AvahiWideAreaLookupEngine *e, + AvahiBrowserEvent event, + AvahiLookupResultFlags flags, + AvahiRecord *r, + void *userdata); + +AvahiWideAreaLookupEngine *avahi_wide_area_engine_new(AvahiServer *s); +void avahi_wide_area_engine_free(AvahiWideAreaLookupEngine *e); + +unsigned avahi_wide_area_scan_cache(AvahiWideAreaLookupEngine *e, AvahiKey *key, AvahiWideAreaLookupCallback callback, void *userdata); +void avahi_wide_area_cache_dump(AvahiWideAreaLookupEngine *e, AvahiDumpCallback callback, void* userdata); +void avahi_wide_area_set_servers(AvahiWideAreaLookupEngine *e, const AvahiAddress *a, unsigned n); +void avahi_wide_area_clear_cache(AvahiWideAreaLookupEngine *e); +void avahi_wide_area_cleanup(AvahiWideAreaLookupEngine *e); +int avahi_wide_area_has_servers(AvahiWideAreaLookupEngine *e); + +AvahiWideAreaLookup *avahi_wide_area_lookup_new(AvahiWideAreaLookupEngine *e, AvahiKey *key, AvahiWideAreaLookupCallback callback, void *userdata); +void avahi_wide_area_lookup_free(AvahiWideAreaLookup *q); + + + +#endif +