updates.
[silc.git] / lib / silcclient / client_keyagr.c
index 487b8b5b7e6ae94bb77a2f6a035e2b5dbcb675fb..41c6edff3091daadfba0ceda553133799988e920 100644 (file)
@@ -36,12 +36,13 @@ SILC_TASK_CALLBACK(silc_client_perform_key_agreement_start);
 struct SilcClientKeyAgreementStruct {
   SilcClient client;
   SilcClientConnection conn;
-  int fd;                              /* Listening/connection socket */
-  SilcSocketConnection sock;           /* Remote socket connection */
-  SilcClientEntry client_entry;                /* Destination client */
-  SilcKeyAgreementCallback completion; /* Key agreement completion */
-  void *context;                       /* User context */
-  SilcTask timeout;                    /* Timeout task */
+  int fd;                                /* Listening/connection socket */
+  SilcSocketConnection sock;             /* Remote socket connection */
+  SilcClientEntry client_entry;                  /* Destination client */
+  SilcKeyAgreementCallback completion;   /* Key agreement completion */
+  void *context;                         /* User context */
+  SilcTask timeout;                      /* Timeout task */
+  SilcClientKEInternalContext *proto_ctx; /* Key Exchange protocol context */
 };
 
 /* Packet sending function used by the SKE in the key agreement process. */
@@ -69,6 +70,21 @@ static void silc_client_key_agreement_send_packet(SilcSKE ske,
   ske->sock->user_data = tmp;
 }
 
+/* Timeout callback that is called to close the connection and free the
+   socket connection data. */
+
+SILC_TASK_CALLBACK(silc_client_key_agreement_close)
+{
+  SilcClientKeyAgreement ke = (SilcClientKeyAgreement)context;
+
+  silc_schedule_unset_listen_fd(ke->client->schedule, ke->sock->sock);
+  silc_schedule_unset_listen_fd(ke->client->schedule, ke->fd);
+  silc_net_close_connection(ke->sock->sock);
+  silc_net_close_connection(ke->fd);
+  silc_socket_free(ke->sock);
+  silc_free(ke);
+}
+
 /* This callback is called after the key agreement protocol has been
    performed. This calls the final completion callback for the application. */
 
@@ -86,7 +102,8 @@ SILC_TASK_CALLBACK(silc_client_key_agreement_final)
       protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     ke->client_entry->ke = NULL;
-    ke->completion(ke->client, ke->conn, ke->client_entry, NULL, ke->context);
+    ke->completion(ke->client, ke->conn, ke->client_entry, 
+                  SILC_KEY_AGREEMENT_ERROR, NULL, ke->context);
     silc_ske_free_key_material(ctx->keymat);
     goto out;
   }
@@ -94,8 +111,8 @@ SILC_TASK_CALLBACK(silc_client_key_agreement_final)
   /* Pass the negotiated key material to the application. The application
      is responsible of freeing the key material. */
   ke->client_entry->ke = NULL;
-  ke->completion(ke->client, ke->conn, ke->client_entry, ctx->keymat, 
-                ke->context);
+  ke->completion(ke->client, ke->conn, ke->client_entry, 
+                SILC_KEY_AGREEMENT_OK, ctx->keymat, ke->context);
 
  out:
   silc_protocol_free(protocol);
@@ -103,13 +120,18 @@ SILC_TASK_CALLBACK(silc_client_key_agreement_final)
     silc_ske_free(ctx->ske);
   if (ctx->dest_id)
     silc_free(ctx->dest_id);
-  silc_task_unregister_by_callback(client->timeout_queue,
-                                  silc_client_failure_callback);
   silc_task_unregister_by_fd(client->io_queue, ke->fd);
+  silc_schedule_unset_listen_fd(ke->client->schedule, ke->fd);
+  silc_net_close_connection(ke->fd);
   if (ke->timeout)
     silc_task_unregister(client->timeout_queue, ke->timeout);
-  silc_socket_free(ke->sock);
-  silc_free(ke);
+  silc_client_del_socket(ke->client, ke->sock);
+
+  silc_task_register(client->timeout_queue, 0, 
+                    silc_client_key_agreement_close,
+                    (void *)ke, 0, 1, 
+                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+
   silc_free(ctx);
 }
 
