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/dsi.h>
22 #include <atalk/unicode.h>
23 #include <atalk/netatalk_conf.h>
25 #include "afp_zeroconf.h"
29 * We'll store all the DNSServiceRef's here so that we can
30 * deallocate them later
32 static DNSServiceRef *svc_refs = NULL;
33 static int svc_ref_count = 0;
34 static pthread_t poller;
37 * Its easier to use asprintf to set the TXT record values
39 #define TXTRecordPrintf(rec, key, args...) { \
41 asprintf(&str, args); \
42 TXTRecordSetValue(rec, key, strlen(str), str); \
45 #define TXTRecordKeyPrintf(rec, k, var, args...) { \
47 asprintf(&key, k, var); \
48 asprintf(&str, args); \
49 TXTRecordSetValue(rec, key, strlen(str), str); \
50 free(str); free(key); \
55 * This is the thread that polls the filehandles
57 void *polling_thread(void *arg) {
58 // First we loop through getting the filehandles and adding them to our poll, we
59 // need to allocate our pollfd's
60 DNSServiceErrorType error;
61 struct pollfd *fds = calloc(svc_ref_count, sizeof(struct pollfd));
64 for(int i=0; i < svc_ref_count; i++) {
65 int fd = DNSServiceRefSockFD(svc_refs[i]);
67 fds[i].events = POLLIN;
70 // Now we can poll and process the results...
71 while(poll(fds, svc_ref_count, -1) > 0) {
72 for(int i=0; i < svc_ref_count; i++) {
73 if(fds[i].revents & POLLIN) {
74 error = DNSServiceProcessResult(svc_refs[i]);
83 * This is the callback for the service register function ... actually there isn't a lot
84 * we can do if we get problems, so we don't really need to do anything other than report
87 void RegisterReply(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode,
88 const char *name, const char *regtype, const char *domain, void *context) {
90 if(errorCode != kDNSServiceErr_NoError) {
91 LOG(log_error, logtype_afpd, "Failed to register mDNS service: %s%s%s: code=%d",
92 name, regtype, domain, errorCode);
98 * This function unregisters anything we have already
99 * registered and frees associated memory
101 static void unregister_stuff() {
102 pthread_kill(poller, SIGKILL);
104 for(int i=0; i < svc_ref_count; i++) {
105 DNSServiceRefDeallocate(svc_refs[i]);
114 * This function tries to register the AFP DNS
117 static void register_stuff(const AFPObj *obj) {
119 const struct vol *volume;
121 char name[MAXINSTANCENAMELEN+1];
122 DNSServiceErrorType error;
123 TXTRecordRef txt_adisk;
124 TXTRecordRef txt_devinfo;
127 // If we had already registered, then we will unregister and re-register
128 if(svc_refs) unregister_stuff();
130 /* Register our service, prepare the TXT record */
131 TXTRecordCreate(&txt_adisk, 0, NULL);
132 TXTRecordPrintf(&txt_adisk, "sys", "waMa=0,adVF=0x100");
134 /* Build AFP volumes list */
137 for (volume = getvolumes(); volume; volume = volume->v_next) {
139 if (convert_string(CH_UCS2, CH_UTF8_MAC, volume->v_name, -1, tmpname, 255) <= 0) {
140 LOG ( log_error, logtype_afpd, "Could not set Zeroconf volume name for TimeMachine");
144 if (volume->v_flags & AFPVOL_TM) {
145 if (volume->v_uuid) {
146 LOG(log_info, logtype_afpd, "Registering volume '%s' with UUID: '%s' for TimeMachine",
147 volume->v_localname, volume->v_uuid);
148 TXTRecordKeyPrintf(&txt_adisk, "dk%u", i++, "adVN=%s,adVF=0xa1,adVU=%s",
149 tmpname, volume->v_uuid);
151 LOG(log_warning, logtype_afpd, "Registering volume '%s' for TimeMachine. But UUID is invalid.",
152 volume->v_localname);
153 TXTRecordKeyPrintf(&txt_adisk, "dk%u", i++, "adVN=%s,adVF=0xa1", tmpname);
158 // Now we can count the configs so we know how many service
159 // records to allocate
160 for (dsi = obj->dsi; dsi; dsi = dsi->next) {
161 svc_ref_count++; // AFP_DNS_SERVICE_TYPE
162 if (i) svc_ref_count++; // ADISK_SERVICE_TYPE
163 if (obj->options.mimicmodel) svc_ref_count++; // DEV_INFO_SERVICE_TYPE
166 // Allocate the memory to store our service refs
167 svc_refs = calloc(svc_ref_count, sizeof(DNSServiceRef));
172 for (dsi = obj->dsi; dsi; dsi = dsi->next) {
174 port = getip_port((struct sockaddr *)&dsi->server);
176 if (convert_string(obj->options.unixcharset,
178 obj->options.hostname,
181 MAXINSTANCENAMELEN) <= 0) {
182 LOG(log_error, logtype_afpd, "Could not set Zeroconf instance name");
185 if ((dsi->bonjourname = strdup(name)) == NULL) {
186 LOG(log_error, logtype_afpd, "Could not set Zeroconf instance name");
190 LOG(log_info, logtype_afpd, "Registering server '%s' with Bonjour",
193 error = DNSServiceRegister(&svc_refs[svc_ref_count++],
195 0, // all network interfaces
197 AFP_DNS_SERVICE_TYPE,
198 "", // default domains
199 NULL, // default host name
203 RegisterReply, // callback
205 if(error != kDNSServiceErr_NoError) {
206 LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
207 AFP_DNS_SERVICE_TYPE, error);
212 error = DNSServiceRegister(&svc_refs[svc_ref_count++],
214 0, // all network interfaces
217 "", // default domains
218 NULL, // default host name
220 TXTRecordGetLength(&txt_adisk),
221 TXTRecordGetBytesPtr(&txt_adisk),
222 RegisterReply, // callback
224 if(error != kDNSServiceErr_NoError) {
225 LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
226 ADISK_SERVICE_TYPE, error);
231 if (obj->options.mimicmodel) {
232 TXTRecordCreate(&txt_devinfo, 0, NULL);
233 TXTRecordPrintf(&txt_devinfo, "model", obj->options.mimicmodel);
234 error = DNSServiceRegister(&svc_refs[svc_ref_count++],
236 0, // all network interfaces
238 DEV_INFO_SERVICE_TYPE,
239 "", // default domains
240 NULL, // default host name
242 TXTRecordGetLength(&txt_devinfo),
243 TXTRecordGetBytesPtr(&txt_devinfo),
244 RegisterReply, // callback
246 TXTRecordDeallocate(&txt_devinfo);
247 if(error != kDNSServiceErr_NoError) {
248 LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
249 DEV_INFO_SERVICE_TYPE, error);
252 } /* if (config->obj.options.mimicmodel) */
256 * Now we can create the thread that will poll for the results
257 * and handle the calling of the callbacks
259 if(pthread_create(&poller, NULL, polling_thread, NULL) != 0) {
260 LOG(log_error, logtype_afpd, "Unable to start mDNS polling thread");
265 TXTRecordDeallocate(&txt_adisk);
269 /************************************************************************
271 ************************************************************************/
274 * Tries to setup the Zeroconf thread and any
275 * neccessary config setting.
277 void md_zeroconf_register(const AFPObj *obj) {
285 * Tries to shutdown this loop impl.
286 * Call this function from inside this thread.
288 int md_zeroconf_unregister() {
293 #endif /* USE_MDNS */