Improved, sorted printing, provide _to_str methods.
[trust_router.git] / trp / trp_rtable.c
1 #include <stdlib.h>
2
3 #include <glib.h>
4 #include <talloc.h>
5 #include <time.h>
6
7 #include <trust_router/tr_name.h>
8 #include <trp_internal.h>
9 #include <trp_rtable.h>
10 #include <tr_debug.h>
11
12 /* Note: be careful mixing talloc with glib. */
13
14 static int trp_rentry_destructor(void *obj)
15 {
16   TRP_RENTRY *entry=talloc_get_type_abort(obj, TRP_RENTRY);
17   if (entry->apc!=NULL)
18     tr_free_name(entry->apc);
19   if (entry->realm!=NULL)
20     tr_free_name(entry->realm);
21   if (entry->trust_router!=NULL)
22     tr_free_name(entry->trust_router);
23   if (entry->peer!=NULL)
24     tr_free_name(entry->peer);
25   if (entry->next_hop!=NULL)
26     tr_free_name(entry->next_hop);
27   return 0;
28 }
29
30 TRP_RENTRY *trp_rentry_new(TALLOC_CTX *mem_ctx)
31 {
32   TRP_RENTRY *entry=talloc(mem_ctx, TRP_RENTRY);
33   if (entry!=NULL) {
34     entry->apc=NULL;
35     entry->realm=NULL;
36     entry->trust_router=NULL;
37     entry->peer=NULL;
38     entry->next_hop=NULL;
39     entry->selected=0;
40     entry->expiry=talloc(entry, struct timespec);
41     if (entry->expiry==NULL) {
42       talloc_free(entry);
43       return NULL;
44     }
45     talloc_set_destructor((void *)entry, trp_rentry_destructor);
46   }
47   return entry;
48 }
49
50 void trp_rentry_free(TRP_RENTRY *entry)
51 {
52   if (entry!=NULL)
53     talloc_free(entry);
54 }
55
56 void trp_rentry_set_apc(TRP_RENTRY *entry, TR_NAME *apc)
57 {
58   entry->apc=apc;
59 }
60
61 TR_NAME *trp_rentry_get_apc(TRP_RENTRY *entry)
62 {
63   return entry->apc;
64 }
65
66 void trp_rentry_set_realm(TRP_RENTRY *entry, TR_NAME *realm)
67 {
68   entry->realm=realm;
69 }
70
71 TR_NAME *trp_rentry_get_realm(TRP_RENTRY *entry)
72 {
73   return entry->realm;
74 }
75
76 void trp_rentry_set_trust_router(TRP_RENTRY *entry, TR_NAME *tr)
77 {
78   entry->trust_router=tr;
79 }
80
81 TR_NAME *trp_rentry_get_trust_router(TRP_RENTRY *entry)
82 {
83   return entry->trust_router;
84 }
85
86 void trp_rentry_set_peer(TRP_RENTRY *entry, TR_NAME *peer)
87 {
88   entry->peer=peer;
89 }
90
91 TR_NAME *trp_rentry_get_peer(TRP_RENTRY *entry)
92 {
93   return entry->peer;
94 }
95
96 void trp_rentry_set_metric(TRP_RENTRY *entry, unsigned int metric)
97 {
98   entry->metric=metric;
99 }
100
101 unsigned int trp_rentry_get_metric(TRP_RENTRY *entry)
102 {
103   return entry->metric;
104 }
105
106 void trp_rentry_set_next_hop(TRP_RENTRY *entry, TR_NAME *next_hop)
107 {
108   entry->next_hop=next_hop;
109 }
110
111 TR_NAME *trp_rentry_get_next_hop(TRP_RENTRY *entry)
112 {
113   return entry->next_hop;
114 }
115
116 void trp_rentry_set_selected(TRP_RENTRY *entry, int sel)
117 {
118   entry->selected=sel;
119 }
120
121 int trp_rentry_get_selected(TRP_RENTRY *entry)
122 {
123   return entry->selected;
124 }
125
126 /* copies incoming value, does not assume responsibility for freeing */
127 void trp_rentry_set_expiry(TRP_RENTRY *entry, struct timespec *exp)
128 {
129   entry->expiry->tv_sec=exp->tv_sec;
130   entry->expiry->tv_nsec=exp->tv_nsec;
131 }
132
133 struct timespec *trp_rentry_get_expiry(TRP_RENTRY *entry)
134 {
135   return entry->expiry;
136 }
137
138
139 /* result must be freed with g_free */
140 static gchar *tr_name_to_g_str(const TR_NAME *n)
141 {
142   gchar *s=g_strndup(n->buf, n->len);
143   return s;
144 }
145
146 /* hash function for TR_NAME keys */
147 static guint trp_tr_name_hash(gconstpointer key)
148 {
149   const TR_NAME *name=(TR_NAME *)key;
150   gchar *s=tr_name_to_g_str(name);
151   guint hash=g_str_hash(s);
152   g_free(s);
153   return hash;
154 }
155
156 /* hash equality function for TR_NAME keys */
157 static gboolean trp_tr_name_equal(gconstpointer key1, gconstpointer key2)
158 {
159   const TR_NAME *n1=(TR_NAME *)key1;
160   const TR_NAME *n2=(TR_NAME *)key2;
161   gchar *s1=tr_name_to_g_str(n1);
162   gchar *s2=tr_name_to_g_str(n2);
163   gboolean equal=g_str_equal(s1, s2);
164   g_free(s1);
165   g_free(s2);
166   return equal;
167 }
168
169 /* free a value to the top level rtable (a hash of all entries in the apc) */
170 static void trp_rtable_destroy_table(gpointer data)
171 {
172   g_hash_table_destroy(data);
173 }
174
175 static void trp_rtable_destroy_rentry(gpointer data)
176 {
177   trp_rentry_free(data);
178 }
179
180 static void trp_rtable_destroy_tr_name(gpointer data)
181 {
182   tr_free_name(data);
183 }
184
185 TRP_RTABLE *trp_rtable_new(void)
186 {
187   GHashTable *new=g_hash_table_new_full(trp_tr_name_hash,
188                                         trp_tr_name_equal,
189                                         trp_rtable_destroy_tr_name,
190                                         trp_rtable_destroy_table);
191   return new;
192 }
193
194 void trp_rtable_free(TRP_RTABLE *rtbl)
195 {
196   g_hash_table_destroy(rtbl);
197 }
198
199 static GHashTable *trp_rtbl_get_or_add_table(GHashTable *tbl, TR_NAME *key, GDestroyNotify destroy)
200 {
201   GHashTable *val_tbl=NULL;
202
203   val_tbl=g_hash_table_lookup(tbl, key);
204   if (val_tbl==NULL) {
205     val_tbl=g_hash_table_new_full(trp_tr_name_hash,
206                                   trp_tr_name_equal,
207                                   trp_rtable_destroy_tr_name,
208                                   destroy);
209     g_hash_table_insert(tbl, tr_dup_name(key), val_tbl);
210   }
211   return val_tbl;
212 }
213
214 void trp_rtable_add(TRP_RTABLE *rtbl, TRP_RENTRY *entry)
215 {
216   GHashTable *apc_tbl=NULL;
217   GHashTable *realm_tbl=NULL;
218
219   apc_tbl=trp_rtbl_get_or_add_table(rtbl, entry->apc, trp_rtable_destroy_table);
220   realm_tbl=trp_rtbl_get_or_add_table(apc_tbl, entry->realm, trp_rtable_destroy_rentry);
221   g_hash_table_insert(realm_tbl, tr_dup_name(entry->peer), entry); /* destroys and replaces a duplicate */
222 }
223
224 /* note: the entry pointer passed in is invalid after calling this because the entry is freed */
225 void trp_rtable_remove(TRP_RTABLE *rtbl, TRP_RENTRY *entry)
226 {
227   GHashTable *apc_tbl=NULL;
228   GHashTable *realm_tbl=NULL;
229
230   apc_tbl=g_hash_table_lookup(rtbl, entry->apc);
231   if (apc_tbl==NULL)
232     return;
233
234   realm_tbl=g_hash_table_lookup(apc_tbl, entry->realm);
235   if (realm_tbl==NULL)
236     return;
237
238   /* remove the element */
239   g_hash_table_remove(realm_tbl, entry->peer);
240   /* if that was the last entry in the realm, remove the realm table */
241   if (g_hash_table_size(realm_tbl)==0)
242     g_hash_table_remove(apc_tbl, entry->realm);
243   /* if that was the last realm in the apc, remove the apc table */
244   if (g_hash_table_size(apc_tbl)==0)
245     g_hash_table_remove(rtbl, entry->apc);
246 }
247
248 /* gets the actual hash table, for internal use only */
249 static GHashTable *trp_rtable_get_apc_table(TRP_RTABLE *rtbl, TR_NAME *apc)
250 {
251   return g_hash_table_lookup(rtbl, apc);
252 }
253
254 /* gets the actual hash table, for internal use only */
255 static GHashTable *trp_rtable_get_realm_table(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm)
256 {
257   GHashTable *apc_tbl=trp_rtable_get_apc_table(rtbl, apc);
258   if (apc_tbl==NULL)
259     return NULL;
260   else
261     return g_hash_table_lookup(apc_tbl, realm);
262 }
263
264 struct table_size_cookie {
265   TRP_RTABLE *rtbl;
266   size_t size;
267 };
268 static void trp_rtable_size_helper(gpointer key, gpointer value, gpointer user_data)
269 {
270   struct table_size_cookie *data=(struct table_size_cookie *)user_data;
271   data->size += trp_rtable_apc_size(data->rtbl, (TR_NAME *)key);
272 };
273 size_t trp_rtable_size(TRP_RTABLE *rtbl)
274 {
275   struct table_size_cookie data={rtbl, 0};
276   g_hash_table_foreach(rtbl, trp_rtable_size_helper, &data);
277   return data.size;
278 }
279
280 struct table_apc_size_cookie {
281   TR_NAME *apc;
282   TRP_RTABLE *rtbl;
283   size_t size;
284 };
285 static void table_apc_size_helper(gpointer key, gpointer value, gpointer user_data)
286 {
287   struct table_apc_size_cookie *data=(struct table_apc_size_cookie *)user_data;
288   data->size += trp_rtable_realm_size(data->rtbl, data->apc, (TR_NAME *)key);
289 }
290 size_t trp_rtable_apc_size(TRP_RTABLE *rtbl, TR_NAME *apc)
291 {
292   struct table_apc_size_cookie data={apc, rtbl, 0};
293   GHashTable *apc_tbl=trp_rtable_get_apc_table(rtbl, apc);
294   if (apc_tbl==NULL)
295     return 0;;
296   g_hash_table_foreach(apc_tbl, table_apc_size_helper, &data);
297   return data.size;
298 }
299
300 size_t trp_rtable_realm_size(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm)
301 {
302   GHashTable *realm_tbl=trp_rtable_get_realm_table(rtbl, apc, realm);
303   if (realm_tbl==NULL)
304     return 0;
305   else
306     return g_hash_table_size(g_hash_table_lookup(
307                                g_hash_table_lookup(rtbl, apc),
308                                realm));
309 }
310
311 /* Returns an array of pointers to TRP_RENTRY, length of array in n_out.
312  * Caller must free the array (in the talloc NULL context), but must
313  * not free its contents. */
314 TRP_RENTRY **trp_rtable_get_entries(TRP_RTABLE *rtbl, size_t *n_out)
315 {
316   TRP_RENTRY **ret=NULL;
317   TR_NAME **apc=NULL;
318   size_t n_apc=0;
319   TRP_RENTRY **apc_entries=NULL;
320   size_t n_entries=0;
321   size_t ii_ret=0;
322
323   *n_out=trp_rtable_size(rtbl);
324   if (*n_out==0)
325     return NULL;
326
327   ret=talloc_array(NULL, TRP_RENTRY *, *n_out);
328   if (ret==NULL) {
329     tr_crit("trp_rtable_get_entries: unable to allocate return array.");
330     *n_out=0;
331     return NULL;
332   }
333
334   ii_ret=0; /* counts output entries */
335   apc=trp_rtable_get_apcs(rtbl, &n_apc);
336   while(n_apc--) {
337     apc_entries=trp_rtable_get_apc_entries(rtbl, apc[n_apc], &n_entries);
338     while (n_entries--)
339       ret[ii_ret++]=apc_entries[n_entries];
340     talloc_free(apc_entries);
341   }
342   talloc_free(apc);
343
344   if (ii_ret!=*n_out) {
345     tr_crit("trp_rtable_get_entries: found incorrect number of entries.");
346     talloc_free(ret);
347     *n_out=0;
348     return NULL;
349   }
350   return ret;
351 }
352
353 /* Returns an array of pointers to TR_NAME, length of array in n_out.
354  * Caller must free the array (in the talloc NULL context). */
355 TR_NAME **trp_rtable_get_apcs(TRP_RTABLE *rtbl, size_t *n_out)
356 {
357   size_t len=g_hash_table_size(rtbl); /* known apcs are keys in top level hash table */
358   size_t ii=0;
359   GList *apcs=NULL;;
360   GList *p=NULL;
361   TR_NAME **ret=NULL;
362
363   if (len==0) {
364     *n_out=0;
365     return NULL;
366   }
367     
368   ret=talloc_array(NULL, TR_NAME *, len);
369   if (ret==NULL) {
370     tr_crit("trp_rtable_get_apcs: unable to allocate return array.");
371     *n_out=0;
372     return NULL;
373   }
374   apcs=g_hash_table_get_keys(rtbl);
375   for (ii=0,p=apcs; p!=NULL; ii++,p=g_list_next(p))
376     ret[ii]=(TR_NAME *)p->data;
377
378   g_list_free(apcs);
379
380   *n_out=len;
381   return ret;
382 }
383
384 /* Returns an array of pointers to TR_NAME, length of array in n_out.
385  * Caller must free the array (in the talloc NULL context). */
386 TR_NAME **trp_rtable_get_apc_realms(TRP_RTABLE *rtbl, TR_NAME *apc, size_t *n_out)
387 {
388   size_t ii=0;
389   TRP_RTABLE *apc_tbl=g_hash_table_lookup(rtbl, apc);;
390   GList *entries=NULL;
391   GList *p=NULL;
392   TR_NAME **ret=NULL;
393
394   if (apc_tbl==NULL) {
395     *n_out=0;
396     return NULL;
397   }
398   *n_out=g_hash_table_size(apc_tbl); /* set output length */
399   ret=talloc_array(NULL, TR_NAME *, *n_out);
400   entries=g_hash_table_get_keys(apc_tbl);
401   for (ii=0,p=entries; p!=NULL; ii++,p=g_list_next(p))
402     ret[ii]=(TR_NAME *)p->data;
403
404   g_list_free(entries);
405   return ret;
406 }
407
408 /* Get all entries in an apc. Returns an array of pointers in NULL talloc context.
409  * Caller must free this list with talloc_free, but must not free the entries in the
410  * list.. */
411 TRP_RENTRY **trp_rtable_get_apc_entries(TRP_RTABLE *rtbl, TR_NAME *apc, size_t *n_out)
412 {
413   size_t ii=0, jj=0;
414   TR_NAME **realm=NULL;
415   size_t n_realms=0;
416   TRP_RENTRY **realm_entries=NULL;
417   size_t n_entries=0;
418   TRP_RENTRY **ret=NULL;
419   size_t ii_ret=0;
420
421   *n_out=trp_rtable_apc_size(rtbl, apc);
422   if (*n_out==0)
423     return NULL;
424
425   ret=talloc_array(NULL, TRP_RENTRY *, *n_out);
426   if (ret==NULL) {
427     tr_crit("trp_rtable_get_apc_entries: could not allocate return array.");
428     *n_out=0;
429     return NULL;
430   }
431   
432   ii_ret=0; /* counts entries in the output array */
433   realm=trp_rtable_get_apc_realms(rtbl, apc, &n_realms);
434   for (ii=0; ii<n_realms; ii++) {
435     realm_entries=trp_rtable_get_realm_entries(rtbl, apc, realm[ii], &n_entries);
436     for (jj=0; jj<n_entries; jj++)
437       ret[ii_ret++]=realm_entries[jj];
438     talloc_free(realm_entries);
439   }
440   talloc_free(realm);
441
442   if (ii_ret!=*n_out) {
443     tr_crit("trp_rtable_get_apc_entries: found incorrect number of entries.");
444     talloc_free(ret);
445     *n_out=0;
446     return NULL;
447   }
448
449   return ret;
450 }
451
452 /* Get all entries in an apc/realm. Returns an array of pointers in NULL talloc context.
453  * Caller must free this list with talloc_free, but must not free the entries in the
454  * list.. */
455 TRP_RENTRY **trp_rtable_get_realm_entries(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm, size_t *n_out)
456 {
457   size_t ii=0;
458   TRP_RENTRY **ret=NULL;
459   TR_NAME **peer=NULL;
460
461   peer=trp_rtable_get_apc_realm_peers(rtbl, apc, realm, n_out);
462   ret=talloc_array(NULL, TRP_RENTRY *, *n_out);
463   if (ret==NULL) {
464     tr_crit("trp_rtable_get_realm_entries: could not allocate return array.");
465     talloc_free(peer);
466     n_out=0;
467     return NULL;
468   }
469   for (ii=0; ii<*n_out; ii++)
470     ret[ii]=trp_rtable_get_entry(rtbl, apc, realm, peer[ii]);
471   talloc_free(peer);
472   return ret;
473 }
474
475 TR_NAME **trp_rtable_get_apc_realm_peers(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm, size_t *n_out)
476 {
477   TR_NAME **ret=NULL;
478   GHashTable *realm_tbl=NULL;
479   GList *keys=NULL;
480   GList *p=NULL;
481   size_t ii=0;
482
483   *n_out=trp_rtable_realm_size(rtbl, apc, realm);
484   if (*n_out==0)
485     return NULL;
486   realm_tbl=trp_rtable_get_realm_table(rtbl, apc, realm);
487   ret=talloc_array(NULL, TR_NAME *, *n_out);
488   if (ret==NULL) {
489     tr_crit("trp_rtable_get_apc_realm_peers: could not allocate return array.");
490     *n_out=0;
491     return NULL;
492   }
493   keys=g_hash_table_get_keys(realm_tbl);
494   for (ii=0,p=keys; p!=NULL; ii++,p=g_list_next(p))
495     ret[ii]=(TR_NAME *)p->data;
496   g_list_free(keys);
497   return ret;
498 }
499
500 /* Gets a single entry. Do not free it. */
501 TRP_RENTRY *trp_rtable_get_entry(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm, TR_NAME *peer)
502 {
503   GHashTable *apc_tbl=NULL;
504   GHashTable *realm_tbl=NULL;
505   
506   apc_tbl=g_hash_table_lookup(rtbl, apc);
507   if (apc_tbl==NULL)
508     return NULL;
509   realm_tbl=g_hash_table_lookup(apc_tbl, realm);
510   if (realm_tbl==NULL)
511     return NULL;
512   return g_hash_table_lookup(realm_tbl, peer); /* does not copy or increment ref count */
513 }
514
515 static char *timespec_to_str(struct timespec *ts)
516 {
517   struct tm tm;
518   char *s=NULL;
519
520   if (localtime_r(&(ts->tv_sec), &tm)==NULL)
521     return NULL;
522
523   s=malloc(40); /* long enough to contain strftime result */
524   if (s==NULL)
525     return NULL;
526
527   if (strftime(s, 40, "%F %T", &tm)==0) {
528     free(s);
529     return NULL;
530   }
531   return s;
532 }
533
534 /* Pretty print a route table entry to a newly allocated string. If sep is NULL,
535  * returns comma+space separated string. */
536 char *trp_rentry_to_str(TALLOC_CTX *mem_ctx, TRP_RENTRY *entry, const char *sep)
537 {
538   char *apc=tr_name_strdup(entry->apc);
539   char *realm=tr_name_strdup(entry->realm);
540   char *peer=tr_name_strdup(entry->peer);
541   char *trust_router=tr_name_strdup(entry->trust_router);
542   char *next_hop=tr_name_strdup(entry->next_hop);
543   char *expiry=timespec_to_str(entry->expiry);
544   char *result=NULL;
545
546   if (sep==NULL)
547     sep=", ";
548
549   result=talloc_asprintf(mem_ctx,
550                          "%s%s%s%s%s%s%u%s%s%s%s%s%d%s%s",
551                          apc, sep,
552                          realm, sep,
553                          peer, sep,
554                          entry->metric, sep,
555                          trust_router, sep,
556                          next_hop, sep,
557                          entry->selected, sep,
558                          expiry);
559   free(apc);
560   free(realm);
561   free(peer);
562   free(trust_router);
563   free(next_hop);
564   free(expiry);
565   return result;
566 }
567
568 static int sort_tr_names_cmp(const void *a, const void *b)
569 {
570   TR_NAME **n1=(TR_NAME **)a;
571   TR_NAME **n2=(TR_NAME **)b;
572   return tr_name_cmp(*n1, *n2);
573 }
574
575 static void sort_tr_names(TR_NAME **names, size_t n_names)
576 {
577   qsort(names, n_names, sizeof(TR_NAME *), sort_tr_names_cmp);
578 }
579
580 char *trp_rtable_to_str(TALLOC_CTX *mem_ctx, TRP_RTABLE *rtbl, const char *sep, const char *lineterm)
581 {
582   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
583   TR_NAME **apcs=NULL;
584   size_t n_apcs=0;
585   TR_NAME **realms=NULL;
586   size_t n_realms=0;
587   TRP_RENTRY **entries=NULL;
588   size_t n_entries=0;
589   char **tbl_strings=NULL;
590   size_t ii_tbl=0; /* counts tbl_strings */
591   size_t tbl_size=0;
592   size_t len=0;
593   size_t ii=0, jj=0, kk=0;
594   char *p=NULL;
595   char *result=NULL;
596
597   if (lineterm==NULL)
598     lineterm="\n";
599
600   tbl_size=trp_rtable_size(rtbl);
601   if (tbl_size==0) {
602     result=talloc_strdup(mem_ctx, lineterm);
603     goto cleanup;
604   }
605
606   tbl_strings=talloc_array(tmp_ctx, char *, tbl_size);
607   if (tbl_strings==NULL) {
608     result=talloc_strdup(mem_ctx, "error");
609     goto cleanup;
610   }
611   
612   apcs=trp_rtable_get_apcs(rtbl, &n_apcs);
613   talloc_steal(tmp_ctx, apcs);
614   sort_tr_names(apcs, n_apcs);
615   ii_tbl=0;
616   len=0;
617   for (ii=0; ii<n_apcs; ii++) {
618     realms=trp_rtable_get_apc_realms(rtbl, apcs[ii], &n_realms);
619     talloc_steal(tmp_ctx, realms);
620     sort_tr_names(realms, n_realms);
621     for (jj=0; jj<n_realms; jj++) {
622       entries=trp_rtable_get_realm_entries(rtbl, apcs[ii], realms[jj], &n_entries);
623       talloc_steal(tmp_ctx, entries);
624       for (kk=0; kk<n_entries; kk++) {
625         tbl_strings[ii_tbl]=trp_rentry_to_str(tmp_ctx, entries[kk], sep);
626         len+=strlen(tbl_strings[ii_tbl]);
627         ii_tbl++;
628       }
629       talloc_free(entries);
630     }
631     talloc_free(realms);
632   }
633   talloc_free(apcs);
634
635   /* now combine all the strings */
636   len += tbl_size*strlen(lineterm); /* space for line terminations*/
637   len += 1; /* nul terminator */
638   result=(char *)talloc_size(tmp_ctx, len);
639   for (p=result,ii=0; ii < tbl_size; ii++) {
640     p+=sprintf(p, "%s%s", tbl_strings[ii], lineterm);
641   }
642   talloc_steal(mem_ctx, result);
643   
644 cleanup:
645   talloc_free(tmp_ctx);
646   return result;
647 }