Requests nearly work, but not quite.
authorJennifer Richards <jennifer@painless-security.com>
Wed, 3 Aug 2016 17:05:07 +0000 (13:05 -0400)
committerJennifer Richards <jennifer@painless-security.com>
Wed, 3 Aug 2016 17:05:07 +0000 (13:05 -0400)
Issue: the request can go out before the return connection for the
peer to respond has been established, so the reply does not get sent.
Checking in before reworking detection that a peer is connected.

include/tr_trp.h
include/trp_internal.h
include/trp_ptable.h
include/trust_router/trp.h
tr/tr_trp.c
trp/trp_conn.c
trp/trp_ptable.c
trp/trp_req.c
trp/trps.c

index bb055fc..542e590 100644 (file)
@@ -40,7 +40,7 @@ struct tr_instance {
 /* prototypes */
 TRP_RC tr_trps_event_init(struct event_base *base, struct tr_instance *tr);
 TRP_RC tr_add_local_routes(TRPS_INSTANCE *trps, TR_CFG *cfg);
-TRP_RC tr_trpc_initiate(TRPS_INSTANCE *trps, TRP_PEER *peer);
+TRP_RC tr_trpc_initiate(TRPS_INSTANCE *trps, TRP_PEER *peer, struct event *ev);
 void tr_config_changed(TR_CFG *new_cfg, void *cookie);
-TRP_RC tr_connect_to_peers(TRPS_INSTANCE *trps);
+TRP_RC tr_connect_to_peers(TRPS_INSTANCE *trps, struct event *ev);
 #endif /* TR_TRP_H */
index 464f204..051c00a 100644 (file)
@@ -189,7 +189,9 @@ TR_NAME *trps_get_next_hop(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm);
 TRP_RC trps_sweep_routes(TRPS_INSTANCE *trps);
 TRP_RC trps_add_route(TRPS_INSTANCE *trps, TRP_ROUTE *route);
 TRP_RC trps_add_peer(TRPS_INSTANCE *trps, TRP_PEER *peer);
-TRP_PEER *trps_get_peer(TRPS_INSTANCE *trps, TR_NAME *gssname);
+TRP_PEER *trps_get_peer_by_gssname(TRPS_INSTANCE *trps, TR_NAME *gssname);
+TRP_PEER *trps_get_peer_by_servicename(TRPS_INSTANCE *trps, TR_NAME *servicename);
 TRP_RC trps_update(TRPS_INSTANCE *trps, TRP_UPDATE_TYPE type);
 int trps_peer_connected(TRPS_INSTANCE *trps, TRP_PEER *peer);
+TRP_RC trps_wildcard_route_req(TRPS_INSTANCE *trps, TR_NAME *peer_gssname);
 #endif /* TRP_INTERNAL_H */
index 804217c..0b6a9d8 100644 (file)
@@ -12,6 +12,7 @@ struct trp_peer {
   TRP_PEER *next; /* for making a linked list */
   char *server;
   TR_NAME *gssname;
+  TR_NAME *servicename;
   unsigned int port;
   unsigned int linkcost;
   struct timespec last_conn_attempt;
@@ -28,7 +29,8 @@ TRP_PTABLE *trp_ptable_new(TALLOC_CTX *memctx);
 void trp_ptable_free(TRP_PTABLE *ptbl);
 TRP_RC trp_ptable_add(TRP_PTABLE *ptbl, TRP_PEER *newpeer);
 TRP_RC trp_ptable_remove(TRP_PTABLE *ptbl, TRP_PEER *peer);
-TRP_PEER *trp_ptable_find(TRP_PTABLE *ptbl, TR_NAME *gssname);
+TRP_PEER *trp_ptable_find_gssname(TRP_PTABLE *ptbl, TR_NAME *gssname);
+TRP_PEER *trp_ptable_find_servicename(TRP_PTABLE *ptbl, TR_NAME *servicename);
 char *trp_ptable_to_str(TALLOC_CTX *memctx, TRP_PTABLE *ptbl, const char *sep, const char *lineterm);
 
 TRP_PTABLE_ITER *trp_ptable_iter_new(TALLOC_CTX *mem_ctx);
@@ -43,6 +45,8 @@ void trp_peer_set_server(TRP_PEER *peer, char *server);
 void trp_peer_set_gssname(TRP_PEER *peer, TR_NAME *gssname);
 TR_NAME *trp_peer_get_gssname(TRP_PEER *peer);
 TR_NAME *trp_peer_dup_gssname(TRP_PEER *peer);
+TR_NAME *trp_peer_get_servicename(TRP_PEER *peer);
+TR_NAME *trp_peer_dup_servicename(TRP_PEER *peer);
 unsigned int trp_peer_get_port(TRP_PEER *peer);
 void trp_peer_set_port(TRP_PEER *peer, unsigned int port);
 unsigned int trp_peer_get_linkcost(TRP_PEER *peer);
index 0e70b99..9b2381d 100644 (file)
@@ -79,5 +79,6 @@ void trp_req_set_realm(TRP_REQ *req, TR_NAME *realm);
 TR_EXPORT TR_NAME *trp_req_get_peer(TRP_REQ *req);
 void trp_req_set_peer(TRP_REQ *req, TR_NAME *peer);
 int trp_req_is_wildcard(TRP_REQ *req);
+TRP_RC trp_req_make_wildcard(TRP_REQ *req);
 
 #endif /* TRP_H */
index ed07918..6a09d63 100644 (file)
@@ -77,7 +77,7 @@ static int tr_trps_gss_handler(gss_name_t client_name, gss_buffer_t gss_name,
   }
   
   /* look up the TRPS peer matching the GSS name */
-  if (NULL==trps_get_peer(trps, &name)) {
+  if (NULL==trps_get_peer_by_gssname(trps, &name)) {
     tr_warning("tr_trps_gss_handler: Connection attempt from unknown peer (GSS name: %.*s).", name.len, name.buf);
     return -1;
   }
@@ -249,7 +249,7 @@ static void tr_connection_update(int listener, short event, void *arg)
   struct event *ev=cookie->ev;
 
   tr_debug("tr_connection_update: checking peer connections.");
-  tr_connect_to_peers(trps);
+  tr_connect_to_peers(trps, ev);
   /* schedule the event to run again */
   event_add(ev, &(trps->connect_interval));
 }
@@ -452,6 +452,7 @@ static void *tr_trpc_thread(void *arg)
   TR_MQ_MSG *msg=NULL;
   const char *msg_type=NULL;
   char *encoded_msg=NULL;
+  TR_NAME *peer_gssname=NULL;
 
   struct trpc_notify_cb_data cb_data={0,
                                       PTHREAD_COND_INITIALIZER,
@@ -467,16 +468,20 @@ static void *tr_trpc_thread(void *arg)
   tr_mq_unlock(trpc->mq);
 
   rc=trpc_connect(trpc);
-/*  talloc_report_full(trpc, stderr);*/
   if (rc!=TRP_SUCCESS) {
-    /* was tr_notice --jlr */
-    fprintf(stderr, "tr_trpc_thread: failed to initiate connection to %s:%d.",
-            trpc_get_server(trpc),
-            trpc_get_port(trpc));
-    fflush(stderr);
+    tr_notice("tr_trpc_thread: failed to initiate connection to %s:%d.",
+              trpc_get_server(trpc),
+              trpc_get_port(trpc));
   } else {
-    tr_debug("tr_trpc_thread: connected to peer %s", trpc->conn->peer->buf);
-    while (1) {
+    peer_gssname=tr_dup_name(trp_connection_get_peer(trpc_get_conn(trpc)));
+    if (peer_gssname==NULL) {
+      tr_err("tr_trpc_thread: could not duplicate peer_gssname.");
+      talloc_free(tmp_ctx);
+      return NULL;
+    }
+    tr_debug("tr_trpc_thread: connected to peer %s", peer_gssname->buf);
+
+    while(1) {
       cb_data.msg_ready=0;
       pthread_cond_wait(&(cb_data.cond), &(cb_data.mutex));
       /* verify the condition */
@@ -572,7 +577,26 @@ static TRP_ROUTE **tr_make_local_routes(TALLOC_CTX *mem_ctx,
   return entries;
 }
 
+struct tr_wildcard_cookie {
+  TRPS_INSTANCE *trps;
+  TR_NAME *peer_servicename;
+};
+
+/* can only be called once, talloc_frees its argument cookie on termination */
+static void tr_send_wildcard(int listener, short event, void *arg)
+{
+  struct tr_wildcard_cookie *cook=talloc_get_type_abort(arg, struct tr_wildcard_cookie);
+
+  /* queue a route request for all route to every peer */
+  if (TRP_SUCCESS!=trps_wildcard_route_req(cook->trps, cook->peer_servicename))
+    tr_err("tr_send_wildcard: error sending wildcard route request.");
+
+  tr_free_name(cook->peer_servicename);
+  talloc_free(cook);
+}
+
 struct tr_trpc_status_change_cookie {
+  struct event_base *evbase;
   TRPS_INSTANCE *trps;
   TRPC_INSTANCE *trpc;
   TRP_PEER *peer;
@@ -580,17 +604,32 @@ struct tr_trpc_status_change_cookie {
 static void tr_trpc_status_change(TRP_CONNECTION *conn, void *cookie)
 {
   struct tr_trpc_status_change_cookie *cook=talloc_get_type_abort(cookie, struct tr_trpc_status_change_cookie);
+  struct event_base *evbase=cook->evbase;
+  TRPS_INSTANCE *trps=cook->trps;
   TRP_PEER *peer=cook->peer;
   TR_NAME *gssname=trp_peer_get_gssname(peer);
-
-  if (trp_connection_get_status(conn)==TRP_CONNECTION_UP)
+  struct timeval zero_time={0,0};
+  struct tr_wildcard_cookie *wc_cookie=NULL;
+  
+  if (trp_connection_get_status(conn)==TRP_CONNECTION_UP) {
     tr_debug("tr_trpc_status_change: connection to %.*s now up.", gssname->len, gssname->buf);
-  else
+    /* add a one-off event to send a wildcard request from the main thread */
+    wc_cookie=talloc(trps, struct tr_wildcard_cookie);
+    if (wc_cookie==NULL) {
+      tr_err("tr_trpc_status_change: error allocating wildcard cookie.");
+      return;
+    }
+    wc_cookie->trps=trps;
+    wc_cookie->peer_servicename=trp_peer_dup_servicename(peer);
+    if (0!=event_base_once(evbase, -1, EV_TIMEOUT, tr_send_wildcard, wc_cookie, &zero_time)) {
+      tr_err("tr_trpc_status_change: error queueing wildcard route request event.");
+    }
+  } else
     tr_debug("tr_trpc_status_change: connection to %.*s now down.", gssname->len, gssname->buf);
 }
 
 /* starts a trpc thread to connect to server:port */
-TRP_RC tr_trpc_initiate(TRPS_INSTANCE *trps, TRP_PEER *peer)
+TRP_RC tr_trpc_initiate(TRPS_INSTANCE *trps, TRP_PEER *peer, struct event *ev)
 {
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
   TRPC_INSTANCE *trpc=NULL;
@@ -621,6 +660,7 @@ TRP_RC tr_trpc_initiate(TRPS_INSTANCE *trps, TRP_PEER *peer)
     rc=TRP_NOMEM;
     goto cleanup;
   }
+  status_change_cookie->evbase=event_get_base(ev);
   status_change_cookie->trps=trps;
   status_change_cookie->trpc=trpc;
   status_change_cookie->peer=peer;
@@ -651,7 +691,6 @@ TRP_RC tr_trpc_initiate(TRPS_INSTANCE *trps, TRP_PEER *peer)
   rc=TRP_SUCCESS;
 
  cleanup:
-  talloc_report_full(tmp_ctx, stderr);
   talloc_free(tmp_ctx);
   return rc;
 }
@@ -691,7 +730,7 @@ static int tr_conn_attempt_due(TRPS_INSTANCE *trps, TRP_PEER *peer, struct times
 }
 
 /* open missing connections to peers */
-TRP_RC tr_connect_to_peers(TRPS_INSTANCE *trps)
+TRP_RC tr_connect_to_peers(TRPS_INSTANCE *trps, struct event *ev)
 {
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
   TRP_PTABLE_ITER *iter=trp_ptable_iter_new(tmp_ctx);
@@ -715,11 +754,11 @@ TRP_RC tr_connect_to_peers(TRPS_INSTANCE *trps)
       /* has it been long enough since we last tried? */
       if (tr_conn_attempt_due(trps, peer, &curtime)) {
         trp_peer_set_last_conn_attempt(peer, &curtime); /* we are trying again now */
-        if (tr_trpc_initiate(trps, peer)!=TRP_SUCCESS) {
+        if (tr_trpc_initiate(trps, peer, ev)!=TRP_SUCCESS) {
           tr_err("tr_connect_to_peers: unable to initiate TRP connection to %s:%u.",
                  trp_peer_get_server(peer),
                  trp_peer_get_port(peer));
-        }
+        } 
       }
     }
   }
index 99d8593..cd45bc5 100644 (file)
@@ -41,8 +41,6 @@ static TRP_RC trp_connection_set_peer(TRP_CONNECTION *conn)
   gss_buffer_desc peer_display_name={0,NULL};
   int local=0;
 
-  tr_debug("gssctx = %p", trp_connection_get_gssctx(conn));
-  tr_debug("*gssctx = %p", *trp_connection_get_gssctx(conn));
   major_status=gss_inquire_context(&minor_status,
                                    *trp_connection_get_gssctx(conn),
                                   &source_name,
@@ -86,7 +84,8 @@ static TRP_RC trp_connection_set_peer(TRP_CONNECTION *conn)
 
   if (conn->peer==NULL)
     return TRP_ERROR;
-  
+
+  tr_debug("trp_connection_set_peer: set peer for %p to %.*s (%p).", conn, conn->peer->len, conn->peer->buf, conn->peer);
   return TRP_SUCCESS;
 }
 
@@ -257,7 +256,8 @@ void trp_connection_free(TRP_CONNECTION *conn)
 
 void trp_connection_close(TRP_CONNECTION *conn)
 {
-  close(trp_connection_get_fd(conn));
+  if ((conn->status!=TRP_CONNECTION_DOWN) && (conn->fd>0))
+    close(trp_connection_get_fd(conn));
   trp_connection_set_fd(conn, -1);
   trp_connection_set_status(conn, TRP_CONNECTION_DOWN);
 }
@@ -349,12 +349,16 @@ TRP_RC trp_connection_initiate(TRP_CONNECTION *conn, char *server, unsigned int
                       &fd,
                        trp_connection_get_gssctx(conn));
   if (err) {
-    tr_debug("trp_connection_initiate: connection failed.");
+    tr_err("trp_connection_initiate: connection failed.");
     return TRP_ERROR;
   } else {
     tr_debug("trp_connection_initiate: connected.");
     trp_connection_set_fd(conn, fd);
-    trp_connection_set_peer(conn);
+    if (trp_connection_set_peer(conn)!=TRP_SUCCESS) {
+      tr_err("trp_connection_initiate: error setting peer gssname.");
+      trp_connection_close(conn);
+      return TRP_ERROR;
+    }
     trp_connection_set_status(conn, TRP_CONNECTION_UP);
     return TRP_SUCCESS;
   }
index 557bc0d..9814f66 100644 (file)
@@ -9,6 +9,8 @@
 static int trp_peer_destructor(void *object)
 {
   TRP_PEER *peer=talloc_get_type_abort(object, TRP_PEER);
+  if (peer->servicename!=NULL)
+    tr_free_name(peer->servicename);
   if (peer->gssname!=NULL)
     tr_free_name(peer->gssname);
   return 0;
@@ -19,6 +21,7 @@ TRP_PEER *trp_peer_new(TALLOC_CTX *memctx)
   if (peer!=NULL) {
     peer->next=NULL;
     peer->server=NULL;
+    peer->servicename=NULL;
     peer->gssname=NULL;
     peer->port=0;
     peer->linkcost=TRP_LINKCOST_DEFAULT;
@@ -46,10 +49,28 @@ char *trp_peer_get_server(TRP_PEER *peer)
   return peer->server;
 }
 
+static void trp_peer_set_servicename(TRP_PEER *peer, const char *server)
+{
+  char *name=NULL;
+  if (peer->servicename !=NULL)
+    tr_free_name(peer->servicename);
+
+  if (server!=NULL)
+    name=talloc_asprintf(NULL, "trustrouter/%s", server);
+
+  if (name!=NULL) {
+    peer->servicename=tr_new_name(name);
+    talloc_free(name);
+  } else {
+    peer->servicename=NULL;
+  }
+}
+
 /* copies input; on error, peer->gssname will be null */
 void trp_peer_set_server(TRP_PEER *peer, char *server)
 {
   peer->server=talloc_strdup(peer, server); /* will be null on error */
+  trp_peer_set_servicename(peer, server);
 }
 
 void trp_peer_set_gssname(TRP_PEER *peer, TR_NAME *gssname)
@@ -69,6 +90,18 @@ TR_NAME *trp_peer_dup_gssname(TRP_PEER *peer)
   return tr_dup_name(peer->gssname);
 }
 
+/* get the service name (i.e., gssname we see when we connect to this peer) */
+TR_NAME *trp_peer_get_servicename(TRP_PEER *peer)
+{
+  return peer->servicename;
+}
+
+/* get a copy of the servicename, caller must free via tr_free_name */
+TR_NAME *trp_peer_dup_servicename(TRP_PEER *peer)
+{
+  return tr_dup_name(peer->servicename);
+}
+
 unsigned int trp_peer_get_port(TRP_PEER *peer)
 {
   return peer->port;
@@ -158,7 +191,7 @@ TRP_RC trp_ptable_remove(TRP_PTABLE *ptbl, TRP_PEER *peer)
   return TRP_ERROR;
 }
 
-TRP_PEER *trp_ptable_find(TRP_PTABLE *ptbl, TR_NAME *gssname)
+TRP_PEER *trp_ptable_find_gssname(TRP_PTABLE *ptbl, TR_NAME *gssname)
 {
   TRP_PEER *cur=ptbl->head;
   while ((cur!=NULL) && (0 != tr_name_cmp(trp_peer_get_gssname(cur), gssname)))
@@ -166,6 +199,14 @@ TRP_PEER *trp_ptable_find(TRP_PTABLE *ptbl, TR_NAME *gssname)
   return cur;
 }
 
+TRP_PEER *trp_ptable_find_servicename(TRP_PTABLE *ptbl, TR_NAME *servicename)
+{
+  TRP_PEER *cur=ptbl->head;
+  while ((cur!=NULL) && (0 != tr_name_cmp(trp_peer_get_servicename(cur), servicename)))
+    cur=cur->next;
+  return cur;
+}
+
 char *trp_peer_to_str(TALLOC_CTX *memctx, TRP_PEER *peer, const char *sep)
 {
   if (sep==NULL)
index e5b9df7..01c4d58 100644 (file)
@@ -98,3 +98,22 @@ int trp_req_is_wildcard(TRP_REQ *req)
 {
   return (req!=NULL) && trp_req_name_is_wildcard(req->comm) && trp_req_name_is_wildcard(req->realm);
 }
+
+TRP_RC trp_req_make_wildcard(TRP_REQ *req)
+{
+  if (req==NULL)
+    return TRP_BADARG;
+
+  req->comm=tr_new_name("");
+  if (req->comm==NULL)
+    return TRP_NOMEM;
+
+  req->realm=tr_new_name("");
+  if (req->realm==NULL) {
+    tr_free_name(req->comm);
+    req->comm=NULL;
+    return TRP_NOMEM;
+  }
+
+  return TRP_SUCCESS;
+}
index eb135ab..cb0d1ed 100644 (file)
@@ -386,7 +386,7 @@ void trps_handle_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *conn)
     trp_connection_close(conn);
   } else {
     tr_notice("trps_handle_connection: authorized connection");
-  
+
     /* loop as long as the connection exists */
     while (trp_connection_get_status(conn)==TRP_CONNECTION_UP) {
       rc=trps_read_message(trps, conn, &msg);
@@ -888,8 +888,8 @@ static TRP_INFOREC *trps_route_to_inforec(TALLOC_CTX *mem_ctx, TRPS_INSTANCE *tr
     if (trp_route_is_local(route))
       linkcost=0;
     else {
-      linkcost=trp_peer_get_linkcost(trps_get_peer(trps,
-                                                   trp_route_get_next_hop(route)));
+      linkcost=trp_peer_get_linkcost(trps_get_peer_by_gssname(trps,
+                                                              trp_route_get_next_hop(route)));
     }
 
     /* Note that we leave the next hop empty since the recipient fills that in.
@@ -924,22 +924,28 @@ static TRP_RC trps_update_one_peer(TRPS_INSTANCE *trps,
   size_t n_updates=0, ii=0;
   char *encoded=NULL;
   TRP_RC rc=TRP_ERROR;
-  TRP_PEER *peer=trps_get_peer(trps, peer_gssname);
+  TRP_PEER *peer=trps_get_peer_by_gssname(trps, peer_gssname);
 
   if (!trps_peer_connected(trps, peer)) {
     tr_debug("trps_update_one_peer: no TRP connection to %.*s, skipping.",
              peer_gssname->len, peer_gssname->buf);
     goto cleanup;
   }
-  if (update_type==TRP_UPDATE_TRIGGERED) {
+  switch (update_type) {
+  case TRP_UPDATE_TRIGGERED:
     tr_debug("trps_update_one_peer: preparing triggered route update for %.*s",
              peer_gssname->len, peer_gssname->buf);
-  } else {
+    break;
+  case TRP_UPDATE_SCHEDULED:
     tr_debug("trps_update_one_peer: preparing scheduled route update for %.*s",
              peer_gssname->len, peer_gssname->buf);
+    break;
+  case TRP_UPDATE_REQUESTED:
+    tr_debug("trps_update_one_peer: preparing requested route update for %.*s",
+             peer_gssname->len, peer_gssname->buf);
   }
-  /* do not fill in peer, recipient does that */
 
+  /* do not fill in peer, recipient does that */
   if ((comm==NULL) && (realm==NULL)) {
     /* do all realms */
     update_list=trps_select_updates_for_peer(tmp_ctx,
@@ -957,9 +963,15 @@ static TRP_RC trps_update_one_peer(TRPS_INSTANCE *trps,
     }
     *update_list=trps_select_realm_update(trps, comm, realm, peer_gssname);
     if (*update_list==NULL) {
-      /* no update to send */
-      rc=TRP_SUCCESS;
-      goto cleanup;
+      /* we have no actual update to send back, MUST send a retraction */
+      tr_debug("trps_update_one_peer: community/realm without route requested, sending mandatory retraction.");
+      *update_list=trp_route_new(update_list);
+      trp_route_set_apc(*update_list, tr_dup_name(comm));
+      trp_route_set_realm(*update_list, tr_dup_name(realm));
+      trp_route_set_peer(*update_list, tr_new_name(""));
+      trp_route_set_metric(*update_list, TRP_METRIC_INFINITY);
+      trp_route_set_trust_router(*update_list, tr_new_name(""));
+      trp_route_set_next_hop(*update_list, tr_new_name(""));
     }
     n_updates=1;
   } else {
@@ -998,8 +1010,8 @@ static TRP_RC trps_update_one_peer(TRPS_INSTANCE *trps,
     else
       tr_debug("trps_update_one_peer: update queued successfully.");
 
-    encoded=NULL;
     tr_msg_free_encoded(encoded);
+    encoded=NULL;
     trp_upd_free(upd);
     upd=NULL;
   }
@@ -1048,9 +1060,14 @@ TRP_RC trps_add_peer(TRPS_INSTANCE *trps, TRP_PEER *peer)
   return trp_ptable_add(trps->ptable, peer);
 }
 
-TRP_PEER *trps_get_peer(TRPS_INSTANCE *trps, TR_NAME *gssname)
+TRP_PEER *trps_get_peer_by_gssname(TRPS_INSTANCE *trps, TR_NAME *gssname)
 {
-  return trp_ptable_find(trps->ptable, gssname);
+  return trp_ptable_find_gssname(trps->ptable, gssname);
+}
+
+TRP_PEER *trps_get_peer_by_servicename(TRPS_INSTANCE *trps, TR_NAME *servicename)
+{
+  return trp_ptable_find_servicename(trps->ptable, servicename);
 }
 
 int trps_peer_connected(TRPS_INSTANCE *trps, TRP_PEER *peer)
@@ -1113,3 +1130,50 @@ TRP_RC trps_handle_tr_msg(TRPS_INSTANCE *trps, TR_MSG *tr_msg)
   }
 }
 
+/* send wildcard route request to a peer */
+TRP_RC trps_wildcard_route_req(TRPS_INSTANCE *trps, TR_NAME *peer_servicename)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TRP_PEER *peer=trps_get_peer_by_servicename(trps, peer_servicename);
+  TR_MSG msg; /* not a pointer */
+  TRP_REQ *req=trp_req_new(tmp_ctx);
+  char *encoded=NULL;
+  TRP_RC rc=TRP_ERROR;
+
+  if (peer==NULL) {
+    tr_err("trps_wildcard_route_req: unknown peer (%.*s).", peer_servicename->len, peer_servicename->buf);
+    rc=TRP_BADARG;
+    goto cleanup;
+  }
+  if ((req==NULL) || (trp_req_make_wildcard(req)!=TRP_SUCCESS)) {
+    tr_err("trps_wildcard_route_req: unable to create wildcard TRP request.");
+    rc=TRP_NOMEM;
+    goto cleanup;
+  }
+
+  tr_msg_set_trp_req(&msg, req);
+  encoded=tr_msg_encode(&msg);
+  if (encoded==NULL) {
+    tr_err("trps_wildcard_route_req: error encoding wildcard TRP request.");
+    rc=TRP_ERROR;
+    goto cleanup;
+  }
+
+  tr_debug("trps_wildcard_route_req: adding message to queue.");
+  if (trps_send_msg(trps, peer, encoded) != TRP_SUCCESS) {
+    tr_err("trps_wildcard_route_req: error queueing request.");
+    rc=TRP_ERROR;
+  } else {
+    tr_debug("trps_wildcard_route_req: request queued successfully.");
+    rc=TRP_SUCCESS;
+  }
+  tr_crit("got here");
+cleanup:
+  if (encoded!=NULL)
+    tr_msg_free_encoded(encoded);
+  if (req!=NULL)
+    trp_req_free(req);
+
+  talloc_free(tmp_ctx);
+  return rc;
+}