[mdns] CNAME record so we have fixed uri for OAuth redirects

- also refactor mdns_avahi
This commit is contained in:
ejurgensen 2016-11-05 13:31:13 +01:00
parent 33c22a59b9
commit 90ecc61ed7
2 changed files with 144 additions and 39 deletions

View File

@ -37,6 +37,16 @@ mdns_deinit(void);
int int
mdns_register(char *name, char *type, int port, char **txt); mdns_register(char *name, char *type, int port, char **txt);
/*
* Register a CNAME record, it will be an alias for hostname
* Call only from the main thread!
*
* @in name The CNAME alias, e.g. "forked-daapd.local"
* @return 0 on success, -1 on error
*/
int
mdns_cname(char *name);
/* /*
* Start a service browser, a callback will be made when the service changes state * Start a service browser, a callback will be made when the service changes state
* Call only from the main thread! * Call only from the main thread!

View File

@ -33,6 +33,7 @@
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <net/if.h> #include <net/if.h>
#include <unistd.h>
#include <event2/event.h> #include <event2/event.h>
@ -351,8 +352,15 @@ struct mdns_record_browser {
int port; int port;
}; };
enum publish
{
MDNS_PUBLISH_SERVICE,
MDNS_PUBLISH_CNAME,
};
struct mdns_group_entry struct mdns_group_entry
{ {
enum publish publish;
char *name; char *name;
char *type; char *type;
int port; int port;
@ -620,13 +628,100 @@ entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_U
} }
} }
static void static int
_create_services(void) create_group_entry(struct mdns_group_entry *ge, int commit)
{ {
struct mdns_group_entry *pentry; char hostname[HOST_NAME_MAX + 1];
char rdata[HOST_NAME_MAX + 6 + 1]; // Includes room for ".local" and 0-terminator
int count;
int i;
int ret; int ret;
DPRINTF(E_DBG, L_MDNS, "Creating service group\n"); if (!mdns_group)
{
mdns_group = avahi_entry_group_new(mdns_client, entry_group_callback, NULL);
if (!mdns_group)
{
DPRINTF(E_WARN, L_MDNS, "Could not create Avahi EntryGroup: %s\n", MDNSERR);
return -1;
}
}
if (ge->publish == MDNS_PUBLISH_SERVICE)
{
DPRINTF(E_DBG, L_MDNS, "Adding service %s/%s\n", ge->name, ge->type);
ret = avahi_entry_group_add_service_strlst(mdns_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0,
ge->name, ge->type,
NULL, NULL, ge->port, ge->txt);
if (ret < 0)
{
DPRINTF(E_LOG, L_MDNS, "Could not add mDNS service %s/%s: %s\n", ge->name, ge->type, avahi_strerror(ret));
return -1;
}
}
else if (ge->publish == MDNS_PUBLISH_CNAME)
{
DPRINTF(E_DBG, L_MDNS, "Adding CNAME record %s\n", ge->name);
ret = gethostname(hostname, HOST_NAME_MAX);
if (ret < 0)
{
DPRINTF(E_LOG, L_MDNS, "Could not add CNAME %s, gethostname failed\n", ge->name);
return -1;
}
// Note, gethostname does not guarantee 0-termination
ret = snprintf(rdata, sizeof(rdata), ".%s.local", hostname);
if (!(ret > 0 && ret < sizeof(rdata)))
{
DPRINTF(E_LOG, L_MDNS, "Could not add CNAME %s, hostname is invalid\n", ge->name);
return -1;
}
// Convert to dns string: .forked-daapd.local -> \12forked-daapd\6local
count = 0;
for (i = ret - 1; i >= 0; i--)
{
if (rdata[i] == '.')
{
rdata[i] = count;
count = 0;
}
else
count++;
}
// ret + 1 should be the string length of rdata incl. 0-terminator
ret = avahi_entry_group_add_record(mdns_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
AVAHI_PUBLISH_USE_MULTICAST | AVAHI_PUBLISH_ALLOW_MULTIPLE,
ge->name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_CNAME,
AVAHI_DEFAULT_TTL, rdata, ret + 1);
if (ret < 0)
{
DPRINTF(E_LOG, L_MDNS, "Could not add CNAME record %s: %s\n", ge->name, avahi_strerror(ret));
return -1;
}
}
if (!commit)
return 0;
ret = avahi_entry_group_commit(mdns_group);
if (ret < 0)
{
DPRINTF(E_LOG, L_MDNS, "Could not commit mDNS services: %s\n", MDNSERR);
return -1;
}
return 0;
}
static void
create_all_group_entries(void)
{
struct mdns_group_entry *ge;
int ret;
if (!group_entries) if (!group_entries)
{ {
@ -634,32 +729,17 @@ _create_services(void)
return; return;
} }
if (mdns_group == NULL) if (mdns_group)
avahi_entry_group_reset(mdns_group);
DPRINTF(E_INFO, L_MDNS, "Re-registering mDNS groups (services and records)\n");
for (ge = group_entries; ge; ge = ge->next)
{ {
mdns_group = avahi_entry_group_new(mdns_client, entry_group_callback, NULL); create_group_entry(ge, 0);
if (!mdns_group) if (!mdns_group)
{
DPRINTF(E_WARN, L_MDNS, "Could not create Avahi EntryGroup: %s\n", MDNSERR);
return; return;
} }
}
pentry = group_entries;
while (pentry)
{
DPRINTF(E_DBG, L_MDNS, "Re-registering %s/%s\n", pentry->name, pentry->type);
ret = avahi_entry_group_add_service_strlst(mdns_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0,
pentry->name, pentry->type,
NULL, NULL, pentry->port, pentry->txt);
if (ret < 0)
{
DPRINTF(E_WARN, L_MDNS, "Could not add mDNS services: %s\n", avahi_strerror(ret));
return;
}
pentry = pentry->next;
}
ret = avahi_entry_group_commit(mdns_group); ret = avahi_entry_group_commit(mdns_group);
if (ret < 0) if (ret < 0)
@ -678,7 +758,7 @@ client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void *
case AVAHI_CLIENT_S_RUNNING: case AVAHI_CLIENT_S_RUNNING:
DPRINTF(E_LOG, L_MDNS, "Avahi state change: Client running\n"); DPRINTF(E_LOG, L_MDNS, "Avahi state change: Client running\n");
if (!mdns_group) if (!mdns_group)
_create_services(); create_all_group_entries();
for (mb = browser_list; mb; mb = mb->next) for (mb = browser_list; mb; mb = mb->next)
{ {
@ -806,8 +886,6 @@ mdns_register(char *name, char *type, int port, char **txt)
AvahiStringList *txt_sl; AvahiStringList *txt_sl;
int i; int i;
DPRINTF(E_DBG, L_MDNS, "Adding mDNS service %s/%s\n", name, type);
ge = calloc(1, sizeof(struct mdns_group_entry)); ge = calloc(1, sizeof(struct mdns_group_entry));
if (!ge) if (!ge)
{ {
@ -815,6 +893,7 @@ mdns_register(char *name, char *type, int port, char **txt)
return -1; return -1;
} }
ge->publish = MDNS_PUBLISH_SERVICE;
ge->name = strdup(name); ge->name = strdup(name);
ge->type = strdup(type); ge->type = strdup(type);
ge->port = port; ge->port = port;
@ -835,14 +914,30 @@ mdns_register(char *name, char *type, int port, char **txt)
ge->next = group_entries; ge->next = group_entries;
group_entries = ge; group_entries = ge;
if (mdns_group) create_all_group_entries(); // TODO why is this required?
{
DPRINTF(E_DBG, L_MDNS, "Resetting mDNS group\n"); return 0;
avahi_entry_group_reset(mdns_group);
} }
DPRINTF(E_DBG, L_MDNS, "Creating service group\n"); int
_create_services(); mdns_cname(char *name)
{
struct mdns_group_entry *ge;
ge = calloc(1, sizeof(struct mdns_group_entry));
if (!ge)
{
DPRINTF(E_LOG, L_MDNS, "Out of memory for mDNS CNAME\n");
return -1;
}
ge->publish = MDNS_PUBLISH_CNAME;
ge->name = strdup(name);
ge->next = group_entries;
group_entries = ge;
create_all_group_entries();
return 0; return 0;
} }