Initial commit

This commit is contained in:
Jonathan Bagg
2015-11-21 07:22:36 -05:00
commit 813bf77058
116 changed files with 32395 additions and 0 deletions

94
avahi-core/addr-util.c Normal file
View File

@@ -0,0 +1,94 @@
/***
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 <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <assert.h>
#include "addr-util.h"
AvahiAddress *avahi_address_from_sockaddr(const struct sockaddr* sa, AvahiAddress *ret_addr) {
assert(sa);
assert(ret_addr);
assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
ret_addr->proto = avahi_af_to_proto(sa->sa_family);
if (sa->sa_family == AF_INET)
memcpy(&ret_addr->data.ipv4, &((const struct sockaddr_in*) sa)->sin_addr, sizeof(ret_addr->data.ipv4));
else
memcpy(&ret_addr->data.ipv6, &((const struct sockaddr_in6*) sa)->sin6_addr, sizeof(ret_addr->data.ipv6));
return ret_addr;
}
uint16_t avahi_port_from_sockaddr(const struct sockaddr* sa) {
assert(sa);
assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
if (sa->sa_family == AF_INET)
return ntohs(((const struct sockaddr_in*) sa)->sin_port);
else
return ntohs(((const struct sockaddr_in6*) sa)->sin6_port);
}
int avahi_address_is_ipv4_in_ipv6(const AvahiAddress *a) {
static const uint8_t ipv4_in_ipv6[] = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF
};
assert(a);
if (a->proto != AVAHI_PROTO_INET6)
return 0;
return memcmp(a->data.ipv6.address, ipv4_in_ipv6, sizeof(ipv4_in_ipv6)) == 0;
}
#define IPV4LL_NETWORK 0xA9FE0000L
#define IPV4LL_NETMASK 0xFFFF0000L
#define IPV6LL_NETWORK 0xFE80
#define IPV6LL_NETMASK 0xFFC0
int avahi_address_is_link_local(const AvahiAddress *a) {
assert(a);
if (a->proto == AVAHI_PROTO_INET) {
uint32_t n = ntohl(a->data.ipv4.address);
return (n & IPV4LL_NETMASK) == IPV4LL_NETWORK;
}
else if (a->proto == AVAHI_PROTO_INET6) {
unsigned n = (a->data.ipv6.address[0] << 8) | (a->data.ipv6.address[1] << 0);
return (n & IPV6LL_NETMASK) == IPV6LL_NETWORK;
}
return 0;
}

47
avahi-core/addr-util.h Normal file
View File

@@ -0,0 +1,47 @@
#ifndef fooaddrutilhfoo
#define fooaddrutilhfoo
/***
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 <inttypes.h>
#include <sys/socket.h>
#include <avahi-common/cdecl.h>
#include <avahi-common/address.h>
AVAHI_C_DECL_BEGIN
/** Make an address structture of a sockaddr structure */
AvahiAddress *avahi_address_from_sockaddr(const struct sockaddr* sa, AvahiAddress *ret_addr);
/** Return the port number of a sockaddr structure (either IPv4 or IPv6) */
uint16_t avahi_port_from_sockaddr(const struct sockaddr* sa);
/** Check whether the specified IPv6 address is in fact an
* encapsulated IPv4 address, returns 1 if yes, 0 otherwise */
int avahi_address_is_ipv4_in_ipv6(const AvahiAddress *a);
/** Check whether the specified address is a link-local IPv4 or IPv6 address;
* returns 1 if yes, 0 otherwise */
int avahi_address_is_link_local(const AvahiAddress *a);
AVAHI_C_DECL_END
#endif

524
avahi-core/announce.c Normal file
View File

@@ -0,0 +1,524 @@
/***
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 <avahi-common/timeval.h>
#include <avahi-common/malloc.h>
#include "announce.h"
#include "log.h"
#include "rr-util.h"
#define AVAHI_ANNOUNCEMENT_JITTER_MSEC 250
#define AVAHI_PROBE_JITTER_MSEC 250
#define AVAHI_PROBE_INTERVAL_MSEC 250
static void remove_announcer(AvahiServer *s, AvahiAnnouncer *a) {
assert(s);
assert(a);
if (a->time_event)
avahi_time_event_free(a->time_event);
AVAHI_LLIST_REMOVE(AvahiAnnouncer, by_interface, a->interface->announcers, a);
AVAHI_LLIST_REMOVE(AvahiAnnouncer, by_entry, a->entry->announcers, a);
avahi_free(a);
}
static void elapse_announce(AvahiTimeEvent *e, void *userdata);
static void set_timeout(AvahiAnnouncer *a, const struct timeval *tv) {
assert(a);
if (!tv) {
if (a->time_event) {
avahi_time_event_free(a->time_event);
a->time_event = NULL;
}
} else {
if (a->time_event)
avahi_time_event_update(a->time_event, tv);
else
a->time_event = avahi_time_event_new(a->server->time_event_queue, tv, elapse_announce, a);
}
}
static void next_state(AvahiAnnouncer *a);
void avahi_s_entry_group_check_probed(AvahiSEntryGroup *g, int immediately) {
AvahiEntry *e;
assert(g);
assert(!g->dead);
/* Check whether all group members have been probed */
if (g->state != AVAHI_ENTRY_GROUP_REGISTERING || g->n_probing > 0)
return;
avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_ESTABLISHED);
if (g->dead)
return;
for (e = g->entries; e; e = e->by_group_next) {
AvahiAnnouncer *a;
for (a = e->announcers; a; a = a->by_entry_next) {
if (a->state != AVAHI_WAITING)
continue;
a->state = AVAHI_ANNOUNCING;
if (immediately) {
/* Shortcut */
a->n_iteration = 1;
next_state(a);
} else {
struct timeval tv;
a->n_iteration = 0;
avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC);
set_timeout(a, &tv);
}
}
}
}
static void next_state(AvahiAnnouncer *a) {
assert(a);
if (a->state == AVAHI_WAITING) {
assert(a->entry->group);
avahi_s_entry_group_check_probed(a->entry->group, 1);
} else if (a->state == AVAHI_PROBING) {
if (a->n_iteration >= 4) {
/* Probing done */
if (a->entry->group) {
assert(a->entry->group->n_probing);
a->entry->group->n_probing--;
}
if (a->entry->group && a->entry->group->state == AVAHI_ENTRY_GROUP_REGISTERING)
a->state = AVAHI_WAITING;
else {
a->state = AVAHI_ANNOUNCING;
a->n_iteration = 1;
}
set_timeout(a, NULL);
next_state(a);
} else {
struct timeval tv;
avahi_interface_post_probe(a->interface, a->entry->record, 0);
avahi_elapse_time(&tv, AVAHI_PROBE_INTERVAL_MSEC, 0);
set_timeout(a, &tv);
a->n_iteration++;
}
} else if (a->state == AVAHI_ANNOUNCING) {
if (a->entry->flags & AVAHI_PUBLISH_UNIQUE)
/* Send the whole rrset at once */
avahi_server_prepare_matching_responses(a->server, a->interface, a->entry->record->key, 0);
else
avahi_server_prepare_response(a->server, a->interface, a->entry, 0, 0);
avahi_server_generate_response(a->server, a->interface, NULL, NULL, 0, 0, 0);
if (++a->n_iteration >= 4) {
/* Announcing done */
a->state = AVAHI_ESTABLISHED;
set_timeout(a, NULL);
} else {
struct timeval tv;
avahi_elapse_time(&tv, a->sec_delay*1000, AVAHI_ANNOUNCEMENT_JITTER_MSEC);
if (a->n_iteration < 10)
a->sec_delay *= 2;
set_timeout(a, &tv);
}
}
}
static void elapse_announce(AvahiTimeEvent *e, void *userdata) {
assert(e);
next_state(userdata);
}
static AvahiAnnouncer *get_announcer(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) {
AvahiAnnouncer *a;
assert(s);
assert(e);
assert(i);
for (a = e->announcers; a; a = a->by_entry_next)
if (a->interface == i)
return a;
return NULL;
}
static void go_to_initial_state(AvahiAnnouncer *a) {
AvahiEntry *e;
struct timeval tv;
assert(a);
e = a->entry;
if ((e->flags & AVAHI_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_NO_PROBE))
a->state = AVAHI_PROBING;
else if (!(e->flags & AVAHI_PUBLISH_NO_ANNOUNCE)) {
if (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED)
a->state = AVAHI_ANNOUNCING;
else
a->state = AVAHI_WAITING;
} else
a->state = AVAHI_ESTABLISHED;
a->n_iteration = 1;
a->sec_delay = 1;
if (a->state == AVAHI_PROBING && e->group)
e->group->n_probing++;
if (a->state == AVAHI_PROBING)
set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_PROBE_JITTER_MSEC));
else if (a->state == AVAHI_ANNOUNCING)
set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC));
else
set_timeout(a, NULL);
}
static void new_announcer(AvahiServer *s, AvahiInterface *i, AvahiEntry *e) {
AvahiAnnouncer *a;
assert(s);
assert(i);
assert(e);
assert(!e->dead);
if (!avahi_interface_match(i, e->interface, e->protocol) || !i->announcing || !avahi_entry_is_commited(e))
return;
/* We don't want duplicate announcers */
if (get_announcer(s, e, i))
return;
if ((!(a = avahi_new(AvahiAnnouncer, 1)))) {
avahi_log_error(__FILE__": Out of memory.");
return;
}
a->server = s;
a->interface = i;
a->entry = e;
a->time_event = NULL;
AVAHI_LLIST_PREPEND(AvahiAnnouncer, by_interface, i->announcers, a);
AVAHI_LLIST_PREPEND(AvahiAnnouncer, by_entry, e->announcers, a);
go_to_initial_state(a);
}
void avahi_announce_interface(AvahiServer *s, AvahiInterface *i) {
AvahiEntry *e;
assert(s);
assert(i);
if (!i->announcing)
return;
for (e = s->entries; e; e = e->entries_next)
if (!e->dead)
new_announcer(s, i, e);
}
static void announce_walk_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
AvahiEntry *e = userdata;
assert(m);
assert(i);
assert(e);
assert(!e->dead);
new_announcer(m->server, i, e);
}
void avahi_announce_entry(AvahiServer *s, AvahiEntry *e) {
assert(s);
assert(e);
assert(!e->dead);
avahi_interface_monitor_walk(s->monitor, e->interface, e->protocol, announce_walk_callback, e);
}
void avahi_announce_group(AvahiServer *s, AvahiSEntryGroup *g) {
AvahiEntry *e;
assert(s);
assert(g);
for (e = g->entries; e; e = e->by_group_next)
if (!e->dead)
avahi_announce_entry(s, e);
}
int avahi_entry_is_registered(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) {
AvahiAnnouncer *a;
assert(s);
assert(e);
assert(i);
assert(!e->dead);
if (!(a = get_announcer(s, e, i)))
return 0;
return
a->state == AVAHI_ANNOUNCING ||
a->state == AVAHI_ESTABLISHED ||
(a->state == AVAHI_WAITING && !(e->flags & AVAHI_PUBLISH_UNIQUE));
}
int avahi_entry_is_probing(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) {
AvahiAnnouncer *a;
assert(s);
assert(e);
assert(i);
assert(!e->dead);
if (!(a = get_announcer(s, e, i)))
return 0;
return
a->state == AVAHI_PROBING ||
(a->state == AVAHI_WAITING && (e->flags & AVAHI_PUBLISH_UNIQUE));
}
void avahi_entry_return_to_initial_state(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) {
AvahiAnnouncer *a;
assert(s);
assert(e);
assert(i);
if (!(a = get_announcer(s, e, i)))
return;
if (a->state == AVAHI_PROBING && a->entry->group)
a->entry->group->n_probing--;
go_to_initial_state(a);
}
static AvahiRecord *make_goodbye_record(AvahiRecord *r) {
AvahiRecord *g;
assert(r);
if (!(g = avahi_record_copy(r)))
return NULL; /* OOM */
assert(g->ref == 1);
g->ttl = 0;
return g;
}
static int is_duplicate_entry(AvahiServer *s, AvahiEntry *e) {
AvahiEntry *i;
assert(s);
assert(e);
for (i = avahi_hashmap_lookup(s->entries_by_key, e->record->key); i; i = i->by_key_next) {
if ((i == e) || (i->dead))
continue;
if (!avahi_record_equal_no_ttl(i->record, e->record))
continue;
return 1;
}
return 0;
}
static void send_goodbye_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
AvahiEntry *e = userdata;
AvahiRecord *g;
assert(m);
assert(i);
assert(e);
assert(!e->dead);
if (!avahi_interface_match(i, e->interface, e->protocol))
return;
if (e->flags & AVAHI_PUBLISH_NO_ANNOUNCE)
return;
if (!avahi_entry_is_registered(m->server, e, i))
return;
if (is_duplicate_entry(m->server, e))
return;
if (!(g = make_goodbye_record(e->record)))
return; /* OOM */
avahi_interface_post_response(i, g, e->flags & AVAHI_PUBLISH_UNIQUE, NULL, 1);
avahi_record_unref(g);
}
static void reannounce(AvahiAnnouncer *a) {
AvahiEntry *e;
struct timeval tv;
assert(a);
e = a->entry;
/* If the group this entry belongs to is not even commited, there's nothing to reannounce */
if (e->group && (e->group->state == AVAHI_ENTRY_GROUP_UNCOMMITED || e->group->state == AVAHI_ENTRY_GROUP_COLLISION))
return;
/* Because we might change state we decrease the probing counter first */
if (a->state == AVAHI_PROBING && a->entry->group)
a->entry->group->n_probing--;
if (a->state == AVAHI_PROBING ||
(a->state == AVAHI_WAITING && (e->flags & AVAHI_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_NO_PROBE)))
/* We were probing or waiting after probe, so we restart probing from the beginning here */
a->state = AVAHI_PROBING;
else if (a->state == AVAHI_WAITING)
/* We were waiting, but were not probing before, so we continue waiting */
a->state = AVAHI_WAITING;
else if (e->flags & AVAHI_PUBLISH_NO_ANNOUNCE)
/* No announcer needed */
a->state = AVAHI_ESTABLISHED;
else {
/* Ok, let's restart announcing */
a->state = AVAHI_ANNOUNCING;
}
/* Now let's increase the probing counter again */
if (a->state == AVAHI_PROBING && e->group)
e->group->n_probing++;
a->n_iteration = 1;
a->sec_delay = 1;
if (a->state == AVAHI_PROBING)
set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_PROBE_JITTER_MSEC));
else if (a->state == AVAHI_ANNOUNCING)
set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC));
else
set_timeout(a, NULL);
}
static void reannounce_walk_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
AvahiEntry *e = userdata;
AvahiAnnouncer *a;
assert(m);
assert(i);
assert(e);
assert(!e->dead);
if (!(a = get_announcer(m->server, e, i)))
return;
reannounce(a);
}
void avahi_reannounce_entry(AvahiServer *s, AvahiEntry *e) {
assert(s);
assert(e);
assert(!e->dead);
avahi_interface_monitor_walk(s->monitor, e->interface, e->protocol, reannounce_walk_callback, e);
}
void avahi_goodbye_interface(AvahiServer *s, AvahiInterface *i, int send_goodbye, int remove) {
assert(s);
assert(i);
if (send_goodbye)
if (i->announcing) {
AvahiEntry *e;
for (e = s->entries; e; e = e->entries_next)
if (!e->dead)
send_goodbye_callback(s->monitor, i, e);
}
if (remove)
while (i->announcers)
remove_announcer(s, i->announcers);
}
void avahi_goodbye_entry(AvahiServer *s, AvahiEntry *e, int send_goodbye, int remove) {
assert(s);
assert(e);
if (send_goodbye)
if (!e->dead)
avahi_interface_monitor_walk(s->monitor, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, send_goodbye_callback, e);
if (remove)
while (e->announcers)
remove_announcer(s, e->announcers);
}

69
avahi-core/announce.h Normal file
View File

@@ -0,0 +1,69 @@
#ifndef fooannouncehfoo
#define fooannouncehfoo
/***
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 AvahiAnnouncer AvahiAnnouncer;
#include <avahi-common/llist.h>
#include "iface.h"
#include "internal.h"
#include "timeeventq.h"
#include "publish.h"
typedef enum {
AVAHI_PROBING, /* probing phase */
AVAHI_WAITING, /* wait for other records in group */
AVAHI_ANNOUNCING, /* announcing phase */
AVAHI_ESTABLISHED /* we'e established */
} AvahiAnnouncerState;
struct AvahiAnnouncer {
AvahiServer *server;
AvahiInterface *interface;
AvahiEntry *entry;
AvahiTimeEvent *time_event;
AvahiAnnouncerState state;
unsigned n_iteration;
unsigned sec_delay;
AVAHI_LLIST_FIELDS(AvahiAnnouncer, by_interface);
AVAHI_LLIST_FIELDS(AvahiAnnouncer, by_entry);
};
void avahi_announce_interface(AvahiServer *s, AvahiInterface *i);
void avahi_announce_entry(AvahiServer *s, AvahiEntry *e);
void avahi_announce_group(AvahiServer *s, AvahiSEntryGroup *g);
void avahi_entry_return_to_initial_state(AvahiServer *s, AvahiEntry *e, AvahiInterface *i);
void avahi_s_entry_group_check_probed(AvahiSEntryGroup *g, int immediately);
int avahi_entry_is_registered(AvahiServer *s, AvahiEntry *e, AvahiInterface *i);
int avahi_entry_is_probing(AvahiServer *s, AvahiEntry *e, AvahiInterface *i);
void avahi_goodbye_interface(AvahiServer *s, AvahiInterface *i, int send_goodbye, int rem);
void avahi_goodbye_entry(AvahiServer *s, AvahiEntry *e, int send_goodbye, int rem);
void avahi_reannounce_entry(AvahiServer *s, AvahiEntry *e);
#endif

View File

@@ -0,0 +1,322 @@
/***
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 <string.h>
#include <avahi-common/domain.h>
#include <avahi-common/malloc.h>
#include <avahi-common/error.h>
#include "browse.h"
#include "log.h"
#include "rr.h"
typedef struct AvahiDNSServerInfo AvahiDNSServerInfo;
struct AvahiDNSServerInfo {
AvahiSDNSServerBrowser *browser;
AvahiIfIndex interface;
AvahiProtocol protocol;
AvahiRecord *srv_record;
AvahiSHostNameResolver *host_name_resolver;
AvahiAddress address;
AvahiLookupResultFlags flags;
AVAHI_LLIST_FIELDS(AvahiDNSServerInfo, info);
};
struct AvahiSDNSServerBrowser {
AvahiServer *server;
AvahiSRecordBrowser *record_browser;
AvahiSDNSServerBrowserCallback callback;
void* userdata;
AvahiProtocol aprotocol;
AvahiLookupFlags user_flags;
unsigned n_info;
AVAHI_LLIST_FIELDS(AvahiSDNSServerBrowser, browser);
AVAHI_LLIST_HEAD(AvahiDNSServerInfo, info);
};
static AvahiDNSServerInfo* get_server_info(AvahiSDNSServerBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r) {
AvahiDNSServerInfo *i;
assert(b);
assert(r);
for (i = b->info; i; i = i->info_next)
if (i->interface == interface &&
i->protocol == protocol &&
avahi_record_equal_no_ttl(r, i->srv_record))
return i;
return NULL;
}
static void server_info_free(AvahiSDNSServerBrowser *b, AvahiDNSServerInfo *i) {
assert(b);
assert(i);
avahi_record_unref(i->srv_record);
if (i->host_name_resolver)
avahi_s_host_name_resolver_free(i->host_name_resolver);
AVAHI_LLIST_REMOVE(AvahiDNSServerInfo, info, b->info, i);
assert(b->n_info >= 1);
b->n_info--;
avahi_free(i);
}
static void host_name_resolver_callback(
AvahiSHostNameResolver *r,
AVAHI_GCC_UNUSED AvahiIfIndex interface,
AVAHI_GCC_UNUSED 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);
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_FAILURE:
/* 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,
AvahiLookupResultFlags flags,
void* userdata) {
AvahiSDNSServerBrowser *b = userdata;
assert(rr);
assert(b);
/* Filter flags */
flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA;
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_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,
AvahiLookupFlags flags,
AvahiSDNSServerBrowserCallback callback,
void* userdata) {
static const char * const type_table[AVAHI_DNS_SERVER_MAX] = {
"_domain._udp",
"_dns-update._udp"
};
AvahiSDNSServerBrowser *b;
AvahiKey *k = NULL;
char n[AVAHI_DOMAIN_NAME_MAX];
int r;
assert(server);
assert(callback);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(aprotocol), AVAHI_ERR_INVALID_PROTOCOL);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, type < AVAHI_DNS_SERVER_MAX, AVAHI_ERR_INVALID_FLAGS);
if (!domain)
domain = server->domain_name;
if ((r = avahi_service_name_join(n, sizeof(n), NULL, type_table[type], domain)) < 0) {
avahi_server_set_errno(server, r);
return NULL;
}
if (!(b = avahi_new(AvahiSDNSServerBrowser, 1))) {
avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
return NULL;
}
b->server = server;
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);
if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV))) {
avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
goto fail;
}
if (!(b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b)))
goto fail;
avahi_key_unref(k);
return b;
fail:
if (k)
avahi_key_unref(k);
avahi_s_dns_server_browser_free(b);
return NULL;
}
void avahi_s_dns_server_browser_free(AvahiSDNSServerBrowser *b) {
assert(b);
while (b->info)
server_info_free(b, b->info);
AVAHI_LLIST_REMOVE(AvahiSDNSServerBrowser, browser, b->server->dns_server_browsers, b);
if (b->record_browser)
avahi_s_record_browser_free(b->record_browser);
avahi_free(b);
}

235
avahi-core/browse-domain.c Normal file
View File

@@ -0,0 +1,235 @@
/***
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 <avahi-common/domain.h>
#include <avahi-common/malloc.h>
#include <avahi-common/error.h>
#include "browse.h"
#include "log.h"
struct AvahiSDomainBrowser {
int ref;
AvahiServer *server;
AvahiSRecordBrowser *record_browser;
AvahiDomainBrowserType type;
AvahiSDomainBrowserCallback callback;
void* userdata;
AvahiTimeEvent *defer_event;
int all_for_now_scheduled;
AVAHI_LLIST_FIELDS(AvahiSDomainBrowser, browser);
};
static void inc_ref(AvahiSDomainBrowser *b) {
assert(b);
assert(b->ref >= 1);
b->ref++;
}
static void record_browser_callback(
AvahiSRecordBrowser*rr,
AvahiIfIndex interface,
AvahiProtocol protocol,
AvahiBrowserEvent event,
AvahiRecord *record,
AvahiLookupResultFlags flags,
void* userdata) {
AvahiSDomainBrowser *b = userdata;
char *n = NULL;
assert(rr);
assert(b);
if (event == AVAHI_BROWSER_ALL_FOR_NOW &&
b->defer_event) {
b->all_for_now_scheduled = 1;
return;
}
/* Filter flags */
flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA;
if (record) {
assert(record->key->type == AVAHI_DNS_TYPE_PTR);
n = record->data.ptr.name;
if (b->type == AVAHI_DOMAIN_BROWSER_BROWSE) {
AvahiStringList *l;
/* Filter out entries defined statically */
for (l = b->server->config.browse_domains; l; l = l->next)
if (avahi_domain_equal((char*) l->text, n))
return;
}
}
b->callback(b, interface, protocol, event, n, flags, b->userdata);
}
static void defer_callback(AvahiTimeEvent *e, void *userdata) {
AvahiSDomainBrowser *b = userdata;
AvahiStringList *l;
assert(e);
assert(b);
assert(b->type == AVAHI_DOMAIN_BROWSER_BROWSE);
avahi_time_event_free(b->defer_event);
b->defer_event = NULL;
/* Increase ref counter */
inc_ref(b);
for (l = b->server->config.browse_domains; l; l = l->next) {
/* Check whether this object still exists outside our own
* stack frame */
if (b->ref <= 1)
break;
b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_BROWSER_NEW, (char*) l->text, AVAHI_LOOKUP_RESULT_STATIC, b->userdata);
}
if (b->ref > 1) {
/* If the ALL_FOR_NOW event has already been scheduled, execute it now */
if (b->all_for_now_scheduled)
b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_BROWSER_ALL_FOR_NOW, NULL, 0, b->userdata);
}
/* Decrease ref counter */
avahi_s_domain_browser_free(b);
}
AvahiSDomainBrowser *avahi_s_domain_browser_new(
AvahiServer *server,
AvahiIfIndex interface,
AvahiProtocol protocol,
const char *domain,
AvahiDomainBrowserType type,
AvahiLookupFlags flags,
AvahiSDomainBrowserCallback callback,
void* userdata) {
static const char * const type_table[AVAHI_DOMAIN_BROWSER_MAX] = {
"b",
"db",
"r",
"dr",
"lb"
};
AvahiSDomainBrowser *b;
AvahiKey *k = NULL;
char n[AVAHI_DOMAIN_NAME_MAX];
int r;
assert(server);
assert(callback);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, type < AVAHI_DOMAIN_BROWSER_MAX, AVAHI_ERR_INVALID_FLAGS);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
if (!domain)
domain = server->domain_name;
if ((r = avahi_service_name_join(n, sizeof(n), type_table[type], "_dns-sd._udp", domain)) < 0) {
avahi_server_set_errno(server, r);
return NULL;
}
if (!(b = avahi_new(AvahiSDomainBrowser, 1))) {
avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
return NULL;
}
b->ref = 1;
b->server = server;
b->callback = callback;
b->userdata = userdata;
b->record_browser = NULL;
b->type = type;
b->all_for_now_scheduled = 0;
b->defer_event = NULL;
AVAHI_LLIST_PREPEND(AvahiSDomainBrowser, browser, server->domain_browsers, b);
if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) {
avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
goto fail;
}
if (!(b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b)))
goto fail;
avahi_key_unref(k);
if (type == AVAHI_DOMAIN_BROWSER_BROWSE && b->server->config.browse_domains)
b->defer_event = avahi_time_event_new(server->time_event_queue, NULL, defer_callback, b);
return b;
fail:
if (k)
avahi_key_unref(k);
avahi_s_domain_browser_free(b);
return NULL;
}
void avahi_s_domain_browser_free(AvahiSDomainBrowser *b) {
assert(b);
assert(b->ref >= 1);
if (--b->ref > 0)
return;
AVAHI_LLIST_REMOVE(AvahiSDomainBrowser, browser, b->server->domain_browsers, b);
if (b->record_browser)
avahi_s_record_browser_free(b->record_browser);
if (b->defer_event)
avahi_time_event_free(b->defer_event);
avahi_free(b);
}

View File

@@ -0,0 +1,157 @@
/***
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 <string.h>
#include <avahi-common/domain.h>
#include <avahi-common/malloc.h>
#include <avahi-common/error.h>
#include "browse.h"
#include "log.h"
struct AvahiSServiceTypeBrowser {
AvahiServer *server;
char *domain_name;
AvahiSRecordBrowser *record_browser;
AvahiSServiceTypeBrowserCallback callback;
void* userdata;
AVAHI_LLIST_FIELDS(AvahiSServiceTypeBrowser, browser);
};
static void record_browser_callback(
AvahiSRecordBrowser*rr,
AvahiIfIndex interface,
AvahiProtocol protocol,
AvahiBrowserEvent event,
AvahiRecord *record,
AvahiLookupResultFlags flags,
void* userdata) {
AvahiSServiceTypeBrowser *b = userdata;
assert(rr);
assert(b);
/* Filter flags */
flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA;
if (record) {
char type[AVAHI_DOMAIN_NAME_MAX], domain[AVAHI_DOMAIN_NAME_MAX];
assert(record->key->type == AVAHI_DNS_TYPE_PTR);
if (avahi_service_name_split(record->data.ptr.name, NULL, 0, type, sizeof(type), domain, sizeof(domain)) < 0) {
avahi_log_warn("Invalid service type '%s'", record->key->name);
return;
}
b->callback(b, interface, protocol, event, type, domain, flags, b->userdata);
} else
b->callback(b, interface, protocol, event, NULL, b->domain_name, flags, b->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 = NULL;
char n[AVAHI_DOMAIN_NAME_MAX];
int r;
assert(server);
assert(callback);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
if (!domain)
domain = server->domain_name;
if ((r = avahi_service_name_join(n, sizeof(n), NULL, "_services._dns-sd._udp", domain)) < 0) {
avahi_server_set_errno(server, r);
return NULL;
}
if (!(b = avahi_new(AvahiSServiceTypeBrowser, 1))) {
avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
return NULL;
}
b->server = server;
b->callback = callback;
b->userdata = userdata;
b->record_browser = NULL;
AVAHI_LLIST_PREPEND(AvahiSServiceTypeBrowser, browser, server->service_type_browsers, b);
if (!(b->domain_name = avahi_normalize_name_strdup(domain))) {
avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
goto fail;
}
if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) {
avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
goto fail;
}
if (!(b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b)))
goto fail;
avahi_key_unref(k);
return b;
fail:
if (k)
avahi_key_unref(k);
avahi_s_service_type_browser_free(b);
return NULL;
}
void avahi_s_service_type_browser_free(AvahiSServiceTypeBrowser *b) {
assert(b);
AVAHI_LLIST_REMOVE(AvahiSServiceTypeBrowser, browser, b->server->service_type_browsers, b);
if (b->record_browser)
avahi_s_record_browser_free(b->record_browser);
avahi_free(b->domain_name);
avahi_free(b);
}

167
avahi-core/browse-service.c Normal file
View File

@@ -0,0 +1,167 @@
/***
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 <string.h>
#include <avahi-common/domain.h>
#include <avahi-common/malloc.h>
#include <avahi-common/error.h>
#include "browse.h"
#include "log.h"
struct AvahiSServiceBrowser {
AvahiServer *server;
char *domain_name;
char *service_type;
AvahiSRecordBrowser *record_browser;
AvahiSServiceBrowserCallback callback;
void* userdata;
AVAHI_LLIST_FIELDS(AvahiSServiceBrowser, browser);
};
static void record_browser_callback(
AvahiSRecordBrowser*rr,
AvahiIfIndex interface,
AvahiProtocol protocol,
AvahiBrowserEvent event,
AvahiRecord *record,
AvahiLookupResultFlags flags,
void* userdata) {
AvahiSServiceBrowser *b = userdata;
assert(rr);
assert(b);
/* Filter flags */
flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA;
if (record) {
char service[AVAHI_LABEL_MAX], type[AVAHI_DOMAIN_NAME_MAX], domain[AVAHI_DOMAIN_NAME_MAX];
assert(record->key->type == AVAHI_DNS_TYPE_PTR);
if (event == AVAHI_BROWSER_NEW && avahi_server_is_service_local(b->server, interface, protocol, record->data.ptr.name))
flags |= AVAHI_LOOKUP_RESULT_LOCAL;
if (avahi_service_name_split(record->data.ptr.name, service, sizeof(service), type, sizeof(type), domain, sizeof(domain)) < 0) {
avahi_log_warn("Failed to split '%s'", record->key->name);
return;
}
b->callback(b, interface, protocol, event, service, type, domain, flags, b->userdata);
} else
b->callback(b, interface, protocol, event, NULL, b->service_type, b->domain_name, flags, b->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 = NULL;
char n[AVAHI_DOMAIN_NAME_MAX];
int r;
assert(server);
assert(callback);
assert(service_type);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_is_valid_service_type_generic(service_type), AVAHI_ERR_INVALID_SERVICE_TYPE);
if (!domain)
domain = server->domain_name;
if ((r = avahi_service_name_join(n, sizeof(n), NULL, service_type, domain)) < 0) {
avahi_server_set_errno(server, r);
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 = b->service_type = NULL;
b->callback = callback;
b->userdata = userdata;
b->record_browser = NULL;
AVAHI_LLIST_PREPEND(AvahiSServiceBrowser, browser, server->service_browsers, b);
if (!(b->domain_name = avahi_normalize_name_strdup(domain)) ||
!(b->service_type = avahi_normalize_name_strdup(service_type))) {
avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
goto fail;
}
if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) {
avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
goto fail;
}
if (!(b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b)))
goto fail;
avahi_key_unref(k);
return b;
fail:
if (k)
avahi_key_unref(k);
avahi_s_service_browser_free(b);
return NULL;
}
void avahi_s_service_browser_free(AvahiSServiceBrowser *b) {
assert(b);
AVAHI_LLIST_REMOVE(AvahiSServiceBrowser, browser, b->server->service_browsers, b);
if (b->record_browser)
avahi_s_record_browser_free(b->record_browser);
avahi_free(b->domain_name);
avahi_free(b->service_type);
avahi_free(b);
}

613
avahi-core/browse.c Normal file
View File

