/*
- client.c
+ client.c
- Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+ Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 1997 - 2001 Pekka Riikonen
+ Copyright (C) 1997 - 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; either version 2 of the License, or
- (at your option) any later version.
-
+ 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
*/
/* $Id$ */
-#include "clientlibincludes.h"
+#include "silcincludes.h"
+#include "silcclient.h"
#include "client_internal.h"
/* Static task callback prototypes */
SILC_TASK_CALLBACK(silc_client_connect_to_server_start);
SILC_TASK_CALLBACK(silc_client_connect_to_server_second);
SILC_TASK_CALLBACK(silc_client_connect_to_server_final);
-SILC_TASK_CALLBACK(silc_client_packet_parse_real);
SILC_TASK_CALLBACK(silc_client_rekey_callback);
SILC_TASK_CALLBACK(silc_client_rekey_final);
-static void silc_client_packet_parse(SilcPacketParserContext *parser_context);
+static bool silc_client_packet_parse(SilcPacketParserContext *parser_context,
+ void *context);
static void silc_client_packet_parse_type(SilcClient client,
SilcSocketConnection sock,
SilcPacketContext *packet);
+void silc_client_resolve_auth_method(bool success,
+ SilcProtocolAuthMeth auth_meth,
+ const unsigned char *auth_data,
+ SilcUInt32 auth_data_len, void *context);
/* Allocates new client object. This has to be done before client may
work. After calling this one must call silc_client_init to initialize
new_client = silc_calloc(1, sizeof(*new_client));
new_client->application = application;
- new_client->ops = ops;
- new_client->silc_client_version = strdup(silc_version);
- new_client->params = silc_calloc(1, sizeof(*new_client->params));
+
+ new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
+ new_client->internal->ops = ops;
+ new_client->internal->params =
+ silc_calloc(1, sizeof(*new_client->internal->params));
+ new_client->internal->silc_client_version = strdup(silc_version);
if (params)
- memcpy(new_client->params, params, sizeof(*params));
+ memcpy(new_client->internal->params, params, sizeof(*params));
+
+ if (!new_client->internal->params->task_max)
+ new_client->internal->params->task_max = 200;
+
+ if (!new_client->internal->params->rekey_secs)
+ new_client->internal->params->rekey_secs = 3600;
- if (!new_client->params->rekey_secs)
- new_client->params->rekey_secs = 3600;
+ if (!new_client->internal->params->connauth_request_secs)
+ new_client->internal->params->connauth_request_secs = 2;
+
+ new_client->internal->params->
+ nickname_format[sizeof(new_client->internal->
+ params->nickname_format) - 1] = 0;
return new_client;
}
if (client->rng)
silc_rng_free(client->rng);
- silc_free(client->silc_client_version);
- silc_free(client->params);
+ silc_free(client->internal->params);
+ silc_free(client->internal->silc_client_version);
+ silc_free(client->internal);
silc_free(client);
}
}
silc_hash_alloc("sha1", &client->sha1hash);
/* Initialize none cipher */
- silc_cipher_alloc("none", &client->none_cipher);
+ silc_cipher_alloc("none", &client->internal->none_cipher);
/* Initialize random number generator */
client->rng = silc_rng_alloc();
silc_client_protocols_register();
/* Initialize the scheduler */
- client->schedule = silc_schedule_init(client->params->task_max ?
- client->params->task_max : 200);
+ client->schedule =
+ silc_schedule_init(client->internal->params->task_max ?
+ client->internal->params->task_max : 200, client);
if (!client->schedule)
return FALSE;
+ /* Register commands */
+ silc_client_commands_register(client);
+
return TRUE;
}
silc_schedule_uninit(client->schedule);
silc_client_protocols_unregister();
+ silc_client_commands_unregister(client);
SILC_LOG_DEBUG(("Client stopped"));
}
silc_schedule(client->schedule);
}
+/* Runs the client and returns immeadiately. This function is used when
+ the SILC Client object indicated by the `client' is run under some
+ other scheduler, or event loop or main loop. On GUI applications,
+ for example this may be desired to use to run the client under the
+ GUI application's main loop. Typically the GUI application would
+ register an idle task that calls this function multiple times in
+ a second to quickly process the SILC specific data. */
+
+void silc_client_run_one(SilcClient client)
+{
+ /* Run the scheduler once. */
+ silc_schedule_one(client->schedule, 0);
+}
+
+static void silc_client_entry_destructor(SilcIDCache cache,
+ SilcIDCacheEntry entry)
+{
+ silc_free(entry->name);
+}
+
/* Allocates and adds new connection to the client. This adds the allocated
connection to the connection table and returns a pointer to it. A client
can have multiple connections to multiple servers. Every connection must
application performed the connecting outside the library. The library
however may use this internally. */
-SilcClientConnection silc_client_add_connection(SilcClient client,
- char *hostname,
- int port,
- void *context)
+SilcClientConnection
+silc_client_add_connection(SilcClient client,
+ SilcClientConnectionParams *params,
+ char *hostname, int port, void *context)
{
SilcClientConnection conn;
int i;
+ SILC_LOG_DEBUG(("Adding new connection to %s:%d", hostname, port));
+
conn = silc_calloc(1, sizeof(*conn));
/* Initialize ID caches */
- conn->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT, NULL);
+ conn->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
+ silc_client_entry_destructor);
conn->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL);
conn->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER, NULL);
conn->client = client;
conn->remote_port = port;
conn->context = context;
conn->pending_commands = silc_dlist_init();
+ conn->ftp_sessions = silc_dlist_init();
+
+ if (params) {
+ if (params->detach_data)
+ conn->params.detach_data = silc_memdup(params->detach_data,
+ params->detach_data_len);
+ conn->params.detach_data_len = params->detach_data_len;
+ }
/* Add the connection to connections table */
- for (i = 0; i < client->conns_count; i++)
- if (client->conns && !client->conns[i]) {
- client->conns[i] = conn;
+ for (i = 0; i < client->internal->conns_count; i++)
+ if (client->internal->conns && !client->internal->conns[i]) {
+ client->internal->conns[i] = conn;
return conn;
}
- client->conns = silc_realloc(client->conns, sizeof(*client->conns)
- * (client->conns_count + 1));
- client->conns[client->conns_count] = conn;
- client->conns_count++;
+ client->internal->conns =
+ silc_realloc(client->internal->conns, sizeof(*client->internal->conns)
+ * (client->internal->conns_count + 1));
+ client->internal->conns[client->internal->conns_count] = conn;
+ client->internal->conns_count++;
return conn;
}
{
int i;
- for (i = 0; i < client->conns_count; i++)
- if (client->conns[i] == conn) {
+ for (i = 0; i < client->internal->conns_count; i++)
+ if (client->internal->conns[i] == conn) {
+
+ silc_idcache_free(conn->client_cache);
+ silc_idcache_free(conn->channel_cache);
+ silc_idcache_free(conn->server_cache);
if (conn->pending_commands)
silc_dlist_uninit(conn->pending_commands);
+ silc_free(conn->remote_host);
+ if (conn->ftp_sessions)
+ silc_dlist_uninit(conn->ftp_sessions);
silc_free(conn);
- client->conns[i] = NULL;
+
+ client->internal->conns[i] = NULL;
}
}
{
int i;
- if (!client->sockets) {
- client->sockets = silc_calloc(1, sizeof(*client->sockets));
- client->sockets[0] = silc_socket_dup(sock);
- client->sockets_count = 1;
+ if (!client->internal->sockets) {
+ client->internal->sockets =
+ silc_calloc(1, sizeof(*client->internal->sockets));
+ client->internal->sockets[0] = silc_socket_dup(sock);
+ client->internal->sockets_count = 1;
return;
}
- for (i = 0; i < client->sockets_count; i++) {
- if (client->sockets[i] == NULL) {
- client->sockets[i] = silc_socket_dup(sock);
+ for (i = 0; i < client->internal->sockets_count; i++) {
+ if (client->internal->sockets[i] == NULL) {
+ client->internal->sockets[i] = silc_socket_dup(sock);
return;
}
}
- client->sockets = silc_realloc(client->sockets, sizeof(*client->sockets) *
- (client->sockets_count + 1));
- client->sockets[client->sockets_count] = silc_socket_dup(sock);
- client->sockets_count++;
+ client->internal->sockets =
+ silc_realloc(client->internal->sockets,
+ sizeof(*client->internal->sockets) *
+ (client->internal->sockets_count + 1));
+ client->internal->sockets[client->internal->sockets_count] =
+ silc_socket_dup(sock);
+ client->internal->sockets_count++;
}
/* Deletes listener socket from the listener sockets table. */
{
int i;
- if (!client->sockets)
+ if (!client->internal->sockets)
return;
- for (i = 0; i < client->sockets_count; i++) {
- if (client->sockets[i] == sock) {
+ for (i = 0; i < client->internal->sockets_count; i++) {
+ if (client->internal->sockets[i] == sock) {
silc_socket_free(sock);
- client->sockets[i] = NULL;
+ client->internal->sockets[i] = NULL;
return;
}
}
/* XXX In the future we should give up this non-blocking connect all
together and use threads instead. */
/* Create connection to server asynchronously */
- sock = silc_net_create_connection_async(ctx->port, ctx->host);
+ sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host);
if (sock < 0)
return -1;
case then this function is not used at all. When the connecting is
done the `connect' client operation is called. */
-int silc_client_connect_to_server(SilcClient client, int port,
- char *host, void *context)
+int silc_client_connect_to_server(SilcClient client,
+ SilcClientConnectionParams *params,
+ int port, char *host, void *context)
{
SilcClientInternalConnectContext *ctx;
SilcClientConnection conn;
SILC_LOG_DEBUG(("Connecting to port %d of server %s",
port, host));
- conn = silc_client_add_connection(client, host, port, context);
+ conn = silc_client_add_connection(client, params, host, port, context);
- client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
- "Connecting to port %d of server %s", port, host);
+ client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
+ "Connecting to port %d of server %s", port, host);
/* Allocate internal context for connection process. This is
needed as we are doing async connecting. */
return sock;
}
-/* Start SILC Key Exchange (SKE) protocol to negotiate shared secret
- key material between client and server. This function can be called
- directly if application is performing its own connecting and does not
- use the connecting provided by this library. This function is normally
- used only if the application performed the connecting outside the library.
- The library however may use this internally. */
+/* Socket hostname and IP lookup callback that is called before actually
+ starting the key exchange. The lookup is called from the function
+ silc_client_start_key_exchange. */
-int silc_client_start_key_exchange(SilcClient client,
- SilcClientConnection conn,
- int fd)
+static void silc_client_start_key_exchange_cb(SilcSocketConnection sock,
+ void *context)
{
+ SilcClientConnection conn = (SilcClientConnection)context;
+ SilcClient client = conn->client;
SilcProtocol protocol;
SilcClientKEInternalContext *proto_ctx;
- void *context;
- /* Allocate new socket connection object */
- silc_socket_alloc(fd, SILC_SOCKET_TYPE_SERVER, (void *)conn, &conn->sock);
+ SILC_LOG_DEBUG(("Start"));
- conn->nickname = strdup(client->username);
- conn->sock->hostname = conn->remote_host;
- conn->sock->ip = strdup(conn->remote_host);
+ if (conn->sock->hostname) {
+ silc_free(conn->remote_host);
+ conn->remote_host = strdup(conn->sock->hostname);
+ } else {
+ conn->sock->hostname = strdup(conn->remote_host);
+ }
+ if (!conn->sock->ip)
+ conn->sock->ip = strdup(conn->sock->hostname);
conn->sock->port = conn->remote_port;
/* Allocate internal Key Exchange context. This is sent to the
&protocol, (void *)proto_ctx,
silc_client_connect_to_server_second);
if (!protocol) {
- client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
- "Error: Could not start authentication protocol");
- return FALSE;
+ 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, SILC_CLIENT_CONN_ERROR);
+ return;
}
conn->sock->protocol = protocol;
be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
later when outgoing data is available. */
context = (void *)client;
- SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(fd);
+ SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(conn->sock->sock);
/* Execute the protocol */
silc_protocol_execute(protocol, client->schedule, 0, 0);
- return TRUE;
+}
+
+/* Start SILC Key Exchange (SKE) protocol to negotiate shared secret
+ key material between client and server. This function can be called
+ directly if application is performing its own connecting and does not
+ use the connecting provided by this library. This function is normally
+ used only if the application performed the connecting outside the library.
+ The library however may use this internally. */
+
+void silc_client_start_key_exchange(SilcClient client,
+ SilcClientConnection conn,
+ int fd)
+{
+ /* Allocate new socket connection object */
+ silc_socket_alloc(fd, SILC_SOCKET_TYPE_SERVER, (void *)conn, &conn->sock);
+
+ /* Sometimes when doing quick reconnects the new socket may be same as
+ the old one and there might be pending stuff for the old socket.
+ If new one is same then those pending sutff might cause problems.
+ Make sure they do not do that. */
+ silc_schedule_task_del_by_fd(client->schedule, fd);
+
+ conn->nickname = (client->nickname ? strdup(client->nickname) :
+ strdup(client->username));
+
+ /* Resolve the remote hostname and IP address for our socket connection */
+ silc_socket_host_lookup(conn->sock, FALSE, silc_client_start_key_exchange_cb,
+ conn, client->schedule);
+}
+
+/* Callback called when error has occurred during connecting to the server.
+ The `connect' client operation will be called. */
+
+SILC_TASK_CALLBACK(silc_client_connect_failure)
+{
+ SilcClientKEInternalContext *ctx =
+ (SilcClientKEInternalContext *)context;
+ SilcClient client = (SilcClient)ctx->client;
+
+ 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);
}
/* Start of the connection to the remote server. This is called after
if (opt != 0) {
if (ctx->tries < 2) {
/* Connection failed but lets try again */
- client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
- "Could not connect to server %s: %s",
- ctx->host, strerror(opt));
- client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
- "Connecting to port %d of server %s resumed",
- ctx->port, ctx->host);
+ client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+ "Could not connect to server %s: %s",
+ ctx->host, strerror(opt));
+ client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
+ "Connecting to port %d of server %s resumed",
+ ctx->port, ctx->host);
/* Unregister old connection try */
silc_schedule_unset_listen_fd(client->schedule, fd);
ctx->tries++;
} else {
/* Connection failed and we won't try anymore */
- client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
- "Could not connect to server %s: %s",
- ctx->host, strerror(opt));
+ client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+ "Could not connect to server %s: %s",
+ ctx->host, strerror(opt));
silc_schedule_unset_listen_fd(client->schedule, fd);
silc_net_close_connection(fd);
silc_schedule_task_del(client->schedule, ctx->task);
silc_free(ctx);
/* Notify application of failure */
- client->ops->connect(client, conn, FALSE);
+ client->internal->ops->connect(client, conn, SILC_CLIENT_CONN_ERROR);
silc_client_del_connection(client, conn);
}
return;
silc_schedule_task_del(client->schedule, ctx->task);
silc_free(ctx);
- if (!silc_client_start_key_exchange(client, conn, fd)) {
- silc_net_close_connection(fd);
- client->ops->connect(client, conn, FALSE);
- }
+ silc_client_start_key_exchange(client, conn, fd);
}
/* Second part of the connecting to the server. This executed
silc_socket_free(ctx->sock);
/* Notify application of failure */
- client->ops->connect(client, ctx->sock->user_data, FALSE);
- silc_free(ctx);
+ silc_schedule_task_add(client->schedule, ctx->sock->sock,
+ silc_client_connect_failure, ctx,
+ 0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
return;
}
ctx->ske->prop->pkcs,
ctx->ske->prop->hash,
ctx->ske->prop->hmac,
- ctx->ske->prop->group);
+ ctx->ske->prop->group,
+ ctx->responder);
silc_ske_free_key_material(ctx->keymat);
/* Allocate internal context for the authentication protocol. This
proto_ctx->dest_id_type = ctx->dest_id_type;
proto_ctx->dest_id = ctx->dest_id;
- /* Resolve the authentication method to be used in this connection */
- if (!client->ops->get_auth_method(client, sock->user_data, sock->hostname,
- sock->port, &proto_ctx->auth_meth,
- &proto_ctx->auth_data,
- &proto_ctx->auth_data_len))
- proto_ctx->auth_meth = SILC_AUTH_NONE;
-
/* Free old protocol as it is finished now */
silc_protocol_free(protocol);
if (ctx->packet)
silc_free(ctx);
sock->protocol = NULL;
+ /* Resolve the authentication method to be used in this connection. The
+ completion callback is called after the application has resolved
+ the authentication method. */
+ client->internal->ops->get_auth_method(client, sock->user_data,
+ sock->hostname,
+ sock->port,
+ silc_client_resolve_auth_method,
+ proto_ctx);
+}
+
+/* Authentication method resolving callback. Application calls this function
+ after we've called the client->internal->ops->get_auth_method
+ client operation to resolve the authentication method. We will continue
+ the executiong of the protocol in this function. */
+
+void silc_client_resolve_auth_method(bool success,
+ SilcProtocolAuthMeth auth_meth,
+ const unsigned char *auth_data,
+ SilcUInt32 auth_data_len, void *context)
+{
+ SilcClientConnAuthInternalContext *proto_ctx =
+ (SilcClientConnAuthInternalContext *)context;
+ SilcClient client = (SilcClient)proto_ctx->client;
+
+ if (!success)
+ auth_meth = SILC_AUTH_NONE;
+
+ proto_ctx->auth_meth = auth_meth;
+
+ if (auth_data && auth_data_len) {
+ proto_ctx->auth_data = silc_memdup(auth_data, auth_data_len);
+ proto_ctx->auth_data_len = auth_data_len;
+ }
+
/* Allocate the authenteication protocol and execute it. */
silc_protocol_alloc(SILC_PROTOCOL_CLIENT_CONNECTION_AUTH,
- &sock->protocol, (void *)proto_ctx,
+ &proto_ctx->sock->protocol, (void *)proto_ctx,
silc_client_connect_to_server_final);
/* Execute the protocol */
- silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
+ silc_protocol_execute(proto_ctx->sock->protocol, client->schedule, 0, 0);
}
/* Finalizes the connection to the remote SILC server. This is called
protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
/* Error occured during protocol */
SILC_LOG_DEBUG(("Error during authentication protocol"));
- silc_protocol_free(protocol);
- if (ctx->auth_data)
- silc_free(ctx->auth_data);
- if (ctx->ske)
- silc_ske_free(ctx->ske);
- if (ctx->dest_id)
- silc_free(ctx->dest_id);
- conn->sock->protocol = NULL;
- silc_socket_free(ctx->sock);
-
- /* Notify application of failure */
- client->ops->connect(client, ctx->sock->user_data, FALSE);
- silc_free(ctx);
- return;
+ goto err;
}
- /* 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;
+
+ if (!silc_client_process_detach_data(client, conn, &old_id, &old_id_len))
+ goto err;
+
+ old_client_id = silc_id_str2id(old_id, old_id_len, SILC_ID_CLIENT);
+ if (!old_client_id) {
+ silc_free(old_id);
+ goto err;
+ }
- /* 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);
+ /* 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);
+ goto err;
+ }
+
+ 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;
conn->remote_id_data_len = silc_id_get_len(ctx->dest_id, SILC_ID_SERVER);
/* Register re-key timeout */
- conn->rekey->timeout = client->params->rekey_secs;
+ conn->rekey->timeout = client->internal->params->rekey_secs;
conn->rekey->context = (void *)client;
silc_schedule_task_add(client->schedule, conn->sock->sock,
- silc_client_rekey_callback,
- (void *)conn->sock, conn->rekey->timeout, 0,
- SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+ silc_client_rekey_callback,
+ (void *)conn->sock, conn->rekey->timeout, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
silc_protocol_free(protocol);
- if (ctx->auth_data)
- silc_free(ctx->auth_data);
+ silc_free(ctx->auth_data);
if (ctx->ske)
silc_ske_free(ctx->ske);
silc_socket_free(ctx->sock);
silc_free(ctx);
conn->sock->protocol = NULL;
+ return;
+
+ err:
+ silc_protocol_free(protocol);
+ silc_free(ctx->auth_data);
+ silc_free(ctx->dest_id);
+ if (ctx->ske)
+ silc_ske_free(ctx->ske);
+ conn->sock->protocol = NULL;
+ silc_socket_free(ctx->sock);
+
+ /* Notify application of failure */
+ silc_schedule_task_add(client->schedule, ctx->sock->sock,
+ silc_client_connect_failure, ctx,
+ 0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
}
/* Internal routine that sends packet or marks packet to be sent. This
int silc_client_packet_send_real(SilcClient client,
SilcSocketConnection sock,
- bool force_send,
- bool flush)
+ bool force_send)
{
int ret;
/* If rekey protocol is active we must assure that all packets are
sent through packet queue. */
- if (flush == FALSE && SILC_CLIENT_IS_REKEY(sock))
+ if (SILC_CLIENT_IS_REKEY(sock))
+ force_send = FALSE;
+
+ /* If outbound data is already pending do not force send */
+ if (SILC_IS_OUTBUF_PENDING(sock))
force_send = FALSE;
/* Send the packet */
/* Packet sending */
if (type == SILC_TASK_WRITE) {
- SILC_LOG_DEBUG(("Writing data to connection"));
-
- if (sock->outbuf->data - sock->outbuf->head)
- silc_buffer_push(sock->outbuf,
- sock->outbuf->data - sock->outbuf->head);
+ /* Do not send data to disconnected connection */
+ if (SILC_IS_DISCONNECTED(sock))
+ return;
- ret = silc_client_packet_send_real(client, sock, TRUE, TRUE);
+ ret = silc_packet_send(sock, TRUE);
/* If returned -2 could not write to connection now, will do
it later. */
if (ret == -2)
return;
+
+ /* Error */
+ if (ret == -1)
+ return;
/* The packet has been sent and now it is time to set the connection
back to only for input. When there is again some outgoing data
/* Packet receiving */
if (type == SILC_TASK_READ) {
- SILC_LOG_DEBUG(("Reading data from connection"));
-
/* Read data from network */
ret = silc_packet_receive(sock);
if (ret < 0)
/* If connection is disconnecting already we will finally
close the connection */
if (SILC_IS_DISCONNECTING(sock)) {
- if (sock == conn->sock)
- client->ops->disconnect(client, conn);
- silc_client_close_connection(client, sock, conn);
+ if (sock == conn->sock && sock->type != SILC_SOCKET_TYPE_CLIENT)
+ client->internal->ops->disconnect(client, conn, 0, NULL);
+ silc_client_close_connection_real(client, sock, conn);
return;
}
SILC_LOG_DEBUG(("EOF from connection %d", sock->sock));
- if (sock == conn->sock)
- client->ops->disconnect(client, conn);
- silc_client_close_connection(client, sock, conn);
+ if (sock == conn->sock && sock->type != SILC_SOCKET_TYPE_CLIENT)
+ client->internal->ops->disconnect(client, conn, 0, NULL);
+ silc_client_close_connection_real(client, sock, conn);
return;
}
/* Process the packet. This will call the parser that will then
decrypt and parse the packet. */
if (sock->type != SILC_SOCKET_TYPE_UNKNOWN)
- silc_packet_receive_process(sock, conn->receive_key, conn->hmac_receive,
+ silc_packet_receive_process(sock, FALSE, conn->receive_key,
+ conn->hmac_receive, conn->psn_receive,
silc_client_packet_parse, client);
else
- silc_packet_receive_process(sock, NULL, NULL,
+ silc_packet_receive_process(sock, FALSE, NULL, NULL, 0,
silc_client_packet_parse, client);
}
}
-/* Callback function that the silc_packet_decrypt will call to make the
- decision whether the packet is normal or special packet. We will
- return TRUE if it is normal and FALSE if it is special */
-
-static int silc_client_packet_decrypt_check(SilcPacketType packet_type,
- SilcBuffer buffer,
- SilcPacketContext *packet,
- void *context)
-{
-
- /* Packet is normal packet, if:
-
- 1) packet is private message packet and does not have private key set
- 2) is other packet than channel message packet
-
- all other packets are special packets
- */
-
- if (packet_type == SILC_PACKET_PRIVATE_MESSAGE &&
- (buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
- return FALSE;
-
- if (packet_type != SILC_PACKET_CHANNEL_MESSAGE)
- return TRUE;
-
- return FALSE;
-}
-
-/* Parses whole packet, received earlier. */
+/* Parser callback called by silc_packet_receive_process. Thie merely
+ registers timeout that will handle the actual parsing when appropriate. */
-SILC_TASK_CALLBACK(silc_client_packet_parse_real)
+static bool silc_client_packet_parse(SilcPacketParserContext *parser_context,
+ void *context)
{
- SilcPacketParserContext *parse_ctx = (SilcPacketParserContext *)context;
- SilcClient client = (SilcClient)parse_ctx->context;
- SilcPacketContext *packet = parse_ctx->packet;
- SilcBuffer buffer = packet->buffer;
- SilcSocketConnection sock = parse_ctx->sock;
+ SilcClient client = (SilcClient)context;
+ SilcSocketConnection sock = parser_context->sock;
SilcClientConnection conn = (SilcClientConnection)sock->user_data;
- int ret;
+ SilcPacketContext *packet = parser_context->packet;
+ SilcPacketType ret;
- SILC_LOG_DEBUG(("Start"));
+ if (conn && conn->hmac_receive && conn->sock == sock)
+ conn->psn_receive = parser_context->packet->sequence + 1;
- /* Decrypt the received packet */
- if (sock->type != SILC_SOCKET_TYPE_UNKNOWN)
- ret = silc_packet_decrypt(conn->receive_key, conn->hmac_receive,
- buffer, packet,
- silc_client_packet_decrypt_check, parse_ctx);
+ /* Parse the packet immediately */
+ if (parser_context->normal)
+ ret = silc_packet_parse(packet, conn->receive_key);
else
- ret = silc_packet_decrypt(NULL, NULL, buffer, packet,
- silc_client_packet_decrypt_check, parse_ctx);
+ ret = silc_packet_parse_special(packet, conn->receive_key);
- if (ret < 0)
- goto out;
-
- if (ret == 0) {
- /* Parse the packet. Packet type is returned. */
- ret = silc_packet_parse(packet);
- } else {
- /* Parse the packet header in special way as this is "special"
- packet type. */
- ret = silc_packet_parse_special(packet);
+ if (ret == SILC_PACKET_NONE) {
+ silc_packet_context_free(packet);
+ silc_free(parser_context);
+ return FALSE;
+ }
+
+ /* If protocol for this connection is key exchange or rekey then we'll
+ process all packets synchronously, since there might be packets in
+ queue that we are not able to decrypt without first processing the
+ packets before them. */
+ if ((ret == SILC_PACKET_REKEY || ret == SILC_PACKET_REKEY_DONE) ||
+ (sock->protocol && sock->protocol->protocol &&
+ (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE ||
+ sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY))) {
+
+ /* Parse the incoming packet type */
+ silc_client_packet_parse_type(client, sock, packet);
+ silc_packet_context_free(packet);
+ silc_free(parser_context);
+
+ /* Reprocess the buffer since we'll return FALSE. This is because
+ the `conn->receive_key' might have become valid by processing
+ the previous packet */
+ if (sock->type != SILC_SOCKET_TYPE_UNKNOWN)
+ silc_packet_receive_process(sock, FALSE, conn->receive_key,
+ conn->hmac_receive, conn->psn_receive,
+ silc_client_packet_parse, client);
+ else
+ silc_packet_receive_process(sock, FALSE, NULL, NULL, 0,
+ silc_client_packet_parse, client);
+
+ return FALSE;
}
-
- if (ret == SILC_PACKET_NONE)
- goto out;
/* Parse the incoming packet type */
silc_client_packet_parse_type(client, sock, packet);
-
- out:
- /* silc_buffer_clear(sock->inbuf); */
silc_packet_context_free(packet);
- silc_free(parse_ctx);
-}
-
-/* Parser callback called by silc_packet_receive_process. Thie merely
- registers timeout that will handle the actual parsing when appropriate. */
-
-void silc_client_packet_parse(SilcPacketParserContext *parser_context)
-{
- SilcClient client = (SilcClient)parser_context->context;
-
- /* Parse the packet */
- silc_schedule_task_add(client->schedule, parser_context->sock->sock,
- silc_client_packet_parse_real,
- (void *)parser_context, 0, 1,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
+ silc_free(parser_context);
+ return TRUE;
}
/* Parses the packet type and calls what ever routines the packet type
SilcBuffer buffer = packet->buffer;
SilcPacketType type = packet->type;
- SILC_LOG_DEBUG(("Parsing packet type %d", type));
+ SILC_LOG_DEBUG(("Parsing %s packet", silc_get_packet_name(type)));
/* Parse the packet type */
switch(type) {
+
case SILC_PACKET_DISCONNECT:
silc_client_disconnected_by_server(client, sock, buffer);
break;
+
case SILC_PACKET_SUCCESS:
/*
* Success received for something. For now we can have only
if (sock->protocol)
silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
break;
+
case SILC_PACKET_FAILURE:
/*
* Failure received for some protocol. Set the protocol state to
*/
silc_client_process_failure(client, sock, packet);
break;
+
case SILC_PACKET_REJECT:
break;
*/
silc_client_channel_message(client, sock, packet);
break;
+
case SILC_PACKET_CHANNEL_KEY:
/*
* Received key for a channel. By receiving this key the client will be
*/
silc_client_private_message(client, sock, packet);
break;
+
case SILC_PACKET_PRIVATE_MESSAGE_KEY:
/*
* Received private message key
*/
break;
+ case SILC_PACKET_COMMAND:
+ /*
+ * Received command packet, a special case since normally client
+ * does not receive commands.
+ */
+ silc_client_command_process(client, sock, packet);
+ break;
+
case SILC_PACKET_COMMAND_REPLY:
/*
* Recived reply for a command
"protocol active, packet dropped."));
}
break;
+
case SILC_PACKET_KEY_EXCHANGE_2:
if (sock->protocol && sock->protocol->protocol &&
(sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE ||
if (proto_ctx->packet)
silc_packet_context_free(proto_ctx->packet);
-
+ if (proto_ctx->dest_id)
+ silc_free(proto_ctx->dest_id);
proto_ctx->packet = silc_packet_context_dup(packet);
proto_ctx->dest_id_type = packet->src_id_type;
proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
*/
SilcIDPayload idp;
- idp = silc_id_payload_parse(buffer);
+ idp = silc_id_payload_parse(buffer->data, buffer->len);
if (!idp)
break;
if (silc_id_payload_get_type(idp) != SILC_ID_CLIENT)
}
break;
+ case SILC_PACKET_CONNECTION_AUTH_REQUEST:
+ /*
+ * Reveived reply to our connection authentication method request
+ * packet. This is used to resolve the authentication method for the
+ * current session from the server if the client does not know it.
+ */
+ silc_client_connection_auth_request(client, sock, packet);
+ break;
+
+ case SILC_PACKET_FTP:
+ /* Received file transfer packet. */
+ silc_client_ftp(client, sock, packet);
+ break;
+
default:
SILC_LOG_DEBUG(("Incorrect packet type %d, packet dropped", type));
break;
SilcCipher cipher,
SilcHmac hmac,
unsigned char *data,
- uint32 data_len,
+ SilcUInt32 data_len,
int force_send)
{
SilcPacketContext packetdata;
+ const SilcBufferStruct packet;
+ int block_len;
+ SilcUInt32 sequence = 0;
if (!sock)
return;
dst_id = ((SilcClientConnection)sock->user_data)->remote_id;
dst_id_type = SILC_ID_SERVER;
}
+
+ if (hmac)
+ sequence = ((SilcClientConnection)sock->user_data)->psn_send++;
}
+ block_len = cipher ? silc_cipher_get_block_len(cipher) : 0;
+
/* Set the packet context pointers */
packetdata.flags = 0;
packetdata.type = type;
packetdata.dst_id_len = 0;
packetdata.dst_id_type = SILC_ID_NONE;
}
+ data_len = SILC_PACKET_DATALEN(data_len, (SILC_PACKET_HEADER_LEN +
+ packetdata.src_id_len +
+ packetdata.dst_id_len));
packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN +
packetdata.src_id_len + packetdata.dst_id_len;
- packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
-
- /* Prepare outgoing data buffer for packet sending */
- silc_packet_send_prepare(sock,
- SILC_PACKET_HEADER_LEN +
- packetdata.src_id_len +
- packetdata.dst_id_len,
- packetdata.padlen,
- data_len);
-
- SILC_LOG_DEBUG(("Putting data to outgoing buffer, len %d", data_len));
-
- packetdata.buffer = sock->outbuf;
-
- /* Put the data to the buffer */
- if (data && data_len)
- silc_buffer_put(sock->outbuf, data, data_len);
+ packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen, block_len);
/* Create the outgoing packet */
- silc_packet_assemble(&packetdata);
+ if (!silc_packet_assemble(&packetdata, client->rng, cipher, hmac, sock,
+ data, data_len, (const SilcBuffer)&packet)) {
+ SILC_LOG_ERROR(("Error assembling packet"));
+ return;
+ }
/* Encrypt the packet */
if (cipher)
- silc_packet_encrypt(cipher, hmac, sock->outbuf, sock->outbuf->len);
+ silc_packet_encrypt(cipher, hmac, sequence, (SilcBuffer)&packet,
+ packet.len);
- SILC_LOG_HEXDUMP(("Packet, len %d", sock->outbuf->len),
- sock->outbuf->data, sock->outbuf->len);
+ SILC_LOG_HEXDUMP(("Packet (%d), len %d", sequence, packet.len),
+ packet.data, packet.len);
/* Now actually send the packet */
- silc_client_packet_send_real(client, sock, force_send, FALSE);
+ silc_client_packet_send_real(client, sock, force_send);
+}
+
+void silc_client_packet_queue_purge(SilcClient client,
+ SilcSocketConnection sock)
+{
+ if (sock && SILC_IS_OUTBUF_PENDING(sock) &&
+ (SILC_IS_DISCONNECTED(sock) == FALSE)) {
+ silc_packet_send(sock, TRUE);
+ SILC_CLIENT_SET_CONNECTION_FOR_INPUT(client->schedule, sock->sock);
+ SILC_UNSET_OUTBUF_PENDING(sock);
+ silc_buffer_clear(sock->outbuf);
+ }
}
/* Closes connection to remote end. Free's all allocated data except
connection but `conn->sock' might be actually a different connection
than the `sock'). */
-void silc_client_close_connection(SilcClient client,
- SilcSocketConnection sock,
- SilcClientConnection conn)
+void silc_client_close_connection_real(SilcClient client,
+ SilcSocketConnection sock,
+ SilcClientConnection conn)
{
int del = FALSE;
+ SILC_LOG_DEBUG(("Start"));
+
+ if (!sock && !conn)
+ return;
+
if (!sock || (sock && conn->sock == sock))
del = TRUE;
if (!sock)
/* Unregister all tasks */
silc_schedule_task_del_by_fd(client->schedule, sock->sock);
- silc_schedule_task_del_by_fd(client->schedule, sock->sock);
/* Close the actual connection */
silc_net_close_connection(sock->sock);
SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
silc_protocol_execute_final(sock->protocol, client->schedule);
- sock->protocol = NULL;
/* The application will recall this function with these protocols
(the ops->connect client operation). */
return;
/* Free all cache entries */
SilcIDCacheList list;
SilcIDCacheEntry entry;
+ SilcClientCommandPending *r;
bool ret;
if (silc_idcache_get_all(conn->client_cache, &list)) {
/* Clear ID caches */
if (conn->client_cache)
- silc_idcache_del_all(conn->client_cache);
+ silc_idcache_free(conn->client_cache);
if (conn->channel_cache)
- silc_idcache_del_all(conn->channel_cache);
+ silc_idcache_free(conn->channel_cache);
if (conn->server_cache)
- silc_idcache_del_all(conn->server_cache);
+ silc_idcache_free(conn->server_cache);
- /* Free data */
+ /* Free data (my ID is freed in above silc_client_del_client).
+ conn->nickname is freed when freeing the local_entry->nickname. */
if (conn->remote_host)
silc_free(conn->remote_host);
- if (conn->local_id)
- silc_free(conn->local_id);
if (conn->local_id_data)
silc_free(conn->local_id_data);
if (conn->send_key)
silc_cipher_free(conn->send_key);
if (conn->receive_key)
silc_cipher_free(conn->receive_key);
- if (conn->hmac_send) /* conn->hmac_receive is same */
+ if (conn->hmac_send)
silc_hmac_free(conn->hmac_send);
- if (conn->pending_commands)
- silc_dlist_uninit(conn->pending_commands);
+ if (conn->hmac_receive)
+ silc_hmac_free(conn->hmac_receive);
if (conn->rekey)
silc_free(conn->rekey);
+ if (conn->active_session) {
+ sock->user_data = NULL;
+ silc_client_ftp_session_free(conn->active_session);
+ conn->active_session = NULL;
+ }
+
+ silc_client_ftp_free_sessions(client, conn);
+
+ silc_dlist_start(conn->pending_commands);
+ while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END)
+ silc_dlist_del(conn->pending_commands, r);
+ if (conn->pending_commands)
+ silc_dlist_uninit(conn->pending_commands);
+
memset(conn, 0, sizeof(*conn));
silc_client_del_connection(client, conn);
}
silc_socket_free(sock);
}
+/* Closes the connection to the remote end */
+
+void silc_client_close_connection(SilcClient client,
+ SilcClientConnection conn)
+{
+ silc_client_close_connection_real(client, NULL, conn);
+}
+
+/* Called when we receive disconnection packet from server. This
+ closes our end properly and displays the reason of the disconnection
+ on the screen. */
+
+SILC_TASK_CALLBACK(silc_client_disconnected_by_server_later)
+{
+ SilcClient client = (SilcClient)context;
+ SilcSocketConnection sock;
+
+ SILC_CLIENT_GET_SOCK(client, fd, sock);
+ if (sock == NULL)
+ return;
+
+ silc_client_close_connection_real(client, sock, sock->user_data);
+}
+
/* Called when we receive disconnection packet from server. This
closes our end properly and displays the reason of the disconnection
on the screen. */
void silc_client_disconnected_by_server(SilcClient client,
SilcSocketConnection sock,
- SilcBuffer message)
+ SilcBuffer packet)
{
- char *msg;
+ SilcClientConnection conn;
+ SilcStatus status;
+ char *message = NULL;
SILC_LOG_DEBUG(("Server disconnected us, sock %d", sock->sock));
- msg = silc_calloc(message->len + 1, sizeof(char));
- memcpy(msg, message->data, message->len);
- client->ops->say(client, sock->user_data, SILC_CLIENT_MESSAGE_AUDIT, msg);
- silc_free(msg);
+ if (packet->len < 1)
+ return;
+
+ status = (SilcStatus)packet->data[0];
+
+ if (packet->len > 1 &&
+ silc_utf8_valid(packet->data + 1, packet->len - 1))
+ message = silc_memdup(packet->data + 1, packet->len - 1);
+
+ conn = (SilcClientConnection)sock->user_data;
+ if (sock == conn->sock && sock->type != SILC_SOCKET_TYPE_CLIENT)
+ client->internal->ops->disconnect(client, conn, status, message);
+
+ silc_free(message);
SILC_SET_DISCONNECTED(sock);
- silc_client_close_connection(client, sock, sock->user_data);
+
+ /* Close connection through scheduler. */
+ silc_schedule_task_add(client->schedule, sock->sock,
+ silc_client_disconnected_by_server_later,
+ client, 0, 1, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
}
/* Received error message from server. Display it on the screen.
{
char *msg;
- msg = silc_calloc(message->len + 1, sizeof(char));
- memcpy(msg, message->data, message->len);
- client->ops->say(client, sock->user_data, SILC_CLIENT_MESSAGE_AUDIT, msg);
+ msg = silc_memdup(message->data, message->len);
+ client->internal->ops->say(client, sock->user_data,
+ SILC_CLIENT_MESSAGE_AUDIT, msg);
silc_free(msg);
}
+/* Auto-nicking callback to send NICK command to server. */
+
+SILC_TASK_CALLBACK(silc_client_send_auto_nick)
+{
+ SilcClientConnection conn = (SilcClientConnection)context;
+ SilcClient client = conn->client;
+
+ silc_client_command_send(client, conn, SILC_COMMAND_NICK,
+ ++conn->cmd_ident, 1, 1,
+ 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);
+
+ if (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);
+ }
+}
+
/* 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;
- SilcBuffer sidp;
+ SilcClientID *client_id = silc_id_payload_get_id(idp);
if (!conn->local_entry)
connecting = TRUE;
/* Delete old ID from ID cache */
if (conn->local_id) {
+ /* Check whether they are different */
+ if (SILC_ID_CLIENT_COMPARE(conn->local_id, client_id)) {
+ silc_free(client_id);
+ return;
+ }
+
silc_idcache_del_by_context(conn->client_cache, conn->local_entry);
silc_free(conn->local_id);
}
if (conn->local_id_data)
silc_free(conn->local_id_data);
- conn->local_id = silc_id_payload_get_id(idp);
+ conn->local_id = client_id;
conn->local_id_data = silc_id_payload_get_data(idp);
conn->local_id_data_len = silc_id_payload_get_len(idp);;
conn->local_entry = silc_calloc(1, sizeof(*conn->local_entry));
conn->local_entry->nickname = conn->nickname;
- if (!conn->local_entry->username) {
- conn->local_entry->username =
- silc_calloc(strlen(client->username) + strlen(client->hostname) + 1,
- sizeof(conn->local_entry->username));
- sprintf(conn->local_entry->username, "%s@%s", client->username,
- client->hostname);
- }
- conn->local_entry->server = strdup(conn->remote_host);
+ if (!conn->local_entry->username)
+ conn->local_entry->username = strdup(client->username);
+ if (!conn->local_entry->server)
+ conn->local_entry->server = strdup(conn->remote_host);
conn->local_entry->id = conn->local_id;
-
- /* Put it to the ID cache */
- silc_idcache_add(conn->client_cache, conn->nickname, conn->local_id,
- (void *)conn->local_entry, FALSE);
-
- /* Issue INFO command to fetch the real server name and server information
- and other stuff. */
- sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
- silc_client_send_command(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. */
- if (connecting)
- client->ops->connect(client, conn, TRUE);
-}
-
-/* Processed received Channel ID for a channel. This is called when client
- joins to channel and server replies with channel ID. The ID is cached.
- Returns the created channel entry. This is also called when received
- channel ID in for example USERS command reply that we do not have. */
-
-SilcChannelEntry silc_client_new_channel_id(SilcClient client,
- SilcSocketConnection sock,
- char *channel_name,
- uint32 mode,
- SilcIDPayload idp)
-{
- SilcClientConnection conn = (SilcClientConnection)sock->user_data;
- SilcChannelEntry channel;
-
- SILC_LOG_DEBUG(("New channel ID"));
-
- channel = silc_calloc(1, sizeof(*channel));
- channel->channel_name = channel_name;
- channel->id = silc_id_payload_get_id(idp);
- channel->mode = mode;
- silc_list_init(channel->clients, struct SilcChannelUserStruct, next);
+ conn->local_entry->valid = TRUE;
+ if (!conn->local_entry->channels)
+ conn->local_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr,
+ NULL, NULL,
+ NULL, NULL, NULL,
+ TRUE);
/* Put it to the ID cache */
- silc_idcache_add(conn->channel_cache, channel_name, (void *)channel->id,
- (void *)channel, FALSE);
-
- return channel;
+ silc_idcache_add(conn->client_cache, strdup(conn->nickname), conn->local_id,
+ (void *)conn->local_entry, 0, NULL);
+
+ if (connecting) {
+ SilcBuffer sidp;
+
+ /* Issue IDENTIFY command for itself to get resolved hostname
+ correctly from server. */
+ silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
+ silc_client_command_reply_identify_i, 0,
+ ++conn->cmd_ident);
+ sidp = silc_id_payload_encode(conn->local_entry->id, SILC_ID_CLIENT);
+ silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
+ conn->cmd_ident, 1, 5, sidp->data, sidp->len);
+ silc_buffer_free(sidp);
+
+ if (!conn->params.detach_data) {
+ /* 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);
+ }
+ }
}
-/* Removes a client entry from all channel it has joined. This really is
- a performance killer (client_entry should have pointers to channel
- entry list). */
+/* Removes a client entry from all channels it has joined. */
void silc_client_remove_from_channels(SilcClient client,
SilcClientConnection conn,
SilcClientEntry client_entry)
{
- SilcIDCacheEntry id_cache;
- SilcIDCacheList list;
- SilcChannelEntry channel;
+ SilcHashTableList htl;
SilcChannelUser chu;
- if (!silc_idcache_get_all(conn->channel_cache, &list))
- return;
-
- silc_idcache_list_first(list, &id_cache);
- channel = (SilcChannelEntry)id_cache->context;
-
- while (channel) {
-
- /* Remove client from channel */
- silc_list_start(channel->clients);
- while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
- if (chu->client == client_entry) {
- silc_list_del(channel->clients, chu);
- silc_free(chu);
- break;
- }
- }
-
- if (!silc_idcache_list_next(list, &id_cache))
- break;
-
- channel = (SilcChannelEntry)id_cache->context;
+ silc_hash_table_list(client_entry->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void **)&chu)) {
+ silc_hash_table_del(chu->client->channels, chu->channel);
+ silc_hash_table_del(chu->channel->user_list, chu->client);
+ silc_free(chu);
}
- silc_idcache_list_free(list);
+ silc_hash_table_list_reset(&htl);
}
/* Replaces `old' client entries from all channels to `new' client entry.
SilcClientEntry old,
SilcClientEntry new)
{
- SilcIDCacheEntry id_cache;
- SilcIDCacheList list;
- SilcChannelEntry channel;
+ SilcHashTableList htl;
SilcChannelUser chu;
- if (!silc_idcache_get_all(conn->channel_cache, &list))
- return;
-
- silc_idcache_list_first(list, &id_cache);
- channel = (SilcChannelEntry)id_cache->context;
-
- while (channel) {
-
+ silc_hash_table_list(old->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void **)&chu)) {
/* Replace client entry */
- silc_list_start(channel->clients);
- while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
- if (chu->client == old) {
- chu->client = new;
- break;
- }
- }
-
- if (!silc_idcache_list_next(list, &id_cache))
- break;
+ silc_hash_table_del(chu->client->channels, chu->channel);
+ silc_hash_table_del(chu->channel->user_list, chu->client);
- channel = (SilcChannelEntry)id_cache->context;
+ chu->client = new;
+ silc_hash_table_add(chu->channel->user_list, chu->client, chu);
+ silc_hash_table_add(chu->client->channels, chu->channel, chu);
}
-
- silc_idcache_list_free(list);
+ silc_hash_table_list_reset(&htl);
}
/* Registers failure timeout to process the received failure packet
SilcSocketConnection sock,
SilcPacketContext *packet)
{
- uint32 failure = 0;
+ SilcUInt32 failure = 0;
if (sock->protocol) {
if (packet->buffer->len >= 4)
SILC_GET32_MSB(failure, packet->buffer->data);
/* Notify application */
- client->ops->failure(client, sock->user_data, sock->protocol,
- (void *)failure);
+ client->internal->ops->failure(client, sock->user_data, sock->protocol,
+ (void *)failure);
}
}
/* Re-register re-key timeout */
silc_schedule_task_add(client->schedule, sock->sock,
- silc_client_rekey_callback,
- context, conn->rekey->timeout, 0,
- SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+ silc_client_rekey_callback,
+ context, conn->rekey->timeout, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
}
/* The final callback for the REKEY protocol. This will actually take the
return;
}
+ /* Purge the outgoing data queue to assure that all rekey packets really
+ go to the network before we quit the protocol. */
+ silc_client_packet_queue_purge(client, sock);
+
/* Cleanup */
silc_protocol_free(protocol);
sock->protocol = NULL;
silc_socket_free(ctx->sock);
silc_free(ctx);
}
+
+/* Processes incoming connection authentication method request packet.
+ It is a reply to our previously sent request. The packet can be used
+ to resolve the authentication method for the current session if the
+ client does not know it beforehand. */
+
+void silc_client_connection_auth_request(SilcClient client,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+ SilcUInt16 conn_type, auth_meth;
+ int ret;
+
+ /* If we haven't send our request then ignore this one. */
+ if (!conn->connauth)
+ return;
+
+ /* Parse the payload */
+ ret = silc_buffer_unformat(packet->buffer,
+ SILC_STR_UI_SHORT(&conn_type),
+ SILC_STR_UI_SHORT(&auth_meth),
+ SILC_STR_END);
+ if (ret == -1)
+ auth_meth = SILC_AUTH_NONE;
+
+ /* Call the request callback to notify application for received
+ authentication method information. */
+ if (conn->connauth->callback)
+ (*conn->connauth->callback)(client, conn, auth_meth,
+ conn->connauth->context);
+
+ silc_schedule_task_del(client->schedule, conn->connauth->timeout);
+
+ silc_free(conn->connauth);
+ conn->connauth = NULL;
+}
+
+/* Timeout task callback called if the server does not reply to our
+ connection authentication method request in the specified time interval. */
+
+SILC_TASK_CALLBACK(silc_client_request_authentication_method_timeout)
+{
+ SilcClientConnection conn = (SilcClientConnection)context;
+ SilcClient client = conn->client;
+
+ if (!conn->connauth)
+ return;
+
+ /* Call the request callback to notify application */
+ if (conn->connauth->callback)
+ (*conn->connauth->callback)(client, conn, SILC_AUTH_NONE,
+ conn->connauth->context);
+
+ silc_free(conn->connauth);
+ conn->connauth = NULL;
+}
+
+/* This function can be used to request the current authentication method
+ from the server. This may be called when connecting to the server
+ and the client library requests the authentication data from the
+ application. If the application does not know the current authentication
+ method it can request it from the server using this function.
+ The `callback' with `context' will be called after the server has
+ replied back with the current authentication method. */
+
+void
+silc_client_request_authentication_method(SilcClient client,
+ SilcClientConnection conn,
+ SilcConnectionAuthRequest callback,
+ void *context)
+{
+ SilcClientConnAuthRequest connauth;
+ SilcBuffer packet;
+
+ connauth = silc_calloc(1, sizeof(*connauth));
+ connauth->callback = callback;
+ connauth->context = context;
+
+ if (conn->connauth)
+ silc_free(conn->connauth);
+
+ conn->connauth = connauth;
+
+ /* Assemble the request packet and send it to the server */
+ packet = silc_buffer_alloc(4);
+ silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+ silc_buffer_format(packet,
+ SILC_STR_UI_SHORT(SILC_SOCKET_TYPE_CLIENT),
+ SILC_STR_UI_SHORT(SILC_AUTH_NONE),
+ SILC_STR_END);
+ silc_client_packet_send(client, conn->sock,
+ SILC_PACKET_CONNECTION_AUTH_REQUEST,
+ NULL, 0, NULL, NULL,
+ packet->data, packet->len, FALSE);
+ silc_buffer_free(packet);
+
+ /* Register a timeout in case server does not reply anything back. */
+ connauth->timeout =
+ silc_schedule_task_add(client->schedule, conn->sock->sock,
+ silc_client_request_authentication_method_timeout,
+ conn,
+ client->internal->params->connauth_request_secs, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+}