]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/afp_mdns.c
Fix _device-info service type registered with dns-sd API
[netatalk.git] / etc / afpd / afp_mdns.c
1 /*
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
5  *
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11
12 #ifdef HAVE_MDNS
13
14 #include <unistd.h>
15 #include <time.h>
16 #include <pthread.h>
17 #include <poll.h>
18
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>
24
25 #include "afp_zeroconf.h"
26 #include "afp_mdns.h"
27
28 /*
29  * We'll store all the DNSServiceRef's here so that we can
30  * deallocate them later
31  */
32 static DNSServiceRef   *svc_refs = NULL;
33 static int             svc_ref_count = 0;
34 static pthread_t       poller;
35
36 /*
37  * Its easier to use asprintf to set the TXT record values
38  */
39 #define TXTRecordPrintf(rec, key, args...) {            \
40         char *str;                                      \
41         asprintf(&str, args);                           \
42         TXTRecordSetValue(rec, key, strlen(str), str);  \
43         free(str);                                      \
44     }
45 #define TXTRecordKeyPrintf(rec, k, var, args...) {      \
46         char *key, *str;                                \
47         asprintf(&key, k, var);                         \
48         asprintf(&str, args);                           \
49         TXTRecordSetValue(rec, key, strlen(str), str);  \
50         free(str); free(key);                           \
51     }
52
53
54 /*
55  * This is the thread that polls the filehandles
56  */
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));
62     assert(fds);
63
64     for(int i=0; i < svc_ref_count; i++) {
65         int fd = DNSServiceRefSockFD(svc_refs[i]);
66         fds[i].fd = fd;
67         fds[i].events = POLLIN;
68     }
69
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]);
75             }
76         }
77     }
78     return(NULL);
79 }
80
81
82 /*
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
85  * the issue.
86  */
87 void RegisterReply(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode,
88                    const char *name, const char *regtype, const char *domain, void *context) {
89
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);
93     }
94 }
95
96
97 /*
98  * This function unregisters anything we have already
99  * registered and frees associated memory
100  */
101 static void unregister_stuff() {
102     pthread_kill(poller, SIGKILL);
103     if(svc_refs) {
104         for(int i=0; i < svc_ref_count; i++) {
105             DNSServiceRefDeallocate(svc_refs[i]);
106         }
107         free(svc_refs);
108         svc_refs = NULL;
109         svc_ref_count = 0;
110     }
111 }
112
113 /*
114  * This function tries to register the AFP DNS
115  * SRV service type.
116  */
117 static void register_stuff(const AFPObj *obj) {
118     uint                                        port;
119     const struct vol                *volume;
120     DSI                                         *dsi;
121     char                                        name[MAXINSTANCENAMELEN+1];
122     DNSServiceErrorType         error;
123     TXTRecordRef                        txt_adisk;
124     TXTRecordRef                        txt_devinfo;
125     char                                        tmpname[256];
126
127     // If we had already registered, then we will unregister and re-register
128     if(svc_refs) unregister_stuff();
129
130     /* Register our service, prepare the TXT record */
131     TXTRecordCreate(&txt_adisk, 0, NULL);
132     TXTRecordPrintf(&txt_adisk, "sys", "waMa=0,adVF=0x100");
133
134     /* Build AFP volumes list */
135     int i = 0;
136
137     for (volume = getvolumes(); volume; volume = volume->v_next) {
138
139         if (convert_string(CH_UCS2, CH_UTF8_MAC, volume->v_u8mname, -1, tmpname, 255) <= 0) {
140             LOG ( log_error, logtype_afpd, "Could not set Zeroconf volume name for TimeMachine");
141             goto fail;
142         }
143
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);
150             } else {
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);
154             }
155         }
156     }
157
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
164     }
165
166     // Allocate the memory to store our service refs
167     svc_refs = calloc(svc_ref_count, sizeof(DNSServiceRef));
168     assert(svc_ref);
169     svc_ref_count = 0;
170
171     /* AFP server */
172     for (dsi = obj->dsi; dsi; dsi = dsi->next) {
173
174         port = getip_port((struct sockaddr *)&dsi->server);
175
176         if (convert_string(obj->options.unixcharset,
177                            CH_UTF8,
178                            obj->options.hostname,
179                            -1,
180                            name,
181                            MAXINSTANCENAMELEN) <= 0) {
182             LOG(log_error, logtype_afpd, "Could not set Zeroconf instance name");
183             goto fail;
184         }
185         if ((dsi->bonjourname = strdup(name)) == NULL) {
186             LOG(log_error, logtype_afpd, "Could not set Zeroconf instance name");
187             goto fail;
188
189         }
190         LOG(log_info, logtype_afpd, "Registering server '%s' with Bonjour",
191             dsi->bonjourname);
192
193         error = DNSServiceRegister(&svc_refs[svc_ref_count++],
194                                    0,               // no flags
195                                    0,               // all network interfaces
196                                    dsi->bonjourname,
197                                    AFP_DNS_SERVICE_TYPE,
198                                    "",            // default domains
199                                    NULL,            // default host name
200                                    htons(port),
201                                    0,               // length of TXT
202                                    NULL,            // no TXT
203                                    RegisterReply,           // callback
204                                    NULL);       // no context
205         if(error != kDNSServiceErr_NoError) {
206             LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
207                 AFP_DNS_SERVICE_TYPE, error);
208             goto fail;
209         }
210
211         if(i) {
212             error = DNSServiceRegister(&svc_refs[svc_ref_count++],
213                                        0,               // no flags
214                                        0,               // all network interfaces
215                                        dsi->bonjourname,
216                                        ADISK_SERVICE_TYPE,
217                                        "",            // default domains
218                                        NULL,            // default host name
219                                        htons(port),
220                                        TXTRecordGetLength(&txt_adisk),
221                                        TXTRecordGetBytesPtr(&txt_adisk),
222                                        RegisterReply,           // callback
223                                        NULL);       // no context
224             if(error != kDNSServiceErr_NoError) {
225                 LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
226                     ADISK_SERVICE_TYPE, error);
227                 goto fail;
228             }
229         }
230
231         if (obj->options.mimicmodel) {
232             LOG(log_info, logtype_afpd, "Registering server '%s' with model '%s'",
233                 dsi->bonjourname, obj->options.mimicmodel);
234             TXTRecordCreate(&txt_devinfo, 0, NULL);
235             TXTRecordPrintf(&txt_devinfo, "model", obj->options.mimicmodel);
236             error = DNSServiceRegister(&svc_refs[svc_ref_count++],
237                                        0,               // no flags
238                                        0,               // all network interfaces
239                                        dsi->bonjourname,
240                                        DEV_INFO_SERVICE_TYPE,
241                                        "",            // default domains
242                                        NULL,            // default host name
243                                        /*
244                                         * We would probably use port 0 zero, but we can't, from man DNSServiceRegister:
245                                         *   "A value of 0 for a port is passed to register placeholder services.
246                                         *    Place holder services are not found  when browsing, but other
247                                         *    clients cannot register with the same name as the placeholder service."
248                                         * We therefor use port 9 which is used by the adisk service type.
249                                         */
250                                        htons(9),
251                                        TXTRecordGetLength(&txt_devinfo),
252                                        TXTRecordGetBytesPtr(&txt_devinfo),
253                                        RegisterReply,           // callback
254                                        NULL);       // no context
255             TXTRecordDeallocate(&txt_devinfo);
256             if(error != kDNSServiceErr_NoError) {
257                 LOG(log_error, logtype_afpd, "Failed to add service: %s, error=%d",
258                     DEV_INFO_SERVICE_TYPE, error);
259                 goto fail;
260             }
261         } /* if (config->obj.options.mimicmodel) */
262     }   /* for config*/
263
264         /*
265          * Now we can create the thread that will poll for the results
266          * and handle the calling of the callbacks
267          */
268     if(pthread_create(&poller, NULL, polling_thread, NULL) != 0) {
269         LOG(log_error, logtype_afpd, "Unable to start mDNS polling thread");
270         goto fail;
271     }
272
273 fail:
274     TXTRecordDeallocate(&txt_adisk);
275     return;
276 }
277
278 /************************************************************************
279  * Public funcions
280  ************************************************************************/
281
282 /*
283  * Tries to setup the Zeroconf thread and any
284  * neccessary config setting.
285  */
286 void md_zeroconf_register(const AFPObj *obj) {
287     int error;
288
289     register_stuff(obj);
290     return;
291 }
292
293 /*
294  * Tries to shutdown this loop impl.
295  * Call this function from inside this thread.
296  */
297 int md_zeroconf_unregister() {
298     unregister_stuff();
299     return 0;
300 }
301
302 #endif /* USE_MDNS */
303