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