]> git.meshlink.io Git - catta/blob - server.c
fold local.c into iface.c
[catta] / server.c
1 #include <sys/socket.h>
2 #include <arpa/inet.h>
3 #include <string.h>
4 #include <sys/utsname.h>
5
6 #include "server.h"
7 #include "util.h"
8 #include "iface.h"
9
10 static gint timeval_cmp(const GTimeVal *a, const GTimeVal *b) {
11     g_assert(a);
12     g_assert(b);
13
14     if (a->tv_sec < b->tv_sec)
15         return -1;
16
17     if (a->tv_sec > b->tv_sec)
18         return 1;
19
20     if (a->tv_usec < b->tv_usec)
21         return -1;
22
23     if (a->tv_usec > b->tv_usec)
24         return 1;
25
26     return 0;
27 }
28
29 static gint query_job_instance_compare(gpointer a, gpointer b) {
30     flxQueryJobInstance *j = a, *k = b;
31     g_assert(j);
32     g_assert(k);
33
34     return timeval_cmp(&j->job->time, &k->job->time);
35 }
36
37 static gint response_job_instance_compare(gpointer a, gpointer b) {
38     flxResponseJobInstance *j = a, *k = b;
39     g_assert(j);
40     g_assert(k);
41
42     return timeval_cmp(&j->job->time, &k->job->time);
43 }
44
45 flxServer *flx_server_new(GMainContext *c) {
46     gchar *hn, *e, *hinfo;
47     struct utsname utsname;
48     gint length;
49     flxAddress a;
50     flxServer *s = g_new(flxServer, 1);
51
52     if (c) {
53         g_main_context_ref(c);
54         s->context = c;
55     } else
56         s->context = g_main_context_default();
57     
58     s->current_id = 1;
59     s->rrset_by_id = g_hash_table_new(g_int_hash, g_int_equal);
60     s->rrset_by_name = g_hash_table_new(g_str_hash, g_str_equal);
61     s->entries = NULL;
62
63     s->query_job_queue = flx_prio_queue_new(query_job_instance_compare);
64     s->response_job_queue = flx_prio_queue_new(response_job_instance_compare);
65     
66     s->monitor = flx_interface_monitor_new(s);
67
68     /* Get host name */
69     hn = flx_get_host_name();
70     if ((e = strchr(hn, '.')))
71         *e = 0;
72
73     s->hostname = g_strdup_printf("%s.local.", hn);
74     g_free(hn);
75
76     /* Fill in HINFO rr */
77     s->hinfo_rr_id = flx_server_get_next_id(s);
78
79     uname(&utsname);
80     hinfo = g_strdup_printf("%s%c%s%n", g_strup(utsname.machine), 0, g_strup(utsname.sysname), &length);
81     
82     flx_server_add(s, s->hinfo_rr_id, 0, AF_UNSPEC,
83                    s->hostname, FLX_DNS_TYPE_HINFO, hinfo, length+1);
84
85
86     /* Add localhost entries */
87     flx_address_parse("127.0.0.1", AF_INET, &a);
88     flx_server_add_address(s, 0, 0, AF_UNSPEC, "localhost", &a);
89
90     flx_address_parse("::1", AF_INET6, &a);
91     flx_server_add_address(s, 0, 0, AF_UNSPEC, "ip6-localhost", &a);
92
93     return s;
94 }
95
96 void flx_server_free(flxServer* s) {
97     g_assert(s);
98
99     while (s->query_job_queue->last)
100         flx_server_remove_query_job_instance(s, s->query_job_queue->last->data);
101     
102     flx_prio_queue_free(s->query_job_queue);
103     flx_prio_queue_free(s->response_job_queue);
104
105     flx_interface_monitor_free(s->monitor);
106     
107     flx_server_remove(s, 0);
108     
109     g_hash_table_destroy(s->rrset_by_id);
110     g_hash_table_destroy(s->rrset_by_name);
111     g_main_context_unref(s->context);
112
113     g_free(s->hostname);
114     g_free(s);
115 }
116
117 gint flx_server_get_next_id(flxServer *s) {
118     g_assert(s);
119
120     return s->current_id++;
121 }
122
123 void flx_server_add_rr(flxServer *s, gint id, gint interface, guchar protocol, const flxRecord *rr) {
124     flxEntry *e;
125     g_assert(s);
126     g_assert(rr);
127     g_assert(rr->name);
128     g_assert(rr->data);
129     g_assert(rr->size);
130
131     e = g_new(flxEntry, 1);
132     flx_record_copy_normalize(&e->rr, rr);
133     e->id = id;
134     e->interface = interface;
135     e->protocol = protocol;
136
137     /* Insert into linked list */
138     e->prev = NULL;
139     if ((e->next = s->entries))
140         e->next->prev = e;
141     s->entries = e;
142
143     /* Insert into hash table indexed by id */
144     e->prev_by_id = NULL;
145     if ((e->next_by_id = g_hash_table_lookup(s->rrset_by_id, &id)))
146         e->next_by_id->prev = e;
147     g_hash_table_replace(s->rrset_by_id, &e->id, e);
148
149     /* Insert into hash table indexed by name */
150     e->prev_by_name = NULL;
151     if ((e->next_by_name = g_hash_table_lookup(s->rrset_by_name, e->rr.name)))
152         e->next_by_name->prev = e;
153     g_hash_table_replace(s->rrset_by_name, e->rr.name, e);
154 }
155
156 void flx_server_add(flxServer *s, gint id, gint interface, guchar protocol, const gchar *name, guint16 type, gconstpointer data, guint size) {
157     flxRecord rr;
158     g_assert(s);
159     g_assert(name);
160     g_assert(data);
161     g_assert(size);
162
163     rr.name = (gchar*) name;
164     rr.type = type;
165     rr.class = FLX_DNS_CLASS_IN;
166     rr.data = (gpointer) data;
167     rr.size = size;
168     rr.ttl = FLX_DEFAULT_TTL;
169     flx_server_add_rr(s, id, interface, protocol, &rr);
170 }
171
172 const flxRecord *flx_server_iterate(flxServer *s, gint id, void **state) {
173     flxEntry **e = (flxEntry**) state;
174     g_assert(s);
175     g_assert(e);
176
177     if (e)
178         *e = id > 0 ? (*e)->next_by_id : (*e)->next;
179     else
180         *e = id > 0 ? g_hash_table_lookup(s->rrset_by_id, &id) : s->entries;
181         
182     if (!*e)
183         return NULL;
184
185     return &(*e)->rr;
186 }
187
188 static void free_entry(flxServer*s, flxEntry *e) {
189     g_assert(e);
190
191     /* Remove from linked list */
192     if (e->prev)
193         e->prev->next = e->next;
194     else
195         s->entries = e->next;
196     
197     if (e->next)
198         e->next->prev = e->prev;
199
200     /* Remove from hash table indexed by id */
201     if (e->prev_by_id)
202         e->prev_by_id = e->next_by_id;
203     else {
204         if (e->next_by_id)
205             g_hash_table_replace(s->rrset_by_id, &e->next_by_id->id, e->next_by_id);
206         else
207             g_hash_table_remove(s->rrset_by_id, &e->id);
208     }
209
210     if (e->next_by_id)
211         e->next_by_id->prev_by_id = e->prev_by_id;
212
213     /* Remove from hash table indexed by name */
214     if (e->prev_by_name)
215         e->prev_by_name = e->next_by_name;
216     else {
217         if (e->next_by_name)
218             g_hash_table_replace(s->rrset_by_name, &e->next_by_name->rr.name, e->next_by_name);
219         else
220             g_hash_table_remove(s->rrset_by_name, &e->rr.name);
221     }
222     
223     if (e->next_by_name)
224         e->next_by_name->prev_by_name = e->prev_by_name;
225 }
226
227 void flx_server_remove(flxServer *s, gint id) {
228     g_assert(s);
229
230     if (id <= 0) {
231         while (s->entries)
232             free_entry(s, s->entries);
233     } else {
234         flxEntry *e;
235
236         while ((e = g_hash_table_lookup(s->rrset_by_id, &id)))
237             free_entry(s, e);
238     }
239 }
240
241 flxRecord *flx_record_copy_normalize(flxRecord *ret_dest, const flxRecord*src) {
242     g_assert(ret_dest);
243     g_assert(src);
244
245     *ret_dest = *src;
246     ret_dest->name = flx_normalize_name(src->name);
247     ret_dest->data = g_memdup(src->data, src->size);
248
249     return ret_dest;    
250 }
251
252 static const gchar *dns_class_to_string(guint16 class) {
253     if (class == FLX_DNS_CLASS_IN)
254         return "IN";
255
256     return NULL;
257 }
258
259 static const gchar *dns_type_to_string(guint16 type) {
260     switch (type) {
261         case FLX_DNS_TYPE_A:
262             return "A";
263         case FLX_DNS_TYPE_AAAA:
264             return "AAAA";
265         case FLX_DNS_TYPE_PTR:
266             return "PTR";
267         case FLX_DNS_TYPE_HINFO:
268             return "HINFO";
269         case FLX_DNS_TYPE_TXT:
270             return "TXT";
271         default:
272             return NULL;
273     }
274 }
275
276 void flx_server_dump(flxServer *s, FILE *f) {
277     flxEntry *e;
278     g_assert(s);
279     g_assert(f);
280
281     for (e = s->entries; e; e = e->next) {
282         char t[256];
283         fprintf(f, "%i.%u: %-40s %-8s %-8s ", e->interface, e->protocol, e->rr.name, dns_class_to_string(e->rr.class), dns_type_to_string(e->rr.type));
284
285         t[0] = 0;
286         
287         if (e->rr.class == FLX_DNS_CLASS_IN) {
288             if (e->rr.type == FLX_DNS_TYPE_A)
289                 inet_ntop(AF_INET, e->rr.data, t, sizeof(t));
290             else if (e->rr.type == FLX_DNS_TYPE_AAAA)
291                 inet_ntop(AF_INET6, e->rr.data, t, sizeof(t));
292             else if (e->rr.type == FLX_DNS_TYPE_PTR)
293                 g_strlcpy(t, e->rr.data, sizeof(t));
294             else if (e->rr.type == FLX_DNS_TYPE_HINFO) {
295                 char *s2;
296
297                 if ((s2 = memchr(e->rr.data, 0, e->rr.size))) {
298                     s2++;
299                     if (memchr(s2, 0, e->rr.size - ((char*) s2 - (char*) e->rr.data)))
300                         snprintf(t, sizeof(t), "'%s' '%s'", (char*) e->rr.data, s2);
301                 }
302                 
303             } else if (e->rr.type == FLX_DNS_TYPE_TXT) {
304                 size_t l;
305
306                 l = e->rr.size;
307                 if (l > sizeof(t)-1)
308                     l = sizeof(t)-1;
309
310                 memcpy(t, e->rr.data, l);
311                 t[l] = 0;
312             }
313         }
314             
315         fprintf(f, "%s\n", t);
316     }
317 }
318
319 void flx_server_add_address(flxServer *s, gint id, gint interface, guchar protocol, const gchar *name, flxAddress *a) {
320     gchar *n;
321     g_assert(s);
322     g_assert(a);
323
324     n = flx_normalize_name(name ? name : s->hostname);
325     
326     if (a->family == AF_INET) {
327         gchar *r;
328         
329         flx_server_add(s, id, interface, protocol, n, FLX_DNS_TYPE_A, &a->ipv4, sizeof(a->ipv4));
330
331         r = flx_reverse_lookup_name_ipv4(&a->ipv4);
332         g_assert(r);
333         flx_server_add(s, id, interface, protocol, r, FLX_DNS_TYPE_PTR, n, strlen(n)+1);
334         g_free(r);
335         
336     } else {
337         gchar *r;
338             
339         flx_server_add(s, id, interface, protocol, n, FLX_DNS_TYPE_AAAA, &a->ipv6, sizeof(a->ipv6));
340
341         r = flx_reverse_lookup_name_ipv6_arpa(&a->ipv6);
342         g_assert(r);
343         flx_server_add(s, id, interface, protocol, r, FLX_DNS_TYPE_PTR, n, strlen(n)+1);
344         g_free(r);
345     
346         r = flx_reverse_lookup_name_ipv6_int(&a->ipv6);
347         g_assert(r);
348         flx_server_add(s, id, interface, protocol, r, FLX_DNS_TYPE_PTR, n, strlen(n)+1);
349         g_free(r);
350     }
351     
352     g_free(n);
353 }
354
355 void flx_server_add_text(flxServer *s, gint id, gint interface, guchar protocol, const gchar *name, const gchar *text) {
356     gchar *n;
357     g_assert(s);
358     g_assert(text);
359
360     n = flx_normalize_name(name ? name : s->hostname);
361     flx_server_add(s, id, interface, protocol, n, FLX_DNS_TYPE_TXT, text, strlen(text));
362     g_free(n);
363 }
364
365
366 flxQueryJob* flx_query_job_new(void) {
367     flxQueryJob *job = g_new(flxQueryJob, 1);
368     job->query.name = NULL;
369     job->query.class = 0;
370     job->query.type = 0;
371     job->ref = 1;
372     job->time.tv_sec = 0;
373     job->time.tv_usec = 0;
374     return job;
375 }
376
377 flxQueryJob* flx_query_job_ref(flxQueryJob *job) {
378     g_assert(job);
379     g_assert(job->ref >= 1);
380     job->ref++;
381     return job;
382 }
383
384 void flx_query_job_unref(flxQueryJob *job) {
385     g_assert(job);
386     g_assert(job->ref >= 1);
387     if (!(--job->ref))
388         g_free(job);
389 }
390
391 static gboolean query_job_exists(flxServer *s, gint interface, guchar protocol, flxQuery *q) {
392     flxPrioQueueNode *n;
393     g_assert(s);
394     g_assert(q);
395
396     for (n = s->query_job_queue->root; n; n = n->next)
397         if (flx_query_equal(&((flxQueryJobInstance*) n->data)->job->query, q))
398             return TRUE;
399
400     return FALSE;
401 }
402
403 static void post_query_job(flxServer *s, gint interface, guchar protocol, flxQueryJob *job) {
404     g_assert(s);
405     g_assert(job);
406
407     if (interface <= 0) {
408         const flxInterface *i;
409         
410         for (i = flx_interface_monitor_get_first(s->monitor); i; i = i->next)
411             post_query_job(s, i->index, protocol, job);
412     } else if (protocol == AF_UNSPEC) {
413         post_query_job(s, interface, AF_INET, job);
414         post_query_job(s, interface, AF_INET6, job);
415     } else {
416         flxQueryJobInstance *i;
417
418         if (query_job_exists(s, interface, protocol, &job->query))
419             return;
420         
421         i = g_new(flxQueryJobInstance, 1);
422         i->job = flx_query_job_ref(job);
423         i->interface = interface;
424         i->protocol = protocol;
425         i->node = flx_prio_queue_put(s->query_job_queue, i);
426     }
427 }
428
429 void flx_server_post_query_job(flxServer *s, gint interface, guchar protocol, const GTimeVal *tv, const flxQuery *q) {
430     flxQueryJob *job;
431     g_assert(s);
432     g_assert(q);
433
434     job = flx_query_job_new();
435     job->query.name = g_strdup(q->name);
436     job->query.class = q->class;
437     job->query.type = q->type;
438     if (tv)
439         job->time = *tv;
440     post_query_job(s, interface, protocol, job);
441 }
442
443 void flx_server_drop_query_job(flxServer *s, gint interface, guchar protocol, const flxQuery *q) {
444     flxPrioQueueNode *n, *next;
445     g_assert(s);
446     g_assert(interface > 0);
447     g_assert(protocol != AF_UNSPEC);
448     g_assert(q);
449
450     for (n = s->query_job_queue->root; n; n = next) {
451         next = n->next;
452     
453         if (flx_query_equal(&((flxQueryJobInstance*) n->data)->job->query, q))
454             flx_server_remove_query_job_instance(s, n->data);
455     }
456 }
457
458 void flx_server_remove_query_job_instance(flxServer *s, flxQueryJobInstance *i) {
459     g_assert(s);
460     g_assert(i);
461     g_assert(i->node);
462
463     flx_prio_queue_remove(s->query_job_queue, i->node);
464     flx_query_job_unref(i->job);
465     g_free(i);
466 }
467
468 gboolean flx_query_equal(const flxQuery *a, const flxQuery *b) {
469     return strcmp(a->name, b->name) == 0 && a->type == b->type && a->class == b->class;
470 }
471