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