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