Refactoring to enable community flooding. Partially tested.
[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
41 #include <gsscon.h>
42 #include <tr_apc.h>
43 #include <tr_rp.h>
44 #include <trust_router/tr_name.h>
45 #include <trp_internal.h>
46 #include <tr_gss.h>
47 #include <trp_ptable.h>
48 #include <trp_rtable.h>
49 #include <tr_debug.h>
50
51
52 static int trps_destructor(void *object)
53 {
54   TRPS_INSTANCE *trps=talloc_get_type_abort(object, TRPS_INSTANCE);
55   if (trps->rtable!=NULL)
56     trp_rtable_free(trps->rtable);
57   return 0;
58 }
59
60 TRPS_INSTANCE *trps_new (TALLOC_CTX *mem_ctx)
61 {
62   TRPS_INSTANCE *trps=talloc(mem_ctx, TRPS_INSTANCE);
63   if (trps!=NULL)  {
64     trps->hostname=NULL;
65     trps->port=0;
66     trps->cookie=NULL;
67     trps->conn=NULL;
68     trps->trpc=NULL;
69     trps->update_interval=(struct timeval){0,0};
70     trps->sweep_interval=(struct timeval){0,0};
71     trps->ptable=NULL;
72
73     trps->mq=tr_mq_new(trps);
74     if (trps->mq==NULL) {
75       /* failed to allocate mq */
76       talloc_free(trps);
77       return NULL;
78     }
79
80     trps->rtable=NULL;
81     if (trps_init_rtable(trps) != TRP_SUCCESS) {
82       /* failed to allocate rtable */
83       talloc_free(trps);
84       return NULL;
85     }
86
87     talloc_set_destructor((void *)trps, trps_destructor);
88   }
89   return trps;
90 }
91
92 /* create a new route table, first discarding an old one if necessary */
93 TRP_RC trps_init_rtable(TRPS_INSTANCE *trps)
94 {
95   if (trps->rtable != NULL) {
96     trp_rtable_free(trps->rtable);
97     trps->rtable=NULL;
98   }
99
100   trps->rtable=trp_rtable_new();
101   if (trps->rtable==NULL) {
102     return TRP_NOMEM;
103   }
104   return TRP_SUCCESS;
105 }
106
107 void trps_clear_rtable(TRPS_INSTANCE *trps)
108 {
109   trp_rtable_clear(trps->rtable);
110 }
111
112 void trps_free (TRPS_INSTANCE *trps)
113 {
114   if (trps!=NULL)
115     talloc_free(trps);
116 }
117
118 TR_MQ_MSG *trps_mq_pop(TRPS_INSTANCE *trps)
119 {
120   return tr_mq_pop(trps->mq);
121 }
122
123 void trps_mq_add(TRPS_INSTANCE *trps, TR_MQ_MSG *msg)
124 {
125   tr_mq_add(trps->mq, msg);
126 }
127
128 unsigned int trps_get_connect_interval(TRPS_INSTANCE *trps)
129 {
130   return trps->connect_interval.tv_sec;
131 }
132
133 void trps_set_connect_interval(TRPS_INSTANCE *trps, unsigned int interval)
134 {
135   trps->connect_interval.tv_sec=interval;
136   trps->connect_interval.tv_usec=0;
137 }
138
139 unsigned int trps_get_update_interval(TRPS_INSTANCE *trps)
140 {
141   return trps->update_interval.tv_sec;
142 }
143
144 void trps_set_update_interval(TRPS_INSTANCE *trps, unsigned int interval)
145 {
146   trps->update_interval.tv_sec=interval;
147   trps->update_interval.tv_usec=0;
148 }
149
150 unsigned int trps_get_sweep_interval(TRPS_INSTANCE *trps)
151 {
152   return trps->sweep_interval.tv_sec;
153 }
154
155 void trps_set_sweep_interval(TRPS_INSTANCE *trps, unsigned int interval)
156 {
157   trps->sweep_interval.tv_sec=interval;
158   trps->sweep_interval.tv_usec=0;
159 }
160
161 void trps_set_ctable(TRPS_INSTANCE *trps, TR_COMM_TABLE *comm)
162 {
163   if (trps->ctable!=NULL)
164     tr_comm_table_free(trps->ctable);
165   trps->ctable=comm;
166 }
167
168 void trps_set_ptable(TRPS_INSTANCE *trps, TRP_PTABLE *ptable)
169 {
170   if (trps->ptable!=NULL)
171     trp_ptable_free(trps->ptable);
172   trps->ptable=ptable;
173 }
174
175 void trps_set_peer_status_callback(TRPS_INSTANCE *trps, void (*cb)(TRP_PEER *, void *), void *cookie)
176 {
177   TRP_PTABLE_ITER *iter=NULL;
178   TRP_PEER *peer=NULL;
179   if (trps->ptable==NULL)
180     return;
181
182   iter=trp_ptable_iter_new(NULL);
183   for (peer=trp_ptable_iter_first(iter, trps->ptable); peer!=NULL; peer=trp_ptable_iter_next(iter))
184     trp_peer_set_conn_status_cb(peer, cb, cookie);
185   trp_ptable_iter_free(iter);
186 }
187
188 TRPC_INSTANCE *trps_find_trpc(TRPS_INSTANCE *trps, TRP_PEER *peer)
189 {
190   TRPC_INSTANCE *cur=NULL;
191   TR_NAME *name=NULL;
192   TR_NAME *peer_servicename=trp_peer_get_servicename(peer);
193
194   for (cur=trps->trpc; cur!=NULL; cur=trpc_get_next(cur)) {
195     name=trpc_get_gssname(cur);
196     if ((name!=NULL) && (0==tr_name_cmp(peer_servicename, name))) {
197       break;
198     }
199   }
200   return cur;
201 }
202
203 void trps_add_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *new)
204 {
205   if (trps->conn==NULL)
206     trps->conn=new;
207   else
208     trp_connection_append(trps->conn, new);
209
210   talloc_steal(trps, new);
211 }
212
213 /* ok to call more than once; guarantees connection no longer in the list.
214  * Caller is responsible for freeing the removed element afterwards.  */
215 void trps_remove_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *remove)
216 {
217   trps->conn=trp_connection_remove(trps->conn, remove);
218 }
219
220 void trps_add_trpc(TRPS_INSTANCE *trps, TRPC_INSTANCE *trpc)
221 {
222   if (trps->trpc==NULL)
223     trps->trpc=trpc;
224   else
225     trpc_append(trps->trpc, trpc);
226
227   talloc_steal(trps, trpc);
228 }
229
230 /* ok to call more than once; guarantees trpc no longer in the list.
231  * Caller is responsible for freeing the removed element afterwards.  */
232 void trps_remove_trpc(TRPS_INSTANCE *trps, TRPC_INSTANCE *remove)
233 {
234   trps->trpc=trpc_remove(trps->trpc, remove);
235 }
236
237 TRP_RC trps_send_msg(TRPS_INSTANCE *trps, TRP_PEER *peer, const char *msg)
238 {
239   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
240   TR_MQ_MSG *mq_msg=NULL;
241   char *msg_dup=NULL;
242   TRP_RC rc=TRP_ERROR;
243   TRPC_INSTANCE *trpc=NULL;
244
245   /* get the connection for this peer */
246   trpc=trps_find_trpc(trps, peer);
247   /* instead, let's let that happen and then clear the queue when an attempt to
248    * connect fails */
249   if (trpc==NULL) {
250     tr_warning("trps_send_msg: skipping message queued for missing TRP client entry.");
251   } else {
252     mq_msg=tr_mq_msg_new(tmp_ctx, TR_MQMSG_TRPC_SEND, TR_MQ_PRIO_NORMAL);
253     msg_dup=talloc_strdup(mq_msg, msg); /* get local copy in mq_msg context */
254     tr_mq_msg_set_payload(mq_msg, msg_dup, NULL); /* no need for a free() func */
255     trpc_mq_add(trpc, mq_msg);
256     rc=TRP_SUCCESS;
257   }
258   talloc_free(tmp_ctx);
259   return rc;
260 }
261
262 static int trps_listen (TRPS_INSTANCE *trps, int port) 
263 {
264   int rc = 0;
265   int conn = -1;
266   int optval = 1;
267
268   union {
269     struct sockaddr_storage storage;
270     struct sockaddr_in in4;
271   } addr;
272
273   struct sockaddr_in *saddr = (struct sockaddr_in *) &addr.in4;
274
275   saddr->sin_port = htons (port);
276   saddr->sin_family = AF_INET;
277   saddr->sin_addr.s_addr = INADDR_ANY;
278
279   if (0 > (conn = socket (AF_INET, SOCK_STREAM, 0)))
280     return conn;
281
282   setsockopt(conn, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
283
284   if (0 > (rc = bind (conn, (struct sockaddr *) saddr, sizeof(struct sockaddr_in))))
285     return rc;
286
287   if (0 > (rc = listen(conn, 512)))
288     return rc;
289
290   tr_debug("trps_listen: TRP Server listening on port %d", port);
291   return conn;
292 }
293
294 /* get the currently selected route if available */
295 TRP_ROUTE *trps_get_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer)
296 {
297   return trp_rtable_get_entry(trps->rtable, comm, realm, peer);
298 }
299
300 TRP_ROUTE *trps_get_selected_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm)
301 {
302   tr_debug("trps_get_selected_route: entered. trps=%p, comm=%p, realm=%p", trps, comm, realm);
303   return trp_rtable_get_selected_entry(trps->rtable, comm, realm);
304 }
305
306 /* copy the result if you want to keep it */
307 TR_NAME *trps_get_next_hop(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm)
308 {
309   TRP_ROUTE *route=trps_get_selected_route(trps, comm, realm);
310   if (route==NULL)
311     return NULL;
312
313   return trp_route_get_next_hop(route);
314 }
315
316
317 /* mark a route as retracted */
318 static void trps_retract_route(TRPS_INSTANCE *trps, TRP_ROUTE *entry)
319 {
320   trp_route_set_metric(entry, TRP_METRIC_INFINITY);
321   trp_route_set_triggered(entry, 1);
322 }
323
324 /* is this route retracted? */
325 static int trps_route_retracted(TRPS_INSTANCE *trps, TRP_ROUTE *entry)
326 {
327   return (trp_metric_is_infinite(trp_route_get_metric(entry)));
328 }
329
330 static TRP_RC trps_read_message(TRPS_INSTANCE *trps, TRP_CONNECTION *conn, TR_MSG **msg)
331 {
332   int err=0;
333   char *buf=NULL;
334   size_t buflen = 0;
335   TRP_PEER *peer=NULL; /* entry in the peer table */
336   TR_NAME *conn_peer=NULL; /* name from the TRP_CONN, which comes from the gss context */
337
338   tr_debug("trps_read_message: started");
339   if (err = gsscon_read_encrypted_token(trp_connection_get_fd(conn),
340                                        *(trp_connection_get_gssctx(conn)), 
341                                        &buf,
342                                        &buflen)) {
343     tr_debug("trps_read_message: error");
344     if (buf)
345       free(buf);
346     return TRP_ERROR;
347   }
348
349   tr_debug("trps_read_message: message received, %u bytes.", (unsigned) buflen);
350   tr_debug("trps_read_message: %.*s", buflen, buf);
351
352   *msg=tr_msg_decode(buf, buflen);
353   free(buf);
354   if (*msg==NULL)
355     return TRP_NOPARSE;
356
357   conn_peer=trp_connection_get_peer(conn);
358   if (conn_peer==NULL) {
359     tr_err("trps_read_message: connection has no peer name");
360     return TRP_ERROR;
361   }
362
363   peer=trps_get_peer_by_gssname(trps, conn_peer);
364   if (peer==NULL) {
365     tr_err("trps_read_message: could not find peer with gssname=%s", trp_connection_get_gssname(conn));
366     return TRP_ERROR;
367   }
368
369   /* verify we received a message we support, otherwise drop it now */
370   switch (tr_msg_get_msg_type(*msg)) {
371   case TRP_UPDATE:
372     trp_upd_set_peer(tr_msg_get_trp_upd(*msg), tr_dup_name(conn_peer));
373     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 */
374     break;
375
376   case TRP_REQUEST:
377     trp_req_set_peer(tr_msg_get_trp_req(*msg), tr_dup_name(conn_peer));
378     break;
379
380   default:
381     tr_debug("trps_read_message: received unsupported message from %.*s", conn_peer->len, conn_peer->buf);
382     tr_msg_free_decoded(*msg);
383     *msg=NULL;
384     return TRP_UNSUPPORTED;
385   }
386   
387   return TRP_SUCCESS;
388 }
389
390 int trps_get_listener(TRPS_INSTANCE *trps,
391                       TRPS_MSG_FUNC msg_handler,
392                       TRP_AUTH_FUNC auth_handler,
393                       const char *hostname,
394                       unsigned int port,
395                       void *cookie)
396 {
397   int listen = -1;
398
399   if (0 > (listen = trps_listen(trps, port))) {
400     char errbuf[256];
401     if (0 == strerror_r(errno, errbuf, 256)) {
402       tr_debug("trps_get_listener: Error opening port %d: %s.", port, errbuf);
403     } else {
404       tr_debug("trps_get_listener: Unknown error openining port %d.", port);
405     }
406   } 
407
408   if (listen > 0) {
409     /* opening port succeeded */
410     tr_debug("trps_get_listener: Opened port %d.", port);
411     
412     /* make this socket non-blocking */
413     if (0 != fcntl(listen, F_SETFL, O_NONBLOCK)) {
414       tr_debug("trps_get_listener: Error setting O_NONBLOCK.");
415       close(listen);
416       listen=-1;
417     }
418   }
419
420   if (listen > 0) {
421     /* store the caller's request handler & cookie */
422     trps->msg_handler = msg_handler;
423     trps->auth_handler = auth_handler;
424     trps->hostname = talloc_strdup(trps, hostname);
425     trps->port = port;
426     trps->cookie = cookie;
427   }
428
429   return listen;
430 }
431
432 TRP_RC trps_authorize_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *conn)
433 {
434   /* try to establish a GSS context */
435   if (0!=trp_connection_auth(conn, trps->auth_handler, trps->cookie)) {
436     tr_notice("trps_authorize_connection: failed to authorize connection");
437     trp_connection_close(conn);
438     return TRP_ERROR;
439   }
440   tr_notice("trps_authorize_connection: authorized connection");
441   return TRP_SUCCESS;
442 }
443
444 void trps_handle_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *conn)
445 {
446   TR_MSG *msg=NULL;
447   TRP_RC rc=TRP_ERROR;
448
449   /* loop as long as the connection exists */
450   while (trp_connection_get_status(conn)==TRP_CONNECTION_UP) {
451     rc=trps_read_message(trps, conn, &msg);
452     switch(rc) {
453     case TRP_SUCCESS:
454       trps->msg_handler(trps, conn, msg); /* send the TR_MSG off to the callback */
455       break;
456
457     case TRP_ERROR:
458       trp_connection_close(conn);
459       break;
460
461     default:
462       tr_debug("trps_handle_connection: trps_read_message failed (%d)", rc);
463     }
464   }
465
466   tr_debug("trps_handle_connection: connection closed.");
467 }
468
469 /* TODO: check realm/comm, now part of the update instead of inforec */
470 static TRP_RC trps_validate_update(TRPS_INSTANCE *trps, TRP_UPD *upd)
471 {
472   if (upd==NULL) {
473     tr_notice("trps_validate_update: null TRP update.");
474     return TRP_BADARG;
475   }
476
477   if (trp_upd_get_realm(upd)==NULL) {
478     tr_notice("trps_validate_update: received TRP update without realm.");
479     return TRP_ERROR;
480   }
481
482   if (trp_upd_get_comm(upd)==NULL) {
483     tr_notice("trps_validate_update: received TRP update without community.");
484     return TRP_ERROR;
485   }
486
487   if (trp_upd_get_inforec(upd)==NULL) {
488     tr_notice("trps_validate_update: received TRP update with no info records.");
489     return TRP_ERROR;
490   }
491
492   if (trp_upd_get_peer(upd)==NULL) {
493     tr_notice("trps_validate_update: received TRP update without origin peer information.");
494     return TRP_ERROR;
495   }
496
497   
498   return TRP_SUCCESS;
499 }
500
501 /* ensure that the update could be accepted if feasible */
502 static TRP_RC trps_validate_inforec(TRPS_INSTANCE *trps, TRP_INFOREC *rec)
503 {
504   switch(trp_inforec_get_type(rec)) {
505   case TRP_INFOREC_TYPE_ROUTE:
506     if ((trp_inforec_get_trust_router(rec)==NULL)
507        || (trp_inforec_get_next_hop(rec)==NULL)) {
508       tr_debug("trps_validate_inforec: missing record info.");
509       return TRP_ERROR;
510     }
511
512     /* check for valid metric */
513     if (trp_metric_is_invalid(trp_inforec_get_metric(rec))) {
514       tr_debug("trps_validate_inforec: invalid metric (%u).", trp_inforec_get_metric(rec));
515       return TRP_ERROR;
516     }
517
518     /* check for valid interval */
519     if (trp_inforec_get_interval(rec)==TRP_INTERVAL_INVALID) {
520       tr_debug("trps_validate_inforec: invalid interval.");
521       return TRP_ERROR;
522     }
523     break;
524
525   default:
526     tr_notice("trps_validate_inforec: unsupported record type.");
527     return TRP_UNSUPPORTED;
528   }
529
530   return TRP_SUCCESS;
531 }
532
533 /* link cost to a peer */
534 static unsigned int trps_cost(TRPS_INSTANCE *trps, TR_NAME *peer)
535 {
536   return 1;
537 }
538
539 static unsigned int trps_advertised_metric(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer)
540 {
541   TRP_ROUTE *entry=trp_rtable_get_entry(trps->rtable, comm, realm, peer);
542   if (entry==NULL)
543     return TRP_METRIC_INFINITY;
544   return trp_route_get_metric(entry) + trps_cost(trps, peer);
545 }
546
547 static int trps_check_feasibility(TRPS_INSTANCE *trps, TR_NAME *realm, TR_NAME *comm, TRP_INFOREC *rec)
548 {
549   unsigned int rec_metric=trp_inforec_get_metric(rec);
550   unsigned int new_metric=0;
551   unsigned int current_metric=0;
552   TR_NAME *next_hop=NULL;
553
554   /* we check these in the validation stage, but just in case... */
555   if (trp_metric_is_invalid(rec_metric))
556     return 0;
557
558   /* retractions (aka infinite metrics) are always feasible */
559   if (trp_metric_is_infinite(rec_metric))
560     return 1;
561
562   /* updates from our current next hop are always feasible*/
563   next_hop=trps_get_next_hop(trps, comm, realm);
564   if ((next_hop!=NULL)
565      && (0==tr_name_cmp(next_hop,trp_inforec_get_next_hop(rec)))) {
566     return 1;
567   }
568     
569
570   /* compare the existing metric we advertise to what we would advertise
571    * if we accept this update */
572   current_metric=trps_advertised_metric(trps, comm, realm, trp_inforec_get_next_hop(rec));
573   new_metric=rec_metric + trps_cost(trps, trp_inforec_get_next_hop(rec));
574   if (new_metric <= current_metric)
575     return 1;
576   else
577     return 0;
578 }
579
580 /* uses memory pointed to by *ts, also returns that value. On error, its contents are {0,0} */
581 static struct timespec *trps_compute_expiry(TRPS_INSTANCE *trps, unsigned int interval, struct timespec *ts)
582 {
583   const unsigned int small_factor=3; /* how many intervals we wait before expiring */
584   if (0!=clock_gettime(CLOCK_REALTIME, ts)) {
585     tr_err("trps_compute_expiry: could not read realtime clock.");
586     ts->tv_sec=0;
587     ts->tv_nsec=0;
588   }
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 static TRP_RC trps_handle_update(TRPS_INSTANCE *trps, TRP_UPD *upd)
653 {
654   unsigned int feas=0;
655   TRP_INFOREC *rec=NULL;
656   TRP_ROUTE *route=NULL;
657
658   if (trps_validate_update(trps, upd) != TRP_SUCCESS) {
659     tr_notice("trps_handle_update: received invalid TRP update.");
660     return TRP_ERROR;
661   }
662
663   for (rec=trp_upd_get_inforec(upd); rec!=NULL; rec=trp_inforec_get_next(rec)) {
664     /* validate/sanity check the record update */
665     if (trps_validate_inforec(trps, rec) != TRP_SUCCESS) {
666       tr_notice("trps_handle_update: invalid record in TRP update, discarding entire update.");
667       return TRP_ERROR;
668     }
669   }
670
671   for (rec=trp_upd_get_inforec(upd); rec!=NULL; rec=trp_inforec_get_next(rec)) {
672     /* determine feasibility */
673     feas=trps_check_feasibility(trps, trp_upd_get_realm(upd), trp_upd_get_comm(upd), rec);
674     tr_debug("trps_handle_update: record feasibility=%d", feas);
675
676     /* do we have an existing route? */
677     route=trps_get_route(trps,
678                          trp_upd_get_comm(upd),
679                          trp_upd_get_realm(upd),
680                          trp_upd_get_peer(upd));
681     if (route!=NULL) {
682       /* there was a route table entry already */
683       tr_debug("trps_handle_updates: route entry already exists.");
684       if (feas) {
685         /* Update is feasible. Accept it. */
686         trps_accept_update(trps, upd, rec);
687       } else {
688         /* Update is infeasible. Ignore it unless the trust router has changed. */
689         if (0!=tr_name_cmp(trp_route_get_trust_router(route),
690                            trp_inforec_get_trust_router(rec))) {
691           /* the trust router associated with the route has changed, treat update as a retraction */
692           trps_retract_route(trps, route);
693         }
694       }
695     } else {
696       /* No existing route table entry. Ignore it unless it is feasible and not a retraction. */
697       tr_debug("trps_handle_update: no route entry exists yet.");
698       if (feas && trp_metric_is_finite(trp_inforec_get_metric(rec)))
699         trps_accept_update(trps, upd, rec);
700     }
701   }
702   return TRP_SUCCESS;
703 }
704
705 static TRP_RC trps_validate_request(TRPS_INSTANCE *trps, TRP_REQ *req)
706 {
707   if (req==NULL) {
708     tr_notice("trps_validate_request: null TRP request.");
709     return TRP_BADARG;
710   }
711
712   if (trp_req_get_comm(req)==NULL) {
713     tr_notice("trps_validate_request: received TRP request with null community.");
714     return TRP_ERROR;
715   }
716   
717   if (trp_req_get_realm(req)==NULL) {
718     tr_notice("trps_validate_request: received TRP request with null realm.");
719     return TRP_ERROR;
720   }
721   
722   if (trp_req_get_peer(req)==NULL) {
723     tr_notice("trps_validate_request: received TRP request without origin peer information.");
724     return TRP_ERROR;
725   }
726   
727   return TRP_SUCCESS;
728 }
729
730 /* choose the best route to comm/realm, optionally excluding routes to a particular peer */
731 static TRP_ROUTE *trps_find_best_route(TRPS_INSTANCE *trps,
732                                         TR_NAME *comm,
733                                         TR_NAME *realm,
734                                         TR_NAME *exclude_peer)
735 {
736   TRP_ROUTE **entry=NULL;
737   TRP_ROUTE *best=NULL;
738   size_t n_entry=0;
739   unsigned int kk=0;
740   unsigned int kk_min=0;
741   unsigned int min_metric=TRP_METRIC_INFINITY;
742
743   entry=trp_rtable_get_realm_entries(trps->rtable, comm, realm, &n_entry);
744   for (kk=0; kk<n_entry; kk++) {
745     if (trp_route_get_metric(entry[kk]) < min_metric) {
746       if ((exclude_peer==NULL) || (0!=tr_name_cmp(trp_route_get_peer(entry[kk]),
747                                                   exclude_peer))) {
748         kk_min=kk;
749         min_metric=trp_route_get_metric(entry[kk]);
750       } 
751     }
752   }
753   if (trp_metric_is_finite(min_metric))
754     best=entry[kk_min];
755   
756   talloc_free(entry);
757   return best;
758 }
759
760 /* TODO: think this through more carefully. At least ought to add hysteresis
761  * to avoid flapping between routers or routes. */
762 TRP_RC trps_update_active_routes(TRPS_INSTANCE *trps)
763 {
764   size_t n_comm=0, ii=0;
765   TR_NAME **comm=trp_rtable_get_comms(trps->rtable, &n_comm);
766   size_t n_realm=0, jj=0;
767   TR_NAME **realm=NULL;
768   TRP_ROUTE *best_route=NULL, *cur_route=NULL;
769   unsigned int best_metric=0, cur_metric=0;
770
771   for (ii=0; ii<n_comm; ii++) {
772     realm=trp_rtable_get_comm_realms(trps->rtable, comm[ii], &n_realm);
773     for (jj=0; jj<n_realm; jj++) {
774       best_route=trps_find_best_route(trps, comm[ii], realm[jj], NULL);
775       if (best_route==NULL)
776         best_metric=TRP_METRIC_INFINITY;
777       else
778         best_metric=trp_route_get_metric(best_route);
779
780       cur_route=trps_get_selected_route(trps, comm[ii], realm[jj]);
781       if (cur_route!=NULL) {
782         cur_metric=trp_route_get_metric(cur_route);
783         if ((best_metric < cur_metric) && (trp_metric_is_finite(best_metric))) {
784           /* The new route has a lower metric than the previous, and is finite. Accept. */
785           trp_route_set_selected(cur_route, 0);
786           trp_route_set_selected(best_route, 1);
787         } else if (!trp_metric_is_finite(cur_metric)) /* rejects infinite or invalid metrics */
788           trp_route_set_selected(cur_route, 0);
789       } else if (trp_metric_is_finite(best_metric)) {
790         trp_route_set_selected(best_route, 1);
791       }
792     }
793     if (realm!=NULL)
794       talloc_free(realm);
795     realm=NULL; n_realm=0;
796   }
797   if (comm!=NULL)
798     talloc_free(comm);
799   comm=NULL; n_comm=0;
800
801   return TRP_SUCCESS;
802 }
803
804 /* true if curtime >= expiry */
805 static int trps_expired(struct timespec *expiry, struct timespec *curtime)
806 {
807   return ((curtime->tv_sec > expiry->tv_sec)
808          || ((curtime->tv_sec == expiry->tv_sec)
809             &&(curtime->tv_nsec >= expiry->tv_nsec)));
810 }
811
812 /* Sweep for expired routes. For each expired route, if its metric is infinite, the route is flushed.
813  * If its metric is finite, the metric is set to infinite and the route's expiration time is updated. */
814 TRP_RC trps_sweep_routes(TRPS_INSTANCE *trps)
815 {
816   struct timespec sweep_time={0,0};
817   TRP_ROUTE **entry=NULL;
818   size_t n_entry=0;
819   size_t ii=0;
820
821   /* use a single time for the entire sweep */
822   if (0!=clock_gettime(CLOCK_REALTIME, &sweep_time)) {
823     tr_err("trps_sweep_routes: could not read realtime clock.");
824     sweep_time.tv_sec=0;
825     sweep_time.tv_nsec=0;
826     return TRP_ERROR;
827   }
828
829   entry=trp_rtable_get_entries(trps->rtable, &n_entry); /* must talloc_free *entry */
830
831   /* loop over the entries */
832   for (ii=0; ii<n_entry; ii++) {
833     if (!trp_route_is_local(entry[ii]) && trps_expired(trp_route_get_expiry(entry[ii]), &sweep_time)) {
834       tr_debug("trps_sweep_routes: route expired.");
835       if (!trp_metric_is_finite(trp_route_get_metric(entry[ii]))) {
836         /* flush route */
837         tr_debug("trps_sweep_routes: metric was infinity, flushing route.");
838         trp_rtable_remove(trps->rtable, entry[ii]); /* entry[ii] is no longer valid */
839         entry[ii]=NULL;
840       } else {
841         /* set metric to infinity and reset timer */
842         tr_debug("trps_sweep_routes: setting metric to infinity and resetting expiry.");
843         trp_route_set_metric(entry[ii], TRP_METRIC_INFINITY);
844         trp_route_set_expiry(entry[ii], trps_compute_expiry(trps,
845                                                              trp_route_get_interval(entry[ii]),
846                                                              trp_route_get_expiry(entry[ii])));
847       }
848     }
849   }
850
851   talloc_free(entry);
852   return TRP_SUCCESS;
853 }
854
855 /* select the correct route to comm/realm to be announced to peer */
856 static TRP_ROUTE *trps_select_realm_update(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer_gssname)
857 {
858   TRP_ROUTE *route;
859
860   /* Take the currently selected route unless it is through the peer we're sending the update to.
861    * I.e., enforce the split horizon rule. */
862   route=trp_rtable_get_selected_entry(trps->rtable, comm, realm);
863   if (route==NULL) {
864     /* No selected route, this should only happen if the only route has been retracted,
865      * in which case we do not want to advertise it. */
866     return NULL;
867   }
868   tr_debug("trps_select_realm_update: %s vs %s", peer_gssname->buf,
869            trp_route_get_peer(route)->buf);
870   if (0==tr_name_cmp(peer_gssname, trp_route_get_peer(route))) {
871     tr_debug("trps_select_realm_update: matched, finding alternate route");
872     /* the selected entry goes through the peer we're reporting to, choose an alternate */
873     route=trps_find_best_route(trps, comm, realm, peer_gssname);
874     if ((route==NULL) || (!trp_metric_is_finite(trp_route_get_metric(route))))
875       return NULL; /* don't advertise a nonexistent or retracted route */
876   }
877   return route;
878 }
879
880 /* returns an array of pointers to updates (*not* an array of updates). Returns number of entries
881  * via n_update parameter. (The allocated space will generally be larger than required, see note in
882  * the code.) If triggered is set, sends only triggered updates. */
883 static TRP_ROUTE **trps_select_updates_for_peer(TALLOC_CTX *memctx,
884                                                  TRPS_INSTANCE *trps,
885                                                  TR_NAME *peer_gssname,
886                                                  int triggered,
887                                                  size_t *n_update)
888 {
889   size_t n_comm=0;
890   TR_NAME **comm=trp_rtable_get_comms(trps->rtable, &n_comm);
891   TR_NAME **realm=NULL;
892   size_t n_realm=0;
893   size_t ii=0, jj=0;
894   TRP_ROUTE *best=NULL;
895   TRP_ROUTE **result=NULL;
896   size_t n_used=0;
897
898   /* Need to allocate space for the results. For simplicity, we just allocate a block
899    * with space for every route table entry to be returned. This is guaranteed to be large
900    * enough. If the routing table gets very large, this may be wasteful, but that seems
901    * unlikely to be significant in the near future. */
902   result=talloc_array(memctx, TRP_ROUTE *, trp_rtable_size(trps->rtable));
903   if (result==NULL) {
904     talloc_free(comm);
905     *n_update=0;
906     return NULL;
907   }
908   
909   for (ii=0; ii<n_comm; ii++) {
910     realm=trp_rtable_get_comm_realms(trps->rtable, comm[ii], &n_realm);
911     for (jj=0; jj<n_realm; jj++) {
912       best=trps_select_realm_update(trps, comm[ii], realm[jj], peer_gssname);
913       /* If we found a route, add it to the list. If triggered!=0, then only
914        * add triggered routes. */
915       if ((best!=NULL) && ((!triggered) || trp_route_is_triggered(best)))
916         result[n_used++]=best;
917     }
918     if (realm!=NULL)
919       talloc_free(realm);
920     realm=NULL;
921     n_realm=0;
922   }
923   if (comm!=NULL)
924     talloc_free(comm);
925
926   *n_update=n_used;
927   return result;
928 }
929
930 /* add metrics */
931 static unsigned int trps_metric_add(unsigned int m1, unsigned int m2)
932 {
933   if (trp_metric_is_invalid(m1) || trp_metric_is_invalid(m2))
934     return TRP_METRIC_INVALID;
935
936   if (trp_metric_is_infinite(m1) || trp_metric_is_infinite(m2))
937     return TRP_METRIC_INFINITY;
938
939   if (trp_metric_is_finite(m1+m2))
940     return m1+m2;
941   else
942     return TRP_METRIC_INFINITY;
943 }
944
945 /* convert an rentry into a new trp update info record */
946 static TRP_INFOREC *trps_route_to_inforec(TALLOC_CTX *mem_ctx, TRPS_INSTANCE *trps, TRP_ROUTE *route)
947 {
948   TRP_INFOREC *rec=trp_inforec_new(mem_ctx, TRP_INFOREC_TYPE_ROUTE);
949   unsigned int linkcost=0;
950
951   if (rec!=NULL) {
952     if (trp_route_is_local(route))
953       linkcost=0;
954     else {
955       linkcost=trp_peer_get_linkcost(trps_get_peer_by_gssname(trps,
956                                                               trp_route_get_peer(route)));
957     }
958
959     /* Note that we leave the next hop empty since the recipient fills that in.
960      * This is where we add the link cost (currently always 1) to the next peer. */
961     if ((trp_inforec_set_trust_router(rec, trp_route_dup_trust_router(route)) != TRP_SUCCESS)
962        ||(trp_inforec_set_metric(rec,
963                                  trps_metric_add(trp_route_get_metric(route),
964                                                  linkcost)) != TRP_SUCCESS)
965        ||(trp_inforec_set_interval(rec, trps_get_update_interval(trps)) != TRP_SUCCESS)) {
966       tr_err("trps_route_to_inforec: error creating route update.");
967       talloc_free(rec);
968       rec=NULL;
969     }
970   }
971   return rec;
972 }
973
974 /* Every realm has a community, so always returns at least one record except on error. */
975 static TRP_INFOREC *trps_comm_inforecs_for_realm(TALLOC_CTX *mem_ctx, TRPS_INSTANCE *trps, TR_NAME *comm_name, TR_NAME *realm)
976 {
977   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
978 /*  TRP_INFOREC *results=NULL;*/
979 /*  TRP_INFOREC *rec=NULL;*/
980   TR_COMM *this_comm=NULL;
981   TR_COMM_ITER *comm_iter=NULL;
982
983   comm_iter=tr_comm_iter_new(tmp_ctx);
984   for (this_comm=tr_comm_iter_first(comm_iter, trps->ctable, comm_name);
985        this_comm!=NULL;
986        this_comm=tr_comm_iter_next(comm_iter)) {
987     printf("dink");
988   }
989
990   talloc_free(tmp_ctx);
991   return NULL;
992 }
993
994 /* all routes to a single peer, unless comm/realm are specified (both or neither must be NULL) */
995 static TRP_RC trps_update_one_peer(TRPS_INSTANCE *trps,
996                                    TRP_PEER *peer,
997                                    TRP_UPDATE_TYPE update_type,
998                                    TR_NAME *comm,
999                                    TR_NAME *realm)
1000 {
1001   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1002   TR_MSG msg; /* not a pointer! */
1003   TRP_UPD *upd=NULL;
1004   TRP_ROUTE **update_list=NULL;
1005   TRP_INFOREC *rec=NULL;
1006   size_t n_updates=0, ii=0;
1007   char *encoded=NULL;
1008   TRP_RC rc=TRP_ERROR;
1009   TR_NAME *peer_label=trp_peer_get_label(peer);
1010
1011   switch (update_type) {
1012   case TRP_UPDATE_TRIGGERED:
1013     tr_debug("trps_update_one_peer: preparing triggered route update for %.*s",
1014              peer_label->len, peer_label->buf);
1015     break;
1016   case TRP_UPDATE_SCHEDULED:
1017     tr_debug("trps_update_one_peer: preparing scheduled route update for %.*s",
1018              peer_label->len, peer_label->buf);
1019     break;
1020   case TRP_UPDATE_REQUESTED:
1021     tr_debug("trps_update_one_peer: preparing requested route update for %.*s",
1022              peer_label->len, peer_label->buf);
1023   }
1024
1025   /* do not fill in peer, recipient does that */
1026   if ((comm==NULL) && (realm==NULL)) {
1027     /* do all realms */
1028     update_list=trps_select_updates_for_peer(tmp_ctx,
1029                                              trps,
1030                                              peer_label,
1031                                              update_type==TRP_UPDATE_TRIGGERED,
1032                                             &n_updates);
1033   } else if ((comm!=NULL) && (realm!=NULL)) {
1034     /* a single community/realm was requested */
1035     update_list=talloc(tmp_ctx, TRP_ROUTE *);
1036     if (update_list==NULL) {
1037       tr_err("trps_update_one_peer: could not allocate update_list.");
1038       rc=TRP_NOMEM;
1039       goto cleanup;
1040     }
1041     *update_list=trps_select_realm_update(trps, comm, realm, peer_label);
1042     if (*update_list==NULL) {
1043       /* we have no actual update to send back, MUST send a retraction */
1044       tr_debug("trps_update_one_peer: community/realm without route requested, sending mandatory retraction.");
1045       *update_list=trp_route_new(update_list);
1046       trp_route_set_comm(*update_list, tr_dup_name(comm));
1047       trp_route_set_realm(*update_list, tr_dup_name(realm));
1048       trp_route_set_peer(*update_list, tr_new_name(""));
1049       trp_route_set_metric(*update_list, TRP_METRIC_INFINITY);
1050       trp_route_set_trust_router(*update_list, tr_new_name(""));
1051       trp_route_set_next_hop(*update_list, tr_new_name(""));
1052     }
1053     n_updates=1;
1054   } else {
1055     tr_err("trps_update_one_peer: error: only comm or realm was specified. Need both or neither.");
1056     rc=TRP_ERROR;
1057     goto cleanup;
1058   }
1059   if ((n_updates>0) && (update_list!=NULL)) {
1060     tr_debug("trps_update_one_peer: sending %u update messages.", (unsigned int)n_updates);
1061     for (ii=0; ii<n_updates; ii++) {
1062       upd=trp_upd_new(tmp_ctx);
1063       if (upd==NULL) {
1064         tr_err("trps_update_one_peer: could not create update message.");
1065         rc=TRP_NOMEM;
1066         goto cleanup;
1067       }
1068       trp_upd_set_realm(upd, trp_route_dup_realm(update_list[ii]));
1069       if (trp_upd_get_realm(upd)==NULL) {
1070         tr_err("trps_update_one_peer: could not copy realm.");
1071         rc=TRP_NOMEM;
1072         goto cleanup;
1073       }
1074       trp_upd_set_comm(upd, trp_route_dup_comm(update_list[ii]));
1075       if (trp_upd_get_comm(upd)==NULL) {
1076         tr_err("trps_update_one_peer: could not copy comm.");
1077         rc=TRP_NOMEM;
1078         goto cleanup;
1079       }
1080       rec=trps_route_to_inforec(tmp_ctx, trps, update_list[ii]);
1081       if (rec==NULL) {
1082         tr_err("trps_update_one_peer: could not create route info record for realm %.*s in comm %.*s.",
1083                realm->len, realm->buf,
1084                comm->len, comm->buf);
1085         rc=TRP_NOMEM;
1086         goto cleanup;
1087       }
1088       trp_upd_add_inforec(upd, rec);
1089
1090       /* now add community info records */
1091       rec=trps_comm_inforecs_for_realm(tmp_ctx,
1092                                        trps,
1093                                        trp_route_get_comm(update_list[ii]),
1094                                        trp_route_get_realm(update_list[ii]));
1095       if (rec==NULL) {
1096         tr_err("trps_update_one_peer: could not create all update records.");
1097         rc=TRP_NOMEM;
1098         goto cleanup;
1099       }
1100       trp_upd_add_inforec(upd, rec);
1101
1102       /* now encode the update message */
1103       tr_msg_set_trp_upd(&msg, upd);
1104       encoded=tr_msg_encode(&msg);
1105       if (encoded==NULL) {
1106         tr_err("trps_update_one_peer: error encoding update.");
1107         rc=TRP_ERROR;
1108         goto cleanup;
1109       }
1110
1111       tr_debug("trps_update_one_peer: adding message to queue.");
1112       if (trps_send_msg(trps, peer, encoded) != TRP_SUCCESS)
1113         tr_err("trps_update_one_peer: error queueing update.");
1114       else
1115         tr_debug("trps_update_one_peer: update queued successfully.");
1116
1117       tr_msg_free_encoded(encoded);
1118       encoded=NULL;
1119       trp_upd_free(upd);
1120       upd=NULL;
1121     }
1122     talloc_free(update_list);
1123     update_list=NULL;
1124
1125   } else if (n_updates==0)
1126     tr_debug("trps_update_one_peer: no updates for %.*s", peer_label->len, peer_label->buf);
1127
1128   rc=TRP_SUCCESS;
1129
1130 cleanup:
1131   talloc_free(tmp_ctx);
1132   return rc;
1133 }
1134
1135 /* all routes to all peers */
1136 TRP_RC trps_update(TRPS_INSTANCE *trps, TRP_UPDATE_TYPE update_type)
1137 {
1138   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1139   TRP_PTABLE_ITER *iter=trp_ptable_iter_new(tmp_ctx);
1140   TRP_PEER *peer=NULL;
1141   TRP_RC rc=TRP_SUCCESS;
1142
1143   if (trps->ptable==NULL)
1144     return TRP_SUCCESS; /* no peers, nothing to do */
1145
1146   if (iter==NULL) {
1147     tr_err("trps_update: failed to allocate peer table iterator.");
1148     talloc_free(tmp_ctx);
1149     return TRP_NOMEM;
1150   }
1151
1152   for (peer=trp_ptable_iter_first(iter, trps->ptable);
1153        peer!=NULL && rc==TRP_SUCCESS;
1154        peer=trp_ptable_iter_next(iter))
1155   {
1156     if (!trps_peer_connected(trps, peer)) {
1157       TR_NAME *peer_label=trp_peer_get_label(peer);
1158       tr_debug("trps_update: no TRP connection to %.*s, skipping.",
1159                peer_label->len, peer_label->buf);
1160       continue;
1161     }
1162     rc=trps_update_one_peer(trps, peer, update_type, NULL, NULL);
1163   }
1164
1165   tr_debug("trps_update: rc=%u after attempting update.", rc);
1166   trp_ptable_iter_free(iter);
1167   trp_rtable_clear_triggered(trps->rtable); /* don't re-send triggered updates */
1168   talloc_free(tmp_ctx);
1169   return rc;
1170 }        
1171
1172 TRP_RC trps_add_route(TRPS_INSTANCE *trps, TRP_ROUTE *route)
1173 {
1174   trp_rtable_add(trps->rtable, route); /* should return status */
1175   return TRP_SUCCESS; 
1176 }
1177
1178 /* steals the peer object */
1179 TRP_RC trps_add_peer(TRPS_INSTANCE *trps, TRP_PEER *peer)
1180 {
1181   if (trps->ptable==NULL) {
1182     trps->ptable=trp_ptable_new(trps);
1183     if (trps->ptable==NULL)
1184       return TRP_NOMEM;
1185   }
1186   return trp_ptable_add(trps->ptable, peer);
1187 }
1188
1189 TRP_PEER *trps_get_peer_by_gssname(TRPS_INSTANCE *trps, TR_NAME *gssname)
1190 {
1191   if (trps->ptable==NULL)
1192     return NULL;
1193
1194   return trp_ptable_find_gss_name(trps->ptable, gssname);
1195 }
1196
1197 TRP_PEER *trps_get_peer_by_servicename(TRPS_INSTANCE *trps, TR_NAME *servicename)
1198 {
1199   if (trps->ptable==NULL)
1200     return NULL;
1201
1202   return trp_ptable_find_servicename(trps->ptable, servicename);
1203 }
1204
1205 int trps_peer_connected(TRPS_INSTANCE *trps, TRP_PEER *peer)
1206 {
1207   TRPC_INSTANCE *trpc=trps_find_trpc(trps, peer);
1208   if (trpc==NULL)
1209     return 0;
1210
1211   if (trpc_get_status(trpc)==TRP_CONNECTION_UP)
1212     return 1;
1213   else
1214     return 0;
1215 }
1216
1217
1218 static TRP_RC trps_handle_request(TRPS_INSTANCE *trps, TRP_REQ *req)
1219 {
1220   TR_NAME *comm=NULL;
1221   TR_NAME *realm=NULL;
1222
1223   tr_debug("trps_handle_request: handling TRP request.");
1224
1225   if (trps_validate_request(trps, req) != TRP_SUCCESS) {
1226     tr_notice("trps_handle_request: received invalid TRP request.");
1227     return TRP_ERROR;
1228   }
1229
1230   if (!trp_req_is_wildcard(req)) {
1231     comm=trp_req_get_comm(req);
1232     realm=trp_req_get_realm(req);
1233     tr_debug("trps_handle_request: route for %.*s/%.*s requested.",
1234              comm->len, comm->buf, realm->len, realm->buf);
1235   } else {
1236     tr_debug("trps_handle_request: all routes requested.");
1237     /* leave comm/realm NULL */
1238   }
1239   return trps_update_one_peer(trps,
1240                               trps_get_peer_by_gssname(trps, trp_req_get_peer(req)),
1241                               TRP_UPDATE_REQUESTED,
1242                               comm,
1243                               realm);
1244 }
1245
1246
1247 TRP_RC trps_handle_tr_msg(TRPS_INSTANCE *trps, TR_MSG *tr_msg)
1248 {
1249   TRP_RC rc=TRP_ERROR;
1250
1251   switch (tr_msg_get_msg_type(tr_msg)) {
1252   case TRP_UPDATE:
1253     rc=trps_handle_update(trps, tr_msg_get_trp_upd(tr_msg));
1254     if (rc==TRP_SUCCESS) {
1255       rc=trps_update_active_routes(trps);
1256       trps_update(trps, TRP_UPDATE_TRIGGERED); /* send any triggered routes */
1257     }
1258     return rc;
1259
1260   case TRP_REQUEST:
1261     rc=trps_handle_request(trps, tr_msg_get_trp_req(tr_msg));
1262     return rc;
1263
1264   default:
1265     /* unknown error or one we don't care about (e.g., TID messages) */
1266     return TRP_ERROR;
1267   }
1268 }
1269
1270 /* send wildcard route request to a peer */
1271 TRP_RC trps_wildcard_route_req(TRPS_INSTANCE *trps, TR_NAME *peer_servicename)
1272 {
1273   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1274   TRP_PEER *peer=trps_get_peer_by_servicename(trps, peer_servicename);
1275   TR_MSG msg; /* not a pointer */
1276   TRP_REQ *req=trp_req_new(tmp_ctx);
1277   char *encoded=NULL;
1278   TRP_RC rc=TRP_ERROR;
1279
1280   if (peer==NULL) {
1281     tr_err("trps_wildcard_route_req: unknown peer (%.*s).", peer_servicename->len, peer_servicename->buf);
1282     rc=TRP_BADARG;
1283     goto cleanup;
1284   }
1285   if ((req==NULL) || (trp_req_make_wildcard(req)!=TRP_SUCCESS)) {
1286     tr_err("trps_wildcard_route_req: unable to create wildcard TRP request.");
1287     rc=TRP_NOMEM;
1288     goto cleanup;
1289   }
1290
1291   tr_msg_set_trp_req(&msg, req);
1292   encoded=tr_msg_encode(&msg);
1293   if (encoded==NULL) {
1294     tr_err("trps_wildcard_route_req: error encoding wildcard TRP request.");
1295     rc=TRP_ERROR;
1296     goto cleanup;
1297   }
1298
1299   tr_debug("trps_wildcard_route_req: adding message to queue.");
1300   if (trps_send_msg(trps, peer, encoded) != TRP_SUCCESS) {
1301     tr_err("trps_wildcard_route_req: error queueing request.");
1302     rc=TRP_ERROR;
1303   } else {
1304     tr_debug("trps_wildcard_route_req: request queued successfully.");
1305     rc=TRP_SUCCESS;
1306   }
1307
1308 cleanup:
1309   if (encoded!=NULL)
1310     tr_msg_free_encoded(encoded);
1311   if (req!=NULL)
1312     trp_req_free(req);
1313
1314   talloc_free(tmp_ctx);
1315   return rc;
1316 }