/** * Copyright (C) 2011-2012 Juho Vähä-Herttua * * This library 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. * * This library 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. * *================================================================= * modified by fduncanh 2022 */ /* These defines allow us to compile on iOS */ #ifndef __has_feature # define __has_feature(x) 0 #endif #ifndef __has_extension # define __has_extension __has_feature #endif #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "dnssdint.h" #include "dnssd.h" #include "global.h" #include "compat.h" #include "utils.h" #include #define MAX_DEVICEID 18 #define MAX_SERVNAME 256 #if defined(HAVE_LIBDL) && !defined(__APPLE__) # define USE_LIBDL 1 #else # define USE_LIBDL 0 #endif #if defined(_WIN32) || USE_LIBDL # ifdef _WIN32 # include # if !defined(EFI32) && !defined(EFI64) # define DNSSD_STDCALL __stdcall # else # define DNSSD_STDCALL # endif # else # include # define DNSSD_STDCALL # endif typedef struct _DNSServiceRef_t *DNSServiceRef; #ifndef _WIN32 typedef union _TXTRecordRef_t { char PrivateData[16]; char *ForceNaturalAlignment; } TXTRecordRef; #endif typedef uint32_t DNSServiceFlags; typedef int32_t DNSServiceErrorType; typedef void (DNSSD_STDCALL *DNSServiceRegisterReply) ( DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, void *context ); #else //# include # define DNSSD_STDCALL #endif typedef DNSServiceErrorType (DNSSD_STDCALL *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_STDCALL *DNSServiceRefDeallocate_t)(DNSServiceRef sdRef); typedef void (DNSSD_STDCALL *TXTRecordCreate_t) ( TXTRecordRef *txtRecord, uint16_t bufferLen, void *buffer ); typedef void (DNSSD_STDCALL *TXTRecordDeallocate_t)(TXTRecordRef *txtRecord); typedef DNSServiceErrorType (DNSSD_STDCALL *TXTRecordSetValue_t) ( TXTRecordRef *txtRecord, const char *key, uint8_t valueSize, const void *value ); typedef uint16_t (DNSSD_STDCALL *TXTRecordGetLength_t)(const TXTRecordRef *txtRecord); typedef const void * (DNSSD_STDCALL *TXTRecordGetBytesPtr_t)(const TXTRecordRef *txtRecord); struct dnssd_s { #ifdef WIN32 HMODULE module; #elif USE_LIBDL void *module; #endif DNSServiceRegister_t DNSServiceRegister; DNSServiceRefDeallocate_t DNSServiceRefDeallocate; TXTRecordCreate_t TXTRecordCreate; TXTRecordSetValue_t TXTRecordSetValue; TXTRecordGetLength_t TXTRecordGetLength; TXTRecordGetBytesPtr_t TXTRecordGetBytesPtr; TXTRecordDeallocate_t TXTRecordDeallocate; TXTRecordRef raop_record; TXTRecordRef airplay_record; DNSServiceRef raop_service; DNSServiceRef airplay_service; char *name; int name_len; char *hw_addr; int hw_addr_len; char *pk; uint32_t features1; uint32_t features2; unsigned char require_pw; }; dnssd_t * dnssd_init(const char* name, int name_len, const char* hw_addr, int hw_addr_len, int *error, int require_pw) { dnssd_t *dnssd; char *end; unsigned long features; if (error) *error = DNSSD_ERROR_NOERROR; dnssd = calloc(1, sizeof(dnssd_t)); if (!dnssd) { if (error) *error = DNSSD_ERROR_OUTOFMEM; return NULL; } dnssd->require_pw = (unsigned char) require_pw; features = strtoul(FEATURES_1, &end, 16); if (!end || (features & 0xFFFFFFFF) != features) { free (dnssd); if (error) *error = DNSSD_ERROR_BADFEATURES; return NULL; } dnssd->features1 = (uint32_t) features; features = strtoul(FEATURES_2, &end, 16); if (!end || (features & 0xFFFFFFFF) != features) { free (dnssd); if (error) *error = DNSSD_ERROR_BADFEATURES; return NULL; } dnssd->features2 = (uint32_t) features; #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; } #elif USE_LIBDL dnssd->module = dlopen("libdns_sd.so", RTLD_LAZY); if (!dnssd->module) { if (error) *error = DNSSD_ERROR_LIBNOTFOUND; free(dnssd); return NULL; } dnssd->DNSServiceRegister = (DNSServiceRegister_t)dlsym(dnssd->module, "DNSServiceRegister"); dnssd->DNSServiceRefDeallocate = (DNSServiceRefDeallocate_t)dlsym(dnssd->module, "DNSServiceRefDeallocate"); dnssd->TXTRecordCreate = (TXTRecordCreate_t)dlsym(dnssd->module, "TXTRecordCreate"); dnssd->TXTRecordSetValue = (TXTRecordSetValue_t)dlsym(dnssd->module, "TXTRecordSetValue"); dnssd->TXTRecordGetLength = (TXTRecordGetLength_t)dlsym(dnssd->module, "TXTRecordGetLength"); dnssd->TXTRecordGetBytesPtr = (TXTRecordGetBytesPtr_t)dlsym(dnssd->module, "TXTRecordGetBytesPtr"); dnssd->TXTRecordDeallocate = (TXTRecordDeallocate_t)dlsym(dnssd->module, "TXTRecordDeallocate"); if (!dnssd->DNSServiceRegister || !dnssd->DNSServiceRefDeallocate || !dnssd->TXTRecordCreate || !dnssd->TXTRecordSetValue || !dnssd->TXTRecordGetLength || !dnssd->TXTRecordGetBytesPtr || !dnssd->TXTRecordDeallocate) { if (error) *error = DNSSD_ERROR_PROCNOTFOUND; dlclose(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 dnssd->name_len = name_len; dnssd->name = calloc(1, name_len + 1); if (!dnssd->name) { free(dnssd); if (error) *error = DNSSD_ERROR_OUTOFMEM; return NULL; } memcpy(dnssd->name, name, name_len); dnssd->hw_addr_len = hw_addr_len; dnssd->hw_addr = calloc(1, dnssd->hw_addr_len); if (!dnssd->hw_addr) { free(dnssd->name); free(dnssd); if (error) *error = DNSSD_ERROR_OUTOFMEM; return NULL; } memcpy(dnssd->hw_addr, hw_addr, hw_addr_len); return dnssd; } void dnssd_destroy(dnssd_t *dnssd) { if (dnssd) { #ifdef WIN32 FreeLibrary(dnssd->module); #elif USE_LIBDL dlclose(dnssd->module); #endif free(dnssd); } } int dnssd_register_raop(dnssd_t *dnssd, unsigned short port) { char servname[MAX_SERVNAME]; DNSServiceErrorType retval; char features[22]; assert(dnssd); snprintf(features, sizeof(features), "0x%X,0x%X", dnssd->features1, dnssd->features2); dnssd->TXTRecordCreate(&dnssd->raop_record, 0, NULL); dnssd->TXTRecordSetValue(&dnssd->raop_record, "ch", strlen(RAOP_CH), RAOP_CH); dnssd->TXTRecordSetValue(&dnssd->raop_record, "cn", strlen(RAOP_CN), RAOP_CN); dnssd->TXTRecordSetValue(&dnssd->raop_record, "da", strlen(RAOP_DA), RAOP_DA); dnssd->TXTRecordSetValue(&dnssd->raop_record, "et", strlen(RAOP_ET), RAOP_ET); dnssd->TXTRecordSetValue(&dnssd->raop_record, "vv", strlen(RAOP_VV), RAOP_VV); dnssd->TXTRecordSetValue(&dnssd->raop_record, "ft", strlen(features), features); dnssd->TXTRecordSetValue(&dnssd->raop_record, "am", strlen(GLOBAL_MODEL), GLOBAL_MODEL); dnssd->TXTRecordSetValue(&dnssd->raop_record, "md", strlen(RAOP_MD), RAOP_MD); dnssd->TXTRecordSetValue(&dnssd->raop_record, "rhd", strlen(RAOP_RHD), RAOP_RHD); if (dnssd->require_pw) { dnssd->TXTRecordSetValue(&dnssd->raop_record, "pw", strlen("true"), "true"); } else { dnssd->TXTRecordSetValue(&dnssd->raop_record, "pw", strlen("false"), "false"); } dnssd->TXTRecordSetValue(&dnssd->raop_record, "sr", strlen(RAOP_SR), RAOP_SR); dnssd->TXTRecordSetValue(&dnssd->raop_record, "ss", strlen(RAOP_SS), RAOP_SS); dnssd->TXTRecordSetValue(&dnssd->raop_record, "sv", strlen(RAOP_SV), RAOP_SV); dnssd->TXTRecordSetValue(&dnssd->raop_record, "tp", strlen(RAOP_TP), RAOP_TP); dnssd->TXTRecordSetValue(&dnssd->raop_record, "txtvers", strlen(RAOP_TXTVERS), RAOP_TXTVERS); dnssd->TXTRecordSetValue(&dnssd->raop_record, "sf", strlen(RAOP_SF), RAOP_SF); dnssd->TXTRecordSetValue(&dnssd->raop_record, "vs", strlen(RAOP_VS), RAOP_VS); dnssd->TXTRecordSetValue(&dnssd->raop_record, "vn", strlen(RAOP_VN), RAOP_VN); dnssd->TXTRecordSetValue(&dnssd->raop_record, "pk", strlen(dnssd->pk), dnssd->pk); /* Convert hardware address to string */ if (utils_hwaddr_raop(servname, sizeof(servname), dnssd->hw_addr, dnssd->hw_addr_len) < 0) { /* FIXME: handle better */ return -1; } /* Check that we have bytes for 'hw@name' format */ if (sizeof(servname) < strlen(servname) + 1 + dnssd->name_len + 1) { /* FIXME: handle better */ return -2; } strncat(servname, "@", sizeof(servname)-strlen(servname)-1); strncat(servname, dnssd->name, sizeof(servname)-strlen(servname)-1); /* Register the service */ retval = dnssd->DNSServiceRegister(&dnssd->raop_service, 0, 0, servname, "_raop._tcp", NULL, NULL, htons(port), dnssd->TXTRecordGetLength(&dnssd->raop_record), dnssd->TXTRecordGetBytesPtr(&dnssd->raop_record), NULL, NULL); return (int) retval; /* error codes are listed in Apple's dns_sd.h */ } int dnssd_register_airplay(dnssd_t *dnssd, unsigned short port) { char device_id[3 * MAX_HWADDR_LEN]; DNSServiceErrorType retval; char features[22]; assert(dnssd); snprintf(features, sizeof(features), "0x%X,0x%X", dnssd->features1, dnssd->features2); /* Convert hardware address to string */ if (utils_hwaddr_airplay(device_id, sizeof(device_id), dnssd->hw_addr, dnssd->hw_addr_len) < 0) { /* FIXME: handle better */ return -1; } dnssd->TXTRecordCreate(&dnssd->airplay_record, 0, NULL); dnssd->TXTRecordSetValue(&dnssd->airplay_record, "deviceid", strlen(device_id), device_id); dnssd->TXTRecordSetValue(&dnssd->airplay_record, "features", strlen(features), features); dnssd->TXTRecordSetValue(&dnssd->airplay_record, "flags", strlen(AIRPLAY_FLAGS), AIRPLAY_FLAGS); dnssd->TXTRecordSetValue(&dnssd->airplay_record, "model", strlen(GLOBAL_MODEL), GLOBAL_MODEL); dnssd->TXTRecordSetValue(&dnssd->airplay_record, "pk", strlen(dnssd->pk), dnssd->pk); if (dnssd->require_pw) { dnssd->TXTRecordSetValue(&dnssd->airplay_record, "pw", strlen("true"), "true"); } else { dnssd->TXTRecordSetValue(&dnssd->airplay_record, "pw", strlen("false"), "false"); } dnssd->TXTRecordSetValue(&dnssd->airplay_record, "pi", strlen(AIRPLAY_PI), AIRPLAY_PI); dnssd->TXTRecordSetValue(&dnssd->airplay_record, "srcvers", strlen(AIRPLAY_SRCVERS), AIRPLAY_SRCVERS); dnssd->TXTRecordSetValue(&dnssd->airplay_record, "vv", strlen(AIRPLAY_VV), AIRPLAY_VV); /* Register the service */ retval = dnssd->DNSServiceRegister(&dnssd->airplay_service, 0, 0, dnssd->name, "_airplay._tcp", NULL, NULL, htons(port), dnssd->TXTRecordGetLength(&dnssd->airplay_record), dnssd->TXTRecordGetBytesPtr(&dnssd->airplay_record), NULL, NULL); return (int) retval; /* error codes are listed in Apple's dns_sd.h */ } const char * dnssd_get_airplay_txt(dnssd_t *dnssd, int *length) { *length = dnssd->TXTRecordGetLength(&dnssd->airplay_record); return dnssd->TXTRecordGetBytesPtr(&dnssd->airplay_record); } const char * dnssd_get_name(dnssd_t *dnssd, int *length) { *length = dnssd->name_len; return dnssd->name; } const char * dnssd_get_hw_addr(dnssd_t *dnssd, int *length) { *length = dnssd->hw_addr_len; return dnssd->hw_addr; } void dnssd_unregister_raop(dnssd_t *dnssd) { assert(dnssd); if (!dnssd->raop_service) { return; } /* Deallocate TXT record */ dnssd->TXTRecordDeallocate(&dnssd->raop_record); dnssd->DNSServiceRefDeallocate(dnssd->raop_service); dnssd->raop_service = NULL; if (dnssd->airplay_service == NULL) { free(dnssd->name); free(dnssd->hw_addr); } } void dnssd_unregister_airplay(dnssd_t *dnssd) { assert(dnssd); if (!dnssd->airplay_service) { return; } /* Deallocate TXT record */ dnssd->TXTRecordDeallocate(&dnssd->airplay_record); dnssd->DNSServiceRefDeallocate(dnssd->airplay_service); dnssd->airplay_service = NULL; if (dnssd->raop_service == NULL) { free(dnssd->name); free(dnssd->hw_addr); } } uint64_t dnssd_get_airplay_features(dnssd_t *dnssd) { uint64_t features = ((uint64_t) dnssd->features2) << 32; features += (uint64_t) dnssd->features1; return features; } void dnssd_set_pk(dnssd_t *dnssd, char * pk_str) { dnssd->pk = pk_str; } void dnssd_set_airplay_features(dnssd_t *dnssd, int bit, int val) { uint32_t mask; uint32_t *features; if (bit < 0 || bit > 63) return; if (val < 0 || val > 1) return; if (bit >= 32) { mask = 0x1 << (bit - 32); features = &(dnssd->features2); } else { mask = 0x1 << bit; features = &(dnssd->features1); } if (val) { *features = *features | mask; } else { *features = *features & ~mask; } }