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