/*
- server_query.c
+ server_query.c
Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 2002 Pekka Riikonen
+ Copyright (C) 2002 - 2003 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
*/
/* $Id$ */
-/* XXX TODO Requested Attributes to WHOIS */
-
#include "serverincludes.h"
#include "server_internal.h"
SilcUInt32 servers_count,
SilcChannelEntry *channels,
SilcUInt32 channels_count);
-unsigned char *silc_server_query_reply_attrs(SilcServer server,
- SilcServerQuery query,
- SilcUInt32 *attrs_len);
+SilcBuffer silc_server_query_reply_attrs(SilcServer server,
+ SilcServerQuery query,
+ SilcClientEntry client_entry);
/* Free the query context structure and all allocated resources. */
/* Send the command reply with error */
silc_server_send_command_reply(server, query->cmd->sock,
- query->querycmd, error, 0,
+ query->querycmd, error, 0,
silc_command_get_ident(query->cmd->payload),
argc, data_type, data, data_len);
va_end(va);
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_server_packet_send(server,
SILC_PRIMARY_ROUTE(server),
SILC_PACKET_COMMAND, 0,
tmpbuf->data, tmpbuf->len, TRUE);
silc_command_get_ident(query->cmd->payload));
buffer = silc_command_payload_encode_payload(cmdr->payload);
silc_server_packet_send(server, query->cmd->sock,
- SILC_PACKET_COMMAND_REPLY, 0,
+ SILC_PACKET_COMMAND_REPLY, 0,
buffer->data, buffer->len, FALSE);
silc_buffer_free(buffer);
silc_server_query_free(query);
if (!tmp)
continue;
- id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
- if (!id) {
+ id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
+ if (!id || id_type != SILC_ID_CLIENT) {
silc_server_query_add_error(server, query, TRUE, i + 4,
SILC_STATUS_ERR_BAD_CLIENT_ID);
continue;
for (i = 0; i < query->ids_count; i++)
silc_free(query->ids[i].id);
silc_free(query->ids);
+ query->ids = NULL;
+ query->ids_count = 0;
silc_free(id);
return;
}
/* Get requested attributes if set */
tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
- if (tmp)
- query->attrs = silc_attribute_payload_parse_list(tmp, tmp_len);
+ if (tmp && tmp_len <= SILC_ATTRIBUTE_MAX_REQUEST_LEN) {
+ query->attrs = silc_attribute_payload_parse(tmp, tmp_len);
+
+ /* When Requested Attributes is present we will assure that this
+ client cannot execute the WHOIS command too fast. This would be
+ same as having SILC_CF_LAG_STRICT. */
+ if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
+ cmd->sock->user_data)
+ ((SilcClientEntry)cmd->sock->user_data)->fast_command = 6;
+ }
break;
case SILC_COMMAND_WHOWAS:
/* Normal server must check whether this ID exist, and if not then
send the query to router, unless done so already */
if (server->server_type == SILC_SERVER && !query->resolved) {
- if (!silc_idlist_find_client_by_id(server->local_list,
- id, TRUE, NULL)) {
- if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT ||
- !silc_idlist_find_client_by_id(server->global_list,
+ if (id_type == SILC_ID_CLIENT) {
+ if (!silc_idlist_find_client_by_id(server->local_list,
id, TRUE, NULL)) {
- silc_server_query_send_router(server, query);
- for (i = 0; i < query->ids_count; i++)
- silc_free(query->ids[i].id);
- silc_free(query->ids);
- silc_free(id);
- return;
+ if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT ||
+ !silc_idlist_find_client_by_id(server->global_list,
+ id, TRUE, NULL)) {
+ silc_server_query_send_router(server, query);
+ for (i = 0; i < query->ids_count; i++)
+ silc_free(query->ids[i].id);
+ silc_free(query->ids);
+ query->ids = NULL;
+ query->ids_count = 0;
+ silc_free(id);
+ return;
+ }
}
+ } else {
+ /* For now all other ID's except Client ID's are explicitly
+ sent to router for resolving. */
+ silc_server_query_send_router(server, query);
+ for (i = 0; i < query->ids_count; i++)
+ silc_free(query->ids[i].id);
+ silc_free(query->ids);
+ query->ids = NULL;
+ query->ids_count = 0;
+ silc_free(id);
+ return;
}
}
if (query->nickname) {
/* Get all clients matching nickname from local list */
- if (!silc_idlist_get_clients_by_hash(server->local_list,
+ if (!silc_idlist_get_clients_by_hash(server->local_list,
query->nickname, server->md5hash,
&clients, &clients_count))
- silc_idlist_get_clients_by_nickname(server->local_list,
+ silc_idlist_get_clients_by_nickname(server->local_list,
query->nickname,
query->nick_server,
&clients, &clients_count);
/* Check global list as well */
if (check_global) {
- if (!silc_idlist_get_clients_by_hash(server->global_list,
+ if (!silc_idlist_get_clients_by_hash(server->global_list,
query->nickname, server->md5hash,
&clients, &clients_count))
- silc_idlist_get_clients_by_nickname(server->global_list,
+ silc_idlist_get_clients_by_nickname(server->global_list,
query->nickname,
query->nick_server,
&clients, &clients_count);
}
}
+ SILC_LOG_DEBUG(("Querying %d clients", clients_count));
+ SILC_LOG_DEBUG(("Querying %d servers", servers_count));
+ SILC_LOG_DEBUG(("Querying %d channels", channels_count));
+
/* If nothing was found, then just send the errors */
if (!clients && !channels && !servers) {
silc_server_query_send_reply(server, query, NULL, 0, NULL, 0, NULL, 0);
}
}
+ /* Remove the NOATTR status periodically */
+ if (client_entry->data.status & SILC_IDLIST_STATUS_NOATTR &&
+ client_entry->updated + 600 < time(NULL))
+ client_entry->data.status &= ~SILC_IDLIST_STATUS_NOATTR;
+
/* When requested attributes is present and local client is detached
we cannot send the command to the client, we'll reply on behalf of
the client instead. */
for (i = 0; i < query->querylist_count; i++) {
r = &query->querylist[i];
+ /* If Requested Attributes were present put them to this resolving */
+ if (query->attrs && query->querycmd == SILC_COMMAND_WHOIS) {
+ len = r->argc + 1;
+ r->arg = silc_realloc(r->arg, sizeof(*r->arg) * len);
+ r->arg_lens = silc_realloc(r->arg_lens, sizeof(*r->arg_lens) * len);
+ r->arg_types = silc_realloc(r->arg_types, sizeof(*r->arg_types) * len);
+
+ tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
+ if (tmp)
+ r->arg[r->argc] = silc_memdup(tmp, len);
+ r->arg_lens[r->argc] = len;
+ r->arg_types[r->argc] = 3;
+ r->argc++;
+ }
+
/* Send WHOIS command */
res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
r->argc, r->arg, r->arg_lens,
case SILC_COMMAND_WHOIS:
case SILC_COMMAND_IDENTIFY:
/* Take existing query context if exist for this connection */
- for (i = 0; i < query->queries_count; i++)
+ for (i = 0; i < query->querylist_count; i++)
if (query->querylist[i].sock == sock) {
r = &query->querylist[i];
break;
if (!r) {
/* Allocate new temp query list context */
query->querylist = silc_realloc(query->querylist,
- sizeof(*query->querylist) *
+ sizeof(*query->querylist) *
(query->querylist_count + 1));
r = &query->querylist[query->querylist_count];
query->querylist_count++;
r->timeout = 3;
}
- /* If Requested Attributes were present put them to this resolving */
- if (query->attrs && query->querycmd == SILC_COMMAND_WHOIS) {
- len = r->argc + 1;
- r->arg = silc_realloc(r->arg, sizeof(*r->arg) * len);
- r->arg_lens = silc_realloc(r->arg_lens, sizeof(*r->arg_lens) * len);
- r->arg_types = silc_realloc(r->arg_types, sizeof(*r->arg_types) * len);
-
- tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
- if (tmp)
- r->arg[r->argc] = silc_memdup(tmp, len);
- r->arg_lens[r->argc] = len;
- r->arg_types[r->argc] = 3;
- r->argc++;
- }
-
len = r->argc + 1;
r->arg = silc_realloc(r->arg, sizeof(*r->arg) * len);
r->arg_lens = silc_realloc(r->arg_lens, sizeof(*r->arg_lens) * len);
client_entry->data.status |= SILC_IDLIST_STATUS_RESOLVING;
client_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
client_entry->resolve_cmd_ident = ident;
+ client_entry->updated = time(NULL);
/* Save the queried ID, which we will reprocess after we get this and
all other queries back. */
bool sent_reply = FALSE;
SILC_LOG_DEBUG(("Sending reply to query"));
+ SILC_LOG_DEBUG(("Sending %d clients", clients_count));
+ SILC_LOG_DEBUG(("Sending %d servers", servers_count));
+ SILC_LOG_DEBUG(("Sending %d channels", channels_count));
+ SILC_LOG_DEBUG(("Sending %d errors", query->errors_count));
status = SILC_STATUS_OK;
if (k >= 1)
status = SILC_STATUS_LIST_ITEM;
- if (valid_count > 1 && k == valid_count - 1
+ if (valid_count > 1 && k == valid_count - 1
&& !servers_count && !channels_count && !query->errors_count)
status = SILC_STATUS_LIST_END;
if (query->reply_count && k - 1 == query->reply_count)
server->server_name, len);
}
}
-
+
switch (query->querycmd) {
-
+
case SILC_COMMAND_WHOIS:
{
unsigned char idle[4], mode[4];
unsigned char *fingerprint, fempty[20], *attrs = NULL;
- SilcBuffer channels, umode_list = NULL;
+ SilcBuffer channels, umode_list = NULL, tmpattrs = NULL;
memset(fempty, 0, sizeof(fempty));
+ memset(idle, 0, sizeof(idle));
silc_strncat(uh, sizeof(uh), entry->username,
strlen(entry->username));
if (!strchr(entry->username, '@') && entry->connection) {
if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
channels =
- silc_server_get_client_channel_list(server, entry, FALSE,
+ silc_server_get_client_channel_list(server, entry, FALSE,
FALSE, &umode_list);
else
channels =
- silc_server_get_client_channel_list(server, entry, TRUE,
+ silc_server_get_client_channel_list(server, entry, TRUE,
TRUE, &umode_list);
if (memcmp(entry->data.fingerprint, fempty, sizeof(fempty)))
attributes we will reply to them on behalf of the client. */
len = 0;
if (query->attrs) {
- if (!entry->attrs) {
- attrs = silc_server_query_reply_attrs(server, query, &len);
- } else {
- attrs = entry->attrs;
- len = entry->attrs_len;
+ if (!entry->attrs && SILC_IS_LOCAL(entry)) {
+ tmpattrs = silc_server_query_reply_attrs(server, query, entry);
+ entry->attrs = silc_memdup(tmpattrs->data, tmpattrs->len);
+ entry->attrs_len = tmpattrs->len;
+ silc_buffer_free(tmpattrs);
}
+ attrs = entry->attrs;
+ len = entry->attrs_len;
}
/* Send command reply */
2, idp->data, idp->len,
3, nh, strlen(nh),
4, uh, strlen(uh),
- 5, entry->userinfo,
+ 5, entry->userinfo,
strlen(entry->userinfo),
6, channels ? channels->data : NULL,
channels ? channels->len : 0,
sent_reply = TRUE;
- /* For now we will delete Requested Attributes */
- silc_free(entry->attrs);
- entry->attrs = NULL;
+ /* For now we always delete Requested Attributes, unless the client
+ is detached, in which case we don't want to reconstruct the
+ same data everytime */
+ if (!(entry->mode & SILC_UMODE_DETACHED) &&
+ !(entry->data.status & SILC_IDLIST_STATUS_NOATTR)) {
+ silc_free(entry->attrs);
+ entry->attrs = NULL;
+ }
if (channels)
silc_buffer_free(channels);
2, idp->data, idp->len,
3, nh, strlen(nh),
4, uh, strlen(uh),
- 5, entry->userinfo,
- entry->userinfo ?
+ 5, entry->userinfo,
+ entry->userinfo ?
strlen(entry->userinfo) : 0);
sent_reply = TRUE;
break;
k = 0;
for (i = 0; i < servers_count; i++) {
entry = servers[i];
-
+
if (k >= 1)
status = SILC_STATUS_LIST_ITEM;
+ if (servers_count == 1 && status != SILC_STATUS_OK && !channels_count &&
+ !query->errors_count)
+ status = SILC_STATUS_LIST_END;
if (servers_count > 1 && k == servers_count - 1 && !channels_count &&
!query->errors_count)
status = SILC_STATUS_LIST_END;
if (query->reply_count && k - 1 == query->reply_count)
status = SILC_STATUS_LIST_END;
-
+
SILC_LOG_DEBUG(("%s: server %s",
(status == SILC_STATUS_OK ? " OK" :
status == SILC_STATUS_LIST_START ? "START" :
idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_IDENTIFY,
status, 0, ident, 2,
- 2, idp->data, idp->len,
- 3, entry->server_name,
- entry->server_name ?
+ 2, idp->data, idp->len,
+ 3, entry->server_name,
+ entry->server_name ?
strlen(entry->server_name) : 0);
silc_buffer_free(idp);
sent_reply = TRUE;
-
+
if (status == SILC_STATUS_LIST_END)
break;
k++;
k = 0;
for (i = 0; i < channels_count; i++) {
entry = channels[i];
-
+
if (k >= 1)
status = SILC_STATUS_LIST_ITEM;
+ if (channels_count == 1 && status != SILC_STATUS_OK &&
+ !query->errors_count)
+ status = SILC_STATUS_LIST_END;
if (channels_count > 1 && k == channels_count - 1 &&
!query->errors_count)
status = SILC_STATUS_LIST_END;
if (query->reply_count && k - 1 == query->reply_count)
status = SILC_STATUS_LIST_END;
-
+
SILC_LOG_DEBUG(("%s: channel %s",
(status == SILC_STATUS_OK ? " OK" :
status == SILC_STATUS_LIST_START ? "START" :
idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_IDENTIFY,
status, 0, ident, 2,
- 2, idp->data, idp->len,
- 3, entry->channel_name,
- entry->channel_name ?
+ 2, idp->data, idp->len,
+ 3, entry->channel_name,
+ entry->channel_name ?
strlen(entry->channel_name) : 0);
silc_buffer_free(idp);
sent_reply = TRUE;
-
+
if (status == SILC_STATUS_LIST_END)
break;
k++;
if (k >= 1)
status = SILC_STATUS_LIST_ITEM;
+ if (query->errors_count == 1 && status != SILC_STATUS_OK)
+ status = SILC_STATUS_LIST_END;
if (query->errors_count > 1 && k == query->errors_count - 1)
status = SILC_STATUS_LIST_END;
if (query->reply_count && k - 1 == query->reply_count)
status == SILC_STATUS_LIST_START ? "START" :
status == SILC_STATUS_LIST_ITEM ? " ITEM" :
status == SILC_STATUS_LIST_END ? " END" :
- " : "),
+ " : "),
silc_get_status_message(query->errors[i].error),
query->errors[i].error));
Either client does not support Requested Attributes or isn't replying
to them like it should. */
-unsigned char *silc_server_query_reply_attrs(SilcServer server,
- SilcServerQuery query,
- SilcUInt32 *attrs_len)
+SilcBuffer silc_server_query_reply_attrs(SilcServer server,
+ SilcServerQuery query,
+ SilcClientEntry client_entry)
{
- unsigned char *attrs = NULL;
- SilcUInt32 len = 0;
+ SilcBuffer buffer = NULL;
+ SilcAttribute attribute;
+ SilcAttributePayload attr;
+ SilcAttributeObjPk pk;
+ SilcAttributeObjService service;
+ unsigned char *tmp;
+ unsigned char sign[2048 + 1];
+ SilcUInt32 sign_len;
SILC_LOG_DEBUG(("Constructing Requested Attributes"));
- if (attrs_len)
- *attrs_len = len;
+ /* Go through all requested attributes */
+ silc_dlist_start(query->attrs);
+ while ((attr = silc_dlist_get(query->attrs)) != SILC_LIST_END) {
+ attribute = silc_attribute_get_attribute(attr);
+ switch (attribute) {
+
+ case SILC_ATTRIBUTE_SERVICE:
+ /* Put SERVICE. Put only SILC service. */
+ memset(&service, 0, sizeof(service));
+ service.port = (server->config->server_info->primary ?
+ server->config->server_info->primary->port : SILC_PORT);
+ silc_strncat(service.address, sizeof(service.address),
+ server->server_name, strlen(server->server_name));
+ service.status = !(client_entry->mode & SILC_UMODE_DETACHED);
+ if (client_entry->connection)
+ service.idle = time(NULL) - client_entry->data.last_receive;
+ buffer = silc_attribute_payload_encode(buffer, attribute,
+ SILC_ATTRIBUTE_FLAG_VALID,
+ &service, sizeof(service));
+ if (!buffer)
+ return NULL;
+ break;
+
+ case SILC_ATTRIBUTE_STATUS_MOOD:
+ /* Put STATUS_MOOD */
+ buffer = silc_attribute_payload_encode(buffer, attribute,
+ SILC_ATTRIBUTE_FLAG_VALID,
+ (void *)
+ SILC_ATTRIBUTE_MOOD_NORMAL,
+ sizeof(SilcUInt32));
+ if (!buffer)
+ return NULL;
+ break;
+
+ case SILC_ATTRIBUTE_STATUS_FREETEXT:
+ /* Put STATUS_FREETEXT. We just tell in the message that we are
+ replying on behalf of the client. */
+ tmp =
+ "This information was provided by the server on behalf of the user";
+ buffer = silc_attribute_payload_encode(buffer, attribute,
+ SILC_ATTRIBUTE_FLAG_VALID,
+ tmp, strlen(tmp));
+ if (!buffer)
+ return NULL;
+ break;
+
+ case SILC_ATTRIBUTE_PREFERRED_CONTACT:
+ /* Put PREFERRED_CONTACT */
+ buffer = silc_attribute_payload_encode(buffer, attribute,
+ SILC_ATTRIBUTE_FLAG_VALID,
+ (void *)
+ SILC_ATTRIBUTE_CONTACT_CHAT,
+ sizeof(SilcUInt32));
+ if (!buffer)
+ return NULL;
+ break;
+
+ case SILC_ATTRIBUTE_USER_PUBLIC_KEY:
+ /* Put USER_PUBLIC_KEY */
+ if (client_entry->data.public_key) {
+ pk.type = "silc-rsa";
+ pk.data = silc_pkcs_public_key_encode(client_entry->data.public_key,
+ &pk.data_len);
+ buffer = silc_attribute_payload_encode(buffer, attribute, pk.data ?
+ SILC_ATTRIBUTE_FLAG_VALID :
+ SILC_ATTRIBUTE_FLAG_INVALID,
+ &pk, sizeof(pk));
+ silc_free(pk.data);
+ if (!buffer)
+ return NULL;
+ break;
+ }
+
+ /* No public key available */
+ buffer = silc_attribute_payload_encode(buffer, attribute,
+ SILC_ATTRIBUTE_FLAG_INVALID,
+ NULL, 0);
+ if (!buffer)
+ return NULL;
+ break;
+
+ default:
+ /* Ignore SERVER_PUBLIC_KEY since we are going to put it anyway later */
+ if (attribute == SILC_ATTRIBUTE_SERVER_PUBLIC_KEY ||
+ attribute == SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE)
+ break;
+
+ /* For other attributes we cannot reply so mark it invalid */
+ buffer = silc_attribute_payload_encode(buffer, attribute,
+ SILC_ATTRIBUTE_FLAG_INVALID,
+ NULL, 0);
+ if (!buffer)
+ return NULL;
+ break;
+ }
+ }
+
+ /* Always put our public key. This assures that we send at least
+ something valid back always. */
+ pk.type = "silc-rsa";
+ pk.data = silc_pkcs_public_key_encode(server->public_key, &pk.data_len);
+ buffer = silc_attribute_payload_encode(buffer,
+ SILC_ATTRIBUTE_SERVER_PUBLIC_KEY,
+ pk.data ? SILC_ATTRIBUTE_FLAG_VALID :
+ SILC_ATTRIBUTE_FLAG_INVALID,
+ &pk, sizeof(pk));
+ silc_free(pk.data);
+ if (!buffer)
+ return NULL;
+
+ /* Finally compute the digital signature of all the data we provided
+ as an indication that we provided rightfull information, and this
+ also authenticates our public key. */
+ if (silc_pkcs_get_key_len(server->pkcs) / 8 <= sizeof(sign) -1 &&
+ silc_pkcs_sign_with_hash(server->pkcs, server->sha1hash,
+ buffer->data, buffer->len,
+ sign, &sign_len)) {
+ pk.type = NULL;
+ pk.data = sign;
+ pk.data_len = sign_len;
+ buffer =
+ silc_attribute_payload_encode(buffer,
+ SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE,
+ SILC_ATTRIBUTE_FLAG_VALID,
+ &pk, sizeof(pk));
+ }
+ if (!buffer)
+ return NULL;
- return attrs;
+ return buffer;
}
/* Find client by the Client ID indicated by the `client_id', and if not