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