Merge pull request #53 from painless-security/jennifer/show_routes
[trust_router.git] / trp / trps.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 <fcntl.h>
36 #include <talloc.h>
37 #include <errno.h>
38 #include <unistd.h>
39 #include <sys/time.h>
40 #include <glib.h>
41 #include <string.h>
42 #include <poll.h> // for nfds_t
43
44 #include <gsscon.h>
45 #include <tr_comm.h>
46 #include <tr_apc.h>
47 #include <tr_rp.h>
48 #include <tr_name_internal.h>
49 #include <trp_route.h>
50 #include <trp_internal.h>
51 #include <tr_gss_names.h>
52 #include <trp_peer.h>
53 #include <trp_ptable.h>
54 #include <trp_rtable.h>
55 #include <tr_debug.h>
56 #include <tr_util.h>
57 #include <tr_socket.h>
58
59 static int trps_destructor(void *object)
60 {
61   TRPS_INSTANCE *trps=talloc_get_type_abort(object, TRPS_INSTANCE);
62   if (trps->rtable!=NULL)
63     trp_rtable_free(trps->rtable);
64   return 0;
65 }
66
67 TRPS_INSTANCE *trps_new (TALLOC_CTX *mem_ctx)
68 {
69   TRPS_INSTANCE *trps=talloc(mem_ctx, TRPS_INSTANCE);
70   if (trps!=NULL)  {
71     trps->hostname=NULL;
72     trps->port=0;
73     trps->cookie=NULL;
74     trps->conn=NULL;
75     trps->trpc=NULL;
76     trps->update_interval=(struct timeval){0,0};
77     trps->sweep_interval=(struct timeval){0,0};
78     trps->ptable=NULL;
79
80     trps->mq=tr_mq_new(trps);
81     if (trps->mq==NULL) {
82       /* failed to allocate mq */
83       talloc_free(trps);
84       return NULL;
85     }
86
87     trps->rtable=NULL;
88     if (trps_init_rtable(trps) != TRP_SUCCESS) {
89       /* failed to allocate rtable */
90       talloc_free(trps);
91       return NULL;
92     }
93
94     talloc_set_destructor((void *)trps, trps_destructor);
95   }
96   return trps;
97 }
98
99 /* create a new route table, first discarding an old one if necessary */
100 TRP_RC trps_init_rtable(TRPS_INSTANCE *trps)
101 {
102   if (trps->rtable != NULL) {
103     trp_rtable_free(trps->rtable);
104     trps->rtable=NULL;
105   }
106
107   trps->rtable=trp_rtable_new();
108   if (trps->rtable==NULL) {
109     return TRP_NOMEM;
110   }
111   return TRP_SUCCESS;
112 }
113
114 void trps_clear_rtable(TRPS_INSTANCE *trps)
115 {
116   trp_rtable_clear(trps->rtable);
117 }
118
119 void trps_free (TRPS_INSTANCE *trps)
120 {
121   if (trps!=NULL)
122     talloc_free(trps);
123 }
124
125 TR_MQ_MSG *trps_mq_pop(TRPS_INSTANCE *trps)
126 {
127   return tr_mq_pop(trps->mq, 0);
128 }
129
130 void trps_mq_add(TRPS_INSTANCE *trps, TR_MQ_MSG *msg)
131 {
132   tr_mq_add(trps->mq, msg);
133 }
134
135 unsigned int trps_get_connect_interval(TRPS_INSTANCE *trps)
136 {
137   return trps->connect_interval.tv_sec;
138 }
139
140 void trps_set_connect_interval(TRPS_INSTANCE *trps, unsigned int interval)
141 {
142   trps->connect_interval.tv_sec=interval;
143   trps->connect_interval.tv_usec=0;
144 }
145
146 unsigned int trps_get_update_interval(TRPS_INSTANCE *trps)
147 {
148   return trps->update_interval.tv_sec;
149 }
150
151 void trps_set_update_interval(TRPS_INSTANCE *trps, unsigned int interval)
152 {
153   trps->update_interval.tv_sec=interval;
154   trps->update_interval.tv_usec=0;
155 }
156
157 unsigned int trps_get_sweep_interval(TRPS_INSTANCE *trps)
158 {
159   return trps->sweep_interval.tv_sec;
160 }
161
162 void trps_set_sweep_interval(TRPS_INSTANCE *trps, unsigned int interval)
163 {
164   trps->sweep_interval.tv_sec=interval;
165   trps->sweep_interval.tv_usec=0;
166 }
167
168 void trps_set_ctable(TRPS_INSTANCE *trps, TR_COMM_TABLE *comm)
169 {
170   trps->ctable=comm;
171 }
172
173 void trps_set_ptable(TRPS_INSTANCE *trps, TRP_PTABLE *ptable)
174 {
175   if (trps->ptable!=NULL)
176     trp_ptable_free(trps->ptable);
177   trps->ptable=ptable;
178 }
179
180 void trps_set_peer_status_callback(TRPS_INSTANCE *trps, void (*cb)(TRP_PEER *, void *), void *cookie)
181 {
182   TRP_PTABLE_ITER *iter=NULL;
183   TRP_PEER *peer=NULL;
184   if (trps->ptable==NULL)
185     return;
186
187   iter=trp_ptable_iter_new(NULL);
188   for (peer=trp_ptable_iter_first(iter, trps->ptable); peer!=NULL; peer=trp_ptable_iter_next(iter))
189     trp_peer_set_conn_status_cb(peer, cb, cookie);
190   trp_ptable_iter_free(iter);
191 }
192
193 /* Get the label peers will know us by - needs to match trp_peer_get_label() output.
194  * There is no get, only dup, because we don't store the label except when requested. */
195 TR_NAME *trps_dup_label(TRPS_INSTANCE *trps)
196 {
197   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
198   TR_NAME *label=NULL;
199   char *s=talloc_asprintf(tmp_ctx, "%s:%u", trps->hostname, trps->port);
200   if (s==NULL)
201     goto cleanup;
202   label=tr_new_name(s);
203
204 cleanup:
205   talloc_free(tmp_ctx);
206   return label;
207 }
208
209 TRPC_INSTANCE *trps_find_trpc(TRPS_INSTANCE *trps, TRP_PEER *peer)
210 {
211   TRPC_INSTANCE *cur=NULL;
212   TR_NAME *name=NULL;
213   TR_NAME *peer_servicename=trp_peer_get_servicename(peer);
214
215   for (cur=trps->trpc; cur!=NULL; cur=trpc_get_next(cur)) {
216     name=trpc_get_gssname(cur);
217     if ((name!=NULL) && (0==tr_name_cmp(peer_servicename, name))) {
218       break;
219     }
220   }
221   return cur;
222 }
223
224 void trps_add_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *new)
225 {
226   if (trps->conn==NULL)
227     trps->conn=new;
228   else
229     trp_connection_append(trps->conn, new);
230
231   talloc_steal(trps, new);
232 }
233
234 /* ok to call more than once; guarantees connection no longer in the list.
235  * Caller is responsible for freeing the removed element afterwards.  */
236 void trps_remove_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *remove)
237 {
238   trps->conn=trp_connection_remove(trps->conn, remove);
239 }
240
241 void trps_add_trpc(TRPS_INSTANCE *trps, TRPC_INSTANCE *trpc)
242 {
243   if (trps->trpc==NULL)
244     trps->trpc=trpc;
245   else
246     trpc_append(trps->trpc, trpc);
247
248   talloc_steal(trps, trpc);
249 }
250
251 /* ok to call more than once; guarantees trpc no longer in the list.
252  * Caller is responsible for freeing the removed element afterwards.  */
253 void trps_remove_trpc(TRPS_INSTANCE *trps, TRPC_INSTANCE *remove)
254 {
255   trps->trpc=trpc_remove(trps->trpc, remove);
256 }
257
258 TRP_RC trps_send_msg(TRPS_INSTANCE *trps, TRP_PEER *peer, const char *msg)
259 {
260   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
261   TR_MQ_MSG *mq_msg=NULL;
262   char *msg_dup=NULL;
263   TRP_RC rc=TRP_ERROR;
264   TRPC_INSTANCE *trpc=NULL;
265
266   /* get the connection for this peer */
267   trpc=trps_find_trpc(trps, peer);
268   /* instead, let's let that happen and then clear the queue when an attempt to
269    * connect fails */
270   if (trpc==NULL) {
271     tr_warning("trps_send_msg: skipping message queued for missing TRP client entry.");
272   } else {
273     mq_msg=tr_mq_msg_new(tmp_ctx, TR_MQMSG_TRPC_SEND, TR_MQ_PRIO_NORMAL);
274     msg_dup=talloc_strdup(mq_msg, msg); /* get local copy in mq_msg context */
275     tr_mq_msg_set_payload(mq_msg, msg_dup, NULL); /* no need for a free() func */
276     trpc_mq_add(trpc, mq_msg);
277     rc=TRP_SUCCESS;
278   }
279   talloc_free(tmp_ctx);
280   return rc;
281 }
282
283 /* get the currently selected route if available */
284 TRP_ROUTE *trps_get_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer)
285 {
286   return trp_rtable_get_entry(trps->rtable, comm, realm, peer);
287 }
288
289 TRP_ROUTE *trps_get_selected_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm)
290 {
291   tr_debug("trps_get_selected_route: entered. trps=%p, comm=%p, realm=%p", trps, comm, realm);
292   return trp_rtable_get_selected_entry(trps->rtable, comm, realm);
293 }
294
295 /* copy the result if you want to keep it */
296 TR_NAME *trps_get_next_hop(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm)
297 {
298   TRP_ROUTE *route=trps_get_selected_route(trps, comm, realm);
299   if (route==NULL)
300     return NULL;
301
302   return trp_route_get_next_hop(route);
303 }
304
305
306 /* mark a route as retracted */
307 static void trps_retract_route(TRPS_INSTANCE *trps, TRP_ROUTE *entry)
308 {
309   trp_route_set_metric(entry, TRP_METRIC_INFINITY);
310   trp_route_set_triggered(entry, 1);
311 }
312
313 /* is this route retracted? */
314 static int trps_route_retracted(TRPS_INSTANCE *trps, TRP_ROUTE *entry)
315 {
316   return (trp_metric_is_infinite(trp_route_get_metric(entry)));
317 }
318
319 static TRP_RC trps_read_message(TRPS_INSTANCE *trps, TRP_CONNECTION *conn, TR_MSG **msg)
320 {
321   int err=0;
322   char *buf=NULL;
323   size_t buflen = 0;
324   TRP_PEER *peer=NULL; /* entry in the peer table */
325   TR_NAME *conn_peer=NULL; /* name from the TRP_CONN, which comes from the gss context */
326
327   tr_debug("trps_read_message: started");
328   if (err = gsscon_read_encrypted_token(trp_connection_get_fd(conn),
329                                        *(trp_connection_get_gssctx(conn)), 
330                                        &buf,
331                                        &buflen)) {
332     tr_debug("trps_read_message: error");
333     if (buf)
334       free(buf);
335     return TRP_ERROR;
336   }
337
338   tr_debug("trps_read_message: message received, %u bytes.", (unsigned) buflen);
339   tr_debug("trps_read_message: %.*s", buflen, buf);
340
341   *msg= tr_msg_decode(NULL, buf, buflen);
342   free(buf);
343   if (*msg==NULL)
344     return TRP_NOPARSE;
345
346   conn_peer=trp_connection_get_peer(conn);
347   if (conn_peer==NULL) {
348     tr_err("trps_read_message: connection has no peer name");
349     return TRP_ERROR;
350   }
351
352   peer=trps_get_peer_by_gssname(trps, conn_peer);
353   if (peer==NULL) {
354     tr_err("trps_read_message: could not find peer with gssname=%s", trp_connection_get_gssname(conn));
355     return TRP_ERROR;
356   }
357
358   /* verify we received a message we support, otherwise drop it now */
359   switch (tr_msg_get_msg_type(*msg)) {
360   case TRP_UPDATE:
361     trp_upd_set_peer(tr_msg_get_trp_upd(*msg), tr_dup_name(conn_peer));
362     trp_upd_set_next_hop(tr_msg_get_trp_upd(*msg), trp_peer_get_server(peer), 0); /* TODO: 0 should be the configured TID port */
363     /* update provenance if necessary */
364     trp_upd_add_to_provenance(tr_msg_get_trp_upd(*msg), trp_peer_get_label(peer));
365     break;
366
367   case TRP_REQUEST:
368     trp_req_set_peer(tr_msg_get_trp_req(*msg), tr_dup_name(conn_peer));
369     break;
370
371   default:
372     tr_debug("trps_read_message: received unsupported message from %.*s", conn_peer->len, conn_peer->buf);
373     tr_msg_free_decoded(*msg);
374     *msg=NULL;
375     return TRP_UNSUPPORTED;
376   }
377   
378   return TRP_SUCCESS;
379 }
380
381 int trps_get_listener(TRPS_INSTANCE *trps,
382                       TRPS_MSG_FUNC msg_handler,
383                       TRP_AUTH_FUNC auth_handler,
384                       const char *hostname,
385                       unsigned int port,
386                       void *cookie,
387                       int *fd_out,
388                       size_t max_fd)
389 {
390   nfds_t n_fd=0;
391   nfds_t ii=0;
392
393   n_fd = tr_sock_listen_all(port, fd_out, max_fd);
394
395   if (n_fd == 0)
396     tr_err("trps_get_listener: Error opening port %d.");
397   else {
398     /* opening port succeeded */
399     tr_info("trps_get_listener: Opened port %d.", port);
400     
401     /* make the sockets non-blocking */
402     for (ii=0; ii<n_fd; ii++) {
403       if (0 != fcntl(fd_out[ii], F_SETFL, O_NONBLOCK)) {
404         tr_err("trps_get_listener: Error setting O_NONBLOCK.");
405         for (ii=0; ii<n_fd; ii++) {
406           close(fd_out[ii]);
407           fd_out[ii]=-1;
408         }
409         n_fd = 0;
410         break;
411       }
412     }
413   }
414
415   if (n_fd > 0) {
416     /* store the caller's request handler & cookie */
417     trps->msg_handler = msg_handler;
418     trps->auth_handler = auth_handler;
419     trps->hostname = talloc_strdup(trps, hostname);
420     trps->port = port;
421     trps->cookie = cookie;
422   }
423
424   return (int) n_fd;
425 }
426
427 TRP_RC trps_authorize_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *conn)
428 {
429   /* try to establish a GSS context */
430   if (0!=trp_connection_auth(conn, trps->auth_handler, trps->cookie)) {
431     tr_notice("trps_authorize_connection: failed to authorize connection");
432     trp_connection_close(conn);
433     return TRP_ERROR;
434   }
435   tr_notice("trps_authorize_connection: authorized connection");
436   return TRP_SUCCESS;
437 }
438
439 void trps_handle_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *conn)
440 {
441   TR_MSG *msg=NULL;
442   TRP_RC rc=TRP_ERROR;
443
444   /* loop as long as the connection exists */
445   while (trp_connection_get_status(conn)==TRP_CONNECTION_UP) {
446     rc=trps_read_message(trps, conn, &msg);
447     switch(rc) {
448     case TRP_SUCCESS:
449       trps->msg_handler(trps, conn, msg); /* send the TR_MSG off to the callback */
450       break;
451
452     case TRP_ERROR:
453       trp_connection_close(conn);
454       break;
455
456     default:
457       tr_debug("trps_handle_connection: trps_read_message failed (%d)", rc);
458     }
459   }
460
461   tr_debug("trps_handle_connection: connection closed.");
462 }
463
464 /* TODO: check realm/comm, now part of the update instead of inforec */
465 static TRP_RC trps_validate_update(TRPS_INSTANCE *trps, TRP_UPD *upd)
466 {
467   if (upd==NULL) {
468     tr_notice("trps_validate_update: null TRP update.");
469     return TRP_BADARG;
470   }
471
472   if (trp_upd_get_realm(upd)==NULL) {
473     tr_notice("trps_validate_update: received TRP update without realm.");
474     return TRP_ERROR;
475   }
476
477   if (trp_upd_get_comm(upd)==NULL) {
478     tr_notice("trps_validate_update: received TRP update without community.");
479     return TRP_ERROR;
480   }
481
482   if (trp_upd_get_inforec(upd)==NULL) {
483     tr_notice("trps_validate_update: received TRP update with no info records.");
484     return TRP_ERROR;
485   }
486
487   if (trp_upd_get_peer(upd)==NULL) {
488     tr_notice("trps_validate_update: received TRP update without origin peer information.");
489     return TRP_ERROR;
490   }
491
492   
493   return TRP_SUCCESS;
494 }
495
496 /* ensure that the update could be accepted if feasible */
497 static TRP_RC trps_validate_inforec(TRPS_INSTANCE *trps, TRP_INFOREC *rec)
498 {
499   switch(trp_inforec_get_type(rec)) {
500   case TRP_INFOREC_TYPE_ROUTE:
501     if ((trp_inforec_get_trust_router(rec)==NULL)
502        || (trp_inforec_get_next_hop(rec)==NULL)) {
503       tr_debug("trps_validate_inforec: missing record info.");
504       return TRP_ERROR;
505     }
506
507     /* check for valid metric */
508     if (trp_metric_is_invalid(trp_inforec_get_metric(rec))) {
509       tr_debug("trps_validate_inforec: invalid metric (%u).", trp_inforec_get_metric(rec));
510       return TRP_ERROR;
511     }
512
513     /* check for valid interval */
514     if (trp_inforec_get_interval(rec)==TRP_INTERVAL_INVALID) {
515       tr_debug("trps_validate_inforec: invalid interval.");
516       return TRP_ERROR;
517     }
518     break;
519
520   case TRP_INFOREC_TYPE_COMMUNITY:
521     /* TODO: validate community updates */
522     break;
523     
524   default:
525     tr_notice("trps_validate_inforec: unsupported record type.");
526     return TRP_UNSUPPORTED;
527   }
528
529   return TRP_SUCCESS;
530 }
531
532 /* link cost to a peer */
533 static unsigned int trps_cost(TRPS_INSTANCE *trps, TR_NAME *peer)
534 {
535   return 1;
536 }
537
538 static unsigned int trps_advertised_metric(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer)
539 {
540   TRP_ROUTE *entry=trp_rtable_get_entry(trps->rtable, comm, realm, peer);
541   if (entry==NULL)
542     return TRP_METRIC_INFINITY;
543   return trp_route_get_metric(entry) + trps_cost(trps, peer);
544 }
545
546 static int trps_check_feasibility(TRPS_INSTANCE *trps, TR_NAME *realm, TR_NAME *comm, TRP_INFOREC *rec)
547 {
548   unsigned int rec_metric=trp_inforec_get_metric(rec);
549   unsigned int new_metric=0;
550   unsigned int current_metric=0;
551   TR_NAME *next_hop=NULL;
552
553   /* we check these in the validation stage, but just in case... */
554   if (trp_metric_is_invalid(rec_metric))
555     return 0;
556
557   /* retractions (aka infinite metrics) are always feasible */
558   if (trp_metric_is_infinite(rec_metric))
559     return 1;
560
561   /* updates from our current next hop are always feasible*/
562   next_hop=trps_get_next_hop(trps, comm, realm);
563   if ((next_hop!=NULL)
564      && (0==tr_name_cmp(next_hop,trp_inforec_get_next_hop(rec)))) {
565     return 1;
566   }
567     
568
569   /* compare the existing metric we advertise to what we would advertise
570    * if we accept this update */
571   current_metric=trps_advertised_metric(trps, comm, realm, trp_inforec_get_next_hop(rec));
572   new_metric=rec_metric + trps_cost(trps, trp_inforec_get_next_hop(rec));
573   if (new_metric <= current_metric)
574     return 1;
575   else
576     return 0;
577 }
578
579 /* uses memory pointed to by *ts, also returns that value. On error, its contents are {0,0} */
580 static struct timespec *trps_compute_expiry(TRPS_INSTANCE *trps, unsigned int interval, struct timespec *ts)
581 {
582   const unsigned int small_factor=3; /* how many intervals we wait before expiring */
583   if (0!=clock_gettime(TRP_CLOCK, ts)) {
584     tr_err("trps_compute_expiry: could not read realtime clock.");
585     ts->tv_sec=0;
586     ts->tv_nsec=0;
587   }
588   tr_debug("trps_compute_expiry: tv_sec=%u, interval=%u, small_factor*interval=%u", ts->tv_sec, interval, small_factor*interval);
589   ts->tv_sec += small_factor*interval;
590   return ts;
591 }
592
593 static TRP_RC trps_accept_update(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC *rec)
594 {
595   TRP_ROUTE *entry=NULL;
596
597   entry=trp_rtable_get_entry(trps->rtable,
598                              trp_upd_get_comm(upd),
599                              trp_upd_get_realm(upd),
600                              trp_inforec_get_next_hop(rec));
601   if (entry==NULL) {
602     entry=trp_route_new(NULL);
603     if (entry==NULL) {
604       tr_err("trps_accept_update: unable to allocate new entry.");
605       return TRP_NOMEM;
606     }
607
608     trp_route_set_comm(entry, trp_upd_dup_comm(upd));
609     trp_route_set_realm(entry, trp_upd_dup_realm(upd));
610     trp_route_set_peer(entry, trp_upd_dup_peer(upd));
611     trp_route_set_trust_router(entry, trp_inforec_dup_trust_router(rec));
612     trp_route_set_next_hop(entry, trp_inforec_dup_next_hop(rec));
613     /* TODO: pass next hop port (now defaults to TID_PORT) --jlr */
614     if ((trp_route_get_comm(entry)==NULL)
615        ||(trp_route_get_realm(entry)==NULL)
616        ||(trp_route_get_peer(entry)==NULL)
617        ||(trp_route_get_trust_router(entry)==NULL)
618        ||(trp_route_get_next_hop(entry)==NULL)) {
619       /* at least one field could not be allocated */
620       tr_err("trps_accept_update: unable to allocate all fields for entry.");
621       trp_route_free(entry);
622       return TRP_NOMEM;
623     }
624     trp_rtable_add(trps->rtable, entry);
625   }
626
627   /* We now have an entry in the table, whether it's new or not. Update metric and expiry, unless
628    * the metric is infinity. An infinite metric can only occur here if we just retracted an existing
629    * route (we never accept retractions as new routes), so there is no risk of leaving the expiry
630    * time unset on a new route entry. */
631   tr_debug("trps_accept_update: accepting route update.");
632   trp_route_set_metric(entry, trp_inforec_get_metric(rec));
633   trp_route_set_interval(entry, trp_inforec_get_interval(rec));
634
635   /* check whether the trust router has changed */
636   if (0!=tr_name_cmp(trp_route_get_trust_router(entry),
637                      trp_inforec_get_trust_router(rec))) {
638     /* The name changed. Set this route as triggered. */
639     tr_debug("trps_accept_update: trust router for route changed.");
640     trp_route_set_triggered(entry, 1);
641     trp_route_set_trust_router(entry, trp_inforec_dup_trust_router(rec)); /* frees old name */
642   }
643   if (!trps_route_retracted(trps, entry)) {
644     tr_debug("trps_accept_update: route not retracted, setting expiry timer.");
645     trp_route_set_expiry(entry, trps_compute_expiry(trps,
646                                                      trp_route_get_interval(entry),
647                                                      trp_route_get_expiry(entry)));
648   }
649   return TRP_SUCCESS;
650 }
651
652
653 static TRP_RC trps_handle_inforec_route(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC *rec)
654 {
655   TRP_ROUTE *route=NULL;
656   unsigned int feas=0;
657
658   /* determine feasibility */
659   feas=trps_check_feasibility(trps, trp_upd_get_realm(upd), trp_upd_get_comm(upd), rec);
660   tr_debug("trps_handle_update: record feasibility=%d", feas);
661
662   /* do we have an existing route? */
663   route=trps_get_route(trps,
664                        trp_upd_get_comm(upd),
665                        trp_upd_get_realm(upd),
666                        trp_upd_get_peer(upd));
667   if (route!=NULL) {
668     /* there was a route table entry already */
669     tr_debug("trps_handle_updates: route entry already exists.");
670     if (feas) {
671       /* Update is feasible. Accept it. */
672       trps_accept_update(trps, upd, rec);
673     } else {
674       /* Update is infeasible. Ignore it unless the trust router has changed. */
675       if (0!=tr_name_cmp(trp_route_get_trust_router(route),
676                          trp_inforec_get_trust_router(rec))) {
677         /* the trust router associated with the route has changed, treat update as a retraction */
678         trps_retract_route(trps, route);
679       }
680     }
681   } else {
682     /* No existing route table entry. Ignore it unless it is feasible and not a retraction. */
683     tr_debug("trps_handle_update: no route entry exists yet.");
684     if (feas && trp_metric_is_finite(trp_inforec_get_metric(rec)))
685       trps_accept_update(trps, upd, rec);
686   }
687
688   return TRP_SUCCESS;
689 }
690
691 static int trps_name_in_provenance(TR_NAME *name, json_t *prov)
692 {
693   size_t ii=0;
694   TR_NAME *this_name=NULL;
695   const char *s=NULL;
696
697   if (prov==NULL)
698     return 0; /* no provenance list, so it has no names in it */
699
700   /* now check to see if name is in the provenance */
701   for (ii=0; ii<json_array_size(prov); ii++) {
702     s=json_string_value(json_array_get(prov, ii));
703     if (s==NULL) {
704       tr_debug("trps_name_in_provenance: empty entry in provenance list.");
705       continue;
706     }
707
708     this_name=tr_new_name(s);
709     if (this_name==NULL) {
710       tr_debug("trps_name_in_provenance: unable to allocate name.");
711       return -1;
712     }
713     if (0==tr_name_cmp(name, this_name)) {
714       tr_free_name(this_name);
715       return 1;
716     }
717     tr_free_name(this_name);
718   }
719   return 0;
720 }
721
722 static TR_COMM *trps_create_new_comm(TALLOC_CTX *mem_ctx, TR_NAME *comm_id, TRP_INFOREC *rec)
723 {
724   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
725   TR_COMM *comm=tr_comm_new(tmp_ctx);
726   
727   if (comm==NULL) {
728     tr_debug("trps_create_new_comm: unable to allocate new community.");
729     goto cleanup;
730   }
731   /* fill in the community with info */
732   tr_comm_set_id(comm, tr_dup_name(comm_id));
733   if (tr_comm_get_id(comm)==NULL) {
734     tr_debug("trps_create_new_comm: unable to allocate community name.");
735     comm=NULL;
736     goto cleanup;
737   }
738   tr_comm_set_type(comm, trp_inforec_get_comm_type(rec));
739   if (trp_inforec_get_apcs(rec)!=NULL) {
740     tr_comm_set_apcs(comm, tr_apc_dup(tmp_ctx, trp_inforec_get_apcs(rec)));
741     if (tr_comm_get_apcs(comm)==NULL) {
742       tr_debug("trps_create_new_comm: unable to allocate APC list.");
743       comm=NULL;
744       goto cleanup;
745     }
746   }
747   if (trp_inforec_get_owner_realm(rec)!=NULL) {
748     tr_comm_set_owner_realm(comm, tr_dup_name(trp_inforec_get_owner_realm(rec)));
749     if (tr_comm_get_owner_realm(comm)==NULL) {
750       tr_debug("trps_create_new_comm: unable to allocate owner realm name.");
751       comm=NULL;
752       goto cleanup;
753     }
754   }
755   if (trp_inforec_get_owner_contact(rec)!=NULL) {
756     tr_comm_set_owner_contact(comm, tr_dup_name(trp_inforec_get_owner_contact(rec)));
757     if (tr_comm_get_owner_contact(comm)==NULL) {
758       tr_debug("trps_create_new_comm: unable to allocate owner contact.");
759       comm=NULL;
760       goto cleanup;
761     }
762   }
763   comm->expiration_interval=trp_inforec_get_exp_interval(rec);
764   talloc_steal(mem_ctx, comm);
765   
766 cleanup:
767   talloc_free(tmp_ctx);
768   return comm;
769 }
770
771 static TR_RP_REALM *trps_create_new_rp_realm(TALLOC_CTX *mem_ctx, TR_NAME *realm_id, TRP_INFOREC *rec)
772 {
773   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
774   TR_RP_REALM *rp=tr_rp_realm_new(tmp_ctx);
775   
776   if (rp==NULL) {
777     tr_debug("trps_create_new_rp_realm: unable to allocate new realm.");
778     goto cleanup;
779   }
780   /* fill in the realm */
781   tr_rp_realm_set_id(rp, tr_dup_name(realm_id));
782   if (tr_rp_realm_get_id(rp)==NULL) {
783     tr_debug("trps_create_new_rp_realm: unable to allocate realm name.");
784     rp=NULL;
785     goto cleanup;
786   }
787   talloc_steal(mem_ctx, rp);
788   
789 cleanup:
790   talloc_free(tmp_ctx);
791   return rp;
792 }
793
794 static TR_IDP_REALM *trps_create_new_idp_realm(TALLOC_CTX *mem_ctx, TR_NAME *realm_id, TRP_INFOREC *rec)
795 {
796   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
797   TR_IDP_REALM *idp=tr_idp_realm_new(tmp_ctx);
798   
799   if (idp==NULL) {
800     tr_debug("trps_create_new_idp_realm: unable to allocate new realm.");
801     goto cleanup;
802   }
803   /* fill in the realm */
804   tr_idp_realm_set_id(idp, tr_dup_name(realm_id));
805   if (tr_idp_realm_get_id(idp)==NULL) {
806     tr_debug("trps_create_new_idp_realm: unable to allocate realm name.");
807     idp=NULL;
808     goto cleanup;
809   }
810   if (trp_inforec_get_apcs(rec)!=NULL) {
811     tr_idp_realm_set_apcs(idp, tr_apc_dup(tmp_ctx, trp_inforec_get_apcs(rec)));
812     if (tr_idp_realm_get_apcs(idp)==NULL) {
813       tr_debug("trps_create_new_idp_realm: unable to allocate APC list.");
814       idp=NULL;
815       goto cleanup;
816     }
817   }
818   idp->origin=TR_REALM_DISCOVERED;
819   
820   talloc_steal(mem_ctx, idp);
821   
822 cleanup:
823   talloc_free(tmp_ctx);
824   return idp;
825 }
826
827 static TRP_RC trps_handle_inforec_comm(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC *rec)
828 {
829   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
830   TR_NAME *comm_id=trp_upd_get_comm(upd);
831   TR_NAME *realm_id=trp_upd_get_realm(upd);
832   TR_NAME *origin_id=NULL;
833   TR_NAME *our_peer_label=NULL;
834   TR_COMM *comm=NULL;
835   TR_RP_REALM *rp_realm=NULL;
836   TR_IDP_REALM *idp_realm=NULL;
837   struct timespec expiry={0,0};
838   TRP_RC rc=TRP_ERROR;
839
840   if ((comm_id==NULL) || (realm_id==NULL))
841     goto cleanup;
842
843   origin_id=trp_inforec_dup_origin(rec);
844   if (origin_id==NULL)
845     goto cleanup;
846     
847   /* see whether we want to add this */
848   our_peer_label=trps_dup_label(trps);
849   if (our_peer_label==NULL) {
850     tr_debug("trps_handle_inforec_comm: unable to allocate peer label.");
851     goto cleanup;
852   }
853
854   if (trps_name_in_provenance(our_peer_label, trp_inforec_get_provenance(rec)))
855     tr_debug("trps_handle_inforec_comm: rejecting community inforec to avoid provenance loop.");
856   else {
857     /* no loop occurring, accept the update */
858     comm=tr_comm_table_find_comm(trps->ctable, comm_id);
859     if (comm==NULL) {
860       tr_debug("trps_handle_inforec_comm: unknown community %.*s in inforec, creating it.",
861                comm_id->len, comm_id->buf);
862       comm=trps_create_new_comm(tmp_ctx, comm_id, rec);
863       if (comm==NULL) {
864         tr_debug("trps_handle_inforec_comm: unable to create new community.");
865         goto cleanup;
866       }
867       tr_comm_table_add_comm(trps->ctable, comm);
868     }
869     /* TODO: see if other comm data match the new inforec and update or complain */
870
871     trps_compute_expiry(trps, trp_inforec_get_interval(rec), &expiry);
872     if ((expiry.tv_sec==0)&&(expiry.tv_nsec==0))
873       goto cleanup;
874
875     switch (trp_inforec_get_role(rec)) {
876     case TR_ROLE_RP:
877       rp_realm=tr_rp_realm_lookup(trps->ctable->rp_realms, realm_id);
878       if (rp_realm==NULL) {
879         tr_debug("trps_handle_inforec_comm: unknown RP realm %.*s in inforec, creating it.",
880                  realm_id->len, realm_id->buf);
881         rp_realm=trps_create_new_rp_realm(tmp_ctx, realm_id, rec);
882         if (rp_realm==NULL) {
883           tr_debug("trps_handle_inforec_comm: unable to create new RP realm.");
884           /* we may leave an unused community in the table, but it will only last until
885            * the next table sweep if it does not get any realms before that happens */
886           goto cleanup;
887         }
888         tr_comm_table_add_rp_realm(trps->ctable, rp_realm);
889       }
890       /* TODO: if realm existed, see if data match the new inforec and update or complain */
891       tr_comm_add_rp_realm(trps->ctable, comm, rp_realm, trp_inforec_get_interval(rec), trp_inforec_get_provenance(rec), &expiry);
892       tr_debug("trps_handle_inforec_comm: added RP realm %.*s to comm %.*s (origin %.*s).",
893                realm_id->len, realm_id->buf,
894                comm_id->len, comm_id->buf,
895                origin_id->len, origin_id->buf);
896       break;
897     case TR_ROLE_IDP:
898       idp_realm=tr_idp_realm_lookup(trps->ctable->idp_realms, realm_id);
899       if (idp_realm==NULL) {
900         tr_debug("trps_handle_inforec_comm: unknown IDP realm %.*s in inforec, creating it.",
901                  realm_id->len, realm_id->buf);
902         idp_realm=trps_create_new_idp_realm(tmp_ctx, realm_id, rec);
903         if (idp_realm==NULL) {
904           tr_debug("trps_handle_inforec_comm: unable to create new IDP realm.");
905           /* we may leave an unused community in the table, but it will only last until
906            * the next table sweep if it does not get any realms before that happens */
907           goto cleanup;
908         }
909         tr_comm_table_add_idp_realm(trps->ctable, idp_realm);
910       }
911       /* TODO: if realm existed, see if data match the new inforec and update or complain */
912       tr_comm_add_idp_realm(trps->ctable, comm, idp_realm, trp_inforec_get_interval(rec), trp_inforec_get_provenance(rec), &expiry);
913       tr_debug("trps_handle_inforec_comm: added IDP realm %.*s to comm %.*s (origin %.*s).",
914                realm_id->len, realm_id->buf,
915                comm_id->len, comm_id->buf,
916                origin_id->len, origin_id->buf);
917       break;
918     default:
919       tr_debug("trps_handle_inforec_comm: unable to add realm.");
920       goto cleanup;
921     }
922   } 
923
924   rc=TRP_SUCCESS;
925
926 cleanup:
927   if (our_peer_label!=NULL)
928     tr_free_name(our_peer_label);
929   if (origin_id!=NULL)
930     tr_free_name(origin_id);
931   talloc_free(tmp_ctx);
932   return rc;
933 }
934
935 /**
936  * Apply applicable TRP_INBOUND filters to an inforec. Rejects everything if peer has no filters.
937  *
938  * @param trps Active TRPS instance
939  * @param upd TRP_UPD that contains the inforec to filter
940  * @param rec Inforec to filter
941  * @return 1 if accepted by the filter, 0 otherwise
942  */
943 static int trps_filter_inbound_inforec(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC *rec)
944 {
945   TRP_PEER *peer=NULL;
946   TR_NAME *peer_name=NULL;
947   TR_FILTER_ACTION action=TR_FILTER_ACTION_REJECT;
948   TR_FILTER_TARGET *target=NULL;
949   int retval=0;
950
951   /* Look up the peer. For inbound messages, the peer is identified by its GSS name */
952   peer_name=trp_upd_get_peer(upd);
953   peer=trps_get_peer_by_gssname(trps, peer_name);
954   if (peer==NULL) {
955     tr_err("trps_filter_inbound_inforec: received inforec from unknown peer (%.*s), rejecting.",
956            peer_name->len,
957            peer_name->buf);
958     return 0;
959   }
960
961   /* tr_filter_apply() and tr_filter_set_get() handle null filter sets/filters by rejecting */
962   target= tr_filter_target_trp_inforec(NULL, upd, rec);
963   if (target==NULL) {
964     /* TODO: signal that filtering failed. Until then, just filter everything and give an error message. */
965     tr_crit("trps_filter_inbound_inforec: Unable to allocate filter target, cannot apply filter!");
966   }
967   if ((target==NULL)
968       || (TR_FILTER_NO_MATCH==tr_filter_apply(target,
969                                               tr_filter_set_get(peer->filters, TR_FILTER_TYPE_TRP_INBOUND),
970                                               NULL,
971                                               &action))
972       || (action!=TR_FILTER_ACTION_ACCEPT)) {
973     /* either the filter did not match or it matched a reject rule or allocating the target failed */
974     retval=0;
975   } else
976     retval=1;
977   if (target!=NULL)
978     tr_filter_target_free(target);
979
980   /* filter matched an accept rule */
981   return retval;
982 }
983
984
985 static TRP_RC trps_handle_update(TRPS_INSTANCE *trps, TRP_UPD *upd)
986 {
987   TRP_INFOREC *rec=NULL;
988
989   if (trps_validate_update(trps, upd) != TRP_SUCCESS) {
990     tr_notice("trps_handle_update: received invalid TRP update.");
991     return TRP_ERROR;
992   }
993
994   for (rec=trp_upd_get_inforec(upd); rec!=NULL; rec=trp_inforec_get_next(rec)) {
995     /* validate/sanity check the record update */
996     if (trps_validate_inforec(trps, rec) != TRP_SUCCESS) {
997       tr_notice("trps_handle_update: invalid inforec in TRP update, discarding entire update.");
998       return TRP_ERROR;
999     }
1000   }
1001
1002   for (rec=trp_upd_get_inforec(upd); rec!=NULL; rec=trp_inforec_get_next(rec)) {
1003     if (!trps_filter_inbound_inforec(trps, upd, rec)) {
1004       tr_debug("trps_handle_update: inforec rejected by filter.");
1005       continue; /* just go on to the next record */
1006     }
1007
1008     switch (trp_inforec_get_type(rec)) {
1009     case TRP_INFOREC_TYPE_ROUTE:
1010       tr_debug("trps_handle_update: handling route inforec.");
1011       if (TRP_SUCCESS!=trps_handle_inforec_route(trps, upd, rec))
1012         tr_notice("trps_handle_update: error handling route inforec.");
1013       break;
1014     case TRP_INFOREC_TYPE_COMMUNITY:
1015       tr_debug("trps_handle_update: handling community inforec.");
1016       if (TRP_SUCCESS!=trps_handle_inforec_comm(trps, upd, rec))
1017         tr_notice("trps_handle_update: error handling community inforec.");
1018
1019       break;
1020     default:
1021       tr_notice("trps_handle_update: unsupported inforec in TRP update.");
1022       break;
1023     }
1024   }
1025   return TRP_SUCCESS;
1026 }
1027
1028 static TRP_RC trps_validate_request(TRPS_INSTANCE *trps, TRP_REQ *req)
1029 {
1030   if (req==NULL) {
1031     tr_notice("trps_validate_request: null TRP request.");
1032     return TRP_BADARG;
1033   }
1034
1035   if (trp_req_get_comm(req)==NULL) {
1036     tr_notice("trps_validate_request: received TRP request with null community.");
1037     return TRP_ERROR;
1038   }
1039   
1040   if (trp_req_get_realm(req)==NULL) {
1041     tr_notice("trps_validate_request: received TRP request with null realm.");
1042     return TRP_ERROR;
1043   }
1044   
1045   if (trp_req_get_peer(req)==NULL) {
1046     tr_notice("trps_validate_request: received TRP request without origin peer information.");
1047     return TRP_ERROR;
1048   }
1049   
1050   return TRP_SUCCESS;
1051 }
1052
1053 /* choose the best route to comm/realm, optionally excluding routes to a particular peer */
1054 static TRP_ROUTE *trps_find_best_route(TRPS_INSTANCE *trps,
1055                                         TR_NAME *comm,
1056                                         TR_NAME *realm,
1057                                         TR_NAME *exclude_peer)
1058 {
1059   TRP_ROUTE **entry=NULL;
1060   TRP_ROUTE *best=NULL;
1061   size_t n_entry=0;
1062   unsigned int kk=0;
1063   unsigned int kk_min=0;
1064   unsigned int min_metric=TRP_METRIC_INFINITY;
1065
1066   entry=trp_rtable_get_realm_entries(trps->rtable, comm, realm, &n_entry);
1067   for (kk=0; kk<n_entry; kk++) {
1068     if (trp_route_get_metric(entry[kk]) < min_metric) {
1069       if ((exclude_peer==NULL) || (0!=tr_name_cmp(trp_route_get_peer(entry[kk]),
1070                                                   exclude_peer))) {
1071         kk_min=kk;
1072         min_metric=trp_route_get_metric(entry[kk]);
1073       } 
1074     }
1075   }
1076   if (trp_metric_is_finite(min_metric))
1077     best=entry[kk_min];
1078   
1079   talloc_free(entry);
1080   return best;
1081 }
1082
1083 /* TODO: think this through more carefully. At least ought to add hysteresis
1084  * to avoid flapping between routers or routes. */
1085 TRP_RC trps_update_active_routes(TRPS_INSTANCE *trps)
1086 {
1087   size_t n_comm=0, ii=0;
1088   TR_NAME **comm=trp_rtable_get_comms(trps->rtable, &n_comm);
1089   size_t n_realm=0, jj=0;
1090   TR_NAME **realm=NULL;
1091   TRP_ROUTE *best_route=NULL, *cur_route=NULL;
1092   unsigned int best_metric=0, cur_metric=0;
1093
1094   for (ii=0; ii<n_comm; ii++) {
1095     realm=trp_rtable_get_comm_realms(trps->rtable, comm[ii], &n_realm);
1096     for (jj=0; jj<n_realm; jj++) {
1097       best_route=trps_find_best_route(trps, comm[ii], realm[jj], NULL);
1098       if (best_route==NULL)
1099         best_metric=TRP_METRIC_INFINITY;
1100       else
1101         best_metric=trp_route_get_metric(best_route);
1102
1103       cur_route=trps_get_selected_route(trps, comm[ii], realm[jj]);
1104       if (cur_route!=NULL) {
1105         cur_metric=trp_route_get_metric(cur_route);
1106         if ((best_metric < cur_metric) && (trp_metric_is_finite(best_metric))) {
1107           /* The new route has a lower metric than the previous, and is finite. Accept. */
1108           trp_route_set_selected(cur_route, 0);
1109           trp_route_set_selected(best_route, 1);
1110         } else if (!trp_metric_is_finite(cur_metric)) /* rejects infinite or invalid metrics */
1111           trp_route_set_selected(cur_route, 0);
1112       } else if (trp_metric_is_finite(best_metric)) {
1113         trp_route_set_selected(best_route, 1);
1114       }
1115     }
1116     if (realm!=NULL)
1117       talloc_free(realm);
1118     realm=NULL; n_realm=0;
1119   }
1120   if (comm!=NULL)
1121     talloc_free(comm);
1122   comm=NULL; n_comm=0;
1123
1124   return TRP_SUCCESS;
1125 }
1126
1127 /* true if curtime >= expiry */
1128 static int trps_expired(struct timespec *expiry, struct timespec *curtime)
1129 {
1130   return (tr_cmp_timespec(curtime, expiry) >= 0);
1131 }
1132
1133 /* Sweep for expired routes. For each expired route, if its metric is infinite, the route is flushed.
1134  * If its metric is finite, the metric is set to infinite and the route's expiration time is updated. */
1135 TRP_RC trps_sweep_routes(TRPS_INSTANCE *trps)
1136 {
1137   struct timespec sweep_time={0,0};
1138   TRP_ROUTE **entry=NULL;
1139   size_t n_entry=0;
1140   size_t ii=0;
1141
1142   /* use a single time for the entire sweep */
1143   if (0!=clock_gettime(TRP_CLOCK, &sweep_time)) {
1144     tr_err("trps_sweep_routes: could not read realtime clock.");
1145     sweep_time.tv_sec=0;
1146     sweep_time.tv_nsec=0;
1147     return TRP_ERROR;
1148   }
1149
1150   entry= trp_rtable_get_entries(NULL, trps->rtable, &n_entry); /* must talloc_free *entry */
1151
1152   /* loop over the entries */
1153   for (ii=0; ii<n_entry; ii++) {
1154     if (!trp_route_is_local(entry[ii]) && trps_expired(trp_route_get_expiry(entry[ii]), &sweep_time)) {
1155       tr_debug("trps_sweep_routes: route expired.");
1156       if (!trp_metric_is_finite(trp_route_get_metric(entry[ii]))) {
1157         /* flush route */
1158         tr_debug("trps_sweep_routes: metric was infinity, flushing route.");
1159         trp_rtable_remove(trps->rtable, entry[ii]); /* entry[ii] is no longer valid */
1160         entry[ii]=NULL;
1161       } else {
1162         /* set metric to infinity and reset timer */
1163         tr_debug("trps_sweep_routes: setting metric to infinity and resetting expiry.");
1164         trp_route_set_metric(entry[ii], TRP_METRIC_INFINITY);
1165         trp_route_set_expiry(entry[ii], trps_compute_expiry(trps,
1166                                                              trp_route_get_interval(entry[ii]),
1167                                                              trp_route_get_expiry(entry[ii])));
1168       }
1169     }
1170   }
1171
1172   talloc_free(entry);
1173   return TRP_SUCCESS;
1174 }
1175
1176
1177 /* Sweep for expired communities/realms/memberships. */
1178 TRP_RC trps_sweep_ctable(TRPS_INSTANCE *trps)
1179 {
1180   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1181   struct timespec sweep_time={0,0};
1182   TR_COMM_MEMB *memb=NULL;
1183   TR_COMM_ITER *iter=NULL;
1184   TRP_RC rc=TRP_ERROR;
1185
1186   /* use a single time for the entire sweep */
1187   if (0!=clock_gettime(TRP_CLOCK, &sweep_time)) {
1188     tr_err("trps_sweep_ctable: could not read realtime clock.");
1189     sweep_time.tv_sec=0;
1190     sweep_time.tv_nsec=0;
1191     goto cleanup;
1192   }
1193
1194   /* iterate all memberships */
1195   iter=tr_comm_iter_new(tmp_ctx);
1196   if (iter==NULL) {
1197     tr_err("trps_sweep_ctable: unable to allocate iterator.");
1198     rc=TRP_NOMEM;
1199     goto cleanup;
1200   }
1201   for (memb=tr_comm_memb_iter_all_first(iter, trps->ctable);
1202        memb!=NULL;
1203        memb=tr_comm_memb_iter_all_next(iter)) {
1204     if (tr_comm_memb_get_origin(memb)==NULL)
1205       continue; /* do not expire local entries */
1206
1207     if (tr_comm_memb_is_expired(memb, &sweep_time)) {
1208       if (tr_comm_memb_get_times_expired(memb)>0) {
1209         /* Already expired once; flush. */
1210         tr_debug("trps_sweep_ctable: flushing expired community membership (%.*s in %.*s, origin %.*s, expired %s).",
1211                  tr_comm_memb_get_realm_id(memb)->len, tr_comm_memb_get_realm_id(memb)->buf,
1212                  tr_comm_get_id(tr_comm_memb_get_comm(memb))->len, tr_comm_get_id(tr_comm_memb_get_comm(memb))->buf,
1213                  tr_comm_memb_get_origin(memb)->len, tr_comm_memb_get_origin(memb)->buf,
1214                  timespec_to_str(tr_comm_memb_get_expiry(memb)));
1215         tr_comm_table_remove_memb(trps->ctable, memb);
1216         tr_comm_memb_free(memb);
1217       } else {
1218         /* This is the first expiration. Note this and reset the expiry time. */
1219         tr_comm_memb_expire(memb);
1220         trps_compute_expiry(trps, tr_comm_memb_get_interval(memb), tr_comm_memb_get_expiry(memb));
1221         tr_debug("trps_sweep_ctable: community membership expired at %s, resetting expiry to %s (%.*s in %.*s, origin %.*s).",
1222                  timespec_to_str(&sweep_time),
1223                  timespec_to_str(tr_comm_memb_get_expiry(memb)),
1224                  tr_comm_memb_get_realm_id(memb)->len, tr_comm_memb_get_realm_id(memb)->buf,
1225                  tr_comm_get_id(tr_comm_memb_get_comm(memb))->len, tr_comm_get_id(tr_comm_memb_get_comm(memb))->buf,
1226                  tr_comm_memb_get_origin(memb)->len, tr_comm_memb_get_origin(memb)->buf);
1227       }
1228     }
1229   }
1230
1231   /* get rid of any unreferenced realms, etc */
1232   tr_comm_table_sweep(trps->ctable);
1233
1234 cleanup:
1235   talloc_free(tmp_ctx);
1236   return rc;
1237 }
1238
1239 /* add metrics */
1240 static unsigned int trps_metric_add(unsigned int m1, unsigned int m2)
1241 {
1242   if (trp_metric_is_invalid(m1) || trp_metric_is_invalid(m2))
1243     return TRP_METRIC_INVALID;
1244
1245   if (trp_metric_is_infinite(m1) || trp_metric_is_infinite(m2))
1246     return TRP_METRIC_INFINITY;
1247
1248   if (trp_metric_is_finite(m1+m2))
1249     return m1+m2;
1250   else
1251     return TRP_METRIC_INFINITY;
1252 }
1253
1254 /* convert an rentry into a new trp update info record */
1255 static TRP_INFOREC *trps_route_to_inforec(TALLOC_CTX *mem_ctx, TRPS_INSTANCE *trps, TRP_ROUTE *route)
1256 {
1257   TRP_INFOREC *rec=trp_inforec_new(mem_ctx, TRP_INFOREC_TYPE_ROUTE);
1258   unsigned int linkcost=0;
1259
1260   if (rec!=NULL) {
1261     if (trp_route_is_local(route))
1262       linkcost=0;
1263     else {
1264       linkcost=trp_peer_get_linkcost(trps_get_peer_by_gssname(trps,
1265                                                               trp_route_get_peer(route)));
1266     }
1267
1268     /* Note that we leave the next hop empty since the recipient fills that in.
1269      * This is where we add the link cost (currently always 1) to the next peer. */
1270     if ((trp_inforec_set_trust_router(rec, trp_route_dup_trust_router(route)) != TRP_SUCCESS)
1271        ||(trp_inforec_set_metric(rec,
1272                                  trps_metric_add(trp_route_get_metric(route),
1273                                                  linkcost)) != TRP_SUCCESS)
1274        ||(trp_inforec_set_interval(rec, trps_get_update_interval(trps)) != TRP_SUCCESS)) {
1275       tr_err("trps_route_to_inforec: error creating route update.");
1276       talloc_free(rec);
1277       rec=NULL;
1278     }
1279   }
1280   return rec;
1281 }
1282
1283 static TRP_UPD *trps_route_to_upd(TALLOC_CTX *mem_ctx, TRPS_INSTANCE *trps, TRP_ROUTE *route)
1284 {
1285   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1286   TRP_UPD *upd=trp_upd_new(tmp_ctx);
1287   TRP_INFOREC *rec=NULL;
1288
1289   if (upd==NULL) {
1290     tr_err("trps_route_to_upd: could not create update message.");
1291     goto cleanup;
1292   }
1293   trp_upd_set_realm(upd, trp_route_dup_realm(route));
1294   if (trp_upd_get_realm(upd)==NULL) {
1295     tr_err("trps_route_to_upd: could not copy realm.");
1296     upd=NULL; /* it's still in tmp_ctx, so it will be freed */
1297     goto cleanup;
1298   }
1299   trp_upd_set_comm(upd, trp_route_dup_comm(route));
1300   if (trp_upd_get_comm(upd)==NULL) {
1301     tr_err("trps_route_to_upd: could not copy comm.");
1302     upd=NULL; /* it's still in tmp_ctx, so it will be freed */
1303     goto cleanup;
1304   }
1305   rec=trps_route_to_inforec(tmp_ctx, trps, route);
1306   if (rec==NULL) {
1307     tr_err("trps_route_to_upd: could not create route info record for realm %.*s in comm %.*s.",
1308            trp_route_get_realm(route)->len, trp_route_get_realm(route)->buf,
1309            trp_route_get_comm(route)->len, trp_route_get_comm(route)->buf);
1310     upd=NULL; /* it's till in tmp_ctx, so it will be freed */
1311     goto cleanup;
1312   }
1313   trp_upd_add_inforec(upd, rec);
1314
1315   /* sucess */
1316   talloc_steal(mem_ctx, upd);
1317
1318 cleanup:
1319   talloc_free(tmp_ctx);
1320   return upd;
1321 }
1322
1323 /* select the correct route to comm/realm to be announced to peer */
1324 static TRP_ROUTE *trps_select_realm_update(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer_gssname)
1325 {
1326   TRP_ROUTE *route;
1327
1328   /* Take the currently selected route unless it is through the peer we're sending the update to.
1329    * I.e., enforce the split horizon rule. */
1330   route=trp_rtable_get_selected_entry(trps->rtable, comm, realm);
1331   if (route==NULL) {
1332     /* No selected route, this should only happen if the only route has been retracted,
1333      * in which case we do not want to advertise it. */
1334     return NULL;
1335   }
1336   tr_debug("trps_select_realm_update: %s vs %s", peer_gssname->buf,
1337            trp_route_get_peer(route)->buf);
1338   if (0==tr_name_cmp(peer_gssname, trp_route_get_peer(route))) {
1339     tr_debug("trps_select_realm_update: matched, finding alternate route");
1340     /* the selected entry goes through the peer we're reporting to, choose an alternate */
1341     route=trps_find_best_route(trps, comm, realm, peer_gssname);
1342     if ((route==NULL) || (!trp_metric_is_finite(trp_route_get_metric(route))))
1343       return NULL; /* don't advertise a nonexistent or retracted route */
1344   }
1345   return route;
1346 }
1347
1348 /* Add TRP_UPD msgs to the updates GPtrArray. Caller needs to arrange for these to be freed. */
1349 static TRP_RC trps_select_route_updates_for_peer(TALLOC_CTX *mem_ctx,
1350                                                  GPtrArray *updates,
1351                                                  TRPS_INSTANCE *trps,
1352                                                  TR_NAME *peer_gssname,
1353                                                  int triggered)
1354 {
1355   size_t n_comm=0;
1356   TR_NAME **comm=trp_rtable_get_comms(trps->rtable, &n_comm);
1357   TR_NAME **realm=NULL;
1358   size_t n_realm=0;
1359   size_t ii=0, jj=0;
1360   TRP_ROUTE *best=NULL;
1361   TRP_UPD *upd=NULL;
1362
1363   if (updates==NULL)
1364     return TRP_BADARG;
1365
1366   for (ii=0; ii<n_comm; ii++) {
1367     realm=trp_rtable_get_comm_realms(trps->rtable, comm[ii], &n_realm);
1368     for (jj=0; jj<n_realm; jj++) {
1369       best=trps_select_realm_update(trps, comm[ii], realm[jj], peer_gssname);
1370       /* If we found a route, add it to the list. If triggered!=0, then only
1371        * add triggered routes. */
1372       if ((best!=NULL) && ((!triggered) || trp_route_is_triggered(best))) {
1373         upd=trps_route_to_upd(mem_ctx, trps, best);
1374         if (upd==NULL) {
1375           tr_err("trps_select_route_updates_for_peer: unable to create update message.");
1376           continue;
1377         }
1378         g_ptr_array_add(updates, upd);
1379       }
1380     }
1381     
1382     if (realm!=NULL)
1383       talloc_free(realm);
1384     realm=NULL;
1385     n_realm=0;
1386   }
1387
1388   if (comm!=NULL)
1389     talloc_free(comm);
1390   
1391   return TRP_SUCCESS;
1392 }
1393
1394 static TRP_INFOREC *trps_memb_to_inforec(TALLOC_CTX *mem_ctx, TRPS_INSTANCE *trps, TR_COMM_MEMB *memb)
1395 {
1396   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1397   TRP_INFOREC *rec=NULL;
1398   TR_COMM *comm=NULL;
1399
1400   if (memb==NULL)
1401     goto cleanup;
1402
1403   comm=tr_comm_memb_get_comm(memb);
1404   rec=trp_inforec_new(tmp_ctx, TRP_INFOREC_TYPE_COMMUNITY);
1405   if (rec==NULL)
1406     goto cleanup;
1407   
1408   if (TRP_SUCCESS!=trp_inforec_set_comm_type(rec, tr_comm_get_type(comm))) {
1409     rec=NULL;
1410     goto cleanup;
1411   }
1412   
1413   if (TRP_SUCCESS!=trp_inforec_set_role(rec, tr_comm_memb_get_role(memb))) {
1414     rec=NULL;
1415     goto cleanup;
1416   }
1417
1418   if ((NULL!=tr_comm_get_apcs(comm)) &&
1419       ( (TRP_SUCCESS!=trp_inforec_set_apcs(rec,
1420                                            tr_apc_dup(rec, tr_comm_get_apcs(comm)))) ||
1421         (NULL==trp_inforec_get_apcs(rec)))) {
1422     rec=NULL;
1423     goto cleanup;
1424   }
1425
1426   if ((NULL!=tr_comm_get_owner_realm(comm)) &&
1427       ( (TRP_SUCCESS!=trp_inforec_set_owner_realm(rec, tr_dup_name(tr_comm_get_owner_realm(comm)))) ||
1428         (NULL==trp_inforec_get_owner_realm(rec)))) {
1429     rec=NULL;
1430     goto cleanup;
1431   }
1432
1433   if ((NULL!=tr_comm_get_owner_contact(comm)) &&
1434       ( (TRP_SUCCESS!=trp_inforec_set_owner_contact(rec, tr_dup_name(tr_comm_get_owner_contact(comm)))) ||
1435         (NULL==trp_inforec_get_owner_contact(rec)))) {
1436     rec=NULL;
1437     goto cleanup;
1438   }
1439
1440   if ((NULL!=tr_comm_memb_get_provenance(memb)) &&
1441       (TRP_SUCCESS!=trp_inforec_set_provenance(rec, tr_comm_memb_get_provenance(memb)))) {
1442     rec=NULL;
1443     goto cleanup;
1444   }
1445
1446   if (TRP_SUCCESS!=trp_inforec_set_interval(rec, trps_get_update_interval(trps))) {
1447     rec=NULL;
1448     goto cleanup;
1449   }
1450
1451   /* success! */
1452   talloc_steal(mem_ctx, rec);
1453
1454 cleanup:
1455   talloc_free(tmp_ctx);
1456   return rec;
1457 }
1458
1459 /* construct an update with all the inforecs for comm/realm/role to be sent to peer */
1460 static TRP_UPD *trps_comm_update(TALLOC_CTX *mem_ctx, TRPS_INSTANCE *trps, TR_NAME *peer_gssname, TR_COMM *comm, TR_REALM *realm)
1461 {
1462   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1463   TRP_UPD *upd=trp_upd_new(tmp_ctx);
1464   TRP_INFOREC *rec=NULL;
1465   TR_COMM_ITER *iter=NULL;
1466   TR_COMM_MEMB *memb=NULL;
1467
1468   if (upd==NULL)
1469     goto cleanup;
1470   
1471   trp_upd_set_comm(upd, tr_comm_dup_id(comm));
1472   trp_upd_set_realm(upd, tr_realm_dup_id(realm));
1473   /* leave peer empty */
1474
1475   iter=tr_comm_iter_new(tmp_ctx);
1476   if (iter==NULL) {
1477     tr_err("trps_comm_update: unable to allocate iterator.");
1478     upd=NULL;
1479     goto cleanup;
1480   }
1481   
1482   /* now add inforecs */
1483   switch (realm->role) {
1484   case TR_ROLE_IDP:
1485     memb=tr_comm_table_find_idp_memb(trps->ctable,
1486                                      tr_realm_get_id(realm),
1487                                      tr_comm_get_id(comm));
1488     break;
1489   case TR_ROLE_RP:
1490     memb=tr_comm_table_find_rp_memb(trps->ctable,
1491                                     tr_realm_get_id(realm),
1492                                     tr_comm_get_id(comm));
1493     break;
1494   default:
1495     break;
1496   }
1497   if (memb!=NULL) {
1498     for (memb=tr_comm_memb_iter_first(iter, memb);
1499          memb!=NULL;
1500          memb=tr_comm_memb_iter_next(iter)) {
1501       rec=trps_memb_to_inforec(tmp_ctx, trps, memb);
1502       if (rec==NULL) {
1503         tr_err("trps_comm_update: unable to allocate inforec.");
1504         upd=NULL;
1505         goto cleanup;
1506       }
1507       trp_upd_add_inforec(upd, rec);
1508     }
1509   }
1510
1511   if (trp_upd_get_inforec(upd)==NULL)
1512     upd=NULL; /* no inforecs, no reason to send the update */
1513   else
1514     talloc_steal(mem_ctx, upd); /* success! */
1515
1516 cleanup:
1517   talloc_free(tmp_ctx);
1518   return upd;
1519 }
1520
1521 /* Find all community updates to send to a peer and add these as TR_UPD records
1522  * to the updates GPtrArray. */
1523 static TRP_RC trps_select_comm_updates_for_peer(TALLOC_CTX *mem_ctx,
1524                                                 GPtrArray *updates,
1525                                                 TRPS_INSTANCE *trps,
1526                                                 TR_NAME *peer_gssname,
1527                                                 int triggered)
1528 {
1529   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1530   TR_COMM_ITER *comm_iter=NULL;
1531   TR_COMM *comm=NULL;
1532   TR_COMM_ITER *realm_iter=NULL;
1533   TR_REALM *realm=NULL;
1534   TRP_UPD *upd=NULL;
1535   TRP_RC rc=TRP_ERROR;
1536
1537   /* currently do not send any communities on triggered updates */
1538   if (triggered) {
1539     rc=TRP_SUCCESS;
1540     goto cleanup;
1541   }
1542
1543   comm_iter=tr_comm_iter_new(tmp_ctx);
1544   realm_iter=tr_comm_iter_new(tmp_ctx);
1545   if ((comm_iter==NULL) || (realm_iter==NULL)) {
1546     tr_err("trps_select_comm_updates_for_peer: unable to allocate iterator.");
1547     rc=TRP_NOMEM;
1548     goto cleanup;
1549   }
1550
1551   /* do every community */
1552   for (comm=tr_comm_table_iter_first(comm_iter, trps->ctable);
1553        comm!=NULL;
1554        comm=tr_comm_table_iter_next(comm_iter)) {
1555     /* do every realm in this community */
1556     tr_debug("trps_select_comm_updates_for_peer: looking through community %.*s",
1557              tr_comm_get_id(comm)->len,
1558              tr_comm_get_id(comm)->buf);
1559     for (realm=tr_realm_iter_first(realm_iter, trps->ctable, tr_comm_get_id(comm));
1560          realm!=NULL;
1561          realm=tr_realm_iter_next(realm_iter)) {
1562       /* get the update for this comm/realm */
1563       tr_debug("trps_select_comm_updates_for_peer: adding realm %.*s",
1564                tr_realm_get_id(realm)->len,
1565                tr_realm_get_id(realm)->buf);
1566       upd=trps_comm_update(mem_ctx, trps, peer_gssname, comm, realm);
1567       if (upd!=NULL)
1568         g_ptr_array_add(updates, upd);
1569     }
1570   }
1571
1572 cleanup:
1573   talloc_free(tmp_ctx);
1574   return rc;
1575 }
1576
1577 /**
1578  * Filter the inforecs in a single update
1579  *
1580  * @param filt The filter to apply
1581  * @param upd The update to filter
1582  */
1583 static void trps_filter_one_outbound_update(TR_FILTER *filt, TRP_UPD *upd)
1584 {
1585   TRP_INFOREC *this=NULL, *next=NULL;
1586   TR_FILTER_ACTION action=TR_FILTER_ACTION_REJECT;
1587   TR_FILTER_TARGET *target=NULL;
1588
1589   for(this=trp_upd_get_inforec(upd); this!=NULL; this=next) {
1590     next=this->next;
1591     target= tr_filter_target_trp_inforec(NULL, upd, this);
1592     if (target==NULL) {
1593       /* TODO: signal that filtering failed. Until then, just filter everything and give an error message. */
1594       tr_crit("trps_filter_one_outbound_update: Unable to allocate filter target, cannot apply filter!");
1595     }
1596     if ((target==NULL)
1597         || (TR_FILTER_NO_MATCH==tr_filter_apply(target, filt, NULL, &action))
1598         || (action!=TR_FILTER_ACTION_ACCEPT)) {
1599       /* Either no filter matched or one matched and rejected this record.
1600        * Also filter out record if we were unable to allocate a target. */
1601       trp_upd_remove_inforec(upd, this); /* "this" is now invalid */
1602     }
1603     if (target!=NULL)
1604       tr_filter_target_free(target);
1605   }
1606 }
1607
1608 /**
1609  * May shuffle the update list.
1610  *
1611  * @param filters The filter set for the relevant TRP peer
1612  * @param updates GPtrArray of updates to filter
1613  */
1614 static void trps_filter_outbound_updates(TR_FILTER_SET *filters, GPtrArray *updates)
1615 {
1616   TRP_UPD *upd=NULL;
1617   guint ii=0;
1618
1619   /* Walk backward through the array so we can remove elements. Careful about loop
1620    * termination - remember that ii is unsigned. */
1621   for (ii=updates->len; ii>0; ii--) {
1622     upd=g_ptr_array_index(updates, ii-1);
1623     trps_filter_one_outbound_update(tr_filter_set_get(filters, TR_FILTER_TYPE_TRP_OUTBOUND), upd);
1624     /* see if we removed all the records from this update */
1625     if (trp_upd_num_inforecs(upd)==0)
1626       g_ptr_array_remove_index_fast(updates, ii-1); /* does not preserve order at index ii or higher */
1627   }
1628 }
1629
1630 /* helper for trps_update_one_peer. Frees the TRP_UPD pointed to by a GPtrArray element */
1631 static void trps_trp_upd_destroy(gpointer data)
1632 {
1633   trp_upd_free((TRP_UPD *)data);
1634 }
1635
1636 /* all routes/communities to a single peer, unless comm/realm are specified (both or neither must be NULL) */
1637 static TRP_RC trps_update_one_peer(TRPS_INSTANCE *trps,
1638                                    TRP_PEER *peer,
1639                                    TRP_UPDATE_TYPE update_type,
1640                                    TR_NAME *realm,
1641                                    TR_NAME *comm)
1642 {
1643   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1644   TR_MSG msg; /* not a pointer! */
1645   TRP_UPD *upd=NULL;
1646   TRP_ROUTE *route=NULL;
1647   size_t ii=0;
1648   char *encoded=NULL;
1649   TRP_RC rc=TRP_ERROR;
1650   TR_NAME *peer_label=trp_peer_get_label(peer);
1651   GPtrArray *updates=g_ptr_array_new_with_free_func(trps_trp_upd_destroy);
1652
1653   if (updates==NULL) {
1654     tr_err("trps_update_one_peer: unable to allocate updates array.");
1655     rc=TRP_NOMEM;
1656     goto cleanup;
1657   }
1658
1659   switch (update_type) {
1660   case TRP_UPDATE_TRIGGERED:
1661     tr_debug("trps_update_one_peer: preparing triggered update for %.*s",
1662              peer_label->len, peer_label->buf);
1663     break;
1664   case TRP_UPDATE_SCHEDULED:
1665     tr_debug("trps_update_one_peer: preparing scheduled update for %.*s",
1666              peer_label->len, peer_label->buf);
1667     break;
1668   case TRP_UPDATE_REQUESTED:
1669     tr_debug("trps_update_one_peer: preparing requested update for %.*s",
1670              peer_label->len, peer_label->buf);
1671     break;
1672   default:
1673     tr_err("trps_update_one_peer: invalid update type requested.");
1674     rc=TRP_BADARG;
1675     goto cleanup;
1676   }
1677
1678   /* First, gather route updates. */
1679   tr_debug("trps_update_one_peer: selecting route updates for %.*s.", peer_label->len, peer_label->buf);
1680   if ((comm==NULL) && (realm==NULL)) {
1681     /* do all realms */
1682     rc=trps_select_route_updates_for_peer(tmp_ctx,
1683                                           updates,
1684                                           trps,
1685                                           peer_label,
1686                                           update_type==TRP_UPDATE_TRIGGERED);
1687   } else if ((comm!=NULL) && (realm!=NULL)) {
1688     /* a single community/realm was requested */
1689     route=trps_select_realm_update(trps, comm, realm, peer_label);
1690     if (route==NULL) {
1691       /* we have no actual update to send back, MUST send a retraction */
1692       tr_debug("trps_update_one_peer: community/realm without route requested, sending mandatory retraction.");
1693       route=trp_route_new(tmp_ctx);
1694       trp_route_set_comm(route, tr_dup_name(comm));
1695       trp_route_set_realm(route, tr_dup_name(realm));
1696       trp_route_set_peer(route, tr_new_name(""));
1697       trp_route_set_metric(route, TRP_METRIC_INFINITY);
1698       trp_route_set_trust_router(route, tr_new_name(""));
1699       trp_route_set_next_hop(route, tr_new_name(""));
1700     }
1701     upd=trps_route_to_upd(tmp_ctx, trps, route);
1702     if (upd==NULL) {
1703       tr_err("trps_update_one_peer: unable to allocate route update.");
1704       rc=TRP_NOMEM;
1705       goto cleanup;
1706     }
1707     g_ptr_array_add(updates, upd);
1708   } else {
1709     tr_err("trps_update_one_peer: error: only comm or realm was specified. Need both or neither.");
1710     rc=TRP_ERROR;
1711     goto cleanup;
1712   }
1713
1714   /* Second, gather community updates */
1715   tr_debug("trps_update_one_peer: selecting community updates for %.*s.", peer_label->len, peer_label->buf);
1716   rc=trps_select_comm_updates_for_peer(tmp_ctx, updates, trps, peer_label, update_type==TRP_UPDATE_TRIGGERED);
1717
1718   /* see if we have anything to send */
1719   if (updates->len<=0)
1720     tr_debug("trps_update_one_peer: no updates for %.*s", peer_label->len, peer_label->buf);
1721   else {
1722     /* Apply outbound TRP filters for this peer */
1723     trps_filter_outbound_updates(peer->filters, updates);
1724
1725     if (updates->len<=0)
1726       tr_debug("trps_update_one_peer: no updates for %.*s after filtering.", peer_label->len, peer_label->buf);
1727     else {
1728       tr_debug("trps_update_one_peer: sending %d update messages.", updates->len);
1729       for (ii=0; ii<updates->len; ii++) {
1730         upd = (TRP_UPD *) g_ptr_array_index(updates, ii);
1731         /* now encode the update message */
1732         tr_msg_set_trp_upd(&msg, upd);
1733         encoded = tr_msg_encode(NULL, &msg);
1734         if (encoded == NULL) {
1735           tr_err("trps_update_one_peer: error encoding update.");
1736           rc = TRP_ERROR;
1737           goto cleanup;
1738         }
1739
1740         tr_debug("trps_update_one_peer: adding message to queue.");
1741         if (trps_send_msg(trps, peer, encoded) != TRP_SUCCESS)
1742           tr_err("trps_update_one_peer: error queueing update.");
1743         else
1744           tr_debug("trps_update_one_peer: update queued successfully.");
1745
1746         tr_msg_free_encoded(encoded);
1747         encoded = NULL;
1748       }
1749     }
1750   }
1751
1752   rc=TRP_SUCCESS;
1753
1754 cleanup:
1755   if (updates!=NULL)
1756     g_ptr_array_free(updates, TRUE); /* frees any TRP_UPD records */
1757   talloc_free(tmp_ctx);
1758   return rc;
1759 }
1760
1761 /* all routes/communities to all peers */
1762 TRP_RC trps_update(TRPS_INSTANCE *trps, TRP_UPDATE_TYPE update_type)
1763 {
1764   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1765   TRP_PTABLE_ITER *iter=trp_ptable_iter_new(tmp_ctx);
1766   TRP_PEER *peer=NULL;
1767   TRP_RC rc=TRP_SUCCESS;
1768
1769   if (trps->ptable==NULL)
1770     return TRP_SUCCESS; /* no peers, nothing to do */
1771
1772   if (iter==NULL) {
1773     tr_err("trps_update: failed to allocate peer table iterator.");
1774     talloc_free(tmp_ctx);
1775     return TRP_NOMEM;
1776   }
1777
1778   for (peer=trp_ptable_iter_first(iter, trps->ptable);
1779        (peer!=NULL) && (rc==TRP_SUCCESS);
1780        peer=trp_ptable_iter_next(iter))
1781   {
1782     if (!trps_peer_connected(trps, peer)) {
1783       TR_NAME *peer_label=trp_peer_get_label(peer);
1784       tr_debug("trps_update: no TRP connection to %.*s, skipping.",
1785                peer_label->len, peer_label->buf);
1786       continue;
1787     }
1788     rc=trps_update_one_peer(trps, peer, update_type, NULL, NULL);
1789   }
1790
1791   tr_debug("trps_update: rc=%u after attempting update.", rc);
1792   trp_ptable_iter_free(iter);
1793   trp_rtable_clear_triggered(trps->rtable); /* don't re-send triggered updates */
1794   talloc_free(tmp_ctx);
1795   return rc;
1796 }        
1797
1798 TRP_RC trps_add_route(TRPS_INSTANCE *trps, TRP_ROUTE *route)
1799 {
1800   trp_rtable_add(trps->rtable, route); /* should return status */
1801   return TRP_SUCCESS; 
1802 }
1803
1804 /* steals the peer object */
1805 TRP_RC trps_add_peer(TRPS_INSTANCE *trps, TRP_PEER *peer)
1806 {
1807   if (trps->ptable==NULL) {
1808     trps->ptable=trp_ptable_new(trps);
1809     if (trps->ptable==NULL)
1810       return TRP_NOMEM;
1811   }
1812   return trp_ptable_add(trps->ptable, peer);
1813 }
1814
1815 TRP_PEER *trps_get_peer_by_gssname(TRPS_INSTANCE *trps, TR_NAME *gssname)
1816 {
1817   if (trps->ptable==NULL)
1818     return NULL;
1819
1820   return trp_ptable_find_gss_name(trps->ptable, gssname);
1821 }
1822
1823 TRP_PEER *trps_get_peer_by_servicename(TRPS_INSTANCE *trps, TR_NAME *servicename)
1824 {
1825   if (trps->ptable==NULL)
1826     return NULL;
1827
1828   return trp_ptable_find_servicename(trps->ptable, servicename);
1829 }
1830
1831 int trps_peer_connected(TRPS_INSTANCE *trps, TRP_PEER *peer)
1832 {
1833   TRPC_INSTANCE *trpc=trps_find_trpc(trps, peer);
1834   if (trpc==NULL)
1835     return 0;
1836
1837   if (trpc_get_status(trpc)==TRP_CONNECTION_UP)
1838     return 1;
1839   else
1840     return 0;
1841 }
1842
1843
1844 static TRP_RC trps_handle_request(TRPS_INSTANCE *trps, TRP_REQ *req)
1845 {
1846   TR_NAME *comm=NULL;
1847   TR_NAME *realm=NULL;
1848
1849   tr_debug("trps_handle_request: handling TRP request.");
1850
1851   if (trps_validate_request(trps, req) != TRP_SUCCESS) {
1852     tr_notice("trps_handle_request: received invalid TRP request.");
1853     return TRP_ERROR;
1854   }
1855
1856   if (!trp_req_is_wildcard(req)) {
1857     comm=trp_req_get_comm(req);
1858     realm=trp_req_get_realm(req);
1859     tr_debug("trps_handle_request: route for %.*s/%.*s requested.",
1860              comm->len, comm->buf, realm->len, realm->buf);
1861   } else {
1862     tr_debug("trps_handle_request: all routes requested.");
1863     /* leave comm/realm NULL */
1864   }
1865   return trps_update_one_peer(trps,
1866                               trps_get_peer_by_gssname(trps, trp_req_get_peer(req)),
1867                               TRP_UPDATE_REQUESTED,
1868                               realm,
1869                               comm);
1870 }
1871
1872
1873 TRP_RC trps_handle_tr_msg(TRPS_INSTANCE *trps, TR_MSG *tr_msg)
1874 {
1875   TRP_RC rc=TRP_ERROR;
1876
1877   switch (tr_msg_get_msg_type(tr_msg)) {
1878   case TRP_UPDATE:
1879     rc=trps_handle_update(trps, tr_msg_get_trp_upd(tr_msg));
1880     if (rc==TRP_SUCCESS) {
1881       rc=trps_update_active_routes(trps);
1882       trps_update(trps, TRP_UPDATE_TRIGGERED); /* send any triggered routes */
1883     }
1884     return rc;
1885
1886   case TRP_REQUEST:
1887     rc=trps_handle_request(trps, tr_msg_get_trp_req(tr_msg));
1888     return rc;
1889
1890   default:
1891     /* unknown error or one we don't care about (e.g., TID messages) */
1892     return TRP_ERROR;
1893   }
1894 }
1895
1896 /* send wildcard route request to a peer */
1897 TRP_RC trps_wildcard_route_req(TRPS_INSTANCE *trps, TR_NAME *peer_servicename)
1898 {
1899   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1900   TRP_PEER *peer=trps_get_peer_by_servicename(trps, peer_servicename);
1901   TR_MSG msg; /* not a pointer */
1902   TRP_REQ *req=trp_req_new(tmp_ctx);
1903   char *encoded=NULL;
1904   TRP_RC rc=TRP_ERROR;
1905
1906   if (peer==NULL) {
1907     tr_err("trps_wildcard_route_req: unknown peer (%.*s).", peer_servicename->len, peer_servicename->buf);
1908     rc=TRP_BADARG;
1909     goto cleanup;
1910   }
1911   if ((req==NULL) || (trp_req_make_wildcard(req)!=TRP_SUCCESS)) {
1912     tr_err("trps_wildcard_route_req: unable to create wildcard TRP request.");
1913     rc=TRP_NOMEM;
1914     goto cleanup;
1915   }
1916
1917   tr_msg_set_trp_req(&msg, req);
1918   encoded= tr_msg_encode(NULL, &msg);
1919   if (encoded==NULL) {
1920     tr_err("trps_wildcard_route_req: error encoding wildcard TRP request.");
1921     rc=TRP_ERROR;
1922     goto cleanup;
1923   }
1924
1925   tr_debug("trps_wildcard_route_req: adding message to queue.");
1926   if (trps_send_msg(trps, peer, encoded) != TRP_SUCCESS) {
1927     tr_err("trps_wildcard_route_req: error queueing request.");
1928     rc=TRP_ERROR;
1929   } else {
1930     tr_debug("trps_wildcard_route_req: request queued successfully.");
1931     rc=TRP_SUCCESS;
1932   }
1933
1934 cleanup:
1935   if (encoded!=NULL)
1936     tr_msg_free_encoded(encoded);
1937   if (req!=NULL)
1938     trp_req_free(req);
1939
1940   talloc_free(tmp_ctx);
1941   return rc;
1942 }