Only compute routes for connected peers. Other progress.
[trust_router.git] / tr / tr_trp.c
1 #include <pthread.h>
2 #include <fcntl.h>
3 #include <event2/event.h>
4 #include <talloc.h>
5 #include <errno.h>
6 #include <unistd.h>
7 #include <string.h>
8 #include <sys/time.h>
9 #include <time.h>
10
11 #include <gsscon.h>
12 #include <tr_rp.h>
13 #include <trp_internal.h>
14 #include <trp_ptable.h>
15 #include <trp_rtable.h>
16 #include <tr_config.h>
17 #include <tr_event.h>
18 #include <tr_msg.h>
19 #include <tr_trp.h>
20 #include <tr_debug.h>
21
22 /* data for event callbacks */
23 struct tr_trps_event_cookie {
24   TRPS_INSTANCE *trps;
25   TR_CFG_MGR *cfg_mgr;
26   struct event *ev;
27 };
28
29 /* callback to schedule event to process messages */
30 static void tr_trps_mq_cb(TR_MQ *mq, void *arg)
31 {
32   struct event *mq_ev=(struct event *)arg;
33   event_active(mq_ev, 0, 0);
34 }
35
36 static void msg_free_helper(void *p)
37 {
38   tr_msg_free_decoded((TR_MSG *)p);
39 }
40 /* takes a TR_MSG and puts it in a TR_MQ_MSG for processing by the main thread */
41 static TRP_RC tr_trps_msg_handler(TRPS_INSTANCE *trps,
42                                   TRP_CONNECTION *conn,
43                                   TR_MSG *tr_msg)
44 {
45   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
46   TR_MQ_MSG *mq_msg=NULL;
47
48   /* n.b., conn is available here, but do not hold onto the reference
49    * because it may be cleaned up if the originating connection goes
50    * down before the message is processed */
51   mq_msg=tr_mq_msg_new(tmp_ctx, "tr_msg");
52   if (mq_msg==NULL) {
53     return TRP_NOMEM;
54   }
55   tr_mq_msg_set_payload(mq_msg, (void *)tr_msg, msg_free_helper);
56   trps_mq_append(trps, mq_msg);
57   talloc_free(tmp_ctx); /* cleans up the message if it did not get appended correctly */
58   return TRP_SUCCESS;
59 }
60
61
62 static int tr_trps_gss_handler(gss_name_t client_name, gss_buffer_t gss_name,
63                                void *cookie_in)
64 {
65   struct tr_trps_event_cookie *cookie=(struct tr_trps_event_cookie *)cookie_in;
66   TRPS_INSTANCE *trps = cookie->trps;
67   TR_CFG_MGR *cfg_mgr = cookie->cfg_mgr;
68   TR_NAME name={gss_name->value, gss_name->length};
69
70   tr_debug("tr_trps_gss_handler()");
71
72   if ((!client_name) || (!gss_name) || (!trps) || (!cfg_mgr)) {
73     tr_debug("tr_trps_gss_handler: Bad parameters.");
74     return -1;
75   }
76   
77   /* look up the TRPS peer matching the GSS name */
78   if (NULL==trps_get_peer(trps, &name)) {
79     tr_warning("tr_trps_gss_handler: Connection attempt from unknown peer (GSS name: %.*s).", name.len, name.buf);
80     return -1;
81   }
82
83   tr_debug("Client's GSS Name: %.*s", name.len, name.buf);
84   return 0;
85 }
86
87 /* data passed to thread */
88 struct trps_thread_data {
89   TRP_CONNECTION *conn;
90   TRPS_INSTANCE *trps;
91 };
92 /* thread to handle GSS connections to peers */
93 static void *tr_trps_thread(void *arg)
94 {
95   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
96   struct trps_thread_data *thread_data=talloc_get_type_abort(arg, struct trps_thread_data);
97   TRP_CONNECTION *conn=thread_data->conn;
98   TRPS_INSTANCE *trps=thread_data->trps;
99   TR_MQ_MSG *msg=NULL;
100
101   tr_debug("tr_trps_thread: started");
102   trps_handle_connection(trps, conn);
103
104   msg=tr_mq_msg_new(tmp_ctx, TR_MQMSG_TRPS_DISCONNECTED);
105   tr_mq_msg_set_payload(msg, (void *)conn, NULL); /* do not pass a free routine */
106   if (msg==NULL)
107     tr_err("tr_trps_thread: error allocating TR_MQ_MSG");
108   else
109     trps_mq_append(trps, msg);
110
111   tr_debug("tr_trps_thread: exit");
112   talloc_free(tmp_ctx);
113   return NULL;
114 }
115
116 /* called when a connection to the TRPS port is received */
117 static void tr_trps_event_cb(int listener, short event, void *arg)
118 {
119   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
120   TRPS_INSTANCE *trps = talloc_get_type_abort(arg, TRPS_INSTANCE); /* aborts on wrong type */
121   TRP_CONNECTION *conn=NULL;
122   TR_NAME *gssname=NULL;
123   char *name=NULL;
124   struct trps_thread_data *thread_data=NULL;
125
126   if (0==(event & EV_READ)) {
127     tr_debug("tr_trps_event_cb: unexpected event on TRPS socket (event=0x%X)", event);
128   } else {
129     /* create a thread to handle this connection */
130     asprintf(&name, "trustrouter@%s", trps->hostname);
131     gssname=tr_new_name(name);
132     free(name); name=NULL;
133     conn=trp_connection_accept(tmp_ctx, listener, gssname);
134     if (conn!=NULL) {
135       /* need to monitor this fd and trigger events when read becomes possible */
136       thread_data=talloc(conn, struct trps_thread_data);
137       if (thread_data==NULL) {
138         tr_err("tr_trps_event_cb: unable to allocate trps_thread_data");
139         talloc_free(tmp_ctx);
140         return;
141       }
142       thread_data->conn=conn;
143       thread_data->trps=trps;
144       pthread_create(trp_connection_get_thread(conn), NULL, tr_trps_thread, thread_data);
145       pthread_detach(*(trp_connection_get_thread(conn))); /* we will not rejoin the thread */
146       trps_add_connection(trps, conn); /* remember the connection */
147     }
148   }
149   talloc_free(tmp_ctx);
150 }
151
152 static void tr_trps_cleanup_conn(TRPS_INSTANCE *trps, TRP_CONNECTION *conn)
153 {
154   /* everything belonging to the thread is in the TRP_CONNECTION
155    * associated with it */
156   trps_remove_connection(trps, conn);
157   trp_connection_free(conn);
158   tr_debug("Deleted connection");
159 }
160
161 #if 0
162 static void tr_trpc_abort(TRPC_INSTANCE *trpc)
163 {
164   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
165   TR_MQ_MSG *msg=tr_mq_msg_new(tmp_ctx, TR_MQMSG_ABORT);
166   tr_mq_msg_set_payload(msg, (void *)conn, NULL); /* do not pass a free routine */
167   trpc_mq_append(msg); /* gives msg over to the queue to manage */
168   
169 }
170 #endif
171
172 static void tr_trps_cleanup_trpc(TRPS_INSTANCE *trps, TRPC_INSTANCE *trpc)
173 {
174   /* everything belonging to the thread is in the TRP_CONNECTION
175    * associated with it */
176 /*  tr_trpc_abort(trpc); */ /* tell trpc to abort */
177   trps_remove_trpc(trps, trpc);
178   trpc_free(trpc);
179   tr_debug("Deleted connection");
180 }
181
182 static void tr_trps_print_route_table(TRPS_INSTANCE *trps, FILE *f)
183 {
184   char *table=trp_rtable_to_str(NULL, trps->rtable, " | ", NULL);
185   if (table==NULL)
186     fprintf(f, "Unable to print route table.\n");
187   else {
188     fprintf(f, "%s\n", table);
189     talloc_free(table);
190   }
191 }
192
193 static void tr_trps_process_mq(int socket, short event, void *arg)
194 {
195   TRPS_INSTANCE *trps=talloc_get_type_abort(arg, TRPS_INSTANCE);
196   TR_MQ_MSG *msg=NULL;
197   const char *s=NULL;
198
199   msg=trps_mq_pop(trps);
200   while (msg!=NULL) {
201     s=tr_mq_msg_get_message(msg);
202     if (0==strcmp(s, TR_MQMSG_TRPS_DISCONNECTED)) {
203       tr_trps_cleanup_conn(trps,
204                            talloc_get_type_abort(tr_mq_msg_get_payload(msg),
205                                                  TRP_CONNECTION));
206     }
207     else if (0==strcmp(s, TR_MQMSG_TRPC_DISCONNECTED)) {
208       /* trpc connection died */
209       tr_trps_cleanup_trpc(trps,
210                            talloc_get_type_abort(tr_mq_msg_get_payload(msg),
211                                                  TRPC_INSTANCE));
212     }
213
214     else if (0==strcmp(s, "tr_msg")) {
215       if (trps_handle_tr_msg(trps, tr_mq_msg_get_payload(msg))!=TRP_SUCCESS)
216         tr_notice("tr_trps_process_mq: error handling message.");
217       else {
218         tr_trps_print_route_table(trps, stderr);
219       }
220     }
221     else
222       tr_notice("tr_trps_process_mq: unknown message '%s' received.", tr_mq_msg_get_message(msg));
223
224     tr_mq_msg_free(msg);
225     msg=trps_mq_pop(trps);
226   }
227 }
228
229 static void tr_trps_update(int listener, short event, void *arg)
230 {
231   struct tr_trps_event_cookie *cookie=talloc_get_type_abort(arg, struct tr_trps_event_cookie);
232   TRPS_INSTANCE *trps=cookie->trps;
233   struct event *ev=cookie->ev;
234
235   tr_debug("tr_trps_update: sending scheduled route updates.");
236   trps_scheduled_update(trps);
237   event_add(ev, &(trps->update_interval));
238 }
239
240 static void tr_trps_sweep(int listener, short event, void *arg)
241 {
242   struct tr_trps_event_cookie *cookie=talloc_get_type_abort(arg, struct tr_trps_event_cookie);
243   TRPS_INSTANCE *trps=cookie->trps;
244   struct event *ev=cookie->ev;
245
246   tr_debug("tr_trps_sweep: sweeping routes.");
247   trps_sweep_routes(trps);
248   /* schedule the event to run again */
249   event_add(ev, &(trps->sweep_interval));
250 }
251
252 static void tr_connection_update(int listener, short event, void *arg)
253 {
254   struct tr_trps_event_cookie *cookie=talloc_get_type_abort(arg, struct tr_trps_event_cookie);
255   TRPS_INSTANCE *trps=cookie->trps;
256   struct event *ev=cookie->ev;
257
258   tr_debug("tr_connection_update: checking peer connections.");
259   tr_connect_to_peers(trps);
260   /* schedule the event to run again */
261   event_add(ev, &(trps->connect_interval));
262 }
263
264 static int tr_trps_events_destructor(void *obj)
265 {
266   TR_TRPS_EVENTS *ev=talloc_get_type_abort(obj, TR_TRPS_EVENTS);
267   if (ev->mq_ev!=NULL)
268     event_free(ev->mq_ev);
269   if (ev->connect_ev!=NULL)
270     event_free(ev->connect_ev);
271   if (ev->update_ev!=NULL)
272     event_free(ev->update_ev);
273   if (ev->sweep_ev!=NULL)
274     event_free(ev->sweep_ev);
275   return 0;
276 }
277 TR_TRPS_EVENTS *tr_trps_events_new(TALLOC_CTX *mem_ctx)
278 {
279   TR_TRPS_EVENTS *ev=talloc(mem_ctx, TR_TRPS_EVENTS);
280   if (ev!=NULL) {
281     ev->listen_ev=talloc(ev, struct tr_socket_event);
282     ev->mq_ev=NULL;
283     ev->connect_ev=NULL;
284     ev->update_ev=NULL;
285     ev->sweep_ev=NULL;
286     if (ev->listen_ev==NULL) {
287       talloc_free(ev);
288       ev=NULL;
289     }
290     talloc_set_destructor((void *)ev, tr_trps_events_destructor);
291   }
292   return ev;
293 }
294
295 /* Configure the trps instance and set up its event handler.
296  * Fills in trps_ev, which should be allocated by caller. */
297 TRP_RC tr_trps_event_init(struct event_base *base,
298                        TRPS_INSTANCE *trps,
299                        TR_CFG_MGR *cfg_mgr,
300                        TR_TRPS_EVENTS *trps_ev)
301 {
302   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
303   struct tr_socket_event *listen_ev=NULL;
304   struct tr_trps_event_cookie *trps_cookie=NULL;
305   struct tr_trps_event_cookie *connection_cookie=NULL;
306   struct tr_trps_event_cookie *update_cookie=NULL;
307   struct tr_trps_event_cookie *sweep_cookie=NULL;
308   TRP_RC retval=TRP_ERROR;
309
310   if (trps_ev == NULL) {
311     tr_debug("tr_trps_event_init: Null trps_ev.");
312     retval=TRP_BADARG;
313     goto cleanup;
314   }
315
316   /* get convenient handles */
317   listen_ev=trps_ev->listen_ev;
318
319   /* Create the cookie for callbacks. It is part of the trps context, so it will
320    * be cleaned up when trps is freed by talloc_free. */
321   trps_cookie=talloc(tmp_ctx, struct tr_trps_event_cookie);
322   if (trps_cookie == NULL) {
323     tr_debug("tr_trps_event_init: Unable to allocate trps_cookie.");
324     retval=TRP_NOMEM;
325     goto cleanup;
326   }
327   trps_cookie->trps=trps;
328   trps_cookie->cfg_mgr=cfg_mgr;
329   talloc_steal(trps, trps_cookie);
330
331   /* get a trps listener */
332   listen_ev->sock_fd=trps_get_listener(trps,
333                                        tr_trps_msg_handler,
334                                        tr_trps_gss_handler,
335                                        cfg_mgr->active->internal->hostname,
336                                        cfg_mgr->active->internal->trps_port,
337                                        (void *)trps_cookie);
338   if (listen_ev->sock_fd < 0) {
339     tr_crit("Error opening TRP server socket.");
340     retval=TRP_ERROR;
341     goto cleanup;
342   }
343   trps_cookie->ev=listen_ev->ev; /* in case it needs to frob the event */
344   
345   /* and its event */
346   listen_ev->ev=event_new(base,
347                           listen_ev->sock_fd,
348                           EV_READ|EV_PERSIST,
349                           tr_trps_event_cb,
350                           (void *)trps);
351   event_add(listen_ev->ev, NULL);
352   
353   /* now set up message queue processing event, only triggered by
354    * tr_trps_mq_cb() */
355   trps_ev->mq_ev=event_new(base,
356                            0,
357                            EV_PERSIST,
358                            tr_trps_process_mq,
359                            (void *)trps);
360   tr_mq_set_notify_cb(trps->mq, tr_trps_mq_cb, trps_ev->mq_ev);
361
362   /* now set up the peer connection timer event */
363   connection_cookie=talloc(tmp_ctx, struct tr_trps_event_cookie);
364   if (connection_cookie == NULL) {
365     tr_debug("tr_trps_event_init: Unable to allocate connection_cookie.");
366     retval=TRP_NOMEM;
367     goto cleanup;
368   }
369   connection_cookie->trps=trps;
370   connection_cookie->cfg_mgr=cfg_mgr;
371   talloc_steal(trps, connection_cookie);
372   trps_ev->connect_ev=event_new(base, -1, EV_TIMEOUT, tr_connection_update, (void *)connection_cookie);
373   connection_cookie->ev=trps_ev->connect_ev; /* in case it needs to frob the event */
374   event_add(trps_ev->connect_ev, &(trps->connect_interval));
375
376   /* now set up the route update timer event */
377   update_cookie=talloc(tmp_ctx, struct tr_trps_event_cookie);
378   if (update_cookie == NULL) {
379     tr_debug("tr_trps_event_init: Unable to allocate update_cookie.");
380     retval=TRP_NOMEM;
381     goto cleanup;
382   }
383   update_cookie->trps=trps;
384   update_cookie->cfg_mgr=cfg_mgr;
385   talloc_steal(trps, update_cookie);
386   trps_ev->update_ev=event_new(base, -1, EV_TIMEOUT, tr_trps_update, (void *)update_cookie);
387   update_cookie->ev=trps_ev->update_ev; /* in case it needs to frob the event */
388   event_add(trps_ev->update_ev, &(trps->update_interval));
389
390   /* now set up the route table sweep timer event */
391   sweep_cookie=talloc(tmp_ctx, struct tr_trps_event_cookie);
392   if (sweep_cookie == NULL) {
393     tr_debug("tr_trps_event_init: Unable to allocate sweep_cookie.");
394     retval=TRP_NOMEM;
395     goto cleanup;
396   }
397   sweep_cookie->trps=trps;
398   sweep_cookie->cfg_mgr=cfg_mgr;
399   talloc_steal(trps, sweep_cookie);
400   trps_ev->sweep_ev=event_new(base, -1, EV_TIMEOUT, tr_trps_sweep, (void *)sweep_cookie);
401   sweep_cookie->ev=trps_ev->sweep_ev; /* in case it needs to frob the event */
402   event_add(trps_ev->sweep_ev, &(trps->sweep_interval));
403
404   retval=TRP_SUCCESS;
405
406 cleanup:
407   talloc_free(tmp_ctx);
408   return retval;
409 }
410
411
412 struct trpc_notify_cb_data {
413   int msg_ready;
414   pthread_cond_t cond;
415   pthread_mutex_t mutex;
416 };
417
418 static void tr_trpc_mq_cb(TR_MQ *mq, void *arg)
419 {
420   struct trpc_notify_cb_data *cb_data=(struct trpc_notify_cb_data *) arg;
421   pthread_mutex_lock(&(cb_data->mutex));
422   if (!cb_data->msg_ready) {
423     cb_data->msg_ready=1;
424     pthread_cond_signal(&(cb_data->cond));
425   }
426   pthread_mutex_unlock(&(cb_data->mutex));
427 }
428
429 /* data passed to thread */
430 struct trpc_thread_data {
431   TRPC_INSTANCE *trpc;
432   TRPS_INSTANCE *trps;
433 };
434 static void *tr_trpc_thread(void *arg)
435 {
436   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
437   struct trpc_thread_data *thread_data=talloc_get_type_abort(arg, struct trpc_thread_data);
438   TRPC_INSTANCE *trpc=thread_data->trpc;
439   TRPS_INSTANCE *trps=thread_data->trps;
440   TRP_RC rc=TRP_ERROR;
441   TR_MQ_MSG *msg=NULL;
442   const char *msg_type=NULL;
443   char *encoded_msg=NULL;
444
445   struct trpc_notify_cb_data cb_data={0,
446                                       PTHREAD_COND_INITIALIZER,
447                                       PTHREAD_MUTEX_INITIALIZER};
448
449   tr_debug("tr_trpc_thread: started");
450
451   /* set up the mq for receiving */
452   pthread_mutex_lock(&(cb_data.mutex)); /* hold this lock until we enter the main loop */
453
454   tr_mq_lock(trpc->mq);
455   tr_mq_set_notify_cb(trpc->mq, tr_trpc_mq_cb, (void *) &cb_data);
456   tr_mq_unlock(trpc->mq);
457
458   rc=trpc_connect(trpc);
459   if (rc!=TRP_SUCCESS) {
460     tr_notice("tr_trpc_thread: failed to initiate connection to %s:%d.",
461               trpc_get_server(trpc),
462               trpc_get_port(trpc));
463   } else {
464     tr_debug("tr_trpc_thread: connected to peer %s", trpc->conn->peer->buf);
465     while (1) {
466       cb_data.msg_ready=0;
467       pthread_cond_wait(&(cb_data.cond), &(cb_data.mutex));
468       /* verify the condition */
469       if (cb_data.msg_ready) {
470         msg=trpc_mq_pop(trpc);
471         if (msg==NULL) {
472           /* no message in the queue */
473           tr_err("tr_trpc_thread: notified of msg, but queue empty");
474           break;
475         }
476
477         msg_type=tr_mq_msg_get_message(msg);
478
479         if (0==strcmp(msg_type, TR_MQMSG_ABORT)) {
480           tr_mq_msg_free(msg);
481           break; /* exit loop */
482         }
483         else if (0==strcmp(msg_type, TR_MQMSG_TRPC_SEND)) {
484           encoded_msg=tr_mq_msg_get_payload(msg);
485           if (encoded_msg==NULL)
486             tr_notice("tr_trpc_thread: null outgoing TRP message.");
487           else {
488             rc = trpc_send_msg(trpc, encoded_msg);
489             if (rc!=TRP_SUCCESS) {
490               tr_notice("tr_trpc_thread: trpc_send_msg failed.");
491               tr_mq_msg_free(msg);
492               break;
493             }
494           }
495         }
496         else
497           tr_notice("tr_trpc_thread: unknown message '%s' received.", msg_type);
498
499         tr_mq_msg_free(msg);
500       }
501     }
502   }
503
504   msg=tr_mq_msg_new(tmp_ctx, TR_MQMSG_TRPC_DISCONNECTED);
505   tr_mq_msg_set_payload(msg, (void *)trpc, NULL); /* do not pass a free routine */
506   if (msg==NULL)
507     tr_err("tr_trpc_thread: error allocating TR_MQ_MSG");
508   else
509     trps_mq_append(trps, msg);
510
511   talloc_free(tmp_ctx);
512   return NULL;
513 }
514
515 /* convert an IDP realm into routing table entries. Outputs number in *n_routes */
516 static TRP_RENTRY **tr_make_local_routes(TALLOC_CTX *mem_ctx,
517                                          TR_IDP_REALM *realm,
518                                          char *trust_router,
519                                          size_t *n_routes)
520 {
521   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
522   TR_APC *apc=NULL;
523   TRP_RENTRY *new_entry=NULL;
524   TRP_RENTRY **entries=NULL;
525   size_t n_apcs=0, ii=0;
526
527   *n_routes=0;
528
529   if (realm==NULL)
530     goto cleanup;
531
532   /* count apcs */
533   for (apc=realm->apcs, n_apcs=0; apc!=NULL; apc=apc->next,n_apcs++) {}
534
535   entries=talloc_array(tmp_ctx, TRP_RENTRY *, n_apcs);
536   for (apc=realm->apcs,ii=0; apc!=NULL; apc=apc->next, ii++) {
537     new_entry=trp_rentry_new(entries);
538     if (new_entry==NULL) {
539       tr_crit("tr_make_local_routes: unable to allocate entry.");
540       talloc_free(entries);
541       goto cleanup;
542     }
543     trp_rentry_set_apc(new_entry, tr_dup_name(apc->id));
544     trp_rentry_set_realm(new_entry, tr_dup_name(realm->realm_id));
545     trp_rentry_set_peer(new_entry, tr_new_name("")); /* no peer, it's us */
546     trp_rentry_set_metric(new_entry, 0);
547     trp_rentry_set_trust_router(new_entry, tr_new_name(trust_router));
548     trp_rentry_set_next_hop(new_entry, tr_new_name(""));
549     /* we do not set selected (tbd later) or expiry/interval (not needed for
550      * local routes) */
551     entries[ii]=new_entry;
552   }
553
554   talloc_steal(mem_ctx, entries);
555   *n_routes=n_apcs;
556  cleanup:
557   talloc_free(tmp_ctx);
558   return entries;
559 }
560
561 struct tr_trpc_status_change_cookie {
562   TRPS_INSTANCE *trps;
563   TRPC_INSTANCE *trpc;
564   TRP_PEER *peer;
565 };
566 static void tr_trpc_status_change(TRP_CONNECTION *conn, void *cookie)
567 {
568   struct tr_trpc_status_change_cookie *cook=talloc_get_type_abort(cookie, struct tr_trpc_status_change_cookie);
569   /*TRPS_INSTANCE *trps=cook->trps;*/
570   /* TRPC_INSTANCE *trpc=cook->trpc;*/
571   TRP_PEER *peer=cook->peer;
572   TR_NAME *gssname=trp_peer_get_gssname(peer);
573
574   if (trp_connection_get_status(conn)==TRP_CONNECTION_UP)
575     tr_debug("tr_trpc_status_change: connection to %.*s now up.", gssname->len, gssname->buf);
576   else
577     tr_debug("tr_trpc_status_change: connection to %.*s now down.", gssname->len, gssname->buf);
578 }
579
580 /* starts a trpc thread to connect to server:port */
581 TRPC_INSTANCE *tr_trpc_initiate(TRPS_INSTANCE *trps, TRP_PEER *peer)
582 {
583   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
584   TRPC_INSTANCE *trpc=NULL;
585   TRP_CONNECTION *conn=NULL;
586   struct trpc_thread_data *thread_data=NULL;
587   struct tr_trpc_status_change_cookie *status_change_cookie=NULL;
588
589   tr_debug("tr_trpc_initiate entered");
590   trpc=trpc_new(tmp_ctx);
591   if (trpc==NULL) {
592     tr_crit("tr_trpc_initiate: could not allocate TRPC_INSTANCE.");
593     goto cleanup;
594   }
595   tr_debug("tr_trpc_initiate: allocated trpc");
596
597   conn=trp_connection_new(trpc);
598   if (conn==NULL) {
599     tr_crit("tr_trpc_initiate: could not allocate TRP_CONNECTION.");
600     goto cleanup;
601   }
602
603   status_change_cookie=talloc(conn, struct tr_trpc_status_change_cookie);
604   if (status_change_cookie==NULL) {
605     tr_crit("tr_trpc_initiate: could not allocate connection status cookie.");
606     goto cleanup;
607   }
608   status_change_cookie->trps=trps;
609   status_change_cookie->trpc=trpc;
610   status_change_cookie->peer=peer;
611   conn->status_change_cookie=status_change_cookie;
612   status_change_cookie=NULL;
613   conn->status_change_cb=tr_trpc_status_change;
614
615   trpc_set_conn(trpc, conn);
616   trpc_set_server(trpc, talloc_strdup(trpc, trp_peer_get_server(peer)));
617   trpc_set_port(trpc, trp_peer_get_port(peer));
618   trpc_set_gssname(trpc, trp_peer_dup_gssname(peer));
619   tr_debug("tr_trpc_initiate: allocated connection");
620   
621   /* start thread */
622   thread_data=talloc(trpc, struct trpc_thread_data);
623   if (thread_data==NULL) {
624     tr_crit("tr_trpc_initiate: could not allocate struct trpc_thread_data.");
625     goto cleanup;
626   }
627   thread_data->trpc=trpc;
628   thread_data->trps=trps;
629
630   pthread_create(trp_connection_get_thread(conn), NULL, tr_trpc_thread, thread_data);
631   pthread_detach(*(trp_connection_get_thread(conn))); /* we will not rejoin the thread */
632
633   tr_debug("tr_trpc_initiate: started trpc thread");
634   trps_add_trpc(trps, trpc);
635
636  cleanup:
637   talloc_free(tmp_ctx);
638   return trpc;
639 }
640
641 /* Add local routes to the route table. */
642 TRP_RC tr_add_local_routes(TRPS_INSTANCE *trps, TR_CFG *cfg)
643 {
644   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
645   TR_IDP_REALM *cur=NULL;
646   TRP_RENTRY **local_routes=NULL;
647   size_t n_routes=0;
648   size_t ii=0;
649   char *trust_router_name=talloc_asprintf(tmp_ctx, "%s:%d", cfg->internal->hostname, cfg->internal->trps_port);
650
651   /* determine our trust router name */
652   if (trust_router_name==NULL)
653     return TRP_NOMEM;
654
655   for (cur=cfg->idp_realms; cur!=NULL; cur=cur->next) {
656     local_routes=tr_make_local_routes(tmp_ctx, cur, trust_router_name, &n_routes);
657     for (ii=0; ii<n_routes; ii++)
658       trps_add_route(trps, local_routes[ii]);
659
660     talloc_free(local_routes);
661     local_routes=NULL;
662     n_routes=0;
663   }
664
665   talloc_free(tmp_ctx);
666   return TRP_SUCCESS;
667 }
668
669 /* decide how often to attempt to connect to a peer */
670 static int tr_conn_attempt_due(TRPS_INSTANCE *trps, TRP_PEER *peer, struct timespec *when)
671 {
672   return 1; /* currently make an attempt every cycle */
673 }
674
675 /* open missing connections to peers */
676 TRP_RC tr_connect_to_peers(TRPS_INSTANCE *trps)
677 {
678   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
679   TRP_PTABLE_ITER *iter=trp_ptable_iter_new(tmp_ctx);
680   TRP_PEER *peer=NULL;
681   TRPC_INSTANCE *trpc=NULL;
682   struct timespec curtime={0,0};
683   TRP_RC rc=TRP_ERROR;
684
685   if (clock_gettime(CLOCK_REALTIME, &curtime)) {
686     tr_err("tr_connect_to_peers: failed to read time.");
687     rc=TRP_CLOCKERR;
688     goto cleanup;
689   }
690
691   for (peer=trp_ptable_iter_first(iter, trps->ptable);
692        peer!=NULL;
693        peer=trp_ptable_iter_next(iter))
694   {
695     if (trps_find_trpc(trps, peer)==NULL) {
696       tr_debug("tr_connect_to_peers: %.*s missing connection.",
697                trp_peer_get_gssname(peer)->len, trp_peer_get_gssname(peer)->buf);
698       /* has it been long enough since we last tried? */
699       if (tr_conn_attempt_due(trps, peer, &curtime)) {
700         trp_peer_set_last_conn_attempt(peer, &curtime); /* we are trying again now */
701         trpc=tr_trpc_initiate(trps, peer);
702         if (trpc==NULL) {
703           tr_err("tr_connect_to_peers: unable to initiate TRP connection to %s:%u.",
704                  trp_peer_get_server(peer),
705                  trp_peer_get_port(peer));
706         }
707       }
708     }
709   }
710   rc=TRP_SUCCESS;
711     
712 cleanup:
713   trp_ptable_iter_free(iter);
714   talloc_free(tmp_ctx);
715   return rc;
716 }
717
718
719 /* Called by the config manager after a change to the active configuration.
720  * Updates configuration of objects that do not know about the config manager. */
721 void tr_config_changed(TR_CFG *new_cfg, void *cookie)
722 {
723   TRPS_INSTANCE *trps=talloc_get_type_abort(cookie, TRPS_INSTANCE);
724   trps_set_connect_interval(trps, new_cfg->internal->trp_connect_interval);
725   trps_set_update_interval(trps, new_cfg->internal->trp_update_interval);
726   trps_set_sweep_interval(trps, new_cfg->internal->trp_sweep_interval);
727   trps_clear_rtable(trps); /* should we do this every time??? */
728   tr_add_local_routes(trps, new_cfg); /* should we do this every time??? */
729   trps_update_active_routes(trps);
730   tr_trps_print_route_table(trps, stderr);
731 }
732