@@ -0,0 +1,613 @@
/***
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 <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"
#include "domain-util.h"
#include "rr-util.h"
#define AVAHI_LOOKUPS_PER_BROWSER_MAX 15
struct AvahiSRBLookup {
AvahiSRecordBrowser *record_browser;
unsigned ref;
AvahiIfIndex interface;
AvahiProtocol protocol;
AvahiLookupFlags flags;
AvahiKey *key;
AvahiWideAreaLookup *wide_area;
AvahiMulticastLookup *multicast;
AvahiRList *cname_lookups;
AVAHI_LLIST_FIELDS(AvahiSRBLookup, lookups);
};
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(b);
assert(AVAHI_IF_VALID(interface));
assert(AVAHI_PROTO_VALID(protocol));
if (b->n_lookups >= AVAHI_LOOKUPS_PER_BROWSER_MAX)
/* 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);
b->n_lookups ++;
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;
}
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_key_unref(l->key);
avahi_free(l);
}
static AvahiSRBLookup* lookup_ref(AvahiSRBLookup *l) {
assert(l);
assert(l->ref >= 1);
l->ref++;
return l;
}
static AvahiSRBLookup *lookup_find(
AvahiSRecordBrowser *b,
AvahiIfIndex interface,
AvahiProtocol protocol,
AvahiLookupFlags flags,
AvahiKey *key) {
AvahiSRBLookup *l;
assert(b);
for (l = b->lookups; l; l = l->lookups_next) {
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 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;
}
}
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);
assert(l->ref >= 1);
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_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 */
if (avahi_server_is_record_local(b->server, interface, protocol, r))
flags |= AVAHI_LOOKUP_RESULT_LOCAL;
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_FAILURE:
/* Not defined for multicast DNS */
abort();
}
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) {
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 ((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 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);
lookup_unref(n);
}
static void lookup_drop_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r) {
AvahiKey *k;
AvahiSRBLookup *n = NULL;
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(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void *userdata) {
AvahiSRecordBrowser *b = userdata;
int n;
assert(b);
assert(!b->dead);
/* 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->dead)
return;
if (n < 0) {
/* sending of the initial query failed */
avahi_server_set_errno(b->server, AVAHI_ERR_FAILURE);
b->callback(
b, b->interface, b->protocol, AVAHI_BROWSER_FAILURE, NULL,
b->flags & AVAHI_LOOKUP_USE_WIDE_AREA ? AVAHI_LOOKUP_RESULT_WIDE_AREA : AVAHI_LOOKUP_RESULT_MULTICAST,
b->userdata);
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_RESULT_WIDE_AREA : AVAHI_LOOKUP_RESULT_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_RESULT_WIDE_AREA, b->userdata);
}
}
void avahi_s_record_browser_restart(AvahiSRecordBrowser *b) {
assert(b);
assert(!b->dead);
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);
}
}
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);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !avahi_key_is_pattern(key), AVAHI_ERR_IS_PATTERN);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_key_is_valid(key), AVAHI_ERR_INVALID_KEY);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
AVAHI_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);
return NULL;
}
b->dead = 0;
b->server = server;
b->interface = interface;
b->protocol = protocol;
b->key = avahi_key_ref(key);
b->flags = flags;
b->callback = callback;
b->userdata = userdata;
b->n_lookups = 0;
AVAHI_LLIST_HEAD_INIT(AvahiSRBLookup, b->lookups);
b->root_lookup = NULL;
AVAHI_LLIST_PREPEND(AvahiSRecordBrowser, browser, server->record_browsers, b);
/* 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_free(AvahiSRecordBrowser *b) {
assert(b);
assert(!b->dead);
b->dead = 1;
b->server->need_browser_cleanup = 1;
browser_cancel(b);
}
void avahi_s_record_browser_destroy(AvahiSRecordBrowser *b) {
assert(b);
browser_cancel(b);
AVAHI_LLIST_REMOVE(AvahiSRecordBrowser, browser, b->server->record_browsers, b);
avahi_key_unref(b->key);
avahi_free(b);
}
void avahi_browser_cleanup(AvahiServer *server) {
AvahiSRecordBrowser *b;
AvahiSRecordBrowser *n;
assert(server);
while (server->need_browser_cleanup) {
server->need_browser_cleanup = 0;
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);
}

60
avahi-core/browse.h Normal file
View File

@@ -0,0 +1,60 @@
#ifndef foobrowsehfoo
#define foobrowsehfoo
/***
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 <avahi-common/llist.h>
#include "core.h"
#include "timeeventq.h"
#include "internal.h"
#include "dns.h"
#include "lookup.h"
typedef struct AvahiSRBLookup AvahiSRBLookup;
struct AvahiSRecordBrowser {
AVAHI_LLIST_FIELDS(AvahiSRecordBrowser, browser);
int dead;
AvahiServer *server;
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);
#endif

513
avahi-core/cache.c Normal file
View File

@@ -0,0 +1,513 @@
/***
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 <string.h>
#include <stdlib.h>
#include <time.h>
#include <avahi-common/timeval.h>
#include <avahi-common/malloc.h>
#include "cache.h"
#include "log.h"
#include "rr-util.h"
static void remove_entry(AvahiCache *c, AvahiCacheEntry *e) {
AvahiCacheEntry *t;
assert(c);
assert(e);
/* avahi_log_debug("removing from cache: %p %p", c, e); */
/* Remove from hash table */
t = avahi_hashmap_lookup(c->hashmap, e->record->key);
AVAHI_LLIST_REMOVE(AvahiCacheEntry, by_key, t, e);
if (t)
avahi_hashmap_replace(c->hashmap, t->record->key, t);
else
avahi_hashmap_remove(c->hashmap, e->record->key);
/* Remove from linked list */
AVAHI_LLIST_REMOVE(AvahiCacheEntry, entry, c->entries, e);
if (e->time_event)
avahi_time_event_free(e->time_event);
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);
assert(c->n_entries >= 1);
--c->n_entries;
}
AvahiCache *avahi_cache_new(AvahiServer *server, AvahiInterface *iface) {
AvahiCache *c;
assert(server);
if (!(c = avahi_new(AvahiCache, 1))) {
avahi_log_error(__FILE__": Out of memory.");
return NULL; /* OOM */
}
c->server = server;
c->interface = iface;
if (!(c->hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL))) {
avahi_log_error(__FILE__": Out of memory.");
avahi_free(c);
return NULL; /* OOM */
}
AVAHI_LLIST_HEAD_INIT(AvahiCacheEntry, c->entries);
c->n_entries = 0;
c->last_rand_timestamp = 0;
return c;
}
void avahi_cache_free(AvahiCache *c) {
assert(c);
while (c->entries)
remove_entry(c, c->entries);
assert(c->n_entries == 0);
avahi_hashmap_free(c->hashmap);
avahi_free(c);
}
static AvahiCacheEntry *lookup_key(AvahiCache *c, AvahiKey *k) {
assert(c);
assert(k);
assert(!avahi_key_is_pattern(k));
return avahi_hashmap_lookup(c->hashmap, k);
}
void* avahi_cache_walk(AvahiCache *c, AvahiKey *pattern, AvahiCacheWalkCallback cb, void* userdata) {
void* ret;
assert(c);
assert(pattern);
assert(cb);
if (avahi_key_is_pattern(pattern)) {
AvahiCacheEntry *e, *n;
for (e = c->entries; e; e = n) {
n = e->entry_next;
if (avahi_key_pattern_match(pattern, e->record->key))
if ((ret = cb(c, pattern, e, userdata)))
return ret;
}
} else {
AvahiCacheEntry *e, *n;
for (e = lookup_key(c, pattern); e; e = n) {
n = e->by_key_next;
if ((ret = cb(c, pattern, e, userdata)))
return ret;
}
}
return NULL;
}
static void* lookup_record_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void *userdata) {
assert(c);
assert(pattern);
assert(e);
if (avahi_record_equal_no_ttl(e->record, userdata))
return e;
return NULL;
}
static AvahiCacheEntry *lookup_record(AvahiCache *c, AvahiRecord *r) {
assert(c);
assert(r);
return avahi_cache_walk(c, r->key, lookup_record_callback, r);
}
static void next_expiry(AvahiCache *c, AvahiCacheEntry *e, unsigned percent);
static void elapse_func(AvahiTimeEvent *t, void *userdata) {
AvahiCacheEntry *e = userdata;
/* char *txt; */
unsigned percent = 0;
assert(t);
assert(e);
/* txt = avahi_record_to_string(e->record); */
switch (e->state) {
case AVAHI_CACHE_EXPIRY_FINAL:
case AVAHI_CACHE_POOF_FINAL:
case AVAHI_CACHE_GOODBYE_FINAL:
case AVAHI_CACHE_REPLACE_FINAL:
remove_entry(e->cache, e);
e = NULL;
/* avahi_log_debug("Removing entry from cache due to expiration (%s)", txt); */
break;
case AVAHI_CACHE_VALID:
case AVAHI_CACHE_POOF:
e->state = AVAHI_CACHE_EXPIRY1;
percent = 85;
break;
case AVAHI_CACHE_EXPIRY1:
e->state = AVAHI_CACHE_EXPIRY2;
percent = 90;
break;
case AVAHI_CACHE_EXPIRY2:
e->state = AVAHI_CACHE_EXPIRY3;
percent = 95;
break;
case AVAHI_CACHE_EXPIRY3:
e->state = AVAHI_CACHE_EXPIRY_FINAL;
percent = 100;
break;
}
if (e) {
assert(percent > 0);
/* Request a cache update if we are subscribed to this entry */
if (avahi_querier_shall_refresh_cache(e->cache->interface, e->record->key))
avahi_interface_post_query(e->cache->interface, e->record->key, 0, NULL);
/* Check again later */
next_expiry(e->cache, e, percent);
}
/* avahi_free(txt); */
}
static void update_time_event(AvahiCache *c, AvahiCacheEntry *e) {
assert(c);
assert(e);
if (e->time_event)
avahi_time_event_update(e->time_event, &e->expiry);
else
e->time_event = avahi_time_event_new(c->server->time_event_queue, &e->expiry, elapse_func, e);
}
static void next_expiry(AvahiCache *c, AvahiCacheEntry *e, unsigned percent) {
AvahiUsec usec, left, right;
time_t now;
assert(c);
assert(e);
assert(percent > 0 && percent <= 100);
usec = (AvahiUsec) e->record->ttl * 10000;
left = usec * percent;
right = usec * (percent+2); /* 2% jitter */
now = time(NULL);
if (now >= c->last_rand_timestamp + 10) {
c->last_rand = rand();
c->last_rand_timestamp = now;
}
usec = left + (AvahiUsec) ((double) (right-left) * c->last_rand / (RAND_MAX+1.0));
e->expiry = e->timestamp;
avahi_timeval_add(&e->expiry, usec);
/* g_message("wake up in +%lu seconds", e->expiry.tv_sec - e->timestamp.tv_sec); */
update_time_event(c, e);
}
static void expire_in_one_second(AvahiCache *c, AvahiCacheEntry *e, AvahiCacheEntryState state) {
assert(c);
assert(e);
e->state = state;
gettimeofday(&e->expiry, NULL);
avahi_timeval_add(&e->expiry, 1000000); /* 1s */
update_time_event(c, e);
}
void avahi_cache_update(AvahiCache *c, AvahiRecord *r, int cache_flush, const AvahiAddress *a) {
/* char *txt; */
assert(c);
assert(r && r->ref >= 1);
/* txt = avahi_record_to_string(r); */
if (r->ttl == 0) {
/* This is a goodbye request */
AvahiCacheEntry *e;
if ((e = lookup_record(c, r)))
expire_in_one_second(c, e, AVAHI_CACHE_GOODBYE_FINAL);
} else {
AvahiCacheEntry *e = NULL, *first;
struct timeval now;
gettimeofday(&now, NULL);
/* This is an update request */
if ((first = lookup_key(c, r->key))) {
if (cache_flush) {
/* For unique entries drop all entries older than one second */
for (e = first; e; e = e->by_key_next) {
AvahiUsec t;
t = avahi_timeval_diff(&now, &e->timestamp);
if (t > 1000000)
expire_in_one_second(c, e, AVAHI_CACHE_REPLACE_FINAL);
}
}
/* Look for exactly the same entry */
for (e = first; e; e = e->by_key_next)
if (avahi_record_equal_no_ttl(e->record, r))
break;
}
if (e) {
/* avahi_log_debug("found matching cache entry"); */
/* We need to update the hash table key if we replace the
* record */
if (e->by_key_prev == NULL)
avahi_hashmap_replace(c->hashmap, r->key, e);
/* Update the record */
avahi_record_unref(e->record);
e->record = avahi_record_ref(r);
/* avahi_log_debug("cache: updating %s", txt); */
} else {
/* No entry found, therefore we create a new one */
/* avahi_log_debug("cache: couldn't find matching cache entry for %s", txt); */
if (c->n_entries >= c->server->config.n_cache_entries_max)
return;
if (!(e = avahi_new(AvahiCacheEntry, 1))) {
avahi_log_error(__FILE__": Out of memory");
return;
}
e->cache = c;
e->time_event = NULL;
e->record = avahi_record_ref(r);
/* Append to hash table */
AVAHI_LLIST_PREPEND(AvahiCacheEntry, by_key, first, e);
avahi_hashmap_replace(c->hashmap, e->record->key, first);
/* Append to linked list */
AVAHI_LLIST_PREPEND(AvahiCacheEntry, entry, c->entries, e);
c->n_entries++;
/* Notify subscribers */
avahi_multicast_lookup_engine_notify(c->server->multicast_lookup_engine, c->interface, e->record, AVAHI_BROWSER_NEW);
}
e->origin = *a;
e->timestamp = now;
next_expiry(c, e, 80);
e->state = AVAHI_CACHE_VALID;
e->cache_flush = cache_flush;
}
/* avahi_free(txt); */
}
struct dump_data {
AvahiDumpCallback callback;
void* userdata;
};
static void dump_callback(void* key, void* data, void* userdata) {
AvahiCacheEntry *e = data;
AvahiKey *k = key;
struct dump_data *dump_data = userdata;
assert(k);
assert(e);
assert(data);
for (; e; e = e->by_key_next) {
char *t;
if (!(t = avahi_record_to_string(e->record)))
continue; /* OOM */
dump_data->callback(t, dump_data->userdata);
avahi_free(t);
}
}
int avahi_cache_dump(AvahiCache *c, AvahiDumpCallback callback, void* userdata) {
struct dump_data data;
assert(c);
assert(callback);
callback(";;; CACHE DUMP FOLLOWS ;;;", userdata);
data.callback = callback;
data.userdata = userdata;
avahi_hashmap_foreach(c->hashmap, dump_callback, &data);
return 0;
}
int avahi_cache_entry_half_ttl(AvahiCache *c, AvahiCacheEntry *e) {
struct timeval now;
unsigned age;
assert(c);
assert(e);
gettimeofday(&now, NULL);
age = (unsigned) (avahi_timeval_diff(&now, &e->timestamp)/1000000);
/* avahi_log_debug("age: %lli, ttl/2: %u", age, e->record->ttl); */
return age >= e->record->ttl/2;
}
void avahi_cache_flush(AvahiCache *c) {
assert(c);
while (c->entries)
remove_entry(c, c->entries);
}
/*** Passive observation of failure ***/
static void* start_poof_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void *userdata) {
AvahiAddress *a = userdata;
struct timeval now;
assert(c);
assert(pattern);
assert(e);
assert(a);
gettimeofday(&now, NULL);
switch (e->state) {
case AVAHI_CACHE_VALID:
/* The entry was perfectly valid till, now, so let's enter
* POOF mode */
e->state = AVAHI_CACHE_POOF;
e->poof_address = *a;
e->poof_timestamp = now;
e->poof_num = 0;
break;
case AVAHI_CACHE_POOF:
if (avahi_timeval_diff(&now, &e->poof_timestamp) < 1000000)
break;
e->poof_timestamp = now;
e->poof_address = *a;
e->poof_num ++;
/* This is the 4th time we got no response, so let's
* fucking remove this entry. */
if (e->poof_num > 3)
expire_in_one_second(c, e, AVAHI_CACHE_POOF_FINAL);
break;
default:
;
}
return NULL;
}
void avahi_cache_start_poof(AvahiCache *c, AvahiKey *key, const AvahiAddress *a) {
assert(c);
assert(key);
avahi_cache_walk(c, key, start_poof_callback, (void*) a);
}
void avahi_cache_stop_poof(AvahiCache *c, AvahiRecord *record, const AvahiAddress *a) {
AvahiCacheEntry *e;
assert(c);
assert(record);
assert(a);
if (!(e = lookup_record(c, record)))
return;
/* This function is called for each response suppression
record. If the matching cache entry is in POOF state and the
query address is the same, we put it back into valid mode */
if (e->state == AVAHI_CACHE_POOF || e->state == AVAHI_CACHE_POOF_FINAL)
if (avahi_address_cmp(a, &e->poof_address) == 0) {
e->state = AVAHI_CACHE_VALID;
next_expiry(c, e, 80);
}
}

101
avahi-core/cache.h Normal file
View File

@@ -0,0 +1,101 @@
#ifndef foocachehfoo
#define foocachehfoo
/***
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 AvahiCache AvahiCache;
#include <avahi-common/llist.h>
#include "prioq.h"
#include "internal.h"
#include "timeeventq.h"
#include "hashmap.h"
typedef enum {
AVAHI_CACHE_VALID,
AVAHI_CACHE_EXPIRY1,
AVAHI_CACHE_EXPIRY2,
AVAHI_CACHE_EXPIRY3,
AVAHI_CACHE_EXPIRY_FINAL,
AVAHI_CACHE_POOF, /* Passive observation of failure */
AVAHI_CACHE_POOF_FINAL,
AVAHI_CACHE_GOODBYE_FINAL,
AVAHI_CACHE_REPLACE_FINAL
} AvahiCacheEntryState;
typedef struct AvahiCacheEntry AvahiCacheEntry;
struct AvahiCacheEntry {
AvahiCache *cache;
AvahiRecord *record;
struct timeval timestamp;
struct timeval poof_timestamp;
struct timeval expiry;
int cache_flush;
int poof_num;
AvahiAddress origin;
AvahiCacheEntryState state;
AvahiTimeEvent *time_event;
AvahiAddress poof_address;
AVAHI_LLIST_FIELDS(AvahiCacheEntry, by_key);
AVAHI_LLIST_FIELDS(AvahiCacheEntry, entry);
};
struct AvahiCache {
AvahiServer *server;
AvahiInterface *interface;
AvahiHashmap *hashmap;
AVAHI_LLIST_HEAD(AvahiCacheEntry, entries);
unsigned n_entries;
int last_rand;
time_t last_rand_timestamp;
};
AvahiCache *avahi_cache_new(AvahiServer *server, AvahiInterface *interface);
void avahi_cache_free(AvahiCache *c);
void avahi_cache_update(AvahiCache *c, AvahiRecord *r, int cache_flush, const AvahiAddress *a);
int avahi_cache_dump(AvahiCache *c, AvahiDumpCallback callback, void* userdata);
typedef void* AvahiCacheWalkCallback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata);
void* avahi_cache_walk(AvahiCache *c, AvahiKey *pattern, AvahiCacheWalkCallback cb, void* userdata);
int avahi_cache_entry_half_ttl(AvahiCache *c, AvahiCacheEntry *e);
/** Start the "Passive observation of Failure" algorithm for all
* records of the specified key. The specified address is */
void avahi_cache_start_poof(AvahiCache *c, AvahiKey *key, const AvahiAddress *a);
/* Stop a previously started POOF algorithm for a record. (Used for response suppresions records */
void avahi_cache_stop_poof(AvahiCache *c, AvahiRecord *record, const AvahiAddress *a);
void avahi_cache_flush(AvahiCache *c);
#endif

165
avahi-core/core.h Normal file
View File

@@ -0,0 +1,165 @@
#ifndef foocorehfoo
#define foocorehfoo
/***
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 core.h The Avahi Multicast DNS and DNS Service Discovery implementation. */
/** An mDNS responder object */
typedef struct AvahiServer AvahiServer;
#include <avahi-common/cdecl.h>
#include <avahi-common/address.h>
#include <avahi-common/defs.h>
#include <avahi-common/watch.h>
#include <avahi-common/timeval.h>
#include <avahi-core/rr.h>
AVAHI_C_DECL_BEGIN
/** Maximum number of defined DNS servers for wide area DNS */
#define AVAHI_WIDE_AREA_SERVERS_MAX 4
/** Prototype for callback functions which are called whenever the state of an AvahiServer object changes */
typedef void (*AvahiServerCallback) (AvahiServer *s, AvahiServerState 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 */
int use_ipv4; /**< Enable IPv4 support */
int use_ipv6; /**< Enable IPv6 support */
AvahiStringList *allow_interfaces;/**< Allow specific interface to be used for Avahi */
AvahiStringList *deny_interfaces; /**< Deny specific interfaces to be used for Avahi */
int publish_hinfo; /**< Register a HINFO record for the host containing the local OS and CPU type */
int publish_addresses; /**< Register A, AAAA and PTR records for all local IP addresses */
int publish_workstation; /**< Register a _workstation._tcp service */
int publish_domain; /**< Announce the local domain for browsing */
int check_response_ttl; /**< If enabled the server ignores all incoming responses with IP TTL != 255. Newer versions of the RFC do no longer contain this check, so it is disabled by default. */
int use_iff_running; /**< Require IFF_RUNNING on local network interfaces. This is the official way to check for link beat. Unfortunately this doesn't work with all drivers. So bettere leave this off. */
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_WIDE_AREA_SERVERS_MAX]; /** Unicast DNS server to use for wide area lookup */
unsigned n_wide_area_servers; /**< Number of servers in wide_area_servers[] */
int disallow_other_stacks; /**< Make sure that only one mDNS responder is run at the same time on the local machine. If this is enable Avahi will not set SO_REUSADDR on its sockets, effectively preventing other stacks from running on the local machine */
AvahiStringList *browse_domains; /**< Additional browsing domains */
int disable_publishing; /**< Disable publishing of any record */
int allow_point_to_point; /**< Enable publishing on POINTOPOINT interfaces */
int publish_a_on_ipv6; /**< Publish an IPv4 A RR on IPv6 sockets */
int publish_aaaa_on_ipv4; /**< Publish an IPv4 A RR on IPv6 sockets */
unsigned n_cache_entries_max; /**< Maximum number of cache entries per interface */
AvahiUsec ratelimit_interval; /**< If non-zero, rate-limiting interval parameter. */
unsigned ratelimit_burst; /**< If ratelimit_interval is non-zero, rate-limiting burst parameter. */
} AvahiServerConfig;
/** Allocate a new mDNS responder object. */
AvahiServer *avahi_server_new(
const AvahiPoll *api, /**< The main loop adapter */
const AvahiServerConfig *sc, /**< If non-NULL a pointer to a configuration structure for the server. The server makes an internal deep copy of this structure, so you may free it using avahi_server_config_done() immediately after calling this function. */
AvahiServerCallback callback, /**< A callback which is called whenever the state of the server changes */
void* userdata, /**< An opaque pointer which is passed to the callback function */
int *error);
/** Free an mDNS responder object */
void avahi_server_free(AvahiServer* s);
/** Fill in default values for a server configuration structure. If you
* make use of an AvahiServerConfig structure be sure to initialize
* it with this function for the sake of upwards library
* compatibility. This call may allocate strings on the heap. To
* release this memory make sure to call
* avahi_server_config_done(). If you want to replace any strings in
* the structure be sure to free the strings filled in by this
* function with avahi_free() first and allocate the replacements with
* g_malloc() (or g_strdup()).*/
AvahiServerConfig* avahi_server_config_init(
AvahiServerConfig *c /**< A structure which shall be filled in */ );
/** Make a deep copy of the configuration structure *c to *ret. */
AvahiServerConfig* avahi_server_config_copy(
AvahiServerConfig *ret /**< destination */,
const AvahiServerConfig *c /**< source */);
/** Free the data in a server configuration structure. */
void avahi_server_config_free(AvahiServerConfig *c);
/** Return the currently chosen domain name of the server object. The
* return value points to an internally allocated string. Be sure to
* make a copy of the string before calling any other library
* functions. */
const char* avahi_server_get_domain_name(AvahiServer *s);
/** Return the currently chosen host name. The return value points to a internally allocated string. */
const char* avahi_server_get_host_name(AvahiServer *s);
/** Return the currently chosen host name as a FQDN ("fully qualified
* domain name", i.e. the concatenation of the host and domain
* name). The return value points to a internally allocated string. */
const char* avahi_server_get_host_name_fqdn(AvahiServer *s);
/** Change the host name of a running mDNS responder. This will drop
all automicatilly generated RRs and readd them with the new
name. Since the responder has to probe for the new RRs this function
takes some time to take effect altough it returns immediately. This
function is intended to be called when a host name conflict is
reported using AvahiServerCallback. The caller should readd all user
defined RRs too since they otherwise continue to point to the outdated
host name..*/
int avahi_server_set_host_name(AvahiServer *s, const char *host_name);
/** Change the domain name of a running mDNS responder. The same rules
* as with avahi_server_set_host_name() apply. */
int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name);
/** Return the opaque user data pointer attached to a server object */
void* avahi_server_get_data(AvahiServer *s);
/** Change the opaque user data pointer attached to a server object */
void avahi_server_set_data(AvahiServer *s, void* userdata);
/** Return the current state of the server object */
AvahiServerState avahi_server_get_state(AvahiServer *s);
/** 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);
/** Return the last error code */
int avahi_server_errno(AvahiServer *s);
/** Return the local service cookie */
uint32_t avahi_server_get_local_service_cookie(AvahiServer *s);
/** Set the wide area DNS servers */
int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n);
/** Set the browsing domains */
int avahi_server_set_browse_domains(AvahiServer *s, AvahiStringList *domains);
/** Return the current configuration of the server \since 0.6.17 */
const AvahiServerConfig* avahi_server_get_config(AvahiServer *s);
AVAHI_C_DECL_END
#endif

87
avahi-core/dns-srv-rr.h Normal file
View File

