]> git.meshlink.io Git - catta/blob - avahi-core/server.c
Fix registration of really large records, like it is necessary for XEP-0174 (jabber...
[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 + AVAHI_DNS_PACKET_EXTRA_SIZE /* 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                         break;
442                     }
443
444                     if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
445                         size_t size;
446
447                         /* The record is too large for one packet, so create a larger packet */
448
449                         avahi_dns_packet_free(reply);
450                         size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
451
452                         if (!(reply = avahi_dns_packet_new_reply(p, size + AVAHI_DNS_PACKET_EXTRA_SIZE, 0, 1)))
453                             break; /* OOM */
454
455                         if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
456
457                             /* Appending this record succeeded, so incremeant
458                              * the specific header field, and return to the caller */
459                             
460                             avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
461                             break;
462
463                         }  else {
464
465                             /* We completely fucked up, there's
466                              * nothing we can do. The RR just doesn't
467                              * fit in. Let's ignore it. */
468                             
469                             char *t;
470                             avahi_dns_packet_free(reply);
471                             reply = NULL;
472                             t = avahi_record_to_string(r);
473                             avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
474                             avahi_free(t);
475                             break;
476                         }
477                     }
478
479                     /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
480                     avahi_interface_send_packet_unicast(i, reply, a, port);
481                     avahi_dns_packet_free(reply);
482                     reply = NULL;
483                 }
484             }
485
486             avahi_record_unref(r);
487         }
488
489         if (reply) {
490             if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0) 
491                 avahi_interface_send_packet_unicast(i, reply, a, port);
492             avahi_dns_packet_free(reply);
493         }
494     }
495
496     avahi_record_list_flush(s->record_list);
497 }
498
499 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) {
500     AvahiInterface *j;
501     
502     assert(s);
503     assert(i);
504     assert(r);
505
506     if (!s->config.enable_reflector)
507         return;
508
509     for (j = s->monitor->interfaces; j; j = j->interface_next)
510         if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
511             avahi_interface_post_response(j, r, flush_cache, NULL, 1);
512 }
513
514 static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
515     AvahiServer *s = userdata;
516
517     assert(c);
518     assert(pattern);
519     assert(e);
520     assert(s);
521
522     avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
523     return NULL;
524 }
525
526 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
527     AvahiInterface *j;
528     
529     assert(s);
530     assert(i);
531     assert(k);
532
533     if (!s->config.enable_reflector)
534         return;
535
536     for (j = s->monitor->interfaces; j; j = j->interface_next)
537         if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
538             /* Post the query to other networks */
539             avahi_interface_post_query(j, k, 1, NULL);
540
541             /* Reply from caches of other network. This is needed to
542              * "work around" known answer suppression. */
543
544             avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
545         }
546 }
547
548 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
549     AvahiInterface *j;
550     
551     assert(s);
552     assert(i);
553     assert(r);
554
555     if (!s->config.enable_reflector)
556         return;
557
558     for (j = s->monitor->interfaces; j; j = j->interface_next)
559         if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
560             avahi_interface_post_probe(j, r, 1);
561 }
562
563 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) {
564     size_t n;
565     int is_probe;
566     
567     assert(s);
568     assert(p);
569     assert(i);
570     assert(a);
571
572     assert(avahi_record_list_is_empty(s->record_list));
573
574     is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
575     
576     /* Handle the questions */
577     for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
578         AvahiKey *key;
579         int unicast_response = 0;
580
581         if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
582             avahi_log_warn(__FILE__": Packet too short or invalid while reading question key. (Maybe an UTF8 problem?)");
583             goto fail;
584         }
585
586         if (!legacy_unicast && !from_local_iface) {
587             reflect_query(s, i, key);
588             avahi_cache_start_poof(i->cache, key, a);
589         }
590
591         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
592             !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
593             /* Allow our own queries to be suppressed by incoming
594              * queries only when they do not include known answers */
595             avahi_query_scheduler_incoming(i->query_scheduler, key);
596         
597         avahi_server_prepare_matching_responses(s, i, key, unicast_response);
598         avahi_key_unref(key);
599     }
600
601     if (!legacy_unicast) {
602
603         /* Known Answer Suppression */
604         for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
605             AvahiRecord *record;
606             int unique = 0;
607             
608             if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
609                 avahi_log_warn(__FILE__": Packet too short or invalid while reading known answer record. (Maybe an UTF8 problem?)");
610                 goto fail;
611             }
612             
613             if (handle_conflict(s, i, record, unique)) {
614                 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
615                 avahi_record_list_drop(s->record_list, record);
616                 avahi_cache_stop_poof(i->cache, record, a);
617             }
618             
619             avahi_record_unref(record);
620         }
621         
622         /* Probe record */
623         for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
624             AvahiRecord *record;
625             int unique = 0;
626             
627             if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
628                 avahi_log_warn(__FILE__": Packet too short or invalid while reading probe record. (Maybe an UTF8 problem?)");
629                 goto fail;
630             }
631             
632             if (!avahi_key_is_pattern(record->key)) {
633                 if (!from_local_iface)
634                     reflect_probe(s, i, record);
635                 incoming_probe(s, record, i);
636             }
637             
638             avahi_record_unref(record);
639         }
640     }
641
642     if (!avahi_record_list_is_empty(s->record_list))
643         avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
644
645     return;
646     
647 fail:
648     avahi_record_list_flush(s->record_list);
649 }
650
651 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
652     unsigned n;
653     
654     assert(s);
655     assert(p);
656     assert(i);
657     assert(a);
658
659     for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
660              avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
661         AvahiRecord *record;
662         int cache_flush = 0;
663 /*         char *txt; */
664         
665         if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
666             avahi_log_warn(__FILE__": Packet too short or invalid while reading response record. (Maybe an UTF8 problem?)");
667             break;
668         }
669
670         if (!avahi_key_is_pattern(record->key)) {
671
672             if (handle_conflict(s, i, record, cache_flush)) {
673                 if (!from_local_iface)
674                     reflect_response(s, i, record, cache_flush);
675                 avahi_cache_update(i->cache, record, cache_flush, a);
676                 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
677             }
678         }
679             
680         avahi_record_unref(record);
681     }
682
683     /* If the incoming response contained a conflicting record, some
684        records have been scheduling for sending. We need to flush them
685        here. */
686     if (!avahi_record_list_is_empty(s->record_list))
687         avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
688 }
689
690 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
691     unsigned n, idx = (unsigned) -1;
692     AvahiLegacyUnicastReflectSlot *slot;
693     
694     assert(s);
695
696     if (!s->legacy_unicast_reflect_slots)
697         s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX);
698
699     for (n = 0; n < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; n++, s->legacy_unicast_reflect_id++) {
700         idx = s->legacy_unicast_reflect_id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
701         
702         if (!s->legacy_unicast_reflect_slots[idx])
703             break;
704     }
705
706     if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
707         return NULL;
708
709     if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
710         return NULL; /* OOM */
711
712     s->legacy_unicast_reflect_slots[idx] = slot;
713     slot->id = s->legacy_unicast_reflect_id++;
714     slot->server = s;
715     
716     return slot;
717 }
718
719 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
720     unsigned idx;
721
722     assert(s);
723     assert(slot);
724
725     idx = slot->id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
726
727     assert(s->legacy_unicast_reflect_slots[idx] == slot);
728
729     avahi_time_event_free(slot->time_event);
730     
731     avahi_free(slot);
732     s->legacy_unicast_reflect_slots[idx] = NULL;
733 }
734
735 static void free_slots(AvahiServer *s) {
736     unsigned idx;
737     assert(s);
738
739     if (!s->legacy_unicast_reflect_slots)
740         return;
741
742     for (idx = 0; idx < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; idx ++)
743         if (s->legacy_unicast_reflect_slots[idx])
744             deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
745
746     avahi_free(s->legacy_unicast_reflect_slots);
747     s->legacy_unicast_reflect_slots = NULL;
748 }
749
750 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
751     unsigned idx;
752     
753     assert(s);
754
755     if (!s->legacy_unicast_reflect_slots)
756         return NULL;
757     
758     idx = id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
759
760     if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
761         return NULL;
762
763     return s->legacy_unicast_reflect_slots[idx];
764 }
765
766 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
767     AvahiLegacyUnicastReflectSlot *slot = userdata;
768
769     assert(e);
770     assert(slot);
771     assert(slot->time_event == e);
772
773     deallocate_slot(slot->server, slot);
774 }
775
776 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
777     AvahiLegacyUnicastReflectSlot *slot;
778     AvahiInterface *j;
779
780     assert(s);
781     assert(p);
782     assert(i);
783     assert(a);
784     assert(port > 0);
785     assert(i->protocol == a->proto);
786     
787     if (!s->config.enable_reflector)
788         return;
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 (j->announcing &&
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         !i->announcing) {
898         avahi_log_warn("Recieved packet from invalid interface.");
899         return;
900     }
901
902     if (avahi_address_is_ipv4_in_ipv6(src_address))
903         /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
904         return;
905
906     if (originates_from_local_legacy_unicast_socket(s, src_address, port))
907         /* This originates from our local reflector, so let's ignore it */
908         return;
909
910     /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
911     if (s->config.enable_reflector)
912         from_local_iface = originates_from_local_iface(s, iface, src_address, port);
913
914     if (avahi_dns_packet_check_valid_multicast(p) < 0) {
915         avahi_log_warn("Recieved invalid packet.");
916         return;
917     }
918
919     if (avahi_dns_packet_is_query(p)) {
920         int legacy_unicast = 0;
921
922         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
923             avahi_log_warn("Invalid query packet.");
924             return;
925         }
926
927         if (port != AVAHI_MDNS_PORT) {
928             /* Legacy Unicast */
929
930             if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
931                  avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
932                 avahi_log_warn("Invalid legacy unicast query packet.");
933                 return;
934             }
935         
936             legacy_unicast = 1;
937         }
938
939         if (legacy_unicast)
940             reflect_legacy_unicast_query_packet(s, p, i, src_address, port);
941         
942         handle_query_packet(s, p, i, src_address, port, legacy_unicast, from_local_iface);
943         
944     } else {
945         if (port != AVAHI_MDNS_PORT) {
946             avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
947             return;
948         }
949
950         if (ttl != 255 && s->config.check_response_ttl) {
951             avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
952             return;
953         }
954
955         if (!is_mdns_mcast_address(dst_address) &&
956             !avahi_interface_address_on_link(i, src_address)) {
957             avahi_log_warn("Received non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
958             return;
959         }
960         
961         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
962             avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
963             avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
964             avahi_log_warn("Invalid response packet.");
965             return;
966         }
967
968         handle_response_packet(s, p, i, src_address, from_local_iface);
969     }
970 }
971
972 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p) {
973     AvahiInterface *j;
974     AvahiLegacyUnicastReflectSlot *slot;
975     
976     assert(s);
977     assert(p);
978
979     if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
980         avahi_log_warn("Recieved invalid packet.");
981         return;
982     }
983
984     if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
985         avahi_log_warn("Recieved legacy unicast response with unknown id");
986         return;
987     }
988
989     if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
990         !j->announcing)
991         return;
992
993     /* Patch the original ID into this response */
994     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
995
996     /* Forward the response to the correct client */
997     avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
998
999     /* Undo changes to packet */
1000     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1001 }
1002
1003 static void cleanup_dead(AvahiServer *s) {
1004     assert(s);
1005     
1006     avahi_cleanup_dead_entries(s);
1007     avahi_browser_cleanup(s);
1008 }
1009
1010 static void mcast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1011     AvahiServer *s = userdata;
1012     AvahiAddress dest, src;
1013     AvahiDnsPacket *p = NULL;
1014     AvahiIfIndex iface;
1015     uint16_t port;
1016     uint8_t ttl;
1017
1018     assert(w);
1019     assert(fd >= 0);
1020     assert(events & AVAHI_WATCH_IN);
1021     
1022     if (fd == s->fd_ipv4) {
1023         dest.proto = src.proto = AVAHI_PROTO_INET;
1024         p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &src.data.ipv4, &port, &dest.data.ipv4, &iface, &ttl);
1025     } else {
1026         assert(fd == s->fd_ipv6);
1027         dest.proto = src.proto = AVAHI_PROTO_INET6;
1028         p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &src.data.ipv6, &port, &dest.data.ipv6, &iface, &ttl);
1029     }
1030         
1031     if (p) {
1032         if (iface == AVAHI_IF_UNSPEC) 
1033             iface = avahi_find_interface_for_address(s->monitor, &dest);
1034         
1035         if (iface != AVAHI_IF_UNSPEC)
1036             dispatch_packet(s, p, &src, port, &dest, iface, ttl);
1037         else
1038             avahi_log_error("Incoming packet recieved on address that isn't local.");
1039
1040         avahi_dns_packet_free(p);
1041
1042         cleanup_dead(s);
1043     }
1044 }
1045
1046 static void legacy_unicast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1047     AvahiServer *s = userdata;
1048     AvahiDnsPacket *p = NULL;
1049
1050     assert(w);
1051     assert(fd >= 0);
1052     assert(events & AVAHI_WATCH_IN);
1053     
1054     if (fd == s->fd_legacy_unicast_ipv4)
1055         p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, NULL, NULL, NULL, NULL, NULL);
1056     else {
1057         assert(fd == s->fd_legacy_unicast_ipv6);
1058         p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, NULL, NULL, NULL, NULL, NULL);
1059     }
1060
1061     if (p) {
1062         dispatch_legacy_unicast_packet(s, p);
1063         avahi_dns_packet_free(p);
1064
1065         cleanup_dead(s);
1066     }
1067 }
1068
1069 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1070     assert(s);
1071
1072     if (s->state == state)
1073         return;
1074     
1075     s->state = state;
1076
1077     avahi_interface_monitor_update_rrs(s->monitor, 0);
1078     
1079     if (s->callback)
1080         s->callback(s, state, s->userdata);
1081 }
1082
1083 static void withdraw_host_rrs(AvahiServer *s) {
1084     assert(s);
1085
1086     if (s->hinfo_entry_group)
1087         avahi_s_entry_group_reset(s->hinfo_entry_group);
1088
1089     if (s->browse_domain_entry_group)
1090         avahi_s_entry_group_reset(s->browse_domain_entry_group);
1091
1092     avahi_interface_monitor_update_rrs(s->monitor, 1);
1093     s->n_host_rr_pending = 0;
1094 }
1095
1096 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1097     assert(s);
1098     
1099     assert(s->n_host_rr_pending > 0);
1100
1101     if (--s->n_host_rr_pending == 0)
1102         server_set_state(s, AVAHI_SERVER_RUNNING);
1103 }
1104
1105 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
1106     assert(s);
1107     assert(g);
1108
1109     if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1110         s->state == AVAHI_SERVER_REGISTERING)
1111         s->n_host_rr_pending ++;
1112     
1113     else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1114         (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1115         withdraw_host_rrs(s);
1116         server_set_state(s, AVAHI_SERVER_COLLISION);
1117         
1118     } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1119                s->state == AVAHI_SERVER_REGISTERING)
1120         avahi_server_decrease_host_rr_pending(s);
1121 }
1122
1123 static void register_hinfo(AvahiServer *s) {
1124     struct utsname utsname;
1125     AvahiRecord *r;
1126     
1127     assert(s);
1128     
1129     if (!s->config.publish_hinfo)
1130         return;
1131
1132     if (s->hinfo_entry_group)
1133         assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1134     else
1135         s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1136
1137     if (!s->hinfo_entry_group) {
1138         avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1139         return;
1140     }
1141     
1142     /* Fill in HINFO rr */
1143     if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1144
1145         if (uname(&utsname) < 0)
1146             avahi_log_warn("uname() failed: %s\n", avahi_strerror(errno));
1147         else {
1148             
1149             r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1150             r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1151             
1152             avahi_log_info("Registering HINFO record with values '%s'/'%s'.", r->data.hinfo.cpu, r->data.hinfo.os);
1153             
1154             if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
1155                 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1156                 return;
1157             }
1158         }
1159
1160         avahi_record_unref(r);
1161     }
1162
1163     if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1164         avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1165
1166 }
1167
1168 static void register_localhost(AvahiServer *s) {
1169     AvahiAddress a;
1170     assert(s);
1171     
1172     /* Add localhost entries */
1173     avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1174     avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a);
1175
1176     avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1177     avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
1178 }
1179
1180 static void register_browse_domain(AvahiServer *s) {
1181     assert(s);
1182
1183     if (!s->config.publish_domain)
1184         return;
1185
1186     if (avahi_domain_equal(s->domain_name, "local"))
1187         return;
1188
1189     if (s->browse_domain_entry_group)
1190         assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1191     else
1192         s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1193
1194     if (!s->browse_domain_entry_group) {
1195         avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1196         return;
1197     }
1198     
1199     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) {
1200         avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1201         return;
1202     }
1203
1204     if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1205         avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1206 }
1207
1208 static void register_stuff(AvahiServer *s) {
1209     assert(s);
1210
1211     server_set_state(s, AVAHI_SERVER_REGISTERING);
1212     s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1213
1214     register_hinfo(s);
1215     register_browse_domain(s);
1216     avahi_interface_monitor_update_rrs(s->monitor, 0);
1217
1218     s->n_host_rr_pending --;
1219     
1220     if (s->n_host_rr_pending == 0)
1221         server_set_state(s, AVAHI_SERVER_RUNNING);
1222 }
1223
1224 static void update_fqdn(AvahiServer *s) {
1225     char *n;
1226     
1227     assert(s);
1228     assert(s->host_name);
1229     assert(s->domain_name);
1230
1231     if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1232         return; /* OOM */
1233
1234     avahi_free(s->host_name_fqdn);
1235     s->host_name_fqdn = n;
1236 }
1237
1238 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1239     char *hn = NULL;
1240     assert(s);
1241     
1242     AVAHI_CHECK_VALIDITY(s, !host_name || avahi_is_valid_host_name(host_name), AVAHI_ERR_INVALID_HOST_NAME);
1243
1244     if (!host_name) {
1245         hn = avahi_get_host_name_strdup();
1246         hn[strcspn(hn, ".")] = 0;
1247         host_name = hn;
1248     }
1249
1250     if (avahi_domain_equal(s->host_name, host_name) && s->state != AVAHI_SERVER_COLLISION) {
1251         avahi_free(hn);
1252         return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1253     }
1254     
1255     withdraw_host_rrs(s);
1256
1257     avahi_free(s->host_name);
1258     s->host_name = hn ? hn : avahi_strdup(host_name);
1259     
1260     update_fqdn(s);
1261
1262     register_stuff(s);
1263     return AVAHI_OK;
1264 }
1265
1266 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1267     char *dn = NULL;
1268     assert(s);
1269
1270     AVAHI_CHECK_VALIDITY(s, !domain_name || avahi_is_valid_domain_name(domain_name), AVAHI_ERR_INVALID_DOMAIN_NAME);
1271
1272     if (!domain_name) {
1273         dn = avahi_strdup("local");
1274         domain_name = dn;
1275     }
1276     
1277     if (avahi_domain_equal(s->domain_name, domain_name)) {
1278         avahi_free(dn);
1279         return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1280     }
1281
1282     withdraw_host_rrs(s);
1283
1284     avahi_free(s->domain_name);
1285     s->domain_name = avahi_normalize_name_strdup(domain_name);
1286     update_fqdn(s);
1287
1288     register_stuff(s);
1289
1290     avahi_free(dn);
1291     return AVAHI_OK;
1292 }
1293
1294 static int valid_server_config(const AvahiServerConfig *sc) {
1295
1296     if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1297         return AVAHI_ERR_INVALID_HOST_NAME;
1298     
1299     if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1300         return AVAHI_ERR_INVALID_DOMAIN_NAME;
1301
1302     return AVAHI_OK;
1303 }
1304
1305 static int setup_sockets(AvahiServer *s) {
1306     assert(s);
1307     
1308     s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
1309     s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
1310     
1311     if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1312         return AVAHI_ERR_NO_NETWORK;
1313
1314     if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1315         avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1316     else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1317         avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1318
1319     s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1;
1320     s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1;
1321     
1322     s->watch_ipv4 =
1323         s->watch_ipv6 =
1324         s->watch_legacy_unicast_ipv4 =
1325         s->watch_legacy_unicast_ipv6 = NULL;
1326     
1327     if (s->fd_ipv4 >= 0)
1328         s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, mcast_socket_event, s);
1329     if (s->fd_ipv6 >= 0)
1330         s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, mcast_socket_event, s);
1331     
1332     if (s->fd_legacy_unicast_ipv4 >= 0)
1333         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);
1334     if (s->fd_legacy_unicast_ipv6 >= 0)
1335         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);
1336             
1337     return 0;
1338 }
1339
1340 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1341     AvahiServer *s;
1342     int e;
1343     
1344     if (sc && (e = valid_server_config(sc)) < 0) {
1345         if (error)
1346             *error = e;
1347         return NULL;
1348     }
1349     
1350     if (!(s = avahi_new(AvahiServer, 1))) {
1351         if (error)
1352             *error = AVAHI_ERR_NO_MEMORY;
1353
1354         return NULL;
1355     }
1356
1357     s->poll_api = poll_api;
1358
1359     if (sc)
1360         avahi_server_config_copy(&s->config, sc);
1361     else
1362         avahi_server_config_init(&s->config);
1363
1364     if ((e = setup_sockets(s)) < 0) {
1365         if (error)
1366             *error = e;
1367
1368         avahi_server_config_free(&s->config);
1369         avahi_free(s);
1370         
1371         return NULL;
1372     }
1373     
1374     s->n_host_rr_pending = 0;
1375     s->need_entry_cleanup = 0;
1376     s->need_group_cleanup = 0;
1377     s->need_browser_cleanup = 0;
1378     s->hinfo_entry_group = NULL;
1379     s->browse_domain_entry_group = NULL;
1380     s->error = AVAHI_OK;
1381     s->state = AVAHI_SERVER_INVALID;
1382
1383     s->callback = callback;
1384     s->userdata = userdata;
1385
1386     s->time_event_queue = avahi_time_event_queue_new(poll_api);
1387
1388     s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1389     AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1390     AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1391
1392     s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1393     AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1394     AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1395     AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1396     AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1397     AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1398     AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1399     AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1400     AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1401
1402     s->legacy_unicast_reflect_slots = NULL;
1403     s->legacy_unicast_reflect_id = 0;
1404
1405     s->record_list = avahi_record_list_new();
1406     
1407     /* Get host name */
1408     s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
1409     s->host_name[strcspn(s->host_name, ".")] = 0;
1410     s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
1411     s->host_name_fqdn = NULL;
1412     update_fqdn(s);
1413
1414     do {
1415         s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1416     } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
1417
1418     if (s->config.enable_wide_area) {
1419         s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
1420         avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1421     } else
1422         s->wide_area_lookup_engine = NULL;
1423
1424     s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
1425     
1426     s->monitor = avahi_interface_monitor_new(s);
1427     avahi_interface_monitor_sync(s->monitor);
1428
1429     register_localhost(s);
1430     register_stuff(s);
1431     
1432     return s;
1433 }
1434
1435 void avahi_server_free(AvahiServer* s) {
1436     assert(s);
1437
1438     /* Remove all browsers */
1439
1440     while (s->dns_server_browsers)
1441         avahi_s_dns_server_browser_free(s->dns_server_browsers);
1442     while (s->host_name_resolvers)
1443         avahi_s_host_name_resolver_free(s->host_name_resolvers);
1444     while (s->address_resolvers)
1445         avahi_s_address_resolver_free(s->address_resolvers);
1446     while (s->domain_browsers)
1447         avahi_s_domain_browser_free(s->domain_browsers);
1448     while (s->service_type_browsers)
1449         avahi_s_service_type_browser_free(s->service_type_browsers);
1450     while (s->service_browsers)
1451         avahi_s_service_browser_free(s->service_browsers);
1452     while (s->service_resolvers)
1453         avahi_s_service_resolver_free(s->service_resolvers);
1454     while (s->record_browsers)
1455         avahi_s_record_browser_destroy(s->record_browsers);
1456     
1457     /* Remove all locally rgeistered stuff */
1458
1459     while(s->entries)
1460         avahi_entry_free(s, s->entries);
1461
1462     avahi_interface_monitor_free(s->monitor);
1463
1464     while (s->groups)
1465         avahi_entry_group_free(s, s->groups);
1466
1467     free_slots(s);
1468
1469     avahi_hashmap_free(s->entries_by_key);
1470     avahi_record_list_free(s->record_list);
1471     avahi_hashmap_free(s->record_browser_hashmap);
1472
1473     if (s->wide_area_lookup_engine)
1474         avahi_wide_area_engine_free(s->wide_area_lookup_engine);
1475     avahi_multicast_lookup_engine_free(s->multicast_lookup_engine);
1476
1477     avahi_time_event_queue_free(s->time_event_queue);
1478     
1479     /* Free watches */
1480     
1481     if (s->watch_ipv4)
1482         s->poll_api->watch_free(s->watch_ipv4);
1483     if (s->watch_ipv6)
1484         s->poll_api->watch_free(s->watch_ipv6);
1485     
1486     if (s->watch_legacy_unicast_ipv4)
1487         s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1488     if (s->watch_legacy_unicast_ipv6)
1489         s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1490
1491     /* Free sockets */
1492     
1493     if (s->fd_ipv4 >= 0)
1494         close(s->fd_ipv4);
1495     if (s->fd_ipv6 >= 0)
1496         close(s->fd_ipv6);
1497     
1498     if (s->fd_legacy_unicast_ipv4 >= 0)
1499         close(s->fd_legacy_unicast_ipv4);
1500     if (s->fd_legacy_unicast_ipv6 >= 0)
1501         close(s->fd_legacy_unicast_ipv6);
1502     
1503     /* Free other stuff */
1504     
1505     avahi_free(s->host_name);
1506     avahi_free(s->domain_name);
1507     avahi_free(s->host_name_fqdn);
1508
1509     avahi_server_config_free(&s->config);
1510
1511     avahi_free(s);
1512 }
1513
1514 const char* avahi_server_get_domain_name(AvahiServer *s) {
1515     assert(s);
1516
1517     return s->domain_name;
1518 }
1519
1520 const char* avahi_server_get_host_name(AvahiServer *s) {
1521     assert(s);
1522
1523     return s->host_name;
1524 }
1525
1526 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1527     assert(s);
1528
1529     return s->host_name_fqdn;
1530 }
1531
1532 void* avahi_server_get_data(AvahiServer *s) {
1533     assert(s);
1534
1535     return s->userdata;
1536 }
1537
1538 void avahi_server_set_data(AvahiServer *s, void* userdata) {
1539     assert(s);
1540
1541     s->userdata = userdata;
1542 }
1543
1544 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1545     assert(s);
1546
1547     return s->state;
1548 }
1549
1550 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1551     assert(c);
1552
1553     memset(c, 0, sizeof(AvahiServerConfig));
1554     c->use_ipv6 = 1;
1555     c->use_ipv4 = 1;
1556     c->host_name = NULL;
1557     c->domain_name = NULL;
1558     c->check_response_ttl = 0;
1559     c->publish_hinfo = 1;
1560     c->publish_addresses = 1;
1561     c->publish_workstation = 1;
1562     c->publish_domain = 1;
1563     c->use_iff_running = 0;
1564     c->enable_reflector = 0;
1565     c->reflect_ipv = 0;
1566     c->add_service_cookie = 1;
1567     c->enable_wide_area = 0;
1568     c->n_wide_area_servers = 0;
1569     c->disallow_other_stacks = 0;
1570     c->browse_domains = NULL;
1571     c->disable_publishing = 0;
1572     c->allow_point_to_point = 0;
1573     c->publish_aaaa_on_ipv4 = 1;
1574     c->publish_a_on_ipv6 = 0;
1575     
1576     return c;
1577 }
1578
1579 void avahi_server_config_free(AvahiServerConfig *c) {
1580     assert(c);
1581
1582     avahi_free(c->host_name);
1583     avahi_free(c->domain_name);
1584     avahi_string_list_free(c->browse_domains);
1585 }
1586
1587 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1588     char *d = NULL, *h = NULL;
1589     AvahiStringList *l = NULL;
1590     assert(ret);
1591     assert(c);
1592
1593     if (c->host_name)
1594         if (!(h = avahi_strdup(c->host_name)))
1595             return NULL;
1596
1597     if (c->domain_name)
1598         if (!(d = avahi_strdup(c->domain_name))) {
1599             avahi_free(h);
1600             return NULL;
1601         }
1602
1603     if (!(l = avahi_string_list_copy(c->browse_domains)) && c->browse_domains) {
1604         avahi_free(h);
1605         avahi_free(d);
1606         return NULL;
1607     }
1608     
1609     *ret = *c;
1610     ret->host_name = h;
1611     ret->domain_name = d;
1612     ret->browse_domains = l;
1613
1614     return ret;
1615 }
1616
1617 int avahi_server_errno(AvahiServer *s) {
1618     assert(s);
1619     
1620     return s->error;
1621 }
1622
1623 /* Just for internal use */
1624 int avahi_server_set_errno(AvahiServer *s, int error) {
1625     assert(s);
1626
1627     return s->error = error;
1628 }
1629
1630 uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
1631     assert(s);
1632
1633     return s->local_service_cookie;
1634 }
1635
1636 static AvahiEntry *find_entry(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
1637     AvahiEntry *e;
1638     
1639     assert(s);
1640     assert(key);
1641
1642     for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
1643
1644         if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1645             (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1646             (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING))
1647
1648             return e;
1649
1650     return NULL;
1651 }
1652
1653 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) {
1654     AvahiKey *key = NULL;
1655     AvahiEntry *e;
1656     int ret;
1657     char n[AVAHI_DOMAIN_NAME_MAX];
1658     
1659     assert(s);
1660     assert(name);
1661     assert(type);
1662     assert(ret_group);
1663
1664     AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
1665     AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
1666     AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
1667     AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
1668     AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
1669
1670     if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0))
1671         return avahi_server_set_errno(s, ret);
1672         
1673     if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1674         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1675
1676     e = find_entry(s, interface, protocol, key);
1677     avahi_key_unref(key);
1678
1679     if (e) {
1680         *ret_group = e->group;
1681         return AVAHI_OK;
1682     }
1683
1684     return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
1685 }
1686
1687 int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name) {
1688     AvahiKey *key = NULL;
1689     AvahiEntry *e;
1690
1691     assert(s);
1692     assert(name);
1693
1694     if (!s->host_name_fqdn)
1695         return 0;
1696     
1697     if (!(key = avahi_key_new(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1698         return 0;
1699
1700     e = find_entry(s, interface, protocol, key);
1701     avahi_key_unref(key);
1702
1703     if (!e)
1704         return 0;
1705     
1706     return avahi_domain_equal(s->host_name_fqdn, e->record->data.srv.name);
1707 }
1708
1709 int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record) {
1710     AvahiEntry *e;
1711
1712     assert(s);
1713     assert(record);
1714     
1715     for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = e->by_key_next)
1716
1717         if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1718             (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1719             (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING) &&
1720             avahi_record_equal_no_ttl(record, e->record))
1721             return 1;
1722
1723     return 0;
1724 }
1725
1726 /** Set the wide area DNS servers */
1727 int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) {
1728     assert(s);
1729
1730     if (!s->wide_area_lookup_engine)
1731         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG);
1732
1733     avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n);
1734     return AVAHI_OK;
1735 }
1736
1737 const AvahiServerConfig* avahi_server_get_config(AvahiServer *s) {
1738     assert(s);
1739
1740     return &s->config;
1741 }