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