Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 156 additions & 1 deletion libmdnsd/mdnsd.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <time.h>
#include <errno.h>
#include <ifaddrs.h>
#include <arpa/inet.h>

#define SPRIME 109 /* Size of query/publish hashes */
#define LPRIME 1009 /* Size of cache hash */
Expand Down Expand Up @@ -944,7 +945,7 @@ int mdnsd_in(mdns_daemon_t *d, struct message *m, struct in_addr ip, unsigned sh
if (!r)
continue;

/* Service enumeration/discovery prepeare to send all matching records */
/* Service enumeration/discovery prepare to send all matching records */
if (!strcmp(m->qd[i].name, DISCO_NAME)) {
d->disco = 1;
while (r) {
Expand Down Expand Up @@ -1533,6 +1534,160 @@ void mdnsd_set_srv(mdns_daemon_t *d, mdns_record_t *r, unsigned short priority,
mdnsd_set_host(d, r, name);
}

/* Internal helper: update set of addresses for given host & type (A/AAAA) */
static int _update_addresses_for_host(mdns_daemon_t *d, const char *host, unsigned short type, const void *addrs, size_t count)
{
mdns_record_t *r;
mdns_record_t *cur;
unsigned long ttl = 120; /* default TTL if none exists */
char buf6[INET6_ADDRSTRLEN];

if (!d || !host)
return -1;

/* Determine TTL from any existing record of this type */
r = mdnsd_find(d, host, type);
if (r) {
const mdns_answer_t *data = mdnsd_record_data(r);
if (data && data->ttl)
ttl = data->ttl;
}

/* For each desired address, check if there's a matching existing record */
for (size_t i = 0; i < count; i++) {
bool found = false;
cur = mdnsd_get_published(d, host);
while (cur) {
const mdns_answer_t *data = mdnsd_record_data(cur);
if (data->type == type && strcmp(data->name, host) == 0) {
if (type == QTYPE_A) {
const struct in_addr *a = (const struct in_addr *)addrs;
if (data->ip.s_addr == a[i].s_addr) {
found = true;
INFO("Found A record for %s addr %s", host, inet_ntoa(a[i]));
break;
}
} else if (type == QTYPE_AAAA) {
const struct in6_addr *a6 = (const struct in6_addr *)addrs;
if (memcmp(&data->ip6, &a6[i], sizeof(struct in6_addr)) == 0) {
found = true;
INFO("Found AAAA record for %s addr %s", host, inet_ntop(AF_INET6, &a6[i], buf6, sizeof(buf6)));
break;
}
}
}
cur = mdnsd_record_next(cur);
}
if (!found) {
/* Create new record for this address */
mdns_record_t *nr = mdnsd_shared(d, host, type, ttl);
if (!nr)
continue;
if (type == QTYPE_A) {
const struct in_addr *a = (const struct in_addr *)addrs;
mdnsd_set_ip(d, nr, a[i]);
INFO("Created A record for %s addr %s", host, inet_ntoa(a[i]));
} else {
const struct in6_addr *a6 = (const struct in6_addr *)addrs;
mdnsd_set_ipv6(d, nr, a6[i]);
INFO("Created AAAA record for %s addr %s", host, inet_ntop(AF_INET6, &a6[i], buf6, sizeof(buf6)));
}
}
}

/* Remove stale records (present but not in desired set) */
cur = mdnsd_get_published(d, host);
while (cur) {
mdns_record_t *next = mdnsd_record_next(cur);
const mdns_answer_t *data = mdnsd_record_data(cur);
if (data->type == type && strcmp(data->name, host) == 0) {
bool still_present = false;
for (size_t i = 0; i < count; i++) {
if (type == QTYPE_A) {
const struct in_addr *a = (const struct in_addr *)addrs;
if (data->ip.s_addr == a[i].s_addr) { still_present = true; break; }
} else {
const struct in6_addr *a6 = (const struct in6_addr *)addrs;
if (memcmp(&data->ip6, &a6[i], sizeof(struct in6_addr)) == 0) { still_present = true; break; }
}
}
if (!still_present)
mdnsd_done(d, cur);
}
cur = next;
}

return 0;
}

int mdnsd_set_addresses_for_host(mdns_daemon_t *d, const char *host, const struct in_addr *addrs, size_t count)
{
return _update_addresses_for_host(d, host, QTYPE_A, addrs, count);
}

int mdnsd_set_ipv6_addresses_for_host(mdns_daemon_t *d, const char *host, const struct in6_addr *addrs, size_t count)
{
return _update_addresses_for_host(d, host, QTYPE_AAAA, addrs, count);
}

int mdnsd_set_interface_addresses(mdns_daemon_t *d, const char *ifname)
{
struct ifaddrs *ifa = NULL;
struct ifaddrs *it;
struct in_addr *v4 = NULL; size_t v4c = 0;
struct in6_addr *v6 = NULL; size_t v6c = 0;
char buf[INET_ADDRSTRLEN];
char buf6[INET6_ADDRSTRLEN];

if (getifaddrs(&ifa) != 0)
return -1;

/* Collect all v4/v6 addresses for the interface */
for (it = ifa; it; it = it->ifa_next) {
if (!it->ifa_addr || !it->ifa_name || strcmp(it->ifa_name, ifname))
continue;
if (it->ifa_addr->sa_family == AF_INET) {
v4 = realloc(v4, (v4c + 1) * sizeof(*v4));
if (v4) {
struct sockaddr_in *sin = (struct sockaddr_in *)it->ifa_addr;
v4[v4c++] = sin->sin_addr;
if(inet_ntop(AF_INET, &sin->sin_addr, buf, sizeof(buf)))
INFO("Adding address %s for interface %s", buf, ifname);
}
} else if (it->ifa_addr->sa_family == AF_INET6) {
v6 = realloc(v6, (v6c + 1) * sizeof(*v6));
if (v6) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)it->ifa_addr;
v6[v6c++] = sin6->sin6_addr;
if (inet_ntop(AF_INET6, &sin6->sin6_addr, buf6, sizeof(buf6)))
INFO("Adding address %s for interface %s", buf6, ifname);
}
}
}
freeifaddrs(ifa);

/* Find all host names currently published as A/AAAA and update each */
for (size_t idx = 0; idx < SPRIME; idx++) {
mdns_record_t *cur = d->published[idx];
while (cur) {
const mdns_answer_t *data = mdnsd_record_data(cur);
if (data->type == QTYPE_A || data->type == QTYPE_AAAA) {
INFO("Updating addresses for host %s type %d", data->name, data->type);
const char *host = data->name;
if (v4c)
mdnsd_set_addresses_for_host(d, host, v4, v4c);
if (v6c)
mdnsd_set_ipv6_addresses_for_host(d, host, v6, v6c);
}
cur = mdnsd_record_next(cur);
}
}

free(v4);
free(v6);
return 0;
}

static int process_in(mdns_daemon_t *d, int sd)
{
static unsigned char buf[MAX_PACKET_LEN + 1];
Expand Down
27 changes: 25 additions & 2 deletions libmdnsd/mdnsd.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ int mdnsd_in(mdns_daemon_t *d, struct message *m, struct in_addr ip, unsigned sh
int mdnsd_out(mdns_daemon_t *d, struct message *m, struct in_addr *ip, unsigned short *port);

/**
* returns the max wait-time until mdnsd_out() needs to be called again
* returns the max wait-time until mdnsd_out() needs to be called again
*/
struct timeval *mdnsd_sleep(mdns_daemon_t *d);

Expand Down Expand Up @@ -216,7 +216,7 @@ const mdns_answer_t *mdnsd_record_data(const mdns_record_t *r);
mdns_record_t *mdnsd_unique(mdns_daemon_t *d, const char *host, unsigned short type, unsigned long ttl, void (*conflict)(char *host, int type, void *arg), void *arg);


/**
/**
* Create a new shared record
*/
mdns_record_t *mdnsd_shared(mdns_daemon_t *d, const char *host, unsigned short type, unsigned long ttl);
Expand Down Expand Up @@ -262,4 +262,27 @@ int mdnsd_step(mdns_daemon_t *d, int mdns_socket, bool processIn, bool processOu
* Returns none
*/
void records_clear(mdns_daemon_t *d);

/**
* Multi-address support: set all IPv4 addresses for a given host name.
* Adds missing A records and removes stale ones.
* Returns 0 on success.
*/
int mdnsd_set_addresses_for_host(mdns_daemon_t *d, const char *host,
const struct in_addr *addrs, size_t count);

/**
* Multi-address support: set all IPv6 addresses for a given host name.
* Adds missing AAAA records and removes stale ones.
* Returns 0 on success.
*/
int mdnsd_set_ipv6_addresses_for_host(mdns_daemon_t *d, const char *host,
const struct in6_addr *addrs, size_t count);

/**
* Automatic interface discovery: update all published host A/AAAA records
* to match all addresses currently assigned to the given interface.
* Returns 0 on success.
*/
int mdnsd_set_interface_addresses(mdns_daemon_t *d, const char *ifname);
#endif /* LIB_MDNSD_H_ */
17 changes: 5 additions & 12 deletions src/conf.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,19 +207,12 @@ static int load(struct iface *iface, const char *path, const char *hostname)
r = record(iface, 0, NULL, hlocal, QTYPE_SRV, 120);
mdnsd_set_srv(d, r, 0, 0, srec.port, nlocal);

/* If an IPv4 address is already set, add an A record for it. */
struct in_addr ipv4addr = mdnsd_get_address(d);
if (ipv4addr.s_addr != 0) {
r = record(iface, 0, NULL, nlocal, QTYPE_A, 120);
mdnsd_set_ip(d, r, ipv4addr);
}
/* Ensure A/AAAA records exist; addresses populated from interface */
r = record(iface, 0, NULL, nlocal, QTYPE_A, 120);
r = record(iface, 0, NULL, nlocal, QTYPE_AAAA, 120);

/* If an IPv6 address is already set, add an AAAA record for it. */
struct in6_addr ipv6addr = mdnsd_get_ipv6_address(d);
if (!IN6_IS_ADDR_UNSPECIFIED(&ipv6addr)) {
r = record(iface, 0, NULL, nlocal, QTYPE_AAAA, 120);
mdnsd_set_ipv6(d, r, ipv6addr);
}
/* Publish all v4/v6 addresses for this interface and host */
mdnsd_set_interface_addresses(d, iface->ifname);

if (srec.cname)
record(iface, 1, srec.cname, nlocal, QTYPE_CNAME, 120);
Expand Down
8 changes: 3 additions & 5 deletions src/mdnsd.c
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,6 @@ static void setup_iface(struct iface *iface)
ERR("Failed creating mDNS context for interface %s: %s", iface->ifname, strerror(errno));
exit(1);
}

mdnsd_set_address(iface->mdns, iface->inaddr);
mdnsd_set_ipv6_address(iface->mdns, iface->in6addr);
mdnsd_register_receive_callback(iface->mdns, record_received, NULL);
}

Expand All @@ -164,8 +161,9 @@ static void setup_iface(struct iface *iface)
}
}

mdnsd_set_address(iface->mdns, iface->inaddr);
mdnsd_set_ipv6_address(iface->mdns, iface->in6addr);
/* Update all A/AAAA records to reflect all addresses on this interface */
/* NOTE this does not appear to be necessary. On startup, no records exist until load() is called, see conf.c */
// mdnsd_set_interface_addresses(iface->mdns, iface->ifname);

records_clear(iface->mdns);
conf_init(iface, path, hostnm);
Expand Down
Loading