37fee17cd6f58dfb2bccb389b4551c864384c2f3
[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 #include <trust_router/trp.h>
12 #include <trust_router/tid.h>
13
14 /* Note: be careful mixing talloc with glib. */
15
16 static int trp_route_destructor(void *obj)
17 {
18   TRP_ROUTE *entry=talloc_get_type_abort(obj, TRP_ROUTE);
19   if (entry->comm!=NULL)
20     tr_free_name(entry->comm);
21   if (entry->realm!=NULL)
22     tr_free_name(entry->realm);
23   if (entry->trust_router!=NULL)
24     tr_free_name(entry->trust_router);
25   if (entry->peer!=NULL)
26     tr_free_name(entry->peer);
27   if (entry->next_hop!=NULL)
28     tr_free_name(entry->next_hop);
29   return 0;
30 }
31
32 TRP_ROUTE *trp_route_new(TALLOC_CTX *mem_ctx)
33 {
34   TRP_ROUTE *entry=talloc(mem_ctx, TRP_ROUTE);
35   if (entry!=NULL) {
36     entry->comm=NULL;
37     entry->realm=NULL;
38     entry->trust_router=NULL;
39     entry->trp_port=TRP_PORT;
40     entry->tid_port=TID_PORT;
41     entry->peer=NULL;
42     entry->next_hop=NULL;
43     entry->selected=0;
44     entry->interval=0;
45     entry->expiry=talloc(entry, struct timespec);
46     if (entry->expiry==NULL) {
47       talloc_free(entry);
48       return NULL;
49     }
50     *(entry->expiry)=(struct timespec){0,0};
51     entry->local=0;
52     entry->triggered=0;
53     talloc_set_destructor((void *)entry, trp_route_destructor);
54   }
55   return entry;
56 }
57
58 void trp_route_free(TRP_ROUTE *entry)
59 {
60   if (entry!=NULL)
61     talloc_free(entry);
62 }
63
64 void trp_route_set_comm(TRP_ROUTE *entry, TR_NAME *comm)
65 {
66   if (entry->comm!=NULL)
67     tr_free_name(entry->comm);
68   entry->comm=comm;
69 }
70
71 TR_NAME *trp_route_get_comm(TRP_ROUTE *entry)
72 {
73   return entry->comm;
74 }
75
76 TR_NAME *trp_route_dup_comm(TRP_ROUTE *entry)
77 {
78   return tr_dup_name(trp_route_get_comm(entry));
79 }
80
81 void trp_route_set_realm(TRP_ROUTE *entry, TR_NAME *realm)
82 {
83   if (entry->realm!=NULL)
84     tr_free_name(entry->realm);
85   entry->realm=realm;
86 }
87
88 TR_NAME *trp_route_get_realm(TRP_ROUTE *entry)
89 {
90   return entry->realm;
91 }
92
93 TR_NAME *trp_route_dup_realm(TRP_ROUTE *entry)
94 {
95   return tr_dup_name(trp_route_get_realm(entry));
96 }
97
98 void trp_route_set_trust_router(TRP_ROUTE *entry, TR_NAME *tr)
99 {
100   if (entry->trust_router!=NULL)
101     tr_free_name(entry->trust_router);
102   entry->trust_router=tr;
103 }
104
105 TR_NAME *trp_route_get_trust_router(TRP_ROUTE *entry)
106 {
107   return entry->trust_router;
108 }
109
110 TR_NAME *trp_route_dup_trust_router(TRP_ROUTE *entry)
111 {
112   return tr_dup_name(trp_route_get_trust_router(entry));
113 }
114
115 void trp_route_set_peer(TRP_ROUTE *entry, TR_NAME *peer)
116 {
117   if (entry->peer!=NULL)
118     tr_free_name(entry->peer);
119   entry->peer=peer;
120 }
121
122 TR_NAME *trp_route_get_peer(TRP_ROUTE *entry)
123 {
124   return entry->peer;
125 }
126
127 TR_NAME *trp_route_dup_peer(TRP_ROUTE *entry)
128 {
129   return tr_dup_name(trp_route_get_peer(entry));
130 }
131
132 void trp_route_set_metric(TRP_ROUTE *entry, unsigned int metric)
133 {
134   entry->metric=metric;
135 }
136
137 unsigned int trp_route_get_metric(TRP_ROUTE *entry)
138 {
139   return entry->metric;
140 }
141
142 /* TODO: set the hostname and port for the next hop. Currently assume default TID port. --jlr */
143 void trp_route_set_next_hop(TRP_ROUTE *entry, TR_NAME *next_hop)
144 {
145   if (entry->next_hop!=NULL)
146     tr_free_name(entry->next_hop);
147   entry->next_hop=next_hop;
148 }
149
150 TR_NAME *trp_route_get_next_hop(TRP_ROUTE *entry)
151 {
152   return entry->next_hop;
153 }
154
155 TR_NAME *trp_route_dup_next_hop(TRP_ROUTE *entry)
156 {
157   return tr_dup_name(trp_route_get_next_hop(entry));
158 }
159
160 void trp_route_set_selected(TRP_ROUTE *entry, int sel)
161 {
162   entry->selected=sel;
163 }
164
165 int trp_route_is_selected(TRP_ROUTE *entry)
166 {
167   return entry->selected;
168 }
169
170 void trp_route_set_interval(TRP_ROUTE *entry, int interval)
171 {
172   entry->interval=interval;
173 }
174
175 int trp_route_get_interval(TRP_ROUTE *entry)
176 {
177   return entry->interval;
178 }
179
180 /* copies incoming value, does not assume responsibility for freeing */
181 void trp_route_set_expiry(TRP_ROUTE *entry, struct timespec *exp)
182 {
183   entry->expiry->tv_sec=exp->tv_sec;
184   entry->expiry->tv_nsec=exp->tv_nsec;
185 }
186
187 struct timespec *trp_route_get_expiry(TRP_ROUTE *entry)
188 {
189   return entry->expiry;
190 }
191
192 void trp_route_set_local(TRP_ROUTE *entry, int local)
193 {
194   entry->local=local;
195 }
196
197 int trp_route_is_local(TRP_ROUTE *entry)
198 {
199   return entry->local;
200 }
201
202 void trp_route_set_triggered(TRP_ROUTE *entry, int trig)
203 {
204   tr_debug("trp_route_set_triggered: setting route to %.*s/%.*s through %.*s to %s",
205            entry->comm->len, entry->comm->buf,
206            entry->realm->len, entry->realm->buf,
207            entry->peer->len, entry->peer->buf,
208            trig ? "triggered" : "not triggered");
209   entry->triggered=trig;
210 }
211
212 int trp_route_is_triggered(TRP_ROUTE *entry)
213 {
214   return entry->triggered;
215 }
216
217
218 /* result must be freed with g_free */
219 static gchar *tr_name_to_g_str(const TR_NAME *n)
220 {
221   gchar *s=g_strndup(n->buf, n->len);
222   if (s==NULL)
223     tr_debug("tr_name_to_g_str: allocation failure.");
224   return s;
225 }
226
227 /* hash function for TR_NAME keys */
228 static guint trp_tr_name_hash(gconstpointer key)
229 {
230   const TR_NAME *name=(TR_NAME *)key;
231   gchar *s=tr_name_to_g_str(name);
232   guint hash=g_str_hash(s);
233   g_free(s);
234   return hash;
235 }
236
237 /* hash equality function for TR_NAME keys */
238 static gboolean trp_tr_name_equal(gconstpointer key1, gconstpointer key2)
239 {
240   const TR_NAME *n1=(TR_NAME *)key1;
241   const TR_NAME *n2=(TR_NAME *)key2;
242   gchar *s1=tr_name_to_g_str(n1);
243   gchar *s2=tr_name_to_g_str(n2);
244   gboolean equal=g_str_equal(s1, s2);
245   g_free(s1);
246   g_free(s2);
247   return equal;
248 }
249
250 /* free a value to the top level rtable (a hash of all entries in the comm) */
251 static void trp_rtable_destroy_table(gpointer data)
252 {
253   g_hash_table_destroy(data);
254 }
255
256 static void trp_rtable_destroy_rentry(gpointer data)
257 {
258   trp_route_free(data);
259 }
260
261 static void trp_rtable_destroy_tr_name(gpointer data)
262 {
263   tr_free_name(data);
264 }
265
266 TRP_RTABLE *trp_rtable_new(void)
267 {
268   GHashTable *new=g_hash_table_new_full(trp_tr_name_hash,
269                                         trp_tr_name_equal,
270                                         trp_rtable_destroy_tr_name,
271                                         trp_rtable_destroy_table);
272   return new;
273 }
274
275 void trp_rtable_free(TRP_RTABLE *rtbl)
276 {
277   g_hash_table_destroy(rtbl);
278 }
279
280 static GHashTable *trp_rtbl_get_or_add_table(GHashTable *tbl, TR_NAME *key, GDestroyNotify destroy)
281 {
282   GHashTable *val_tbl=NULL;
283
284   val_tbl=g_hash_table_lookup(tbl, key);
285   if (val_tbl==NULL) {
286     val_tbl=g_hash_table_new_full(trp_tr_name_hash,
287                                   trp_tr_name_equal,
288                                   trp_rtable_destroy_tr_name,
289                                   destroy);
290     g_hash_table_insert(tbl, tr_dup_name(key), val_tbl);
291   }
292   return val_tbl;
293 }
294
295 void trp_rtable_add(TRP_RTABLE *rtbl, TRP_ROUTE *entry)
296 {
297   GHashTable *comm_tbl=NULL;
298   GHashTable *realm_tbl=NULL;
299
300   comm_tbl=trp_rtbl_get_or_add_table(rtbl, entry->comm, trp_rtable_destroy_table);
301   realm_tbl=trp_rtbl_get_or_add_table(comm_tbl, entry->realm, trp_rtable_destroy_rentry);
302   g_hash_table_insert(realm_tbl, tr_dup_name(entry->peer), entry); /* destroys and replaces a duplicate */
303   /* the route entry should not belong to any context, we will manage it ourselves */
304   talloc_steal(NULL, entry);
305 }
306
307 /* note: the entry pointer passed in is invalid after calling this because the entry is freed */
308 void trp_rtable_remove(TRP_RTABLE *rtbl, TRP_ROUTE *entry)
309 {
310   GHashTable *comm_tbl=NULL;
311   GHashTable *realm_tbl=NULL;
312
313   comm_tbl=g_hash_table_lookup(rtbl, entry->comm);
314   if (comm_tbl==NULL)
315     return;
316
317   realm_tbl=g_hash_table_lookup(comm_tbl, entry->realm);
318   if (realm_tbl==NULL)
319     return;
320
321   /* remove the element */
322   g_hash_table_remove(realm_tbl, entry->peer);
323   /* if that was the last entry in the realm, remove the realm table */
324   if (g_hash_table_size(realm_tbl)==0)
325     g_hash_table_remove(comm_tbl, entry->realm);
326   /* if that was the last realm in the comm, remove the comm table */
327   if (g_hash_table_size(comm_tbl)==0)
328     g_hash_table_remove(rtbl, entry->comm);
329 }
330
331 void trp_rtable_clear(TRP_RTABLE *rtbl)
332 {
333   g_hash_table_remove_all(rtbl); /* destructors should do all the cleanup */
334 }
335
336 /* gets the actual hash table, for internal use only */
337 static GHashTable *trp_rtable_get_comm_table(TRP_RTABLE *rtbl, TR_NAME *comm)
338 {
339   return g_hash_table_lookup(rtbl, comm);
340 }
341
342 /* gets the actual hash table, for internal use only */
343 static GHashTable *trp_rtable_get_realm_table(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm)
344 {
345   GHashTable *comm_tbl=trp_rtable_get_comm_table(rtbl, comm);
346   if (comm_tbl==NULL)
347     return NULL;
348   else
349     return g_hash_table_lookup(comm_tbl, realm);
350 }
351
352 struct table_size_cookie {
353   TRP_RTABLE *rtbl;
354   size_t size;
355 };
356 static void trp_rtable_size_helper(gpointer key, gpointer value, gpointer user_data)
357 {
358   struct table_size_cookie *data=(struct table_size_cookie *)user_data;
359   data->size += trp_rtable_comm_size(data->rtbl, (TR_NAME *)key);
360 };
361 size_t trp_rtable_size(TRP_RTABLE *rtbl)
362 {
363   struct table_size_cookie data={rtbl, 0};
364   g_hash_table_foreach(rtbl, trp_rtable_size_helper, &data);
365   return data.size;
366 }
367
368 struct table_comm_size_cookie {
369   TR_NAME *comm;
370   TRP_RTABLE *rtbl;
371   size_t size;
372 };
373 static void table_comm_size_helper(gpointer key, gpointer value, gpointer user_data)
374 {
375   struct table_comm_size_cookie *data=(struct table_comm_size_cookie *)user_data;
376   data->size += trp_rtable_realm_size(data->rtbl, data->comm, (TR_NAME *)key);
377 }
378 size_t trp_rtable_comm_size(TRP_RTABLE *rtbl, TR_NAME *comm)
379 {
380   struct table_comm_size_cookie data={comm, rtbl, 0};
381   GHashTable *comm_tbl=trp_rtable_get_comm_table(rtbl, comm);
382   if (comm_tbl==NULL)
383     return 0;;
384   g_hash_table_foreach(comm_tbl, table_comm_size_helper, &data);
385   return data.size;
386 }
387
388 size_t trp_rtable_realm_size(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm)
389 {
390   GHashTable *realm_tbl=trp_rtable_get_realm_table(rtbl, comm, realm);
391   if (realm_tbl==NULL)
392     return 0;
393   else
394     return g_hash_table_size(g_hash_table_lookup(
395                                g_hash_table_lookup(rtbl, comm),
396                                realm));
397 }
398
399 /* Returns an array of pointers to TRP_ROUTE, length of array in n_out.
400  * Caller must free the array (in the talloc NULL context), but must
401  * not free its contents. */
402 TRP_ROUTE **trp_rtable_get_entries(TRP_RTABLE *rtbl, size_t *n_out)
403 {
404   TRP_ROUTE **ret=NULL;
405   TR_NAME **comm=NULL;
406   size_t n_comm=0;
407   TRP_ROUTE **comm_entries=NULL;
408   size_t n_entries=0;
409   size_t ii_ret=0;
410
411   *n_out=trp_rtable_size(rtbl);
412   if (*n_out==0)
413     return NULL;
414
415   ret=talloc_array(NULL, TRP_ROUTE *, *n_out);
416   if (ret==NULL) {
417     tr_crit("trp_rtable_get_entries: unable to allocate return array.");
418     *n_out=0;
419     return NULL;
420   }
421
422   ii_ret=0; /* counts output entries */
423   comm=trp_rtable_get_comms(rtbl, &n_comm);
424   while(n_comm--) {
425     comm_entries=trp_rtable_get_comm_entries(rtbl, comm[n_comm], &n_entries);
426     while (n_entries--)
427       ret[ii_ret++]=comm_entries[n_entries];
428     talloc_free(comm_entries);
429   }
430   talloc_free(comm);
431
432   if (ii_ret!=*n_out) {
433     tr_crit("trp_rtable_get_entries: found incorrect number of entries.");
434     talloc_free(ret);
435     *n_out=0;
436     return NULL;
437   }
438   return ret;
439 }
440
441 /* Returns an array of pointers to TR_NAME, length of array in n_out.
442  * Caller must free the array (in the talloc NULL context). */
443 TR_NAME **trp_rtable_get_comms(TRP_RTABLE *rtbl, size_t *n_out)
444 {
445   size_t len=g_hash_table_size(rtbl); /* known comms are keys in top level hash table */
446   size_t ii=0;
447   GList *comms=NULL;;
448   GList *p=NULL;
449   TR_NAME **ret=NULL;
450
451   if (len==0) {
452     *n_out=0;
453     return NULL;
454   }
455     
456   ret=talloc_array(NULL, TR_NAME *, len);
457   if (ret==NULL) {
458     tr_crit("trp_rtable_get_comms: unable to allocate return array.");
459     *n_out=0;
460     return NULL;
461   }
462   comms=g_hash_table_get_keys(rtbl);
463   for (ii=0,p=comms; p!=NULL; ii++,p=g_list_next(p))
464     ret[ii]=(TR_NAME *)p->data;
465
466   g_list_free(comms);
467
468   *n_out=len;
469   return ret;
470 }
471
472 /* Returns an array of pointers to TR_NAME, length of array in n_out.
473  * Caller must free the array (in the talloc NULL context). */
474 TR_NAME **trp_rtable_get_comm_realms(TRP_RTABLE *rtbl, TR_NAME *comm, size_t *n_out)
475 {
476   size_t ii=0;
477   TRP_RTABLE *comm_tbl=g_hash_table_lookup(rtbl, comm);;
478   GList *entries=NULL;
479   GList *p=NULL;
480   TR_NAME **ret=NULL;
481
482   if (comm_tbl==NULL) {
483     *n_out=0;
484     return NULL;
485   }
486   *n_out=g_hash_table_size(comm_tbl); /* set output length */
487   ret=talloc_array(NULL, TR_NAME *, *n_out);
488   entries=g_hash_table_get_keys(comm_tbl);
489   for (ii=0,p=entries; p!=NULL; ii++,p=g_list_next(p))
490     ret[ii]=(TR_NAME *)p->data;
491
492   g_list_free(entries);
493   return ret;
494 }
495
496 /* Get all entries in an comm. Returns an array of pointers in NULL talloc context.
497  * Caller must free this list with talloc_free, but must not free the entries in the
498  * list.. */
499 TRP_ROUTE **trp_rtable_get_comm_entries(TRP_RTABLE *rtbl, TR_NAME *comm, size_t *n_out)
500 {
501   size_t ii=0, jj=0;
502   TR_NAME **realm=NULL;
503   size_t n_realms=0;
504   TRP_ROUTE **realm_entries=NULL;
505   size_t n_entries=0;
506   TRP_ROUTE **ret=NULL;
507   size_t ii_ret=0;
508
509   *n_out=trp_rtable_comm_size(rtbl, comm);
510   if (*n_out==0)
511     return NULL;
512
513   ret=talloc_array(NULL, TRP_ROUTE *, *n_out);
514   if (ret==NULL) {
515     tr_crit("trp_rtable_get_comm_entries: could not allocate return array.");
516     *n_out=0;
517     return NULL;
518   }
519   
520   ii_ret=0; /* counts entries in the output array */
521   realm=trp_rtable_get_comm_realms(rtbl, comm, &n_realms);
522   for (ii=0; ii<n_realms; ii++) {
523     realm_entries=trp_rtable_get_realm_entries(rtbl, comm, realm[ii], &n_entries);
524     for (jj=0; jj<n_entries; jj++)
525       ret[ii_ret++]=realm_entries[jj];
526     talloc_free(realm_entries);
527   }
528   talloc_free(realm);
529
530   if (ii_ret!=*n_out) {
531     tr_crit("trp_rtable_get_comm_entries: found incorrect number of entries.");
532     talloc_free(ret);
533     *n_out=0;
534     return NULL;
535   }
536
537   return ret;
538 }
539
540 /* Get all entries in an comm/realm. Returns an array of pointers in NULL talloc context.
541  * Caller must free this list with talloc_free, but must not free the entries in the
542  * list.. */
543 TRP_ROUTE **trp_rtable_get_realm_entries(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm, size_t *n_out)
544 {
545   size_t ii=0;
546   TRP_ROUTE **ret=NULL;
547   TR_NAME **peer=NULL;
548
549   tr_debug("trp_rtable_get_realm_entries: entered.");
550   peer=trp_rtable_get_comm_realm_peers(rtbl, comm, realm, n_out);
551   ret=talloc_array(NULL, TRP_ROUTE *, *n_out);
552   if (ret==NULL) {
553     tr_crit("trp_rtable_get_realm_entries: could not allocate return array.");
554     talloc_free(peer);
555     n_out=0;
556     return NULL;
557   }
558   for (ii=0; ii<*n_out; ii++)
559     ret[ii]=trp_rtable_get_entry(rtbl, comm, realm, peer[ii]);
560   talloc_free(peer);
561   return ret;
562 }
563
564 TR_NAME **trp_rtable_get_comm_realm_peers(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm, size_t *n_out)
565 {
566   TR_NAME **ret=NULL;
567   GHashTable *realm_tbl=NULL;
568   GList *keys=NULL;
569   GList *p=NULL;
570   size_t ii=0;
571
572   *n_out=trp_rtable_realm_size(rtbl, comm, realm);
573   if (*n_out==0)
574     return NULL;
575   realm_tbl=trp_rtable_get_realm_table(rtbl, comm, realm);
576   ret=talloc_array(NULL, TR_NAME *, *n_out);
577   if (ret==NULL) {
578     tr_crit("trp_rtable_get_comm_realm_peers: could not allocate return array.");
579     *n_out=0;
580     return NULL;
581   }
582   keys=g_hash_table_get_keys(realm_tbl);
583   for (ii=0,p=keys; p!=NULL; ii++,p=g_list_next(p))
584     ret[ii]=(TR_NAME *)p->data;
585   g_list_free(keys);
586   return ret;
587 }
588
589 /* Gets a single entry. Do not free it. */
590 TRP_ROUTE *trp_rtable_get_entry(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer)
591 {
592   GHashTable *realm_tbl=NULL;
593
594   realm_tbl=trp_rtable_get_realm_table(rtbl, comm, realm);
595   if (realm_tbl==NULL)
596     return NULL;
597
598   return g_hash_table_lookup(realm_tbl, peer); /* does not copy or increment ref count */
599 }
600
601 static char *timespec_to_str(struct timespec *ts)
602 {
603   struct tm tm;
604   char *s=NULL;
605
606   if (localtime_r(&(ts->tv_sec), &tm)==NULL)
607     return NULL;
608
609   s=malloc(40); /* long enough to contain strftime result */
610   if (s==NULL)
611     return NULL;
612
613   if (strftime(s, 40, "%F %T", &tm)==0) {
614     free(s);
615     return NULL;
616   }
617   return s;
618 }
619
620 TRP_ROUTE *trp_rtable_get_selected_entry(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm)
621 {
622   size_t n=0;
623   int ii=0;
624   TRP_ROUTE **entry=trp_rtable_get_realm_entries(rtbl, comm, realm, &n);
625   TRP_ROUTE *selected=NULL;
626
627   if (n==0)
628     return NULL;
629
630   tr_debug("trp_rtable_get_selected_entry: looking through route table entries for realm %.*s.",
631            realm->len, realm->buf);
632   for(ii=0; ii<n; ii++) {
633     if (trp_route_is_selected(entry[ii])) {
634       selected=entry[ii];
635       break;
636     }
637   }
638   tr_debug("trp_rtable_get_selected_entry: ii=%d.", ii);
639
640   talloc_free(entry);
641   return selected;
642 }
643
644 /* Pretty print a route table entry to a newly allocated string. If sep is NULL,
645  * returns comma+space separated string. */
646 char *trp_route_to_str(TALLOC_CTX *mem_ctx, TRP_ROUTE *entry, const char *sep)
647 {
648   char *comm=tr_name_strdup(entry->comm);
649   char *realm=tr_name_strdup(entry->realm);
650   char *peer=tr_name_strdup(entry->peer);
651   char *trust_router=tr_name_strdup(entry->trust_router);
652   char *next_hop=tr_name_strdup(entry->next_hop);
653   char *expiry=timespec_to_str(entry->expiry);
654   char *result=NULL;
655
656   if (sep==NULL)
657     sep=", ";
658
659   result=talloc_asprintf(mem_ctx,
660                          "%s%s%s%s%s%s%u%s%s%s%s%s%u%s%u%s%s%s%u",
661                          comm, sep,
662                          realm, sep,
663                          peer, sep,
664                          entry->metric, sep,
665                          trust_router, sep,
666                          next_hop, sep,
667                          entry->selected, sep,
668                          entry->local, sep,
669                          expiry, sep,
670                          entry->triggered);
671   free(comm);
672   free(realm);
673   free(peer);
674   free(trust_router);
675   free(next_hop);
676   free(expiry);
677   return result;
678 }
679
680 void trp_rtable_clear_triggered(TRP_RTABLE *rtbl)
681 {
682   size_t n_entries=0;
683   TRP_ROUTE **entries=trp_rtable_get_entries(rtbl, &n_entries);
684   size_t ii=0;
685
686   if (entries!=NULL) {
687     for (ii=0; ii<n_entries; ii++)
688       trp_route_set_triggered(entries[ii], 0);
689     talloc_free(entries);
690   }
691 }
692
693 static int sort_tr_names_cmp(const void *a, const void *b)
694 {
695   TR_NAME **n1=(TR_NAME **)a;
696   TR_NAME **n2=(TR_NAME **)b;
697   return tr_name_cmp(*n1, *n2);
698 }
699
700 static void sort_tr_names(TR_NAME **names, size_t n_names)
701 {
702   qsort(names, n_names, sizeof(TR_NAME *), sort_tr_names_cmp);
703 }
704
705 char *trp_rtable_to_str(TALLOC_CTX *mem_ctx, TRP_RTABLE *rtbl, const char *sep, const char *lineterm)
706 {
707   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
708   TR_NAME **comms=NULL;
709   size_t n_comms=0;
710   TR_NAME **realms=NULL;
711   size_t n_realms=0;
712   TRP_ROUTE **entries=NULL;
713   size_t n_entries=0;
714   char **tbl_strings=NULL;
715   size_t ii_tbl=0; /* counts tbl_strings */
716   size_t tbl_size=0;
717   size_t len=0;
718   size_t ii=0, jj=0, kk=0;
719   char *p=NULL;
720   char *result=NULL;
721
722   if (lineterm==NULL)
723     lineterm="\n";
724
725   tbl_size=trp_rtable_size(rtbl);
726   if (tbl_size==0) {
727     result=talloc_strdup(mem_ctx, lineterm);
728     goto cleanup;
729   }
730
731   tbl_strings=talloc_array(tmp_ctx, char *, tbl_size);
732   if (tbl_strings==NULL) {
733     result=talloc_strdup(mem_ctx, "error");
734     goto cleanup;
735   }
736   
737   comms=trp_rtable_get_comms(rtbl, &n_comms);
738   talloc_steal(tmp_ctx, comms);
739   sort_tr_names(comms, n_comms);
740   ii_tbl=0;
741   len=0;
742   for (ii=0; ii<n_comms; ii++) {
743     realms=trp_rtable_get_comm_realms(rtbl, comms[ii], &n_realms);
744     talloc_steal(tmp_ctx, realms);
745     sort_tr_names(realms, n_realms);
746     for (jj=0; jj<n_realms; jj++) {
747       entries=trp_rtable_get_realm_entries(rtbl, comms[ii], realms[jj], &n_entries);
748       talloc_steal(tmp_ctx, entries);
749       for (kk=0; kk<n_entries; kk++) {
750         tbl_strings[ii_tbl]=trp_route_to_str(tmp_ctx, entries[kk], sep);
751         len+=strlen(tbl_strings[ii_tbl]);
752         ii_tbl++;
753       }
754       talloc_free(entries);
755     }
756     talloc_free(realms);
757   }
758   talloc_free(comms);
759
760   /* now combine all the strings */
761   len += tbl_size*strlen(lineterm); /* space for line terminations*/
762   len += 1; /* nul terminator */
763   result=(char *)talloc_size(tmp_ctx, len);
764   for (p=result,ii=0; ii < tbl_size; ii++) {
765     p+=sprintf(p, "%s%s", tbl_strings[ii], lineterm);
766   }
767   talloc_steal(mem_ctx, result);
768   
769 cleanup:
770   talloc_free(tmp_ctx);
771   return result;
772 }