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