Add libdl support to dnssd.c for lazy linking
[deb_shairplay.git] / src / lib / dnssd.c
CommitLineData
23e7e3ae
JVH
1/**
2 * Copyright (C) 2011-2012 Juho Vähä-Herttua
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 */
14
2340bcd3
JVH
15#include <stdlib.h>
16#include <string.h>
17#include <stdio.h>
18#include <assert.h>
19
20#include "dnssd.h"
21#include "dnssdint.h"
22#include "global.h"
23#include "compat.h"
24#include "utils.h"
25
26#define MAX_DEVICEID 18
27#define MAX_SERVNAME 256
28
6da21350
JVH
29#define USE_LIBDL (defined(HAVE_LIBDL) && !defined(__APPLE__))
30
31#if defined(WIN32) || USE_LIBDL
32# ifdef WIN32
33# include <stdint.h>
34# if !defined(EFI32) && !defined(EFI64)
35# define DNSSD_STDCALL __stdcall
36# else
37# define DNSSD_STDCALL
38# endif
2340bcd3 39# else
6da21350 40# include <dlfcn.h>
427cd9fb 41# define DNSSD_STDCALL
2340bcd3
JVH
42# endif
43
44typedef struct _DNSServiceRef_t *DNSServiceRef;
45typedef union _TXTRecordRef_t { char PrivateData[16]; char *ForceNaturalAlignment; } TXTRecordRef;
46
47typedef uint32_t DNSServiceFlags;
48typedef int32_t DNSServiceErrorType;
49
427cd9fb 50typedef void (DNSSD_STDCALL *DNSServiceRegisterReply)
2340bcd3
JVH
51 (
52 DNSServiceRef sdRef,
53 DNSServiceFlags flags,
54 DNSServiceErrorType errorCode,
55 const char *name,
56 const char *regtype,
57 const char *domain,
58 void *context
59 );
6da21350
JVH
60
61#else
62# include <dns_sd.h>
63# define DNSSD_STDCALL
2340bcd3
JVH
64#endif
65
427cd9fb 66typedef DNSServiceErrorType (DNSSD_STDCALL *DNSServiceRegister_t)
2340bcd3
JVH
67 (
68 DNSServiceRef *sdRef,
69 DNSServiceFlags flags,
70 uint32_t interfaceIndex,
71 const char *name,
72 const char *regtype,
73 const char *domain,
74 const char *host,
75 uint16_t port,
76 uint16_t txtLen,
77 const void *txtRecord,
78 DNSServiceRegisterReply callBack,
79 void *context
80 );
427cd9fb
JVH
81typedef void (DNSSD_STDCALL *DNSServiceRefDeallocate_t)(DNSServiceRef sdRef);
82typedef void (DNSSD_STDCALL *TXTRecordCreate_t)
2340bcd3
JVH
83 (
84 TXTRecordRef *txtRecord,
85 uint16_t bufferLen,
86 void *buffer
87 );
427cd9fb
JVH
88typedef void (DNSSD_STDCALL *TXTRecordDeallocate_t)(TXTRecordRef *txtRecord);
89typedef DNSServiceErrorType (DNSSD_STDCALL *TXTRecordSetValue_t)
2340bcd3
JVH
90 (
91 TXTRecordRef *txtRecord,
92 const char *key,
93 uint8_t valueSize,
94 const void *value
95 );
427cd9fb
JVH
96typedef uint16_t (DNSSD_STDCALL *TXTRecordGetLength_t)(const TXTRecordRef *txtRecord);
97typedef const void * (DNSSD_STDCALL *TXTRecordGetBytesPtr_t)(const TXTRecordRef *txtRecord);
2340bcd3
JVH
98
99
100struct dnssd_s {
101#ifdef WIN32
102 HMODULE module;
6da21350
JVH
103#elif USE_LIBDL
104 void *module;
2340bcd3
JVH
105#endif
106
107 DNSServiceRegister_t DNSServiceRegister;
108 DNSServiceRefDeallocate_t DNSServiceRefDeallocate;
109 TXTRecordCreate_t TXTRecordCreate;
110 TXTRecordSetValue_t TXTRecordSetValue;
111 TXTRecordGetLength_t TXTRecordGetLength;
112 TXTRecordGetBytesPtr_t TXTRecordGetBytesPtr;
113 TXTRecordDeallocate_t TXTRecordDeallocate;
114
2340bcd3
JVH
115 DNSServiceRef raopService;
116 DNSServiceRef airplayService;
117};
118
119
120
121dnssd_t *
067f00ef 122dnssd_init(int *error)
2340bcd3
JVH
123{
124 dnssd_t *dnssd;
125
126 if (error) *error = DNSSD_ERROR_NOERROR;
2340bcd3
JVH
127
128 dnssd = calloc(1, sizeof(dnssd_t));
129 if (!dnssd) {
130 if (error) *error = DNSSD_ERROR_OUTOFMEM;
131 return NULL;
132 }
133
134#ifdef WIN32
135 dnssd->module = LoadLibraryA("dnssd.dll");
136 if (!dnssd->module) {
137 if (error) *error = DNSSD_ERROR_LIBNOTFOUND;
138 free(dnssd);
139 return NULL;
140 }
141 dnssd->DNSServiceRegister = (DNSServiceRegister_t)GetProcAddress(dnssd->module, "DNSServiceRegister");
142 dnssd->DNSServiceRefDeallocate = (DNSServiceRefDeallocate_t)GetProcAddress(dnssd->module, "DNSServiceRefDeallocate");
143 dnssd->TXTRecordCreate = (TXTRecordCreate_t)GetProcAddress(dnssd->module, "TXTRecordCreate");
144 dnssd->TXTRecordSetValue = (TXTRecordSetValue_t)GetProcAddress(dnssd->module, "TXTRecordSetValue");
145 dnssd->TXTRecordGetLength = (TXTRecordGetLength_t)GetProcAddress(dnssd->module, "TXTRecordGetLength");
146 dnssd->TXTRecordGetBytesPtr = (TXTRecordGetBytesPtr_t)GetProcAddress(dnssd->module, "TXTRecordGetBytesPtr");
147 dnssd->TXTRecordDeallocate = (TXTRecordDeallocate_t)GetProcAddress(dnssd->module, "TXTRecordDeallocate");
148
149 if (!dnssd->DNSServiceRegister || !dnssd->DNSServiceRefDeallocate || !dnssd->TXTRecordCreate ||
150 !dnssd->TXTRecordSetValue || !dnssd->TXTRecordGetLength || !dnssd->TXTRecordGetBytesPtr ||
151 !dnssd->TXTRecordDeallocate) {
152 if (error) *error = DNSSD_ERROR_PROCNOTFOUND;
153 FreeLibrary(dnssd->module);
154 free(dnssd);
155 return NULL;
156 }
6da21350
JVH
157#elif USE_LIBDL
158 dnssd->module = dlopen("libdns_sd.so", RTLD_LAZY);
159 if (!dnssd->module) {
160 if (error) *error = DNSSD_ERROR_LIBNOTFOUND;
161 free(dnssd);
162 return NULL;
163 }
164 dnssd->DNSServiceRegister = (DNSServiceRegister_t)dlsym(dnssd->module, "DNSServiceRegister");
165 dnssd->DNSServiceRefDeallocate = (DNSServiceRefDeallocate_t)dlsym(dnssd->module, "DNSServiceRefDeallocate");
166 dnssd->TXTRecordCreate = (TXTRecordCreate_t)dlsym(dnssd->module, "TXTRecordCreate");
167 dnssd->TXTRecordSetValue = (TXTRecordSetValue_t)dlsym(dnssd->module, "TXTRecordSetValue");
168 dnssd->TXTRecordGetLength = (TXTRecordGetLength_t)dlsym(dnssd->module, "TXTRecordGetLength");
169 dnssd->TXTRecordGetBytesPtr = (TXTRecordGetBytesPtr_t)dlsym(dnssd->module, "TXTRecordGetBytesPtr");
170 dnssd->TXTRecordDeallocate = (TXTRecordDeallocate_t)dlsym(dnssd->module, "TXTRecordDeallocate");
171
172 if (!dnssd->DNSServiceRegister || !dnssd->DNSServiceRefDeallocate || !dnssd->TXTRecordCreate ||
173 !dnssd->TXTRecordSetValue || !dnssd->TXTRecordGetLength || !dnssd->TXTRecordGetBytesPtr ||
174 !dnssd->TXTRecordDeallocate) {
175 if (error) *error = DNSSD_ERROR_PROCNOTFOUND;
176 dlclose(dnssd->module);
177 free(dnssd);
178 return NULL;
179 }
2340bcd3
JVH
180#else
181 dnssd->DNSServiceRegister = &DNSServiceRegister;
182 dnssd->DNSServiceRefDeallocate = &DNSServiceRefDeallocate;
183 dnssd->TXTRecordCreate = &TXTRecordCreate;
184 dnssd->TXTRecordSetValue = &TXTRecordSetValue;
185 dnssd->TXTRecordGetLength = &TXTRecordGetLength;
186 dnssd->TXTRecordGetBytesPtr = &TXTRecordGetBytesPtr;
187 dnssd->TXTRecordDeallocate = &TXTRecordDeallocate;
188#endif
189
2340bcd3
JVH
190 return dnssd;
191}
192
193void
194dnssd_destroy(dnssd_t *dnssd)
195{
196 if (dnssd) {
197#ifdef WIN32
198 FreeLibrary(dnssd->module);
6da21350
JVH
199#elif USE_LIBDL
200 dlclose(dnssd->module);
2340bcd3
JVH
201#endif
202 free(dnssd);
203 }
204}
205
206int
e4169f77 207dnssd_register_raop(dnssd_t *dnssd, const char *name, unsigned short port, const char *hwaddr, int hwaddrlen, int password)
2340bcd3
JVH
208{
209 TXTRecordRef txtRecord;
210 char servname[MAX_SERVNAME];
211 int ret;
212
213 assert(dnssd);
067f00ef
JVH
214 assert(name);
215 assert(hwaddr);
2340bcd3
JVH
216
217 dnssd->TXTRecordCreate(&txtRecord, 0, NULL);
218 dnssd->TXTRecordSetValue(&txtRecord, "txtvers", strlen(RAOP_TXTVERS), RAOP_TXTVERS);
219 dnssd->TXTRecordSetValue(&txtRecord, "ch", strlen(RAOP_CH), RAOP_CH);
220 dnssd->TXTRecordSetValue(&txtRecord, "cn", strlen(RAOP_CN), RAOP_CN);
221 dnssd->TXTRecordSetValue(&txtRecord, "et", strlen(RAOP_ET), RAOP_ET);
222 dnssd->TXTRecordSetValue(&txtRecord, "sv", strlen(RAOP_SV), RAOP_SV);
223 dnssd->TXTRecordSetValue(&txtRecord, "da", strlen(RAOP_DA), RAOP_DA);
224 dnssd->TXTRecordSetValue(&txtRecord, "sr", strlen(RAOP_SR), RAOP_SR);
225 dnssd->TXTRecordSetValue(&txtRecord, "ss", strlen(RAOP_SS), RAOP_SS);
e4169f77
JVH
226 if (password) {
227 dnssd->TXTRecordSetValue(&txtRecord, "pw", strlen("true"), "true");
228 } else {
229 dnssd->TXTRecordSetValue(&txtRecord, "pw", strlen("false"), "false");
230 }
2340bcd3
JVH
231 dnssd->TXTRecordSetValue(&txtRecord, "vn", strlen(RAOP_VN), RAOP_VN);
232 dnssd->TXTRecordSetValue(&txtRecord, "tp", strlen(RAOP_TP), RAOP_TP);
233 dnssd->TXTRecordSetValue(&txtRecord, "md", strlen(RAOP_MD), RAOP_MD);
234 dnssd->TXTRecordSetValue(&txtRecord, "vs", strlen(GLOBAL_VERSION), GLOBAL_VERSION);
e66980f2
JVH
235 dnssd->TXTRecordSetValue(&txtRecord, "sm", strlen(RAOP_SM), RAOP_SM);
236 dnssd->TXTRecordSetValue(&txtRecord, "ek", strlen(RAOP_EK), RAOP_EK);
2340bcd3
JVH
237
238 /* Convert hardware address to string */
067f00ef 239 ret = utils_hwaddr_raop(servname, sizeof(servname), hwaddr, hwaddrlen);
2340bcd3
JVH
240 if (ret < 0) {
241 /* FIXME: handle better */
242 return -1;
243 }
244
245 /* Check that we have bytes for 'hw@name' format */
246 if (sizeof(servname) < strlen(servname)+1+strlen(name)+1) {
247 /* FIXME: handle better */
248 return -2;
249 }
250
251 strncat(servname, "@", sizeof(servname)-strlen(servname)-1);
252 strncat(servname, name, sizeof(servname)-strlen(servname)-1);
253
254 /* Register the service */
255 dnssd->DNSServiceRegister(&dnssd->raopService, 0, 0,
256 servname, "_raop._tcp",
257 NULL, NULL,
258 htons(port),
259 dnssd->TXTRecordGetLength(&txtRecord),
260 dnssd->TXTRecordGetBytesPtr(&txtRecord),
261 NULL, NULL);
262
263 /* Deallocate TXT record */
264 dnssd->TXTRecordDeallocate(&txtRecord);
265 return 1;
266}
267
268int
067f00ef 269dnssd_register_airplay(dnssd_t *dnssd, const char *name, unsigned short port, const char *hwaddr, int hwaddrlen)
2340bcd3
JVH
270{
271 TXTRecordRef txtRecord;
272 char deviceid[3*MAX_HWADDR_LEN];
273 char features[16];
274 int ret;
275
276 assert(dnssd);
067f00ef
JVH
277 assert(name);
278 assert(hwaddr);
2340bcd3
JVH
279
280 /* Convert hardware address to string */
067f00ef 281 ret = utils_hwaddr_airplay(deviceid, sizeof(deviceid), hwaddr, hwaddrlen);
2340bcd3
JVH
282 if (ret < 0) {
283 /* FIXME: handle better */
284 return -1;
285 }
286
287 features[sizeof(features)-1] = '\0';
288 snprintf(features, sizeof(features)-1, "0x%x", GLOBAL_FEATURES);
289
290 dnssd->TXTRecordCreate(&txtRecord, 0, NULL);
291 dnssd->TXTRecordSetValue(&txtRecord, "deviceid", strlen(deviceid), deviceid);
292 dnssd->TXTRecordSetValue(&txtRecord, "features", strlen(features), features);
293 dnssd->TXTRecordSetValue(&txtRecord, "model", strlen(GLOBAL_MODEL), GLOBAL_MODEL);
294
295 /* Register the service */
296 dnssd->DNSServiceRegister(&dnssd->airplayService, 0, 0,
297 name, "_airplay._tcp",
298 NULL, NULL,
299 htons(port),
300 dnssd->TXTRecordGetLength(&txtRecord),
301 dnssd->TXTRecordGetBytesPtr(&txtRecord),
302 NULL, NULL);
303
304 /* Deallocate TXT record */
305 dnssd->TXTRecordDeallocate(&txtRecord);
306 return 0;
307}
308
309void
310dnssd_unregister_raop(dnssd_t *dnssd)
311{
312 assert(dnssd);
313
314 if (!dnssd->raopService) {
315 return;
316 }
317
318 dnssd->DNSServiceRefDeallocate(dnssd->raopService);
319 dnssd->raopService = NULL;
320}
321
322void
323dnssd_unregister_airplay(dnssd_t *dnssd)
324{
325 assert(dnssd);
326
327 if (!dnssd->airplayService) {
328 return;
329 }
330
331 dnssd->DNSServiceRefDeallocate(dnssd->airplayService);
332 dnssd->airplayService = NULL;
333}