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 */
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. */
+ unsigned int parsed : 1; /* Set when query is parsed */
} *SilcServerQuery;
+
void silc_server_query_free(SilcServerQuery query);
void silc_server_query_send_error(SilcServer server,
SilcServerQuery query,
void *id, SilcIdType id_type);
void silc_server_query_send_router(SilcServer server, SilcServerQuery query);
void silc_server_query_send_router_reply(void *context, void *reply);
-void silc_server_query_parse(SilcServer server, SilcServerQuery query);
+SilcBool silc_server_query_parse(SilcServer server, SilcServerQuery query,
+ SilcBool parse_only);
void silc_server_query_process(SilcServer server, SilcServerQuery query,
SilcBool resolve);
void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
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) {
(!silc_argument_get_arg_type(cmd->args, 1, NULL) &&
!silc_argument_get_arg_type(cmd->args, 4, NULL) &&
silc_argument_get_arg_type(cmd->args, 3, NULL)))) {
+ if (!silc_server_query_parse(server, query, TRUE))
+ return FALSE;
silc_server_query_send_router(server, query);
return TRUE;
}
if (server->server_type == SILC_SERVER && !server->standalone &&
cmd->sock != SILC_PRIMARY_ROUTE(server) &&
!silc_argument_get_arg_type(cmd->args, 5, NULL)) {
+ if (!silc_server_query_parse(server, query, TRUE))
+ return FALSE;
silc_server_query_send_router(server, query);
return TRUE;
}
}
/* Now parse the request */
- silc_server_query_parse(server, query);
+ silc_server_query_parse(server, query, FALSE);
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 */
+ SilcConnectionType type = (server->server_type == SILC_ROUTER ?
+ SILC_CONN_SERVER : SILC_CONN_ROUTER);
+
+ if (query->dynamic_prim /* && @serv != prim.host.name */ &&
+ !silc_server_num_sockets_by_remote(server, query->nick_server,
+ query->nick_server, 706, type)) {
+ /* 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. */
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++;
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);
/* Check if router sent error reply */
if (cmdr && !silc_command_get_status(cmdr->payload, NULL, NULL)) {
SilcBuffer buffer;
+ SilcConnectionType type = (server->server_type == SILC_ROUTER ?
+ SILC_CONN_SERVER : SILC_CONN_ROUTER);
+
+ /* 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, 1334, type)) {
+ SILC_LOG_DEBUG(("Retry query by connecting to %s:%d",
+ query->nick_server, 706));
+ silc_server_create_connection(server, FALSE, TRUE, query->nick_server,
+ 1334, silc_server_query_connected,
+ query);
+ query->dynamic_retry = TRUE;
+ query->resolved = FALSE;
+ return;
+ }
SILC_LOG_DEBUG(("Sending error to original query"));
}
/* Continue with parsing */
- silc_server_query_parse(server, query);
+ silc_server_query_parse(server, query, FALSE);
}
/* Parse the command query and start processing the queries in detail. */
-void silc_server_query_parse(SilcServer server, SilcServerQuery query)
+SilcBool silc_server_query_parse(SilcServer server, SilcServerQuery query,
+ SilcBool parse_only)
{
SilcServerCommandContext cmd = query->cmd;
SilcIDListData idata = silc_packet_get_context(cmd->sock);
SILC_LOG_DEBUG(("Parsing %s query",
silc_get_command_name(query->querycmd)));
+ if (query->parsed)
+ goto parsed;
+
switch (query->querycmd) {
case SILC_COMMAND_WHOIS:
silc_server_query_send_error(server, query,
SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
silc_server_query_free(query);
- return;
+ return FALSE;
}
/* Get the nickname@server string and parse it */
silc_server_query_send_error(server, query,
SILC_STATUS_ERR_BAD_NICKNAME, 0);
silc_server_query_free(query);
- return;
+ return FALSE;
}
/* Check nickname */
silc_server_query_send_error(server, query,
SILC_STATUS_ERR_BAD_NICKNAME, 0);
silc_server_query_free(query);
- return;
+ return FALSE;
}
memset(query->nickname, 0, sizeof(query->nickname));
silc_snprintf(query->nickname, sizeof(query->nickname), "%s", tmp);
silc_free(query->ids);
query->ids = NULL;
query->ids_count = 0;
- return;
+ return FALSE;
}
}
}
silc_server_query_send_error(server, query,
SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
silc_server_query_free(query);
- return;
+ return FALSE;
}
/* Get the nickname@server string and parse it */
silc_server_query_send_error(server, query,
SILC_STATUS_ERR_BAD_NICKNAME, 0);
silc_server_query_free(query);
- return;
+ return FALSE;
}
/* Check nickname */
silc_server_query_send_error(server, query,
SILC_STATUS_ERR_BAD_NICKNAME, 0);
silc_server_query_free(query);
- return;
+ return FALSE;
}
memset(query->nickname, 0, sizeof(query->nickname));
silc_snprintf(query->nickname, sizeof(query->nickname), "%s", tmp);
silc_server_query_send_error(server, query,
SILC_STATUS_ERR_BAD_NICKNAME, 0);
silc_server_query_free(query);
- return;
+ return FALSE;
}
memset(query->nickname, 0, sizeof(query->nickname));
silc_snprintf(query->nickname, sizeof(query->nickname), "%s", tmp);
silc_server_query_send_error(server, query,
SILC_STATUS_ERR_BAD_SERVER, 0);
silc_server_query_free(query);
- return;
+ return FALSE;
}
query->server_name = tmp;
}
silc_server_query_send_error(server, query,
SILC_STATUS_ERR_BAD_CHANNEL, 0);
silc_server_query_free(query);
- return;
+ return FALSE;
}
query->channel_name = tmp;
}
silc_server_query_send_error(server, query,
SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
silc_server_query_free(query);
- return;
+ return FALSE;
}
} else {
silc_free(query->ids);
query->ids = NULL;
query->ids_count = 0;
- return;
+ return FALSE;
}
}
} else {
silc_free(query->ids);
query->ids = NULL;
query->ids_count = 0;
- return;
+ return FALSE;
}
}
break;
}
+ query->parsed = TRUE;
+
+ parsed:
+ if (!parse_only && query->nickname) {
+ switch (query->querycmd) {
+ case SILC_COMMAND_WHOIS:
+ case SILC_COMMAND_IDENTIFY:
+ /* 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->dynamic_server &&
+ !query->resolved) {
+ /* If primary router is specified, use that. Otherwise connect
+ to the server in nick@server string. */
+ SilcServerConfigRouter *router;
+ SilcConnectionType type = (server->server_type == SILC_ROUTER ?
+ SILC_CONN_SERVER : SILC_CONN_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 FALSE;
+ } else if (!silc_server_num_sockets_by_remote(server,
+ query->nick_server,
+ query->nick_server,
+ 706, type)) {
+ /* 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 FALSE;
+ }
+ }
+ }
+ }
+
/* Start processing the query information */
- silc_server_query_process(server, query, TRUE);
+ if (!parse_only)
+ silc_server_query_process(server, query, TRUE);
+
+ return TRUE;
}
/* Context for holding clients searched by public key. */
SilcBool found;
} *SilcServerPublicKeyUser, SilcServerPublicKeyUserStruct;
-void silc_server_public_key_hash_foreach(void *key, void *context,
- void *user_context)
+/* SKR find callbcak */
+
+static void silc_server_query_skr_callback(SilcSKR skr,
+ SilcSKRFind find,
+ SilcSKRStatus status,
+ SilcDList keys,
+ void *context)
{
- SilcServerPublicKeyUser uc = user_context;
- SilcClientEntry entry = context;
+ SilcServerPublicKeyUser uc = context;
+ SilcSKRKey key;
- /* Nothing was found, just return */
- if (!context)
- return;
+ if (keys) {
+ (*uc->clients) = silc_realloc((*uc->clients),
+ sizeof((**uc->clients)) *
+ ((*uc->clients_count) +
+ silc_dlist_count(keys)));
- uc->found = TRUE;
+ silc_dlist_start(keys);
+ while ((key = silc_dlist_get(keys)))
+ (*uc->clients)[(*uc->clients_count)++] = key->key_context;
- (*uc->clients) = silc_realloc((*uc->clients),
- sizeof((**uc->clients)) *
- ((*uc->clients_count) + 1));
- (*uc->clients)[(*uc->clients_count)++] = entry;
+ uc->found = TRUE;
+ silc_dlist_uninit(keys);
+ }
+
+ silc_skr_find_free(find);
}
/* If clients are set, limit the found clients using the attributes in
SilcAttributePayload attr;
SilcAttribute attribute;
SilcAttributeObjPk pk;
- SilcPublicKey publickey;
+ SilcPublicKey publickey, cmp_pubkey;
SilcPKCSType type;
- SilcBool found = FALSE, no_clients = FALSE;
+ SilcBool found = FALSE, no_clients = FALSE, search_pubkey = FALSE;
int i;
/* If no clients were found, we only check the attributes
silc_free(pk.data);
continue;
}
+ search_pubkey = TRUE;
- /* If no clients were set on calling this function, we
- just search for clients, otherwise we try to limit
- the clients */
+ /* If no clients were set on calling this function, we just search
+ for clients, otherwise we try to limit the clients. */
if (no_clients) {
SilcServerPublicKeyUserStruct usercontext;
+ SilcSKRFind find;
usercontext.clients = clients;
usercontext.clients_count = clients_count;
usercontext.found = FALSE;
- silc_hash_table_find_foreach(server->pk_hash, publickey,
- silc_server_public_key_hash_foreach,
- &usercontext);
+ find = silc_skr_find_alloc();
+ if (!find)
+ continue;
+
+ silc_skr_find_set_public_key(find, publickey);
+ silc_skr_find_set_usage(find, SILC_SKR_USAGE_IDENTIFICATION);
+ silc_skr_find(server->repository, server->schedule,
+ find, silc_server_query_skr_callback, &usercontext);
if (usercontext.found == TRUE)
found = TRUE;
if (!entry->data.public_key)
continue;
- if (!silc_hash_table_find_by_context(server->pk_hash, publickey,
- entry, NULL))
- (*clients)[i] = NULL;
- else
- found = TRUE;
+ if (silc_server_get_public_key_by_client(server, entry,
+ &cmp_pubkey)) {
+ if (silc_pkcs_public_key_compare(cmp_pubkey, publickey)) {
+ found = TRUE;
+ continue;
+ }
+ }
+
+ (*clients)[i] = NULL;
}
}
silc_free(pk.type);
if (!found && !query->nickname[0] && !query->ids)
silc_server_query_add_error(server, query, 2, 0,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ search_pubkey ?
+ SILC_STATUS_ERR_NO_SUCH_PUBLIC_KEY :
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
}
/* Processes the parsed query. This does the actual finding of the
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);
}