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