@@ -0,0 +1,87 @@
#ifndef foodnssrvhfoo
#define foodnssrvhfoo
/***
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 avahi-core/dns-srv-rr.h Functions for announcing and browsing for unicast DNS servers via mDNS */
/** 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;
#include <avahi-common/cdecl.h>
#include <avahi-common/defs.h>
#include <avahi-core/core.h>
#include <avahi-core/publish.h>
AVAHI_C_DECL_BEGIN
/** 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)*/
AVAHI_DNS_SERVER_MAX
} 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,
AvahiPublishFlags flags,
const char *domain,
AvahiDNSServerType type,
const AvahiAddress *address,
uint16_t port /** should be 53 */);
/** 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);
AVAHI_C_DECL_END
#endif

877
avahi-core/dns.c Normal file
View File

@@ -0,0 +1,877 @@
/***
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 <string.h>
#include <stdio.h>
#include <assert.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <avahi-common/defs.h>
#include <avahi-common/domain.h>
#include <avahi-common/malloc.h>
#include "dns.h"
#include "log.h"
AvahiDnsPacket* avahi_dns_packet_new(unsigned mtu) {
AvahiDnsPacket *p;
size_t max_size;
if (mtu <= 0)
max_size = AVAHI_DNS_PACKET_SIZE_MAX;
else if (mtu >= AVAHI_DNS_PACKET_EXTRA_SIZE)
max_size = mtu - AVAHI_DNS_PACKET_EXTRA_SIZE;
else
max_size = 0;
if (max_size < AVAHI_DNS_PACKET_HEADER_SIZE)
max_size = AVAHI_DNS_PACKET_HEADER_SIZE;
if (!(p = avahi_malloc(sizeof(AvahiDnsPacket) + max_size)))
return p;
p->size = p->rindex = AVAHI_DNS_PACKET_HEADER_SIZE;
p->max_size = max_size;
p->name_table = NULL;
p->data = NULL;
memset(AVAHI_DNS_PACKET_DATA(p), 0, p->size);
return p;
}
AvahiDnsPacket* avahi_dns_packet_new_query(unsigned mtu) {
AvahiDnsPacket *p;
if (!(p = avahi_dns_packet_new(mtu)))
return NULL;
avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
return p;
}
AvahiDnsPacket* avahi_dns_packet_new_response(unsigned mtu, int aa) {
AvahiDnsPacket *p;
if (!(p = avahi_dns_packet_new(mtu)))
return NULL;
avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(1, 0, aa, 0, 0, 0, 0, 0, 0, 0));
return p;
}
AvahiDnsPacket* avahi_dns_packet_new_reply(AvahiDnsPacket* p, unsigned mtu, int copy_queries, int aa) {
AvahiDnsPacket *r;
assert(p);
if (!(r = avahi_dns_packet_new_response(mtu, aa)))
return NULL;
if (copy_queries) {
unsigned saved_rindex;
uint32_t n;
saved_rindex = p->rindex;
p->rindex = AVAHI_DNS_PACKET_HEADER_SIZE;
for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n--) {
AvahiKey *k;
int unicast_response;
if ((k = avahi_dns_packet_consume_key(p, &unicast_response))) {
avahi_dns_packet_append_key(r, k, unicast_response);
avahi_key_unref(k);
}
}
p->rindex = saved_rindex;
avahi_dns_packet_set_field(r, AVAHI_DNS_FIELD_QDCOUNT, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT));
}
avahi_dns_packet_set_field(r, AVAHI_DNS_FIELD_ID, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID));
avahi_dns_packet_set_field(r, AVAHI_DNS_FIELD_FLAGS,
(avahi_dns_packet_get_field(r, AVAHI_DNS_FIELD_FLAGS) & ~AVAHI_DNS_FLAG_OPCODE) |
(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_OPCODE));
return r;
}
void avahi_dns_packet_free(AvahiDnsPacket *p) {
assert(p);
if (p->name_table)
avahi_hashmap_free(p->name_table);
avahi_free(p);
}
void avahi_dns_packet_set_field(AvahiDnsPacket *p, unsigned idx, uint16_t v) {
assert(p);
assert(idx < AVAHI_DNS_PACKET_HEADER_SIZE);
((uint16_t*) AVAHI_DNS_PACKET_DATA(p))[idx] = htons(v);
}
uint16_t avahi_dns_packet_get_field(AvahiDnsPacket *p, unsigned idx) {
assert(p);
assert(idx < AVAHI_DNS_PACKET_HEADER_SIZE);
return ntohs(((uint16_t*) AVAHI_DNS_PACKET_DATA(p))[idx]);
}
void avahi_dns_packet_inc_field(AvahiDnsPacket *p, unsigned idx) {
assert(p);
assert(idx < AVAHI_DNS_PACKET_HEADER_SIZE);
avahi_dns_packet_set_field(p, idx, avahi_dns_packet_get_field(p, idx) + 1);
}
static void name_table_cleanup(void *key, void *value, void *user_data) {
AvahiDnsPacket *p = user_data;
if ((uint8_t*) value >= AVAHI_DNS_PACKET_DATA(p) + p->size)
avahi_hashmap_remove(p->name_table, key);
}
void avahi_dns_packet_cleanup_name_table(AvahiDnsPacket *p) {
if (p->name_table)
avahi_hashmap_foreach(p->name_table, name_table_cleanup, p);
}
uint8_t* avahi_dns_packet_append_name(AvahiDnsPacket *p, const char *name) {
uint8_t *d, *saved_ptr = NULL;
size_t saved_size;
assert(p);
assert(name);
saved_size = p->size;
saved_ptr = avahi_dns_packet_extend(p, 0);
while (*name) {
uint8_t* prev;
const char *pname;
char label[64], *u;
/* Check whether we can compress this name. */
if (p->name_table && (prev = avahi_hashmap_lookup(p->name_table, name))) {
unsigned idx;
assert(prev >= AVAHI_DNS_PACKET_DATA(p));
idx = (unsigned) (prev - AVAHI_DNS_PACKET_DATA(p));
assert(idx < p->size);
if (idx < 0x4000) {
uint8_t *t;
if (!(t = (uint8_t*) avahi_dns_packet_extend(p, sizeof(uint16_t))))
return NULL;
t[0] = (uint8_t) ((0xC000 | idx) >> 8);
t[1] = (uint8_t) idx;
return saved_ptr;
}
}
pname = name;
if (!(avahi_unescape_label(&name, label, sizeof(label))))
goto fail;
if (!(d = avahi_dns_packet_append_string(p, label)))
goto fail;
if (!p->name_table)
/* This works only for normalized domain names */
p->name_table = avahi_hashmap_new(avahi_string_hash, avahi_string_equal, avahi_free, NULL);
if (!(u = avahi_strdup(pname)))
avahi_log_error("avahi_strdup() failed.");
else
avahi_hashmap_insert(p->name_table, u, d);
}
if (!(d = avahi_dns_packet_extend(p, 1)))
goto fail;
*d = 0;
return saved_ptr;
fail:
p->size = saved_size;
avahi_dns_packet_cleanup_name_table(p);
return NULL;
}
uint8_t* avahi_dns_packet_append_uint16(AvahiDnsPacket *p, uint16_t v) {
uint8_t *d;
assert(p);
if (!(d = avahi_dns_packet_extend(p, sizeof(uint16_t))))
return NULL;
d[0] = (uint8_t) (v >> 8);
d[1] = (uint8_t) v;
return d;
}
uint8_t *avahi_dns_packet_append_uint32(AvahiDnsPacket *p, uint32_t v) {
uint8_t *d;
assert(p);
if (!(d = avahi_dns_packet_extend(p, sizeof(uint32_t))))
return NULL;
d[0] = (uint8_t) (v >> 24);
d[1] = (uint8_t) (v >> 16);
d[2] = (uint8_t) (v >> 8);
d[3] = (uint8_t) v;
return d;
}
uint8_t *avahi_dns_packet_append_bytes(AvahiDnsPacket *p, const void *b, size_t l) {
uint8_t* d;
assert(p);
assert(b);
assert(l);
if (!(d = avahi_dns_packet_extend(p, l)))
return NULL;
memcpy(d, b, l);
return d;
}
uint8_t* avahi_dns_packet_append_string(AvahiDnsPacket *p, const char *s) {
uint8_t* d;
size_t k;
assert(p);
assert(s);
if ((k = strlen(s)) >= 255)
k = 255;
if (!(d = avahi_dns_packet_extend(p, k+1)))
return NULL;
*d = (uint8_t) k;
memcpy(d+1, s, k);
return d;
}
uint8_t *avahi_dns_packet_extend(AvahiDnsPacket *p, size_t l) {
uint8_t *d;
assert(p);
if (p->size+l > p->max_size)
return NULL;
d = AVAHI_DNS_PACKET_DATA(p) + p->size;
p->size += l;
return d;
}
int avahi_dns_packet_check_valid(AvahiDnsPacket *p) {
uint16_t flags;
assert(p);
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 (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);
return !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_QR);
}
static int consume_labels(AvahiDnsPacket *p, unsigned idx, char *ret_name, size_t l) {
int ret = 0;
int compressed = 0;
int first_label = 1;
unsigned label_ptr;
int i;
assert(p && ret_name && l);
for (i = 0; i < AVAHI_DNS_LABELS_MAX; i++) {
uint8_t n;
if (idx+1 > p->size)
return -1;
n = AVAHI_DNS_PACKET_DATA(p)[idx];
if (!n) {
idx++;
if (!compressed)
ret++;
if (l < 1)
return -1;
*ret_name = 0;
return ret;
} else if (n <= 63) {
/* Uncompressed label */
idx++;
if (!compressed)
ret++;
if (idx + n > p->size)
return -1;
if ((size_t) n + 1 > l)
return -1;
if (!first_label) {
*(ret_name++) = '.';
l--;
} else
first_label = 0;
if (!(avahi_escape_label((char*) AVAHI_DNS_PACKET_DATA(p) + idx, n, &ret_name, &l)))
return -1;
idx += n;
if (!compressed)
ret += n;
} else if ((n & 0xC0) == 0xC0) {
/* Compressed label */
if (idx+2 > p->size)
return -1;
label_ptr = ((unsigned) (AVAHI_DNS_PACKET_DATA(p)[idx] & ~0xC0)) << 8 | AVAHI_DNS_PACKET_DATA(p)[idx+1];
if ((label_ptr < AVAHI_DNS_PACKET_HEADER_SIZE) || (label_ptr >= idx))
return -1;
idx = label_ptr;
if (!compressed)
ret += 2;
compressed = 1;
} else
return -1;
}
return -1;
}
int avahi_dns_packet_consume_name(AvahiDnsPacket *p, char *ret_name, size_t l) {
int r;
if ((r = consume_labels(p, p->rindex, ret_name, l)) < 0)
return -1;
p->rindex += r;
return 0;
}
int avahi_dns_packet_consume_uint16(AvahiDnsPacket *p, uint16_t *ret_v) {
uint8_t *d;
assert(p);
assert(ret_v);
if (p->rindex + sizeof(uint16_t) > p->size)
return -1;
d = (uint8_t*) (AVAHI_DNS_PACKET_DATA(p) + p->rindex);
*ret_v = (d[0] << 8) | d[1];
p->rindex += sizeof(uint16_t);
return 0;
}
int avahi_dns_packet_consume_uint32(AvahiDnsPacket *p, uint32_t *ret_v) {
uint8_t* d;
assert(p);
assert(ret_v);
if (p->rindex + sizeof(uint32_t) > p->size)
return -1;
d = (uint8_t*) (AVAHI_DNS_PACKET_DATA(p) + p->rindex);
*ret_v = (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
p->rindex += sizeof(uint32_t);
return 0;
}
int avahi_dns_packet_consume_bytes(AvahiDnsPacket *p, void * ret_data, size_t l) {
assert(p);
assert(ret_data);
assert(l > 0);
if (p->rindex + l > p->size)
return -1;
memcpy(ret_data, AVAHI_DNS_PACKET_DATA(p) + p->rindex, l);
p->rindex += l;
return 0;
}
int avahi_dns_packet_consume_string(AvahiDnsPacket *p, char *ret_string, size_t l) {
size_t k;
assert(p);
assert(ret_string);
assert(l > 0);
if (p->rindex >= p->size)
return -1;
k = AVAHI_DNS_PACKET_DATA(p)[p->rindex];
if (p->rindex+1+k > p->size)
return -1;
if (l > k+1)
l = k+1;
memcpy(ret_string, AVAHI_DNS_PACKET_DATA(p)+p->rindex+1, l-1);
ret_string[l-1] = 0;
p->rindex += 1+k;
return 0;
}
const void* avahi_dns_packet_get_rptr(AvahiDnsPacket *p) {
assert(p);
if (p->rindex > p->size)
return NULL;
return AVAHI_DNS_PACKET_DATA(p) + p->rindex;
}
int avahi_dns_packet_skip(AvahiDnsPacket *p, size_t length) {
assert(p);
if (p->rindex + length > p->size)
return -1;
p->rindex += length;
return 0;
}
static int parse_rdata(AvahiDnsPacket *p, AvahiRecord *r, uint16_t rdlength) {
char buf[AVAHI_DOMAIN_NAME_MAX];
const void* start;
assert(p);
assert(r);
start = avahi_dns_packet_get_rptr(p);
switch (r->key->type) {
case AVAHI_DNS_TYPE_PTR:
case AVAHI_DNS_TYPE_CNAME:
case AVAHI_DNS_TYPE_NS:
if (avahi_dns_packet_consume_name(p, buf, sizeof(buf)) < 0)
return -1;
r->data.ptr.name = avahi_strdup(buf);
break;
case AVAHI_DNS_TYPE_SRV:
if (avahi_dns_packet_consume_uint16(p, &r->data.srv.priority) < 0 ||
avahi_dns_packet_consume_uint16(p, &r->data.srv.weight) < 0 ||
avahi_dns_packet_consume_uint16(p, &r->data.srv.port) < 0 ||
avahi_dns_packet_consume_name(p, buf, sizeof(buf)) < 0)
return -1;
r->data.srv.name = avahi_strdup(buf);
break;
case AVAHI_DNS_TYPE_HINFO:
if (avahi_dns_packet_consume_string(p, buf, sizeof(buf)) < 0)
return -1;
r->data.hinfo.cpu = avahi_strdup(buf);
if (avahi_dns_packet_consume_string(p, buf, sizeof(buf)) < 0)
return -1;
r->data.hinfo.os = avahi_strdup(buf);
break;
case AVAHI_DNS_TYPE_TXT:
if (rdlength > 0) {
if (avahi_string_list_parse(avahi_dns_packet_get_rptr(p), rdlength, &r->data.txt.string_list) < 0)
return -1;
if (avahi_dns_packet_skip(p, rdlength) < 0)
return -1;
} else
r->data.txt.string_list = NULL;
break;
case AVAHI_DNS_TYPE_A:
/* avahi_log_debug("A"); */
if (avahi_dns_packet_consume_bytes(p, &r->data.a.address, sizeof(AvahiIPv4Address)) < 0)
return -1;
break;
case AVAHI_DNS_TYPE_AAAA:
/* avahi_log_debug("aaaa"); */
if (avahi_dns_packet_consume_bytes(p, &r->data.aaaa.address, sizeof(AvahiIPv6Address)) < 0)
return -1;
break;
default:
/* avahi_log_debug("generic"); */
if (rdlength > 0) {
r->data.generic.data = avahi_memdup(avahi_dns_packet_get_rptr(p), rdlength);
r->data.generic.size = rdlength;
if (avahi_dns_packet_skip(p, rdlength) < 0)
return -1;
}
break;
}
/* Check if we read enough data */
if ((const uint8_t*) avahi_dns_packet_get_rptr(p) - (const uint8_t*) start != rdlength)
return -1;
return 0;
}
AvahiRecord* avahi_dns_packet_consume_record(AvahiDnsPacket *p, int *ret_cache_flush) {
char name[AVAHI_DOMAIN_NAME_MAX];
uint16_t type, class;
uint32_t ttl;
uint16_t rdlength;
AvahiRecord *r = NULL;
assert(p);
if (avahi_dns_packet_consume_name(p, name, sizeof(name)) < 0 ||
avahi_dns_packet_consume_uint16(p, &type) < 0 ||
avahi_dns_packet_consume_uint16(p, &class) < 0 ||
avahi_dns_packet_consume_uint32(p, &ttl) < 0 ||
avahi_dns_packet_consume_uint16(p, &rdlength) < 0 ||
p->rindex + rdlength > p->size)
goto fail;
if (ret_cache_flush)
*ret_cache_flush = !!(class & AVAHI_DNS_CACHE_FLUSH);
class &= ~AVAHI_DNS_CACHE_FLUSH;
if (!(r = avahi_record_new_full(name, class, type, ttl)))
goto fail;
if (parse_rdata(p, r, rdlength) < 0)
goto fail;
if (!avahi_record_is_valid(r))
goto fail;
return r;
fail:
if (r)
avahi_record_unref(r);
return NULL;
}
AvahiKey* avahi_dns_packet_consume_key(AvahiDnsPacket *p, int *ret_unicast_response) {
char name[256];
uint16_t type, class;
AvahiKey *k;
assert(p);
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;
if (ret_unicast_response)
*ret_unicast_response = !!(class & AVAHI_DNS_UNICAST_RESPONSE);
class &= ~AVAHI_DNS_UNICAST_RESPONSE;
if (!(k = avahi_key_new(name, class, type)))
return NULL;
if (!avahi_key_is_valid(k)) {
avahi_key_unref(k);
return NULL;
}
return k;
}
uint8_t* avahi_dns_packet_append_key(AvahiDnsPacket *p, AvahiKey *k, int unicast_response) {
uint8_t *t;
size_t size;
assert(p);
assert(k);
size = p->size;
if (!(t = avahi_dns_packet_append_name(p, k->name)) ||
!avahi_dns_packet_append_uint16(p, k->type) ||
!avahi_dns_packet_append_uint16(p, k->clazz | (unicast_response ? AVAHI_DNS_UNICAST_RESPONSE : 0))) {
p->size = size;
avahi_dns_packet_cleanup_name_table(p);
return NULL;
}
return t;
}
static int append_rdata(AvahiDnsPacket *p, AvahiRecord *r) {
assert(p);
assert(r);
switch (r->key->type) {
case AVAHI_DNS_TYPE_PTR:
case AVAHI_DNS_TYPE_CNAME:
case AVAHI_DNS_TYPE_NS:
if (!(avahi_dns_packet_append_name(p, r->data.ptr.name)))
return -1;
break;
case AVAHI_DNS_TYPE_SRV:
if (!avahi_dns_packet_append_uint16(p, r->data.srv.priority) ||
!avahi_dns_packet_append_uint16(p, r->data.srv.weight) ||
!avahi_dns_packet_append_uint16(p, r->data.srv.port) ||
!avahi_dns_packet_append_name(p, r->data.srv.name))
return -1;
break;
case AVAHI_DNS_TYPE_HINFO:
if (!avahi_dns_packet_append_string(p, r->data.hinfo.cpu) ||
!avahi_dns_packet_append_string(p, r->data.hinfo.os))
return -1;
break;
case AVAHI_DNS_TYPE_TXT: {
uint8_t *data;
size_t n;
n = avahi_string_list_serialize(r->data.txt.string_list, NULL, 0);
if (!(data = avahi_dns_packet_extend(p, n)))
return -1;
avahi_string_list_serialize(r->data.txt.string_list, data, n);
break;
}
case AVAHI_DNS_TYPE_A:
if (!avahi_dns_packet_append_bytes(p, &r->data.a.address, sizeof(r->data.a.address)))
return -1;
break;
case AVAHI_DNS_TYPE_AAAA:
if (!avahi_dns_packet_append_bytes(p, &r->data.aaaa.address, sizeof(r->data.aaaa.address)))
return -1;
break;
default:
if (r->data.generic.size)
if (!avahi_dns_packet_append_bytes(p, r->data.generic.data, r->data.generic.size))
return -1;
break;
}
return 0;
}
uint8_t* avahi_dns_packet_append_record(AvahiDnsPacket *p, AvahiRecord *r, int cache_flush, unsigned max_ttl) {
uint8_t *t, *l, *start;
size_t size;
assert(p);
assert(r);
size = p->size;
if (!(t = avahi_dns_packet_append_name(p, r->key->name)) ||
!avahi_dns_packet_append_uint16(p, r->key->type) ||
!avahi_dns_packet_append_uint16(p, cache_flush ? (r->key->clazz | AVAHI_DNS_CACHE_FLUSH) : (r->key->clazz &~ AVAHI_DNS_CACHE_FLUSH)) ||
!avahi_dns_packet_append_uint32(p, (max_ttl && r->ttl > max_ttl) ? max_ttl : r->ttl) ||
!(l = avahi_dns_packet_append_uint16(p, 0)))
goto fail;
start = avahi_dns_packet_extend(p, 0);
if (append_rdata(p, r) < 0)
goto fail;
size = avahi_dns_packet_extend(p, 0) - start;
assert(size <= AVAHI_DNS_RDATA_MAX);
/* avahi_log_debug("appended %u", size); */
l[0] = (uint8_t) ((uint16_t) size >> 8);
l[1] = (uint8_t) ((uint16_t) size);
return t;
fail:
p->size = size;
avahi_dns_packet_cleanup_name_table(p);
return NULL;
}
int avahi_dns_packet_is_empty(AvahiDnsPacket *p) {
assert(p);
return p->size <= AVAHI_DNS_PACKET_HEADER_SIZE;
}
size_t avahi_dns_packet_space(AvahiDnsPacket *p) {
assert(p);
assert(p->size <= p->max_size);
return p->max_size - p->size;
}
int avahi_rdata_parse(AvahiRecord *record, const void* rdata, size_t size) {
int ret;
AvahiDnsPacket p;
assert(record);
assert(rdata);
p.data = (void*) rdata;
p.max_size = p.size = size;
p.rindex = 0;
p.name_table = NULL;
ret = parse_rdata(&p, record, size);
assert(!p.name_table);
return ret;
}
size_t avahi_rdata_serialize(AvahiRecord *record, void *rdata, size_t max_size) {
int ret;
AvahiDnsPacket p;
assert(record);
assert(rdata);
assert(max_size > 0);
p.data = (void*) rdata;
p.max_size = max_size;
p.size = p.rindex = 0;
p.name_table = NULL;
ret = append_rdata(&p, record);
if (p.name_table)
avahi_hashmap_free(p.name_table);
if (ret < 0)
return (size_t) -1;
return p.size;
}

111
avahi-core/dns.h Normal file
View File

@@ -0,0 +1,111 @@
#ifndef foodnshfoo
#define foodnshfoo
/***
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 "rr.h"
#include "hashmap.h"
#define AVAHI_DNS_PACKET_HEADER_SIZE 12
#define AVAHI_DNS_PACKET_EXTRA_SIZE 48
#define AVAHI_DNS_LABELS_MAX 127
#define AVAHI_DNS_RDATA_MAX 0xFFFF
#define AVAHI_DNS_PACKET_SIZE_MAX (AVAHI_DNS_PACKET_HEADER_SIZE + 256 + 2 + 2 + 4 + 2 + AVAHI_DNS_RDATA_MAX)
typedef struct AvahiDnsPacket {
size_t size, rindex, max_size;
AvahiHashmap *name_table; /* for name compression */
uint8_t *data;
} AvahiDnsPacket;
#define AVAHI_DNS_PACKET_DATA(p) ((p)->data ? (p)->data : ((uint8_t*) p) + sizeof(AvahiDnsPacket))
AvahiDnsPacket* avahi_dns_packet_new(unsigned mtu);
AvahiDnsPacket* avahi_dns_packet_new_query(unsigned mtu);
AvahiDnsPacket* avahi_dns_packet_new_response(unsigned mtu, int aa);
AvahiDnsPacket* avahi_dns_packet_new_reply(AvahiDnsPacket* p, unsigned mtu, int copy_queries, int aa);
void avahi_dns_packet_free(AvahiDnsPacket *p);
void avahi_dns_packet_set_field(AvahiDnsPacket *p, unsigned idx, uint16_t v);
uint16_t avahi_dns_packet_get_field(AvahiDnsPacket *p, unsigned idx);
void avahi_dns_packet_inc_field(AvahiDnsPacket *p, unsigned idx);
uint8_t *avahi_dns_packet_extend(AvahiDnsPacket *p, size_t l);
void avahi_dns_packet_cleanup_name_table(AvahiDnsPacket *p);
uint8_t *avahi_dns_packet_append_uint16(AvahiDnsPacket *p, uint16_t v);
uint8_t *avahi_dns_packet_append_uint32(AvahiDnsPacket *p, uint32_t v);
uint8_t *avahi_dns_packet_append_name(AvahiDnsPacket *p, const char *name);
uint8_t *avahi_dns_packet_append_bytes(AvahiDnsPacket *p, const void *d, size_t l);
uint8_t* avahi_dns_packet_append_key(AvahiDnsPacket *p, AvahiKey *k, int unicast_response);
uint8_t* avahi_dns_packet_append_record(AvahiDnsPacket *p, AvahiRecord *r, int cache_flush, unsigned max_ttl);
uint8_t* avahi_dns_packet_append_string(AvahiDnsPacket *p, const char *s);
int avahi_dns_packet_is_query(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);
int avahi_dns_packet_consume_name(AvahiDnsPacket *p, char *ret_name, size_t l);
int avahi_dns_packet_consume_bytes(AvahiDnsPacket *p, void* ret_data, size_t l);
AvahiKey* avahi_dns_packet_consume_key(AvahiDnsPacket *p, int *ret_unicast_response);
AvahiRecord* avahi_dns_packet_consume_record(AvahiDnsPacket *p, int *ret_cache_flush);
int avahi_dns_packet_consume_string(AvahiDnsPacket *p, char *ret_string, size_t l);
const void* avahi_dns_packet_get_rptr(AvahiDnsPacket *p);
int avahi_dns_packet_skip(AvahiDnsPacket *p, size_t length);
int avahi_dns_packet_is_empty(AvahiDnsPacket *p);
size_t avahi_dns_packet_space(AvahiDnsPacket *p);
#define AVAHI_DNS_FIELD_ID 0
#define AVAHI_DNS_FIELD_FLAGS 1
#define AVAHI_DNS_FIELD_QDCOUNT 2
#define AVAHI_DNS_FIELD_ANCOUNT 3
#define AVAHI_DNS_FIELD_NSCOUNT 4
#define AVAHI_DNS_FIELD_ARCOUNT 5
#define AVAHI_DNS_FLAG_QR (1 << 15)
#define AVAHI_DNS_FLAG_OPCODE (15 << 11)
#define AVAHI_DNS_FLAG_RCODE (15)
#define AVAHI_DNS_FLAG_TC (1 << 9)
#define AVAHI_DNS_FLAG_AA (1 << 10)
#define AVAHI_DNS_FLAGS(qr, opcode, aa, tc, rd, ra, z, ad, cd, rcode) \
(((uint16_t) !!qr << 15) | \
((uint16_t) (opcode & 15) << 11) | \
((uint16_t) !!aa << 10) | \
((uint16_t) !!tc << 9) | \
((uint16_t) !!rd << 8) | \
((uint16_t) !!ra << 7) | \
((uint16_t) !!ad << 5) | \
((uint16_t) !!cd << 4) | \
((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

188
avahi-core/domain-util.c Normal file
View File

@@ -0,0 +1,188 @@
/***
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 <string.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/utsname.h>
#include <stdio.h>
#include <avahi-common/malloc.h>
#include "log.h"
#include "domain-util.h"
#include "util.h"
static void strip_bad_chars(char *s) {
char *p, *d;
s[strcspn(s, ".")] = 0;
for (p = s, d = s; *p; p++)
if ((*p >= 'a' && *p <= 'z') ||
(*p >= 'A' && *p <= 'Z') ||
(*p >= '0' && *p <= '9') ||
*p == '-')
*(d++) = *p;
*d = 0;
}
#ifdef __linux__
static int load_lsb_distrib_id(char *ret_s, size_t size) {
FILE *f;
assert(ret_s);
assert(size > 0);
if (!(f = fopen("/etc/lsb-release", "r")))
return -1;
while (!feof(f)) {
char ln[256], *p;
if (!fgets(ln, sizeof(ln), f))
break;
if (strncmp(ln, "DISTRIB_ID=", 11))
continue;
p = ln + 11;
p += strspn(p, "\"");
p[strcspn(p, "\"")] = 0;
snprintf(ret_s, size, "%s", p);
fclose(f);
return 0;
}
fclose(f);
return -1;
}
#endif
char *avahi_get_host_name(char *ret_s, size_t size) {
assert(ret_s);
assert(size > 0);
if (gethostname(ret_s, size) >= 0) {
ret_s[size-1] = 0;
strip_bad_chars(ret_s);
} else
*ret_s = 0;
if (strcmp(ret_s, "localhost") == 0 || strncmp(ret_s, "localhost.", 10) == 0) {
*ret_s = 0;
avahi_log_warn("System host name is set to 'localhost'. This is not a suitable mDNS host name, looking for alternatives.");
}
if (*ret_s == 0) {
/* No hostname was set, so let's take the OS name */
#ifdef __linux__
/* Try LSB distribution name first */
if (load_lsb_distrib_id(ret_s, size) >= 0) {
strip_bad_chars(ret_s);
avahi_strdown(ret_s);
}
if (*ret_s == 0)
#endif
{
/* Try uname() second */
struct utsname utsname;
if (uname(&utsname) >= 0) {
snprintf(ret_s, size, "%s", utsname.sysname);
strip_bad_chars(ret_s);
avahi_strdown(ret_s);
}
/* Give up */
if (*ret_s == 0)
snprintf(ret_s, size, "unnamed");
}
}
if (size >= AVAHI_LABEL_MAX)
ret_s[AVAHI_LABEL_MAX-1] = 0;
return ret_s;
}
char *avahi_get_host_name_strdup(void) {
char t[AVAHI_DOMAIN_NAME_MAX];
if (!(avahi_get_host_name(t, sizeof(t))))
return NULL;
return avahi_strdup(t);
}
int avahi_binary_domain_cmp(const char *a, const char *b) {
assert(a);
assert(b);
if (a == b)
return 0;
for (;;) {
char ca[AVAHI_LABEL_MAX], cb[AVAHI_LABEL_MAX], *p;
int r;
p = avahi_unescape_label(&a, ca, sizeof(ca));
assert(p);
p = avahi_unescape_label(&b, cb, sizeof(cb));
assert(p);
if ((r = strcmp(ca, cb)))
return r;
if (!*a && !*b)
return 0;
}
}
int avahi_domain_ends_with(const char *domain, const char *suffix) {
assert(domain);
assert(suffix);
for (;;) {
char dummy[AVAHI_LABEL_MAX], *r;
if (*domain == 0)
return 0;
if (avahi_domain_equal(domain, suffix))
return 1;
r = avahi_unescape_label(&domain, dummy, sizeof(dummy));
assert(r);
}
}

45
avahi-core/domain-util.h Normal file
View File

@@ -0,0 +1,45 @@
#ifndef foodomainutilhfoo
#define foodomainutilhfoo
/***
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 <inttypes.h>
#include <sys/types.h>
#include <avahi-common/cdecl.h>
#include <avahi-common/domain.h>
AVAHI_C_DECL_BEGIN
/** Return the local host name. */
char *avahi_get_host_name(char *ret_s, size_t size);
/** Return the local host name. avahi_free() the result! */
char *avahi_get_host_name_strdup(void);
/** Do a binary comparison of to specified domain names, return -1, 0, or 1, depending on the order. */
int avahi_binary_domain_cmp(const char *a, const char *b);
/** Returns 1 if the the end labels of domain are eqal to suffix */
int avahi_domain_ends_with(const char *domain, const char *suffix);
AVAHI_C_DECL_END
#endif

1233
avahi-core/entry.c Normal file

File diff suppressed because it is too large Load Diff

72
avahi-core/fdutil.c Normal file
View File

@@ -0,0 +1,72 @@
/***
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 <sys/select.h>
#include <unistd.h>
#include <fcntl.h>
#include <assert.h>
#include "fdutil.h"
int avahi_set_cloexec(int fd) {
int n;
assert(fd >= 0);
if ((n = fcntl(fd, F_GETFD)) < 0)
return -1;
if (n & FD_CLOEXEC)
return 0;
return fcntl(fd, F_SETFD, n|FD_CLOEXEC);
}
int avahi_set_nonblock(int fd) {
int n;
assert(fd >= 0);
if ((n = fcntl(fd, F_GETFL)) < 0)
return -1;
if (n & O_NONBLOCK)
return 0;
return fcntl(fd, F_SETFL, n|O_NONBLOCK);
}
int avahi_wait_for_write(int fd) {
fd_set fds;
int r;
FD_ZERO(&fds);
FD_SET(fd, &fds);
if ((r = select(fd+1, NULL, &fds, NULL, NULL)) < 0)
return -1;
assert(r > 0);
return 0;
}

33
avahi-core/fdutil.h Normal file
View File

@@ -0,0 +1,33 @@
#ifndef foofdutilhfoo
#define foofdutilhfoo
/***
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 <avahi-common/cdecl.h>
AVAHI_C_DECL_BEGIN
int avahi_set_cloexec(int fd);
int avahi_set_nonblock(int fd);
int avahi_wait_for_write(int fd);
AVAHI_C_DECL_END
#endif

248
avahi-core/hashmap.c Normal file
View File

@@ -0,0 +1,248 @@
/***
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 <string.h>
#include <avahi-common/llist.h>
#include <avahi-common/domain.h>
#include <avahi-common/malloc.h>
#include "hashmap.h"
#include "util.h"
#define HASH_MAP_SIZE 123
typedef struct Entry Entry;
struct Entry {
AvahiHashmap *hashmap;
void *key;
void *value;
AVAHI_LLIST_FIELDS(Entry, bucket);
AVAHI_LLIST_FIELDS(Entry, entries);
};
struct AvahiHashmap {
AvahiHashFunc hash_func;
AvahiEqualFunc equal_func;
AvahiFreeFunc key_free_func, value_free_func;
Entry *entries[HASH_MAP_SIZE];
AVAHI_LLIST_HEAD(Entry, entries_list);
};
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)
if (m->equal_func(key, e->key))
return e;
return NULL;
}
static void entry_free(AvahiHashmap *m, Entry *e, int stolen) {
unsigned idx;
assert(m);
assert(e);
idx = m->hash_func(e->key) % HASH_MAP_SIZE;
AVAHI_LLIST_REMOVE(Entry, bucket, m->entries[idx], e);
AVAHI_LLIST_REMOVE(Entry, entries, m->entries_list, e);
if (m->key_free_func)
m->key_free_func(e->key);
if (m->value_free_func && !stolen)
m->value_free_func(e->value);
avahi_free(e);
}
AvahiHashmap* avahi_hashmap_new(AvahiHashFunc hash_func, AvahiEqualFunc equal_func, AvahiFreeFunc key_free_func, AvahiFreeFunc value_free_func) {
AvahiHashmap *m;
assert(hash_func);
assert(equal_func);
if (!(m = avahi_new0(AvahiHashmap, 1)))
return NULL;
m->hash_func = hash_func;
m->equal_func = equal_func;
m->key_free_func = key_free_func;
m->value_free_func = value_free_func;
AVAHI_LLIST_HEAD_INIT(Entry, m->entries_list);
return m;
}
void avahi_hashmap_free(AvahiHashmap *m) {
assert(m);
while (m->entries_list)
entry_free(m, m->entries_list, 0);
avahi_free(m);
}
void* avahi_hashmap_lookup(AvahiHashmap *m, const void *key) {
Entry *e;
assert(m);
if (!(e = entry_get(m, key)))
return NULL;
return e->value;
}
int avahi_hashmap_insert(AvahiHashmap *m, void *key, void *value) {
unsigned idx;
Entry *e;
assert(m);
if ((e = entry_get(m, key))) {
if (m->key_free_func)
m->key_free_func(key);
if (m->value_free_func)
m->value_free_func(value);
return 1;
}
if (!(e = avahi_new(Entry, 1)))
return -1;
e->hashmap = m;
e->key = key;
e->value = value;
AVAHI_LLIST_PREPEND(Entry, entries, m->entries_list, e);
idx = m->hash_func(key) % HASH_MAP_SIZE;
AVAHI_LLIST_PREPEND(Entry, bucket, m->entries[idx], e);
return 0;
}
int avahi_hashmap_replace(AvahiHashmap *m, void *key, void *value) {
unsigned idx;
Entry *e;
assert(m);
if ((e = entry_get(m, key))) {
if (m->key_free_func)
m->key_free_func(e->key);
if (m->value_free_func)
m->value_free_func(e->value);
e->key = key;
e->value = value;
return 1;
}
if (!(e = avahi_new(Entry, 1)))
return -1;
e->hashmap = m;
e->key = key;
e->value = value;
AVAHI_LLIST_PREPEND(Entry, entries, m->entries_list, e);
idx = m->hash_func(key) % HASH_MAP_SIZE;
AVAHI_LLIST_PREPEND(Entry, bucket, m->entries[idx], e);
return 0;
}
void avahi_hashmap_remove(AvahiHashmap *m, const void *key) {
Entry *e;
assert(m);
if (!(e = entry_get(m, key)))
return;
entry_free(m, e, 0);
}
void avahi_hashmap_foreach(AvahiHashmap *m, AvahiHashmapForeachCallback callback, void *userdata) {
Entry *e, *next;
assert(m);
assert(callback);
for (e = m->entries_list; e; e = next) {
next = e->entries_next;
callback(e->key, e->value, userdata);
}
}
unsigned avahi_string_hash(const void *data) {
const char *p = data;
unsigned hash = 0;
assert(p);
for (; *p; p++)
hash = 31 * hash + *p;
return hash;
}
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;
}

53
avahi-core/hashmap.h Normal file
View File

@@ -0,0 +1,53 @@
#ifndef foohashmaphfoo
#define foohashmaphfoo
/***
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 <avahi-common/cdecl.h>
AVAHI_C_DECL_BEGIN
typedef struct AvahiHashmap AvahiHashmap;
typedef unsigned (*AvahiHashFunc)(const void *data);
typedef int (*AvahiEqualFunc)(const void *a, const void *b);
typedef void (*AvahiFreeFunc)(void *p);
AvahiHashmap* avahi_hashmap_new(AvahiHashFunc hash_func, AvahiEqualFunc equal_func, AvahiFreeFunc key_free_func, AvahiFreeFunc value_free_func);
void avahi_hashmap_free(AvahiHashmap *m);
void* avahi_hashmap_lookup(AvahiHashmap *m, const void *key);
int avahi_hashmap_insert(AvahiHashmap *m, void *key, void *value);
int avahi_hashmap_replace(AvahiHashmap *m, void *key, void *value);
void avahi_hashmap_remove(AvahiHashmap *m, const void *key);
typedef void (*AvahiHashmapForeachCallback)(void *key, void *value, void *userdata);
void avahi_hashmap_foreach(AvahiHashmap *m, AvahiHashmapForeachCallback callback, void *userdata);
unsigned avahi_string_hash(const void *data);
int avahi_string_equal(const void *a, const void *b);
unsigned avahi_int_hash(const void *data);
int avahi_int_equal(const void *a, const void *b);
AVAHI_C_DECL_END
#endif

391
avahi-core/iface-linux.c Normal file
View File

@@ -0,0 +1,391 @@
/***
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 <string.h>
#include <net/if.h>
#include <errno.h>
#include <string.h>
#include <avahi-common/malloc.h>
#include "log.h"
#include "iface.h"
#include "iface-linux.h"
#ifndef IFLA_RTA
#include <linux/if_addr.h>
#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
#endif
#ifndef IFA_RTA
#include <linux/if_addr.h>
#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
#endif
static int netlink_list_items(AvahiNetlink *nl, uint16_t type, unsigned *ret_seq) {
struct nlmsghdr *n;
struct rtgenmsg *gen;
uint8_t req[1024];
/* Issue a wild dump NETLINK request */
memset(&req, 0, sizeof(req));
n = (struct nlmsghdr*) req;
n->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
n->nlmsg_type = type;
n->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP;
n->nlmsg_pid = 0;
gen = NLMSG_DATA(n);
memset(gen, 0, sizeof(struct rtgenmsg));
gen->rtgen_family = AF_UNSPEC;
return avahi_netlink_send(nl, n, ret_seq);
}
static void netlink_callback(AvahiNetlink *nl, struct nlmsghdr *n, void* userdata) {
AvahiInterfaceMonitor *m = userdata;
/* This routine is called for every RTNETLINK response packet */
assert(m);
assert(n);
assert(m->osdep.netlink == nl);
if (n->nlmsg_type == RTM_NEWLINK) {
/* A new interface appeared or an existing one has been modified */
struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
AvahiHwInterface *hw;
struct rtattr *a = NULL;
size_t l;
/* A (superfluous?) sanity check */
if (ifinfomsg->ifi_family != AF_UNSPEC)
return;
/* Check whether there already is an AvahiHwInterface object
* for this link, so that we can update its data. Note that
* Netlink sends us an RTM_NEWLINK not only when a new
* interface appears, but when it changes, too */
if (!(hw = avahi_interface_monitor_get_hw_interface(m, ifinfomsg->ifi_index)))
/* No object found, so let's create a new
* one. avahi_hw_interface_new() will call
* avahi_interface_new() internally twice for IPv4 and
* IPv6, so there is no need for us to do that
* ourselves */
if (!(hw = avahi_hw_interface_new(m, (AvahiIfIndex) ifinfomsg->ifi_index)))
return; /* OOM */
/* Check whether the flags of this interface are OK for us */
hw->flags_ok =
(ifinfomsg->ifi_flags & IFF_UP) &&
(!m->server->config.use_iff_running || (ifinfomsg->ifi_flags & IFF_RUNNING)) &&
!(ifinfomsg->ifi_flags & IFF_LOOPBACK) &&
(ifinfomsg->ifi_flags & IFF_MULTICAST) &&
(m->server->config.allow_point_to_point || !(ifinfomsg->ifi_flags & IFF_POINTOPOINT));
/* Handle interface attributes */
l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg));
a = IFLA_RTA(ifinfomsg);
while (RTA_OK(a, l)) {
switch(a->rta_type) {
case IFLA_IFNAME:
/* Fill in interface name */
avahi_free(hw->name);
hw->name = avahi_strndup(RTA_DATA(a), RTA_PAYLOAD(a));
break;
case IFLA_MTU:
/* Fill in MTU */
assert(RTA_PAYLOAD(a) == sizeof(unsigned int));
hw->mtu = *((unsigned int*) RTA_DATA(a));
break;
case IFLA_ADDRESS:
/* Fill in hardware (MAC) address */
hw->mac_address_size = RTA_PAYLOAD(a);
if (hw->mac_address_size > AVAHI_MAC_ADDRESS_MAX)
hw->mac_address_size = AVAHI_MAC_ADDRESS_MAX;
memcpy(hw->mac_address, RTA_DATA(a), hw->mac_address_size);
break;
default:
;
}
a = RTA_NEXT(a, l);
}
/* Check whether this interface is now "relevant" for us. If
* it is Avahi will start to announce its records on this
* interface and send out queries for subscribed records on
* it */
avahi_hw_interface_check_relevant(hw);
/* Update any associated RRs of this interface. (i.e. the
* _workstation._tcp record containing the MAC address) */
avahi_hw_interface_update_rrs(hw, 0);
} else if (n->nlmsg_type == RTM_DELLINK) {
/* An interface has been removed */
struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
AvahiHwInterface *hw;
/* A (superfluous?) sanity check */
if (ifinfomsg->ifi_family != AF_UNSPEC)
return;
/* Get a reference to our AvahiHwInterface object of this interface */
if (!(hw = avahi_interface_monitor_get_hw_interface(m, (AvahiIfIndex) ifinfomsg->ifi_index)))
return;
/* Free our object */
avahi_hw_interface_free(hw, 0);
} else if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) {
/* An address has been added, modified or removed */
struct ifaddrmsg *ifaddrmsg = NLMSG_DATA(n);
AvahiInterface *i;
struct rtattr *a = NULL;
size_t l;
AvahiAddress raddr, rlocal, *r;
int raddr_valid = 0, rlocal_valid = 0;
/* We are only interested in IPv4 and IPv6 */
if (ifaddrmsg->ifa_family != AF_INET && ifaddrmsg->ifa_family != AF_INET6)
return;
/* Try to get a reference to our AvahiInterface object for the
* interface this address is assigned to. If ther is no object
* for this interface, we ignore this address. */
if (!(i = avahi_interface_monitor_get_interface(m, (AvahiIfIndex) ifaddrmsg->ifa_index, avahi_af_to_proto(ifaddrmsg->ifa_family))))
return;
/* Fill in address family for our new address */
rlocal.proto = raddr.proto = avahi_af_to_proto(ifaddrmsg->ifa_family);
l = NLMSG_PAYLOAD(n, sizeof(struct ifaddrmsg));
a = IFA_RTA(ifaddrmsg);
while (RTA_OK(a, l)) {
switch(a->rta_type) {
case IFA_ADDRESS:
if ((rlocal.proto == AVAHI_PROTO_INET6 && RTA_PAYLOAD(a) != 16) ||
(rlocal.proto == AVAHI_PROTO_INET && RTA_PAYLOAD(a) != 4))
return;
memcpy(rlocal.data.data, RTA_DATA(a), RTA_PAYLOAD(a));
rlocal_valid = 1;
break;
case IFA_LOCAL:
/* Fill in local address data. Usually this is
* preferable over IFA_ADDRESS if both are set,
* since this refers to the local address of a PPP
* link while IFA_ADDRESS refers to the other
* end. */
if ((raddr.proto == AVAHI_PROTO_INET6 && RTA_PAYLOAD(a) != 16) ||
(raddr.proto == AVAHI_PROTO_INET && RTA_PAYLOAD(a) != 4))
return;
memcpy(raddr.data.data, RTA_DATA(a), RTA_PAYLOAD(a));
raddr_valid = 1;
break;
default:
;
}
a = RTA_NEXT(a, l);
}
/* If there was no adress attached to this message, let's quit. */
if (rlocal_valid)
r = &rlocal;
else if (raddr_valid)
r = &raddr;
else
return;
if (n->nlmsg_type == RTM_NEWADDR) {
AvahiInterfaceAddress *addr;
/* This address is new or has been modified, so let's get an object for it */
if (!(addr = avahi_interface_monitor_get_address(m, i, r)))
/* Mmm, no object existing yet, so let's create a new one */
if (!(addr = avahi_interface_address_new(m, i, r, ifaddrmsg->ifa_prefixlen)))
return; /* OOM */
/* Update the scope field for the address */
addr->global_scope = ifaddrmsg->ifa_scope == RT_SCOPE_UNIVERSE || ifaddrmsg->ifa_scope == RT_SCOPE_SITE;
addr->deprecated = !!(ifaddrmsg->ifa_flags & IFA_F_DEPRECATED);
} else {
AvahiInterfaceAddress *addr;
assert(n->nlmsg_type == RTM_DELADDR);
/* Try to get a reference to our AvahiInterfaceAddress object for this address */
if (!(addr = avahi_interface_monitor_get_address(m, i, r)))
return;
/* And free it */
avahi_interface_address_free(addr);
}
/* Avahi only considers interfaces with at least one address
* attached relevant. Since we migh have added or removed an
* address, let's have it check again whether the interface is
* now relevant */
avahi_interface_check_relevant(i);
/* Update any associated RRs, like A or AAAA for our new/removed address */
avahi_interface_update_rrs(i, 0);
} else if (n->nlmsg_type == NLMSG_DONE) {
/* This wild dump request ended, so let's see what we do next */
if (m->osdep.list == LIST_IFACE) {
/* Mmmm, interfaces have been wild dumped already, so
* let's go on with wild dumping the addresses */
if (netlink_list_items(m->osdep.netlink, RTM_GETADDR, &m->osdep.query_addr_seq) < 0) {
avahi_log_warn("NETLINK: Failed to list addrs: %s", strerror(errno));
m->osdep.list = LIST_DONE;
} else
/* Update state information */
m->osdep.list = LIST_ADDR;
} else
/* We're done. Tell avahi_interface_monitor_sync() to finish. */
m->osdep.list = LIST_DONE;
if (m->osdep.list == LIST_DONE) {
/* Only after this boolean variable has been set, Avahi
* will start to announce or browse on all interfaces. It
* is originaly set to 0, which means that relevancy
* checks and RR updates are disabled during the wild
* dumps. */
m->list_complete = 1;
/* So let's check if any interfaces are relevant now */
avahi_interface_monitor_check_relevant(m);
/* And update all RRs attached to any interface */
avahi_interface_monitor_update_rrs(m, 0);
/* Tell the user that the wild dump is complete */
avahi_log_info("Network interface enumeration completed.");
}
} else if (n->nlmsg_type == NLMSG_ERROR &&
(n->nlmsg_seq == m->osdep.query_link_seq || n->nlmsg_seq == m->osdep.query_addr_seq)) {
struct nlmsgerr *e = NLMSG_DATA (n);
/* Some kind of error happened. Let's just tell the user and
* ignore it otherwise */
if (e->error)
avahi_log_warn("NETLINK: Failed to browse: %s", strerror(-e->error));
}
}
int avahi_interface_monitor_init_osdep(AvahiInterfaceMonitor *m) {
assert(m);
/* Initialize our own data */
m->osdep.netlink = NULL;
m->osdep.query_addr_seq = m->osdep.query_link_seq = 0;
/* Create a netlink object for us. It abstracts some things and
* makes netlink easier to use. It will attach to the main loop
* for us and call netlink_callback() whenever an event
* happens. */
if (!(m->osdep.netlink = avahi_netlink_new(m->server->poll_api, RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR, netlink_callback, m)))
goto fail;
/* Set the initial state. */
m->osdep.list = LIST_IFACE;
/* Start the wild dump for the interfaces */
if (netlink_list_items(m->osdep.netlink, RTM_GETLINK, &m->osdep.query_link_seq) < 0)
goto fail;
return 0;
fail:
if (m->osdep.netlink) {
avahi_netlink_free(m->osdep.netlink);
m->osdep.netlink = NULL;
}
return -1;
}
void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m) {
assert(m);
if (m->osdep.netlink) {
avahi_netlink_free(m->osdep.netlink);
m->osdep.netlink = NULL;
}
}
void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) {
assert(m);
/* Let's handle netlink events until we are done with wild
* dumping */
while (!m->list_complete)
if (!avahi_netlink_work(m->osdep.netlink, 1) == 0)
break;
/* At this point Avahi knows about all local interfaces and
* addresses in existance. */
}

