Export symbols when compiling windows DLL.
[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>
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
39typedef struct _DNSServiceRef_t *DNSServiceRef;
40typedef union _TXTRecordRef_t { char PrivateData[16]; char *ForceNaturalAlignment; } TXTRecordRef;
41
42typedef uint32_t DNSServiceFlags;
43typedef int32_t DNSServiceErrorType;
44
45typedef 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
57typedef 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 );
72typedef void (DNSSD_API *DNSServiceRefDeallocate_t)(DNSServiceRef sdRef);
73typedef void (DNSSD_API *TXTRecordCreate_t)
74 (
75 TXTRecordRef *txtRecord,
76 uint16_t bufferLen,
77 void *buffer
78 );
79typedef void (DNSSD_API *TXTRecordDeallocate_t)(TXTRecordRef *txtRecord);
80typedef DNSServiceErrorType (DNSSD_API *TXTRecordSetValue_t)
81 (
82 TXTRecordRef *txtRecord,
83 const char *key,
84 uint8_t valueSize,
85 const void *value
86 );
87typedef uint16_t (DNSSD_API *TXTRecordGetLength_t)(const TXTRecordRef *txtRecord);
88typedef const void * (DNSSD_API *TXTRecordGetBytesPtr_t)(const TXTRecordRef *txtRecord);
89
90
91struct 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
113dnssd_t *
114dnssd_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
169void
170dnssd_destroy(dnssd_t *dnssd)
171{
172 if (dnssd) {
173#ifdef WIN32
174 FreeLibrary(dnssd->module);
175#endif
176 free(dnssd);
177 }
178}
179
180int
181dnssd_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
236int
237dnssd_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
275void
276dnssd_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
288void
289dnssd_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}