+Thu Apr 11 16:32:08 EEST 2002 Pekka Riikonen <priikone@silcnet.org>
+
+ * Changed the UMODE's mode mask argument to be optional. If
+ not provided then the command merely returns the current mode
+ mask to the client. Updated protocol specs and the server.
+ Affected file is silcd/command.c.
+
+ * Added SILC session detachment/resuming support. It is possible
+ to detach by closing the network connection and then re-connect
+ and resume to the old client session. Added DETACHED user
+ mode that server will set for detached client. Added new
+ packet RESUME_CLIENT which is used to perform the resuming
+ process. Added DETACH command. Updated the protocol specs,
+ core library, client and server. Protocol TODO #22. Very
+ many affected files around the tree.
+
+Wed Apr 10 16:32:01 EEST 2002 Pekka Riikonen <priikone@silcnet.org>
+
+ * Changed the CMODE's mode mask argument to be optional. If
+ not provided then the command merely returns the current mode
+ mask to the client. Updated protocol specs and the server.
+ Affected file is silcd/command.c.
+
+ * Changed the Killer's Client ID in KILLED notify to be just
+ any ID payload since router server is allowed to kill as well.
+ Updated protocol specs, client libary and server. Affected
+ files are lib/silcclient/client_notify.c, silcd/packet_receive.c,
+ and irssi/src/silc/core/client_ops.c.
+
Tue Apr 9 17:15:42 EEST 2002 Pekka Riikonen <priikone@silcnet.org>
* Added new user modes ANONYMOUS for special anonymous servers
21. Subscription/IRC's notify kind support?
- 22. Session detachment/resume?
-
o Inviting and banning by public key should be made possible. To be
included in protocol version 1.2.
signal_emit("message topic", 5, server, channel->channel_name,
tmp, server_entry->server_name,
server_entry->server_name);
- } else {
+ } else if (idtype == SILC_ID_CHANNEL) {
channel = (SilcChannelEntry)entry;
signal_emit("message topic", 5, server, channel->channel_name,
tmp, channel->channel_name, channel->channel_name);
MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
channel->channel_name, tmp ? tmp : "removed all",
server_entry->server_name);
- } else {
+ } else if (idtype == SILC_ID_CHANNEL) {
channel2 = (SilcChannelEntry)entry;
printformat_module("fe-common/silc", server, channel->channel_name,
MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
channel->channel_name, client_entry2->nickname,
tmp ? tmp : "removed all",
server_entry->server_name);
- } else {
+ } else if (idtype == SILC_ID_CHANNEL) {
channel2 = (SilcChannelEntry)entry;
printformat_module("fe-common/silc", server, channel->channel_name,
MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
client_entry = va_arg(va, SilcClientEntry);
tmp = va_arg(va, char *);
- client_entry2 = va_arg(va, SilcClientEntry);
+ idtype = va_arg(va, int);
+ entry = va_arg(va, SilcClientEntry);
if (client_entry == conn->local_entry) {
- printformat_module("fe-common/silc", server, NULL,
- MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
- client_entry2 ? client_entry2->nickname : "",
- tmp ? tmp : "");
+ if (idtype == SILC_ID_CLIENT) {
+ client_entry2 = (SilcClientEntry)entry;
+ printformat_module("fe-common/silc", server, NULL,
+ MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
+ client_entry2 ? client_entry2->nickname : "",
+ tmp ? tmp : "");
+ } else if (idtype == SILC_ID_SERVER) {
+ server_entry = (SilcServerEntry)entry;
+ printformat_module("fe-common/silc", server, NULL,
+ MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
+ server_entry->server_name, tmp ? tmp : "");
+ } else if (idtype == SILC_ID_CHANNEL) {
+ channel = (SilcChannelEntry)entry;
+ printformat_module("fe-common/silc", server, NULL,
+ MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
+ channel->channel_name, tmp ? tmp : "");
+ }
} else {
list1 = nicklist_get_same_unique(SERVER(server), client_entry);
for (list_tmp = list1; list_tmp != NULL; list_tmp =
nicklist_remove(channel, nickrec);
}
- printformat_module("fe-common/silc", server, NULL,
- MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
- client_entry->nickname,
- client_entry2 ? client_entry2->nickname : "",
- tmp ? tmp : "");
+ if (idtype == SILC_ID_CLIENT) {
+ client_entry2 = (SilcClientEntry)entry;
+ printformat_module("fe-common/silc", server, NULL,
+ MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
+ client_entry->nickname,
+ client_entry2 ? client_entry2->nickname : "",
+ tmp ? tmp : "");
+ } else if (idtype == SILC_ID_SERVER) {
+ server_entry = (SilcServerEntry)entry;
+ printformat_module("fe-common/silc", server, NULL,
+ MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
+ client_entry->nickname,
+ server_entry->server_name, tmp ? tmp : "");
+ } else if (idtype == SILC_ID_CHANNEL) {
+ channel = (SilcChannelEntry)entry;
+ printformat_module("fe-common/silc", server, NULL,
+ MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
+ client_entry->nickname,
+ channel->channel_name, tmp ? tmp : "");
+ }
}
break;
or connecting failed. This is also the first time application receives
the SilcClientConnection object which it should save somewhere. */
-void silc_connect(SilcClient client, SilcClientConnection conn, int success)
+void silc_connect(SilcClient client, SilcClientConnection conn,
+ SilcClientConnectionStatus status)
{
SILC_SERVER_REC *server = conn->context;
- if (!server && !success) {
+ if (!server && status == SILC_CLIENT_CONN_ERROR) {
silc_client_close_connection(client, conn);
return;
}
- if (success) {
+ switch (status) {
+ case SILC_CLIENT_CONN_SUCCESS:
+ /* We have successfully connected to server */
server->connected = TRUE;
signal_emit("event connected", 1, server);
- } else {
+ break;
+
+ case SILC_CLIENT_CONN_SUCCESS_RESUME:
+ /* We have successfully resumed old detached session */
+ server->connected = TRUE;
+ signal_emit("event connected", 1, server);
+
+ /* If we resumed old session check whether we need to update
+ our nickname */
+ if (strcmp(server->nick, conn->local_entry->nickname)) {
+ char *old;
+ old = g_strdup(server->nick);
+ server_change_nick(SERVER(server), conn->local_entry->nickname);
+ nicklist_rename_unique(SERVER(server),
+ conn->local_entry, server->nick,
+ conn->local_entry, conn->local_entry->nickname);
+ signal_emit("message own_nick", 4, server, server->nick, old, "");
+ g_free(old);
+ }
+ break;
+
+ default:
server->connection_lost = TRUE;
if (server->conn)
server->conn->context = NULL;
server_disconnect(SERVER(server));
+ break;
}
}
ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
nicklist_set_own(CHANNEL(chanrec), ownnick);
signal_emit("channel joined", 1, chanrec);
+ chanrec->entry = channel;
if (chanrec->topic)
printformat_module("fe-common/silc", server, channel->channel_name,
"SILC Operator" : "[Unknown mode]");
}
if (mode & SILC_UMODE_GONE)
- strcat(buf, " away");
+ strcat(buf, " [away]");
if (mode & SILC_UMODE_INDISPOSED)
- strcat(buf, " indisposed");
+ strcat(buf, " [indisposed]");
if (mode & SILC_UMODE_BUSY)
- strcat(buf, " busy");
+ strcat(buf, " [busy]");
if (mode & SILC_UMODE_PAGE)
- strcat(buf, " page to reach");
+ strcat(buf, " [page to reach]");
if (mode & SILC_UMODE_HYPER)
- strcat(buf, " hyper active");
+ strcat(buf, " [hyper active]");
if (mode & SILC_UMODE_ROBOT)
- strcat(buf, " robot");
+ strcat(buf, " [robot]");
if (mode & SILC_UMODE_ANONYMOUS)
- strcat(buf, " anonymous");
+ strcat(buf, " [anonymous]");
if (mode & SILC_UMODE_BLOCK_PRIVMSG)
- strcat(buf, " blocks private messages");
+ strcat(buf, " [blocks private messages]");
+ if (mode & SILC_UMODE_DETACHED)
+ strcat(buf, " [detached]");
printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
SILCTXT_WHOIS_MODES, buf);
silc_detach(SilcClient client, SilcClientConnection conn,
const unsigned char *detach_data, SilcUInt32 detach_data_len)
{
+ char file[256];
+ /* Save the detachment data to file. */
+
+ memset(file, 0, sizeof(file));
+ snprintf(file, sizeof(file) - 1, "%s/session.%s.%d", get_irssi_dir(),
+ conn->remote_host, conn->remote_port);
+ silc_file_writefile(file, detach_data, detach_data_len);
}
void silc_command_reply(SilcClient client, SilcClientConnection conn,
SilcCommandPayload cmd_payload, int success,
SilcCommand command, SilcCommandStatus status, ...);
-void silc_connect(SilcClient client, SilcClientConnection conn, int success);
+void silc_connect(SilcClient client, SilcClientConnection conn,
+ SilcClientConnectionStatus status);
void silc_disconnect(SilcClient client, SilcClientConnection conn);
void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
SilcAskPassphrase completion, void *context);
SILC_CHANNEL_REC *rec;
rec = silc_channel_find(server, channel);
- if (rec == NULL || rec->entry == NULL)
+ if (rec == NULL || rec->entry == NULL) {
+ cmd_return_error(CMDERR_NOT_JOINED);
return;
-
+ }
+
silc_client_send_channel_message(silc_client, server->conn, rec->entry,
NULL, 0, msg, strlen(msg), TRUE);
}
static void sig_connected(SILC_SERVER_REC *server)
{
SilcClientConnection conn;
+ SilcClientConnectionParams params;
+ char file[256];
int fd;
if (!IS_SILC_SERVER(server))
return;
- conn = silc_client_add_connection(silc_client, NULL,
+ /* Try to read detached session data and use it if found. */
+ memset(¶ms, 0, sizeof(params));
+ memset(file, 0, sizeof(file));
+ snprintf(file, sizeof(file) - 1, "%s/session.%s.%d", get_irssi_dir(),
+ server->connrec->address, server->connrec->port);
+ params.detach_data = silc_file_readfile(file, ¶ms.detach_data_len);
+
+ /* Add connection to the client library */
+ conn = silc_client_add_connection(silc_client, ¶ms,
server->connrec->address,
server->connrec->port,
server);
server->conn = conn;
-
+
+ silc_free(params.detach_data);
+ unlink(file);
+
fd = g_io_channel_unix_get_fd(net_sendbuffer_handle(server->handle));
+
+ /* Start key exchange with the server */
silc_client_start_key_exchange(silc_client, conn, fd);
server->ftp_sessions = silc_dlist_init();
/* SYNTAX: FILE CLOSE [<nickname>] */
/* SYNTAX: FILE */
/* SYNTAX: JOIN <channel> [<passphrase>] [-cipher <cipher>] [-hmac <hmac>] [-founder <-pubkey|passwd>] */
+/* SYNTAX: DETACH */
void silc_command_exec(SILC_SERVER_REC *server,
const char *command, const char *args)
command_bind_silc("getkey", MODULE_NAME, (SIGNAL_FUNC) command_self);
command_bind_silc("sconnect", MODULE_NAME, (SIGNAL_FUNC) command_sconnect);
command_bind_silc("file", MODULE_NAME, (SIGNAL_FUNC) command_file);
+ command_bind_silc("detach", MODULE_NAME, (SIGNAL_FUNC) command_self);
command_set_options("connect", "+silcnet");
}
command_unbind("getkey", (SIGNAL_FUNC) command_self);
command_unbind("sconnect", (SIGNAL_FUNC) command_sconnect);
command_unbind("file", (SIGNAL_FUNC) command_file);
+ command_unbind("detach", (SIGNAL_FUNC) command_self);
}
void silc_server_free_ftp(SILC_SERVER_REC *server,
SILC_SERVER_CMD(cumode, CUMODE, SILC_CF_LAG | SILC_CF_REG),
SILC_SERVER_CMD(kick, KICK, SILC_CF_LAG_STRICT | SILC_CF_REG),
SILC_SERVER_CMD(ban, BAN, SILC_CF_LAG_STRICT | SILC_CF_REG),
+ SILC_SERVER_CMD(detach, DETACH, SILC_CF_LAG_STRICT | SILC_CF_REG),
SILC_SERVER_CMD(silcoper, SILCOPER,
SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER),
SILC_SERVER_CMD(leave, LEAVE, SILC_CF_LAG_STRICT | SILC_CF_REG),
of arguments. */
#define SILC_SERVER_COMMAND_CHECK(command, context, min, max) \
do { \
- SilcUInt32 _argc; \
+ SilcUInt32 _argc; \
\
SILC_LOG_DEBUG(("Start")); \
\
cmd->server->md5hash, nick,
&new_id)) {
nickfail++;
+ if (nickfail > 9) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
+ SILC_STATUS_ERR_BAD_NICKNAME);
+ goto out;
+ }
snprintf(&nick[strlen(nick) - 1], 1, "%d", nickfail);
}
if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
goto out;
- /* Get destination ID */
+ /* Get message */
tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
if (len > 128)
tmp = NULL;
/* We quit the connection with little timeout */
silc_schedule_task_add(server->schedule, sock->sock,
- silc_server_command_quit_cb, (void *)q,
- 0, 200000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+ silc_server_command_quit_cb, (void *)q,
+ 0, 200000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
out:
silc_server_command_free(cmd);
SilcClientID *client_id;
unsigned char *tmp, *comment;
SilcUInt32 tmp_len, tmp_len2;
- SilcBuffer killer;
bool local;
SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_KILL, cmd, 1, 2);
silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
SILC_STATUS_OK);
- /* Send the KILL notify packets. First send it to the channel, then
- to our primary router and then directly to the client who is being
- killed right now. */
-
- /* Send KILLED notify to the channels. It is not sent to the client
- as it will be sent differently destined directly to the client and not
- to the channel. */
- killer = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
- silc_server_send_notify_on_channels(server, remote_client,
- remote_client, SILC_NOTIFY_TYPE_KILLED,
- 3, tmp, tmp_len,
- comment, comment ? tmp_len2 : 0,
- killer->data, killer->len);
- silc_buffer_free(killer);
-
- /* Send KILLED notify to primary route */
- if (!server->standalone)
- silc_server_send_notify_killed(server, server->router->connection, TRUE,
- remote_client->id, comment, client->id);
-
- /* Send KILLED notify to the client directly */
- silc_server_send_notify_killed(server, remote_client->connection ?
- remote_client->connection :
- remote_client->router->connection, FALSE,
- remote_client->id, comment, client->id);
-
- /* Remove the client from all channels. This generates new keys to the
- channels as well. */
- silc_server_remove_from_channels(server, NULL, remote_client, FALSE,
- NULL, TRUE);
-
- /* Remove the client entry, If it is locally connected then we will also
- disconnect the client here */
- if (remote_client->connection) {
- /* Remove locally conneted client */
- SilcSocketConnection sock = remote_client->connection;
- silc_server_free_client_data(server, sock, remote_client, FALSE, NULL);
- silc_server_close_connection(server, sock);
- } else {
- /* Update statistics */
- if (remote_client->connection)
- server->stat.my_clients--;
- if (server->stat.cell_clients)
- server->stat.cell_clients--;
- SILC_OPER_STATS_UPDATE(remote_client, server, SILC_UMODE_SERVER_OPERATOR);
- SILC_OPER_STATS_UPDATE(remote_client, router, SILC_UMODE_ROUTER_OPERATOR);
-
- /* Remove remote client */
- silc_idlist_del_client(local ? server->local_list :
- server->global_list, remote_client);
- }
+ /* Now do the killing */
+ silc_server_kill_client(server, remote_client, comment, client->id,
+ SILC_ID_CLIENT);
out:
silc_server_command_free(cmd);
if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
tmp = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
- keyp = silc_channel_key_payload_encode(SILC_ID_CHANNEL_LEN, tmp,
+ keyp = silc_channel_key_payload_encode(silc_id_get_len(channel->id,
+ SILC_ID_CHANNEL),
+ tmp,
strlen(channel->channel_key->
cipher->name),
channel->channel_key->cipher->name,
SilcServer server = cmd->server;
SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
SilcBuffer packet;
- unsigned char *tmp_mask;
- SilcUInt32 mask;
+ unsigned char *tmp_mask, m[4];
+ SilcUInt32 mask = 0;
SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+ bool set_mask = FALSE;
if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
goto out;
- SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_UMODE, cmd, 2, 2);
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_UMODE, cmd, 1, 2);
/* Get the client's mode mask */
tmp_mask = silc_argument_get_arg_type(cmd->args, 2, NULL);
- if (!tmp_mask) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
- SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
- goto out;
- }
- SILC_GET32_MSB(mask, tmp_mask);
-
- /* Check that mode changing is allowed. */
- if (!silc_server_check_umode_rights(server, client, mask)) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
- SILC_STATUS_ERR_PERM_DENIED);
- goto out;
+ if (tmp_mask) {
+ SILC_GET32_MSB(mask, tmp_mask);
+ set_mask = TRUE;
}
- /* Anonymous mode cannot be set by client */
- if (mask & SILC_UMODE_ANONYMOUS) {
- if (!(client->mode & SILC_UMODE_ANONYMOUS)) {
+ if (set_mask) {
+ /* Check that mode changing is allowed. */
+ if (!silc_server_check_umode_rights(server, client, mask)) {
silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
SILC_STATUS_ERR_PERM_DENIED);
goto out;
}
- } else {
- if (client->mode & SILC_UMODE_ANONYMOUS) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
- SILC_STATUS_ERR_PERM_DENIED);
- goto out;
+
+ /* Anonymous mode cannot be set by client */
+ if (mask & SILC_UMODE_ANONYMOUS) {
+ if (!(client->mode & SILC_UMODE_ANONYMOUS)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
+ SILC_STATUS_ERR_PERM_DENIED);
+ goto out;
+ }
+ } else {
+ if (client->mode & SILC_UMODE_ANONYMOUS) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
+ SILC_STATUS_ERR_PERM_DENIED);
+ goto out;
+ }
}
- }
- /* Change the mode */
- client->mode = mask;
+ /* Change the mode */
+ client->mode = mask;
- /* Send UMODE change to primary router */
- if (!server->standalone)
- silc_server_send_notify_umode(server, server->router->connection, TRUE,
- client->id, client->mode);
+ /* Send UMODE change to primary router */
+ if (!server->standalone)
+ silc_server_send_notify_umode(server, server->router->connection, TRUE,
+ client->id, client->mode);
+ }
/* Send command reply to sender */
+ SILC_PUT32_MSB(client->mode, m);
packet = silc_command_reply_payload_encode_va(SILC_COMMAND_UMODE,
SILC_STATUS_OK, 0, ident, 1,
- 2, tmp_mask, 4);
+ 2, m, sizeof(m));
silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0,
packet->data, packet->len, FALSE);
silc_buffer_free(packet);
silc_server_command_free(cmd);
}
+SILC_TASK_CALLBACK(silc_server_command_detach_cb)
+{
+ QuitInternal q = (QuitInternal)context;
+ SilcClientEntry client = (SilcClientEntry)q->sock->user_data;
+
+ /* If there is pending outgoing data for the client then purge it
+ to the network before closing connection. */
+ silc_server_packet_queue_purge(q->server, q->sock);
+
+ /* Close the connection on our side */
+ client->router = NULL;
+ client->connection = NULL;
+ q->sock->user_data = NULL;
+ silc_server_close_connection(q->server, q->sock);
+
+ silc_free(q);
+}
+
+/* Server side of DETACH command. Detached the client from the network
+ by closing the connection but preserving the session. */
+
+SILC_SERVER_CMD_FUNC(detach)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcServer server = cmd->server;
+ SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+ QuitInternal q;
+
+ if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+ goto out;
+
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_DETACH, cmd, 0, 0);
+
+ /* Send the user mode notify to notify that client is detached */
+ client->mode |= SILC_UMODE_DETACHED;
+ client->data.status &= ~SILC_IDLIST_STATUS_RESUMED;
+ if (!server->standalone)
+ silc_server_send_notify_umode(server, server->router->connection,
+ server->server_type == SILC_SERVER ?
+ FALSE : TRUE, client->id, client->mode);
+
+ q = silc_calloc(1, sizeof(*q));
+ q->server = server;
+ q->sock = cmd->sock;
+ silc_schedule_task_add(server->schedule, 0, silc_server_command_detach_cb,
+ q, 0, 200000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+
+ /* Send reply to the sender */
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_DETACH,
+ SILC_STATUS_OK);
+
+ out:
+ silc_server_command_free(cmd);
+}
+
/* Server side of SILCOPER command. Client uses this comand to obtain router
operator privileges to this router. */
SILC_SERVER_CMD_FUNC(cumode);
SILC_SERVER_CMD_FUNC(kick);
SILC_SERVER_CMD_FUNC(ban);
+SILC_SERVER_CMD_FUNC(detach);
SILC_SERVER_CMD_FUNC(silcoper);
SILC_SERVER_CMD_FUNC(leave);
SILC_SERVER_CMD_FUNC(users);
}
client->data.public_key = public_key;
+ public_key = NULL;
} else if (id_type == SILC_ID_SERVER) {
server_id = silc_id_payload_get_id(idp);
}
server_entry->data.public_key = public_key;
+ public_key = NULL;
} else {
goto out;
}
#define SILC_IDLIST_STATUS_RESOLVING 0x04 /* Entry is being resolved
with WHOIS or IDENTIFY */
#define SILC_IDLIST_STATUS_DISABLED 0x08 /* Entry is disabled */
+#define SILC_IDLIST_STATUS_RESUMED 0x10 /* Entry is resumed */
/*
Generic ID list data structure.
#include "serverincludes.h"
#include "server_internal.h"
-extern char *server_version;
-
/* Received notify packet. Server can receive notify packets from router.
Server then relays the notify messages to clients if needed. */
/* From protocol version 1.1 we get the killer's ID as well. */
tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
if (tmp) {
- client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
+ client_id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
if (!client_id)
goto out;
- /* If the the client is not in local list we check global list */
- client2 = silc_idlist_find_client_by_id(server->global_list,
- client_id, TRUE, NULL);
- if (!client2) {
- client2 = silc_idlist_find_client_by_id(server->local_list,
+ if (id_type == SILC_ID_CLIENT) {
+ /* If the the client is not in local list we check global list */
+ client2 = silc_idlist_find_client_by_id(server->global_list,
client_id, TRUE, NULL);
if (!client2) {
- silc_free(client_id);
- goto out;
+ client2 = silc_idlist_find_client_by_id(server->local_list,
+ client_id, TRUE, NULL);
+ if (!client2) {
+ silc_free(client_id);
+ goto out;
+ }
}
- }
- silc_free(client_id);
+ silc_free(client_id);
- /* Killer must be router operator */
- if (!(client2->mode & SILC_UMODE_ROUTER_OPERATOR)) {
- SILC_LOG_DEBUG(("Killing is not allowed"));
- goto out;
+ /* Killer must be router operator */
+ if (!(client2->mode & SILC_UMODE_ROUTER_OPERATOR)) {
+ SILC_LOG_DEBUG(("Killing is not allowed"));
+ goto out;
+ }
}
}
goto out;
}
+ /* Remove internal resumed flag if client is marked detached now */
+ if (mode & SILC_UMODE_DETACHED)
+ client->data.status &= ~SILC_IDLIST_STATUS_RESUMED;
+
/* Change the mode */
client->mode = mode;
packet->dst_id_len, NULL,
&idata, &client);
if (!dst_sock) {
+ SilcBuffer idp;
+
+ if (client && client->mode & SILC_UMODE_DETACHED) {
+ SILC_LOG_DEBUG(("Locally connected client is detached, "
+ "discarding packet"));
+ return;
+ }
+
/* Send IDENTIFY command reply with error status to indicate that
such destination ID does not exist or is invalid */
- SilcBuffer idp = silc_id_payload_encode_data(packet->dst_id,
- packet->dst_id_len,
- packet->dst_id_type);
+ idp = silc_id_payload_encode_data(packet->dst_id,
+ packet->dst_id_len,
+ packet->dst_id_type);
if (!idp)
return;
while (!silc_id_create_client_id(server, server->id, server->rng,
server->md5hash, nickname, &client_id)) {
nickfail++;
+ if (nickfail > 9) {
+ silc_server_disconnect_remote(server, sock,
+ "Server closed connection: Bad nickname");
+ return NULL;
+ }
snprintf(&nickname[strlen(nickname) - 1], 1, "%d", nickfail);
}
/* Send the new client ID to the client. */
id_string = silc_id_id2str(client->id, SILC_ID_CLIENT);
- reply = silc_buffer_alloc(2 + 2 + id_len);
- silc_buffer_pull_tail(reply, SILC_BUFFER_END(reply));
+ reply = silc_buffer_alloc_size(2 + 2 + id_len);
silc_buffer_format(reply,
SILC_STR_UI_SHORT(SILC_ID_CLIENT),
SILC_STR_UI_SHORT(id_len),
silc_buffer_free(reply);
/* Send some nice info to the client */
- SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
- ("Welcome to the SILC Network %s",
- username));
- SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
- ("Your host is %s, running version %s",
- server->server_name, server_version));
-
- if (server->stat.clients && server->stat.servers + 1)
- SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
- ("There are %d clients on %d servers in SILC "
- "Network", server->stat.clients,
- server->stat.servers + 1));
- if (server->stat.cell_clients && server->stat.cell_servers + 1)
- SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
- ("There are %d clients on %d server in our cell",
- server->stat.cell_clients,
- server->stat.cell_servers + 1));
- if (server->server_type == SILC_ROUTER) {
- SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
- ("I have %d clients, %d channels, %d servers and "
- "%d routers",
- server->stat.my_clients,
- server->stat.my_channels,
- server->stat.my_servers,
- server->stat.my_routers));
- } else {
- SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
- ("I have %d clients and %d channels formed",
- server->stat.my_clients,
- server->stat.my_channels));
- }
-
- if (server->stat.server_ops || server->stat.router_ops)
- SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
- ("There are %d server operators and %d router "
- "operators online",
- server->stat.server_ops,
- server->stat.router_ops));
- if (server->stat.my_router_ops + server->stat.my_server_ops)
- SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
- ("I have %d operators online",
- server->stat.my_router_ops +
- server->stat.my_server_ops));
-
- SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
- ("Your connection is secured with %s cipher, "
- "key length %d bits",
- idata->send_key->cipher->name,
- idata->send_key->cipher->key_len));
- SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
- ("Your current nickname is %s",
- client->nickname));
-
- /* Send motd */
- silc_server_send_motd(server, sock);
+ silc_server_send_connect_notifys(server, sock, client);
return client;
}
idata->hmac_send, idata->psn_send++,
packet, FALSE);
}
+
+typedef struct {
+ SilcServer server;
+ SilcSocketConnection sock;
+ SilcPacketContext *packet;
+} *SilcServerResumeResolve;
+
+SILC_SERVER_CMD_FUNC(resume_resolve)
+{
+ SilcServerResumeResolve r = (SilcServerResumeResolve)context;
+ SilcServer server = r->server;
+ SilcSocketConnection sock = r->sock;
+ SilcServerCommandReplyContext reply = context2;
+
+ if (!context2 || !silc_command_get_status(reply->payload, NULL, NULL)) {
+ SILC_LOG_ERROR(("Client %s (%s) tried to resume unknown client, "
+ "closing connection", sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock,
+ "Server closed connection: "
+ "Incomplete resume information");
+ goto out;
+ }
+
+ /* Reprocess the packet */
+ silc_server_resume_client(server, sock, r->packet);
+
+ out:
+ silc_socket_free(r->sock);
+ silc_packet_context_free(r->packet);
+ silc_free(r);
+}
+
+/* Received client resuming packet. This is used to resume detached
+ client session. It can be sent by the client who wishes to resume
+ but this is also sent by servers and routers to notify other routers
+ that the client is not detached anymore. */
+
+void silc_server_resume_client(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcBuffer buffer = packet->buffer, buf;
+ SilcIDListData idata;
+ SilcClientEntry detached_client;
+ SilcClientID *client_id = NULL;
+ unsigned char *id_string, *auth = NULL;
+ SilcUInt16 id_len, auth_len = 0;
+ int ret, nickfail = 0;
+ bool resolved, local;
+ SilcServerResumeResolve r;
+
+ ret = silc_buffer_unformat(buffer,
+ SILC_STR_UI16_NSTRING(&id_string, &id_len),
+ SILC_STR_END);
+ if (ret != -1)
+ client_id = silc_id_str2id(id_string, id_len, SILC_ID_CLIENT);
+
+ if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
+ /* Client send this and is attempting to resume to old client session */
+ SilcClientEntry client;
+ SilcChannelEntry channel;
+ SilcHashTableList htl;
+ SilcChannelClientEntry chl;
+ SilcBuffer keyp;
+
+ if (ret != -1) {
+ silc_buffer_pull(buffer, 2 + id_len);
+ auth = buffer->data;
+ auth_len = buffer->len;
+ silc_buffer_push(buffer, 2 + id_len);
+ }
+
+ if (!client_id || auth_len < 128) {
+ SILC_LOG_ERROR(("Client %s (%s) sent incomplete resume information, "
+ "closing connection", sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock, "Server closed connection: "
+ "Incomplete resume information");
+ return;
+ }
+
+ /* Take client entry of this connection */
+ client = (SilcClientEntry)sock->user_data;
+ idata = (SilcIDListData)client;
+
+ /* Get entry to the client, and resolve it if we don't have it. */
+ detached_client = silc_server_get_client_resolve(server, client_id,
+ &resolved);
+ if (!detached_client) {
+ if (resolved) {
+ /* The client info is being resolved. Reprocess this packet after
+ receiving the reply to the query. */
+ r = silc_calloc(1, sizeof(*r));
+ if (!r)
+ return;
+
+ r->server = server;
+ r->sock = silc_socket_dup(sock);
+ r->packet = silc_packet_context_dup(packet);
+ silc_server_command_pending(server, SILC_COMMAND_WHOIS,
+ server->cmd_ident,
+ silc_server_command_resume_resolve, r);
+ } else {
+ SILC_LOG_ERROR(("Client %s (%s) tried to resume unknown client, "
+ "closing connection", sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock,
+ "Server closed connection: "
+ "Incomplete resume information");
+ }
+ return;
+ }
+
+ /* Check that the client is detached */
+ if (!(detached_client->mode & SILC_UMODE_DETACHED)) {
+ SILC_LOG_ERROR(("Client %s (%s) tried to resume un-detached client, "
+ "closing connection", sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock, "Server closed connection: "
+ "Incomplete resume information");
+ return;
+ }
+
+ /* Check that we have the public key of the client, if not then we must
+ resolve it first. */
+ if (!detached_client->data.public_key) {
+ if (server->standalone) {
+ silc_server_disconnect_remote(server, sock,
+ "Server closed connection: "
+ "Incomplete resume information");
+ } else {
+ /* We must retrieve the detached client's public key by sending
+ GETKEY command. Reprocess this packet after receiving the key */
+ SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+ SilcSocketConnection dest_sock =
+ silc_server_get_client_route(server, NULL, 0, client_id, NULL, NULL);
+
+ silc_server_send_command(server, dest_sock ? dest_sock :
+ server->router->connection,
+ SILC_COMMAND_GETKEY, ++server->cmd_ident,
+ 1, idp->data, idp->len);
+
+ r = silc_calloc(1, sizeof(*r));
+ if (!r)
+ return;
+
+ r->server = server;
+ r->sock = silc_socket_dup(sock);
+ r->packet = silc_packet_context_dup(packet);
+ silc_server_command_pending(server, SILC_COMMAND_GETKEY,
+ server->cmd_ident,
+ silc_server_command_resume_resolve, r);
+
+ silc_buffer_free(idp);
+ }
+ return;
+ }
+
+ /* Verify the authentication payload. This has to be successful in
+ order to allow the resuming */
+ if (!silc_auth_verify_data(auth, auth_len, SILC_AUTH_PUBLIC_KEY,
+ detached_client->data.public_key, 0,
+ idata->hash, detached_client->id,
+ SILC_ID_CLIENT)) {
+ SILC_LOG_ERROR(("Client %s (%s) resume authentication failed, "
+ "closing connection", sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock, "Server closed connection: "
+ "Incomplete resume information");
+ return;
+ }
+
+ /* Now resume the client to the network */
+
+ sock->user_data = detached_client;
+ detached_client->connection = sock;
+
+ /* Take new keys and stuff into use in the old entry */
+ silc_idlist_del_data(detached_client);
+ silc_idlist_add_data(detached_client, idata);
+ detached_client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+ detached_client->data.status |= SILC_IDLIST_STATUS_RESUMED;
+ detached_client->mode &= ~SILC_UMODE_DETACHED;
+
+ /* Send the RESUME_CLIENT packet to our primary router so that others
+ know this client isn't detached anymore. */
+ if (!server->standalone) {
+ buf = silc_buffer_alloc_size(2 + id_len);
+ silc_buffer_format(buf,
+ SILC_STR_UI_SHORT(id_len),
+ SILC_STR_UI_XNSTRING(id_string, id_len),
+ SILC_STR_END);
+ silc_server_packet_send(server, server->router->connection,
+ SILC_PACKET_RESUME_CLIENT, 0,
+ buf->data, buf->len, TRUE);
+ silc_buffer_free(buf);
+ }
+
+ /* Delete this client entry since we're resuming to old one. */
+ server->stat.my_clients--;
+ server->stat.clients--;
+ if (server->stat.cell_clients)
+ server->stat.cell_clients--;
+ silc_idlist_del_client(server->local_list, client);
+ client = detached_client;
+
+ /* If the ID is not based in our ID then change it */
+ if (!SILC_ID_COMPARE(client->id, server->id, server->id->ip.data_len)) {
+ while (!silc_id_create_client_id(server, server->id, server->rng,
+ server->md5hash, client->nickname,
+ &client_id)) {
+ nickfail++;
+ if (nickfail > 9) {
+ silc_server_disconnect_remote(server, sock,
+ "Server closed connection: "
+ "Bad nickname");
+ return;
+ }
+ snprintf(&client->nickname[strlen(client->nickname) - 1], 1,
+ "%d", nickfail);
+ }
+
+ /* Notify about Client ID change, nickname doesn't actually change. */
+ if (!server->standalone)
+ silc_server_send_notify_nick_change(server, server->router->connection,
+ FALSE, client->id, client_id,
+ client->nickname);
+
+ silc_free(client->id);
+ client->id = client_id;
+ }
+
+ /* Add the client again to the ID cache to get it to correct list */
+ if (!silc_idcache_del_by_context(server->local_list->clients, client))
+ silc_idcache_del_by_context(server->global_list->clients, client);
+ silc_idcache_add(server->local_list->clients, client->nickname,
+ client->id, client, client->mode, NULL);
+
+ /* Send the new client ID to the client. */
+ id_string = silc_id_id2str(client->id, SILC_ID_CLIENT);
+ buf = silc_buffer_alloc_size(2 + 2 + id_len);
+ silc_buffer_format(buf,
+ SILC_STR_UI_SHORT(SILC_ID_CLIENT),
+ SILC_STR_UI_SHORT(id_len),
+ SILC_STR_UI_XNSTRING(id_string, id_len),
+ SILC_STR_END);
+ silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 0,
+ buf->data, buf->len, FALSE);
+ silc_free(id_string);
+ silc_buffer_free(buf);
+
+ /* Send some nice info to the client */
+ silc_server_send_connect_notifys(server, sock, client);
+
+ /* Send all channel keys of channels the client has joined */
+ silc_hash_table_list(client->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
+ channel = chl->channel;
+ id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+ keyp =
+ silc_channel_key_payload_encode(silc_id_get_len(channel->id,
+ SILC_ID_CHANNEL),
+ id_string,
+ strlen(channel->channel_key->
+ cipher->name),
+ channel->channel_key->cipher->name,
+ channel->key_len / 8, channel->key);
+ silc_free(id_string);
+
+ /* Send the key packet to client */
+ silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0,
+ keyp->data, keyp->len, FALSE);
+ }
+ silc_hash_table_list_reset(&htl);
+
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("Your session is successfully resumed"));
+
+ } else if (sock->type != SILC_SOCKET_TYPE_CLIENT) {
+ /* Server or router sent this to us to notify that that a client has
+ been resumed. */
+ SilcServerEntry server_entry;
+ SilcServerID *server_id;
+
+ if (!client_id)
+ return;
+
+ /* Get entry to the client, and resolve it if we don't have it. */
+ detached_client = silc_idlist_find_client_by_id(server->local_list,
+ client_id, TRUE, NULL);
+ if (!detached_client) {
+ detached_client = silc_idlist_find_client_by_id(server->global_list,
+ client_id, TRUE, NULL);
+ if (!detached_client)
+ return;
+ }
+
+ /* Check that the client has not been resumed already because it is
+ protocol error to attempt to resume more than once. The client
+ will be killed if this protocol error occurs. */
+ if (detached_client->data.status & SILC_IDLIST_STATUS_RESUMED &&
+ !(detached_client->mode & SILC_UMODE_DETACHED)) {
+ /* The client is clearly attempting to resume more than once and
+ perhaps playing around by resuming from several different places
+ at the same time. */
+ silc_server_kill_client(server, detached_client, NULL,
+ server->id, SILC_ID_SERVER);
+ return;
+ }
+
+ /* Check whether client is detached at all */
+ if (!(detached_client->mode & SILC_UMODE_DETACHED))
+ return;
+
+ /* Client is detached, and now it is resumed. Remove the detached
+ mode and mark that it is resumed. */
+ detached_client->mode &= ~SILC_UMODE_DETACHED;
+ detached_client->data.status |= SILC_IDLIST_STATUS_RESUMED;
+
+ /* Get the new owner of the resumed client */
+ server_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+ packet->src_id_type);
+ if (!server_id)
+ return;
+
+ /* Get server entry */
+ server_entry = silc_idlist_find_server_by_id(server->global_list,
+ server_id, TRUE, NULL);
+ local = TRUE;
+ if (!server_entry) {
+ server_entry = silc_idlist_find_server_by_id(server->local_list,
+ server_id, TRUE, NULL);
+ local = FALSE;
+ if (!server_entry) {
+ silc_free(server_id);
+ return;
+ }
+ }
+
+ /* Change the client to correct list. */
+ if (!silc_idcache_del_by_context(server->local_list->clients,
+ detached_client))
+ silc_idcache_del_by_context(server->global_list->clients,
+ detached_client);
+ silc_idcache_add(local ? server->local_list->clients :
+ server->global_list->clients, detached_client->nickname,
+ detached_client->id, detached_client, FALSE, NULL);
+
+ /* Change the owner of the client if needed */
+ if (detached_client->router != server_entry)
+ detached_client->router = server_entry;
+
+ /* If the sender of this packet is server and we are router we need to
+ broadcast this packet to other routers in the network. */
+ if (!server->standalone && server->server_type == SILC_ROUTER &&
+ sock->type == SILC_SOCKET_TYPE_SERVER &&
+ !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
+ SILC_LOG_DEBUG(("Broadcasting received Resume Client packet"));
+ silc_server_packet_send(server, server->router->connection,
+ packet->type,
+ packet->flags | SILC_PACKET_FLAG_BROADCAST,
+ buffer->data, buffer->len, FALSE);
+ silc_server_backup_send(server, (SilcServerEntry)sock->user_data,
+ packet->type, packet->flags,
+ packet->buffer->data, packet->buffer->len,
+ FALSE, TRUE);
+ }
+
+ silc_free(server_id);
+ }
+
+ silc_free(client_id);
+}
void silc_server_ftp(SilcServer server,
SilcSocketConnection sock,
SilcPacketContext *packet);
+void silc_server_resume_client(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet);
#endif
/* Send to locally connected client */
sock = (SilcSocketConnection)client->connection;
+ if (!sock)
+ continue;
+
silc_server_packet_send_dest(server, sock, type, flags,
client->id, SILC_ID_CLIENT,
data, data_len, force_send);
SilcSocketConnection sock,
bool broadcast,
SilcClientID *client_id,
- char *comment,
- SilcClientID *killer)
+ const char *comment,
+ void *killer, SilcIdType killer_type)
{
SilcBuffer idp1;
SilcBuffer idp2;
- idp1 = silc_id_payload_encode((void *)client_id, SILC_ID_CLIENT);
- idp2 = silc_id_payload_encode((void *)killer, SILC_ID_CLIENT);
+ idp1 = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+ idp2 = silc_id_payload_encode(killer, killer_type);
silc_server_send_notify_dest(server, sock, broadcast, (void *)client_id,
SILC_ID_CLIENT, SILC_NOTIFY_TYPE_KILLED,
3, idp1->data, idp1->len,
SilcSocketConnection sock,
bool broadcast,
SilcClientID *client_id,
- char *comment,
- SilcClientID *killer);
+ const char *comment,
+ void *killer, SilcIdType killer_type);
void silc_server_send_notify_umode(SilcServer server,
SilcSocketConnection sock,
bool broadcast,
silc_server_ftp(server, sock, packet);
break;
+ case SILC_PACKET_RESUME_CLIENT:
+ /* Resume client */
+ SILC_LOG_DEBUG(("Resume Client packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_resume_client(server, sock, packet);
+ break;
+
case SILC_PACKET_RESUME_ROUTER:
/* Resume router packet received. This packet is received for backup
router resuming protocol. */
(void *)i, 300, 0,
SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+ client->mode = 0;
client->router = NULL;
client->connection = NULL;
- client->mode = 0;
}
/* Frees user_data pointer from socket connection object. This also sends
SILC_LOG_DEBUG(("Start"));
+ if (client_entry)
+ *client_entry = NULL;
+
/* Decode destination Client ID */
if (!client_id) {
id = silc_id_str2id(id_data, id_len, SILC_ID_CLIENT);
id = silc_id_dup(client_id, SILC_ID_CLIENT);
}
- if (client_entry)
- *client_entry = NULL;
-
/* If the destination belongs to our server we don't have to route
the packet anywhere but to send it to the local destination. */
client = silc_idlist_find_client_by_id(server->local_list, id, TRUE, NULL);
#include "serverincludes.h"
#include "server_internal.h"
+extern char *server_version;
+
/* Removes the client from channels and possibly removes the channels
as well. After removing those channels that exist, their channel
keys are regnerated. This is called only by the function
return TRUE;
}
+
+/* This function is used to send the notify packets and motd to the
+ incoming client connection. */
+
+void silc_server_send_connect_notifys(SilcServer server,
+ SilcSocketConnection sock,
+ SilcClientEntry client)
+{
+ SilcIDListData idata = (SilcIDListData)client;
+
+ /* Send some nice info to the client */
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("Welcome to the SILC Network %s",
+ client->username));
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("Your host is %s, running version %s",
+ server->server_name, server_version));
+
+ if (server->stat.clients && server->stat.servers + 1)
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("There are %d clients on %d servers in SILC "
+ "Network", server->stat.clients,
+ server->stat.servers + 1));
+ if (server->stat.cell_clients && server->stat.cell_servers + 1)
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("There are %d clients on %d server in our cell",
+ server->stat.cell_clients,
+ server->stat.cell_servers + 1));
+ if (server->server_type == SILC_ROUTER) {
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("I have %d clients, %d channels, %d servers and "
+ "%d routers",
+ server->stat.my_clients,
+ server->stat.my_channels,
+ server->stat.my_servers,
+ server->stat.my_routers));
+ } else {
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("I have %d clients and %d channels formed",
+ server->stat.my_clients,
+ server->stat.my_channels));
+ }
+
+ if (server->stat.server_ops || server->stat.router_ops)
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("There are %d server operators and %d router "
+ "operators online",
+ server->stat.server_ops,
+ server->stat.router_ops));
+ if (server->stat.my_router_ops + server->stat.my_server_ops)
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("I have %d operators online",
+ server->stat.my_router_ops +
+ server->stat.my_server_ops));
+
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("Your connection is secured with %s cipher, "
+ "key length %d bits",
+ idata->send_key->cipher->name,
+ idata->send_key->cipher->key_len));
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("Your current nickname is %s",
+ client->nickname));
+
+ /* Send motd */
+ silc_server_send_motd(server, sock);
+}
+
+/* Kill the client indicated by `remote_client' sending KILLED notify
+ to the client, to all channels client has joined and to primary
+ router if needed. The killed client is also removed from all channels. */
+
+void silc_server_kill_client(SilcServer server,
+ SilcClientEntry remote_client,
+ const char *comment,
+ void *killer_id,
+ SilcIdType killer_id_type)
+{
+ SilcBuffer killed, killer;
+
+ /* Send the KILL notify packets. First send it to the channel, then
+ to our primary router and then directly to the client who is being
+ killed right now. */
+
+ killed = silc_id_payload_encode(remote_client->id, SILC_ID_CLIENT);
+ killer = silc_id_payload_encode(killer_id, killer_id_type);
+
+ /* Send KILLED notify to the channels. It is not sent to the client
+ as it will be sent differently destined directly to the client and not
+ to the channel. */
+ silc_server_send_notify_on_channels(server, remote_client,
+ remote_client, SILC_NOTIFY_TYPE_KILLED,
+ 3, killed->data, killed->len,
+ comment, comment ? strlen(comment) : 0,
+ killer->data, killer->len);
+
+ /* Send KILLED notify to primary route */
+ if (!server->standalone)
+ silc_server_send_notify_killed(server, server->router->connection, TRUE,
+ remote_client->id, comment,
+ killer_id, killer_id_type);
+
+ /* Send KILLED notify to the client directly */
+ if (remote_client->connection || remote_client->router)
+ silc_server_send_notify_killed(server, remote_client->connection ?
+ remote_client->connection :
+ remote_client->router->connection, FALSE,
+ remote_client->id, comment,
+ killer_id, killer_id_type);
+
+ /* Remove the client from all channels. This generates new keys to the
+ channels as well. */
+ silc_server_remove_from_channels(server, NULL, remote_client, FALSE,
+ NULL, TRUE);
+
+ /* Remove the client entry, If it is locally connected then we will also
+ disconnect the client here */
+ if (remote_client->connection) {
+ /* Remove locally conneted client */
+ SilcSocketConnection sock = remote_client->connection;
+ silc_server_free_client_data(server, sock, remote_client, FALSE, NULL);
+ silc_server_close_connection(server, sock);
+ } else {
+ /* Update statistics */
+ server->stat.clients--;
+ server->stat.my_clients--;
+ if (server->stat.cell_clients)
+ server->stat.cell_clients--;
+ SILC_OPER_STATS_UPDATE(remote_client, server, SILC_UMODE_SERVER_OPERATOR);
+ SILC_OPER_STATS_UPDATE(remote_client, router, SILC_UMODE_ROUTER_OPERATOR);
+
+ /* Remove remote client */
+ if (!silc_idlist_del_client(server->global_list, remote_client))
+ silc_idlist_del_client(server->local_list, remote_client);
+}
+
+ silc_buffer_free(killer);
+ silc_buffer_free(killed);
+}
SilcClientEntry client,
SilcUInt32 mode);
+/* This function is used to send the notify packets and motd to the
+ incoming client connection. */
+void silc_server_send_connect_notifys(SilcServer server,
+ SilcSocketConnection sock,
+ SilcClientEntry client);
+
+/* Kill the client indicated by `remote_client' sending KILLED notify
+ to the client, to all channels client has joined and to primary
+ router if needed. The killed client is also removed from all channels. */
+void silc_server_kill_client(SilcServer server,
+ SilcClientEntry remote_client,
+ const char *comment,
+ void *killer_id,
+ SilcIdType killer_id_type);
+
#endif /* SERVER_UTIL_H */
<count> option were defined in the query there will be only
<count> many replies from the server.
- The server may return the list of channel the client has joined.
- In this case the list is list of Channel Payloads. The Mode Mask
- in the Channel Payload (see [SILC2] and section 2.3.2.3 for the
- Channel Payload) is the client's mode on the channel. The list
- is encoded by adding the Channel Payloads one after the other.
+ The server may return the list of channels if the client has
+ joined channels. In this case the list is list of Channel
+ Payloads. The Mode Mask in the Channel Payload (see [SILC2] and
+ section 2.3.2.3 for the Channel Payload) is the client's mode
+ on the channel. The list is encoded by adding the Channel
+ Payloads one after the other.
The server may also send client's user mode, idle time, and the
fingerprint of the client's public key. The <fingerprint> is the
16 SILC_COMMAND_UMODE
Max Arguments: 2
- Arguments: (1) <Client ID> (2) <client mode mask>
+ Arguments: (1) <Client ID> (2) [<client mode mask>]
This command is used by client to set/unset modes for itself.
However, there are some modes that the client MUST NOT set itself,
The client MAY set and unset this mode.
+ 0x00000400 SILC_UMODE_DETACHED
+
+ Marks that the client is detached from the SILC network.
+ This means that the actual network connection to the
+ client is lost but the client entry is still valid. The
+ detached client can be resumed at a later time. This
+ mode MUST NOT be set by client. It can only be set when
+ client has issued command SILC_COMMAND_DETACH. The server
+ sets this mode. This mode cannot be unset with this
+ command. It is unset when the client is resuming back to
+ the network and SILC_PACKET_RESUME_CLIENT packet is
+ received.
+
+ This flag MUST NOT be used to determine whether a packet
+ can be sent to the client or not. Only the server that
+ had the original client connection can make the decision
+ by noticising that the network connection is not active.
+ In this case the default case is to discard the packet.
+
+ If the <client mode mask> was not provided this command merely
+ returns the mode mask to the client.
+
+
Reply messages to the command:
Max Arguments: 2
2.3.20 Key Agreement Payload .............................. 43
2.3.21 Resume Router Payload .............................. 44
2.3.22 File Transfer Payload .............................. 44
+ 2.3.23 Resume Client Payload .............................. XXXXXX
2.4 SILC ID Types ............................................. 46
2.5 Packet Encryption And Decryption .......................... 46
2.5.1 Normal Packet Encryption And Decryption ............. 46
Figure 21: Key Agreement Payload
Figure 22: Resume Router Payload
Figure 23: File Transfer Payload
+Figure 24: Resume Client Payload
.ti 0
Payload of the packet: See section 2.3.22 File Transfer Payload
- 28 - 199
+ 28 SILC_PACKET_RESUME_CLIENT
+
+ This packet is used to resume a client back to the network
+ after it has been detached. A client is able to detach from
+ the network but the client is still valid client in the network.
+ The client may then later resume its session back by sending
+ this packet to a server. Routers also use this packet to notify
+ other routers in the network that the detached client has resumed.
+
+ This packet MUST NOT be sent as list and the List flag MUST
+ NOT be set.
+
+ Payload of the packet: See section 2.3.23 Resume Client Payload
+
+
+ 29 - 199
Currently undefined commands.
The payload may only be sent with SILC_PACKET_FTP packet. It MUST NOT
be sent in any other packet type. The following diagram represents the
-File Transfer Payload
+File Transfer Payload.
.in 5
.nf
.in 3
+.ti 0
+2.3.23 Resume Client Payload
+
+This payload is used by client to resume its detached session in the
+SILC Network. A client is able to detach itself from the network by
+sending SILC_COMMAND_DETACH command to its server. The network
+connection to the client is lost but the client remains as valid
+client in the network. The client is able to resume the session back
+by sending this packet and including the old Client ID, and an
+Authentication Payload [SILC1] which the server uses to verify with
+the detached client's public key. This also implies that the
+mandatory authentication method is public key authentication.
+
+Server or router that receives this from the client also sends this,
+without the Authentication Payload, to routers in the network so that
+they know the detached client has resumed. Refer to the [SILC1] for
+detailed description how the detaching and resuming prodecure is
+performed.
+
+The payload may only be sent with SILC_PACKET_RESUME CLIENT packet. It
+MUST NOT be sent in any other packet type. The following diagram
+represents the Resume Client Payload.
+
+.in 5
+.nf
+ 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Client ID Length | |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
+| |
+~ Client ID ~
+| |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| |
+~ Authentication Payload ~
+| |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 24: Resume Client Payload
+
+
+.in 6
+o Client ID Length (1 byte) - The length of the Client ID
+ field not including any other field.
+
+o Client ID (variable length) - The detached client's Client
+ ID. The client that sends this payload must know the Client
+ ID.
+
+o Authentication Payload (variable length) - The authentication
+ payload that the server will verify with the detached client's
+ public key. If the server doesn't know the public key, it must
+ retrieve it for example with SILC_COMMAND_GETKEY command.
+.in 3
+
.ti 0
4.8 Session Key Regeneration .................................. 39
4.9 Command Sending and Reception ............................. 40
4.10 Closing Connection ....................................... 41
+ 4.11 Detaching and Resuming a Session ......................... XXXXX
5 Security Considerations ....................................... 41
6 References .................................................... 42
7 Author's Address .............................................. 44
server's or router's clients.
+.ti 0
+4.11 Detaching and Resuming a Session
+
+SILC protocol provides a possibility for a client to detach itself from
+the network without actually signing off from the network. The client
+connection to the server is closed but the client remains as valid client
+in the network. The client may then later resume its session back from
+any server in the network.
+
+When client wishes to detach from the network it MUST send the
+SILC_COMMAND_DETACH command to its server. The server then MUST set
+SILC_UMODE_DETACHED mode to the client and send SILC_NOTIFY_UMODE_CHANGE
+notify to its primary router, which will then MUST broadcast it further
+to other routers in the network. This user mode indicates that the
+client is detached from the network. Implementations MUST NOT use
+the SILC_UMODE_DETACHED flag to determine whether a packet can be sent
+to the client. All packets MUST still be sent to the client even if
+client is detached from the network. Only the server that originally
+had the active client connection is able to make the decision after it
+notices that the network connection is not active. In this case the
+default case is to discard the packet.
+
+The SILC_UMODE_DETACHED flag cannot be set by client itself directly
+with SILC_COMMAND_UMODE command, but only implicitly by sending the
+SILC_COMMAND_DETACH command. The flag also cannot be unset by the
+client, server or router with SILC_COMMAND_UMODE command, but only
+implicitly by sending and receiving the SILC_PACKET_RESUME_CLIENT
+packet.
+
+When the client wishes to resume its session in the SILC Network it
+connects to a server in the network, which MAY also be a different
+from the original server, and performs normal procedures regarding
+creating a connection as described in section 4.1. After the SKE
+and the Connection Authentication protocols has been successfully
+completed the client MUST NOT send SILC_PACKET_NEW_CLIENT packet, but
+MUST send SILC_PACKET_RESUME_CLIENT packet. This packet is used to
+perform the resuming procedure. The packet MUST include the detached
+client's Client ID, which the client must know. It also includes
+Authentication Payload which includes signature made with the client's
+private key. The signature is computed as defined in the section
+3.9.1. Thus, the authentication method MUST be based in public key
+authentication.
+
+When server receives the SILC_PACKET_RESUME_CLIENT packet it MUST
+verify that the Client ID is valid client and that it has the
+SILC_UMODE_DETACHED mode set. It then MUST verify the Authentication
+Payload with the detached client's public key. If it does not have
+the public key it MUST retrieve it by sending SILC_COMMAND_GETKEY
+command to the server that has the public key from the original
+client connection. The server MUST NOT use the public key received
+in the SKE protocol for this connection. If the signature is valid
+the server MUST unset the SILC_UMODE_DETACHED flag, and send the
+SILC_PACKET_RESUME_CLIENT packet to its primary router. The routers
+MUST broadcast the packet and unset the SILC_UMODE_DETACHED flag
+when the packet is received.
+
+The servers and routers that receives the SILC_PACKET_RESUME_CLIENT
+packet MUST know whether the packet already has been received for
+the client. It is protocol error to attempt to resume the client
+session from more than one server. The implementations could set
+internal flag that indicates that the client is resumed. If router
+receive SILC_PACKET_RESUME_CLIENT packet for client that is already
+resumed the client MUST be killed from the network. This would
+indicate that the client is attempting to resume the session more
+than once which is protocol error. In this case the router sends
+SILC_NOTIFY_TYPE_KILLED to the client. All routers that detect
+the same situation MUST also send the notify for the client.
+
+The servers and routers that receive the SILC_PACKET_RESUME_CLIENT
+must also understand that the client may not be found behind the
+same server that it originally came from. They must update their
+caches according this. The server that now owns the client session
+MUST check whether the Client ID of the resumed client is based
+on the server's Server ID. If it is not it MUST create new Client
+ID and send SILC_NOTIFY_TYPE_NICK_CHANGE to the network. It MUST
+also send the channel keys of all channels that the client is
+joined to the client since it does not have them. Whether the
+Client ID was changed or not the server MUST send SILC_PACKET_NEW_ID
+packet to the client. Only after this the client is resumed back
+to the network and may start sending packets and messages.
+
+It is also possible that the server does not know about the channels
+that the client has joined. In this case it MUST join client internally
+to the channels, generate new channel keys and distribute the keys
+to the channels as described in section 4.4.
+
+It is implementation issue for how long servers keep detached client
+sessions. It is RECOMMENDED that the detached sessions would be
+persistent as long as the server is running.
+
+
.ti 0
5 Security Considerations
client_prvmsg.c \
client_channel.c \
client_ftp.c \
+ client_resume.c \
command.c \
command_reply.c \
idlist.c \
client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
"Error: Could not start key exchange protocol");
silc_net_close_connection(conn->sock->sock);
- client->internal->ops->connect(client, conn, FALSE);
+ client->internal->ops->connect(client, conn, SILC_CLIENT_CONN_ERROR);
return;
}
conn->sock->protocol = protocol;
(SilcClientKEInternalContext *)context;
SilcClient client = (SilcClient)ctx->client;
- client->internal->ops->connect(client, ctx->sock->user_data, FALSE);
+ client->internal->ops->connect(client, ctx->sock->user_data,
+ SILC_CLIENT_CONN_ERROR);
if (ctx->packet)
silc_packet_context_free(ctx->packet);
silc_free(ctx);
silc_free(ctx);
/* Notify application of failure */
- client->internal->ops->connect(client, conn, FALSE);
+ client->internal->ops->connect(client, conn, SILC_CLIENT_CONN_ERROR);
silc_client_del_connection(client, conn);
}
return;
return;
}
- /* Send NEW_CLIENT packet to the server. We will become registered
- to the SILC network after sending this packet and we will receive
- client ID from the server. */
- packet = silc_buffer_alloc(2 + 2 + strlen(client->username) +
- strlen(client->realname));
- silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
- silc_buffer_format(packet,
- SILC_STR_UI_SHORT(strlen(client->username)),
- SILC_STR_UI_XNSTRING(client->username,
- strlen(client->username)),
- SILC_STR_UI_SHORT(strlen(client->realname)),
- SILC_STR_UI_XNSTRING(client->realname,
- strlen(client->realname)),
- SILC_STR_END);
+ if (conn->params.detach_data) {
+ /* Send RESUME_CLIENT packet to the server, which is used to resume
+ old detached session back. */
+ SilcBuffer auth;
+ SilcClientID *old_client_id;
+ unsigned char *old_id;
+ SilcUInt16 old_id_len;
- /* Send the packet */
- silc_client_packet_send(client, ctx->sock, SILC_PACKET_NEW_CLIENT,
- NULL, 0, NULL, NULL,
- packet->data, packet->len, TRUE);
- silc_buffer_free(packet);
+ if (!silc_client_process_detach_data(client, conn, &old_id, &old_id_len))
+ return;
+
+ old_client_id = silc_id_str2id(old_id, old_id_len, SILC_ID_CLIENT);
+ if (!old_client_id) {
+ silc_free(old_id);
+ return;
+ }
+
+ /* Generate authentication data that server will verify */
+ auth = silc_auth_public_key_auth_generate(client->public_key,
+ client->private_key,
+ client->rng, conn->hash,
+ old_client_id, SILC_ID_CLIENT);
+ if (!auth) {
+ silc_free(old_client_id);
+ silc_free(old_id);
+ return;
+ }
+
+ packet = silc_buffer_alloc_size(2 + old_id_len + auth->len);
+ silc_buffer_format(packet,
+ SILC_STR_UI_SHORT(old_id_len),
+ SILC_STR_UI_XNSTRING(old_id, old_id_len),
+ SILC_STR_UI_XNSTRING(auth->data, auth->len),
+ SILC_STR_END);
+
+ /* Send the packet */
+ silc_client_packet_send(client, ctx->sock, SILC_PACKET_RESUME_CLIENT,
+ NULL, 0, NULL, NULL,
+ packet->data, packet->len, TRUE);
+ silc_buffer_free(packet);
+ silc_buffer_free(auth);
+ silc_free(old_client_id);
+ silc_free(old_id);
+ } else {
+ /* Send NEW_CLIENT packet to the server. We will become registered
+ to the SILC network after sending this packet and we will receive
+ client ID from the server. */
+ packet = silc_buffer_alloc(2 + 2 + strlen(client->username) +
+ strlen(client->realname));
+ silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+ silc_buffer_format(packet,
+ SILC_STR_UI_SHORT(strlen(client->username)),
+ SILC_STR_UI_XNSTRING(client->username,
+ strlen(client->username)),
+ SILC_STR_UI_SHORT(strlen(client->realname)),
+ SILC_STR_UI_XNSTRING(client->realname,
+ strlen(client->realname)),
+ SILC_STR_END);
+
+ /* Send the packet */
+ silc_client_packet_send(client, ctx->sock, SILC_PACKET_NEW_CLIENT,
+ NULL, 0, NULL, NULL,
+ packet->data, packet->len, TRUE);
+ silc_buffer_free(packet);
+ }
/* Save remote ID. */
conn->remote_id = ctx->dest_id;
client->nickname, strlen(client->nickname));
}
+/* Client session resuming callback. If the session was resumed
+ this callback is called after the resuming is completed. This
+ will call the `connect' client operation to the application
+ since it has not been called yet. */
+
+static void silc_client_resume_session_cb(SilcClient client,
+ SilcClientConnection conn,
+ bool success,
+ void *context)
+{
+ SilcBuffer sidp;
+
+ /* Notify application that connection is created to server */
+ client->internal->ops->connect(client, conn, success ?
+ SILC_CLIENT_CONN_SUCCESS_RESUME :
+ SILC_CLIENT_CONN_ERROR);
+
+ /* Issue INFO command to fetch the real server name and server
+ information and other stuff. */
+ silc_client_command_register(client, SILC_COMMAND_INFO, NULL, NULL,
+ silc_client_command_reply_info_i, 0,
+ ++conn->cmd_ident);
+ sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
+ silc_client_command_send(client, conn, SILC_COMMAND_INFO,
+ conn->cmd_ident, 1, 2, sidp->data, sidp->len);
+ silc_buffer_free(sidp);
+}
+
/* Processes the received new Client ID from server. Old Client ID is
deleted from cache and new one is added. */
SilcClientConnection conn = (SilcClientConnection)sock->user_data;
int connecting = FALSE;
SilcClientID *client_id = silc_id_payload_get_id(idp);
- SilcBuffer sidp;
if (!conn->local_entry)
connecting = TRUE;
(void *)conn->local_entry, 0, NULL);
if (connecting) {
- /* Send NICK command if the nickname was set by the application (and is
- not same as the username). Send this with little timeout. */
- if (client->nickname && strcmp(client->nickname, client->username))
- silc_schedule_task_add(client->schedule, 0,
- silc_client_send_auto_nick, conn,
- 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-
- /* Issue INFO command to fetch the real server name and server information
- and other stuff. */
- silc_client_command_register(client, SILC_COMMAND_INFO, NULL, NULL,
- silc_client_command_reply_info_i, 0,
- ++conn->cmd_ident);
- sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
- silc_client_command_send(client, conn, SILC_COMMAND_INFO,
- conn->cmd_ident, 1, 2, sidp->data, sidp->len);
- silc_buffer_free(sidp);
-
- /* Notify application of successful connection. We do it here now that
- we've received the Client ID and are allowed to send traffic. */
- client->internal->ops->connect(client, conn, TRUE);
+ if (!conn->params.detach_data) {
+ SilcBuffer sidp;
+
+ /* Send NICK command if the nickname was set by the application (and is
+ not same as the username). Send this with little timeout. */
+ if (client->nickname && strcmp(client->nickname, client->username))
+ silc_schedule_task_add(client->schedule, 0,
+ silc_client_send_auto_nick, conn,
+ 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+
+ /* Notify application of successful connection. We do it here now that
+ we've received the Client ID and are allowed to send traffic. */
+ client->internal->ops->connect(client, conn, SILC_CLIENT_CONN_SUCCESS);
+
+ /* Issue INFO command to fetch the real server name and server
+ information and other stuff. */
+ silc_client_command_register(client, SILC_COMMAND_INFO, NULL, NULL,
+ silc_client_command_reply_info_i, 0,
+ ++conn->cmd_ident);
+ sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
+ silc_client_command_send(client, conn, SILC_COMMAND_INFO,
+ conn->cmd_ident, 1, 2, sidp->data, sidp->len);
+ silc_buffer_free(sidp);
+ } else {
+ /* We are resuming session. Start resolving informations from the
+ server we need to set the client libary in the state before
+ detaching the session. The connect client operation is called
+ after this is successfully completed */
+ silc_client_resume_session(client, conn, silc_client_resume_session_cb,
+ NULL);
+ }
}
}
client->internal->params->connauth_request_secs, 0,
SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
}
-
-SilcBuffer silc_client_get_detach_data(SilcClient client,
- SilcClientConnection conn)
-{
- SilcBuffer detach;
- SilcHashTableList htl;
- SilcChannelUser chu;
-
- SILC_LOG_DEBUG(("Creating detachment data"));
-
- /* Save the nickname, Client ID and user mode in SILC network */
- detach = silc_buffer_alloc_size(2 + strlen(conn->nickname) +
- 2 + conn->local_id_data_len + 4);
- silc_buffer_format(detach,
- SILC_STR_UI_SHORT(strlen(conn->nickname)),
- SILC_STR_UI_XNSTRING(conn->nickname,
- strlen(conn->nickname)),
- SILC_STR_UI_SHORT(conn->local_id_data_len),
- SILC_STR_UI_XNSTRING(conn->local_id_data,
- conn->local_id_data_len),
- SILC_STR_UI_INT(conn->local_entry->mode),
- SILC_STR_END);
-
- /* Save all joined channels */
- silc_hash_table_list(conn->local_entry->channels, &htl);
- while (silc_hash_table_get(&htl, NULL, (void **)&chu)) {
- unsigned char *chid = silc_id_id2str(chu->channel->id, SILC_ID_CHANNEL);
- SilcUInt16 chid_len = silc_id_get_len(chu->channel->id, SILC_ID_CHANNEL);
-
- detach = silc_buffer_realloc(detach, detach->truelen + 2 +
- strlen(chu->channel->channel_name) +
- 2 + chid_len + 4);
- silc_buffer_pull(detach, detach->len);
- silc_buffer_format(detach,
- SILC_STR_UI_SHORT(strlen(chu->channel->channel_name)),
- SILC_STR_UI_XNSTRING(chu->channel->channel_name,
- strlen(chu->channel->channel_name)),
- SILC_STR_UI_SHORT(chid_len),
- SILC_STR_UI_XNSTRING(chid, chid_len),
- SILC_STR_UI_INT(chu->channel->mode),
- SILC_STR_END);
-
- silc_free(chid);
- }
- silc_hash_table_list_reset(&htl);
-
- silc_buffer_push(detach, detach->data - detach->head);
-
- SILC_LOG_HEXDUMP(("Detach data"), detach->data, detach->len);
-
- return detach;
-}
char *silc_client_version;
};
+/* Session resuming callback */
+typedef void (*SilcClientResumeSessionCallback)(SilcClient client,
+ SilcClientConnection conn,
+ bool success,
+ void *context);
+
/* Macros */
/* Registers generic task for file descriptor for reading from network and
void silc_client_ftp(SilcClient client,
SilcSocketConnection sock,
SilcPacketContext *packet);
+SilcBuffer silc_client_get_detach_data(SilcClient client,
+ SilcClientConnection conn);
+bool silc_client_process_detach_data(SilcClient client,
+ SilcClientConnection conn,
+ unsigned char **old_id,
+ SilcUInt16 *old_id_len);
+void silc_client_resume_session(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientResumeSessionCallback callback,
+ void *context);
#endif
tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
if (tmp) {
silc_free(client_id);
- client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
- if (!client_id)
+ id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
+ if (!id)
goto out;
- /* Find killer's 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,
- SILC_ID_CLIENT, client_id);
- goto out;
+ /* Find Client entry */
+ if (id_type == SILC_ID_CLIENT) {
+ /* Find Client entry */
+ client_id = id;
+ client_entry2 = silc_client_get_client_by_id(client, conn,
+ client_id);
+ if (!client_entry) {
+ silc_client_notify_by_server_resolve(client, conn, packet,
+ SILC_ID_CLIENT, client_id);
+ goto out;
+ }
+ } else if (id_type == SILC_ID_SERVER) {
+ /* Find Server entry */
+ server_id = id;
+ server = silc_client_get_server_by_id(client, conn, server_id);
+ if (!server) {
+ silc_client_notify_by_server_resolve(client, conn, packet,
+ SILC_ID_SERVER, server_id);
+ goto out;
+ }
+
+ /* Save the pointer to the client_entry pointer */
+ client_entry2 = (SilcClientEntry)server;
} else {
- if (client_entry2 != conn->local_entry)
- silc_client_nickname_format(client, conn, client_entry2);
+ /* Find Channel entry */
+ channel_id = id;
+ channel = silc_client_get_channel_by_id(client, conn, channel_id);
+ if (!channel) {
+ silc_client_notify_by_server_resolve(client, conn, packet,
+ SILC_ID_CHANNEL, channel_id);
+ goto out;
+ }
+
+ /* Save the pointer to the client_entry pointer */
+ client_entry2 = (SilcClientEntry)channel;
+ silc_free(channel_id);
+ channel_id = NULL;
}
}
/* Notify application. */
client->internal->ops->notify(client, conn, type, client_entry,
- comment, client_entry2);
+ comment, id_type, client_entry2);
if (client_entry != conn->local_entry)
/* Remove the client from all channels and free it */
silc_client_close_connection. */
static void
-silc_connect(SilcClient client, SilcClientConnection conn, int success)
+silc_connect(SilcClient client, SilcClientConnection conn,
+ SilcClientConnectionStatus status);
{
}
--- /dev/null
+/*
+
+ client_resume.c
+
+ Author: Pekka Riikonen <priikone@silcnet.org>
+
+ Copyright (C) 2002 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
+ GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+#include "silcclient.h"
+#include "client_internal.h"
+
+SILC_CLIENT_CMD_REPLY_FUNC(resume);
+SILC_CLIENT_CMD_FUNC(resume_identify);
+SILC_CLIENT_CMD_FUNC(resume_cmode);
+SILC_CLIENT_CMD_FUNC(resume_users);
+
+/* Generates the session detachment data. This data can be used later
+ to resume back to the server. */
+
+SilcBuffer silc_client_get_detach_data(SilcClient client,
+ SilcClientConnection conn)
+{
+ SilcBuffer detach;
+ SilcHashTableList htl;
+ SilcChannelUser chu;
+ int ch_count;
+
+ SILC_LOG_DEBUG(("Creating detachment data"));
+
+ ch_count = silc_hash_table_count(conn->local_entry->channels);
+
+ /* Save the nickname, Client ID and user mode in SILC network */
+ detach = silc_buffer_alloc_size(2 + strlen(conn->nickname) +
+ 2 + conn->local_id_data_len + 4 + 4);
+ silc_buffer_format(detach,
+ SILC_STR_UI_SHORT(strlen(conn->nickname)),
+ SILC_STR_UI_XNSTRING(conn->nickname,
+ strlen(conn->nickname)),
+ SILC_STR_UI_SHORT(conn->local_id_data_len),
+ SILC_STR_UI_XNSTRING(conn->local_id_data,
+ conn->local_id_data_len),
+ SILC_STR_UI_INT(conn->local_entry->mode),
+ SILC_STR_UI_INT(ch_count),
+ SILC_STR_END);
+
+ /* Save all joined channels */
+ silc_hash_table_list(conn->local_entry->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void **)&chu)) {
+ unsigned char *chid = silc_id_id2str(chu->channel->id, SILC_ID_CHANNEL);
+ SilcUInt16 chid_len = silc_id_get_len(chu->channel->id, SILC_ID_CHANNEL);
+
+ detach = silc_buffer_realloc(detach, detach->truelen + 2 +
+ strlen(chu->channel->channel_name) +
+ 2 + chid_len + 4);
+ silc_buffer_pull(detach, detach->len);
+ silc_buffer_pull_tail(detach, 2 + strlen(chu->channel->channel_name) +
+ 2 + chid_len + 4);
+ silc_buffer_format(detach,
+ SILC_STR_UI_SHORT(strlen(chu->channel->channel_name)),
+ SILC_STR_UI_XNSTRING(chu->channel->channel_name,
+ strlen(chu->channel->channel_name)),
+ SILC_STR_UI_SHORT(chid_len),
+ SILC_STR_UI_XNSTRING(chid, chid_len),
+ SILC_STR_UI_INT(chu->channel->mode),
+ SILC_STR_END);
+ silc_free(chid);
+ }
+ silc_hash_table_list_reset(&htl);
+
+ silc_buffer_push(detach, detach->data - detach->head);
+
+ SILC_LOG_HEXDUMP(("Detach data"), detach->data, detach->len);
+
+ return detach;
+}
+
+/* Processes the detachment data. This creates channels and other
+ stuff according the data found in the the connection parameters.
+ This doesn't actually resolve any detailed information from the
+ server. To do that call silc_client_resume_session function.
+ This returns the old detached session client ID. */
+
+bool silc_client_process_detach_data(SilcClient client,
+ SilcClientConnection conn,
+ unsigned char **old_id,
+ SilcUInt16 *old_id_len)
+{
+ SilcBufferStruct detach;
+ SilcUInt32 ch_count;
+ int i, len;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ silc_free(conn->nickname);
+ silc_buffer_set(&detach, conn->params.detach_data,
+ conn->params.detach_data_len);
+
+ SILC_LOG_HEXDUMP(("Detach data"), detach.data, detach.len);
+
+ /* Take the old client ID from the detachment data */
+ len = silc_buffer_unformat(&detach,
+ SILC_STR_UI16_NSTRING_ALLOC(&conn->nickname,
+ NULL),
+ SILC_STR_UI16_NSTRING_ALLOC(old_id, old_id_len),
+ SILC_STR_UI_INT(NULL),
+ SILC_STR_UI_INT(&ch_count),
+ SILC_STR_END);
+ if (len == -1)
+ return FALSE;
+
+ silc_buffer_pull(&detach, len);
+
+ for (i = 0; i < ch_count; i++) {
+ char *channel;
+ unsigned char *chid;
+ SilcUInt16 chid_len;
+ SilcUInt32 ch_mode;
+ SilcChannelID *channel_id;
+ SilcChannelEntry channel_entry;
+
+ len = silc_buffer_unformat(&detach,
+ SILC_STR_UI16_NSTRING_ALLOC(&channel, NULL),
+ SILC_STR_UI16_NSTRING(&chid, &chid_len),
+ SILC_STR_UI_INT(&ch_mode),
+ SILC_STR_END);
+ if (len == -1)
+ return FALSE;
+
+ /* Add new channel */
+ channel_id = silc_id_str2id(chid, chid_len, SILC_ID_CHANNEL);
+ channel_entry = silc_client_get_channel_by_id(client, conn, channel_id);
+ if (!channel_entry) {
+ channel_entry = silc_client_add_channel(client, conn, channel, ch_mode,
+ channel_id);
+ } else {
+ silc_free(channel);
+ silc_free(channel_id);
+ }
+
+ silc_buffer_pull(&detach, len);
+ }
+ silc_buffer_push(&detach, detach.data - detach.head);
+
+ return TRUE;
+}
+
+/* Generic command reply callback */
+
+SILC_CLIENT_CMD_REPLY_FUNC(resume)
+{
+ SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (cmd->callback)
+ (*cmd->callback)(cmd->context, cmd);
+}
+
+/* Resume session context */
+typedef struct {
+ SilcClient client;
+ SilcClientConnection conn;
+ SilcClientResumeSessionCallback callback;
+ void *context;
+ SilcUInt32 channel_count;
+} *SilcClientResumeSession;
+
+/* This function is used to perform the resuming procedure after the
+ client has connected to the server properly and has received the
+ Client ID for the resumed session. This resolves all channels
+ that the resumed client is joined, joined users, users modes
+ and channel modes. The `callback' is called after this procedure
+ is completed. */
+
+void silc_client_resume_session(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientResumeSessionCallback callback,
+ void *context)
+{
+ SilcClientResumeSession session;
+ SilcIDCacheList list;
+ SilcIDCacheEntry entry;
+ SilcChannelEntry channel;
+ SilcBuffer tmp;
+ int i;
+ bool ret;
+
+ SILC_LOG_DEBUG(("Resuming detached session"));
+
+ session = silc_calloc(1, sizeof(*session));
+ if (!session) {
+ callback(client, conn, FALSE, context);
+ return;
+ }
+ session->client = client;
+ session->conn = conn;
+ session->callback = callback;
+ session->context = context;
+
+ /* First, send UMODE commandto get our own user mode in the network */
+ SILC_LOG_DEBUG(("Sending UMODE"));
+ tmp = silc_id_payload_encode(conn->local_entry->id, SILC_ID_CLIENT);
+ silc_client_command_send(client, conn, SILC_COMMAND_UMODE,
+ conn->cmd_ident, 1, 1, tmp->data, tmp->len);
+ silc_buffer_free(tmp);
+
+ /* Second, send IDENTIFY command of all channels we know about. These
+ are the channels we've joined to according our detachment data. */
+ if (silc_idcache_get_all(conn->channel_cache, &list)) {
+ unsigned char **res_argv = NULL;
+ SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
+
+ session->channel_count = silc_idcache_list_count(list);
+
+ ret = silc_idcache_list_first(list, &entry);
+ while (ret) {
+ channel = entry->context;
+ tmp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+ res_argv = silc_realloc(res_argv, sizeof(*res_argv) * (res_argc + 1));
+ res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
+ (res_argc + 1));
+ res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
+ (res_argc + 1));
+ res_argv[res_argc] = silc_memdup(tmp->data, tmp->len);
+ res_argv_lens[res_argc] = tmp->len;
+ res_argv_types[res_argc] = res_argc + 5;
+ res_argc++;
+ silc_buffer_free(tmp);
+ ret = silc_idcache_list_next(list, &entry);
+ }
+ silc_idcache_list_free(list);
+
+ if (res_argc) {
+ /* Send the IDENTIFY command */
+ SILC_LOG_DEBUG(("Sending IDENTIFY"));
+ silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
+ silc_client_command_reply_resume,
+ 0, ++conn->cmd_ident);
+ tmp = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
+ res_argc, res_argv, res_argv_lens,
+ res_argv_types, conn->cmd_ident);
+ silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
+ conn->cmd_ident,
+ silc_client_command_resume_identify,
+ session);
+ silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
+ NULL, 0, NULL, NULL, tmp->data, tmp->len, TRUE);
+
+ for (i = 0; i < res_argc; i++)
+ silc_free(res_argv[i]);
+ silc_free(res_argv);
+ silc_free(res_argv_lens);
+ silc_free(res_argv_types);
+ silc_buffer_free(tmp);
+ }
+ }
+
+ /* Now, we wait for replies to come back and then continue with USERS,
+ CMODE and TOPIC commands. */
+}
+
+/* Received identify reply for a channel entry */
+
+SILC_CLIENT_CMD_FUNC(resume_identify)
+{
+ SilcClientResumeSession session = context;
+ SilcClientCommandReplyContext cmd = context2;
+ SilcClient client = session->client;
+ SilcClientConnection conn = session->conn;
+ unsigned char *tmp;
+ SilcUInt32 tmp_len;
+ SilcChannelEntry channel = NULL;
+ SilcChannelID *channel_id;
+ SilcIDPayload idp;
+ SilcIdType id_type;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+ if (!tmp)
+ goto err;
+
+ if (cmd->error != SILC_STATUS_OK) {
+ /* Delete unknown channel from our cache */
+ if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID) {
+ channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
+ if (channel_id) {
+ channel = silc_client_get_channel_by_id(client, conn, channel_id);
+ if (channel)
+ silc_client_del_channel(client, conn, channel);
+ silc_free(channel_id);
+ }
+ }
+ goto err;
+ }
+
+ idp = silc_id_payload_parse(tmp, tmp_len);
+ if (!idp) {
+ return;
+ }
+ id_type = silc_id_payload_get_type(idp);
+
+ switch (id_type) {
+ case SILC_ID_CHANNEL:
+ channel_id = silc_id_payload_get_id(idp);
+ channel = silc_client_get_channel_by_id(client, conn, channel_id);
+ silc_free(channel_id);
+ break;
+ default:
+ silc_id_payload_free(idp);
+ goto err;
+ break;
+ }
+
+ /* Now, send CMODE command for this channel. We send only this one
+ because this will return also error if we are not currently joined
+ on this channel, plus we get the channel mode. USERS and TOPIC
+ commands are called after this returns. */
+ if (channel) {
+ SILC_LOG_DEBUG(("Sending CMODE"));
+ silc_client_command_register(client, SILC_COMMAND_CMODE, NULL, NULL,
+ silc_client_command_reply_resume, 0,
+ ++conn->cmd_ident);
+ silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
+ conn->cmd_ident, 1, 1, tmp, tmp_len);
+ silc_client_command_pending(conn, SILC_COMMAND_CMODE, conn->cmd_ident,
+ silc_client_command_resume_cmode, session);
+ }
+
+ silc_id_payload_free(idp);
+
+ if (cmd->status != SILC_STATUS_OK &&
+ cmd->status != SILC_STATUS_LIST_END)
+ return;
+
+ /* Unregister this command reply */
+ silc_client_command_unregister(client, SILC_COMMAND_IDENTIFY, NULL,
+ silc_client_command_reply_resume,
+ cmd->ident);
+ return;
+
+ err:
+ session->channel_count--;
+ if (!session->channel_count)
+ session->callback(session->client, session->conn, FALSE,
+ session->context);
+}
+
+/* Received cmode to channel entry */
+
+SILC_CLIENT_CMD_FUNC(resume_cmode)
+{
+ SilcClientResumeSession session = context;
+ SilcClientCommandReplyContext cmd = context2;
+ SilcClient client = session->client;
+ SilcClientConnection conn = session->conn;
+ unsigned char *tmp;
+ SilcChannelID *channel_id;
+ SilcChannelEntry channel;
+ SilcUInt32 len;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ /* Unregister this command reply */
+ silc_client_command_unregister(client, SILC_COMMAND_CMODE, NULL,
+ silc_client_command_reply_resume,
+ cmd->ident);
+
+ if (cmd->error != SILC_STATUS_OK)
+ goto err;
+
+ /* Take Channel ID */
+ tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+ if (!tmp)
+ goto err;
+ channel_id = silc_id_payload_parse_id(tmp, len, NULL);
+ if (!channel_id)
+ goto err;
+
+ /* Get the channel entry */
+ channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
+ if (channel) {
+
+ /* Get channel mode */
+ tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
+ if (tmp)
+ SILC_GET32_MSB(channel->mode, tmp);
+
+ tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+
+ /* And now, we will send USERS to get users on the channel */
+ SILC_LOG_DEBUG(("Sending USERS"));
+ silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
+ silc_client_command_reply_users_i, 0,
+ ++conn->cmd_ident);
+ silc_client_command_send(client, conn, SILC_COMMAND_USERS,
+ conn->cmd_ident, 1, 1, tmp, len);
+ silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
+ silc_client_command_resume_users, session);
+ }
+
+ silc_free(channel_id);
+ return;
+
+ err:
+ session->channel_count--;
+ if (!session->channel_count)
+ session->callback(session->client, session->conn, FALSE,
+ session->context);
+}
+
+/* Received users reply to a channel entry */
+
+SILC_CLIENT_CMD_FUNC(resume_users)
+{
+ SilcClientResumeSession session = context;
+ SilcClientCommandReplyContext cmd = context2;
+ SilcClient client = session->client;
+ SilcClientConnection conn = session->conn;
+ SilcBufferStruct client_id_list, client_mode_list;
+ unsigned char *tmp;
+ SilcUInt32 tmp_len, list_count;
+ SilcChannelEntry channel;
+ SilcChannelID *channel_id = NULL;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ /* Unregister this command reply */
+ silc_client_command_unregister(client, SILC_COMMAND_USERS, NULL,
+ silc_client_command_reply_users_i,
+ cmd->ident);
+
+ if (cmd->error != SILC_STATUS_OK)
+ goto err;
+
+ /* Get channel ID */
+ tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+ if (!tmp) {
+ COMMAND_REPLY_ERROR;
+ goto err;
+ }
+ channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
+ if (!channel_id) {
+ COMMAND_REPLY_ERROR;
+ goto err;
+ }
+
+ /* Get the list count */
+ tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+ if (!tmp) {
+ COMMAND_REPLY_ERROR;
+ goto err;
+ }
+ SILC_GET32_MSB(list_count, tmp);
+
+ /* Get Client ID list */
+ tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
+ if (!tmp) {
+ COMMAND_REPLY_ERROR;
+ goto err;
+ }
+ silc_buffer_set(&client_id_list, tmp, tmp_len);
+
+ /* Get client mode list */
+ tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
+ if (!tmp) {
+ COMMAND_REPLY_ERROR;
+ goto err;
+ }
+ silc_buffer_set(&client_mode_list, tmp, tmp_len);
+
+ /* Get channel entry */
+ channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
+ if (!channel)
+ goto err;
+
+ /* Send fake JOIN command reply to application */
+ client->internal->ops->command_reply(client, conn, cmd->payload, TRUE,
+ SILC_COMMAND_JOIN, cmd->status,
+ channel->channel_name, channel,
+ channel->mode, 0,
+ NULL, NULL, NULL, NULL,
+ channel->hmac, list_count,
+ &client_id_list, client_mode_list);
+
+ /* Send TOPIC for this channel to get the topic */
+ SILC_LOG_DEBUG(("Sending TOPIC"));
+ tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+ silc_client_command_send(client, conn, SILC_COMMAND_TOPIC,
+ conn->cmd_ident, 1, 1, tmp, tmp_len);
+
+ /* Call the completion callback after we've got reply to all of
+ our channels */
+ session->channel_count--;
+ if (!session->channel_count)
+ session->callback(session->client, session->conn, TRUE,
+ session->context);
+
+ silc_free(channel_id);
+ return;
+
+ err:
+ silc_free(channel_id);
+ session->channel_count--;
+ if (!session->channel_count)
+ session->callback(session->client, session->conn, FALSE,
+ session->context);
+}
chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
/* Send the command */
- if (ban)
- buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 2,
- 1, chidp->data, chidp->len,
- type, ban, strlen(ban));
- else
- buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 1,
- 1, chidp->data, chidp->len);
-
+ buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN,
+ ++conn->cmd_ident, 2,
+ 1, chidp->data, chidp->len,
+ type, ban, ban ? strlen(ban) : 0);
silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
0, NULL, NULL, buffer->data, buffer->len, TRUE);
silc_buffer_free(buffer);
silc_client_command_free(cmd);
}
+/* Command DETACH. This is used to detach from the server */
+
+SILC_CLIENT_CMD_FUNC(detach)
+{
+ SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+ SilcClientConnection conn = cmd->conn;
+ SilcBuffer buffer;
+
+ if (!cmd->conn) {
+ SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+ COMMAND_ERROR;
+ goto out;
+ }
+
+ buffer = silc_command_payload_encode_va(SILC_COMMAND_DETACH,
+ ++conn->cmd_ident, 0);
+ silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
+ 0, NULL, NULL, buffer->data, buffer->len, TRUE);
+ silc_buffer_free(buffer);
+
+ /* Notify application */
+ COMMAND;
+
+ out:
+ silc_client_command_free(cmd);
+}
+
/* LEAVE command. Leaves a channel. Client removes itself from a channel. */
SILC_CLIENT_CMD_FUNC(leave)
SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 5);
SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
+ SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
SILC_CLIENT_CMD(users, USERS, "USERS", 2);
SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
SILC_CLIENT_CMDU(kick, KICK, "KICK");
SILC_CLIENT_CMDU(ban, BAN, "BAN");
+ SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
SILC_CLIENT_CMDU(users, USERS, "USERS");
SILC_CLIENT_CMD_FUNC(quit);
SILC_CLIENT_CMD_FUNC(kill);
SILC_CLIENT_CMD_FUNC(info);
-SILC_CLIENT_CMD_FUNC(connect);
SILC_CLIENT_CMD_FUNC(ping);
SILC_CLIENT_CMD_FUNC(oper);
SILC_CLIENT_CMD_FUNC(join);
SILC_CLIENT_CMD_FUNC(cumode);
SILC_CLIENT_CMD_FUNC(kick);
SILC_CLIENT_CMD_FUNC(ban);
-SILC_CLIENT_CMD_FUNC(close);
-SILC_CLIENT_CMD_FUNC(shutdown);
+SILC_CLIENT_CMD_FUNC(detach);
SILC_CLIENT_CMD_FUNC(silcoper);
SILC_CLIENT_CMD_FUNC(leave);
SILC_CLIENT_CMD_FUNC(users);
SILC_CLIENT_CMD_FUNC(getkey);
+SILC_CLIENT_CMD_FUNC(shutdown);
+SILC_CLIENT_CMD_FUNC(close);
+SILC_CLIENT_CMD_FUNC(connect);
+
#endif
{ 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->internal->ops->command_reply args
-#define ARGS cmd->client, cmd->sock->user_data, \
- cmd->payload, TRUE, silc_command_get(cmd->payload), cmd->status
-
-/* Error reply to application. Usage: COMMAND_REPLY_ERROR; */
-#define COMMAND_REPLY_ERROR cmd->client->internal->ops-> \
- command_reply(cmd->client, cmd->sock->user_data, cmd->payload, \
- FALSE, silc_command_get(cmd->payload), cmd->status)
#define SAY cmd->client->internal->ops->say
SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
SilcClientID *client_id;
SilcClientEntry client_entry = NULL;
- int argc;
SilcUInt32 len;
unsigned char *id_data, *tmp;
char *nickname = NULL, *username = NULL;
char *realname = NULL;
SilcUInt32 idle = 0, mode = 0;
- SilcBuffer channels = NULL;
+ SilcBufferStruct channels;
unsigned char *fingerprint;
SilcUInt32 fingerprint_len;
- argc = silc_argument_get_arg_num(cmd->args);
-
id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
if (!id_data) {
if (notify)
}
tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
- if (tmp) {
- channels = silc_buffer_alloc(len);
- silc_buffer_pull_tail(channels, SILC_BUFFER_END(channels));
- silc_buffer_put(channels, tmp, len);
- }
+ if (tmp)
+ silc_buffer_set(&channels, tmp, len);
tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
if (tmp)
/* Notify application */
if (!cmd->callback && notify)
COMMAND_REPLY((ARGS, client_entry, nickname, username, realname,
- channels, mode, idle, fingerprint));
-
- if (channels)
- silc_buffer_free(channels);
+ &channels, mode, idle, fingerprint));
}
/* Received reply for WHOIS command. This maybe called several times
SilcClientEntry client_entry;
SilcServerEntry server_entry;
SilcChannelEntry channel_entry;
- int argc;
SilcUInt32 len;
unsigned char *id_data;
char *name = NULL, *info = NULL;
SilcIDPayload idp = NULL;
SilcIdType id_type;
- argc = silc_argument_get_arg_num(cmd->args);
-
id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
if (!id_data) {
if (notify)
silc_client_command_reply_free(cmd);
}
+SILC_CLIENT_CMD_REPLY_FUNC(detach)
+{
+ SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+ SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+ SilcBuffer detach;
+
+ if (cmd->error != SILC_STATUS_OK) {
+ SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+ "%s", silc_client_command_status_message(cmd->error));
+ COMMAND_REPLY_ERROR;
+ goto out;
+ }
+
+ /* Notify application */
+ COMMAND_REPLY((ARGS));
+
+ /* Generate the detachment data and deliver it to the client in the
+ detach client operation */
+ detach = silc_client_get_detach_data(cmd->client, conn);
+ if (detach) {
+ cmd->client->internal->ops->detach(cmd->client, conn,
+ detach->data, detach->len);
+ silc_buffer_free(detach);
+ }
+
+ out:
+ SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_DETACH);
+ silc_client_command_reply_free(cmd);
+}
+
SILC_CLIENT_CMD_REPLY_FUNC(ban)
{
SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
silc_client_command_reply_users(context, NULL);
}
-/* Reply to USERS command. Received list of client ID's and theirs modes
- on the channel we requested. */
-
-SILC_CLIENT_CMD_REPLY_FUNC(users)
+static int
+silc_client_command_reply_users_save(SilcClientCommandReplyContext cmd,
+ SilcCommandStatus status,
+ bool notify,
+ SilcGetChannelCallback get_channel,
+ SilcCommandCb get_clients)
{
- SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
SilcChannelEntry channel;
SilcClientEntry client_entry;
SilcChannelUser chu;
SilcChannelID *channel_id = NULL;
- SilcBuffer client_id_list = NULL;
- SilcBuffer client_mode_list = NULL;
+ SilcBufferStruct client_id_list, client_mode_list;
unsigned char *tmp;
SilcUInt32 tmp_len, list_count;
int i;
SILC_LOG_DEBUG(("Start"));
- if (cmd->error != SILC_STATUS_OK) {
- SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
- "%s", silc_client_command_status_message(cmd->error));
- COMMAND_REPLY_ERROR;
- goto out;
- }
-
/* Get channel ID */
tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
if (!tmp) {
COMMAND_REPLY_ERROR;
goto out;
}
-
- client_id_list = silc_buffer_alloc(tmp_len);
- silc_buffer_pull_tail(client_id_list, tmp_len);
- silc_buffer_put(client_id_list, tmp, tmp_len);
+ silc_buffer_set(&client_id_list, tmp, tmp_len);
/* Get client mode list */
tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
COMMAND_REPLY_ERROR;
goto out;
}
-
- client_mode_list = silc_buffer_alloc(tmp_len);
- silc_buffer_pull_tail(client_mode_list, tmp_len);
- silc_buffer_put(client_mode_list, tmp, tmp_len);
+ silc_buffer_set(&client_mode_list, tmp, tmp_len);
/* Get channel entry */
channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
if (!channel) {
/* Resolve the channel from server */
silc_client_get_channel_by_id_resolve(cmd->client, conn, channel_id,
- silc_client_command_reply_users_cb,
- cmd);
+ get_channel, cmd);
silc_free(channel_id);
- if (client_id_list)
- silc_buffer_free(client_id_list);
- if (client_mode_list)
- silc_buffer_free(client_mode_list);
- return;
+ return 1;
}
/* Cache the received Client ID's and modes. */
SilcClientID *client_id;
/* Client ID */
- SILC_GET16_MSB(idp_len, client_id_list->data + 2);
+ SILC_GET16_MSB(idp_len, client_id_list.data + 2);
idp_len += 4;
- client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
+ client_id = silc_id_payload_parse_id(client_id_list.data, idp_len, NULL);
if (!client_id)
continue;
/* Mode */
- SILC_GET32_MSB(mode, client_mode_list->data);
+ SILC_GET32_MSB(mode, client_mode_list.data);
/* Check if we have this client cached already. */
client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
if (!client_entry || !client_entry->username || !client_entry->realname) {
if (client_entry) {
if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
- silc_buffer_pull(client_id_list, idp_len);
- silc_buffer_pull(client_mode_list, 4);
+ silc_buffer_pull(&client_id_list, idp_len);
+ silc_buffer_pull(&client_mode_list, 4);
continue;
}
client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
(res_argc + 1));
res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
(res_argc + 1));
- res_argv[res_argc] = client_id_list->data;
+ res_argv[res_argc] = client_id_list.data;
res_argv_lens[res_argc] = idp_len;
res_argv_types[res_argc] = res_argc + 3;
res_argc++;
}
silc_free(client_id);
- silc_buffer_pull(client_id_list, idp_len);
- silc_buffer_pull(client_mode_list, 4);
+ silc_buffer_pull(&client_id_list, idp_len);
+ silc_buffer_pull(&client_mode_list, 4);
}
/* Query the client information from server if the list included clients
command reply we will reprocess this command reply by re-calling this
USERS command reply callback. */
silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
- silc_client_command_reply_users, cmd);
+ get_clients, cmd);
silc_buffer_free(res_cmd);
silc_free(channel_id);
silc_free(res_argv);
silc_free(res_argv_lens);
silc_free(res_argv_types);
- if (client_id_list)
- silc_buffer_free(client_id_list);
- if (client_mode_list)
- silc_buffer_free(client_mode_list);
- return;
+ return 1;
}
- silc_buffer_push(client_id_list, (client_id_list->data -
- client_id_list->head));
- silc_buffer_push(client_mode_list, (client_mode_list->data -
- client_mode_list->head));
+ silc_buffer_push(&client_id_list, (client_id_list.data -
+ client_id_list.head));
+ silc_buffer_push(&client_mode_list, (client_mode_list.data -
+ client_mode_list.head));
/* Notify application */
- COMMAND_REPLY((ARGS, channel, list_count, client_id_list, client_mode_list));
+ if (notify)
+ COMMAND_REPLY((ARGS, channel, list_count, &client_id_list,
+ &client_mode_list));
+
+ out:
+ silc_free(channel_id);
+ return 0;
+}
+
+/* Reply to USERS command. Received list of client ID's and theirs modes
+ on the channel we requested. */
+
+SILC_CLIENT_CMD_REPLY_FUNC(users)
+{
+ SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+ SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (cmd->error != SILC_STATUS_OK) {
+ SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+ "%s", silc_client_command_status_message(cmd->error));
+ COMMAND_REPLY_ERROR;
+ goto out;
+ }
+
+ if (silc_client_command_reply_users_save(cmd, cmd->status, TRUE,
+ silc_client_command_reply_users_cb,
+ silc_client_command_reply_users))
+ return;
out:
SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
silc_client_command_reply_free(cmd);
- silc_free(channel_id);
- if (client_id_list)
- silc_buffer_free(client_id_list);
- if (client_mode_list)
- silc_buffer_free(client_mode_list);
}
/* Received command reply to GETKEY command. WE've received the remote
silc_client_command_reply_free(cmd);
}
+static void silc_client_command_reply_users_i_cb(SilcClient client,
+ SilcClientConnection conn,
+ SilcChannelEntry *channels,
+ SilcUInt32 channels_count,
+ void *context)
+{
+ if (!channels_count) {
+ SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+ SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+
+ cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
+ SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+ "%s", silc_client_command_status_message(cmd->error));
+ COMMAND_REPLY_ERROR;
+ SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
+ silc_client_command_reply_free(cmd);
+ return;
+ }
+
+ silc_client_command_reply_users_i(context, NULL);
+}
+
+SILC_CLIENT_CMD_REPLY_FUNC(users_i)
+{
+ SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (cmd->error != SILC_STATUS_OK)
+ goto out;
+
+ /* Save USERS info */
+ if (silc_client_command_reply_users_save(
+ cmd, cmd->status, FALSE,
+ silc_client_command_reply_users_i_cb,
+ silc_client_command_reply_users_i))
+ return;
+
+ out:
+ SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
+
+ /* Unregister this command reply */
+ silc_client_command_unregister(cmd->client, SILC_COMMAND_USERS,
+ NULL, silc_client_command_reply_users_i,
+ cmd->ident);
+
+ silc_client_command_reply_free(cmd);
+}
/* Private range commands, specific to this implementation (and compatible
- with SILC Server). */
+ with SILC Server >= 0.9). */
SILC_CLIENT_CMD_REPLY_FUNC(connect)
{
SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_SHUTDOWN);
silc_client_command_reply_free(cmd);
}
-
/* Macros */
+/* 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->internal->ops->command_reply args
+#define ARGS cmd->client, cmd->sock->user_data, \
+ cmd->payload, TRUE, silc_command_get(cmd->payload), cmd->status
+
+/* Error reply to application. Usage: COMMAND_REPLY_ERROR; */
+#define COMMAND_REPLY_ERROR cmd->client->internal->ops-> \
+ command_reply(cmd->client, cmd->sock->user_data, cmd->payload, \
+ FALSE, silc_command_get(cmd->payload), cmd->status)
+
/* Macro used to declare command reply functions */
#define SILC_CLIENT_CMD_REPLY_FUNC(func) \
void silc_client_command_reply_##func(void *context, void *context2)
SilcSocketConnection sock,
SilcPacketContext *packet);
char *silc_client_command_status_message(SilcCommandStatus status);
+void silc_client_command_reply_free(SilcClientCommandReplyContext cmd);
SILC_CLIENT_CMD_REPLY_FUNC(whois);
SILC_CLIENT_CMD_REPLY_FUNC(whowas);
SILC_CLIENT_CMD_REPLY_FUNC(identify);
SILC_CLIENT_CMD_REPLY_FUNC(invite);
SILC_CLIENT_CMD_REPLY_FUNC(kill);
SILC_CLIENT_CMD_REPLY_FUNC(info);
-SILC_CLIENT_CMD_REPLY_FUNC(links);
SILC_CLIENT_CMD_REPLY_FUNC(stats);
-SILC_CLIENT_CMD_REPLY_FUNC(users);
-SILC_CLIENT_CMD_REPLY_FUNC(connect);
SILC_CLIENT_CMD_REPLY_FUNC(ping);
-SILC_CLIENT_CMD_REPLY_FUNC(pong);
SILC_CLIENT_CMD_REPLY_FUNC(oper);
SILC_CLIENT_CMD_REPLY_FUNC(join);
SILC_CLIENT_CMD_REPLY_FUNC(motd);
SILC_CLIENT_CMD_REPLY_FUNC(cumode);
SILC_CLIENT_CMD_REPLY_FUNC(kick);
SILC_CLIENT_CMD_REPLY_FUNC(ban);
-SILC_CLIENT_CMD_REPLY_FUNC(close);
-SILC_CLIENT_CMD_REPLY_FUNC(shutdown);
+SILC_CLIENT_CMD_REPLY_FUNC(detach);
SILC_CLIENT_CMD_REPLY_FUNC(silcoper);
SILC_CLIENT_CMD_REPLY_FUNC(leave);
SILC_CLIENT_CMD_REPLY_FUNC(users);
SILC_CLIENT_CMD_REPLY_FUNC(whois_i);
SILC_CLIENT_CMD_REPLY_FUNC(identify_i);
SILC_CLIENT_CMD_REPLY_FUNC(info_i);
+SILC_CLIENT_CMD_REPLY_FUNC(users_i);
+
+SILC_CLIENT_CMD_REPLY_FUNC(connect);
+SILC_CLIENT_CMD_REPLY_FUNC(close);
+SILC_CLIENT_CMD_REPLY_FUNC(shutdown);
#endif
SilcUInt16 idp_len;
SilcClientID *client_id;
SilcClientEntry entry;
+ bool ret;
/* Get Client ID */
SILC_GET16_MSB(idp_len, client_id_list->data + 2);
}
/* Check if we have this client cached already. */
- id_cache = NULL;
- silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id,
- NULL, NULL,
- silc_hash_client_id_compare, NULL,
- &id_cache);
+ ret =
+ silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id,
+ NULL, NULL,
+ silc_hash_client_id_compare, NULL,
+ &id_cache);
/* If we don't have the entry or it has incomplete info, then resolve
it from the server. */
- entry = id_cache ? (SilcClientEntry)id_cache->context : NULL;
- if (!id_cache || !entry->nickname) {
+ if (!ret || !((SilcClientEntry)id_cache->context)->nickname) {
+ entry = ret ? (SilcClientEntry)id_cache->context : NULL;
if (entry) {
if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
} SilcClientMessageType;
/***/
+/****d* silcclient/SilcClientAPI/SilcClientConnectionStatus
+ *
+ * NAME
+ *
+ * typedef enum { ... } SilcClientConnectionStatus
+ *
+ * DESCRIPTION
+ *
+ * This type is returned to the `connect' client operation to indicate
+ * the status of the created connection. It can indicated if it was
+ * successful or whether an error occurred.
+ *
+ * SOURCE
+ */
+typedef enum {
+ SILC_CLIENT_CONN_SUCCESS, /* Successfully connected */
+ SILC_CLIENT_CONN_SUCCESS_RESUME, /* Successfully connected and
+ resumed old detached session */
+ SILC_CLIENT_CONN_ERROR, /* Error occurred during connecting */
+} SilcClientConnectionStatus;
+/***/
+
/****s* silcclient/SilcClientAPI/SilcClientOperations
*
* NAME
/* Called to indicate that connection was either successfully established
or connecting failed. This is also the first time application receives
the SilcClientConnection object which it should save somewhere.
- If the `success' is FALSE the application must always call the function
+ The `status' indicated whether the connection were successful. If it
+ is error value the application must always call the function
silc_client_close_connection. */
- void (*connect)(SilcClient client, SilcClientConnection conn, int success);
+ void (*connect)(SilcClient client, SilcClientConnection conn,
+ SilcClientConnectionStatus status);
/* Called to indicate that connection was disconnected to the server. */
void (*disconnect)(SilcClient client, SilcClientConnection conn);
#define SILC_COMMAND_CUMODE 18
#define SILC_COMMAND_KICK 19
#define SILC_COMMAND_BAN 20
+#define SILC_COMMAND_DETACH 21
#define SILC_COMMAND_SILCOPER 23
#define SILC_COMMAND_LEAVE 24
#define SILC_COMMAND_USERS 25
#define SILC_UMODE_ROBOT 0x00000080 /* Client is a robot */
#define SILC_UMODE_ANONYMOUS 0x00000100 /* Client is anonymous */
#define SILC_UMODE_BLOCK_PRIVMSG 0x00000200 /* Client blocks privmsgs */
+#define SILC_UMODE_DETACHED 0x00000400 /* Client is detached */
/***/
#endif
#define SILC_PACKET_KEY_AGREEMENT 25 /* Key Agreement request */
#define SILC_PACKET_RESUME_ROUTER 26 /* Backup router resume */
#define SILC_PACKET_FTP 27 /* File Transfer */
+#define SILC_PACKET_RESUME_CLIENT 28 /* Client resume */
#define SILC_PACKET_PRIVATE 200 /* Private range start */
#define SILC_PACKET_MAX 255 /* RESERVED */