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