Added dynamic server connection support with WHOIS and IDENTIFY
authorPekka Riikonen <priikone@silcnet.org>
Fri, 8 Jun 2007 15:47:21 +0000 (15:47 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Fri, 8 Jun 2007 15:47:21 +0000 (15:47 +0000)
queries.  Added support checking server name in nick@serv query.

Fixed various connecting and disconnecting bugs.

12 files changed:
apps/silcd/command.c
apps/silcd/command_reply.c
apps/silcd/idlist.c
apps/silcd/idlist.h
apps/silcd/server.c
apps/silcd/server.h
apps/silcd/server_backup.c
apps/silcd/server_query.c
apps/silcd/server_query.h
apps/silcd/server_util.c
apps/silcd/serverconfig.c
apps/silcd/serverconfig.h

index f5c3824fa0fede2c7f7405a0d4e7427a1fcad8d7..f2211f50877cbdaa3be76a01f8e367227a5a54c5 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2005, 2007 Pekka Riikonen
+  Copyright (C) 1997 - 2007 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
@@ -599,7 +599,7 @@ SILC_SERVER_CMD_FUNC(whois)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOIS, cmd, 1, 256);
-  silc_server_query_command(cmd->server, SILC_COMMAND_WHOIS, cmd);
+  silc_server_query_command(cmd->server, SILC_COMMAND_WHOIS, cmd, NULL);
   silc_server_command_free(cmd);
 }
 
@@ -609,7 +609,7 @@ SILC_SERVER_CMD_FUNC(whowas)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOWAS, cmd, 1, 2);
-  silc_server_query_command(cmd->server, SILC_COMMAND_WHOWAS, cmd);
+  silc_server_query_command(cmd->server, SILC_COMMAND_WHOWAS, cmd, NULL);
   silc_server_command_free(cmd);
 }
 
@@ -619,7 +619,7 @@ SILC_SERVER_CMD_FUNC(identify)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_IDENTIFY, cmd, 1, 256);
-  silc_server_query_command(cmd->server, SILC_COMMAND_IDENTIFY, cmd);
+  silc_server_query_command(cmd->server, SILC_COMMAND_IDENTIFY, cmd, NULL);
   silc_server_command_free(cmd);
 }
 
@@ -1325,9 +1325,8 @@ SILC_TASK_CALLBACK(silc_server_command_quit_cb)
   if (client) {
     /* Free all client specific data, such as client entry and entires
        on channels this client may be on. */
-    silc_server_free_client_data(server, q->sock, client,
-                                TRUE, q->signoff);
-    silc_packet_set_context(q->sock, NULL);
+    silc_server_free_sock_user_data(server, q->sock, q->signoff);
+    silc_server_close_connection(server, q->sock);
   }
 
   silc_packet_stream_unref(q->sock);
@@ -1438,7 +1437,7 @@ SILC_SERVER_CMD_FUNC(kill)
   comment = silc_argument_get_arg_type(cmd->args, 2, &tmp_len2);
   if (comment && tmp_len2 > 128) {
     tmp_len2 = 128;
-    comment[127] = '\0';
+    comment[tmp_len2 - 1] = '\0';
   }
 
   /* If authentication data is provided then verify that killing is
@@ -2295,6 +2294,37 @@ static void silc_server_command_join_channel(SilcServer server,
 /* Server side of command JOIN. Joins client into requested channel. If
    the channel does not exist it will be created. */
 
+/* Ways of creating channel with dynamic connections:
+
+   1. If channels are not local (no local_channels in silcd.conf) then
+      /join silc, will create connection to default router if it is
+      specified in the silcd.conf.  If it isn't, it creates local channel.
+
+   2. If channels are not local then /join silc@silcnet.org, will create
+      connection to default if it is specified in the silcd.conf and if it
+      isn't or join fails it creates connection to silcnet.org and sends
+      the JOIN command to that server/router.
+
+   3. If channels are local (local_channels set in silcd.conf) then
+      /join silc, will create local channel.  No connections are created
+      to anywhere.
+
+   4. If channels are local then /join silc@silcnet.org will create
+      connection to default router if it is specified in the silcd.conf and
+      if it isn't, or join fails it creates connection to silcnet.org and
+      send the JOIN command to that server/router.
+
+   5. If we create connection to a remote that already has a channel that
+      we also have as a local channel, should we merge those channels?
+      Should I announce my local channels when I connect to router?  Should
+      I keep local channels local, unless I say /join localch@silcnet.org
+      in which case the local channel 'localch' becomes global?
+
+   6. After we have connection established to router, depending on the
+      local_channels setting /join silc will join locally or globally.
+      /join silc@silcnet.org would always join globally.
+*/
+
 SILC_SERVER_CMD_FUNC(join)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
@@ -2303,6 +2333,7 @@ SILC_SERVER_CMD_FUNC(join)
   unsigned char *auth, *cauth;
   SilcUInt32 tmp_len, auth_len, cauth_len;
   char *tmp, *channel_name, *channel_namec = NULL, *cipher, *hmac;
+  char parsed[256 + 1], serv[256 + 1];
   SilcChannelEntry channel;
   SilcUInt32 umode = 0;
   SilcBool created = FALSE, create_key = TRUE;
@@ -2321,15 +2352,19 @@ SILC_SERVER_CMD_FUNC(join)
 
   /* Truncate over long channel names */
   if (tmp_len > 256) {
-    tmp[tmp_len - 1] = '\0';
     tmp_len = 256;
+    tmp[tmp_len - 1] = '\0';
   }
-  channel_name = tmp;
+
+  /* Parse server name from the channel name */
+  silc_parse_userfqdn(channel_name, parsed, sizeof(parsed), serv,
+                     sizeof(serv));
+  channel_name = parsed;
 
   /* Check for valid channel name.  This is cached, the original is saved
      in the channel context. */
-  channel_namec = silc_channel_name_check(tmp, tmp_len, SILC_STRING_UTF8, 256,
-                                         NULL);
+  channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
+                                         SILC_STRING_UTF8, 256, NULL);
   if (!channel_namec) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
                                          SILC_STATUS_ERR_BAD_CHANNEL, 0);
@@ -4152,12 +4187,12 @@ SILC_SERVER_CMD_FUNC(watch)
   }
 
   if (add_nick && add_nick_len > 128) {
-    add_nick[128] = '\0';
     add_nick_len = 128;
+    add_nick[add_nick_len - 1] = '\0';
   }
   if (del_nick && del_nick_len > 128) {
-    del_nick[128] = '\0';
     del_nick_len = 128;
+    del_nick[del_nick_len - 1] = '\0';
   }
 
   /* Add new nickname to be watched in our cell */
@@ -5138,7 +5173,7 @@ SILC_SERVER_CMD_FUNC(connect)
     SILC_GET32_MSB(port, tmp);
 
   /* Create the connection. It is done with timeout and is async. */
-  silc_server_create_connection(server, FALSE, host, port, NULL, NULL);
+  silc_server_create_connection(server, FALSE, FALSE, host, port, NULL, NULL);
 
   /* Send reply to the sender */
   silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CONNECT,
index e6858ae43e0fca5192173ab51f14ecf23803a750..acf1e2dd4438f94729db70a1bfa75c4d3bfcd88e 100644 (file)
@@ -261,9 +261,10 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
       return FALSE;
     }
 
-    /* Remove the old cache entry  */
-    silc_idcache_del_by_context(global ? server->global_list->clients :
-                               server->local_list->clients, client, NULL);
+    /* Update entry */
+    silc_idcache_update_by_context(global ? server->global_list->clients :
+                                  server->local_list->clients, client, NULL,
+                                  nickname, TRUE);
 
     silc_free(client->nickname);
     silc_free(client->username);
@@ -277,11 +278,6 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
     client->mode = mode;
     client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
     client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
-
-    /* Create new cache entry */
-    silc_idcache_add(global ? server->global_list->clients :
-                    server->local_list->clients, nickname, client->id,
-                    client);
   }
 
   /* Save channel list if it was sent to us */
