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