Fully test trp_rtable code. All tests pass.
[trust_router.git] / trp / trp_rtable.c
1 #include <glib.h>
2 #include <talloc.h>
3
4 #include <trust_router/tr_name.h>
5 #include <trp_internal.h>
6 #include <trp_rtable.h>
7 #include <tr_debug.h>
8
9 /* Note: be careful mixing talloc with glib. */
10
11 static int trp_rentry_destructor(void *obj)
12 {
13   TRP_RENTRY *entry=talloc_get_type_abort(obj, TRP_RENTRY);
14   if (entry->apc!=NULL)
15     tr_free_name(entry->apc);
16   if (entry->realm!=NULL)
17     tr_free_name(entry->realm);
18   if (entry->trust_router!=NULL)
19     tr_free_name(entry->trust_router);
20   if (entry->peer!=NULL)
21     tr_free_name(entry->peer);
22   if (entry->next_hop!=NULL)
23     tr_free_name(entry->next_hop);
24   return 0;
25 }
26
27 TRP_RENTRY *trp_rentry_new(TALLOC_CTX *mem_ctx)
28 {
29   TRP_RENTRY *entry=talloc(mem_ctx, TRP_RENTRY);
30   if (entry!=NULL) {
31     entry->apc=NULL;
32     entry->realm=NULL;
33     entry->trust_router=NULL;
34     entry->peer=NULL;
35     entry->next_hop=NULL;
36     entry->selected=0;
37     entry->expiry=talloc(entry, struct timespec);
38     if (entry->expiry==NULL) {
39       talloc_free(entry);
40       return NULL;
41     }
42     talloc_set_destructor((void *)entry, trp_rentry_destructor);
43   }
44   return entry;
45 }
46
47 void trp_rentry_free(TRP_RENTRY *entry)
48 {
49   if (entry!=NULL)
50     talloc_free(entry);
51 }
52
53 void trp_rentry_set_apc(TRP_RENTRY *entry, TR_NAME *apc)
54 {
55   entry->apc=apc;
56 }
57
58 TR_NAME *trp_rentry_get_apc(TRP_RENTRY *entry)
59 {
60   return entry->apc;
61 }
62
63 void trp_rentry_set_realm(TRP_RENTRY *entry, TR_NAME *realm)
64 {
65   entry->realm=realm;
66 }
67
68 TR_NAME *trp_rentry_get_realm(TRP_RENTRY *entry)
69 {
70   return entry->realm;
71 }
72
73 void trp_rentry_set_trust_router(TRP_RENTRY *entry, TR_NAME *tr)
74 {
75   entry->trust_router=tr;
76 }
77
78 TR_NAME *trp_rentry_get_trust_router(TRP_RENTRY *entry)
79 {
80   return entry->trust_router;
81 }
82
83 void trp_rentry_set_peer(TRP_RENTRY *entry, TR_NAME *peer)
84 {
85   entry->peer=peer;
86 }
87
88 TR_NAME *trp_rentry_get_peer(TRP_RENTRY *entry)
89 {
90   return entry->peer;
91 }
92
93 void trp_rentry_set_metric(TRP_RENTRY *entry, unsigned int metric)
94 {
95   entry->metric=metric;
96 }
97
98 unsigned int trp_rentry_get_metric(TRP_RENTRY *entry)
99 {
100   return entry->metric;
101 }
102
103 void trp_rentry_set_next_hop(TRP_RENTRY *entry, TR_NAME *next_hop)
104 {
105   entry->next_hop=next_hop;
106 }
107
108 TR_NAME *trp_rentry_get_next_hop(TRP_RENTRY *entry)
109 {
110   return entry->next_hop;
111 }
112
113 void trp_rentry_set_selected(TRP_RENTRY *entry, int sel)
114 {
115   entry->selected=sel;
116 }
117
118 int trp_rentry_get_selected(TRP_RENTRY *entry)
119 {
120   return entry->selected;
121 }
122
123 /* copies incoming value, does not assume responsibility for freeing */
124 void trp_rentry_set_expiry(TRP_RENTRY *entry, struct timespec *exp)
125 {
126   entry->expiry->tv_sec=exp->tv_sec;
127   entry->expiry->tv_nsec=exp->tv_nsec;
128 }
129
130 struct timespec *trp_rentry_get_expiry(TRP_RENTRY *entry)
131 {
132   return entry->expiry;
133 }
134
135
136 /* result must be freed with g_free */
137 static gchar *tr_name_to_g_str(const TR_NAME *n)
138 {
139   gchar *s=g_strndup(n->buf, n->len);
140   return s;
141 }
142
143 /* hash function for TR_NAME keys */
144 static guint trp_tr_name_hash(gconstpointer key)
145 {
146   const TR_NAME *name=(TR_NAME *)key;
147   gchar *s=tr_name_to_g_str(name);
148   guint hash=g_str_hash(s);
149   g_free(s);
150   return hash;
151 }
152
153 /* hash equality function for TR_NAME keys */
154 static gboolean trp_tr_name_equal(gconstpointer key1, gconstpointer key2)
155 {
156   const TR_NAME *n1=(TR_NAME *)key1;
157   const TR_NAME *n2=(TR_NAME *)key2;
158   gchar *s1=tr_name_to_g_str(n1);
159   gchar *s2=tr_name_to_g_str(n2);
160   gboolean equal=g_str_equal(s1, s2);
161   g_free(s1);
162   g_free(s2);
163   return equal;
164 }
165
166 /* free a value to the top level rtable (a hash of all entries in the apc) */
167 static void trp_rtable_destroy_table(gpointer data)
168 {
169   g_hash_table_destroy(data);
170 }
171
172 static void trp_rtable_destroy_rentry(gpointer data)
173 {
174   trp_rentry_free(data);
175 }
176
177 static void trp_rtable_destroy_tr_name(gpointer data)
178 {
179   tr_free_name(data);
180 }
181
182 TRP_RTABLE *trp_rtable_new(void)
183 {
184   GHashTable *new=g_hash_table_new_full(trp_tr_name_hash,
185                                         trp_tr_name_equal,
186                                         trp_rtable_destroy_tr_name,
187                                         trp_rtable_destroy_table);
188   return new;
189 }
190
191 void trp_rtable_free(TRP_RTABLE *rtbl)
192 {
193   g_hash_table_destroy(rtbl);
194 }
195
196 static GHashTable *trp_rtbl_get_or_add_table(GHashTable *tbl, TR_NAME *key, GDestroyNotify destroy)
197 {
198   GHashTable *val_tbl=NULL;
199
200   val_tbl=g_hash_table_lookup(tbl, key);
201   if (val_tbl==NULL) {
202     val_tbl=g_hash_table_new_full(trp_tr_name_hash,
203                                   trp_tr_name_equal,
204                                   trp_rtable_destroy_tr_name,
205                                   destroy);
206     g_hash_table_insert(tbl, tr_dup_name(key), val_tbl);
207   }
208   return val_tbl;
209 }
210
211 void trp_rtable_add(TRP_RTABLE *rtbl, TRP_RENTRY *entry)
212 {
213   GHashTable *apc_tbl=NULL;
214   GHashTable *realm_tbl=NULL;
215
216   apc_tbl=trp_rtbl_get_or_add_table(rtbl, entry->apc, trp_rtable_destroy_table);
217   realm_tbl=trp_rtbl_get_or_add_table(apc_tbl, entry->realm, trp_rtable_destroy_rentry);
218   g_hash_table_insert(realm_tbl, tr_dup_name(entry->peer), entry); /* destroys and replaces a duplicate */
219 }
220
221 /* note: the entry pointer passed in is invalid after calling this because the entry is freed */
222 void trp_rtable_remove(TRP_RTABLE *rtbl, TRP_RENTRY *entry)
223 {
224   GHashTable *apc_tbl=NULL;
225   GHashTable *realm_tbl=NULL;
226
227   apc_tbl=g_hash_table_lookup(rtbl, entry->apc);
228   if (apc_tbl==NULL)
229     return;
230
231   realm_tbl=g_hash_table_lookup(apc_tbl, entry->realm);
232   if (realm_tbl==NULL)
233     return;
234
235   /* remove the element */
236   g_hash_table_remove(realm_tbl, entry->peer);
237   /* if that was the last entry in the realm, remove the realm table */
238   if (g_hash_table_size(realm_tbl)==0)
239     g_hash_table_remove(apc_tbl, entry->realm);
240   /* if that was the last realm in the apc, remove the apc table */
241   if (g_hash_table_size(apc_tbl)==0)
242     g_hash_table_remove(rtbl, entry->apc);
243 }
244
245 /* gets the actual hash table, for internal use only */
246 static GHashTable *trp_rtable_get_apc_table(TRP_RTABLE *rtbl, TR_NAME *apc)
247 {
248   return g_hash_table_lookup(rtbl, apc);
249 }
250
251 /* gets the actual hash table, for internal use only */
252 static GHashTable *trp_rtable_get_realm_table(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm)
253 {
254   GHashTable *apc_tbl=trp_rtable_get_apc_table(rtbl, apc);
255   if (apc_tbl==NULL)
256     return NULL;
257   else
258     return g_hash_table_lookup(apc_tbl, realm);
259 }
260
261 struct table_size_cookie {
262   TRP_RTABLE *rtbl;
263   size_t size;
264 };
265 static void trp_rtable_size_helper(gpointer key, gpointer value, gpointer user_data)
266 {
267   struct table_size_cookie *data=(struct table_size_cookie *)user_data;
268   data->size += trp_rtable_apc_size(data->rtbl, (TR_NAME *)key);
269 };
270 size_t trp_rtable_size(TRP_RTABLE *rtbl)
271 {
272   struct table_size_cookie data={rtbl, 0};
273   g_hash_table_foreach(rtbl, trp_rtable_size_helper, &data);
274   return data.size;
275 }
276
277 struct table_apc_size_cookie {
278   TR_NAME *apc;
279   TRP_RTABLE *rtbl;
280   size_t size;
281 };
282 static void table_apc_size_helper(gpointer key, gpointer value, gpointer user_data)
283 {
284   struct table_apc_size_cookie *data=(struct table_apc_size_cookie *)user_data;
285   data->size += trp_rtable_realm_size(data->rtbl, data->apc, (TR_NAME *)key);
286 }
287 size_t trp_rtable_apc_size(TRP_RTABLE *rtbl, TR_NAME *apc)
288 {
289   struct table_apc_size_cookie data={apc, rtbl, 0};
290   GHashTable *apc_tbl=trp_rtable_get_apc_table(rtbl, apc);
291   if (apc_tbl==NULL)
292     return 0;;
293   g_hash_table_foreach(apc_tbl, table_apc_size_helper, &data);
294   return data.size;
295 }
296
297 size_t trp_rtable_realm_size(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm)
298 {
299   GHashTable *realm_tbl=trp_rtable_get_realm_table(rtbl, apc, realm);
300   if (realm_tbl==NULL)
301     return 0;
302   else
303     return g_hash_table_size(g_hash_table_lookup(
304                                g_hash_table_lookup(rtbl, apc),
305                                realm));
306 }
307
308 /* Returns an array of pointers to TRP_RENTRY, length of array in n_out.
309  * Caller must free the array (in the talloc NULL context), but must
310  * not free its contents. */
311 TRP_RENTRY **trp_rtable_get_entries(TRP_RTABLE *rtbl, size_t *n_out)
312 {
313   TRP_RENTRY **ret=NULL;
314   TR_NAME **apc=NULL;
315   size_t n_apc=0;
316   TRP_RENTRY **apc_entries=NULL;
317   size_t n_entries=0;
318   size_t ii_ret=0;
319
320   *n_out=trp_rtable_size(rtbl);
321   if (*n_out==0)
322     return NULL;
323
324   ret=talloc_array(NULL, TRP_RENTRY *, *n_out);
325   if (ret==NULL) {
326     tr_crit("trp_rtable_get_entries: unable to allocate return array.");
327     *n_out=0;
328     return NULL;
329   }
330
331   ii_ret=0; /* counts output entries */
332   apc=trp_rtable_get_apcs(rtbl, &n_apc);
333   while(n_apc--) {
334     apc_entries=trp_rtable_get_apc_entries(rtbl, apc[n_apc], &n_entries);
335     while (n_entries--)
336       ret[ii_ret++]=apc_entries[n_entries];
337     talloc_free(apc_entries);
338   }
339   talloc_free(apc);
340
341   if (ii_ret!=*n_out) {
342     tr_crit("trp_rtable_get_entries: found incorrect number of entries.");
343     talloc_free(ret);
344     *n_out=0;
345     return NULL;
346   }
347   return ret;
348 }
349
350 /* Returns an array of pointers to TR_NAME, length of array in n_out.
351  * Caller must free the array (in the talloc NULL context). */
352 TR_NAME **trp_rtable_get_apcs(TRP_RTABLE *rtbl, size_t *n_out)
353 {
354   size_t len=g_hash_table_size(rtbl); /* known apcs are keys in top level hash table */
355   size_t ii=0;
356   GList *apcs=NULL;;
357   GList *p=NULL;
358   TR_NAME **ret=NULL;
359
360   if (len==0) {
361     *n_out=0;
362     return NULL;
363   }
364     
365   ret=talloc_array(NULL, TR_NAME *, len);
366   if (ret==NULL) {
367     tr_crit("trp_rtable_get_apcs: unable to allocate return array.");
368     *n_out=0;
369     return NULL;
370   }
371   apcs=g_hash_table_get_keys(rtbl);
372   for (ii=0,p=apcs; p!=NULL; ii++,p=g_list_next(p))
373     ret[ii]=(TR_NAME *)p->data;
374
375   g_list_free(apcs);
376
377   *n_out=len;
378   return ret;
379 }
380
381 /* Returns an array of pointers to TR_NAME, length of array in n_out.
382  * Caller must free the array (in the talloc NULL context). */
383 TR_NAME **trp_rtable_get_apc_realms(TRP_RTABLE *rtbl, TR_NAME *apc, size_t *n_out)
384 {
385   size_t ii=0;
386   TRP_RTABLE *apc_tbl=g_hash_table_lookup(rtbl, apc);;
387   GList *entries=NULL;
388   GList *p=NULL;
389   TR_NAME **ret=NULL;
390
391   if (apc_tbl==NULL) {
392     *n_out=0;
393     return NULL;
394   }
395   *n_out=g_hash_table_size(apc_tbl); /* set output length */
396   ret=talloc_array(NULL, TR_NAME *, *n_out);
397   entries=g_hash_table_get_keys(apc_tbl);
398   for (ii=0,p=entries; p!=NULL; ii++,p=g_list_next(p))
399     ret[ii]=(TR_NAME *)p->data;
400
401   g_list_free(entries);
402   return ret;
403 }
404
405 /* Get all entries in an apc. Returns an array of pointers in NULL talloc context.
406  * Caller must free this list with talloc_free, but must not free the entries in the
407  * list.. */
408 TRP_RENTRY **trp_rtable_get_apc_entries(TRP_RTABLE *rtbl, TR_NAME *apc, size_t *n_out)
409 {
410   size_t ii=0, jj=0;
411   TR_NAME **realm=NULL;
412   size_t n_realms=0;
413   TRP_RENTRY **realm_entries=NULL;
414   size_t n_entries=0;
415   TRP_RENTRY **ret=NULL;
416   size_t ii_ret=0;
417
418   *n_out=trp_rtable_apc_size(rtbl, apc);
419   if (*n_out==0)
420     return NULL;
421
422   ret=talloc_array(NULL, TRP_RENTRY *, *n_out);
423   if (ret==NULL) {
424     tr_crit("trp_rtable_get_apc_entries: could not allocate return array.");
425     *n_out=0;
426     return NULL;
427   }
428   
429   ii_ret=0; /* counts entries in the output array */
430   realm=trp_rtable_get_apc_realms(rtbl, apc, &n_realms);
431   for (ii=0; ii<n_realms; ii++) {
432     realm_entries=trp_rtable_get_realm_entries(rtbl, apc, realm[ii], &n_entries);
433     for (jj=0; jj<n_entries; jj++)
434       ret[ii_ret++]=realm_entries[jj];
435     talloc_free(realm_entries);
436   }
437   talloc_free(realm);
438
439   if (ii_ret!=*n_out) {
440     tr_crit("trp_rtable_get_apc_entries: found incorrect number of entries.");
441     talloc_free(ret);
442     *n_out=0;
443     return NULL;
444   }
445
446   return ret;
447 }
448
449 /* Get all entries in an apc/realm. Returns an array of pointers in NULL talloc context.
450  * Caller must free this list with talloc_free, but must not free the entries in the
451  * list.. */
452 TRP_RENTRY **trp_rtable_get_realm_entries(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm, size_t *n_out)
453 {
454   size_t ii=0;
455   TRP_RENTRY **ret=NULL;
456   TR_NAME **peer=NULL;
457
458   peer=trp_rtable_get_apc_realm_peers(rtbl, apc, realm, n_out);
459   ret=talloc_array(NULL, TRP_RENTRY *, *n_out);
460   if (ret==NULL) {
461     tr_crit("trp_rtable_get_realm_entries: could not allocate return array.");
462     talloc_free(peer);
463     n_out=0;
464     return NULL;
465   }
466   for (ii=0; ii<*n_out; ii++)
467     ret[ii]=trp_rtable_get_entry(rtbl, apc, realm, peer[ii]);
468   talloc_free(peer);
469   return ret;
470 }
471
472 TR_NAME **trp_rtable_get_apc_realm_peers(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm, size_t *n_out)
473 {
474   TR_NAME **ret=NULL;
475   GHashTable *realm_tbl=NULL;
476   GList *keys=NULL;
477   GList *p=NULL;
478   size_t ii=0;
479
480   *n_out=trp_rtable_realm_size(rtbl, apc, realm);
481   if (*n_out==0)
482     return NULL;
483   realm_tbl=trp_rtable_get_realm_table(rtbl, apc, realm);
484   ret=talloc_array(NULL, TR_NAME *, *n_out);
485   if (ret==NULL) {
486     tr_crit("trp_rtable_get_apc_realm_peers: could not allocate return array.");
487     *n_out=0;
488     return NULL;
489   }
490   keys=g_hash_table_get_keys(realm_tbl);
491   for (ii=0,p=keys; p!=NULL; ii++,p=g_list_next(p))
492     ret[ii]=(TR_NAME *)p->data;
493   g_list_free(keys);
494   return ret;
495 }
496
497 /* Gets a single entry. Do not free it. */
498 TRP_RENTRY *trp_rtable_get_entry(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm, TR_NAME *peer)
499 {
500   GHashTable *apc_tbl=NULL;
501   GHashTable *realm_tbl=NULL;
502   
503   apc_tbl=g_hash_table_lookup(rtbl, apc);
504   if (apc_tbl==NULL)
505     return NULL;
506   realm_tbl=g_hash_table_lookup(apc_tbl, realm);
507   if (realm_tbl==NULL)
508     return NULL;
509   return g_hash_table_lookup(realm_tbl, peer); /* does not copy or increment ref count */
510 }