index 8088150e8c1ee1e880ee5ff9d9f9fa4e434d2400..a29ba9fb1f9a1817e182917437ef706ab71f511b 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2005, 2007 Pekka Riikonen
+  Copyright (C) 1997 - 2007 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
@@ -62,23 +62,6 @@ void silc_idlist_del_data(void *entry)
   idata->public_key = NULL;
 }
 
-/* Purges ID cache */
-
-SILC_TASK_CALLBACK(silc_idlist_purge)
-{
-  SilcServer server = app_context;
-  SilcIDListPurge i = (SilcIDListPurge)context;
-
-  SILC_LOG_DEBUG(("Purging cache"));
-
-#if 0
-  /* XXX */
-  silc_idcache_purge(i->cache);
-  silc_schedule_task_add_timeout(server->schedule, silc_idlist_purge, i,
-                                i->timeout, 0);
-#endif
-}
-
 /******************************************************************************
 
                           Server entry functions
@@ -380,7 +363,7 @@ silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username,
 
 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
 {
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Delete client %p", entry));
 
   if (entry) {
     /* Delete client, destructor will free data */
@@ -459,7 +442,8 @@ int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
    is returned to `clients_count'. Caller must free the returned table.
    The 'nickname' must be normalized already. */
 
-int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
+int silc_idlist_get_clients_by_hash(SilcIDList id_list,
+                                   char *nickname, char *server,
                                    SilcHash md5hash,
                                    SilcClientEntry **clients,
                                    SilcUInt32 *clients_count)
@@ -468,6 +452,7 @@ int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
   SilcIDCacheEntry id_cache = NULL;
   unsigned char hash[SILC_HASH_MAXLEN];
   SilcClientID client_id;
+  SilcClientEntry client_entry;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -482,6 +467,21 @@ int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
   if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list))
     return FALSE;
 
+  /* If server is specified, narrow the search with it. */
+  if (server) {
+    silc_list_start(list);
+    while ((id_cache = silc_list_get(list))) {
+      client_entry = id_cache->context;
+      if (!client_entry->servername)
+       continue;
+      if (!silc_utf8_strcasecmp(client_entry->servername, server))
+       silc_list_del(list, id_cache);
+    }
+  }
+
+  if (!silc_list_count(list))
+    return FALSE;
+
   *clients = silc_realloc(*clients,
                          (silc_list_count(list) + *clients_count) *
                          sizeof(**clients));
index fe9f6ced5adf9716128240b10c3aa47b8033ce44..6af4a9eb99f561cc841cfdd23330dd1d05e71e66 100644 (file)
@@ -543,6 +543,7 @@ struct SilcIDListStruct {
 typedef struct {
   /* Generic data structure. DO NOT add anything before this! */
   SilcIDListDataStruct data;
+  SilcAsyncOperation op;
   SilcServerConfigRef cconfig;
   SilcServerConfigRef sconfig;
   SilcServerConfigRef rconfig;
@@ -588,7 +589,8 @@ int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
                                        char *server,
                                        SilcClientEntry **clients,
                                        SilcUInt32 *clients_count);
-int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
+int silc_idlist_get_clients_by_hash(SilcIDList id_list,
+                                   char *nickname, char *server,
                                    SilcHash md5hash,
                                    SilcClientEntry **clients,
                                    SilcUInt32 *clients_count);
index 16d2298881a375e8e088cd4ed047529175e3df1e..d4770c1f11f489def1cef96471d1e071fd8544c8 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2005, 2007 Pekka Riikonen
+  Copyright (C) 1997 - 2007 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
@@ -219,6 +219,7 @@ static void silc_server_packet_error(SilcPacketEngine engine,
                                     void *callback_context,
                                     void *stream_context)
 {
+  SilcServer server = callback_context;
   SilcIDListData idata = silc_packet_get_context(stream);
   SilcStream sock = silc_packet_stream_get_stream(stream);
   const char *ip;
@@ -230,9 +231,26 @@ static void silc_server_packet_error(SilcPacketEngine engine,
   if (!silc_socket_stream_get_info(sock, NULL, NULL, &ip, &port))
     return;
 
-  SILC_LOG_ERROR(("Connection %s:%d [%s]: %s",
-                 SILC_CONNTYPE_STRING(idata->conn_type), ip, port,
+  SILC_LOG_ERROR(("Connection %s:%d [%s]: %s", ip, port,
+                 SILC_CONNTYPE_STRING(idata->conn_type),
                  silc_packet_error_string(error)));
+
+  if (server->router_conn && server->router_conn->sock == stream &&
+      !server->router && server->standalone) {
+    silc_server_create_connections(server);
+  } else {
+    /* If backup disconnected then mark that resuming will not be allowed */
+     if (server->server_type == SILC_ROUTER && !server->backup_router &&
+         idata->conn_type == SILC_CONN_SERVER) {
+      SilcServerEntry server_entry = (SilcServerEntry)idata;
+      if (server_entry->server_type == SILC_BACKUP_ROUTER)
+        server->backup_closed = TRUE;
+    }
+
+    silc_server_free_sock_user_data(server, stream, NULL);
+  }
+
+  silc_server_close_connection(server, stream);
 }
 
 /* Packet stream callbacks */
@@ -337,11 +355,13 @@ static void silc_server_packet_parse_type(SilcServer server,
 
       status = (SilcStatus)packet->buffer.data[0];
       if (silc_buffer_len(&packet->buffer) > 1 &&
-         silc_utf8_valid(packet->buffer.data + 1, silc_buffer_len(&packet->buffer) - 1))
+         silc_utf8_valid(packet->buffer.data + 1,
+                         silc_buffer_len(&packet->buffer) - 1))
        message = silc_memdup(packet->buffer.data + 1,
                              silc_buffer_len(&packet->buffer) - 1);
 
-      if (!silc_socket_stream_get_info(sock, NULL, &hostname, &ip, NULL))
+      if (!silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
+                                      NULL, &hostname, &ip, NULL))
        break;
 
       SILC_LOG_INFO(("Disconnected by %s (%s): %s (%d) %s", ip, hostname,
@@ -560,6 +580,9 @@ void silc_server_free(SilcServer server)
 {
   SilcList list;
   SilcIDCacheEntry cache;
+  SilcIDListData idata;
+
+  SILC_LOG_DEBUG(("Free server %p", server));
 
   if (!server)
     return;
@@ -574,8 +597,12 @@ void silc_server_free(SilcServer server)
     silc_pkcs_private_key_free(server->private_key);
   if (server->pending_commands)
     silc_dlist_uninit(server->pending_commands);
-  if (server->id_entry)
+  if (server->id_entry) {
+    if (server->id_entry->data.sconn)
+      silc_schedule_task_del_by_context(server->schedule,
+                                       server->id_entry->data.sconn->sock);
     silc_idlist_del_server(server->local_list, server->id_entry);
+  }
 
   /* Delete all channels */
   if (silc_idcache_get_all(server->local_list->channels, &list)) {
@@ -592,24 +619,38 @@ void silc_server_free(SilcServer server)
   /* Delete all clients */
   if (silc_idcache_get_all(server->local_list->clients, &list)) {
     silc_list_start(list);
-    while ((cache = silc_list_get(list)))
+    while ((cache = silc_list_get(list))) {
+      silc_schedule_task_del_by_context(server->schedule, cache->context);
       silc_idlist_del_client(server->local_list, cache->context);
+    }
   }
   if (silc_idcache_get_all(server->global_list->clients, &list)) {
     silc_list_start(list);
-    while ((cache = silc_list_get(list)))
+    while ((cache = silc_list_get(list))) {
+      silc_schedule_task_del_by_context(server->schedule, cache->context);
       silc_idlist_del_client(server->global_list, cache->context);
+    }
   }
 
   /* Delete all servers */
   if (silc_idcache_get_all(server->local_list->servers, &list)) {
     silc_list_start(list);
-    while ((cache = silc_list_get(list)))
+    while ((cache = silc_list_get(list))) {
+      idata = (SilcIDListData)cache->context;
+      if (idata->sconn)
+       silc_schedule_task_del_by_context(server->schedule,
+                                         idata->sconn->sock);
       silc_idlist_del_server(server->local_list, cache->context);
+    }
   }
   if (silc_idcache_get_all(server->global_list->servers, &list)) {
-    while ((cache = silc_list_get(list)))
+    while ((cache = silc_list_get(list))) {
+      idata = (SilcIDListData)cache->context;
+      if (idata->sconn)
+       silc_schedule_task_del_by_context(server->schedule,
+                                         idata->sconn->sock);
       silc_idlist_del_server(server->global_list, cache->context);
+    }
   }
 
   silc_idcache_free(server->local_list->clients);
@@ -629,11 +670,13 @@ void silc_server_free(SilcServer server)
   silc_skr_free(server->repository);
   silc_packet_engine_stop(server->packet_engine);
 
+  silc_schedule_task_del_by_context(server->schedule, server);
+  silc_schedule_uninit(server->schedule);
+  server->schedule = NULL;
+
   silc_free(server->local_list);
   silc_free(server->global_list);
   silc_free(server->server_name);
-  silc_free(server->purge_i);
-  silc_free(server->purge_g);
   silc_free(server);
 
   silc_hmac_unregister_all();
@@ -734,7 +777,6 @@ SilcBool silc_server_init(SilcServer server)
 {
   SilcServerID *id;
   SilcServerEntry id_entry;
-  SilcIDListPurge purge;
   SilcNetListener listener;
   SilcUInt16 *port;
   char **ip;
@@ -842,7 +884,9 @@ SilcBool silc_server_init(SilcServer server)
   /* Create a Server ID for the server. */
   port = silc_net_listener_get_port(listener, NULL);
   ip = silc_net_listener_get_ip(listener, NULL);
-  silc_id_create_server_id(ip[0], port[0], server->rng, &id);
+  silc_id_create_server_id(server->config->server_info->primary->public_ip ?
+                          server->config->server_info->primary->public_ip :
+                          ip[0], port[0], server->rng, &id);
   if (!id)
     goto err;
 
@@ -896,6 +940,7 @@ SilcBool silc_server_init(SilcServer server)
     }
   }
 
+#if 0
   /* Register the ID Cache purge task. This periodically purges the ID cache
      and removes the expired cache entries. */
 
@@ -912,6 +957,7 @@ SilcBool silc_server_init(SilcServer server)
   purge->timeout = 300;
   silc_schedule_task_add_timeout(server->schedule, silc_idlist_purge,
                                 (void *)purge, purge->timeout, 0);
+#endif /Ã* 0 */
 
   /* If we are normal server we'll retrieve network statisticial information
      once in a while from the router. */
@@ -932,7 +978,7 @@ SilcBool silc_server_init(SilcServer server)
   /* Register client entry expiration timeout */
   silc_schedule_task_add_timeout(server->schedule,
                                 silc_server_purge_expired_clients, server,
-                                600, 0);
+                                120, 0);
 
   /* Initialize HTTP server */
   silc_server_http_init(server);
@@ -1246,8 +1292,6 @@ void silc_server_stop(SilcServer server)
   silc_server_http_uninit(server);
 
   silc_schedule_stop(server->schedule);
-  silc_schedule_uninit(server->schedule);
-  server->schedule = NULL;
 
   SILC_LOG_DEBUG(("Server stopped"));
 }
@@ -1259,6 +1303,7 @@ SILC_TASK_CALLBACK(silc_server_purge_expired_clients)
   SilcServer server = context;
   SilcClientEntry client;
   SilcIDList id_list;
+  SilcUInt64 curtime = silc_time();
 
   SILC_LOG_DEBUG(("Expire timeout"));
 
@@ -1267,6 +1312,12 @@ SILC_TASK_CALLBACK(silc_server_purge_expired_clients)
     if (client->data.status & SILC_IDLIST_STATUS_REGISTERED)
       continue;
 
+    /* For unregistered clients the created timestamp is actually
+       unregistered timestamp.  Make sure client remains in history
+       at least 500 seconds. */
+    if (curtime - client->data.created < 500)
+      continue;
+
     id_list = (client->data.status & SILC_IDLIST_STATUS_LOCAL ?
               server->local_list : server->global_list);
 
@@ -1277,7 +1328,7 @@ SILC_TASK_CALLBACK(silc_server_purge_expired_clients)
 
   silc_schedule_task_add_timeout(server->schedule,
                                 silc_server_purge_expired_clients, server,
-                                600, 0);
+                                120, 0);
 }
 
 
@@ -1285,8 +1336,9 @@ SILC_TASK_CALLBACK(silc_server_purge_expired_clients)
 
 /* Free connection context */
 
-static void silc_server_connection_free(SilcServerConnection sconn)
+void silc_server_connection_free(SilcServerConnection sconn)
 {
+  SILC_LOG_DEBUG(("Free connection %p", sconn));
   silc_dlist_del(sconn->server->conns, sconn);
   silc_server_config_unref(&sconn->conn);
   silc_free(sconn->remote_host);
@@ -1298,6 +1350,7 @@ static void silc_server_connection_free(SilcServerConnection sconn)
 
 void silc_server_create_connection(SilcServer server,
                                   SilcBool reconnect,
+                                  SilcBool dynamic,
                                   const char *remote_host, SilcUInt32 port,
                                   SilcServerConnectCallback callback,
                                   void *context)
@@ -1313,6 +1366,10 @@ void silc_server_create_connection(SilcServer server,
   sconn->no_reconnect = reconnect == FALSE;
   sconn->callback = callback;
   sconn->callback_context = context;
+  sconn->no_conf = dynamic;
+  sconn->server = server;
+
+  SILC_LOG_DEBUG(("Created connection %p", sconn));
 
   silc_schedule_task_add_timeout(server->schedule, silc_server_connect_router,
                                 sconn, 0, 0);
@@ -1330,22 +1387,29 @@ silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success,
   SilcServerConfigServer *conn;
   SilcServerConfigConnParams *param;
   SilcIDListData idata;
-  SilcServerEntry id_entry;
+  SilcServerEntry id_entry = NULL;
   unsigned char id[32];
   SilcUInt32 id_len;
   SilcID remote_id;
 
   SILC_LOG_DEBUG(("Connection authentication completed"));
 
+  sconn->op = NULL;
+
   if (success == FALSE) {
     /* Authentication failed */
     /* XXX retry connecting */
 
     silc_server_disconnect_remote(server, sconn->sock,
                                  SILC_STATUS_ERR_AUTH_FAILED, NULL);
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
     return;
   }
 
+  /* XXX For now remote is router always */
+  entry->data.conn_type = SILC_CONN_ROUTER;
+
   SILC_LOG_INFO(("Connected to %s %s",
                 SILC_CONNTYPE_STRING(entry->data.conn_type),
                 sconn->remote_host));
@@ -1363,6 +1427,8 @@ silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success,
     if (!id_entry) {
       silc_server_disconnect_remote(server, sconn->sock,
                                    SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+      if (sconn->callback)
+       (*sconn->callback)(server, NULL, sconn->callback_context);
       silc_server_connection_free(sconn);
       silc_free(entry);
       return;
@@ -1385,6 +1451,8 @@ silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success,
                             SILC_STR_END)) {
       silc_server_disconnect_remote(server, sconn->sock,
                                    SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+      if (sconn->callback)
+       (*sconn->callback)(server, NULL, sconn->callback_context);
       silc_server_connection_free(sconn);
       silc_free(entry);
       return;
@@ -1415,11 +1483,15 @@ silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success,
        as NULL since it's local to us. */
     id_entry = silc_idlist_add_server(server->global_list,
                                      strdup(sconn->remote_host),
-                                     SILC_ROUTER, &remote_id.u.server_id,
+                                     SILC_ROUTER,
+                                     silc_id_dup(&remote_id.u.server_id,
+                                                 SILC_ID_SERVER),
                                      NULL, sconn->sock);
     if (!id_entry) {
       silc_server_disconnect_remote(server, sconn->sock,
                                    SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+      if (sconn->callback)
+       (*sconn->callback)(server, NULL, sconn->callback_context);
       silc_server_connection_free(sconn);
       silc_free(entry);
       return;
@@ -1427,9 +1499,10 @@ silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success,
 
     /* Registered */
     silc_idlist_add_data(id_entry, (SilcIDListData)entry);
-    idata = (SilcIDListData)entry;
+    idata = (SilcIDListData)id_entry;
     idata->status |= (SILC_IDLIST_STATUS_REGISTERED |
                      SILC_IDLIST_STATUS_LOCAL);
+    idata->sconn = sconn;
 
     if (!sconn->backup) {
       /* Mark this router our primary router if we're still standalone */
@@ -1479,11 +1552,15 @@ silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success,
   default:
     silc_server_disconnect_remote(server, sconn->sock,
                                  SILC_STATUS_ERR_AUTH_FAILED, NULL);
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
     silc_server_connection_free(sconn);
     silc_free(entry);
     return;
   }
 
+  SILC_LOG_DEBUG(("Connection established, sock %p", sconn->sock));
+
   conn = sconn->conn.ref_ptr;
   param = &server->config->param;
   if (conn && conn->param)
@@ -1499,6 +1576,10 @@ silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success,
   silc_socket_set_heartbeat(sock, param->keepalive_secs, server,
                            silc_server_perform_heartbeat,
                            server->schedule);
+#endif /* 0 */
+
+  /* Set the entry as packet stream context */
+  silc_packet_set_context(sconn->sock, id_entry);
 
  out:
   /* Call the completion callback to indicate that we've connected to
@@ -1506,6 +1587,7 @@ silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success,
   if (sconn && sconn->callback)
     (*sconn->callback)(server, id_entry, sconn->callback_context);
 
+#if 0
   /* Free the temporary connection data context */
   if (sconn) {
     silc_server_config_unref(&sconn->conn);
@@ -1528,8 +1610,9 @@ static void silc_server_ke_completed(SilcSKE ske, SilcSKEStatus status,
                                     SilcSKERekeyMaterial rekey,
                                     void *context)
 {
-  SilcServerConnection sconn = context;
-  SilcUnknownEntry entry = silc_packet_get_context(sconn->sock);
+  SilcPacketStream sock = context;
+  SilcUnknownEntry entry = silc_packet_get_context(sock);
+  SilcServerConnection sconn = silc_ske_get_context(ske);
   SilcServer server = entry->server;
   SilcServerConfigRouter *conn = sconn->conn.ref_ptr;
   SilcAuthMethod auth_meth = SILC_AUTH_NONE;
@@ -1540,6 +1623,8 @@ static void silc_server_ke_completed(SilcSKE ske, SilcSKEStatus status,
   SilcHmac hmac_send, hmac_receive;
   SilcHash hash;
 
+  sconn->op = NULL;
+
   if (status != SILC_SKE_STATUS_OK) {
     /* SKE failed */
     SILC_LOG_ERROR(("Error (%s) during Key Exchange protocol with %s (%s)",
@@ -1549,6 +1634,8 @@ static void silc_server_ke_completed(SilcSKE ske, SilcSKEStatus status,
     silc_ske_free(ske);
     silc_server_disconnect_remote(server, sconn->sock,
                                  SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
     silc_server_connection_free(sconn);
     return;
   }
@@ -1565,6 +1652,8 @@ static void silc_server_ke_completed(SilcSKE ske, SilcSKEStatus status,
     silc_ske_free(ske);
     silc_server_disconnect_remote(server, sconn->sock,
                                  SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
     silc_server_connection_free(sconn);
     return;
   }
@@ -1582,6 +1671,8 @@ static void silc_server_ke_completed(SilcSKE ske, SilcSKEStatus status,
     silc_ske_free(ske);
     silc_server_disconnect_remote(server, sconn->sock,
                                  SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
     silc_server_connection_free(sconn);
     return;
   }
@@ -1604,10 +1695,11 @@ static void silc_server_ke_completed(SilcSKE ske, SilcSKEStatus status,
   }
 
   /* Start connection authentication */
-  silc_connauth_initiator(connauth, server->server_type == SILC_ROUTER ?
-                         SILC_CONN_ROUTER : SILC_CONN_SERVER, auth_meth,
-                         auth_data, auth_data_len,
-                         silc_server_ke_auth_compl, sconn);
+  sconn->op =
+    silc_connauth_initiator(connauth, server->server_type == SILC_ROUTER ?
+                           SILC_CONN_ROUTER : SILC_CONN_SERVER, auth_meth,
+                           auth_data, auth_data_len,
+                           silc_server_ke_auth_compl, sconn);
 }
 
 /* Function that is called when the network connection to a router has
@@ -1631,6 +1723,8 @@ void silc_server_start_key_exchange(SilcServerConnection sconn)
   if (!sconn->sock) {
     SILC_LOG_ERROR(("Cannot connect: cannot create packet stream"));
     silc_stream_destroy(sconn->stream);
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
     silc_server_connection_free(sconn);
     return;
   }
@@ -1640,6 +1734,8 @@ void silc_server_start_key_exchange(SilcServerConnection sconn)
   if (!silc_packet_set_ids(sconn->sock, SILC_ID_SERVER, server->id,
                           0, NULL)) {
     silc_packet_stream_destroy(sconn->sock);
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
     silc_server_connection_free(sconn);
     return;
   }
@@ -1664,10 +1760,12 @@ void silc_server_start_key_exchange(SilcServerConnection sconn)
   /* Start SILC Key Exchange protocol */
   SILC_LOG_DEBUG(("Starting key exchange protocol"));
   ske = silc_ske_alloc(server->rng, server->schedule, server->repository,
-                      server->public_key, server->private_key, sconn->sock);
+                      server->public_key, server->private_key, sconn);
   if (!ske) {
     silc_free(entry);
     silc_packet_stream_destroy(sconn->sock);
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
     silc_server_connection_free(sconn);
     return;
   }
@@ -1677,7 +1775,7 @@ void silc_server_start_key_exchange(SilcServerConnection sconn)
   /* Start key exchange protocol */
   params.version = silc_version_string;
   params.timeout_secs = server->config->key_exchange_timeout;
-  silc_ske_initiator(ske, sconn->sock, &params, NULL);
+  sconn->op = silc_ske_initiator(ske, sconn->sock, &params, NULL);
 }
 
 /* Timeout callback that will be called to retry connecting to remote
@@ -1712,6 +1810,9 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
   if ((sconn->retry_count > param->reconnect_count) &&
       !param->reconnect_keep_trying) {
     SILC_LOG_ERROR(("Could not connect, giving up"));
+
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
     silc_server_connection_free(sconn);
     return;
   }
@@ -1754,6 +1855,9 @@ static void silc_server_connection_established(SilcNetStatus status,
     SILC_LOG_ERROR(("Could not connect to %s:%d: %s",
                    sconn->remote_host, sconn->remote_port,
                    silc_net_get_error_string(status)));
+
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
     silc_server_connection_free(sconn);
     break;
 
@@ -1766,6 +1870,8 @@ static void silc_server_connection_established(SilcNetStatus status,
                                     silc_server_connect_to_router_retry,
                                     sconn, 1, 0);
     } else {
+      if (sconn->callback)
+       (*sconn->callback)(server, NULL, sconn->callback_context);
       silc_server_connection_free(sconn);
     }
     break;
@@ -1784,6 +1890,8 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
 
   /* Don't connect if we are shutting down. */
   if (server->server_shutdown) {
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
     silc_server_connection_free(sconn);
     return;
   }
@@ -1792,7 +1900,7 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
                 (sconn->backup ? "backup router" : "router"),
                 sconn->remote_host, sconn->remote_port));
 
-  if (!server->no_conf) {
+  if (!sconn->no_conf) {
     /* Find connection configuration */
     rconn = silc_server_config_find_router_conn(server, sconn->remote_host,
                                                sconn->remote_port);
@@ -1868,6 +1976,8 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
 
     if (!ptr->initiator)
       continue;
+    if (ptr->dynamic_connection)
+      continue;
 
     /* Check whether we are connecting or connected to this host already */
     if (silc_server_num_sockets_by_remote(server,
@@ -1916,6 +2026,8 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
       sconn->backup_replace_port = ptr->backup_replace_port;
     }
 
+    SILC_LOG_DEBUG(("Created connection %p", sconn));
+
     /* XXX */
     if (!server->router_conn && !sconn->backup)
       server->router_conn = sconn;
@@ -2019,6 +2131,7 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
   const char *hostname;
   SilcUInt16 port;
 
+  entry->op = NULL;
   silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
                              NULL, &hostname, NULL, &port);
 
@@ -2140,7 +2253,7 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
       SilcBool backup_router = FALSE;
       char *backup_replace_ip = NULL;
       SilcUInt16 backup_replace_port = 0;
-      SilcServerConfigServer *sconn = entry->sconfig.ref_ptr;
+      SilcServerConfigServer *srvconn = entry->sconfig.ref_ptr;
       SilcServerConfigRouter *rconn = entry->rconfig.ref_ptr;
 
       /* If we are backup router and this is incoming server connection
@@ -2199,14 +2312,14 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
        if (!silc_server_connection_allowed(server, sock,
                                            entry->data.conn_type,
                                            &server->config->param,
-                                           sconn ? sconn->param : NULL,
+                                           srvconn ? srvconn->param : NULL,
                                            silc_connauth_get_ske(connauth))) {
          server->stat.auth_failures++;
          goto out;
        }
-       if (sconn) {
-         if (sconn->param) {
-           param = sconn->param;
+       if (srvconn) {
+         if (srvconn->param) {
+           param = srvconn->param;
 
            if (!param->keepalive_secs)
              param->keepalive_secs = server->config->param.keepalive_secs;
@@ -2220,7 +2333,7 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
            }
          }
 
-         backup_router = sconn->backup_router;
+         backup_router = srvconn->backup_router;
        }
       }
 
@@ -2357,7 +2470,7 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
   silc_packet_set_context(sock, id_entry);
 
   /* Connection has been fully established now. Everything is ok. */
-  SILC_LOG_DEBUG(("New connection authenticated"));
+  SILC_LOG_DEBUG(("New connection %p authenticated", sconn));
 
 #if 0
   /* Perform keepalive. */
@@ -2400,6 +2513,10 @@ silc_server_accept_completed(SilcSKE ske, SilcSKEStatus status,
   SilcCipher send_key, receive_key;
   SilcHmac hmac_send, hmac_receive;
   SilcHash hash;
+  unsigned char *pk;
+  SilcUInt32 pk_len;
+
+  entry->op = NULL;
 
   if (status != SILC_SKE_STATUS_OK) {
     /* SKE failed */
@@ -2427,6 +2544,8 @@ silc_server_accept_completed(SilcSKE ske, SilcSKEStatus status,
 
   idata->rekey = rekey;
   idata->public_key = silc_pkcs_public_key_copy(prop->public_key);
+  pk = silc_pkcs_public_key_encode(idata->public_key, &pk_len);
+  silc_hash_make(server->sha1hash, pk, pk_len, idata->fingerprint);
 
   SILC_LOG_DEBUG(("Starting connection authentication"));
   server->stat.auth_attempts++;
@@ -2442,8 +2561,9 @@ silc_server_accept_completed(SilcSKE ske, SilcSKEStatus status,
   }
 
   /* Start connection authentication */
-  silc_connauth_responder(connauth, silc_server_accept_get_auth,
-                         silc_server_accept_auth_compl, sock);
+  entry->op =
+    silc_connauth_responder(connauth, silc_server_accept_get_auth,
+                           silc_server_accept_auth_compl, sock);
 }
 
 /* Accept new TCP connection */
@@ -2548,6 +2668,7 @@ static void silc_server_accept_new_connection(SilcNetStatus status,
   entry->ip = ip;
   entry->port = port;
   entry->server = server;
+  entry->data.conn_type = SILC_CONN_UNKNOWN;
   silc_packet_set_context(packet_stream, entry);
 
   silc_server_config_ref(&entry->cconfig, server->config, cconfig);
@@ -2584,7 +2705,7 @@ static void silc_server_accept_new_connection(SilcNetStatus status,
   /* Start key exchange protocol */
   params.version = silc_version_string;
   params.timeout_secs = server->config->key_exchange_timeout;
-  silc_ske_responder(ske, packet_stream, &params);
+  entry->op = silc_ske_responder(ske, packet_stream, &params);
 }
 
 
@@ -2631,6 +2752,8 @@ SILC_TASK_CALLBACK(silc_server_do_rekey)
   SilcIDListData idata = silc_packet_get_context(sock);
   SilcSKE ske;
 
+  SILC_LOG_DEBUG(("Perform rekey, sock %p", sock));
+
   /* Do not execute rekey with disabled connections */
   if (idata->status & SILC_IDLIST_STATUS_DISABLED)
     return;
@@ -2736,20 +2859,6 @@ void silc_server_close_connection(SilcServer server,
   const char *hostname;
   SilcUInt16 port;
 
-#if 0
-  /* If any protocol is active cancel its execution. It will call
-     the final callback which will finalize the disconnection. */
-  if (sock->protocol && sock->protocol->protocol &&
-      sock->protocol->protocol->type != SILC_PROTOCOL_SERVER_BACKUP) {
-    SILC_LOG_DEBUG(("Cancelling protocol, calling final callback"));
-    silc_protocol_cancel(sock->protocol, server->schedule);
-    sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
-    silc_protocol_execute_final(sock->protocol, server->schedule);
-    sock->protocol = NULL;
-    return;
-  }
-#endif
-
   memset(tmp, 0, sizeof(tmp));
   //  silc_socket_get_error(sock, tmp, sizeof(tmp));
   silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
@@ -2760,8 +2869,15 @@ void silc_server_close_connection(SilcServer server,
 
   //  silc_socket_set_qos(sock, 0, 0, 0, 0, NULL);
 
+  if (idata && idata->sconn) {
+    silc_server_connection_free(idata->sconn);
+    idata->sconn = NULL;
+  }
+
   /* Close connection with timeout */
   server->stat.conn_num--;
+  silc_schedule_task_del_by_all(server->schedule, 0,
+                               silc_server_close_connection_final, sock);
   silc_schedule_task_add_timeout(server->schedule,
                                 silc_server_close_connection_final,
                                 sock, 0, 1);
@@ -2799,16 +2915,6 @@ void silc_server_disconnect_remote(SilcServer server,
   silc_server_close_connection(server, sock);
 }
 
-SILC_TASK_CALLBACK(silc_server_free_client_data_timeout)
-{
-  SilcClientEntry client = context;
-
-  assert(!silc_hash_table_count(client->channels));
-
-  silc_idlist_del_data(client);
-  //  silc_idcache_purge_by_context(server->local_list->clients, client);
-}
-
 /* Frees client data and notifies about client's signoff. */
 
 void silc_server_free_client_data(SilcServer server,
@@ -2817,7 +2923,7 @@ void silc_server_free_client_data(SilcServer server,
                                  int notify,
                                  const char *signoff)
 {
-  SILC_LOG_DEBUG(("Freeing client data"));
+  SILC_LOG_DEBUG(("Freeing client %p data", client));
 
   if (client->id) {
     /* Check if anyone is watching this nickname */
@@ -2863,16 +2969,15 @@ void silc_server_free_client_data(SilcServer server,
      into history (for WHOWAS command) for 5 minutes, unless we're
      shutting down server. */
   if (!server->server_shutdown) {
-    silc_schedule_task_add_timeout(server->schedule,
-                                  silc_server_free_client_data_timeout,
-                                  client, 600, 0);
     client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
-    client->data.status &= ~SILC_IDLIST_STATUS_LOCAL;
     client->mode = 0;
     client->router = NULL;
     client->connection = NULL;
+    client->data.created = silc_time();
+    silc_dlist_add(server->expired_clients, client);
   } else {
     /* Delete directly since we're shutting down server */
+    SILC_LOG_DEBUG(("Delete client directly"));
     silc_idlist_del_data(client);
     silc_idlist_del_client(server->local_list, client);
   }
@@ -2888,15 +2993,34 @@ void silc_server_free_sock_user_data(SilcServer server,
 {
   SilcIDListData idata = silc_packet_get_context(sock);
 
+  SILC_LOG_DEBUG(("Start"));
+
   if (!idata)
     return;
 
+  silc_schedule_task_del_by_context(server->schedule, sock);
+
+  /* Cancel active protocols */
+  if (idata) {
+    if (idata->sconn && idata->sconn->op) {
+      SILC_LOG_DEBUG(("Abort active protocol"));
+      silc_async_abort(idata->sconn->op, NULL, NULL);
+    }
+    if (idata->conn_type == SILC_CONN_UNKNOWN &&
+        ((SilcUnknownEntry)idata)->op) {
+      SILC_LOG_DEBUG(("Abort active protocol"));
+      silc_async_abort(((SilcUnknownEntry)idata)->op, NULL, NULL);
+    }
+  }
+
   switch (idata->conn_type) {
   case SILC_CONN_CLIENT:
     {
       SilcClientEntry client_entry = (SilcClientEntry)idata;
       silc_server_free_client_data(server, sock, client_entry, TRUE,
                                   signoff_message);
+      if (idata->sconn)
+       silc_server_connection_free(idata->sconn);
       silc_packet_set_context(sock, NULL);
       break;
     }
@@ -2907,7 +3031,7 @@ void silc_server_free_sock_user_data(SilcServer server,
       SilcServerEntry user_data = (SilcServerEntry)idata;
       SilcServerEntry backup_router = NULL;
 
-      SILC_LOG_DEBUG(("Freeing server data"));
+      SILC_LOG_DEBUG(("Freeing server %p data", user_data));
 
       if (user_data->id)
        backup_router = silc_server_backup_get(server, user_data->id);
@@ -3088,6 +3212,8 @@ void silc_server_free_sock_user_data(SilcServer server,
                                      backup_router->connection);
       }
 
+      if (idata->sconn)
+       silc_server_connection_free(idata->sconn);
       silc_packet_set_context(sock, NULL);
       break;
     }
@@ -3098,6 +3224,8 @@ void silc_server_free_sock_user_data(SilcServer server,
 
       SILC_LOG_DEBUG(("Freeing unknown connection data"));
 
+      if (idata->sconn)
+       silc_server_connection_free(idata->sconn);
       silc_idlist_del_data(idata);
       silc_free(entry);
       silc_packet_set_context(sock, NULL);
@@ -4919,15 +5047,17 @@ SILC_TASK_CALLBACK(silc_server_get_stats)
     SILC_LOG_DEBUG(("Retrieving stats from router"));
     server->stat.commands_sent++;
     idp = silc_id_payload_encode(server->router->id, SILC_ID_SERVER);
-    packet = silc_command_payload_encode_va(SILC_COMMAND_STATS,
-                                           ++server->cmd_ident, 1,
-                                           1, idp->data,
-                                           silc_buffer_len(idp));
-    silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
-                           SILC_PACKET_COMMAND, 0, packet->data,
-                           silc_buffer_len(packet));
-    silc_buffer_free(packet);
-    silc_buffer_free(idp);
+    if (idp) {
+      packet = silc_command_payload_encode_va(SILC_COMMAND_STATS,
+                                             ++server->cmd_ident, 1,
+                                             1, idp->data,
+                                             silc_buffer_len(idp));
+      silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+                             SILC_PACKET_COMMAND, 0, packet->data,
+                             silc_buffer_len(packet));
+      silc_buffer_free(packet);
+      silc_buffer_free(idp);
+    }
   }
 
   silc_schedule_task_add_timeout(server->schedule, silc_server_get_stats,
index b3021e7839cb3307775f9198181fddaa17cd0829..f249a3766680c2edf4d4ef87e39437cd5c11afd0 100644 (file)
@@ -146,6 +146,7 @@ void silc_server_stop(SilcServer server);
 void silc_server_start_key_exchange(SilcServerConnection sconn);
 void silc_server_create_connection(SilcServer server,
                                   SilcBool reconnect,
+                                  SilcBool dynamic,
                                   const char *remote_host, SilcUInt32 port,
                                   SilcServerConnectCallback callback,
                                   void *context);
index c52b3e3597a6651ebc440a5fef4d8b9cd0a2427f..1ef5fb0fb3a91f077f754a1b8aa76b4cf00fa3e6 100644 (file)
@@ -791,7 +791,7 @@ SILC_TASK_CALLBACK(silc_server_backup_connected_again)
   if (primary) {
     if (!silc_server_find_socket_by_host(server, SILC_CONN_ROUTER,
                                         primary->host, primary->port))
-      silc_server_create_connection(server, FALSE,
+      silc_server_create_connection(server, FALSE, FALSE,
                                    primary->host, primary->port,
                                    silc_server_backup_connected,
                                    context);
@@ -841,7 +841,7 @@ SILC_TASK_CALLBACK(silc_server_backup_connect_primary_again)
   if (primary) {
     if (!silc_server_find_socket_by_host(server, SILC_CONN_ROUTER,
                                         primary->host, primary->port))
-      silc_server_create_connection(server, FALSE,
+      silc_server_create_connection(server, FALSE, FALSE,
                                    primary->host, primary->port,
                                    silc_server_backup_connect_primary,
                                    context);
@@ -1034,7 +1034,7 @@ SILC_TASK_CALLBACK(silc_server_protocol_backup)
                                             SILC_CONN_ROUTER)) {
        SILC_LOG_DEBUG(("Received START (session %d), reconnect to router",
                        ctx->session));
-       silc_server_create_connection(server, FALSE;
+       silc_server_create_connection(server, FALSE, FALSE,
                                      primary->host, primary->port,
                                      silc_server_backup_connect_primary,
                                      silc_socket_dup(ctx->sock));
index 166db3af25b8efd350e61b5c4350c92a30b4522e..634a15fa38232cebb8bffb23b9e1619169f6f7a9 100644 (file)
@@ -61,6 +61,7 @@ typedef struct {
   SilcDList attrs;                 /* Requested Attributes in WHOIS */
 
   /* Query session data */
+  SilcPacketStream router;         /* Router to send our query */
   SilcServerCommandContext cmd;            /* Command context for query */
   SilcServerQueryList querylist;    /* Temporary query list context */
   SilcServerQueryID queries;       /* Ongoing queries */
@@ -69,11 +70,15 @@ typedef struct {
   SilcUInt16 queries_count;        /* Number of ongoing queries */
   SilcUInt16 queries_left;         /* Number of ongoing queries left */
   SilcUInt16 errors_count;         /* number of errors */
-  unsigned int querycmd : 7;       /* Query command (SilcCommand) */
-  unsigned int resolved : 1;       /* TRUE if normal server has resolved
+  unsigned int querycmd      : 7;   /* Query command (SilcCommand) */
+  unsigned int resolved      : 1;   /* TRUE if normal server has resolved
                                       information from router */
+  unsigned int dynamic_prim  : 1;   /* Dynamic connection attempt to primary */
+  unsigned int dynamic_retry : 1;   /* Primary returned error, send to
+                                      nick@serv server. */
 } *SilcServerQuery;
 
+
 void silc_server_query_free(SilcServerQuery query);
 void silc_server_query_send_error(SilcServer server,
                                  SilcServerQuery query,
@@ -220,16 +225,22 @@ void silc_server_query_add_error_id(SilcServer server,
    to the entity who sent this query to us automatically.  Returns
    TRUE if the query is being processed or FALSE on error. */
 
-SilcBool silc_server_query_command(SilcServer server, SilcCommand querycmd,
-                                  SilcServerCommandContext cmd)
+SilcBool silc_server_query_command(SilcServer server,
+                                  SilcCommand querycmd,
+                                  SilcServerCommandContext cmd,
+                                  void *old_query)
 {
   SilcServerQuery query;
 
   SILC_LOG_DEBUG(("Query %s command", silc_get_command_name(querycmd)));
 
-  query = silc_calloc(1, sizeof(*query));
-  query->querycmd = querycmd;
-  query->cmd = silc_server_command_dup(cmd);
+  if (!old_query) {
+    query = silc_calloc(1, sizeof(*query));
+    query->querycmd = querycmd;
+    query->cmd = silc_server_command_dup(cmd);
+    query->router = SILC_PRIMARY_ROUTE(server);
+  } else
+    query = old_query;
 
   switch (querycmd) {
 
@@ -281,6 +292,43 @@ SilcBool silc_server_query_command(SilcServer server, SilcCommand querycmd,
   return TRUE;
 }
 
+/* Remote server connected callback. */
+
+void silc_server_query_connected(SilcServer server,
+                                SilcServerEntry server_entry,
+                                void *context)
+{
+  SilcServerQuery query = context;
+
+  if (!server_entry) {
+    /* Connecting failed */
+
+    if (query->dynamic_prim /* && @serv != prim.host.name */ &&
+       !silc_server_num_sockets_by_remote(server, query->nick_server,
+                                          query->nick_server, 706)) {
+      /* Connection attempt to primary router failed, now try to the one
+        specified in nick@server. */
+      silc_server_create_connection(server, FALSE, TRUE, query->nick_server,
+                                   706, silc_server_query_connected,
+                                   query);
+      query->dynamic_prim = FALSE;
+      return;
+    }
+
+    /* Process the query after failed connect.  This will send error back
+       because such nick was not found. */
+    SILC_LOG_DEBUG(("Process query, connecting failed"));
+    silc_server_query_process(server, query, TRUE);
+    return;
+  }
+
+  /* Reprocess the query */
+  SILC_LOG_DEBUG(("Reprocess query after creating connection to %s",
+                 server_entry->server_name));
+  query->router = server_entry->data.sconn->sock;
+  silc_server_query_command(server, query->querycmd, query->cmd, query);
+}
+
 /* Send the received query to our primary router since we could not
    handle the query directly.  We will reprocess the query after our
    router replies back. */
@@ -290,7 +338,8 @@ void silc_server_query_send_router(SilcServer server, SilcServerQuery query)
   SilcBuffer tmpbuf;
   SilcUInt16 old_ident;
 
-  SILC_LOG_DEBUG(("Forwarding the query to router for processing"));
+  SILC_LOG_DEBUG(("Forwarding the query to router %p for processing",
+                 query->router));
 
   /* Statistics */
   server->stat.commands_sent++;
@@ -299,8 +348,7 @@ void silc_server_query_send_router(SilcServer server, SilcServerQuery query)
   old_ident = silc_command_get_ident(query->cmd->payload);
   silc_command_set_ident(query->cmd->payload, ++server->cmd_ident);
   tmpbuf = silc_command_payload_encode_payload(query->cmd->payload);
-  silc_server_packet_send(server,
-                         SILC_PRIMARY_ROUTE(server),
+  silc_server_packet_send(server, query->router,
                          SILC_PACKET_COMMAND, 0,
                          tmpbuf->data, silc_buffer_len(tmpbuf));
   silc_command_set_ident(query->cmd->payload, old_ident);
@@ -335,6 +383,21 @@ void silc_server_query_send_router_reply(void *context, void *reply)
   if (cmdr && !silc_command_get_status(cmdr->payload, NULL, NULL)) {
     SilcBuffer buffer;
 
+    /* If this was nick@server query, retry to @serv if the primary router
+       returned error. */
+    if (query->nick_server[0] && !query->dynamic_retry &&
+       !silc_server_num_sockets_by_remote(server, query->nick_server,
+                                          query->nick_server, 706)) {
+      SILC_LOG_DEBUG(("Retry query by connecting to %s:%d",
+                     query->nick_server, 706));
+      silc_server_create_connection(server, FALSE, TRUE, query->nick_server,
+                                   706, silc_server_query_connected,
+                                   query);
+      query->dynamic_retry = TRUE;
+      query->resolved = FALSE;
+      return;
+    }
+
     SILC_LOG_DEBUG(("Sending error to original query"));
 
     /* Statistics */
@@ -427,6 +490,38 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
        silc_free(tmp);
       }
 
+      /* Check server name.  If we are open server and don't yet have
+        connection to remote router, create it now. */
+      if (query->nick_server[0] && server->config->open_server &&
+         !query->resolved) {
+       /* If primary router is specified, use that.  Otherwise connect
+          to the server in nick@server string. */
+       SilcServerConfigRouter *router;
+
+       router = silc_server_config_get_primary_router(server);
+       if (router && server->standalone) {
+         /* Create connection to primary router */
+         SILC_LOG_DEBUG(("Create dynamic connection to primary router %s:%d",
+                         router->host, router->port));
+         query->dynamic_prim = TRUE;
+         silc_server_create_connection(server, FALSE, TRUE,
+                                       router->host, router->port,
+                                       silc_server_query_connected, query);
+         return;
+       } else if (!silc_server_num_sockets_by_remote(server,
+                                                     query->nick_server,
+                                                     query->nick_server,
+                                                     706)) {
+         /* Create connection and handle the query after connection */
+         SILC_LOG_DEBUG(("Create dynamic connection to %s:%d",
+                         query->nick_server, 706));
+         silc_server_create_connection(server, FALSE, TRUE,
+                                       query->nick_server, 706,
+                                       silc_server_query_connected, query);
+         return;
+       }
+      }
+
     } else {
       /* Parse the IDs included in the query */
       query->ids = silc_calloc(argc, sizeof(*query->ids));
@@ -582,6 +677,38 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
        return;
       }
 
+      /* Check server name.  If we are open server and don't yet have
+        connection to remote router, create it now. */
+      if (query->nick_server[0] && server->config->open_server &&
+         !query->resolved) {
+       /* If primary router is specified, use that.  Otherwise connect
+          to the server in nick@server string. */
+       SilcServerConfigRouter *router;
+
+       router = silc_server_config_get_primary_router(server);
+       if (router && server->standalone) {
+         /* Create connection to primary router */
+         SILC_LOG_DEBUG(("Create dynamic connection to primary router %s:%d",
+                         router->host, router->port));
+         query->dynamic_prim = TRUE;
+         silc_server_create_connection(server, FALSE, TRUE,
+                                       router->host, router->port,
+                                       silc_server_query_connected, query);
+         return;
+       } else if (!silc_server_num_sockets_by_remote(server,
+                                                     query->nick_server,
+                                                     query->nick_server,
+                                                     706)) {
+         /* Create connection and handle the query after connection */
+         SILC_LOG_DEBUG(("Create dynamic connection to %s:%d",
+                         query->nick_server, 706));
+         silc_server_create_connection(server, FALSE, TRUE,
+                                       query->nick_server, 706,
+                                       silc_server_query_connected, query);
+         return;
+       }
+      }
+
     } else {
       /* Parse the IDs included in the query */
       query->ids = silc_calloc(argc, sizeof(*query->ids));
@@ -827,23 +954,29 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query,
   if (query->nickname[0]) {
     /* Get all clients matching nickname from local list */
     if (!silc_idlist_get_clients_by_hash(server->local_list,
-                                        query->nickname, server->md5hash,
+                                        query->nickname,
+                                        query->nick_server[0] ?
+                                        query->nick_server : NULL,
+                                        server->md5hash,
                                         &clients, &clients_count))
       silc_idlist_get_clients_by_nickname(server->local_list,
                                          query->nickname,
-                                         query->nick_server,
-                                        /* XXX nick_server may not be set */
+                                         query->nick_server[0] ?
+                                         query->nick_server : NULL,
                                          &clients, &clients_count);
 
     /* Check global list as well */
     if (check_global) {
       if (!silc_idlist_get_clients_by_hash(server->global_list,
-                                          query->nickname, server->md5hash,
+                                          query->nickname,
+                                          query->nick_server[0] ?
+                                          query->nick_server : NULL,
+                                          server->md5hash,
                                           &clients, &clients_count))
        silc_idlist_get_clients_by_nickname(server->global_list,
                                            query->nickname,
-                                           query->nick_server,
-                                        /* XXX nick_server may not be set */
+                                           query->nick_server[0] ?
+                                           query->nick_server : NULL,
                                            &clients, &clients_count);
     }
 
index 53a11ee25fc86372e3df271861b958ab9349558f..a9a0e02c90142549c388e1fbb4fc55b01264e509 100644 (file)
@@ -27,7 +27,8 @@
    to the entity who sent this query to us automatically.  Returns
    TRUE if the query is being processed or FALSE on error. */
 SilcBool silc_server_query_command(SilcServer server, SilcCommand querycmd,
-                                  SilcServerCommandContext cmd);
+                                  SilcServerCommandContext cmd,
+                                  void *old_query);
 
 /* Find client by the Client ID indicated by the `client_id', and if not
    found then query it by using WHOIS command.  The client information
