Allow rekey with PFS in backup router (for disabled connections).
[silc.git] / apps / silcd / server.c
index 963746f9045acc439a214a8294b17724b529f0cd..20188476f68f96e6aa3bea390095a2e0702580ed 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2003 Pekka Riikonen
+  Copyright (C) 1997 - 2004 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -42,6 +42,7 @@ SILC_TASK_CALLBACK(silc_server_free_client_data_timeout);
 SILC_TASK_CALLBACK(silc_server_timeout_remote);
 SILC_TASK_CALLBACK(silc_server_channel_key_rekey);
 SILC_TASK_CALLBACK(silc_server_get_stats);
+SILC_TASK_CALLBACK(silc_server_connect_router);
 
 /* Allocates a new SILC server object. This has to be done before the server
    can be used. After allocation one must call silc_server_init to initialize
@@ -128,6 +129,9 @@ void silc_server_free(SilcServer server)
   if (list)
     silc_idcache_list_free(list);
 
+  if (server->pk_hash)
+    silc_hash_table_free(server->pk_hash);
+
   /* Delete all clients */
   list = NULL;
   if (silc_idcache_get_all(server->local_list->clients, &list) &&
@@ -148,6 +152,7 @@ void silc_server_free(SilcServer server)
   if (list)
     silc_idcache_list_free(list);
 
+
   /* Delete all servers */
   list = NULL;
   if (silc_idcache_get_all(server->local_list->servers, &list) &&
@@ -175,6 +180,7 @@ void silc_server_free(SilcServer server)
   silc_idcache_free(server->global_list->servers);
   silc_idcache_free(server->global_list->channels);
   silc_hash_table_free(server->watcher_list);
+  silc_hash_table_free(server->watcher_list_pk);
 
   silc_hash_free(server->md5hash);
   silc_hash_free(server->sha1hash);
@@ -348,13 +354,28 @@ bool silc_server_init(SilcServer server)
   server->global_list->servers = silc_idcache_alloc(0, SILC_ID_SERVER, NULL);
   server->global_list->channels = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL);
 
-  /* Init watcher list */
+  /* Init watcher lists */
   server->watcher_list =
     silc_hash_table_alloc(1, silc_hash_client_id_hash, NULL,
                          silc_hash_data_compare, (void *)CLIENTID_HASH_LEN,
                          NULL, NULL, TRUE);
   if (!server->watcher_list)
     goto err;
+  server->watcher_list_pk =
+    silc_hash_table_alloc(1, silc_hash_public_key, NULL,
+                         silc_hash_public_key_compare, NULL,
+                         NULL, NULL, TRUE);
+  if (!server->watcher_list_pk)
+    goto err;
+
+  /* Init public key list */
+  server->pk_hash =
+    silc_hash_table_alloc(0, silc_hash_public_key, NULL,
+                          silc_hash_public_key_compare, NULL,
+                          NULL, NULL, TRUE);
+
+  if (!server->pk_hash)
+    goto err;
 
   /* Create a listening server */
   if (!silc_server_listen(server,
@@ -730,8 +751,6 @@ bool silc_server_rehash(SilcServer server)
   if (server->config->debug_string) {
     silc_debug = TRUE;
     silc_log_set_debug_string(server->config->debug_string);
-  } else {
-    silc_debug = FALSE;
   }
 #endif /* SILC_DEBUG */
 
@@ -949,6 +968,43 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
                         SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 }
 
+/* callback for async connection to remote router */
+
+SILC_TASK_CALLBACK(silc_server_connection_established)
+{
+  SilcServer server = app_context;
+  SilcServerConnection sconn = (SilcServerConnection)context;
+  int sock = fd;
+  int opt = EINVAL, optlen = sizeof(opt);
+
+  silc_schedule_task_del_by_fd(server->schedule, sock);
+  silc_schedule_unset_listen_fd(server->schedule, sock);
+
+  if (silc_net_get_socket_opt(sock, SOL_SOCKET, SO_ERROR, &opt, &optlen) ||
+      (opt != 0)) {
+    SILC_LOG_ERROR(("Could not connect to router %s:%d: %s",
+                   sconn->remote_host, sconn->remote_port,
+                   strerror(opt)));
+    if (!sconn->no_reconnect)
+      silc_schedule_task_add(server->schedule, 0,
+                            silc_server_connect_to_router_retry,
+                            context, 0, 1, SILC_TASK_TIMEOUT,
+                            SILC_TASK_PRI_NORMAL);
+    else {
+      silc_server_config_unref(&sconn->conn);
+      silc_free(sconn->remote_host);
+      silc_free(sconn->backup_replace_ip);
+      silc_free(sconn);
+    }
+    return;
+  }
+
+  SILC_LOG_DEBUG(("Connection to router %s:%d established", sconn->remote_host,
+                 sconn->remote_port));
+
+  /* Continue with key exchange protocol */
+  silc_server_start_key_exchange(server, sconn, sock);
+}
 /* Generic routine to use connect to a router. */
 
 SILC_TASK_CALLBACK(silc_server_connect_router)
@@ -985,7 +1041,7 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
   silc_server_config_ref(&sconn->conn, server->config, (void *)rconn);
 
   /* Connect to remote host */
-  sock = silc_net_create_connection(
+  sock = silc_net_create_connection_async(
                 (!server->config->server_info->primary ? NULL :
                  server->config->server_info->primary->server_ip),
                 sconn->remote_port, sconn->remote_host);
@@ -1006,8 +1062,13 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
     return;
   }
 
-  /* Continue with key exchange protocol */
-  silc_server_start_key_exchange(server, sconn, sock);
+  /* wait for the connection to be established */
+  silc_schedule_task_add(server->schedule, sock,
+                        silc_server_connection_established,
+                        context, 0, 0, SILC_TASK_FD,
+                        SILC_TASK_PRI_NORMAL);
+  silc_schedule_set_listen_fd(server->schedule, sock,
+                             SILC_TASK_WRITE, FALSE);
 }
 
 /* This function connects to our primary router or if we are a router this
@@ -1551,7 +1612,7 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
                 sock->ip));
 
   /* Listenning port */
-  if (!server->sockets[(SilcUInt32)proto_ctx->context]) {
+  if (!server->sockets[SILC_PTR_TO_32(proto_ctx->context)]) {
     silc_server_disconnect_remote(server, sock,
                                  SILC_STATUS_ERR_RESOURCE_LIMIT,
                                  "Connection refused");
@@ -1559,7 +1620,7 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
     silc_free(proto_ctx);
     return;
   }
-  port = server->sockets[(SilcUInt32)proto_ctx->context]->port;
+  port = server->sockets[SILC_PTR_TO_32(proto_ctx->context)]->port;
 
   /* Check whether this connection is denied to connect to us. */
   deny = silc_server_config_find_denied(server, sock->ip);
@@ -1685,7 +1746,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
      is accepted further. */
   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
   proto_ctx->server = server;
-  proto_ctx->context = (void *)fd;
+  proto_ctx->context = SILC_32_TO_PTR(fd);
   silc_socket_host_lookup(newsocket, TRUE,
                          silc_server_accept_new_connection_lookup,
                          (void *)proto_ctx, server->schedule);
@@ -1905,10 +1966,10 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
          silc_free(sock->user_data);
          server->stat.auth_failures++;
 
-         /* From here on, wait 10 seconds for the backup router to appear. */
+         /* From here on, wait 20 seconds for the backup router to appear. */
          silc_schedule_task_add(server->schedule, 0,
                                 silc_server_backup_router_wait,
-                                (void *)server, 10, 0,
+                                (void *)server, 20, 0,
                                 SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
          goto out;
        }
@@ -1959,6 +2020,10 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
          client->mode |= SILC_UMODE_ANONYMOUS;
       }
 
+      /* Add public key to hash list (for whois using attributes) */
+      silc_hash_table_add(server->pk_hash,
+                          entry->data.public_key, client);
+
       id_entry = (void *)client;
       break;
     }
