2 * Author: Lee Essen <lee.essen@nowonline.co.uk>
3 * Based on: avahi support from Daniel S. Haischt <me@daniel.stefan.haischt.name>
4 * Purpose: mdns based Zeroconf support
19 #include <atalk/logger.h>
20 #include <atalk/util.h>
21 #include <atalk/unicode.h>
22 #include <atalk/netatalk_conf.h>
24 #include "afp_zeroconf.h"
28 * We'll store all the DNSServiceRef's here so that we can
29 * deallocate them later
31 static DNSServiceRef *svc_refs = NULL;
32 static int svc_ref_count = 0;
33 static pthread_t poller;
36 * Its easier to use asprintf to set the TXT record values
39 int TXTRecordPrintf(TXTRecordRef * rec, const char * key, const char * fmt, ... )
46 if( 0 > vasprintf(&str, fmt, ap ) ) {
52 if( kDNSServiceErr_NoError != TXTRecordSetValue(rec, key, strlen(str), str) ) {
60 int TXTRecordKeyPrintf(TXTRecordRef * rec, const char * key_fmt, int key_var, const char * fmt, ...)
63 char *key = NULL, *str = NULL;
66 if( 0 > asprintf(&key, key_fmt, key_var))
70 if( 0 > vasprintf(&str, fmt, ap )) {
77 if( kDNSServiceErr_NoError != TXTRecordSetValue(rec, key, strlen(str), str) ) {
90 static struct pollfd *fds;
93 * This is the thread that polls the filehandles
95 static void *polling_thread(void *arg) {
96 // First we loop through getting the filehandles and adding them to our poll, we
97 // need to allocate our pollfd's
98 DNSServiceErrorType error;
99 fds = calloc(svc_ref_count, sizeof(struct pollfd));
102 for(int i=0; i < svc_ref_count; i++) {
103 int fd = DNSServiceRefSockFD(svc_refs[i]);
105 fds[i].events = POLLIN;
108 // Now we can poll and process the results...
109 while(poll(fds, svc_ref_count, -1) > 0) {
110 for(int i=0; i < svc_ref_count; i++) {
111 if(fds[i].revents & POLLIN) {
112 error = DNSServiceProcessResult(svc_refs[i]);
120 * This is the callback for the service register function ... actually there isn't a lot
121 * we can do if we get problems, so we don't really need to do anything other than report
124 static void RegisterReply(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode,
125 const char *name, const char *regtype, const char *domain, void *context)
127 if (errorCode != kDNSServiceErr_NoError) {
128 LOG(log_error, logtype_afpd, "Failed to register mDNS service: %s%s%s: code=%d",
129 name, regtype, domain, errorCode);
134 * This function unregisters anything we have already
135 * registered and frees associated memory
137 static void unregister_stuff() {
138 pthread_cancel(poller);
140 for (int i = 0; i < svc_ref_count; i++)
146 for(int i=0; i < svc_ref_count; i++) {
147 DNSServiceRefDeallocate(svc_refs[i]);
156 * This function tries to register the AFP DNS
159 static void register_stuff(const AFPObj *obj) {
161 const struct vol *volume;
162 char name[MAXINSTANCENAMELEN+1];
163 DNSServiceErrorType error;
164 TXTRecordRef txt_adisk;
165 TXTRecordRef txt_devinfo;
168 // If we had already registered, then we will unregister and re-register
169 if(svc_refs) unregister_stuff();
171 /* Register our service, prepare the TXT record */
172 TXTRecordCreate(&txt_adisk, 0, NULL);
173 if( 0 > TXTRecordPrintf(&txt_adisk, "sys", "waMa=0,adVF=0x100") ) {
174 LOG ( log_error, logtype_afpd, "Could not create Zeroconf TXTRecord for sys");
178 /* Build AFP volumes list */
181 for (volume = getvolumes(); volume; volume = volume->v_next) {
183 if (convert_string(CH_UCS2, CH_UTF8_MAC, volume->v_u8mname, -1, tmpname, 255) <= 0) {
184 LOG ( log_error, logtype_afpd, "Could not set Zeroconf volume name for TimeMachine");
188 if (volume->v_flags & AFPVOL_TM) {
189 if (volume->v_uuid) {
190 LOG(log_info, logtype_afpd, "Registering volume '%s' with UUID: '%s' for TimeMachine",
191 volume->v_localname, volume->v_uuid);
192 if( 0 > TXTRecordKeyPrintf(&txt_adisk, "dk%u", i++, "adVN=%s,adVF=0xa1,adVU=%s",
193 tmpname, volume->v_uuid) ) {
194 LOG ( log_error, logtype_afpd, "Could not set Zeroconf TXTRecord for dk%u", i);
198 LOG(log_warning, logtype_afpd, "Registering volume '%s' for TimeMachine. But UUID is invalid.",
199 volume->v_localname);
200 if( 0 > TXTRecordKeyPrintf(&txt_adisk, "dk%u", i++, "adVN=%s,adVF=0xa1", tmpname) ) {
201 LOG ( log_error, logtype_afpd, "Could not set Zeroconf TXTRecord for dk%u", i);
208 /* AFP_DNS_SERVICE_TYPE */
211 /* ADISK_SERVICE_TYPE */
214 if (obj->options.mimicmodel) {
215 /* DEV_INFO_SERVICE_TYPE */
219 // Allocate the memory to store our service refs
220 svc_refs = calloc(svc_ref_count, sizeof(DNSServiceRef));
224 port = atoi(obj->options.port);
226 if (convert_string(obj->options.unixcharset,
228 obj->options.hostname,
231 MAXINSTANCENAMELEN) <= 0) {
232 LOG(log_error, logtype_afpd, "Could not set Zeroconf instance name");
236 error = DNSServiceRegister(&svc_refs[svc_ref_count++],
238 0, // all network interfaces
240 AFP_DNS_SERVICE_TYPE,
241 "", // default domains
242 NULL, // default host name
246 RegisterReply, // callback
248 if (error != kDNSServiceErr_NoError) {
249 LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
250 AFP_DNS_SERVICE_TYPE, error);
255 error = DNSServiceRegister(&svc_refs[svc_ref_count++],
257 0, // all network interfaces
260 "", // default domains
261 NULL, // default host name
263 TXTRecordGetLength(&txt_adisk),
264 TXTRecordGetBytesPtr(&txt_adisk),
265 RegisterReply, // callback
267 if (error != kDNSServiceErr_NoError) {
268 LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
269 ADISK_SERVICE_TYPE, error);
274 if (obj->options.mimicmodel) {
275 LOG(log_info, logtype_afpd, "Registering server as model '%s'",
276 obj->options.mimicmodel);
277 TXTRecordCreate(&txt_devinfo, 0, NULL);
278 if ( 0 > TXTRecordPrintf(&txt_devinfo, "model", obj->options.mimicmodel) ) {
279 LOG ( log_error, logtype_afpd, "Could not create Zeroconf TXTRecord for model");
283 error = DNSServiceRegister(&svc_refs[svc_ref_count++],
285 0, // all network interfaces
287 DEV_INFO_SERVICE_TYPE,
288 "", // default domains
289 NULL, // default host name
291 * We would probably use port 0 zero, but we can't, from man DNSServiceRegister:
292 * "A value of 0 for a port is passed to register placeholder services.
293 * Place holder services are not found when browsing, but other
294 * clients cannot register with the same name as the placeholder service."
295 * We therefor use port 9 which is used by the adisk service type.
298 TXTRecordGetLength(&txt_devinfo),
299 TXTRecordGetBytesPtr(&txt_devinfo),
300 RegisterReply, // callback
302 TXTRecordDeallocate(&txt_devinfo);
303 if (error != kDNSServiceErr_NoError) {
304 LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
305 DEV_INFO_SERVICE_TYPE, error);
308 } /* if (config->obj.options.mimicmodel) */
311 * Now we can create the thread that will poll for the results
312 * and handle the calling of the callbacks
314 if(pthread_create(&poller, NULL, polling_thread, NULL) != 0) {
315 LOG(log_error, logtype_afpd, "Unable to start mDNS polling thread");
320 TXTRecordDeallocate(&txt_adisk);
324 /************************************************************************
326 ************************************************************************/
329 * Tries to setup the Zeroconf thread and any
330 * neccessary config setting.
332 void md_zeroconf_register(const AFPObj *obj) {
340 * Tries to shutdown this loop impl.
341 * Call this function from inside this thread.
343 int md_zeroconf_unregister() {
348 #endif /* USE_MDNS */