+Tue Mar 13 13:26:18 EET 2001 Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+ * Added support to the server to enforce that commands are not
+ executed more than once in 2 seconds. If server receives
+ commands from client more frequently, timeout is registered
+ to process the commands. Affected file silcd/command.c.
+ Added new function silc_server_command_process_timeout.
+
+ * Changed NICK_NOTIFY handling in client library to check that
+ if the client's nickname was changed, so there is no need to
+ resolve anything from the server.
+
+ * Removed error printing from the WHOIS and IDENTIFY commands.
+ If error occurs then it is ignored silently in the client library.
+ The application, however, may map the received error to
+ human readable error string. The application currently maps
+ the NO_SUCH_NICKNAME error to string.
+
+ * Made the command status message public to the application. Moved
+ them from lib/silcclient/command_reply.c to
+ lib/silcclient/command_reply.h. The application can map the
+ received command status to the string with the
+ silc_client_command_status_message function.
+
+ * Added check to the server to check that client's ID is same
+ as the Source ID in the packet the client sent. They must
+ match.
+
Tue Mar 13 12:49:21 EET 2001 Pekka Riikonen <priikone@poseidon.pspt.fi>
* Added dist-bzip hook to the Makefile.am to make bzip2
SilcChannelUser chu;
va_list vp;
- if (!success)
- return;
-
va_start(vp, status);
switch(command)
int len;
unsigned int idle;
+ if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
+ char *tmp;
+ tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
+ 3, NULL);
+ if (tmp)
+ client->ops->say(client, conn, "%s: %s", tmp,
+ silc_client_command_status_message(status));
+ else
+ client->ops->say(client, conn, "%s",
+ silc_client_command_status_message(status));
+ break;
+ }
+
+ if (!success)
+ return;
+
(void)va_arg(vp, SilcClientEntry);
nickname = va_arg(vp, char *);
username = va_arg(vp, char *);
unsigned int list_count;
SilcChannelEntry channel;
+ if (!success)
+ return;
+
app->screen->bottom_line->channel = va_arg(vp, char *);
channel = va_arg(vp, SilcChannelEntry);
mode = va_arg(vp, unsigned int);
{
SilcClientEntry entry;
+ if (!success)
+ return;
+
entry = va_arg(vp, SilcClientEntry);
silc_say(client, conn, "Your current nickname is %s", entry->nickname);
app->screen->bottom_line->nickname = entry->nickname;
break;
case SILC_COMMAND_USERS:
+ if (!success)
+ return;
+
silc_list_start(conn->current_channel->clients);
while ((chu = silc_list_get(conn->current_channel->clients))
!= SILC_LIST_END) {
unsigned int arg_type,
unsigned char *arg,
unsigned int arg_len);
+SILC_TASK_CALLBACK(silc_server_command_process_timeout);
/* Server command list. */
SilcServerCommand silc_command_list[] =
return FALSE;
}
+/* Internal context to hold data when executed command with timeout. */
+typedef struct {
+ SilcServer server;
+ SilcSocketConnection sock;
+ SilcPacketContext *packet;
+} *SilcServerCommandTimeout;
+
+/* Timeout callback to process commands with timeout for client. Client's
+ commands are always executed with timeout. */
+
+SILC_TASK_CALLBACK(silc_server_command_process_timeout)
+{
+ SilcServerCommandTimeout timeout = (SilcServerCommandTimeout)context;
+ SilcServerCommandContext ctx;
+ SilcServerCommand *cmd;
+ SilcClientEntry client = (SilcClientEntry)timeout->sock->user_data;
+
+ /* Update access time */
+ client->last_command = time(NULL);
+
+ /* Allocate command context. This must be free'd by the
+ command routine receiving it. */
+ ctx = silc_server_command_alloc();
+ ctx->server = timeout->server;
+ ctx->sock = timeout->sock;
+ ctx->packet = timeout->packet;
+
+ /* Parse the command payload in the packet */
+ ctx->payload = silc_command_payload_parse(ctx->packet->buffer);
+ if (!ctx->payload) {
+ SILC_LOG_ERROR(("Bad command payload, packet dropped"));
+ silc_buffer_free(ctx->packet->buffer);
+ silc_socket_free(ctx->sock);
+ silc_packet_context_free(ctx->packet);
+ silc_free(ctx);
+ silc_free(timeout);
+ return;
+ }
+ ctx->args = silc_command_get_args(ctx->payload);
+
+ /* Execute command. If this fails the packet is dropped. */
+ for (cmd = silc_command_list; cmd->cb; cmd++)
+ if (cmd->cmd == silc_command_get(ctx->payload)) {
+
+ if (!(cmd->flags & SILC_CF_REG)) {
+ cmd->cb(ctx);
+ break;
+ }
+
+ if (silc_server_is_registered(ctx->server, ctx->sock, ctx, cmd->cmd)) {
+ cmd->cb(ctx);
+ break;
+ }
+ }
+
+ if (cmd == NULL) {
+ SILC_LOG_ERROR(("Unknown command, packet dropped"));
+ silc_server_command_free(ctx);
+ silc_free(timeout);
+ return;
+ }
+
+ silc_free(timeout);
+}
+
/* Processes received command packet. */
void silc_server_command_process(SilcServer server,
SilcServerCommandContext ctx;
SilcServerCommand *cmd;
-#if 0
- /* XXX allow commands in but do not execute them more than once per
- two seconds. */
-
- /* Check whether it is allowed for this connection to execute any
- command. */
+ /* Execute client's commands always with timeout. Normally they are
+ executed with zero (0) timeout but if client is sending command more
+ frequently than once in 2 seconds, then the timeout may be 0 to 2
+ seconds. */
if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
- time_t curtime;
SilcClientEntry client = (SilcClientEntry)sock->user_data;
-
- if (!client)
- return;
-
- /* Allow only one command executed in 2 seconds. */
- curtime = time(NULL);
- if (client->last_command && (curtime - client->last_command) < 2)
- return;
-
- /* Update access time */
- client->last_command = curtime;
+ SilcServerCommandTimeout timeout = silc_calloc(1, sizeof(*timeout));
+
+ timeout->server = server;
+ timeout->sock = silc_socket_dup(sock);
+ timeout->packet = silc_packet_context_dup(packet);
+
+ if (client->last_command && (time(NULL) - client->last_command) < 2)
+ silc_task_register(server->timeout_queue, sock->sock,
+ silc_server_command_process_timeout,
+ (void *)timeout,
+ 2 - (time(NULL) - client->last_command), 0,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
+ else
+ silc_task_register(server->timeout_queue, sock->sock,
+ silc_server_command_process_timeout,
+ (void *)timeout,
+ 0, 1,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
+ return;
}
-#endif
-
+
/* Allocate command context. This must be free'd by the
command routine receiving it. */
ctx = silc_server_command_alloc();
SilcClientID *new_id;
char *nick;
+ if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+ goto out;
+
SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_NICK, cmd, 1, 1);
/* Check nickname */
if (ret == SILC_PACKET_NONE)
goto out;
+ /* Check that the the current client ID is same as in the client's packet. */
+ if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
+ SilcClientEntry client = (SilcClientEntry)sock->user_data;
+ if (client && client->id) {
+ void *id = silc_id_str2id(packet->src_id, packet->src_id_len,
+ packet->src_id_type);
+ if (SILC_ID_CLIENT_COMPARE(client->id, id)) {
+ silc_free(id);
+ goto out;
+ }
+ silc_free(id);
+ }
+ }
+
if (server->server_type == SILC_ROUTER) {
/* Route the packet if it is not destined to us. Other ID types but
server are handled separately after processing them. */
* application.
*/
- /* Get new Client ID */
- tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ /* Get old Client ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
if (!tmp)
goto out;
if (!SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
break;
- /* Find Client entry and if not found query it */
- client_entry2 =
- silc_client_get_client_by_id(client, conn, client_id);
- if (!client_entry2) {
- silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+ /* Find old Client entry */
+ client_entry = silc_client_get_client_by_id(client, conn, client_id);
+ if (!client_entry)
goto out;
- }
silc_free(client_id);
- /* Get old Client ID */
- tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ /* Get new Client ID */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
if (!tmp)
goto out;
if (!client_id)
goto out;
- /* Find old Client entry */
- client_entry =
- silc_client_get_client_by_id(client, conn, client_id);
- if (!client_entry)
+ /* Find Client entry and if not found resolve it */
+ client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
+ if (!client_entry2) {
+ silc_client_notify_by_server_resolve(client, conn, packet, client_id);
goto out;
+ }
/* Remove the old from cache */
silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
buffer = silc_command_payload_encode(SILC_COMMAND_NICK,
cmd->argc - 1, ++cmd->argv,
++cmd->argv_lens, ++cmd->argv_types,
- 0);
+ ++cmd->conn->cmd_ident);
silc_client_packet_send(cmd->client, cmd->conn->sock,
SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
buffer->data, buffer->len, TRUE);
SilcClientCommandReplyContext ctx,
SilcCommand command,
unsigned short ident);
+
SILC_CLIENT_CMD_FUNC(whois);
SILC_CLIENT_CMD_FUNC(whowas);
SILC_CLIENT_CMD_FUNC(identify);
{ NULL, 0 },
};
-/* Status message structure. Messages are defined below. */
-typedef struct {
- SilcCommandStatus status;
- char *message;
-} SilcCommandStatusMessage;
-
-/* Status messages returned by the server */
-#define STAT(x) SILC_STATUS_ERR_##x
const SilcCommandStatusMessage silc_command_status_messages[] = {
{ STAT(NO_SUCH_NICK), "No such nickname" },
{ 0, NULL }
};
-
/* Command reply operation that is called at the end of all command replys.
Usage: COMMAND_REPLY((ARGS, argument1, argument2, etc...)), */
#define COMMAND_REPLY(args) cmd->client->ops->command_reply args
/* Returns status message string */
-static char *
-silc_client_command_status_message(SilcCommandStatus status)
+char *silc_client_command_status_message(SilcCommandStatus status)
{
int i;
}
/* Notify application */
- COMMAND_REPLY((ARGS, client_entry, nickname, username, realname,
- NULL, idle));
+ if (!cmd->callback)
+ COMMAND_REPLY((ARGS, client_entry, nickname, username, realname,
+ NULL, idle));
}
/* Received reply for WHOIS command. This maybe called several times
SILC_CLIENT_CMD_REPLY_FUNC(whois)
{
SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
- SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
SilcCommandStatus status;
unsigned char *tmp;
status != SILC_STATUS_LIST_START &&
status != SILC_STATUS_LIST_ITEM &&
status != SILC_STATUS_LIST_END) {
- if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
- /* Take nickname which may be provided */
- tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
- if (tmp)
- cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
- silc_client_command_status_message(status));
- else
- cmd->client->ops->say(cmd->client, conn, "%s",
- silc_client_command_status_message(status));
- COMMAND_REPLY_ERROR;
- goto out;
- } else {
- cmd->client->ops->say(cmd->client, conn,
- "%s", silc_client_command_status_message(status));
- COMMAND_REPLY_ERROR;
- goto out;
- }
+ COMMAND_REPLY_ERROR;
+ goto out;
}
/* Display one whois reply */
SILC_CLIENT_CMD_REPLY_FUNC(identify)
{
SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
- SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
SilcCommandStatus status;
unsigned char *tmp;
status != SILC_STATUS_LIST_START &&
status != SILC_STATUS_LIST_ITEM &&
status != SILC_STATUS_LIST_END) {
- if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
- /* Take nickname which may be provided */
- tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
- if (tmp)
- cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
- silc_client_command_status_message(status));
- else
- cmd->client->ops->say(cmd->client, conn, "%s",
- silc_client_command_status_message(status));
- COMMAND_REPLY_ERROR;
- goto out;
- } else {
- cmd->client->ops->say(cmd->client, conn,
- "%s", silc_client_command_status_message(status));
- COMMAND_REPLY_ERROR;
- goto out;
- }
+ COMMAND_REPLY_ERROR;
+ goto out;
}
/* Save one IDENTIFY entry */
#define SILC_CLIENT_CMD_REPLY_FUNC(func) \
void silc_client_command_reply_##func(void *context)
+/* Status message structure. Messages are defined below. */
+typedef struct {
+ SilcCommandStatus status;
+ char *message;
+} SilcCommandStatusMessage;
+
+/* Status messages returned by the server */
+#define STAT(x) SILC_STATUS_ERR_##x
+extern const SilcCommandStatusMessage silc_command_status_messages[];
+
/* Prototypes */
void silc_client_command_reply_process(SilcClient client,
SilcSocketConnection sock,
SilcPacketContext *packet);
+char *silc_client_command_status_message(SilcCommandStatus status);
SILC_CLIENT_CMD_REPLY_FUNC(whois);
SILC_CLIENT_CMD_REPLY_FUNC(whowas);
SILC_CLIENT_CMD_REPLY_FUNC(identify);