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/types.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
28 #include <string.h>
29 #include <sys/utsname.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <stdio.h>
33 #include <assert.h>
34 #include <stdlib.h>
35
36 #include <avahi-common/domain.h>
37 #include <avahi-common/timeval.h>
38 #include "avahi-common/avahi-malloc.h"
39 #include <avahi-common/error.h>
40
41 #include "internal.h"
42 #include "iface.h"
43 #include "socket.h"
44 #include "browse.h"
45 #include "log.h"
46 #include "util.h"
47 #include "dns-srv-rr.h"
48 #include "addr-util.h"
49 #include "domain-util.h"
50 #include "rr-util.h"
51
52 #define AVAHI_DEFAULT_CACHE_ENTRIES_MAX 4096
53
enum_aux_records(AvahiServer * s,AvahiInterface * i,const char * name,uint16_t type,void (* callback)(AvahiServer * s,AvahiRecord * r,int flush_cache,void * userdata),void * userdata)54 static void enum_aux_records(AvahiServer *s, AvahiInterface *i, const char *name, uint16_t type, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) {
55 assert(s);
56 assert(i);
57 assert(name);
58 assert(callback);
59
60 if (type == AVAHI_DNS_TYPE_ANY) {
61 AvahiEntry *e;
62
63 for (e = s->entries; e; e = e->entries_next)
64 if (!e->dead &&
65 avahi_entry_is_registered(s, e, i) &&
66 e->record->key->clazz == AVAHI_DNS_CLASS_IN &&
67 avahi_domain_equal(name, e->record->key->name))
68 callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata);
69
70 } else {
71 AvahiEntry *e;
72 AvahiKey *k;
73
74 if (!(k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type)))
75 return; /** OOM */
76
77 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
78 if (!e->dead && avahi_entry_is_registered(s, e, i))
79 callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata);
80
81 avahi_key_unref(k);
82 }
83 }
84
avahi_server_enumerate_aux_records(AvahiServer * s,AvahiInterface * i,AvahiRecord * r,void (* callback)(AvahiServer * s,AvahiRecord * r,int flush_cache,void * userdata),void * userdata)85 void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) {
86 assert(s);
87 assert(i);
88 assert(r);
89 assert(callback);
90
91 /* Call the specified callback far all records referenced by the one specified in *r */
92
93 if (r->key->clazz == AVAHI_DNS_CLASS_IN) {
94 if (r->key->type == AVAHI_DNS_TYPE_PTR) {
95 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
96 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
97 } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
98 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
99 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
100 } else if (r->key->type == AVAHI_DNS_TYPE_CNAME)
101 enum_aux_records(s, i, r->data.cname.name, AVAHI_DNS_TYPE_ANY, callback, userdata);
102 }
103 }
104
avahi_server_prepare_response(AvahiServer * s,AvahiInterface * i,AvahiEntry * e,int unicast_response,int auxiliary)105 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary) {
106 assert(s);
107 assert(i);
108 assert(e);
109
110 avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, unicast_response, auxiliary);
111 }
112
avahi_server_prepare_matching_responses(AvahiServer * s,AvahiInterface * i,AvahiKey * k,int unicast_response)113 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response) {
114 assert(s);
115 assert(i);
116 assert(k);
117
118 /* Push all records that match the specified key to the record list */
119
120 if (avahi_key_is_pattern(k)) {
121 AvahiEntry *e;
122
123 /* Handle ANY query */
124
125 for (e = s->entries; e; e = e->entries_next)
126 if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_is_registered(s, e, i))
127 avahi_server_prepare_response(s, i, e, unicast_response, 0);
128
129 } else {
130 AvahiEntry *e;
131
132 /* Handle all other queries */
133
134 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
135 if (!e->dead && avahi_entry_is_registered(s, e, i))
136 avahi_server_prepare_response(s, i, e, unicast_response, 0);
137 }
138
139 /* Look for CNAME records */
140
141 if ((k->clazz == AVAHI_DNS_CLASS_IN || k->clazz == AVAHI_DNS_CLASS_ANY)
142 && k->type != AVAHI_DNS_TYPE_CNAME && k->type != AVAHI_DNS_TYPE_ANY) {
143
144 AvahiKey *cname_key;
145
146 if (!(cname_key = avahi_key_new(k->name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_CNAME)))
147 return;
148
149 avahi_server_prepare_matching_responses(s, i, cname_key, unicast_response);
150 avahi_key_unref(cname_key);
151 }
152 }
153
withdraw_entry(AvahiServer * s,AvahiEntry * e)154 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
155 assert(s);
156 assert(e);
157
158 /* Withdraw the specified entry, and if is part of an entry group,
159 * put that into COLLISION state */
160
161 if (e->dead)
162 return;
163
164 if (e->group) {
165 AvahiEntry *k;
166
167 for (k = e->group->entries; k; k = k->by_group_next)
168 if (!k->dead) {
169 avahi_goodbye_entry(s, k, 0, 1);
170 k->dead = 1;
171 }
172
173 e->group->n_probing = 0;
174
175 avahi_s_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
176 } else {
177 avahi_goodbye_entry(s, e, 0, 1);
178 e->dead = 1;
179 }
180
181 s->need_entry_cleanup = 1;
182 }
183
withdraw_rrset(AvahiServer * s,AvahiKey * key)184 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
185 AvahiEntry *e;
186
187 assert(s);
188 assert(key);
189
190 /* Withdraw an entry RRSset */
191
192 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
193 withdraw_entry(s, e);
194 }
195
incoming_probe(AvahiServer * s,AvahiRecord * record,AvahiInterface * i)196 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
197 AvahiEntry *e, *n;
198 int ours = 0, won = 0, lost = 0;
199
200 assert(s);
201 assert(record);
202 assert(i);
203
204 /* Handle incoming probes and check if they conflict our own probes */
205
206 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
207 int cmp;
208 n = e->by_key_next;
209
210 if (e->dead)
211 continue;
212
213 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
214 ours = 1;
215 break;
216 } else {
217
218 if (avahi_entry_is_probing(s, e, i)) {
219 if (cmp > 0)
220 won = 1;
221 else /* cmp < 0 */
222 lost = 1;
223 }
224 }
225 }
226
227 if (!ours) {
228 char *t = avahi_record_to_string(record);
229
230 if (won)
231 avahi_log_debug("Received conflicting probe [%s]. Local host won.", t);
232 else if (lost) {
233 avahi_log_debug("Received conflicting probe [%s]. Local host lost. Withdrawing.", t);
234 withdraw_rrset(s, record->key);
235 }
236
237 avahi_free(t);
238 }
239 }
240
handle_conflict(AvahiServer * s,AvahiInterface * i,AvahiRecord * record,int unique)241 static int handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, int unique) {
242 int valid = 1, ours = 0, conflict = 0, withdraw_immediately = 0;
243 AvahiEntry *e, *n, *conflicting_entry = NULL;
244
245 assert(s);
246 assert(i);
247 assert(record);
248
249 /* Check whether an incoming record conflicts with one of our own */
250
251 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
252 n = e->by_key_next;
253
254 if (e->dead)
255 continue;
256
257 /* Check if the incoming is a goodbye record */
258 if (avahi_record_is_goodbye(record)) {
259
260 if (avahi_record_equal_no_ttl(e->record, record)) {
261 char *t;
262
263 /* Refresh */
264 t = avahi_record_to_string(record);
265 avahi_log_debug("Received goodbye record for one of our records [%s]. Refreshing.", t);
266 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
267
268 valid = 0;
269 avahi_free(t);
270 break;
271 }
272
273 /* If the goodybe packet doesn't match one of our own RRs, we simply ignore it. */
274 continue;
275 }
276
277 if (!(e->flags & AVAHI_PUBLISH_UNIQUE) && !unique)
278 continue;
279
280 /* Either our entry or the other is intended to be unique, so let's check */
281
282 if (avahi_record_equal_no_ttl(e->record, record)) {
283 ours = 1; /* We have an identical record, so this is no conflict */
284
285 /* Check wheter there is a TTL conflict */
286 if (record->ttl <= e->record->ttl/2 &&
287 avahi_entry_is_registered(s, e, i)) {
288 char *t;
289 /* Refresh */
290 t = avahi_record_to_string(record);
291
292 avahi_log_debug("Received record with bad TTL [%s]. Refreshing.", t);
293 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
294 valid = 0;
295
296 avahi_free(t);
297 }
298
299 /* There's no need to check the other entries of this RRset */
300 break;
301
302 } else {
303
304 if (avahi_entry_is_registered(s, e, i)) {
305
306 /* A conflict => we have to return to probe mode */
307 conflict = 1;
308 conflicting_entry = e;
309
310 } else if (avahi_entry_is_probing(s, e, i)) {
311
312 /* We are currently registering a matching record, but
313 * someone else already claimed it, so let's
314 * withdraw */
315 conflict = 1;
316 withdraw_immediately = 1;
317 }
318 }
319 }
320
321 if (!ours && conflict) {
322 char *t;
323
324 valid = 0;
325
326 t = avahi_record_to_string(record);
327
328 if (withdraw_immediately) {
329 avahi_log_debug("Received conflicting record [%s] with local record to be. Withdrawing.", t);
330 withdraw_rrset(s, record->key);
331 } else {
332 assert(conflicting_entry);
333 avahi_log_debug("Received conflicting record [%s]. Resetting our record.", t);
334 avahi_entry_return_to_initial_state(s, conflicting_entry, i);
335
336 /* Local unique records are returned to probing
337 * state. Local shared records are reannounced. */
338 }
339
340 avahi_free(t);
341 }
342
343 return valid;
344 }
345
append_aux_callback(AvahiServer * s,AvahiRecord * r,int flush_cache,void * userdata)346 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) {
347 int *unicast_response = userdata;
348
349 assert(s);
350 assert(r);
351 assert(unicast_response);
352
353 avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, 1);
354 }
355
append_aux_records_to_list(AvahiServer * s,AvahiInterface * i,AvahiRecord * r,int unicast_response)356 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int unicast_response) {
357 assert(s);
358 assert(r);
359
360 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
361 }
362
avahi_server_generate_response(AvahiServer * s,AvahiInterface * i,AvahiDnsPacket * p,const AvahiAddress * a,uint16_t port,int legacy_unicast,int immediately)363 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int immediately) {
364
365 assert(s);
366 assert(i);
367 assert(!legacy_unicast || (a && port > 0 && p));
368
369 if (legacy_unicast) {
370 AvahiDnsPacket *reply;
371 AvahiRecord *r;
372
373 if (!(reply = avahi_dns_packet_new_reply(p, 512 + AVAHI_DNS_PACKET_EXTRA_SIZE /* unicast DNS maximum packet size is 512 */ , 1, 1)))
374 return; /* OOM */
375
376 while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
377
378 append_aux_records_to_list(s, i, r, 0);
379
380 if (avahi_dns_packet_append_record(reply, r, 0, 10))
381 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
382 else {
383 char *t = avahi_record_to_string(r);
384 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
385 avahi_free(t);
386 }
387
388 avahi_record_unref(r);
389 }
390
391 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
392 avahi_interface_send_packet_unicast(i, reply, a, port);
393
394 avahi_dns_packet_free(reply);
395
396 } else {
397 int unicast_response, flush_cache, auxiliary;
398 AvahiDnsPacket *reply = NULL;
399 AvahiRecord *r;
400
401 /* In case the query packet was truncated never respond
402 immediately, because known answer suppression records might be
403 contained in later packets */
404 int tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
405
406 while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
407
408 int im = immediately;
409
410 /* Only send the response immediately if it contains a
411 * unique entry AND it is not in reply to a truncated
412 * packet AND it is not an auxiliary record AND all other
413 * responses for this record are unique too. */
414
415 if (flush_cache && !tc && !auxiliary && avahi_record_list_all_flush_cache(s->record_list))
416 im = 1;
417
418 if (!avahi_interface_post_response(i, r, flush_cache, a, im) && unicast_response) {
419
420 /* Due to some reasons the record has not been scheduled.
421 * The client requested an unicast response in that
422 * case. Therefore we prepare such a response */
423
424 append_aux_records_to_list(s, i, r, unicast_response);
425
426 for (;;) {
427
428 if (!reply) {
429 assert(p);
430
431 if (!(reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, 0, 0)))
432 break; /* OOM */
433 }
434
435 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
436
437 /* Appending this record succeeded, so incremeant
438 * the specific header field, and return to the caller */
439
440 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
441 break;
442 }
443
444 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
445 size_t size;
446
447 /* The record is too large for one packet, so create a larger packet */
448
449 avahi_dns_packet_free(reply);
450 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
451
452 if (!(reply = avahi_dns_packet_new_reply(p, size + AVAHI_DNS_PACKET_EXTRA_SIZE, 0, 1)))
453 break; /* OOM */
454
455 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
456
457 /* Appending this record succeeded, so incremeant
458 * the specific header field, and return to the caller */
459
460 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
461 break;
462
463 } else {
464
465 /* We completely fucked up, there's
466 * nothing we can do. The RR just doesn't
467 * fit in. Let's ignore it. */
468
469 char *t;
470 avahi_dns_packet_free(reply);
471 reply = NULL;
472 t = avahi_record_to_string(r);
473 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
474 avahi_free(t);
475 break;
476 }
477 }
478
479 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
480 avahi_interface_send_packet_unicast(i, reply, a, port);
481 avahi_dns_packet_free(reply);
482 reply = NULL;
483 }
484 }
485
486 avahi_record_unref(r);
487 }
488
489 if (reply) {
490 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
491 avahi_interface_send_packet_unicast(i, reply, a, port);
492 avahi_dns_packet_free(reply);
493 }
494 }
495
496 avahi_record_list_flush(s->record_list);
497 }
498
reflect_response(AvahiServer * s,AvahiInterface * i,AvahiRecord * r,int flush_cache)499 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) {
500 AvahiInterface *j;
501
502 assert(s);
503 assert(i);
504 assert(r);
505
506 if (!s->config.enable_reflector)
507 return;
508
509 for (j = s->monitor->interfaces; j; j = j->interface_next)
510 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
511 avahi_interface_post_response(j, r, flush_cache, NULL, 1);
512 }
513
reflect_cache_walk_callback(AvahiCache * c,AvahiKey * pattern,AvahiCacheEntry * e,void * userdata)514 static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
515 AvahiServer *s = userdata;
516 AvahiRecord* r;
517
518 assert(c);
519 assert(pattern);
520 assert(e);
521 assert(s);
522
523 /* Don't reflect cache entry with ipv6 link-local addresses. */
524 r = e->record;
525 if ((r->key->type == AVAHI_DNS_TYPE_AAAA) &&
526 (r->data.aaaa.address.address[0] == 0xFE) &&
527 (r->data.aaaa.address.address[1] == 0x80))
528 return NULL;
529
530 avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
531 return NULL;
532 }
533
reflect_query(AvahiServer * s,AvahiInterface * i,AvahiKey * k)534 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
535 AvahiInterface *j;
536
537 assert(s);
538 assert(i);
539 assert(k);
540
541 if (!s->config.enable_reflector)
542 return;
543
544 for (j = s->monitor->interfaces; j; j = j->interface_next)
545 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
546 /* Post the query to other networks */
547 avahi_interface_post_query(j, k, 1, NULL);
548
549 /* Reply from caches of other network. This is needed to
550 * "work around" known answer suppression. */
551
552 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
553 }
554 }
555
reflect_probe(AvahiServer * s,AvahiInterface * i,AvahiRecord * r)556 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
557 AvahiInterface *j;
558
559 assert(s);
560 assert(i);
561 assert(r);
562
563 if (!s->config.enable_reflector)
564 return;
565
566 for (j = s->monitor->interfaces; j; j = j->interface_next)
567 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
568 avahi_interface_post_probe(j, r, 1);
569 }
570
handle_query_packet(AvahiServer * s,AvahiDnsPacket * p,AvahiInterface * i,const AvahiAddress * a,uint16_t port,int legacy_unicast,int from_local_iface)571 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) {
572 size_t n;
573 int is_probe;
574
575 assert(s);
576 assert(p);
577 assert(i);
578 assert(a);
579
580 assert(avahi_record_list_is_empty(s->record_list));
581
582 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
583
584 /* Handle the questions */
585 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
586 AvahiKey *key;
587 int unicast_response = 0;
588
589 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
590 avahi_log_warn(__FILE__": Packet too short or invalid while reading question key. (Maybe a UTF-8 problem?)");
591 goto fail;
592 }
593
594 if (!legacy_unicast && !from_local_iface) {
595 reflect_query(s, i, key);
596 if (!unicast_response)
597 avahi_cache_start_poof(i->cache, key, a);
598 }
599
600 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
601 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
602 /* Allow our own queries to be suppressed by incoming
603 * queries only when they do not include known answers */
604 avahi_query_scheduler_incoming(i->query_scheduler, key);
605
606 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
607 avahi_key_unref(key);
608 }
609
610 if (!legacy_unicast) {
611
612 /* Known Answer Suppression */
613 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
614 AvahiRecord *record;
615 int unique = 0;
616
617 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
618 avahi_log_warn(__FILE__": Packet too short or invalid while reading known answer record. (Maybe a UTF-8 problem?)");
619 goto fail;
620 }
621
622 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
623 avahi_record_list_drop(s->record_list, record);
624 avahi_cache_stop_poof(i->cache, record, a);
625
626 avahi_record_unref(record);
627 }
628
629 /* Probe record */
630 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
631 AvahiRecord *record;
632 int unique = 0;
633
634 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
635 avahi_log_warn(__FILE__": Packet too short or invalid while reading probe record. (Maybe a UTF-8 problem?)");
636 goto fail;
637 }
638
639 if (!avahi_key_is_pattern(record->key)) {
640 if (!from_local_iface)
641 reflect_probe(s, i, record);
642 incoming_probe(s, record, i);
643 }
644
645 avahi_record_unref(record);
646 }
647 }
648
649 if (!avahi_record_list_is_empty(s->record_list))
650 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
651
652 return;
653
654 fail:
655 avahi_record_list_flush(s->record_list);
656 }
657
handle_response_packet(AvahiServer * s,AvahiDnsPacket * p,AvahiInterface * i,const AvahiAddress * a,int from_local_iface)658 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
659 unsigned n;
660
661 assert(s);
662 assert(p);
663 assert(i);
664 assert(a);
665
666 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
667 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
668 AvahiRecord *record;
669 int cache_flush = 0;
670
671 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
672 avahi_log_warn(__FILE__": Packet too short or invalid while reading response record. (Maybe a UTF-8 problem?)");
673 break;
674 }
675
676 if (!avahi_key_is_pattern(record->key)) {
677
678 if (handle_conflict(s, i, record, cache_flush)) {
679 if (!from_local_iface && !avahi_record_is_link_local_address(record))
680 reflect_response(s, i, record, cache_flush);
681 avahi_cache_update(i->cache, record, cache_flush, a);
682 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
683 }
684 }
685
686 avahi_record_unref(record);
687 }
688
689 /* If the incoming response contained a conflicting record, some
690 records have been scheduled for sending. We need to flush them
691 here. */
692 if (!avahi_record_list_is_empty(s->record_list))
693 avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
694 }
695
allocate_slot(AvahiServer * s)696 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
697 unsigned n, idx = (unsigned) -1;
698 AvahiLegacyUnicastReflectSlot *slot;
699
700 assert(s);
701
702 if (!s->legacy_unicast_reflect_slots)
703 s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX);
704
705 for (n = 0; n < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; n++, s->legacy_unicast_reflect_id++) {
706 idx = s->legacy_unicast_reflect_id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
707
708 if (!s->legacy_unicast_reflect_slots[idx])
709 break;
710 }
711
712 if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
713 return NULL;
714
715 if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
716 return NULL; /* OOM */
717
718 s->legacy_unicast_reflect_slots[idx] = slot;
719 slot->id = s->legacy_unicast_reflect_id++;
720 slot->server = s;
721
722 return slot;
723 }
724
deallocate_slot(AvahiServer * s,AvahiLegacyUnicastReflectSlot * slot)725 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
726 unsigned idx;
727
728 assert(s);
729 assert(slot);
730
731 idx = slot->id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
732
733 assert(s->legacy_unicast_reflect_slots[idx] == slot);
734
735 avahi_time_event_free(slot->time_event);
736
737 avahi_free(slot);
738 s->legacy_unicast_reflect_slots[idx] = NULL;
739 }
740
free_slots(AvahiServer * s)741 static void free_slots(AvahiServer *s) {
742 unsigned idx;
743 assert(s);
744
745 if (!s->legacy_unicast_reflect_slots)
746 return;
747
748 for (idx = 0; idx < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; idx ++)
749 if (s->legacy_unicast_reflect_slots[idx])
750 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
751
752 avahi_free(s->legacy_unicast_reflect_slots);
753 s->legacy_unicast_reflect_slots = NULL;
754 }
755
find_slot(AvahiServer * s,uint16_t id)756 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
757 unsigned idx;
758
759 assert(s);
760
761 if (!s->legacy_unicast_reflect_slots)
762 return NULL;
763
764 idx = id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
765
766 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
767 return NULL;
768
769 return s->legacy_unicast_reflect_slots[idx];
770 }
771
legacy_unicast_reflect_slot_timeout(AvahiTimeEvent * e,void * userdata)772 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
773 AvahiLegacyUnicastReflectSlot *slot = userdata;
774
775 assert(e);
776 assert(slot);
777 assert(slot->time_event == e);
778
779 deallocate_slot(slot->server, slot);
780 }
781
reflect_legacy_unicast_query_packet(AvahiServer * s,AvahiDnsPacket * p,AvahiInterface * i,const AvahiAddress * a,uint16_t port)782 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
783 AvahiLegacyUnicastReflectSlot *slot;
784 AvahiInterface *j;
785
786 assert(s);
787 assert(p);
788 assert(i);
789 assert(a);
790 assert(port > 0);
791 assert(i->protocol == a->proto);
792
793 if (!s->config.enable_reflector)
794 return;
795
796 /* Reflecting legacy unicast queries is a little more complicated
797 than reflecting normal queries, since we must route the
798 responses back to the right client. Therefore we must store
799 some information for finding the right client contact data for
800 response packets. In contrast to normal queries legacy
801 unicast query and response packets are reflected untouched and
802 are not reassembled into larger packets */
803
804 if (!(slot = allocate_slot(s))) {
805 /* No slot available, we drop this legacy unicast query */
806 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
807 return;
808 }
809
810 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
811 slot->address = *a;
812 slot->port = port;
813 slot->interface = i->hardware->index;
814
815 avahi_elapse_time(&slot->elapse_time, 2000, 0);
816 slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
817
818 /* Patch the packet with our new locally generatet id */
819 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
820
821 for (j = s->monitor->interfaces; j; j = j->interface_next)
822 if (j->announcing &&
823 j != i &&
824 (s->config.reflect_ipv || j->protocol == i->protocol)) {
825
826 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
827 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, NULL, 0);
828 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
829 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, NULL, 0);
830 }
831
832 /* Reset the id */
833 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
834 }
835
originates_from_local_legacy_unicast_socket(AvahiServer * s,const AvahiAddress * address,uint16_t port)836 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const AvahiAddress *address, uint16_t port) {
837 assert(s);
838 assert(address);
839 assert(port > 0);
840
841 if (!s->config.enable_reflector)
842 return 0;
843
844 if (!avahi_address_is_local(s->monitor, address))
845 return 0;
846
847 if (address->proto == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
848 struct sockaddr_in lsa;
849 socklen_t l = sizeof(lsa);
850
851 if (getsockname(s->fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0)
852 avahi_log_warn("getsockname(): %s", strerror(errno));
853 else
854 return avahi_port_from_sockaddr((struct sockaddr*) &lsa) == port;
855
856 }
857
858 if (address->proto == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
859 struct sockaddr_in6 lsa;
860 socklen_t l = sizeof(lsa);
861
862 if (getsockname(s->fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0)
863 avahi_log_warn("getsockname(): %s", strerror(errno));
864 else
865 return avahi_port_from_sockaddr((struct sockaddr*) &lsa) == port;
866 }
867
868 return 0;
869 }
870
is_mdns_mcast_address(const AvahiAddress * a)871 static int is_mdns_mcast_address(const AvahiAddress *a) {
872 AvahiAddress b;
873 assert(a);
874
875 avahi_address_parse(a->proto == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->proto, &b);
876 return avahi_address_cmp(a, &b) == 0;
877 }
878
originates_from_local_iface(AvahiServer * s,AvahiIfIndex iface,const AvahiAddress * a,uint16_t port)879 static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) {
880 assert(s);
881 assert(iface != AVAHI_IF_UNSPEC);
882 assert(a);
883
884 /* If it isn't the MDNS port it can't be generated by us */
885 if (port != AVAHI_MDNS_PORT)
886 return 0;
887
888 return avahi_interface_has_address(s->monitor, iface, a);
889 }
890
dispatch_packet(AvahiServer * s,AvahiDnsPacket * p,const AvahiAddress * src_address,uint16_t port,const AvahiAddress * dst_address,AvahiIfIndex iface,int ttl)891 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const AvahiAddress *src_address, uint16_t port, const AvahiAddress *dst_address, AvahiIfIndex iface, int ttl) {
892 AvahiInterface *i;
893 int from_local_iface = 0;
894
895 assert(s);
896 assert(p);
897 assert(src_address);
898 assert(dst_address);
899 assert(iface > 0);
900 assert(src_address->proto == dst_address->proto);
901
902 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, src_address->proto)) ||
903 !i->announcing) {
904 avahi_log_warn("Received packet from invalid interface.");
905 return;
906 }
907
908 if (port <= 0) {
909 /* This fixes RHBZ #475394 */
910 avahi_log_warn("Received packet from invalid source port %u.", (unsigned) port);
911 return;
912 }
913
914 if (avahi_address_is_ipv4_in_ipv6(src_address))
915 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
916 return;
917
918 if (originates_from_local_legacy_unicast_socket(s, src_address, port))
919 /* This originates from our local reflector, so let's ignore it */
920 return;
921
922 /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
923 if (s->config.enable_reflector)
924 from_local_iface = originates_from_local_iface(s, iface, src_address, port);
925
926 if (avahi_dns_packet_check_valid_multicast(p) < 0) {
927 avahi_log_warn("Received invalid packet.");
928 return;
929 }
930
931 if (avahi_dns_packet_is_query(p)) {
932 int legacy_unicast = 0;
933
934 /* For queries EDNS0 might allow ARCOUNT != 0. We ignore the
935 * AR section completely here, so far. Until the day we add
936 * EDNS0 support. */
937
938 if (port != AVAHI_MDNS_PORT) {
939 /* Legacy Unicast */
940
941 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
942 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
943 avahi_log_warn("Invalid legacy unicast query packet.");
944 return;
945 }
946
947 legacy_unicast = 1;
948 }
949
950 if (legacy_unicast)
951 reflect_legacy_unicast_query_packet(s, p, i, src_address, port);
952
953 handle_query_packet(s, p, i, src_address, port, legacy_unicast, from_local_iface);
954
955 } else {
956 char t[AVAHI_ADDRESS_STR_MAX];
957
958 if (port != AVAHI_MDNS_PORT) {
959 avahi_log_warn("Received response from host %s with invalid source port %u on interface '%s.%i'", avahi_address_snprint(t, sizeof(t), src_address), port, i->hardware->name, i->protocol);
960 return;
961 }
962
963 if (ttl != 255 && s->config.check_response_ttl) {
964 avahi_log_warn("Received response from host %s with invalid TTL %u on interface '%s.%i'.", avahi_address_snprint(t, sizeof(t), src_address), ttl, i->hardware->name, i->protocol);
965 return;
966 }
967
968 if (!is_mdns_mcast_address(dst_address) &&
969 !avahi_interface_address_on_link(i, src_address)) {
970
971 avahi_log_warn("Received non-local response from host %s on interface '%s.%i'.", avahi_address_snprint(t, sizeof(t), src_address), i->hardware->name, i->protocol);
972 return;
973 }
974
975 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
976 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
977 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
978
979 avahi_log_warn("Invalid response packet from host %s.", avahi_address_snprint(t, sizeof(t), src_address));
980 return;
981 }
982
983 handle_response_packet(s, p, i, src_address, from_local_iface);
984 }
985 }
986
dispatch_legacy_unicast_packet(AvahiServer * s,AvahiDnsPacket * p)987 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p) {
988 AvahiInterface *j;
989 AvahiLegacyUnicastReflectSlot *slot;
990
991 assert(s);
992 assert(p);
993
994 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
995 avahi_log_warn("Received invalid packet.");
996 return;
997 }
998
999 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
1000 avahi_log_warn("Received legacy unicast response with unknown id");
1001 return;
1002 }
1003
1004 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
1005 !j->announcing)
1006 return;
1007
1008 /* Patch the original ID into this response */
1009 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
1010
1011 /* Forward the response to the correct client */
1012 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
1013
1014 /* Undo changes to packet */
1015 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1016 }
1017
mcast_socket_event(AvahiWatch * w,int fd,AvahiWatchEvent events,void * userdata)1018 static void mcast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1019 AvahiServer *s = userdata;
1020 AvahiAddress dest, src;
1021 AvahiDnsPacket *p = NULL;
1022 AvahiIfIndex iface;
1023 uint16_t port;
1024 uint8_t ttl;
1025
1026 assert(w);
1027 assert(fd >= 0);
1028 assert(events & AVAHI_WATCH_IN);
1029
1030 if (fd == s->fd_ipv4) {
1031 dest.proto = src.proto = AVAHI_PROTO_INET;
1032 p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &src.data.ipv4, &port, &dest.data.ipv4, &iface, &ttl);
1033 } else {
1034 assert(fd == s->fd_ipv6);
1035 dest.proto = src.proto = AVAHI_PROTO_INET6;
1036 p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &src.data.ipv6, &port, &dest.data.ipv6, &iface, &ttl);
1037 }
1038
1039 if (p) {
1040 if (iface == AVAHI_IF_UNSPEC)
1041 iface = avahi_find_interface_for_address(s->monitor, &dest);
1042
1043 if (iface != AVAHI_IF_UNSPEC)
1044 dispatch_packet(s, p, &src, port, &dest, iface, ttl);
1045 else
1046 avahi_log_error("Incoming packet received on address that isn't local.");
1047
1048 avahi_dns_packet_free(p);
1049
1050 avahi_cleanup_dead_entries(s);
1051 }
1052 }
1053
legacy_unicast_socket_event(AvahiWatch * w,int fd,AvahiWatchEvent events,void * userdata)1054 static void legacy_unicast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1055 AvahiServer *s = userdata;
1056 AvahiDnsPacket *p = NULL;
1057
1058 assert(w);
1059 assert(fd >= 0);
1060 assert(events & AVAHI_WATCH_IN);
1061
1062 if (fd == s->fd_legacy_unicast_ipv4)
1063 p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, NULL, NULL, NULL, NULL, NULL);
1064 else {
1065 assert(fd == s->fd_legacy_unicast_ipv6);
1066 p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, NULL, NULL, NULL, NULL, NULL);
1067 }
1068
1069 if (p) {
1070 dispatch_legacy_unicast_packet(s, p);
1071 avahi_dns_packet_free(p);
1072
1073 avahi_cleanup_dead_entries(s);
1074 }
1075 }
1076
server_set_state(AvahiServer * s,AvahiServerState state)1077 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1078 assert(s);
1079
1080 if (s->state == state)
1081 return;
1082
1083 s->state = state;
1084
1085 avahi_interface_monitor_update_rrs(s->monitor, 0);
1086
1087 if (s->callback)
1088 s->callback(s, state, s->userdata);
1089 }
1090
withdraw_host_rrs(AvahiServer * s)1091 static void withdraw_host_rrs(AvahiServer *s) {
1092 assert(s);
1093
1094 if (s->hinfo_entry_group)
1095 avahi_s_entry_group_reset(s->hinfo_entry_group);
1096
1097 if (s->browse_domain_entry_group)
1098 avahi_s_entry_group_reset(s->browse_domain_entry_group);
1099
1100 avahi_interface_monitor_update_rrs(s->monitor, 1);
1101 s->n_host_rr_pending = 0;
1102 }
1103
avahi_server_decrease_host_rr_pending(AvahiServer * s)1104 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1105 assert(s);
1106
1107 assert(s->n_host_rr_pending > 0);
1108
1109 if (--s->n_host_rr_pending == 0)
1110 server_set_state(s, AVAHI_SERVER_RUNNING);
1111 }
1112
avahi_host_rr_entry_group_callback(AvahiServer * s,AvahiSEntryGroup * g,AvahiEntryGroupState state,AVAHI_GCC_UNUSED void * userdata)1113 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
1114 assert(s);
1115 assert(g);
1116
1117 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1118 s->state == AVAHI_SERVER_REGISTERING)
1119 s->n_host_rr_pending ++;
1120
1121 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1122 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1123 withdraw_host_rrs(s);
1124 server_set_state(s, AVAHI_SERVER_COLLISION);
1125
1126 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1127 s->state == AVAHI_SERVER_REGISTERING)
1128 avahi_server_decrease_host_rr_pending(s);
1129 }
1130
register_hinfo(AvahiServer * s)1131 static void register_hinfo(AvahiServer *s) {
1132 struct utsname utsname;
1133 AvahiRecord *r;
1134
1135 assert(s);
1136
1137 if (!s->config.publish_hinfo)
1138 return;
1139
1140 if (s->hinfo_entry_group)
1141 assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1142 else
1143 s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1144
1145 if (!s->hinfo_entry_group) {
1146 avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1147 return;
1148 }
1149
1150 /* Fill in HINFO rr */
1151 if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1152
1153 if (uname(&utsname) < 0)
1154 avahi_log_warn("uname() failed: %s\n", avahi_strerror(errno));
1155 else {
1156
1157 r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1158 r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1159
1160 avahi_log_info("Registering HINFO record with values '%s'/'%s'.", r->data.hinfo.cpu, r->data.hinfo.os);
1161
1162 if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
1163 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1164 return;
1165 }
1166 }
1167
1168 avahi_record_unref(r);
1169 }
1170
1171 if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1172 avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1173
1174 }
1175
register_localhost(AvahiServer * s)1176 static void register_localhost(AvahiServer *s) {
1177 AvahiAddress a;
1178 assert(s);
1179
1180 /* Add localhost entries */
1181 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1182 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a);
1183
1184 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1185 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
1186 }
1187
register_browse_domain(AvahiServer * s)1188 static void register_browse_domain(AvahiServer *s) {
1189 assert(s);
1190
1191 if (!s->config.publish_domain)
1192 return;
1193
1194 if (avahi_domain_equal(s->domain_name, "local"))
1195 return;
1196
1197 if (s->browse_domain_entry_group)
1198 assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1199 else
1200 s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1201
1202 if (!s->browse_domain_entry_group) {
1203 avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1204 return;
1205 }
1206
1207 if (avahi_server_add_ptr(s, s->browse_domain_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, AVAHI_DEFAULT_TTL, "b._dns-sd._udp.local", s->domain_name) < 0) {
1208 avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1209 return;
1210 }
1211
1212 if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1213 avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1214 }
1215
register_stuff(AvahiServer * s)1216 static void register_stuff(AvahiServer *s) {
1217 assert(s);
1218
1219 server_set_state(s, AVAHI_SERVER_REGISTERING);
1220 s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1221
1222 register_hinfo(s);
1223 register_browse_domain(s);
1224 avahi_interface_monitor_update_rrs(s->monitor, 0);
1225
1226 assert(s->n_host_rr_pending > 0);
1227 s->n_host_rr_pending --;
1228
1229 if (s->n_host_rr_pending == 0)
1230 server_set_state(s, AVAHI_SERVER_RUNNING);
1231 }
1232
update_fqdn(AvahiServer * s)1233 static void update_fqdn(AvahiServer *s) {
1234 char *n;
1235
1236 assert(s);
1237 assert(s->host_name);
1238 assert(s->domain_name);
1239
1240 if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1241 return; /* OOM */
1242
1243 avahi_free(s->host_name_fqdn);
1244 s->host_name_fqdn = n;
1245 }
1246
avahi_server_set_host_name(AvahiServer * s,const char * host_name)1247 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1248 char *hn = NULL;
1249 assert(s);
1250
1251 AVAHI_CHECK_VALIDITY(s, !host_name || avahi_is_valid_host_name(host_name), AVAHI_ERR_INVALID_HOST_NAME);
1252
1253 if (!host_name)
1254 hn = avahi_get_host_name_strdup();
1255 else
1256 hn = avahi_normalize_name_strdup(host_name);
1257
1258 hn[strcspn(hn, ".")] = 0;
1259
1260 if (avahi_domain_equal(s->host_name, hn) && s->state != AVAHI_SERVER_COLLISION) {
1261 avahi_free(hn);
1262 return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1263 }
1264
1265 withdraw_host_rrs(s);
1266
1267 avahi_free(s->host_name);
1268 s->host_name = hn;
1269
1270 update_fqdn(s);
1271
1272 register_stuff(s);
1273 return AVAHI_OK;
1274 }
1275
avahi_server_set_domain_name(AvahiServer * s,const char * domain_name)1276 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1277 char *dn = NULL;
1278 assert(s);
1279
1280 AVAHI_CHECK_VALIDITY(s, !domain_name || avahi_is_valid_domain_name(domain_name), AVAHI_ERR_INVALID_DOMAIN_NAME);
1281
1282 if (!domain_name)
1283 dn = avahi_strdup("local");
1284 else
1285 dn = avahi_normalize_name_strdup(domain_name);
1286
1287 if (avahi_domain_equal(s->domain_name, domain_name)) {
1288 avahi_free(dn);
1289 return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1290 }
1291
1292 withdraw_host_rrs(s);
1293
1294 avahi_free(s->domain_name);
1295 s->domain_name = dn;
1296 update_fqdn(s);
1297
1298 register_stuff(s);
1299
1300 avahi_free(dn);
1301 return AVAHI_OK;
1302 }
1303
valid_server_config(const AvahiServerConfig * sc)1304 static int valid_server_config(const AvahiServerConfig *sc) {
1305 AvahiStringList *l;
1306
1307 assert(sc);
1308
1309 if (sc->n_wide_area_servers > AVAHI_WIDE_AREA_SERVERS_MAX)
1310 return AVAHI_ERR_INVALID_CONFIG;
1311
1312 if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1313 return AVAHI_ERR_INVALID_HOST_NAME;
1314
1315 if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1316 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1317
1318 for (l = sc->browse_domains; l; l = l->next)
1319 if (!avahi_is_valid_domain_name((char*) l->text))
1320 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1321
1322 return AVAHI_OK;
1323 }
1324
setup_sockets(AvahiServer * s)1325 static int setup_sockets(AvahiServer *s) {
1326 assert(s);
1327
1328 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
1329 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
1330
1331 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1332 return AVAHI_ERR_NO_NETWORK;
1333
1334 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1335 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1336 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1337 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1338
1339 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1;
1340 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1;
1341
1342 s->watch_ipv4 =
1343 s->watch_ipv6 =
1344 s->watch_legacy_unicast_ipv4 =
1345 s->watch_legacy_unicast_ipv6 = NULL;
1346
1347 if (s->fd_ipv4 >= 0)
1348 s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, mcast_socket_event, s);
1349 if (s->fd_ipv6 >= 0)
1350 s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, mcast_socket_event, s);
1351
1352 if (s->fd_legacy_unicast_ipv4 >= 0)
1353 s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, legacy_unicast_socket_event, s);
1354 if (s->fd_legacy_unicast_ipv6 >= 0)
1355 s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, legacy_unicast_socket_event, s);
1356
1357 return 0;
1358 }
1359
avahi_server_new(const AvahiPoll * poll_api,const AvahiServerConfig * sc,AvahiServerCallback callback,void * userdata,int * error)1360 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1361 AvahiServer *s;
1362 int e;
1363
1364 if (sc && (e = valid_server_config(sc)) < 0) {
1365 if (error)
1366 *error = e;
1367 return NULL;
1368 }
1369
1370 if (!(s = avahi_new(AvahiServer, 1))) {
1371 if (error)
1372 *error = AVAHI_ERR_NO_MEMORY;
1373
1374 return NULL;
1375 }
1376
1377 s->poll_api = poll_api;
1378
1379 if (sc)
1380 avahi_server_config_copy(&s->config, sc);
1381 else
1382 avahi_server_config_init(&s->config);
1383
1384 if ((e = setup_sockets(s)) < 0) {
1385 if (error)
1386 *error = e;
1387
1388 avahi_server_config_free(&s->config);
1389 avahi_free(s);
1390
1391 return NULL;
1392 }
1393
1394 s->n_host_rr_pending = 0;
1395 s->need_entry_cleanup = 0;
1396 s->need_group_cleanup = 0;
1397 s->need_browser_cleanup = 0;
1398 s->cleanup_time_event = NULL;
1399 s->hinfo_entry_group = NULL;
1400 s->browse_domain_entry_group = NULL;
1401 s->error = AVAHI_OK;
1402 s->state = AVAHI_SERVER_INVALID;
1403
1404 s->callback = callback;
1405 s->userdata = userdata;
1406
1407 s->time_event_queue = avahi_time_event_queue_new(poll_api);
1408
1409 s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1410 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1411 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1412
1413 s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1414 AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1415 AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1416 AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1417 AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1418 AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1419 AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1420 AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1421 AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1422
1423 s->legacy_unicast_reflect_slots = NULL;
1424 s->legacy_unicast_reflect_id = 0;
1425
1426 s->record_list = avahi_record_list_new();
1427
1428 /* Get host name */
1429 s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
1430 s->host_name[strcspn(s->host_name, ".")] = 0;
1431 s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
1432 s->host_name_fqdn = NULL;
1433 update_fqdn(s);
1434
1435 do {
1436 s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1437 } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
1438
1439 if (s->config.enable_wide_area) {
1440 s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
1441 avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1442 } else
1443 s->wide_area_lookup_engine = NULL;
1444
1445 s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
1446
1447 s->monitor = avahi_interface_monitor_new(s);
1448 avahi_interface_monitor_sync(s->monitor);
1449
1450 register_localhost(s);
1451 register_stuff(s);
1452
1453 return s;
1454 }
1455
avahi_server_free(AvahiServer * s)1456 void avahi_server_free(AvahiServer* s) {
1457 assert(s);
1458
1459 /* Remove all browsers */
1460
1461 while (s->dns_server_browsers)
1462 avahi_s_dns_server_browser_free(s->dns_server_browsers);
1463 while (s->host_name_resolvers)
1464 avahi_s_host_name_resolver_free(s->host_name_resolvers);
1465 while (s->address_resolvers)
1466 avahi_s_address_resolver_free(s->address_resolvers);
1467 while (s->domain_browsers)
1468 avahi_s_domain_browser_free(s->domain_browsers);
1469 while (s->service_type_browsers)
1470 avahi_s_service_type_browser_free(s->service_type_browsers);
1471 while (s->service_browsers)
1472 avahi_s_service_browser_free(s->service_browsers);
1473 while (s->service_resolvers)
1474 avahi_s_service_resolver_free(s->service_resolvers);
1475 while (s->record_browsers)
1476 avahi_s_record_browser_destroy(s->record_browsers);
1477
1478 /* Remove all locally rgeistered stuff */
1479
1480 while(s->entries)
1481 avahi_entry_free(s, s->entries);
1482
1483 avahi_interface_monitor_free(s->monitor);
1484
1485 while (s->groups)
1486 avahi_entry_group_free(s, s->groups);
1487
1488 free_slots(s);
1489
1490 avahi_hashmap_free(s->entries_by_key);
1491 avahi_record_list_free(s->record_list);
1492 avahi_hashmap_free(s->record_browser_hashmap);
1493
1494 if (s->wide_area_lookup_engine)
1495 avahi_wide_area_engine_free(s->wide_area_lookup_engine);
1496 avahi_multicast_lookup_engine_free(s->multicast_lookup_engine);
1497
1498 if (s->cleanup_time_event)
1499 avahi_time_event_free(s->cleanup_time_event);
1500
1501 avahi_time_event_queue_free(s->time_event_queue);
1502
1503 /* Free watches */
1504
1505 if (s->watch_ipv4)
1506 s->poll_api->watch_free(s->watch_ipv4);
1507 if (s->watch_ipv6)
1508 s->poll_api->watch_free(s->watch_ipv6);
1509
1510 if (s->watch_legacy_unicast_ipv4)
1511 s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1512 if (s->watch_legacy_unicast_ipv6)
1513 s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1514
1515 /* Free sockets */
1516
1517 if (s->fd_ipv4 >= 0)
1518 close(s->fd_ipv4);
1519 if (s->fd_ipv6 >= 0)
1520 close(s->fd_ipv6);
1521
1522 if (s->fd_legacy_unicast_ipv4 >= 0)
1523 close(s->fd_legacy_unicast_ipv4);
1524 if (s->fd_legacy_unicast_ipv6 >= 0)
1525 close(s->fd_legacy_unicast_ipv6);
1526
1527 /* Free other stuff */
1528
1529 avahi_free(s->host_name);
1530 avahi_free(s->domain_name);
1531 avahi_free(s->host_name_fqdn);
1532
1533 avahi_server_config_free(&s->config);
1534
1535 avahi_free(s);
1536 }
1537
avahi_server_get_domain_name(AvahiServer * s)1538 const char* avahi_server_get_domain_name(AvahiServer *s) {
1539 assert(s);
1540
1541 return s->domain_name;
1542 }
1543
avahi_server_get_host_name(AvahiServer * s)1544 const char* avahi_server_get_host_name(AvahiServer *s) {
1545 assert(s);
1546
1547 return s->host_name;
1548 }
1549
avahi_server_get_host_name_fqdn(AvahiServer * s)1550 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1551 assert(s);
1552
1553 return s->host_name_fqdn;
1554 }
1555
avahi_server_get_data(AvahiServer * s)1556 void* avahi_server_get_data(AvahiServer *s) {
1557 assert(s);
1558
1559 return s->userdata;
1560 }
1561
avahi_server_set_data(AvahiServer * s,void * userdata)1562 void avahi_server_set_data(AvahiServer *s, void* userdata) {
1563 assert(s);
1564
1565 s->userdata = userdata;
1566 }
1567
avahi_server_get_state(AvahiServer * s)1568 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1569 assert(s);
1570
1571 return s->state;
1572 }
1573
avahi_server_config_init(AvahiServerConfig * c)1574 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1575 assert(c);
1576
1577 memset(c, 0, sizeof(AvahiServerConfig));
1578 c->use_ipv6 = 1;
1579 c->use_ipv4 = 1;
1580 c->allow_interfaces = NULL;
1581 c->deny_interfaces = NULL;
1582 c->host_name = NULL;
1583 c->domain_name = NULL;
1584 c->check_response_ttl = 0;
1585 c->publish_hinfo = 0;
1586 c->publish_addresses = 1;
1587 c->publish_workstation = 0;
1588 c->publish_domain = 1;
1589 c->use_iff_running = 0;
1590 c->enable_reflector = 0;
1591 c->reflect_ipv = 0;
1592 c->add_service_cookie = 0;
1593 c->enable_wide_area = 0;
1594 c->n_wide_area_servers = 0;
1595 c->disallow_other_stacks = 0;
1596 c->browse_domains = NULL;
1597 c->disable_publishing = 0;
1598 c->allow_point_to_point = 0;
1599 c->publish_aaaa_on_ipv4 = 1;
1600 c->publish_a_on_ipv6 = 0;
1601 c->n_cache_entries_max = AVAHI_DEFAULT_CACHE_ENTRIES_MAX;
1602 c->ratelimit_interval = 0;
1603 c->ratelimit_burst = 0;
1604
1605 return c;
1606 }
1607
avahi_server_config_free(AvahiServerConfig * c)1608 void avahi_server_config_free(AvahiServerConfig *c) {
1609 assert(c);
1610
1611 avahi_free(c->host_name);
1612 avahi_free(c->domain_name);
1613 avahi_string_list_free(c->browse_domains);
1614 avahi_string_list_free(c->allow_interfaces);
1615 avahi_string_list_free(c->deny_interfaces);
1616 }
1617
avahi_server_config_copy(AvahiServerConfig * ret,const AvahiServerConfig * c)1618 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1619 char *d = NULL, *h = NULL;
1620 AvahiStringList *browse = NULL, *allow = NULL, *deny = NULL;
1621 assert(ret);
1622 assert(c);
1623
1624 if (c->host_name)
1625 if (!(h = avahi_strdup(c->host_name)))
1626 return NULL;
1627
1628 if (c->domain_name)
1629 if (!(d = avahi_strdup(c->domain_name))) {
1630 avahi_free(h);
1631 return NULL;
1632 }
1633
1634 if (!(browse = avahi_string_list_copy(c->browse_domains)) && c->browse_domains) {
1635 avahi_free(h);
1636 avahi_free(d);
1637 return NULL;
1638 }
1639
1640 if (!(allow = avahi_string_list_copy(c->allow_interfaces)) && c->allow_interfaces) {
1641 avahi_string_list_free(browse);
1642 avahi_free(h);
1643 avahi_free(d);
1644 return NULL;
1645 }
1646
1647 if (!(deny = avahi_string_list_copy(c->deny_interfaces)) && c->deny_interfaces) {
1648 avahi_string_list_free(allow);
1649 avahi_string_list_free(browse);
1650 avahi_free(h);
1651 avahi_free(d);
1652 return NULL;
1653 }
1654
1655 *ret = *c;
1656 ret->host_name = h;
1657 ret->domain_name = d;
1658 ret->browse_domains = browse;
1659 ret->allow_interfaces = allow;
1660 ret->deny_interfaces = deny;
1661
1662 return ret;
1663 }
1664
avahi_server_errno(AvahiServer * s)1665 int avahi_server_errno(AvahiServer *s) {
1666 assert(s);
1667
1668 return s->error;
1669 }
1670
1671 /* Just for internal use */
avahi_server_set_errno(AvahiServer * s,int error)1672 int avahi_server_set_errno(AvahiServer *s, int error) {
1673 assert(s);
1674
1675 return s->error = error;
1676 }
1677
avahi_server_get_local_service_cookie(AvahiServer * s)1678 uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
1679 assert(s);
1680
1681 return s->local_service_cookie;
1682 }
1683
find_entry(AvahiServer * s,AvahiIfIndex interface,AvahiProtocol protocol,AvahiKey * key)1684 static AvahiEntry *find_entry(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
1685 AvahiEntry *e;
1686
1687 assert(s);
1688 assert(key);
1689
1690 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
1691
1692 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1693 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1694 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING))
1695
1696 return e;
1697
1698 return NULL;
1699 }
1700
avahi_server_get_group_of_service(AvahiServer * s,AvahiIfIndex interface,AvahiProtocol protocol,const char * name,const char * type,const char * domain,AvahiSEntryGroup ** ret_group)1701 int avahi_server_get_group_of_service(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain, AvahiSEntryGroup** ret_group) {
1702 AvahiKey *key = NULL;
1703 AvahiEntry *e;
1704 int ret;
1705 char n[AVAHI_DOMAIN_NAME_MAX];
1706
1707 assert(s);
1708 assert(name);
1709 assert(type);
1710 assert(ret_group);
1711
1712 AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
1713 AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
1714 AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
1715 AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
1716 AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
1717
1718 if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0))
1719 return avahi_server_set_errno(s, ret);
1720
1721 if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1722 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1723
1724 e = find_entry(s, interface, protocol, key);
1725 avahi_key_unref(key);
1726
1727 if (e) {
1728 *ret_group = e->group;
1729 return AVAHI_OK;
1730 }
1731
1732 return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
1733 }
1734
avahi_server_is_service_local(AvahiServer * s,AvahiIfIndex interface,AvahiProtocol protocol,const char * name)1735 int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name) {
1736 AvahiKey *key = NULL;
1737 AvahiEntry *e;
1738
1739 assert(s);
1740 assert(name);
1741
1742 if (!s->host_name_fqdn)
1743 return 0;
1744
1745 if (!(key = avahi_key_new(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1746 return 0;
1747
1748 e = find_entry(s, interface, protocol, key);
1749 avahi_key_unref(key);
1750
1751 if (!e)
1752 return 0;
1753
1754 return avahi_domain_equal(s->host_name_fqdn, e->record->data.srv.name);
1755 }
1756
avahi_server_is_record_local(AvahiServer * s,AvahiIfIndex interface,AvahiProtocol protocol,AvahiRecord * record)1757 int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record) {
1758 AvahiEntry *e;
1759
1760 assert(s);
1761 assert(record);
1762
1763 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = e->by_key_next)
1764
1765 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1766 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1767 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING) &&
1768 avahi_record_equal_no_ttl(record, e->record))
1769 return 1;
1770
1771 return 0;
1772 }
1773
1774 /** Set the wide area DNS servers */
avahi_server_set_wide_area_servers(AvahiServer * s,const AvahiAddress * a,unsigned n)1775 int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) {
1776 assert(s);
1777
1778 if (!s->wide_area_lookup_engine)
1779 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG);
1780
1781 avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n);
1782 return AVAHI_OK;
1783 }
1784
avahi_server_get_config(AvahiServer * s)1785 const AvahiServerConfig* avahi_server_get_config(AvahiServer *s) {
1786 assert(s);
1787
1788 return &s->config;
1789 }
1790
1791 /** Set the browsing domains */
avahi_server_set_browse_domains(AvahiServer * s,AvahiStringList * domains)1792 int avahi_server_set_browse_domains(AvahiServer *s, AvahiStringList *domains) {
1793 AvahiStringList *l;
1794
1795 assert(s);
1796
1797 for (l = s->config.browse_domains; l; l = l->next)
1798 if (!avahi_is_valid_domain_name((char*) l->text))
1799 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1800
1801 avahi_string_list_free(s->config.browse_domains);
1802 s->config.browse_domains = avahi_string_list_copy(domains);
1803
1804 return AVAHI_OK;
1805 }
1806