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