index 3086aef444e2d77a4e4cec4cd46cf344e934ec2a..545ec504320478204ed4152f74d6123711f880a1 100644 (file)
@@ -162,15 +162,17 @@ SilcBool silc_server_remove_clients_by_server(SilcServer server,
 
   if (server_signoff) {
     idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
-    argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
-    argv_lens = silc_realloc(argv_lens,  sizeof(*argv_lens) * (argc + 1));
-    argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
-    argv[argc] = silc_calloc(silc_buffer_len(idp), sizeof(*argv[0]));
-    memcpy(argv[argc], idp->data, silc_buffer_len(idp));
-    argv_lens[argc] = silc_buffer_len(idp);
-    argv_types[argc] = argc + 1;
-    argc++;
-    silc_buffer_free(idp);
+    if (idp) {
+      argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
+      argv_lens = silc_realloc(argv_lens,  sizeof(*argv_lens) * (argc + 1));
+      argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
+      argv[argc] = silc_calloc(silc_buffer_len(idp), sizeof(*argv[0]));
+      memcpy(argv[argc], idp->data, silc_buffer_len(idp));
+      argv_lens[argc] = silc_buffer_len(idp);
+      argv_types[argc] = argc + 1;
+      argc++;
+      silc_buffer_free(idp);
+    }
   }
 
   if (silc_idcache_get_all(server->local_list->clients, &list)) {
@@ -189,17 +191,19 @@ SilcBool silc_server_remove_clients_by_server(SilcServer server,
 
       if (server_signoff) {
        idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-       argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
-       argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) *
-                                (argc + 1));
-       argv_types = silc_realloc(argv_types, sizeof(*argv_types) *
-                                 (argc + 1));
-       argv[argc] = silc_calloc(silc_buffer_len(idp), sizeof(*argv[0]));
-       memcpy(argv[argc], idp->data, silc_buffer_len(idp));
-       argv_lens[argc] = silc_buffer_len(idp);
-       argv_types[argc] = argc + 1;
-       argc++;
-       silc_buffer_free(idp);
+       if (idp) {
+         argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
+         argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) *
+                                  (argc + 1));
+         argv_types = silc_realloc(argv_types, sizeof(*argv_types) *
+                                   (argc + 1));
+         argv[argc] = silc_calloc(silc_buffer_len(idp), sizeof(*argv[0]));
+         memcpy(argv[argc], idp->data, silc_buffer_len(idp));
+         argv_lens[argc] = silc_buffer_len(idp);
+         argv_types[argc] = argc + 1;
+         argc++;
+         silc_buffer_free(idp);
+       }
       }
 
       /* Update statistics */