@@ -2079,10 +2144,10 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
          silc_free(sock->user_data);
          server->stat.auth_failures++;
 
-         /* From here on, wait 10 seconds for the backup router to appear. */
+         /* From here on, wait 20 seconds for the backup router to appear. */
          silc_schedule_task_add(server->schedule, 0,
                                 silc_server_backup_router_wait,
-                                (void *)server, 10, 0,
+                                (void *)server, 20, 0,
                                 SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
          goto out;
        }
@@ -2142,7 +2207,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
        /* Remove the backup waiting with timeout */
        silc_schedule_task_add(server->schedule, 0,
                               silc_server_backup_router_wait,
-                              (void *)server, 5, 0,
+                              (void *)server, 10, 0,
                               SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
       }
 
@@ -2241,6 +2306,9 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
     /* Do not send data to disconnected connection */
     if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
       SILC_LOG_DEBUG(("Disconnected socket connection, cannot send"));
+      SILC_SET_CONNECTION_FOR_INPUT(server->schedule, fd);
+      SILC_UNSET_OUTBUF_PENDING(sock);
+      silc_buffer_clear(sock->outbuf);
       return;
     }
 
@@ -2270,8 +2338,17 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
                       sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
                       "Router")));
 
-      if (sock->user_data)
+      if (sock->user_data) {
+       /* If backup then mark that resuming will not be allowed */
+       if (server->server_type == SILC_ROUTER && !server->backup_router &&
+           sock->type == SILC_SOCKET_TYPE_SERVER) {
+         SilcServerEntry server_entry = sock->user_data;
+         if (server_entry->server_type == SILC_BACKUP_ROUTER)
+           server->backup_closed = TRUE;
+       }
+
        silc_server_free_sock_user_data(server, sock, NULL);
+      }
       SILC_SET_DISCONNECTING(sock);
       silc_server_close_connection(server, sock);
     }
