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