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/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_debug(__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_debug(__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_debug(__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_debug(__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 /* Filter services that will be cached. Allow all local services */
678 if (!from_local_iface && s->config.enable_reflector && s->config.reflect_filters != NULL) {
679 AvahiStringList *l;
680 int match = 0;
681
682 if (record->key->type == AVAHI_DNS_TYPE_PTR) {
683 /* Need to match DNS pointer target with filter */
684 for (l = s->config.reflect_filters; l; l = l->next) {
685 if (strstr(record->data.ptr.name, (char*) l->text) != NULL) {
686 match = 1;
687 break;
688 }
689 }
690
691 if (!match) {
692 avahi_log_debug("Reject Ptr SRC [%s] Dest [%s]", record->key->name, record->data.ptr.name);
693 goto unref;
694 }
695 else
696 avahi_log_debug("Match Ptr SRC [%s] Dest [%s]", record->key->name, record->data.ptr.name);
697 }
698 else if (record->key->type == AVAHI_DNS_TYPE_SRV || record->key->type == AVAHI_DNS_TYPE_TXT) {
699 /* Need to match key name with filter */
700 for (l = s->config.reflect_filters; l; l = l->next) {
701 if (strstr(record->key->name, (char*) l->text) != NULL) {
702 match = 1;
703 break;
704 }
705 }
706
707 if (!match) {
708 avahi_log_debug("Reject Key [%s] iface [%d]", record->key->name, from_local_iface);
709 goto unref;
710 }
711 else
712 avahi_log_debug("Match Key [%s] iface [%d]", record->key->name, from_local_iface);
713 }
714 }
715
716 if (handle_conflict(s, i, record, cache_flush)) {
717 if (!from_local_iface) {
718 if (!avahi_record_is_link_local_address(record))
719 reflect_response(s, i, record, cache_flush);
720 avahi_cache_update(i->cache, record, cache_flush, a);
721 }
722 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
723 }
724 }
725
726 unref:
727 avahi_record_unref(record);
728 }
729
730 /* If the incoming response contained a conflicting record, some
731 records have been scheduled for sending. We need to flush them
732 here. */
733 if (!avahi_record_list_is_empty(s->record_list))
734 avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
735 }
736
allocate_slot(AvahiServer * s)737 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
738 unsigned n, idx = (unsigned) -1;
739 AvahiLegacyUnicastReflectSlot *slot;
740
741 assert(s);
742
743 if (!s->legacy_unicast_reflect_slots)
744 s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX);
745
746 for (n = 0; n < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; n++, s->legacy_unicast_reflect_id++) {
747 idx = s->legacy_unicast_reflect_id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
748
749 if (!s->legacy_unicast_reflect_slots[idx])
750 break;
751 }
752
753 if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
754 return NULL;
755
756 if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
757 return NULL; /* OOM */
758
759 s->legacy_unicast_reflect_slots[idx] = slot;
760 slot->id = s->legacy_unicast_reflect_id++;
761 slot->server = s;
762
763 return slot;
764 }
765
deallocate_slot(AvahiServer * s,AvahiLegacyUnicastReflectSlot * slot)766 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
767 unsigned idx;
768
769 assert(s);
770 assert(slot);
771
772 idx = slot->id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
773
774 assert(s->legacy_unicast_reflect_slots[idx] == slot);
775
776 avahi_time_event_free(slot->time_event);
777
778 avahi_free(slot);
779 s->legacy_unicast_reflect_slots[idx] = NULL;
780 }
781
free_slots(AvahiServer * s)782 static void free_slots(AvahiServer *s) {
783 unsigned idx;
784 assert(s);
785
786 if (!s->legacy_unicast_reflect_slots)
787 return;
788
789 for (idx = 0; idx < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; idx ++)
790 if (s->legacy_unicast_reflect_slots[idx])
791 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
792
793 avahi_free(s->legacy_unicast_reflect_slots);
794 s->legacy_unicast_reflect_slots = NULL;
795 }
796
find_slot(AvahiServer * s,uint16_t id)797 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
798 unsigned idx;
799
800 assert(s);
801
802 if (!s->legacy_unicast_reflect_slots)
803 return NULL;
804
805 idx = id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
806
807 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
808 return NULL;
809
810 return s->legacy_unicast_reflect_slots[idx];
811 }
812
legacy_unicast_reflect_slot_timeout(AvahiTimeEvent * e,void * userdata)813 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
814 AvahiLegacyUnicastReflectSlot *slot = userdata;
815
816 assert(e);
817 assert(slot);
818 assert(slot->time_event == e);
819
820 deallocate_slot(slot->server, slot);
821 }
822
reflect_legacy_unicast_query_packet(AvahiServer * s,AvahiDnsPacket * p,AvahiInterface * i,const AvahiAddress * a,uint16_t port)823 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
824 AvahiLegacyUnicastReflectSlot *slot;
825 AvahiInterface *j;
826
827 assert(s);
828 assert(p);
829 assert(i);
830 assert(a);
831 assert(port > 0);
832 assert(i->protocol == a->proto);
833
834 if (!s->config.enable_reflector)
835 return;
836
837 /* Reflecting legacy unicast queries is a little more complicated
838 than reflecting normal queries, since we must route the
839 responses back to the right client. Therefore we must store
840 some information for finding the right client contact data for
841 response packets. In contrast to normal queries legacy
842 unicast query and response packets are reflected untouched and
843 are not reassembled into larger packets */
844
845 if (!(slot = allocate_slot(s))) {
846 /* No slot available, we drop this legacy unicast query */
847 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
848 return;
849 }
850
851 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
852 slot->address = *a;
853 slot->port = port;
854 slot->interface = i->hardware->index;
855
856 avahi_elapse_time(&slot->elapse_time, 2000, 0);
857 slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
858
859 /* Patch the packet with our new locally generatet id */
860 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
861
862 for (j = s->monitor->interfaces; j; j = j->interface_next)
863 if (j->announcing &&
864 j != i &&
865 (s->config.reflect_ipv || j->protocol == i->protocol)) {
866
867 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
868 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, NULL, 0);
869 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
870 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, NULL, 0);
871 }
872
873 /* Reset the id */
874 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
875 }
876
originates_from_local_legacy_unicast_socket(AvahiServer * s,const AvahiAddress * address,uint16_t port)877 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const AvahiAddress *address, uint16_t port) {
878 assert(s);
879 assert(address);
880 assert(port > 0);
881
882 if (!s->config.enable_reflector)
883 return 0;
884
885 if (!avahi_address_is_local(s->monitor, address))
886 return 0;
887
888 if (address->proto == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
889 struct sockaddr_in lsa;
890 socklen_t l = sizeof(lsa);
891
892 if (getsockname(s->fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0)
893 avahi_log_warn("getsockname(): %s", strerror(errno));
894 else
895 return avahi_port_from_sockaddr((struct sockaddr*) &lsa) == port;
896
897 }
898
899 if (address->proto == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
900 struct sockaddr_in6 lsa;
901 socklen_t l = sizeof(lsa);
902
903 if (getsockname(s->fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0)
904 avahi_log_warn("getsockname(): %s", strerror(errno));
905 else
906 return avahi_port_from_sockaddr((struct sockaddr*) &lsa) == port;
907 }
908
909 return 0;
910 }
911
is_mdns_mcast_address(const AvahiAddress * a)912 static int is_mdns_mcast_address(const AvahiAddress *a) {
913 AvahiAddress b;
914 assert(a);
915
916 avahi_address_parse(a->proto == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->proto, &b);
917 return avahi_address_cmp(a, &b) == 0;
918 }
919
originates_from_local_iface(AvahiServer * s,AvahiIfIndex iface,const AvahiAddress * a,uint16_t port)920 static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) {
921 assert(s);
922 assert(iface != AVAHI_IF_UNSPEC);
923 assert(a);
924
925 /* If it isn't the MDNS port it can't be generated by us */
926 if (port != AVAHI_MDNS_PORT)
927 return 0;
928
929 return avahi_interface_has_address(s->monitor, iface, a);
930 }
931
dispatch_packet(AvahiServer * s,AvahiDnsPacket * p,const AvahiAddress * src_address,uint16_t port,const AvahiAddress * dst_address,AvahiIfIndex iface,int ttl)932 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const AvahiAddress *src_address, uint16_t port, const AvahiAddress *dst_address, AvahiIfIndex iface, int ttl) {
933 AvahiInterface *i;
934 int from_local_iface = 0;
935
936 assert(s);
937 assert(p);
938 assert(src_address);
939 assert(dst_address);
940 assert(iface > 0);
941 assert(src_address->proto == dst_address->proto);
942
943 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, src_address->proto)) ||
944 !i->announcing) {
945 avahi_log_debug("Received packet from invalid interface.");
946 return;
947 }
948
949 if (port <= 0) {
950 /* This fixes RHBZ #475394 */
951 avahi_log_debug("Received packet from invalid source port %u.", (unsigned) port);
952 return;
953 }
954
955 if (avahi_address_is_ipv4_in_ipv6(src_address))
956 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
957 return;
958
959 if (originates_from_local_legacy_unicast_socket(s, src_address, port))
960 /* This originates from our local reflector, so let's ignore it */
961 return;
962
963 /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
964 if (s->config.enable_reflector)
965 from_local_iface = originates_from_local_iface(s, iface, src_address, port);
966
967 if (avahi_dns_packet_check_valid_multicast(p) < 0) {
968 avahi_log_debug("Received invalid packet.");
969 return;
970 }
971
972 if (avahi_dns_packet_is_query(p)) {
973 int legacy_unicast = 0;
974 char t[AVAHI_ADDRESS_STR_MAX];
975
976 /* For queries EDNS0 might allow ARCOUNT != 0. We ignore the
977 * AR section completely here, so far. Until the day we add
978 * EDNS0 support. */
979
980 if (port != AVAHI_MDNS_PORT) {
981 /* Legacy Unicast */
982
983 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
984 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
985 avahi_log_debug("Invalid legacy unicast query packet.");
986 return;
987 }
988
989 legacy_unicast = 1;
990 }
991
992 if (!is_mdns_mcast_address(dst_address) &&
993 !avahi_interface_address_on_link(i, src_address)) {
994
995 avahi_log_debug("Received non-local unicast query from host %s on interface '%s.%i'.", avahi_address_snprint(t, sizeof(t), src_address), i->hardware->name, i->protocol);
996 return;
997 }
998
999 if (legacy_unicast)
1000 reflect_legacy_unicast_query_packet(s, p, i, src_address, port);
1001
1002 handle_query_packet(s, p, i, src_address, port, legacy_unicast, from_local_iface);
1003
1004 } else {
1005 char t[AVAHI_ADDRESS_STR_MAX];
1006
1007 if (port != AVAHI_MDNS_PORT) {
1008 avahi_log_debug("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);
1009 return;
1010 }
1011
1012 if (ttl != 255 && s->config.check_response_ttl) {
1013 avahi_log_debug("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);
1014 return;
1015 }
1016
1017 if (!is_mdns_mcast_address(dst_address) &&
1018 !avahi_interface_address_on_link(i, src_address)) {
1019
1020 avahi_log_debug("Received non-local response from host %s on interface '%s.%i'.", avahi_address_snprint(t, sizeof(t), src_address), i->hardware->name, i->protocol);
1021 return;
1022 }
1023
1024 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
1025 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
1026 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
1027
1028 avahi_log_debug("Invalid response packet from host %s.", avahi_address_snprint(t, sizeof(t), src_address));
1029 return;
1030 }
1031
1032 handle_response_packet(s, p, i, src_address, from_local_iface);
1033 }
1034 }
1035
dispatch_legacy_unicast_packet(AvahiServer * s,AvahiDnsPacket * p)1036 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p) {
1037 AvahiInterface *j;
1038 AvahiLegacyUnicastReflectSlot *slot;
1039
1040 assert(s);
1041 assert(p);
1042
1043 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
1044 avahi_log_debug("Received invalid packet.");
1045 return;
1046 }
1047
1048 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
1049 avahi_log_debug("Received legacy unicast response with unknown id");
1050 return;
1051 }
1052
1053 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
1054 !j->announcing)
1055 return;
1056
1057 /* Patch the original ID into this response */
1058 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
1059
1060 /* Forward the response to the correct client */
1061 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
1062
1063 /* Undo changes to packet */
1064 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1065 }
1066
mcast_socket_event(AvahiWatch * w,int fd,AvahiWatchEvent events,void * userdata)1067 static void mcast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1068 AvahiServer *s = userdata;
1069 AvahiAddress dest, src;
1070 AvahiDnsPacket *p = NULL;
1071 AvahiIfIndex iface;
1072 uint16_t port;
1073 uint8_t ttl;
1074
1075 assert(w);
1076 assert(fd >= 0);
1077 assert(events & AVAHI_WATCH_IN);
1078
1079 if (fd == s->fd_ipv4) {
1080 dest.proto = src.proto = AVAHI_PROTO_INET;
1081 p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &src.data.ipv4, &port, &dest.data.ipv4, &iface, &ttl);
1082 } else {
1083 assert(fd == s->fd_ipv6);
1084 dest.proto = src.proto = AVAHI_PROTO_INET6;
1085 p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &src.data.ipv6, &port, &dest.data.ipv6, &iface, &ttl);
1086 }
1087
1088 if (p) {
1089 if (iface == AVAHI_IF_UNSPEC)
1090 iface = avahi_find_interface_for_address(s->monitor, &dest);
1091
1092 if (iface != AVAHI_IF_UNSPEC)
1093 dispatch_packet(s, p, &src, port, &dest, iface, ttl);
1094 else
1095 avahi_log_error("Incoming packet received on address that isn't local.");
1096
1097 avahi_dns_packet_free(p);
1098
1099 avahi_cleanup_dead_entries(s);
1100 }
1101 }
1102
legacy_unicast_socket_event(AvahiWatch * w,int fd,AvahiWatchEvent events,void * userdata)1103 static void legacy_unicast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1104 AvahiServer *s = userdata;
1105 AvahiDnsPacket *p = NULL;
1106
1107 assert(w);
1108 assert(fd >= 0);
1109 assert(events & AVAHI_WATCH_IN);
1110
1111 if (fd == s->fd_legacy_unicast_ipv4)
1112 p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, NULL, NULL, NULL, NULL, NULL);
1113 else {
1114 assert(fd == s->fd_legacy_unicast_ipv6);
1115 p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, NULL, NULL, NULL, NULL, NULL);
1116 }
1117
1118 if (p) {
1119 dispatch_legacy_unicast_packet(s, p);
1120 avahi_dns_packet_free(p);
1121
1122 avahi_cleanup_dead_entries(s);
1123 }
1124 }
1125
server_set_state(AvahiServer * s,AvahiServerState state)1126 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1127 assert(s);
1128
1129 if (s->state == state)
1130 return;
1131
1132 s->state = state;
1133
1134 avahi_interface_monitor_update_rrs(s->monitor, 0);
1135
1136 if (s->callback)
1137 s->callback(s, state, s->userdata);
1138 }
1139
withdraw_host_rrs(AvahiServer * s)1140 static void withdraw_host_rrs(AvahiServer *s) {
1141 assert(s);
1142
1143 if (s->hinfo_entry_group)
1144 avahi_s_entry_group_reset(s->hinfo_entry_group);
1145
1146 if (s->browse_domain_entry_group)
1147 avahi_s_entry_group_reset(s->browse_domain_entry_group);
1148
1149 avahi_interface_monitor_update_rrs(s->monitor, 1);
1150 s->n_host_rr_pending = 0;
1151 }
1152
avahi_server_decrease_host_rr_pending(AvahiServer * s)1153 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1154 assert(s);
1155
1156 assert(s->n_host_rr_pending > 0);
1157
1158 if (--s->n_host_rr_pending == 0)
1159 server_set_state(s, AVAHI_SERVER_RUNNING);
1160 }
1161
avahi_host_rr_entry_group_callback(AvahiServer * s,AvahiSEntryGroup * g,AvahiEntryGroupState state,AVAHI_GCC_UNUSED void * userdata)1162 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
1163 assert(s);
1164 assert(g);
1165
1166 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1167 s->state == AVAHI_SERVER_REGISTERING)
1168 s->n_host_rr_pending ++;
1169
1170 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1171 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1172 withdraw_host_rrs(s);
1173 server_set_state(s, AVAHI_SERVER_COLLISION);
1174
1175 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1176 s->state == AVAHI_SERVER_REGISTERING)
1177 avahi_server_decrease_host_rr_pending(s);
1178 }
1179
register_hinfo(AvahiServer * s)1180 static void register_hinfo(AvahiServer *s) {
1181 struct utsname utsname;
1182 AvahiRecord *r;
1183
1184 assert(s);
1185
1186 if (!s->config.publish_hinfo)
1187 return;
1188
1189 if (s->hinfo_entry_group)
1190 assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1191 else
1192 s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1193
1194 if (!s->hinfo_entry_group) {
1195 avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1196 return;
1197 }
1198
1199 /* Fill in HINFO rr */
1200 if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1201
1202 if (uname(&utsname) < 0)
1203 avahi_log_warn("uname() failed: %s\n", avahi_strerror(errno));
1204 else {
1205
1206 r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1207 r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1208
1209 avahi_log_info("Registering HINFO record with values '%s'/'%s'.", r->data.hinfo.cpu, r->data.hinfo.os);
1210
1211 if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
1212 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1213 avahi_record_unref(r);
1214 return;
1215 }
1216 }
1217
1218 avahi_record_unref(r);
1219 }
1220
1221 if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1222 avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1223
1224 }
1225
register_localhost(AvahiServer * s)1226 static void register_localhost(AvahiServer *s) {
1227 AvahiAddress a;
1228 assert(s);
1229
1230 /* Add localhost entries */
1231 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1232 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a);
1233
1234 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1235 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
1236 }
1237
register_browse_domain(AvahiServer * s)1238 static void register_browse_domain(AvahiServer *s) {
1239 assert(s);
1240
1241 if (!s->config.publish_domain)
1242 return;
1243
1244 if (avahi_domain_equal(s->domain_name, "local"))
1245 return;
1246
1247 if (s->browse_domain_entry_group)
1248 assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1249 else
1250 s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1251
1252 if (!s->browse_domain_entry_group) {
1253 avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1254 return;
1255 }
1256
1257 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) {
1258 avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1259 return;
1260 }
1261
1262 if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1263 avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1264 }
1265
register_stuff(AvahiServer * s)1266 static void register_stuff(AvahiServer *s) {
1267 assert(s);
1268
1269 server_set_state(s, AVAHI_SERVER_REGISTERING);
1270 s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1271
1272 register_hinfo(s);
1273 register_browse_domain(s);
1274 avahi_interface_monitor_update_rrs(s->monitor, 0);
1275
1276 assert(s->n_host_rr_pending > 0);
1277 s->n_host_rr_pending --;
1278
1279 if (s->n_host_rr_pending == 0)
1280 server_set_state(s, AVAHI_SERVER_RUNNING);
1281 }
1282
update_fqdn(AvahiServer * s)1283 static void update_fqdn(AvahiServer *s) {
1284 char *n;
1285
1286 assert(s);
1287 assert(s->host_name);
1288 assert(s->domain_name);
1289
1290 if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1291 return; /* OOM */
1292
1293 avahi_free(s->host_name_fqdn);
1294 s->host_name_fqdn = n;
1295 }
1296
avahi_server_set_host_name(AvahiServer * s,const char * host_name)1297 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1298 char *hn = NULL;
1299 assert(s);
1300
1301 AVAHI_CHECK_VALIDITY(s, !host_name || avahi_is_valid_host_name(host_name), AVAHI_ERR_INVALID_HOST_NAME);
1302
1303 if (!host_name)
1304 hn = avahi_get_host_name_strdup();
1305 else
1306 hn = avahi_normalize_name_strdup(host_name);
1307
1308 hn[strcspn(hn, ".")] = 0;
1309
1310 if (avahi_domain_equal(s->host_name, hn) && s->state != AVAHI_SERVER_COLLISION) {
1311 avahi_free(hn);
1312 return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1313 }
1314
1315 withdraw_host_rrs(s);
1316
1317 avahi_free(s->host_name);
1318 s->host_name = hn;
1319
1320 update_fqdn(s);
1321
1322 register_stuff(s);
1323 return AVAHI_OK;
1324 }
1325
avahi_server_set_domain_name(AvahiServer * s,const char * domain_name)1326 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1327 char *dn = NULL;
1328 assert(s);
1329
1330 AVAHI_CHECK_VALIDITY(s, !domain_name || avahi_is_valid_domain_name(domain_name), AVAHI_ERR_INVALID_DOMAIN_NAME);
1331
1332 if (!domain_name)
1333 dn = avahi_strdup("local");
1334 else
1335 dn = avahi_normalize_name_strdup(domain_name);
1336
1337 if (avahi_domain_equal(s->domain_name, domain_name)) {
1338 avahi_free(dn);
1339 return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1340 }
1341
1342 withdraw_host_rrs(s);
1343
1344 avahi_free(s->domain_name);
1345 s->domain_name = dn;
1346 update_fqdn(s);
1347
1348 register_stuff(s);
1349
1350 avahi_free(dn);
1351 return AVAHI_OK;
1352 }
1353
valid_server_config(const AvahiServerConfig * sc)1354 static int valid_server_config(const AvahiServerConfig *sc) {
1355 AvahiStringList *l;
1356
1357 assert(sc);
1358
1359 if (sc->n_wide_area_servers > AVAHI_WIDE_AREA_SERVERS_MAX)
1360 return AVAHI_ERR_INVALID_CONFIG;
1361
1362 if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1363 return AVAHI_ERR_INVALID_HOST_NAME;
1364
1365 if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1366 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1367
1368 for (l = sc->browse_domains; l; l = l->next)
1369 if (!avahi_is_valid_domain_name((char*) l->text))
1370 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1371
1372 return AVAHI_OK;
1373 }
1374
setup_sockets(AvahiServer * s)1375 static int setup_sockets(AvahiServer *s) {
1376 assert(s);
1377
1378 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
1379 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
1380
1381 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1382 return AVAHI_ERR_NO_NETWORK;
1383
1384 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1385 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1386 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1387 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1388
1389 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1;
1390 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1;
1391
1392 s->watch_ipv4 =
1393 s->watch_ipv6 =
1394 s->watch_legacy_unicast_ipv4 =
1395 s->watch_legacy_unicast_ipv6 = NULL;
1396
1397 if (s->fd_ipv4 >= 0)
1398 s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, mcast_socket_event, s);
1399 if (s->fd_ipv6 >= 0)
1400 s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, mcast_socket_event, s);
1401
1402 if (s->fd_legacy_unicast_ipv4 >= 0)
1403 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);
1404 if (s->fd_legacy_unicast_ipv6 >= 0)
1405 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);
1406
1407 return 0;
1408 }
1409
avahi_server_new(const AvahiPoll * poll_api,const AvahiServerConfig * sc,AvahiServerCallback callback,void * userdata,int * error)1410 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1411 AvahiServer *s;
1412 int e;
1413
1414 if (sc && (e = valid_server_config(sc)) < 0) {
1415 if (error)
1416 *error = e;
1417 return NULL;
1418 }
1419
1420 if (!(s = avahi_new(AvahiServer, 1))) {
1421 if (error)
1422 *error = AVAHI_ERR_NO_MEMORY;
1423
1424 return NULL;
1425 }
1426
1427 s->poll_api = poll_api;
1428
1429 if (sc)
1430 avahi_server_config_copy(&s->config, sc);
1431 else
1432 avahi_server_config_init(&s->config);
1433
1434 if ((e = setup_sockets(s)) < 0) {
1435 if (error)
1436 *error = e;
1437
1438 avahi_server_config_free(&s->config);
1439 avahi_free(s);
1440
1441 return NULL;
1442 }
1443
1444 s->n_host_rr_pending = 0;
1445 s->need_entry_cleanup = 0;
1446 s->need_group_cleanup = 0;
1447 s->need_browser_cleanup = 0;
1448 s->cleanup_time_event = NULL;
1449 s->hinfo_entry_group = NULL;
1450 s->browse_domain_entry_group = NULL;
1451 s->error = AVAHI_OK;
1452 s->state = AVAHI_SERVER_INVALID;
1453
1454 s->callback = callback;
1455 s->userdata = userdata;
1456
1457 s->time_event_queue = avahi_time_event_queue_new(poll_api);
1458
1459 s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1460 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1461 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1462
1463 s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1464 AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1465 AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1466 AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1467 AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1468 AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1469 AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1470 AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1471 AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1472
1473 s->legacy_unicast_reflect_slots = NULL;
1474 s->legacy_unicast_reflect_id = 0;
1475
1476 s->record_list = avahi_record_list_new();
1477
1478 /* Get host name */
1479 s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
1480 s->host_name[strcspn(s->host_name, ".")] = 0;
1481 s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
1482 s->host_name_fqdn = NULL;
1483 update_fqdn(s);
1484
1485 do {
1486 s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1487 } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
1488
1489 if (s->config.enable_wide_area) {
1490 s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
1491 avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1492 } else
1493 s->wide_area_lookup_engine = NULL;
1494
1495 s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
1496
1497 s->monitor = avahi_interface_monitor_new(s);
1498 avahi_interface_monitor_sync(s->monitor);
1499
1500 register_localhost(s);
1501 register_stuff(s);
1502
1503 return s;
1504 }
1505
avahi_server_free(AvahiServer * s)1506 void avahi_server_free(AvahiServer* s) {
1507 assert(s);
1508
1509 /* Remove all browsers */
1510
1511 while (s->dns_server_browsers)
1512 avahi_s_dns_server_browser_free(s->dns_server_browsers);
1513 while (s->host_name_resolvers)
1514 avahi_s_host_name_resolver_free(s->host_name_resolvers);
1515 while (s->address_resolvers)
1516 avahi_s_address_resolver_free(s->address_resolvers);
1517 while (s->domain_browsers)
1518 avahi_s_domain_browser_free(s->domain_browsers);
1519 while (s->service_type_browsers)
1520 avahi_s_service_type_browser_free(s->service_type_browsers);
1521 while (s->service_browsers)
1522 avahi_s_service_browser_free(s->service_browsers);
1523 while (s->service_resolvers)
1524 avahi_s_service_resolver_free(s->service_resolvers);
1525 while (s->record_browsers)
1526 avahi_s_record_browser_destroy(s->record_browsers);
1527
1528 /* Remove all locally rgeistered stuff */
1529
1530 while(s->entries)
1531 avahi_entry_free(s, s->entries);
1532
1533 avahi_interface_monitor_free(s->monitor);
1534
1535 while (s->groups)
1536 avahi_entry_group_free(s, s->groups);
1537
1538 free_slots(s);
1539
1540 avahi_hashmap_free(s->entries_by_key);
1541 avahi_record_list_free(s->record_list);
1542 avahi_hashmap_free(s->record_browser_hashmap);
1543
1544 if (s->wide_area_lookup_engine)
1545 avahi_wide_area_engine_free(s->wide_area_lookup_engine);
1546 avahi_multicast_lookup_engine_free(s->multicast_lookup_engine);
1547
1548 if (s->cleanup_time_event)
1549 avahi_time_event_free(s->cleanup_time_event);
1550
1551 avahi_time_event_queue_free(s->time_event_queue);
1552
1553 /* Free watches */
1554
1555 if (s->watch_ipv4)
1556 s->poll_api->watch_free(s->watch_ipv4);
1557 if (s->watch_ipv6)
1558 s->poll_api->watch_free(s->watch_ipv6);
1559
1560 if (s->watch_legacy_unicast_ipv4)
1561 s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1562 if (s->watch_legacy_unicast_ipv6)
1563 s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1564
1565 /* Free sockets */
1566
1567 if (s->fd_ipv4 >= 0)
1568 close(s->fd_ipv4);
1569 if (s->fd_ipv6 >= 0)
1570 close(s->fd_ipv6);
1571
1572 if (s->fd_legacy_unicast_ipv4 >= 0)
1573 close(s->fd_legacy_unicast_ipv4);
1574 if (s->fd_legacy_unicast_ipv6 >= 0)
1575 close(s->fd_legacy_unicast_ipv6);
1576
1577 /* Free other stuff */
1578
1579 avahi_free(s->host_name);
1580 avahi_free(s->domain_name);
1581 avahi_free(s->host_name_fqdn);
1582
1583 avahi_server_config_free(&s->config);
1584
1585 avahi_free(s);
1586 }
1587
avahi_server_get_domain_name(AvahiServer * s)1588 const char* avahi_server_get_domain_name(AvahiServer *s) {
1589 assert(s);
1590
1591 return s->domain_name;
1592 }
1593
avahi_server_get_host_name(AvahiServer * s)1594 const char* avahi_server_get_host_name(AvahiServer *s) {
1595 assert(s);
1596
1597 return s->host_name;
1598 }
1599
avahi_server_get_host_name_fqdn(AvahiServer * s)1600 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1601 assert(s);
1602
1603 return s->host_name_fqdn;
1604 }
1605
avahi_server_get_data(AvahiServer * s)1606 void* avahi_server_get_data(AvahiServer *s) {
1607 assert(s);
1608
1609 return s->userdata;
1610 }
1611
avahi_server_set_data(AvahiServer * s,void * userdata)1612 void avahi_server_set_data(AvahiServer *s, void* userdata) {
1613 assert(s);
1614
1615 s->userdata = userdata;
1616 }
1617
avahi_server_get_state(AvahiServer * s)1618 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1619 assert(s);
1620
1621 return s->state;
1622 }
1623
avahi_server_config_init(AvahiServerConfig * c)1624 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1625 assert(c);
1626
1627 memset(c, 0, sizeof(AvahiServerConfig));
1628 c->use_ipv6 = 1;
1629 c->use_ipv4 = 1;
1630 c->allow_interfaces = NULL;
1631 c->deny_interfaces = NULL;
1632 c->host_name = NULL;
1633 c->domain_name = NULL;
1634 c->check_response_ttl = 0;
1635 c->publish_hinfo = 0;
1636 c->publish_addresses = 1;
1637 c->publish_workstation = 0;
1638 c->publish_domain = 1;
1639 c->use_iff_running = 0;
1640 c->enable_reflector = 0;
1641 c->reflect_ipv = 0;
1642 c->reflect_filters = NULL;
1643 c->add_service_cookie = 0;
1644 c->enable_wide_area = 0;
1645 c->n_wide_area_servers = 0;
1646 c->disallow_other_stacks = 0;
1647 c->browse_domains = NULL;
1648 c->disable_publishing = 0;
1649 c->allow_point_to_point = 0;
1650 c->publish_aaaa_on_ipv4 = 1;
1651 c->publish_a_on_ipv6 = 0;
1652 c->n_cache_entries_max = AVAHI_DEFAULT_CACHE_ENTRIES_MAX;
1653 c->ratelimit_interval = 0;
1654 c->ratelimit_burst = 0;
1655
1656 return c;
1657 }
1658
avahi_server_config_free(AvahiServerConfig * c)1659 void avahi_server_config_free(AvahiServerConfig *c) {
1660 assert(c);
1661
1662 avahi_free(c->host_name);
1663 avahi_free(c->domain_name);
1664 avahi_string_list_free(c->browse_domains);
1665 avahi_string_list_free(c->reflect_filters);
1666 avahi_string_list_free(c->allow_interfaces);
1667 avahi_string_list_free(c->deny_interfaces);
1668 }
1669
avahi_server_config_copy(AvahiServerConfig * ret,const AvahiServerConfig * c)1670 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1671 char *d = NULL, *h = NULL;
1672 AvahiStringList *browse = NULL, *allow = NULL, *deny = NULL, *reflect = NULL ;
1673 assert(ret);
1674 assert(c);
1675
1676 if (c->host_name)
1677 if (!(h = avahi_strdup(c->host_name)))
1678 return NULL;
1679
1680 if (c->domain_name)
1681 if (!(d = avahi_strdup(c->domain_name))) {
1682 avahi_free(h);
1683 return NULL;
1684 }
1685
1686 if (!(browse = avahi_string_list_copy(c->browse_domains)) && c->browse_domains) {
1687 avahi_free(h);
1688 avahi_free(d);
1689 return NULL;
1690 }
1691
1692 if (!(allow = avahi_string_list_copy(c->allow_interfaces)) && c->allow_interfaces) {
1693 avahi_string_list_free(browse);
1694 avahi_free(h);
1695 avahi_free(d);
1696 return NULL;
1697 }
1698
1699 if (!(deny = avahi_string_list_copy(c->deny_interfaces)) && c->deny_interfaces) {
1700 avahi_string_list_free(allow);
1701 avahi_string_list_free(browse);
1702 avahi_free(h);
1703 avahi_free(d);
1704 return NULL;
1705 }
1706
1707 if (!(reflect = avahi_string_list_copy(c->reflect_filters)) && c->reflect_filters) {
1708 avahi_string_list_free(allow);
1709 avahi_string_list_free(browse);
1710 avahi_string_list_free(deny);
1711 avahi_free(h);
1712 avahi_free(d);
1713 return NULL;
1714 }
1715
1716 *ret = *c;
1717 ret->host_name = h;
1718 ret->domain_name = d;
1719 ret->browse_domains = browse;
1720 ret->allow_interfaces = allow;
1721 ret->deny_interfaces = deny;
1722 ret->reflect_filters = reflect;
1723
1724 return ret;
1725 }
1726
avahi_server_errno(AvahiServer * s)1727 int avahi_server_errno(AvahiServer *s) {
1728 assert(s);
1729
1730 return s->error;
1731 }
1732
1733 /* Just for internal use */
avahi_server_set_errno(AvahiServer * s,int error)1734 int avahi_server_set_errno(AvahiServer *s, int error) {
1735 assert(s);
1736
1737 return s->error = error;
1738 }
1739
avahi_server_get_local_service_cookie(AvahiServer * s)1740 uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
1741 assert(s);
1742
1743 return s->local_service_cookie;
1744 }
1745
find_entry(AvahiServer * s,AvahiIfIndex interface,AvahiProtocol protocol,AvahiKey * key)1746 static AvahiEntry *find_entry(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
1747 AvahiEntry *e;
1748
1749 assert(s);
1750 assert(key);
1751
1752 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
1753
1754 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1755 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1756 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING))
1757
1758 return e;
1759
1760 return NULL;
1761 }
1762
avahi_server_get_group_of_service(AvahiServer * s,AvahiIfIndex interface,AvahiProtocol protocol,const char * name,const char * type,const char * domain,AvahiSEntryGroup ** ret_group)1763 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) {
1764 AvahiKey *key = NULL;
1765 AvahiEntry *e;
1766 int ret;
1767 char n[AVAHI_DOMAIN_NAME_MAX];
1768
1769 assert(s);
1770 assert(name);
1771 assert(type);
1772 assert(ret_group);
1773
1774 AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
1775 AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
1776 AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
1777 AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
1778 AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
1779
1780 if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0))
1781 return avahi_server_set_errno(s, ret);
1782
1783 if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1784 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1785
1786 e = find_entry(s, interface, protocol, key);
1787 avahi_key_unref(key);
1788
1789 if (e) {
1790 *ret_group = e->group;
1791 return AVAHI_OK;
1792 }
1793
1794 return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
1795 }
1796
avahi_server_is_service_local(AvahiServer * s,AvahiIfIndex interface,AvahiProtocol protocol,const char * name)1797 int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name) {
1798 AvahiKey *key = NULL;
1799 AvahiEntry *e;
1800
1801 assert(s);
1802 assert(name);
1803
1804 if (!s->host_name_fqdn)
1805 return 0;
1806
1807 if (!(key = avahi_key_new(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1808 return 0;
1809
1810 e = find_entry(s, interface, protocol, key);
1811 avahi_key_unref(key);
1812
1813 if (!e)
1814 return 0;
1815
1816 return avahi_domain_equal(s->host_name_fqdn, e->record->data.srv.name);
1817 }
1818
avahi_server_is_record_local(AvahiServer * s,AvahiIfIndex interface,AvahiProtocol protocol,AvahiRecord * record)1819 int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record) {
1820 AvahiEntry *e;
1821
1822 assert(s);
1823 assert(record);
1824
1825 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = e->by_key_next)
1826
1827 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1828 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1829 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING) &&
1830 avahi_record_equal_no_ttl(record, e->record))
1831 return 1;
1832
1833 return 0;
1834 }
1835
1836 /** Set the wide area DNS servers */
avahi_server_set_wide_area_servers(AvahiServer * s,const AvahiAddress * a,unsigned n)1837 int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) {
1838 assert(s);
1839
1840 if (!s->wide_area_lookup_engine)
1841 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG);
1842
1843 avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n);
1844 return AVAHI_OK;
1845 }
1846
avahi_server_get_config(AvahiServer * s)1847 const AvahiServerConfig* avahi_server_get_config(AvahiServer *s) {
1848 assert(s);
1849
1850 return &s->config;
1851 }
1852
1853 /** Set the browsing domains */
avahi_server_set_browse_domains(AvahiServer * s,AvahiStringList * domains)1854 int avahi_server_set_browse_domains(AvahiServer *s, AvahiStringList *domains) {
1855 AvahiStringList *l;
1856
1857 assert(s);
1858
1859 for (l = domains; l; l = l->next)
1860 if (!avahi_is_valid_domain_name((char*) l->text))
1861 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1862
1863 avahi_string_list_free(s->config.browse_domains);
1864 s->config.browse_domains = avahi_string_list_copy(domains);
1865
1866 return AVAHI_OK;
1867 }
1868