@@ -2292,8 +2369,17 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
                       sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
                       "Router"), strerror(errno)));
 
-      if (sock->user_data)
+      if (sock->user_data) {
+       /* If backup then mark that resuming will not be allowed */
+       if (server->server_type == SILC_ROUTER && !server->backup_router &&
+           sock->type == SILC_SOCKET_TYPE_SERVER) {
+         SilcServerEntry server_entry = sock->user_data;
+         if (server_entry->server_type == SILC_BACKUP_ROUTER)
+           server->backup_closed = TRUE;
+       }
+
        silc_server_free_sock_user_data(server, sock, NULL);
+      }
       SILC_SET_DISCONNECTING(sock);
       silc_server_close_connection(server, sock);
     }
@@ -2318,7 +2404,7 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
     if (sock->user_data) {
       char tmp[128];
 
-      /* If backup disconnected then mark that resuming willl not be allowed */
+      /* If backup disconnected then mark that resuming will not be allowed */
       if (server->server_type == SILC_ROUTER && !server->backup_router &&
          sock->type == SILC_SOCKET_TYPE_SERVER && sock->user_data) {
        SilcServerEntry server_entry = sock->user_data;
@@ -2379,8 +2465,18 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
     if (SILC_PRIMARY_ROUTE(server) == sock && server->backup_router)
       server->backup_noswitch = TRUE;
 
-    if (sock->user_data)
+    if (sock->user_data) {
+      /* If we are router and backup errorred then mark that resuming
+        will not be allowed */
+      if (server->server_type == SILC_ROUTER && !server->backup_router &&
+         sock->type == SILC_SOCKET_TYPE_SERVER) {
+       SilcServerEntry server_entry = sock->user_data;
+       if (server_entry->server_type == SILC_BACKUP_ROUTER)
+         server->backup_closed = TRUE;
+      }
+
       silc_server_free_sock_user_data(server, sock, NULL);
+    }
     SILC_SET_DISCONNECTING(sock);
     silc_server_close_connection(server, sock);
   }
@@ -2397,6 +2493,11 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
   SilcIDListData idata = (SilcIDListData)sock->user_data;
   int ret;
 
+  if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
+    SILC_LOG_DEBUG(("Connection is disconnected"));
+    goto out;
+  }
+
   server->stat.packets_received++;
 
   /* Parse the packet */
@@ -2408,8 +2509,10 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
   /* If entry is disabled ignore what we got. */
   if (idata && idata->status & SILC_IDLIST_STATUS_DISABLED &&
       ret != SILC_PACKET_HEARTBEAT && ret != SILC_PACKET_RESUME_ROUTER &&
-      ret != SILC_PACKET_REKEY && ret != SILC_PACKET_REKEY_DONE) {
-    SILC_LOG_DEBUG(("Connection is disabled"));
+      ret != SILC_PACKET_REKEY && ret != SILC_PACKET_REKEY_DONE &&
+      ret != SILC_PACKET_KEY_EXCHANGE_1 && ret != SILC_PACKET_KEY_EXCHANGE_2) {
+    SILC_LOG_DEBUG(("Connection is disabled (packet %s dropped)",
+                   silc_get_packet_name(ret)));
     goto out;
   }
 
