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