6fbbd6fd1d592aeaa31413bc1ba2add81b4e1c33
[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 }
259
260 /* note: the entry pointer passed in is invalid after calling this because the entry is freed */
261 void trp_rtable_remove(TRP_RTABLE *rtbl, TRP_RENTRY *entry)
262 {
263   GHashTable *apc_tbl=NULL;
264   GHashTable *realm_tbl=NULL;
265
266   apc_tbl=g_hash_table_lookup(rtbl, entry->apc);
267   if (apc_tbl==NULL)
268     return;
269
270   realm_tbl=g_hash_table_lookup(apc_tbl, entry->realm);
271   if (realm_tbl==NULL)
272     return;
273
274   /* remove the element */
275   g_hash_table_remove(realm_tbl, entry->peer);
276   /* if that was the last entry in the realm, remove the realm table */
277   if (g_hash_table_size(realm_tbl)==0)
278     g_hash_table_remove(apc_tbl, entry->realm);
279   /* if that was the last realm in the apc, remove the apc table */
280   if (g_hash_table_size(apc_tbl)==0)
281     g_hash_table_remove(rtbl, entry->apc);
282 }
283
284 /* gets the actual hash table, for internal use only */
285 static GHashTable *trp_rtable_get_apc_table(TRP_RTABLE *rtbl, TR_NAME *apc)
286 {
287   return g_hash_table_lookup(rtbl, apc);
288 }
289
290 /* gets the actual hash table, for internal use only */
291 static GHashTable *trp_rtable_get_realm_table(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm)
292 {
293   GHashTable *apc_tbl=trp_rtable_get_apc_table(rtbl, apc);
294   if (apc_tbl==NULL)
295     return NULL;
296   else
297     return g_hash_table_lookup(apc_tbl, realm);
298 }
299
300 struct table_size_cookie {
301   TRP_RTABLE *rtbl;
302   size_t size;
303 };
304 static void trp_rtable_size_helper(gpointer key, gpointer value, gpointer user_data)
305 {
306   struct table_size_cookie *data=(struct table_size_cookie *)user_data;
307   data->size += trp_rtable_apc_size(data->rtbl, (TR_NAME *)key);
308 };
309 size_t trp_rtable_size(TRP_RTABLE *rtbl)
310 {
311   struct table_size_cookie data={rtbl, 0};
312   g_hash_table_foreach(rtbl, trp_rtable_size_helper, &data);
313   return data.size;
314 }
315
316 struct table_apc_size_cookie {
317   TR_NAME *apc;
318   TRP_RTABLE *rtbl;
319   size_t size;
320 };
321 static void table_apc_size_helper(gpointer key, gpointer value, gpointer user_data)
322 {
323   struct table_apc_size_cookie *data=(struct table_apc_size_cookie *)user_data;
324   data->size += trp_rtable_realm_size(data->rtbl, data->apc, (TR_NAME *)key);
325 }
326 size_t trp_rtable_apc_size(TRP_RTABLE *rtbl, TR_NAME *apc)
327 {
328   struct table_apc_size_cookie data={apc, rtbl, 0};
329   GHashTable *apc_tbl=trp_rtable_get_apc_table(rtbl, apc);
330   if (apc_tbl==NULL)
331     return 0;;
332   g_hash_table_foreach(apc_tbl, table_apc_size_helper, &data);
333   return data.size;
334 }
335
336 size_t trp_rtable_realm_size(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm)
337 {
338   GHashTable *realm_tbl=trp_rtable_get_realm_table(rtbl, apc, realm);
339   if (realm_tbl==NULL)
340     return 0;
341   else
342     return g_hash_table_size(g_hash_table_lookup(
343                                g_hash_table_lookup(rtbl, apc),
344                                realm));
345 }
346
347 /* Returns an array of pointers to TRP_RENTRY, length of array in n_out.
348  * Caller must free the array (in the talloc NULL context), but must
349  * not free its contents. */
350 TRP_RENTRY **trp_rtable_get_entries(TRP_RTABLE *rtbl, size_t *n_out)
351 {
352   TRP_RENTRY **ret=NULL;
353   TR_NAME **apc=NULL;
354   size_t n_apc=0;
355   TRP_RENTRY **apc_entries=NULL;
356   size_t n_entries=0;
357   size_t ii_ret=0;
358
359   *n_out=trp_rtable_size(rtbl);
360   if (*n_out==0)
361     return NULL;
362
363   ret=talloc_array(NULL, TRP_RENTRY *, *n_out);
364   if (ret==NULL) {
365     tr_crit("trp_rtable_get_entries: unable to allocate return array.");
366     *n_out=0;
367     return NULL;
368   }
369
370   ii_ret=0; /* counts output entries */
371   apc=trp_rtable_get_apcs(rtbl, &n_apc);
372   while(n_apc--) {
373     apc_entries=trp_rtable_get_apc_entries(rtbl, apc[n_apc], &n_entries);
374     while (n_entries--)
375       ret[ii_ret++]=apc_entries[n_entries];
376     talloc_free(apc_entries);
377   }
378   talloc_free(apc);
379
380   if (ii_ret!=*n_out) {
381     tr_crit("trp_rtable_get_entries: found incorrect number of entries.");
382     talloc_free(ret);
383     *n_out=0;
384     return NULL;
385   }
386   return ret;
387 }
388
389 /* Returns an array of pointers to TR_NAME, length of array in n_out.
390  * Caller must free the array (in the talloc NULL context). */
391 TR_NAME **trp_rtable_get_apcs(TRP_RTABLE *rtbl, size_t *n_out)
392 {
393   size_t len=g_hash_table_size(rtbl); /* known apcs are keys in top level hash table */
394   size_t ii=0;
395   GList *apcs=NULL;;
396   GList *p=NULL;
397   TR_NAME **ret=NULL;
398
399   if (len==0) {
400     *n_out=0;
401     return NULL;
402   }
403     
404   ret=talloc_array(NULL, TR_NAME *, len);
405   if (ret==NULL) {
406     tr_crit("trp_rtable_get_apcs: unable to allocate return array.");
407     *n_out=0;
408     return NULL;
409   }
410   apcs=g_hash_table_get_keys(rtbl);
411   for (ii=0,p=apcs; p!=NULL; ii++,p=g_list_next(p))
412     ret[ii]=(TR_NAME *)p->data;
413
414   g_list_free(apcs);
415
416   *n_out=len;
417   return ret;
418 }
419
420 /* Returns an array of pointers to TR_NAME, length of array in n_out.
421  * Caller must free the array (in the talloc NULL context). */
422 TR_NAME **trp_rtable_get_apc_realms(TRP_RTABLE *rtbl, TR_NAME *apc, size_t *n_out)
423 {
424   size_t ii=0;
425   TRP_RTABLE *apc_tbl=g_hash_table_lookup(rtbl, apc);;
426   GList *entries=NULL;
427   GList *p=NULL;
428   TR_NAME **ret=NULL;
429
430   if (apc_tbl==NULL) {
431     *n_out=0;
432     return NULL;
433   }
434   *n_out=g_hash_table_size(apc_tbl); /* set output length */
435   ret=talloc_array(NULL, TR_NAME *, *n_out);
436   entries=g_hash_table_get_keys(apc_tbl);
437   for (ii=0,p=entries; p!=NULL; ii++,p=g_list_next(p))
438     ret[ii]=(TR_NAME *)p->data;
439
440   g_list_free(entries);
441   return ret;
442 }
443
444 /* Get all entries in an apc. Returns an array of pointers in NULL talloc context.
445  * Caller must free this list with talloc_free, but must not free the entries in the
446  * list.. */
447 TRP_RENTRY **trp_rtable_get_apc_entries(TRP_RTABLE *rtbl, TR_NAME *apc, size_t *n_out)
448 {
449   size_t ii=0, jj=0;
450   TR_NAME **realm=NULL;
451   size_t n_realms=0;
452   TRP_RENTRY **realm_entries=NULL;
453   size_t n_entries=0;
454   TRP_RENTRY **ret=NULL;
455   size_t ii_ret=0;
456
457   *n_out=trp_rtable_apc_size(rtbl, apc);
458   if (*n_out==0)
459     return NULL;
460
461   ret=talloc_array(NULL, TRP_RENTRY *, *n_out);
462   if (ret==NULL) {
463     tr_crit("trp_rtable_get_apc_entries: could not allocate return array.");
464     *n_out=0;
465     return NULL;
466   }
467   
468   ii_ret=0; /* counts entries in the output array */
469   realm=trp_rtable_get_apc_realms(rtbl, apc, &n_realms);
470   for (ii=0; ii<n_realms; ii++) {
471     realm_entries=trp_rtable_get_realm_entries(rtbl, apc, realm[ii], &n_entries);
472     for (jj=0; jj<n_entries; jj++)
473       ret[ii_ret++]=realm_entries[jj];
474     talloc_free(realm_entries);
475   }
476   talloc_free(realm);
477
478   if (ii_ret!=*n_out) {
479     tr_crit("trp_rtable_get_apc_entries: found incorrect number of entries.");
480     talloc_free(ret);
481     *n_out=0;
482     return NULL;
483   }
484
485   return ret;
486 }
487
488 /* Get all entries in an apc/realm. Returns an array of pointers in NULL talloc context.
489  * Caller must free this list with talloc_free, but must not free the entries in the
490  * list.. */
491 TRP_RENTRY **trp_rtable_get_realm_entries(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm, size_t *n_out)
492 {
493   size_t ii=0;
494   TRP_RENTRY **ret=NULL;
495   TR_NAME **peer=NULL;
496
497   peer=trp_rtable_get_apc_realm_peers(rtbl, apc, realm, n_out);
498   ret=talloc_array(NULL, TRP_RENTRY *, *n_out);
499   if (ret==NULL) {
500     tr_crit("trp_rtable_get_realm_entries: could not allocate return array.");
501     talloc_free(peer);
502     n_out=0;
503     return NULL;
504   }
505   for (ii=0; ii<*n_out; ii++)
506     ret[ii]=trp_rtable_get_entry(rtbl, apc, realm, peer[ii]);
507   talloc_free(peer);
508   return ret;
509 }
510
511 TR_NAME **trp_rtable_get_apc_realm_peers(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm, size_t *n_out)
512 {
513   TR_NAME **ret=NULL;
514   GHashTable *realm_tbl=NULL;
515   GList *keys=NULL;
516   GList *p=NULL;
517   size_t ii=0;
518
519   *n_out=trp_rtable_realm_size(rtbl, apc, realm);
520   if (*n_out==0)
521     return NULL;
522   realm_tbl=trp_rtable_get_realm_table(rtbl, apc, realm);
523   ret=talloc_array(NULL, TR_NAME *, *n_out);
524   if (ret==NULL) {
525     tr_crit("trp_rtable_get_apc_realm_peers: could not allocate return array.");
526     *n_out=0;
527     return NULL;
528   }
529   keys=g_hash_table_get_keys(realm_tbl);
530   for (ii=0,p=keys; p!=NULL; ii++,p=g_list_next(p))
531     ret[ii]=(TR_NAME *)p->data;
532   g_list_free(keys);
533   return ret;
534 }
535
536 /* Gets a single entry. Do not free it. */
537 TRP_RENTRY *trp_rtable_get_entry(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm, TR_NAME *peer)
538 {
539   GHashTable *realm_tbl=NULL;
540   
541   realm_tbl=trp_rtable_get_realm_table(rtbl, apc, realm);
542   if (realm_tbl==NULL)
543     return NULL;
544   return g_hash_table_lookup(realm_tbl, peer); /* does not copy or increment ref count */
545 }
546
547 static char *timespec_to_str(struct timespec *ts)
548 {
549   struct tm tm;
550   char *s=NULL;
551
552   if (localtime_r(&(ts->tv_sec), &tm)==NULL)
553     return NULL;
554
555   s=malloc(40); /* long enough to contain strftime result */
556   if (s==NULL)
557     return NULL;
558
559   if (strftime(s, 40, "%F %T", &tm)==0) {
560     free(s);
561     return NULL;
562   }
563   return s;
564 }
565
566 TRP_RENTRY *trp_rtable_get_selected_entry(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm)
567 {
568   size_t n=0;
569   TRP_RENTRY **entry=trp_rtable_get_realm_entries(rtbl, apc, realm, &n);
570   TRP_RENTRY *selected=NULL;
571
572   if (n==0)
573     return NULL;
574
575   while(n-- && !trp_rentry_get_selected(entry[n])) { }
576   selected=entry[n];
577   talloc_free(entry);
578   return selected;
579 }
580
581 /* Pretty print a route table entry to a newly allocated string. If sep is NULL,
582  * returns comma+space separated string. */
583 char *trp_rentry_to_str(TALLOC_CTX *mem_ctx, TRP_RENTRY *entry, const char *sep)
584 {
585   char *apc=tr_name_strdup(entry->apc);
586   char *realm=tr_name_strdup(entry->realm);
587   char *peer=tr_name_strdup(entry->peer);
588   char *trust_router=tr_name_strdup(entry->trust_router);
589   char *next_hop=tr_name_strdup(entry->next_hop);
590   char *expiry=timespec_to_str(entry->expiry);
591   char *result=NULL;
592
593   if (sep==NULL)
594     sep=", ";
595
596   result=talloc_asprintf(mem_ctx,
597                          "%s%s%s%s%s%s%u%s%s%s%s%s%d%s%s",
598                          apc, sep,
599                          realm, sep,
600                          peer, sep,
601                          entry->metric, sep,
602                          trust_router, sep,
603                          next_hop, sep,
604                          entry->selected, sep,
605                          expiry);
606   free(apc);
607   free(realm);
608   free(peer);
609   free(trust_router);
610   free(next_hop);
611   free(expiry);
612   return result;
613 }
614
615 static int sort_tr_names_cmp(const void *a, const void *b)
616 {
617   TR_NAME **n1=(TR_NAME **)a;
618   TR_NAME **n2=(TR_NAME **)b;
619   return tr_name_cmp(*n1, *n2);
620 }
621
622 static void sort_tr_names(TR_NAME **names, size_t n_names)
623 {
624   qsort(names, n_names, sizeof(TR_NAME *), sort_tr_names_cmp);
625 }
626
627 char *trp_rtable_to_str(TALLOC_CTX *mem_ctx, TRP_RTABLE *rtbl, const char *sep, const char *lineterm)
628 {
629   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
630   TR_NAME **apcs=NULL;
631   size_t n_apcs=0;
632   TR_NAME **realms=NULL;
633   size_t n_realms=0;
634   TRP_RENTRY **entries=NULL;
635   size_t n_entries=0;
636   char **tbl_strings=NULL;
637   size_t ii_tbl=0; /* counts tbl_strings */
638   size_t tbl_size=0;
639   size_t len=0;
640   size_t ii=0, jj=0, kk=0;
641   char *p=NULL;
642   char *result=NULL;
643
644   if (lineterm==NULL)
645     lineterm="\n";
646
647   tbl_size=trp_rtable_size(rtbl);
648   if (tbl_size==0) {
649     result=talloc_strdup(mem_ctx, lineterm);
650     goto cleanup;
651   }
652
653   tbl_strings=talloc_array(tmp_ctx, char *, tbl_size);
654   if (tbl_strings==NULL) {
655     result=talloc_strdup(mem_ctx, "error");
656     goto cleanup;
657   }
658   
659   apcs=trp_rtable_get_apcs(rtbl, &n_apcs);
660   talloc_steal(tmp_ctx, apcs);
661   sort_tr_names(apcs, n_apcs);
662   ii_tbl=0;
663   len=0;
664   for (ii=0; ii<n_apcs; ii++) {
665     realms=trp_rtable_get_apc_realms(rtbl, apcs[ii], &n_realms);
666     talloc_steal(tmp_ctx, realms);
667     sort_tr_names(realms, n_realms);
668     for (jj=0; jj<n_realms; jj++) {
669       entries=trp_rtable_get_realm_entries(rtbl, apcs[ii], realms[jj], &n_entries);
670       talloc_steal(tmp_ctx, entries);
671       for (kk=0; kk<n_entries; kk++) {
672         tbl_strings[ii_tbl]=trp_rentry_to_str(tmp_ctx, entries[kk], sep);
673         len+=strlen(tbl_strings[ii_tbl]);
674         ii_tbl++;
675       }
676       talloc_free(entries);
677     }
678     talloc_free(realms);
679   }
680   talloc_free(apcs);
681
682   /* now combine all the strings */
683   len += tbl_size*strlen(lineterm); /* space for line terminations*/
684   len += 1; /* nul terminator */
685   result=(char *)talloc_size(tmp_ctx, len);
686   for (p=result,ii=0; ii < tbl_size; ii++) {
687     p+=sprintf(p, "%s%s", tbl_strings[ii], lineterm);
688   }
689   talloc_steal(mem_ctx, result);
690   
691 cleanup:
692   talloc_free(tmp_ctx);
693   return result;
694 }