Move src to src/lib, include to src/include, test to src/test.
[deb_shairplay.git] / src / lib / dnssd.c
diff --git a/src/lib/dnssd.c b/src/lib/dnssd.c
new file mode 100644 (file)
index 0000000..96e32dc
--- /dev/null
@@ -0,0 +1,285 @@
+#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;
+}