@@ -2500,6 +2603,11 @@ bool silc_server_packet_parse(SilcPacketParserContext *parser_context,
     silc_server_packet_parse_real(server->schedule, server, 0, sock->sock,
                                  parser_context);
 
+    if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
+      SILC_LOG_DEBUG(("Connection is disconnected"));
+      return FALSE;
+    }
+
     /* Reprocess data since we'll return FALSE here.  This is because
        the idata->receive_key might have become valid in the last packet
        and we want to call this processor with valid cipher. */
@@ -2547,8 +2655,6 @@ bool silc_server_packet_parse(SilcPacketParserContext *parser_context,
     silc_server_packet_parse_real(server->schedule, server, 0, sock->sock,
                                  parser_context);
     break;
-  default:
-    return TRUE;
   }
 
   return TRUE;
@@ -2594,7 +2700,7 @@ void silc_server_packet_parse_type(SilcServer server,
       /* Do not switch to backup in case of error */
       server->backup_noswitch = (status == SILC_STATUS_OK ? FALSE : TRUE);
 
-      /* If backup disconnected then mark that resuming willl not be allowed */
+      /* If backup disconnected then mark that resuming will not be allowed */
       if (server->server_type == SILC_ROUTER && !server->backup_router &&
          sock->type == SILC_SOCKET_TYPE_SERVER && sock->user_data) {
        SilcServerEntry server_entry = sock->user_data;
@@ -3138,6 +3244,7 @@ void silc_server_close_connection(SilcServer server,
                  "Router"), tmp[0] ? tmp : ""));
 
   SILC_SET_DISCONNECTED(sock);
