]> git.meshlink.io Git - catta/blob - avahi-core/server.c
get rid of a lot of old svn cruft
[catta] / avahi-core / server.c
1 /***
2   This file is part of avahi.
3
4   avahi is free software; you can redistribute it and/or modify it
5   under the terms of the GNU Lesser General Public License as
6   published by the Free Software Foundation; either version 2.1 of the
7   License, or (at your option) any later version.
8
9   avahi is distributed in the hope that it will be useful, but WITHOUT
10   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12   Public License for more details.
13
14   You should have received a copy of the GNU Lesser General Public
15   License along with avahi; if not, write to the Free Software
16   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17   USA.
18 ***/
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <netinet/in.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 "internal.h"
42 #include "iface.h"
43 #include "socket.h"
44 #include "browse.h"
45 #include "log.h"
46 #include "util.h"
47 #include "dns-srv-rr.h"
48 #include "addr-util.h"
49 #include "domain-util.h"
50 #include "rr-util.h"
51
52 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) {
53     assert(s);
54     assert(i);
55     assert(name);
56     assert(callback);
57
58     if (type == AVAHI_DNS_TYPE_ANY) {
59         AvahiEntry *e;
60
61         for (e = s->entries; e; e = e->entries_next)
62             if (!e->dead &&
63                 avahi_entry_is_registered(s, e, i) &&
64                 e->record->key->clazz == AVAHI_DNS_CLASS_IN &&
65                 avahi_domain_equal(name, e->record->key->name))
66                 callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata);
67
68     } else {
69         AvahiEntry *e;
70         AvahiKey *k;
71
72         if (!(k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type)))
73             return; /** OOM */
74
75         for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
76             if (!e->dead && avahi_entry_is_registered(s, e, i))
77                 callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata);
78
79         avahi_key_unref(k);
80     }
81 }
82
83 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) {
84     assert(s);
85     assert(i);
86     assert(r);
87     assert(callback);
88
89     /* Call the specified callback far all records referenced by the one specified in *r */
90
91     if (r->key->clazz == AVAHI_DNS_CLASS_IN) {
92         if (r->key->type == AVAHI_DNS_TYPE_PTR) {
93             enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
94             enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
95         } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
96             enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
97             enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
98         } else if (r->key->type == AVAHI_DNS_TYPE_CNAME)
99             enum_aux_records(s, i, r->data.cname.name, AVAHI_DNS_TYPE_ANY, callback, userdata);
100     }
101 }
102
103 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary) {
104     assert(s);
105     assert(i);
106     assert(e);
107
108     avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, unicast_response, auxiliary);
109 }
110
111 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response) {
112     assert(s);
113     assert(i);
114     assert(k);
115
116     /* Push all records that match the specified key to the record list */
117
118     if (avahi_key_is_pattern(k)) {
119         AvahiEntry *e;
120
121         /* Handle ANY query */
122
123         for (e = s->entries; e; e = e->entries_next)
124             if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_is_registered(s, e, i))
125                 avahi_server_prepare_response(s, i, e, unicast_response, 0);
126
127     } else {
128         AvahiEntry *e;
129
130         /* Handle all other queries */
131
132         for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
133             if (!e->dead && avahi_entry_is_registered(s, e, i))
134                 avahi_server_prepare_response(s, i, e, unicast_response, 0);
135     }
136
137     /* Look for CNAME records */
138
139     if ((k->clazz == AVAHI_DNS_CLASS_IN || k->clazz == AVAHI_DNS_CLASS_ANY)
140         && k->type != AVAHI_DNS_TYPE_CNAME && k->type != AVAHI_DNS_TYPE_ANY) {
141
142         AvahiKey *cname_key;
143
144         if (!(cname_key = avahi_key_new(k->name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_CNAME)))
145             return;
146
147         avahi_server_prepare_matching_responses(s, i, cname_key, unicast_response);
148         avahi_key_unref(cname_key);
149     }
150 }
151
152 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
153     assert(s);
154     assert(e);
155
156     /* Withdraw the specified entry, and if is part of an entry group,
157      * put that into COLLISION state */
158
159     if (e->dead)
160         return;
161
162     if (e->group) {
163         AvahiEntry *k;
164
165         for (k = e->group->entries; k; k = k->by_group_next)
166             if (!k->dead) {
167                 avahi_goodbye_entry(s, k, 0, 1);
168                 k->dead = 1;
169             }
170
171         e->group->n_probing = 0;
172
173         avahi_s_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
174     } else {
175         avahi_goodbye_entry(s, e, 0, 1);
176         e->dead = 1;
177     }
178
179     s->need_entry_cleanup = 1;
180 }
181
182 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
183     AvahiEntry *e;
184
185     assert(s);
186     assert(key);
187
188     /* Withdraw an entry RRSset */
189
190     for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
191         withdraw_entry(s, e);
192 }
193
194 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
195     AvahiEntry *e, *n;
196     int ours = 0, won = 0, lost = 0;
197
198     assert(s);
199     assert(record);
200     assert(i);
201
202     /* Handle incoming probes and check if they conflict our own probes */
203
204     for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
205         int cmp;
206         n = e->by_key_next;
207
208         if (e->dead)
209             continue;
210
211         if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
212             ours = 1;
213             break;
214         } else {
215
216             if (avahi_entry_is_probing(s, e, i)) {
217                 if (cmp > 0)
218                     won = 1;
219                 else /* cmp < 0 */
220                     lost = 1;
221             }
222         }
223     }
224
225     if (!ours) {
226         char *t = avahi_record_to_string(record);
227
228         if (won)
229             avahi_log_debug("Received conflicting probe [%s]. Local host won.", t);
230         else if (lost) {
231             avahi_log_debug("Received conflicting probe [%s]. Local host lost. Withdrawing.", t);
232             withdraw_rrset(s, record->key);
233         }
234
235         avahi_free(t);
236     }
237 }
238
239 static int handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, int unique) {
240     int valid = 1, ours = 0, conflict = 0, withdraw_immediately = 0;
241     AvahiEntry *e, *n, *conflicting_entry = NULL;
242
243     assert(s);
244     assert(i);
245     assert(record);
246
247     /* Check whether an incoming record conflicts with one of our own */
248
249     for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
250         n = e->by_key_next;
251
252         if (e->dead)
253             continue;
254
255         /* Check if the incoming is a goodbye record */
256         if (avahi_record_is_goodbye(record)) {
257
258             if (avahi_record_equal_no_ttl(e->record, record)) {
259                 char *t;
260
261                 /* Refresh */
262                 t = avahi_record_to_string(record);
263                 avahi_log_debug("Received goodbye record for one of our records [%s]. Refreshing.", t);
264                 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
265
266                 valid = 0;
267                 avahi_free(t);
268                 break;
269             }
270
271             /* If the goodybe packet doesn't match one of our own RRs, we simply ignore it. */
272             continue;
273         }
274
275         if (!(e->flags & AVAHI_PUBLISH_UNIQUE) && !unique)
276             continue;
277
278         /* Either our entry or the other is intended to be unique, so let's check */
279
280         if (avahi_record_equal_no_ttl(e->record, record)) {
281             ours = 1; /* We have an identical record, so this is no conflict */
282
283             /* Check wheter there is a TTL conflict */
284             if (record->ttl <= e->record->ttl/2 &&
285                 avahi_entry_is_registered(s, e, i)) {
286                 char *t;
287                 /* Refresh */
288                 t = avahi_record_to_string(record);
289
290                 avahi_log_debug("Received record with bad TTL [%s]. Refreshing.", t);
291                 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
292                 valid = 0;
293
294                 avahi_free(t);
295             }
296
297             /* There's no need to check the other entries of this RRset */
298             break;
299
300         } else {
301
302             if (avahi_entry_is_registered(s, e, i)) {
303
304                 /* A conflict => we have to return to probe mode */
305                 conflict = 1;
306                 conflicting_entry = e;
307
308             } else if (avahi_entry_is_probing(s, e, i)) {
309
310                 /* We are currently registering a matching record, but
311                  * someone else already claimed it, so let's
312                  * withdraw */
313                 conflict = 1;
314                 withdraw_immediately = 1;
315             }
316         }
317     }
318
319     if (!ours && conflict) {
320         char *t;
321
322         valid = 0;
323
324         t = avahi_record_to_string(record);
325
326         if (withdraw_immediately) {
327             avahi_log_debug("Received conflicting record [%s] with local record to be. Withdrawing.", t);
328             withdraw_rrset(s, record->key);
329         } else {
330             assert(conflicting_entry);
331             avahi_log_debug("Received conflicting record [%s]. Resetting our record.", t);
332             avahi_entry_return_to_initial_state(s, conflicting_entry, i);
333
334             /* Local unique records are returned to probing
335              * state. Local shared records are reannounced. */
336         }
337
338         avahi_free(t);
339     }
340
341     return valid;
342 }
343
344 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) {
345     int *unicast_response = userdata;
346
347     assert(s);
348     assert(r);
349     assert(unicast_response);
350
351     avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, 1);
352 }
353
354 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int unicast_response) {
355     assert(s);
356     assert(r);
357
358     avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
359 }
360
361 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int immediately) {
362
363     assert(s);
364     assert(i);
365     assert(!legacy_unicast || (a && port > 0 && p));
366
367     if (legacy_unicast) {
368         AvahiDnsPacket *reply;
369         AvahiRecord *r;
370
371         if (!(reply = avahi_dns_packet_new_reply(p, 512 + AVAHI_DNS_PACKET_EXTRA_SIZE /* unicast DNS maximum packet size is 512 */ , 1, 1)))
372             return; /* OOM */
373
374         while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
375
376             append_aux_records_to_list(s, i, r, 0);
377
378             if (avahi_dns_packet_append_record(reply, r, 0, 10))
379                 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
380             else {
381                 char *t = avahi_record_to_string(r);
382                 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
383                 avahi_free(t);
384             }
385
386             avahi_record_unref(r);
387         }
388
389         if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
390             avahi_interface_send_packet_unicast(i, reply, a, port);
391
392         avahi_dns_packet_free(reply);
393
394     } else {
395         int unicast_response, flush_cache, auxiliary;
396         AvahiDnsPacket *reply = NULL;
397         AvahiRecord *r;
398
399         /* In case the query packet was truncated never respond
400         immediately, because known answer suppression records might be
401         contained in later packets */
402         int tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
403
404         while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
405
406             int im = immediately;
407
408             /* Only send the response immediately if it contains a
409              * unique entry AND it is not in reply to a truncated
410              * packet AND it is not an auxiliary record AND all other
411              * responses for this record are unique too. */
412
413             if (flush_cache && !tc && !auxiliary && avahi_record_list_all_flush_cache(s->record_list))
414                 im = 1;
415
416             if (!avahi_interface_post_response(i, r, flush_cache, a, im) && unicast_response) {
417
418                 /* Due to some reasons the record has not been scheduled.
419                  * The client requested an unicast response in that
420                  * case. Therefore we prepare such a response */
421
422                 append_aux_records_to_list(s, i, r, unicast_response);
423
424                 for (;;) {
425
426                     if (!reply) {
427                         assert(p);
428
429                         if (!(reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, 0, 0)))
430                             break; /* OOM */
431                     }
432
433                     if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
434
435                         /* Appending this record succeeded, so incremeant
436                          * the specific header field, and return to the caller */
437
438                         avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
439                         break;
440                     }
441
442                     if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
443                         size_t size;
444
445                         /* The record is too large for one packet, so create a larger packet */
446
447                         avahi_dns_packet_free(reply);
448                         size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
449
450                         if (!(reply = avahi_dns_packet_new_reply(p, size + AVAHI_DNS_PACKET_EXTRA_SIZE, 0, 1)))
451                             break; /* OOM */
452
453                         if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
454
455                             /* Appending this record succeeded, so incremeant
456                              * the specific header field, and return to the caller */
457
458                             avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
459                             break;
460
461                         }  else {
462
463                             /* We completely fucked up, there's
464                              * nothing we can do. The RR just doesn't
465                              * fit in. Let's ignore it. */
466
467                             char *t;
468                             avahi_dns_packet_free(reply);
469                             reply = NULL;
470                             t = avahi_record_to_string(r);
471                             avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
472                             avahi_free(t);
473                             break;
474                         }
475                     }
476
477                     /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
478                     avahi_interface_send_packet_unicast(i, reply, a, port);
479                     avahi_dns_packet_free(reply);
480                     reply = NULL;
481                 }
482             }
483
484             avahi_record_unref(r);
485         }
486
487         if (reply) {
488             if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
489                 avahi_interface_send_packet_unicast(i, reply, a, port);
490             avahi_dns_packet_free(reply);
491         }
492     }
493
494     avahi_record_list_flush(s->record_list);
495 }
496
497 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) {
498     AvahiInterface *j;
499
500     assert(s);
501     assert(i);
502     assert(r);
503
504     if (!s->config.enable_reflector)
505         return;
506
507     for (j = s->monitor->interfaces; j; j = j->interface_next)
508         if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
509             avahi_interface_post_response(j, r, flush_cache, NULL, 1);
510 }
511
512 static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
513     AvahiServer *s = userdata;
514
515     assert(c);
516     assert(pattern);
517     assert(e);
518     assert(s);
519
520     avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
521     return NULL;
522 }
523
524 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
525     AvahiInterface *j;
526
527     assert(s);
528     assert(i);
529     assert(k);
530
531     if (!s->config.enable_reflector)
532         return;
533
534     for (j = s->monitor->interfaces; j; j = j->interface_next)
535         if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
536             /* Post the query to other networks */
537             avahi_interface_post_query(j, k, 1, NULL);
538
539             /* Reply from caches of other network. This is needed to
540              * "work around" known answer suppression. */
541
542             avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
543         }
544 }
545
546 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
547     AvahiInterface *j;
548
549     assert(s);
550     assert(i);
551     assert(r);
552
553     if (!s->config.enable_reflector)
554         return;
555
556     for (j = s->monitor->interfaces; j; j = j->interface_next)
557         if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
558             avahi_interface_post_probe(j, r, 1);
559 }
560
561 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) {
562     size_t n;
563     int is_probe;
564
565     assert(s);
566     assert(p);
567     assert(i);
568     assert(a);
569
570     assert(avahi_record_list_is_empty(s->record_list));
571
572     is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
573
574     /* Handle the questions */
575     for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
576         AvahiKey *key;
577         int unicast_response = 0;
578
579         if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
580             avahi_log_warn(__FILE__": Packet too short or invalid while reading question key. (Maybe a UTF-8 problem?)");
581             goto fail;
582         }
583
584         if (!legacy_unicast && !from_local_iface) {
585             reflect_query(s, i, key);
586             if (!unicast_response)
587               avahi_cache_start_poof(i->cache, key, a);
588         }
589
590         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
591             !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
592             /* Allow our own queries to be suppressed by incoming
593              * queries only when they do not include known answers */
594             avahi_query_scheduler_incoming(i->query_scheduler, key);
595
596         avahi_server_prepare_matching_responses(s, i, key, unicast_response);
597         avahi_key_unref(key);
598     }
599
600     if (!legacy_unicast) {
601
602         /* Known Answer Suppression */
603         for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
604             AvahiRecord *record;
605             int unique = 0;
606
607             if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
608                 avahi_log_warn(__FILE__": Packet too short or invalid while reading known answer record. (Maybe a UTF-8 problem?)");
609                 goto fail;
610             }
611
612             avahi_response_scheduler_suppress(i->response_scheduler, record, a);
613             avahi_record_list_drop(s->record_list, record);
614             avahi_cache_stop_poof(i->cache, record, a);
615
616             avahi_record_unref(record);
617         }
618
619         /* Probe record */
620         for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
621             AvahiRecord *record;
622             int unique = 0;
623
624             if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
625                 avahi_log_warn(__FILE__": Packet too short or invalid while reading probe record. (Maybe a UTF-8 problem?)");
626                 goto fail;
627             }
628
629             if (!avahi_key_is_pattern(record->key)) {
630                 if (!from_local_iface)
631                     reflect_probe(s, i, record);
632                 incoming_probe(s, record, i);
633             }
634
635             avahi_record_unref(record);
636         }
637     }
638
639     if (!avahi_record_list_is_empty(s->record_list))
640         avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
641
642     return;
643
644 fail:
645     avahi_record_list_flush(s->record_list);
646 }
647
648 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
649     unsigned n;
650
651     assert(s);
652     assert(p);
653     assert(i);
654     assert(a);
655
656     for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
657              avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
658         AvahiRecord *record;
659         int cache_flush = 0;
660 /*         char *txt; */
661
662         if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
663             avahi_log_warn(__FILE__": Packet too short or invalid while reading response record. (Maybe a UTF-8 problem?)");
664             break;
665         }
666
667         if (!avahi_key_is_pattern(record->key) && !avahi_record_is_link_local_address(record)) {
668
669             if (handle_conflict(s, i, record, cache_flush)) {
670                 if (!from_local_iface)
671                     reflect_response(s, i, record, cache_flush);
672                 avahi_cache_update(i->cache, record, cache_flush, a);
673                 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
674             }
675         }
676
677         avahi_record_unref(record);
678     }
679
680     /* If the incoming response contained a conflicting record, some
681        records have been scheduling for sending. We need to flush them
682        here. */
683     if (!avahi_record_list_is_empty(s->record_list))
684         avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
685 }
686
687 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
688     unsigned n, idx = (unsigned) -1;
689     AvahiLegacyUnicastReflectSlot *slot;
690
691     assert(s);
692
693     if (!s->legacy_unicast_reflect_slots)
694         s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX);
695
696     for (n = 0; n < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; n++, s->legacy_unicast_reflect_id++) {
697         idx = s->legacy_unicast_reflect_id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
698
699         if (!s->legacy_unicast_reflect_slots[idx])
700             break;
701     }
702
703     if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
704         return NULL;
705
706     if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
707         return NULL; /* OOM */
708
709     s->legacy_unicast_reflect_slots[idx] = slot;
710     slot->id = s->legacy_unicast_reflect_id++;
711     slot->server = s;
712
713     return slot;
714 }
715
716 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
717     unsigned idx;
718
719     assert(s);
720     assert(slot);
721
722     idx = slot->id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
723
724     assert(s->legacy_unicast_reflect_slots[idx] == slot);
725
726     avahi_time_event_free(slot->time_event);
727
728     avahi_free(slot);
729     s->legacy_unicast_reflect_slots[idx] = NULL;
730 }
731
732 static void free_slots(AvahiServer *s) {
733     unsigned idx;
734     assert(s);
735
736     if (!s->legacy_unicast_reflect_slots)
737         return;
738
739     for (idx = 0; idx < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; idx ++)
740         if (s->legacy_unicast_reflect_slots[idx])
741             deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
742
743     avahi_free(s->legacy_unicast_reflect_slots);
744     s->legacy_unicast_reflect_slots = NULL;
745 }
746
747 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
748     unsigned idx;
749
750     assert(s);
751
752     if (!s->legacy_unicast_reflect_slots)
753         return NULL;
754
755     idx = id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
756
757     if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
758         return NULL;
759
760     return s->legacy_unicast_reflect_slots[idx];
761 }
762
763 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
764     AvahiLegacyUnicastReflectSlot *slot = userdata;
765
766     assert(e);
767     assert(slot);
768     assert(slot->time_event == e);
769
770     deallocate_slot(slot->server, slot);
771 }
772
773 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
774     AvahiLegacyUnicastReflectSlot *slot;
775     AvahiInterface *j;
776
777     assert(s);
778     assert(p);
779     assert(i);
780     assert(a);
781     assert(port > 0);
782     assert(i->protocol == a->proto);
783
784     if (!s->config.enable_reflector)
785         return;
786
787     /* Reflecting legacy unicast queries is a little more complicated
788        than reflecting normal queries, since we must route the
789        responses back to the right client. Therefore we must store
790        some information for finding the right client contact data for
791        response packets. In contrast to normal queries legacy
792        unicast query and response packets are reflected untouched and
793        are not reassembled into larger packets */
794
795     if (!(slot = allocate_slot(s))) {
796         /* No slot available, we drop this legacy unicast query */
797         avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
798         return;
799     }
800
801     slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
802     slot->address = *a;
803     slot->port = port;
804     slot->interface = i->hardware->index;
805
806     avahi_elapse_time(&slot->elapse_time, 2000, 0);
807     slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
808
809     /* Patch the packet with our new locally generatet id */
810     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
811
812     for (j = s->monitor->interfaces; j; j = j->interface_next)
813         if (j->announcing &&
814             j != i &&
815             (s->config.reflect_ipv || j->protocol == i->protocol)) {
816
817             if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
818                 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, NULL, 0);
819             } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
820                 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, NULL, 0);
821         }
822
823     /* Reset the id */
824     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
825 }
826
827 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const AvahiAddress *address, uint16_t port) {
828     assert(s);
829     assert(address);
830     assert(port > 0);
831
832     if (!s->config.enable_reflector)
833         return 0;
834
835     if (!avahi_address_is_local(s->monitor, address))
836         return 0;
837
838     if (address->proto == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
839         struct sockaddr_in lsa;
840         socklen_t l = sizeof(lsa);
841
842         if (getsockname(s->fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0)
843             avahi_log_warn("getsockname(): %s", strerror(errno));
844         else
845             return avahi_port_from_sockaddr((struct sockaddr*) &lsa) == port;
846
847     }
848
849     if (address->proto == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
850         struct sockaddr_in6 lsa;
851         socklen_t l = sizeof(lsa);
852
853         if (getsockname(s->fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0)
854             avahi_log_warn("getsockname(): %s", strerror(errno));
855         else
856             return avahi_port_from_sockaddr((struct sockaddr*) &lsa) == port;
857     }
858
859     return 0;
860 }
861
862 static int is_mdns_mcast_address(const AvahiAddress *a) {
863     AvahiAddress b;
864     assert(a);
865
866     avahi_address_parse(a->proto == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->proto, &b);
867     return avahi_address_cmp(a, &b) == 0;
868 }
869
870 static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) {
871     assert(s);
872     assert(iface != AVAHI_IF_UNSPEC);
873     assert(a);
874
875     /* If it isn't the MDNS port it can't be generated by us */
876     if (port != AVAHI_MDNS_PORT)
877         return 0;
878
879     return avahi_interface_has_address(s->monitor, iface, a);
880 }
881
882 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const AvahiAddress *src_address, uint16_t port, const AvahiAddress *dst_address, AvahiIfIndex iface, int ttl) {
883     AvahiInterface *i;
884     int from_local_iface = 0;
885
886     assert(s);
887     assert(p);
888     assert(src_address);
889     assert(dst_address);
890     assert(iface > 0);
891     assert(src_address->proto == dst_address->proto);
892
893     if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, src_address->proto)) ||
894         !i->announcing) {
895         avahi_log_warn("Received packet from invalid interface.");
896         return;
897     }
898
899     if (port <= 0) {
900         /* This fixes RHBZ #475394 */
901         avahi_log_warn("Received packet from invalid source port %u.", (unsigned) port);
902         return;
903     }
904
905     if (avahi_address_is_ipv4_in_ipv6(src_address))
906         /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
907         return;
908
909     if (originates_from_local_legacy_unicast_socket(s, src_address, port))
910         /* This originates from our local reflector, so let's ignore it */
911         return;
912
913     /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
914     if (s->config.enable_reflector)
915         from_local_iface = originates_from_local_iface(s, iface, src_address, port);
916
917     if (avahi_dns_packet_check_valid_multicast(p) < 0) {
918         avahi_log_warn("Received invalid packet.");
919         return;
920     }
921
922     if (avahi_dns_packet_is_query(p)) {
923         int legacy_unicast = 0;
924
925         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
926             avahi_log_warn("Invalid query packet.");
927             return;
928         }
929
930         if (port != AVAHI_MDNS_PORT) {
931             /* Legacy Unicast */
932
933             if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
934                  avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
935                 avahi_log_warn("Invalid legacy unicast query packet.");
936                 return;
937             }
938
939             legacy_unicast = 1;
940         }
941
942         if (legacy_unicast)
943             reflect_legacy_unicast_query_packet(s, p, i, src_address, port);
944
945         handle_query_packet(s, p, i, src_address, port, legacy_unicast, from_local_iface);
946
947     } else {
948         char t[AVAHI_ADDRESS_STR_MAX];
949
950         if (port != AVAHI_MDNS_PORT) {
951             avahi_log_warn("Received response from host %s with invalid source port %u on interface '%s.%i'", avahi_address_snprint(t, sizeof(t), src_address), port, i->hardware->name, i->protocol);
952             return;
953         }
954
955         if (ttl != 255 && s->config.check_response_ttl) {
956             avahi_log_warn("Received response from host %s with invalid TTL %u on interface '%s.%i'.", avahi_address_snprint(t, sizeof(t), src_address), ttl, i->hardware->name, i->protocol);
957             return;
958         }
959
960         if (!is_mdns_mcast_address(dst_address) &&
961             !avahi_interface_address_on_link(i, src_address)) {
962
963             avahi_log_warn("Received non-local response from host %s on interface '%s.%i'.", avahi_address_snprint(t, sizeof(t), src_address), i->hardware->name, i->protocol);
964             return;
965         }
966
967         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
968             avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
969             avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
970
971             avahi_log_warn("Invalid response packet from host %s.", avahi_address_snprint(t, sizeof(t), src_address));
972             return;
973         }
974
975         handle_response_packet(s, p, i, src_address, from_local_iface);
976     }
977 }
978
979 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p) {
980     AvahiInterface *j;
981     AvahiLegacyUnicastReflectSlot *slot;
982
983     assert(s);
984     assert(p);
985
986     if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
987         avahi_log_warn("Received invalid packet.");
988         return;
989     }
990
991     if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
992         avahi_log_warn("Received legacy unicast response with unknown id");
993         return;
994     }
995
996     if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
997         !j->announcing)
998         return;
999
1000     /* Patch the original ID into this response */
1001     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
1002
1003     /* Forward the response to the correct client */
1004     avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
1005
1006     /* Undo changes to packet */
1007     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1008 }
1009
1010 static void mcast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1011     AvahiServer *s = userdata;
1012     AvahiAddress dest, src;
1013     AvahiDnsPacket *p = NULL;
1014     AvahiIfIndex iface;
1015     uint16_t port;
1016     uint8_t ttl;
1017
1018     assert(w);
1019     assert(fd >= 0);
1020     assert(events & AVAHI_WATCH_IN);
1021
1022     if (fd == s->fd_ipv4) {
1023         dest.proto = src.proto = AVAHI_PROTO_INET;
1024         p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &src.data.ipv4, &port, &dest.data.ipv4, &iface, &ttl);
1025     } else {
1026         assert(fd == s->fd_ipv6);
1027         dest.proto = src.proto = AVAHI_PROTO_INET6;
1028         p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &src.data.ipv6, &port, &dest.data.ipv6, &iface, &ttl);
1029     }
1030
1031     if (p) {
1032         if (iface == AVAHI_IF_UNSPEC)
1033             iface = avahi_find_interface_for_address(s->monitor, &dest);
1034
1035         if (iface != AVAHI_IF_UNSPEC)
1036             dispatch_packet(s, p, &src, port, &dest, iface, ttl);
1037         else
1038             avahi_log_error("Incoming packet received on address that isn't local.");
1039
1040         avahi_dns_packet_free(p);
1041
1042         avahi_cleanup_dead_entries(s);
1043     }
1044 }
1045
1046 static void legacy_unicast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1047     AvahiServer *s = userdata;
1048     AvahiDnsPacket *p = NULL;
1049
1050     assert(w);
1051     assert(fd >= 0);
1052     assert(events & AVAHI_WATCH_IN);
1053
1054     if (fd == s->fd_legacy_unicast_ipv4)
1055         p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, NULL, NULL, NULL, NULL, NULL);
1056     else {
1057         assert(fd == s->fd_legacy_unicast_ipv6);
1058         p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, NULL, NULL, NULL, NULL, NULL);
1059     }
1060
1061     if (p) {
1062         dispatch_legacy_unicast_packet(s, p);
1063         avahi_dns_packet_free(p);
1064
1065         avahi_cleanup_dead_entries(s);
1066     }
1067 }
1068
1069 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1070     assert(s);
1071
1072     if (s->state == state)
1073         return;
1074
1075     s->state = state;
1076
1077     avahi_interface_monitor_update_rrs(s->monitor, 0);
1078
1079     if (s->callback)
1080         s->callback(s, state, s->userdata);
1081 }
1082
1083 static void withdraw_host_rrs(AvahiServer *s) {
1084     assert(s);
1085
1086     if (s->hinfo_entry_group)
1087         avahi_s_entry_group_reset(s->hinfo_entry_group);
1088
1089     if (s->browse_domain_entry_group)
1090         avahi_s_entry_group_reset(s->browse_domain_entry_group);
1091
1092     avahi_interface_monitor_update_rrs(s->monitor, 1);
1093     s->n_host_rr_pending = 0;
1094 }
1095
1096 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1097     assert(s);
1098
1099     assert(s->n_host_rr_pending > 0);
1100
1101     if (--s->n_host_rr_pending == 0)
1102         server_set_state(s, AVAHI_SERVER_RUNNING);
1103 }
1104
1105 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
1106     assert(s);
1107     assert(g);
1108
1109     if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1110         s->state == AVAHI_SERVER_REGISTERING)
1111         s->n_host_rr_pending ++;
1112
1113     else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1114         (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1115         withdraw_host_rrs(s);
1116         server_set_state(s, AVAHI_SERVER_COLLISION);
1117
1118     } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1119                s->state == AVAHI_SERVER_REGISTERING)
1120         avahi_server_decrease_host_rr_pending(s);
1121 }
1122
1123 static void register_hinfo(AvahiServer *s) {
1124     struct utsname utsname;
1125     AvahiRecord *r;
1126
1127     assert(s);
1128
1129     if (!s->config.publish_hinfo)
1130         return;
1131
1132     if (s->hinfo_entry_group)
1133         assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1134     else
1135         s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1136
1137     if (!s->hinfo_entry_group) {
1138         avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1139         return;
1140     }
1141
1142     /* Fill in HINFO rr */
1143     if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1144
1145         if (uname(&utsname) < 0)
1146             avahi_log_warn("uname() failed: %s\n", avahi_strerror(errno));
1147         else {
1148
1149             r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1150             r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1151
1152             avahi_log_info("Registering HINFO record with values '%s'/'%s'.", r->data.hinfo.cpu, r->data.hinfo.os);
1153
1154             if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
1155                 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1156                 return;
1157             }
1158         }
1159
1160         avahi_record_unref(r);
1161     }
1162
1163     if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1164         avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1165
1166 }
1167
1168 static void register_localhost(AvahiServer *s) {
1169     AvahiAddress a;
1170     assert(s);
1171
1172     /* Add localhost entries */
1173     avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1174     avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a);
1175
1176     avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1177     avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
1178 }
1179
1180 static void register_browse_domain(AvahiServer *s) {
1181     assert(s);
1182
1183     if (!s->config.publish_domain)
1184         return;
1185
1186     if (avahi_domain_equal(s->domain_name, "local"))
1187         return;
1188
1189     if (s->browse_domain_entry_group)
1190         assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1191     else
1192         s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1193
1194     if (!s->browse_domain_entry_group) {
1195         avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1196         return;
1197     }
1198
1199     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) {
1200         avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1201         return;
1202     }
1203
1204     if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1205         avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1206 }
1207
1208 static void register_stuff(AvahiServer *s) {
1209     assert(s);
1210
1211     server_set_state(s, AVAHI_SERVER_REGISTERING);
1212     s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1213
1214     register_hinfo(s);
1215     register_browse_domain(s);
1216     avahi_interface_monitor_update_rrs(s->monitor, 0);
1217
1218     s->n_host_rr_pending --;
1219
1220     if (s->n_host_rr_pending == 0)
1221         server_set_state(s, AVAHI_SERVER_RUNNING);
1222 }
1223
1224 static void update_fqdn(AvahiServer *s) {
1225     char *n;
1226
1227     assert(s);
1228     assert(s->host_name);
1229     assert(s->domain_name);
1230
1231     if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1232         return; /* OOM */
1233
1234     avahi_free(s->host_name_fqdn);
1235     s->host_name_fqdn = n;
1236 }
1237
1238 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1239     char *hn = NULL;
1240     assert(s);
1241
1242     AVAHI_CHECK_VALIDITY(s, !host_name || avahi_is_valid_host_name(host_name), AVAHI_ERR_INVALID_HOST_NAME);
1243
1244     if (!host_name) {
1245         hn = avahi_get_host_name_strdup();
1246         hn[strcspn(hn, ".")] = 0;
1247         host_name = hn;
1248     }
1249
1250     if (avahi_domain_equal(s->host_name, host_name) && s->state != AVAHI_SERVER_COLLISION) {
1251         avahi_free(hn);
1252         return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1253     }
1254
1255     withdraw_host_rrs(s);
1256
1257     avahi_free(s->host_name);
1258     s->host_name = hn ? hn : avahi_strdup(host_name);
1259
1260     update_fqdn(s);
1261
1262     register_stuff(s);
1263     return AVAHI_OK;
1264 }
1265
1266 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1267     char *dn = NULL;
1268     assert(s);
1269
1270     AVAHI_CHECK_VALIDITY(s, !domain_name || avahi_is_valid_domain_name(domain_name), AVAHI_ERR_INVALID_DOMAIN_NAME);
1271
1272     if (!domain_name) {
1273         dn = avahi_strdup("local");
1274         domain_name = dn;
1275     }
1276
1277     if (avahi_domain_equal(s->domain_name, domain_name)) {
1278         avahi_free(dn);
1279         return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1280     }
1281
1282     withdraw_host_rrs(s);
1283
1284     avahi_free(s->domain_name);
1285     s->domain_name = avahi_normalize_name_strdup(domain_name);
1286     update_fqdn(s);
1287
1288     register_stuff(s);
1289
1290     avahi_free(dn);
1291     return AVAHI_OK;
1292 }
1293
1294 static int valid_server_config(const AvahiServerConfig *sc) {
1295     AvahiStringList *l;
1296
1297     assert(sc);
1298
1299     if (sc->n_wide_area_servers > AVAHI_WIDE_AREA_SERVERS_MAX)
1300         return AVAHI_ERR_INVALID_CONFIG;
1301
1302     if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1303         return AVAHI_ERR_INVALID_HOST_NAME;
1304
1305     if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1306         return AVAHI_ERR_INVALID_DOMAIN_NAME;
1307
1308     for (l = sc->browse_domains; l; l = l->next)
1309         if (!avahi_is_valid_domain_name((char*) l->text))
1310             return AVAHI_ERR_INVALID_DOMAIN_NAME;
1311
1312     return AVAHI_OK;
1313 }
1314
1315 static int setup_sockets(AvahiServer *s) {
1316     assert(s);
1317
1318     s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
1319     s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
1320
1321     if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1322         return AVAHI_ERR_NO_NETWORK;
1323
1324     if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1325         avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1326     else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1327         avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1328
1329     s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1;
1330     s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1;
1331
1332     s->watch_ipv4 =
1333         s->watch_ipv6 =
1334         s->watch_legacy_unicast_ipv4 =
1335         s->watch_legacy_unicast_ipv6 = NULL;
1336
1337     if (s->fd_ipv4 >= 0)
1338         s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, mcast_socket_event, s);
1339     if (s->fd_ipv6 >= 0)
1340         s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, mcast_socket_event, s);
1341
1342     if (s->fd_legacy_unicast_ipv4 >= 0)
1343         s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, legacy_unicast_socket_event, s);
1344     if (s->fd_legacy_unicast_ipv6 >= 0)
1345         s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, legacy_unicast_socket_event, s);
1346
1347     return 0;
1348 }
1349
1350 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1351     AvahiServer *s;
1352     int e;
1353
1354     if (sc && (e = valid_server_config(sc)) < 0) {
1355         if (error)
1356             *error = e;
1357         return NULL;
1358     }
1359
1360     if (!(s = avahi_new(AvahiServer, 1))) {
1361         if (error)
1362             *error = AVAHI_ERR_NO_MEMORY;
1363
1364         return NULL;
1365     }
1366
1367     s->poll_api = poll_api;
1368
1369     if (sc)
1370         avahi_server_config_copy(&s->config, sc);
1371     else
1372         avahi_server_config_init(&s->config);
1373
1374     if ((e = setup_sockets(s)) < 0) {
1375         if (error)
1376             *error = e;
1377
1378         avahi_server_config_free(&s->config);
1379         avahi_free(s);
1380
1381         return NULL;
1382     }
1383
1384     s->n_host_rr_pending = 0;
1385     s->need_entry_cleanup = 0;
1386     s->need_group_cleanup = 0;
1387     s->need_browser_cleanup = 0;
1388     s->hinfo_entry_group = NULL;
1389     s->browse_domain_entry_group = NULL;
1390     s->error = AVAHI_OK;
1391     s->state = AVAHI_SERVER_INVALID;
1392
1393     s->callback = callback;
1394     s->userdata = userdata;
1395
1396     s->time_event_queue = avahi_time_event_queue_new(poll_api);
1397
1398     s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1399     AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1400     AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1401
1402     s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1403     AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1404     AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1405     AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1406     AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1407     AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1408     AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1409     AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1410     AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1411
1412     s->legacy_unicast_reflect_slots = NULL;
1413     s->legacy_unicast_reflect_id = 0;
1414
1415     s->record_list = avahi_record_list_new();
1416
1417     /* Get host name */
1418     s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
1419     s->host_name[strcspn(s->host_name, ".")] = 0;
1420     s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
1421     s->host_name_fqdn = NULL;
1422     update_fqdn(s);
1423
1424     do {
1425         s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1426     } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
1427
1428     if (s->config.enable_wide_area) {
1429         s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
1430         avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1431     } else
1432         s->wide_area_lookup_engine = NULL;
1433
1434     s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
1435
1436     s->monitor = avahi_interface_monitor_new(s);
1437     avahi_interface_monitor_sync(s->monitor);
1438
1439     register_localhost(s);
1440     register_stuff(s);
1441
1442     return s;
1443 }
1444
1445 void avahi_server_free(AvahiServer* s) {
1446     assert(s);
1447
1448     /* Remove all browsers */
1449
1450     while (s->dns_server_browsers)
1451         avahi_s_dns_server_browser_free(s->dns_server_browsers);
1452     while (s->host_name_resolvers)
1453         avahi_s_host_name_resolver_free(s->host_name_resolvers);
1454     while (s->address_resolvers)
1455         avahi_s_address_resolver_free(s->address_resolvers);
1456     while (s->domain_browsers)
1457         avahi_s_domain_browser_free(s->domain_browsers);
1458     while (s->service_type_browsers)
1459         avahi_s_service_type_browser_free(s->service_type_browsers);
1460     while (s->service_browsers)
1461         avahi_s_service_browser_free(s->service_browsers);
1462     while (s->service_resolvers)
1463         avahi_s_service_resolver_free(s->service_resolvers);
1464     while (s->record_browsers)
1465         avahi_s_record_browser_destroy(s->record_browsers);
1466
1467     /* Remove all locally rgeistered stuff */
1468
1469     while(s->entries)
1470         avahi_entry_free(s, s->entries);
1471
1472     avahi_interface_monitor_free(s->monitor);
1473
1474     while (s->groups)
1475         avahi_entry_group_free(s, s->groups);
1476
1477     free_slots(s);
1478
1479     avahi_hashmap_free(s->entries_by_key);
1480     avahi_record_list_free(s->record_list);
1481     avahi_hashmap_free(s->record_browser_hashmap);
1482
1483     if (s->wide_area_lookup_engine)
1484         avahi_wide_area_engine_free(s->wide_area_lookup_engine);
1485     avahi_multicast_lookup_engine_free(s->multicast_lookup_engine);
1486
1487     avahi_time_event_queue_free(s->time_event_queue);
1488
1489     /* Free watches */
1490
1491     if (s->watch_ipv4)
1492         s->poll_api->watch_free(s->watch_ipv4);
1493     if (s->watch_ipv6)
1494         s->poll_api->watch_free(s->watch_ipv6);
1495
1496     if (s->watch_legacy_unicast_ipv4)
1497         s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1498     if (s->watch_legacy_unicast_ipv6)
1499         s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1500
1501     /* Free sockets */
1502
1503     if (s->fd_ipv4 >= 0)
1504         close(s->fd_ipv4);
1505     if (s->fd_ipv6 >= 0)
1506         close(s->fd_ipv6);
1507
1508     if (s->fd_legacy_unicast_ipv4 >= 0)
1509         close(s->fd_legacy_unicast_ipv4);
1510     if (s->fd_legacy_unicast_ipv6 >= 0)
1511         close(s->fd_legacy_unicast_ipv6);
1512
1513     /* Free other stuff */
1514
1515     avahi_free(s->host_name);
1516     avahi_free(s->domain_name);
1517     avahi_free(s->host_name_fqdn);
1518
1519     avahi_server_config_free(&s->config);
1520
1521     avahi_free(s);
1522 }
1523
1524 const char* avahi_server_get_domain_name(AvahiServer *s) {
1525     assert(s);
1526
1527     return s->domain_name;
1528 }
1529
1530 const char* avahi_server_get_host_name(AvahiServer *s) {
1531     assert(s);
1532
1533     return s->host_name;
1534 }
1535
1536 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1537     assert(s);
1538
1539     return s->host_name_fqdn;
1540 }
1541
1542 void* avahi_server_get_data(AvahiServer *s) {
1543     assert(s);
1544
1545     return s->userdata;
1546 }
1547
1548 void avahi_server_set_data(AvahiServer *s, void* userdata) {
1549     assert(s);
1550
1551     s->userdata = userdata;
1552 }
1553
1554 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1555     assert(s);
1556
1557     return s->state;
1558 }
1559
1560 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1561     assert(c);
1562
1563     memset(c, 0, sizeof(AvahiServerConfig));
1564     c->use_ipv6 = 1;
1565     c->use_ipv4 = 1;
1566     c->allow_interfaces = NULL;
1567     c->deny_interfaces = NULL;
1568     c->host_name = NULL;
1569     c->domain_name = NULL;
1570     c->check_response_ttl = 0;
1571     c->publish_hinfo = 1;
1572     c->publish_addresses = 1;
1573     c->publish_workstation = 1;
1574     c->publish_domain = 1;
1575     c->use_iff_running = 0;
1576     c->enable_reflector = 0;
1577     c->reflect_ipv = 0;
1578     c->add_service_cookie = 0;
1579     c->enable_wide_area = 0;
1580     c->n_wide_area_servers = 0;
1581     c->disallow_other_stacks = 0;
1582     c->browse_domains = NULL;
1583     c->disable_publishing = 0;
1584     c->allow_point_to_point = 0;
1585     c->publish_aaaa_on_ipv4 = 1;
1586     c->publish_a_on_ipv6 = 0;
1587
1588     return c;
1589 }
1590
1591 void avahi_server_config_free(AvahiServerConfig *c) {
1592     assert(c);
1593
1594     avahi_free(c->host_name);
1595     avahi_free(c->domain_name);
1596     avahi_string_list_free(c->browse_domains);
1597     avahi_string_list_free(c->allow_interfaces);
1598     avahi_string_list_free(c->deny_interfaces);
1599 }
1600
1601 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1602     char *d = NULL, *h = NULL;
1603     AvahiStringList *browse = NULL, *allow = NULL, *deny = NULL;
1604     assert(ret);
1605     assert(c);
1606
1607     if (c->host_name)
1608         if (!(h = avahi_strdup(c->host_name)))
1609             return NULL;
1610
1611     if (c->domain_name)
1612         if (!(d = avahi_strdup(c->domain_name))) {
1613             avahi_free(h);
1614             return NULL;
1615         }
1616
1617     if (!(browse = avahi_string_list_copy(c->browse_domains)) && c->browse_domains) {
1618         avahi_free(h);
1619         avahi_free(d);
1620         return NULL;
1621     }
1622
1623     if (!(allow = avahi_string_list_copy(c->allow_interfaces)) && c->allow_interfaces) {
1624         avahi_string_list_free(browse);
1625         avahi_free(h);
1626         avahi_free(d);
1627         return NULL;
1628     }
1629
1630     if (!(deny = avahi_string_list_copy(c->deny_interfaces)) && c->deny_interfaces) {
1631         avahi_string_list_free(allow);
1632         avahi_string_list_free(browse);
1633         avahi_free(h);
1634         avahi_free(d);
1635         return NULL;
1636     }
1637
1638     *ret = *c;
1639     ret->host_name = h;
1640     ret->domain_name = d;
1641     ret->browse_domains = browse;
1642     ret->allow_interfaces = allow;
1643     ret->deny_interfaces = deny;
1644
1645     return ret;
1646 }
1647
1648 int avahi_server_errno(AvahiServer *s) {
1649     assert(s);
1650
1651     return s->error;
1652 }
1653
1654 /* Just for internal use */
1655 int avahi_server_set_errno(AvahiServer *s, int error) {
1656     assert(s);
1657
1658     return s->error = error;
1659 }
1660
1661 uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
1662     assert(s);
1663
1664     return s->local_service_cookie;
1665 }
1666
1667 static AvahiEntry *find_entry(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
1668     AvahiEntry *e;
1669
1670     assert(s);
1671     assert(key);
1672
1673     for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
1674
1675         if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1676             (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1677             (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING))
1678
1679             return e;
1680
1681     return NULL;
1682 }
1683
1684 int avahi_server_get_group_of_service(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain, AvahiSEntryGroup** ret_group) {
1685     AvahiKey *key = NULL;
1686     AvahiEntry *e;
1687     int ret;
1688     char n[AVAHI_DOMAIN_NAME_MAX];
1689
1690     assert(s);
1691     assert(name);
1692     assert(type);
1693     assert(ret_group);
1694
1695     AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
1696     AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
1697     AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
1698     AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
1699     AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
1700
1701     if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0))
1702         return avahi_server_set_errno(s, ret);
1703
1704     if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1705         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1706
1707     e = find_entry(s, interface, protocol, key);
1708     avahi_key_unref(key);
1709
1710     if (e) {
1711         *ret_group = e->group;
1712         return AVAHI_OK;
1713     }
1714
1715     return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
1716 }
1717
1718 int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name) {
1719     AvahiKey *key = NULL;
1720     AvahiEntry *e;
1721
1722     assert(s);
1723     assert(name);
1724
1725     if (!s->host_name_fqdn)
1726         return 0;
1727
1728     if (!(key = avahi_key_new(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1729         return 0;
1730
1731     e = find_entry(s, interface, protocol, key);
1732     avahi_key_unref(key);
1733
1734     if (!e)
1735         return 0;
1736
1737     return avahi_domain_equal(s->host_name_fqdn, e->record->data.srv.name);
1738 }
1739
1740 int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record) {
1741     AvahiEntry *e;
1742
1743     assert(s);
1744     assert(record);
1745
1746     for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = e->by_key_next)
1747
1748         if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1749             (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1750             (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING) &&
1751             avahi_record_equal_no_ttl(record, e->record))
1752             return 1;
1753
1754     return 0;
1755 }
1756
1757 /** Set the wide area DNS servers */
1758 int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) {
1759     assert(s);
1760
1761     if (!s->wide_area_lookup_engine)
1762         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG);
1763
1764     avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n);
1765     return AVAHI_OK;
1766 }
1767
1768 const AvahiServerConfig* avahi_server_get_config(AvahiServer *s) {
1769     assert(s);
1770
1771     return &s->config;
1772 }
1773
1774 /** Set the browsing domains */
1775 int avahi_server_set_browse_domains(AvahiServer *s, AvahiStringList *domains) {
1776     AvahiStringList *l;
1777
1778     assert(s);
1779
1780     for (l = s->config.browse_domains; l; l = l->next)
1781         if (!avahi_is_valid_domain_name((char*) l->text))
1782             return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1783
1784     avahi_string_list_free(s->config.browse_domains);
1785     s->config.browse_domains = avahi_string_list_copy(domains);
1786
1787     return AVAHI_OK;
1788 }