- }
- }
-
- *callbacks_count = i;
- return callbacks;
-}
-
-/* Sends simple status message as command reply packet */
-
-static void
-silc_server_command_send_status_reply(SilcServerCommandContext cmd,
- SilcCommand command,
- SilcStatus status,
- SilcStatus error)
-{
- SilcBuffer buffer;
-
- SILC_LOG_DEBUG(("Sending command status %d", status));
-
- buffer =
- silc_command_reply_payload_encode_va(command, status, error,
- silc_command_get_ident(cmd->payload),
- 0);
- silc_server_packet_send(cmd->server, cmd->sock,
- SILC_PACKET_COMMAND_REPLY, 0,
- buffer->data, buffer->len, FALSE);
- silc_buffer_free(buffer);
-}
-
-/* Sends command status reply with one extra argument. The argument
- type must be sent as argument. */
-
-static void
-silc_server_command_send_status_data(SilcServerCommandContext cmd,
- SilcCommand command,
- SilcStatus status,
- SilcStatus error,
- SilcUInt32 arg_type,
- const unsigned char *arg,
- SilcUInt32 arg_len)
-{
- SilcBuffer buffer;
-
- SILC_LOG_DEBUG(("Sending command status %d", status));
-
- buffer =
- silc_command_reply_payload_encode_va(command, status, 0,
- silc_command_get_ident(cmd->payload),
- 1, arg_type, arg, arg_len);
- silc_server_packet_send(cmd->server, cmd->sock,
- SILC_PACKET_COMMAND_REPLY, 0,
- buffer->data, buffer->len, FALSE);
- silc_buffer_free(buffer);
-}
-
-/* This function can be called to check whether in the command reply
- an error occurred. This function has no effect if this is called
- when the command function was not called as pending command callback.
- This returns TRUE if error had occurred. */
-
-static bool
-silc_server_command_pending_error_check(SilcServerCommandContext cmd,
- SilcServerCommandReplyContext cmdr,
- SilcCommand command)
-{
- if (!cmd->pending || !cmdr)
- return FALSE;
-
- if (!silc_command_get_status(cmdr->payload, NULL, NULL)) {
- SilcBuffer buffer;
-
- /* Send the same command reply payload */
- silc_command_set_ident(cmdr->payload,
- silc_command_get_ident(cmd->payload));
- buffer = silc_command_payload_encode_payload(cmdr->payload);
- silc_server_packet_send(cmd->server, cmd->sock,
- SILC_PACKET_COMMAND_REPLY, 0,
- buffer->data, buffer->len, FALSE);
- silc_buffer_free(buffer);
- return TRUE;
- }
-
- return FALSE;
-}
-
-/******************************************************************************
-
- WHOIS Functions
-
-******************************************************************************/
-
-typedef struct {
- void *id;
- SilcIdType id_type;
- SilcUInt32 index;
- SilcStatus error;
-} *ResolveError;
-
-#define ADD_ERROR(errptr, errptr_count, _id, _id_type, _index, _status) \
-do { \
- errptr = silc_realloc(errptr, sizeof(*errptr) * (errptr_count + 1)); \
- if (!errptr) \
- return FALSE; \
- errptr[errptr_count].id = _id; \
- errptr[errptr_count].id_type = _id_type; \
- errptr[errptr_count].index = _index; \
- errptr[errptr_count].error = _status; \
- errptr_count++; \
-} while(0)
-
-static int
-silc_server_command_whois_parse(SilcServerCommandContext cmd,
- SilcClientID ***client_id,
- SilcUInt32 *client_id_count,
- char **nickname,
- char **server_name,
- int *count,
- ResolveError *error_client,
- SilcUInt32 *error_client_count)
-{
- unsigned char *tmp;
- SilcUInt32 len;
- SilcUInt32 argc = silc_argument_get_arg_num(cmd->args);
- void *id;
- int i, k;
-
- /* If client ID is in the command it must be used instead of nickname */
- tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
- if (!tmp) {
- /* No ID, get the nickname@server string and parse it. */
- tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
- if (tmp) {
- silc_parse_userfqdn(tmp, nickname, server_name);
- } else {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS,
- SILC_STATUS_ERR_BAD_NICKNAME, 0);
- return FALSE;
- }
- } else {
- /* Command includes ID, we must use that. Take all ID's from the
- command packet */
- for (k = 0, i = 0; i < argc; i++) {
- tmp = silc_argument_get_arg_type(cmd->args, i + 4, &len);
- if (!tmp)
- continue;
- id = silc_id_payload_parse_id(tmp, len, NULL);
- if (id) {
- *client_id = silc_realloc(*client_id, sizeof(**client_id) *
- (*client_id_count + 1));
- (*client_id)[k] = id;
- (*client_id_count)++;
- k++;
- } else {
- ADD_ERROR((*error_client), (*error_client_count), NULL, 0, i + 4,
- SILC_STATUS_ERR_BAD_CLIENT_ID);
- }
- }
- }
-
- /* Get the max count of reply messages allowed */
- tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
- if (tmp)
- SILC_GET32_MSB(*count, tmp);
- else
- *count = 0;
-
- return TRUE;
-}
-
-/* Resolve context used by both WHOIS and IDENTIFY commands */
-typedef struct {
- SilcServerEntry router;
- SilcUInt16 ident;
- unsigned char **res_argv;
- SilcUInt32 *res_argv_lens;
- SilcUInt32 *res_argv_types;
- SilcUInt32 res_argc;
-} *SilcServerResolveContext;
-
-static bool
-silc_server_command_whois_check(SilcServerCommandContext cmd,
- SilcClientEntry *clients,
- SilcUInt32 clients_count)
-{
- SilcServer server = cmd->server;
- SilcClientEntry entry;
- SilcServerResolveContext resolve = NULL, r = NULL;
- SilcUInt32 resolve_count = 0;
- int i, k;
- bool no_res = TRUE;
-
- SILC_LOG_DEBUG(("Start"));
-
- for (i = 0; i < clients_count; i++) {
- entry = clients[i];
- if (!entry)
- continue;
-
- if ((entry->nickname && entry->username && entry->userinfo) ||
- !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
- if (!entry->router)
- continue;
-
- /* If we are normal server, and we've not resolved this client from
- router and it is global client, we'll check whether it is on some
- channel. If not then we cannot be sure about its validity, and
- we'll resolve it from router. */
- if (cmd->server->server_type != SILC_SERVER || cmd->pending ||
- entry->connection || silc_hash_table_count(entry->channels))
- continue;
- }
-
- /* We need to resolve this entry since it is not complete */
-
- if (!cmd->pending && entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
- /* The entry is being resolved (and we are not the resolver) so attach
- to the command reply and we're done with this one. */
- silc_server_command_pending(server, SILC_COMMAND_NONE,
- entry->resolve_cmd_ident,
- silc_server_command_whois,
- silc_server_command_dup(cmd));
- no_res = FALSE;
- } else {
- if (entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
- /* We've resolved this and it still is not ready. We'll return
- and are that this will be handled again after it is resolved. */
- for (i = 0; i < resolve_count; i++) {
- for (k = 0; k < r->res_argc; k++)
- silc_free(r->res_argv[k]);
- silc_free(r->res_argv);
- silc_free(r->res_argv_lens);
- silc_free(r->res_argv_types);
- }
- silc_free(resolve);
- return FALSE;
- } else {
- /* We'll resolve this client */
- SilcBuffer idp;
-
- r = NULL;
- for (k = 0; k < resolve_count; k++) {
- if (resolve[k].router == entry->router) {
- r = &resolve[k];
- break;
- }
- }
-
- if (!r) {
- resolve = silc_realloc(resolve, sizeof(*resolve) *
- (resolve_count + 1));
- r = &resolve[resolve_count];
- memset(r, 0, sizeof(*r));
- r->router = entry->router;
- r->ident = ++server->cmd_ident;
- resolve_count++;
- }
-
- r->res_argv = silc_realloc(r->res_argv, sizeof(*r->res_argv) *
- (r->res_argc + 1));
- r->res_argv_lens = silc_realloc(r->res_argv_lens,
- sizeof(*r->res_argv_lens) *
- (r->res_argc + 1));
- r->res_argv_types = silc_realloc(r->res_argv_types,
- sizeof(*r->res_argv_types) *
- (r->res_argc + 1));
- idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
- r->res_argv[r->res_argc] = silc_calloc(idp->len,
- sizeof(**r->res_argv));
- memcpy(r->res_argv[r->res_argc], idp->data, idp->len);
- r->res_argv_lens[r->res_argc] = idp->len;
- r->res_argv_types[r->res_argc] = r->res_argc + 4;
- r->res_argc++;
- silc_buffer_free(idp);
-
- entry->resolve_cmd_ident = r->ident;
- entry->data.status |= SILC_IDLIST_STATUS_RESOLVING;
- entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
- }
- }
- }
-
- /* Do the resolving */
- for (i = 0; i < resolve_count; i++) {
- SilcBuffer res_cmd;
-
- r = &resolve[i];
-
- /* Send WHOIS request. We send WHOIS since we're doing the requesting
- now anyway so make it a good one. */
- res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
- r->res_argc, r->res_argv,
- r->res_argv_lens,
- r->res_argv_types,
- r->ident);
- silc_server_packet_send(server, r->router->connection,
- SILC_PACKET_COMMAND, cmd->packet->flags,
- res_cmd->data, res_cmd->len, FALSE);
-
- /* Reprocess this packet after received reply */
- silc_server_command_pending(server, SILC_COMMAND_WHOIS,
- r->ident,
- silc_server_command_whois,
- silc_server_command_dup(cmd));
- cmd->pending = TRUE;
-
- silc_buffer_free(res_cmd);
- for (k = 0; k < r->res_argc; k++)
- silc_free(r->res_argv[k]);
- silc_free(r->res_argv);
- silc_free(r->res_argv_lens);
- silc_free(r->res_argv_types);
- no_res = FALSE;
- }
- silc_free(resolve);
-
- return no_res;
-}
-
-static void
-silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
- SilcClientEntry *clients,
- SilcUInt32 clients_count,
- ResolveError errors,
- SilcUInt32 errors_count,
- int count, const char *nickname,
- SilcClientID **client_ids)
-{
- SilcServer server = cmd->server;
- char *tmp;
- int i, k, valid_count = clients_count;
- SilcUInt32 len;
- SilcBuffer packet, idp, channels, umode_list = NULL;
- SilcClientEntry entry;
- SilcStatus status;
- SilcUInt16 ident = silc_command_get_ident(cmd->payload);
- char nh[256], uh[256];
- unsigned char idle[4], mode[4];
- unsigned char *fingerprint;
- SilcSocketConnection hsock;
-
- if (nickname) {
- /* Process only valid clients and ignore those that are not registered.
- This is checked with nickname only because when resolved client IDs
- we check that they are registered earlier. */
- valid_count = 0;
- for (i = 0; i < clients_count; i++)
- if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
- valid_count++;
- else
- clients[i] = NULL;
-
- if (!valid_count) {
- silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
- SILC_STATUS_ERR_NO_SUCH_NICK, 0,
- 3, nickname, strlen(nickname));
- return;
- }
- }
-
- /* Start processing found clients. */
- status = SILC_STATUS_OK;
- if (valid_count > 1)
- status = SILC_STATUS_LIST_START;
-
- for (i = 0, k = 0; i < clients_count; i++) {
- entry = clients[i];
- if (!entry)
- continue;
-
- if (k >= 1)
- status = SILC_STATUS_LIST_ITEM;
- if (valid_count > 1 && k == valid_count - 1 && !errors_count)
- status = SILC_STATUS_LIST_END;
- if (count && k - 1 == count)
- status = SILC_STATUS_LIST_END;
-
- /* Send WHOIS reply */
- idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
- tmp = silc_argument_get_first_arg(cmd->args, NULL);
-
- memset(uh, 0, sizeof(uh));
- memset(nh, 0, sizeof(nh));
- memset(idle, 0, sizeof(idle));
-
- strncat(nh, entry->nickname, strlen(entry->nickname));
- if (!strchr(entry->nickname, '@')) {
- strncat(nh, "@", 1);
- if (entry->servername) {
- strncat(nh, entry->servername, strlen(entry->servername));
- } else {
- len = entry->router ? strlen(entry->router->server_name) :
- strlen(server->server_name);
- strncat(nh, entry->router ? entry->router->server_name :
- server->server_name, len);
- }
- }
-
- strncat(uh, entry->username, strlen(entry->username));
- if (!strchr(entry->username, '@') && entry->connection) {
- strncat(uh, "@", 1);
- hsock = (SilcSocketConnection)entry->connection;
- len = strlen(hsock->hostname);
- strncat(uh, hsock->hostname, len);
- }
-
- if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
- channels = silc_server_get_client_channel_list(server, entry, FALSE,
- FALSE, &umode_list);
- else
- channels = silc_server_get_client_channel_list(server, entry, TRUE,
- TRUE, &umode_list);
-
- if (entry->data.fingerprint[0] != 0 && entry->data.fingerprint[1] != 0)
- fingerprint = entry->data.fingerprint;
- else
- fingerprint = NULL;
-
- SILC_PUT32_MSB(entry->mode, mode);
- if (entry->connection)
- SILC_PUT32_MSB((time(NULL) - entry->data.last_receive), idle);
-
- packet =
- silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
- status, 0, ident, 9,
- 2, idp->data, idp->len,
- 3, nh, strlen(nh),
- 4, uh, strlen(uh),
- 5, entry->userinfo,
- strlen(entry->userinfo),
- 6, channels ? channels->data : NULL,
- channels ? channels->len : 0,
- 7, mode, 4,
- 8, idle, 4,
- 9, fingerprint,
- fingerprint ? 20 : 0,
- 10, umode_list ? umode_list->data :
- NULL, umode_list ? umode_list->len :
- 0);
-
- silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
- 0, packet->data, packet->len, FALSE);
-
- silc_buffer_free(packet);
- silc_buffer_free(idp);
- if (channels)
- silc_buffer_free(channels);
- if (umode_list) {
- silc_buffer_free(umode_list);
- umode_list = NULL;
- }
-
- if (status == SILC_STATUS_LIST_END)
- break;
- k++;
- }
-
- /* Send error replies */
- if (status == SILC_STATUS_OK && errors_count > 1)
- status = SILC_STATUS_LIST_START;
-
- idp = NULL;
- for (i = 0, k = 0; i < errors_count; i++) {
- if (errors[i].id) {
- idp = silc_id_payload_encode(errors[i].id, SILC_ID_CLIENT);
- tmp = idp->data;
- len = idp->len;
- } else {
- tmp = silc_argument_get_arg_type(cmd->args, errors[i].index, &len);
- }
-
- if (k >= 1)
- status = SILC_STATUS_LIST_ITEM;
- if (errors_count > 1 && k == errors_count - 1)
- status = SILC_STATUS_LIST_END;
- if (count && k - 1 == count)
- status = SILC_STATUS_LIST_END;
-
- /* Send error */
- silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
- (status == SILC_STATUS_OK ?
- errors[i].error : status),
- (status == SILC_STATUS_OK ?
- 0 : errors[i].error),
- 2, tmp, len);
- silc_buffer_free(idp);
- idp = NULL;
-
- if (status == SILC_STATUS_LIST_END)
- break;
- k++;
- }
-}
-
-static void
-silc_server_command_whois_send_router(SilcServerCommandContext cmd)
-{
- SilcServer server = cmd->server;
- SilcBuffer tmpbuf;
- SilcUInt16 old_ident;
-
- old_ident = silc_command_get_ident(cmd->payload);
- silc_command_set_ident(cmd->payload, ++server->cmd_ident);
- tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-
- /* Send WHOIS command to our router */
- silc_server_packet_send(server, (SilcSocketConnection)
- server->router->connection,
- SILC_PACKET_COMMAND, cmd->packet->flags,
- tmpbuf->data, tmpbuf->len, TRUE);
-
- /* Reprocess this packet after received reply from router */
- silc_server_command_pending(server, SILC_COMMAND_WHOIS,
- silc_command_get_ident(cmd->payload),
- silc_server_command_whois,
- silc_server_command_dup(cmd));
- cmd->pending = TRUE;
- silc_command_set_ident(cmd->payload, old_ident);
- silc_buffer_free(tmpbuf);
-}
-
-static int
-silc_server_command_whois_process(SilcServerCommandContext cmd)
-{
- SilcServer server = cmd->server;
- char *nick = NULL, *server_name = NULL;
- int count = 0;
- SilcClientEntry *clients = NULL, entry;
- SilcClientID **client_id = NULL;
- SilcUInt32 client_id_count = 0, clients_count = 0, error_client_count = 0;
- ResolveError error_client = NULL;
- int i, ret = 0;
- bool check_global = FALSE;
-
- /* Parse the whois request */
- if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count,
- &nick, &server_name, &count,
- &error_client, &error_client_count))
- return 0;
-
- /* Send the WHOIS request to the router only if it included nickname.
- Since nicknames can be expanded into many clients we need to send it
- to router. If the WHOIS included only client ID's we will check them
- first locally since we just might have them. */
- if (nick && !client_id_count && cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
- server->server_type == SILC_SERVER && !cmd->pending &&
- !server->standalone) {
- silc_server_command_whois_send_router(cmd);
- ret = -1;
- goto out;
- }
-
- if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
- check_global = TRUE;
- else if (server->server_type != SILC_SERVER)
- check_global = TRUE;
-
- /* Get all clients matching that ID or nickname from local list */
- if (client_id_count) {
- /* Check all Client ID's received in the command packet */
- for (i = 0; i < client_id_count; i++) {
- entry = silc_idlist_find_client_by_id(server->local_list,
- client_id[i], TRUE, NULL);
- if (!entry && check_global)
- entry = silc_idlist_find_client_by_id(server->global_list,
- client_id[i], TRUE, NULL);
- if (entry) {
- clients = silc_realloc(clients, sizeof(*clients) *
- (clients_count + 1));
- clients[clients_count++] = entry;
- } else {
- /* If we are normal server and did not send the request first to router
- do it now, since we do not have the Client ID information. */
- if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
- server->server_type == SILC_SERVER && !cmd->pending &&
- !server->standalone) {
- silc_server_command_whois_send_router(cmd);
- ret = -1;
- goto out;
- }
-
- ADD_ERROR(error_client, error_client_count, client_id[i],
- SILC_ID_CLIENT, 0, SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
- }
- }
- } else if (nick) {
- /* Find by nickname */
- if (!silc_idlist_get_clients_by_hash(server->local_list,
- nick, server->md5hash,
- &clients, &clients_count))
- silc_idlist_get_clients_by_nickname(server->local_list,
- nick, server_name,
- &clients, &clients_count);
- if (check_global) {
- if (!silc_idlist_get_clients_by_hash(server->global_list,
- nick, server->md5hash,
- &clients, &clients_count))
- silc_idlist_get_clients_by_nickname(server->global_list,
- nick, server_name,
- &clients, &clients_count);
- }
- }
-
- if (!clients && (client_id_count || nick)) {
- /* If we are normal server and did not send the request first to router
- do it now, since we do not have the information. */
- if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
- server->server_type == SILC_SERVER && !cmd->pending &&
- !server->standalone) {
- silc_server_command_whois_send_router(cmd);
- ret = -1;
- goto out;
- }
-
- /* Such client(s) really does not exist in the SILC network. */
- if (!client_id_count)
- silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
- SILC_STATUS_ERR_NO_SUCH_NICK, 0,
- 3, nick, strlen(nick));
- else
- silc_server_command_whois_send_reply(cmd, NULL, 0,
- error_client, error_client_count,
- 0, NULL, NULL);
- goto out;
- }
-
- /* Router always finds the client entry if it exists in the SILC network.
- However, it might be incomplete entry and does not include all the
- mandatory fields that WHOIS command reply requires. Check for these and
- make query from the server who owns the client if some fields are
- missing. */
- if (!silc_server_command_whois_check(cmd, clients, clients_count)) {
- ret = -1;
- goto out;
- }
-
- /* Send the command reply */
- silc_server_command_whois_send_reply(cmd, clients, clients_count,
- error_client, error_client_count,
- count, nick, client_id);
-
- out:
- if (client_id_count) {
- for (i = 0; i < client_id_count; i++)
- silc_free(client_id[i]);
- silc_free(client_id);
- }
- silc_free(clients);
- silc_free(error_client);
- silc_free(nick);
- silc_free(server_name);
-
- return ret;
-}
-
-/* Server side of command WHOIS. Processes user's query and sends found
- results as command replies back to the client. */
-
-SILC_SERVER_CMD_FUNC(whois)
-{
- SilcServerCommandContext cmd = (SilcServerCommandContext)context;
- int ret = 0;
-
- SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOIS, cmd, 1, 3328);
-
- ret = silc_server_command_whois_process(cmd);
- silc_server_command_free(cmd);
-}
-
-/******************************************************************************
-
- WHOWAS Functions
-
-******************************************************************************/
-
-static int
-silc_server_command_whowas_parse(SilcServerCommandContext cmd,
- char **nickname,
- char **server_name,
- int *count)
-{
- unsigned char *tmp;
- SilcUInt32 len;
-
- tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
- if (!tmp) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOWAS,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
- 0);
- return FALSE;
- }
-
- /* Get the nickname@server string and parse it. */
- silc_parse_userfqdn(tmp, nickname, server_name);
-
- /* Get the max count of reply messages allowed */
- tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
- if (tmp)
- SILC_GET32_MSB(*count, tmp);
- else
- *count = 0;
-
- return TRUE;
-}
-
-static char
-silc_server_command_whowas_check(SilcServerCommandContext cmd,
- SilcClientEntry *clients,
- SilcUInt32 clients_count)
-{
- SilcServer server = cmd->server;
- int i;
- SilcClientEntry entry;
-
- for (i = 0; i < clients_count; i++) {
- entry = clients[i];
-
- if (!entry->nickname || !entry->username) {
- SilcBuffer tmpbuf;
- SilcUInt16 old_ident;
-
- if (!entry->router)
- continue;
-
- old_ident = silc_command_get_ident(cmd->payload);
- silc_command_set_ident(cmd->payload, ++server->cmd_ident);
- tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-
- /* Send WHOWAS command */
- silc_server_packet_send(server, entry->router->connection,
- SILC_PACKET_COMMAND, cmd->packet->flags,
- tmpbuf->data, tmpbuf->len, TRUE);
-
- /* Reprocess this packet after received reply */
- silc_server_command_pending(server, SILC_COMMAND_WHOWAS,
- silc_command_get_ident(cmd->payload),
- silc_server_command_whowas,
- silc_server_command_dup(cmd));
- cmd->pending = TRUE;
- silc_command_set_ident(cmd->payload, old_ident);
-
- silc_buffer_free(tmpbuf);
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-static void
-silc_server_command_whowas_send_reply(SilcServerCommandContext cmd,
- SilcClientEntry *clients,
- SilcUInt32 clients_count)
-{
- SilcServer server = cmd->server;
- char *tmp;
- int i, k, count = 0, len;
- SilcBuffer packet, idp;
- SilcClientEntry entry = NULL;
- SilcStatus status;
- SilcUInt16 ident = silc_command_get_ident(cmd->payload);
- char nh[256], uh[256];
- int valid_count;
-
- status = SILC_STATUS_OK;
-
- /* Process only entries that are not registered anymore. */
- valid_count = 0;
- for (i = 0; i < clients_count; i++) {
- if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
- clients[i] = NULL;
- else
- valid_count++;
- }
-
- if (!valid_count) {
- /* No valid entries found at all, just send error */
- tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
- silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS,
- SILC_STATUS_ERR_NO_SUCH_NICK, 0,
- 3, tmp, tmp ? strlen(tmp) : 0);
- return;
- }
-
- if (valid_count > 1)
- status = SILC_STATUS_LIST_START;
-
- for (i = 0, k = 0; i < clients_count; i++) {
- entry = clients[i];
- if (!entry)
- continue;
-
- if (k >= 1)
- status = SILC_STATUS_LIST_ITEM;
- if (valid_count > 1 && k == valid_count - 1)
- status = SILC_STATUS_LIST_END;
- if (count && k - 1 == count)
- status = SILC_STATUS_LIST_END;
-
- /* Send WHOWAS reply */
- idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
- tmp = silc_argument_get_first_arg(cmd->args, NULL);
- memset(uh, 0, sizeof(uh));
- memset(nh, 0, sizeof(nh));
-
- strncat(nh, entry->nickname, strlen(entry->nickname));
- if (!strchr(entry->nickname, '@')) {
- strncat(nh, "@", 1);
- if (entry->servername) {
- strncat(nh, entry->servername, strlen(entry->servername));
- } else {
- len = entry->router ? strlen(entry->router->server_name) :
- strlen(server->server_name);
- strncat(nh, entry->router ? entry->router->server_name :
- server->server_name, len);
- }
- }
-
- strncat(uh, entry->username, strlen(entry->username));
- if (!strchr(entry->username, '@')) {
- strncat(uh, "@", 1);
- strcat(uh, "*private*");
- }
-
- packet =
- silc_command_reply_payload_encode_va(SILC_COMMAND_WHOWAS,
- status, 0, ident, 4,
- 2, idp->data, idp->len,
- 3, nh, strlen(nh),
- 4, uh, strlen(uh),
- 5, entry->userinfo,
- entry->userinfo ?
- strlen(entry->userinfo) : 0);
- silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
- 0, packet->data, packet->len, FALSE);
-
- silc_buffer_free(packet);
- silc_buffer_free(idp);
-
- if (status == SILC_STATUS_LIST_END)
- break;
- k++;
- }
-}
-
-static int
-silc_server_command_whowas_process(SilcServerCommandContext cmd)
-{
- SilcServer server = cmd->server;
- char *nick = NULL, *server_name = NULL;
- int count = 0;
- SilcClientEntry *clients = NULL;
- SilcUInt32 clients_count = 0;
- int ret = 0;
- bool check_global = FALSE;
-
- /* Protocol dictates that we must always send the received WHOWAS request
- to our router if we are normal server, so let's do it now unless we
- are standalone. We will not send any replies to the client until we
- have received reply from the router. */
- if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
- server->server_type == SILC_SERVER && !cmd->pending &&
- !server->standalone) {
- SilcBuffer tmpbuf;
- SilcUInt16 old_ident;
-
- old_ident = silc_command_get_ident(cmd->payload);
- silc_command_set_ident(cmd->payload, ++server->cmd_ident);
- tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-
- /* Send WHOWAS command to our router */
- silc_server_packet_send(server, (SilcSocketConnection)
- server->router->connection,
- SILC_PACKET_COMMAND, cmd->packet->flags,
- tmpbuf->data, tmpbuf->len, TRUE);
-
- /* Reprocess this packet after received reply from router */
- silc_server_command_pending(server, SILC_COMMAND_WHOWAS,
- silc_command_get_ident(cmd->payload),
- silc_server_command_whowas,
- silc_server_command_dup(cmd));
- cmd->pending = TRUE;
- silc_command_set_ident(cmd->payload, old_ident);
-
- silc_buffer_free(tmpbuf);
- ret = -1;
- goto out;
- }
-
- /* We are ready to process the command request. Let's search for the
- requested client and send reply to the requesting client. */
-
- if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
- check_global = TRUE;
- else if (server->server_type != SILC_SERVER)
- check_global = TRUE;
-
- /* Parse the whowas request */
- if (!silc_server_command_whowas_parse(cmd, &nick, &server_name, &count))
- return 0;
-
- /* Get all clients matching that nickname from local list */
- if (!silc_idlist_get_clients_by_nickname(server->local_list,
- nick, server_name,
- &clients, &clients_count))
- silc_idlist_get_clients_by_hash(server->local_list,
- nick, server->md5hash,
- &clients, &clients_count);
-
- /* Check global list as well */
- if (check_global) {
- if (!silc_idlist_get_clients_by_nickname(server->global_list,
- nick, server_name,
- &clients, &clients_count))
- silc_idlist_get_clients_by_hash(server->global_list,
- nick, server->md5hash,
- &clients, &clients_count);
- }
-
- if (!clients) {
- /* Such a client really does not exist in the SILC network. */
- silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS,
- SILC_STATUS_ERR_NO_SUCH_NICK, 0,
- 3, nick, strlen(nick));
- goto out;
- }
-
- if (!silc_server_command_whowas_check(cmd, clients, clients_count)) {
- ret = -1;
- goto out;
- }
-
- /* Send the command reply to the client */
- silc_server_command_whowas_send_reply(cmd, clients, clients_count);
-
- out:
- silc_free(clients);
- silc_free(nick);
- silc_free(server_name);
- return ret;
-}
-
-/* Server side of command WHOWAS. */
-
-SILC_SERVER_CMD_FUNC(whowas)
-{
- SilcServerCommandContext cmd = (SilcServerCommandContext)context;
- int ret = 0;
-
- SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOWAS, cmd, 1, 2);
-
- ret = silc_server_command_whowas_process(cmd);
- silc_server_command_free(cmd);
-}
-
-/******************************************************************************
-
- IDENTIFY Functions
-
-******************************************************************************/
-
-static void
-silc_server_command_identify_send_router(SilcServerCommandContext cmd)
-{
- SilcServer server = cmd->server;
- SilcBuffer tmpbuf;
- SilcUInt16 old_ident;
-
- old_ident = silc_command_get_ident(cmd->payload);
- silc_command_set_ident(cmd->payload, ++server->cmd_ident);
- tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-
- /* Send IDENTIFY command to our router */
- silc_server_packet_send(server, (SilcSocketConnection)
- server->router->connection,
- SILC_PACKET_COMMAND, cmd->packet->flags,
- tmpbuf->data, tmpbuf->len, TRUE);
-
- /* Reprocess this packet after received reply from router */
- silc_server_command_pending(server, SILC_COMMAND_IDENTIFY,
- silc_command_get_ident(cmd->payload),
- silc_server_command_identify,
- silc_server_command_dup(cmd));
- cmd->pending = TRUE;
- silc_command_set_ident(cmd->payload, old_ident);
- silc_buffer_free(tmpbuf);
-}
-
-static int
-silc_server_command_identify_parse(SilcServerCommandContext cmd,
- SilcClientEntry **clients,
- SilcUInt32 *clients_count,
- SilcServerEntry **servers,
- SilcUInt32 *servers_count,
- SilcChannelEntry **channels,
- SilcUInt32 *channels_count,
- SilcUInt32 *count,
- ResolveError *error_id,
- SilcUInt32 *error_id_count)
-{
- SilcServer server = cmd->server;
- unsigned char *tmp;
- SilcUInt32 len;
- SilcUInt32 argc = silc_argument_get_arg_num(cmd->args);
- SilcIDPayload idp;
- bool check_global = FALSE;
- void *entry;
- int i;
-
- if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
- check_global = TRUE;
- else if (server->server_type != SILC_SERVER)
- check_global = TRUE;
-
- /* If ID Payload is in the command it must be used instead of names */
- tmp = silc_argument_get_arg_type(cmd->args, 5, &len);
- if (!tmp) {
- /* No ID, get the names. */
-
- /* If we are normal server and have not resolved information from
- router yet, do so now. */
- if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
- server->server_type == SILC_SERVER && !cmd->pending &&
- !server->standalone) {
- silc_server_command_identify_send_router(cmd);
- return -1;
- }
-
- /* Try to get nickname@server. */
- tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
- if (tmp) {
- char *nick = NULL;
- char *nick_server = NULL;
-
- silc_parse_userfqdn(tmp, &nick, &nick_server);
-
- if (!silc_idlist_get_clients_by_hash(server->local_list,
- nick, server->md5hash,
- clients, clients_count))
- silc_idlist_get_clients_by_nickname(server->local_list,
- nick, nick_server,
- clients, clients_count);
- if (check_global) {
- if (!silc_idlist_get_clients_by_hash(server->global_list,
- nick, server->md5hash,
- clients, clients_count))
- silc_idlist_get_clients_by_nickname(server->global_list,
- nick, nick_server,
- clients, clients_count);
- }
-
- silc_free(nick);
- silc_free(nick_server);
-
- if (!(*clients)) {
- /* the nickname does not exist, send error reply */
- silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
- SILC_STATUS_ERR_NO_SUCH_NICK, 0,
- 3, tmp, strlen(tmp));
- return 0;
- }
- }
-
- /* Try to get server name */
- tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
- if (tmp) {
- entry = silc_idlist_find_server_by_name(server->local_list,
- tmp, TRUE, NULL);
- if (!entry && check_global)
- entry = silc_idlist_find_server_by_name(server->global_list,
- tmp, TRUE, NULL);
- if (entry) {
- *servers = silc_realloc(*servers, sizeof(**servers) *
- (*servers_count + 1));
- (*servers)[(*servers_count)++] = entry;
- }
-
- if (!(*servers)) {
- /* the server does not exist, send error reply */
- silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
- SILC_STATUS_ERR_NO_SUCH_SERVER,
- 0, 3, tmp, strlen(tmp));
- return 0;
- }
- }
-
- /* Try to get channel name */
- tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
- if (tmp) {
- entry = silc_idlist_find_channel_by_name(server->local_list,
- tmp, NULL);
- if (!entry && check_global)
- entry = silc_idlist_find_channel_by_name(server->global_list,
- tmp, NULL);
- if (entry) {
- *channels = silc_realloc(*channels, sizeof(**channels) *
- (*channels_count + 1));
- (*channels)[(*channels_count)++] = entry;
- }
-
- if (!(*channels)) {
- /* The channel does not exist, send error reply */
- silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
- SILC_STATUS_ERR_NO_SUCH_CHANNEL,
- 0, 3, tmp, strlen(tmp));
- return 0;
- }
- }
-
- if (!(*clients) && !(*servers) && !(*channels)) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
- 0);
- return 0;
- }
- } else {
- /* Command includes ID, we must use that. Also check whether the command
- has more than one ID set - take them all. */
-
- /* Take all ID's from the command packet */
- for (i = 0; i < argc; i++) {
- void *id;
-
- tmp = silc_argument_get_arg_type(cmd->args, i + 5, &len);
- if (!tmp)
- continue;
-
- idp = silc_id_payload_parse(tmp, len);
- if (!idp)
- ADD_ERROR((*error_id), (*error_id_count), NULL, 0, i + 5,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-
- id = silc_id_payload_get_id(idp);
- switch (silc_id_payload_get_type(idp)) {
-
- case SILC_ID_CLIENT:
- entry = silc_idlist_find_client_by_id(server->local_list,
- id, TRUE, NULL);
- if (!entry && check_global)
- entry = silc_idlist_find_client_by_id(server->global_list,
- id, TRUE, NULL);
- if (entry) {
- *clients = silc_realloc(*clients, sizeof(**clients) *
- (*clients_count + 1));
- (*clients)[(*clients_count)++] = (SilcClientEntry)entry;
- } else {
- /* If we are normal server and have not resolved information from
- router yet, do so now. */
- if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
- server->server_type == SILC_SERVER && !cmd->pending &&
- !server->standalone) {
- silc_server_command_identify_send_router(cmd);
- silc_free(*clients);
- silc_free(*servers);
- silc_free(*channels);
- silc_free(*error_id);
- return -1;
- }
-
- ADD_ERROR((*error_id), (*error_id_count), NULL, 0, i + 5,
- SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
- }
-
- break;
-
- case SILC_ID_SERVER:
- entry = silc_idlist_find_server_by_id(server->local_list,
- id, TRUE, NULL);
- if (!entry && check_global)
- entry = silc_idlist_find_server_by_id(server->global_list,
- id, TRUE, NULL);
- if (entry) {
- *servers = silc_realloc(*servers, sizeof(**servers) *
- (*servers_count + 1));
- (*servers)[(*servers_count)++] = (SilcServerEntry)entry;
- } else {
- /* If we are normal server and have not resolved information from
- router yet, do so now. */
- if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
- server->server_type == SILC_SERVER && !cmd->pending &&
- !server->standalone) {
- silc_server_command_identify_send_router(cmd);
- silc_free(*clients);
- silc_free(*servers);
- silc_free(*channels);
- silc_free(*error_id);
- return -1;
- }
-
- ADD_ERROR((*error_id), (*error_id_count), NULL, 0, i + 5,
- SILC_STATUS_ERR_NO_SUCH_SERVER_ID);
- }
- break;
-
- case SILC_ID_CHANNEL:
- entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
- if (!entry && check_global)
- entry = silc_idlist_find_channel_by_id(server->global_list, id,
- NULL);
- if (entry) {
- *channels = silc_realloc(*channels, sizeof(**channels) *
- (*channels_count + 1));
- (*channels)[(*channels_count)++] = (SilcChannelEntry)entry;
- } else {
- /* If we are normal server and have not resolved information from
- router yet, do so now. */
- if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
- server->server_type == SILC_SERVER && !cmd->pending &&
- !server->standalone) {
- silc_server_command_identify_send_router(cmd);
- silc_free(*clients);
- silc_free(*servers);
- silc_free(*channels);
- silc_free(*error_id);
- return -1;
- }
-
- ADD_ERROR((*error_id), (*error_id_count), NULL, 0, i + 5,
- SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID);
- }
- break;
- }
-
- silc_free(id);
- }
- }
-
- /* Get the max count of reply messages allowed */
- tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
- if (tmp)
- SILC_GET32_MSB(*count, tmp);
- else
- *count = 0;
-
- return 1;
-}
-
-/* Checks that all mandatory fields in client entry are present. If not
- then send WHOIS request to the server who owns the client. We use
- WHOIS because we want to get as much information as possible at once. */
-
-static bool
-silc_server_command_identify_check_client(SilcServerCommandContext cmd,
- SilcClientEntry *clients,
- SilcUInt32 clients_count)
-{
- SilcServer server = cmd->server;
- SilcClientEntry entry;
- SilcServerResolveContext resolve = NULL, r = NULL;
- SilcUInt32 resolve_count = 0;
- int i, k;
- bool no_res = TRUE;
-
- for (i = 0; i < clients_count; i++) {
- entry = clients[i];
- if (!entry)
- continue;
-
- if (entry->nickname ||
- !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
- if (!entry->router)
- continue;
-
- /* If we are normal server, and we've not resolved this client from
- router and it is global client, we'll check whether it is on some
- channel. If not then we cannot be sure about its validity, and
- we'll resolve it from router. */
- if (cmd->server->server_type != SILC_SERVER || cmd->pending ||
- entry->connection || silc_hash_table_count(entry->channels))
- continue;
- }
-
- /* We need to resolve this entry since it is not complete */
-
- if (!cmd->pending && entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
- /* The entry is being resolved (and we are not the resolver) so attach
- to the command reply and we're done with this one. */
- silc_server_command_pending(server, SILC_COMMAND_NONE,
- entry->resolve_cmd_ident,
- silc_server_command_identify,
- silc_server_command_dup(cmd));
- no_res = FALSE;
- } else {
- if (entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
- /* We've resolved this and it still is not ready. We'll return
- and are that this will be handled again after it is resolved. */
- for (i = 0; i < resolve_count; i++) {
- for (k = 0; k < r->res_argc; k++)
- silc_free(r->res_argv[k]);
- silc_free(r->res_argv);
- silc_free(r->res_argv_lens);
- silc_free(r->res_argv_types);
- }
- silc_free(resolve);
- return FALSE;
- } else {
- /* We'll resolve this client */
- SilcBuffer idp;
-
- r = NULL;
- for (k = 0; k < resolve_count; k++) {
- if (resolve[k].router == entry->router) {
- r = &resolve[k];
- break;
- }
- }
-
- if (!r) {
- resolve = silc_realloc(resolve, sizeof(*resolve) *
- (resolve_count + 1));
- r = &resolve[resolve_count];
- memset(r, 0, sizeof(*r));
- r->router = entry->router;
- r->ident = ++server->cmd_ident;
- resolve_count++;
- }
-
- r->res_argv = silc_realloc(r->res_argv, sizeof(*r->res_argv) *
- (r->res_argc + 1));
- r->res_argv_lens = silc_realloc(r->res_argv_lens,
- sizeof(*r->res_argv_lens) *
- (r->res_argc + 1));
- r->res_argv_types = silc_realloc(r->res_argv_types,
- sizeof(*r->res_argv_types) *
- (r->res_argc + 1));
- idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
- r->res_argv[r->res_argc] = silc_calloc(idp->len,
- sizeof(**r->res_argv));
- memcpy(r->res_argv[r->res_argc], idp->data, idp->len);
- r->res_argv_lens[r->res_argc] = idp->len;
- r->res_argv_types[r->res_argc] = r->res_argc + 4;
- r->res_argc++;
- silc_buffer_free(idp);
-
- entry->resolve_cmd_ident = r->ident;
- entry->data.status |= SILC_IDLIST_STATUS_RESOLVING;
- entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
- }
- }
- }
-
- /* Do the resolving */
- for (i = 0; i < resolve_count; i++) {
- SilcBuffer res_cmd;
-
- r = &resolve[i];
-
- /* Send WHOIS request. We send WHOIS since we're doing the requesting
- now anyway so make it a good one. */
- res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
- r->res_argc, r->res_argv,
- r->res_argv_lens,
- r->res_argv_types,
- r->ident);
- silc_server_packet_send(server, r->router->connection,
- SILC_PACKET_COMMAND, cmd->packet->flags,
- res_cmd->data, res_cmd->len, FALSE);
-
- /* Reprocess this packet after received reply */
- silc_server_command_pending(server, SILC_COMMAND_WHOIS,
- r->ident,
- silc_server_command_identify,
- silc_server_command_dup(cmd));
- cmd->pending = TRUE;
-
- silc_buffer_free(res_cmd);
- for (k = 0; k < r->res_argc; k++)
- silc_free(r->res_argv[k]);
- silc_free(r->res_argv);
- silc_free(r->res_argv_lens);
- silc_free(r->res_argv_types);
- no_res = FALSE;