40
avahi-core/iface-linux.h Normal file
View File

@@ -0,0 +1,40 @@
#ifndef fooifacelinuxhfoo
#define fooifacelinuxhfoo
/***
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 AvahiInterfaceMonitorOSDep AvahiInterfaceMonitorOSDep;
#include "netlink.h"
struct AvahiInterfaceMonitorOSDep {
AvahiNetlink *netlink;
unsigned query_addr_seq, query_link_seq;
enum {
LIST_IFACE,
LIST_ADDR,
LIST_DONE
} list;
};
#endif

View File

@@ -0,0 +1,37 @@
#ifndef fooifacepfroutehfoo
#define fooifacepfroutehfoo
/***
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 <avahi-common/watch.h>
typedef struct AvahiPfRoute AvahiPfRoute;
struct AvahiPfRoute {
int fd;
AvahiWatch *watch;
AvahiInterfaceMonitor *m;
};
typedef struct AvahiInterfaceMonitorOSDep AvahiInterfaceMonitorOSDep;
struct AvahiInterfaceMonitorOSDep {
AvahiPfRoute *pfroute;
};
#endif

865
avahi-core/iface.c Normal file
View File

@@ -0,0 +1,865 @@
/***
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 <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <avahi-common/error.h>
#include <avahi-common/malloc.h>
#include <avahi-common/domain.h>
#include "iface.h"
#include "dns.h"
#include "socket.h"
#include "announce.h"
#include "util.h"
#include "log.h"
#include "multicast-lookup.h"
#include "querier.h"
void avahi_interface_address_update_rrs(AvahiInterfaceAddress *a, int remove_rrs) {
AvahiInterfaceMonitor *m;
assert(a);
m = a->monitor;
if (m->list_complete &&
avahi_interface_address_is_relevant(a) &&
avahi_interface_is_relevant(a->interface) &&
!remove_rrs &&
m->server->config.publish_addresses &&
(m->server->state == AVAHI_SERVER_RUNNING ||
m->server->state == AVAHI_SERVER_REGISTERING)) {
/* Fill the entry group */
if (!a->entry_group)
a->entry_group = avahi_s_entry_group_new(m->server, avahi_host_rr_entry_group_callback, NULL);
if (!a->entry_group) /* OOM */
return;
if (avahi_s_entry_group_is_empty(a->entry_group)) {
char t[AVAHI_ADDRESS_STR_MAX];
AvahiProtocol p;
p = (a->interface->protocol == AVAHI_PROTO_INET && m->server->config.publish_a_on_ipv6) ||
(a->interface->protocol == AVAHI_PROTO_INET6 && m->server->config.publish_aaaa_on_ipv4) ? AVAHI_PROTO_UNSPEC : a->interface->protocol;
avahi_address_snprint(t, sizeof(t), &a->address);
avahi_log_info("Registering new address record for %s on %s.%s.", t, a->interface->hardware->name, p == AVAHI_PROTO_UNSPEC ? "*" : avahi_proto_to_string(p));
if (avahi_server_add_address(m->server, a->entry_group, a->interface->hardware->index, p, 0, NULL, &a->address) < 0) {
avahi_log_warn(__FILE__": avahi_server_add_address() failed: %s", avahi_strerror(m->server->error));
avahi_s_entry_group_free(a->entry_group);
a->entry_group = NULL;
return;
}
avahi_s_entry_group_commit(a->entry_group);
}
} else {
/* Clear the entry group */
if (a->entry_group && !avahi_s_entry_group_is_empty(a->entry_group)) {
char t[AVAHI_ADDRESS_STR_MAX];
avahi_address_snprint(t, sizeof(t), &a->address);
avahi_log_info("Withdrawing address record for %s on %s.", t, a->interface->hardware->name);
if (avahi_s_entry_group_get_state(a->entry_group) == AVAHI_ENTRY_GROUP_REGISTERING &&
m->server->state == AVAHI_SERVER_REGISTERING)
avahi_server_decrease_host_rr_pending(m->server);
avahi_s_entry_group_reset(a->entry_group);
}
}
}
void avahi_interface_update_rrs(AvahiInterface *i, int remove_rrs) {
AvahiInterfaceAddress *a;
assert(i);
for (a = i->addresses; a; a = a->address_next)
avahi_interface_address_update_rrs(a, remove_rrs);
}
void avahi_hw_interface_update_rrs(AvahiHwInterface *hw, int remove_rrs) {
AvahiInterface *i;
AvahiInterfaceMonitor *m;
assert(hw);
m = hw->monitor;
for (i = hw->interfaces; i; i = i->by_hardware_next)
avahi_interface_update_rrs(i, remove_rrs);
if (m->list_complete &&
!remove_rrs &&
m->server->config.publish_workstation &&
(m->server->state == AVAHI_SERVER_RUNNING)) {
if (!hw->entry_group)
hw->entry_group = avahi_s_entry_group_new(m->server, avahi_host_rr_entry_group_callback, NULL);
if (!hw->entry_group)
return; /* OOM */
if (avahi_s_entry_group_is_empty(hw->entry_group)) {
char name[AVAHI_LABEL_MAX], unescaped[AVAHI_LABEL_MAX], mac[256];
const char *p = m->server->host_name;
avahi_unescape_label(&p, unescaped, sizeof(unescaped));
avahi_format_mac_address(mac, sizeof(mac), hw->mac_address, hw->mac_address_size);
snprintf(name, sizeof(name), "%s [%s]", unescaped, mac);
if (avahi_server_add_service(m->server, hw->entry_group, hw->index, AVAHI_PROTO_UNSPEC, 0, name, "_workstation._tcp", NULL, NULL, 9, NULL) < 0) {
avahi_log_warn(__FILE__": avahi_server_add_service() failed: %s", avahi_strerror(m->server->error));
avahi_s_entry_group_free(hw->entry_group);
hw->entry_group = NULL;
} else
avahi_s_entry_group_commit(hw->entry_group);
}
} else {
if (hw->entry_group && !avahi_s_entry_group_is_empty(hw->entry_group)) {
avahi_log_info("Withdrawing workstation service for %s.", hw->name);
if (avahi_s_entry_group_get_state(hw->entry_group) == AVAHI_ENTRY_GROUP_REGISTERING &&
m->server->state == AVAHI_SERVER_REGISTERING)
avahi_server_decrease_host_rr_pending(m->server);
avahi_s_entry_group_reset(hw->entry_group);
}
}
}
void avahi_interface_monitor_update_rrs(AvahiInterfaceMonitor *m, int remove_rrs) {
AvahiHwInterface *hw;
assert(m);
for (hw = m->hw_interfaces; hw; hw = hw->hardware_next)
avahi_hw_interface_update_rrs(hw, remove_rrs);
}
static int interface_mdns_mcast_join(AvahiInterface *i, int join) {
char at[AVAHI_ADDRESS_STR_MAX];
int r;
assert(i);
if (!!join == !!i->mcast_joined)
return 0;
if ((i->protocol == AVAHI_PROTO_INET6 && i->monitor->server->fd_ipv6 < 0) ||
(i->protocol == AVAHI_PROTO_INET && i->monitor->server->fd_ipv4 < 0))
return -1;
if (join) {
AvahiInterfaceAddress *a;
/* Look if there's an address with global scope */
for (a = i->addresses; a; a = a->address_next)
if (a->global_scope)
break;
/* No address with a global scope has been found, so let's use
* any. */
if (!a)
a = i->addresses;
/* Hmm, there is no address available. */
if (!a)
return -1;
i->local_mcast_address = a->address;
}
avahi_log_info("%s mDNS multicast group on interface %s.%s with address %s.",
join ? "Joining" : "Leaving",
i->hardware->name,
avahi_proto_to_string(i->protocol),
avahi_address_snprint(at, sizeof(at), &i->local_mcast_address));
if (i->protocol == AVAHI_PROTO_INET6)
r = avahi_mdns_mcast_join_ipv6(i->monitor->server->fd_ipv6, &i->local_mcast_address.data.ipv6, i->hardware->index, join);
else {
assert(i->protocol == AVAHI_PROTO_INET);
r = avahi_mdns_mcast_join_ipv4(i->monitor->server->fd_ipv4, &i->local_mcast_address.data.ipv4, i->hardware->index, join);
}
if (r < 0)
i->mcast_joined = 0;
else
i->mcast_joined = join;
return 0;
}
static int interface_mdns_mcast_rejoin(AvahiInterface *i) {
AvahiInterfaceAddress *a, *usable = NULL, *found = NULL;
assert(i);
if (!i->mcast_joined)
return 0;
/* Check whether old address we joined with is still available. If
* not, rejoin using an other address. */
for (a = i->addresses; a; a = a->address_next) {
if (a->global_scope && !usable)
usable = a;
if (avahi_address_cmp(&a->address, &i->local_mcast_address) == 0) {
if (a->global_scope)
/* No action necessary: the address still exists and
* has global scope. */
return 0;
found = a;
}
}
if (found && !usable)
/* No action necessary: the address still exists and no better one has been found */
return 0;
interface_mdns_mcast_join(i, 0);
return interface_mdns_mcast_join(i, 1);
}
void avahi_interface_address_free(AvahiInterfaceAddress *a) {
assert(a);
assert(a->interface);
avahi_interface_address_update_rrs(a, 1);
AVAHI_LLIST_REMOVE(AvahiInterfaceAddress, address, a->interface->addresses, a);
if (a->entry_group)
avahi_s_entry_group_free(a->entry_group);
interface_mdns_mcast_rejoin(a->interface);
avahi_free(a);
}
void avahi_interface_free(AvahiInterface *i, int send_goodbye) {
assert(i);
/* Handle goodbyes and remove announcers */
avahi_goodbye_interface(i->monitor->server, i, send_goodbye, 1);
avahi_response_scheduler_force(i->response_scheduler);
assert(!i->announcers);
if (i->mcast_joined)
interface_mdns_mcast_join(i, 0);
/* Remove queriers */
avahi_querier_free_all(i);
avahi_hashmap_free(i->queriers_by_key);
/* Remove local RRs */
avahi_interface_update_rrs(i, 1);
while (i->addresses)
avahi_interface_address_free(i->addresses);
avahi_response_scheduler_free(i->response_scheduler);
avahi_query_scheduler_free(i->query_scheduler);
avahi_probe_scheduler_free(i->probe_scheduler);
avahi_cache_free(i->cache);
AVAHI_LLIST_REMOVE(AvahiInterface, interface, i->monitor->interfaces, i);
AVAHI_LLIST_REMOVE(AvahiInterface, by_hardware, i->hardware->interfaces, i);
avahi_free(i);
}
void avahi_hw_interface_free(AvahiHwInterface *hw, int send_goodbye) {
assert(hw);
avahi_hw_interface_update_rrs(hw, 1);
while (hw->interfaces)
avahi_interface_free(hw->interfaces, send_goodbye);
if (hw->entry_group)
avahi_s_entry_group_free(hw->entry_group);
AVAHI_LLIST_REMOVE(AvahiHwInterface, hardware, hw->monitor->hw_interfaces, hw);
avahi_hashmap_remove(hw->monitor->hashmap, &hw->index);
avahi_free(hw->name);
avahi_free(hw);
}
AvahiInterface* avahi_interface_new(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, AvahiProtocol protocol) {
AvahiInterface *i;
assert(m);
assert(hw);
assert(AVAHI_PROTO_VALID(protocol));
if (!(i = avahi_new(AvahiInterface, 1)))
goto fail; /* OOM */
i->monitor = m;
i->hardware = hw;
i->protocol = protocol;
i->announcing = 0;
i->mcast_joined = 0;
AVAHI_LLIST_HEAD_INIT(AvahiInterfaceAddress, i->addresses);
AVAHI_LLIST_HEAD_INIT(AvahiAnnouncer, i->announcers);
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);
i->probe_scheduler = avahi_probe_scheduler_new(i);
if (!i->cache || !i->response_scheduler || !i->query_scheduler || !i->probe_scheduler)
goto fail; /* OOM */
AVAHI_LLIST_PREPEND(AvahiInterface, by_hardware, hw->interfaces, i);
AVAHI_LLIST_PREPEND(AvahiInterface, interface, m->interfaces, i);
return i;
fail:
if (i) {
if (i->cache)
avahi_cache_free(i->cache);
if (i->response_scheduler)
avahi_response_scheduler_free(i->response_scheduler);
if (i->query_scheduler)
avahi_query_scheduler_free(i->query_scheduler);
if (i->probe_scheduler)
avahi_probe_scheduler_free(i->probe_scheduler);
}
return NULL;
}
AvahiHwInterface *avahi_hw_interface_new(AvahiInterfaceMonitor *m, AvahiIfIndex idx) {
AvahiHwInterface *hw;
assert(m);
assert(AVAHI_IF_VALID(idx));
if (!(hw = avahi_new(AvahiHwInterface, 1)))
return NULL;
hw->monitor = m;
hw->name = NULL;
hw->flags_ok = 0;
hw->mtu = 1500;
hw->index = idx;
hw->mac_address_size = 0;
hw->entry_group = NULL;
hw->ratelimit_begin.tv_sec = 0;
hw->ratelimit_begin.tv_usec = 0;
hw->ratelimit_counter = 0;
AVAHI_LLIST_HEAD_INIT(AvahiInterface, hw->interfaces);
AVAHI_LLIST_PREPEND(AvahiHwInterface, hardware, m->hw_interfaces, hw);
avahi_hashmap_insert(m->hashmap, &hw->index, hw);
if (m->server->fd_ipv4 >= 0 || m->server->config.publish_a_on_ipv6)
avahi_interface_new(m, hw, AVAHI_PROTO_INET);
if (m->server->fd_ipv6 >= 0 || m->server->config.publish_aaaa_on_ipv4)
avahi_interface_new(m, hw, AVAHI_PROTO_INET6);
return hw;
}
AvahiInterfaceAddress *avahi_interface_address_new(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *addr, unsigned prefix_len) {
AvahiInterfaceAddress *a;
assert(m);
assert(i);
if (!(a = avahi_new(AvahiInterfaceAddress, 1)))
return NULL;
a->interface = i;
a->monitor = m;
a->address = *addr;
a->prefix_len = prefix_len;
a->global_scope = 0;
a->deprecated = 0;
a->entry_group = NULL;
AVAHI_LLIST_PREPEND(AvahiInterfaceAddress, address, i->addresses, a);
return a;
}
void avahi_interface_check_relevant(AvahiInterface *i) {
int b;
AvahiInterfaceMonitor *m;
assert(i);
m = i->monitor;
b = avahi_interface_is_relevant(i);
if (m->list_complete && b && !i->announcing) {
interface_mdns_mcast_join(i, 1);
if (i->mcast_joined) {
avahi_log_info("New relevant interface %s.%s for mDNS.", i->hardware->name, avahi_proto_to_string(i->protocol));
i->announcing = 1;
avahi_announce_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 for mDNS.", i->hardware->name, avahi_proto_to_string(i->protocol));
interface_mdns_mcast_join(i, 0);
avahi_goodbye_interface(m->server, i, 0, 1);
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);
avahi_cache_flush(i->cache);
i->announcing = 0;
} else
interface_mdns_mcast_rejoin(i);
}
void avahi_hw_interface_check_relevant(AvahiHwInterface *hw) {
AvahiInterface *i;
assert(hw);
for (i = hw->interfaces; i; i = i->by_hardware_next)
avahi_interface_check_relevant(i);
}
void avahi_interface_monitor_check_relevant(AvahiInterfaceMonitor *m) {
AvahiInterface *i;
assert(m);
for (i = m->interfaces; i; i = i->interface_next)
avahi_interface_check_relevant(i);
}
AvahiInterfaceMonitor *avahi_interface_monitor_new(AvahiServer *s) {
AvahiInterfaceMonitor *m = NULL;
if (!(m = avahi_new0(AvahiInterfaceMonitor, 1)))
return NULL; /* OOM */
m->server = s;
m->list_complete = 0;
m->hashmap = avahi_hashmap_new(avahi_int_hash, avahi_int_equal, NULL, NULL);
AVAHI_LLIST_HEAD_INIT(AvahiInterface, m->interfaces);
AVAHI_LLIST_HEAD_INIT(AvahiHwInterface, m->hw_interfaces);
if (avahi_interface_monitor_init_osdep(m) < 0)
goto fail;
return m;
fail:
avahi_interface_monitor_free(m);
return NULL;
}
void avahi_interface_monitor_free(AvahiInterfaceMonitor *m) {
assert(m);
while (m->hw_interfaces)
avahi_hw_interface_free(m->hw_interfaces, 1);
assert(!m->interfaces);
avahi_interface_monitor_free_osdep(m);
if (m->hashmap)
avahi_hashmap_free(m->hashmap);
avahi_free(m);
}
AvahiInterface* avahi_interface_monitor_get_interface(AvahiInterfaceMonitor *m, AvahiIfIndex idx, AvahiProtocol protocol) {
AvahiHwInterface *hw;
AvahiInterface *i;
assert(m);
assert(idx >= 0);
assert(protocol != AVAHI_PROTO_UNSPEC);
if (!(hw = avahi_interface_monitor_get_hw_interface(m, idx)))
return NULL;
for (i = hw->interfaces; i; i = i->by_hardware_next)
if (i->protocol == protocol)
return i;
return NULL;
}
AvahiHwInterface* avahi_interface_monitor_get_hw_interface(AvahiInterfaceMonitor *m, AvahiIfIndex idx) {
assert(m);
assert(idx >= 0);
return avahi_hashmap_lookup(m->hashmap, &idx);
}
AvahiInterfaceAddress* avahi_interface_monitor_get_address(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *raddr) {
AvahiInterfaceAddress *ia;
assert(m);
assert(i);
assert(raddr);
for (ia = i->addresses; ia; ia = ia->address_next)
if (avahi_address_cmp(&ia->address, raddr) == 0)
return ia;
return NULL;
}
void avahi_interface_send_packet_unicast(AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port) {
assert(i);
assert(p);
if (!i->announcing)
return;
assert(!a || a->proto == i->protocol);
if (i->monitor->server->config.ratelimit_interval > 0) {
struct timeval now, end;
gettimeofday(&now, NULL);
end = i->hardware->ratelimit_begin;
avahi_timeval_add(&end, i->monitor->server->config.ratelimit_interval);
if (i->hardware->ratelimit_begin.tv_sec <= 0 ||
avahi_timeval_compare(&end, &now) < 0) {
i->hardware->ratelimit_begin = now;
i->hardware->ratelimit_counter = 0;
}
if (i->hardware->ratelimit_counter > i->monitor->server->config.ratelimit_burst)
return;
i->hardware->ratelimit_counter++;
}
if (i->protocol == AVAHI_PROTO_INET && i->monitor->server->fd_ipv4 >= 0)
avahi_send_dns_packet_ipv4(i->monitor->server->fd_ipv4, i->hardware->index, p, i->mcast_joined ? &i->local_mcast_address.data.ipv4 : NULL, a ? &a->data.ipv4 : NULL, port);
else if (i->protocol == AVAHI_PROTO_INET6 && i->monitor->server->fd_ipv6 >= 0)
avahi_send_dns_packet_ipv6(i->monitor->server->fd_ipv6, i->hardware->index, p, i->mcast_joined ? &i->local_mcast_address.data.ipv6 : NULL, a ? &a->data.ipv6 : NULL, port);
}
void avahi_interface_send_packet(AvahiInterface *i, AvahiDnsPacket *p) {
assert(i);
assert(p);
avahi_interface_send_packet_unicast(i, p, NULL, 0);
}
int avahi_interface_post_query(AvahiInterface *i, AvahiKey *key, int immediately, unsigned *ret_id) {
assert(i);
assert(key);
if (!i->announcing)
return 0;
return avahi_query_scheduler_post(i->query_scheduler, key, immediately, ret_id);
}
int avahi_interface_withraw_query(AvahiInterface *i, unsigned id) {
return avahi_query_scheduler_withdraw_by_id(i->query_scheduler, id);
}
int avahi_interface_post_response(AvahiInterface *i, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately) {
assert(i);
assert(record);
if (!i->announcing)
return 0;
return avahi_response_scheduler_post(i->response_scheduler, record, flush_cache, querier, immediately);
}
int avahi_interface_post_probe(AvahiInterface *i, AvahiRecord *record, int immediately) {
assert(i);
assert(record);
if (!i->announcing)
return 0;
return avahi_probe_scheduler_post(i->probe_scheduler, record, immediately);
}
int avahi_dump_caches(AvahiInterfaceMonitor *m, AvahiDumpCallback callback, void* userdata) {
AvahiInterface *i;
assert(m);
for (i = m->interfaces; i; i = i->interface_next) {
if (avahi_interface_is_relevant(i)) {
char ln[256];
snprintf(ln, sizeof(ln), ";;; INTERFACE %s.%s ;;;", i->hardware->name, avahi_proto_to_string(i->protocol));
callback(ln, userdata);
if (avahi_cache_dump(i->cache, callback, userdata) < 0)
return -1;
}
}
return 0;
}
static int avahi_interface_is_relevant_internal(AvahiInterface *i) {
AvahiInterfaceAddress *a;
assert(i);
if (!i->hardware->flags_ok)
return 0;
for (a = i->addresses; a; a = a->address_next)
if (avahi_interface_address_is_relevant(a))
return 1;
return 0;
}
int avahi_interface_is_relevant(AvahiInterface *i) {
AvahiStringList *l;
assert(i);
for (l = i->monitor->server->config.deny_interfaces; l; l = l->next)
if (strcasecmp((char*) l->text, i->hardware->name) == 0)
return 0;
if (i->monitor->server->config.allow_interfaces) {
for (l = i->monitor->server->config.allow_interfaces; l; l = l->next)
if (strcasecmp((char*) l->text, i->hardware->name) == 0)
goto good;
return 0;
}
good:
return avahi_interface_is_relevant_internal(i);
}
int avahi_interface_address_is_relevant(AvahiInterfaceAddress *a) {
AvahiInterfaceAddress *b;
assert(a);
/* Publish public and non-deprecated IP addresses */
if (a->global_scope && !a->deprecated)
return 1;
/* Publish link-local and deprecated IP addresses only if they are
* the only ones on the link */
for (b = a->interface->addresses; b; b = b->address_next) {
if (b == a)
continue;
if (b->global_scope && !b->deprecated)
return 0;
}
return 1;
}
int avahi_interface_match(AvahiInterface *i, AvahiIfIndex idx, AvahiProtocol protocol) {
assert(i);
if (idx != AVAHI_IF_UNSPEC && idx != i->hardware->index)
return 0;
if (protocol != AVAHI_PROTO_UNSPEC && protocol != i->protocol)
return 0;
return 1;
}
void avahi_interface_monitor_walk(AvahiInterfaceMonitor *m, AvahiIfIndex interface, AvahiProtocol protocol, AvahiInterfaceMonitorWalkCallback callback, void* userdata) {
assert(m);
assert(callback);
if (interface != AVAHI_IF_UNSPEC) {
if (protocol != AVAHI_PROTO_UNSPEC) {
AvahiInterface *i;
if ((i = avahi_interface_monitor_get_interface(m, interface, protocol)))
callback(m, i, userdata);
} else {
AvahiHwInterface *hw;
AvahiInterface *i;
if ((hw = avahi_interface_monitor_get_hw_interface(m, interface)))
for (i = hw->interfaces; i; i = i->by_hardware_next)
if (avahi_interface_match(i, interface, protocol))
callback(m, i, userdata);
}
} else {
AvahiInterface *i;
for (i = m->interfaces; i; i = i->interface_next)
if (avahi_interface_match(i, interface, protocol))
callback(m, i, userdata);
}
}
int avahi_address_is_local(AvahiInterfaceMonitor *m, const AvahiAddress *a) {
AvahiInterface *i;
AvahiInterfaceAddress *ia;
assert(m);
assert(a);
for (i = m->interfaces; i; i = i->interface_next)
for (ia = i->addresses; ia; ia = ia->address_next)
if (avahi_address_cmp(a, &ia->address) == 0)
return 1;
return 0;
}
int avahi_interface_address_on_link(AvahiInterface *i, const AvahiAddress *a) {
AvahiInterfaceAddress *ia;
assert(i);
assert(a);
if (a->proto != i->protocol)
return 0;
for (ia = i->addresses; ia; ia = ia->address_next) {
if (a->proto == AVAHI_PROTO_INET) {
uint32_t m;
m = ~(((uint32_t) -1) >> ia->prefix_len);
if ((ntohl(a->data.ipv4.address) & m) == (ntohl(ia->address.data.ipv4.address) & m))
return 1;
} else {
unsigned j;
unsigned char pl;
assert(a->proto == AVAHI_PROTO_INET6);
pl = ia->prefix_len;
for (j = 0; j < 16; j++) {
uint8_t m;
if (pl == 0)
return 1;
if (pl >= 8) {
m = 0xFF;
pl -= 8;
} else {
m = ~(0xFF >> pl);
pl = 0;
}
if ((a->data.ipv6.address[j] & m) != (ia->address.data.ipv6.address[j] & m))
break;
}
}
}
return 0;
}
int avahi_interface_has_address(AvahiInterfaceMonitor *m, AvahiIfIndex iface, const AvahiAddress *a) {
AvahiInterface *i;
AvahiInterfaceAddress *j;
assert(m);
assert(iface != AVAHI_IF_UNSPEC);
assert(a);
if (!(i = avahi_interface_monitor_get_interface(m, iface, a->proto)))
return 0;
for (j = i->addresses; j; j = j->address_next)
if (avahi_address_cmp(a, &j->address) == 0)
return 1;
return 0;
}
AvahiIfIndex avahi_find_interface_for_address(AvahiInterfaceMonitor *m, const AvahiAddress *a) {
AvahiInterface *i;
assert(m);
/* Some stupid OS don't support passing the interface index when a
* packet is received. We have to work around that limitation by
* looking for an interface that has the incoming address
* attached. This is sometimes ambiguous, but we have to live with
* it. */
for (i = m->interfaces; i; i = i->interface_next) {
AvahiInterfaceAddress *ai;
if (i->protocol != a->proto)
continue;
for (ai = i->addresses; ai; ai = ai->address_next)
if (avahi_address_cmp(a, &ai->address) == 0)
return i->hardware->index;
}
return AVAHI_IF_UNSPEC;
}

195
avahi-core/iface.h Normal file
View File

@@ -0,0 +1,195 @@
#ifndef fooifacehfoo
#define fooifacehfoo
/***
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 AvahiInterfaceMonitor AvahiInterfaceMonitor;
typedef struct AvahiInterfaceAddress AvahiInterfaceAddress;
typedef struct AvahiInterface AvahiInterface;
typedef struct AvahiHwInterface AvahiHwInterface;
#include <avahi-common/llist.h>
#include <avahi-common/address.h>
#include "internal.h"
#include "cache.h"
#include "response-sched.h"
#include "query-sched.h"
#include "probe-sched.h"
#include "dns.h"
#include "announce.h"
#include "browse.h"
#include "querier.h"
#ifdef HAVE_NETLINK
#include "iface-linux.h"
#elif defined(HAVE_PF_ROUTE)
#include "iface-pfroute.h"
#else
typedef struct AvahiInterfaceMonitorOSDep AvahiInterfaceMonitorOSDep;
struct AvahiInterfaceMonitorOSDep {
unsigned query_addr_seq, query_link_seq;
enum {
LIST_IFACE,
LIST_ADDR,
LIST_DONE
} list;
};
#endif
#define AVAHI_MAC_ADDRESS_MAX 32
struct AvahiInterfaceMonitor {
AvahiServer *server;
AvahiHashmap *hashmap;
AVAHI_LLIST_HEAD(AvahiInterface, interfaces);
AVAHI_LLIST_HEAD(AvahiHwInterface, hw_interfaces);
int list_complete;
AvahiInterfaceMonitorOSDep osdep;
};
struct AvahiHwInterface {
AvahiInterfaceMonitor *monitor;
AVAHI_LLIST_FIELDS(AvahiHwInterface, hardware);
char *name;
AvahiIfIndex index;
int flags_ok;
unsigned mtu;
uint8_t mac_address[AVAHI_MAC_ADDRESS_MAX];
size_t mac_address_size;
AvahiSEntryGroup *entry_group;
/* Packet rate limiting */
struct timeval ratelimit_begin;
unsigned ratelimit_counter;
AVAHI_LLIST_HEAD(AvahiInterface, interfaces);
};
struct AvahiInterface {
AvahiInterfaceMonitor *monitor;
AvahiHwInterface *hardware;
AVAHI_LLIST_FIELDS(AvahiInterface, interface);
AVAHI_LLIST_FIELDS(AvahiInterface, by_hardware);
AvahiProtocol protocol;
int announcing;
AvahiAddress local_mcast_address;
int mcast_joined;
AvahiCache *cache;
AvahiQueryScheduler *query_scheduler;
AvahiResponseScheduler * response_scheduler;
AvahiProbeScheduler *probe_scheduler;
AVAHI_LLIST_HEAD(AvahiInterfaceAddress, addresses);
AVAHI_LLIST_HEAD(AvahiAnnouncer, announcers);
AvahiHashmap *queriers_by_key;
AVAHI_LLIST_HEAD(AvahiQuerier, queriers);
};
struct AvahiInterfaceAddress {
AvahiInterfaceMonitor *monitor;
AvahiInterface *interface;
AVAHI_LLIST_FIELDS(AvahiInterfaceAddress, address);
AvahiAddress address;
unsigned prefix_len;
int global_scope;
int deprecated;
AvahiSEntryGroup *entry_group;
};
AvahiInterfaceMonitor *avahi_interface_monitor_new(AvahiServer *server);
void avahi_interface_monitor_free(AvahiInterfaceMonitor *m);
int avahi_interface_monitor_init_osdep(AvahiInterfaceMonitor *m);
void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m);
void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m);
typedef void (*AvahiInterfaceMonitorWalkCallback)(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata);
void avahi_interface_monitor_walk(AvahiInterfaceMonitor *m, AvahiIfIndex idx, AvahiProtocol protocol, AvahiInterfaceMonitorWalkCallback callback, void* userdata);
int avahi_dump_caches(AvahiInterfaceMonitor *m, AvahiDumpCallback callback, void* userdata);
void avahi_interface_monitor_update_rrs(AvahiInterfaceMonitor *m, int remove_rrs);
int avahi_address_is_local(AvahiInterfaceMonitor *m, const AvahiAddress *a);
void avahi_interface_monitor_check_relevant(AvahiInterfaceMonitor *m);
/* AvahiHwInterface */
AvahiHwInterface *avahi_hw_interface_new(AvahiInterfaceMonitor *m, AvahiIfIndex idx);
void avahi_hw_interface_free(AvahiHwInterface *hw, int send_goodbye);
void avahi_hw_interface_update_rrs(AvahiHwInterface *hw, int remove_rrs);
void avahi_hw_interface_check_relevant(AvahiHwInterface *hw);
AvahiHwInterface* avahi_interface_monitor_get_hw_interface(AvahiInterfaceMonitor *m, int idx);
/* AvahiInterface */
AvahiInterface* avahi_interface_new(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, AvahiProtocol protocol);
void avahi_interface_free(AvahiInterface *i, int send_goodbye);
void avahi_interface_update_rrs(AvahiInterface *i, int remove_rrs);
void avahi_interface_check_relevant(AvahiInterface *i);
int avahi_interface_is_relevant(AvahiInterface *i);
void avahi_interface_send_packet(AvahiInterface *i, AvahiDnsPacket *p);
void avahi_interface_send_packet_unicast(AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port);
int avahi_interface_post_query(AvahiInterface *i, AvahiKey *k, int immediately, unsigned *ret_id);
int avahi_interface_withraw_query(AvahiInterface *i, unsigned id);
int avahi_interface_post_response(AvahiInterface *i, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately);
int avahi_interface_post_probe(AvahiInterface *i, AvahiRecord *p, int immediately);
int avahi_interface_match(AvahiInterface *i, AvahiIfIndex idx, AvahiProtocol protocol);
int avahi_interface_address_on_link(AvahiInterface *i, const AvahiAddress *a);
int avahi_interface_has_address(AvahiInterfaceMonitor *m, AvahiIfIndex iface, const AvahiAddress *a);
AvahiInterface* avahi_interface_monitor_get_interface(AvahiInterfaceMonitor *m, AvahiIfIndex idx, AvahiProtocol protocol);
/* AvahiInterfaceAddress */
AvahiInterfaceAddress *avahi_interface_address_new(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *addr, unsigned prefix_len);
void avahi_interface_address_free(AvahiInterfaceAddress *a);
void avahi_interface_address_update_rrs(AvahiInterfaceAddress *a, int remove_rrs);
int avahi_interface_address_is_relevant(AvahiInterfaceAddress *a);
AvahiInterfaceAddress* avahi_interface_monitor_get_address(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *raddr);
AvahiIfIndex avahi_find_interface_for_address(AvahiInterfaceMonitor *m, const AvahiAddress *a);
#endif

227
avahi-core/internal.h Normal file
View File

@@ -0,0 +1,227 @@
#ifndef foointernalhfoo
#define foointernalhfoo
/***
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.
***/
/** A locally registered DNS resource record */
typedef struct AvahiEntry AvahiEntry;
#include <avahi-common/llist.h>
#include <avahi-common/watch.h>
#include <avahi-common/timeval.h>
#include "core.h"
#include "iface.h"
#include "prioq.h"
#include "timeeventq.h"
#include "announce.h"
#include "browse.h"
#include "dns.h"
#include "rrlist.h"
#include "hashmap.h"
#include "wide-area.h"
#include "multicast-lookup.h"
#include "dns-srv-rr.h"
#define AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX 100
#define AVAHI_FLAGS_VALID(flags, max) (!((flags) & ~(max)))
#define AVAHI_RR_HOLDOFF_MSEC 1000
#define AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT 20000
#define AVAHI_RR_RATE_LIMIT_COUNT 15
typedef struct AvahiLegacyUnicastReflectSlot AvahiLegacyUnicastReflectSlot;
struct AvahiLegacyUnicastReflectSlot {
AvahiServer *server;
uint16_t id, original_id;
AvahiAddress address;
uint16_t port;
int interface;
struct timeval elapse_time;
AvahiTimeEvent *time_event;
};
struct AvahiEntry {
AvahiServer *server;
AvahiSEntryGroup *group;
int dead;
AvahiPublishFlags flags;
AvahiRecord *record;
AvahiIfIndex interface;
AvahiProtocol protocol;
AVAHI_LLIST_FIELDS(AvahiEntry, entries);
AVAHI_LLIST_FIELDS(AvahiEntry, by_key);
AVAHI_LLIST_FIELDS(AvahiEntry, by_group);
AVAHI_LLIST_HEAD(AvahiAnnouncer, announcers);
};
struct AvahiSEntryGroup {
AvahiServer *server;
int dead;
AvahiEntryGroupState state;
void* userdata;
AvahiSEntryGroupCallback callback;
unsigned n_probing;
unsigned n_register_try;
struct timeval register_time;
AvahiTimeEvent *register_time_event;
struct timeval established_at;
AVAHI_LLIST_FIELDS(AvahiSEntryGroup, groups);
AVAHI_LLIST_HEAD(AvahiEntry, entries);
};
struct AvahiServer {
const AvahiPoll *poll_api;
AvahiInterfaceMonitor *monitor;
AvahiServerConfig config;
AVAHI_LLIST_HEAD(AvahiEntry, entries);
AvahiHashmap *entries_by_key;
AVAHI_LLIST_HEAD(AvahiSEntryGroup, groups);
AVAHI_LLIST_HEAD(AvahiSRecordBrowser, record_browsers);
AvahiHashmap *record_browser_hashmap;
AVAHI_LLIST_HEAD(AvahiSHostNameResolver, host_name_resolvers);
AVAHI_LLIST_HEAD(AvahiSAddressResolver, address_resolvers);
AVAHI_LLIST_HEAD(AvahiSDomainBrowser, domain_browsers);
AVAHI_LLIST_HEAD(AvahiSServiceTypeBrowser, service_type_browsers);
AVAHI_LLIST_HEAD(AvahiSServiceBrowser, service_browsers);
AVAHI_LLIST_HEAD(AvahiSServiceResolver, service_resolvers);
AVAHI_LLIST_HEAD(AvahiSDNSServerBrowser, dns_server_browsers);
int need_entry_cleanup, need_group_cleanup, need_browser_cleanup;
/* Used for scheduling RR cleanup */
AvahiTimeEvent *cleanup_time_event;
AvahiTimeEventQueue *time_event_queue;
char *host_name, *host_name_fqdn, *domain_name;
int fd_ipv4, fd_ipv6,
/* The following two sockets two are used for reflection only */
fd_legacy_unicast_ipv4, fd_legacy_unicast_ipv6;
AvahiWatch *watch_ipv4, *watch_ipv6,
*watch_legacy_unicast_ipv4, *watch_legacy_unicast_ipv6;
AvahiServerState state;
AvahiServerCallback callback;
void* userdata;
AvahiSEntryGroup *hinfo_entry_group;
AvahiSEntryGroup *browse_domain_entry_group;
unsigned n_host_rr_pending;
/* Used for assembling responses */
AvahiRecordList *record_list;
/* Used for reflection of legacy unicast packets */
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;
};
void avahi_entry_free(AvahiServer*s, AvahiEntry *e);
void avahi_entry_group_free(AvahiServer *s, AvahiSEntryGroup *g);
void avahi_cleanup_dead_entries(AvahiServer *s);
void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary);
void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response);
void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int is_probe);
void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState state);
int avahi_entry_is_commited(AvahiEntry *e);
void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata);
void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void *userdata);
void avahi_server_decrease_host_rr_pending(AvahiServer *s);
int avahi_server_set_errno(AvahiServer *s, int error);
int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name);
int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record);
int avahi_server_add_ptr(
AvahiServer *s,
AvahiSEntryGroup *g,
AvahiIfIndex interface,
AvahiProtocol protocol,
AvahiPublishFlags flags,
uint32_t ttl,
const char *name,
const char *dest);
#define AVAHI_CHECK_VALIDITY(server, expression, error) { \
if (!(expression)) \
return avahi_server_set_errno((server), (error)); \
}
#define AVAHI_CHECK_VALIDITY_RETURN_NULL(server, expression, error) { \
if (!(expression)) { \
avahi_server_set_errno((server), (error)); \
return NULL; \
} \
}
#define AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(server, expression, error) {\
if (!(expression)) { \
ret = avahi_server_set_errno((server), (error)); \
goto fail; \
} \
}
#define AVAHI_ASSERT_TRUE(expression) { \
int __tmp = !!(expression); \
assert(__tmp); \
}
#define AVAHI_ASSERT_SUCCESS(expression) { \
int __tmp = (expression); \
assert(__tmp == 0); \
}
#endif

