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