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