86
avahi-core/log.c Normal file
View File

@@ -0,0 +1,86 @@
/***
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 <stdio.h>
#include <stdarg.h>
#include "log.h"
static AvahiLogFunction log_function = NULL;
void avahi_set_log_function(AvahiLogFunction function) {
log_function = function;
}
void avahi_log_ap(AvahiLogLevel level, const char*format, va_list ap) {
char txt[256];
vsnprintf(txt, sizeof(txt), format, ap);
if (log_function)
log_function(level, txt);
else
fprintf(stderr, "%s\n", txt);
}
void avahi_log(AvahiLogLevel level, const char*format, ...) {
va_list ap;
va_start(ap, format);
avahi_log_ap(level, format, ap);
va_end(ap);
}
void avahi_log_error(const char*format, ...) {
va_list ap;
va_start(ap, format);
avahi_log_ap(AVAHI_LOG_ERROR, format, ap);
va_end(ap);
}
void avahi_log_warn(const char*format, ...) {
va_list ap;
va_start(ap, format);
avahi_log_ap(AVAHI_LOG_WARN, format, ap);
va_end(ap);
}
void avahi_log_notice(const char*format, ...) {
va_list ap;
va_start(ap, format);
avahi_log_ap(AVAHI_LOG_NOTICE, format, ap);
va_end(ap);
}
void avahi_log_info(const char*format, ...) {
va_list ap;
va_start(ap, format);
avahi_log_ap(AVAHI_LOG_INFO, format, ap);
va_end(ap);
}
void avahi_log_debug(const char*format, ...) {
va_list ap;
va_start(ap, format);
avahi_log_ap(AVAHI_LOG_DEBUG, format, ap);
va_end(ap);
}

73
avahi-core/log.h Normal file
View File

@@ -0,0 +1,73 @@
#ifndef foologhfoo
#define foologhfoo
/***
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 <stdarg.h>
#include <avahi-common/cdecl.h>
#include <avahi-common/gccmacro.h>
/** \file log.h Extensible logging subsystem */
AVAHI_C_DECL_BEGIN
/** Log level for avahi_log_xxx() */
typedef enum {
AVAHI_LOG_ERROR = 0, /**< Error messages */
AVAHI_LOG_WARN = 1, /**< Warning messages */
AVAHI_LOG_NOTICE = 2, /**< Notice messages */
AVAHI_LOG_INFO = 3, /**< Info messages */
AVAHI_LOG_DEBUG = 4, /**< Debug messages */
AVAHI_LOG_LEVEL_MAX
} AvahiLogLevel;
/** Prototype for a user supplied log function */
typedef void (*AvahiLogFunction)(AvahiLogLevel level, const char *txt);
/** Set a user supplied log function, replacing the default which
* prints to log messages unconditionally to STDERR. Pass NULL for
* resetting to the default log function */
void avahi_set_log_function(AvahiLogFunction function);
/** Issue a log message using a va_list object */
void avahi_log_ap(AvahiLogLevel level, const char *format, va_list ap);
/** Issue a log message by passing a log level and a format string */
void avahi_log(AvahiLogLevel level, const char*format, ...) AVAHI_GCC_PRINTF_ATTR23;
/** Shortcut for avahi_log(AVAHI_LOG_ERROR, ...) */
void avahi_log_error(const char*format, ...) AVAHI_GCC_PRINTF_ATTR12;
/** Shortcut for avahi_log(AVAHI_LOG_WARN, ...) */
void avahi_log_warn(const char*format, ...) AVAHI_GCC_PRINTF_ATTR12;
/** Shortcut for avahi_log(AVAHI_LOG_NOTICE, ...) */
void avahi_log_notice(const char*format, ...) AVAHI_GCC_PRINTF_ATTR12;
/** Shortcut for avahi_log(AVAHI_LOG_INFO, ...) */
void avahi_log_info(const char*format, ...) AVAHI_GCC_PRINTF_ATTR12;
/** Shortcut for avahi_log(AVAHI_LOG_DEBUG, ...) */
void avahi_log_debug(const char*format, ...) AVAHI_GCC_PRINTF_ATTR12;
AVAHI_C_DECL_END
#endif

235
avahi-core/lookup.h Normal file
View File

@@ -0,0 +1,235 @@
#ifndef foolookuphfoo
#define foolookuphfoo
/***
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 avahi-core/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. */
/** 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;
#include <avahi-common/cdecl.h>
#include <avahi-common/defs.h>
#include <avahi-core/core.h>
AVAHI_C_DECL_BEGIN
/** 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);
AVAHI_C_DECL_END
#endif

View File

@@ -0,0 +1,350 @@
/***
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 <avahi-common/malloc.h>
#include <avahi-common/timeval.h>
#include "internal.h"
#include "browse.h"
#include "socket.h"
#include "log.h"
#include "hashmap.h"
#include "multicast-lookup.h"
#include "rr-util.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_RESULT_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 tv;
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, &tv);
l->queriers_added = 1;
/* Add a second */
avahi_timeval_add(&tv, 1000000);
/* Issue the ALL_FOR_NOW event one second after the querier was initially created */
l->all_for_now_event = avahi_time_event_new(e->server->time_event_queue, &tv, 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_RESULT_CACHED|AVAHI_LOOKUP_RESULT_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_RESULT_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_RESULT_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);
}

View File

@@ -0,0 +1,51 @@
#ifndef foomulticastlookuphfoo
#define foomulticastlookuphfoo
/***
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

209
avahi-core/netlink.c Normal file
View File

@@ -0,0 +1,209 @@
/***
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 <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include <assert.h>
//#include <linux/socket.h>
#include <avahi-common/malloc.h>
#include "netlink.h"
#include "log.h"
struct AvahiNetlink {
int fd;
unsigned seq;
AvahiNetlinkCallback callback;
void* userdata;
uint8_t* buffer;
size_t buffer_length;
const AvahiPoll *poll_api;
AvahiWatch *watch;
};
int avahi_netlink_work(AvahiNetlink *nl, int block) {
ssize_t bytes;
struct msghdr smsg;
struct cmsghdr *cmsg;
struct ucred *cred;
struct iovec iov;
struct nlmsghdr *p;
char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
assert(nl);
iov.iov_base = nl->buffer;
iov.iov_len = nl->buffer_length;
smsg.msg_name = NULL;
smsg.msg_namelen = 0;
smsg.msg_iov = &iov;
smsg.msg_iovlen = 1;
smsg.msg_control = cred_msg;
smsg.msg_controllen = sizeof(cred_msg);
smsg.msg_flags = (block ? 0 : MSG_DONTWAIT);
if ((bytes = recvmsg(nl->fd, &smsg, 0)) < 0) {
if (errno == EAGAIN || errno == EINTR)
return 0;
avahi_log_error(__FILE__": recvmsg() failed: %s", strerror(errno));
return -1;
}
cmsg = CMSG_FIRSTHDR(&smsg);
if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS) {
avahi_log_warn("No sender credentials received, ignoring data.");
return -1;
}
cred = (struct ucred*) CMSG_DATA(cmsg);
if (cred->uid != 0)
return -1;
p = (struct nlmsghdr *) nl->buffer;
assert(nl->callback);
for (; bytes > 0; p = NLMSG_NEXT(p, bytes)) {
if (!NLMSG_OK(p, (size_t) bytes)) {
avahi_log_warn(__FILE__": packet truncated");
return -1;
}
nl->callback(nl, p, nl->userdata);
}
return 0;
}
static void socket_event(AvahiWatch *w, int fd, AVAHI_GCC_UNUSED AvahiWatchEvent event, void *userdata) {
AvahiNetlink *nl = userdata;
assert(w);
assert(nl);
assert(fd == nl->fd);
avahi_netlink_work(nl, 0);
}
AvahiNetlink *avahi_netlink_new(const AvahiPoll *poll_api, uint32_t groups, void (*cb) (AvahiNetlink *nl, struct nlmsghdr *n, void* userdata), void* userdata) {
int fd = -1;
const int on = 1;
struct sockaddr_nl addr;
AvahiNetlink *nl = NULL;
assert(poll_api);
assert(cb);
if ((fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) {
avahi_log_error(__FILE__": socket(PF_NETLINK): %s", strerror(errno));
return NULL;
}
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_groups = groups;
addr.nl_pid = getpid();
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
avahi_log_error(__FILE__": bind(): %s", strerror(errno));
goto fail;
}
if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
avahi_log_error(__FILE__": SO_PASSCRED: %s", strerror(errno));
goto fail;
}
if (!(nl = avahi_new(AvahiNetlink, 1))) {
avahi_log_error(__FILE__": avahi_new() failed.");
goto fail;
}
nl->poll_api = poll_api;
nl->fd = fd;
nl->seq = 0;
nl->callback = cb;
nl->userdata = userdata;
if (!(nl->buffer = avahi_new(uint8_t, nl->buffer_length = 64*1024))) {
avahi_log_error(__FILE__": avahi_new() failed.");
goto fail;
}
if (!(nl->watch = poll_api->watch_new(poll_api, fd, AVAHI_WATCH_IN, socket_event, nl))) {
avahi_log_error(__FILE__": Failed to create watch.");
goto fail;
}
return nl;
fail:
if (fd >= 0)
close(fd);
if (nl) {
avahi_free(nl->buffer);
avahi_free(nl);
}
return NULL;
}
void avahi_netlink_free(AvahiNetlink *nl) {
assert(nl);
if (nl->watch)
nl->poll_api->watch_free(nl->watch);
if (nl->fd >= 0)
close(nl->fd);
avahi_free(nl->buffer);
avahi_free(nl);
}
int avahi_netlink_send(AvahiNetlink *nl, struct nlmsghdr *m, unsigned *ret_seq) {
assert(nl);
assert(m);
m->nlmsg_seq = nl->seq++;
m->nlmsg_flags |= NLM_F_ACK;
if (send(nl->fd, m, m->nlmsg_len, 0) < 0) {
avahi_log_error(__FILE__": send(): %s", strerror(errno));
return -1;
}
if (ret_seq)
*ret_seq = m->nlmsg_seq;
return 0;
}

41
avahi-core/netlink.h Normal file
View File

@@ -0,0 +1,41 @@
#ifndef foonetlinkhfoo
#define foonetlinkhfoo
/***
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 <sys/socket.h>
#include <asm/types.h>
#include <inttypes.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <avahi-common/watch.h>
typedef struct AvahiNetlink AvahiNetlink;
typedef void (*AvahiNetlinkCallback)(AvahiNetlink *n, struct nlmsghdr *m, void* userdata);
AvahiNetlink *avahi_netlink_new(const AvahiPoll *poll_api, uint32_t groups, AvahiNetlinkCallback callback, void* userdata);
void avahi_netlink_free(AvahiNetlink *n);
int avahi_netlink_send(AvahiNetlink *n, struct nlmsghdr *m, unsigned *ret_seq);
int avahi_netlink_work(AvahiNetlink *n, int block);
#endif

388
avahi-core/prioq.c Normal file
View File

@@ -0,0 +1,388 @@
/***
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 <assert.h>
#include <stdlib.h>
#include <avahi-common/malloc.h>
#include "prioq.h"
AvahiPrioQueue* avahi_prio_queue_new(AvahiPQCompareFunc compare) {
AvahiPrioQueue *q;
assert(compare);
if (!(q = avahi_new(AvahiPrioQueue, 1)))
return NULL; /* OOM */
q->root = q->last = NULL;
q->n_nodes = 0;
q->compare = compare;
return q;
}
void avahi_prio_queue_free(AvahiPrioQueue *q) {
assert(q);
while (q->last)
avahi_prio_queue_remove(q, q->last);
assert(!q->n_nodes);
avahi_free(q);
}
static AvahiPrioQueueNode* get_node_at_xy(AvahiPrioQueue *q, unsigned x, unsigned y) {
unsigned r;
AvahiPrioQueueNode *n;
assert(q);
n = q->root;
assert(n);
for (r = 0; r < y; r++) {
assert(n);
if ((x >> (y-r-1)) & 1)
n = n->right;
else
n = n->left;
}
assert(n->x == x);
assert(n->y == y);
return n;
}
static void exchange_nodes(AvahiPrioQueue *q, AvahiPrioQueueNode *a, AvahiPrioQueueNode *b) {
AvahiPrioQueueNode *l, *r, *p, *ap, *an, *bp, *bn;
unsigned t;
assert(q);
assert(a);
assert(b);
assert(a != b);
/* Swap positions */
t = a->x; a->x = b->x; b->x = t;
t = a->y; a->y = b->y; b->y = t;
if (a->parent == b) {
/* B is parent of A */
p = b->parent;
b->parent = a;
if ((a->parent = p)) {
if (a->parent->left == b)
a->parent->left = a;
else
a->parent->right = a;
} else
q->root = a;
if (b->left == a) {
if ((b->left = a->left))
b->left->parent = b;
a->left = b;
r = a->right;
if ((a->right = b->right))
a->right->parent = a;
if ((b->right = r))
b->right->parent = b;
} else {
if ((b->right = a->right))
b->right->parent = b;
a->right = b;
l = a->left;
if ((a->left = b->left))
a->left->parent = a;
if ((b->left = l))
b->left->parent = b;
}
} else if (b->parent == a) {
/* A ist parent of B */
p = a->parent;
a->parent = b;
if ((b->parent = p)) {
if (b->parent->left == a)
b->parent->left = b;
else
b->parent->right = b;
} else
q->root = b;
if (a->left == b) {
if ((a->left = b->left))
a->left->parent = a;
b->left = a;
r = a->right;
if ((a->right = b->right))
a->right->parent = a;
if ((b->right = r))
b->right->parent = b;
} else {
if ((a->right = b->right))
a->right->parent = a;
b->right = a;
l = a->left;
if ((a->left = b->left))
a->left->parent = a;
if ((b->left = l))
b->left->parent = b;
}
} else {
AvahiPrioQueueNode *apl = NULL, *bpl = NULL;
/* Swap parents */
ap = a->parent;
bp = b->parent;
if (ap)
apl = ap->left;
if (bp)
bpl = bp->left;
if ((a->parent = bp)) {
if (bpl == b)
bp->left = a;
else
bp->right = a;
} else
q->root = a;
if ((b->parent = ap)) {
if (apl == a)
ap->left = b;
else
ap->right = b;
} else
q->root = b;
/* Swap children */
l = a->left;
r = a->right;
if ((a->left = b->left))
a->left->parent = a;
if ((b->left = l))
b->left->parent = b;
if ((a->right = b->right))
a->right->parent = a;
if ((b->right = r))
b->right->parent = b;
}
/* Swap siblings */
ap = a->prev; an = a->next;
bp = b->prev; bn = b->next;
if (a->next == b) {
/* A is predecessor of B */
a->prev = b;
b->next = a;
if ((a->next = bn))
a->next->prev = a;
else
q->last = a;
if ((b->prev = ap))
b->prev->next = b;
} else if (b->next == a) {
/* B is predecessor of A */
a->next = b;
b->prev = a;
if ((a->prev = bp))
a->prev->next = a;
if ((b->next = an))
b->next->prev = b;
else
q->last = b;
} else {
/* A is no neighbour of B */
if ((a->prev = bp))
a->prev->next = a;
if ((a->next = bn))
a->next->prev = a;
else
q->last = a;
if ((b->prev = ap))
b->prev->next = b;
if ((b->next = an))
b->next->prev = b;
else
q->last = b;
}
}
/* Move a node to the correct position */
void avahi_prio_queue_shuffle(AvahiPrioQueue *q, AvahiPrioQueueNode *n) {
assert(q);
assert(n);
assert(n->queue == q);
/* Move up until the position is OK */
while (n->parent && q->compare(n->parent->data, n->data) > 0)
exchange_nodes(q, n, n->parent);
/* Move down until the position is OK */
for (;;) {
AvahiPrioQueueNode *min;
if (!(min = n->left)) {
/* No children */
assert(!n->right);
break;
}
if (n->right && q->compare(n->right->data, min->data) < 0)
min = n->right;
/* min now contains the smaller one of our two children */
if (q->compare(n->data, min->data) <= 0)
/* Order OK */
break;
exchange_nodes(q, n, min);
}
}
AvahiPrioQueueNode* avahi_prio_queue_put(AvahiPrioQueue *q, void* data) {
AvahiPrioQueueNode *n;
assert(q);
if (!(n = avahi_new(AvahiPrioQueueNode, 1)))
return NULL; /* OOM */
n->queue = q;
n->data = data;
if (q->last) {
assert(q->root);
assert(q->n_nodes);
n->y = q->last->y;
n->x = q->last->x+1;
if (n->x >= ((unsigned) 1 << n->y)) {
n->x = 0;
n->y++;
}
q->last->next = n;
n->prev = q->last;
assert(n->y > 0);
n->parent = get_node_at_xy(q, n->x/2, n->y-1);
if (n->x & 1)
n->parent->right = n;
else
n->parent->left = n;
} else {
assert(!q->root);
assert(!q->n_nodes);
n->y = n->x = 0;
q->root = n;
n->prev = n->parent = NULL;
}
n->next = n->left = n->right = NULL;
q->last = n;
q->n_nodes++;
avahi_prio_queue_shuffle(q, n);
return n;
}
void avahi_prio_queue_remove(AvahiPrioQueue *q, AvahiPrioQueueNode *n) {
assert(q);
assert(n);
assert(q == n->queue);
if (n != q->last) {
AvahiPrioQueueNode *replacement = q->last;
exchange_nodes(q, replacement, n);
avahi_prio_queue_remove(q, n);
avahi_prio_queue_shuffle(q, replacement);
return;
}
assert(n == q->last);
assert(!n->next);
assert(!n->left);
assert(!n->right);
q->last = n->prev;
if (n->prev) {
n->prev->next = NULL;
assert(n->parent);
} else
assert(!n->parent);
if (n->parent) {
assert(n->prev);
if (n->parent->left == n) {
assert(n->parent->right == NULL);
n->parent->left = NULL;
} else {
assert(n->parent->right == n);
assert(n->parent->left != NULL);
n->parent->right = NULL;
}
} else {
assert(q->root == n);
assert(!n->prev);
assert(q->n_nodes == 1);
q->root = NULL;
}
avahi_free(n);
assert(q->n_nodes > 0);
q->n_nodes--;
}

49
avahi-core/prioq.h Normal file
View File

@@ -0,0 +1,49 @@
#ifndef fooprioqhfoo
#define fooprioqhfoo
/***
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 AvahiPrioQueue AvahiPrioQueue;
typedef struct AvahiPrioQueueNode AvahiPrioQueueNode;
typedef int (*AvahiPQCompareFunc)(const void* a, const void* b);
struct AvahiPrioQueue {
AvahiPrioQueueNode *root, *last;
unsigned n_nodes;
AvahiPQCompareFunc compare;
};
struct AvahiPrioQueueNode {
AvahiPrioQueue *queue;
void* data;
unsigned x, y;
AvahiPrioQueueNode *left, *right, *parent, *next, *prev;
};
AvahiPrioQueue* avahi_prio_queue_new(AvahiPQCompareFunc compare);
void avahi_prio_queue_free(AvahiPrioQueue *q);
AvahiPrioQueueNode* avahi_prio_queue_put(AvahiPrioQueue *q, void* data);
void avahi_prio_queue_remove(AvahiPrioQueue *q, AvahiPrioQueueNode *n);
void avahi_prio_queue_shuffle(AvahiPrioQueue *q, AvahiPrioQueueNode *n);
#endif

397
avahi-core/probe-sched.c Normal file
View File

@@ -0,0 +1,397 @@
/***
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 <avahi-common/domain.h>
#include <avahi-common/timeval.h>
#include <avahi-common/malloc.h>
#include "probe-sched.h"
#include "log.h"
#include "rr-util.h"
#define AVAHI_PROBE_HISTORY_MSEC 150
#define AVAHI_PROBE_DEFER_MSEC 50
typedef struct AvahiProbeJob AvahiProbeJob;
struct AvahiProbeJob {
AvahiProbeScheduler *scheduler;
AvahiTimeEvent *time_event;
int chosen; /* Use for packet assembling */
int done;
struct timeval delivery;
AvahiRecord *record;
AVAHI_LLIST_FIELDS(AvahiProbeJob, jobs);
};
struct AvahiProbeScheduler {
AvahiInterface *interface;
AvahiTimeEventQueue *time_event_queue;
AVAHI_LLIST_HEAD(AvahiProbeJob, jobs);
AVAHI_LLIST_HEAD(AvahiProbeJob, history);
};
static AvahiProbeJob* job_new(AvahiProbeScheduler *s, AvahiRecord *record, int done) {
AvahiProbeJob *pj;
assert(s);
assert(record);
if (!(pj = avahi_new(AvahiProbeJob, 1))) {
avahi_log_error(__FILE__": Out of memory");
return NULL; /* OOM */
}
pj->scheduler = s;
pj->record = avahi_record_ref(record);
pj->time_event = NULL;
pj->chosen = 0;
if ((pj->done = done))
AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->history, pj);
else
AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->jobs, pj);
return pj;
}
static void job_free(AvahiProbeScheduler *s, AvahiProbeJob *pj) {
assert(pj);
if (pj->time_event)
avahi_time_event_free(pj->time_event);
if (pj->done)
AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->history, pj);
else
AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->jobs, pj);
avahi_record_unref(pj->record);
avahi_free(pj);
}
static void elapse_callback(AvahiTimeEvent *e, void* data);
static void job_set_elapse_time(AvahiProbeScheduler *s, AvahiProbeJob *pj, unsigned msec, unsigned jitter) {
struct timeval tv;
assert(s);
assert(pj);
avahi_elapse_time(&tv, msec, jitter);
if (pj->time_event)
avahi_time_event_update(pj->time_event, &tv);
else
pj->time_event = avahi_time_event_new(s->time_event_queue, &tv, elapse_callback, pj);
}
static void job_mark_done(AvahiProbeScheduler *s, AvahiProbeJob *pj) {
assert(s);
assert(pj);
assert(!pj->done);
AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->jobs, pj);
AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->history, pj);
pj->done = 1;
job_set_elapse_time(s, pj, AVAHI_PROBE_HISTORY_MSEC, 0);
gettimeofday(&pj->delivery, NULL);
}
AvahiProbeScheduler *avahi_probe_scheduler_new(AvahiInterface *i) {
AvahiProbeScheduler *s;
assert(i);
if (!(s = avahi_new(AvahiProbeScheduler, 1))) {
avahi_log_error(__FILE__": Out of memory");
return NULL;
}
s->interface = i;
s->time_event_queue = i->monitor->server->time_event_queue;
AVAHI_LLIST_HEAD_INIT(AvahiProbeJob, s->jobs);
AVAHI_LLIST_HEAD_INIT(AvahiProbeJob, s->history);
return s;
}
void avahi_probe_scheduler_free(AvahiProbeScheduler *s) {
assert(s);
avahi_probe_scheduler_clear(s);
avahi_free(s);
}
void avahi_probe_scheduler_clear(AvahiProbeScheduler *s) {
assert(s);
while (s->jobs)
job_free(s, s->jobs);
while (s->history)
job_free(s, s->history);
}
static int packet_add_probe_query(AvahiProbeScheduler *s, AvahiDnsPacket *p, AvahiProbeJob *pj) {
size_t size;
AvahiKey *k;
int b;
assert(s);
assert(p);
assert(pj);
assert(!pj->chosen);
/* Estimate the size for this record */
size =
avahi_key_get_estimate_size(pj->record->key) +
avahi_record_get_estimate_size(pj->record);
/* Too large */
if (size > avahi_dns_packet_space(p))
return 0;
/* Create the probe query */
if (!(k = avahi_key_new(pj->record->key->name, pj->record->key->clazz, AVAHI_DNS_TYPE_ANY)))
return 0; /* OOM */
b = !!avahi_dns_packet_append_key(p, k, 0);
assert(b);
/* Mark this job for addition to the packet */
pj->chosen = 1;
/* Scan for more jobs whith matching key pattern */
for (pj = s->jobs; pj; pj = pj->jobs_next) {
if (pj->chosen)
continue;
/* Does the record match the probe? */
if (k->clazz != pj->record->key->clazz || !avahi_domain_equal(k->name, pj->record->key->name))
continue;
/* This job wouldn't fit in */
if (avahi_record_get_estimate_size(pj->record) > avahi_dns_packet_space(p))
break;
/* Mark this job for addition to the packet */
pj->chosen = 1;
}
avahi_key_unref(k);
return 1;
}
static void elapse_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* data) {
AvahiProbeJob *pj = data, *next;
AvahiProbeScheduler *s;
AvahiDnsPacket *p;
unsigned n;
assert(pj);
s = pj->scheduler;
if (pj->done) {
/* Lets remove it from the history */
job_free(s, pj);
return;
}
if (!(p = avahi_dns_packet_new_query(s->interface->hardware->mtu)))
return; /* OOM */
n = 1;
/* Add the import probe */
if (!packet_add_probe_query(s, p, pj)) {
size_t size;
AvahiKey *k;
int b;
avahi_dns_packet_free(p);
/* The probe didn't fit in the package, so let's allocate a larger one */
size =
avahi_key_get_estimate_size(pj->record->key) +
avahi_record_get_estimate_size(pj->record) +
AVAHI_DNS_PACKET_HEADER_SIZE;
if (!(p = avahi_dns_packet_new_query(size + AVAHI_DNS_PACKET_EXTRA_SIZE)))
return; /* OOM */
if (!(k = avahi_key_new(pj->record->key->name, pj->record->key->clazz, AVAHI_DNS_TYPE_ANY))) {
avahi_dns_packet_free(p);
return; /* OOM */
}
b = avahi_dns_packet_append_key(p, k, 0) && avahi_dns_packet_append_record(p, pj->record, 0, 0);
avahi_key_unref(k);
if (b) {
avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_NSCOUNT, 1);
avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, 1);
avahi_interface_send_packet(s->interface, p);
} else
avahi_log_warn("Probe record too large, cannot send");
avahi_dns_packet_free(p);
job_mark_done(s, pj);
return;
}
/* Try to fill up packet with more probes, if available */
for (pj = s->jobs; pj; pj = pj->jobs_next) {
if (pj->chosen)
continue;
if (!packet_add_probe_query(s, p, pj))
break;
n++;
}
avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, n);
n = 0;
/* Now add the chosen records to the authorative section */
for (pj = s->jobs; pj; pj = next) {
next = pj->jobs_next;
if (!pj->chosen)
continue;
if (!avahi_dns_packet_append_record(p, pj->record, 0, 0)) {
/* avahi_log_warn("Bad probe size estimate!"); */
/* Unmark all following jobs */
for (; pj; pj = pj->jobs_next)
pj->chosen = 0;
break;
}
job_mark_done(s, pj);
n ++;
}
avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_NSCOUNT, n);
/* Send it now */
avahi_interface_send_packet(s->interface, p);
avahi_dns_packet_free(p);
}
static AvahiProbeJob* find_scheduled_job(AvahiProbeScheduler *s, AvahiRecord *record) {
AvahiProbeJob *pj;
assert(s);
assert(record);
for (pj = s->jobs; pj; pj = pj->jobs_next) {
assert(!pj->done);
if (avahi_record_equal_no_ttl(pj->record, record))
return pj;
}
return NULL;
}
static AvahiProbeJob* find_history_job(AvahiProbeScheduler *s, AvahiRecord *record) {
AvahiProbeJob *pj;
assert(s);
assert(record);
for (pj = s->history; pj; pj = pj->jobs_next) {
assert(pj->done);
if (avahi_record_equal_no_ttl(pj->record, record)) {
/* Check whether this entry is outdated */
if (avahi_age(&pj->delivery) > AVAHI_PROBE_HISTORY_MSEC*1000) {
/* it is outdated, so let's remove it */
job_free(s, pj);
return NULL;
}
return pj;
}
}
return NULL;
}
int avahi_probe_scheduler_post(AvahiProbeScheduler *s, AvahiRecord *record, int immediately) {
AvahiProbeJob *pj;
struct timeval tv;
assert(s);
assert(record);
assert(!avahi_key_is_pattern(record->key));
if ((pj = find_history_job(s, record)))
return 0;
avahi_elapse_time(&tv, immediately ? 0 : AVAHI_PROBE_DEFER_MSEC, 0);
if ((pj = find_scheduled_job(s, record))) {
if (avahi_timeval_compare(&tv, &pj->delivery) < 0) {
/* If the new entry should be scheduled earlier, update the old entry */
pj->delivery = tv;
avahi_time_event_update(pj->time_event, &pj->delivery);
}
return 1;
} else {
/* Create a new job and schedule it */
if (!(pj = job_new(s, record, 0)))
return 0; /* OOM */
pj->delivery = tv;
pj->time_event = avahi_time_event_new(s->time_event_queue, &pj->delivery, elapse_callback, pj);
/* avahi_log_debug("Accepted new probe job."); */
return 1;
}
}

34
avahi-core/probe-sched.h Normal file
View File

@@ -0,0 +1,34 @@
#ifndef fooprobeschedhfoo
#define fooprobeschedhfoo
/***
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 AvahiProbeScheduler AvahiProbeScheduler;
#include <avahi-common/address.h>
#include "iface.h"
AvahiProbeScheduler *avahi_probe_scheduler_new(AvahiInterface *i);
void avahi_probe_scheduler_free(AvahiProbeScheduler *s);
void avahi_probe_scheduler_clear(AvahiProbeScheduler *s);
int avahi_probe_scheduler_post(AvahiProbeScheduler *s, AvahiRecord *record, int immediately);
#endif

175
avahi-core/publish.h Normal file
View File

@@ -0,0 +1,175 @@
#ifndef foopublishhfoo
#define foopublishhfoo
/***
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 core/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. */
/** A group of locally registered DNS RRs */
typedef struct AvahiSEntryGroup AvahiSEntryGroup;
#include <avahi-common/cdecl.h>
#include <avahi-core/core.h>
AVAHI_C_DECL_BEGIN
/** 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). */
AvahiPublishFlags flags, /**< Special flags for this record */
AvahiRecord *r /**< The record to add. This function increases the reference counter 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,
AvahiPublishFlags 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,
AvahiPublishFlags flags,
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 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,
AvahiPublishFlags flags,
const char *name,
const char *type,
const char *domain,
const char *host,
uint16_t port,
AvahiStringList *strlst);
/** Add a subtype for an already existing service */
int avahi_server_add_service_subtype(
AvahiServer *s,
AvahiSEntryGroup *g,
AvahiIfIndex interface,
AvahiProtocol protocol,
AvahiPublishFlags flags,
const char *name, /**< Specify the name of main service you already added here */
const char *type, /**< Specify the main type of the service you already added here */
const char *domain, /**< Specify the main type of the service you already added here */
const char *subtype /**< The new subtype for the specified service */ );
/** Update the TXT record for a service with the data from the specified string list */
int avahi_server_update_service_txt_strlst(
AvahiServer *s,
AvahiSEntryGroup *g,
AvahiIfIndex interface,
AvahiProtocol protocol,
AvahiPublishFlags flags,
const char *name,
const char *type,
const char *domain,
AvahiStringList *strlst);
/** Update the TXT record for a service with the NULL termonate list of strings */
int avahi_server_update_service_txt(
AvahiServer *s,
AvahiSEntryGroup *g,
AvahiIfIndex interface,
AvahiProtocol protocol,
AvahiPublishFlags flags,
const char *name,
const char *type,
const char *domain,
...) AVAHI_GCC_SENTINEL;
/** Check if there is a service locally defined and return the entry group it is attached to. Returns NULL if the service isn't local*/
int avahi_server_get_group_of_service(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain, AvahiSEntryGroup** ret_group);
AVAHI_C_DECL_END
#endif

268
avahi-core/querier.c Normal file
View File

@@ -0,0 +1,268 @@
/***
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 <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;
unsigned post_id;
int post_id_valid;
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(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void *userdata) {
AvahiQuerier *q = userdata;
struct timeval tv;
assert(q);
if (q->n_used <= 0) {
/* We are not referenced by anyone anymore, so let's free
* ourselves. We should not send out any further queries from
* this querier object anymore. */
avahi_querier_free(q);
return;
}
if (avahi_interface_post_query(q->interface, q->key, 0, &q->post_id)) {
/* The queue accepted our query. We store the query id here,
* that allows us to drop the query at a later point if the
* query is very short-lived. */
q->post_id_valid = 1;
}
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. This is used for generating the
* ALL_FOR_NOW event one second after the querier was
* initially created. */
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;
q->post_id_valid = 0;
gettimeofday(&q->creation_time, NULL);
/* Do the initial query */
if (avahi_interface_post_query(i, key, 0, &q->post_id))
q->post_id_valid = 1;
/* 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. This is used for generating the
* ALL_FOR_NOW event one second after the querier was initially
* created. */
if (ret_ctime)
*ret_ctime = q->creation_time;
}
void avahi_querier_remove(AvahiInterface *i, AvahiKey *key) {
AvahiQuerier *q;
/* There was no querier for this RR key, or it wasn't referenced
* by anyone. */
if (!(q = avahi_hashmap_lookup(i->queriers_by_key, key)) || q->n_used <= 0)
return;
if ((--q->n_used) <= 0) {
/* Nobody references us anymore. */
if (q->post_id_valid && avahi_interface_withraw_query(i, q->post_id)) {
/* We succeeded in withdrawing our query from the queue,
* so let's drop dead. */
avahi_querier_free(q);
}
/* If we failed to withdraw our query from the queue, we stay
* alive, in case someone else might recycle our querier at a
* later point. We are freed at our next expiry, in case
* nobody recycled us. */
}
}
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_shall_refresh_cache(AvahiInterface *i, AvahiKey *key) {
AvahiQuerier *q;
assert(i);
assert(key);
/* Called by the cache maintainer */
if (!(q = avahi_hashmap_lookup(i->queriers_by_key, key)))
/* This key is currently not subscribed at all, so no cache
* refresh is needed */
return 0;
if (q->n_used <= 0) {
/* If this is an entry nobody references right now, don't
* consider it "existing". */
/* Remove this querier since it is referenced by nobody
* and the cached data will soon be out of date */
avahi_querier_free(q);
/* Tell the cache that no refresh is needed */
return 0;
} else {
struct timeval tv;
/* We can defer our query a little, since the cache will now
* issue a refresh query anyway. */
avahi_elapse_time(&tv, q->sec_delay*1000, 0);
avahi_time_event_update(q->time_event, &tv);
/* Tell the cache that a refresh should be issued */
return 1;
}
}
void avahi_querier_free_all(AvahiInterface *i) {
assert(i);
while (i->queriers)
avahi_querier_free(i->queriers);
}

