Bug fixes to make TID forwarding work between trust routers.
[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   entry->triggered=trig;
205 }
206
207 int trp_route_is_triggered(TRP_ROUTE *entry)
208 {
209   return entry->triggered;
210 }
211
212
213 /* result must be freed with g_free */
214 static gchar *tr_name_to_g_str(const TR_NAME *n)
215 {
216   gchar *s=g_strndup(n->buf, n->len);
217   if (s==NULL)
218     tr_debug("tr_name_to_g_str: allocation failure.");
219   return s;
220 }
221
222 /* hash function for TR_NAME keys */
223 static guint trp_tr_name_hash(gconstpointer key)
224 {
225   const TR_NAME *name=(TR_NAME *)key;
226   gchar *s=tr_name_to_g_str(name);
227   guint hash=g_str_hash(s);
228   g_free(s);
229   return hash;
230 }
231
232 /* hash equality function for TR_NAME keys */
233 static gboolean trp_tr_name_equal(gconstpointer key1, gconstpointer key2)
234 {
235   const TR_NAME *n1=(TR_NAME *)key1;
236   const TR_NAME *n2=(TR_NAME *)key2;
237   gchar *s1=tr_name_to_g_str(n1);
238   gchar *s2=tr_name_to_g_str(n2);
239   gboolean equal=g_str_equal(s1, s2);
240   g_free(s1);
241   g_free(s2);
242   return equal;
243 }
244
245 /* free a value to the top level rtable (a hash of all entries in the comm) */
246 static void trp_rtable_destroy_table(gpointer data)
247 {
248   g_hash_table_destroy(data);
249 }
250
251 static void trp_rtable_destroy_rentry(gpointer data)
252 {
253   trp_route_free(data);
254 }
255
256 static void trp_rtable_destroy_tr_name(gpointer data)
257 {
258   tr_free_name(data);
259 }
260
261 TRP_RTABLE *trp_rtable_new(void)
262 {
263   GHashTable *new=g_hash_table_new_full(trp_tr_name_hash,
264                                         trp_tr_name_equal,
265                                         trp_rtable_destroy_tr_name,
266                                         trp_rtable_destroy_table);
267   return new;
268 }
269
270 void trp_rtable_free(TRP_RTABLE *rtbl)
271 {
272   g_hash_table_destroy(rtbl);
273 }
274
275 static GHashTable *trp_rtbl_get_or_add_table(GHashTable *tbl, TR_NAME *key, GDestroyNotify destroy)
276 {
277   GHashTable *val_tbl=NULL;
278
279   val_tbl=g_hash_table_lookup(tbl, key);
280   if (val_tbl==NULL) {
281     val_tbl=g_hash_table_new_full(trp_tr_name_hash,
282                                   trp_tr_name_equal,
283                                   trp_rtable_destroy_tr_name,
284                                   destroy);
285     g_hash_table_insert(tbl, tr_dup_name(key), val_tbl);
286   }
287   return val_tbl;
288 }
289
290 void trp_rtable_add(TRP_RTABLE *rtbl, TRP_ROUTE *entry)
291 {
292   GHashTable *comm_tbl=NULL;
293   GHashTable *realm_tbl=NULL;
294
295   comm_tbl=trp_rtbl_get_or_add_table(rtbl, entry->comm, trp_rtable_destroy_table);
296   realm_tbl=trp_rtbl_get_or_add_table(comm_tbl, entry->realm, trp_rtable_destroy_rentry);
297   g_hash_table_insert(realm_tbl, tr_dup_name(entry->peer), entry); /* destroys and replaces a duplicate */
298   /* the route entry should not belong to any context, we will manage it ourselves */
299   talloc_steal(NULL, entry);
300 }
301
302 /* note: the entry pointer passed in is invalid after calling this because the entry is freed */
303 void trp_rtable_remove(TRP_RTABLE *rtbl, TRP_ROUTE *entry)
304 {
305   GHashTable *comm_tbl=NULL;
306   GHashTable *realm_tbl=NULL;
307
308   comm_tbl=g_hash_table_lookup(rtbl, entry->comm);
309   if (comm_tbl==NULL)
310     return;
311
312   realm_tbl=g_hash_table_lookup(comm_tbl, entry->realm);
313   if (realm_tbl==NULL)
314     return;
315
316   /* remove the element */
317   g_hash_table_remove(realm_tbl, entry->peer);
318   /* if that was the last entry in the realm, remove the realm table */
319   if (g_hash_table_size(realm_tbl)==0)
320     g_hash_table_remove(comm_tbl, entry->realm);
321   /* if that was the last realm in the comm, remove the comm table */
322   if (g_hash_table_size(comm_tbl)==0)
323     g_hash_table_remove(rtbl, entry->comm);
324 }
325
326 void trp_rtable_clear(TRP_RTABLE *rtbl)
327 {
328   g_hash_table_remove_all(rtbl); /* destructors should do all the cleanup */
329 }
330
331 /* gets the actual hash table, for internal use only */
332 static GHashTable *trp_rtable_get_comm_table(TRP_RTABLE *rtbl, TR_NAME *comm)
333 {
334   return g_hash_table_lookup(rtbl, comm);
335 }
336
337 /* gets the actual hash table, for internal use only */
338 static GHashTable *trp_rtable_get_realm_table(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm)
339 {
340   GHashTable *comm_tbl=trp_rtable_get_comm_table(rtbl, comm);
341   if (comm_tbl==NULL)
342     return NULL;
343   else
344     return g_hash_table_lookup(comm_tbl, realm);
345 }
346
347 struct table_size_cookie {
348   TRP_RTABLE *rtbl;
349   size_t size;
350 };
351 static void trp_rtable_size_helper(gpointer key, gpointer value, gpointer user_data)
352 {
353   struct table_size_cookie *data=(struct table_size_cookie *)user_data;
354   data->size += trp_rtable_comm_size(data->rtbl, (TR_NAME *)key);
355 };
356 size_t trp_rtable_size(TRP_RTABLE *rtbl)
357 {
358   struct table_size_cookie data={rtbl, 0};
359   g_hash_table_foreach(rtbl, trp_rtable_size_helper, &data);
360   return data.size;
361 }
362
363 struct table_comm_size_cookie {
364   TR_NAME *comm;
365   TRP_RTABLE *rtbl;
366   size_t size;
367 };
368 static void table_comm_size_helper(gpointer key, gpointer value, gpointer user_data)
369 {
370   struct table_comm_size_cookie *data=(struct table_comm_size_cookie *)user_data;
371   data->size += trp_rtable_realm_size(data->rtbl, data->comm, (TR_NAME *)key);
372 }
373 size_t trp_rtable_comm_size(TRP_RTABLE *rtbl, TR_NAME *comm)
374 {
375   struct table_comm_size_cookie data={comm, rtbl, 0};
376   GHashTable *comm_tbl=trp_rtable_get_comm_table(rtbl, comm);
377   if (comm_tbl==NULL)
378     return 0;;
379   g_hash_table_foreach(comm_tbl, table_comm_size_helper, &data);
380   return data.size;
381 }
382
383 size_t trp_rtable_realm_size(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm)
384 {
385   GHashTable *realm_tbl=trp_rtable_get_realm_table(rtbl, comm, realm);
386   if (realm_tbl==NULL)
387     return 0;
388   else
389     return g_hash_table_size(g_hash_table_lookup(
390                                g_hash_table_lookup(rtbl, comm),
391                                realm));
392 }
393
394 /* Returns an array of pointers to TRP_ROUTE, length of array in n_out.
395  * Caller must free the array (in the talloc NULL context), but must
396  * not free its contents. */
397 TRP_ROUTE **trp_rtable_get_entries(TRP_RTABLE *rtbl, size_t *n_out)
398 {
399   TRP_ROUTE **ret=NULL;
400   TR_NAME **comm=NULL;
401   size_t n_comm=0;
402   TRP_ROUTE **comm_entries=NULL;
403   size_t n_entries=0;
404   size_t ii_ret=0;
405
406   *n_out=trp_rtable_size(rtbl);
407   if (*n_out==0)
408     return NULL;
409
410   ret=talloc_array(NULL, TRP_ROUTE *, *n_out);
411   if (ret==NULL) {
412     tr_crit("trp_rtable_get_entries: unable to allocate return array.");
413     *n_out=0;
414     return NULL;
415   }
416
417   ii_ret=0; /* counts output entries */
418   comm=trp_rtable_get_comms(rtbl, &n_comm);
419   while(n_comm--) {
420     comm_entries=trp_rtable_get_comm_entries(rtbl, comm[n_comm], &n_entries);
421     while (n_entries--)
422       ret[ii_ret++]=comm_entries[n_entries];
423     talloc_free(comm_entries);
424   }
425   talloc_free(comm);
426
427   if (ii_ret!=*n_out) {
428     tr_crit("trp_rtable_get_entries: found incorrect number of entries.");
429     talloc_free(ret);
430     *n_out=0;
431     return NULL;
432   }
433   return ret;
434 }
435
436 /* Returns an array of pointers to TR_NAME, length of array in n_out.
437  * Caller must free the array (in the talloc NULL context). */
438 TR_NAME **trp_rtable_get_comms(TRP_RTABLE *rtbl, size_t *n_out)
439 {
440   size_t len=g_hash_table_size(rtbl); /* known comms are keys in top level hash table */
441   size_t ii=0;
442   GList *comms=NULL;;
443   GList *p=NULL;
444   TR_NAME **ret=NULL;
445
446   if (len==0) {
447     *n_out=0;
448     return NULL;
449   }
450     
451   ret=talloc_array(NULL, TR_NAME *, len);
452   if (ret==NULL) {
453     tr_crit("trp_rtable_get_comms: unable to allocate return array.");
454     *n_out=0;
455     return NULL;
456   }
457   comms=g_hash_table_get_keys(rtbl);
458   for (ii=0,p=comms; p!=NULL; ii++,p=g_list_next(p))
459     ret[ii]=(TR_NAME *)p->data;
460
461   g_list_free(comms);
462
463   *n_out=len;
464   return ret;
465 }
466
467 /* Returns an array of pointers to TR_NAME, length of array in n_out.
468  * Caller must free the array (in the talloc NULL context). */
469 TR_NAME **trp_rtable_get_comm_realms(TRP_RTABLE *rtbl, TR_NAME *comm, size_t *n_out)
470 {
471   size_t ii=0;
472   TRP_RTABLE *comm_tbl=g_hash_table_lookup(rtbl, comm);;
473   GList *entries=NULL;
474   GList *p=NULL;
475   TR_NAME **ret=NULL;
476
477   if (comm_tbl==NULL) {
478     *n_out=0;
479     return NULL;
480   }
481   *n_out=g_hash_table_size(comm_tbl); /* set output length */
482   ret=talloc_array(NULL, TR_NAME *, *n_out);
483   entries=g_hash_table_get_keys(comm_tbl);
484   for (ii=0,p=entries; p!=NULL; ii++,p=g_list_next(p))
485     ret[ii]=(TR_NAME *)p->data;
486
487   g_list_free(entries);
488   return ret;
489 }
490
491 /* Get all entries in an comm. Returns an array of pointers in NULL talloc context.
492  * Caller must free this list with talloc_free, but must not free the entries in the
493  * list.. */
494 TRP_ROUTE **trp_rtable_get_comm_entries(TRP_RTABLE *rtbl, TR_NAME *comm, size_t *n_out)
495 {
496   size_t ii=0, jj=0;
497   TR_NAME **realm=NULL;
498   size_t n_realms=0;
499   TRP_ROUTE **realm_entries=NULL;
500   size_t n_entries=0;
501   TRP_ROUTE **ret=NULL;
502   size_t ii_ret=0;
503
504   *n_out=trp_rtable_comm_size(rtbl, comm);
505   if (*n_out==0)
506     return NULL;
507
508   ret=talloc_array(NULL, TRP_ROUTE *, *n_out);
509   if (ret==NULL) {
510     tr_crit("trp_rtable_get_comm_entries: could not allocate return array.");
511     *n_out=0;
512     return NULL;
513   }
514   
515   ii_ret=0; /* counts entries in the output array */
516   realm=trp_rtable_get_comm_realms(rtbl, comm, &n_realms);
517   for (ii=0; ii<n_realms; ii++) {
518     realm_entries=trp_rtable_get_realm_entries(rtbl, comm, realm[ii], &n_entries);
519     for (jj=0; jj<n_entries; jj++)
520       ret[ii_ret++]=realm_entries[jj];
521     talloc_free(realm_entries);
522   }
523   talloc_free(realm);
524
525   if (ii_ret!=*n_out) {
526     tr_crit("trp_rtable_get_comm_entries: found incorrect number of entries.");
527     talloc_free(ret);
528     *n_out=0;
529     return NULL;
530   }
531
532   return ret;
533 }
534
535 /* Get all entries in an comm/realm. Returns an array of pointers in NULL talloc context.
536  * Caller must free this list with talloc_free, but must not free the entries in the
537  * list.. */
538 TRP_ROUTE **trp_rtable_get_realm_entries(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm, size_t *n_out)
539 {
540   size_t ii=0;
541   TRP_ROUTE **ret=NULL;
542   TR_NAME **peer=NULL;
543
544   tr_debug("trp_rtable_get_realm_entries: entered.");
545   peer=trp_rtable_get_comm_realm_peers(rtbl, comm, realm, n_out);
546   ret=talloc_array(NULL, TRP_ROUTE *, *n_out);
547   if (ret==NULL) {
548     tr_crit("trp_rtable_get_realm_entries: could not allocate return array.");
549     talloc_free(peer);
550     n_out=0;
551     return NULL;
552   }
553   for (ii=0; ii<*n_out; ii++)
554     ret[ii]=trp_rtable_get_entry(rtbl, comm, realm, peer[ii]);
555   talloc_free(peer);
556   return ret;
557 }
558
559 TR_NAME **trp_rtable_get_comm_realm_peers(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm, size_t *n_out)
560 {
561   TR_NAME **ret=NULL;
562   GHashTable *realm_tbl=NULL;
563   GList *keys=NULL;
564   GList *p=NULL;
565   size_t ii=0;
566
567   *n_out=trp_rtable_realm_size(rtbl, comm, realm);
568   if (*n_out==0)
569     return NULL;
570   realm_tbl=trp_rtable_get_realm_table(rtbl, comm, realm);
571   ret=talloc_array(NULL, TR_NAME *, *n_out);
572   if (ret==NULL) {
573     tr_crit("trp_rtable_get_comm_realm_peers: could not allocate return array.");
574     *n_out=0;
575     return NULL;
576   }
577   keys=g_hash_table_get_keys(realm_tbl);
578   for (ii=0,p=keys; p!=NULL; ii++,p=g_list_next(p))
579     ret[ii]=(TR_NAME *)p->data;
580   g_list_free(keys);
581   return ret;
582 }
583
584 /* Gets a single entry. Do not free it. */
585 TRP_ROUTE *trp_rtable_get_entry(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer)
586 {
587   GHashTable *realm_tbl=NULL;
588
589   realm_tbl=trp_rtable_get_realm_table(rtbl, comm, realm);
590   if (realm_tbl==NULL)
591     return NULL;
592
593   return g_hash_table_lookup(realm_tbl, peer); /* does not copy or increment ref count */
594 }
595
596 static char *timespec_to_str(struct timespec *ts)
597 {
598   struct tm tm;
599   char *s=NULL;
600
601   if (localtime_r(&(ts->tv_sec), &tm)==NULL)
602     return NULL;
603
604   s=malloc(40); /* long enough to contain strftime result */
605   if (s==NULL)
606     return NULL;
607
608   if (strftime(s, 40, "%F %T", &tm)==0) {
609     free(s);
610     return NULL;
611   }
612   return s;
613 }
614
615 TRP_ROUTE *trp_rtable_get_selected_entry(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm)
616 {
617   size_t n=0;
618   int ii=0;
619   TRP_ROUTE **entry=trp_rtable_get_realm_entries(rtbl, comm, realm, &n);
620   TRP_ROUTE *selected=NULL;
621
622   if (n==0)
623     return NULL;
624
625   tr_debug("trp_rtable_get_selected_entry: looking through route table entries for realm %.*s.",
626            realm->len, realm->buf);
627   for(ii=0; ii<n; ii++) {
628     if (trp_route_is_selected(entry[ii])) {
629       selected=entry[ii];
630       break;
631     }
632   }
633   tr_debug("trp_rtable_get_selected_entry: ii=%d.", ii);
634
635   talloc_free(entry);
636   return selected;
637 }
638
639 /* Pretty print a route table entry to a newly allocated string. If sep is NULL,
640  * returns comma+space separated string. */
641 char *trp_route_to_str(TALLOC_CTX *mem_ctx, TRP_ROUTE *entry, const char *sep)
642 {
643   char *comm=tr_name_strdup(entry->comm);
644   char *realm=tr_name_strdup(entry->realm);
645   char *peer=tr_name_strdup(entry->peer);
646   char *trust_router=tr_name_strdup(entry->trust_router);
647   char *next_hop=tr_name_strdup(entry->next_hop);
648   char *expiry=timespec_to_str(entry->expiry);
649   char *result=NULL;
650
651   if (sep==NULL)
652     sep=", ";
653
654   result=talloc_asprintf(mem_ctx,
655                          "%s%s%s%s%s%s%u%s%s%s%s%s%u%s%u%s%s%s%u",
656                          comm, sep,
657                          realm, sep,
658                          peer, sep,
659                          entry->metric, sep,
660                          trust_router, sep,
661                          next_hop, sep,
662                          entry->selected, sep,
663                          entry->local, sep,
664                          expiry, sep,
665                          entry->triggered);
666   free(comm);
667   free(realm);
668   free(peer);
669   free(trust_router);
670   free(next_hop);
671   free(expiry);
672   return result;
673 }
674
675 void trp_rtable_clear_triggered(TRP_RTABLE *rtbl)
676 {
677   size_t n_entries=0;
678   TRP_ROUTE **entries=trp_rtable_get_entries(rtbl, &n_entries);
679   size_t ii=0;
680
681   if (entries!=NULL) {
682     for (ii=0; ii<n_entries; ii++)
683       trp_route_set_triggered(entries[ii], 0);
684     talloc_free(entries);
685   }
686 }
687
688 static int sort_tr_names_cmp(const void *a, const void *b)
689 {
690   TR_NAME **n1=(TR_NAME **)a;
691   TR_NAME **n2=(TR_NAME **)b;
692   return tr_name_cmp(*n1, *n2);
693 }
694
695 static void sort_tr_names(TR_NAME **names, size_t n_names)
696 {
697   qsort(names, n_names, sizeof(TR_NAME *), sort_tr_names_cmp);
698 }
699
700 char *trp_rtable_to_str(TALLOC_CTX *mem_ctx, TRP_RTABLE *rtbl, const char *sep, const char *lineterm)
701 {
702   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
703   TR_NAME **comms=NULL;
704   size_t n_comms=0;
705   TR_NAME **realms=NULL;
706   size_t n_realms=0;
707   TRP_ROUTE **entries=NULL;
708   size_t n_entries=0;
709   char **tbl_strings=NULL;
710   size_t ii_tbl=0; /* counts tbl_strings */
711   size_t tbl_size=0;
712   size_t len=0;
713   size_t ii=0, jj=0, kk=0;
714   char *p=NULL;
715   char *result=NULL;
716
717   if (lineterm==NULL)
718     lineterm="\n";
719
720   tbl_size=trp_rtable_size(rtbl);
721   if (tbl_size==0) {
722     result=talloc_strdup(mem_ctx, lineterm);
723     goto cleanup;
724   }
725
726   tbl_strings=talloc_array(tmp_ctx, char *, tbl_size);
727   if (tbl_strings==NULL) {
728     result=talloc_strdup(mem_ctx, "error");
729     goto cleanup;
730   }
731   
732   comms=trp_rtable_get_comms(rtbl, &n_comms);
733   talloc_steal(tmp_ctx, comms);
734   sort_tr_names(comms, n_comms);
735   ii_tbl=0;
736   len=0;
737   for (ii=0; ii<n_comms; ii++) {
738     realms=trp_rtable_get_comm_realms(rtbl, comms[ii], &n_realms);
739     talloc_steal(tmp_ctx, realms);
740     sort_tr_names(realms, n_realms);
741     for (jj=0; jj<n_realms; jj++) {
742       entries=trp_rtable_get_realm_entries(rtbl, comms[ii], realms[jj], &n_entries);
743       talloc_steal(tmp_ctx, entries);
744       for (kk=0; kk<n_entries; kk++) {
745         tbl_strings[ii_tbl]=trp_route_to_str(tmp_ctx, entries[kk], sep);
746         len+=strlen(tbl_strings[ii_tbl]);
747         ii_tbl++;
748       }
749       talloc_free(entries);
750     }
751     talloc_free(realms);
752   }
753   talloc_free(comms);
754
755   /* now combine all the strings */
756   len += tbl_size*strlen(lineterm); /* space for line terminations*/
757   len += 1; /* nul terminator */
758   result=(char *)talloc_size(tmp_ctx, len);
759   for (p=result,ii=0; ii < tbl_size; ii++) {
760     p+=sprintf(p, "%s%s", tbl_strings[ii], lineterm);
761   }
762   talloc_steal(mem_ctx, result);
763   
764 cleanup:
765   talloc_free(tmp_ctx);
766   return result;
767 }