1623b84bc41eb3aec5e885543067c3192e9a8c8d
[trust_router.git] / trp / trp_upd.c
1 /*
2  * Copyright (c) 2016, JANET(UK)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of JANET(UK) nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31  * OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  */
34
35 #include <jansson.h>
36 #include <talloc.h>
37
38 #include <trust_router/tr_name.h>
39 #include <trp_internal.h>
40 #include <tr_comm.h>
41 #include <tr_apc.h>
42 #include <tr_debug.h>
43
44
45 /* static prototypes */
46 static TRP_INFOREC_DATA *trp_inforec_route_new(TALLOC_CTX *mem_ctx);
47 static void trp_inforec_route_print(TRP_INFOREC_DATA *);
48 static TRP_INFOREC_DATA *trp_inforec_comm_new(TALLOC_CTX *mem_ctx);
49 static void trp_inforec_comm_print(TRP_INFOREC_DATA *);
50
51
52 struct trp_inforec_type_entry {
53   const char *name;
54   TRP_INFOREC_TYPE type;
55   TRP_INFOREC_DATA *(*allocate)(TALLOC_CTX *);
56   void (*print)(TRP_INFOREC_DATA *);
57 };
58 static struct trp_inforec_type_entry trp_inforec_type_table[] = {
59   { "route", TRP_INFOREC_TYPE_ROUTE, trp_inforec_route_new, trp_inforec_route_print },
60   { "comm", TRP_INFOREC_TYPE_COMMUNITY, trp_inforec_comm_new, trp_inforec_comm_print },
61   { NULL, TRP_INFOREC_TYPE_UNKNOWN, NULL, NULL } /* must be the last entry */
62 };
63
64
65 /* look up an entry in the trp_inforec_type_table */
66 static struct trp_inforec_type_entry *get_trp_inforec_type_entry(TRP_INFOREC_TYPE msgtype)
67 {
68   struct trp_inforec_type_entry *entry=trp_inforec_type_table;
69
70   while ((entry->type != TRP_INFOREC_TYPE_UNKNOWN)
71         && (entry->type != msgtype)) {
72     entry ++;
73   }
74   return entry;
75 }
76
77 /* translate strings to codes */
78 TRP_INFOREC_TYPE trp_inforec_type_from_string(const char *s)
79 {
80   struct trp_inforec_type_entry *entry=trp_inforec_type_table;
81
82   while ((entry->type != TRP_INFOREC_TYPE_UNKNOWN)
83         && (strcmp(s, entry->name)!=0)) {
84     entry++;
85   }
86   return entry->type;
87 }
88 /* translate codes to strings (do not need to be freed) 
89  * Returns NULL on an unknown code */
90 const char *trp_inforec_type_to_string(TRP_INFOREC_TYPE msgtype)
91 {
92   struct trp_inforec_type_entry *entry=get_trp_inforec_type_entry(msgtype);
93   return entry->name;
94 }
95
96 /* called by talloc when destroying an update message body */
97 static int trp_inforec_route_destructor(void *object)
98 {
99   TRP_INFOREC_ROUTE *body=talloc_get_type_abort(object, TRP_INFOREC_ROUTE);
100   
101   /* clean up TR_NAME data, which are not managed by talloc */
102   if (body->trust_router != NULL)
103     tr_free_name(body->trust_router);
104   if (body->next_hop != NULL)
105     tr_free_name(body->next_hop);
106   return 0;
107 }
108
109 static TRP_INFOREC_DATA *trp_inforec_route_new(TALLOC_CTX *mem_ctx)
110 {
111   TRP_INFOREC_DATA *new_data=talloc(mem_ctx, TRP_INFOREC_DATA);
112   TRP_INFOREC_ROUTE *new_rec=NULL;
113
114   if (new_data==NULL)
115     return NULL;
116
117   new_rec=talloc(new_data, TRP_INFOREC_ROUTE);
118   if (new_rec == NULL) {
119     talloc_free(new_data);
120     new_data=NULL;
121   } else {
122     new_rec->trust_router=NULL;
123     new_rec->next_hop=NULL;
124     new_rec->next_hop_port=0;
125     new_rec->metric=TRP_METRIC_INFINITY;
126     new_rec->interval=0;
127     talloc_set_destructor((void *)new_rec, trp_inforec_route_destructor);
128     new_data->route=new_rec;
129   }
130     
131   return new_data;
132 }
133
134
135 static int trp_inforec_comm_destructor(void *obj)
136 {
137   TRP_INFOREC_COMM *rec=talloc_get_type_abort(obj, TRP_INFOREC_COMM);
138   if (rec->owner_realm!=NULL)
139     tr_free_name(rec->owner_realm);
140   if (rec->owner_contact!=NULL)
141     tr_free_name(rec->owner_contact);
142   if (rec->provenance!=NULL)
143     json_decref(rec->provenance);
144   return 0;
145 }
146
147 static TRP_INFOREC_DATA *trp_inforec_comm_new(TALLOC_CTX *mem_ctx)
148 {
149   TRP_INFOREC_DATA *new_data=talloc(mem_ctx, TRP_INFOREC_DATA);
150   TRP_INFOREC_COMM *new_rec=NULL;
151
152   if (new_data==NULL)
153     return NULL;
154
155   new_rec=talloc(new_data, TRP_INFOREC_COMM);
156   if (new_rec==NULL) {
157     talloc_free(new_data);
158     new_data=NULL;
159   } else {
160     new_rec->comm_type=TR_COMM_UNKNOWN;
161     new_rec->role=TR_ROLE_UNKNOWN;
162     new_rec->apcs=NULL;
163     new_rec->owner_realm=NULL;
164     new_rec->owner_contact=NULL;
165     new_rec->provenance=NULL;
166     new_rec->interval=0;
167     talloc_set_destructor((void *)new_rec, trp_inforec_comm_destructor);
168     new_data->comm=new_rec;
169   }
170
171   return new_data;
172 }
173
174 TRP_INFOREC *trp_inforec_get_next(TRP_INFOREC *rec)
175 {
176   if (rec!=NULL)
177     return rec->next;
178   else
179     return NULL;
180 }
181
182 static TRP_INFOREC *trp_inforec_get_tail(TRP_INFOREC *rec)
183 {
184   while ((rec->next)!=NULL)
185     rec=trp_inforec_get_next(rec);
186   return rec;
187 }
188
189 void trp_inforec_set_next(TRP_INFOREC *rec, TRP_INFOREC *next_rec)
190 {
191   if (rec !=NULL)
192     rec->next=next_rec;
193 }
194
195 TRP_INFOREC_TYPE trp_inforec_get_type(TRP_INFOREC *rec)
196 {
197   if (rec!=NULL)
198     return rec->type;
199   else
200     return TRP_INFOREC_TYPE_UNKNOWN;
201 }
202
203 void trp_inforec_set_type(TRP_INFOREC *rec, TRP_INFOREC_TYPE type)
204 {
205   if (rec!=NULL)
206     rec->type=type;
207 }
208
209 TR_NAME *trp_inforec_get_trust_router(TRP_INFOREC *rec)
210 {
211   switch (rec->type) {
212   case TRP_INFOREC_TYPE_ROUTE:
213     if (rec->data->route!=NULL)
214       return rec->data->route->trust_router;
215     break;
216   default:
217     break;
218   }
219   return NULL;
220 }
221
222 TR_NAME *trp_inforec_dup_trust_router(TRP_INFOREC *rec)
223 {
224   return tr_dup_name(trp_inforec_get_trust_router(rec));
225 }
226
227 TRP_RC trp_inforec_set_trust_router(TRP_INFOREC *rec, TR_NAME *trust_router)
228 {
229   switch (rec->type) {
230   case TRP_INFOREC_TYPE_ROUTE:
231     if (rec->data->route!=NULL) {
232       rec->data->route->trust_router=trust_router;
233       return TRP_SUCCESS;
234     }
235     break;
236   default:
237     break;
238   }
239   return TRP_ERROR;
240 }
241
242 /* TODO: need to return hostname/port --jlr */
243 TR_NAME *trp_inforec_get_next_hop(TRP_INFOREC *rec)
244 {
245   switch (rec->type) {
246   case TRP_INFOREC_TYPE_ROUTE:
247     if (rec->data->route!=NULL)
248       return rec->data->route->next_hop;
249     break;
250   default:
251     break;
252   }
253   return NULL;
254 }
255
256 TR_NAME *trp_inforec_dup_next_hop(TRP_INFOREC *rec)
257 {
258   return tr_dup_name(trp_inforec_get_next_hop(rec));
259 }
260
261 TRP_RC trp_inforec_set_next_hop(TRP_INFOREC *rec, TR_NAME *next_hop)
262 {
263   switch (rec->type) {
264   case TRP_INFOREC_TYPE_ROUTE:
265     if (rec->data->route==NULL)
266       return TRP_ERROR;
267     rec->data->route->next_hop=next_hop;
268     break;
269   case TRP_INFOREC_TYPE_COMMUNITY:
270     /* next hop not used for community records */
271     break;
272   default:
273     break;
274   }
275   return TRP_SUCCESS;
276 }
277
278 unsigned int trp_inforec_get_metric(TRP_INFOREC *rec)
279 {
280   switch (rec->type) {
281   case TRP_INFOREC_TYPE_ROUTE:
282     if (rec->data->route!=NULL)
283       return rec->data->route->metric;
284     break;
285   default:
286     break;
287   }
288   return TRP_METRIC_INVALID;
289 }
290
291 TRP_RC trp_inforec_set_metric(TRP_INFOREC *rec, unsigned int metric)
292 {
293   switch (rec->type) {
294   case TRP_INFOREC_TYPE_ROUTE:
295     if (rec->data->route!=NULL) {
296       rec->data->route->metric=metric;
297       return TRP_SUCCESS;
298     }
299     break;
300   default:
301     break;
302   }
303   return TRP_ERROR;
304 }
305
306 unsigned int trp_inforec_get_interval(TRP_INFOREC *rec)
307 {
308   switch (rec->type) {
309   case TRP_INFOREC_TYPE_ROUTE:
310     if (rec->data->route!=NULL)
311       return rec->data->route->interval;
312     break;
313   case TRP_INFOREC_TYPE_COMMUNITY:
314     if (rec->data->comm!=NULL)
315       return rec->data->comm->interval;
316     break;
317   default:
318     break;
319   }
320   return TRP_INTERVAL_INVALID;
321 }
322
323 TRP_RC trp_inforec_set_interval(TRP_INFOREC *rec, unsigned int interval)
324 {
325   switch (rec->type) {
326   case TRP_INFOREC_TYPE_ROUTE:
327     if (rec->data->route!=NULL) {
328       rec->data->route->interval=interval;
329       return TRP_SUCCESS;
330     }
331     break;
332   case TRP_INFOREC_TYPE_COMMUNITY:
333     if (rec->data->comm!=NULL) {
334       rec->data->comm->interval=interval;
335       return TRP_SUCCESS;
336     }
337   default:
338     break;
339   }
340   return TRP_ERROR;
341 }
342
343 TR_COMM_TYPE trp_inforec_get_comm_type(TRP_INFOREC *rec)
344 {
345   switch (rec->type) {
346   case TRP_INFOREC_TYPE_COMMUNITY:
347     if (rec->data->comm!=NULL)
348       return rec->data->comm->comm_type;
349     break;
350   default:
351     break;
352   }
353   return TR_COMM_UNKNOWN;
354 }
355
356 TRP_RC trp_inforec_set_comm_type(TRP_INFOREC *rec, TR_COMM_TYPE type)
357 {
358   switch (rec->type) {
359   case TRP_INFOREC_TYPE_COMMUNITY:
360     if (rec->data->comm!=NULL) {
361       rec->data->comm->comm_type=type;
362       return TRP_SUCCESS;
363     }
364     break;
365   default:
366     break;
367   }
368   return TRP_ERROR;
369 }
370
371 TR_REALM_ROLE trp_inforec_get_role(TRP_INFOREC *rec)
372 {
373   switch (rec->type) {
374   case TRP_INFOREC_TYPE_COMMUNITY:
375     if (rec->data->comm!=NULL)
376       return rec->data->comm->role;
377     break;
378   default:
379     break;
380   }
381   return TR_ROLE_UNKNOWN;
382 }
383
384 TRP_RC trp_inforec_set_role(TRP_INFOREC *rec, TR_REALM_ROLE role)
385 {
386   switch (rec->type) {
387   case TRP_INFOREC_TYPE_COMMUNITY:
388     if (rec->data->comm!=NULL) {
389       rec->data->comm->role=role;
390       return TRP_SUCCESS;
391       break;
392     }
393   default:
394     break;
395   }
396   return TRP_ERROR;
397 }
398
399 TR_APC *trp_inforec_get_apcs(TRP_INFOREC *rec)
400 {
401   switch (rec->type) {
402   case TRP_INFOREC_TYPE_COMMUNITY:
403     if (rec->data->comm!=NULL)
404       return rec->data->comm->apcs;
405     break;
406   default:
407     break;
408   }
409   return NULL;
410 }
411
412 TRP_RC trp_inforec_set_apcs(TRP_INFOREC *rec, TR_APC *apcs)
413 {
414   switch (rec->type) {
415   case TRP_INFOREC_TYPE_COMMUNITY:
416     if (rec->data->comm!=NULL) {
417       rec->data->comm->apcs=apcs;
418       talloc_steal(rec, apcs);
419       return TRP_SUCCESS;
420     }
421     break;
422
423   default:
424     break;
425   }
426   return TRP_ERROR;
427 }
428
429 TR_NAME *trp_inforec_get_owner_realm(TRP_INFOREC *rec)
430 {
431   switch (rec->type) {
432   case TRP_INFOREC_TYPE_COMMUNITY:
433     if (rec->data->comm!=NULL)
434       return rec->data->comm->owner_realm;
435     break;
436   default:
437     break;
438   }
439   return NULL;
440 }
441
442 TRP_RC trp_inforec_set_owner_realm(TRP_INFOREC *rec, TR_NAME *name)
443 {
444   switch (rec->type) {
445   case TRP_INFOREC_TYPE_COMMUNITY:
446     if (rec->data->comm!=NULL) {
447       rec->data->comm->owner_realm=name;
448       return TRP_SUCCESS;
449   default:
450     break;
451     }
452     break;
453   }
454   return TRP_ERROR;
455 }
456
457 TR_NAME *trp_inforec_get_owner_contact(TRP_INFOREC *rec)
458 {
459   switch (rec->type) {
460   case TRP_INFOREC_TYPE_COMMUNITY:
461     if (rec->data->comm!=NULL)
462       return rec->data->comm->owner_contact;
463     break;
464   default:
465     break;
466   }
467   return NULL;
468 }
469
470 TRP_RC trp_inforec_set_owner_contact(TRP_INFOREC *rec, TR_NAME *name)
471 {
472   switch (rec->type) {
473   case TRP_INFOREC_TYPE_COMMUNITY:
474     if (rec->data->comm!=NULL) {
475       rec->data->comm->owner_contact=name;
476       return TRP_SUCCESS;
477     }
478     break;
479   default:
480     break;
481   }
482   return TRP_ERROR;
483 }
484
485 /* caller needs to incref the output if they're going to hang on to it */
486 json_t *trp_inforec_get_provenance(TRP_INFOREC *rec)
487 {
488   switch (rec->type) {
489   case TRP_INFOREC_TYPE_COMMUNITY:
490     if (rec->data->comm!=NULL)
491       return rec->data->comm->provenance;
492     break;
493   default:
494     break;
495   }
496   return NULL;
497 }
498
499 /* increments the reference count */
500 TRP_RC trp_inforec_set_provenance(TRP_INFOREC *rec, json_t *prov)
501 {
502   switch (rec->type) {
503   case TRP_INFOREC_TYPE_COMMUNITY:
504     if (rec->data->comm!=NULL) {
505       if (rec->data->comm->provenance!=NULL)
506         json_decref(rec->data->comm->provenance);
507       rec->data->comm->provenance=prov;
508       json_incref(prov);
509       return TRP_SUCCESS;
510     }
511     break;
512   default:
513     break;
514   }
515   return TRP_ERROR;
516 }
517
518 static TRP_RC trp_inforec_add_to_provenance(TRP_INFOREC *rec, TR_NAME *peer)
519 {
520   json_t *jpeer=NULL;
521
522   switch (rec->type) {
523   case TRP_INFOREC_TYPE_ROUTE:
524     /* no provenance list */
525     break;
526   case TRP_INFOREC_TYPE_COMMUNITY:
527     jpeer=tr_name_to_json_string(peer);
528     if (jpeer==NULL)
529       return TRP_ERROR;
530     if (rec->data->comm->provenance==NULL) {
531       rec->data->comm->provenance=json_array();
532       if (rec->data->comm->provenance==NULL) {
533         json_decref(jpeer);
534         return TRP_ERROR;
535       }
536     }
537     if (0!=json_array_append_new(rec->data->comm->provenance, jpeer)) {
538       json_decref(jpeer);
539       return TRP_ERROR;
540     }
541     break;
542   default:
543     break;
544   }
545   return TRP_SUCCESS;
546 }
547
548 /* generic record type */
549 TRP_INFOREC *trp_inforec_new(TALLOC_CTX *mem_ctx, TRP_INFOREC_TYPE type)
550 {
551   TRP_INFOREC *new_rec=talloc(mem_ctx, TRP_INFOREC);
552   TRP_INFOREC_DATA *data=NULL;
553   struct trp_inforec_type_entry *dtype=get_trp_inforec_type_entry(type);
554
555   if ((new_rec != NULL) && (dtype->type != TRP_INFOREC_TYPE_UNKNOWN)) {
556     trp_inforec_set_type(new_rec, type);
557     trp_inforec_set_next(new_rec, NULL);
558     if (dtype->allocate!=NULL) {
559       data=dtype->allocate(new_rec);
560       if (data!=NULL)
561         new_rec->data=data;
562       else {
563         talloc_free(new_rec);
564         return NULL;
565       }
566     }
567   }
568   return new_rec;
569 }
570
571 void trp_inforec_free(TRP_INFOREC *rec)
572 {
573   if (rec!=NULL)
574     talloc_free(rec);
575 }
576
577 static int trp_upd_destructor(void *object)
578 {
579   TRP_UPD *upd=talloc_get_type_abort(object, TRP_UPD);
580   if (upd->realm!=NULL)
581     tr_free_name(upd->realm);
582   if (upd->comm!=NULL)
583     tr_free_name(upd->comm);
584   if (upd->peer!=NULL)
585     tr_free_name(upd->peer);
586   return 0;
587 }
588
589 TRP_UPD *trp_upd_new(TALLOC_CTX *mem_ctx)
590 {
591   TRP_UPD *new_body=talloc(mem_ctx, TRP_UPD);
592
593   if (new_body!=NULL) {
594     new_body->realm=NULL;
595     new_body->comm=NULL;
596     new_body->records=NULL;
597     new_body->peer=NULL;
598     talloc_set_destructor((void *)new_body, trp_upd_destructor);
599   }
600   return new_body;
601 }
602
603 void trp_upd_free(TRP_UPD *update)
604 {
605   if (update!=NULL)
606     talloc_free(update);
607 }
608
609 TRP_INFOREC *trp_upd_get_inforec(TRP_UPD *upd)
610 {
611   if (upd!=NULL)
612     return upd->records;
613   else
614     return NULL;
615 }
616
617 void trp_upd_set_inforec(TRP_UPD *upd, TRP_INFOREC *rec)
618 {
619   if (upd!=NULL)
620     upd->records=rec;
621 }
622
623 void trp_upd_add_inforec(TRP_UPD *upd, TRP_INFOREC *rec)
624 {
625   tr_debug("trp_upd_add_inforec: adding record.");
626   if (upd->records==NULL)
627     upd->records=rec;
628   else
629     trp_inforec_set_next(trp_inforec_get_tail(upd->records), rec);
630   talloc_steal(upd, rec);
631 }
632
633 TR_NAME *trp_upd_get_realm(TRP_UPD *upd)
634 {
635   return upd->realm;
636 }
637
638 TR_NAME *trp_upd_dup_realm(TRP_UPD *upd)
639 {
640   return tr_dup_name(upd->realm);
641 }
642
643 void trp_upd_set_realm(TRP_UPD *upd, TR_NAME *realm)
644 {
645   if (upd->realm!=NULL)
646     tr_free_name(upd->realm);
647   upd->realm=realm;
648 }
649
650 TR_NAME *trp_upd_get_comm(TRP_UPD *upd)
651 {
652   return upd->comm;
653 }
654
655 TR_NAME *trp_upd_dup_comm(TRP_UPD *upd)
656 {
657   return tr_dup_name(upd->comm);
658 }
659
660 void trp_upd_set_comm(TRP_UPD *upd, TR_NAME *comm)
661 {
662   if (upd->comm!=NULL)
663     tr_free_name(upd->comm);
664   upd->comm=comm;
665 }
666
667 TR_NAME *trp_upd_get_peer(TRP_UPD *upd)
668 {
669   return upd->peer;
670 }
671
672 TR_NAME *trp_upd_dup_peer(TRP_UPD *upd)
673 {
674   return tr_dup_name(upd->peer);
675 }
676
677 void trp_upd_set_peer(TRP_UPD *upd, TR_NAME *peer)
678 {
679   TRP_INFOREC *rec=NULL;
680   TR_NAME *cpy=NULL;
681
682   upd->peer=peer;
683
684   /* add to provenance list of any records that need it */
685   for (rec=trp_upd_get_inforec(upd); rec!=NULL; rec=trp_inforec_get_next(rec)) {
686     if (trp_inforec_add_to_provenance(rec, cpy=tr_dup_name(peer)) != TRP_SUCCESS) {
687       tr_err("trp_upd_set_peer: error adding peer to provenance list.");
688       tr_free_name(cpy);
689     }
690   }
691 }
692
693 void trp_upd_set_next_hop(TRP_UPD *upd, const char *hostname, unsigned int port)
694 {
695   TRP_INFOREC *rec=NULL;
696   TR_NAME *cpy=NULL;
697   
698   for (rec=trp_upd_get_inforec(upd); rec!=NULL; rec=trp_inforec_get_next(rec)) {
699     if (trp_inforec_set_next_hop(rec, cpy=tr_new_name(hostname)) != TRP_SUCCESS) {
700       tr_err("trp_upd_set_next_hop: error setting next hop.");
701       tr_free_name(cpy);
702     }
703   }
704 }
705
706 /* pretty print */
707 static void trp_inforec_route_print(TRP_INFOREC_DATA *data)
708 {
709   if (data->route!=NULL) {
710     printf("     trust_router=%.*s\n     metric=%d\n     interval=%d]\n",
711            data->route->trust_router->len, data->route->trust_router->buf,
712            data->route->metric, data->route->interval);
713   }
714 }
715
716 static void trp_inforec_comm_print(TRP_INFOREC_DATA *data)
717 {
718   if (data->comm!=NULL) {
719     printf("     type=%s\n     role=%s\n     owner=%.*s\n     contact=%.*s]\n",
720            tr_comm_type_to_str(data->comm->comm_type),
721            tr_realm_role_to_str(data->comm->role),
722            data->comm->owner_realm->len, data->comm->owner_realm->buf,
723            data->comm->owner_contact->len, data->comm->owner_contact->buf);
724     /* TODO: print apcs */
725   }
726 }