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>
24 #include "afp_zeroconf.h"
26 #include "afp_config.h"
30 * We'll store all the DNSServiceRef's here so that we can
31 * deallocate them later
33 static DNSServiceRef *svc_refs = NULL;
34 static int svc_ref_count = 0;
35 static pthread_t poller;
38 * Its easier to use asprintf to set the TXT record values
40 #define TXTRecordPrintf(rec, key, args...) { \
42 asprintf(&str, args); \
43 TXTRecordSetValue(rec, key, strlen(str), str); \
46 #define TXTRecordKeyPrintf(rec, k, var, args...) { \
48 asprintf(&key, k, var); \
49 asprintf(&str, args); \
50 TXTRecordSetValue(rec, key, strlen(str), str); \
51 free(str); free(key); \
56 * This is the thread that polls the filehandles
58 void *polling_thread(void *arg) {
59 // First we loop through getting the filehandles and adding them to our poll, we
60 // need to allocate our pollfd's
61 DNSServiceErrorType error;
62 struct pollfd *fds = calloc(svc_ref_count, sizeof(struct pollfd));
65 for(int i=0; i < svc_ref_count; i++) {
66 int fd = DNSServiceRefSockFD(svc_refs[i]);
68 fds[i].events = POLLIN;
71 // Now we can poll and process the results...
72 while(poll(fds, svc_ref_count, -1) > 0) {
73 for(int i=0; i < svc_ref_count; i++) {
74 if(fds[i].revents & POLLIN) {
75 error = DNSServiceProcessResult(svc_refs[i]);
84 * This is the callback for the service register function ... actually there isn't a lot
85 * we can do if we get problems, so we don't really need to do anything other than report
88 void RegisterReply(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode,
89 const char *name, const char *regtype, const char *domain, void *context) {
91 if(errorCode != kDNSServiceErr_NoError) {
92 LOG(log_error, logtype_afpd, "Failed to register mDNS service: %s%s%s: code=%d",
93 name, regtype, domain, errorCode);
99 * This function unregisters anything we have already
100 * registered and frees associated memory
102 static void unregister_stuff() {
103 pthread_kill(poller, SIGKILL);
105 for(int i=0; i < svc_ref_count; i++) {
106 DNSServiceRefDeallocate(svc_refs[i]);
115 * This function tries to register the AFP DNS
118 static void register_stuff(const AFPConfig *configs) {
120 const AFPConfig *config;
121 const struct vol *volume;
123 char name[MAXINSTANCENAMELEN+1];
124 DNSServiceErrorType error;
125 TXTRecordRef txt_adisk;
126 TXTRecordRef txt_devinfo;
129 // If we had already registered, then we will unregister and re-register
130 if(svc_refs) unregister_stuff();
132 /* Register our service, prepare the TXT record */
133 TXTRecordCreate(&txt_adisk, 0, NULL);
134 TXTRecordPrintf(&txt_adisk, "sys", "waMa=0,adVF=0x100");
136 /* Build AFP volumes list */
139 for (volume = getvolumes(); volume; volume = volume->v_next) {
141 if (convert_string(CH_UCS2, CH_UTF8_MAC, volume->v_u8mname, -1, tmpname, 255) <= 0) {
142 LOG ( log_error, logtype_afpd, "Could not set Zeroconf volume name for TimeMachine");
146 if (volume->v_flags & AFPVOL_TM) {
147 if (volume->v_uuid) {
148 LOG(log_info, logtype_afpd, "Registering volume '%s' with UUID: '%s' for TimeMachine",
149 volume->v_localname, volume->v_uuid);
150 TXTRecordKeyPrintf(&txt_adisk, "dk%u", i++, "adVN=%s,adVF=0xa1,adVU=%s",
151 tmpname, volume->v_uuid);
153 LOG(log_warning, logtype_afpd, "Registering volume '%s' for TimeMachine. But UUID is invalid.",
154 volume->v_localname);
155 TXTRecordKeyPrintf(&txt_adisk, "dk%u", i++, "adVN=%s,adVF=0xa1", tmpname);
160 // Now we can count the configs so we know how many service
161 // records to allocate
162 for (config = configs; config; config = config->next) {
163 svc_ref_count++; // AFP_DNS_SERVICE_TYPE
164 if(i) svc_ref_count++; // ADISK_SERVICE_TYPE
165 if (config->obj.options.mimicmodel) svc_ref_count++; // DEV_INFO_SERVICE_TYPE
168 // Allocate the memory to store our service refs
169 svc_refs = calloc(svc_ref_count, sizeof(DNSServiceRef));
174 for (config = configs; config; config = config->next) {
176 dsi = (DSI *)config->obj.handle;
177 port = getip_port((struct sockaddr *)&dsi->server);
179 if (convert_string(config->obj.options.unixcharset,
181 config->obj.options.server ?
182 config->obj.options.server :
183 config->obj.options.hostname,
186 MAXINSTANCENAMELEN) <= 0) {
187 LOG(log_error, logtype_afpd, "Could not set Zeroconf instance name");
190 if ((dsi->bonjourname = strdup(name)) == NULL) {
191 LOG(log_error, logtype_afpd, "Could not set Zeroconf instance name");
195 LOG(log_info, logtype_afpd, "Registering server '%s' with Bonjour",
198 error = DNSServiceRegister(&svc_refs[svc_ref_count++],
200 0, // all network interfaces
202 AFP_DNS_SERVICE_TYPE,
203 "", // default domains
204 NULL, // default host name
208 RegisterReply, // callback
210 if(error != kDNSServiceErr_NoError) {
211 LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
212 AFP_DNS_SERVICE_TYPE, error);
217 error = DNSServiceRegister(&svc_refs[svc_ref_count++],
219 0, // all network interfaces
222 "", // default domains
223 NULL, // default host name
225 TXTRecordGetLength(&txt_adisk),
226 TXTRecordGetBytesPtr(&txt_adisk),
227 RegisterReply, // callback
229 if(error != kDNSServiceErr_NoError) {
230 LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
231 ADISK_SERVICE_TYPE, error);
236 if (config->obj.options.mimicmodel) {
237 TXTRecordCreate(&txt_devinfo, 0, NULL);
238 TXTRecordPrintf(&txt_devinfo, "model", config->obj.options.mimicmodel);
239 error = DNSServiceRegister(&svc_refs[svc_ref_count++],
241 0, // all network interfaces
243 DEV_INFO_SERVICE_TYPE,
244 "", // default domains
245 NULL, // default host name
247 TXTRecordGetLength(&txt_devinfo),
248 TXTRecordGetBytesPtr(&txt_devinfo),
249 RegisterReply, // callback
251 TXTRecordDeallocate(&txt_devinfo);
252 if(error != kDNSServiceErr_NoError) {
253 LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
254 DEV_INFO_SERVICE_TYPE, error);
257 } /* if (config->obj.options.mimicmodel) */
261 * Now we can create the thread that will poll for the results
262 * and handle the calling of the callbacks
264 if(pthread_create(&poller, NULL, polling_thread, NULL) != 0) {
265 LOG(log_error, logtype_afpd, "Unable to start mDNS polling thread");
270 TXTRecordDeallocate(&txt_adisk);
274 /************************************************************************
276 ************************************************************************/
279 * Tries to setup the Zeroconf thread and any
280 * neccessary config setting.
282 void md_zeroconf_register(const AFPConfig *configs) {
285 register_stuff(configs);
290 * Tries to shutdown this loop impl.
291 * Call this function from inside this thread.
293 int md_zeroconf_unregister() {
298 #endif /* USE_MDNS */