xpc: support termination of IKE_SAs using XPC RPC on connection channel
authorMartin Willi <martin@revosec.ch>
Thu, 2 May 2013 15:45:58 +0000 (17:45 +0200)
committerMartin Willi <martin@revosec.ch>
Thu, 18 Jul 2013 10:17:55 +0000 (12:17 +0200)
src/frontends/osx/charon-xpc/xpc_channels.c

index 0d82d70..ce2c83b 100644 (file)
@@ -76,34 +76,128 @@ static void destroy_entry(entry_t *entry)
 }
 
 /**
- * Remove an entry for a given XPC connection
+ * Find an IKE_SA unique identifier by a given XPC channel
  */
-static void remove_conn(private_xpc_channels_t *this, xpc_connection_t conn)
+static u_int32_t find_ike_sa_by_conn(private_xpc_channels_t *this,
+                                                                        xpc_connection_t conn)
 {
        enumerator_t *enumerator;
        entry_t *entry;
+       u_int32_t ike_sa = 0;
 
-       this->lock->write_lock(this->lock);
+       this->lock->read_lock(this->lock);
        enumerator = this->channels->create_enumerator(this->channels);
        while (enumerator->enumerate(enumerator, NULL, &entry))
        {
                if (entry->conn == conn)
                {
-                       this->channels->remove(this->channels, enumerator);
-                       destroy_entry(entry);
+                       ike_sa = entry->sa;
                        break;
                }
        }
        enumerator->destroy(enumerator);
        this->lock->unlock(this->lock);
+
+       return ike_sa;
+}
+
+/**
+ * Remove an entry for a given XPC connection
+ */
+static void remove_conn(private_xpc_channels_t *this, xpc_connection_t conn)
+{
+       uintptr_t ike_sa;
+       entry_t *entry;
+
+       ike_sa = find_ike_sa_by_conn(this, conn);
+       if (ike_sa)
+       {
+               this->lock->write_lock(this->lock);
+               entry = this->channels->remove(this->channels, (void*)ike_sa);
+               this->lock->unlock(this->lock);
+
+               if (entry)
+               {
+                       destroy_entry(entry);
+               }
+       }
 }
 
 /**
+ * Trigger termination of a connection
+ */
+static void stop_connection(private_xpc_channels_t *this, u_int32_t ike_sa,
+                                                       xpc_object_t request, xpc_object_t reply)
+{
+       status_t status;
+
+       status = charon->controller->terminate_ike(charon->controller, ike_sa,
+                                                                                          NULL, NULL, 0);
+       xpc_dictionary_set_bool(reply, "success", status != NOT_FOUND);
+}
+
+/**
+ * XPC RPC command dispatch table
+ */
+static struct {
+       char *name;
+       void (*handler)(private_xpc_channels_t *this, u_int32_t ike_sa,
+                                       xpc_object_t request, xpc_object_t reply);
+} commands[] = {
+       { "stop_connection", stop_connection },
+};
+
+/**
  * Handle a request message from App
  */
-static void handle(private_xpc_channels_t *this, xpc_object_t request)
+static void handle(private_xpc_channels_t *this, xpc_connection_t conn,
+                                  xpc_object_t request)
 {
-       /* TODO: */
+       xpc_object_t reply;
+       const char *type, *rpc;
+       bool found = FALSE;
+       u_int32_t ike_sa;
+       int i;
+
+       type = xpc_dictionary_get_string(request, "type");
+       if (type)
+       {
+               if (streq(type, "rpc"))
+               {
+                       reply = xpc_dictionary_create_reply(request);
+                       rpc = xpc_dictionary_get_string(request, "rpc");
+                       ike_sa = find_ike_sa_by_conn(this, conn);
+                       if (reply && rpc && ike_sa)
+                       {
+                               for (i = 0; i < countof(commands); i++)
+                               {
+                                       if (streq(commands[i].name, rpc))
+                                       {
+                                               found = TRUE;
+                                               commands[i].handler(this, ike_sa, request, reply);
+                                               break;
+                                       }
+                               }
+                       }
+                       if (!found)
+                       {
+                               DBG1(DBG_CFG, "received invalid XPC rpc command: %s", rpc);
+                       }
+                       if (reply)
+                       {
+                               xpc_connection_send_message(conn, reply);
+                               xpc_release(reply);
+                       }
+               }
+               else
+               {
+                       DBG1(DBG_CFG, "received unknown XPC message type: %s", type);
+               }
+       }
+       else
+       {
+               DBG1(DBG_CFG, "received XPC message without a type");
+       }
 }
 
 METHOD(xpc_channels_t, add, void,
@@ -126,7 +220,7 @@ METHOD(xpc_channels_t, add, void,
                }
                else if (xpc_get_type(event) == XPC_TYPE_DICTIONARY)
                {
-                       handle(this, event);
+                       handle(this, conn, event);
                }
        });