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