48
avahi-core/querier.h Normal file
View File

@@ -0,0 +1,48 @@
#ifndef fooquerierhfoo
#define fooquerierhfoo
/***
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_shall_refresh_cache(AvahiInterface *i, AvahiKey *key);
#endif

450
avahi-core/query-sched.c Normal file
View File

@@ -0,0 +1,450 @@
/***
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 <avahi-common/timeval.h>
#include <avahi-common/malloc.h>
#include "query-sched.h"
#include "log.h"
#define AVAHI_QUERY_HISTORY_MSEC 100
#define AVAHI_QUERY_DEFER_MSEC 100
typedef struct AvahiQueryJob AvahiQueryJob;
typedef struct AvahiKnownAnswer AvahiKnownAnswer;
struct AvahiQueryJob {
unsigned id;
int n_posted;
AvahiQueryScheduler *scheduler;
AvahiTimeEvent *time_event;
int done;
struct timeval delivery;
AvahiKey *key;
/* Jobs are stored in a simple linked list. It might turn out in
* the future that this list grows too long and we must switch to
* some other kind of data structure. This needs further
* investigation. I expect the list to be very short (< 20
* entries) most of the time, but this might be a wrong
* assumption, especially on setups where traffic reflection is
* involved. */
AVAHI_LLIST_FIELDS(AvahiQueryJob, jobs);
};
struct AvahiKnownAnswer {
AvahiQueryScheduler *scheduler;
AvahiRecord *record;
AVAHI_LLIST_FIELDS(AvahiKnownAnswer, known_answer);
};
struct AvahiQueryScheduler {
AvahiInterface *interface;
AvahiTimeEventQueue *time_event_queue;
unsigned next_id;
AVAHI_LLIST_HEAD(AvahiQueryJob, jobs);
AVAHI_LLIST_HEAD(AvahiQueryJob, history);
AVAHI_LLIST_HEAD(AvahiKnownAnswer, known_answers);
};
static AvahiQueryJob* job_new(AvahiQueryScheduler *s, AvahiKey *key, int done) {
AvahiQueryJob *qj;
assert(s);
assert(key);
if (!(qj = avahi_new(AvahiQueryJob, 1))) {
avahi_log_error(__FILE__": Out of memory");
return NULL;
}
qj->scheduler = s;
qj->key = avahi_key_ref(key);
qj->time_event = NULL;
qj->n_posted = 1;
qj->id = s->next_id++;
if ((qj->done = done))
AVAHI_LLIST_PREPEND(AvahiQueryJob, jobs, s->history, qj);
else
AVAHI_LLIST_PREPEND(AvahiQueryJob, jobs, s->jobs, qj);
return qj;
}
static void job_free(AvahiQueryScheduler *s, AvahiQueryJob *qj) {
assert(s);
assert(qj);
if (qj->time_event)
avahi_time_event_free(qj->time_event);
if (qj->done)
AVAHI_LLIST_REMOVE(AvahiQueryJob, jobs, s->history, qj);
else
AVAHI_LLIST_REMOVE(AvahiQueryJob, jobs, s->jobs, qj);
avahi_key_unref(qj->key);
avahi_free(qj);
}
static void elapse_callback(AvahiTimeEvent *e, void* data);
static void job_set_elapse_time(AvahiQueryScheduler *s, AvahiQueryJob *qj, unsigned msec, unsigned jitter) {
struct timeval tv;
assert(s);
assert(qj);
avahi_elapse_time(&tv, msec, jitter);
if (qj->time_event)
avahi_time_event_update(qj->time_event, &tv);
else
qj->time_event = avahi_time_event_new(s->time_event_queue, &tv, elapse_callback, qj);
}
static void job_mark_done(AvahiQueryScheduler *s, AvahiQueryJob *qj) {
assert(s);
assert(qj);
assert(!qj->done);
AVAHI_LLIST_REMOVE(AvahiQueryJob, jobs, s->jobs, qj);
AVAHI_LLIST_PREPEND(AvahiQueryJob, jobs, s->history, qj);
qj->done = 1;
job_set_elapse_time(s, qj, AVAHI_QUERY_HISTORY_MSEC, 0);
gettimeofday(&qj->delivery, NULL);
}
AvahiQueryScheduler *avahi_query_scheduler_new(AvahiInterface *i) {
AvahiQueryScheduler *s;
assert(i);
if (!(s = avahi_new(AvahiQueryScheduler, 1))) {
avahi_log_error(__FILE__": Out of memory");
return NULL; /* OOM */
}
s->interface = i;
s->time_event_queue = i->monitor->server->time_event_queue;
s->next_id = 0;
AVAHI_LLIST_HEAD_INIT(AvahiQueryJob, s->jobs);
AVAHI_LLIST_HEAD_INIT(AvahiQueryJob, s->history);
AVAHI_LLIST_HEAD_INIT(AvahiKnownAnswer, s->known_answers);
return s;
}
void avahi_query_scheduler_free(AvahiQueryScheduler *s) {
assert(s);
assert(!s->known_answers);
avahi_query_scheduler_clear(s);
avahi_free(s);
}
void avahi_query_scheduler_clear(AvahiQueryScheduler *s) {
assert(s);
while (s->jobs)
job_free(s, s->jobs);
while (s->history)
job_free(s, s->history);
}
static void* known_answer_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
AvahiQueryScheduler *s = userdata;
AvahiKnownAnswer *ka;
assert(c);
assert(pattern);
assert(e);
assert(s);
if (avahi_cache_entry_half_ttl(c, e))
return NULL;
if (!(ka = avahi_new0(AvahiKnownAnswer, 1))) {
avahi_log_error(__FILE__": Out of memory");
return NULL;
}
ka->scheduler = s;
ka->record = avahi_record_ref(e->record);
AVAHI_LLIST_PREPEND(AvahiKnownAnswer, known_answer, s->known_answers, ka);
return NULL;
}
static int packet_add_query_job(AvahiQueryScheduler *s, AvahiDnsPacket *p, AvahiQueryJob *qj) {
assert(s);
assert(p);
assert(qj);
if (!avahi_dns_packet_append_key(p, qj->key, 0))
return 0;
/* Add all matching known answers to the list */
avahi_cache_walk(s->interface->cache, qj->key, known_answer_walk_callback, s);
job_mark_done(s, qj);
return 1;
}
static void append_known_answers_and_send(AvahiQueryScheduler *s, AvahiDnsPacket *p) {
AvahiKnownAnswer *ka;
unsigned n;
assert(s);
assert(p);
n = 0;
while ((ka = s->known_answers)) {
int too_large = 0;
while (!avahi_dns_packet_append_record(p, ka->record, 0, 0)) {
if (avahi_dns_packet_is_empty(p)) {
/* The record is too large to fit into one packet, so
there's no point in sending it. Better is letting
the owner of the record send it as a response. This
has the advantage of a cache refresh. */
too_large = 1;
break;
}
avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) | AVAHI_DNS_FLAG_TC);
avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n);
avahi_interface_send_packet(s->interface, p);
avahi_dns_packet_free(p);
p = avahi_dns_packet_new_query(s->interface->hardware->mtu);
n = 0;
}
AVAHI_LLIST_REMOVE(AvahiKnownAnswer, known_answer, s->known_answers, ka);
avahi_record_unref(ka->record);
avahi_free(ka);
if (!too_large)
n++;
}
avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n);
avahi_interface_send_packet(s->interface, p);
avahi_dns_packet_free(p);
}
static void elapse_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* data) {
AvahiQueryJob *qj = data;
AvahiQueryScheduler *s;
AvahiDnsPacket *p;
unsigned n;
int b;
assert(qj);
s = qj->scheduler;
if (qj->done) {
/* Lets remove it from the history */
job_free(s, qj);
return;
}
assert(!s->known_answers);
if (!(p = avahi_dns_packet_new_query(s->interface->hardware->mtu)))
return; /* OOM */
b = packet_add_query_job(s, p, qj);
assert(b); /* An query must always fit in */
n = 1;
/* Try to fill up packet with more queries, if available */
while (s->jobs) {
if (!packet_add_query_job(s, p, s->jobs))
break;
n++;
}
avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, n);
/* Now add known answers */
append_known_answers_and_send(s, p);
}
static AvahiQueryJob* find_scheduled_job(AvahiQueryScheduler *s, AvahiKey *key) {
AvahiQueryJob *qj;
assert(s);
assert(key);
for (qj = s->jobs; qj; qj = qj->jobs_next) {
assert(!qj->done);
if (avahi_key_equal(qj->key, key))
return qj;
}
return NULL;
}
static AvahiQueryJob* find_history_job(AvahiQueryScheduler *s, AvahiKey *key) {
AvahiQueryJob *qj;
assert(s);
assert(key);
for (qj = s->history; qj; qj = qj->jobs_next) {
assert(qj->done);
if (avahi_key_equal(qj->key, key)) {
/* Check whether this entry is outdated */
if (avahi_age(&qj->delivery) > AVAHI_QUERY_HISTORY_MSEC*1000) {
/* it is outdated, so let's remove it */
job_free(s, qj);
return NULL;
}
return qj;
}
}
return NULL;
}
int avahi_query_scheduler_post(AvahiQueryScheduler *s, AvahiKey *key, int immediately, unsigned *ret_id) {
struct timeval tv;
AvahiQueryJob *qj;
assert(s);
assert(key);
if ((qj = find_history_job(s, key)))
return 0;
avahi_elapse_time(&tv, immediately ? 0 : AVAHI_QUERY_DEFER_MSEC, 0);
if ((qj = find_scheduled_job(s, key))) {
/* Duplicate questions suppression */
if (avahi_timeval_compare(&tv, &qj->delivery) < 0) {
/* If the new entry should be scheduled earlier,
* update the old entry */
qj->delivery = tv;
avahi_time_event_update(qj->time_event, &qj->delivery);
}
qj->n_posted++;
} else {
if (!(qj = job_new(s, key, 0)))
return 0; /* OOM */
qj->delivery = tv;
qj->time_event = avahi_time_event_new(s->time_event_queue, &qj->delivery, elapse_callback, qj);
}
if (ret_id)
*ret_id = qj->id;
return 1;
}
void avahi_query_scheduler_incoming(AvahiQueryScheduler *s, AvahiKey *key) {
AvahiQueryJob *qj;
assert(s);
assert(key);
/* This function is called whenever an incoming query was
* received. We drop scheduled queries that match. The keyword is
* "DUPLICATE QUESTION SUPPRESION". */
if ((qj = find_scheduled_job(s, key))) {
job_mark_done(s, qj);
return;
}
/* Look if there's a history job for this key. If there is, just
* update the elapse time */
if (!(qj = find_history_job(s, key)))
if (!(qj = job_new(s, key, 1)))
return; /* OOM */
gettimeofday(&qj->delivery, NULL);
job_set_elapse_time(s, qj, AVAHI_QUERY_HISTORY_MSEC, 0);
}
int avahi_query_scheduler_withdraw_by_id(AvahiQueryScheduler *s, unsigned id) {
AvahiQueryJob *qj;
assert(s);
/* Very short lived queries can withdraw an already scheduled item
* from the queue using this function, simply by passing the id
* returned by avahi_query_scheduler_post(). */
for (qj = s->jobs; qj; qj = qj->jobs_next) {
assert(!qj->done);
if (qj->id == id) {
/* Entry found */
assert(qj->n_posted >= 1);
if (--qj->n_posted <= 0) {
/* We withdraw this job only if the calling object was
* the only remaining poster. (Usually this is the
* case since there should exist only one querier per
* key, but there are exceptions, notably reflected
* traffic.) */
job_free(s, qj);
return 1;
}
}
}
return 0;
}

36
avahi-core/query-sched.h Normal file
View File

@@ -0,0 +1,36 @@
#ifndef fooqueryschedhfoo
#define fooqueryschedhfoo
/***
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 AvahiQueryScheduler AvahiQueryScheduler;
#include <avahi-common/address.h>
#include "iface.h"
AvahiQueryScheduler *avahi_query_scheduler_new(AvahiInterface *i);
void avahi_query_scheduler_free(AvahiQueryScheduler *s);
void avahi_query_scheduler_clear(AvahiQueryScheduler *s);
int avahi_query_scheduler_post(AvahiQueryScheduler *s, AvahiKey *key, int immediately, unsigned *ret_id);
int avahi_query_scheduler_withdraw_by_id(AvahiQueryScheduler *s, unsigned id);
void avahi_query_scheduler_incoming(AvahiQueryScheduler *s, AvahiKey *key);
#endif

View File

@@ -0,0 +1,268 @@
/***
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 <avahi-common/timeval.h>
#include <avahi-common/malloc.h>
#include <avahi-common/error.h>
#include <avahi-common/domain.h>
#include "browse.h"
#define TIMEOUT_MSEC 5000
struct AvahiSAddressResolver {
AvahiServer *server;
AvahiAddress address;
AvahiSRecordBrowser *record_browser;
AvahiSAddressResolverCallback callback;
void* userdata;
AvahiRecord *ptr_record;
AvahiIfIndex interface;
AvahiProtocol protocol;
AvahiLookupResultFlags flags;
int retry_with_multicast;
AvahiKey *key;
AvahiTimeEvent *time_event;
AVAHI_LLIST_FIELDS(AvahiSAddressResolver, resolver);
};
static void finish(AvahiSAddressResolver *r, AvahiResolverEvent event) {
assert(r);
if (r->time_event) {
avahi_time_event_free(r->time_event);
r->time_event = NULL;
}
switch (event) {
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;
}
}
static void time_event_callback(AvahiTimeEvent *e, void *userdata) {
AvahiSAddressResolver *r = userdata;
assert(e);
assert(r);
avahi_server_set_errno(r->server, AVAHI_ERR_TIMEOUT);
finish(r, AVAHI_RESOLVER_FAILURE);
}
static void start_timeout(AvahiSAddressResolver *r) {
struct timeval tv;
assert(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,
AvahiLookupResultFlags flags,
void* userdata) {
AvahiSAddressResolver *r = userdata;
assert(rr);
assert(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_FAILURE:
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;
}
}
r->flags = flags;
finish(r, AVAHI_RESOLVER_FAILURE);
break;
}
}
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[AVAHI_DOMAIN_NAME_MAX];
assert(server);
assert(address);
assert(callback);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, address->proto == AVAHI_PROTO_INET || address->proto == AVAHI_PROTO_INET6, AVAHI_ERR_INVALID_PROTOCOL);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
avahi_reverse_lookup_name(address, n, sizeof(n));
if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) {
avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
return NULL;
}
if (!(r = avahi_new(AvahiSAddressResolver, 1))) {
avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
avahi_key_unref(k);
return NULL;
}
r->server = server;
r->address = *address;
r->callback = callback;
r->userdata = userdata;
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;
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, flags, record_browser_callback, r);
if (!r->record_browser) {
avahi_s_address_resolver_free(r);
return NULL;
}
start_timeout(r);
return r;
}
void avahi_s_address_resolver_free(AvahiSAddressResolver *r) {
assert(r);
AVAHI_LLIST_REMOVE(AvahiSAddressResolver, resolver, r->server->address_resolvers, r);
if (r->record_browser)
avahi_s_record_browser_free(r->record_browser);
if (r->time_event)
avahi_time_event_free(r->time_event);
if (r->ptr_record)
avahi_record_unref(r->ptr_record);
if (r->key)
avahi_key_unref(r->key);
avahi_free(r);
}

View File

@@ -0,0 +1,297 @@
/***
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 <avahi-common/domain.h>
#include <avahi-common/timeval.h>
#include <avahi-common/malloc.h>
#include <avahi-common/error.h>
#include "browse.h"
#include "log.h"
#define TIMEOUT_MSEC 5000
struct AvahiSHostNameResolver {
AvahiServer *server;
char *host_name;
AvahiSRecordBrowser *record_browser_a;
AvahiSRecordBrowser *record_browser_aaaa;
AvahiSHostNameResolverCallback callback;
void* userdata;
AvahiRecord *address_record;
AvahiIfIndex interface;
AvahiProtocol protocol;
AvahiLookupResultFlags flags;
AvahiTimeEvent *time_event;
AVAHI_LLIST_FIELDS(AvahiSHostNameResolver, resolver);
};
static void finish(AvahiSHostNameResolver *r, AvahiResolverEvent event) {
assert(r);
if (r->time_event) {
avahi_time_event_free(r->time_event);
r->time_event = NULL;
}
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;
}
case AVAHI_RESOLVER_FAILURE:
r->callback(r, r->interface, r->protocol, event, r->host_name, NULL, r->flags, r->userdata);
break;
}
}
static void time_event_callback(AvahiTimeEvent *e, void *userdata) {
AvahiSHostNameResolver *r = userdata;
assert(e);
assert(r);
avahi_server_set_errno(r->server, AVAHI_ERR_TIMEOUT);
finish(r, AVAHI_RESOLVER_FAILURE);
}
static void start_timeout(AvahiSHostNameResolver *r) {
struct timeval tv;
assert(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,
AvahiLookupResultFlags flags,
void* userdata) {
AvahiSHostNameResolver *r = userdata;
assert(rr);
assert(r);
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;
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);
}
break;
case AVAHI_BROWSER_REMOVE:
assert(record);
assert(record->key->type == AVAHI_DNS_TYPE_A || record->key->type == AVAHI_DNS_TYPE_AAAA);
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:
/* Stop browsers */
if (r->record_browser_aaaa)
avahi_s_record_browser_free(r->record_browser_aaaa);
if (r->record_browser_a)
avahi_s_record_browser_free(r->record_browser_a);
r->record_browser_a = r->record_browser_aaaa = NULL;
r->flags = flags;
finish(r, AVAHI_RESOLVER_FAILURE);
break;
}
}
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);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_is_valid_fqdn(host_name), AVAHI_ERR_INVALID_HOST_NAME);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(aprotocol), AVAHI_ERR_INVALID_PROTOCOL);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
if (!(r = avahi_new(AvahiSHostNameResolver, 1))) {
avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
return NULL;
}
r->server = server;
r->host_name = avahi_normalize_name_strdup(host_name);
r->callback = callback;
r->userdata = userdata;
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;
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, flags, record_browser_callback, r);
avahi_key_unref(k);
if (!r->record_browser_a)
goto fail;
}
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, flags, record_browser_callback, r);
avahi_key_unref(k);
if (!r->record_browser_aaaa)
goto fail;
}
assert(r->record_browser_aaaa || r->record_browser_a);
start_timeout(r);
return r;
fail:
avahi_s_host_name_resolver_free(r);
return NULL;
}
void avahi_s_host_name_resolver_free(AvahiSHostNameResolver *r) {
assert(r);
AVAHI_LLIST_REMOVE(AvahiSHostNameResolver, resolver, r->server->host_name_resolvers, r);
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);
if (r->time_event)
avahi_time_event_free(r->time_event);
if (r->address_record)
avahi_record_unref(r->address_record);
avahi_free(r->host_name);
avahi_free(r);
}

View File

@@ -0,0 +1,489 @@
/***
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 <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <avahi-common/domain.h>
#include <avahi-common/timeval.h>
#include <avahi-common/malloc.h>
#include <avahi-common/error.h>
#include "browse.h"
#include "log.h"
#define TIMEOUT_MSEC 5000
struct AvahiSServiceResolver {
AvahiServer *server;
char *service_name;
char *service_type;
char *domain_name;
AvahiProtocol address_protocol;
AvahiIfIndex interface;
AvahiProtocol protocol;
AvahiSRecordBrowser *record_browser_srv;
AvahiSRecordBrowser *record_browser_txt;
AvahiSRecordBrowser *record_browser_a;
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;
AVAHI_LLIST_FIELDS(AvahiSServiceResolver, resolver);
};
static void finish(AvahiSServiceResolver *r, AvahiResolverEvent event) {
AvahiLookupResultFlags flags;
assert(r);
if (r->time_event) {
avahi_time_event_free(r->time_event);
r->time_event = NULL;
}
flags =
r->txt_flags |
r->srv_flags |
r->address_flags;
switch (event) {
case AVAHI_RESOLVER_FAILURE:
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->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;
}
}
}
static void time_event_callback(AvahiTimeEvent *e, void *userdata) {
AvahiSServiceResolver *r = userdata;
assert(e);
assert(r);
avahi_server_set_errno(r->server, AVAHI_ERR_TIMEOUT);
finish(r, AVAHI_RESOLVER_FAILURE);
}
static void start_timeout(AvahiSServiceResolver *r) {
struct timeval tv;
assert(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,
AvahiLookupResultFlags flags,
void* userdata) {
AvahiSServiceResolver *r = userdata;
assert(rr);
assert(r);
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 > 0 && interface != r->interface)
return;
if (r->protocol != AVAHI_PROTO_UNSPEC && 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:
assert(!(r->user_flags & AVAHI_LOOKUP_NO_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:
assert(!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS));
if (!r->address_record) {
r->address_record = avahi_record_ref(record);
changed = 1;
}
break;
default:
abort();
}
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);
break;
}
case AVAHI_BROWSER_REMOVE:
assert(record);
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();
}
break;
case AVAHI_BROWSER_CACHE_EXHAUSTED:
case AVAHI_BROWSER_ALL_FOR_NOW:
break;
case AVAHI_BROWSER_FAILURE:
if (rr == r->record_browser_a && r->record_browser_aaaa) {
/* We were looking for both AAAA and A, and the other query is still living, so we'll not die */
avahi_s_record_browser_free(r->record_browser_a);
r->record_browser_a = NULL;
break;
}
if (rr == r->record_browser_aaaa && r->record_browser_a) {
/* We were looking for both AAAA and A, and the other query is still living, so we'll not die */
avahi_s_record_browser_free(r->record_browser_aaaa);
r->record_browser_aaaa = NULL;
break;
}
/* Hmm, everything's lost, tell the user */
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);
r->record_browser_srv = r->record_browser_txt = r->record_browser_a = r->record_browser_aaaa = NULL;
finish(r, AVAHI_RESOLVER_FAILURE);
break;
}
}
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 n[AVAHI_DOMAIN_NAME_MAX];
int ret;
assert(server);
assert(type);
assert(callback);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(aprotocol), AVAHI_ERR_INVALID_PROTOCOL);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !name || avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST|AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), AVAHI_ERR_INVALID_FLAGS);
if (!domain)
domain = server->domain_name;
if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain)) < 0) {
avahi_server_set_errno(server, ret);
return NULL;
}
if (!(r = avahi_new(AvahiSServiceResolver, 1))) {
avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
return NULL;
}
r->server = server;
r->service_name = avahi_strdup(name);
r->service_type = avahi_normalize_name_strdup(type);
r->domain_name = avahi_normalize_name_strdup(domain);
r->callback = callback;
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;
r->user_flags = flags;
r->record_browser_a = r->record_browser_aaaa = r->record_browser_srv = r->record_browser_txt = NULL;
r->time_event = NULL;
AVAHI_LLIST_PREPEND(AvahiSServiceResolver, resolver, server->service_resolvers, r);
k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV);
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;
}
if (!(flags & AVAHI_LOOKUP_NO_TXT)) {
k = avahi_key_new(n, 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;
}
void avahi_s_service_resolver_free(AvahiSServiceResolver *r) {
assert(r);
AVAHI_LLIST_REMOVE(AvahiSServiceResolver, resolver, r->server->service_resolvers, r);
if (r->time_event)
avahi_time_event_free(r->time_event);
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);
if (r->srv_record)
avahi_record_unref(r->srv_record);
if (r->txt_record)
avahi_record_unref(r->txt_record);
if (r->address_record)
avahi_record_unref(r->address_record);
avahi_free(r->service_name);
avahi_free(r->service_type);
avahi_free(r->domain_name);
avahi_free(r);
}

511
avahi-core/response-sched.c Normal file
View File

@@ -0,0 +1,511 @@
/***
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 <avahi-common/timeval.h>
#include <avahi-common/malloc.h>
#include "response-sched.h"
#include "log.h"
#include "rr-util.h"
/* Local packets are supressed this long after sending them */
#define AVAHI_RESPONSE_HISTORY_MSEC 500
/* Local packets are deferred this long before sending them */
#define AVAHI_RESPONSE_DEFER_MSEC 20
/* Additional jitter for deferred packets */
#define AVAHI_RESPONSE_JITTER_MSEC 100
/* Remote packets can suppress local traffic as long as this value */
#define AVAHI_RESPONSE_SUPPRESS_MSEC 700
typedef struct AvahiResponseJob AvahiResponseJob;
typedef enum {
AVAHI_SCHEDULED,
AVAHI_DONE,
AVAHI_SUPPRESSED
} AvahiResponseJobState;
struct AvahiResponseJob {
AvahiResponseScheduler *scheduler;
AvahiTimeEvent *time_event;
AvahiResponseJobState state;
struct timeval delivery;
AvahiRecord *record;
int flush_cache;
AvahiAddress querier;
int querier_valid;
AVAHI_LLIST_FIELDS(AvahiResponseJob, jobs);
};
struct AvahiResponseScheduler {
AvahiInterface *interface;
AvahiTimeEventQueue *time_event_queue;
AVAHI_LLIST_HEAD(AvahiResponseJob, jobs);
AVAHI_LLIST_HEAD(AvahiResponseJob, history);
AVAHI_LLIST_HEAD(AvahiResponseJob, suppressed);
};
static AvahiResponseJob* job_new(AvahiResponseScheduler *s, AvahiRecord *record, AvahiResponseJobState state) {
AvahiResponseJob *rj;
assert(s);
assert(record);
if (!(rj = avahi_new(AvahiResponseJob, 1))) {
avahi_log_error(__FILE__": Out of memory");
return NULL;
}
rj->scheduler = s;
rj->record = avahi_record_ref(record);
rj->time_event = NULL;
rj->flush_cache = 0;
rj->querier_valid = 0;
if ((rj->state = state) == AVAHI_SCHEDULED)
AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->jobs, rj);
else if (rj->state == AVAHI_DONE)
AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->history, rj);
else /* rj->state == AVAHI_SUPPRESSED */
AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->suppressed, rj);
return rj;
}
static void job_free(AvahiResponseScheduler *s, AvahiResponseJob *rj) {
assert(s);
assert(rj);
if (rj->time_event)
avahi_time_event_free(rj->time_event);
if (rj->state == AVAHI_SCHEDULED)
AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->jobs, rj);
else if (rj->state == AVAHI_DONE)
AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->history, rj);
else /* rj->state == AVAHI_SUPPRESSED */
AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->suppressed, rj);
avahi_record_unref(rj->record);
avahi_free(rj);
}
static void elapse_callback(AvahiTimeEvent *e, void* data);
static void job_set_elapse_time(AvahiResponseScheduler *s, AvahiResponseJob *rj, unsigned msec, unsigned jitter) {
struct timeval tv;
assert(s);
assert(rj);
avahi_elapse_time(&tv, msec, jitter);
if (rj->time_event)
avahi_time_event_update(rj->time_event, &tv);
else
rj->time_event = avahi_time_event_new(s->time_event_queue, &tv, elapse_callback, rj);
}
static void job_mark_done(AvahiResponseScheduler *s, AvahiResponseJob *rj) {
assert(s);
assert(rj);
assert(rj->state == AVAHI_SCHEDULED);
AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->jobs, rj);
AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->history, rj);
rj->state = AVAHI_DONE;
job_set_elapse_time(s, rj, AVAHI_RESPONSE_HISTORY_MSEC, 0);
gettimeofday(&rj->delivery, NULL);
}
AvahiResponseScheduler *avahi_response_scheduler_new(AvahiInterface *i) {
AvahiResponseScheduler *s;
assert(i);
if (!(s = avahi_new(AvahiResponseScheduler, 1))) {
avahi_log_error(__FILE__": Out of memory");
return NULL;
}
s->interface = i;
s->time_event_queue = i->monitor->server->time_event_queue;
AVAHI_LLIST_HEAD_INIT(AvahiResponseJob, s->jobs);
AVAHI_LLIST_HEAD_INIT(AvahiResponseJob, s->history);
AVAHI_LLIST_HEAD_INIT(AvahiResponseJob, s->suppressed);
return s;
}
void avahi_response_scheduler_free(AvahiResponseScheduler *s) {
assert(s);
avahi_response_scheduler_clear(s);
avahi_free(s);
}
void avahi_response_scheduler_clear(AvahiResponseScheduler *s) {
assert(s);
while (s->jobs)
job_free(s, s->jobs);
while (s->history)
job_free(s, s->history);
while (s->suppressed)
job_free(s, s->suppressed);
}
static void enumerate_aux_records_callback(AVAHI_GCC_UNUSED AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) {
AvahiResponseJob *rj = userdata;
assert(r);
assert(rj);
avahi_response_scheduler_post(rj->scheduler, r, flush_cache, rj->querier_valid ? &rj->querier : NULL, 0);
}
static int packet_add_response_job(AvahiResponseScheduler *s, AvahiDnsPacket *p, AvahiResponseJob *rj) {
assert(s);
assert(p);
assert(rj);
/* Try to add this record to the packet */
if (!avahi_dns_packet_append_record(p, rj->record, rj->flush_cache, 0))
return 0;
/* Ok, this record will definitely be sent, so schedule the
* auxilliary packets, too */
avahi_server_enumerate_aux_records(s->interface->monitor->server, s->interface, rj->record, enumerate_aux_records_callback, rj);
job_mark_done(s, rj);
return 1;
}
static void send_response_packet(AvahiResponseScheduler *s, AvahiResponseJob *rj) {
AvahiDnsPacket *p;
unsigned n;
assert(s);
assert(rj);
if (!(p = avahi_dns_packet_new_response(s->interface->hardware->mtu, 1)))
return; /* OOM */
n = 1;
/* Put it in the packet. */
if (packet_add_response_job(s, p, rj)) {
/* Try to fill up packet with more responses, if available */
while (s->jobs) {
if (!packet_add_response_job(s, p, s->jobs))
break;
n++;
}
} else {
size_t size;
avahi_dns_packet_free(p);
/* OK, the packet was too small, so create one that fits */
size = avahi_record_get_estimate_size(rj->record) + AVAHI_DNS_PACKET_HEADER_SIZE;
if (!(p = avahi_dns_packet_new_response(size + AVAHI_DNS_PACKET_EXTRA_SIZE, 1)))
return; /* OOM */
if (!packet_add_response_job(s, p, rj)) {
avahi_dns_packet_free(p);
avahi_log_warn("Record too large, cannot send");
job_mark_done(s, rj);
return;
}
}
avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n);
avahi_interface_send_packet(s->interface, p);
avahi_dns_packet_free(p);
}
static void elapse_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* data) {
AvahiResponseJob *rj = data;
assert(rj);
if (rj->state == AVAHI_DONE || rj->state == AVAHI_SUPPRESSED)
job_free(rj->scheduler, rj); /* Lets drop this entry */
else
send_response_packet(rj->scheduler, rj);
}
static AvahiResponseJob* find_scheduled_job(AvahiResponseScheduler *s, AvahiRecord *record) {
AvahiResponseJob *rj;
assert(s);
assert(record);
for (rj = s->jobs; rj; rj = rj->jobs_next) {
assert(rj->state == AVAHI_SCHEDULED);
if (avahi_record_equal_no_ttl(rj->record, record))
return rj;
}
return NULL;
}
static AvahiResponseJob* find_history_job(AvahiResponseScheduler *s, AvahiRecord *record) {
AvahiResponseJob *rj;
assert(s);
assert(record);
for (rj = s->history; rj; rj = rj->jobs_next) {
assert(rj->state == AVAHI_DONE);
if (avahi_record_equal_no_ttl(rj->record, record)) {
/* Check whether this entry is outdated */
/* avahi_log_debug("history age: %u", (unsigned) (avahi_age(&rj->delivery)/1000)); */
if (avahi_age(&rj->delivery)/1000 > AVAHI_RESPONSE_HISTORY_MSEC) {
/* it is outdated, so let's remove it */
job_free(s, rj);
return NULL;
}
return rj;
}
}
return NULL;
}
static AvahiResponseJob* find_suppressed_job(AvahiResponseScheduler *s, AvahiRecord *record, const AvahiAddress *querier) {
AvahiResponseJob *rj;
assert(s);
assert(record);
assert(querier);
for (rj = s->suppressed; rj; rj = rj->jobs_next) {
assert(rj->state == AVAHI_SUPPRESSED);
assert(rj->querier_valid);
if (avahi_record_equal_no_ttl(rj->record, record) &&
avahi_address_cmp(&rj->querier, querier) == 0) {
/* Check whether this entry is outdated */
if (avahi_age(&rj->delivery) > AVAHI_RESPONSE_SUPPRESS_MSEC*1000) {
/* it is outdated, so let's remove it */
job_free(s, rj);
return NULL;
}
return rj;
}
}
return NULL;
}
int avahi_response_scheduler_post(AvahiResponseScheduler *s, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately) {
AvahiResponseJob *rj;
struct timeval tv;
/* char *t; */
assert(s);
assert(record);
assert(!avahi_key_is_pattern(record->key));
/* t = avahi_record_to_string(record); */
/* avahi_log_debug("post %i %s", immediately, t); */
/* avahi_free(t); */
/* Check whether this response is suppressed */
if (querier &&
(rj = find_suppressed_job(s, record, querier)) &&
avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) &&
rj->record->ttl >= record->ttl/2) {
/* avahi_log_debug("Response suppressed by known answer suppression."); */
return 0;
}
/* Check if we already sent this response recently */
if ((rj = find_history_job(s, record))) {
if (avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) &&
rj->record->ttl >= record->ttl/2 &&
(rj->flush_cache || !flush_cache)) {
/* avahi_log_debug("Response suppressed by local duplicate suppression (history)"); */
return 0;
}
/* Outdated ... */
job_free(s, rj);
}
avahi_elapse_time(&tv, immediately ? 0 : AVAHI_RESPONSE_DEFER_MSEC, immediately ? 0 : AVAHI_RESPONSE_JITTER_MSEC);
if ((rj = find_scheduled_job(s, record))) {
/* avahi_log_debug("Response suppressed by local duplicate suppression (scheduled)"); */
/* Update a little ... */
/* Update the time if the new is prior to the old */
if (avahi_timeval_compare(&tv, &rj->delivery) < 0) {
rj->delivery = tv;
avahi_time_event_update(rj->time_event, &rj->delivery);
}
/* Update the flush cache bit */
if (flush_cache)
rj->flush_cache = 1;
/* Update the querier field */
if (!querier || (rj->querier_valid && avahi_address_cmp(querier, &rj->querier) != 0))
rj->querier_valid = 0;
/* Update record data (just for the TTL) */
avahi_record_unref(rj->record);
rj->record = avahi_record_ref(record);
return 1;
} else {
/* avahi_log_debug("Accepted new response job."); */
/* Create a new job and schedule it */
if (!(rj = job_new(s, record, AVAHI_SCHEDULED)))
return 0; /* OOM */
rj->delivery = tv;
rj->time_event = avahi_time_event_new(s->time_event_queue, &rj->delivery, elapse_callback, rj);
rj->flush_cache = flush_cache;
if ((rj->querier_valid = !!querier))
rj->querier = *querier;
return 1;
}
}
void avahi_response_scheduler_incoming(AvahiResponseScheduler *s, AvahiRecord *record, int flush_cache) {
AvahiResponseJob *rj;
assert(s);
/* This function is called whenever an incoming response was
* receieved. We drop scheduled responses which match here. The
* keyword is "DUPLICATE ANSWER SUPPRESION". */
if ((rj = find_scheduled_job(s, record))) {
if ((!rj->flush_cache || flush_cache) && /* flush cache bit was set correctly */
avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) && /* both goodbye packets, or both not */
record->ttl >= rj->record->ttl/2) { /* sensible TTL */
/* A matching entry was found, so let's mark it done */
/* avahi_log_debug("Response suppressed by distributed duplicate suppression"); */
job_mark_done(s, rj);
}
return;
}
if ((rj = find_history_job(s, record))) {
/* Found a history job, let's update it */
avahi_record_unref(rj->record);
rj->record = avahi_record_ref(record);
} else
/* Found no existing history job, so let's create a new one */
if (!(rj = job_new(s, record, AVAHI_DONE)))
return; /* OOM */
rj->flush_cache = flush_cache;
rj->querier_valid = 0;
gettimeofday(&rj->delivery, NULL);
job_set_elapse_time(s, rj, AVAHI_RESPONSE_HISTORY_MSEC, 0);
}
void avahi_response_scheduler_suppress(AvahiResponseScheduler *s, AvahiRecord *record, const AvahiAddress *querier) {
AvahiResponseJob *rj;
assert(s);
assert(record);
assert(querier);
if ((rj = find_scheduled_job(s, record))) {
if (rj->querier_valid && avahi_address_cmp(querier, &rj->querier) == 0 && /* same originator */
avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) && /* both goodbye packets, or both not */
record->ttl >= rj->record->ttl/2) { /* sensible TTL */
/* A matching entry was found, so let's drop it */
/* avahi_log_debug("Known answer suppression active!"); */
job_free(s, rj);
}
}
if ((rj = find_suppressed_job(s, record, querier))) {
/* Let's update the old entry */
avahi_record_unref(rj->record);
rj->record = avahi_record_ref(record);
} else {
/* Create a new entry */
if (!(rj = job_new(s, record, AVAHI_SUPPRESSED)))
return; /* OOM */
rj->querier_valid = 1;
rj->querier = *querier;
}
gettimeofday(&rj->delivery, NULL);
job_set_elapse_time(s, rj, AVAHI_RESPONSE_SUPPRESS_MSEC, 0);
}
void avahi_response_scheduler_force(AvahiResponseScheduler *s) {
assert(s);
/* Send all scheduled responses immediately */
while (s->jobs)
send_response_packet(s, s->jobs);
}

