Connect to hard-coded peer and exchange route info. Buggy and incomplete.
[trust_router.git] / trp / trp_rtable.c
index 64396bf..6fbbd6f 100644 (file)
@@ -1,5 +1,8 @@
+#include <stdlib.h>
+
 #include <glib.h>
 #include <talloc.h>
+#include <time.h>
 
 #include <trust_router/tr_name.h>
 #include <trp_internal.h>
@@ -34,6 +37,7 @@ TRP_RENTRY *trp_rentry_new(TALLOC_CTX *mem_ctx)
     entry->peer=NULL;
     entry->next_hop=NULL;
     entry->selected=0;
+    entry->interval=0;
     entry->expiry=talloc(entry, struct timespec);
     if (entry->expiry==NULL) {
       talloc_free(entry);
@@ -41,7 +45,6 @@ TRP_RENTRY *trp_rentry_new(TALLOC_CTX *mem_ctx)
     }
     talloc_set_destructor((void *)entry, trp_rentry_destructor);
   }
-  tr_debug("trp_rentry_new: %p", entry);
   return entry;
 }
 
@@ -61,6 +64,11 @@ TR_NAME *trp_rentry_get_apc(TRP_RENTRY *entry)
   return entry->apc;
 }
 
+TR_NAME *trp_rentry_dup_apc(TRP_RENTRY *entry)
+{
+  return tr_dup_name(trp_rentry_get_apc(entry));
+}
+
 void trp_rentry_set_realm(TRP_RENTRY *entry, TR_NAME *realm)
 {
   entry->realm=realm;
@@ -71,6 +79,11 @@ TR_NAME *trp_rentry_get_realm(TRP_RENTRY *entry)
   return entry->realm;
 }
 
+TR_NAME *trp_rentry_dup_realm(TRP_RENTRY *entry)
+{
+  return tr_dup_name(trp_rentry_get_realm(entry));
+}
+
 void trp_rentry_set_trust_router(TRP_RENTRY *entry, TR_NAME *tr)
 {
   entry->trust_router=tr;
@@ -81,6 +94,11 @@ TR_NAME *trp_rentry_get_trust_router(TRP_RENTRY *entry)
   return entry->trust_router;
 }
 
+TR_NAME *trp_rentry_dup_trust_router(TRP_RENTRY *entry)
+{
+  return tr_dup_name(trp_rentry_get_trust_router(entry));
+}
+
 void trp_rentry_set_peer(TRP_RENTRY *entry, TR_NAME *peer)
 {
   entry->peer=peer;
@@ -91,6 +109,11 @@ TR_NAME *trp_rentry_get_peer(TRP_RENTRY *entry)
   return entry->peer;
 }
 
+TR_NAME *trp_rentry_dup_peer(TRP_RENTRY *entry)
+{
+  return tr_dup_name(trp_rentry_get_peer(entry));
+}
+
 void trp_rentry_set_metric(TRP_RENTRY *entry, unsigned int metric)
 {
   entry->metric=metric;
@@ -111,6 +134,11 @@ TR_NAME *trp_rentry_get_next_hop(TRP_RENTRY *entry)
   return entry->next_hop;
 }
 
+TR_NAME *trp_rentry_dup_next_hop(TRP_RENTRY *entry)
+{
+  return tr_dup_name(trp_rentry_get_next_hop(entry));
+}
+
 void trp_rentry_set_selected(TRP_RENTRY *entry, int sel)
 {
   entry->selected=sel;
@@ -121,6 +149,16 @@ int trp_rentry_get_selected(TRP_RENTRY *entry)
   return entry->selected;
 }
 
+void trp_rentry_set_interval(TRP_RENTRY *entry, int interval)
+{
+  entry->interval=interval;
+}
+
+int trp_rentry_get_interval(TRP_RENTRY *entry)
+{
+  return entry->interval;
+}
+
 /* copies incoming value, does not assume responsibility for freeing */
 void trp_rentry_set_expiry(TRP_RENTRY *entry, struct timespec *exp)
 {
@@ -175,13 +213,17 @@ static void trp_rtable_destroy_rentry(gpointer data)
   trp_rentry_free(data);
 }
 
