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