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