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