]> git.meshlink.io Git - catta/blob - server.c
* add subscription feature - with reissuing
[catta] / server.c
1 #include <sys/socket.h>
2 #include <arpa/inet.h>
3 #include <string.h>
4 #include <sys/utsname.h>
5 #include <unistd.h>
6
7 #include "server.h"
8 #include "util.h"
9 #include "iface.h"
10 #include "socket.h"
11 #include "subscribe.h"
12
13 static void handle_query_key(flxServer *s, flxKey *k, flxInterface *i, const flxAddress *a) {
14     flxServerEntry *e;
15     gchar *txt;
16     
17     g_assert(s);
18     g_assert(k);
19     g_assert(i);
20     g_assert(a);
21
22     g_message("Handling query: %s", txt = flx_key_to_string(k));
23     g_free(txt);
24
25     for (e = g_hash_table_lookup(s->rrset_by_key, k); e; e = e->by_key_next)
26         if (flx_interface_match(i, e->interface, e->protocol))
27             flx_interface_post_response(i, e->record);
28 }
29
30 static void handle_query(flxServer *s, flxDnsPacket *p, flxInterface *i, const flxAddress *a) {
31     guint n;
32     
33     g_assert(s);
34     g_assert(p);
35     g_assert(i);
36     g_assert(a);
37
38     for (n = flx_dns_packet_get_field(p, DNS_FIELD_QDCOUNT); n > 0; n --) {
39         flxKey *key;
40
41         if (!(key = flx_dns_packet_consume_key(p))) {
42             g_warning("Packet too short");
43             return;
44         }
45
46         handle_query_key(s, key, i, a);
47         flx_key_unref(key);
48     }
49 }
50
51 static void handle_response(flxServer *s, flxDnsPacket *p, flxInterface *i, const flxAddress *a) {
52     guint n;
53     
54     g_assert(s);
55     g_assert(p);
56     g_assert(i);
57     g_assert(a);
58     
59     for (n = flx_dns_packet_get_field(p, DNS_FIELD_ANCOUNT) +
60              flx_dns_packet_get_field(p, DNS_FIELD_ARCOUNT); n > 0; n--) {
61         flxRecord *record;
62         gboolean cache_flush = FALSE;
63         gchar *txt;
64         
65         if (!(record = flx_dns_packet_consume_record(p, &cache_flush))) {
66             g_warning("Packet too short");
67             return;
68         }
69
70         g_message("Handling response: %s", txt = flx_record_to_string(record));
71         g_free(txt);
72
73         flx_cache_update(i->cache, record, cache_flush, a);
74
75         if (record->ttl != 0)
76             flx_packet_scheduler_drop_response(i->scheduler, record);
77         flx_record_unref(record);
78     }
79 }
80
81 static void dispatch_packet(flxServer *s, flxDnsPacket *p, struct sockaddr *sa, gint iface, gint ttl) {
82     flxInterface *i;
83     flxAddress a;
84     
85     g_assert(s);
86     g_assert(p);
87     g_assert(sa);
88     g_assert(iface > 0);
89
90     g_message("new packet recieved.");
91
92     if (!(i = flx_interface_monitor_get_interface(s->monitor, iface, sa->sa_family))) {
93         g_warning("Recieved packet from invalid interface.");
94         return;
95     }
96
97     if (ttl != 255) {
98         g_warning("Recieved packet with invalid TTL on interface '%s.%i'.", i->hardware->name, i->protocol);
99         return;
100     }
101
102     if (sa->sa_family == AF_INET6) {
103         static const unsigned char ipv4_in_ipv6[] = {
104             0x00, 0x00, 0x00, 0x00,
105             0x00, 0x00, 0x00, 0x00,
106             0xFF, 0xFF, 0xFF, 0xFF };
107
108         if (memcmp(((struct sockaddr_in6*) sa)->sin6_addr.s6_addr, ipv4_in_ipv6, sizeof(ipv4_in_ipv6)) == 0) {
109
110             /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
111             return;
112         }
113     }
114
115     if (flx_dns_packet_check_valid(p) < 0) {
116         g_warning("Recieved invalid packet.");
117         return;
118     }
119
120     flx_address_from_sockaddr(sa, &a);
121
122     if (flx_dns_packet_is_query(p)) {
123
124         if (flx_dns_packet_get_field(p, DNS_FIELD_QDCOUNT) == 0 ||
125             flx_dns_packet_get_field(p, DNS_FIELD_ARCOUNT) != 0 ||
126             flx_dns_packet_get_field(p, DNS_FIELD_NSCOUNT) != 0) {
127             g_warning("Invalid query packet.");
128             return;
129         }
130                 
131         handle_query(s, p, i, &a);    
132         g_message("Handled query");
133     } else {
134         if (flx_dns_packet_get_field(p, DNS_FIELD_QDCOUNT) != 0 ||
135             flx_dns_packet_get_field(p, DNS_FIELD_ANCOUNT) == 0 ||
136             flx_dns_packet_get_field(p, DNS_FIELD_NSCOUNT) != 0) {
137             g_warning("Invalid response packet.");
138             return;
139         }
140
141         handle_response(s, p, i, &a);
142         g_message("Handled response");
143     }
144 }
145
146 static gboolean work(flxServer *s) {
147     struct sockaddr_in6 sa6;
148     struct sockaddr_in sa;
149     flxDnsPacket *p;
150     gint iface = -1;
151     guint8 ttl;
152         
153     g_assert(s);
154
155     if (s->pollfd_ipv4.revents & G_IO_IN) {
156         if ((p = flx_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &iface, &ttl))) {
157             dispatch_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
158             flx_dns_packet_free(p);
159         }
160     }
161
162     if (s->pollfd_ipv6.revents & G_IO_IN) {
163         if ((p = flx_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &iface, &ttl))) {
164             dispatch_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
165             flx_dns_packet_free(p);
166         }
167     }
168
169     return TRUE;
170 }
171
172 static gboolean prepare_func(GSource *source, gint *timeout) {
173     g_assert(source);
174     g_assert(timeout);
175     
176     *timeout = -1;
177     return FALSE;
178 }
179
180 static gboolean check_func(GSource *source) {
181     flxServer* s;
182     g_assert(source);
183
184     s = *((flxServer**) (((guint8*) source) + sizeof(GSource)));
185     g_assert(s);
186     
187     return (s->pollfd_ipv4.revents | s->pollfd_ipv6.revents) & (G_IO_IN | G_IO_HUP | G_IO_ERR);
188 }
189
190 static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) {
191     flxServer* s;
192     g_assert(source);
193
194     s = *((flxServer**) (((guint8*) source) + sizeof(GSource)));
195     g_assert(s);
196     
197     return work(s);
198 }
199
200 static void add_default_entries(flxServer *s) {
201     gint length = 0;
202     struct utsname utsname;
203     gchar *hinfo;
204     flxAddress a;
205     
206     g_assert(s);
207     
208     /* Fill in HINFO rr */
209     uname(&utsname);
210     hinfo = g_strdup_printf("%c%s%c%s%n",
211                             strlen(utsname.machine), g_strup(utsname.machine),
212                             strlen(utsname.sysname), g_strup(utsname.sysname),
213                             &length);
214     
215     flx_server_add_full(s, 0, 0, AF_UNSPEC, TRUE,
216                         s->hostname, FLX_DNS_CLASS_IN, FLX_DNS_TYPE_HINFO, hinfo, length, FLX_DEFAULT_TTL);
217
218     g_free(hinfo);
219
220     /* Add localhost entries */
221     flx_address_parse("127.0.0.1", AF_INET, &a);
222     flx_server_add_address(s, 0, 0, AF_UNSPEC, TRUE, "localhost", &a);
223
224     flx_address_parse("::1", AF_INET6, &a);
225     flx_server_add_address(s, 0, 0, AF_UNSPEC, TRUE, "ip6-localhost", &a);
226 }
227
228 flxServer *flx_server_new(GMainContext *c) {
229     gchar *hn, *e;
230     flxServer *s;
231     
232     static GSourceFuncs source_funcs = {
233         prepare_func,
234         check_func,
235         dispatch_func,
236         NULL,
237         NULL,
238         NULL
239     };
240
241     s = g_new(flxServer, 1);
242
243     s->fd_ipv4 = flx_open_socket_ipv4();
244     s->fd_ipv6 = flx_open_socket_ipv6();
245     
246     if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) {
247         g_critical("Failed to create sockets.\n");
248         g_free(s);
249         return NULL;
250     }
251
252     if (s->fd_ipv4 < 0)
253         g_message("Failed to create IPv4 socket, proceeding in IPv6 only mode");
254     else if (s->fd_ipv6 < 0)
255         g_message("Failed to create IPv6 socket, proceeding in IPv4 only mode");
256     
257     if (c)
258         g_main_context_ref(s->context = c);
259     else
260         s->context = g_main_context_default();
261     
262     s->current_id = 1;
263
264     FLX_LLIST_HEAD_INIT(flxServerEntry, s->entries);
265     s->rrset_by_id = g_hash_table_new(g_int_hash, g_int_equal);
266     s->rrset_by_key = g_hash_table_new((GHashFunc) flx_key_hash, (GEqualFunc) flx_key_equal);
267
268     FLX_LLIST_HEAD_INIT(flxSubscription, s->subscriptions);
269     s->subscription_hashtable = g_hash_table_new((GHashFunc) flx_key_hash, (GEqualFunc) flx_key_equal);
270
271     s->monitor = flx_interface_monitor_new(s);
272     s->time_event_queue = flx_time_event_queue_new(s->context);
273     
274     /* Get host name */
275     hn = flx_get_host_name();
276     if ((e = strchr(hn, '.')))
277         *e = 0;
278
279     s->hostname = g_strdup_printf("%s.local.", hn);
280     g_free(hn);
281
282     add_default_entries(s);
283
284     s->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(flxServer*));
285     *((flxServer**) (((guint8*) s->source) + sizeof(GSource))) = s;
286
287     memset(&s->pollfd_ipv4, 0, sizeof(s->pollfd_ipv4));
288     s->pollfd_ipv4.fd = s->fd_ipv4;
289     s->pollfd_ipv4.events = G_IO_IN|G_IO_ERR|G_IO_HUP;
290     g_source_add_poll(s->source, &s->pollfd_ipv4);
291     
292     memset(&s->pollfd_ipv6, 0, sizeof(s->pollfd_ipv6));
293     s->pollfd_ipv6.fd = s->fd_ipv6;
294     s->pollfd_ipv6.events = G_IO_IN|G_IO_ERR|G_IO_HUP;
295     g_source_add_poll(s->source, &s->pollfd_ipv6);
296
297     g_source_attach(s->source, s->context);
298     
299     return s;
300 }
301
302 void flx_server_free(flxServer* s) {
303     g_assert(s);
304
305     flx_interface_monitor_free(s->monitor);
306     
307     flx_server_remove(s, 0);
308
309     while (s->subscriptions)
310         flx_subscription_free(s->subscriptions);
311     g_hash_table_destroy(s->subscription_hashtable);
312     
313     g_hash_table_destroy(s->rrset_by_id);
314     g_hash_table_destroy(s->rrset_by_key);
315
316     flx_time_event_queue_free(s->time_event_queue);
317
318     if (s->fd_ipv4 >= 0)
319         close(s->fd_ipv4);
320     if (s->fd_ipv6 >= 0)
321         close(s->fd_ipv6);
322     
323     g_free(s->hostname);
324
325     g_source_destroy(s->source);
326     g_source_unref(s->source);
327     g_main_context_unref(s->context);
328
329     g_free(s);
330 }
331
332 gint flx_server_get_next_id(flxServer *s) {
333     g_assert(s);
334
335     return s->current_id++;
336 }
337
338 void flx_server_add(
339     flxServer *s,
340     gint id,
341     gint interface,
342     guchar protocol,
343     gboolean unique,
344     flxRecord *r) {
345     
346     flxServerEntry *e, *t;
347     g_assert(s);
348     g_assert(r);
349
350     e = g_new(flxServerEntry, 1);
351     e->record = flx_record_ref(r);
352     e->id = id;
353     e->interface = interface;
354     e->protocol = protocol;
355     e->unique = unique;
356
357     FLX_LLIST_HEAD_INIT(flxAnnouncement, e->announcements);
358
359     FLX_LLIST_PREPEND(flxServerEntry, entry, s->entries, e);
360
361     /* Insert into hash table indexed by id */
362     t = g_hash_table_lookup(s->rrset_by_id, &e->id);
363     FLX_LLIST_PREPEND(flxServerEntry, by_id, t, e);
364     g_hash_table_replace(s->rrset_by_id, &e->id, t);
365     
366     /* Insert into hash table indexed by name */
367     t = g_hash_table_lookup(s->rrset_by_key, e->record->key);
368     FLX_LLIST_PREPEND(flxServerEntry, by_key, t, e);
369     g_hash_table_replace(s->rrset_by_key, e->record->key, t);
370
371     flx_announce_entry(s, e);
372 }
373
374 void flx_server_add_full(
375     flxServer *s,
376     gint id,
377     gint interface,
378     guchar protocol,
379     gboolean unique,
380     const gchar *name,
381     guint16 class,
382     guint16 type,
383     gconstpointer data,
384     guint size,
385     guint32 ttl) {
386     
387     flxRecord *r;
388     g_assert(s);
389     g_assert(data);
390     g_assert(size);
391
392     r = flx_record_new_full(name ? name : s->hostname, class, type, data, size, ttl);
393     flx_server_add(s, id, interface, protocol, unique, r);
394     flx_record_unref(r);
395 }
396
397 const flxRecord *flx_server_iterate(flxServer *s, gint id, void **state) {
398     flxServerEntry **e = (flxServerEntry**) state;
399     g_assert(s);
400     g_assert(e);
401
402     if (e)
403         *e = id > 0 ? (*e)->by_id_next : (*e)->entry_next;
404     else
405         *e = id > 0 ? g_hash_table_lookup(s->rrset_by_id, &id) : s->entries;
406         
407     if (!*e)
408         return NULL;
409
410     return flx_record_ref((*e)->record);
411 }
412
413 static void free_entry(flxServer*s, flxServerEntry *e) {
414     flxServerEntry *t;
415     
416     g_assert(e);
417
418     flx_goodbye_entry(s, e, TRUE);
419
420     /* Remove from linked list */
421     FLX_LLIST_REMOVE(flxServerEntry, entry, s->entries, e);
422
423     /* Remove from hash table indexed by id */
424     t = g_hash_table_lookup(s->rrset_by_id, &e->id);
425     FLX_LLIST_REMOVE(flxServerEntry, by_id, t, e);
426     if (t)
427         g_hash_table_replace(s->rrset_by_id, &t->id, t);
428     else
429         g_hash_table_remove(s->rrset_by_id, &e->id);
430     
431     /* Remove from hash table indexed by name */
432     t = g_hash_table_lookup(s->rrset_by_key, e->record->key);
433     FLX_LLIST_REMOVE(flxServerEntry, by_key, t, e);
434     if (t)
435         g_hash_table_replace(s->rrset_by_key, t->record->key, t);
436     else
437         g_hash_table_remove(s->rrset_by_key, e->record->key);
438
439     flx_record_unref(e->record);
440     g_free(e);
441 }
442
443 void flx_server_remove(flxServer *s, gint id) {
444     g_assert(s);
445
446     if (id <= 0) {
447         while (s->entries)
448             free_entry(s, s->entries);
449     } else {
450         flxServerEntry *e;
451
452         while ((e = g_hash_table_lookup(s->rrset_by_id, &id)))
453             free_entry(s, e);
454     }
455 }
456
457 void flx_server_dump(flxServer *s, FILE *f) {
458     flxServerEntry *e;
459     g_assert(s);
460     g_assert(f);
461
462     fprintf(f, "\n;;; ZONE DUMP FOLLOWS ;;;\n");
463
464     for (e = s->entries; e; e = e->entry_next) {
465         gchar *t;
466
467         t = flx_record_to_string(e->record);
468         fprintf(f, "%s\n", t);
469         g_free(t);
470     }
471
472     flx_dump_caches(s->monitor, f);
473 }
474
475 void flx_server_add_address(
476     flxServer *s,
477     gint id,
478     gint interface,
479     guchar protocol,
480     gboolean unique,
481     const gchar *name,
482     flxAddress *a) {
483
484     gchar *n;
485     g_assert(s);
486     g_assert(a);
487
488     n = name ? flx_normalize_name(name) : s->hostname;
489     
490     if (a->family == AF_INET) {
491         gchar *r;
492         
493         flx_server_add_full(s, id, interface, protocol, unique, n, FLX_DNS_CLASS_IN, FLX_DNS_TYPE_A, &a->data.ipv4, sizeof(a->data.ipv4), FLX_DEFAULT_TTL);
494
495         r = flx_reverse_lookup_name_ipv4(&a->data.ipv4);
496         g_assert(r);
497         flx_server_add_full(s, id, interface, protocol, unique, r, FLX_DNS_CLASS_IN, FLX_DNS_TYPE_PTR, n, strlen(n)+1, FLX_DEFAULT_TTL);
498         g_free(r);
499         
500     } else {
501         gchar *r;
502             
503         flx_server_add_full(s, id, interface, protocol, unique, n, FLX_DNS_CLASS_IN, FLX_DNS_TYPE_AAAA, &a->data.ipv6, sizeof(a->data.ipv6), FLX_DEFAULT_TTL);
504
505         r = flx_reverse_lookup_name_ipv6_arpa(&a->data.ipv6);
506         g_assert(r);
507         flx_server_add_full(s, id, interface, protocol, unique, r, FLX_DNS_CLASS_IN, FLX_DNS_TYPE_PTR, n, strlen(n)+1, FLX_DEFAULT_TTL);
508         g_free(r);
509     
510         r = flx_reverse_lookup_name_ipv6_int(&a->data.ipv6);
511         g_assert(r);
512         flx_server_add_full(s, id, interface, protocol, unique, r, FLX_DNS_CLASS_IN, FLX_DNS_TYPE_PTR, n, strlen(n)+1, FLX_DEFAULT_TTL);
513         g_free(r);
514     }
515     
516     g_free(n);
517 }
518
519 void flx_server_add_text(
520     flxServer *s,
521     gint id,
522     gint interface,
523     guchar protocol,
524     gboolean unique,
525     const gchar *name,
526     const gchar *text) {
527     
528     gchar buf[256];
529     guint l;
530     
531     g_assert(s);
532     g_assert(text);
533
534     if ((l = strlen(text)) > 255)
535         buf[0] = 255;
536     else
537         buf[0] = (gchar) l;
538
539     memcpy(buf+1, text, l);
540
541     flx_server_add_full(s, id, interface, protocol, unique, name, FLX_DNS_CLASS_IN, FLX_DNS_TYPE_TXT, buf, l+1, FLX_DEFAULT_TTL);
542 }
543
544 static void post_query_callback(flxInterfaceMonitor *m, flxInterface *i, gpointer userdata) {
545     flxKey *k = userdata;
546
547     g_assert(m);
548     g_assert(i);
549     g_assert(k);
550
551     flx_interface_post_query(i, k);
552 }
553
554 void flx_server_post_query(flxServer *s, gint interface, guchar protocol, flxKey *key) {
555     g_assert(s);
556     g_assert(key);
557
558     flx_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
559 }
560
561 static void post_response_callback(flxInterfaceMonitor *m, flxInterface *i, gpointer userdata) {
562     flxRecord *r = userdata;
563
564     g_assert(m);
565     g_assert(i);
566     g_assert(r);
567
568     flx_interface_post_response(i, r);
569 }
570
571 void flx_server_post_response(flxServer *s, gint interface, guchar protocol, flxRecord *record) {
572     g_assert(s);
573     g_assert(record);
574
575     flx_interface_monitor_walk(s->monitor, interface, protocol, post_response_callback, record);
576 }