@@ -130,12 +152,15 @@ SILC_TASK_CALLBACK(silc_client_process_key_agreement)
 
   sock = silc_net_accept_connection(ke->fd);
   if (sock < 0) {
-    client->ops->say(client, conn, 
+    client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
                     "Could not accept key agreement connection: ", 
                     strerror(errno));
     ke->client_entry->ke = NULL;
-    ke->completion(ke->client, ke->conn, ke->client_entry, NULL, ke->context);
+    ke->completion(ke->client, ke->conn, ke->client_entry, 
+                  SILC_KEY_AGREEMENT_ERROR, NULL, ke->context);
     silc_task_unregister_by_fd(client->io_queue, ke->fd);
+    silc_schedule_unset_listen_fd(ke->client->schedule, ke->fd);
+    silc_net_close_connection(ke->fd);
     if (ke->timeout)
       silc_task_unregister(client->timeout_queue, ke->timeout);
     silc_free(ke);
@@ -155,11 +180,14 @@ SILC_TASK_CALLBACK(silc_client_process_key_agreement)
   /* Perform name and address lookups for the remote host. */
   silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
   if (!newsocket->hostname && !newsocket->ip) {
-    client->ops->say(client, conn, 
+    client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
                     "Could not resolve the remote IP or hostname");
     ke->client_entry->ke = NULL;
-    ke->completion(ke->client, ke->conn, ke->client_entry, NULL, ke->context);
+    ke->completion(ke->client, ke->conn, ke->client_entry, 
+                  SILC_KEY_AGREEMENT_ERROR, NULL, ke->context);
     silc_task_unregister_by_fd(client->io_queue, ke->fd);
+    silc_schedule_unset_listen_fd(ke->client->schedule, ke->fd);
+    silc_net_close_connection(ke->fd);
     if (ke->timeout)
       silc_task_unregister(client->timeout_queue, ke->timeout);
     silc_free(ke);
@@ -168,16 +196,19 @@ SILC_TASK_CALLBACK(silc_client_process_key_agreement)
   if (!newsocket->hostname)
     newsocket->hostname = strdup(newsocket->ip);
   newsocket->port = silc_net_get_remote_port(sock);
+  silc_client_add_socket(client, newsocket);
 
   /* Allocate internal context for key exchange protocol. This is
      sent as context for the protocol. */
   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
   proto_ctx->client = client;
-  proto_ctx->sock = newsocket;
+  proto_ctx->sock = silc_socket_dup(newsocket);
   proto_ctx->rng = client->rng;
   proto_ctx->responder = TRUE;
   proto_ctx->context = context;
   proto_ctx->send_packet = silc_client_key_agreement_send_packet;
+  proto_ctx->verify = silc_client_protocol_ke_verify_key;
+  ke->proto_ctx = proto_ctx;
 
   /* Prepare the connection for key exchange protocol. We allocate the
      protocol but will not start it yet. The connector will be the
@@ -193,6 +224,7 @@ SILC_TASK_CALLBACK(silc_client_process_key_agreement)
      However, this doesn't set the scheduler for outgoing traffic, it
      will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
      later when outgoing data is available. */
+  context = (void *)client;
   SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
 }
 
@@ -205,15 +237,21 @@ SILC_TASK_CALLBACK(silc_client_key_agreement_timeout)
   SilcClientKeyAgreement ke = (SilcClientKeyAgreement)context;
 
   ke->client_entry->ke = NULL;
-  ke->completion(ke->client, ke->conn, ke->client_entry, NULL, ke->context);
+  ke->completion(ke->client, ke->conn, ke->client_entry, 
+                SILC_KEY_AGREEMENT_TIMEOUT, NULL, ke->context);
 
-  if (ke->sock)
+  if (ke->sock) {
+    silc_client_del_socket(ke->client, ke->sock);
     silc_socket_free(ke->sock);
+  }
+  if (ke->proto_ctx && ke->proto_ctx->ske)
+    silc_ske_free(ke->proto_ctx->ske);
   ke->client_entry->ke = NULL;
+  if (ke->fd)
+    silc_task_unregister_by_fd(ke->client->io_queue, ke->fd);
+  silc_schedule_unset_listen_fd(ke->client->schedule, ke->fd);
+  silc_net_close_connection(ke->fd);
   silc_free(ke);
-  silc_task_unregister_by_callback(ke->client->timeout_queue,
-                                  silc_client_failure_callback);
-  silc_task_unregister_by_fd(ke->client->io_queue, ke->fd);
 }
 
 /* Sends key agreement request to the remote client indicated by the
@@ -245,6 +283,10 @@ SILC_TASK_CALLBACK(silc_client_key_agreement_timeout)
    If remote side decides to ignore the request the `completion' will be
    called after the specified timeout, `timeout_secs'. 
 
+   NOTE: If the `hostname' and the `port' was not provided the `completion'
+   will not be called at all since this does nothing more than sending
+   a packet to the remote host.
+
    NOTE: There can be only one active key agreement for one client entry.
    Before setting new one, the old one must be finished (it is finished
    after calling the completion callback) or the function 
@@ -255,12 +297,12 @@ void silc_client_send_key_agreement(SilcClient client,
                                    SilcClientEntry client_entry,
                                    char *hostname,
                                    int port,
-                                   unsigned long timeout_secs,
+                                   uint32 timeout_secs,
                                    SilcKeyAgreementCallback completion,
                                    void *context)
 {
   SilcSocketConnection sock = conn->sock;
-  SilcClientKeyAgreement ke;
+  SilcClientKeyAgreement ke = NULL;
   SilcBuffer buffer;
 
   assert(client_entry);
@@ -274,10 +316,11 @@ void silc_client_send_key_agreement(SilcClient client,
     ke->fd = silc_net_create_server(port, hostname);
 
     if (ke->fd < 0) {
-      client->ops->say(client, conn, 
+      client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, 
                       "Cannot create listener on %s on port %d: %s", 
                       hostname, port, strerror(errno));
-      completion(client, conn, client_entry, NULL, context);
+      completion(client, conn, client_entry, SILC_KEY_AGREEMENT_FAILURE,
+                NULL, context);
       silc_free(ke);
       return;
     }
@@ -299,15 +342,16 @@ void silc_client_send_key_agreement(SilcClient client,
     /* Register a timeout task that will be executed if the connector
        will not start the key exchange protocol within the specified 
        timeout. */