index 7d00e4de4667a8f195d498715e83d8d650752586..d2b67d2b0a9e9e741818a24901e45a6a62d37486 100644 (file)
@@ -338,6 +338,12 @@ SILC_CONFIG_CALLBACK(fetch_generic)
     }
     config->httpd_port = (SilcUInt16)port;
   }
+  else if (!strcmp(name, "open_server")) {
+    config->open_server = *(SilcBool *)val;
+  }
+  else if (!strcmp(name, "local_channels")) {
+    config->local_channels = *(SilcBool *)val;
+  }
   else
     return SILC_CONFIG_EINTERNAL;
 
@@ -582,6 +588,11 @@ SILC_CONFIG_CALLBACK(fetch_serverinfo)
     CONFIG_IS_DOUBLE(tmp->server_ip);
     tmp->server_ip = strdup((char *) val);
   }
+  else if (!strcmp(name, "public_ip")) {
+    SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigServerInfoInterface);
+    CONFIG_IS_DOUBLE(tmp->public_ip);
+    tmp->public_ip = strdup((char *) val);
+  }
   else if (!strcmp(name, "port")) {
     int port = *(int *)val;
     SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigServerInfoInterface);
@@ -1157,6 +1168,9 @@ SILC_CONFIG_CALLBACK(fetch_router)
   else if (!strcmp(name, "backuplocal")) {
     tmp->backup_local = *(SilcBool *)val;
   }