+static void trp_rtable_destroy_tr_name(gpointer data)
+{
+  tr_free_name(data);
+}
+
 TRP_RTABLE *trp_rtable_new(void)
 {
   GHashTable *new=g_hash_table_new_full(trp_tr_name_hash,
                                         trp_tr_name_equal,
-                                        NULL, /* no need to free the key, it is part of the TRP_RENTRY */
+                                        trp_rtable_destroy_tr_name,
                                         trp_rtable_destroy_table);
-  tr_debug("trp_rtable_new: %p", new);
   return new;
 }
 
@@ -198,10 +240,9 @@ static GHashTable *trp_rtbl_get_or_add_table(GHashTable *tbl, TR_NAME *key, GDes
   if (val_tbl==NULL) {
     val_tbl=g_hash_table_new_full(trp_tr_name_hash,
                                   trp_tr_name_equal,
-                                  NULL, /* no need to free the key */
+                                  trp_rtable_destroy_tr_name,
                                   destroy);
-    tr_debug("tr_rtbl_get_or_add_table: %p", val_tbl, trp_rtable_destroy_table);
-    g_hash_table_insert(tbl, key, val_tbl);
+    g_hash_table_insert(tbl, tr_dup_name(key), val_tbl);
   }
   return val_tbl;
 }
@@ -213,9 +254,10 @@ void trp_rtable_add(TRP_RTABLE *rtbl, TRP_RENTRY *entry)
 
   apc_tbl=trp_rtbl_get_or_add_table(rtbl, entry->apc, trp_rtable_destroy_table);
   realm_tbl=trp_rtbl_get_or_add_table(apc_tbl, entry->realm, trp_rtable_destroy_rentry);
-  g_hash_table_insert(realm_tbl, entry->peer, entry); /* destroys and replaces a duplicate */
+  g_hash_table_insert(realm_tbl, tr_dup_name(entry->peer), entry); /* destroys and replaces a duplicate */
 }
 
+/* note: the entry pointer passed in is invalid after calling this because the entry is freed */
 void trp_rtable_remove(TRP_RTABLE *rtbl, TRP_RENTRY *entry)
 {
   GHashTable *apc_tbl=NULL;
@@ -224,98 +266,429 @@ void trp_rtable_remove(TRP_RTABLE *rtbl, TRP_RENTRY *entry)
   apc_tbl=g_hash_table_lookup(rtbl, entry->apc);
   if (apc_tbl==NULL)
     return;
+
   realm_tbl=g_hash_table_lookup(apc_tbl, entry->realm);
   if (realm_tbl==NULL)
     return;
+
+  /* remove the element */
   g_hash_table_remove(realm_tbl, entry->peer);
+  /* if that was the last entry in the realm, remove the realm table */
+  if (g_hash_table_size(realm_tbl)==0)
+    g_hash_table_remove(apc_tbl, entry->realm);
+  /* if that was the last realm in the apc, remove the apc table */
+  if (g_hash_table_size(apc_tbl)==0)
+    g_hash_table_remove(rtbl, entry->apc);
 }
 
-/* Get all entries in an apc. Returned as a talloc'ed array in the NULL
- * context. Caller should free these. */
-size_t trp_rtable_get_apc(TRP_RTABLE *rtbl, TR_NAME *apc, TRP_RENTRY **ret)
+/* gets the actual hash table, for internal use only */
+static GHashTable *trp_rtable_get_apc_table(TRP_RTABLE *rtbl, TR_NAME *apc)
 {
-  GHashTable *apc_tbl=NULL;
-  size_t len=0; /* length of return array */
-  size_t ii=0;
-  GList *realms=NULL;
-  GList *realm_entries=NULL;
-  GList *p1=NULL, *p2=NULL;
+  return g_hash_table_lookup(rtbl, apc);
+}
 
-  apc_tbl=g_hash_table_lookup(rtbl, apc);
+/* gets the actual hash table, for internal use only */
+static GHashTable *trp_rtable_get_realm_table(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm)
+{
+  GHashTable *apc_tbl=trp_rtable_get_apc_table(rtbl, apc);
   if (apc_tbl==NULL)
-    return 0;
+    return NULL;
+  else
+    return g_hash_table_lookup(apc_tbl, realm);
+}
 
