]> git.meshlink.io Git - catta/blob - src/server.c
fffbfdfdad85506dce2979af0518e29809fc063a
[catta] / src / server.c
1 /***
2   This file is part of catta.
3
4   catta 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   catta 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 catta; 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 <catta/domain.h>
37 #include <catta/timeval.h>
38 #include <catta/malloc.h>
39 #include <catta/error.h>
40 #include <catta/log.h>
41
42 #include "internal.h"
43 #include "iface.h"
44 #include "socket.h"
45 #include "browse.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 CATTA_DEFAULT_CACHE_ENTRIES_MAX 4096
53
54 static void enum_aux_records(CattaServer *s, CattaInterface *i, const char *name, uint16_t type, void (*callback)(CattaServer *s, CattaRecord *r, int flush_cache, void* userdata), void* userdata) {
55     assert(s);
56     assert(i);
57     assert(name);
58     assert(callback);
59
60     if (type == CATTA_DNS_TYPE_ANY) {
61         CattaEntry *e;
62
63         for (e = s->entries; e; e = e->entries_next)
64             if (!e->dead &&
65                 catta_entry_is_registered(s, e, i) &&
66                 e->record->key->clazz == CATTA_DNS_CLASS_IN &&
67                 catta_domain_equal(name, e->record->key->name))
68                 callback(s, e->record, e->flags & CATTA_PUBLISH_UNIQUE, userdata);
69
70     } else {
71         CattaEntry *e;
72         CattaKey *k;
73
74         if (!(k = catta_key_new(name, CATTA_DNS_CLASS_IN, type)))
75             return; /** OOM */
76
77         for (e = catta_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
78             if (!e->dead && catta_entry_is_registered(s, e, i))
79                 callback(s, e->record, e->flags & CATTA_PUBLISH_UNIQUE, userdata);
80
81         catta_key_unref(k);
82     }
83 }
84
85 void catta_server_enumerate_aux_records(CattaServer *s, CattaInterface *i, CattaRecord *r, void (*callback)(CattaServer *s, CattaRecord *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 == CATTA_DNS_CLASS_IN) {
94         if (r->key->type == CATTA_DNS_TYPE_PTR) {
95             enum_aux_records(s, i, r->data.ptr.name, CATTA_DNS_TYPE_SRV, callback, userdata);
96             enum_aux_records(s, i, r->data.ptr.name, CATTA_DNS_TYPE_TXT, callback, userdata);
97         } else if (r->key->type == CATTA_DNS_TYPE_SRV) {
98             enum_aux_records(s, i, r->data.srv.name, CATTA_DNS_TYPE_A, callback, userdata);
99             enum_aux_records(s, i, r->data.srv.name, CATTA_DNS_TYPE_AAAA, callback, userdata);
100         } else if (r->key->type == CATTA_DNS_TYPE_CNAME)
101             enum_aux_records(s, i, r->data.cname.name, CATTA_DNS_TYPE_ANY, callback, userdata);
102     }
103 }
104
105 void catta_server_prepare_response(CattaServer *s, CattaInterface *i, CattaEntry *e, int unicast_response, int auxiliary) {
106     assert(s);
107     assert(i);
108     assert(e);
109
110     catta_record_list_push(s->record_list, e->record, e->flags & CATTA_PUBLISH_UNIQUE, unicast_response, auxiliary);
111 }
112
113 void catta_server_prepare_matching_responses(CattaServer *s, CattaInterface *i, CattaKey *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 (catta_key_is_pattern(k)) {
121         CattaEntry *e;
122
123         /* Handle ANY query */
124
125         for (e = s->entries; e; e = e->entries_next)
126             if (!e->dead && catta_key_pattern_match(k, e->record->key) && catta_entry_is_registered(s, e, i))
127                 catta_server_prepare_response(s, i, e, unicast_response, 0);
128
129     } else {
130         CattaEntry *e;
131
132         /* Handle all other queries */
133
134         for (e = catta_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
135             if (!e->dead && catta_entry_is_registered(s, e, i))
136                 catta_server_prepare_response(s, i, e, unicast_response, 0);
137     }
138
139     /* Look for CNAME records */
140
141     if ((k->clazz == CATTA_DNS_CLASS_IN || k->clazz == CATTA_DNS_CLASS_ANY)
142         && k->type != CATTA_DNS_TYPE_CNAME && k->type != CATTA_DNS_TYPE_ANY) {
143
144         CattaKey *cname_key;
145
146         if (!(cname_key = catta_key_new(k->name, CATTA_DNS_CLASS_IN, CATTA_DNS_TYPE_CNAME)))
147             return;
148
149         catta_server_prepare_matching_responses(s, i, cname_key, unicast_response);
150         catta_key_unref(cname_key);
151     }
152 }
153
154 static void withdraw_entry(CattaServer *s, CattaEntry *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         CattaEntry *k;
166
167         for (k = e->group->entries; k; k = k->by_group_next)
168             if (!k->dead) {
169                 catta_goodbye_entry(s, k, 0, 1);
170                 k->dead = 1;
171             }
172
173         e->group->n_probing = 0;
174
175         catta_s_entry_group_change_state(e->group, CATTA_ENTRY_GROUP_COLLISION);
176     } else {
177         catta_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(CattaServer *s, CattaKey *key) {
185     CattaEntry *e;
186
187     assert(s);
188     assert(key);
189
190     /* Withdraw an entry RRSset */
191
192     for (e = catta_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(CattaServer *s, CattaRecord *record, CattaInterface *i) {
197     CattaEntry *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 = catta_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 = catta_record_lexicographical_compare(e->record, record)) == 0) {
214             ours = 1;
215             break;
216         } else {
217
218             if (catta_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 = catta_record_to_string(record);
229
230         if (won)
231             catta_log_debug("Received conflicting probe [%s]. Local host won.", t);
232         else if (lost) {
233             catta_log_debug("Received conflicting probe [%s]. Local host lost. Withdrawing.", t);
234             withdraw_rrset(s, record->key);
235         }
236
237         catta_free(t);
238     }
239 }
240
241 static int handle_conflict(CattaServer *s, CattaInterface *i, CattaRecord *record, int unique) {
242     int valid = 1, ours = 0, conflict = 0, withdraw_immediately = 0;
243     CattaEntry *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 = catta_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 (catta_record_is_goodbye(record)) {
259
260             if (catta_record_equal_no_ttl(e->record, record)) {
261                 char *t;
262
263                 /* Refresh */
264                 t = catta_record_to_string(record);
265                 catta_log_debug("Received goodbye record for one of our records [%s]. Refreshing.", t);
266                 catta_server_prepare_matching_responses(s, i, e->record->key, 0);
267
268                 valid = 0;
269                 catta_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 & CATTA_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 (catta_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                 catta_entry_is_registered(s, e, i)) {
288                 char *t;
289                 /* Refresh */
290                 t = catta_record_to_string(record);
291
292                 catta_log_debug("Received record with bad TTL [%s]. Refreshing.", t);
293                 catta_server_prepare_matching_responses(s, i, e->record->key, 0);
294                 valid = 0;
295
296                 catta_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 (catta_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 (catta_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 = catta_record_to_string(record);
327
328         if (withdraw_immediately) {
329             catta_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             catta_log_debug("Received conflicting record [%s]. Resetting our record.", t);
334             catta_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         catta_free(t);
341     }
342
343     return valid;
344 }
345
346 static void append_aux_callback(CattaServer *s, CattaRecord *r, int flush_cache, void* userdata) {
347     int *unicast_response = userdata;
348
349     assert(s);
350     assert(r);
351     assert(unicast_response);
352
353     catta_record_list_push(s->record_list, r, flush_cache, *unicast_response, 1);
354 }
355
356 static void append_aux_records_to_list(CattaServer *s, CattaInterface *i, CattaRecord *r, int unicast_response) {
357     assert(s);
358     assert(r);
359
360     catta_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
361 }
362
363 void catta_server_generate_response(CattaServer *s, CattaInterface *i, CattaDnsPacket *p, const CattaAddress *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         CattaDnsPacket *reply;
371         CattaRecord *r;
372
373         if (!(reply = catta_dns_packet_new_reply(p, 512 + CATTA_DNS_PACKET_EXTRA_SIZE /* unicast DNS maximum packet size is 512 */ , 1, 1)))
374             return; /* OOM */
375
376         while ((r = catta_record_list_next(s->record_list, NULL, NULL, NULL))) {
377
378             append_aux_records_to_list(s, i, r, 0);
379
380             if (catta_dns_packet_append_record(reply, r, 0, 10))
381                 catta_dns_packet_inc_field(reply, CATTA_DNS_FIELD_ANCOUNT);
382             else {
383                 char *t = catta_record_to_string(r);
384                 catta_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
385                 catta_free(t);
386             }
387
388             catta_record_unref(r);
389         }
390
391         if (catta_dns_packet_get_field(reply, CATTA_DNS_FIELD_ANCOUNT) != 0)
392             catta_interface_send_packet_unicast(i, reply, a, port);
393
394         catta_dns_packet_free(reply);
395
396     } else {
397         int unicast_response, flush_cache, auxiliary;
398         CattaDnsPacket *reply = NULL;
399         CattaRecord *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 && !!(catta_dns_packet_get_field(p, CATTA_DNS_FIELD_FLAGS) & CATTA_DNS_FLAG_TC);
405
406         while ((r = catta_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 && catta_record_list_all_flush_cache(s->record_list))
416                 im = 1;
417
418             if (!catta_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 = catta_dns_packet_new_reply(p, i->hardware->mtu, 0, 0)))
432                             break; /* OOM */
433                     }
434
435                     if (catta_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                         catta_dns_packet_inc_field(reply, CATTA_DNS_FIELD_ANCOUNT);
441                         break;
442                     }
443
444                     if (catta_dns_packet_get_field(reply, CATTA_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                         catta_dns_packet_free(reply);
450                         size = catta_record_get_estimate_size(r) + CATTA_DNS_PACKET_HEADER_SIZE;
451
452                         if (!(reply = catta_dns_packet_new_reply(p, size + CATTA_DNS_PACKET_EXTRA_SIZE, 0, 1)))
453                             break; /* OOM */
454
455                         if (catta_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                             catta_dns_packet_inc_field(reply, CATTA_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                             catta_dns_packet_free(reply);
471                             reply = NULL;
472                             t = catta_record_to_string(r);
473                             catta_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
474                             catta_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                     catta_interface_send_packet_unicast(i, reply, a, port);
481                     catta_dns_packet_free(reply);
482                     reply = NULL;
483                 }
484             }
485
486             catta_record_unref(r);
487         }
488
489         if (reply) {
490             if (catta_dns_packet_get_field(reply, CATTA_DNS_FIELD_ANCOUNT) != 0)
491                 catta_interface_send_packet_unicast(i, reply, a, port);
492             catta_dns_packet_free(reply);
493         }
494     }
495
496     catta_record_list_flush(s->record_list);
497 }
498
499 static void reflect_response(CattaServer *s, CattaInterface *i, CattaRecord *r, int flush_cache) {
500     CattaInterface *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->iface_next)
510         if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
511             catta_interface_post_response(j, r, flush_cache, NULL, 1);
512 }
513
514 static void* reflect_cache_walk_callback(CattaCache *c, CattaKey *pattern, CattaCacheEntry *e, void* userdata) {
515     CattaServer *s = userdata;
516     CattaRecord* r;
517
518     assert(c);
519     assert(pattern);
520     assert(e);
521     assert(s);
522
523     /* Don't reflect cache entry with ipv6 link-local addresses. */
524     r = e->record;
525     if ((r->key->type == CATTA_DNS_TYPE_AAAA) &&
526             (r->data.aaaa.address.address[0] == 0xFE) &&
527             (r->data.aaaa.address.address[1] == 0x80))
528       return NULL;
529
530     catta_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
531     return NULL;
532 }
533
534 static void reflect_query(CattaServer *s, CattaInterface *i, CattaKey *k) {
535     CattaInterface *j;
536
537     assert(s);
538     assert(i);
539     assert(k);
540
541     if (!s->config.enable_reflector)
542         return;
543
544     for (j = s->monitor->interfaces; j; j = j->iface_next)
545         if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
546             /* Post the query to other networks */
547             catta_interface_post_query(j, k, 1, NULL);
548
549             /* Reply from caches of other network. This is needed to
550              * "work around" known answer suppression. */
551
552             catta_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
553         }
554 }
555
556 static void reflect_probe(CattaServer *s, CattaInterface *i, CattaRecord *r) {
557     CattaInterface *j;
558
559     assert(s);
560     assert(i);
561     assert(r);
562
563     if (!s->config.enable_reflector)
564         return;
565
566     for (j = s->monitor->interfaces; j; j = j->iface_next)
567         if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
568             catta_interface_post_probe(j, r, 1);
569 }
570
571 static void handle_query_packet(CattaServer *s, CattaDnsPacket *p, CattaInterface *i, const CattaAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) {
572     size_t n;
573     int is_probe;
574
575     assert(s);
576     assert(p);
577     assert(i);
578     assert(a);
579
580     assert(catta_record_list_is_empty(s->record_list));
581
582     is_probe = catta_dns_packet_get_field(p, CATTA_DNS_FIELD_NSCOUNT) > 0;
583
584     /* Handle the questions */
585     for (n = catta_dns_packet_get_field(p, CATTA_DNS_FIELD_QDCOUNT); n > 0; n --) {
586         CattaKey *key;
587         int unicast_response = 0;
588
589         if (!(key = catta_dns_packet_consume_key(p, &unicast_response))) {
590             catta_log_warn(__FILE__": Packet too short or invalid while reading question key. (Maybe a UTF-8 problem?)");
591             goto fail;
592         }
593
594         if (!legacy_unicast && !from_local_iface) {
595             reflect_query(s, i, key);
596             if (!unicast_response)
597               catta_cache_start_poof(i->cache, key, a);
598         }
599
600         if (catta_dns_packet_get_field(p, CATTA_DNS_FIELD_ANCOUNT) == 0 &&
601             !(catta_dns_packet_get_field(p, CATTA_DNS_FIELD_FLAGS) & CATTA_DNS_FLAG_TC))
602             /* Allow our own queries to be suppressed by incoming
603              * queries only when they do not include known answers */
604             catta_query_scheduler_incoming(i->query_scheduler, key);
605
606         catta_server_prepare_matching_responses(s, i, key, unicast_response);
607         catta_key_unref(key);
608     }
609
610     if (!legacy_unicast) {
611
612         /* Known Answer Suppression */
613         for (n = catta_dns_packet_get_field(p, CATTA_DNS_FIELD_ANCOUNT); n > 0; n --) {
614             CattaRecord *record;
615             int unique = 0;
616
617             if (!(record = catta_dns_packet_consume_record(p, &unique))) {
618                 catta_log_warn(__FILE__": Packet too short or invalid while reading known answer record. (Maybe a UTF-8 problem?)");
619                 goto fail;
620             }
621
622             catta_response_scheduler_suppress(i->response_scheduler, record, a);
623             catta_record_list_drop(s->record_list, record);
624             catta_cache_stop_poof(i->cache, record, a);
625
626             catta_record_unref(record);
627         }
628
629         /* Probe record */
630         for (n = catta_dns_packet_get_field(p, CATTA_DNS_FIELD_NSCOUNT); n > 0; n --) {
631             CattaRecord *record;
632             int unique = 0;
633
634             if (!(record = catta_dns_packet_consume_record(p, &unique))) {
635                 catta_log_warn(__FILE__": Packet too short or invalid while reading probe record. (Maybe a UTF-8 problem?)");
636                 goto fail;
637             }
638
639             if (!catta_key_is_pattern(record->key)) {
640                 if (!from_local_iface)
641                     reflect_probe(s, i, record);
642                 incoming_probe(s, record, i);
643             }
644
645             catta_record_unref(record);
646         }
647     }
648
649     if (!catta_record_list_is_empty(s->record_list))
650         catta_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
651
652     return;
653
654 fail:
655     catta_record_list_flush(s->record_list);
656 }
657
658 static void handle_response_packet(CattaServer *s, CattaDnsPacket *p, CattaInterface *i, const CattaAddress *a, int from_local_iface) {
659     unsigned n;
660
661     assert(s);
662     assert(p);
663     assert(i);
664     assert(a);
665
666     for (n = catta_dns_packet_get_field(p, CATTA_DNS_FIELD_ANCOUNT) +
667              catta_dns_packet_get_field(p, CATTA_DNS_FIELD_ARCOUNT); n > 0; n--) {
668         CattaRecord *record;
669         int cache_flush = 0;
670
671         if (!(record = catta_dns_packet_consume_record(p, &cache_flush))) {
672             catta_log_warn(__FILE__": Packet too short or invalid while reading response record. (Maybe a UTF-8 problem?)");
673             break;
674         }
675
676         if (!catta_key_is_pattern(record->key)) {
677
678             if (handle_conflict(s, i, record, cache_flush)) {
679                 if (!from_local_iface && !catta_record_is_link_local_address(record))
680                     reflect_response(s, i, record, cache_flush);
681                 catta_cache_update(i->cache, record, cache_flush, a);
682                 catta_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
683             }
684         }
685
686         catta_record_unref(record);
687     }
688
689     /* If the incoming response contained a conflicting record, some
690        records have been scheduled for sending. We need to flush them
691        here. */
692     if (!catta_record_list_is_empty(s->record_list))
693         catta_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
694 }
695
696 static CattaLegacyUnicastReflectSlot* allocate_slot(CattaServer *s) {
697     unsigned n, idx = (unsigned) -1;
698     CattaLegacyUnicastReflectSlot *slot;
699
700     assert(s);
701
702     if (!s->legacy_unicast_reflect_slots)
703         s->legacy_unicast_reflect_slots = catta_new0(CattaLegacyUnicastReflectSlot*, CATTA_LEGACY_UNICAST_REFLECT_SLOTS_MAX);
704
705     for (n = 0; n < CATTA_LEGACY_UNICAST_REFLECT_SLOTS_MAX; n++, s->legacy_unicast_reflect_id++) {
706         idx = s->legacy_unicast_reflect_id % CATTA_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
707
708         if (!s->legacy_unicast_reflect_slots[idx])
709             break;
710     }
711
712     if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
713         return NULL;
714
715     if (!(slot = catta_new(CattaLegacyUnicastReflectSlot, 1)))
716         return NULL; /* OOM */
717
718     s->legacy_unicast_reflect_slots[idx] = slot;
719     slot->id = s->legacy_unicast_reflect_id++;
720     slot->server = s;
721
722     return slot;
723 }
724
725 static void deallocate_slot(CattaServer *s, CattaLegacyUnicastReflectSlot *slot) {
726     unsigned idx;
727
728     assert(s);
729     assert(slot);
730
731     idx = slot->id % CATTA_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
732
733     assert(s->legacy_unicast_reflect_slots[idx] == slot);
734
735     catta_time_event_free(slot->time_event);
736
737     catta_free(slot);
738     s->legacy_unicast_reflect_slots[idx] = NULL;
739 }
740
741 static void free_slots(CattaServer *s) {
742     unsigned idx;
743     assert(s);
744
745     if (!s->legacy_unicast_reflect_slots)
746         return;
747
748     for (idx = 0; idx < CATTA_LEGACY_UNICAST_REFLECT_SLOTS_MAX; idx ++)
749         if (s->legacy_unicast_reflect_slots[idx])
750             deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
751
752     catta_free(s->legacy_unicast_reflect_slots);
753     s->legacy_unicast_reflect_slots = NULL;
754 }
755
756 static CattaLegacyUnicastReflectSlot* find_slot(CattaServer *s, uint16_t id) {
757     unsigned idx;
758
759     assert(s);
760
761     if (!s->legacy_unicast_reflect_slots)
762         return NULL;
763
764     idx = id % CATTA_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
765
766     if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
767         return NULL;
768
769     return s->legacy_unicast_reflect_slots[idx];
770 }
771
772 static void legacy_unicast_reflect_slot_timeout(CattaTimeEvent *e, void *userdata) {
773     CattaLegacyUnicastReflectSlot *slot = userdata;
774
775     assert(e);
776     assert(slot);
777     assert(slot->time_event == e);
778
779     deallocate_slot(slot->server, slot);
780 }
781
782 static void reflect_legacy_unicast_query_packet(CattaServer *s, CattaDnsPacket *p, CattaInterface *i, const CattaAddress *a, uint16_t port) {
783     CattaLegacyUnicastReflectSlot *slot;
784     CattaInterface *j;
785
786     assert(s);
787     assert(p);
788     assert(i);
789     assert(a);
790     assert(port > 0);
791     assert(i->protocol == a->proto);
792
793     if (!s->config.enable_reflector)
794         return;
795
796     /* Reflecting legacy unicast queries is a little more complicated
797        than reflecting normal queries, since we must route the
798        responses back to the right client. Therefore we must store
799        some information for finding the right client contact data for
800        response packets. In contrast to normal queries legacy
801        unicast query and response packets are reflected untouched and
802        are not reassembled into larger packets */
803
804     if (!(slot = allocate_slot(s))) {
805         /* No slot available, we drop this legacy unicast query */
806         catta_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
807         return;
808     }
809
810     slot->original_id = catta_dns_packet_get_field(p, CATTA_DNS_FIELD_ID);
811     slot->address = *a;
812     slot->port = port;
813     slot->iface = i->hardware->index;
814
815     catta_elapse_time(&slot->elapse_time, 2000, 0);
816     slot->time_event = catta_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
817
818     /* Patch the packet with our new locally generatet id */
819     catta_dns_packet_set_field(p, CATTA_DNS_FIELD_ID, slot->id);
820
821     for (j = s->monitor->interfaces; j; j = j->iface_next)
822         if (j->announcing &&
823             j != i &&
824             (s->config.reflect_ipv || j->protocol == i->protocol)) {
825
826             if (j->protocol == CATTA_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
827                 catta_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, NULL, 0);
828             } else if (j->protocol == CATTA_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
829                 catta_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, NULL, 0);
830         }
831
832     /* Reset the id */
833     catta_dns_packet_set_field(p, CATTA_DNS_FIELD_ID, slot->original_id);
834 }
835
836 static int originates_from_local_legacy_unicast_socket(CattaServer *s, const CattaAddress *address, uint16_t port) {
837     assert(s);
838     assert(address);
839     assert(port > 0);
840
841     if (!s->config.enable_reflector)
842         return 0;
843
844     if (!catta_address_is_local(s->monitor, address))
845         return 0;
846
847     if (address->proto == CATTA_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
848         struct sockaddr_in lsa;
849         socklen_t l = sizeof(lsa);
850
851         if (getsockname(s->fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0)
852             catta_log_warn("getsockname(): %s", errnostrsocket());
853         else
854             return catta_port_from_sockaddr((struct sockaddr*) &lsa) == port;
855
856     }
857
858     if (address->proto == CATTA_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
859         struct sockaddr_in6 lsa;
860         socklen_t l = sizeof(lsa);
861
862         if (getsockname(s->fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0)
863             catta_log_warn("getsockname(): %s", errnostrsocket());
864         else
865             return catta_port_from_sockaddr((struct sockaddr*) &lsa) == port;
866     }
867
868     return 0;
869 }
870
871 static int is_mdns_mcast_address(const CattaAddress *a) {
872     CattaAddress b;
873     assert(a);
874
875     catta_address_parse(a->proto == CATTA_PROTO_INET ? CATTA_IPV4_MCAST_GROUP : CATTA_IPV6_MCAST_GROUP, a->proto, &b);
876     return catta_address_cmp(a, &b) == 0;
877 }
878
879 static int originates_from_local_iface(CattaServer *s, CattaIfIndex iface, const CattaAddress *a, uint16_t port) {
880     assert(s);
881     assert(iface != CATTA_IF_UNSPEC);
882     assert(a);
883
884     /* If it isn't the MDNS port it can't be generated by us */
885     if (port != CATTA_MDNS_PORT)
886         return 0;
887
888     return catta_interface_has_address(s->monitor, iface, a);
889 }
890
891 static void dispatch_packet(CattaServer *s, CattaDnsPacket *p, const CattaAddress *src_address, uint16_t port, const CattaAddress *dst_address, CattaIfIndex iface, int ttl) {
892     CattaInterface *i;
893     int from_local_iface = 0;
894
895     assert(s);
896     assert(p);
897     assert(src_address);
898     assert(dst_address);
899     assert(iface > 0);
900     assert(src_address->proto == dst_address->proto);
901
902     if (!(i = catta_interface_monitor_get_interface(s->monitor, iface, src_address->proto))) {
903         catta_log_warn("Received packet from unrecognized interface (%d).", iface);
904         return;
905     }
906     if (!i->announcing) {
907         catta_log_warn("Received packet from invalid interface %d (not announcing).", iface);
908         return;
909     }
910
911     if (port <= 0) {
912         /* This fixes RHBZ #475394 */
913         catta_log_warn("Received packet from invalid source port %u.", (unsigned) port);
914         return;
915     }
916
917     if (catta_address_is_ipv4_in_ipv6(src_address))
918         /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
919         return;
920
921     if (originates_from_local_legacy_unicast_socket(s, src_address, port))
922         /* This originates from our local reflector, so let's ignore it */
923         return;
924
925     /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
926     if (s->config.enable_reflector)
927         from_local_iface = originates_from_local_iface(s, iface, src_address, port);
928
929     if (catta_dns_packet_check_valid_multicast(p) < 0) {
930         catta_log_warn("Received invalid packet.");
931         return;
932     }
933
934     if (catta_dns_packet_is_query(p)) {
935         int legacy_unicast = 0;
936
937         /* For queries EDNS0 might allow ARCOUNT != 0. We ignore the
938          * AR section completely here, so far. Until the day we add
939          * EDNS0 support. */
940
941         if (port != CATTA_MDNS_PORT) {
942             /* Legacy Unicast */
943
944             if ((catta_dns_packet_get_field(p, CATTA_DNS_FIELD_ANCOUNT) != 0 ||
945                  catta_dns_packet_get_field(p, CATTA_DNS_FIELD_NSCOUNT) != 0)) {
946                 catta_log_warn("Invalid legacy unicast query packet.");
947                 return;
948             }
949
950             legacy_unicast = 1;
951         }
952
953         if (legacy_unicast)
954             reflect_legacy_unicast_query_packet(s, p, i, src_address, port);
955
956         handle_query_packet(s, p, i, src_address, port, legacy_unicast, from_local_iface);
957
958     } else {
959         char t[CATTA_ADDRESS_STR_MAX];
960
961         if (port != CATTA_MDNS_PORT) {
962             catta_log_warn("Received response from host %s with invalid source port %u on interface '%s.%i'", catta_address_snprint(t, sizeof(t), src_address), port, i->hardware->name, i->protocol);
963             return;
964         }
965
966         if (ttl != 255 && s->config.check_response_ttl) {
967             catta_log_warn("Received response from host %s with invalid TTL %u on interface '%s.%i'.", catta_address_snprint(t, sizeof(t), src_address), ttl, i->hardware->name, i->protocol);
968             return;
969         }
970
971         if (!is_mdns_mcast_address(dst_address) &&
972             !catta_interface_address_on_link(i, src_address)) {
973
974             catta_log_warn("Received non-local response from host %s on interface '%s.%i'.", catta_address_snprint(t, sizeof(t), src_address), i->hardware->name, i->protocol);
975             return;
976         }
977
978         if (catta_dns_packet_get_field(p, CATTA_DNS_FIELD_QDCOUNT) != 0 ||
979             catta_dns_packet_get_field(p, CATTA_DNS_FIELD_ANCOUNT) == 0 ||
980             catta_dns_packet_get_field(p, CATTA_DNS_FIELD_NSCOUNT) != 0) {
981
982             catta_log_warn("Invalid response packet from host %s.", catta_address_snprint(t, sizeof(t), src_address));
983             return;
984         }
985
986         handle_response_packet(s, p, i, src_address, from_local_iface);
987     }
988 }
989
990 static void dispatch_legacy_unicast_packet(CattaServer *s, CattaDnsPacket *p) {
991     CattaInterface *j;
992     CattaLegacyUnicastReflectSlot *slot;
993
994     assert(s);
995     assert(p);
996
997     if (catta_dns_packet_check_valid(p) < 0 || catta_dns_packet_is_query(p)) {
998         catta_log_warn("Received invalid packet.");
999         return;
1000     }
1001
1002     if (!(slot = find_slot(s, catta_dns_packet_get_field(p, CATTA_DNS_FIELD_ID)))) {
1003         catta_log_warn("Received legacy unicast response with unknown id");
1004         return;
1005     }
1006
1007     if (!(j = catta_interface_monitor_get_interface(s->monitor, slot->iface, slot->address.proto)) ||
1008         !j->announcing)
1009         return;
1010
1011     /* Patch the original ID into this response */
1012     catta_dns_packet_set_field(p, CATTA_DNS_FIELD_ID, slot->original_id);
1013
1014     /* Forward the response to the correct client */
1015     catta_interface_send_packet_unicast(j, p, &slot->address, slot->port);
1016
1017     /* Undo changes to packet */
1018     catta_dns_packet_set_field(p, CATTA_DNS_FIELD_ID, slot->id);
1019 }
1020
1021 static void mcast_socket_event(CattaWatch *w, int fd, CattaWatchEvent events, void *userdata) {
1022     CattaServer *s = userdata;
1023     CattaAddress dest, src;
1024     CattaDnsPacket *p = NULL;
1025     CattaIfIndex iface;
1026     uint16_t port;
1027     uint8_t ttl;
1028
1029     assert(w);
1030     assert(fd >= 0);
1031     assert(events & CATTA_WATCH_IN);
1032
1033     if (fd == s->fd_ipv4) {
1034         dest.proto = src.proto = CATTA_PROTO_INET;
1035         p = catta_recv_dns_packet_ipv4(s->fd_ipv4, &src.data.ipv4, &port, &dest.data.ipv4, &iface, &ttl);
1036     } else {
1037         assert(fd == s->fd_ipv6);
1038         dest.proto = src.proto = CATTA_PROTO_INET6;
1039         p = catta_recv_dns_packet_ipv6(s->fd_ipv6, &src.data.ipv6, &port, &dest.data.ipv6, &iface, &ttl);
1040     }
1041
1042     if (p) {
1043         if (iface == CATTA_IF_UNSPEC)
1044             iface = catta_find_interface_for_address(s->monitor, &dest);
1045
1046         if (iface != CATTA_IF_UNSPEC)
1047             dispatch_packet(s, p, &src, port, &dest, iface, ttl);
1048         else
1049             catta_log_error("Incoming packet received on address that isn't local.");
1050
1051         catta_dns_packet_free(p);
1052
1053         catta_cleanup_dead_entries(s);
1054     }
1055 }
1056
1057 static void legacy_unicast_socket_event(CattaWatch *w, int fd, CattaWatchEvent events, void *userdata) {
1058     CattaServer *s = userdata;
1059     CattaDnsPacket *p = NULL;
1060
1061     assert(w);
1062     assert(fd >= 0);
1063     assert(events & CATTA_WATCH_IN);
1064
1065     if (fd == s->fd_legacy_unicast_ipv4)
1066         p = catta_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, NULL, NULL, NULL, NULL, NULL);
1067     else {
1068         assert(fd == s->fd_legacy_unicast_ipv6);
1069         p = catta_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, NULL, NULL, NULL, NULL, NULL);
1070     }
1071
1072     if (p) {
1073         dispatch_legacy_unicast_packet(s, p);
1074         catta_dns_packet_free(p);
1075
1076         catta_cleanup_dead_entries(s);
1077     }
1078 }
1079
1080 static void server_set_state(CattaServer *s, CattaServerState state) {
1081     assert(s);
1082
1083     if (s->state == state)
1084         return;
1085
1086     s->state = state;
1087
1088     catta_interface_monitor_update_rrs(s->monitor, 0);
1089
1090     if (s->callback)
1091         s->callback(s, state, s->userdata);
1092 }
1093
1094 static void withdraw_host_rrs(CattaServer *s) {
1095     assert(s);
1096
1097     if (s->hinfo_entry_group)
1098         catta_s_entry_group_reset(s->hinfo_entry_group);
1099
1100     if (s->browse_domain_entry_group)
1101         catta_s_entry_group_reset(s->browse_domain_entry_group);
1102
1103     catta_interface_monitor_update_rrs(s->monitor, 1);
1104     s->n_host_rr_pending = 0;
1105 }
1106
1107 void catta_server_decrease_host_rr_pending(CattaServer *s) {
1108     assert(s);
1109
1110     assert(s->n_host_rr_pending > 0);
1111
1112     if (--s->n_host_rr_pending == 0)
1113         server_set_state(s, CATTA_SERVER_RUNNING);
1114 }
1115
1116 void catta_host_rr_entry_group_callback(CattaServer *s, CattaSEntryGroup *g, CattaEntryGroupState state, CATTA_GCC_UNUSED void *userdata) {
1117     assert(s);
1118     assert(g);
1119
1120     if (state == CATTA_ENTRY_GROUP_REGISTERING &&
1121         s->state == CATTA_SERVER_REGISTERING)
1122         s->n_host_rr_pending ++;
1123
1124     else if (state == CATTA_ENTRY_GROUP_COLLISION &&
1125         (s->state == CATTA_SERVER_REGISTERING || s->state == CATTA_SERVER_RUNNING)) {
1126         withdraw_host_rrs(s);
1127         server_set_state(s, CATTA_SERVER_COLLISION);
1128
1129     } else if (state == CATTA_ENTRY_GROUP_ESTABLISHED &&
1130                s->state == CATTA_SERVER_REGISTERING)
1131         catta_server_decrease_host_rr_pending(s);
1132 }
1133
1134 static void register_hinfo(CattaServer *s) {
1135     struct utsname utsname;
1136     CattaRecord *r;
1137
1138     assert(s);
1139
1140     if (!s->config.publish_hinfo)
1141         return;
1142
1143     if (s->hinfo_entry_group)
1144         assert(catta_s_entry_group_is_empty(s->hinfo_entry_group));
1145     else
1146         s->hinfo_entry_group = catta_s_entry_group_new(s, catta_host_rr_entry_group_callback, NULL);
1147
1148     if (!s->hinfo_entry_group) {
1149         catta_log_warn("Failed to create HINFO entry group: %s", catta_strerror(s->error));
1150         return;
1151     }
1152
1153     /* Fill in HINFO rr */
1154     if ((r = catta_record_new_full(s->host_name_fqdn, CATTA_DNS_CLASS_IN, CATTA_DNS_TYPE_HINFO, CATTA_DEFAULT_TTL_HOST_NAME))) {
1155
1156         if (uname(&utsname) < 0)
1157             catta_log_warn("uname() failed: %s\n", catta_strerror(errno));
1158         else {
1159
1160             r->data.hinfo.cpu = catta_strdup(catta_strup(utsname.machine));
1161             r->data.hinfo.os = catta_strdup(catta_strup(utsname.sysname));
1162
1163             catta_log_info("Registering HINFO record with values '%s'/'%s'.", r->data.hinfo.cpu, r->data.hinfo.os);
1164
1165             if (catta_server_add(s, s->hinfo_entry_group, CATTA_IF_UNSPEC, CATTA_PROTO_UNSPEC, CATTA_PUBLISH_UNIQUE, r) < 0) {
1166                 catta_log_warn("Failed to add HINFO RR: %s", catta_strerror(s->error));
1167                 return;
1168             }
1169         }
1170
1171         catta_record_unref(r);
1172     }
1173
1174     if (catta_s_entry_group_commit(s->hinfo_entry_group) < 0)
1175         catta_log_warn("Failed to commit HINFO entry group: %s", catta_strerror(s->error));
1176
1177 }
1178
1179 static void register_localhost(CattaServer *s) {
1180     CattaAddress a;
1181     assert(s);
1182
1183     /* Add localhost entries */
1184     catta_address_parse("127.0.0.1", CATTA_PROTO_INET, &a);
1185     catta_server_add_address(s, NULL, CATTA_IF_UNSPEC, CATTA_PROTO_UNSPEC, CATTA_PUBLISH_NO_PROBE|CATTA_PUBLISH_NO_ANNOUNCE, "localhost", &a);
1186
1187     catta_address_parse("::1", CATTA_PROTO_INET6, &a);
1188     catta_server_add_address(s, NULL, CATTA_IF_UNSPEC, CATTA_PROTO_UNSPEC, CATTA_PUBLISH_NO_PROBE|CATTA_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
1189 }
1190
1191 static void register_browse_domain(CattaServer *s) {
1192     assert(s);
1193
1194     if (!s->config.publish_domain)
1195         return;
1196
1197     if (catta_domain_equal(s->domain_name, "local"))
1198         return;
1199
1200     if (s->browse_domain_entry_group)
1201         assert(catta_s_entry_group_is_empty(s->browse_domain_entry_group));
1202     else
1203         s->browse_domain_entry_group = catta_s_entry_group_new(s, NULL, NULL);
1204
1205     if (!s->browse_domain_entry_group) {
1206         catta_log_warn("Failed to create browse domain entry group: %s", catta_strerror(s->error));
1207         return;
1208     }
1209
1210     if (catta_server_add_ptr(s, s->browse_domain_entry_group, CATTA_IF_UNSPEC, CATTA_PROTO_UNSPEC, 0, CATTA_DEFAULT_TTL, "b._dns-sd._udp.local", s->domain_name) < 0) {
1211         catta_log_warn("Failed to add browse domain RR: %s", catta_strerror(s->error));
1212         return;
1213     }
1214
1215     if (catta_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1216         catta_log_warn("Failed to commit browse domain entry group: %s", catta_strerror(s->error));
1217 }
1218
1219 static void register_stuff(CattaServer *s) {
1220     assert(s);
1221
1222     server_set_state(s, CATTA_SERVER_REGISTERING);
1223     s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp CATTA_SERVER_RUNNING too early */
1224
1225     register_hinfo(s);
1226     register_browse_domain(s);
1227     catta_interface_monitor_update_rrs(s->monitor, 0);
1228
1229     assert(s->n_host_rr_pending > 0);
1230     s->n_host_rr_pending --;
1231
1232     if (s->n_host_rr_pending == 0)
1233         server_set_state(s, CATTA_SERVER_RUNNING);
1234 }
1235
1236 static void update_fqdn(CattaServer *s) {
1237     char *n;
1238
1239     assert(s);
1240     assert(s->host_name);
1241     assert(s->domain_name);
1242
1243     if (!(n = catta_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1244         return; /* OOM */
1245
1246     catta_free(s->host_name_fqdn);
1247     s->host_name_fqdn = n;
1248 }
1249
1250 int catta_server_set_host_name(CattaServer *s, const char *host_name) {
1251     char *hn = NULL;
1252     assert(s);
1253
1254     CATTA_CHECK_VALIDITY(s, !host_name || catta_is_valid_host_name(host_name), CATTA_ERR_INVALID_HOST_NAME);
1255
1256     if (!host_name)
1257         hn = catta_get_host_name_strdup();
1258     else
1259         hn = catta_normalize_name_strdup(host_name);
1260
1261     hn[strcspn(hn, ".")] = 0;
1262
1263     if (catta_domain_equal(s->host_name, hn) && s->state != CATTA_SERVER_COLLISION) {
1264         catta_free(hn);
1265         return catta_server_set_errno(s, CATTA_ERR_NO_CHANGE);
1266     }
1267
1268     withdraw_host_rrs(s);
1269
1270     catta_free(s->host_name);
1271     s->host_name = hn;
1272
1273     update_fqdn(s);
1274
1275     register_stuff(s);
1276     return CATTA_OK;
1277 }
1278
1279 int catta_server_set_domain_name(CattaServer *s, const char *domain_name) {
1280     char *dn = NULL;
1281     assert(s);
1282
1283     CATTA_CHECK_VALIDITY(s, !domain_name || catta_is_valid_domain_name(domain_name), CATTA_ERR_INVALID_DOMAIN_NAME);
1284
1285     if (!domain_name)
1286         dn = catta_strdup("local");
1287     else
1288         dn = catta_normalize_name_strdup(domain_name);
1289
1290     if (catta_domain_equal(s->domain_name, domain_name)) {
1291         catta_free(dn);
1292         return catta_server_set_errno(s, CATTA_ERR_NO_CHANGE);
1293     }
1294
1295     withdraw_host_rrs(s);
1296
1297     catta_free(s->domain_name);
1298     s->domain_name = dn;
1299     update_fqdn(s);
1300
1301     register_stuff(s);
1302
1303     catta_free(dn);
1304     return CATTA_OK;
1305 }
1306
1307 static int valid_server_config(const CattaServerConfig *sc) {
1308     CattaStringList *l;
1309
1310     assert(sc);
1311
1312     if (sc->n_wide_area_servers > CATTA_WIDE_AREA_SERVERS_MAX)
1313         return CATTA_ERR_INVALID_CONFIG;
1314
1315     if (sc->host_name && !catta_is_valid_host_name(sc->host_name))
1316         return CATTA_ERR_INVALID_HOST_NAME;
1317
1318     if (sc->domain_name && !catta_is_valid_domain_name(sc->domain_name))
1319         return CATTA_ERR_INVALID_DOMAIN_NAME;
1320
1321     for (l = sc->browse_domains; l; l = l->next)
1322         if (!catta_is_valid_domain_name((char*) l->text))
1323             return CATTA_ERR_INVALID_DOMAIN_NAME;
1324
1325     return CATTA_OK;
1326 }
1327
1328 static int setup_sockets(CattaServer *s) {
1329     assert(s);
1330
1331     s->fd_ipv4 = s->config.use_ipv4 ? catta_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
1332     s->fd_ipv6 = s->config.use_ipv6 ? catta_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
1333
1334     if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1335         return CATTA_ERR_NO_NETWORK;
1336
1337     if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1338         catta_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1339     else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1340         catta_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1341
1342     s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? catta_open_unicast_socket_ipv4() : -1;
1343     s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? catta_open_unicast_socket_ipv6() : -1;
1344
1345     s->watch_ipv4 =
1346         s->watch_ipv6 =
1347         s->watch_legacy_unicast_ipv4 =
1348         s->watch_legacy_unicast_ipv6 = NULL;
1349
1350     if (s->fd_ipv4 >= 0)
1351         s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, CATTA_WATCH_IN, mcast_socket_event, s);
1352     if (s->fd_ipv6 >= 0)
1353         s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, CATTA_WATCH_IN, mcast_socket_event, s);
1354
1355     if (s->fd_legacy_unicast_ipv4 >= 0)
1356         s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, CATTA_WATCH_IN, legacy_unicast_socket_event, s);
1357     if (s->fd_legacy_unicast_ipv6 >= 0)
1358         s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, CATTA_WATCH_IN, legacy_unicast_socket_event, s);
1359
1360     return 0;
1361 }
1362
1363 CattaServer *catta_server_new(const CattaPoll *poll_api, const CattaServerConfig *sc, CattaServerCallback callback, void* userdata, int *error) {
1364     CattaServer *s;
1365     int e;
1366
1367     if (sc && (e = valid_server_config(sc)) < 0) {
1368         if (error)
1369             *error = e;
1370         return NULL;
1371     }
1372
1373     if (!(s = catta_new(CattaServer, 1))) {
1374         if (error)
1375             *error = CATTA_ERR_NO_MEMORY;
1376
1377         return NULL;
1378     }
1379
1380     s->poll_api = poll_api;
1381
1382     if (sc)
1383         catta_server_config_copy(&s->config, sc);
1384     else
1385         catta_server_config_init(&s->config);
1386
1387     winsock_init();  // on Windows, call WSAStartup; no-op on other platforms
1388     if ((e = setup_sockets(s)) < 0) {
1389         if (error)
1390             *error = e;
1391
1392         catta_server_config_free(&s->config);
1393         catta_free(s);
1394         winsock_exit();
1395
1396         return NULL;
1397     }
1398
1399     s->n_host_rr_pending = 0;
1400     s->need_entry_cleanup = 0;
1401     s->need_group_cleanup = 0;
1402     s->need_browser_cleanup = 0;
1403     s->cleanup_time_event = NULL;
1404     s->hinfo_entry_group = NULL;
1405     s->browse_domain_entry_group = NULL;
1406     s->error = CATTA_OK;
1407     s->state = CATTA_SERVER_INVALID;
1408
1409     s->callback = callback;
1410     s->userdata = userdata;
1411
1412     s->time_event_queue = catta_time_event_queue_new(poll_api);
1413
1414     s->entries_by_key = catta_hashmap_new((CattaHashFunc) catta_key_hash, (CattaEqualFunc) catta_key_equal, NULL, NULL);
1415     CATTA_LLIST_HEAD_INIT(CattaEntry, s->entries);
1416     CATTA_LLIST_HEAD_INIT(CattaGroup, s->groups);
1417
1418     s->record_browser_hashmap = catta_hashmap_new((CattaHashFunc) catta_key_hash, (CattaEqualFunc) catta_key_equal, NULL, NULL);
1419     CATTA_LLIST_HEAD_INIT(CattaSRecordBrowser, s->record_browsers);
1420     CATTA_LLIST_HEAD_INIT(CattaSHostNameResolver, s->host_name_resolvers);
1421     CATTA_LLIST_HEAD_INIT(CattaSAddressResolver, s->address_resolvers);
1422     CATTA_LLIST_HEAD_INIT(CattaSDomainBrowser, s->domain_browsers);
1423     CATTA_LLIST_HEAD_INIT(CattaSServiceTypeBrowser, s->service_type_browsers);
1424     CATTA_LLIST_HEAD_INIT(CattaSServiceBrowser, s->service_browsers);
1425     CATTA_LLIST_HEAD_INIT(CattaSServiceResolver, s->service_resolvers);
1426     CATTA_LLIST_HEAD_INIT(CattaSDNSServerBrowser, s->dns_server_browsers);
1427
1428     s->legacy_unicast_reflect_slots = NULL;
1429     s->legacy_unicast_reflect_id = 0;
1430
1431     s->record_list = catta_record_list_new();
1432
1433     /* Get host name */
1434     s->host_name = s->config.host_name ? catta_normalize_name_strdup(s->config.host_name) : catta_get_host_name_strdup();
1435     s->host_name[strcspn(s->host_name, ".")] = 0;
1436     s->domain_name = s->config.domain_name ? catta_normalize_name_strdup(s->config.domain_name) : catta_strdup("local");
1437     s->host_name_fqdn = NULL;
1438     update_fqdn(s);
1439
1440     do {
1441         s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1442     } while (s->local_service_cookie == CATTA_SERVICE_COOKIE_INVALID);
1443
1444     if (s->config.enable_wide_area) {
1445         s->wide_area_lookup_engine = catta_wide_area_engine_new(s);
1446         catta_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1447     } else
1448         s->wide_area_lookup_engine = NULL;
1449
1450     s->multicast_lookup_engine = catta_multicast_lookup_engine_new(s);
1451
1452     s->monitor = catta_interface_monitor_new(s);
1453     catta_interface_monitor_sync(s->monitor);
1454
1455     register_localhost(s);
1456     register_stuff(s);
1457
1458     return s;
1459 }
1460
1461 void catta_server_free(CattaServer* s) {
1462     assert(s);
1463
1464     /* Remove all browsers */
1465
1466     while (s->dns_server_browsers)
1467         catta_s_dns_server_browser_free(s->dns_server_browsers);
1468     while (s->host_name_resolvers)
1469         catta_s_host_name_resolver_free(s->host_name_resolvers);
1470     while (s->address_resolvers)
1471         catta_s_address_resolver_free(s->address_resolvers);
1472     while (s->domain_browsers)
1473         catta_s_domain_browser_free(s->domain_browsers);
1474     while (s->service_type_browsers)
1475         catta_s_service_type_browser_free(s->service_type_browsers);
1476     while (s->service_browsers)
1477         catta_s_service_browser_free(s->service_browsers);
1478     while (s->service_resolvers)
1479         catta_s_service_resolver_free(s->service_resolvers);
1480     while (s->record_browsers)
1481         catta_s_record_browser_destroy(s->record_browsers);
1482
1483     /* Remove all locally rgeistered stuff */
1484
1485     while(s->entries)
1486         catta_entry_free(s, s->entries);
1487
1488     catta_interface_monitor_free(s->monitor);
1489
1490     while (s->groups)
1491         catta_entry_group_free(s, s->groups);
1492
1493     free_slots(s);
1494
1495     catta_hashmap_free(s->entries_by_key);
1496     catta_record_list_free(s->record_list);
1497     catta_hashmap_free(s->record_browser_hashmap);
1498
1499     if (s->wide_area_lookup_engine)
1500         catta_wide_area_engine_free(s->wide_area_lookup_engine);
1501     catta_multicast_lookup_engine_free(s->multicast_lookup_engine);
1502
1503     if (s->cleanup_time_event)
1504         catta_time_event_free(s->cleanup_time_event);
1505
1506     catta_time_event_queue_free(s->time_event_queue);
1507
1508     /* Free watches */
1509
1510     if (s->watch_ipv4)
1511         s->poll_api->watch_free(s->watch_ipv4);
1512     if (s->watch_ipv6)
1513         s->poll_api->watch_free(s->watch_ipv6);
1514
1515     if (s->watch_legacy_unicast_ipv4)
1516         s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1517     if (s->watch_legacy_unicast_ipv6)
1518         s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1519
1520     /* Free sockets */
1521
1522     if (s->fd_ipv4 >= 0)
1523         closesocket(s->fd_ipv4);
1524     if (s->fd_ipv6 >= 0)
1525         closesocket(s->fd_ipv6);
1526
1527     if (s->fd_legacy_unicast_ipv4 >= 0)
1528         closesocket(s->fd_legacy_unicast_ipv4);
1529     if (s->fd_legacy_unicast_ipv6 >= 0)
1530         closesocket(s->fd_legacy_unicast_ipv6);
1531
1532     /* Free other stuff */
1533
1534     catta_free(s->host_name);
1535     catta_free(s->domain_name);
1536     catta_free(s->host_name_fqdn);
1537
1538     catta_server_config_free(&s->config);
1539
1540     catta_free(s);
1541     winsock_exit();  // on Windows, call WSACleanup(); no-op on other platforms
1542 }
1543
1544 const char* catta_server_get_domain_name(CattaServer *s) {
1545     assert(s);
1546
1547     return s->domain_name;
1548 }
1549
1550 const char* catta_server_get_host_name(CattaServer *s) {
1551     assert(s);
1552
1553     return s->host_name;
1554 }
1555
1556 const char* catta_server_get_host_name_fqdn(CattaServer *s) {
1557     assert(s);
1558
1559     return s->host_name_fqdn;
1560 }
1561
1562 void* catta_server_get_data(CattaServer *s) {
1563     assert(s);
1564
1565     return s->userdata;
1566 }
1567
1568 void catta_server_set_data(CattaServer *s, void* userdata) {
1569     assert(s);
1570
1571     s->userdata = userdata;
1572 }
1573
1574 CattaServerState catta_server_get_state(CattaServer *s) {
1575     assert(s);
1576
1577     return s->state;
1578 }
1579
1580 CattaServerConfig* catta_server_config_init(CattaServerConfig *c) {
1581     assert(c);
1582
1583     memset(c, 0, sizeof(CattaServerConfig));
1584     c->use_ipv6 = 1;
1585     c->use_ipv4 = 1;
1586     c->allow_interfaces = NULL;
1587     c->deny_interfaces = NULL;
1588     c->host_name = NULL;
1589     c->domain_name = NULL;
1590     c->check_response_ttl = 0;
1591     c->publish_hinfo = 0;
1592     c->publish_addresses = 1;
1593     c->publish_no_reverse = 0;
1594     c->publish_workstation = 0;
1595     c->publish_domain = 1;
1596     c->use_iff_running = 0;
1597     c->enable_reflector = 0;
1598     c->reflect_ipv = 0;
1599     c->add_service_cookie = 0;
1600     c->enable_wide_area = 0;
1601     c->n_wide_area_servers = 0;
1602     c->disallow_other_stacks = 0;
1603     c->browse_domains = NULL;
1604     c->disable_publishing = 0;
1605     c->allow_point_to_point = 0;
1606     c->publish_aaaa_on_ipv4 = 1;
1607     c->publish_a_on_ipv6 = 0;
1608     c->n_cache_entries_max = CATTA_DEFAULT_CACHE_ENTRIES_MAX;
1609     c->ratelimit_interval = 0;
1610     c->ratelimit_burst = 0;
1611
1612     return c;
1613 }
1614
1615 void catta_server_config_free(CattaServerConfig *c) {
1616     assert(c);
1617
1618     catta_free(c->host_name);
1619     catta_free(c->domain_name);
1620     catta_string_list_free(c->browse_domains);
1621     catta_string_list_free(c->allow_interfaces);
1622     catta_string_list_free(c->deny_interfaces);
1623 }
1624
1625 CattaServerConfig* catta_server_config_copy(CattaServerConfig *ret, const CattaServerConfig *c) {
1626     char *d = NULL, *h = NULL;
1627     CattaStringList *browse = NULL, *allow = NULL, *deny = NULL;
1628     assert(ret);
1629     assert(c);
1630
1631     if (c->host_name)
1632         if (!(h = catta_strdup(c->host_name)))
1633             return NULL;
1634
1635     if (c->domain_name)
1636         if (!(d = catta_strdup(c->domain_name))) {
1637             catta_free(h);
1638             return NULL;
1639         }
1640
1641     if (!(browse = catta_string_list_copy(c->browse_domains)) && c->browse_domains) {
1642         catta_free(h);
1643         catta_free(d);
1644         return NULL;
1645     }
1646
1647     if (!(allow = catta_string_list_copy(c->allow_interfaces)) && c->allow_interfaces) {
1648         catta_string_list_free(browse);
1649         catta_free(h);
1650         catta_free(d);
1651         return NULL;
1652     }
1653
1654     if (!(deny = catta_string_list_copy(c->deny_interfaces)) && c->deny_interfaces) {
1655         catta_string_list_free(allow);
1656         catta_string_list_free(browse);
1657         catta_free(h);
1658         catta_free(d);
1659         return NULL;
1660     }
1661
1662     *ret = *c;
1663     ret->host_name = h;
1664     ret->domain_name = d;
1665     ret->browse_domains = browse;
1666     ret->allow_interfaces = allow;
1667     ret->deny_interfaces = deny;
1668
1669     return ret;
1670 }
1671
1672 int catta_server_errno(CattaServer *s) {
1673     assert(s);
1674
1675     return s->error;
1676 }
1677
1678 /* Just for internal use */
1679 int catta_server_set_errno(CattaServer *s, int error) {
1680     assert(s);
1681
1682     return s->error = error;
1683 }
1684
1685 uint32_t catta_server_get_local_service_cookie(CattaServer *s) {
1686     assert(s);
1687
1688     return s->local_service_cookie;
1689 }
1690
1691 static CattaEntry *find_entry(CattaServer *s, CattaIfIndex iface, CattaProtocol protocol, CattaKey *key) {
1692     CattaEntry *e;
1693
1694     assert(s);
1695     assert(key);
1696
1697     for (e = catta_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
1698
1699         if ((e->iface == iface || e->iface <= 0 || iface <= 0) &&
1700             (e->protocol == protocol || e->protocol == CATTA_PROTO_UNSPEC || protocol == CATTA_PROTO_UNSPEC) &&
1701             (!e->group || e->group->state == CATTA_ENTRY_GROUP_ESTABLISHED || e->group->state == CATTA_ENTRY_GROUP_REGISTERING))
1702
1703             return e;
1704
1705     return NULL;
1706 }
1707
1708 int catta_server_get_group_of_service(CattaServer *s, CattaIfIndex iface, CattaProtocol protocol, const char *name, const char *type, const char *domain, CattaSEntryGroup** ret_group) {
1709     CattaKey *key = NULL;
1710     CattaEntry *e;
1711     int ret;
1712     char n[CATTA_DOMAIN_NAME_MAX];
1713
1714     assert(s);
1715     assert(name);
1716     assert(type);
1717     assert(ret_group);
1718
1719     CATTA_CHECK_VALIDITY(s, CATTA_IF_VALID(iface), CATTA_ERR_INVALID_INTERFACE);
1720     CATTA_CHECK_VALIDITY(s, CATTA_PROTO_VALID(protocol), CATTA_ERR_INVALID_PROTOCOL);
1721     CATTA_CHECK_VALIDITY(s, catta_is_valid_service_name(name), CATTA_ERR_INVALID_SERVICE_NAME);
1722     CATTA_CHECK_VALIDITY(s, catta_is_valid_service_type_strict(type), CATTA_ERR_INVALID_SERVICE_TYPE);
1723     CATTA_CHECK_VALIDITY(s, !domain || catta_is_valid_domain_name(domain), CATTA_ERR_INVALID_DOMAIN_NAME);
1724
1725     if ((ret = catta_service_name_join(n, sizeof(n), name, type, domain) < 0))
1726         return catta_server_set_errno(s, ret);
1727
1728     if (!(key = catta_key_new(n, CATTA_DNS_CLASS_IN, CATTA_DNS_TYPE_SRV)))
1729         return catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
1730
1731     e = find_entry(s, iface, protocol, key);
1732     catta_key_unref(key);
1733
1734     if (e) {
1735         *ret_group = e->group;
1736         return CATTA_OK;
1737     }
1738
1739     return catta_server_set_errno(s, CATTA_ERR_NOT_FOUND);
1740 }
1741
1742 int catta_server_is_service_local(CattaServer *s, CattaIfIndex iface, CattaProtocol protocol, const char *name) {
1743     CattaKey *key = NULL;
1744     CattaEntry *e;
1745
1746     assert(s);
1747     assert(name);
1748
1749     if (!s->host_name_fqdn)
1750         return 0;
1751
1752     if (!(key = catta_key_new(name, CATTA_DNS_CLASS_IN, CATTA_DNS_TYPE_SRV)))
1753         return 0;
1754
1755     e = find_entry(s, iface, protocol, key);
1756     catta_key_unref(key);
1757
1758     if (!e)
1759         return 0;
1760
1761     return catta_domain_equal(s->host_name_fqdn, e->record->data.srv.name);
1762 }
1763
1764 int catta_server_is_record_local(CattaServer *s, CattaIfIndex iface, CattaProtocol protocol, CattaRecord *record) {
1765     CattaEntry *e;
1766
1767     assert(s);
1768     assert(record);
1769
1770     for (e = catta_hashmap_lookup(s->entries_by_key, record->key); e; e = e->by_key_next)
1771
1772         if ((e->iface == iface || e->iface <= 0 || iface <= 0) &&
1773             (e->protocol == protocol || e->protocol == CATTA_PROTO_UNSPEC || protocol == CATTA_PROTO_UNSPEC) &&
1774             (!e->group || e->group->state == CATTA_ENTRY_GROUP_ESTABLISHED || e->group->state == CATTA_ENTRY_GROUP_REGISTERING) &&
1775             catta_record_equal_no_ttl(record, e->record))
1776             return 1;
1777
1778     return 0;
1779 }
1780
1781 /** Set the wide area DNS servers */
1782 int catta_server_set_wide_area_servers(CattaServer *s, const CattaAddress *a, unsigned n) {
1783     assert(s);
1784
1785     if (!s->wide_area_lookup_engine)
1786         return catta_server_set_errno(s, CATTA_ERR_INVALID_CONFIG);
1787
1788     catta_wide_area_set_servers(s->wide_area_lookup_engine, a, n);
1789     return CATTA_OK;
1790 }
1791
1792 const CattaServerConfig* catta_server_get_config(CattaServer *s) {
1793     assert(s);
1794
1795     return &s->config;
1796 }
1797
1798 /** Set the browsing domains */
1799 int catta_server_set_browse_domains(CattaServer *s, CattaStringList *domains) {
1800     CattaStringList *l;
1801
1802     assert(s);
1803
1804     for (l = s->config.browse_domains; l; l = l->next)
1805         if (!catta_is_valid_domain_name((char*) l->text))
1806             return catta_server_set_errno(s, CATTA_ERR_INVALID_DOMAIN_NAME);
1807
1808     catta_string_list_free(s->config.browse_domains);
1809     s->config.browse_domains = catta_string_list_copy(domains);
1810
1811     return CATTA_OK;
1812 }