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