+  else if (!strcmp(name, "dynamic_connection")) {
+    tmp->dynamic_connection = *(SilcBool *)val;
+  }
   else
     return SILC_CONFIG_EINTERNAL;
 
@@ -1203,6 +1217,8 @@ static const SilcConfigTable table_general[] = {
   { "http_server",             SILC_CONFIG_ARG_TOGGLE, fetch_generic,  NULL },
   { "http_server_ip",                  SILC_CONFIG_ARG_STRE,   fetch_generic,  NULL },
   { "http_server_port",                SILC_CONFIG_ARG_INT,    fetch_generic,  NULL },
+  { "open_server",             SILC_CONFIG_ARG_TOGGLE, fetch_generic,  NULL },
+  { "local_channels",          SILC_CONFIG_ARG_TOGGLE, fetch_generic,  NULL },
   { 0, 0, 0, 0 }
 };
 
@@ -1236,6 +1252,7 @@ static const SilcConfigTable table_pkcs[] = {
 
 static const SilcConfigTable table_serverinfo_c[] = {
   { "ip",              SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
+  { "public_ip",       SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
   { "port",            SILC_CONFIG_ARG_INT,    fetch_serverinfo, NULL},
   { 0, 0, 0, 0 }
 };
@@ -1344,6 +1361,7 @@ static const SilcConfigTable table_routerconn[] = {
   { "backuphost",      SILC_CONFIG_ARG_STRE,   fetch_router,   NULL },
   { "backupport",      SILC_CONFIG_ARG_INT,    fetch_router,   NULL },
   { "backuplocal",     SILC_CONFIG_ARG_TOGGLE, fetch_router,   NULL },
+  { "dynamic_connection",      SILC_CONFIG_ARG_TOGGLE, fetch_router,   NULL },
   { 0, 0, 0, 0 }
 };
 
index 7ef873e0797a56131c750d81dcf5c496f16b2eff..b5e91f65f84378d3d1d3739ba26644b94f316e95 100644 (file)
@@ -50,6 +50,7 @@ typedef struct SilcServerConfigPkcsStruct {
 
 typedef struct SilcServerConfigServerInfoInterfaceStruct {
   char *server_ip;
+  char *public_ip;
   SilcUInt16 port;
   struct SilcServerConfigServerInfoInterfaceStruct *next;
 } SilcServerConfigServerInfoInterface;
@@ -149,6 +150,7 @@ typedef struct SilcServerConfigRouterStruct {
   SilcServerConfigConnParams *param;
   SilcBool initiator;
   SilcBool backup_router;
+  SilcBool dynamic_connection;
   char *backup_replace_ip;
   SilcUInt16 backup_replace_port;
   SilcBool backup_local;
@@ -180,6 +182,8 @@ typedef struct {
   SilcBool httpd;
   char *httpd_ip;
   SilcUInt16 httpd_port;
+  SilcBool open_server;
+  SilcBool local_channels;
 
   /* Other configuration sections */
   SilcServerConfigCipher *cipher;