1 /***
2   This file is part of avahi.
3 
4   avahi is free software; you can redistribute it and/or modify it
5   under the terms of the GNU Lesser General Public License as
6   published by the Free Software Foundation; either version 2.1 of the
7   License, or (at your option) any later version.
8 
9   avahi is distributed in the hope that it will be useful, but WITHOUT
10   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12   Public License for more details.
13 
14   You should have received a copy of the GNU Lesser General Public
15   License along with avahi; if not, write to the Free Software
16   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17   USA.
18 ***/
19 
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 
24 #include <sys/stat.h>
25 #include <glob.h>
26 #include <limits.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <stdlib.h>
32 
33 #ifdef USE_EXPAT_H
34 #include <expat.h>
35 #endif /* USE_EXPAT_H */
36 
37 #ifdef USE_BSDXML_H
38 #include <bsdxml.h>
39 #endif /* USE_BSDXML_H */
40 
41 #include <avahi-common/llist.h>
42 #include <avahi-common/malloc.h>
43 #include <avahi-common/alternative.h>
44 #include <avahi-common/error.h>
45 #include <avahi-common/domain.h>
46 #include <avahi-core/log.h>
47 #include <avahi-core/publish.h>
48 
49 #include "main.h"
50 #include "static-services.h"
51 
52 typedef struct StaticService StaticService;
53 typedef struct StaticServiceGroup StaticServiceGroup;
54 
55 struct StaticService {
56     StaticServiceGroup *group;
57 
58     char *type;
59     char *domain_name;
60     char *host_name;
61     uint16_t port;
62     int protocol;
63 
64     AvahiStringList *subtypes;
65 
66     AvahiStringList *txt_records;
67 
68     AVAHI_LLIST_FIELDS(StaticService, services);
69 };
70 
71 struct StaticServiceGroup {
72     char *filename;
73     time_t mtime;
74 
75     char *name, *chosen_name;
76     int replace_wildcards;
77 
78     AvahiSEntryGroup *entry_group;
79     AVAHI_LLIST_HEAD(StaticService, services);
80     AVAHI_LLIST_FIELDS(StaticServiceGroup, groups);
81 };
82 
83 static AVAHI_LLIST_HEAD(StaticServiceGroup, groups) = NULL;
84 
replacestr(const char * pattern,const char * a,const char * b)85 static char *replacestr(const char *pattern, const char *a, const char *b) {
86     char *r = NULL, *e, *n;
87 
88     while ((e = strstr(pattern, a))) {
89         char *k;
90 
91         k = avahi_strndup(pattern, e - pattern);
92         if (r)
93             n = avahi_strdup_printf("%s%s%s", r, k, b);
94         else
95             n = avahi_strdup_printf("%s%s", k, b);
96 
97         avahi_free(k);
98         avahi_free(r);
99         r = n;
100 
101         pattern = e + strlen(a);
102     }
103 
104     if (!r)
105         return avahi_strdup(pattern);
106 
107     n = avahi_strdup_printf("%s%s", r, pattern);
108     avahi_free(r);
109 
110     return n;
111 }
112 
113 static void add_static_service_group_to_server(StaticServiceGroup *g);
114 static void remove_static_service_group_from_server(StaticServiceGroup *g);
115 
static_service_new(StaticServiceGroup * group)116 static StaticService *static_service_new(StaticServiceGroup *group) {
117     StaticService *s;
118 
119     assert(group);
120     s = avahi_new(StaticService, 1);
121     s->group = group;
122 
123     s->type = s->host_name = s->domain_name = NULL;
124     s->port = 0;
125     s->protocol = AVAHI_PROTO_UNSPEC;
126 
127     s->txt_records = NULL;
128     s->subtypes = NULL;
129 
130     AVAHI_LLIST_PREPEND(StaticService, services, group->services, s);
131 
132     return s;
133 }
134 
static_service_group_new(char * filename)135 static StaticServiceGroup *static_service_group_new(char *filename) {
136     StaticServiceGroup *g;
137     assert(filename);
138 
139     g = avahi_new(StaticServiceGroup, 1);
140     g->filename = avahi_strdup(filename);
141     g->mtime = 0;
142     g->name = g->chosen_name = NULL;
143     g->replace_wildcards = 0;
144     g->entry_group = NULL;
145 
146     AVAHI_LLIST_HEAD_INIT(StaticService, g->services);
147     AVAHI_LLIST_PREPEND(StaticServiceGroup, groups, groups, g);
148 
149     return g;
150 }
151 
static_service_free(StaticService * s)152 static void static_service_free(StaticService *s) {
153     assert(s);
154 
155     AVAHI_LLIST_REMOVE(StaticService, services, s->group->services, s);
156 
157     avahi_free(s->type);
158     avahi_free(s->host_name);
159     avahi_free(s->domain_name);
160 
161     avahi_string_list_free(s->txt_records);
162     avahi_string_list_free(s->subtypes);
163 
164     avahi_free(s);
165 }
166 
static_service_group_free(StaticServiceGroup * g)167 static void static_service_group_free(StaticServiceGroup *g) {
168     assert(g);
169 
170     if (g->entry_group)
171         avahi_s_entry_group_free(g->entry_group);
172 
173     while (g->services)
174         static_service_free(g->services);
175 
176     AVAHI_LLIST_REMOVE(StaticServiceGroup, groups, groups, g);
177 
178     avahi_free(g->filename);
179     avahi_free(g->name);
180     avahi_free(g->chosen_name);
181     avahi_free(g);
182 }
183 
entry_group_callback(AvahiServer * s,AVAHI_GCC_UNUSED AvahiSEntryGroup * eg,AvahiEntryGroupState state,void * userdata)184 static void entry_group_callback(AvahiServer *s, AVAHI_GCC_UNUSED AvahiSEntryGroup *eg, AvahiEntryGroupState state, void* userdata) {
185     StaticServiceGroup *g = userdata;
186 
187     assert(s);
188     assert(g);
189 
190     switch (state) {
191 
192         case AVAHI_ENTRY_GROUP_COLLISION: {
193             char *n;
194 
195             remove_static_service_group_from_server(g);
196 
197             n = avahi_alternative_service_name(g->chosen_name);
198             avahi_free(g->chosen_name);
199             g->chosen_name = n;
200 
201             avahi_log_notice("Service name conflict for \"%s\" (%s), retrying with \"%s\".", g->name, g->filename, g->chosen_name);
202 
203             add_static_service_group_to_server(g);
204             break;
205         }
206 
207         case AVAHI_ENTRY_GROUP_ESTABLISHED:
208             avahi_log_info("Service \"%s\" (%s) successfully established.", g->chosen_name, g->filename);
209             break;
210 
211         case AVAHI_ENTRY_GROUP_FAILURE:
212             avahi_log_warn("Failed to publish service \"%s\" (%s): %s", g->chosen_name, g->filename, avahi_strerror(avahi_server_errno(s)));
213             remove_static_service_group_from_server(g);
214             break;
215 
216         case AVAHI_ENTRY_GROUP_UNCOMMITED:
217         case AVAHI_ENTRY_GROUP_REGISTERING:
218             ;
219     }
220 }
221 
add_static_service_group_to_server(StaticServiceGroup * g)222 static void add_static_service_group_to_server(StaticServiceGroup *g) {
223     StaticService *s;
224 
225     assert(g);
226 
227     if (g->entry_group && !avahi_s_entry_group_is_empty(g->entry_group))
228         /* This service group is already registered in the server */
229         return;
230 
231     if (!g->chosen_name || (g->replace_wildcards && strstr(g->name, "%h"))) {
232 
233         avahi_free(g->chosen_name);
234 
235         if (g->replace_wildcards) {
236             char label[AVAHI_LABEL_MAX];
237             const char *p;
238 
239             p = avahi_server_get_host_name(avahi_server);
240             avahi_unescape_label(&p, label, sizeof(label));
241 
242             g->chosen_name = replacestr(g->name, "%h", label);
243         } else
244             g->chosen_name = avahi_strdup(g->name);
245 
246     }
247 
248     if (!g->entry_group)
249         g->entry_group = avahi_s_entry_group_new(avahi_server, entry_group_callback, g);
250 
251     assert(avahi_s_entry_group_is_empty(g->entry_group));
252 
253     for (s = g->services; s; s = s->services_next) {
254         AvahiStringList *i;
255 
256         if (avahi_server_add_service_strlst(
257                 avahi_server,
258                 g->entry_group,
259                 AVAHI_IF_UNSPEC, s->protocol,
260                 0,
261                 g->chosen_name, s->type, s->domain_name,
262                 s->host_name, s->port,
263                 s->txt_records) < 0) {
264             avahi_log_error("Failed to add service '%s' of type '%s', ignoring service group (%s): %s",
265                             g->chosen_name, s->type, g->filename,
266                             avahi_strerror(avahi_server_errno(avahi_server)));
267             remove_static_service_group_from_server(g);
268             return;
269         }
270 
271         for (i = s->subtypes; i; i = i->next) {
272 
273             if (avahi_server_add_service_subtype(
274                     avahi_server,
275                     g->entry_group,
276                     AVAHI_IF_UNSPEC, s->protocol,
277                     0,
278                     g->chosen_name, s->type, s->domain_name,
279                     (char*) i->text) < 0) {
280 
281                 avahi_log_error("Failed to add subtype '%s' for service '%s' of type '%s', ignoring subtype (%s): %s",
282                                 i->text, g->chosen_name, s->type, g->filename,
283                                 avahi_strerror(avahi_server_errno(avahi_server)));
284             }
285         }
286     }
287 
288     avahi_s_entry_group_commit(g->entry_group);
289 }
290 
remove_static_service_group_from_server(StaticServiceGroup * g)291 static void remove_static_service_group_from_server(StaticServiceGroup *g) {
292     assert(g);
293 
294     if (g->entry_group)
295         avahi_s_entry_group_reset(g->entry_group);
296 }
297 
298 typedef enum {
299     XML_TAG_INVALID,
300     XML_TAG_SERVICE_GROUP,
301     XML_TAG_NAME,
302     XML_TAG_SERVICE,
303     XML_TAG_TYPE,
304     XML_TAG_SUBTYPE,
305     XML_TAG_DOMAIN_NAME,
306     XML_TAG_HOST_NAME,
307     XML_TAG_PORT,
308     XML_TAG_TXT_RECORD
309 } xml_tag_name;
310 
311 typedef enum {
312     TXT_RECORD_VALUE_TEXT,
313     TXT_RECORD_VALUE_BINARY_HEX,
314     TXT_RECORD_VALUE_BINARY_BASE64,
315 } txt_record_value_type;
316 
317 struct xml_userdata {
318     StaticServiceGroup *group;
319     StaticService *service;
320     xml_tag_name current_tag;
321     int failed;
322     char *buf;
323     txt_record_value_type txt_type;
324     char *txt_key;
325 };
326 
327 #ifndef XMLCALL
328 #define XMLCALL
329 #endif
330 
xml_start(void * data,const char * el,const char * attr[])331 static void XMLCALL xml_start(void *data, const char *el, const char *attr[]) {
332     struct xml_userdata *u = data;
333 
334     assert(u);
335 
336     if (u->failed)
337         return;
338 
339     if (u->current_tag == XML_TAG_INVALID && strcmp(el, "service-group") == 0) {
340 
341         if (attr[0])
342             goto invalid_attr;
343 
344         u->current_tag = XML_TAG_SERVICE_GROUP;
345     } else if (u->current_tag == XML_TAG_SERVICE_GROUP && strcmp(el, "name") == 0) {
346         u->current_tag = XML_TAG_NAME;
347 
348         if (attr[0]) {
349             if (strcmp(attr[0], "replace-wildcards") == 0)
350                 u->group->replace_wildcards = strcmp(attr[1], "yes") == 0;
351             else
352                 goto invalid_attr;
353 
354             if (attr[2])
355                 goto invalid_attr;
356         }
357 
358     } else if (u->current_tag == XML_TAG_SERVICE_GROUP && strcmp(el, "service") == 0) {
359         u->current_tag = XML_TAG_SERVICE;
360 
361         assert(!u->service);
362         u->service = static_service_new(u->group);
363 
364         if (attr[0]) {
365             if (strcmp(attr[0], "protocol") == 0) {
366                 AvahiProtocol protocol;
367 
368                 if (strcmp(attr[1], "ipv4") == 0) {
369                     protocol = AVAHI_PROTO_INET;
370                 } else if (strcmp(attr[1], "ipv6") == 0) {
371                     protocol = AVAHI_PROTO_INET6;
372                 } else if (strcmp(attr[1], "any") == 0) {
373                     protocol = AVAHI_PROTO_UNSPEC;
374                 } else {
375                     avahi_log_error("%s: parse failure: invalid protocol specification \"%s\".", u->group->filename, attr[1]);
376                     u->failed = 1;
377                     return;
378                 }
379 
380                 u->service->protocol = protocol;
381             } else
382                 goto invalid_attr;
383 
384             if (attr[2])
385                 goto invalid_attr;
386         }
387 
388     } else if (u->current_tag == XML_TAG_SERVICE && strcmp(el, "type") == 0) {
389         if (attr[0])
390             goto invalid_attr;
391 
392         u->current_tag = XML_TAG_TYPE;
393     } else if (u->current_tag == XML_TAG_SERVICE && strcmp(el, "subtype") == 0) {
394         if (attr[0])
395             goto invalid_attr;
396 
397         u->current_tag = XML_TAG_SUBTYPE;
398     } else if (u->current_tag == XML_TAG_SERVICE && strcmp(el, "domain-name") == 0) {
399         if (attr[0])
400             goto invalid_attr;
401 
402         u->current_tag = XML_TAG_DOMAIN_NAME;
403     } else if (u->current_tag == XML_TAG_SERVICE && strcmp(el, "host-name") == 0) {
404         if (attr[0])
405             goto invalid_attr;
406 
407         u->current_tag = XML_TAG_HOST_NAME;
408     } else if (u->current_tag == XML_TAG_SERVICE && strcmp(el, "port") == 0) {
409         if (attr[0])
410             goto invalid_attr;
411 
412         u->current_tag = XML_TAG_PORT;
413     } else if (u->current_tag == XML_TAG_SERVICE && strcmp(el, "txt-record") == 0) {
414         if (attr[0]) {
415             if (strcmp(attr[0], "value-format") == 0) {
416                 txt_record_value_type value_type;
417 
418                 if (strcmp(attr[1], "text") == 0) {
419                     value_type = TXT_RECORD_VALUE_TEXT;
420                 } else if (strcmp(attr[1], "binary-hex") == 0) {
421                     value_type = TXT_RECORD_VALUE_BINARY_HEX;
422                 } else if (strcmp(attr[1], "binary-base64") == 0) {
423                     value_type = TXT_RECORD_VALUE_BINARY_BASE64;
424                 } else {
425                     avahi_log_error("%s: parse failure: invalid txt record value format specification \"%s\".", u->group->filename, attr[1]);
426                     u->failed = 1;
427                     return;
428                 }
429 
430                 u->txt_type = value_type;
431                 if (attr[2])
432                     goto invalid_attr;
433             } else
434                 goto invalid_attr;
435         } else
436             u->txt_type = TXT_RECORD_VALUE_TEXT;
437 
438         u->current_tag = XML_TAG_TXT_RECORD;
439     } else {
440         avahi_log_error("%s: parse failure: didn't expect element <%s>.", u->group->filename, el);
441         u->failed = 1;
442     }
443 
444     return;
445 
446 invalid_attr:
447     avahi_log_error("%s: parse failure: invalid attribute for element <%s>.", u->group->filename, el);
448     u->failed = 1;
449     return;
450 }
451 
hex(char c)452 static uint8_t hex(char c) {
453   if ((c >= '0') && (c <= '9'))
454     return c - '0';
455   if ((c >= 'A') && (c <= 'F'))
456     return c - 'A' + 10;
457   if ((c >= 'a') && (c <= 'f'))
458     return c - 'a' + 10;
459   return 0xFF;
460 }
461 
decode_hex_buf(struct xml_userdata * u,uint8_t ** out_buf,size_t * out_buf_len)462 static int decode_hex_buf(struct xml_userdata *u, uint8_t **out_buf, size_t *out_buf_len) {
463     const char *buf = (u->buf != NULL) ? u->buf : "";
464     size_t buf_len = strlen(buf);
465     uint8_t *raw_buf;
466     size_t iter;
467     size_t raw_buf_len;
468 
469     if (buf_len % 2) {
470         avahi_log_error("%s: parse failure: hex value of the txt record should have an even length", u->group->filename);
471         u->failed = 1;
472         return -1;
473     }
474     raw_buf_len = buf_len / 2;
475     raw_buf = avahi_malloc(raw_buf_len);
476     for (iter = 0; iter < raw_buf_len; iter++) {
477         uint8_t high_nibble = hex(buf[iter * 2]);
478         uint8_t low_nibble = hex(buf[iter * 2 + 1]);
479 
480         if (high_nibble > 0xF || low_nibble > 0xF) {
481             avahi_log_error("%s: parse failure: failed to parse hex data: invalid hex data", u->group->filename);
482             u->failed = 1;
483             avahi_free(raw_buf);
484             return -1;
485         }
486         raw_buf[iter] = (high_nibble << 4) | low_nibble;
487     }
488     *out_buf = raw_buf;
489     *out_buf_len = raw_buf_len;
490     return 0;
491 }
492 
base64(char c)493 static uint8_t base64(char c) {
494   if (c >= 'A' && c <= 'Z')
495     return c - 'A';
496   if (c >= 'a' && c <= 'z')
497     return c - 'a' + 26;
498   if (c >= '0' && c <= '9')
499     return c - '0' + 52;
500   if (c == '+')
501     return 62;
502   if (c == '/')
503     return 63;
504   return 255;
505 }
506 
base64_error(struct xml_userdata * u,uint8_t * raw_buf)507 static int base64_error(struct xml_userdata *u, uint8_t *raw_buf) {
508     avahi_log_error("%s: parse failure: failed to parse base64 data: invalid base64 data", u->group->filename);
509     u->failed = 1;
510     avahi_free(raw_buf);
511     return -1;
512 }
513 
decode_base64_buf(struct xml_userdata * u,uint8_t ** out_buf,size_t * out_buf_len)514 static int decode_base64_buf(struct xml_userdata *u, uint8_t **out_buf, size_t *out_buf_len) {
515     const char *buf = (u->buf != NULL) ? u->buf : "";
516     size_t buf_len = strlen(buf);
517     uint8_t *raw_buf;
518     size_t iter, raw_iter;
519     size_t raw_buf_len;
520     size_t buf_len_no_equals = buf_len;
521 
522     if (buf_len % 4) {
523         avahi_log_error("%s: parse failure: length of the base64 value of the txt record should be a multiple of 4", u->group->filename);
524         u->failed = 1;
525         return -1;
526     }
527     raw_buf_len = (buf_len / 4) * 3;
528     if (buf_len > 0 && buf[buf_len - 1] == '=') {
529         buf_len_no_equals -= 4;
530         raw_buf_len--;
531         if (buf[buf_len - 2] == '=')
532             raw_buf_len--;
533     }
534     raw_buf = avahi_malloc(raw_buf_len);
535     for (iter = 0, raw_iter = 0; iter < buf_len_no_equals; iter += 4, raw_iter += 3) {
536         uint8_t nibble1 = base64(buf[iter + 0]);
537         uint8_t nibble2 = base64(buf[iter + 1]);
538         uint8_t nibble3 = base64(buf[iter + 2]);
539         uint8_t nibble4 = base64(buf[iter + 3]);
540 
541         if (nibble1 > 63 || nibble2 > 63 || nibble3 > 63 || nibble4 > 63)
542             return base64_error(u, raw_buf);
543         raw_buf[raw_iter + 0] = (nibble1 << 2) | (nibble2 >> 4);
544         raw_buf[raw_iter + 1] = (nibble2 << 4) | (nibble3 >> 2);
545         raw_buf[raw_iter + 2] = (nibble3 << 6) | nibble4;
546     }
547     if (buf_len_no_equals < buf_len) {
548         uint8_t nibble1 = base64(buf[iter + 0]);
549         uint8_t nibble2 = base64(buf[iter + 1]);
550 
551         if (nibble1 > 63 || nibble2 > 63)
552             return base64_error(u, raw_buf);
553         raw_buf[raw_iter + 0] = (nibble1 << 2) | (nibble2 >> 4);
554         if (buf[iter + 2] != '=') {
555             uint8_t nibble3 = base64(buf[iter + 2]);
556 
557             if (nibble3 > 63)
558                 return base64_error(u, raw_buf);
559             raw_buf[raw_iter + 1] = (nibble2 << 4) | (nibble3 >> 2);
560         }
561     }
562     *out_buf = raw_buf;
563     *out_buf_len = raw_buf_len;
564     return 0;
565 }
566 
xml_end(void * data,AVAHI_GCC_UNUSED const char * el)567 static void XMLCALL xml_end(void *data, AVAHI_GCC_UNUSED const char *el) {
568     struct xml_userdata *u = data;
569     assert(u);
570 
571     if (u->failed)
572         return;
573 
574     switch (u->current_tag) {
575         case XML_TAG_SERVICE_GROUP:
576 
577             if (!u->group->name || !u->group->services) {
578                 avahi_log_error("%s: parse failure: service group incomplete.", u->group->filename);
579                 u->failed = 1;
580                 return;
581             }
582 
583             u->current_tag = XML_TAG_INVALID;
584             break;
585 
586         case XML_TAG_SERVICE:
587 
588             if (!u->service->type) {
589                 avahi_log_error("%s: parse failure: service incomplete.", u->group->filename);
590                 u->failed = 1;
591                 return;
592             }
593 
594             u->service = NULL;
595             u->current_tag = XML_TAG_SERVICE_GROUP;
596             break;
597 
598         case XML_TAG_NAME:
599             u->current_tag = XML_TAG_SERVICE_GROUP;
600             break;
601 
602         case XML_TAG_PORT: {
603             int p;
604             assert(u->service);
605 
606             p = u->buf ? atoi(u->buf) : 0;
607 
608             if (p < 0 || p > 0xFFFF) {
609                 avahi_log_error("%s: parse failure: invalid port specification \"%s\".", u->group->filename, u->buf);
610                 u->failed = 1;
611                 return;
612             }
613 
614             u->service->port = (uint16_t) p;
615 
616             u->current_tag = XML_TAG_SERVICE;
617             break;
618         }
619 
620         case XML_TAG_TXT_RECORD: {
621             assert(u->service);
622             if (u->txt_key != NULL) {
623                 size_t key_len = strlen(u->txt_key);
624                 uint8_t *value_buf;
625                 uint8_t *free_value_buf = NULL;
626                 size_t value_buf_len = 0;
627 
628                 switch (u->txt_type) {
629                     case TXT_RECORD_VALUE_TEXT:
630                         if (u->buf != NULL) {
631                             value_buf_len = strlen(u->buf);
632                             value_buf = (uint8_t*)u->buf;
633                         } else {
634                             value_buf_len = 0;
635                             value_buf = (uint8_t*)"";
636                         }
637                         break;
638 
639                     case TXT_RECORD_VALUE_BINARY_HEX:
640                         if (decode_hex_buf(u, &value_buf, &value_buf_len) < 0)
641                             return;
642                         free_value_buf = value_buf;
643                         break;
644 
645                     case TXT_RECORD_VALUE_BINARY_BASE64:
646                         if (decode_base64_buf(u, &value_buf, &value_buf_len) < 0)
647                             return;
648                         free_value_buf = value_buf;
649                         break;
650 
651                     default:
652                         assert(0);
653                 }
654 
655                 u->service->txt_records = avahi_string_list_add_anonymous(u->service->txt_records, key_len + 1 + value_buf_len);
656                 memcpy(u->service->txt_records->text, u->txt_key, key_len);
657                 u->service->txt_records->text[key_len] = '=';
658                 memcpy(u->service->txt_records->text + key_len + 1, value_buf, value_buf_len);
659                 avahi_free(u->txt_key);
660                 u->txt_key = NULL;
661                 avahi_free(free_value_buf);
662             } else
663                 u->service->txt_records = avahi_string_list_add(u->service->txt_records, u->buf ? u->buf : "");
664 
665             u->current_tag = XML_TAG_SERVICE;
666             u->txt_type = TXT_RECORD_VALUE_TEXT;
667             break;
668         }
669 
670         case XML_TAG_SUBTYPE: {
671             assert(u->service);
672 
673             u->service->subtypes = avahi_string_list_add(u->service->subtypes, u->buf ? u->buf : "");
674             u->current_tag = XML_TAG_SERVICE;
675             break;
676         }
677 
678         case XML_TAG_TYPE:
679         case XML_TAG_DOMAIN_NAME:
680         case XML_TAG_HOST_NAME:
681             u->current_tag = XML_TAG_SERVICE;
682             break;
683 
684         case XML_TAG_INVALID:
685             ;
686     }
687 
688     avahi_free(u->buf);
689     u->buf = NULL;
690 }
691 
append_cdata(char * t,const char * n,int length)692 static char *append_cdata(char *t, const char *n, int length) {
693     char *r, *k;
694 
695     if (!length)
696         return t;
697 
698 
699     k = avahi_strndup(n, length);
700 
701     if (t) {
702         r = avahi_strdup_printf("%s%s", t, k);
703         avahi_free(k);
704         avahi_free(t);
705     } else
706         r = k;
707 
708     return r;
709 }
710 
xml_cdata(void * data,const XML_Char * s,int len)711 static void XMLCALL xml_cdata(void *data, const XML_Char *s, int len) {
712     struct xml_userdata *u = data;
713     assert(u);
714 
715     if (u->failed)
716         return;
717 
718     switch (u->current_tag) {
719         case XML_TAG_NAME:
720             u->group->name = append_cdata(u->group->name, s, len);
721             break;
722 
723         case XML_TAG_TYPE:
724             assert(u->service);
725             u->service->type = append_cdata(u->service->type, s, len);
726             break;
727 
728         case XML_TAG_DOMAIN_NAME:
729             assert(u->service);
730             u->service->domain_name = append_cdata(u->service->domain_name, s, len);
731             break;
732 
733         case XML_TAG_HOST_NAME:
734             assert(u->service);
735             u->service->host_name = append_cdata(u->service->host_name, s, len);
736             break;
737 
738         case XML_TAG_PORT:
739         case XML_TAG_SUBTYPE:
740             assert(u->service);
741             u->buf = append_cdata(u->buf, s, len);
742             break;
743 
744         case XML_TAG_TXT_RECORD:
745             assert(u->service);
746             if (u->txt_key == NULL) {
747               char *equals = memchr(s, '=', len);
748 
749               if (equals != NULL) {
750                 u->txt_key = append_cdata(u->buf, s, equals - s);
751                 u->buf = NULL;
752                 /* len is now length of the rest of the string past the equals sign */
753                 len -= equals - s + 1;
754                 s = equals + 1;
755               }
756             }
757             if (len > 0)
758                 u->buf = append_cdata(u->buf, s, len);
759             break;
760 
761         case XML_TAG_SERVICE_GROUP:
762         case XML_TAG_SERVICE:
763         case XML_TAG_INVALID:
764             ;
765     }
766 }
767 
static_service_group_load(StaticServiceGroup * g)768 static int static_service_group_load(StaticServiceGroup *g) {
769     XML_Parser parser = NULL;
770     int fd = -1;
771     struct xml_userdata u;
772     int r = -1;
773     struct stat st;
774     ssize_t n;
775 
776     assert(g);
777 
778     u.buf = NULL;
779     u.group = g;
780     u.service = NULL;
781     u.current_tag = XML_TAG_INVALID;
782     u.failed = 0;
783     u.txt_type = TXT_RECORD_VALUE_TEXT;
784     u.txt_key = NULL;
785 
786     /* Cleanup old data in this service group, if available */
787     remove_static_service_group_from_server(g);
788     while (g->services)
789         static_service_free(g->services);
790 
791     avahi_free(g->name);
792     avahi_free(g->chosen_name);
793     g->name = g->chosen_name = NULL;
794     g->replace_wildcards = 0;
795 
796     if (!(parser = XML_ParserCreate(NULL))) {
797         avahi_log_error("XML_ParserCreate() failed.");
798         goto finish;
799     }
800 
801     if ((fd = open(g->filename, O_RDONLY)) < 0) {
802         avahi_log_error("open(\"%s\", O_RDONLY): %s", g->filename, strerror(errno));
803         goto finish;
804     }
805 
806     if (fstat(fd, &st) < 0) {
807         avahi_log_error("fstat(): %s", strerror(errno));
808         goto finish;
809     }
810 
811     g->mtime = st.st_mtime;
812 
813     XML_SetUserData(parser, &u);
814 
815     XML_SetElementHandler(parser, xml_start, xml_end);
816     XML_SetCharacterDataHandler(parser, xml_cdata);
817 
818     do {
819         void *buffer;
820 
821 #define BUFSIZE (10*1024)
822 
823         if (!(buffer = XML_GetBuffer(parser, BUFSIZE))) {
824             avahi_log_error("XML_GetBuffer() failed.");
825             goto finish;
826         }
827 
828         if ((n = read(fd, buffer, BUFSIZE)) < 0) {
829             avahi_log_error("read(): %s\n", strerror(errno));
830             goto finish;
831         }
832 
833         if (!XML_ParseBuffer(parser, n, n == 0)) {
834             avahi_log_error("XML_ParseBuffer() failed at line %d: %s.\n", (int) XML_GetCurrentLineNumber(parser), XML_ErrorString(XML_GetErrorCode(parser)));
835             goto finish;
836         }
837 
838     } while (n != 0);
839 
840     if (!u.failed)
841         r = 0;
842 
843 finish:
844 
845     if (fd >= 0)
846         close(fd);
847 
848     if (parser)
849         XML_ParserFree(parser);
850 
851     avahi_free(u.buf);
852 
853     return r;
854 }
855 
load_file(char * n)856 static void load_file(char *n) {
857     StaticServiceGroup *g;
858     assert(n);
859 
860     for (g = groups; g; g = g->groups_next)
861         if (strcmp(g->filename, n) == 0)
862             return;
863 
864     avahi_log_info("Loading service file %s.", n);
865 
866     g = static_service_group_new(n);
867     if (static_service_group_load(g) < 0) {
868         avahi_log_error("Failed to load service group file %s, ignoring.", g->filename);
869         static_service_group_free(g);
870     }
871 }
872 
static_service_load(int in_chroot)873 void static_service_load(int in_chroot) {
874     StaticServiceGroup *g, *n;
875     glob_t globbuf;
876     int globret;
877     char **p;
878 
879     for (g = groups; g; g = n) {
880         struct stat st;
881 
882         n = g->groups_next;
883 
884         if (stat(g->filename, &st) < 0) {
885 
886             if (errno == ENOENT)
887                 avahi_log_info("Service group file %s vanished, removing services.", g->filename);
888             else
889                 avahi_log_warn("Failed to stat() file %s, ignoring: %s", g->filename, strerror(errno));
890 
891             static_service_group_free(g);
892         } else if (st.st_mtime != g->mtime) {
893             avahi_log_info("Service group file %s changed, reloading.", g->filename);
894 
895             if (static_service_group_load(g) < 0) {
896                 avahi_log_warn("Failed to load service group file %s, removing service.", g->filename);
897                 static_service_group_free(g);
898             }
899         }
900     }
901 
902     memset(&globbuf, 0, sizeof(globbuf));
903 
904     if ((globret = glob(in_chroot ? "/services/*.service" : AVAHI_SERVICE_DIR "/*.service", GLOB_ERR, NULL, &globbuf)) != 0)
905 
906         switch (globret) {
907 #ifdef GLOB_NOSPACE
908 	    case GLOB_NOSPACE:
909 	        avahi_log_error("Not enough memory to read service directory "AVAHI_SERVICE_DIR".");
910 	        break;
911 #endif
912 #ifdef GLOB_NOMATCH
913             case GLOB_NOMATCH:
914 	        avahi_log_info("No service file found in "AVAHI_SERVICE_DIR".");
915 	        break;
916 #endif
917             default:
918 	        avahi_log_error("Failed to read "AVAHI_SERVICE_DIR".");
919 	        break;
920         }
921 
922     else {
923         for (p = globbuf.gl_pathv; *p; p++)
924             load_file(*p);
925 
926         globfree(&globbuf);
927     }
928 }
929 
static_service_free_all(void)930 void static_service_free_all(void) {
931 
932     while (groups)
933         static_service_group_free(groups);
934 }
935 
static_service_add_to_server(void)936 void static_service_add_to_server(void) {
937     StaticServiceGroup *g;
938 
939     for (g = groups; g; g = g->groups_next)
940         add_static_service_group_to_server(g);
941 }
942 
static_service_remove_from_server(void)943 void static_service_remove_from_server(void) {
944     StaticServiceGroup *g;
945 
946     for (g = groups; g; g = g->groups_next)
947         remove_static_service_group_from_server(g);
948 }
949