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