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