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