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