diff options
Diffstat (limited to 'avahi-core')
36 files changed, 3861 insertions, 1119 deletions
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 <avahi-common/alternative.h> #include <avahi-core/core.h> #include <avahi-core/log.h> +#include <avahi-core/publish.h> +#include <avahi-core/lookup.h> 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 <config.h> #endif +#include <stdlib.h> + #include <avahi-common/timeval.h> #include <avahi-common/malloc.h> #include <avahi-common/error.h> +#include <avahi-common/domain.h> +#include <avahi-common/rlist.h> +#include <avahi-common/address.h> #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 <avahi-common/llist.h> + #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 <avahi-common/cdecl.h> @@ -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 <avahi-common/cdecl.h> +#include <avahi-common/defs.h> + +#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 <config.h> +#endif + +#include <avahi-common/malloc.h> + +#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 <avahi-common/cdecl.h> + +#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 <config.h> +#endif + +#include <stdlib.h> +#include <assert.h> + +#include <avahi-common/malloc.h> +#include <avahi-common/simple-watch.h> +#include <avahi-common/alternative.h> +#include <avahi-core/core.h> +#include <avahi-core/log.h> +#include <avahi-core/publish.h> +#include <avahi-core/lookup.h> + +#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 <config.h> +#endif + +#include <avahi-common/timeval.h> +#include <avahi-common/malloc.h> +#include <avahi-common/error.h> +#include <avahi-common/domain.h> + +#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 <avahi-common/error.h> #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 <config.h> +#endif + +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> + +#include <avahi-common/malloc.h> + +#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 + |