+  silc_socket_set_qos(sock, 0, 0, 0, 0, NULL);
   silc_schedule_task_add(server->schedule, sock->sock,
                         silc_server_close_connection_final,
                         (void *)sock, 0, 1, SILC_TASK_TIMEOUT,
@@ -3256,6 +3363,11 @@ void silc_server_free_client_data(SilcServer server,
   /* Remove this client from watcher list if it is */
   silc_server_del_from_watcher_list(server, client);
 
+  /* Remove this client from the public key hash list */
+  if (client->data.public_key)
+    silc_hash_table_del_by_context(server->pk_hash,
+                                   client->data.public_key, client);
+
   /* Update statistics */
   server->stat.my_clients--;
   server->stat.clients--;
@@ -3302,6 +3414,8 @@ void silc_server_free_sock_user_data(SilcServer server,
     sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
     silc_protocol_execute_final(sock->protocol, server->schedule);
     sock->protocol = NULL;
+    if (!sock->user_data)
+      return;
   }
 
   switch (sock->type) {
@@ -4450,7 +4564,7 @@ void silc_server_announce_get_inviteban(SilcServer server,
     type = silc_hash_table_count(channel->invite_list);
     SILC_PUT16_MSB(type, list->data);
     silc_hash_table_list(channel->invite_list, &htl);
-    while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp2))
+    while (silc_hash_table_get(&htl, (void *)&type, (void *)&tmp2))
       list = silc_argument_payload_encode_one(list, tmp2->data, tmp2->len,
                                               type);
     silc_hash_table_list_reset(&htl);
@@ -4474,7 +4588,7 @@ void silc_server_announce_get_inviteban(SilcServer server,
     type = silc_hash_table_count(channel->ban_list);
     SILC_PUT16_MSB(type, list->data);
     silc_hash_table_list(channel->ban_list, &htl);
-    while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp2))
+    while (silc_hash_table_get(&htl, (void *)&type, (void *)&tmp2))
       list = silc_argument_payload_encode_one(list, tmp2->data, tmp2->len,
                                               type);
     silc_hash_table_list_reset(&htl);
@@ -4503,7 +4617,7 @@ void silc_server_announce_get_channel_users(SilcServer server,
   SilcBuffer chidp, clidp, csidp;
   SilcBuffer tmp, fkey = NULL, chpklist;
   int len;
-  unsigned char mode[4];
+  unsigned char mode[4], ulimit[4];
   char *hmac;
 
   SILC_LOG_DEBUG(("Start"));
@@ -4514,12 +4628,14 @@ void silc_server_announce_get_channel_users(SilcServer server,
 
   /* CMODE notify */
   SILC_PUT32_MSB(channel->mode, mode);
+  if (channel->mode & SILC_CHANNEL_MODE_ULIMIT)
+    SILC_PUT32_MSB(channel->user_limit, ulimit);
   hmac = channel->hmac ? (char *)silc_hmac_get_name(channel->hmac) : NULL;
   if (channel->founder_key)
     fkey = silc_pkcs_public_key_payload_encode(channel->founder_key);
   tmp =
     silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_CMODE_CHANGE,
-                                      7, csidp->data, csidp->len,
+                                      8, csidp->data, csidp->len,
                                       mode, sizeof(mode),
                                       NULL, 0,
                                       hmac, hmac ? strlen(hmac) : 0,
@@ -4529,7 +4645,13 @@ void silc_server_announce_get_channel_users(SilcServer server,
                                       fkey ? fkey->data : NULL,
                                       fkey ? fkey->len : 0,
                                       chpklist ? chpklist->data : NULL,
-                                      chpklist ? chpklist->len : 0);
+                                      chpklist ? chpklist->len : 0,
+                                      (channel->mode &
+                                       SILC_CHANNEL_MODE_ULIMIT ?
+                                       ulimit : NULL),
+                                      (channel->mode &
+                                       SILC_CHANNEL_MODE_ULIMIT ?
+                                       sizeof(ulimit) : 0));
   len = tmp->len;
   *channel_modes =
     silc_buffer_realloc(*channel_modes,
@@ -5143,7 +5265,7 @@ void silc_server_save_user_channels(SilcServer server,
      are no part of the list. */
   if (ht) {
     silc_hash_table_list(client->channels, &htl);
-    while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
+    while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
       if (!silc_hash_table_find(ht, chl->channel, NULL, NULL)) {
        silc_hash_table_del(chl->channel->user_list, chl->client);
        silc_hash_table_del(chl->client->channels, chl->channel);
@@ -5154,7 +5276,7 @@ void silc_server_save_user_channels(SilcServer server,
     silc_hash_table_free(ht);
   } else {
     silc_hash_table_list(client->channels, &htl);
-    while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
+    while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
       silc_hash_table_del(chl->channel->user_list, chl->client);
       silc_hash_table_del(chl->client->channels, chl->channel);
       silc_free(chl);
@@ -5356,6 +5478,16 @@ SILC_TASK_CALLBACK(silc_server_rekey_timeout)
     silc_ske_free(ctx->ske);
   silc_socket_free(sock);
   silc_free(ctx);
+
+  /* Disconnect since we failed to rekey, the keys are probably wrong. */
+  silc_server_disconnect_remote(server, sock,
+                               SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+  if (sock->user_data)
+    silc_server_free_sock_user_data(server, sock, NULL);
+
+  /* Reconnect */
+  if (sock->type != SILC_SOCKET_TYPE_CLIENT)
+    silc_server_create_connections(server);
 }
 
 /* A timeout callback for the re-key. We will be the initiator of the
@@ -5418,7 +5550,10 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_callback)
     silc_schedule_task_add(server->schedule, sock->sock,
                           silc_server_rekey_timeout,
                           proto_ctx,
-                          server->config->key_exchange_timeout, 0,
+                          (idata->rekey->timeout >
+                           server->config->key_exchange_timeout ?
+                           idata->rekey->timeout :
+                           server->config->key_exchange_timeout * 4), 0,
                           SILC_TASK_TIMEOUT,
                           SILC_TASK_PRI_LOW);
 
@@ -5457,6 +5592,8 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
     silc_free(ctx);
     silc_server_disconnect_remote(server, sock,
                                  SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+    if (sock->user_data)
+      silc_server_free_sock_user_data(server, sock, NULL);
 
     /* Reconnect */
     if (sock->type != SILC_SOCKET_TYPE_CLIENT)