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