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