From 4d4425e57eb164ce87a0b87ba9e991cbaa9d9baf Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Thu, 29 Jun 2006 21:05:51 +0000 Subject: [PATCH] updates. --- includes/silc.h.in | 3 +- lib/silccore/silcattrs.h | 6 +- lib/silccore/silccommand.h | 6 +- lib/silccore/silcidcache.c | 9 +- lib/silccore/silcidcache.h | 11 +- lib/silccore/silcnotify.h | 20 +- lib/silcserver/server.c | 11 +- lib/silcserver/server_entry.c | 40 +- lib/silcserver/server_internal.h | 32 +- lib/silcserver/server_send.c | 76 +- lib/silcserver/server_st_accept.c | 7 +- lib/silcserver/server_st_command.c | 239 +++++-- lib/silcserver/server_st_command.h | 5 +- lib/silcserver/server_st_packet.c | 2 +- lib/silcserver/server_st_query.c | 1049 +++++++++++++++++++++++++++- lib/silcserver/server_util.c | 18 +- 16 files changed, 1390 insertions(+), 144 deletions(-) diff --git a/includes/silc.h.in b/includes/silc.h.in index 20be3339..3e0b2687 100644 --- a/includes/silc.h.in +++ b/includes/silc.h.in @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2005 Pekka Riikonen + Copyright (C) 1997 - 2006 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 @@ -258,6 +258,7 @@ extern "C" { /* More SILC util library includes */ #include "silctime.h" #include "silcmutex.h" +#include "silccond.h" #include "silcthread.h" #include "silcschedule.h" #include "silchashtable.h" diff --git a/lib/silccore/silcattrs.h b/lib/silccore/silcattrs.h index b51ca824..a7b38d5e 100644 --- a/lib/silccore/silcattrs.h +++ b/lib/silccore/silcattrs.h @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 2002 - 2005 Pekka Riikonen + Copyright (C) 2002 - 2006 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 @@ -454,9 +454,7 @@ typedef struct SilcAttributeObjDeviceStruct { * caller must free the data inside the structure. The 'type' is one * of following: "silc-rsa", "silc-dss, "ssh-rsa", "ssh-dss", * "pgp-sign-rsa", "pgp-sign-dss", "x509v3-sign-rsa", "x509v3-sign-dss". - * See the draft-riikonen-precense-attrs draft for more detailed - * information. The 'type' is NULL when this structure includes a - * digital signature. + * The 'type' is NULL when this structure includes a digital signature. * * In SILC, at least the "silc-rsa" must be supported. In this case * the key is normal SILC Public key. To verify a signature with the diff --git a/lib/silccore/silccommand.h b/lib/silccore/silccommand.h index 8c1094ff..eb0d176e 100644 --- a/lib/silccore/silccommand.h +++ b/lib/silccore/silccommand.h @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2005 Pekka Riikonen + Copyright (C) 1997 - 2006 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 @@ -397,8 +397,8 @@ SilcUInt16 silc_command_get_ident(SilcCommandPayload payload); * ***/ SilcBool silc_command_get_status(SilcCommandPayload payload, - SilcStatus *status, - SilcStatus *error); + SilcStatus *status, + SilcStatus *error); /****f* silccore/SilcCommandAPI/silc_command_set_ident * diff --git a/lib/silccore/silcidcache.c b/lib/silccore/silcidcache.c index accb0917..0d05b46e 100644 --- a/lib/silccore/silcidcache.c +++ b/lib/silccore/silcidcache.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 2000 - 2005 Pekka Riikonen + Copyright (C) 2000 - 2006 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 @@ -229,7 +229,12 @@ SilcBool silc_idcache_update(SilcIDCache cache, SilcIDCacheEntry entry, if (!silc_hash_table_del_by_context(cache->id_table, old_id, entry)) return FALSE; - entry->id = new_id; + if (cache->id_type == SILC_ID_CLIENT) + *(SilcClientID *)entry->id = *(SilcClientID *)new_id; + if (cache->id_type == SILC_ID_SERVER) + *(SilcServerID *)entry->id = *(SilcServerID *)new_id; + if (cache->id_type == SILC_ID_CHANNEL) + *(SilcChannelID *)entry->id = *(SilcChannelID *)new_id; if (!silc_hash_table_add(cache->id_table, entry->id, entry)) return FALSE; diff --git a/lib/silccore/silcidcache.h b/lib/silccore/silcidcache.h index c48fe0b6..7b84e7bc 100644 --- a/lib/silccore/silcidcache.h +++ b/lib/silccore/silcidcache.h @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 2000 - 2005 Pekka Riikonen + Copyright (C) 2000 - 2006 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 @@ -208,10 +208,11 @@ SilcBool silc_idcache_del_by_context(SilcIDCache cache, void *context, * DESCRIPTION * * Updates cache `entry' with new values. If the `new_id' is non-NULL - * then the `entry' will be updated with `new_id'. If the `new_name' is - * non-NULL then the `entry' will be updated with `new_name'. The - * caller is responsible of freeing the old values that was added with - * silc_idcache_add. + * then the new value will be copied over the old value in the `entry'. + * If the `new_name' is non-NULL then the `entry' will be updated with + * `new_name'. The caller is responsible of freeing the old name if it + * was updated with new one. The old ID value does not need to be freed + * as the new value is copied over the old value. * ***/ SilcBool silc_idcache_update(SilcIDCache cache, SilcIDCacheEntry entry, diff --git a/lib/silccore/silcnotify.h b/lib/silccore/silcnotify.h index 41b6d7c5..212a815a 100644 --- a/lib/silccore/silcnotify.h +++ b/lib/silccore/silcnotify.h @@ -1,15 +1,15 @@ /* - + silcnotify.h - + Author: Pekka Riikonen - - Copyright (C) 1997 - 2005 Pekka Riikonen - + + Copyright (C) 1997 - 2006 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 the Free Software Foundation; version 2 of the License. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -33,7 +33,7 @@ /****s* silccore/SilcNotifyAPI/SilcNotifyPayload * * NAME - * + * * typedef struct SilcNotifyPayloadStruct *SilcNotifyPayload; * * DESCRIPTION @@ -49,7 +49,7 @@ typedef struct SilcNotifyPayloadStruct *SilcNotifyPayload; /****d* silccore/SilcNotifyAPI/SilcNotifyType * * NAME - * + * * typedef SilcUInt16 SilcNotifyType; * * DESCRIPTION @@ -116,7 +116,7 @@ SilcNotifyPayload silc_notify_payload_parse(const unsigned char *payload, * arguments must be {unsigned char *, SilcUInt32 (len)}. * ***/ -SilcBuffer silc_notify_payload_encode(SilcNotifyType type, SilcUInt32 argc, +SilcBuffer silc_notify_payload_encode(SilcNotifyType type, SilcUInt32 argc, va_list ap); /****f* silccore/SilcNotifyAPI/silc_notify_payload_encode_args @@ -133,7 +133,7 @@ SilcBuffer silc_notify_payload_encode(SilcNotifyType type, SilcUInt32 argc, * encoded Argument Payload buffer. * ***/ -SilcBuffer silc_notify_payload_encode_args(SilcNotifyType type, +SilcBuffer silc_notify_payload_encode_args(SilcNotifyType type, SilcUInt32 argc, SilcBuffer args); diff --git a/lib/silcserver/server.c b/lib/silcserver/server.c index 974e14d9..756030e0 100644 --- a/lib/silcserver/server.c +++ b/lib/silcserver/server.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2005 Pekka Riikonen + Copyright (C) 1997 - 2006 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 @@ -249,6 +249,7 @@ SILC_FSM_STATE(silc_server_thread_st_run) /* Each packet is processed in FSM thread */ silc_list_start(thread->packet_queue); while ((packet = silc_list_get(thread->packet_queue)) != SILC_LIST_END) { + /* XXX shouldn't the fsm be &thread->fsm */ t = silc_fsm_thread_alloc(fsm, thread, silc_server_thread_packet_dest, NULL, FALSE); if (t) { @@ -276,10 +277,10 @@ SILC_FSM_STATE(silc_server_thread_st_run) silc_list_start(thread->new_conns); while ((ac = silc_list_get(thread->new_conns)) != SILC_LIST_END) { ac->thread = thread; - ac->t = silc_fsm_thread_alloc(&thread->fsm, ac, - silc_server_accept_connection_dest, - NULL, FALSE); - silc_fsm_start(ac->t, silc_server_st_accept_connection); + silc_fsm_thread_init(&ac->t, &thread->fsm, ac, + silc_server_accept_connection_dest, + NULL, FALSE); + silc_fsm_start(&ac->t, silc_server_st_accept_connection); } /* Empty the list */ diff --git a/lib/silcserver/server_entry.c b/lib/silcserver/server_entry.c index 6effe759..da415ffb 100644 --- a/lib/silcserver/server_entry.c +++ b/lib/silcserver/server_entry.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2005 Pekka Riikonen + Copyright (C) 1997 - 2006 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 @@ -23,6 +23,8 @@ #include "silcserver.h" #include "server_internal.h" +/* XXX locking missing from routines! */ + /************************ Static utility functions **************************/ /* Foreach callbcak to free all users from the channel when deleting a @@ -241,7 +243,6 @@ silc_server_replace_server_id(SilcServer server, SilcServerID *old_id, SilcServerID *new_id) { SilcIDCacheEntry id_cache = NULL; - SilcServerEntry entry; if (!old_id || !new_id) return NULL; @@ -251,14 +252,9 @@ silc_server_replace_server_id(SilcServer server, SilcServerID *old_id, SILC_LOG_DEBUG(("New Server ID %s", silc_id_render(new_id, SILC_ID_SERVER))); - if (!silc_idcache_find_by_id_one(server->servers, (void *)old_id, - &id_cache)) + if (!silc_idcache_find_by_id_one(server->servers, old_id, &id_cache)) return NULL; - - entry = id_cache->context; - entry->id = *new_id; - - if (!silc_idcache_update(server->servers, id_cache, old_id, &entry->id, + if (!silc_idcache_update(server->servers, id_cache, old_id, new_id, NULL, NULL)) { SILC_LOG_ERROR(("Error updating Server ID")); return NULL; @@ -266,7 +262,7 @@ silc_server_replace_server_id(SilcServer server, SilcServerID *old_id, SILC_LOG_DEBUG(("Replaced")); - return entry; + return id_cache->context; } @@ -450,9 +446,9 @@ silc_server_replace_client_id(SilcServer server, SilcClientID *old_id, return NULL; SILC_LOG_DEBUG(("Replacing Client ID %s", - silc_id_render(old_id, SILC_ID_SERVER))); + silc_id_render(old_id, SILC_ID_CLIENT))); SILC_LOG_DEBUG(("New Client ID %s", - silc_id_render(new_id, SILC_ID_SERVER))); + silc_id_render(new_id, SILC_ID_CLIENT))); /* Normalize name. This is cached, original is in client context. */ if (nickname) { @@ -462,15 +458,11 @@ silc_server_replace_client_id(SilcServer server, SilcClientID *old_id, return NULL; } - if (!silc_idcache_find_by_id_one(server->clients, (void *)old_id, - &id_cache)) + if (!silc_idcache_find_by_id_one(server->clients, old_id, &id_cache)) return NULL; - entry = id_cache->context; - entry->id = *new_id; - name = id_cache->name; - if (!silc_idcache_update(server->clients, id_cache, old_id, &entry->id, + if (!silc_idcache_update(server->clients, id_cache, old_id, new_id, name, nicknamec)) { SILC_LOG_ERROR(("Error updating Client ID")); return NULL; @@ -651,7 +643,6 @@ SilcChannelEntry silc_server_replace_channel_id(SilcServer server, SilcChannelID *new_id) { SilcIDCacheEntry id_cache = NULL; - SilcChannelEntry entry; if (!old_id || !new_id) return NULL; @@ -661,14 +652,9 @@ SilcChannelEntry silc_server_replace_channel_id(SilcServer server, SILC_LOG_DEBUG(("New Channel ID %s", silc_id_render(new_id, SILC_ID_CHANNEL))); - if (!silc_idcache_find_by_id_one(server->channels, (void *)old_id, - &id_cache)) + if (!silc_idcache_find_by_id_one(server->channels, old_id, &id_cache)) return NULL; - - entry = id_cache->context; - entry->id = *new_id; - - if (!silc_idcache_update(server->channels, id_cache, old_id, &entry->id, + if (!silc_idcache_update(server->channels, id_cache, old_id, new_id, NULL, NULL)) { SILC_LOG_ERROR(("Error updating Channel ID")); return NULL; @@ -676,7 +662,7 @@ SilcChannelEntry silc_server_replace_channel_id(SilcServer server, SILC_LOG_DEBUG(("Replaced")); - return entry; + return id_cache->context; } /* Returns channels from the ID list. If the `channel_id' is NULL then diff --git a/lib/silcserver/server_internal.h b/lib/silcserver/server_internal.h index 64a971b6..80ffe34a 100644 --- a/lib/silcserver/server_internal.h +++ b/lib/silcserver/server_internal.h @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2005 Pekka Riikonen + Copyright (C) 1997 - 2006 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 @@ -123,15 +123,12 @@ struct SilcClientEntryStruct { SilcUInt16 attrs_len; /* Attributes data length */ SilcHashTable channels; /* Joined channels */ SilcPacketStream stream; /* Connection to entry/origin of entry */ + SilcUInt16 resolve_cmd_ident; /* Command identifier when resolving */ long last_command; SilcUInt8 fast_command; unsigned long updated; - /* data.status is RESOLVING and this includes the resolving command - reply identifier. */ - SilcUInt16 resolve_cmd_ident; - /* we need this so nobody can resume more than once at the same time - * server crashes, really odd behaviour, ... */ SilcClientEntry resuming_client; @@ -176,7 +173,7 @@ struct SilcChannelEntryStruct { typedef struct SilcServerAcceptStruct { SilcEntryDataStruct data; SilcServerThread thread; - SilcFSMThread t; /* Thread for accepting connection */ + SilcFSMThreadStruct t; /* Thread for accepting connection */ SilcStream stream; /* Remote connection */ SilcPacketStream packet_stream; /* Remote connection */ SilcConnAuth connauth; /* Connection authentication context */ @@ -357,6 +354,10 @@ struct SilcServerStruct { /* Macros */ +#define SILC_IS_CLIENT(entry) (entry->data.type == SILC_CONN_CLIENT) +#define SILC_IS_SERVER(entry) (entry->data.type == SILC_CONN_SERVER) +#define SILC_IS_ROUTER(entry) (entry->data.type == SILC_CONN_ROUTER) + /* Return pointer to the primary router connection */ #define SILC_PRIMARY_ROUTE(server) server->router @@ -364,8 +365,10 @@ struct SilcServerStruct { #define SILC_BROADCAST(server) (server->server_type == SILC_ROUTER) /* Return TRUE if entry is locally connected or local to us */ -#define SILC_IS_LOCAL(entry) \ - (((SilcIDListData)entry)->status & SILC_IDLIST_STATUS_LOCAL) +#define SILC_IS_LOCAL(entry) (((SilcEntryData)entry)->local) + +/* Return TRUE if entry is registered */ +#define SILC_IS_REGISTERED(entry) (((SilcEntryData)entry)->registered) /* Registers generic task for file descriptor for reading from network and writing to network. As being generic task the actual task is allocated @@ -453,19 +456,6 @@ do { \ silc_free(__fmt__); \ } while(0) -/* Connection retry timeout. We implement exponential backoff algorithm - in connection retry. The interval of timeout grows when retry count - grows. */ -#define SILC_SERVER_RETRY_COUNT 7 /* Max retry count */ -#define SILC_SERVER_RETRY_MULTIPLIER 2 /* Interval growth */ -#define SILC_SERVER_RETRY_RANDOMIZER 2 /* timeout += rnd % 2 */ -#define SILC_SERVER_RETRY_INTERVAL_MIN 10 /* Min retry timeout */ -#define SILC_SERVER_RETRY_INTERVAL_MAX 600 /* Max generated timeout */ - -#define SILC_SERVER_KEEPALIVE 300 /* Heartbeat interval */ -#define SILC_SERVER_REKEY 3600 /* Session rekey interval */ -#define SILC_SERVER_MAX_CONNECTIONS 1000 /* Max connections */ -#define SILC_SERVER_MAX_CONNECTIONS_SINGLE 1000 /* Max connections per host */ #define SILC_SERVER_LOG_FLUSH_DELAY 300 /* Default log flush delay */ /* Macros */ diff --git a/lib/silcserver/server_send.c b/lib/silcserver/server_send.c index 9ec759d1..5f9a61ab 100644 --- a/lib/silcserver/server_send.c +++ b/lib/silcserver/server_send.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2005 Pekka Riikonen + Copyright (C) 1997 - 2006 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 @@ -85,6 +85,80 @@ SilcBool silc_server_send_new_id(SilcPacketStream stream, } +/****************************** Command packets *****************************/ + +/* Generic function to send any command. The arguments must be sent already + encoded into correct form in correct order, and they must as follows: + { argument type, argument data, argument length }. */ + +SilcBool silc_server_send_command(SilcServer server, + SilcPacketStream stream, + SilcCommand command, + SilcUInt16 ident, + SilcUInt32 argc, ...) +{ + SilcBuffer packet; + va_list ap; + SilcBool ret = FALSE; + + /* Statistics */ + server->stat.commands_sent++; + + va_start(ap, argc); + + packet = silc_command_payload_encode_vap(command, ident, argc, ap); + if (!packet) { + va_end(ap); + return ret; + } + + ret = silc_packet_send(stream, SILC_PACKET_COMMAND, 0, + packet->data, silc_buffer_len(packet)); + + silc_buffer_free(packet); + va_end(ap); + + return ret; +} + +/* Generic function to send a command reply. The arguments must be sent + already encoded into correct form in correct order, and they must be + { argument type, argument data, argument length }. */ + +SilcBool silc_server_send_command_reply(SilcServer server, + SilcPacketStream stream, + SilcCommand command, + SilcStatus status, + SilcStatus error, + SilcUInt16 ident, + SilcUInt32 argc, ...) +{ + SilcBuffer packet; + va_list ap; + SilcBool ret = FALSE; + + /* Statistics */ + server->stat.commands_sent++; + + va_start(ap, argc); + + packet = silc_command_reply_payload_encode_vap(command, status, error, + ident, argc, ap); + if (!packet) { + va_end(ap); + return ret; + } + + ret = silc_packet_send(stream, SILC_PACKET_COMMAND_REPLY, 0, + packet->data, silc_buffer_len(packet)); + + silc_buffer_free(packet); + va_end(ap); + + return ret; +} + + /****************************** Notify packets ******************************/ /* Sends notify packet. Each variable argument format in the argument list diff --git a/lib/silcserver/server_st_accept.c b/lib/silcserver/server_st_accept.c index b6e63884..07a83ecf 100644 --- a/lib/silcserver/server_st_accept.c +++ b/lib/silcserver/server_st_accept.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2005 Pekka Riikonen + Copyright (C) 1997 - 2006 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 @@ -66,7 +66,7 @@ silc_server_accept_completed(SilcSKE ske, SilcSKEStatus status, ac->rekey = rekey; /* Continue synchronously to take keys into use immediately */ - SILC_FSM_CALL_CONTINUE_SYNC(ac->t); + SILC_FSM_CALL_CONTINUE_SYNC(&ac->t); } /* Authentication data callback */ @@ -129,7 +129,7 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success, { SilcServerAccept ac = context; ac->auth_success = success; - SILC_FSM_CALL_CONTINUE(ac->t); + SILC_FSM_CALL_CONTINUE(&ac->t); } /* Free context */ @@ -146,7 +146,6 @@ void silc_server_accept_connection_dest(SilcFSM fsm, void *fsm_context, void *destructor_context) { SilcServerAccept ac = fsm_context; - silc_fsm_free(fsm); silc_server_accept_free(ac); } diff --git a/lib/silcserver/server_st_command.c b/lib/silcserver/server_st_command.c index 70f5e2d0..62779e08 100644 --- a/lib/silcserver/server_st_command.c +++ b/lib/silcserver/server_st_command.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2005 Pekka Riikonen + Copyright (C) 1997 - 2006 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 @@ -23,31 +23,31 @@ /************************** Types and definitions ***************************/ -#define SILC_SERVER_COMMAND_CHECK(min, max) \ -do { \ - SilcUInt32 _argc; \ - \ - SILC_LOG_DEBUG(("Start")); \ - \ - _argc = silc_argument_get_arg_num(args); \ - if (_argc < min) { \ - SILC_LOG_DEBUG(("Not enough parameters in command")); \ - silc_server_command_send_status_reply(cmd, \ - silc_command_get(cmd->payload), \ - SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, \ - 0); \ - silc_server_command_free(cmd); \ - return SILC_FSM_FINISH; \ - } \ - if (_argc > max) { \ - SILC_LOG_DEBUG(("Too many parameters in command")); \ - silc_server_command_send_status_reply(cmd, \ - silc_command_get(cmd->payload), \ - SILC_STATUS_ERR_TOO_MANY_PARAMS, \ - 0); \ - silc_server_command_free(cmd); \ - return SILC_FSM_FINISH; \ - } \ +#define SILC_SERVER_COMMAND_CHECK(min, max) \ +do { \ + SilcUInt32 _argc; \ + \ + SILC_LOG_DEBUG(("Start")); \ + \ + _argc = silc_argument_get_arg_num(args); \ + if (_argc < min) { \ + SILC_LOG_DEBUG(("Not enough parameters in command")); \ + silc_server_command_status_reply(cmd, \ + silc_command_get(cmd->payload), \ + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, \ + 0); \ + silc_server_command_free(cmd); \ + return SILC_FSM_FINISH; \ + } \ + if (_argc > max) { \ + SILC_LOG_DEBUG(("Too many parameters in command")); \ + silc_server_command_status_reply(cmd, \ + silc_command_get(cmd->payload), \ + SILC_STATUS_ERR_TOO_MANY_PARAMS, \ + 0); \ + silc_server_command_free(cmd); \ + return SILC_FSM_FINISH; \ + } \ } while(0) @@ -56,7 +56,7 @@ do { \ /* Sends simple status message as command reply packet */ static void -silc_server_command_send_status_reply(SilcServerCommand cmd, +silc_server_command_status_reply(SilcServerCommand cmd, SilcCommand command, SilcStatus status, SilcStatus error) @@ -80,7 +80,7 @@ silc_server_command_send_status_reply(SilcServerCommand cmd, type must be sent as argument. */ static void -silc_server_command_send_status_data(SilcServerCommand cmd, +silc_server_command_status_data(SilcServerCommand cmd, SilcCommand command, SilcStatus status, SilcStatus error, @@ -105,7 +105,7 @@ silc_server_command_send_status_data(SilcServerCommand cmd, } static void -silc_server_command_send_status_data2(SilcServerCommand cmd, +silc_server_command_status_data2(SilcServerCommand cmd, SilcCommand command, SilcStatus status, SilcStatus error, @@ -355,6 +355,8 @@ SILC_FSM_STATE(silc_server_st_packet_command) else client->fast_command -= 2; } + + client->last_command = time(NULL) + timeout; } silc_fsm_set_state_context(fsm, cmd); @@ -514,6 +516,53 @@ SILC_FSM_STATE(silc_server_st_packet_command) return timeout ? SILC_FSM_WAIT : SILC_FSM_CONTINUE; } +/********************************* WHOIS ************************************/ + +SILC_FSM_STATE(silc_server_st_command_whois) +{ + SilcServerCommand cmd = state_context; + SilcArgumentPayload args = silc_command_get_args(cmd->payload); + + SILC_SERVER_COMMAND_CHECK(1, 256); + + /** WHOIS query */ + silc_fsm_next(fsm, silc_server_st_query_whois); + + return SILC_FSM_CONTINUE; +} + + +/********************************* WHOWAS ***********************************/ + +SILC_FSM_STATE(silc_server_st_command_whowas) +{ + SilcServerCommand cmd = state_context; + SilcArgumentPayload args = silc_command_get_args(cmd->payload); + + SILC_SERVER_COMMAND_CHECK(1, 2); + + /** WHOWAS query */ + silc_fsm_next(fsm, silc_server_st_query_whowas); + + return SILC_FSM_CONTINUE; +} + + +/******************************** IDENTIFY **********************************/ + +SILC_FSM_STATE(silc_server_st_command_identify) +{ + SilcServerCommand cmd = state_context; + SilcArgumentPayload args = silc_command_get_args(cmd->payload); + + SILC_SERVER_COMMAND_CHECK(1, 256); + + /** IDENTIFY query */ + silc_fsm_next(fsm, silc_server_st_query_identify); + + return SILC_FSM_CONTINUE; +} + /********************************** NICK ************************************/ @@ -522,7 +571,99 @@ SILC_FSM_STATE(silc_server_st_command_nick) SilcServerThread thread = fsm_context; SilcServerCommand cmd = state_context; SilcArgumentPayload args = silc_command_get_args(cmd->payload); + SilcClientEntry client = silc_packet_get_context(cmd->packet->stream); + SilcBuffer nidp, oidp = NULL; + SilcClientID new_id; + SilcUInt32 nick_len; + unsigned char *nick, *nickc; + SilcUInt16 ident = silc_command_get_ident(cmd->payload); + SILC_SERVER_COMMAND_CHECK(1, 1); + + /* This command can come only from client */ + if (!SILC_IS_CLIENT(client)) { + silc_server_command_status_reply(cmd, SILC_COMMAND_NICK, + SILC_STATUS_ERR_OPERATION_ALLOWED, 0); + goto out; + } + + /* Get nickname */ + nick = silc_argument_get_arg_type(args, 1, &nick_len); + if (!nick) { + silc_server_command_status_reply(cmd, SILC_COMMAND_NICK, + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0); + goto out; + } + + /* Truncate over long nicks */ + if (nick_len > 128) { + nick_len = 128; + nick[nick_len - 1] = '\0'; + } + + /* Check for same nickname */ + if (strlen(client->nickname) == nick_len && + !memcmp(client->nickname, nick, nick_len)) { + nidp = silc_id_payload_encode(&client->id, SILC_ID_CLIENT); + goto send_reply; + } + + /* Check for valid nickname string. */ + nickc = silc_identifier_check(nick, nick_len, SILC_STRING_UTF8, 128, NULL); + if (!nickc) { + silc_server_command_status_reply(cmd, SILC_COMMAND_NICK, + SILC_STATUS_ERR_BAD_NICKNAME, 0); + goto out; + } + + /* Create new Client ID */ + if (!silc_server_create_client_id(thread->server, nickc, &new_id)) { + silc_server_command_status_reply(cmd, SILC_COMMAND_NICK, + SILC_STATUS_ERR_OPERATION_ALLOWED, 0); + goto out; + } + silc_free(nickc); + + oidp = silc_id_payload_encode(&client->id, SILC_ID_CLIENT); + + /* Replace the old nickname and ID with new ones. This checks for + validity of the nickname too. */ + if (!silc_server_replace_client_id(thread->server, &client->id, &new_id, + nick)) { + silc_server_command_status_reply(cmd, SILC_COMMAND_NICK, + SILC_STATUS_ERR_BAD_NICKNAME, 0); + goto out; + } + + nidp = silc_id_payload_encode(&client->id, SILC_ID_CLIENT); + +#if 0 + /* Send notify about nickname and ID change to network. */ + silc_server_send_notify_nick_change(server, SILC_PRIMARY_ROUTE(server), + SILC_BROADCAST(server), client->id, + &new_id, nick); + + /* Send NICK_CHANGE notify to the client's channels */ + silc_server_send_notify_on_channels(server, NULL, client, + SILC_NOTIFY_TYPE_NICK_CHANGE, 3, + oidp->data, silc_buffer_len(oidp), + nidp->data, silc_buffer_len(nidp), + client->nickname, + strlen(client->nickname)); +#endif + + send_reply: + /* Send the new Client ID as reply command back to client */ + silc_server_send_command_reply(thread->server, cmd->packet->stream, + SILC_COMMAND_NICK, + SILC_STATUS_OK, 0, ident, 2, + 2, nidp->data, silc_buffer_len(nidp), + 3, nick, nick_len); + silc_buffer_free(nidp); + silc_buffer_free(oidp); + + out: + silc_server_command_free(cmd); return SILC_FSM_FINISH; } @@ -589,10 +730,6 @@ SILC_FSM_STATE(silc_server_st_command_kill) /********************************** INFO ************************************/ -/* Server side of command INFO. This sends information about us to - the client. If client requested specific server we will send the - command to that server. */ - SILC_FSM_STATE(silc_server_st_command_info) { return SILC_FSM_FINISH; @@ -613,8 +750,6 @@ SILC_FSM_STATE(silc_server_st_command_stats) /********************************** PING ************************************/ -/* Server side of command PING. */ - SILC_FSM_STATE(silc_server_st_command_ping) { SilcServerThread thread = fsm_context; @@ -622,36 +757,36 @@ SILC_FSM_STATE(silc_server_st_command_ping) SilcArgumentPayload args = silc_command_get_args(cmd->payload); SilcUInt32 tmp_len; unsigned char *tmp; - SilcServerID server_id; + SilcID id; SILC_SERVER_COMMAND_CHECK(1, 1); /* Get Server ID */ tmp = silc_argument_get_arg_type(args, 1, &tmp_len); if (!tmp) { - silc_server_command_send_status_reply(cmd, silc_command_get(cmd->payload), - SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, - 0); + silc_server_command_status_reply(cmd, silc_command_get(cmd->payload), + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0); goto out; } - if (!silc_id_payload_parse_id(tmp, tmp_len, NULL, &server_id, - sizeof(server_id))) { - silc_server_command_send_status_data(cmd, silc_command_get(cmd->payload), - SILC_STATUS_ERR_BAD_SERVER_ID, 0, - 2, tmp, tmp_len); + if (!silc_id_payload_parse_id(tmp, tmp_len, &id)) { + silc_server_command_status_data(cmd, silc_command_get(cmd->payload), + SILC_STATUS_ERR_BAD_SERVER_ID, 0, + 2, tmp, tmp_len); goto out; } - if (SILC_ID_SERVER_COMPARE(&server_id, &thread->server->id)) { - /* Send our reply */ - silc_server_command_send_status_reply(cmd, silc_command_get(cmd->payload), - SILC_STATUS_OK, 0); - } else { - silc_server_command_send_status_data(cmd, silc_command_get(cmd->payload), - SILC_STATUS_ERR_NO_SUCH_SERVER_ID, 0, - 2, tmp, tmp_len); + /* Must be our ID */ + if (!SILC_ID_SERVER_COMPARE(&id.u.server_id, &thread->server->id)) { + silc_server_command_status_data(cmd, silc_command_get(cmd->payload), + SILC_STATUS_ERR_NO_SUCH_SERVER_ID, 0, + 2, tmp, tmp_len); + goto out; } + /* Send our reply */ + silc_server_command_status_reply(cmd, silc_command_get(cmd->payload), + SILC_STATUS_OK, 0); + out: silc_server_command_free(cmd); return SILC_FSM_FINISH; diff --git a/lib/silcserver/server_st_command.h b/lib/silcserver/server_st_command.h index 89201f79..0bb03eff 100644 --- a/lib/silcserver/server_st_command.h +++ b/lib/silcserver/server_st_command.h @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 2005 Pekka Riikonen + Copyright (C) 2005, 2006 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 @@ -23,6 +23,9 @@ /***************************** State functions ******************************/ SILC_FSM_STATE(silc_server_st_packet_command); +SILC_FSM_STATE(silc_server_st_command_whois); +SILC_FSM_STATE(silc_server_st_command_whowas); +SILC_FSM_STATE(silc_server_st_command_identify); SILC_FSM_STATE(silc_server_st_command_nick); SILC_FSM_STATE(silc_server_st_command_list); SILC_FSM_STATE(silc_server_st_command_topic); diff --git a/lib/silcserver/server_st_packet.c b/lib/silcserver/server_st_packet.c index 765e7126..e8676ca8 100644 --- a/lib/silcserver/server_st_packet.c +++ b/lib/silcserver/server_st_packet.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2005 Pekka Riikonen + Copyright (C) 1997 - 2006 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 diff --git a/lib/silcserver/server_st_query.c b/lib/silcserver/server_st_query.c index 2737b902..c940ba04 100644 --- a/lib/silcserver/server_st_query.c +++ b/lib/silcserver/server_st_query.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 2005 Pekka Riikonen + Copyright (C) 2002 - 2006 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 @@ -21,19 +21,1056 @@ #include "silcserver.h" #include "server_internal.h" -SILC_FSM_STATE(silc_server_st_command_whois) +/************************** Types and definitions ***************************/ + +/* Resolving entry */ +typedef struct SilcServerQueryResolveStruct { + struct SilcServerQueryResolveStruct *next; + SilcFSMThreadStruct thread; /* FSM thread for waiting reply */ + SilcServerPending pending; /* Pending command context */ + SilcPacketStream stream; /* Resolving connection */ + SilcID *ids; /* Resolved IDs */ + unsigned int ids_count : 30; /* Number of resolved IDs */ + unsigned int attached : 1; /* Set if attached to a resolving */ + unsigned int local : 1; /* Set if client is local to us */ +} *SilcServerQueryResolve; + +/* Represents one error occurred during query */ +typedef struct { + SilcID id; /* ID */ + unsigned int index : 15; /* Index to IDs */ + unsigned int type : 2; /* 0 = take from query->ids, 1 = take + from args, 2 = no args in error. */ + unsigned int error : 7; /* The actual error (SilcStatus) */ +} *SilcServerQueryError; + +/* Query session context */ +typedef struct { + /* Queried data */ + char *nickname; /* Queried nickname, normalized */ + char *nick_server; /* Queried nickname's server */ + char *server_name; /* Queried server name, normalized */ + char *channel_name; /* Queried channel name, normalized */ + SilcID *ids; /* Queried IDs */ + SilcUInt32 ids_count; /* number of queried IDs */ + SilcUInt32 reply_count; /* Requested reply count */ + SilcDList attrs; /* Requested Attributes in WHOIS */ + SilcFSMSemaStruct wait_resolve; /* Resolving signaller */ + + /* Query session data */ + SilcServerComman cmd; /* Command context for query */ + SilcList clients; /* Found clients */ + SilcList servers; /* Found servers */ + SilcList channels; /* Found channels */ + SilcList resolve; /* Clients to resolve */ + SilcList resolvings; /* Ongoing resolvings */ + SilcServerQueryError errors; /* Query errors */ + SilcServerPending redirect; /* Pending redirect */ + SilcUInt16 errors_count; /* number of errors */ + SilcUInt8 resolve_retry; /* Resolving retry count */ + SilcCommand querycmd; /* Query command */ +} *SilcServerQuery; + + +/************************ Static utility functions **************************/ + + +/********************************* WHOIS ************************************/ + +SILC_FSM_STATE(silc_server_st_query_whois) { + SilcServerThread thread = fsm_context; + SilcServer server = thread->server; + SilcServerCommand cmd = state_context; + SilcArgumentPayload args = silc_command_get_args(cmd->payload); + SilcServerQuery query; - return SILC_FSM_FINISH; + SILC_LOG_DEBUG(("WHOIS query")); + + query = silc_calloc(1, sizeof(*query)); + if (!query) { + silc_server_command_free(cmd); + return SILC_FSM_FINISH; + } + + query->querycmd = SILC_COMMAND_WHOIS; + query->cmd = cmd; + + silc_fsm_set_state_context(fsm, query); + + /* If we are normal server and query contains a nickname OR query + doesn't contain nickname or ids BUT does contain user attributes, + send it to the router */ + if (server->server_type != SILC_ROUTER && !server->standalone && + cmd->packet->stream != SILC_PRIMARY_ROUTE(server) && + (silc_argument_get_arg_type(args, 1, NULL) || + (!silc_argument_get_arg_type(args, 1, NULL) && + !silc_argument_get_arg_type(args, 4, NULL) && + silc_argument_get_arg_type(args, 3, NULL)))) { + /** Send query to router */ + silc_fsm_next(fsm, silc_server_st_query_send_router); + return SILC_FSM_CONTINUE; + } + + /** Parse WHOIS query */ + silc_fsm_next(fsm, silc_server_st_query_parse); + return SILC_FSM_CONTINUE; } -SILC_FSM_STATE(silc_server_st_command_whowas) + +/********************************* WHOWAS ***********************************/ + +SILC_FSM_STATE(silc_server_st_query_whowas) { + SilcServerThread thread = fsm_context; + SilcServerCommand cmd = state_context; - return SILC_FSM_FINISH; + SILC_LOG_DEBUG(("WHOWAS query")); + + query = silc_calloc(1, sizeof(*query)); + if (!query) { + silc_server_command_free(cmd); + return SILC_FSM_FINISH; + } + + query->querycmd = SILC_COMMAND_WHOWAS; + query->cmd = cmd; + + silc_fsm_set_state_context(fsm, query); + + /* WHOWAS query is always sent to router if we are normal server */ + if (server->server_type == SILC_SERVER && !server->standalone && + cmd->packet->stream != SILC_PRIMARY_ROUTE(server)) { + /** Send query to router */ + silc_fsm_next(fsm, silc_server_st_query_send_router); + return SILC_FSM_CONTINUE; + } + + /** Parse WHOWAS query */ + silc_fsm_next(fsm, silc_server_st_query_parse); + return SILC_FSM_CONTINUE; +} + + +/******************************** IDENTIFY **********************************/ + +SILC_FSM_STATE(silc_server_st_query_identify) +{ + SilcServerThread thread = fsm_context; + SilcServerCommand cmd = state_context; + SilcArgumentPayload args = silc_command_get_args(cmd->payload); + + SILC_LOG_DEBUG(("IDENTIFY query")); + + query = silc_calloc(1, sizeof(*query)); + if (!query) { + silc_server_command_free(cmd); + return SILC_FSM_FINISH; + } + + query->querycmd = SILC_COMMAND_IDENTIFY; + query->cmd = cmd; + + silc_fsm_set_state_context(fsm, query); + + /* If we are normal server and query does not contain IDs, send it directly + to router (it contains nickname, server name or channel name). */ + if (server->server_type == SILC_SERVER && !server->standalone && + cmd->packet->stream != SILC_PRIMARY_ROUTE(server) && + !silc_argument_get_arg_type(args, 5, NULL)) { + /** Send query to router */ + silc_fsm_next(fsm, silc_server_st_query_send_router); + return SILC_FSM_CONTINUE; + } + + /** Parse IDENTIFY query */ + silc_fsm_next(fsm, silc_server_st_query_parse); + return SILC_FSM_CONTINUE; +} + + +/**************************** Query redirecting *****************************/ + +/* Send the query to router for further processing */ + +SILC_FSM_STATE(silc_server_st_query_send_router) +{ + SilcServerThread thread = fsm_context; + SilcServer server = thread->server; + SilcServerQuery query = state_context; + SilcBuffer tmpbuf; + SilcUInt16 cmd_ident, old_ident; + + SILC_LOG_DEBUG(("Redirecting query to router")); + + /* Send the command to our router */ + cmd_ident = silc_server_cmd_ident(server); + old_ident = silc_command_get_ident(query->cmd->payload); + silc_command_set_ident(query->cmd->payload, cmd_ident); + + tmpbuf = silc_command_payload_encode_payload(query->cmd->payload); + if (!tmpbuf || !silc_packet_send(SILC_PRIMARY_ROUTE(server), + SILC_PACKET_COMMAND, 0, + tmpbuf->data, silc_buffer_len(tmpbuf))) { + /** Error sending packet */ + silc_server_query_send_error(server, query, + SILC_STATUS_ERR_RESOURCE_LIMIT, 0); + silc_fsm_next(fsm, silc_server_st_query_error); + return SILC_FSM_CONTINUE; + } + + silc_command_set_ident(query->cmd->payload, old_ident); + silc_buffer_free(tmpbuf); + + /* Statistics */ + server->stat.commands_sent++; + + /* Continue parsing the query after receiving reply from router */ + query->redirect = silc_server_command_pending(thread, query->redirect_ident); + if (!query->redirect) { + /** No memory */ + silc_server_query_send_error(server, query, + SILC_STATUS_ERR_RESOURCE_LIMIT, 0); + silc_fsm_next(fsm, silc_server_st_query_error); + return SILC_FSM_CONTINUE; + } + + /** Wait router reply */ + query->resolved = TRUE; + silc_fsm_next(fsm, silc_server_st_query_router_reply) + return SILC_FSM_CONTINUE; +} + +/* Wait for router reply and process the reply when it arrives. */ + +SILC_FSM_STATE(silc_server_st_query_router_reply) +{ + SilcServerThread thread = fsm_context; + SilcServer server = thread->server; + SilcServerQuery query = state_context; + SilcServerPending pending = query->redirect; + SilcBool timedout; + + /* Wait here for the reply */ + SILC_FSM_SEMA_TIMEDWAIT(&pending->wait_reply, 10, 0, &timedout); + + if (timedout) { + /** Timeout waiting reply */ + silc_server_command_pending_free(thread, pending); + silc_server_query_send_error(server, query, SILC_STATUS_ERR_TIMEDOUT, 0); + silc_fsm_next(fsm, silc_server_st_query_error); + return SILC_FSM_CONTINUE; + } + + /* Check if the query failed */ + if (!silc_command_get_status(pending->reply->payload, NULL, NULL)) { + SilcBuffer buffer; + + SILC_LOG_DEBUG(("Sending error to original query")); + + /* Send the same command reply payload which contains the error */ + silc_command_set_command(pending->reply->payload, query->querycmd); + silc_command_set_ident(pending->reply->payload, + silc_command_get_ident(query->cmd->payload)); + buffer = silc_command_payload_encode_payload(pending->reply->payload); + if (buffer) + silc_packet_send(query->cmd->packet->stream, + SILC_PACKET_COMMAND_REPLY, 0, + buffer->data, silc_buffer_len(buffer)); + silc_buffer_free(buffer); + + /* Statistics */ + server->stat.commands_sent++; + + /** Query error received */ + silc_server_command_pending_free(thread, pending); + silc_fsm_next(fsm, silc_server_st_query_error); + return SILC_FSM_CONTINUE; + } + + silc_server_command_pending_free(thread, pending); + + /** Parse query command */ + silc_fsm_next(fsm, silc_server_st_query_parse); + return SILC_FSM_CONTINUE; +} + +/***************************** Query processing *****************************/ + +/* Parse the command query */ + +SILC_FSM_STATE(silc_server_st_query_parse) +{ + SilcServerThread thread = fsm_context; + SilcServerQuery query = state_context; + SilcServerCommand cmd = query->cmd; + SilcArgumentPayload args = silc_command_get_args(cmd->payload); + SilcUInt32 tmp_len, argc = silc_argument_get_arg_num(args); + unsigned char *tmp; + SilcID id; + int i; + + SILC_LOG_DEBUG(("Parsing %s query", + silc_get_command_name(query->querycmd))); + + switch (query->querycmd) { + + case SILC_COMMAND_WHOIS: + /* Get requested attributes if set */ + tmp = silc_argument_get_arg_type(args, 3, &tmp_len); + if (tmp && !query->attrs && tmp_len <= SILC_ATTRIBUTE_MAX_REQUEST_LEN) + query->attrs = silc_attribute_payload_parse(tmp, tmp_len); + + /* Get Client IDs if present. Take IDs always instead of nickname. */ + tmp = silc_argument_get_arg_type(args, 4, &tmp_len); + if (!tmp) { + /* No IDs present */ + + /* Get nickname */ + tmp = silc_argument_get_arg_type(args, 1, &tmp_len); + if (!tmp && !query->attrs) { + /* No nickname, no ids and no attributes - send error */ + silc_server_query_send_error(server, query, + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0); + + /** Not enough arguments */ + silc_fsm_next(fsm, silc_server_st_query_error); + return SILC_FSM_CONTINUE; + } + + /* Get the nickname@server string and parse it */ + if (tmp && ((tmp_len > 128) || + !silc_parse_userfqdn(tmp, &query->nickname, + &query->nick_server))) { + /** Bad nickname */ + silc_server_query_send_error(server, query, + SILC_STATUS_ERR_BAD_NICKNAME, 0); + silc_fsm_next(fsm, silc_server_st_query_error); + return SILC_FSM_CONTINUE; + } + + /* Check nickname */ + if (tmp) { + tmp = silc_identifier_check(query->nickname, strlen(query->nickname), + SILC_STRING_UTF8, 128, &tmp_len); + if (!tmp) { + /** Bad nickname */ + silc_server_query_send_error(server, query, + SILC_STATUS_ERR_BAD_NICKNAME, 0); + silc_fsm_next(fsm, silc_server_st_query_error); + return SILC_FSM_CONTINUE; + } + /* XXX why free nickname */ + silc_free(query->nickname); + query->nickname = tmp; + } + + } else { + /* Parse the IDs included in the query */ + query->ids = silc_calloc(argc - 3, sizeof(*query->ids)); + if (!query->ids) { + /** No memory */ + silc_server_query_send_error(server, query, + SILC_STATUS_ERR_RESOURCE_LIMIT, 0); + silc_fsm_next(fsm, silc_server_st_query_error); + return SILC_FSM_CONTINUE; + } + + for (i = 0; i < argc - 3; i++) { + tmp = silc_argument_get_arg_type(args, i + 4, &tmp_len); + if (!tmp) + continue; + + if (!silc_id_payload_parse_id(tmp, tmp_len, &id) || + id.type != SILC_ID_CLIENT) { + silc_server_query_add_error(server, query, 1, i + 4, + SILC_STATUS_ERR_BAD_CLIENT_ID); + continue; + } + + /* 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 && + !silc_server_find_client_by_id(server, &client_id, TRUE, NULL)) { + /** Send query to router */ + silc_free(query->ids); + query->ids = NULL; + query->ids_count = 0; + silc_fsm_next(fsm, silc_server_st_query_send_router); + return SILC_FSM_CONTINUE; + } + + query->ids[query->ids_count] = id; + query->ids_count++; + } + } + + /* Get the max count of reply messages allowed */ + tmp = silc_argument_get_arg_type(args, 2, &tmp_len); + if (tmp && tmp_len == sizeof(SilcUInt32)) + SILC_GET32_MSB(query->reply_count, tmp); + break + + case SILC_COMMAND_WHOWAS: + /* Get nickname */ + tmp = silc_argument_get_arg_type(args, 1, &tmp_len); + if (!tmp) { + /** Not enough arguments */ + silc_server_query_send_error(server, query, + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0); + silc_fsm_next(fsm, silc_server_st_query_error); + return SILC_FSM_CONTINUE; + } + + /* Get the nickname@server string and parse it */ + if (tmp_len > 128 || + !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) { + /** Bad nickname */ + silc_server_query_send_error(server, query, + SILC_STATUS_ERR_BAD_NICKNAME, 0); + silc_fsm_next(fsm, silc_server_st_query_error); + return SILC_FSM_CONTINUE; + } + + /* Check nickname */ + tmp = silc_identifier_check(query->nickname, strlen(query->nickname), + SILC_STRING_UTF8, 128, &tmp_len); + if (!tmp) { + /** Bad nickname */ + silc_server_query_send_error(server, query, + SILC_STATUS_ERR_BAD_NICKNAME, 0); + silc_fsm_next(fsm, silc_server_st_query_error); + return SILC_FSM_CONTINUE; + } + /* XXX why free nickname */ + silc_free(query->nickname); + query->nickname = tmp; + + /* Get the max count of reply messages allowed */ + tmp = silc_argument_get_arg_type(args, 2, &tmp_len); + if (tmp && tmp_len == sizeof(SilcUInt32)) + SILC_GET32_MSB(query->reply_count, tmp); + break; + + case SILC_COMMAND_IDENTIFY: + /* Get IDs if present. Take IDs always instead of names. */ + tmp = silc_argument_get_arg_type(args, 5, &tmp_len); + if (!tmp) { + /* No IDs present */ + + /* Try get nickname */ + tmp = silc_argument_get_arg_type(args, 1, &tmp_len); + if (tmp) { + /* Get the nickname@server string and parse it */ + if (tmp_len > 128 || + !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) + silc_server_query_add_error(server, query, 1, 1, + SILC_STATUS_ERR_BAD_NICKNAME); + + /* Check nickname */ + tmp = silc_identifier_check(query->nickname, strlen(query->nickname), + SILC_STRING_UTF8, 128, &tmp_len); + if (!tmp) { + /** Bad nickname */ + silc_server_query_send_error(server, query, + SILC_STATUS_ERR_BAD_NICKNAME, 0); + silc_fsm_next(fsm, silc_server_st_query_error); + return SILC_FSM_CONTINUE; + } + /* XXX why free nickname */ + silc_free(query->nickname); + query->nickname = tmp; + } + + /* Try get server name */ + tmp = silc_argument_get_arg_type(args, 2, &tmp_len); + if (tmp) { + /* Check server name */ + tmp = silc_identifier_check(tmp, tmp_len, SILC_STRING_UTF8, + 256, &tmp_len); + if (!tmp) { + /** Bad server name */ + silc_server_query_send_error(server, query, + SILC_STATUS_ERR_BAD_SERVER, 0); + silc_fsm_next(fsm, silc_server_st_query_error); + return SILC_FSM_CONTINUE; + } + query->server_name = tmp; + } + + /* Get channel name */ + tmp = silc_argument_get_arg_type(args, 3, &tmp_len); + if (tmp && tmp_len <= 256) { + /* Check channel name */ + tmp = silc_identifier_check(tmp, tmp_len, SILC_STRING_UTF8, + 256, &tmp_len); + if (!tmp) { + /** Bad channel name */ + silc_server_query_send_error(server, query, + SILC_STATUS_ERR_BAD_CHANNEL, 0); + silc_fsm_next(fsm, silc_server_st_query_error); + return SILC_FSM_CONTINUE; + } + query->channel_name = tmp; + } + + if (!query->nickname && !query->server_name && !query->channel_name) { + /** Nothing was queried */ + silc_server_query_send_error(server, query, + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0); + silc_fsm_next(fsm, silc_server_st_query_error); + return SILC_FSM_CONTINUE; + } + + } else { + /* Parse the IDs included in the query */ + query->ids = silc_calloc(argc - 4, sizeof(*query->ids)); + + for (i = 0; i < argc - 4; i++) { + tmp = silc_argument_get_arg_type(args, i + 5, &tmp_len); + if (!tmp) + continue; + + if (!silc_id_payload_parse_id(tmp, tmp_len, &id)) { + silc_server_query_add_error(server, query, 1, i + 5, + SILC_STATUS_ERR_BAD_CLIENT_ID); + continue; + } + + /* 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 (id.type == SILC_ID_CLIENT) { + if (!silc_server_find_client_by_id(server, id, TRUE, NULL)) { + /** Send query to router */ + silc_free(query->ids); + query->ids = NULL; + query->ids_count = 0; + silc_fsm_next(fsm, silc_server_st_query_send_router); + return SILC_FSM_CONTINUE; + } + } else { + /* For now all other ID's except Client ID's are explicitly + sent to router for resolving. */ + + /** Send query to router */ + silc_free(query->ids); + query->ids = NULL; + query->ids_count = 0; + silc_fsm_next(fsm, silc_server_st_query_send_router); + return SILC_FSM_CONTINUE; + } + } + + query->ids[query->ids_count] = id; + query->ids_count++; + } + } + + /* Get the max count of reply messages allowed */ + tmp = silc_argument_get_arg_type(args, 4, &tmp_len); + if (tmp && tmp_len == sizeof(SilcUInt32)) + SILC_GET32_MSB(query->reply_count, tmp); + break; + } + + /** Find entries for query */ + silc_fsm_next(fsm, silc_server_st_query_find); + return SILC_FSM_CONTINUE; +} + +/* Find the entries according to the query */ + +SILC_FSM_STATE(silc_server_st_query_find) +{ + SilcServerThread thread = fsm_context; + SilcServer server = thread->server; + SilcServerQuery query = state_context; + SilcServerCommand cmd = query->cmd; + SilcIDCacheEntry id_entry; + SilcID *id; + void *entry; + int i; + + SILC_LOG_DEBUG(("Finding entries with %s query", + silc_get_command_name(query->querycmd))); + + if (query->nickname) { + /* Find by nickname */ + if (!silc_server_find_clients(server, query->nickname, &query->clients)) + silc_server_query_add_error(server, query, 1, 1, + SILC_STATUS_ERR_NO_SUCH_NICK); + } + + if (query->server_name) { + /* Find server by name */ + if (!silc_server_find_server_by_name(server, query->server_name, TRUE, + &id_entry)) + silc_server_query_add_error(server, query, 1, 2, + SILC_STATUS_ERR_NO_SUCH_SERVER); + else + silc_list_add(query->servers, id_entry); + } + + if (query->channel_name) { + /* Find channel by name */ + if (!silc_server_find_channel_by_name(server, query->channel_name, + &id_entry)) + silc_server_query_add_error(server, query, 1, 3, + SILC_STATUS_ERR_NO_SUCH_CHANNEL); + else + silc_list_add(query->channels, id_entry); + } + + if (query->ids_count) { + /* Find entries by the queried IDs */ + for (i = 0; i < query->ids_count; i++) { + id = &query->ids[i]; + + switch (id->type) { + + case SILC_ID_CLIENT: + /* Get client entry */ + if (!silc_server_find_client_by_id(server, &id->u.client_id, TRUE, + &id_entry)) { + silc_server_query_add_error(server, query, 0, i, + SILC_STATUS_ERR_NO_SUCH_CLIENT_ID); + continue; + } + + silc_list_add(query->clients, id_entry); + break; + + case SILC_ID_SERVER: + /* Get server entry */ + if (!silc_server_find_server_by_id(server, &id->u.server_id, TRUE, + &id_entry)) { + silc_server_query_add_error(server, query, 0, i, + SILC_STATUS_ERR_NO_SUCH_SERVER_ID); + continue; + } + + silc_list_add(query->servers, id_entry); + break; + + case SILC_ID_CHANNEL: + /* Get channel entry */ + if (!silc_server_find_channel_by_id(server, &id->u.channel_id, + &id_entry)) { + silc_server_query_add_error(server, query, 0, i, + SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID); + continue; + } + + silc_list_add(query->channels, id_entry); + break; + + default: + break; + } + } + } + + /* Check the attributes to narrow down the search by using them. */ + if (query->attrs) { + /** Check user attributes */ + silc_fsm_next(fsm, silc_server_st_query_check_attrs); + return SILC_FSM_CONTINUE; + } + + /** Process found entries */ + silc_fsm_next(fsm, silc_server_st_query_process); + return SILC_FSM_CONTINUE; +} + +/* Check user attributes to narrow down clients in WHOIS query */ + +SILC_FSM_STATE(silc_server_st_query_check_attrs) +{ + + /** Proecss found entries */ + silc_fsm_next(fsm, silc_server_st_query_process); + return SILC_FSM_CONTINUE; +} + +/* Process found entries */ + +SILC_FSM_STATE(silc_server_st_query_process) +{ + SilcServerThread thread = fsm_context; + SilcServer server = thread->server; + SilcServerQuery query = state_context; + SilcServerCommand cmd = query->cmd; + SilcServerQueryResolve res; + SilcIDCacheEntry id_entry; + SilcClientEntry client_entry; + SilcServerEntry server_entry; + SilcChannelEntry channel_entry; + SilcID *id; + void *entry; + int i; + + SILC_LOG_DEBUG(("Process %s query", + silc_get_command_name(query->querycmd))); + + SILC_LOG_DEBUG(("Querying %d clients", silc_list_count(query->clients))); + SILC_LOG_DEBUG(("Querying %d servers", silc_list_count(query->servers))); + SILC_LOG_DEBUG(("Querying %d channels", silc_list_count(query->channels))); + + /* If nothing was found, then just send the errors */ + if (!silc_list_count(query->clients) && + !silc_list_count(query->channels) && + !silc_list_count(query->servers)) { + /** Nothing found, send errors */ + silc_fsm_next(fsm, silc_server_st_query_reply); + return SILC_FSM_CONTINUE; + } + +#if 0 + /* If caller does not want us to resolve anything (has resolved already) + then just continue with sending the reply */ + if (!resolve) { + silc_server_query_send_reply(server, query, clients, clients_count, + servers, servers_count, channels, + channels_count); + silc_free(clients); + silc_free(servers); + silc_free(channels); + return; + } +#endif + + /* Now process all found information and if necessary do some more + resolving. */ + switch (query->querycmd) { + + case SILC_COMMAND_WHOIS: + silc_list_start(query->clients); + while ((id_entry = silc_list_get(query->clients)) != SILC_LIST_END) { + client_entry = id_entry->context; + + /* Ignore unregistered clients */ + if (!SILC_IS_REGISTERED(client_entry)) { + silc_list_del(query->clients, id_entry); + continue; + } + + /* If Requested Attributes is set then we always resolve the client + information, if not then check whether the entry is complete or not + and decide whether we need to resolve the missing information. */ + if (!query->attrs) { + + /* Even if nickname and stuff are present, we may need to resolve + the entry on normal server. */ + if (client_entry->nickname && client_entry->username && + client_entry->userinfo) { + + /* If we are router, client is local to us, or client is on channel + we do not need to resolve the client information. */ + if (server->server_type != SILC_SERVER || + SILC_IS_LOCAL(client_entry)|| + silc_hash_table_count(client_entry->channels) || + query->resolved) + continue; + } + } + + /* Remove the NOATTR status periodically */ + if (client_entry->data.noattr && + client_entry->updated + 600 < time(NULL)) + client_entry->data.noattr = FALSE; + + /* 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. */ + if (query->attrs && SILC_IS_LOCAL(client_entry) && + (client_entry->mode & SILC_UMODE_DETACHED || + client_entry->data.noattr)) + continue; + +#if 0 + /* If attributes are present in query, and in the entry and we have + done resolvings already we don't need to resolve anymore */ + if (query->resolved && query->attrs && client_entry->attrs) + continue; +#endif + + /* Mark this entry to be resolved */ + silc_list_add(query->resolve, id_entry); + } + break; + + case SILC_COMMAND_WHOWAS: + silc_list_start(query->clients); + while ((id_entry = silc_list_get(query->clients)) != SILC_LIST_END) { + client_entry = id_entry->context; + + /* Take only unregistered clients */ + if (SILC_IS_REGISTERED(client_entry)) { + silc_list_del(query->clients, id_entry); + continue; + } + + /* If both nickname and username are present no resolving is needed */ + if (client_entry->nickname && client_entry->username) + continue; + + /* Mark this entry to be resolved */ + silc_list_add(query->resolve, id_entry); + } + break; + + case SILC_COMMAND_IDENTIFY: + silc_list_start(query->clients); + while ((id_entry = silc_list_get(query->clients)) != SILC_LIST_END) { + client_entry = id_entry->context; + + /* Ignore unregistered clients */ + if (!SILC_IS_REGISTERED(client_entry)) + continue; + + /* Even if nickname is present, we may need to resolve the entry + on normal server. */ + if (client_entry->nickname) { + + /* If we are router, client is local to us, or client is on channel + we do not need to resolve the client information. */ + if (server->server_type != SILC_SERVER || + SILC_IS_LOCAL(client_entry)|| + silc_hash_table_count(client_entry->channels) || + query->resolved) + continue; + } + + /* Mark this entry to be resolved */ + silc_list_add(query->resolve, id_entry); + } + break; + } + + /* If we need to resolve entries, do it now */ + if (silc_list_count(query->resolve)) { + /** Resolve entries */ + silc_fsm_next(fsm, silc_server_st_query_resolve); + return SILC_FSM_CONTINUE; + } + + /** Send reply to query */ + silc_fsm_next(fsm, silc_server_st_query_reply); + return SILC_FSM_CONTINUE; } -SILC_FSM_STATE(silc_server_st_command_identify) +/* Resolve incomplete client entries. Other types of entries need not + resolving. */ + +SILC_FSM_STATE(silc_server_st_query_resolve) +{ + SilcServerThread thread = fsm_context; + SilcServer server = thread->server; + SilcServerQuery query = state_context; + SilcArgumentPayload cmd_args = silc_command_get_args(query->cmd->payload); + SilcServerQueryResolve res; + SilcIDCacheEntry id_entry; + unsigned char args[256][28]; + SilcUInt32 arg_lens[256], arg_types[256], argc = 0; + SilcBuffer res_cmd; + int i; + + SILC_LOG_DEBUG(("Resolve incomplete entries")); + + silc_list_start(query->resolve); + while ((id_entry = silc_list_get(query->resolve)) != SILC_LIST_END) { + client_entry = id_entry->context; + + /* If entry is being resolved, attach to that resolving */ + if (client_entry->data.resolving) { + res = silc_calloc(1, sizeof(*res)); + if (!res) + continue; + + silc_fsm_thread_init(&res->thread, fsm, res, NULL, NULL, FALSE); + res->stream = client_entry->stream; + + res->pending = + silc_server_command_pending(thread, client_entry->resolve_cmd_ident); + if (!res->pending) { + SILC_LOG_ERROR(("BUG: No pending command for resolving client entry")); + continue; + } + + res->attached = TRUE; + silc_list_add(query->resolvings, res); + continue; + } + + /* Check if we have resolving destination already set */ + silc_list_start(query->resolvings); + while ((res = silc_list_get(query->resolvings)) != SILC_LIST_END) + if (res->stream == client_entry->stream && !res->attached) + break; + + if (!res) { + /* Create new resolving context */ + res = silc_calloc(1, sizeof(*res)); + if (!res) + continue; + + silc_fsm_thread_init(&res->thread, fsm, res, NULL, NULL, FALSE); + res->stream = client_entry->stream; + + res->pending = + silc_server_command_pending(thread, silc_server_cmd_ident(server)); + if (!res->pending) + continue; + + silc_list_add(query->resolvings, res); + } + + /* Mark the entry as being resolved */ + client_entry->data.resolving = TRUE; + client_entry->data.resolved = FALSE; + client_entry->resolve_cmd_ident = res->pending->cmd_ident; + client_entry->updated = time(NULL); + + if (SILC_IS_LOCAL(client_entry)) + res->local = TRUE; + + switch (query->querycmd) { + case SILC_COMMAND_WHOIS: + case SILC_COMMAND_IDENTIFY: + res->ids = silc_realloc(res->ids, sizeof(*res->ids) * + (res->ids_count + 1)); + if (!res->ids) + continue; + + res->ids[res->ids_count++].u.client_id = client_entry->id; + break; + + case SILC_COMMAND_WHOWAS: + break; + } + } + + SILC_LOG_DEBUG(("Sending the resolvings")); + + /* Send the resolvings */ + silc_list_start(query->resolvings); + while ((res = silc_list_get(query->resolvings)) != SILC_LIST_END) { + + if (!res->attached) { + + switch (query->querycmd) { + case SILC_COMMAND_WHOIS: + case SILC_COMMAND_IDENTIFY: + + /* If Requested Attributes were present put them to this resolving */ + if (query->attrs && query->querycmd == SILC_COMMAND_WHOIS) { + arg_types[argc] = 3; + args[argc] = silc_argument_get_arg_type(cmd_args, 3, + &arg_lens[argc]); + argc++; + } + + /* Encode IDs */ + for (i = 0; i < res->ids_count; i++) { + arg_types[argc] = (query->querycmd == SILC_COMMAND_WHOIS ? + 4 + i : 5 + i); + silc_id_id2str(&res->ids[argc].u.client_id, SILC_ID_CLIENT, + args[argc], sizeof(args[argc]), &arg_lens[argc]); + argc++; + if (i + 1 > 255) + break; + } + + /* Send the command */ + res_cmd = silc_command_payload_encode(query->querycmd, argc, + args, arg_lens, arg_types, + res->pending->cmd_ident); + if (!res_cmd) { + /** No memory */ + silc_server_query_send_error(server, query, + SILC_STATUS_ERR_RESOURCE_LIMIT, 0); + silc_fsm_next(fsm, silc_server_st_query_error); + return SILC_FSM_CONTINUE; + } + + silc_packet_send(res->stream, SILC_PACKET_COMMAND, 0, + res_cmd->data, silc_buffer_send(res_cmd)); + silc_buffer_free(res_cmd); + silc_free(res->ids); + res->ids = NULL; + + /* Statistics */ + server->stat.commands_sent++; + break; + + case SILC_COMMAND_WHOWAS: + /* Send WHOWAS command */ + silc_server_send_command(server, res->stream, query->querycmd, + res->pending->cmd_ident, 1, + 1, query->nickname, strlen(query->nickname)); + break; + } + } + + /*** Resolve */ + silc_fsm_set_state_context(&res->thread, query); + silc_fsm_start_sync(&res->thread, silc_server_st_query_wait_resolve); + } + + /** Wait all resolvings */ + silc_fsm_next(fsm, silc_server_st_query_resolved); + return SILC_FSM_CONTINUE; +} + +/* Wait for resolving command reply */ + +SILC_FSM_STATE(silc_server_st_query_wait_resolve) { + SilcServerQueryResolve res = fsm_context; + SilcServerQuery query = state_context; + SilcBool timedout; + + /* Wait here for the reply */ + SILC_FSM_SEMA_TIMEDWAIT(&res->pending->wait_reply, + res->local ? 3 : 10, 0, &timedout); + + + + silc_list_del(query->resolvings, res); + silc_server_command_pending_free(res->pending); + silc_free(res); + + /* Signal main thread that reply was received */ + SILC_FSM_SEMA_POST(&query->wait_resolve); + return SILC_FSM_FINISH; } + +/* Wait here that all resolvings has been received */ + +SILC_FSM_STATE(silc_server_st_query_resolved) +{ + SilcServerThread thread = fsm_context; + SilcServer server = thread->server; + SilcServerQuery query = state_context; + SilcServerCommand cmd = query->cmd; + + /* Wait here until all resolvings has arrived */ + SILC_FSM_SEMA_WAIT(&query->wait_resolve); + if (silc_list_count(query->resolvings) > 0) + return SILC_FSM_CONTINUE; + +} + +/* Send the reply to the query. */ + +SILC_FSM_STATE(silc_server_st_query_reply) +{ + SilcServerThread thread = fsm_context; + SilcServer server = thread->server; + SilcServerQuery query = state_context; + SilcServerCommand cmd = query->cmd; + SilcIDCacheEntry id_entry; + +} diff --git a/lib/silcserver/server_util.c b/lib/silcserver/server_util.c index 4ce698ef..6b5b38eb 100644 --- a/lib/silcserver/server_util.c +++ b/lib/silcserver/server_util.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2005 Pekka Riikonen + Copyright (C) 1997 - 2006 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 @@ -22,6 +22,21 @@ #include "silcserver.h" #include "server_internal.h" +/* Return next available command identifier. */ + +SilcUInt16 silc_server_cmd_ident(SilcServer server) +{ + SilcUInt16 cmd_ident; + + silc_mutex_lock(server->lock); + cmd_ident = ++server->cmd_ident; + silc_mutex_unlock(server->lock); + + return cmd_ident; +} + +/* XXX locking */ + SilcBool silc_server_check_watcher_list(SilcServer server, SilcClientEntry client, const char *new_nick, @@ -125,6 +140,7 @@ SilcBool silc_server_create_client_id(SilcServer server, char *nickname, silc_hash_make(server->md5hash, nickname, strlen(nickname), hash); /* Create the ID */ + memset(new_id, 0, sizeof(*new_id)); memcpy(new_id->ip.data, server->id.ip.data, server->id.ip.data_len); new_id->ip.data_len = server->id.ip.data_len; new_id->rnd = silc_rng_get_byte(server->rng); -- 2.24.0