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