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