]> git.meshlink.io Git - catta/blob - avahi-core/server.c
66cc70592699286d40120494f45b44263729989e
[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
35 #include <avahi-common/domain.h>
36 #include <avahi-common/timeval.h>
37 #include <avahi-common/malloc.h>
38 #include <avahi-common/error.h>
39
40 #include "server.h"
41 #include "iface.h"
42 #include "socket.h"
43 #include "browse.h"
44 #include "log.h"
45 #include "util.h"
46
47 #define AVAHI_RR_HOLDOFF_MSEC 1000
48 #define AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT 60000
49 #define AVAHI_RR_RATE_LIMIT_COUNT 15
50
51 static void free_entry(AvahiServer*s, AvahiEntry *e) {
52     AvahiEntry *t;
53
54     assert(s);
55     assert(e);
56
57     avahi_goodbye_entry(s, e, 1);
58
59     /* Remove from linked list */
60     AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e);
61
62     /* Remove from hash table indexed by name */
63     t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
64     AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e);
65     if (t)
66         avahi_hashmap_replace(s->entries_by_key, t->record->key, t);
67     else
68         avahi_hashmap_remove(s->entries_by_key, e->record->key);
69
70     /* Remove from associated group */
71     if (e->group)
72         AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e);
73
74     avahi_record_unref(e->record);
75     avahi_free(e);
76 }
77
78 static void free_group(AvahiServer *s, AvahiSEntryGroup *g) {
79     assert(s);
80     assert(g);
81
82     while (g->entries)
83         free_entry(s, g->entries);
84
85     if (g->register_time_event)
86         avahi_time_event_free(g->register_time_event);
87     
88     AVAHI_LLIST_REMOVE(AvahiSEntryGroup, groups, s->groups, g);
89     avahi_free(g);
90 }
91
92 static void cleanup_dead(AvahiServer *s) {
93     assert(s);
94
95     if (s->need_group_cleanup) {
96         AvahiSEntryGroup *g, *next;
97         
98         for (g = s->groups; g; g = next) {
99             next = g->groups_next;
100             
101             if (g->dead)
102                 free_group(s, g);
103         }
104
105         s->need_group_cleanup = 0;
106     }
107
108     if (s->need_entry_cleanup) {
109         AvahiEntry *e, *next;
110         
111         for (e = s->entries; e; e = next) {
112             next = e->entries_next;
113             
114             if (e->dead)
115                 free_entry(s, e);
116         }
117
118         s->need_entry_cleanup = 0;
119     }
120
121     if (s->need_browser_cleanup)
122         avahi_browser_cleanup(s);
123 }
124
125 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) {
126     AvahiKey *k;
127     AvahiEntry *e;
128
129     assert(s);
130     assert(i);
131     assert(name);
132     assert(callback);
133
134     assert(type != AVAHI_DNS_TYPE_ANY);
135
136     if (!(k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type)))
137         return; /** OOM */
138
139     for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
140         if (!e->dead && avahi_entry_is_registered(s, e, i)) 
141             callback(s, e->record, e->flags & AVAHI_ENTRY_UNIQUE, userdata);
142
143     avahi_key_unref(k);
144 }
145
146 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) {
147     assert(s);
148     assert(i);
149     assert(r);
150     assert(callback);
151     
152     if (r->key->clazz == AVAHI_DNS_CLASS_IN) {
153         if (r->key->type == AVAHI_DNS_TYPE_PTR) {
154             enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
155             enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
156         } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
157             enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
158             enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
159         }
160     }
161 }
162
163 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary) {
164     assert(s);
165     assert(i);
166     assert(e);
167
168     avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_ENTRY_UNIQUE, unicast_response, auxiliary);
169 }
170
171 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response) {
172     AvahiEntry *e;
173 /*     char *txt; */
174     
175     assert(s);
176     assert(i);
177     assert(k);
178
179 /*     avahi_log_debug("Posting responses matching [%s]", txt = avahi_key_to_string(k)); */
180 /*     avahi_free(txt); */
181
182     if (avahi_key_is_pattern(k)) {
183
184         /* Handle ANY query */
185         
186         for (e = s->entries; e; e = e->entries_next)
187             if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_is_registered(s, e, i))
188                 avahi_server_prepare_response(s, i, e, unicast_response, 0);
189
190     } else {
191
192         /* Handle all other queries */
193         
194         for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
195             if (!e->dead && avahi_entry_is_registered(s, e, i))
196                 avahi_server_prepare_response(s, i, e, unicast_response, 0);
197     }
198 }
199
200 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
201     assert(s);
202     assert(e);
203     
204     if (e->group) {
205         AvahiEntry *k;
206         
207         for (k = e->group->entries; k; k = k->by_group_next) {
208             if (!k->dead) {
209                 avahi_goodbye_entry(s, k, 0);
210                 k->dead = 1;
211             }
212         }
213
214         e->group->n_probing = 0;
215
216         avahi_s_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
217     } else {
218         avahi_goodbye_entry(s, e, 0);
219         e->dead = 1;
220     }
221
222     s->need_entry_cleanup = 1;
223 }
224
225 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
226     AvahiEntry *e;
227     
228     assert(s);
229     assert(key);
230
231    for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
232        if (!e->dead)
233            withdraw_entry(s, e);
234 }
235
236 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
237     AvahiEntry *e, *n;
238     char *t;
239     int ours = 0, won = 0, lost = 0;
240     
241     assert(s);
242     assert(record);
243     assert(i);
244
245     t = avahi_record_to_string(record);
246
247 /*     avahi_log_debug("incoming_probe()");  */
248
249     for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
250         int cmp;
251         n = e->by_key_next;
252
253         if (e->dead)
254             continue;
255         
256         if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
257             ours = 1;
258             break;
259         } else {
260             
261             if (avahi_entry_is_probing(s, e, i)) {
262                 if (cmp > 0)
263                     won = 1;
264                 else /* cmp < 0 */
265                     lost = 1;
266             }
267         }
268     }
269
270     if (!ours) {
271
272         if (won)
273             avahi_log_debug("Recieved conflicting probe [%s]. Local host won.", t);
274         else if (lost) {
275             avahi_log_debug("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
276             withdraw_rrset(s, record->key);
277         }/*  else */
278 /*             avahi_log_debug("Not conflicting probe"); */
279     }
280
281     avahi_free(t);
282 }
283
284 static int handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, int unique, const AvahiAddress *a) {
285     int valid = 1, ours = 0, conflict = 0, withdraw_immediately = 0;
286     AvahiEntry *e, *n, *conflicting_entry = NULL;
287     
288     assert(s);
289     assert(i);
290     assert(record);
291
292
293 /*     avahi_log_debug("CHECKING FOR CONFLICT: [%s]", t);   */
294
295     for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
296         n = e->by_key_next;
297
298         if (e->dead)
299             continue;
300
301         /* Check if the incoming is a goodbye record */
302         if (avahi_record_is_goodbye(record)) {
303
304             if (avahi_record_equal_no_ttl(e->record, record)) {
305                 char *t;
306
307                 /* Refresh */
308                 t = avahi_record_to_string(record); 
309                 avahi_log_debug("Recieved goodbye record for one of our records [%s]. Refreshing.", t);
310                 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
311
312                 valid = 0;
313                 avahi_free(t);
314                 break;
315             }
316
317             /* If the goodybe packet doesn't match one of our own RRs, we simply ignore it. */
318             continue;
319         }
320         
321         if (!(e->flags & AVAHI_ENTRY_UNIQUE) && !unique)
322             continue;
323
324         /* Either our entry or the other is intended to be unique, so let's check */
325         
326         if (avahi_record_equal_no_ttl(e->record, record)) {
327             ours = 1; /* We have an identical record, so this is no conflict */
328             
329             /* Check wheter there is a TTL conflict */
330             if (record->ttl <= e->record->ttl/2 &&
331                 avahi_entry_is_registered(s, e, i)) {
332                 char *t;
333                 /* Refresh */
334                 t = avahi_record_to_string(record); 
335                 
336                 avahi_log_debug("Recieved record with bad TTL [%s]. Refreshing.", t);
337                 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
338                 valid = 0;
339                 
340                 avahi_free(t);
341             }
342                 
343             /* There's no need to check the other entries of this RRset */
344             break;
345
346         } else {
347             
348             if (avahi_entry_is_registered(s, e, i)) {
349                 
350                 /* A conflict => we have to return to probe mode */
351                 conflict = 1;
352                 conflicting_entry = e;
353
354             } else if (avahi_entry_is_probing(s, e, i)) {
355
356                 /* We are currently registering a matching record, but
357                  * someone else already claimed it, so let's
358                  * withdraw */
359                 conflict = 1;
360                 withdraw_immediately = 1;
361             }
362         }
363     }
364
365 /*     avahi_log_debug("ours=%i conflict=%i", ours, conflict); */
366
367     if (!ours && conflict) {
368         char *t;
369  
370         valid = 0;
371
372         t = avahi_record_to_string(record); 
373  
374         if (withdraw_immediately) {
375             avahi_log_debug("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
376             withdraw_rrset(s, record->key);
377         } else {
378             assert(conflicting_entry);
379             avahi_log_debug("Recieved conflicting record [%s]. Resetting our record.", t);
380             avahi_entry_return_to_initial_state(s, conflicting_entry, i);
381
382             /* Local unique records are returned to probing
383              * state. Local shared records are reannounced. */
384         }
385
386         avahi_free(t);
387     }
388
389     return valid;
390 }
391
392 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) {
393     int *unicast_response = userdata;
394
395     assert(s);
396     assert(r);
397     assert(unicast_response);
398     
399     avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, 1);
400 }
401
402 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int unicast_response) {
403     assert(s);
404     assert(r);
405
406     avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
407 }
408
409 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int immediately) {
410
411     assert(s);
412     assert(i);
413     assert(!legacy_unicast || (a && port > 0 && p));
414
415     if (legacy_unicast) {
416         AvahiDnsPacket *reply;
417         AvahiRecord *r;
418
419         if (!(reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , 1, 1)))
420             return; /* OOM */
421         
422         while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
423
424             append_aux_records_to_list(s, i, r, 0);
425             
426             if (avahi_dns_packet_append_record(reply, r, 0, 10))
427                 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
428             else {
429                 char *t = avahi_record_to_string(r);
430                 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
431                 avahi_free(t);
432             }
433
434             avahi_record_unref(r);
435         }
436
437         if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
438             avahi_interface_send_packet_unicast(i, reply, a, port);
439
440         avahi_dns_packet_free(reply);
441
442     } else {
443         int unicast_response, flush_cache, auxiliary;
444         AvahiDnsPacket *reply = NULL;
445         AvahiRecord *r;
446
447         /* In case the query packet was truncated never respond
448         immediately, because known answer suppression records might be
449         contained in later packets */
450         int tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
451         
452         while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
453                         
454             if (!avahi_interface_post_response(i, r, flush_cache, a, immediately || (flush_cache && !tc && !auxiliary)) && unicast_response) {
455
456                 append_aux_records_to_list(s, i, r, unicast_response);
457                 
458                 /* Due to some reasons the record has not been scheduled.
459                  * The client requested an unicast response in that
460                  * case. Therefore we prepare such a response */
461
462                 for (;;) {
463                 
464                     if (!reply) {
465                         assert(p);
466
467                         if (!(reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, 0, 0)))
468                             break; /* OOM */
469                     }
470                 
471                     if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
472
473                         /* Appending this record succeeded, so incremeant
474                          * the specific header field, and return to the caller */
475                         
476                         avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
477
478                         break;
479                     }
480
481                     if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
482                         size_t size;
483
484                         /* The record is too large for one packet, so create a larger packet */
485
486                         avahi_dns_packet_free(reply);
487                         size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
488                         if (size > AVAHI_DNS_PACKET_MAX_SIZE)
489                             size = AVAHI_DNS_PACKET_MAX_SIZE;
490
491                         if (!(reply = avahi_dns_packet_new_reply(p, size, 0, 1)))
492                             break; /* OOM */
493
494                         if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
495                             char *t;
496                             avahi_dns_packet_free(reply);
497                             t = avahi_record_to_string(r);
498                             avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
499                             avahi_free(t);
500                             break;
501                         } else
502                             avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
503                     }
504
505                     /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
506                     avahi_interface_send_packet_unicast(i, reply, a, port);
507                     avahi_dns_packet_free(reply);
508                     reply = NULL;
509                 }
510             }
511
512             avahi_record_unref(r);
513         }
514
515         if (reply) {
516             if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0) 
517                 avahi_interface_send_packet_unicast(i, reply, a, port);
518             avahi_dns_packet_free(reply);
519         }
520     }
521
522     avahi_record_list_flush(s->record_list);
523 }
524
525
526 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) {
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_response(j, r, flush_cache, NULL, 1);
539 }
540
541 static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
542     AvahiServer *s = userdata;
543
544     assert(c);
545     assert(pattern);
546     assert(e);
547     assert(s);
548
549     avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
550     return NULL;
551 }
552
553 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
554     AvahiInterface *j;
555     
556     assert(s);
557     assert(i);
558     assert(k);
559
560     if (!s->config.enable_reflector)
561         return;
562
563     for (j = s->monitor->interfaces; j; j = j->interface_next)
564         if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
565             /* Post the query to other networks */
566             avahi_interface_post_query(j, k, 1);
567
568             /* Reply from caches of other network. This is needed to
569              * "work around" known answer suppression. */
570
571             avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
572         }
573 }
574
575 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
576     AvahiInterface *j;
577     
578     assert(s);
579     assert(i);
580     assert(r);
581
582     if (!s->config.enable_reflector)
583         return;
584
585     for (j = s->monitor->interfaces; j; j = j->interface_next)
586         if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
587             avahi_interface_post_probe(j, r, 1);
588 }
589
590 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) {
591     size_t n;
592     int is_probe;
593     
594     assert(s);
595     assert(p);
596     assert(i);
597     assert(a);
598
599 /*     avahi_log_debug("query"); */
600
601     assert(avahi_record_list_is_empty(s->record_list));
602
603     is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
604     
605     /* Handle the questions */
606     for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
607         AvahiKey *key;
608         int unicast_response = 0;
609
610         if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
611             avahi_log_warn("Packet too short (1)");
612             goto fail;
613         }
614
615         if (!legacy_unicast && !from_local_iface)
616             reflect_query(s, i, key);
617
618         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
619             !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
620             /* Allow our own queries to be suppressed by incoming
621              * queries only when they do not include known answers */
622             avahi_query_scheduler_incoming(i->query_scheduler, key);
623         
624         avahi_server_prepare_matching_responses(s, i, key, unicast_response);
625         avahi_key_unref(key);
626     }
627
628     if (!legacy_unicast) {
629
630         /* Known Answer Suppression */
631         for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
632             AvahiRecord *record;
633             int unique = 0;
634             
635             if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
636                 avahi_log_warn("Packet too short (2)");
637                 goto fail;
638             }
639             
640             if (handle_conflict(s, i, record, unique, a)) {
641                 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
642                 avahi_record_list_drop(s->record_list, record);
643             }
644             
645             avahi_record_unref(record);
646         }
647         
648         /* Probe record */
649         for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
650             AvahiRecord *record;
651             int unique = 0;
652             
653             if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
654                 avahi_log_warn("Packet too short (3)");
655                 goto fail;
656             }
657             
658             if (!avahi_key_is_pattern(record->key)) {
659                 if (!from_local_iface)
660                     reflect_probe(s, i, record);
661                 incoming_probe(s, record, i);
662             }
663             
664             avahi_record_unref(record);
665         }
666     }
667
668     if (!avahi_record_list_is_empty(s->record_list))
669         avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
670
671     return;
672     
673 fail:
674     avahi_record_list_flush(s->record_list);
675 }
676
677 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
678     unsigned n;
679     
680     assert(s);
681     assert(p);
682     assert(i);
683     assert(a);
684
685 /*     avahi_log_debug("response"); */
686     
687     for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
688              avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
689         AvahiRecord *record;
690         int cache_flush = 0;
691 /*         char *txt; */
692         
693         if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
694             avahi_log_warn("Packet too short (4)");
695             break;
696         }
697
698         if (!avahi_key_is_pattern(record->key)) {
699
700 /*             avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
701 /*             avahi_free(txt); */
702             
703             if (handle_conflict(s, i, record, cache_flush, a)) {
704                 if (!from_local_iface)
705                     reflect_response(s, i, record, cache_flush);
706                 avahi_cache_update(i->cache, record, cache_flush, a);
707                 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
708             }
709         }
710             
711         avahi_record_unref(record);
712     }
713
714     /* If the incoming response contained a conflicting record, some
715        records have been scheduling for sending. We need to flush them
716        here. */
717     if (!avahi_record_list_is_empty(s->record_list))
718         avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
719 }
720
721 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
722     unsigned n, idx = (unsigned) -1;
723     AvahiLegacyUnicastReflectSlot *slot;
724     
725     assert(s);
726
727     if (!s->legacy_unicast_reflect_slots)
728         s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
729
730     for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
731         idx = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
732         
733         if (!s->legacy_unicast_reflect_slots[idx])
734             break;
735     }
736
737     if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
738         return NULL;
739
740     if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
741         return NULL; /* OOM */
742
743     s->legacy_unicast_reflect_slots[idx] = slot;
744     slot->id = s->legacy_unicast_reflect_id++;
745     slot->server = s;
746     
747     return slot;
748 }
749
750 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
751     unsigned idx;
752
753     assert(s);
754     assert(slot);
755
756     idx = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
757
758     assert(s->legacy_unicast_reflect_slots[idx] == slot);
759
760     avahi_time_event_free(slot->time_event);
761     
762     avahi_free(slot);
763     s->legacy_unicast_reflect_slots[idx] = NULL;
764 }
765
766 static void free_slots(AvahiServer *s) {
767     unsigned idx;
768     assert(s);
769
770     if (!s->legacy_unicast_reflect_slots)
771         return;
772
773     for (idx = 0; idx < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; idx ++)
774         if (s->legacy_unicast_reflect_slots[idx])
775             deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
776
777     avahi_free(s->legacy_unicast_reflect_slots);
778     s->legacy_unicast_reflect_slots = NULL;
779 }
780
781 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
782     unsigned idx;
783     
784     assert(s);
785
786     if (!s->legacy_unicast_reflect_slots)
787         return NULL;
788     
789     idx = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
790
791     if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
792         return NULL;
793
794     return s->legacy_unicast_reflect_slots[idx];
795 }
796
797 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
798     AvahiLegacyUnicastReflectSlot *slot = userdata;
799
800     assert(e);
801     assert(slot);
802     assert(slot->time_event == e);
803
804     deallocate_slot(slot->server, slot);
805 }
806
807 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
808     AvahiLegacyUnicastReflectSlot *slot;
809     AvahiInterface *j;
810
811     assert(s);
812     assert(p);
813     assert(i);
814     assert(a);
815     assert(port > 0);
816     assert(i->protocol == a->family);
817     
818     if (!s->config.enable_reflector)
819         return;
820
821 /*     avahi_log_debug("legacy unicast reflector"); */
822     
823     /* Reflecting legacy unicast queries is a little more complicated
824        than reflecting normal queries, since we must route the
825        responses back to the right client. Therefore we must store
826        some information for finding the right client contact data for
827        response packets. In contrast to normal queries legacy
828        unicast query and response packets are reflected untouched and
829        are not reassembled into larger packets */
830
831     if (!(slot = allocate_slot(s))) {
832         /* No slot available, we drop this legacy unicast query */
833         avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
834         return;
835     }
836
837     slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
838     slot->address = *a;
839     slot->port = port;
840     slot->interface = i->hardware->index;
841
842     avahi_elapse_time(&slot->elapse_time, 2000, 0);
843     slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
844
845     /* Patch the packet with our new locally generatedt id */
846     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
847     
848     for (j = s->monitor->interfaces; j; j = j->interface_next)
849         if (avahi_interface_relevant(j) &&
850             j != i &&
851             (s->config.reflect_ipv || j->protocol == i->protocol)) {
852
853             if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
854                 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
855                 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
856                 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
857         }
858
859     /* Reset the id */
860     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
861 }
862
863 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
864     AvahiAddress a;
865     assert(s);
866     assert(sa);
867
868     if (!s->config.enable_reflector)
869         return 0;
870     
871     avahi_address_from_sockaddr(sa, &a);
872
873     if (!avahi_address_is_local(s->monitor, &a))
874         return 0;
875     
876     if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
877         struct sockaddr_in lsa;
878         socklen_t l = sizeof(lsa);
879         
880         if (getsockname(s->fd_legacy_unicast_ipv4, &lsa, &l) != 0)
881             avahi_log_warn("getsockname(): %s", strerror(errno));
882         else
883             return lsa.sin_port == ((const struct sockaddr_in*) sa)->sin_port;
884
885     }
886
887     if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
888         struct sockaddr_in6 lsa;
889         socklen_t l = sizeof(lsa);
890
891         if (getsockname(s->fd_legacy_unicast_ipv6, &lsa, &l) != 0)
892             avahi_log_warn("getsockname(): %s", strerror(errno));
893         else
894             return lsa.sin6_port == ((const struct sockaddr_in6*) sa)->sin6_port;
895     }
896
897     return 0;
898 }
899
900 static int is_mdns_mcast_address(const AvahiAddress *a) {
901     AvahiAddress b;
902     assert(a);
903
904     avahi_address_parse(a->family == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->family, &b);
905     return avahi_address_cmp(a, &b) == 0;
906 }
907
908 static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) {
909     assert(s);
910     assert(iface != AVAHI_IF_UNSPEC);
911     assert(a);
912
913     /* If it isn't the MDNS port it can't be generated by us */
914     if (port != AVAHI_MDNS_PORT)
915         return 0;
916
917     return avahi_interface_has_address(s->monitor, iface, a);
918 }
919
920 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiAddress *dest, AvahiIfIndex iface, int ttl) {
921     AvahiInterface *i;
922     AvahiAddress a;
923     uint16_t port;
924     int from_local_iface = 0;
925     
926     assert(s);
927     assert(p);
928     assert(sa);
929     assert(dest);
930     assert(iface > 0);
931
932     if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
933         !avahi_interface_relevant(i)) {
934         avahi_log_warn("Recieved packet from invalid interface.");
935         return;
936     }
937
938 /*     avahi_log_debug("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
939
940     port = avahi_port_from_sockaddr(sa);
941     avahi_address_from_sockaddr(sa, &a);
942     
943     if (avahi_address_is_ipv4_in_ipv6(&a))
944         /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
945         return;
946
947     if (originates_from_local_legacy_unicast_socket(s, sa))
948         /* This originates from our local reflector, so let's ignore it */
949         return;
950
951     /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
952     if (s->config.enable_reflector)
953         from_local_iface = originates_from_local_iface(s, iface, &a, port);
954
955     if (avahi_dns_packet_is_valid(p) < 0) {
956         avahi_log_warn("Recieved invalid packet.");
957         return;
958     }
959
960     if (avahi_dns_packet_is_query(p)) {
961         int legacy_unicast = 0;
962
963         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
964             avahi_log_warn("Invalid query packet.");
965             return;
966         }
967
968         if (port != AVAHI_MDNS_PORT) {
969             /* Legacy Unicast */
970
971             if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
972                  avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
973                 avahi_log_warn("Invalid legacy unicast query packet.");
974                 return;
975             }
976         
977             legacy_unicast = 1;
978         }
979
980         if (legacy_unicast)
981             reflect_legacy_unicast_query_packet(s, p, i, &a, port);
982         
983         handle_query_packet(s, p, i, &a, port, legacy_unicast, from_local_iface);
984         
985 /*         avahi_log_debug("Handled query"); */
986     } else {
987         if (port != AVAHI_MDNS_PORT) {
988             avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
989             return;
990         }
991
992         if (ttl != 255 && s->config.check_response_ttl) {
993             avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
994             return;
995         }
996
997         if (!is_mdns_mcast_address(dest) &&
998             !avahi_interface_address_on_link(i, &a)) {
999             avahi_log_warn("Recivied non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
1000             return;
1001         }
1002         
1003         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
1004             avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
1005             avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
1006             avahi_log_warn("Invalid response packet.");
1007             return;
1008         }
1009
1010         handle_response_packet(s, p, i, &a, from_local_iface);
1011 /*         avahi_log_debug("Handled response"); */
1012     }
1013 }
1014
1015 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface, int ttl) {
1016     AvahiInterface *i, *j;
1017     AvahiAddress a;
1018     uint16_t port;
1019     AvahiLegacyUnicastReflectSlot *slot;
1020     
1021     assert(s);
1022     assert(p);
1023     assert(sa);
1024     assert(iface > 0);
1025
1026     if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
1027         !avahi_interface_relevant(i)) {
1028         avahi_log_warn("Recieved packet from invalid interface.");
1029         return;
1030     }
1031
1032 /*     avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
1033
1034     port = avahi_port_from_sockaddr(sa);
1035     avahi_address_from_sockaddr(sa, &a);
1036     
1037     if (avahi_address_is_ipv4_in_ipv6(&a))
1038         /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
1039         return;
1040
1041     if (avahi_dns_packet_is_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
1042         avahi_log_warn("Recieved invalid packet.");
1043         return;
1044     }
1045
1046     if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
1047         avahi_log_warn("Recieved legacy unicast response with unknown id");
1048         return;
1049     }
1050
1051     if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.family)) ||
1052         !avahi_interface_relevant(j))
1053         return;
1054
1055     /* Patch the original ID into this response */
1056     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
1057
1058     /* Forward the response to the correct client */
1059     avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
1060
1061     /* Undo changes to packet */
1062     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1063 }
1064
1065 static void socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1066     AvahiServer *s = userdata;
1067     AvahiAddress dest;
1068     AvahiDnsPacket *p;
1069     AvahiIfIndex iface;
1070     uint8_t ttl;
1071     struct sockaddr_in sa;
1072     struct sockaddr_in6 sa6;
1073
1074     assert(w);
1075     assert(fd >= 0);
1076
1077     if (events & AVAHI_WATCH_IN) {
1078     
1079         if (fd == s->fd_ipv4) {
1080             dest.family = AVAHI_PROTO_INET;
1081             if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1082                 dispatch_packet(s, p, (struct sockaddr*) &sa, &dest, iface, ttl);
1083                 avahi_dns_packet_free(p);
1084             }
1085         } else if (fd == s->fd_ipv6) {
1086             dest.family = AVAHI_PROTO_INET6;
1087
1088             if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1089                 dispatch_packet(s, p, (struct sockaddr*) &sa6, &dest, iface, ttl);
1090                 avahi_dns_packet_free(p);
1091             }
1092         } else if (fd == s->fd_legacy_unicast_ipv4) {
1093             dest.family = AVAHI_PROTO_INET;
1094             
1095             if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1096                 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
1097                 avahi_dns_packet_free(p);
1098             }
1099         } else if (fd == s->fd_legacy_unicast_ipv6) {
1100             dest.family = AVAHI_PROTO_INET6;
1101             
1102             if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1103                 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
1104                 avahi_dns_packet_free(p);
1105             }
1106         }
1107
1108         cleanup_dead(s);
1109     } else {
1110         assert(0);
1111     }
1112 }
1113
1114 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1115     assert(s);
1116
1117     if (s->state == state)
1118         return;
1119     
1120     s->state = state;
1121
1122     if (s->callback)
1123         s->callback(s, state, s->userdata);
1124 }
1125
1126 static void withdraw_host_rrs(AvahiServer *s) {
1127     assert(s);
1128
1129     if (s->hinfo_entry_group)
1130         avahi_s_entry_group_reset(s->hinfo_entry_group);
1131
1132     if (s->browse_domain_entry_group)
1133         avahi_s_entry_group_reset(s->browse_domain_entry_group);
1134
1135     avahi_update_host_rrs(s->monitor, 1);
1136     s->n_host_rr_pending = 0;
1137 }
1138
1139 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1140     assert(s);
1141     
1142     assert(s->n_host_rr_pending > 0);
1143
1144     if (--s->n_host_rr_pending == 0)
1145         server_set_state(s, AVAHI_SERVER_RUNNING);
1146 }
1147
1148 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1149     assert(s);
1150
1151     s->n_host_rr_pending ++;
1152 }
1153
1154 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1155     assert(s);
1156     assert(g);
1157
1158     if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1159         s->state == AVAHI_SERVER_REGISTERING)
1160         avahi_server_increase_host_rr_pending(s);
1161     
1162     else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1163         (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1164         withdraw_host_rrs(s);
1165         server_set_state(s, AVAHI_SERVER_COLLISION);
1166         
1167     } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1168                s->state == AVAHI_SERVER_REGISTERING)
1169         avahi_server_decrease_host_rr_pending(s);
1170 }
1171
1172 static void register_hinfo(AvahiServer *s) {
1173     struct utsname utsname;
1174     AvahiRecord *r;
1175     
1176     assert(s);
1177     
1178     if (!s->config.publish_hinfo)
1179         return;
1180
1181     if (s->hinfo_entry_group)
1182         assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1183     else
1184         s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1185
1186     if (!s->hinfo_entry_group) {
1187         avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1188         return;
1189     }
1190     
1191     /* Fill in HINFO rr */
1192     if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1193         uname(&utsname);
1194         r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1195         r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1196
1197         if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_UNIQUE, r) < 0) {
1198             avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1199             return;
1200         }
1201
1202         avahi_record_unref(r);
1203     }
1204
1205     if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1206         avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1207
1208 }
1209
1210 static void register_localhost(AvahiServer *s) {
1211     AvahiAddress a;
1212     assert(s);
1213     
1214     /* Add localhost entries */
1215     avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1216     avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1217
1218     avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1219     avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1220 }
1221
1222 static void register_browse_domain(AvahiServer *s) {
1223     assert(s);
1224
1225     if (!s->config.publish_domain)
1226         return;
1227
1228     if (s->browse_domain_entry_group)
1229         assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1230     else
1231         s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1232
1233     if (!s->browse_domain_entry_group) {
1234         avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1235         return;
1236     }
1237     
1238     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) {
1239         avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1240         return;
1241     }
1242
1243     if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1244         avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1245 }
1246
1247 static void register_stuff(AvahiServer *s) {
1248     assert(s);
1249
1250     server_set_state(s, AVAHI_SERVER_REGISTERING);
1251     s->n_host_rr_pending ++;  /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1252
1253     register_hinfo(s);
1254     register_browse_domain(s);
1255     avahi_update_host_rrs(s->monitor, 0);
1256
1257     s->n_host_rr_pending --;
1258     
1259     if (s->n_host_rr_pending == 0)
1260         server_set_state(s, AVAHI_SERVER_RUNNING);
1261 }
1262
1263 static void update_fqdn(AvahiServer *s) {
1264     char *n;
1265     
1266     assert(s);
1267     assert(s->host_name);
1268     assert(s->domain_name);
1269
1270     if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1271         return; /* OOM */
1272
1273     avahi_free(s->host_name_fqdn);
1274     s->host_name_fqdn = n;
1275 }
1276
1277 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1278     assert(s);
1279     assert(host_name);
1280
1281     if (host_name && !avahi_is_valid_host_name(host_name))
1282         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1283
1284     withdraw_host_rrs(s);
1285
1286     avahi_free(s->host_name);
1287     s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1288     s->host_name[strcspn(s->host_name, ".")] = 0;
1289     update_fqdn(s);
1290
1291     register_stuff(s);
1292     return AVAHI_OK;
1293 }
1294
1295 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1296     assert(s);
1297     assert(domain_name);
1298
1299     if (domain_name && !avahi_is_valid_domain_name(domain_name))
1300         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1301
1302     withdraw_host_rrs(s);
1303
1304     avahi_free(s->domain_name);
1305     s->domain_name = domain_name ? avahi_normalize_name(domain_name) : avahi_strdup("local");
1306     update_fqdn(s);
1307
1308     register_stuff(s);
1309     return AVAHI_OK;
1310 }
1311
1312 static int valid_server_config(const AvahiServerConfig *sc) {
1313
1314     if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1315         return AVAHI_ERR_INVALID_HOST_NAME;
1316     
1317     if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1318         return AVAHI_ERR_INVALID_DOMAIN_NAME;
1319
1320     return AVAHI_OK;
1321 }
1322
1323 static int setup_sockets(AvahiServer *s) {
1324     assert(s);
1325     
1326     s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1327     s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1328     
1329     if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1330         return AVAHI_ERR_NO_NETWORK;
1331
1332     if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1333         avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1334     else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1335         avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1336
1337     s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1338     s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1339
1340     s->watch_ipv4 = s->watch_ipv6 = s->watch_legacy_unicast_ipv4 = s->watch_legacy_unicast_ipv6 = NULL;
1341     
1342     if (s->fd_ipv4 >= 0)
1343         s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, socket_event, s);
1344     if (s->fd_ipv6 >= 0)
1345         s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, socket_event, s);
1346     if (s->fd_legacy_unicast_ipv4 >= 0)
1347         s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, socket_event, s);
1348     if (s->fd_legacy_unicast_ipv6 >= 0)
1349         s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, socket_event, s);
1350
1351     return 0;
1352 }
1353
1354 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1355     AvahiServer *s;
1356     int e;
1357     
1358     if ((e = valid_server_config(sc)) < 0) {
1359         if (error)
1360             *error = e;
1361         return NULL;
1362     }
1363     
1364     if (!(s = avahi_new(AvahiServer, 1))) {
1365         if (error)
1366             *error = AVAHI_ERR_NO_MEMORY;
1367
1368         return NULL;
1369     }
1370
1371     s->poll_api = poll_api;
1372
1373     if (sc)
1374         avahi_server_config_copy(&s->config, sc);
1375     else
1376         avahi_server_config_init(&s->config);
1377
1378     if ((e = setup_sockets(s)) < 0) {
1379         if (error)
1380             *error = e;
1381
1382         avahi_server_config_free(&s->config);
1383         avahi_free(s);
1384         
1385         return NULL;
1386     }
1387
1388     s->n_host_rr_pending = 0;
1389     s->need_entry_cleanup = 0;
1390     s->need_group_cleanup = 0;
1391     s->need_browser_cleanup = 0;
1392     
1393     s->time_event_queue = avahi_time_event_queue_new(poll_api);
1394     
1395     s->callback = callback;
1396     s->userdata = userdata;
1397     
1398     s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1399     AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1400     AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1401
1402     s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1403     AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1404     AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1405     AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1406     AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1407     AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1408     AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1409     AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1410     AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1411
1412     s->legacy_unicast_reflect_slots = NULL;
1413     s->legacy_unicast_reflect_id = 0;
1414     
1415     /* Get host name */
1416     s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1417     s->host_name[strcspn(s->host_name, ".")] = 0;
1418     s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : avahi_strdup("local");
1419     s->host_name_fqdn = NULL;
1420     update_fqdn(s);
1421
1422     s->record_list = avahi_record_list_new();
1423
1424     s->state = AVAHI_SERVER_INVALID;
1425
1426     s->monitor = avahi_interface_monitor_new(s);
1427     avahi_interface_monitor_sync(s->monitor);
1428
1429     register_localhost(s);
1430
1431     s->hinfo_entry_group = NULL;
1432     s->browse_domain_entry_group = NULL;
1433     register_stuff(s);
1434
1435     s->error = AVAHI_OK;
1436     
1437     return s;
1438 }
1439
1440 void avahi_server_free(AvahiServer* s) {
1441     assert(s);
1442
1443     while(s->entries)
1444         free_entry(s, s->entries);
1445
1446     avahi_interface_monitor_free(s->monitor);
1447
1448     while (s->groups)
1449         free_group(s, s->groups);
1450
1451     free_slots(s);
1452
1453     while (s->dns_server_browsers)
1454         avahi_s_dns_server_browser_free(s->dns_server_browsers);
1455     while (s->host_name_resolvers)
1456         avahi_s_host_name_resolver_free(s->host_name_resolvers);
1457     while (s->address_resolvers)
1458         avahi_s_address_resolver_free(s->address_resolvers);
1459     while (s->domain_browsers)
1460         avahi_s_domain_browser_free(s->domain_browsers);
1461     while (s->service_type_browsers)
1462         avahi_s_service_type_browser_free(s->service_type_browsers);
1463     while (s->service_browsers)
1464         avahi_s_service_browser_free(s->service_browsers);
1465     while (s->service_resolvers)
1466         avahi_s_service_resolver_free(s->service_resolvers);
1467     while (s->record_browsers)
1468         avahi_s_record_browser_destroy(s->record_browsers);
1469     
1470     avahi_hashmap_free(s->record_browser_hashmap);
1471     avahi_hashmap_free(s->entries_by_key);
1472
1473     avahi_time_event_queue_free(s->time_event_queue);
1474
1475     avahi_record_list_free(s->record_list);
1476
1477     if (s->watch_ipv4)
1478         s->poll_api->watch_free(s->watch_ipv4);
1479     if (s->watch_ipv6)
1480         s->poll_api->watch_free(s->watch_ipv6);
1481     if (s->watch_legacy_unicast_ipv4)
1482         s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1483     if (s->watch_legacy_unicast_ipv6)
1484         s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1485     
1486     if (s->fd_ipv4 >= 0)
1487         close(s->fd_ipv4);
1488     if (s->fd_ipv6 >= 0)
1489         close(s->fd_ipv6);
1490     if (s->fd_legacy_unicast_ipv4 >= 0)
1491         close(s->fd_legacy_unicast_ipv4);
1492     if (s->fd_legacy_unicast_ipv6 >= 0)
1493         close(s->fd_legacy_unicast_ipv6);
1494
1495     avahi_free(s->host_name);
1496     avahi_free(s->domain_name);
1497     avahi_free(s->host_name_fqdn);
1498
1499     avahi_server_config_free(&s->config);
1500
1501     avahi_free(s);
1502 }
1503
1504 static int check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiEntryFlags flags) {
1505     AvahiEntry *e;
1506     
1507     assert(s);
1508     assert(r);
1509
1510     for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1511         if (e->dead)
1512             continue;
1513
1514         if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
1515             continue;
1516         
1517         if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
1518             continue;
1519
1520         if ((interface <= 0 ||
1521              e->interface <= 0 ||
1522              e->interface == interface) &&
1523             (protocol == AVAHI_PROTO_UNSPEC ||
1524              e->protocol == AVAHI_PROTO_UNSPEC ||
1525              e->protocol == protocol))
1526
1527             return -1;
1528     }
1529
1530     return 0;
1531 }
1532
1533 int avahi_server_add(
1534     AvahiServer *s,
1535     AvahiSEntryGroup *g,
1536     AvahiIfIndex interface,
1537     AvahiProtocol protocol,
1538     AvahiEntryFlags flags,
1539     AvahiRecord *r) {
1540     
1541     AvahiEntry *e, *t;
1542     
1543     assert(s);
1544     assert(r);
1545
1546     if (r->ttl == 0)
1547         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_TTL);
1548
1549     if (avahi_key_is_pattern(r->key))
1550         return avahi_server_set_errno(s, AVAHI_ERR_IS_PATTERN);
1551
1552     if (!avahi_record_is_valid(r))
1553         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_RECORD);
1554
1555     if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1556         return avahi_server_set_errno(s, AVAHI_ERR_LOCAL_COLLISION);
1557
1558     if (!(e = avahi_new(AvahiEntry, 1)))
1559         return avahi_server_set_errno(s, AVAHI_ERR_NO_NETWORK);
1560         
1561     e->server = s;
1562     e->record = avahi_record_ref(r);
1563     e->group = g;
1564     e->interface = interface;
1565     e->protocol = protocol;
1566     e->flags = flags;
1567     e->dead = 0;
1568
1569     AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1570
1571     AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1572
1573     /* Insert into hash table indexed by name */
1574     t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
1575     AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1576     avahi_hashmap_replace(s->entries_by_key, e->record->key, t);
1577
1578     /* Insert into group list */
1579     if (g)
1580         AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e); 
1581
1582     avahi_announce_entry(s, e);
1583
1584     return 0;
1585 }
1586
1587 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state) {
1588     AvahiEntry **e = (AvahiEntry**) state;
1589     assert(s);
1590     assert(e);
1591
1592     if (!*e)
1593         *e = g ? g->entries : s->entries;
1594     
1595     while (*e && (*e)->dead)
1596         *e = g ? (*e)->by_group_next : (*e)->entries_next;
1597         
1598     if (!*e)
1599         return NULL;
1600
1601     return avahi_record_ref((*e)->record);
1602 }
1603
1604 int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata) {
1605     AvahiEntry *e;
1606     
1607     assert(s);
1608     assert(callback);
1609
1610     callback(";;; ZONE DUMP FOLLOWS ;;;", userdata);
1611
1612     for (e = s->entries; e; e = e->entries_next) {
1613         char *t;
1614         char ln[256];
1615
1616         if (e->dead)
1617             continue;
1618         
1619         if (!(t = avahi_record_to_string(e->record)))
1620             return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1621         
1622         snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
1623         avahi_free(t);
1624
1625         callback(ln, userdata);
1626     }
1627
1628     avahi_dump_caches(s->monitor, callback, userdata);
1629     return AVAHI_OK;
1630 }
1631
1632 int avahi_server_add_ptr(
1633     AvahiServer *s,
1634     AvahiSEntryGroup *g,
1635     AvahiIfIndex interface,
1636     AvahiProtocol protocol,
1637     AvahiEntryFlags flags,
1638     uint32_t ttl,
1639     const char *name,
1640     const char *dest) {
1641
1642     AvahiRecord *r;
1643     int ret;
1644
1645     assert(s);
1646     assert(dest);
1647
1648     if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl)))
1649         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1650         
1651     r->data.ptr.name = avahi_normalize_name(dest);
1652     ret = avahi_server_add(s, g, interface, protocol, flags, r);
1653     avahi_record_unref(r);
1654     return ret;
1655 }
1656
1657 int avahi_server_add_address(
1658     AvahiServer *s,
1659     AvahiSEntryGroup *g,
1660     AvahiIfIndex interface,
1661     AvahiProtocol protocol,
1662     AvahiEntryFlags flags,
1663     const char *name,
1664     AvahiAddress *a) {
1665
1666     char *n = NULL;
1667     int ret = AVAHI_OK;
1668     assert(s);
1669     assert(a);
1670
1671     if (name) {
1672         if (!(n = avahi_normalize_name(name)))
1673             return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1674
1675         name = n;
1676     } else
1677         name = s->host_name_fqdn;
1678
1679     if (!avahi_is_valid_domain_name(name)) {
1680         ret = avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1681         goto fail;
1682     }
1683     
1684     if (a->family == AVAHI_PROTO_INET) {
1685         char *reverse;
1686         AvahiRecord  *r;
1687
1688         if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1689             ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1690             goto fail;
1691         }
1692         
1693         r->data.a.address = a->data.ipv4;
1694         ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1695         avahi_record_unref(r);
1696
1697         if (ret < 0)
1698             goto fail;
1699         
1700         if (!(reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4))) {
1701             ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1702             goto fail;
1703         }
1704
1705         ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1706         avahi_free(reverse);
1707
1708     } else {
1709         char *reverse;
1710         AvahiRecord *r;
1711
1712         assert(a->family == AVAHI_PROTO_INET6);
1713             
1714         if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1715             ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1716             goto fail;
1717         }
1718         
1719         r->data.aaaa.address = a->data.ipv6;
1720         ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1721         avahi_record_unref(r);
1722
1723         if (ret < 0)
1724             goto fail;
1725
1726         if (!(reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6))) {
1727             ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1728             goto fail;
1729         }
1730             
1731         ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1732         avahi_free(reverse);
1733
1734         if (ret < 0)
1735             goto fail;
1736     
1737         if (!(reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6))) {
1738             ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1739             goto fail;
1740         }
1741
1742         ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1743         avahi_free(reverse);
1744     }
1745
1746 fail:
1747     
1748     avahi_free(n);
1749
1750     return ret;
1751 }
1752
1753 static int server_add_txt_strlst_nocopy(
1754     AvahiServer *s,
1755     AvahiSEntryGroup *g,
1756     AvahiIfIndex interface,
1757     AvahiProtocol protocol,
1758     AvahiEntryFlags flags,
1759     uint32_t ttl,
1760     const char *name,
1761     AvahiStringList *strlst) {
1762
1763     AvahiRecord *r;
1764     int ret;
1765     
1766     assert(s);
1767     
1768     if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl)))
1769         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1770     
1771     r->data.txt.string_list = strlst;
1772     ret = avahi_server_add(s, g, interface, protocol, flags, r);
1773     avahi_record_unref(r);
1774
1775     return ret;
1776 }
1777
1778 int avahi_server_add_txt_strlst(
1779     AvahiServer *s,
1780     AvahiSEntryGroup *g,
1781     AvahiIfIndex interface,
1782     AvahiProtocol protocol,
1783     AvahiEntryFlags flags,
1784     uint32_t ttl,
1785     const char *name,
1786     AvahiStringList *strlst) {
1787
1788     assert(s);
1789
1790     return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_copy(strlst));
1791 }
1792
1793 int avahi_server_add_txt_va(
1794     AvahiServer *s,
1795     AvahiSEntryGroup *g,
1796     AvahiIfIndex interface,
1797     AvahiProtocol protocol,
1798     AvahiEntryFlags flags,
1799     uint32_t ttl,
1800     const char *name,
1801     va_list va) {
1802
1803     assert(s);
1804
1805     return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_new_va(va));
1806 }
1807
1808 int avahi_server_add_txt(
1809     AvahiServer *s,
1810     AvahiSEntryGroup *g,
1811     AvahiIfIndex interface,
1812     AvahiProtocol protocol,
1813     AvahiEntryFlags flags,
1814     uint32_t ttl,
1815     const char *name,
1816     ...) {
1817
1818     va_list va;
1819     int ret;
1820     
1821     assert(s);
1822
1823     va_start(va, name);
1824     ret = avahi_server_add_txt_va(s, g, interface, protocol, flags, ttl, name, va);
1825     va_end(va);
1826
1827     return ret;
1828 }
1829
1830 static void escape_service_name(char *d, size_t size, const char *s) {
1831     assert(d);
1832     assert(size);
1833     assert(s);
1834
1835     while (*s && size >= 2) {
1836         if (*s == '.' || *s == '\\') {
1837             if (size < 3)
1838                 break;
1839
1840             *(d++) = '\\';
1841             size--;
1842         }
1843             
1844         *(d++) = *(s++);
1845         size--;
1846     }
1847
1848     assert(size > 0);
1849     *(d++) = 0;
1850 }
1851
1852 static int server_add_service_strlst_nocopy(
1853     AvahiServer *s,
1854     AvahiSEntryGroup *g,
1855     AvahiIfIndex interface,
1856     AvahiProtocol protocol,
1857     const char *name,
1858     const char *type,
1859     const char *domain,
1860     const char *host,
1861     uint16_t port,
1862     AvahiStringList *strlst) {
1863
1864     char ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1865     char *t = NULL, *d = NULL, *h = NULL;
1866     AvahiRecord *r = NULL;
1867     int ret = AVAHI_OK;
1868     
1869     assert(s);
1870     assert(type);
1871     assert(name);
1872
1873     if (!avahi_is_valid_service_name(name))
1874         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_NAME);
1875
1876     if (!avahi_is_valid_service_type(type))
1877         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_TYPE);
1878
1879     if (domain && !avahi_is_valid_domain_name(domain))
1880         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1881
1882     if (host && !avahi_is_valid_domain_name(host))
1883         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1884
1885     escape_service_name(ename, sizeof(ename), name);
1886
1887     if (!domain)
1888         domain = s->domain_name;
1889
1890     if (!host)
1891         host = s->host_name_fqdn;
1892
1893     if (!(d = avahi_normalize_name(domain)) ||
1894         !(t = avahi_normalize_name(type)) ||
1895         !(h = avahi_normalize_name(host))) {
1896         ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1897         goto fail;
1898     }
1899
1900     snprintf(ptr_name, sizeof(ptr_name), "%s.%s", t, d);
1901     snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, t, d);
1902
1903     if ((ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0)
1904         goto fail;
1905
1906     if (!(r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1907         ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1908         goto fail;
1909     }
1910     
1911     r->data.srv.priority = 0;
1912     r->data.srv.weight = 0;
1913     r->data.srv.port = port;
1914     r->data.srv.name = h;
1915     h = NULL;
1916     ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1917     avahi_record_unref(r);
1918
1919     if (ret < 0)
1920         goto fail;
1921
1922     ret = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
1923     strlst = NULL;
1924
1925     if (ret < 0)
1926         goto fail;
1927
1928     snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", d);
1929     ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name);
1930
1931 fail:
1932     
1933     avahi_free(d);
1934     avahi_free(t);
1935     avahi_free(h);
1936
1937     avahi_string_list_free(strlst);
1938     
1939     return ret;
1940 }
1941
1942 int avahi_server_add_service_strlst(
1943     AvahiServer *s,
1944     AvahiSEntryGroup *g,
1945     AvahiIfIndex interface,
1946     AvahiProtocol protocol,
1947     const char *name,
1948     const char *type,
1949     const char *domain,
1950     const char *host,
1951     uint16_t port,
1952     AvahiStringList *strlst) {
1953
1954     assert(s);
1955     assert(type);
1956     assert(name);
1957
1958     return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_copy(strlst));
1959 }
1960
1961 int avahi_server_add_service_va(
1962     AvahiServer *s,
1963     AvahiSEntryGroup *g,
1964     AvahiIfIndex interface,
1965     AvahiProtocol protocol,
1966     const char *name,
1967     const char *type,
1968     const char *domain,
1969     const char *host,
1970     uint16_t port,
1971     va_list va){
1972
1973     assert(s);
1974     assert(type);
1975     assert(name);
1976
1977     return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_new_va(va));
1978 }
1979
1980 int avahi_server_add_service(
1981     AvahiServer *s,
1982     AvahiSEntryGroup *g,
1983     AvahiIfIndex interface,
1984     AvahiProtocol protocol,
1985     const char *name,
1986     const char *type,
1987     const char *domain,
1988     const char *host,
1989     uint16_t port,
1990     ... ){
1991
1992     va_list va;
1993     int ret;
1994     
1995     assert(s);
1996     assert(type);
1997     assert(name);
1998
1999     va_start(va, port);
2000     ret = avahi_server_add_service_va(s, g, interface, protocol, name, type, domain, host, port, va);
2001     va_end(va);
2002     return ret;
2003 }
2004
2005 static void hexstring(char *s, size_t sl, const void *p, size_t pl) {
2006     static const char hex[] = "0123456789abcdef";
2007     int b = 0;
2008     const uint8_t *k = p;
2009
2010     while (sl > 1 && pl > 0) {
2011         *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
2012
2013         if (b) {
2014             k++;
2015             pl--;
2016         }
2017         
2018         b = !b;
2019
2020         sl--;
2021     }
2022
2023     if (sl > 0)
2024         *s = 0;
2025 }
2026
2027 int avahi_server_add_dns_server_address(
2028     AvahiServer *s,
2029     AvahiSEntryGroup *g,
2030     AvahiIfIndex interface,
2031     AvahiProtocol protocol,
2032     const char *domain,
2033     AvahiDNSServerType type,
2034     const AvahiAddress *address,
2035     uint16_t port /** should be 53 */) {
2036
2037     AvahiRecord *r;
2038     int ret;
2039     char n[64] = "ip-";
2040
2041     assert(s);
2042     assert(address);
2043     assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
2044     assert(address->family == AVAHI_PROTO_INET || address->family == AVAHI_PROTO_INET6);
2045
2046     if (port == 0)
2047         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
2048     
2049     if (domain && !avahi_is_valid_domain_name(domain))
2050         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
2051
2052     if (address->family == AVAHI_PROTO_INET) {
2053         hexstring(n+3, sizeof(n)-3, &address->data, 4);
2054         r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
2055         r->data.a.address = address->data.ipv4;
2056     } else {
2057         hexstring(n+3, sizeof(n)-3, &address->data, 6);
2058         r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
2059         r->data.aaaa.address = address->data.ipv6;
2060     }
2061
2062     if (!r)
2063         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2064     
2065     ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
2066     avahi_record_unref(r);
2067
2068     if (ret < 0)
2069         return ret;
2070     
2071     return avahi_server_add_dns_server_name(s, g, interface, protocol, domain, type, n, port);
2072 }
2073
2074 int avahi_server_add_dns_server_name(
2075     AvahiServer *s,
2076     AvahiSEntryGroup *g,
2077     AvahiIfIndex interface,
2078     AvahiProtocol protocol,
2079     const char *domain,
2080     AvahiDNSServerType type,
2081     const char *name,
2082     uint16_t port /** should be 53 */) {
2083
2084     int ret = -1;
2085     char t[256], *d = NULL, *n = NULL;
2086     AvahiRecord *r;
2087     
2088     assert(s);
2089     assert(name);
2090     assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
2091
2092     if (port == 0)
2093         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
2094
2095     if (!avahi_is_valid_domain_name(name))
2096         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
2097
2098     if (domain && !avahi_is_valid_domain_name(domain))
2099         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
2100
2101     
2102     if (!domain)
2103         domain = s->domain_name;
2104
2105     if (!(n = avahi_normalize_name(name)) ||
2106         !(d = avahi_normalize_name(domain))) {
2107         avahi_free(n);
2108         avahi_free(d);
2109         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2110     }
2111
2112     snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", d);
2113     avahi_free(d);
2114     
2115     if (!(r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
2116         avahi_free(n);
2117         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2118     }
2119     
2120     r->data.srv.priority = 0;
2121     r->data.srv.weight = 0;
2122     r->data.srv.port = port;
2123     r->data.srv.name = n;
2124     ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_NULL, r);
2125     avahi_record_unref(r);
2126
2127     return ret;
2128 }
2129
2130 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
2131     AvahiKey *k = userdata;
2132
2133     assert(m);
2134     assert(i);
2135     assert(k);
2136
2137     avahi_interface_post_query(i, k, 0);
2138 }
2139
2140 void avahi_server_post_query(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
2141     assert(s);
2142     assert(key);
2143
2144     avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
2145 }
2146
2147 void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState state) {
2148     assert(g);
2149
2150     if (g->state == state)
2151         return;
2152
2153     assert(state <= AVAHI_ENTRY_GROUP_COLLISION);
2154
2155     g->state = state;
2156     
2157     if (g->callback)
2158         g->callback(g->server, g, state, g->userdata);
2159 }
2160
2161 AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata) {
2162     AvahiSEntryGroup *g;
2163     
2164     assert(s);
2165
2166     if (!(g = avahi_new(AvahiSEntryGroup, 1))) {
2167         avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2168         return NULL;
2169     }
2170     
2171     g->server = s;
2172     g->callback = callback;
2173     g->userdata = userdata;
2174     g->dead = 0;
2175     g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
2176     g->n_probing = 0;
2177     g->n_register_try = 0;
2178     g->register_time_event = NULL;
2179     g->register_time.tv_sec = 0;
2180     g->register_time.tv_usec = 0;
2181     AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
2182
2183     AVAHI_LLIST_PREPEND(AvahiSEntryGroup, groups, s->groups, g);
2184     return g;
2185 }
2186
2187 void avahi_s_entry_group_free(AvahiSEntryGroup *g) {
2188     AvahiEntry *e;
2189     
2190     assert(g);
2191     assert(g->server);
2192
2193     for (e = g->entries; e; e = e->by_group_next) {
2194         if (!e->dead) {
2195             avahi_goodbye_entry(g->server, e, 1);
2196             e->dead = 1;
2197         }
2198     }
2199
2200     if (g->register_time_event) {
2201         avahi_time_event_free(g->register_time_event);
2202         g->register_time_event = NULL;
2203     }
2204
2205     g->dead = 1;
2206     
2207     g->server->need_group_cleanup = 1;
2208     g->server->need_entry_cleanup = 1;
2209 }
2210
2211 static void entry_group_commit_real(AvahiSEntryGroup *g) {
2212     assert(g);
2213
2214     gettimeofday(&g->register_time, NULL);
2215
2216     avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2217
2218     if (!g->dead) {
2219         avahi_announce_group(g->server, g);
2220         avahi_s_entry_group_check_probed(g, 0);
2221     }
2222 }
2223
2224 static void entry_group_register_time_event_callback(AvahiTimeEvent *e, void* userdata) {
2225     AvahiSEntryGroup *g = userdata;
2226     assert(g);
2227
2228 /*     avahi_log_debug("Holdoff passed, waking up and going on."); */
2229
2230     avahi_time_event_free(g->register_time_event);
2231     g->register_time_event = NULL;
2232     
2233     /* Holdoff time passed, so let's start probing */
2234     entry_group_commit_real(g);
2235 }
2236
2237 int avahi_s_entry_group_commit(AvahiSEntryGroup *g) {
2238     struct timeval now;
2239     
2240     assert(g);
2241     assert(!g->dead);
2242
2243     if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION)
2244         return avahi_server_set_errno(g->server, AVAHI_ERR_BAD_STATE);
2245
2246     g->n_register_try++;
2247
2248     avahi_timeval_add(&g->register_time,
2249                       1000*(g->n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ?
2250                             AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT :
2251                             AVAHI_RR_HOLDOFF_MSEC));
2252
2253     gettimeofday(&now, NULL);
2254
2255     if (avahi_timeval_compare(&g->register_time, &now) <= 0) {
2256         /* Holdoff time passed, so let's start probing */
2257 /*         avahi_log_debug("Holdoff passed, directly going on.");  */
2258
2259         entry_group_commit_real(g);
2260     } else {
2261 /*          avahi_log_debug("Holdoff not passed, sleeping.");  */
2262
2263          /* Holdoff time has not yet passed, so let's wait */
2264         assert(!g->register_time_event);
2265         g->register_time_event = avahi_time_event_new(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g);
2266         
2267         avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2268     }
2269
2270     return AVAHI_OK;
2271 }
2272
2273 void avahi_s_entry_group_reset(AvahiSEntryGroup *g) {
2274     AvahiEntry *e;
2275     assert(g);
2276     
2277     if (g->register_time_event) {
2278         avahi_time_event_free(g->register_time_event);
2279         g->register_time_event = NULL;
2280     }
2281     
2282     for (e = g->entries; e; e = e->by_group_next) {
2283         if (!e->dead) {
2284             avahi_goodbye_entry(g->server, e, 1);
2285             e->dead = 1;
2286         }
2287     }
2288
2289     if (g->register_time_event) {
2290         avahi_time_event_free(g->register_time_event);
2291         g->register_time_event = NULL;
2292     }
2293     
2294     g->server->need_entry_cleanup = 1;
2295     g->n_probing = 0;
2296
2297     avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED);
2298 }
2299
2300 int avahi_entry_is_commited(AvahiEntry *e) {
2301     assert(e);
2302     assert(!e->dead);
2303
2304     return !e->group ||
2305         e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
2306         e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
2307 }
2308
2309 AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g) {
2310     assert(g);
2311     assert(!g->dead);
2312
2313     return g->state;
2314 }
2315
2316 void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata) {
2317     assert(g);
2318
2319     g->userdata = userdata;
2320 }
2321
2322 void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g) {
2323     assert(g);
2324
2325     return g->userdata;
2326 }
2327
2328 int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g) {
2329     AvahiEntry *e;
2330     assert(g);
2331
2332     /* Look for an entry that is not dead */
2333     for (e = g->entries; e; e = e->by_group_next)
2334         if (!e->dead)
2335             return 0;
2336
2337     return 1;
2338 }
2339
2340 const char* avahi_server_get_domain_name(AvahiServer *s) {
2341     assert(s);
2342
2343     return s->domain_name;
2344 }
2345
2346 const char* avahi_server_get_host_name(AvahiServer *s) {
2347     assert(s);
2348
2349     return s->host_name;
2350 }
2351
2352 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
2353     assert(s);
2354
2355     return s->host_name_fqdn;
2356 }
2357
2358 void* avahi_server_get_data(AvahiServer *s) {
2359     assert(s);
2360
2361     return s->userdata;
2362 }
2363
2364 void avahi_server_set_data(AvahiServer *s, void* userdata) {
2365     assert(s);
2366
2367     s->userdata = userdata;
2368 }
2369
2370 AvahiServerState avahi_server_get_state(AvahiServer *s) {
2371     assert(s);
2372
2373     return s->state;
2374 }
2375
2376 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
2377     assert(c);
2378
2379     memset(c, 0, sizeof(AvahiServerConfig));
2380     c->use_ipv6 = 1;
2381     c->use_ipv4 = 1;
2382     c->host_name = NULL;
2383     c->domain_name = NULL;
2384     c->check_response_ttl = 0;
2385     c->publish_hinfo = 1;
2386     c->publish_addresses = 1;
2387     c->publish_workstation = 1;
2388     c->publish_domain = 1;
2389     c->use_iff_running = 0;
2390     c->enable_reflector = 0;
2391     c->reflect_ipv = 0;
2392     
2393     return c;
2394 }
2395
2396 void avahi_server_config_free(AvahiServerConfig *c) {
2397     assert(c);
2398
2399     avahi_free(c->host_name);
2400     avahi_free(c->domain_name);
2401 }
2402
2403 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
2404     char *d = NULL, *h = NULL;
2405     assert(ret);
2406     assert(c);
2407
2408     if (c->host_name)
2409         if (!(h = avahi_strdup(c->host_name)))
2410             return NULL;
2411
2412     if (c->domain_name)
2413         if (!(d = avahi_strdup(c->domain_name))) {
2414             avahi_free(h);
2415             return NULL;
2416         }
2417     
2418     *ret = *c;
2419     ret->host_name = h;
2420     ret->domain_name = d;
2421
2422     return ret;
2423 }
2424
2425 int avahi_server_errno(AvahiServer *s) {
2426     assert(s);
2427     
2428     return s->error;
2429 }
2430
2431 /* Just for internal use */
2432 int avahi_server_set_errno(AvahiServer *s, int error) {
2433     assert(s);
2434
2435     return s->error = error;
2436 }
2437