-    ke->timeout = 
-      silc_task_register(client->timeout_queue, 0, 
-                        silc_client_key_agreement_timeout,
-                        (void *)ke, timeout_secs, 0, 
-                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+    ke->timeout = silc_task_register(client->timeout_queue, 0, 
+                                    silc_client_key_agreement_timeout,
+                                    (void *)ke, timeout_secs, 0, 
+                                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
   }
 
   /* Encode the key agreement payload */
-  buffer = silc_key_agreement_payload_encode(hostname, port);
+  buffer = silc_key_agreement_payload_encode(hostname, 
+                                            !ke ? port : 
+                                            silc_net_get_local_port(ke->fd));
 
   /* Send the key agreement packet to the client */
   silc_client_packet_send(client, sock, SILC_PACKET_KEY_AGREEMENT,
@@ -334,7 +378,7 @@ silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx)
                                 SILC_TASK_FD,
                                 SILC_TASK_PRI_NORMAL);
   silc_task_reset_iotype(ctx->task, SILC_TASK_WRITE);
-  silc_schedule_set_listen_fd(sock, ctx->task->iomask);
+  silc_schedule_set_listen_fd(ctx->client->schedule, sock, ctx->task->iomask);
 
   ctx->sock = sock;
 
@@ -380,18 +424,19 @@ SILC_TASK_CALLBACK(silc_client_perform_key_agreement_start)
   SILC_LOG_DEBUG(("Start"));
 
   /* Check the socket status as it might be in error */
-  getsockopt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
+  silc_net_get_socket_opt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
   if (opt != 0) {
     if (ctx->tries < 2) {
       /* Connection failed but lets try again */
-      client->ops->say(client, conn, "Could not connect to client %s: %s",
+      client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                      "Could not connect to client %s: %s",
                       ctx->host, strerror(opt));
-      client->ops->say(client, conn, 
+      client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
                       "Connecting to port %d of client %s resumed", 
                       ctx->port, ctx->host);
 
       /* Unregister old connection try */
-      silc_schedule_unset_listen_fd(fd);
+      silc_schedule_unset_listen_fd(client->schedule, fd);
       silc_net_close_connection(fd);
       silc_task_unregister(client->io_queue, ctx->task);
 
@@ -400,32 +445,35 @@ SILC_TASK_CALLBACK(silc_client_perform_key_agreement_start)
       ctx->tries++;
     } else {
       /* Connection failed and we won't try anymore */
-      client->ops->say(client, conn, "Could not connect to client %s: %s",
+      client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                      "Could not connect to client %s: %s",
                       ctx->host, strerror(opt));
-      silc_schedule_unset_listen_fd(fd);
+      silc_schedule_unset_listen_fd(client->schedule, fd);
       silc_net_close_connection(fd);
       silc_task_unregister(client->io_queue, ctx->task);
+      silc_free(ctx->host);
       silc_free(ctx);
 
       /* Call the completion callback */
       ke->completion(ke->client, ke->conn, ke->client_entry, 
-                    NULL, ke->context);
+                    SILC_KEY_AGREEMENT_FAILURE, NULL, ke->context);
       silc_free(ke);
     }
     return;
   }
 
