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