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