View File

@@ -0,0 +1,37 @@
#ifndef fooresponseschedhfoo
#define fooresponseschedhfoo
/***
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 AvahiResponseScheduler AvahiResponseScheduler;
#include <avahi-common/address.h>
#include "iface.h"
AvahiResponseScheduler *avahi_response_scheduler_new(AvahiInterface *i);
void avahi_response_scheduler_free(AvahiResponseScheduler *s);
void avahi_response_scheduler_clear(AvahiResponseScheduler *s);
void avahi_response_scheduler_force(AvahiResponseScheduler *s);
int avahi_response_scheduler_post(AvahiResponseScheduler *s, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately);
void avahi_response_scheduler_incoming(AvahiResponseScheduler *s, AvahiRecord *record, int flush_cache);
void avahi_response_scheduler_suppress(AvahiResponseScheduler *s, AvahiRecord *record, const AvahiAddress *querier);
#endif

62
avahi-core/rr-util.h Normal file
View File

@@ -0,0 +1,62 @@
#ifndef foorrutilhfoo
#define foorrutilhfoo
/***
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 "rr.h"
AVAHI_C_DECL_BEGIN
/** Creaze new AvahiKey object based on an existing key but replaceing the type by CNAME */
AvahiKey *avahi_key_new_cname(AvahiKey *key);
/** Match a key to a key pattern. The pattern has a type of
AVAHI_DNS_CLASS_ANY, the classes are taken to be equal. Same for the
type. If the pattern has neither class nor type with ANY constants,
this function is identical to avahi_key_equal(). In contrast to
avahi_equal() this function is not commutative. */
int avahi_key_pattern_match(const AvahiKey *pattern, const AvahiKey *k);
/** Check whether a key is a pattern key, i.e. the class/type has a
* value of AVAHI_DNS_CLASS_ANY/AVAHI_DNS_TYPE_ANY */
int avahi_key_is_pattern(const AvahiKey *k);
/** Returns a maximum estimate for the space that is needed to store
* this key in a DNS packet. */
size_t avahi_key_get_estimate_size(AvahiKey *k);
/** Returns a maximum estimate for the space that is needed to store
* the record in a DNS packet. */
size_t avahi_record_get_estimate_size(AvahiRecord *r);
/** Do a mDNS spec conforming lexicographical comparison of the two
* records. Return a negative value if a < b, a positive if a > b,
* zero if equal. */
int avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b);
/** Return 1 if the specified record is an mDNS goodbye record. i.e. TTL is zero. */
int avahi_record_is_goodbye(AvahiRecord *r);
/** Make a deep copy of an AvahiRecord object */
AvahiRecord *avahi_record_copy(AvahiRecord *r);
AVAHI_C_DECL_END
#endif

733
avahi-core/rr.c Normal file
View File

@@ -0,0 +1,733 @@
/***
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 <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <assert.h>
#include <avahi-common/domain.h>
#include <avahi-common/malloc.h>
#include <avahi-common/defs.h>
#include "rr.h"
#include "log.h"
#include "util.h"
#include "hashmap.h"
#include "domain-util.h"
#include "rr-util.h"
#include "addr-util.h"
AvahiKey *avahi_key_new(const char *name, uint16_t class, uint16_t type) {
AvahiKey *k;
assert(name);
if (!(k = avahi_new(AvahiKey, 1))) {
avahi_log_error("avahi_new() failed.");
return NULL;
}
if (!(k->name = avahi_normalize_name_strdup(name))) {
avahi_log_error("avahi_normalize_name() failed.");
avahi_free(k);
return NULL;
}
k->ref = 1;
k->clazz = class;
k->type = 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);
k->ref++;
return k;
}
void avahi_key_unref(AvahiKey *k) {
assert(k);
assert(k->ref >= 1);
if ((--k->ref) <= 0) {
avahi_free(k->name);
avahi_free(k);
}
}
AvahiRecord *avahi_record_new(AvahiKey *k, uint32_t ttl) {
AvahiRecord *r;
assert(k);
if (!(r = avahi_new(AvahiRecord, 1))) {
avahi_log_error("avahi_new() failed.");
return NULL;
}
r->ref = 1;
r->key = avahi_key_ref(k);
memset(&r->data, 0, sizeof(r->data));
r->ttl = ttl != (uint32_t) -1 ? ttl : AVAHI_DEFAULT_TTL;
return r;
}
AvahiRecord *avahi_record_new_full(const char *name, uint16_t class, uint16_t type, uint32_t ttl) {
AvahiRecord *r;
AvahiKey *k;
assert(name);
if (!(k = avahi_key_new(name, class, type))) {
avahi_log_error("avahi_key_new() failed.");
return NULL;
}
r = avahi_record_new(k, ttl);
avahi_key_unref(k);
if (!r) {
avahi_log_error("avahi_record_new() failed.");
return NULL;
}
return r;
}
AvahiRecord *avahi_record_ref(AvahiRecord *r) {
assert(r);
assert(r->ref >= 1);
r->ref++;
return r;
}
void avahi_record_unref(AvahiRecord *r) {
assert(r);
assert(r->ref >= 1);
if ((--r->ref) <= 0) {
switch (r->key->type) {
case AVAHI_DNS_TYPE_SRV:
avahi_free(r->data.srv.name);
break;
case AVAHI_DNS_TYPE_PTR:
case AVAHI_DNS_TYPE_CNAME:
case AVAHI_DNS_TYPE_NS:
avahi_free(r->data.ptr.name);
break;
case AVAHI_DNS_TYPE_HINFO:
avahi_free(r->data.hinfo.cpu);
avahi_free(r->data.hinfo.os);
break;
case AVAHI_DNS_TYPE_TXT:
avahi_string_list_free(r->data.txt.string_list);
break;
case AVAHI_DNS_TYPE_A:
case AVAHI_DNS_TYPE_AAAA:
break;
default:
avahi_free(r->data.generic.data);
}
avahi_key_unref(r->key);
avahi_free(r);
}
}
const char *avahi_dns_class_to_string(uint16_t class) {
if (class & AVAHI_DNS_CACHE_FLUSH)
return "FLUSH";
switch (class) {
case AVAHI_DNS_CLASS_IN:
return "IN";
case AVAHI_DNS_CLASS_ANY:
return "ANY";
default:
return NULL;
}
}
const char *avahi_dns_type_to_string(uint16_t type) {
switch (type) {
case AVAHI_DNS_TYPE_CNAME:
return "CNAME";
case AVAHI_DNS_TYPE_A:
return "A";
case AVAHI_DNS_TYPE_AAAA:
return "AAAA";
case AVAHI_DNS_TYPE_PTR:
return "PTR";
case AVAHI_DNS_TYPE_HINFO:
return "HINFO";
case AVAHI_DNS_TYPE_TXT:
return "TXT";
case AVAHI_DNS_TYPE_SRV:
return "SRV";
case AVAHI_DNS_TYPE_ANY:
return "ANY";
case AVAHI_DNS_TYPE_SOA:
return "SOA";
case AVAHI_DNS_TYPE_NS:
return "NS";
default:
return NULL;
}
}
char *avahi_key_to_string(const AvahiKey *k) {
char class[16], type[16];
const char *c, *t;
assert(k);
assert(k->ref >= 1);
/* According to RFC3597 */
if (!(c = avahi_dns_class_to_string(k->clazz))) {
snprintf(class, sizeof(class), "CLASS%u", k->clazz);
c = class;
}
if (!(t = avahi_dns_type_to_string(k->type))) {
snprintf(type, sizeof(type), "TYPE%u", k->type);
t = type;
}
return avahi_strdup_printf("%s\t%s\t%s", k->name, c, t);
}
char *avahi_record_to_string(const AvahiRecord *r) {
char *p, *s;
char buf[1024], *t = NULL, *d = NULL;
assert(r);
assert(r->ref >= 1);
switch (r->key->type) {
case AVAHI_DNS_TYPE_A:
inet_ntop(AF_INET, &r->data.a.address.address, t = buf, sizeof(buf));
break;
case AVAHI_DNS_TYPE_AAAA:
inet_ntop(AF_INET6, &r->data.aaaa.address.address, t = buf, sizeof(buf));
break;
case AVAHI_DNS_TYPE_PTR:
case AVAHI_DNS_TYPE_CNAME:
case AVAHI_DNS_TYPE_NS:
t = r->data.ptr.name;
break;
case AVAHI_DNS_TYPE_TXT:
t = d = avahi_string_list_to_string(r->data.txt.string_list);
break;
case AVAHI_DNS_TYPE_HINFO:
snprintf(t = buf, sizeof(buf), "\"%s\" \"%s\"", r->data.hinfo.cpu, r->data.hinfo.os);
break;
case AVAHI_DNS_TYPE_SRV:
snprintf(t = buf, sizeof(buf), "%u %u %u %s",
r->data.srv.priority,
r->data.srv.weight,
r->data.srv.port,
r->data.srv.name);
break;
default: {
uint8_t *c;
uint16_t n;
int i;
char *e;
/* According to RFC3597 */
snprintf(t = buf, sizeof(buf), "\\# %u", r->data.generic.size);
e = strchr(t, 0);
for (c = r->data.generic.data, n = r->data.generic.size, i = 0;
n > 0 && i < 20;
c ++, n --, i++) {
sprintf(e, " %02X", *c);
e = strchr(e, 0);
}
break;
}
}
p = avahi_key_to_string(r->key);
s = avahi_strdup_printf("%s %s ; ttl=%u", p, t, r->ttl);
avahi_free(p);
avahi_free(d);
return s;
}
int avahi_key_equal(const AvahiKey *a, const AvahiKey *b) {
assert(a);
assert(b);
if (a == b)
return 1;
return avahi_domain_equal(a->name, b->name) &&
a->type == b->type &&
a->clazz == b->clazz;
}
int avahi_key_pattern_match(const AvahiKey *pattern, const AvahiKey *k) {
assert(pattern);
assert(k);
assert(!avahi_key_is_pattern(k));
if (pattern == k)
return 1;
return avahi_domain_equal(pattern->name, k->name) &&
(pattern->type == k->type || pattern->type == AVAHI_DNS_TYPE_ANY) &&
(pattern->clazz == k->clazz || pattern->clazz == AVAHI_DNS_CLASS_ANY);
}
int avahi_key_is_pattern(const AvahiKey *k) {
assert(k);
return
k->type == AVAHI_DNS_TYPE_ANY ||
k->clazz == AVAHI_DNS_CLASS_ANY;
}
unsigned avahi_key_hash(const AvahiKey *k) {
assert(k);
return
avahi_domain_hash(k->name) +
k->type +
k->clazz;
}
static int rdata_equal(const AvahiRecord *a, const AvahiRecord *b) {
assert(a);
assert(b);
assert(a->key->type == b->key->type);
switch (a->key->type) {
case AVAHI_DNS_TYPE_SRV:
return
a->data.srv.priority == b->data.srv.priority &&
a->data.srv.weight == b->data.srv.weight &&
a->data.srv.port == b->data.srv.port &&
avahi_domain_equal(a->data.srv.name, b->data.srv.name);
case AVAHI_DNS_TYPE_PTR:
case AVAHI_DNS_TYPE_CNAME:
case AVAHI_DNS_TYPE_NS:
return avahi_domain_equal(a->data.ptr.name, b->data.ptr.name);
case AVAHI_DNS_TYPE_HINFO:
return
!strcmp(a->data.hinfo.cpu, b->data.hinfo.cpu) &&
!strcmp(a->data.hinfo.os, b->data.hinfo.os);
case AVAHI_DNS_TYPE_TXT:
return avahi_string_list_equal(a->data.txt.string_list, b->data.txt.string_list);
case AVAHI_DNS_TYPE_A:
return memcmp(&a->data.a.address, &b->data.a.address, sizeof(AvahiIPv4Address)) == 0;
case AVAHI_DNS_TYPE_AAAA:
return memcmp(&a->data.aaaa.address, &b->data.aaaa.address, sizeof(AvahiIPv6Address)) == 0;
default:
return a->data.generic.size == b->data.generic.size &&
(a->data.generic.size == 0 || memcmp(a->data.generic.data, b->data.generic.data, a->data.generic.size) == 0);
}
}
int avahi_record_equal_no_ttl(const AvahiRecord *a, const AvahiRecord *b) {
assert(a);
assert(b);
if (a == b)
return 1;
return
avahi_key_equal(a->key, b->key) &&
rdata_equal(a, b);
}
AvahiRecord *avahi_record_copy(AvahiRecord *r) {
AvahiRecord *copy;
if (!(copy = avahi_new(AvahiRecord, 1))) {
avahi_log_error("avahi_new() failed.");
return NULL;
}
copy->ref = 1;
copy->key = avahi_key_ref(r->key);
copy->ttl = r->ttl;
switch (r->key->type) {
case AVAHI_DNS_TYPE_PTR:
case AVAHI_DNS_TYPE_CNAME:
case AVAHI_DNS_TYPE_NS:
if (!(copy->data.ptr.name = avahi_strdup(r->data.ptr.name)))
goto fail;
break;
case AVAHI_DNS_TYPE_SRV:
copy->data.srv.priority = r->data.srv.priority;
copy->data.srv.weight = r->data.srv.weight;
copy->data.srv.port = r->data.srv.port;
if (!(copy->data.srv.name = avahi_strdup(r->data.srv.name)))
goto fail;
break;
case AVAHI_DNS_TYPE_HINFO:
if (!(copy->data.hinfo.os = avahi_strdup(r->data.hinfo.os)))
goto fail;
if (!(copy->data.hinfo.cpu = avahi_strdup(r->data.hinfo.cpu))) {
avahi_free(r->data.hinfo.os);
goto fail;
}
break;
case AVAHI_DNS_TYPE_TXT:
copy->data.txt.string_list = avahi_string_list_copy(r->data.txt.string_list);
break;
case AVAHI_DNS_TYPE_A:
copy->data.a.address = r->data.a.address;
break;
case AVAHI_DNS_TYPE_AAAA:
copy->data.aaaa.address = r->data.aaaa.address;
break;
default:
if (!(copy->data.generic.data = avahi_memdup(r->data.generic.data, r->data.generic.size)))
goto fail;
copy->data.generic.size = r->data.generic.size;
break;
}
return copy;
fail:
avahi_log_error("Failed to allocate memory");
avahi_key_unref(copy->key);
avahi_free(copy);
return NULL;
}
size_t avahi_key_get_estimate_size(AvahiKey *k) {
assert(k);
return strlen(k->name)+1+4;
}
size_t avahi_record_get_estimate_size(AvahiRecord *r) {
size_t n;
assert(r);
n = avahi_key_get_estimate_size(r->key) + 4 + 2;
switch (r->key->type) {
case AVAHI_DNS_TYPE_PTR:
case AVAHI_DNS_TYPE_CNAME:
case AVAHI_DNS_TYPE_NS:
n += strlen(r->data.ptr.name) + 1;
break;
case AVAHI_DNS_TYPE_SRV:
n += 6 + strlen(r->data.srv.name) + 1;
break;
case AVAHI_DNS_TYPE_HINFO:
n += strlen(r->data.hinfo.os) + 1 + strlen(r->data.hinfo.cpu) + 1;
break;
case AVAHI_DNS_TYPE_TXT:
n += avahi_string_list_serialize(r->data.txt.string_list, NULL, 0);
break;
case AVAHI_DNS_TYPE_A:
n += sizeof(AvahiIPv4Address);
break;
case AVAHI_DNS_TYPE_AAAA:
n += sizeof(AvahiIPv6Address);
break;
default:
n += r->data.generic.size;
}
return n;
}
static int lexicographical_memcmp(const void* a, size_t al, const void* b, size_t bl) {
size_t c;
int ret;
assert(a);
assert(b);
c = al < bl ? al : bl;
if ((ret = memcmp(a, b, c)))
return ret;
if (al == bl)
return 0;
else
return al == c ? 1 : -1;
}
static int uint16_cmp(uint16_t a, uint16_t b) {
return a == b ? 0 : (a < b ? -1 : 1);
}
int avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b) {
int r;
/* char *t1, *t2; */
assert(a);
assert(b);
/* t1 = avahi_record_to_string(a); */
/* t2 = avahi_record_to_string(b); */
/* g_message("lexicocmp: %s %s", t1, t2); */
/* avahi_free(t1); */
/* avahi_free(t2); */
if (a == b)
return 0;
if ((r = uint16_cmp(a->key->clazz, b->key->clazz)) ||
(r = uint16_cmp(a->key->type, b->key->type)))
return r;
switch (a->key->type) {
case AVAHI_DNS_TYPE_PTR:
case AVAHI_DNS_TYPE_CNAME:
case AVAHI_DNS_TYPE_NS:
return avahi_binary_domain_cmp(a->data.ptr.name, b->data.ptr.name);
case AVAHI_DNS_TYPE_SRV: {
if ((r = uint16_cmp(a->data.srv.priority, b->data.srv.priority)) == 0 &&
(r = uint16_cmp(a->data.srv.weight, b->data.srv.weight)) == 0 &&
(r = uint16_cmp(a->data.srv.port, b->data.srv.port)) == 0)
r = avahi_binary_domain_cmp(a->data.srv.name, b->data.srv.name);
return r;
}
case AVAHI_DNS_TYPE_HINFO: {
if ((r = strcmp(a->data.hinfo.cpu, b->data.hinfo.cpu)) ||
(r = strcmp(a->data.hinfo.os, b->data.hinfo.os)))
return r;
return 0;
}
case AVAHI_DNS_TYPE_TXT: {
uint8_t *ma = NULL, *mb = NULL;
size_t asize, bsize;
asize = avahi_string_list_serialize(a->data.txt.string_list, NULL, 0);
bsize = avahi_string_list_serialize(b->data.txt.string_list, NULL, 0);
if (asize > 0 && !(ma = avahi_new(uint8_t, asize)))
goto fail;
if (bsize > 0 && !(mb = avahi_new(uint8_t, bsize))) {
avahi_free(ma);
goto fail;
}
avahi_string_list_serialize(a->data.txt.string_list, ma, asize);
avahi_string_list_serialize(b->data.txt.string_list, mb, bsize);
if (asize && bsize)
r = lexicographical_memcmp(ma, asize, mb, bsize);
else if (asize && !bsize)
r = 1;
else if (!asize && bsize)
r = -1;
else
r = 0;
avahi_free(ma);
avahi_free(mb);
return r;
}
case AVAHI_DNS_TYPE_A:
return memcmp(&a->data.a.address, &b->data.a.address, sizeof(AvahiIPv4Address));
case AVAHI_DNS_TYPE_AAAA:
return memcmp(&a->data.aaaa.address, &b->data.aaaa.address, sizeof(AvahiIPv6Address));
default:
return lexicographical_memcmp(a->data.generic.data, a->data.generic.size,
b->data.generic.data, b->data.generic.size);
}
fail:
avahi_log_error(__FILE__": Out of memory");
return -1; /* or whatever ... */
}
int avahi_record_is_goodbye(AvahiRecord *r) {
assert(r);
return r->ttl == 0;
}
int avahi_key_is_valid(AvahiKey *k) {
assert(k);
if (!avahi_is_valid_domain_name(k->name))
return 0;
return 1;
}
int avahi_record_is_valid(AvahiRecord *r) {
assert(r);
if (!avahi_key_is_valid(r->key))
return 0;
switch (r->key->type) {
case AVAHI_DNS_TYPE_PTR:
case AVAHI_DNS_TYPE_CNAME:
case AVAHI_DNS_TYPE_NS:
return avahi_is_valid_domain_name(r->data.ptr.name);
case AVAHI_DNS_TYPE_SRV:
return avahi_is_valid_domain_name(r->data.srv.name);
case AVAHI_DNS_TYPE_HINFO:
return
strlen(r->data.hinfo.os) <= 255 &&
strlen(r->data.hinfo.cpu) <= 255;
case AVAHI_DNS_TYPE_TXT: {
AvahiStringList *strlst;
for (strlst = r->data.txt.string_list; strlst; strlst = strlst->next)
if (strlst->size > 255 || strlst->size <= 0)
return 0;
return 1;
}
}
return 1;
}
static AvahiAddress *get_address(const AvahiRecord *r, AvahiAddress *a) {
assert(r);
switch (r->key->type) {
case AVAHI_DNS_TYPE_A:
a->proto = AVAHI_PROTO_INET;
a->data.ipv4 = r->data.a.address;
break;
case AVAHI_DNS_TYPE_AAAA:
a->proto = AVAHI_PROTO_INET6;
a->data.ipv6 = r->data.aaaa.address;
break;
default:
return NULL;
}
return a;
}
int avahi_record_is_link_local_address(const AvahiRecord *r) {
AvahiAddress a;
assert(r);
if (!get_address(r, &a))
return 0;
return avahi_address_is_link_local(&a);
}

175
avahi-core/rr.h Normal file
View File

@@ -0,0 +1,175 @@
#ifndef foorrhfoo
#define foorrhfoo
/***
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 rr.h Functions and definitions for manipulating DNS resource record (RR) data. */
#include <inttypes.h>
#include <sys/types.h>
#include <avahi-common/strlst.h>
#include <avahi-common/address.h>
#include <avahi-common/cdecl.h>
AVAHI_C_DECL_BEGIN
/** DNS record types, see RFC 1035, in addition to those defined in defs.h */
enum {
AVAHI_DNS_TYPE_ANY = 0xFF, /**< Special query type for requesting all records */
AVAHI_DNS_TYPE_OPT = 41, /**< EDNS0 option */
AVAHI_DNS_TYPE_TKEY = 249,
AVAHI_DNS_TYPE_TSIG = 250,
AVAHI_DNS_TYPE_IXFR = 251,
AVAHI_DNS_TYPE_AXFR = 252
};
/** DNS record classes, see RFC 1035, in addition to those defined in defs.h */
enum {
AVAHI_DNS_CLASS_ANY = 0xFF, /**< Special query type for requesting all records */
AVAHI_DNS_CACHE_FLUSH = 0x8000, /**< Not really a class but a bit which may be set in response packets, see mDNS spec for more information */
AVAHI_DNS_UNICAST_RESPONSE = 0x8000 /**< Not really a class but a bit which may be set in query packets, see mDNS spec for more information */
};
/** Encapsulates a DNS query key consisting of class, type and
name. Use avahi_key_ref()/avahi_key_unref() for manipulating the
reference counter. The structure is intended to be treated as "immutable", no
changes should be imposed after creation */
typedef struct AvahiKey {
int ref; /**< Reference counter */
char *name; /**< Record name */
uint16_t clazz; /**< Record class, one of the AVAHI_DNS_CLASS_xxx constants */
uint16_t type; /**< Record type, one of the AVAHI_DNS_TYPE_xxx constants */
} AvahiKey;
/** Encapsulates a DNS resource record. The structure is intended to
* be treated as "immutable", no changes should be imposed after
* creation. */
typedef struct AvahiRecord {
int ref; /**< Reference counter */
AvahiKey *key; /**< Reference to the query key of this record */
uint32_t ttl; /**< DNS TTL of this record */
union {
struct {
void* data;
uint16_t size;
} generic; /**< Generic record data for unknown types */
struct {
uint16_t priority;
uint16_t weight;
uint16_t port;
char *name;
} srv; /**< Data for SRV records */
struct {
char *name;
} ptr, ns, cname; /**< Data for PTR, NS and CNAME records */
struct {
char *cpu;
char *os;
} hinfo; /**< Data for HINFO records */
struct {
AvahiStringList *string_list;
} txt; /**< Data for TXT records */
struct {
AvahiIPv4Address address;
} a; /**< Data for A records */
struct {
AvahiIPv6Address address;
} aaaa; /**< Data for AAAA records */
} data; /**< Record data */
} AvahiRecord;
/** 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);
/** Increase the reference counter of an AvahiKey object by one */
AvahiKey *avahi_key_ref(AvahiKey *k);
/** Decrease the reference counter of an AvahiKey object by one */
void avahi_key_unref(AvahiKey *k);
/** Check whether two AvahiKey object contain the same
* data. AVAHI_DNS_CLASS_ANY/AVAHI_DNS_TYPE_ANY are treated like any
* other class/type. */
int avahi_key_equal(const AvahiKey *a, const AvahiKey *b);
/** Return a numeric hash value for a key for usage in hash tables. */
unsigned avahi_key_hash(const AvahiKey *k);
/** Create a new record object. Record data should be filled in right after creation. The reference counter is set to 1. */
AvahiRecord *avahi_record_new(AvahiKey *k, uint32_t ttl);
/** Create a new record object. Record data should be filled in right after creation. The reference counter is set to 1. */
AvahiRecord *avahi_record_new_full(const char *name, uint16_t clazz, uint16_t type, uint32_t ttl);
/** Increase the reference counter of an AvahiRecord by one. */
AvahiRecord *avahi_record_ref(AvahiRecord *r);
/** Decrease the reference counter of an AvahiRecord by one. */
void avahi_record_unref(AvahiRecord *r);
/** Return a textual representation of the specified DNS class. The
* returned pointer points to a read only internal string. */
const char *avahi_dns_class_to_string(uint16_t clazz);
/** Return a textual representation of the specified DNS class. The
* returned pointer points to a read only internal string. */
const char *avahi_dns_type_to_string(uint16_t type);
/** Create a textual representation of the specified key. avahi_free() the
* result! */
char *avahi_key_to_string(const AvahiKey *k);
/** Create a textual representation of the specified record, similar
* in style to BIND zone file data. avahi_free() the result! */
char *avahi_record_to_string(const AvahiRecord *r);
/** Check whether two records are equal (regardless of the TTL */
int avahi_record_equal_no_ttl(const AvahiRecord *a, const AvahiRecord *b);
/** Check whether the specified key is valid */
int avahi_key_is_valid(AvahiKey *k);
/** Check whether the specified record is valid */
int avahi_record_is_valid(AvahiRecord *r);
/** Parse a binary rdata object and fill it into *record. This function is actually implemented in dns.c */
int avahi_rdata_parse(AvahiRecord *record, const void* rdata, size_t size);
/** Serialize an AvahiRecord object into binary rdata. This function is actually implemented in dns.c */
size_t avahi_rdata_serialize(AvahiRecord *record, void *rdata, size_t max_size);
/** Return TRUE if the AvahiRecord object is a link-local A or AAAA address */
int avahi_record_is_link_local_address(const AvahiRecord *r);
AVAHI_C_DECL_END
#endif

188
avahi-core/rrlist.c Normal file
View File

@@ -0,0 +1,188 @@
/***
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/llist.h>
#include <avahi-common/malloc.h>
#include "rrlist.h"
#include "log.h"
typedef struct AvahiRecordListItem AvahiRecordListItem;
struct AvahiRecordListItem {
int read;
AvahiRecord *record;
int unicast_response;
int flush_cache;
int auxiliary;
AVAHI_LLIST_FIELDS(AvahiRecordListItem, items);
};
struct AvahiRecordList {
AVAHI_LLIST_HEAD(AvahiRecordListItem, read);
AVAHI_LLIST_HEAD(AvahiRecordListItem, unread);
int all_flush_cache;
};
AvahiRecordList *avahi_record_list_new(void) {
AvahiRecordList *l;
if (!(l = avahi_new(AvahiRecordList, 1))) {
avahi_log_error("avahi_new() failed.");
return NULL;
}
AVAHI_LLIST_HEAD_INIT(AvahiRecordListItem, l->read);
AVAHI_LLIST_HEAD_INIT(AvahiRecordListItem, l->unread);
l->all_flush_cache = 1;
return l;
}
void avahi_record_list_free(AvahiRecordList *l) {
assert(l);
avahi_record_list_flush(l);
avahi_free(l);
}
static void item_free(AvahiRecordList *l, AvahiRecordListItem *i) {
assert(l);
assert(i);
if (i->read)
AVAHI_LLIST_REMOVE(AvahiRecordListItem, items, l->read, i);
else
AVAHI_LLIST_REMOVE(AvahiRecordListItem, items, l->unread, i);
avahi_record_unref(i->record);
avahi_free(i);
}
void avahi_record_list_flush(AvahiRecordList *l) {
assert(l);
while (l->read)
item_free(l, l->read);
while (l->unread)
item_free(l, l->unread);
l->all_flush_cache = 1;
}
AvahiRecord* avahi_record_list_next(AvahiRecordList *l, int *ret_flush_cache, int *ret_unicast_response, int *ret_auxiliary) {
AvahiRecord *r;
AvahiRecordListItem *i;
if (!(i = l->unread))
return NULL;
assert(!i->read);
r = avahi_record_ref(i->record);
if (ret_unicast_response)
*ret_unicast_response = i->unicast_response;
if (ret_flush_cache)
*ret_flush_cache = i->flush_cache;
if (ret_auxiliary)
*ret_auxiliary = i->auxiliary;
AVAHI_LLIST_REMOVE(AvahiRecordListItem, items, l->unread, i);
AVAHI_LLIST_PREPEND(AvahiRecordListItem, items, l->read, i);
i->read = 1;
return r;
}
static AvahiRecordListItem *get(AvahiRecordList *l, AvahiRecord *r) {
AvahiRecordListItem *i;
assert(l);
assert(r);
for (i = l->read; i; i = i->items_next)
if (avahi_record_equal_no_ttl(i->record, r))
return i;
for (i = l->unread; i; i = i->items_next)
if (avahi_record_equal_no_ttl(i->record, r))
return i;
return NULL;
}
void avahi_record_list_push(AvahiRecordList *l, AvahiRecord *r, int flush_cache, int unicast_response, int auxiliary) {
AvahiRecordListItem *i;
assert(l);
assert(r);
if (get(l, r))
return;
if (!(i = avahi_new(AvahiRecordListItem, 1))) {
avahi_log_error("avahi_new() failed.");
return;
}
i->unicast_response = unicast_response;
i->flush_cache = flush_cache;
i->auxiliary = auxiliary;
i->record = avahi_record_ref(r);
i->read = 0;
l->all_flush_cache = l->all_flush_cache && flush_cache;
AVAHI_LLIST_PREPEND(AvahiRecordListItem, items, l->unread, i);
}
void avahi_record_list_drop(AvahiRecordList *l, AvahiRecord *r) {
AvahiRecordListItem *i;
assert(l);
assert(r);
if (!(i = get(l, r)))
return;
item_free(l, i);
}
int avahi_record_list_is_empty(AvahiRecordList *l) {
assert(l);
return !l->unread && !l->read;
}
int avahi_record_list_all_flush_cache(AvahiRecordList *l) {
assert(l);
/* Return TRUE if all entries in this list have flush_cache set */
return l->all_flush_cache;
}

40
avahi-core/rrlist.h Normal file
View File

