Added dynamic server connection support with WHOIS and IDENTIFY
[silc.git] / apps / silcd / server_query.c
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);
     }