SILC_LOG_DEBUG(("Initializing client"));
/* Initialize hash functions for client to use */
- silc_hash_alloc("md5", &client->internal->md5hash);
- silc_hash_alloc("sha1", &client->internal->sha1hash);
+ silc_hash_alloc("md5", &client->md5hash);
+ silc_hash_alloc("sha1", &client->sha1hash);
/* Initialize none cipher */
silc_cipher_alloc("none", &client->internal->none_cipher);
/* Initialize the scheduler */
client->schedule =
silc_schedule_init(client->internal->params->task_max ?
- client->internal->params->task_max : 200);
+ client->internal->params->task_max : 200, client);
if (!client->schedule)
return FALSE;
SilcClientConnection conn;
int i;
+ SILC_LOG_DEBUG(("Adding new connection to %s:%d", hostname, port));
+
conn = silc_calloc(1, sizeof(*conn));
/* Initialize ID caches */
if (conn->pending_commands)
silc_dlist_uninit(conn->pending_commands);
silc_free(conn->remote_host);
- silc_dlist_uninit(conn->ftp_sessions);
+ if (conn->ftp_sessions)
+ silc_dlist_uninit(conn->ftp_sessions);
silc_free(conn);
client->internal->conns[i] = NULL;
SILC_LOG_DEBUG(("Start"));
- /* XXX We should most likely use the resolved host name instead of the
- one user provided for us. */
- silc_free(conn->sock->hostname);
- conn->sock->hostname = 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->remote_host);
+ conn->sock->ip = strdup(conn->sock->hostname);
conn->sock->port = conn->remote_port;
/* Allocate internal Key Exchange context. This is sent to the
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;
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 */
- silc_schedule_task_add(client->schedule, ctx->sock->sock,
- silc_client_connect_failure, ctx,
- 0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
- 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;
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
close the connection */
if (SILC_IS_DISCONNECTING(sock)) {
if (sock == conn->sock && sock->type != SILC_SOCKET_TYPE_CLIENT)
- client->internal->ops->disconnect(client, conn);
+ 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 && sock->type != SILC_SOCKET_TYPE_CLIENT)
- client->internal->ops->disconnect(client, conn);
+ client->internal->ops->disconnect(client, conn, 0, NULL);
silc_client_close_connection_real(client, sock, conn);
return;
}
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 ||
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);
/* Free all cache entries */
SilcIDCacheList list;
SilcIDCacheEntry entry;
+ SilcClientCommandPending *r;
bool ret;
if (silc_idcache_get_all(conn->client_cache, &list)) {
silc_hmac_free(conn->hmac_send);
if (conn->hmac_receive)
silc_hmac_free(conn->hmac_receive);
- if (conn->pending_commands)
- silc_dlist_uninit(conn->pending_commands);
if (conn->rekey)
silc_free(conn->rekey);
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);
}
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_memdup(message->data, message->len);
- client->internal->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);
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;
SilcClientID *client_id = silc_id_payload_get_id(idp);
- SilcBuffer sidp;
if (!conn->local_entry)
connecting = TRUE;
conn->local_entry->nickname = conn->nickname;
if (!conn->local_entry->username)
conn->local_entry->username = strdup(client->username);
- if (!conn->local_entry->hostname)
- conn->local_entry->hostname = strdup(client->hostname);
if (!conn->local_entry->server)
conn->local_entry->server = strdup(conn->remote_host);
conn->local_entry->id = conn->local_id;
(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,
+ 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->remote_id, SILC_ID_SERVER);
- silc_client_command_send(client, conn, SILC_COMMAND_INFO,
- conn->cmd_ident, 1, 2, sidp->data, sidp->len);
+ 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);
- /* 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) {
+ /* 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);
+ }
}
}
SilcChannelUser chu;
silc_hash_table_list(client_entry->channels, &htl);
- while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
+ 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);
SilcChannelUser chu;
silc_hash_table_list(old->channels, &htl);
- while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
+ while (silc_hash_table_get(&htl, NULL, (void **)&chu)) {
/* Replace client entry */
silc_hash_table_del(chu->client->channels, chu->channel);
silc_hash_table_del(chu->channel->user_list, chu->client);