]> git.meshlink.io Git - catta/blob - avahi-core/server.c
Include host in invalid packet messages
[catta] / avahi-core / server.c
1 /* $Id$ */
2
3 /***
4   This file is part of avahi.
5
6   avahi is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as
8   published by the Free Software Foundation; either version 2.1 of the
9   License, or (at your option) any later version.
10
11   avahi is distributed in the hope that it will be useful, but WITHOUT
12   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
14   Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with avahi; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 #include <string.h>
31 #include <sys/utsname.h>
32 #include <unistd.h>
33 #include <errno.h>
34 #include <stdio.h>
35 #include <assert.h>
36 #include <stdlib.h>
37
38 #include <avahi-common/domain.h>
39 #include <avahi-common/timeval.h>
40 #include <avahi-common/malloc.h>
41 #include <avahi-common/error.h>
42
43 #include "internal.h"
44 #include "iface.h"
45 #include "socket.h"
46 #include "browse.h"
47 #include "log.h"
48 #include "util.h"
49 #include "dns-srv-rr.h"
50 #include "addr-util.h"
51 #include "domain-util.h"
52 #include "rr-util.h"
53
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
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
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
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
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
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
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
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
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
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
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
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
514 static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
515     AvahiServer *s = userdata;
516
517     assert(c);
518     assert(pattern);
519     assert(e);
520     assert(s);
521
522     avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
523     return NULL;
524 }
525
526 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
527     AvahiInterface *j;
528
529     assert(s);
530     assert(i);
531     assert(k);
532
533     if (!s->config.enable_reflector)
534         return;
535
536     for (j = s->monitor->interfaces; j; j = j->interface_next)
537         if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
538             /* Post the query to other networks */
539             avahi_interface_post_query(j, k, 1, NULL);
540
541             /* Reply from caches of other network. This is needed to
542              * "work around" known answer suppression. */
543
544             avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
545         }
546 }
547
548 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
549     AvahiInterface *j;
550
551     assert(s);
552     assert(i);
553     assert(r);
554
555     if (!s->config.enable_reflector)
556         return;
557
558     for (j = s->monitor->interfaces; j; j = j->interface_next)
559         if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
560             avahi_interface_post_probe(j, r, 1);
561 }
562
563 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) {
564     size_t n;
565     int is_probe;
566
567     assert(s);
568     assert(p);
569     assert(i);
570     assert(a);
571
572     assert(avahi_record_list_is_empty(s->record_list));
573
574     is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
575
576     /* Handle the questions */
577     for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
578         AvahiKey *key;
579         int unicast_response = 0;
580
581         if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
582             avahi_log_warn(__FILE__": Packet too short or invalid while reading question key. (Maybe a UTF-8 problem?)");
583             goto fail;
584         }
585
586         if (!legacy_unicast && !from_local_iface) {
587             reflect_query(s, i, key);
588             if (!unicast_response)
589               avahi_cache_start_poof(i->cache, key, a);
590         }
591
592         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
593             !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
594             /* Allow our own queries to be suppressed by incoming
595              * queries only when they do not include known answers */
596             avahi_query_scheduler_incoming(i->query_scheduler, key);
597
598         avahi_server_prepare_matching_responses(s, i, key, unicast_response);
599         avahi_key_unref(key);
600     }
601
602     if (!legacy_unicast) {
603
604         /* Known Answer Suppression */
605         for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
606             AvahiRecord *record;
607             int unique = 0;
608
609             if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
610                 avahi_log_warn(__FILE__": Packet too short or invalid while reading known answer record. (Maybe a UTF-8 problem?)");
611                 goto fail;
612             }
613
614             avahi_response_scheduler_suppress(i->response_scheduler, record, a);
615             avahi_record_list_drop(s->record_list, record);
616             avahi_cache_stop_poof(i->cache, record, a);
617
618             avahi_record_unref(record);
619         }
620
621         /* Probe record */
622         for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
623             AvahiRecord *record;
624             int unique = 0;
625
626             if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
627                 avahi_log_warn(__FILE__": Packet too short or invalid while reading probe record. (Maybe a UTF-8 problem?)");
628                 goto fail;
629             }
630
631             if (!avahi_key_is_pattern(record->key)) {
632                 if (!from_local_iface)
633                     reflect_probe(s, i, record);
634                 incoming_probe(s, record, i);
635             }
636
637             avahi_record_unref(record);
638         }
639     }
640
641     if (!avahi_record_list_is_empty(s->record_list))
642         avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
643
644     return;
645
646 fail:
647     avahi_record_list_flush(s->record_list);
648 }
649
650 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
651     unsigned n;
652
653     assert(s);
654     assert(p);
655     assert(i);
656     assert(a);
657
658     for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
659              avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
660         AvahiRecord *record;
661         int cache_flush = 0;
662 /*         char *txt; */
663
664         if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
665             avahi_log_warn(__FILE__": Packet too short or invalid while reading response record. (Maybe a UTF-8 problem?)");
666             break;
667         }
668
669         if (!avahi_key_is_pattern(record->key)) {
670
671             if (handle_conflict(s, i, record, cache_flush)) {
672                 if (!from_local_iface)
673                     reflect_response(s, i, record, cache_flush);
674                 avahi_cache_update(i->cache, record, cache_flush, a);
675                 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
676             }
677         }
678
679         avahi_record_unref(record);
680     }
681
682     /* If the incoming response contained a conflicting record, some
683        records have been scheduling for sending. We need to flush them
684        here. */
685     if (!avahi_record_list_is_empty(s->record_list))
686         avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
687 }
688
689 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
690     unsigned n, idx = (unsigned) -1;
691     AvahiLegacyUnicastReflectSlot *slot;
692
693     assert(s);
694
695     if (!s->legacy_unicast_reflect_slots)
696         s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX);
697
698     for (n = 0; n < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; n++, s->legacy_unicast_reflect_id++) {
699         idx = s->legacy_unicast_reflect_id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
700
701         if (!s->legacy_unicast_reflect_slots[idx])
702             break;
703     }
704
705     if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
706         return NULL;
707
708     if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
709         return NULL; /* OOM */
710
711     s->legacy_unicast_reflect_slots[idx] = slot;
712     slot->id = s->legacy_unicast_reflect_id++;
713     slot->server = s;
714
715     return slot;
716 }
717
718 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
719     unsigned idx;
720
721     assert(s);
722     assert(slot);
723
724     idx = slot->id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
725
726     assert(s->legacy_unicast_reflect_slots[idx] == slot);
727
728     avahi_time_event_free(slot->time_event);
729
730     avahi_free(slot);
731     s->legacy_unicast_reflect_slots[idx] = NULL;
732 }
733
734 static void free_slots(AvahiServer *s) {
735     unsigned idx;
736     assert(s);
737
738     if (!s->legacy_unicast_reflect_slots)
739         return;
740
741     for (idx = 0; idx < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; idx ++)
742         if (s->legacy_unicast_reflect_slots[idx])
743             deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
744
745     avahi_free(s->legacy_unicast_reflect_slots);
746     s->legacy_unicast_reflect_slots = NULL;
747 }
748
749 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
750     unsigned idx;
751
752     assert(s);
753
754     if (!s->legacy_unicast_reflect_slots)
755         return NULL;
756
757     idx = id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
758
759     if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
760         return NULL;
761
762     return s->legacy_unicast_reflect_slots[idx];
763 }
764
765 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
766     AvahiLegacyUnicastReflectSlot *slot = userdata;
767
768     assert(e);
769     assert(slot);
770     assert(slot->time_event == e);
771
772     deallocate_slot(slot->server, slot);
773 }
774
775 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
776     AvahiLegacyUnicastReflectSlot *slot;
777     AvahiInterface *j;
778
779     assert(s);
780     assert(p);
781     assert(i);
782     assert(a);
783     assert(port > 0);
784     assert(i->protocol == a->proto);
785
786     if (!s->config.enable_reflector)
787         return;
788
789     /* Reflecting legacy unicast queries is a little more complicated
790        than reflecting normal queries, since we must route the
791        responses back to the right client. Therefore we must store
792        some information for finding the right client contact data for
793        response packets. In contrast to normal queries legacy
794        unicast query and response packets are reflected untouched and
795        are not reassembled into larger packets */
796
797     if (!(slot = allocate_slot(s))) {
798         /* No slot available, we drop this legacy unicast query */
799         avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
800         return;
801     }
802
803     slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
804     slot->address = *a;
805     slot->port = port;
806     slot->interface = i->hardware->index;
807
808     avahi_elapse_time(&slot->elapse_time, 2000, 0);
809     slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
810
811     /* Patch the packet with our new locally generatet id */
812     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
813
814     for (j = s->monitor->interfaces; j; j = j->interface_next)
815         if (j->announcing &&
816             j != i &&
817             (s->config.reflect_ipv || j->protocol == i->protocol)) {
818
819             if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
820                 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, NULL, 0);
821             } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
822                 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, NULL, 0);
823         }
824
825     /* Reset the id */
826     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
827 }
828
829 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const AvahiAddress *address, uint16_t port) {
830     assert(s);
831     assert(address);
832     assert(port > 0);
833
834     if (!s->config.enable_reflector)
835         return 0;
836
837     if (!avahi_address_is_local(s->monitor, address))
838         return 0;
839
840     if (address->proto == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
841         struct sockaddr_in lsa;
842         socklen_t l = sizeof(lsa);
843
844         if (getsockname(s->fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0)
845             avahi_log_warn("getsockname(): %s", strerror(errno));
846         else
847             return lsa.sin_port == port;
848
849     }
850
851     if (address->proto == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
852         struct sockaddr_in6 lsa;
853         socklen_t l = sizeof(lsa);
854
855         if (getsockname(s->fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0)
856             avahi_log_warn("getsockname(): %s", strerror(errno));
857         else
858             return lsa.sin6_port == port;
859     }
860
861     return 0;
862 }
863
864 static int is_mdns_mcast_address(const AvahiAddress *a) {
865     AvahiAddress b;
866     assert(a);
867
868     avahi_address_parse(a->proto == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->proto, &b);
869     return avahi_address_cmp(a, &b) == 0;
870 }
871
872 static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) {
873     assert(s);
874     assert(iface != AVAHI_IF_UNSPEC);
875     assert(a);
876
877     /* If it isn't the MDNS port it can't be generated by us */
878     if (port != AVAHI_MDNS_PORT)
879         return 0;
880
881     return avahi_interface_has_address(s->monitor, iface, a);
882 }
883
884 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const AvahiAddress *src_address, uint16_t port, const AvahiAddress *dst_address, AvahiIfIndex iface, int ttl) {
885     AvahiInterface *i;
886     int from_local_iface = 0;
887
888     assert(s);
889     assert(p);
890     assert(src_address);
891     assert(dst_address);
892     assert(iface > 0);
893     assert(src_address->proto == dst_address->proto);
894
895     if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, src_address->proto)) ||
896         !i->announcing) {
897         avahi_log_warn("Received packet from invalid interface.");
898         return;
899     }
900
901     if (avahi_address_is_ipv4_in_ipv6(src_address))
902         /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
903         return;
904
905     if (originates_from_local_legacy_unicast_socket(s, src_address, port))
906         /* This originates from our local reflector, so let's ignore it */
907         return;
908
909     /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
910     if (s->config.enable_reflector)
911         from_local_iface = originates_from_local_iface(s, iface, src_address, port);
912
913     if (avahi_dns_packet_check_valid_multicast(p) < 0) {
914         avahi_log_warn("Received invalid packet.");
915         return;
916     }
917
918     if (avahi_dns_packet_is_query(p)) {
919         int legacy_unicast = 0;
920
921         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
922             avahi_log_warn("Invalid query packet.");
923             return;
924         }
925
926         if (port != AVAHI_MDNS_PORT) {
927             /* Legacy Unicast */
928
929             if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
930                  avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
931                 avahi_log_warn("Invalid legacy unicast query packet.");
932                 return;
933             }
934
935             legacy_unicast = 1;
936         }
937
938         if (legacy_unicast)
939             reflect_legacy_unicast_query_packet(s, p, i, src_address, port);
940
941         handle_query_packet(s, p, i, src_address, port, legacy_unicast, from_local_iface);
942
943     } else {
944         char t[AVAHI_ADDRESS_STR_MAX];
945
946         if (port != AVAHI_MDNS_PORT) {
947             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);
948             return;
949         }
950
951         if (ttl != 255 && s->config.check_response_ttl) {
952             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);
953             return;
954         }
955
956         if (!is_mdns_mcast_address(dst_address) &&
957             !avahi_interface_address_on_link(i, src_address)) {
958
959             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);
960             return;
961         }
962
963         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
964             avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
965             avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
966
967             avahi_log_warn("Invalid response packet from host %s.", avahi_address_snprint(t, sizeof(t), src_address));
968             return;
969         }
970
971         handle_response_packet(s, p, i, src_address, from_local_iface);
972     }
973 }
974
975 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p) {
976     AvahiInterface *j;
977     AvahiLegacyUnicastReflectSlot *slot;
978
979     assert(s);
980     assert(p);
981
982     if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
983         avahi_log_warn("Received invalid packet.");
984         return;
985     }
986
987     if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
988         avahi_log_warn("Received legacy unicast response with unknown id");
989         return;
990     }
991
992     if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
993         !j->announcing)
994         return;
995
996     /* Patch the original ID into this response */
997     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
998
999     /* Forward the response to the correct client */
1000     avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
1001
1002     /* Undo changes to packet */
1003     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1004 }
1005
1006 static void cleanup_dead(AvahiServer *s) {
1007     assert(s);
1008
1009     avahi_cleanup_dead_entries(s);
1010     avahi_browser_cleanup(s);
1011 }
1012
1013 static void mcast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1014     AvahiServer *s = userdata;
1015     AvahiAddress dest, src;
1016     AvahiDnsPacket *p = NULL;
1017     AvahiIfIndex iface;
1018     uint16_t port;
1019     uint8_t ttl;
1020
1021     assert(w);
1022     assert(fd >= 0);
1023     assert(events & AVAHI_WATCH_IN);
1024
1025     if (fd == s->fd_ipv4) {
1026         dest.proto = src.proto = AVAHI_PROTO_INET;
1027         p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &src.data.ipv4, &port, &dest.data.ipv4, &iface, &ttl);
1028     } else {
1029         assert(fd == s->fd_ipv6);
1030         dest.proto = src.proto = AVAHI_PROTO_INET6;
1031         p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &src.data.ipv6, &port, &dest.data.ipv6, &iface, &ttl);
1032     }
1033
1034     if (p) {
1035         if (iface == AVAHI_IF_UNSPEC)
1036             iface = avahi_find_interface_for_address(s->monitor, &dest);
1037
1038         if (iface != AVAHI_IF_UNSPEC)
1039             dispatch_packet(s, p, &src, port, &dest, iface, ttl);
1040         else
1041             avahi_log_error("Incoming packet recieved on address that isn't local.");
1042
1043         avahi_dns_packet_free(p);
1044
1045         cleanup_dead(s);
1046     }
1047 }
1048
1049 static void legacy_unicast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1050     AvahiServer *s = userdata;
1051     AvahiDnsPacket *p = NULL;
1052
1053     assert(w);
1054     assert(fd >= 0);
1055     assert(events & AVAHI_WATCH_IN);
1056
1057     if (fd == s->fd_legacy_unicast_ipv4)
1058         p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, NULL, NULL, NULL, NULL, NULL);
1059     else {
1060         assert(fd == s->fd_legacy_unicast_ipv6);
1061         p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, NULL, NULL, NULL, NULL, NULL);
1062     }
1063
1064     if (p) {
1065         dispatch_legacy_unicast_packet(s, p);
1066         avahi_dns_packet_free(p);
1067
1068         cleanup_dead(s);
1069     }
1070 }
1071
1072 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1073     assert(s);
1074
1075     if (s->state == state)
1076         return;
1077
1078     s->state = state;
1079
1080     avahi_interface_monitor_update_rrs(s->monitor, 0);
1081
1082     if (s->callback)
1083         s->callback(s, state, s->userdata);
1084 }
1085
1086 static void withdraw_host_rrs(AvahiServer *s) {
1087     assert(s);
1088
1089     if (s->hinfo_entry_group)
1090         avahi_s_entry_group_reset(s->hinfo_entry_group);
1091
1092     if (s->browse_domain_entry_group)
1093         avahi_s_entry_group_reset(s->browse_domain_entry_group);
1094
1095     avahi_interface_monitor_update_rrs(s->monitor, 1);
1096     s->n_host_rr_pending = 0;
1097 }
1098
1099 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1100     assert(s);
1101
1102     assert(s->n_host_rr_pending > 0);
1103
1104     if (--s->n_host_rr_pending == 0)
1105         server_set_state(s, AVAHI_SERVER_RUNNING);
1106 }
1107
1108 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
1109     assert(s);
1110     assert(g);
1111
1112     if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1113         s->state == AVAHI_SERVER_REGISTERING)
1114         s->n_host_rr_pending ++;
1115
1116     else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1117         (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1118         withdraw_host_rrs(s);
1119         server_set_state(s, AVAHI_SERVER_COLLISION);
1120
1121     } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1122                s->state == AVAHI_SERVER_REGISTERING)
1123         avahi_server_decrease_host_rr_pending(s);
1124 }
1125
1126 static void register_hinfo(AvahiServer *s) {
1127     struct utsname utsname;
1128     AvahiRecord *r;
1129
1130     assert(s);
1131
1132     if (!s->config.publish_hinfo)
1133         return;
1134
1135     if (s->hinfo_entry_group)
1136         assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1137     else
1138         s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1139
1140     if (!s->hinfo_entry_group) {
1141         avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1142         return;
1143     }
1144
1145     /* Fill in HINFO rr */
1146     if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1147
1148         if (uname(&utsname) < 0)
1149             avahi_log_warn("uname() failed: %s\n", avahi_strerror(errno));
1150         else {
1151
1152             r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1153             r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1154
1155             avahi_log_info("Registering HINFO record with values '%s'/'%s'.", r->data.hinfo.cpu, r->data.hinfo.os);
1156
1157             if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
1158                 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1159                 return;
1160             }
1161         }
1162
1163         avahi_record_unref(r);
1164     }
1165
1166     if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1167         avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1168
1169 }
1170
1171 static void register_localhost(AvahiServer *s) {
1172     AvahiAddress a;
1173     assert(s);
1174
1175     /* Add localhost entries */
1176     avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1177     avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a);
1178
1179     avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1180     avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
1181 }
1182
1183 static void register_browse_domain(AvahiServer *s) {
1184     assert(s);
1185
1186     if (!s->config.publish_domain)
1187         return;
1188
1189     if (avahi_domain_equal(s->domain_name, "local"))
1190         return;
1191
1192     if (s->browse_domain_entry_group)
1193         assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1194     else
1195         s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1196
1197     if (!s->browse_domain_entry_group) {
1198         avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1199         return;
1200     }
1201
1202     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) {
1203         avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1204         return;
1205     }
1206
1207     if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1208         avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1209 }
1210
1211 static void register_stuff(AvahiServer *s) {
1212     assert(s);
1213
1214     server_set_state(s, AVAHI_SERVER_REGISTERING);
1215     s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1216
1217     register_hinfo(s);
1218     register_browse_domain(s);
1219     avahi_interface_monitor_update_rrs(s->monitor, 0);
1220
1221     s->n_host_rr_pending --;
1222
1223     if (s->n_host_rr_pending == 0)
1224         server_set_state(s, AVAHI_SERVER_RUNNING);
1225 }
1226
1227 static void update_fqdn(AvahiServer *s) {
1228     char *n;
1229
1230     assert(s);
1231     assert(s->host_name);
1232     assert(s->domain_name);
1233
1234     if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1235         return; /* OOM */
1236
1237     avahi_free(s->host_name_fqdn);
1238     s->host_name_fqdn = n;
1239 }
1240
1241 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1242     char *hn = NULL;
1243     assert(s);
1244
1245     AVAHI_CHECK_VALIDITY(s, !host_name || avahi_is_valid_host_name(host_name), AVAHI_ERR_INVALID_HOST_NAME);
1246
1247     if (!host_name) {
1248         hn = avahi_get_host_name_strdup();
1249         hn[strcspn(hn, ".")] = 0;
1250         host_name = hn;
1251     }
1252
1253     if (avahi_domain_equal(s->host_name, host_name) && s->state != AVAHI_SERVER_COLLISION) {
1254         avahi_free(hn);
1255         return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1256     }
1257
1258     withdraw_host_rrs(s);
1259
1260     avahi_free(s->host_name);
1261     s->host_name = hn ? hn : avahi_strdup(host_name);
1262
1263     update_fqdn(s);
1264
1265     register_stuff(s);
1266     return AVAHI_OK;
1267 }
1268
1269 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1270     char *dn = NULL;
1271     assert(s);
1272
1273     AVAHI_CHECK_VALIDITY(s, !domain_name || avahi_is_valid_domain_name(domain_name), AVAHI_ERR_INVALID_DOMAIN_NAME);
1274
1275     if (!domain_name) {
1276         dn = avahi_strdup("local");
1277         domain_name = dn;
1278     }
1279
1280     if (avahi_domain_equal(s->domain_name, domain_name)) {
1281         avahi_free(dn);
1282         return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1283     }
1284
1285     withdraw_host_rrs(s);
1286
1287     avahi_free(s->domain_name);
1288     s->domain_name = avahi_normalize_name_strdup(domain_name);
1289     update_fqdn(s);
1290
1291     register_stuff(s);
1292
1293     avahi_free(dn);
1294     return AVAHI_OK;
1295 }
1296
1297 static int valid_server_config(const AvahiServerConfig *sc) {
1298     AvahiStringList *l;
1299
1300     assert(sc);
1301
1302     if (sc->n_wide_area_servers > AVAHI_WIDE_AREA_SERVERS_MAX)
1303         return AVAHI_ERR_INVALID_CONFIG;
1304
1305     if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1306         return AVAHI_ERR_INVALID_HOST_NAME;
1307
1308     if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1309         return AVAHI_ERR_INVALID_DOMAIN_NAME;
1310
1311     for (l = sc->browse_domains; l; l = l->next)
1312         if (!avahi_is_valid_domain_name((char*) l->text))
1313             return AVAHI_ERR_INVALID_DOMAIN_NAME;
1314
1315     return AVAHI_OK;
1316 }
1317
1318 static int setup_sockets(AvahiServer *s) {
1319     assert(s);
1320
1321     s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
1322     s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
1323
1324     if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1325         return AVAHI_ERR_NO_NETWORK;
1326
1327     if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1328         avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1329     else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1330         avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1331
1332     s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1;
1333     s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1;
1334
1335     s->watch_ipv4 =
1336         s->watch_ipv6 =
1337         s->watch_legacy_unicast_ipv4 =
1338         s->watch_legacy_unicast_ipv6 = NULL;
1339
1340     if (s->fd_ipv4 >= 0)
1341         s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, mcast_socket_event, s);
1342     if (s->fd_ipv6 >= 0)
1343         s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, mcast_socket_event, s);
1344
1345     if (s->fd_legacy_unicast_ipv4 >= 0)
1346         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);
1347     if (s->fd_legacy_unicast_ipv6 >= 0)
1348         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);
1349
1350     return 0;
1351 }
1352
1353 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1354     AvahiServer *s;
1355     int e;
1356
1357     if (sc && (e = valid_server_config(sc)) < 0) {
1358         if (error)
1359             *error = e;
1360         return NULL;
1361     }
1362
1363     if (!(s = avahi_new(AvahiServer, 1))) {
1364         if (error)
1365             *error = AVAHI_ERR_NO_MEMORY;
1366
1367         return NULL;
1368     }
1369
1370     s->poll_api = poll_api;
1371
1372     if (sc)
1373         avahi_server_config_copy(&s->config, sc);
1374     else
1375         avahi_server_config_init(&s->config);
1376
1377     if ((e = setup_sockets(s)) < 0) {
1378         if (error)
1379             *error = e;
1380
1381         avahi_server_config_free(&s->config);
1382         avahi_free(s);
1383
1384         return NULL;
1385     }
1386
1387     s->n_host_rr_pending = 0;
1388     s->need_entry_cleanup = 0;
1389     s->need_group_cleanup = 0;
1390     s->need_browser_cleanup = 0;
1391     s->hinfo_entry_group = NULL;
1392     s->browse_domain_entry_group = NULL;
1393     s->error = AVAHI_OK;
1394     s->state = AVAHI_SERVER_INVALID;
1395
1396     s->callback = callback;
1397     s->userdata = userdata;
1398
1399     s->time_event_queue = avahi_time_event_queue_new(poll_api);
1400
1401     s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1402     AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1403     AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1404
1405     s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1406     AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1407     AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1408     AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1409     AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1410     AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1411     AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1412     AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1413     AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1414
1415     s->legacy_unicast_reflect_slots = NULL;
1416     s->legacy_unicast_reflect_id = 0;
1417
1418     s->record_list = avahi_record_list_new();
1419
1420     /* Get host name */
1421     s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
1422     s->host_name[strcspn(s->host_name, ".")] = 0;
1423     s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
1424     s->host_name_fqdn = NULL;
1425     update_fqdn(s);
1426
1427     do {
1428         s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1429     } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
1430
1431     if (s->config.enable_wide_area) {
1432         s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
1433         avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1434     } else
1435         s->wide_area_lookup_engine = NULL;
1436
1437     s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
1438
1439     s->monitor = avahi_interface_monitor_new(s);
1440     avahi_interface_monitor_sync(s->monitor);
1441
1442     register_localhost(s);
1443     register_stuff(s);
1444
1445     return s;
1446 }
1447
1448 void avahi_server_free(AvahiServer* s) {
1449     assert(s);
1450
1451     /* Remove all browsers */
1452
1453     while (s->dns_server_browsers)
1454         avahi_s_dns_server_browser_free(s->dns_server_browsers);
1455     while (s->host_name_resolvers)
1456         avahi_s_host_name_resolver_free(s->host_name_resolvers);
1457     while (s->address_resolvers)
1458         avahi_s_address_resolver_free(s->address_resolvers);
1459     while (s->domain_browsers)
1460         avahi_s_domain_browser_free(s->domain_browsers);
1461     while (s->service_type_browsers)
1462         avahi_s_service_type_browser_free(s->service_type_browsers);
1463     while (s->service_browsers)
1464         avahi_s_service_browser_free(s->service_browsers);
1465     while (s->service_resolvers)
1466         avahi_s_service_resolver_free(s->service_resolvers);
1467     while (s->record_browsers)
1468         avahi_s_record_browser_destroy(s->record_browsers);
1469
1470     /* Remove all locally rgeistered stuff */
1471
1472     while(s->entries)
1473         avahi_entry_free(s, s->entries);
1474
1475     avahi_interface_monitor_free(s->monitor);
1476
1477     while (s->groups)
1478         avahi_entry_group_free(s, s->groups);
1479
1480     free_slots(s);
1481
1482     avahi_hashmap_free(s->entries_by_key);
1483     avahi_record_list_free(s->record_list);
1484     avahi_hashmap_free(s->record_browser_hashmap);
1485
1486     if (s->wide_area_lookup_engine)
1487         avahi_wide_area_engine_free(s->wide_area_lookup_engine);
1488     avahi_multicast_lookup_engine_free(s->multicast_lookup_engine);
1489
1490     avahi_time_event_queue_free(s->time_event_queue);
1491
1492     /* Free watches */
1493
1494     if (s->watch_ipv4)
1495         s->poll_api->watch_free(s->watch_ipv4);
1496     if (s->watch_ipv6)
1497         s->poll_api->watch_free(s->watch_ipv6);
1498
1499     if (s->watch_legacy_unicast_ipv4)
1500         s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1501     if (s->watch_legacy_unicast_ipv6)
1502         s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1503
1504     /* Free sockets */
1505
1506     if (s->fd_ipv4 >= 0)
1507         close(s->fd_ipv4);
1508     if (s->fd_ipv6 >= 0)
1509         close(s->fd_ipv6);
1510
1511     if (s->fd_legacy_unicast_ipv4 >= 0)
1512         close(s->fd_legacy_unicast_ipv4);
1513     if (s->fd_legacy_unicast_ipv6 >= 0)
1514         close(s->fd_legacy_unicast_ipv6);
1515
1516     /* Free other stuff */
1517
1518     avahi_free(s->host_name);
1519     avahi_free(s->domain_name);
1520     avahi_free(s->host_name_fqdn);
1521
1522     avahi_server_config_free(&s->config);
1523
1524     avahi_free(s);
1525 }
1526
1527 const char* avahi_server_get_domain_name(AvahiServer *s) {
1528     assert(s);
1529
1530     return s->domain_name;
1531 }
1532
1533 const char* avahi_server_get_host_name(AvahiServer *s) {
1534     assert(s);
1535
1536     return s->host_name;
1537 }
1538
1539 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1540     assert(s);
1541
1542     return s->host_name_fqdn;
1543 }
1544
1545 void* avahi_server_get_data(AvahiServer *s) {
1546     assert(s);
1547
1548     return s->userdata;
1549 }
1550
1551 void avahi_server_set_data(AvahiServer *s, void* userdata) {
1552     assert(s);
1553
1554     s->userdata = userdata;
1555 }
1556
1557 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1558     assert(s);
1559
1560     return s->state;
1561 }
1562
1563 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1564     assert(c);
1565
1566     memset(c, 0, sizeof(AvahiServerConfig));
1567     c->use_ipv6 = 1;
1568     c->use_ipv4 = 1;
1569     c->host_name = NULL;
1570     c->domain_name = NULL;
1571     c->check_response_ttl = 0;
1572     c->publish_hinfo = 1;
1573     c->publish_addresses = 1;
1574     c->publish_workstation = 1;
1575     c->publish_domain = 1;
1576     c->use_iff_running = 0;
1577     c->enable_reflector = 0;
1578     c->reflect_ipv = 0;
1579     c->add_service_cookie = 0;
1580     c->enable_wide_area = 0;
1581     c->n_wide_area_servers = 0;
1582     c->disallow_other_stacks = 0;
1583     c->browse_domains = NULL;
1584     c->disable_publishing = 0;
1585     c->allow_point_to_point = 0;
1586     c->publish_aaaa_on_ipv4 = 1;
1587     c->publish_a_on_ipv6 = 0;
1588
1589     return c;
1590 }
1591
1592 void avahi_server_config_free(AvahiServerConfig *c) {
1593     assert(c);
1594
1595     avahi_free(c->host_name);
1596     avahi_free(c->domain_name);
1597     avahi_string_list_free(c->browse_domains);
1598 }
1599
1600 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1601     char *d = NULL, *h = NULL;
1602     AvahiStringList *l = NULL;
1603     assert(ret);
1604     assert(c);
1605
1606     if (c->host_name)
1607         if (!(h = avahi_strdup(c->host_name)))
1608             return NULL;
1609
1610     if (c->domain_name)
1611         if (!(d = avahi_strdup(c->domain_name))) {
1612             avahi_free(h);
1613             return NULL;
1614         }
1615
1616     if (!(l = avahi_string_list_copy(c->browse_domains)) && c->browse_domains) {
1617         avahi_free(h);
1618         avahi_free(d);
1619         return NULL;
1620     }
1621
1622     *ret = *c;
1623     ret->host_name = h;
1624     ret->domain_name = d;
1625     ret->browse_domains = l;
1626
1627     return ret;
1628 }
1629
1630 int avahi_server_errno(AvahiServer *s) {
1631     assert(s);
1632
1633     return s->error;
1634 }
1635
1636 /* Just for internal use */
1637 int avahi_server_set_errno(AvahiServer *s, int error) {
1638     assert(s);
1639
1640     return s->error = error;
1641 }
1642
1643 uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
1644     assert(s);
1645
1646     return s->local_service_cookie;
1647 }
1648
1649 static AvahiEntry *find_entry(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
1650     AvahiEntry *e;
1651
1652     assert(s);
1653     assert(key);
1654
1655     for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
1656
1657         if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1658             (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1659             (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING))
1660
1661             return e;
1662
1663     return NULL;
1664 }
1665
1666 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) {
1667     AvahiKey *key = NULL;
1668     AvahiEntry *e;
1669     int ret;
1670     char n[AVAHI_DOMAIN_NAME_MAX];
1671
1672     assert(s);
1673     assert(name);
1674     assert(type);
1675     assert(ret_group);
1676
1677     AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
1678     AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
1679     AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
1680     AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
1681     AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
1682
1683     if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0))
1684         return avahi_server_set_errno(s, ret);
1685
1686     if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1687         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1688
1689     e = find_entry(s, interface, protocol, key);
1690     avahi_key_unref(key);
1691
1692     if (e) {
1693         *ret_group = e->group;
1694         return AVAHI_OK;
1695     }
1696
1697     return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
1698 }
1699
1700 int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name) {
1701     AvahiKey *key = NULL;
1702     AvahiEntry *e;
1703
1704     assert(s);
1705     assert(name);
1706
1707     if (!s->host_name_fqdn)
1708         return 0;
1709
1710     if (!(key = avahi_key_new(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1711         return 0;
1712
1713     e = find_entry(s, interface, protocol, key);
1714     avahi_key_unref(key);
1715
1716     if (!e)
1717         return 0;
1718
1719     return avahi_domain_equal(s->host_name_fqdn, e->record->data.srv.name);
1720 }
1721
1722 int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record) {
1723     AvahiEntry *e;
1724
1725     assert(s);
1726     assert(record);
1727
1728     for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = e->by_key_next)
1729
1730         if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1731             (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1732             (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING) &&
1733             avahi_record_equal_no_ttl(record, e->record))
1734             return 1;
1735
1736     return 0;
1737 }
1738
1739 /** Set the wide area DNS servers */
1740 int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) {
1741     assert(s);
1742
1743     if (!s->wide_area_lookup_engine)
1744         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG);
1745
1746     avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n);
1747     return AVAHI_OK;
1748 }
1749
1750 const AvahiServerConfig* avahi_server_get_config(AvahiServer *s) {
1751     assert(s);
1752
1753     return &s->config;
1754 }
1755
1756 /** Set the browsing domains */
1757 int avahi_server_set_browse_domains(AvahiServer *s, AvahiStringList *domains) {
1758     AvahiStringList *l;
1759
1760     assert(s);
1761
1762     for (l = s->config.browse_domains; l; l = l->next)
1763         if (!avahi_is_valid_domain_name((char*) l->text))
1764             return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1765
1766     avahi_string_list_free(s->config.browse_domains);
1767     s->config.browse_domains = avahi_string_list_copy(domains);
1768
1769     return AVAHI_OK;
1770 }