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