-  silc_schedule_unset_listen_fd(fd);
+  silc_schedule_unset_listen_fd(client->schedule, fd);
   silc_task_unregister(client->io_queue, ctx->task);
-  silc_free(ctx);
 
   ke->fd = fd;
 
   /* Now actually perform the key agreement protocol */
   silc_client_perform_key_agreement_fd(ke->client, ke->conn,
-                                      ke->client_entry, ke->fd,
+                                      ke->client_entry, ke->fd, ctx->host,
                                       ke->completion, ke->context);
   silc_free(ke);
+  silc_free(ctx->host);
+  silc_free(ctx);
 }
 
 /* Performs the actual key agreement protocol. Application may use this
@@ -458,6 +506,8 @@ void silc_client_perform_key_agreement(SilcClient client,
 {
   SilcClientKeyAgreement ke;
 
+  SILC_LOG_DEBUG(("Start"));
+
   assert(client_entry && hostname && port);
 
   ke = silc_calloc(1, sizeof(*ke));
@@ -470,7 +520,8 @@ void silc_client_perform_key_agreement(SilcClient client,
   /* Connect to the remote client */
   ke->fd = silc_client_connect_to_client(client, conn, port, hostname, ke);
   if (ke->fd < 0) {
-    completion(client, conn, client_entry, NULL, context);
+    completion(client, conn, client_entry, SILC_KEY_AGREEMENT_FAILURE,
+              NULL, context);
     silc_free(ke);
     return;
   }
@@ -485,6 +536,7 @@ void silc_client_perform_key_agreement_fd(SilcClient client,
                                          SilcClientConnection conn,
                                          SilcClientEntry client_entry,
                                          int sock,
+                                         char *hostname,
                                          SilcKeyAgreementCallback completion,
                                          void *context)
 {
@@ -492,6 +544,8 @@ void silc_client_perform_key_agreement_fd(SilcClient client,
   SilcClientKEInternalContext *proto_ctx;
   SilcProtocol protocol;
 
+  SILC_LOG_DEBUG(("Start"));
+
   assert(client_entry);
 
   ke = silc_calloc(1, sizeof(*ke));
@@ -504,16 +558,21 @@ void silc_client_perform_key_agreement_fd(SilcClient client,
 
   /* Allocate new socket connection object */
   silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, (void *)conn, &ke->sock);
+  silc_client_add_socket(client, ke->sock);
+  ke->sock->hostname = strdup(hostname);
+  ke->sock->port = silc_net_get_remote_port(sock);
 
   /* Allocate internal context for key exchange protocol. This is
      sent as context for the protocol. */
   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
   proto_ctx->client = client;
-  proto_ctx->sock = ke->sock;
+  proto_ctx->sock = silc_socket_dup(ke->sock);
   proto_ctx->rng = client->rng;
   proto_ctx->responder = FALSE;
   proto_ctx->context = ke;
   proto_ctx->send_packet = silc_client_key_agreement_send_packet;
+  proto_ctx->verify = silc_client_protocol_ke_verify_key;
+  ke->proto_ctx = proto_ctx;
 
   /* Perform key exchange protocol. */
   silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, 
@@ -531,7 +590,7 @@ void silc_client_perform_key_agreement_fd(SilcClient client,
   SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
 
   /* Execute the protocol */
-  protocol->execute(client->timeout_queue, 0, protocol, sock, 0, 0);
+  silc_protocol_execute(protocol, client->timeout_queue, 0, 0);
 }
 
 /* This function can be called to unbind the hostname and the port for
@@ -548,8 +607,10 @@ void silc_client_abort_key_agreement(SilcClient client,
   assert(client_entry);
 
   if (client_entry->ke) {
-    if (client_entry->ke->sock)
+    if (client_entry->ke->sock) {
+      silc_client_del_socket(client_entry->ke->client, client_entry->ke->sock);
       silc_socket_free(client_entry->ke->sock);
+    }
     client_entry->ke = NULL;
     silc_task_unregister_by_fd(client->io_queue, client_entry->ke->fd);
     if (client_entry->ke->timeout)
@@ -567,7 +628,7 @@ static void
 silc_client_key_agreement_resolve_cb(SilcClient client,
                                     SilcClientConnection conn,
                                     SilcClientEntry *clients,
-                                    unsigned int clients_count,
+                                    uint32 clients_count,
                                     void *context)
 {
   SilcPacketContext *packet = (SilcPacketContext *)context;