--- /dev/null
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "dnssd.h"
+#include "dnssdint.h"
+#include "global.h"
+#include "compat.h"
+#include "utils.h"
+
+#define MAX_DEVICEID 18
+#define MAX_SERVNAME 256
+
+#ifndef WIN32
+# include <dns_sd.h>
+#else
+# include <stdint.h>
+# if !defined(EFI32) && !defined(EFI64)
+# define DNSSD_API __stdcall
+# else
+# define DNSSD_API
+# endif
+
+typedef struct _DNSServiceRef_t *DNSServiceRef;
+typedef union _TXTRecordRef_t { char PrivateData[16]; char *ForceNaturalAlignment; } TXTRecordRef;
+
+typedef uint32_t DNSServiceFlags;
+typedef int32_t DNSServiceErrorType;
+
+typedef void (DNSSD_API *DNSServiceRegisterReply)
+ (
+ DNSServiceRef sdRef,
+ DNSServiceFlags flags,
+ DNSServiceErrorType errorCode,
+ const char *name,
+ const char *regtype,
+ const char *domain,
+ void *context
+ );
+#endif
+
+typedef DNSServiceErrorType (DNSSD_API *DNSServiceRegister_t)
+ (
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ const char *name,
+ const char *regtype,
+ const char *domain,
+ const char *host,
+ uint16_t port,
+ uint16_t txtLen,
+ const void *txtRecord,
+ DNSServiceRegisterReply callBack,
+ void *context
+ );
+typedef void (DNSSD_API *DNSServiceRefDeallocate_t)(DNSServiceRef sdRef);
+typedef void (DNSSD_API *TXTRecordCreate_t)
+ (
+ TXTRecordRef *txtRecord,
+ uint16_t bufferLen,
+ void *buffer
+ );
+typedef void (DNSSD_API *TXTRecordDeallocate_t)(TXTRecordRef *txtRecord);
+typedef DNSServiceErrorType (DNSSD_API *TXTRecordSetValue_t)
+ (
+ TXTRecordRef *txtRecord,
+ const char *key,
+ uint8_t valueSize,
+ const void *value
+ );
+typedef uint16_t (DNSSD_API *TXTRecordGetLength_t)(const TXTRecordRef *txtRecord);
+typedef const void * (DNSSD_API *TXTRecordGetBytesPtr_t)(const TXTRecordRef *txtRecord);
+
+
+struct dnssd_s {
+#ifdef WIN32
+ HMODULE module;
+#endif
+
+ DNSServiceRegister_t DNSServiceRegister;
+ DNSServiceRefDeallocate_t DNSServiceRefDeallocate;
+ TXTRecordCreate_t TXTRecordCreate;
+ TXTRecordSetValue_t TXTRecordSetValue;
+ TXTRecordGetLength_t TXTRecordGetLength;
+ TXTRecordGetBytesPtr_t TXTRecordGetBytesPtr;
+ TXTRecordDeallocate_t TXTRecordDeallocate;
+
+ char hwaddr[MAX_HWADDR_LEN];
+ int hwaddrlen;
+
+ DNSServiceRef raopService;
+ DNSServiceRef airplayService;
+};
+
+
+
+dnssd_t *
+dnssd_init(const char *hwaddr, int hwaddrlen, int *error)
+{
+ dnssd_t *dnssd;
+
+ if (error) *error = DNSSD_ERROR_NOERROR;
+ if (hwaddrlen > MAX_HWADDR_LEN) {
+ if (error) *error = DNSSD_ERROR_HWADDRLEN;
+ return NULL;
+ }
+
+ dnssd = calloc(1, sizeof(dnssd_t));
+ if (!dnssd) {
+ if (error) *error = DNSSD_ERROR_OUTOFMEM;
+ return NULL;
+ }
+
+#ifdef WIN32
+ dnssd->module = LoadLibraryA("dnssd.dll");
+ if (!dnssd->module) {
+ if (error) *error = DNSSD_ERROR_LIBNOTFOUND;
+ free(dnssd);
+ return NULL;
+ }
+ dnssd->DNSServiceRegister = (DNSServiceRegister_t)GetProcAddress(dnssd->module, "DNSServiceRegister");
+ dnssd->DNSServiceRefDeallocate = (DNSServiceRefDeallocate_t)GetProcAddress(dnssd->module, "DNSServiceRefDeallocate");
+ dnssd->TXTRecordCreate = (TXTRecordCreate_t)GetProcAddress(dnssd->module, "TXTRecordCreate");
+ dnssd->TXTRecordSetValue = (TXTRecordSetValue_t)GetProcAddress(dnssd->module, "TXTRecordSetValue");
+ dnssd->TXTRecordGetLength = (TXTRecordGetLength_t)GetProcAddress(dnssd->module, "TXTRecordGetLength");
+ dnssd->TXTRecordGetBytesPtr = (TXTRecordGetBytesPtr_t)GetProcAddress(dnssd->module, "TXTRecordGetBytesPtr");
+ dnssd->TXTRecordDeallocate = (TXTRecordDeallocate_t)GetProcAddress(dnssd->module, "TXTRecordDeallocate");
+
+ if (!dnssd->DNSServiceRegister || !dnssd->DNSServiceRefDeallocate || !dnssd->TXTRecordCreate ||
+ !dnssd->TXTRecordSetValue || !dnssd->TXTRecordGetLength || !dnssd->TXTRecordGetBytesPtr ||
+ !dnssd->TXTRecordDeallocate) {
+ if (error) *error = DNSSD_ERROR_PROCNOTFOUND;
+ FreeLibrary(dnssd->module);
+ free(dnssd);
+ return NULL;
+ }
+#else
+ dnssd->DNSServiceRegister = &DNSServiceRegister;
+ dnssd->DNSServiceRefDeallocate = &DNSServiceRefDeallocate;
+ dnssd->TXTRecordCreate = &TXTRecordCreate;
+ dnssd->TXTRecordSetValue = &TXTRecordSetValue;
+ dnssd->TXTRecordGetLength = &TXTRecordGetLength;
+ dnssd->TXTRecordGetBytesPtr = &TXTRecordGetBytesPtr;
+ dnssd->TXTRecordDeallocate = &TXTRecordDeallocate;
+#endif
+
+ memcpy(dnssd->hwaddr, hwaddr, hwaddrlen);
+ dnssd->hwaddrlen = hwaddrlen;
+
+ return dnssd;
+}
+
+void
+dnssd_destroy(dnssd_t *dnssd)
+{
+ if (dnssd) {
+#ifdef WIN32
+ FreeLibrary(dnssd->module);
+#endif
+ free(dnssd);
+ }
+}
+
+int
+dnssd_register_raop(dnssd_t *dnssd, const char *name, unsigned short port)
+{
+ TXTRecordRef txtRecord;
+ char servname[MAX_SERVNAME];
+ int ret;
+
+ assert(dnssd);
+
+ dnssd->TXTRecordCreate(&txtRecord, 0, NULL);
+ dnssd->TXTRecordSetValue(&txtRecord, "txtvers", strlen(RAOP_TXTVERS), RAOP_TXTVERS);
+ dnssd->TXTRecordSetValue(&txtRecord, "ch", strlen(RAOP_CH), RAOP_CH);
+ dnssd->TXTRecordSetValue(&txtRecord, "cn", strlen(RAOP_CN), RAOP_CN);
+ dnssd->TXTRecordSetValue(&txtRecord, "et", strlen(RAOP_ET), RAOP_ET);
+ dnssd->TXTRecordSetValue(&txtRecord, "sv", strlen(RAOP_SV), RAOP_SV);
+ dnssd->TXTRecordSetValue(&txtRecord, "da", strlen(RAOP_DA), RAOP_DA);
+ dnssd->TXTRecordSetValue(&txtRecord, "sr", strlen(RAOP_SR), RAOP_SR);
+ dnssd->TXTRecordSetValue(&txtRecord, "ss", strlen(RAOP_SS), RAOP_SS);
+ dnssd->TXTRecordSetValue(&txtRecord, "pw", strlen(RAOP_PW), RAOP_PW);
+ dnssd->TXTRecordSetValue(&txtRecord, "vn", strlen(RAOP_VN), RAOP_VN);
+ dnssd->TXTRecordSetValue(&txtRecord, "tp", strlen(RAOP_TP), RAOP_TP);
+ dnssd->TXTRecordSetValue(&txtRecord, "md", strlen(RAOP_MD), RAOP_MD);
+ dnssd->TXTRecordSetValue(&txtRecord, "vs", strlen(GLOBAL_VERSION), GLOBAL_VERSION);
+ dnssd->TXTRecordSetValue(&txtRecord, "am", strlen(RAOP_AM), RAOP_AM);
+ dnssd->TXTRecordSetValue(&txtRecord, "sf", strlen(RAOP_SF), RAOP_SF);
+
+ /* Convert hardware address to string */
+ ret = utils_hwaddr_raop(servname, sizeof(servname), dnssd->hwaddr, dnssd->hwaddrlen);
+ if (ret < 0) {
+ /* FIXME: handle better */
+ return -1;
+ }
+
+ /* Check that we have bytes for 'hw@name' format */
+ if (sizeof(servname) < strlen(servname)+1+strlen(name)+1) {
+ /* FIXME: handle better */
+ return -2;
+ }
+
+ strncat(servname, "@", sizeof(servname)-strlen(servname)-1);
+ strncat(servname, name, sizeof(servname)-strlen(servname)-1);
+
+ /* Register the service */
+ dnssd->DNSServiceRegister(&dnssd->raopService, 0, 0,
+ servname, "_raop._tcp",
+ NULL, NULL,
+ htons(port),
+ dnssd->TXTRecordGetLength(&txtRecord),
+ dnssd->TXTRecordGetBytesPtr(&txtRecord),
+ NULL, NULL);
+
+ /* Deallocate TXT record */
+ dnssd->TXTRecordDeallocate(&txtRecord);
+ return 1;
+}
+
+int
+dnssd_register_airplay(dnssd_t *dnssd, const char *name, unsigned short port)
+{
+ TXTRecordRef txtRecord;
+ char deviceid[3*MAX_HWADDR_LEN];
+ char features[16];
+ int ret;
+
+ assert(dnssd);
+
+ /* Convert hardware address to string */
+ ret = utils_hwaddr_airplay(deviceid, sizeof(deviceid), dnssd->hwaddr, dnssd->hwaddrlen);
+ if (ret < 0) {
+ /* FIXME: handle better */
+ return -1;
+ }
+
+ features[sizeof(features)-1] = '\0';
+ snprintf(features, sizeof(features)-1, "0x%x", GLOBAL_FEATURES);
+
+ dnssd->TXTRecordCreate(&txtRecord, 0, NULL);
+ dnssd->TXTRecordSetValue(&txtRecord, "deviceid", strlen(deviceid), deviceid);
+ dnssd->TXTRecordSetValue(&txtRecord, "features", strlen(features), features);
+ dnssd->TXTRecordSetValue(&txtRecord, "model", strlen(GLOBAL_MODEL), GLOBAL_MODEL);
+
+ /* Register the service */
+ dnssd->DNSServiceRegister(&dnssd->airplayService, 0, 0,
+ name, "_airplay._tcp",
+ NULL, NULL,
+ htons(port),
+ dnssd->TXTRecordGetLength(&txtRecord),
+ dnssd->TXTRecordGetBytesPtr(&txtRecord),
+ NULL, NULL);
+
+ /* Deallocate TXT record */
+ dnssd->TXTRecordDeallocate(&txtRecord);
+ return 0;
+}
+
+void
+dnssd_unregister_raop(dnssd_t *dnssd)
+{
+ assert(dnssd);
+
+ if (!dnssd->raopService) {
+ return;
+ }
+
+ dnssd->DNSServiceRefDeallocate(dnssd->raopService);
+ dnssd->raopService = NULL;
+}
+
+void
+dnssd_unregister_airplay(dnssd_t *dnssd)
+{
+ assert(dnssd);
+
+ if (!dnssd->airplayService) {
+ return;
+ }
+
+ dnssd->DNSServiceRefDeallocate(dnssd->airplayService);
+ dnssd->airplayService = NULL;
+}