-  realms=g_hash_table_get_values(apc_tbl);
-  /* make two passes: first count the entries, then allocate and populate the output array */
-  for (p1=realms; p1!=NULL; p1=g_list_next(p1))
-    len+=g_hash_table_size(p1->data);
-  if (len==0) {
-    g_list_free(realms);
+struct table_size_cookie {
+  TRP_RTABLE *rtbl;
+  size_t size;
+};
+static void trp_rtable_size_helper(gpointer key, gpointer value, gpointer user_data)
+{
+  struct table_size_cookie *data=(struct table_size_cookie *)user_data;
+  data->size += trp_rtable_apc_size(data->rtbl, (TR_NAME *)key);
+};
+size_t trp_rtable_size(TRP_RTABLE *rtbl)
+{
+  struct table_size_cookie data={rtbl, 0};
+  g_hash_table_foreach(rtbl, trp_rtable_size_helper, &data);
+  return data.size;
+}
+
+struct table_apc_size_cookie {
+  TR_NAME *apc;
+  TRP_RTABLE *rtbl;
+  size_t size;
+};
+static void table_apc_size_helper(gpointer key, gpointer value, gpointer user_data)
+{
+  struct table_apc_size_cookie *data=(struct table_apc_size_cookie *)user_data;
+  data->size += trp_rtable_realm_size(data->rtbl, data->apc, (TR_NAME *)key);
+}
+size_t trp_rtable_apc_size(TRP_RTABLE *rtbl, TR_NAME *apc)
+{
+  struct table_apc_size_cookie data={apc, rtbl, 0};
+  GHashTable *apc_tbl=trp_rtable_get_apc_table(rtbl, apc);
+  if (apc_tbl==NULL)
+    return 0;;
+  g_hash_table_foreach(apc_tbl, table_apc_size_helper, &data);
+  return data.size;
+}
+
+size_t trp_rtable_realm_size(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm)
+{
+  GHashTable *realm_tbl=trp_rtable_get_realm_table(rtbl, apc, realm);
+  if (realm_tbl==NULL)
     return 0;
+  else
+    return g_hash_table_size(g_hash_table_lookup(
+                               g_hash_table_lookup(rtbl, apc),
+                               realm));
+}
+
+/* Returns an array of pointers to TRP_RENTRY, length of array in n_out.
+ * Caller must free the array (in the talloc NULL context), but must
+ * not free its contents. */
+TRP_RENTRY **trp_rtable_get_entries(TRP_RTABLE *rtbl, size_t *n_out)
+{
+  TRP_RENTRY **ret=NULL;
+  TR_NAME **apc=NULL;
+  size_t n_apc=0;
+  TRP_RENTRY **apc_entries=NULL;
+  size_t n_entries=0;
+  size_t ii_ret=0;
+
+  *n_out=trp_rtable_size(rtbl);
+  if (*n_out==0)
+    return NULL;
+
+  ret=talloc_array(NULL, TRP_RENTRY *, *n_out);
+  if (ret==NULL) {
+    tr_crit("trp_rtable_get_entries: unable to allocate return array.");
+    *n_out=0;
+    return NULL;
   }
 
-  *ret=talloc_array(NULL, TRP_RENTRY, len);
-  if (*ret==NULL) {
-    tr_crit("trp_rtable_get_apc: could not allocate return array.");
-    g_list_free(realms);
-    return 0;
+  ii_ret=0; /* counts output entries */
+  apc=trp_rtable_get_apcs(rtbl, &n_apc);
+  while(n_apc--) {
+    apc_entries=trp_rtable_get_apc_entries(rtbl, apc[n_apc], &n_entries);
+    while (n_entries--)
+      ret[ii_ret++]=apc_entries[n_entries];
+    talloc_free(apc_entries);
   }
+  talloc_free(apc);
 
-  ii=0;
-  for (p1=realms; p1!=NULL; p1=g_list_next(p1)) {
-    realm_entries=g_hash_table_get_values(p1->data);
-    for (p2=realm_entries; p2!=NULL; p2=g_list_next(p2)) {
-      memcpy(*ret+ii, p2->data, sizeof(TRP_RENTRY));
-      ii++;
-    }
-    g_list_free(realm_entries);
+  if (ii_ret!=*n_out) {
+    tr_crit("trp_rtable_get_entries: found incorrect number of entries.");
+    talloc_free(ret);
+    *n_out=0;
+    return NULL;
   }
+  return ret;
+}
 
-  g_list_free(realms);
-  return len;
+/* Returns an array of pointers to TR_NAME, length of array in n_out.
+ * Caller must free the array (in the talloc NULL context). */
+TR_NAME **trp_rtable_get_apcs(TRP_RTABLE *rtbl, size_t *n_out)
+{
+  size_t len=g_hash_table_size(rtbl); /* known apcs are keys in top level hash table */
+  size_t ii=0;
+  GList *apcs=NULL;;
+  GList *p=NULL;
+  TR_NAME **ret=NULL;
+
+  if (len==0) {
+    *n_out=0;
+    return NULL;
+  }
+    
+  ret=talloc_array(NULL, TR_NAME *, len);
+  if (ret==NULL) {
+    tr_crit("trp_rtable_get_apcs: unable to allocate return array.");
+    *n_out=0;
+    return NULL;
+  }
+  apcs=g_hash_table_get_keys(rtbl);
+  for (ii=0,p=apcs; p!=NULL; ii++,p=g_list_next(p))
+    ret[ii]=(TR_NAME *)p->data;
+
+  g_list_free(apcs);
+
+  *n_out=len;
+  return ret;
 }
 
-/* Get all entries in an apc/realm. Returns as a talloc'ed array in
- * the NULL context via .  Caller must free these. */
-size_t trp_rtable_get_realm(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm, TRP_RENTRY **ret)
+/* Returns an array of pointers to TR_NAME, length of array in n_out.
+ * Caller must free the array (in the talloc NULL context). */
+TR_NAME **trp_rtable_get_apc_realms(TRP_RTABLE *rtbl, TR_NAME *apc, size_t *n_out)
 {
-  GHashTable *apc_tbl=NULL;
-  GHashTable *realm_tbl=NULL;
-  size_t len=0;
   size_t ii=0;
+  TRP_RTABLE *apc_tbl=g_hash_table_lookup(rtbl, apc);;
   GList *entries=NULL;
   GList *p=NULL;
+  TR_NAME **ret=NULL;
 
-  apc_tbl=g_hash_table_lookup(rtbl, apc);
-  if (apc_tbl==NULL)
-    return 0;
-  realm_tbl=g_hash_table_lookup(apc_tbl, realm);
-  if (realm_tbl==NULL)
-    return 0;
-  entries=g_hash_table_get_values(realm_tbl);
-  len=g_hash_table_size(realm_tbl);
-  *ret=talloc_array(NULL, TRP_RENTRY, len);
-  if (*ret==NULL) {
-    tr_crit("trp_rtable_get_realm: could not allocate return array.");
-    return 0;
+  if (apc_tbl==NULL) {
+    *n_out=0;
+    return NULL;
   }
+  *n_out=g_hash_table_size(apc_tbl); /* set output length */
+  ret=talloc_array(NULL, TR_NAME *, *n_out);
+  entries=g_hash_table_get_keys(apc_tbl);
   for (ii=0,p=entries; p!=NULL; ii++,p=g_list_next(p))
-    memcpy(*ret+ii, p->data, sizeof(TRP_RENTRY));
+    ret[ii]=(TR_NAME *)p->data;
+
   g_list_free(entries);
-  return len;
+  return ret;
+}
+
+/* Get all entries in an apc. Returns an array of pointers in NULL talloc context.
+ * Caller must free this list with talloc_free, but must not free the entries in the
+ * list.. */
+TRP_RENTRY **trp_rtable_get_apc_entries(TRP_RTABLE *rtbl, TR_NAME *apc, size_t *n_out)
+{
+  size_t ii=0, jj=0;
+  TR_NAME **realm=NULL;
+  size_t n_realms=0;
+  TRP_RENTRY **realm_entries=NULL;
+  size_t n_entries=0;
+  TRP_RENTRY **ret=NULL;
+  size_t ii_ret=0;
+
+  *n_out=trp_rtable_apc_size(rtbl, apc);
+  if (*n_out==0)
+    return NULL;
+
+  ret=talloc_array(NULL, TRP_RENTRY *, *n_out);
+  if (ret==NULL) {
+    tr_crit("trp_rtable_get_apc_entries: could not allocate return array.");
+    *n_out=0;
+    return NULL;
+  }
+  
+  ii_ret=0; /* counts entries in the output array */
+  realm=trp_rtable_get_apc_realms(rtbl, apc, &n_realms);
+  for (ii=0; ii<n_realms; ii++) {
+    realm_entries=trp_rtable_get_realm_entries(rtbl, apc, realm[ii], &n_entries);
+    for (jj=0; jj<n_entries; jj++)
+      ret[ii_ret++]=realm_entries[jj];
+    talloc_free(realm_entries);
+  }
+  talloc_free(realm);
+
+  if (ii_ret!=*n_out) {
+    tr_crit("trp_rtable_get_apc_entries: found incorrect number of entries.");
+    talloc_free(ret);
+    *n_out=0;
+    return NULL;
+  }
+
+  return ret;
+}
+
+/* Get all entries in an apc/realm. Returns an array of pointers in NULL talloc context.
+ * Caller must free this list with talloc_free, but must not free the entries in the
+ * list.. */
+TRP_RENTRY **trp_rtable_get_realm_entries(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm, size_t *n_out)
+{
+  size_t ii=0;
+  TRP_RENTRY **ret=NULL;
+  TR_NAME **peer=NULL;
+
+  peer=trp_rtable_get_apc_realm_peers(rtbl, apc, realm, n_out);
+  ret=talloc_array(NULL, TRP_RENTRY *, *n_out);
+  if (ret==NULL) {
+    tr_crit("trp_rtable_get_realm_entries: could not allocate return array.");
+    talloc_free(peer);
+    n_out=0;
+    return NULL;
+  }
+  for (ii=0; ii<*n_out; ii++)
+    ret[ii]=trp_rtable_get_entry(rtbl, apc, realm, peer[ii]);
+  talloc_free(peer);
+  return ret;
+}
+
+TR_NAME **trp_rtable_get_apc_realm_peers(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm, size_t *n_out)
+{
+  TR_NAME **ret=NULL;
+  GHashTable *realm_tbl=NULL;
+  GList *keys=NULL;
+  GList *p=NULL;
+  size_t ii=0;
+
+  *n_out=trp_rtable_realm_size(rtbl, apc, realm);
+  if (*n_out==0)
+    return NULL;
+  realm_tbl=trp_rtable_get_realm_table(rtbl, apc, realm);
+  ret=talloc_array(NULL, TR_NAME *, *n_out);
+  if (ret==NULL) {
+    tr_crit("trp_rtable_get_apc_realm_peers: could not allocate return array.");
+    *n_out=0;
+    return NULL;
+  }
+  keys=g_hash_table_get_keys(realm_tbl);
+  for (ii=0,p=keys; p!=NULL; ii++,p=g_list_next(p))
+    ret[ii]=(TR_NAME *)p->data;
+  g_list_free(keys);
+  return ret;
 }
 
-/* Gets a single entry, in the NULL talloc context. Caller must free. */
+/* Gets a single entry. Do not free it. */
 TRP_RENTRY *trp_rtable_get_entry(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm, TR_NAME *peer)
 {
-  GHashTable *apc_tbl=NULL;
   GHashTable *realm_tbl=NULL;
   
-  apc_tbl=g_hash_table_lookup(rtbl, apc);
-  if (apc_tbl==NULL)
-    return NULL;
-  realm_tbl=g_hash_table_lookup(apc_tbl, realm);
+  realm_tbl=trp_rtable_get_realm_table(rtbl, apc, realm);
   if (realm_tbl==NULL)
     return NULL;
-  return (TRP_RENTRY *)g_hash_table_lookup(realm_tbl, peer);
+  return g_hash_table_lookup(realm_tbl, peer); /* does not copy or increment ref count */
+}
+
+static char *timespec_to_str(struct timespec *ts)
+{
+  struct tm tm;
+  char *s=NULL;
+
+  if (localtime_r(&(ts->tv_sec), &tm)==NULL)
+    return NULL;
+
+  s=malloc(40); /* long enough to contain strftime result */
+  if (s==NULL)
+    return NULL;
+
+  if (strftime(s, 40, "%F %T", &tm)==0) {
+    free(s);
+    return NULL;
+  }
+  return s;
+}
+
+TRP_RENTRY *trp_rtable_get_selected_entry(TRP_RTABLE *rtbl, TR_NAME *apc, TR_NAME *realm)
+{
+  size_t n=0;
+  TRP_RENTRY **entry=trp_rtable_get_realm_entries(rtbl, apc, realm, &n);
+  TRP_RENTRY *selected=NULL;
+
+  if (n==0)
+    return NULL;
+
+  while(n-- && !trp_rentry_get_selected(entry[n])) { }
+  selected=entry[n];
+  talloc_free(entry);
+  return selected;
+}
+
+/* Pretty print a route table entry to a newly allocated string. If sep is NULL,
+ * returns comma+space separated string. */
+char *trp_rentry_to_str(TALLOC_CTX *mem_ctx, TRP_RENTRY *entry, const char *sep)
+{
+  char *apc=tr_name_strdup(entry->apc);
+  char *realm=tr_name_strdup(entry->realm);
+  char *peer=tr_name_strdup(entry->peer);
+  char *trust_router=tr_name_strdup(entry->trust_router);
+  char *next_hop=tr_name_strdup(entry->next_hop);
+  char *expiry=timespec_to_str(entry->expiry);
+  char *result=NULL;
+
+  if (sep==NULL)
+    sep=", ";
+
+  result=talloc_asprintf(mem_ctx,
+                         "%s%s%s%s%s%s%u%s%s%s%s%s%d%s%s",
+                         apc, sep,
+                         realm, sep,
+                         peer, sep,
+                         entry->metric, sep,
+                         trust_router, sep,
+                         next_hop, sep,
+                         entry->selected, sep,
+                         expiry);
+  free(apc);
+  free(realm);
+  free(peer);
+  free(trust_router);
+  free(next_hop);
+  free(expiry);
+  return result;
+}
+
+static int sort_tr_names_cmp(const void *a, const void *b)
+{
+  TR_NAME **n1=(TR_NAME **)a;
+  TR_NAME **n2=(TR_NAME **)b;
+  return tr_name_cmp(*n1, *n2);
+}
+
+static void sort_tr_names(TR_NAME **names, size_t n_names)
+{
+  qsort(names, n_names, sizeof(TR_NAME *), sort_tr_names_cmp);
+}
+
+char *trp_rtable_to_str(TALLOC_CTX *mem_ctx, TRP_RTABLE *rtbl, const char *sep, const char *lineterm)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_NAME **apcs=NULL;
+  size_t n_apcs=0;
+  TR_NAME **realms=NULL;
+  size_t n_realms=0;
+  TRP_RENTRY **entries=NULL;
+  size_t n_entries=0;
+  char **tbl_strings=NULL;
+  size_t ii_tbl=0; /* counts tbl_strings */
+  size_t tbl_size=0;
+  size_t len=0;
+  size_t ii=0, jj=0, kk=0;
+  char *p=NULL;
+  char *result=NULL;
+
+  if (lineterm==NULL)
+    lineterm="\n";
+
+  tbl_size=trp_rtable_size(rtbl);
+  if (tbl_size==0) {
+    result=talloc_strdup(mem_ctx, lineterm);
+    goto cleanup;
+  }
+
+  tbl_strings=talloc_array(tmp_ctx, char *, tbl_size);
+  if (tbl_strings==NULL) {
+    result=talloc_strdup(mem_ctx, "error");
+    goto cleanup;
+  }
+  
+  apcs=trp_rtable_get_apcs(rtbl, &n_apcs);
+  talloc_steal(tmp_ctx, apcs);
+  sort_tr_names(apcs, n_apcs);
+  ii_tbl=0;
+  len=0;
+  for (ii=0; ii<n_apcs; ii++) {
+    realms=trp_rtable_get_apc_realms(rtbl, apcs[ii], &n_realms);
+    talloc_steal(tmp_ctx, realms);
+    sort_tr_names(realms, n_realms);
+    for (jj=0; jj<n_realms; jj++) {
+      entries=trp_rtable_get_realm_entries(rtbl, apcs[ii], realms[jj], &n_entries);
+      talloc_steal(tmp_ctx, entries);
+      for (kk=0; kk<n_entries; kk++) {
+        tbl_strings[ii_tbl]=trp_rentry_to_str(tmp_ctx, entries[kk], sep);
+        len+=strlen(tbl_strings[ii_tbl]);
+        ii_tbl++;
+      }
+      talloc_free(entries);
+    }
+    talloc_free(realms);
+  }
+  talloc_free(apcs);
+
+  /* now combine all the strings */
+  len += tbl_size*strlen(lineterm); /* space for line terminations*/
+  len += 1; /* nul terminator */
+  result=(char *)talloc_size(tmp_ctx, len);
+  for (p=result,ii=0; ii < tbl_size; ii++) {
+    p+=sprintf(p, "%s%s", tbl_strings[ii], lineterm);
+  }
+  talloc_steal(mem_ctx, result);
+  
+cleanup:
+  talloc_free(tmp_ctx);
+  return result;
 }