@@ -0,0 +1,40 @@
#ifndef foorrlisthfoo
#define foorrlisthfoo
/***
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 "rr.h"
typedef struct AvahiRecordList AvahiRecordList;
AvahiRecordList *avahi_record_list_new(void);
void avahi_record_list_free(AvahiRecordList *l);
void avahi_record_list_flush(AvahiRecordList *l);
AvahiRecord* avahi_record_list_next(AvahiRecordList *l, int *ret_flush_cache, int *ret_unicast_response, int *ret_auxiliary);
void avahi_record_list_push(AvahiRecordList *l, AvahiRecord *r, int flush_cache, int unicast_response, int auxiliary);
void avahi_record_list_drop(AvahiRecordList *l, AvahiRecord *r);
int avahi_record_list_all_flush_cache(AvahiRecordList *l);
int avahi_record_list_is_empty(AvahiRecordList *l);
#endif

1805
avahi-core/server.c Normal file

File diff suppressed because it is too large Load Diff

993
avahi-core/socket.c Normal file
View File

@@ -0,0 +1,993 @@
/***
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 <inttypes.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#ifdef HAVE_SYS_FILIO_H
#include <sys/filio.h>
#endif
#include <assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <sys/uio.h>
#ifdef IP_RECVIF
#include <net/if_dl.h>
#endif
#include "dns.h"
#include "fdutil.h"
#include "socket.h"
#include "log.h"
#include "addr-util.h"
/* this is a portability hack */
#ifndef IPV6_ADD_MEMBERSHIP
#ifdef IPV6_JOIN_GROUP
#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
#endif
#endif
#ifndef IPV6_DROP_MEMBERSHIP
#ifdef IPV6_LEAVE_GROUP
#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
#endif
#endif
static void mdns_mcast_group_ipv4(struct sockaddr_in *ret_sa) {
assert(ret_sa);
memset(ret_sa, 0, sizeof(struct sockaddr_in));
ret_sa->sin_family = AF_INET;
ret_sa->sin_port = htons(AVAHI_MDNS_PORT);
inet_pton(AF_INET, AVAHI_IPV4_MCAST_GROUP, &ret_sa->sin_addr);
}
static void mdns_mcast_group_ipv6(struct sockaddr_in6 *ret_sa) {
assert(ret_sa);
memset(ret_sa, 0, sizeof(struct sockaddr_in6));
ret_sa->sin6_family = AF_INET6;
ret_sa->sin6_port = htons(AVAHI_MDNS_PORT);
inet_pton(AF_INET6, AVAHI_IPV6_MCAST_GROUP, &ret_sa->sin6_addr);
}
static void ipv4_address_to_sockaddr(struct sockaddr_in *ret_sa, const AvahiIPv4Address *a, uint16_t port) {
assert(ret_sa);
assert(a);
assert(port > 0);
memset(ret_sa, 0, sizeof(struct sockaddr_in));
ret_sa->sin_family = AF_INET;
ret_sa->sin_port = htons(port);
memcpy(&ret_sa->sin_addr, a, sizeof(AvahiIPv4Address));
}
static void ipv6_address_to_sockaddr(struct sockaddr_in6 *ret_sa, const AvahiIPv6Address *a, uint16_t port) {
assert(ret_sa);
assert(a);
assert(port > 0);
memset(ret_sa, 0, sizeof(struct sockaddr_in6));
ret_sa->sin6_family = AF_INET6;
ret_sa->sin6_port = htons(port);
memcpy(&ret_sa->sin6_addr, a, sizeof(AvahiIPv6Address));
}
int avahi_mdns_mcast_join_ipv4(int fd, const AvahiIPv4Address *a, int idx, int join) {
#ifdef HAVE_STRUCT_IP_MREQN
struct ip_mreqn mreq;
#else
struct ip_mreq mreq;
#endif
struct sockaddr_in sa;
assert(fd >= 0);
assert(idx >= 0);
assert(a);
memset(&mreq, 0, sizeof(mreq));
#ifdef HAVE_STRUCT_IP_MREQN
mreq.imr_ifindex = idx;
mreq.imr_address.s_addr = a->address;
#else
mreq.imr_interface.s_addr = a->address;
#endif
mdns_mcast_group_ipv4(&sa);
mreq.imr_multiaddr = sa.sin_addr;
/* Some network drivers have issues with dropping membership of
* mcast groups when the iface is down, but don't allow rejoining
* when it comes back up. This is an ugly workaround */
if (join)
setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
if (setsockopt(fd, IPPROTO_IP, join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
avahi_log_warn("%s failed: %s", join ? "IP_ADD_MEMBERSHIP" : "IP_DROP_MEMBERSHIP", strerror(errno));
return -1;
}
return 0;
}
int avahi_mdns_mcast_join_ipv6(int fd, const AvahiIPv6Address *a, int idx, int join) {
struct ipv6_mreq mreq6;
struct sockaddr_in6 sa6;
assert(fd >= 0);
assert(idx >= 0);
assert(a);
memset(&mreq6, 0, sizeof(mreq6));
mdns_mcast_group_ipv6 (&sa6);
mreq6.ipv6mr_multiaddr = sa6.sin6_addr;
mreq6.ipv6mr_interface = idx;
if (join)
setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6));
if (setsockopt(fd, IPPROTO_IPV6, join ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) {
avahi_log_warn("%s failed: %s", join ? "IPV6_ADD_MEMBERSHIP" : "IPV6_DROP_MEMBERSHIP", strerror(errno));
return -1;
}
return 0;
}
static int reuseaddr(int fd) {
int yes;
yes = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
avahi_log_warn("SO_REUSEADDR failed: %s", strerror(errno));
return -1;
}
#ifdef SO_REUSEPORT
yes = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)) < 0) {
avahi_log_warn("SO_REUSEPORT failed: %s", strerror(errno));
return -1;
}
#endif
return 0;
}
static int bind_with_warn(int fd, const struct sockaddr *sa, socklen_t l) {
assert(fd >= 0);
assert(sa);
assert(l > 0);
if (bind(fd, sa, l) < 0) {
if (errno != EADDRINUSE) {
avahi_log_warn("bind() failed: %s", strerror(errno));
return -1;
}
avahi_log_warn("*** WARNING: Detected another %s mDNS stack running on this host. This makes mDNS unreliable and is thus not recommended. ***",
sa->sa_family == AF_INET ? "IPv4" : "IPv6");
/* Try again, this time with SO_REUSEADDR set */
if (reuseaddr(fd) < 0)
return -1;
if (bind(fd, sa, l) < 0) {
avahi_log_warn("bind() failed: %s", strerror(errno));
return -1;
}
} else {
/* We enable SO_REUSEADDR afterwards, to make sure that the
* user may run other mDNS implementations if he really
* wants. */
if (reuseaddr(fd) < 0)
return -1;
}
return 0;
}
static int ipv4_pktinfo(int fd) {
int yes;
#ifdef IP_PKTINFO
yes = 1;
if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) {
avahi_log_warn("IP_PKTINFO failed: %s", strerror(errno));
return -1;
}
#else
#ifdef IP_RECVINTERFACE
yes = 1;
if (setsockopt (fd, IPPROTO_IP, IP_RECVINTERFACE, &yes, sizeof(yes)) < 0) {
avahi_log_warn("IP_RECVINTERFACE failed: %s", strerror(errno));
return -1;
}
#elif defined(IP_RECVIF)
yes = 1;
if (setsockopt (fd, IPPROTO_IP, IP_RECVIF, &yes, sizeof(yes)) < 0) {
avahi_log_warn("IP_RECVIF failed: %s", strerror(errno));
return -1;
}
#endif
#ifdef IP_RECVDSTADDR
yes = 1;
if (setsockopt (fd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes)) < 0) {
avahi_log_warn("IP_RECVDSTADDR failed: %s", strerror(errno));
return -1;
}
#endif
#endif /* IP_PKTINFO */
#ifdef IP_RECVTTL
yes = 1;
if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) {
avahi_log_warn("IP_RECVTTL failed: %s", strerror(errno));
return -1;
}
#endif
return 0;
}
static int ipv6_pktinfo(int fd) {
int yes;
#ifdef IPV6_RECVPKTINFO
yes = 1;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &yes, sizeof(yes)) < 0) {
avahi_log_warn("IPV6_RECVPKTINFO failed: %s", strerror(errno));
return -1;
}
#elif defined(IPV6_PKTINFO)
yes = 1;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) {
avahi_log_warn("IPV6_PKTINFO failed: %s", strerror(errno));
return -1;
}
#endif
#ifdef IPV6_RECVHOPS
yes = 1;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPS, &yes, sizeof(yes)) < 0) {
avahi_log_warn("IPV6_RECVHOPS failed: %s", strerror(errno));
return -1;
}
#elif defined(IPV6_RECVHOPLIMIT)
yes = 1;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &yes, sizeof(yes)) < 0) {
avahi_log_warn("IPV6_RECVHOPLIMIT failed: %s", strerror(errno));
return -1;
}
#elif defined(IPV6_HOPLIMIT)
yes = 1;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) {
avahi_log_warn("IPV6_HOPLIMIT failed: %s", strerror(errno));
return -1;
}
#endif
return 0;
}
int avahi_open_socket_ipv4(int no_reuse) {
struct sockaddr_in local;
int fd = -1, r, ittl;
uint8_t ttl, cyes;
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
avahi_log_warn("socket() failed: %s", strerror(errno));
goto fail;
}
ttl = 255;
if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) {
avahi_log_warn("IP_MULTICAST_TTL failed: %s", strerror(errno));
goto fail;
}
ittl = 255;
if (setsockopt(fd, IPPROTO_IP, IP_TTL, &ittl, sizeof(ittl)) < 0) {
avahi_log_warn("IP_TTL failed: %s", strerror(errno));
goto fail;
}
cyes = 1;
if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &cyes, sizeof(cyes)) < 0) {
avahi_log_warn("IP_MULTICAST_LOOP failed: %s", strerror(errno));
goto fail;
}
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(AVAHI_MDNS_PORT);
if (no_reuse)
r = bind(fd, (struct sockaddr*) &local, sizeof(local));
else
r = bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local));
if (r < 0)
goto fail;
if (ipv4_pktinfo (fd) < 0)
goto fail;
if (avahi_set_cloexec(fd) < 0) {
avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
goto fail;
}
if (avahi_set_nonblock(fd) < 0) {
avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
goto fail;
}
return fd;
fail:
if (fd >= 0)
close(fd);
return -1;
}
int avahi_open_socket_ipv6(int no_reuse) {
struct sockaddr_in6 sa, local;
int fd = -1, yes, r;
int ttl;
mdns_mcast_group_ipv6(&sa);
if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
avahi_log_warn("socket() failed: %s", strerror(errno));
goto fail;
}
ttl = 255;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
avahi_log_warn("IPV6_MULTICAST_HOPS failed: %s", strerror(errno));
goto fail;
}
ttl = 255;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
avahi_log_warn("IPV6_UNICAST_HOPS failed: %s", strerror(errno));
goto fail;
}
yes = 1;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) {
avahi_log_warn("IPV6_V6ONLY failed: %s", strerror(errno));
goto fail;
}
yes = 1;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) {
avahi_log_warn("IPV6_MULTICAST_LOOP failed: %s", strerror(errno));
goto fail;
}
memset(&local, 0, sizeof(local));
local.sin6_family = AF_INET6;
local.sin6_port = htons(AVAHI_MDNS_PORT);
if (no_reuse)
r = bind(fd, (struct sockaddr*) &local, sizeof(local));
else
r = bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local));
if (r < 0)
goto fail;
if (ipv6_pktinfo(fd) < 0)
goto fail;
if (avahi_set_cloexec(fd) < 0) {
avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
goto fail;
}
if (avahi_set_nonblock(fd) < 0) {
avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
goto fail;
}
return fd;
fail:
if (fd >= 0)
close(fd);
return -1;
}
static int sendmsg_loop(int fd, struct msghdr *msg, int flags) {
assert(fd >= 0);
assert(msg);
for (;;) {
if (sendmsg(fd, msg, flags) >= 0)
break;
if (errno == EINTR)
continue;
if (errno != EAGAIN) {
char where[64];
struct sockaddr_in *sin = msg->msg_name;
inet_ntop(sin->sin_family, &sin->sin_addr, where, sizeof(where));
avahi_log_debug("sendmsg() to %s failed: %s", where, strerror(errno));
return -1;
}
if (avahi_wait_for_write(fd) < 0)
return -1;
}
return 0;
}
int avahi_send_dns_packet_ipv4(
int fd,
AvahiIfIndex interface,
AvahiDnsPacket *p,
const AvahiIPv4Address *src_address,
const AvahiIPv4Address *dst_address,
uint16_t dst_port) {
struct sockaddr_in sa;
struct msghdr msg;
struct iovec io;
#ifdef IP_PKTINFO
struct cmsghdr *cmsg;
size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_pktinfo)) / sizeof(size_t)) + 1];
#elif !defined(IP_MULTICAST_IF) && defined(IP_SENDSRCADDR)
struct cmsghdr *cmsg;
size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_addr)) / sizeof(size_t)) + 1];
#endif
assert(fd >= 0);
assert(p);
assert(avahi_dns_packet_check_valid(p) >= 0);
assert(!dst_address || dst_port > 0);
if (!dst_address)
mdns_mcast_group_ipv4(&sa);
else
ipv4_address_to_sockaddr(&sa, dst_address, dst_port);
memset(&io, 0, sizeof(io));
io.iov_base = AVAHI_DNS_PACKET_DATA(p);
io.iov_len = p->size;
memset(&msg, 0, sizeof(msg));
msg.msg_name = &sa;
msg.msg_namelen = sizeof(sa);
msg.msg_iov = &io;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
msg.msg_control = NULL;
msg.msg_controllen = 0;
#ifdef IP_PKTINFO
if (interface > 0 || src_address) {
struct in_pktinfo *pkti;
memset(cmsg_data, 0, sizeof(cmsg_data));
msg.msg_control = cmsg_data;
msg.msg_controllen = CMSG_LEN(sizeof(struct in_pktinfo));
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = msg.msg_controllen;
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
pkti = (struct in_pktinfo*) CMSG_DATA(cmsg);
if (interface > 0)
pkti->ipi_ifindex = interface;
if (src_address)
pkti->ipi_spec_dst.s_addr = src_address->address;
}
#elif defined(IP_MULTICAST_IF)
if (src_address) {
struct in_addr any = { INADDR_ANY };
if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, src_address ? &src_address->address : &any, sizeof(struct in_addr)) < 0) {
avahi_log_warn("IP_MULTICAST_IF failed: %s", strerror(errno));
return -1;
}
}
#elif defined(IP_SENDSRCADDR)
if (src_address) {
struct in_addr *addr;
memset(cmsg_data, 0, sizeof(cmsg_data));
msg.msg_control = cmsg_data;
msg.msg_controllen = CMSG_LEN(sizeof(struct in_addr));
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = msg.msg_controllen;
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_SENDSRCADDR;
addr = (struct in_addr *)CMSG_DATA(cmsg);
addr->s_addr = src_address->address;
}
#elif defined(__GNUC__)
#warning "FIXME: We need some code to set the outgoing interface/local address here if IP_PKTINFO/IP_MULTICAST_IF is not available"
#endif
return sendmsg_loop(fd, &msg, 0);
}
int avahi_send_dns_packet_ipv6(
int fd,
AvahiIfIndex interface,
AvahiDnsPacket *p,
const AvahiIPv6Address *src_address,
const AvahiIPv6Address *dst_address,
uint16_t dst_port) {
struct sockaddr_in6 sa;
struct msghdr msg;
struct iovec io;
struct cmsghdr *cmsg;
size_t cmsg_data[(CMSG_SPACE(sizeof(struct in6_pktinfo))/sizeof(size_t)) + 1];
assert(fd >= 0);
assert(p);
assert(avahi_dns_packet_check_valid(p) >= 0);
assert(!dst_address || dst_port > 0);
if (!dst_address)
mdns_mcast_group_ipv6(&sa);
else
ipv6_address_to_sockaddr(&sa, dst_address, dst_port);
memset(&io, 0, sizeof(io));
io.iov_base = AVAHI_DNS_PACKET_DATA(p);
io.iov_len = p->size;
memset(&msg, 0, sizeof(msg));
msg.msg_name = &sa;
msg.msg_namelen = sizeof(sa);
msg.msg_iov = &io;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
if (interface > 0 || src_address) {
struct in6_pktinfo *pkti;
memset(cmsg_data, 0, sizeof(cmsg_data));
msg.msg_control = cmsg_data;
msg.msg_controllen = CMSG_LEN(sizeof(struct in6_pktinfo));
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = msg.msg_controllen;
cmsg->cmsg_level = IPPROTO_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
pkti = (struct in6_pktinfo*) CMSG_DATA(cmsg);
if (interface > 0)
pkti->ipi6_ifindex = interface;
if (src_address)
memcpy(&pkti->ipi6_addr, src_address->address, sizeof(src_address->address));
} else {
msg.msg_control = NULL;
msg.msg_controllen = 0;
}
return sendmsg_loop(fd, &msg, 0);
}
AvahiDnsPacket *avahi_recv_dns_packet_ipv4(
int fd,
AvahiIPv4Address *ret_src_address,
uint16_t *ret_src_port,
AvahiIPv4Address *ret_dst_address,
AvahiIfIndex *ret_iface,
uint8_t *ret_ttl) {
AvahiDnsPacket *p= NULL;
struct msghdr msg;
struct iovec io;
size_t aux[1024 / sizeof(size_t)]; /* for alignment on ia64 ! */
ssize_t l;
struct cmsghdr *cmsg;
int found_addr = 0;
int ms;
struct sockaddr_in sa;
assert(fd >= 0);
if (ioctl(fd, FIONREAD, &ms) < 0) {
avahi_log_warn("ioctl(): %s", strerror(errno));
goto fail;
}
if (ms < 0) {
avahi_log_warn("FIONREAD returned negative value.");
goto fail;
}
p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
io.iov_base = AVAHI_DNS_PACKET_DATA(p);
io.iov_len = p->max_size;
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 = aux;
msg.msg_controllen = sizeof(aux);
msg.msg_flags = 0;
if ((l = recvmsg(fd, &msg, 0)) < 0) {
/* Linux returns EAGAIN when an invalid IP packet has been
received. We suppress warnings in this case because this might
create quite a bit of log traffic on machines with unstable
links. (See #60) */
if (errno != EAGAIN)
avahi_log_warn("recvmsg(): %s", strerror(errno));
goto fail;
}
/* For corrupt packets FIONREAD returns zero size (See rhbz #607297). So
* fail after having read them. */
if (!ms)
goto fail;
if (sa.sin_addr.s_addr == INADDR_ANY)
/* Linux 2.4 behaves very strangely sometimes! */
goto fail;
assert(!(msg.msg_flags & MSG_CTRUNC));
assert(!(msg.msg_flags & MSG_TRUNC));
p->size = (size_t) l;
if (ret_src_port)
*ret_src_port = avahi_port_from_sockaddr((struct sockaddr*) &sa);
if (ret_src_address) {
AvahiAddress a;
avahi_address_from_sockaddr((struct sockaddr*) &sa, &a);
*ret_src_address = a.data.ipv4;
}
if (ret_ttl)
*ret_ttl = 255;
if (ret_iface)
*ret_iface = AVAHI_IF_UNSPEC;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == IPPROTO_IP) {
switch (cmsg->cmsg_type) {
#ifdef IP_RECVTTL
case IP_RECVTTL:
#endif
case IP_TTL:
if (ret_ttl)
*ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
break;
#ifdef IP_PKTINFO
case IP_PKTINFO: {
struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg);
if (ret_iface && i->ipi_ifindex > 0)
*ret_iface = (int) i->ipi_ifindex;
if (ret_dst_address)
ret_dst_address->address = i->ipi_addr.s_addr;
found_addr = 1;
break;
}
#endif
#ifdef IP_RECVIF
case IP_RECVIF: {
struct sockaddr_dl *sdl = (struct sockaddr_dl *) CMSG_DATA (cmsg);
if (ret_iface) {
#ifdef __sun
if (*(uint_t*) sdl > 0)
*ret_iface = *(uint_t*) sdl;
#else
if (sdl->sdl_index > 0)
*ret_iface = (int) sdl->sdl_index;
#endif
}
break;
}
#endif
#ifdef IP_RECVDSTADDR
case IP_RECVDSTADDR:
if (ret_dst_address)
memcpy(&ret_dst_address->address, CMSG_DATA (cmsg), 4);
found_addr = 1;
break;
#endif
default:
avahi_log_warn("Unhandled cmsg_type: %d", cmsg->cmsg_type);
break;
}
}
}
assert(found_addr);
return p;
fail:
if (p)
avahi_dns_packet_free(p);
return NULL;
}
AvahiDnsPacket *avahi_recv_dns_packet_ipv6(
int fd,
AvahiIPv6Address *ret_src_address,
uint16_t *ret_src_port,
AvahiIPv6Address *ret_dst_address,
AvahiIfIndex *ret_iface,
uint8_t *ret_ttl) {
AvahiDnsPacket *p = NULL;
struct msghdr msg;
struct iovec io;
size_t aux[1024 / sizeof(size_t)];
ssize_t l;
int ms;
struct cmsghdr *cmsg;
int found_ttl = 0, found_iface = 0;
struct sockaddr_in6 sa;
assert(fd >= 0);
if (ioctl(fd, FIONREAD, &ms) < 0) {
avahi_log_warn("ioctl(): %s", strerror(errno));
goto fail;
}
if (ms < 0) {
avahi_log_warn("FIONREAD returned negative value.");
goto fail;
}
p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
io.iov_base = AVAHI_DNS_PACKET_DATA(p);
io.iov_len = p->max_size;
memset(&msg, 0, sizeof(msg));
msg.msg_name = (struct sockaddr*) &sa;
msg.msg_namelen = sizeof(sa);
msg.msg_iov = &io;
msg.msg_iovlen = 1;
msg.msg_control = aux;
msg.msg_controllen = sizeof(aux);
msg.msg_flags = 0;
if ((l = recvmsg(fd, &msg, 0)) < 0) {
/* Linux returns EAGAIN when an invalid IP packet has been
received. We suppress warnings in this case because this might
create quite a bit of log traffic on machines with unstable
links. (See #60) */
if (errno != EAGAIN)
avahi_log_warn("recvmsg(): %s", strerror(errno));
goto fail;
}
/* For corrupt packets FIONREAD returns zero size (See rhbz #607297). So
* fail after having read them. */
if (!ms)
goto fail;
assert(!(msg.msg_flags & MSG_CTRUNC));
assert(!(msg.msg_flags & MSG_TRUNC));
p->size = (size_t) l;
if (ret_src_port)
*ret_src_port = avahi_port_from_sockaddr((struct sockaddr*) &sa);
if (ret_src_address) {
AvahiAddress a;
avahi_address_from_sockaddr((struct sockaddr*) &sa, &a);
*ret_src_address = a.data.ipv6;
}
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == IPPROTO_IPV6) {
switch (cmsg->cmsg_type) {
case IPV6_HOPLIMIT:
if (ret_ttl)
*ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
found_ttl = 1;
break;
case IPV6_PKTINFO: {
struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg);
if (ret_iface && i->ipi6_ifindex > 0)
*ret_iface = i->ipi6_ifindex;
if (ret_dst_address)
memcpy(ret_dst_address->address, i->ipi6_addr.s6_addr, 16);
found_iface = 1;
break;
}
default:
avahi_log_warn("Unhandled cmsg_type: %d", cmsg->cmsg_type);
break;
}
}
}
assert(found_iface);
assert(found_ttl);
return p;
fail:
if (p)
avahi_dns_packet_free(p);
return NULL;
}
int avahi_open_unicast_socket_ipv4(void) {
struct sockaddr_in local;
int fd = -1;
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
avahi_log_warn("socket() failed: %s", strerror(errno));
goto fail;
}
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
avahi_log_warn("bind() failed: %s", strerror(errno));
goto fail;
}
if (ipv4_pktinfo(fd) < 0) {
goto fail;
}
if (avahi_set_cloexec(fd) < 0) {
avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
goto fail;
}
if (avahi_set_nonblock(fd) < 0) {
avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
goto fail;
}
return fd;
fail:
if (fd >= 0)
close(fd);
return -1;
}
int avahi_open_unicast_socket_ipv6(void) {
struct sockaddr_in6 local;
int fd = -1, yes;
if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
avahi_log_warn("socket() failed: %s", strerror(errno));
goto fail;
}
yes = 1;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) {
avahi_log_warn("IPV6_V6ONLY failed: %s", strerror(errno));
goto fail;
}
memset(&local, 0, sizeof(local));
local.sin6_family = AF_INET6;
if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
avahi_log_warn("bind() failed: %s", strerror(errno));
goto fail;
}
if (ipv6_pktinfo(fd) < 0)
goto fail;
if (avahi_set_cloexec(fd) < 0) {
avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
goto fail;
}
if (avahi_set_nonblock(fd) < 0) {
avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
goto fail;
}
return fd;
fail:
if (fd >= 0)
close(fd);
return -1;
}

47
avahi-core/socket.h Normal file
View File

@@ -0,0 +1,47 @@
#ifndef foosockethfoo
#define foosockethfoo
/***
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 <inttypes.h>
#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(int no_reuse);
int avahi_open_socket_ipv6(int no_reuse);
int avahi_open_unicast_socket_ipv4(void);
int avahi_open_unicast_socket_ipv6(void);
int avahi_send_dns_packet_ipv4(int fd, AvahiIfIndex iface, AvahiDnsPacket *p, const AvahiIPv4Address *src_address, const AvahiIPv4Address *dst_address, uint16_t dst_port);
int avahi_send_dns_packet_ipv6(int fd, AvahiIfIndex iface, AvahiDnsPacket *p, const AvahiIPv6Address *src_address, const AvahiIPv6Address *dst_address, uint16_t dst_port);
AvahiDnsPacket *avahi_recv_dns_packet_ipv4(int fd, AvahiIPv4Address *ret_src_address, uint16_t *ret_src_port, AvahiIPv4Address *ret_dst_address, AvahiIfIndex *ret_iface, uint8_t *ret_ttl);
AvahiDnsPacket *avahi_recv_dns_packet_ipv6(int fd, AvahiIPv6Address *ret_src_address, uint16_t *ret_src_port, AvahiIPv6Address *ret_dst_address, AvahiIfIndex *ret_iface, uint8_t *ret_ttl);
int avahi_mdns_mcast_join_ipv4(int fd, const AvahiIPv4Address *local_address, int iface, int join);
int avahi_mdns_mcast_join_ipv6(int fd, const AvahiIPv6Address *local_address, int iface, int join);
#endif

225
avahi-core/timeeventq.c Normal file
View File

@@ -0,0 +1,225 @@
/***
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 <assert.h>
#include <stdlib.h>
#include <avahi-common/timeval.h>
#include <avahi-common/malloc.h>
#include "timeeventq.h"
#include "log.h"
struct AvahiTimeEvent {
AvahiTimeEventQueue *queue;
AvahiPrioQueueNode *node;
struct timeval expiry;
struct timeval last_run;
AvahiTimeEventCallback callback;
void* userdata;
};
struct AvahiTimeEventQueue {
const AvahiPoll *poll_api;
AvahiPrioQueue *prioq;
AvahiTimeout *timeout;
};
static int compare(const void* _a, const void* _b) {
const AvahiTimeEvent *a = _a, *b = _b;
int ret;
if ((ret = avahi_timeval_compare(&a->expiry, &b->expiry)) != 0)
return ret;
/* If both exevents are scheduled for the same time, put the entry
* that has been run earlier the last time first. */
return avahi_timeval_compare(&a->last_run, &b->last_run);
}
static AvahiTimeEvent* time_event_queue_root(AvahiTimeEventQueue *q) {
assert(q);
return q->prioq->root ? q->prioq->root->data : NULL;
}
static void update_timeout(AvahiTimeEventQueue *q) {
AvahiTimeEvent *e;
assert(q);
if ((e = time_event_queue_root(q)))
q->poll_api->timeout_update(q->timeout, &e->expiry);
else
q->poll_api->timeout_update(q->timeout, NULL);
}
static void expiration_event(AVAHI_GCC_UNUSED AvahiTimeout *timeout, void *userdata) {
AvahiTimeEventQueue *q = userdata;
AvahiTimeEvent *e;
if ((e = time_event_queue_root(q))) {
struct timeval now;
gettimeofday(&now, NULL);
/* Check if expired */
if (avahi_timeval_compare(&now, &e->expiry) >= 0) {
/* Make sure to move the entry away from the front */
e->last_run = now;
avahi_prio_queue_shuffle(q->prioq, e->node);
/* Run it */
assert(e->callback);
e->callback(e, e->userdata);
update_timeout(q);
return;
}
}
avahi_log_debug(__FILE__": Strange, expiration_event() called, but nothing really happened.");
update_timeout(q);
}
static void fix_expiry_time(AvahiTimeEvent *e) {
struct timeval now;
assert(e);
return; /*** DO WE REALLY NEED THIS? ***/
gettimeofday(&now, NULL);
if (avahi_timeval_compare(&now, &e->expiry) > 0)
e->expiry = now;
}
AvahiTimeEventQueue* avahi_time_event_queue_new(const AvahiPoll *poll_api) {
AvahiTimeEventQueue *q;
if (!(q = avahi_new(AvahiTimeEventQueue, 1))) {
avahi_log_error(__FILE__": Out of memory");
goto oom;
}
q->poll_api = poll_api;
if (!(q->prioq = avahi_prio_queue_new(compare)))
goto oom;
if (!(q->timeout = poll_api->timeout_new(poll_api, NULL, expiration_event, q)))
goto oom;
return q;
oom:
if (q) {
avahi_free(q);
if (q->prioq)
avahi_prio_queue_free(q->prioq);
}
return NULL;
}
void avahi_time_event_queue_free(AvahiTimeEventQueue *q) {
AvahiTimeEvent *e;
assert(q);
while ((e = time_event_queue_root(q)))
avahi_time_event_free(e);
avahi_prio_queue_free(q->prioq);
q->poll_api->timeout_free(q->timeout);
avahi_free(q);
}
AvahiTimeEvent* avahi_time_event_new(
AvahiTimeEventQueue *q,
const struct timeval *timeval,
AvahiTimeEventCallback callback,
void* userdata) {
AvahiTimeEvent *e;
assert(q);
assert(callback);
assert(userdata);
if (!(e = avahi_new(AvahiTimeEvent, 1))) {
avahi_log_error(__FILE__": Out of memory");
return NULL; /* OOM */
}
e->queue = q;
e->callback = callback;
e->userdata = userdata;
if (timeval)
e->expiry = *timeval;
else {
e->expiry.tv_sec = 0;
e->expiry.tv_usec = 0;
}
fix_expiry_time(e);
e->last_run.tv_sec = 0;
e->last_run.tv_usec = 0;
if (!(e->node = avahi_prio_queue_put(q->prioq, e))) {
avahi_free(e);
return NULL;
}
update_timeout(q);
return e;
}
void avahi_time_event_free(AvahiTimeEvent *e) {
AvahiTimeEventQueue *q;
assert(e);
q = e->queue;
avahi_prio_queue_remove(q->prioq, e->node);
avahi_free(e);
update_timeout(q);
}
void avahi_time_event_update(AvahiTimeEvent *e, const struct timeval *timeval) {
assert(e);
assert(timeval);
e->expiry = *timeval;
fix_expiry_time(e);
avahi_prio_queue_shuffle(e->queue->prioq, e->node);
update_timeout(e->queue);
}

46
avahi-core/timeeventq.h Normal file
View File

@@ -0,0 +1,46 @@
#ifndef footimeeventqhfoo
#define footimeeventqhfoo
/***
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 <sys/types.h>
typedef struct AvahiTimeEventQueue AvahiTimeEventQueue;
typedef struct AvahiTimeEvent AvahiTimeEvent;
#include <avahi-common/watch.h>
#include "prioq.h"
typedef void (*AvahiTimeEventCallback)(AvahiTimeEvent *e, void* userdata);
AvahiTimeEventQueue* avahi_time_event_queue_new(const AvahiPoll *poll_api);
void avahi_time_event_queue_free(AvahiTimeEventQueue *q);
AvahiTimeEvent* avahi_time_event_new(
AvahiTimeEventQueue *q,
const struct timeval *timeval,
AvahiTimeEventCallback callback,
void* userdata);
void avahi_time_event_free(AvahiTimeEvent *e);
void avahi_time_event_update(AvahiTimeEvent *e, const struct timeval *timeval);
#endif

120
avahi-core/util.c Normal file
View File

@@ -0,0 +1,120 @@
/***
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 <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include <avahi-common/malloc.h>
#include "util.h"
void avahi_hexdump(const void* p, size_t size) {
const uint8_t *c = p;
assert(p);
printf("Dumping %lu bytes from %p:\n", (unsigned long) size, p);
while (size > 0) {
unsigned i;
for (i = 0; i < 16; i++) {
if (i < size)
printf("%02x ", c[i]);
else
printf(" ");
}
for (i = 0; i < 16; i++) {
if (i < size)
printf("%c", c[i] >= 32 && c[i] < 127 ? c[i] : '.');
else
printf(" ");
}
printf("\n");
c += 16;
if (size <= 16)
break;
size -= 16;
}
}
char *avahi_format_mac_address(char *r, size_t l, const uint8_t* mac, size_t size) {
char *t = r;
unsigned i;
static const char hex[] = "0123456789abcdef";
assert(r);
assert(l > 0);
assert(mac);
if (size <= 0) {
*r = 0;
return r;
}
for (i = 0; i < size; i++) {
if (l < 3)
break;
*(t++) = hex[*mac >> 4];
*(t++) = hex[*mac & 0xF];
*(t++) = ':';
l -= 3;
mac++;
}
if (t > r)
*(t-1) = 0;
else
*r = 0;
return r;
}
char *avahi_strup(char *s) {
char *c;
assert(s);
for (c = s; *c; c++)
*c = (char) toupper(*c);
return s;
}
char *avahi_strdown(char *s) {
char *c;
assert(s);
for (c = s; *c; c++)
*c = (char) tolower(*c);
return s;
}

41
avahi-core/util.h Normal file
View File

@@ -0,0 +1,41 @@
#ifndef fooutilhfoo
#define fooutilhfoo
/***
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 <inttypes.h>
#include <avahi-common/cdecl.h>
AVAHI_C_DECL_BEGIN
void avahi_hexdump(const void *p, size_t size);
char *avahi_format_mac_address(char *t, size_t l, const uint8_t* mac, size_t size);
/** Change every character in the string to upper case (ASCII), return a pointer to the string */
char *avahi_strup(char *s);
/** Change every character in the string to lower case (ASCII), return a pointer to the string */
char *avahi_strdown(char *s);
AVAHI_C_DECL_END
#endif

723
avahi-core/wide-area.c Normal file
View File

@@ -0,0 +1,723 @@
/***
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 <netinet/in.h>
#include <avahi-common/malloc.h>
#include <avahi-common/error.h>
#include <avahi-common/timeval.h>
#include "internal.h"
#include "browse.h"
#include "socket.h"
#include "log.h"
#include "hashmap.h"
#include "wide-area.h"
#include "addr-util.h"
#include "rr-util.h"
#define CACHE_ENTRIES_MAX 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_WIDE_AREA_SERVERS_MAX];
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(AvahiWideAreaLookup *l, AvahiDnsPacket *p) {
AvahiAddress *a;
assert(l);
assert(p);
if (l->engine->n_dns_servers <= 0)
return -1;
assert(l->engine->current_dns_server < l->engine->n_dns_servers);
a = &l->engine->dns_servers[l->engine->current_dns_server];
l->dns_server_used = *a;
if (a->proto == AVAHI_PROTO_INET) {
if (l->engine->fd_ipv4 < 0)
return -1;
return avahi_send_dns_packet_ipv4(l->engine->fd_ipv4, AVAHI_IF_UNSPEC, p, NULL, &a->data.ipv4, AVAHI_DNS_PORT);
} else {
assert(a->proto == AVAHI_PROTO_INET6);
if (l->engine->fd_ipv6 < 0)
return -1;
return avahi_send_dns_packet_ipv6(l->engine->fd_ipv6, AVAHI_IF_UNSPEC, p, NULL, &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 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 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.");
avahi_server_set_errno(l->engine->server, AVAHI_ERR_TIMEOUT);
l->callback(l->engine, AVAHI_BROWSER_FAILURE, AVAHI_LOOKUP_RESULT_WIDE_AREA, NULL, l->userdata);
lookup_stop(l);
return;
}
assert(l->packet);
send_to_dns_server(l, 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(l, 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_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_RESULT_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_RESULT_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 >= CACHE_ENTRIES_MAX)
/* 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 int map_dns_error(uint16_t error) {
static const int table[16] = {
AVAHI_OK,
AVAHI_ERR_DNS_FORMERR,
AVAHI_ERR_DNS_SERVFAIL,
AVAHI_ERR_DNS_NXDOMAIN,
AVAHI_ERR_DNS_NOTIMP,
AVAHI_ERR_DNS_REFUSED,
AVAHI_ERR_DNS_YXDOMAIN,
AVAHI_ERR_DNS_YXRRSET,
AVAHI_ERR_DNS_NXRRSET,
AVAHI_ERR_DNS_NOTAUTH,
AVAHI_ERR_DNS_NOTZONE,
AVAHI_ERR_INVALID_DNS_ERROR,
AVAHI_ERR_INVALID_DNS_ERROR,
AVAHI_ERR_INVALID_DNS_ERROR,
AVAHI_ERR_INVALID_DNS_ERROR,
AVAHI_ERR_INVALID_DNS_ERROR
};
assert(error <= 15);
return table[error];
}
static void handle_packet(AvahiWideAreaLookupEngine *e, AvahiDnsPacket *p) {
AvahiWideAreaLookup *l = NULL;
int i, r;
AvahiBrowserEvent final_event = AVAHI_BROWSER_ALL_FOR_NOW;
assert(e);
assert(p);
/* Some superficial validity tests */
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;
}
/* Look for the lookup that issued this query */
if (!(l = find_lookup(e, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID))) || l->dead)
goto finish;
/* Check whether this a packet indicating a failure */
if ((r = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & 15) != 0 ||
avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
avahi_server_set_errno(e->server, r == 0 ? AVAHI_ERR_NOT_FOUND : map_dns_error(r));
/* Tell the user about the failure */
final_event = AVAHI_BROWSER_FAILURE;
/* We go on here, since some of the records contained in the
reply might be interesting in some way */
}
/* 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 or invalid while reading question key. (Maybe a UTF-8 problem?)");
avahi_server_set_errno(e->server, AVAHI_ERR_INVALID_PACKET);
final_event = AVAHI_BROWSER_FAILURE;
goto finish;
}
avahi_key_unref(k);
}
/* Process responses */
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 or invalid while reading response record. (Maybe a UTF-8 problem?)");
avahi_server_set_errno(e->server, AVAHI_ERR_INVALID_PACKET);
final_event = AVAHI_BROWSER_FAILURE;
goto finish;
}
add_to_cache(e, rr);
avahi_record_unref(rr);
}
finish:
if (l && !l->dead) {
if (l->callback)
l->callback(e, final_event, AVAHI_LOOKUP_RESULT_WIDE_AREA, NULL, l->userdata);
lookup_stop(l);
}
}
static void socket_event(AVAHI_GCC_UNUSED AvahiWatch *w, int fd, AVAHI_GCC_UNUSED AvahiWatchEvent events, void *userdata) {
AvahiWideAreaLookupEngine *e = userdata;
AvahiDnsPacket *p = NULL;
if (fd == e->fd_ipv4)
p = avahi_recv_dns_packet_ipv4(e->fd_ipv4, NULL, NULL, NULL, NULL, NULL);
else {
assert(fd == e->fd_ipv6);
p = avahi_recv_dns_packet_ipv6(e->fd_ipv6, NULL, NULL, NULL, NULL, NULL);
}
if (p) {
handle_packet(e, p);
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 = s->config.use_ipv4 ? avahi_open_unicast_socket_ipv4() : -1;
e->fd_ipv6 = s->config.use_ipv6 ? avahi_open_unicast_socket_ipv6() : -1;
if (e->fd_ipv4 < 0 && e->fd_ipv6 < 0) {
avahi_log_error(__FILE__": Failed to create wide area sockets: %s", 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 */
e->watch_ipv4 = e->watch_ipv6 = NULL;
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_WIDE_AREA_SERVERS_MAX; 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_RESULT_WIDE_AREA|AVAHI_LOOKUP_RESULT_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_RESULT_WIDE_AREA|AVAHI_LOOKUP_RESULT_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;
}

52
avahi-core/wide-area.h Normal file
View File

@@ -0,0 +1,52 @@
#ifndef foowideareahfoo
#define foowideareahfoo
/***
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