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 void silc_client_packet_parse_type(SilcClient client,
the client. The `application' is application specific user data pointer
and caller must free it. */
-SilcClient silc_client_alloc(SilcClientOperations *ops, void *application)
+SilcClient silc_client_alloc(SilcClientOperations *ops, void *application,
+ const char *silc_version)
{
SilcClient new_client;
new_client = silc_calloc(1, sizeof(*new_client));
new_client->application = application;
new_client->ops = ops;
+ new_client->silc_client_version = strdup(silc_version);
return new_client;
}
silc_client_protocols_register();
/* Initialize the scheduler */
- silc_schedule_init(&client->io_queue, &client->timeout_queue,
- &client->generic_queue, 5000);
+ client->schedule = silc_schedule_init(&client->io_queue,
+ &client->timeout_queue,
+ &client->generic_queue, 5000);
+ if (!client->schedule)
+ return FALSE;
return TRUE;
}
/* Stop the scheduler, although it might be already stopped. This
doesn't hurt anyone. This removes all the tasks and task queues,
as well. */
- silc_schedule_stop();
- silc_schedule_uninit();
+ silc_schedule_stop(client->schedule);
+ silc_schedule_uninit(client->schedule);
silc_client_protocols_unregister();
/* Start the scheduler, the heart of the SILC client. When this returns
the program will be terminated. */
- silc_schedule();
+ silc_schedule(client->schedule);
}
/* Allocates and adds new connection to the client. This adds the allocated
conn = silc_calloc(1, sizeof(*conn));
/* Initialize ID caches */
- conn->client_cache = silc_idcache_alloc(0);
- conn->channel_cache = silc_idcache_alloc(0);
- conn->server_cache = silc_idcache_alloc(0);
+ conn->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT, NULL);
+ 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_host = strdup(hostname);
conn->remote_port = port;
}
}
+/* Adds listener socket to the listener sockets table. This function is
+ used to add socket objects that are listeners to the client. This should
+ not be used to add other connection objects. */
+
+void silc_client_add_socket(SilcClient client, SilcSocketConnection sock)
+{
+ int i;
+
+ if (!client->sockets) {
+ client->sockets = silc_calloc(1, sizeof(*client->sockets));
+ client->sockets[0] = silc_socket_dup(sock);
+ client->sockets_count = 1;
+ return;
+ }
+
+ for (i = 0; i < client->sockets_count; i++) {
+ if (client->sockets[i] == NULL) {
+ client->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++;
+}
+
+/* Deletes listener socket from the listener sockets table. */
+
+void silc_client_del_socket(SilcClient client, SilcSocketConnection sock)
+{
+ int i;
+
+ if (!client->sockets)
+ return;
+
+ for (i = 0; i < client->sockets_count; i++) {
+ if (client->sockets[i] == sock) {
+ silc_socket_free(sock);
+ client->sockets[i] = NULL;
+ return;
+ }
+ }
+}
+
static int
silc_client_connect_to_server_internal(SilcClientInternalConnectContext *ctx)
{
SILC_TASK_FD,
SILC_TASK_PRI_NORMAL);
silc_task_reset_iotype(ctx->task, SILC_TASK_WRITE);
- silc_schedule_set_listen_fd(sock, ctx->task->iomask);
+ silc_schedule_set_listen_fd(ctx->client->schedule, sock, ctx->task->iomask);
ctx->sock = sock;
conn = silc_client_add_connection(client, host, port, context);
- client->ops->say(client, conn,
+ client->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
protocol as context. */
proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
proto_ctx->client = (void *)client;
- proto_ctx->sock = conn->sock;
+ proto_ctx->sock = silc_socket_dup(conn->sock);
proto_ctx->rng = client->rng;
proto_ctx->responder = FALSE;
proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
+ proto_ctx->verify = silc_client_protocol_ke_verify_key;
/* Perform key exchange protocol. silc_client_connect_to_server_final
will be called after the protocol is finished. */
&protocol, (void *)proto_ctx,
silc_client_connect_to_server_second);
if (!protocol) {
- client->ops->say(client, conn,
+ client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
"Error: Could not start authentication protocol");
return FALSE;
}
SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(fd);
/* Execute the protocol */
- protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 0);
+ silc_protocol_execute(protocol, client->timeout_queue, 0, 0);
return TRUE;
}
SILC_LOG_DEBUG(("Start"));
/* Check the socket status as it might be in error */
- getsockopt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
+ silc_net_get_socket_opt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
if (opt != 0) {
if (ctx->tries < 2) {
/* Connection failed but lets try again */
- client->ops->say(client, conn, "Could not connect to server %s: %s",
+ 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,
+ client->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(fd);
+ silc_schedule_unset_listen_fd(client->schedule, fd);
silc_net_close_connection(fd);
silc_task_unregister(client->io_queue, ctx->task);
ctx->tries++;
} else {
/* Connection failed and we won't try anymore */
- client->ops->say(client, conn, "Could not connect to server %s: %s",
+ client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+ "Could not connect to server %s: %s",
ctx->host, strerror(opt));
- silc_schedule_unset_listen_fd(fd);
+ silc_schedule_unset_listen_fd(client->schedule, fd);
silc_net_close_connection(fd);
silc_task_unregister(client->io_queue, ctx->task);
silc_free(ctx);
return;
}
- silc_schedule_unset_listen_fd(fd);
+ silc_schedule_unset_listen_fd(client->schedule, fd);
silc_task_unregister(client->io_queue, ctx->task);
silc_free(ctx);
if (ctx->dest_id)
silc_free(ctx->dest_id);
ctx->sock->protocol = NULL;
- silc_task_unregister_by_callback(client->timeout_queue,
- silc_client_failure_callback);
+ silc_socket_free(ctx->sock);
/* Notify application of failure */
client->ops->connect(client, ctx->sock->user_data, FALSE);
ctx->ske->prop->cipher,
ctx->ske->prop->pkcs,
ctx->ske->prop->hash,
- ctx->ske->prop->hmac);
+ ctx->ske->prop->hmac,
+ ctx->ske->prop->group);
silc_ske_free_key_material(ctx->keymat);
/* Allocate internal context for the authentication protocol. This
sock->port, &proto_ctx->auth_meth,
&proto_ctx->auth_data,
&proto_ctx->auth_data_len))
- {
- /* XXX do AUTH_REQUEST resolcing with server */
- proto_ctx->auth_meth = SILC_AUTH_NONE;
- }
+ proto_ctx->auth_meth = SILC_AUTH_NONE;
/* Free old protocol as it is finished now */
silc_protocol_free(protocol);
if (ctx->packet)
silc_packet_context_free(ctx->packet);
silc_free(ctx);
- /* silc_free(ctx->keymat....); */
sock->protocol = NULL;
- /* Allocate the authentication protocol. This is allocated here
- but we won't start it yet. We will be receiving party of this
- protocol thus we will wait that connecting party will make
- their first move. */
+ /* Allocate the authenteication protocol and execute it. */
silc_protocol_alloc(SILC_PROTOCOL_CLIENT_CONNECTION_AUTH,
&sock->protocol, (void *)proto_ctx,
silc_client_connect_to_server_final);
/* Execute the protocol */
- sock->protocol->execute(client->timeout_queue, 0, sock->protocol, fd, 0, 0);
+ silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
}
/* Finalizes the connection to the remote SILC server. This is called
if (ctx->dest_id)
silc_free(ctx->dest_id);
conn->sock->protocol = NULL;
- silc_task_unregister_by_callback(client->timeout_queue,
- silc_client_failure_callback);
+ silc_socket_free(ctx->sock);
/* Notify application of failure */
client->ops->connect(client, ctx->sock->user_data, FALSE);
/* Save remote ID. */
conn->remote_id = ctx->dest_id;
conn->remote_id_data = silc_id_id2str(ctx->dest_id, SILC_ID_SERVER);
- conn->remote_id_data_len = SILC_ID_SERVER_LEN;
+ conn->remote_id_data_len = silc_id_get_len(ctx->dest_id, SILC_ID_SERVER);
+
+ /* Register re-key timeout */
+ conn->rekey->timeout = 3600; /* XXX hardcoded */
+ conn->rekey->context = (void *)client;
+ silc_task_register(client->timeout_queue, conn->sock->sock,
+ silc_client_rekey_callback,
+ (void *)conn->sock, conn->rekey->timeout, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
- silc_task_unregister_by_callback(client->timeout_queue,
- silc_client_failure_callback);
silc_protocol_free(protocol);
if (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;
}
int silc_client_packet_send_real(SilcClient client,
SilcSocketConnection sock,
- int force_send)
+ bool force_send,
+ bool flush)
{
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))
+ force_send = FALSE;
+
/* Send the packet */
ret = silc_packet_send(sock, force_send);
if (ret != -2)
This call sets the connection both for input and output (the input
is set always and this call keeps the input setting, actually).
Actual data sending is performed by silc_client_packet_process. */
- SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT(sock->sock);
+ SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT(client->schedule, sock->sock);
/* Mark to socket that data is pending in outgoing buffer. This flag
is needed if new data is added to the buffer before the earlier
silc_buffer_push(sock->outbuf,
sock->outbuf->data - sock->outbuf->head);
- ret = silc_client_packet_send_real(client, sock, TRUE);
+ ret = silc_client_packet_send_real(client, sock, TRUE, TRUE);
/* If returned -2 could not write to connection now, will do
it later. */
back to only for input. When there is again some outgoing data
available for this connection it will be set for output as well.
This call clears the output setting and sets it only for input. */
- SILC_CLIENT_SET_CONNECTION_FOR_INPUT(fd);
+ SILC_CLIENT_SET_CONNECTION_FOR_INPUT(client->schedule, fd);
SILC_UNSET_OUTBUF_PENDING(sock);
silc_buffer_clear(sock->outbuf);
/* If connection is disconnecting already we will finally
close the connection */
if (SILC_IS_DISCONNECTING(sock)) {
- client->ops->disconnect(client, conn);
- silc_client_close_connection(client, conn);
+ if (sock == conn->sock)
+ client->ops->disconnect(client, conn);
+ silc_client_close_connection(client, sock, conn);
return;
}
SILC_LOG_DEBUG(("EOF from connection %d", sock->sock));
- client->ops->disconnect(client, conn);
- silc_client_close_connection(client, conn);
+ if (sock == conn->sock)
+ client->ops->disconnect(client, conn);
+ silc_client_close_connection(client, sock, conn);
return;
}
/* Process the packet. This will call the parser that will then
decrypt and parse the packet. */
- silc_packet_receive_process(sock, conn->receive_key, conn->hmac,
- silc_client_packet_parse, client);
+ if (sock->type != SILC_SOCKET_TYPE_UNKNOWN)
+ silc_packet_receive_process(sock, conn->receive_key, conn->hmac_receive,
+ silc_client_packet_parse, client);
+ else
+ silc_packet_receive_process(sock, NULL, NULL,
+ 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. */
SILC_TASK_CALLBACK(silc_client_packet_parse_real)
SILC_LOG_DEBUG(("Start"));
/* Decrypt the received packet */
- ret = silc_packet_decrypt(conn->receive_key, conn->hmac, buffer, 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);
+ else
+ ret = silc_packet_decrypt(NULL, NULL, buffer, packet,
+ silc_client_packet_decrypt_check, parse_ctx);
+
if (ret < 0)
goto out;
silc_client_packet_parse_type(client, sock, packet);
out:
- silc_buffer_clear(sock->inbuf);
+ /* silc_buffer_clear(sock->inbuf); */
silc_packet_context_free(packet);
silc_free(parse_ctx);
}
SILC_TASK_TIMEOUT,
SILC_TASK_PRI_NORMAL);
}
-
+
/* Parses the packet type and calls what ever routines the packet type
requires. This is done for all incoming packets. */
* one protocol for connection executing at once hence this
* success message is for whatever protocol is executing currently.
*/
- if (sock->protocol) {
- sock->protocol->execute(client->timeout_queue, 0,
- sock->protocol, sock->sock, 0, 0);
- }
+ if (sock->protocol)
+ silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
break;
case SILC_PACKET_FAILURE:
/*
break;
case SILC_PACKET_KEY_EXCHANGE:
- if (sock->protocol) {
+ if (sock->protocol && sock->protocol->protocol &&
+ sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
SilcClientKEInternalContext *proto_ctx =
(SilcClientKEInternalContext *)sock->protocol->context;
break;
/* Let the protocol handle the packet */
- sock->protocol->execute(client->timeout_queue, 0,
- sock->protocol, sock->sock, 0, 0);
+ silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
} else {
SILC_LOG_ERROR(("Received Key Exchange packet but no key exchange "
"protocol active, packet dropped."));
-
- /* XXX Trigger KE protocol?? Rekey actually! */
}
break;
case SILC_PACKET_KEY_EXCHANGE_1:
- if (sock->protocol) {
-
+ if (sock->protocol && sock->protocol->protocol &&
+ (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE ||
+ sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY)) {
+
+ if (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY) {
+ SilcClientRekeyInternalContext *proto_ctx =
+ (SilcClientRekeyInternalContext *)sock->protocol->context;
+
+ if (proto_ctx->packet)
+ silc_packet_context_free(proto_ctx->packet);
+
+ proto_ctx->packet = silc_packet_context_dup(packet);
+
+ /* Let the protocol handle the packet */
+ silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
+ } else {
+ SilcClientKEInternalContext *proto_ctx =
+ (SilcClientKEInternalContext *)sock->protocol->context;
+
+ if (proto_ctx->packet)
+ silc_packet_context_free(proto_ctx->packet);
+
+ 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,
+ packet->src_id_type);
+ if (!proto_ctx->dest_id)
+ break;
+
+ /* Let the protocol handle the packet */
+ silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
+ }
} else {
SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange "
"protocol active, packet dropped."));
}
break;
case SILC_PACKET_KEY_EXCHANGE_2:
- if (sock->protocol) {
- SilcClientKEInternalContext *proto_ctx =
- (SilcClientKEInternalContext *)sock->protocol->context;
-
- if (proto_ctx->packet)
- silc_packet_context_free(proto_ctx->packet);
-
- 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,
- packet->src_id_type);
- if (!proto_ctx->dest_id)
- break;
-
- /* Let the protocol handle the packet */
- sock->protocol->execute(client->timeout_queue, 0,
- sock->protocol, sock->sock, 0, 0);
+ if (sock->protocol && sock->protocol->protocol &&
+ (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE ||
+ sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY)) {
+
+ if (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY) {
+ SilcClientRekeyInternalContext *proto_ctx =
+ (SilcClientRekeyInternalContext *)sock->protocol->context;
+
+ if (proto_ctx->packet)
+ silc_packet_context_free(proto_ctx->packet);
+
+ proto_ctx->packet = silc_packet_context_dup(packet);
+
+ /* Let the protocol handle the packet */
+ silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
+ } else {
+ SilcClientKEInternalContext *proto_ctx =
+ (SilcClientKEInternalContext *)sock->protocol->context;
+
+ if (proto_ctx->packet)
+ silc_packet_context_free(proto_ctx->packet);
+
+ 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,
+ packet->src_id_type);
+ if (!proto_ctx->dest_id)
+ break;
+
+ /* Let the protocol handle the packet */
+ silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
+ }
} else {
SILC_LOG_ERROR(("Received Key Exchange 2 packet but no key exchange "
"protocol active, packet dropped."));
silc_client_key_agreement(client, sock, packet);
break;
+ case SILC_PACKET_REKEY:
+ SILC_LOG_DEBUG(("Re-key packet"));
+ /* We ignore this for now */
+ break;
+
+ case SILC_PACKET_REKEY_DONE:
+ SILC_LOG_DEBUG(("Re-key done packet"));
+
+ if (sock->protocol && sock->protocol->protocol &&
+ sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY) {
+
+ SilcClientRekeyInternalContext *proto_ctx =
+ (SilcClientRekeyInternalContext *)sock->protocol->context;
+
+ if (proto_ctx->packet)
+ silc_packet_context_free(proto_ctx->packet);
+
+ proto_ctx->packet = silc_packet_context_dup(packet);
+
+ /* Let the protocol handle the packet */
+ if (proto_ctx->responder == FALSE)
+ silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
+ else
+ /* Let the protocol handle the packet */
+ silc_protocol_execute(sock->protocol, client->timeout_queue,
+ 0, 100000);
+ } else {
+ SILC_LOG_ERROR(("Received Re-key done packet but no re-key "
+ "protocol active, packet dropped."));
+ }
+ break;
+
default:
SILC_LOG_DEBUG(("Incorrect packet type %d, packet dropped", type));
break;
SilcCipher cipher,
SilcHmac hmac,
unsigned char *data,
- unsigned int data_len,
+ uint32 data_len,
int force_send)
{
SilcPacketContext packetdata;
+ if (!sock)
+ return;
+
SILC_LOG_DEBUG(("Sending packet, type %d", type));
/* Get data used in the packet sending, keys and stuff */
if (!cipher && ((SilcClientConnection)sock->user_data)->send_key)
cipher = ((SilcClientConnection)sock->user_data)->send_key;
- if (!hmac && ((SilcClientConnection)sock->user_data)->hmac)
- hmac = ((SilcClientConnection)sock->user_data)->hmac;
+ if (!hmac && ((SilcClientConnection)sock->user_data)->hmac_send)
+ hmac = ((SilcClientConnection)sock->user_data)->hmac_send;
if (!dst_id && ((SilcClientConnection)sock->user_data)->remote_id) {
dst_id = ((SilcClientConnection)sock->user_data)->remote_id;
/* Set the packet context pointers */
packetdata.flags = 0;
packetdata.type = type;
- if (((SilcClientConnection)sock->user_data)->local_id_data)
+ if (sock->user_data &&
+ ((SilcClientConnection)sock->user_data)->local_id_data) {
packetdata.src_id = ((SilcClientConnection)sock->user_data)->local_id_data;
- else
+ packetdata.src_id_len =
+ silc_id_get_len(((SilcClientConnection)sock->user_data)->local_id,
+ SILC_ID_CLIENT);
+ } else {
packetdata.src_id = silc_calloc(SILC_ID_CLIENT_LEN, sizeof(unsigned char));
- packetdata.src_id_len = SILC_ID_CLIENT_LEN;
+ packetdata.src_id_len = SILC_ID_CLIENT_LEN;
+ }
packetdata.src_id_type = SILC_ID_CLIENT;
if (dst_id) {
packetdata.dst_id = silc_id_id2str(dst_id, dst_id_type);
- packetdata.dst_id_len = silc_id_get_len(dst_id_type);
+ packetdata.dst_id_len = silc_id_get_len(dst_id, dst_id_type);
packetdata.dst_id_type = dst_id_type;
} else {
packetdata.dst_id = NULL;
sock->outbuf->data, sock->outbuf->len);
/* Now actually send the packet */
- silc_client_packet_send_real(client, sock, force_send);
+ silc_client_packet_send_real(client, sock, force_send, FALSE);
}
/* Closes connection to remote end. Free's all allocated data except
- for some information such as nickname etc. that are valid at all time. */
+ for some information such as nickname etc. that are valid at all time.
+ If the `sock' is NULL then the conn->sock will be used. If `sock' is
+ provided it will be checked whether the sock and `conn->sock' are the
+ same (they can be different, ie. a socket can use `conn' as its
+ connection but `conn->sock' might be actually a different connection
+ than the `sock'). */
void silc_client_close_connection(SilcClient client,
+ SilcSocketConnection sock,
SilcClientConnection conn)
{
- SilcSocketConnection sock = conn->sock;
+ int del = FALSE;
+
+ if (!sock || (sock && conn->sock == sock))
+ del = TRUE;
+ if (!sock)
+ sock = conn->sock;
/* We won't listen for this connection anymore */
- silc_schedule_unset_listen_fd(sock->sock);
+ silc_schedule_unset_listen_fd(client->schedule, sock->sock);
/* Unregister all tasks */
silc_task_unregister_by_fd(client->io_queue, sock->sock);
/* Close the actual connection */
silc_net_close_connection(sock->sock);
- client->ops->say(client, sock->user_data,
- "Closed connection to host %s", sock->hostname);
+ /* Cancel any active protocol */
+ if (sock->protocol) {
+ if (sock->protocol->protocol->type ==
+ SILC_PROTOCOL_CLIENT_KEY_EXCHANGE ||
+ sock->protocol->protocol->type ==
+ SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
+ sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
+ silc_protocol_execute_final(sock->protocol, client->timeout_queue);
+ sock->protocol = NULL;
+ /* The application will recall this function with these protocols
+ (the ops->connect client operation). */
+ return;
+ } else {
+ sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
+ silc_protocol_execute_final(sock->protocol, client->timeout_queue);
+ sock->protocol = NULL;
+ }
+ }
/* Free everything */
- if (sock->user_data) {
+ if (del && sock->user_data) {
/* XXX Free all client entries and channel entries. */
/* Clear ID caches */
- silc_idcache_del_all(conn->client_cache);
- silc_idcache_del_all(conn->channel_cache);
+ if (conn->client_cache)
+ silc_idcache_del_all(conn->client_cache);
+ if (conn->channel_cache)
+ silc_idcache_del_all(conn->channel_cache);
/* Free data */
if (conn->remote_host)
silc_cipher_free(conn->send_key);
if (conn->receive_key)
silc_cipher_free(conn->receive_key);
- if (conn->hmac)
- silc_hmac_free(conn->hmac);
- if (conn->hmac_key) {
- memset(conn->hmac_key, 0, conn->hmac_key_len);
- silc_free(conn->hmac_key);
- }
+ if (conn->hmac_send) /* conn->hmac_receive is same */
+ silc_hmac_free(conn->hmac_send);
if (conn->pending_commands)
silc_dlist_uninit(conn->pending_commands);
+ if (conn->rekey)
+ silc_free(conn->rekey);
- conn->sock = NULL;
- conn->remote_port = 0;
- conn->remote_type = 0;
- conn->send_key = NULL;
- conn->receive_key = NULL;
- conn->hmac = NULL;
- conn->hmac_key = NULL;
- conn->hmac_key_len = 0;
- conn->local_id = NULL;
- conn->local_id_data = NULL;
- conn->remote_host = NULL;
- conn->current_channel = NULL;
- conn->pending_commands = NULL;
-
+ memset(conn, 0, sizeof(*conn));
silc_client_del_connection(client, conn);
}
- if (sock->protocol) {
- silc_protocol_free(sock->protocol);
- sock->protocol = NULL;
- }
silc_socket_free(sock);
}
msg = silc_calloc(message->len + 1, sizeof(char));
memcpy(msg, message->data, message->len);
- client->ops->say(client, sock->user_data, msg);
+ client->ops->say(client, sock->user_data, SILC_CLIENT_MESSAGE_AUDIT, msg);
silc_free(msg);
SILC_SET_DISCONNECTED(sock);
- silc_client_close_connection(client, sock->user_data);
+ silc_client_close_connection(client, sock, sock->user_data);
}
/* Received error message from server. Display it on the screen.
msg = silc_calloc(message->len + 1, sizeof(char));
memcpy(msg, message->data, message->len);
- client->ops->say(client, sock->user_data, msg);
+ client->ops->say(client, sock->user_data, SILC_CLIENT_MESSAGE_AUDIT, msg);
silc_free(msg);
}
connecting = TRUE;
/* Delete old ID from ID cache */
- silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, conn->local_id);
+ if (conn->local_id) {
+ silc_idcache_del_by_context(conn->client_cache, conn->local_entry);
+ silc_free(conn->local_id);
+ }
/* Save the new ID */
- if (conn->local_id)
- silc_free(conn->local_id);
+
if (conn->local_id_data)
silc_free(conn->local_id_data);
conn->local_entry->id = conn->local_id;
/* Put it to the ID cache */
- silc_idcache_add(conn->client_cache, conn->nickname, SILC_ID_CLIENT,
- conn->local_id, (void *)conn->local_entry, TRUE);
+ silc_idcache_add(conn->client_cache, conn->nickname, conn->local_id,
+ (void *)conn->local_entry, FALSE);
/* Notify application of successful connection. We do it here now that
we've received the Client ID and are allowed to send traffic. */
SilcChannelEntry silc_client_new_channel_id(SilcClient client,
SilcSocketConnection sock,
char *channel_name,
- unsigned int mode,
+ uint32 mode,
SilcIDPayload idp)
{
SilcClientConnection conn = (SilcClientConnection)sock->user_data;
conn->current_channel = channel;
/* Put it to the ID cache */
- silc_idcache_add(conn->channel_cache, channel_name, SILC_ID_CHANNEL,
- (void *)channel->id, (void *)channel, TRUE);
+ silc_idcache_add(conn->channel_cache, channel_name, (void *)channel->id,
+ (void *)channel, FALSE);
return channel;
}
SilcChannelEntry channel;
SilcChannelUser chu;
- if (!silc_idcache_find_by_id(conn->channel_cache, SILC_ID_CACHE_ANY,
- SILC_ID_CHANNEL, &list))
+ if (!silc_idcache_get_all(conn->channel_cache, &list))
return;
silc_idcache_list_first(list, &id_cache);
SilcChannelEntry channel;
SilcChannelUser chu;
- if (!silc_idcache_find_by_id(conn->channel_cache, SILC_ID_CACHE_ANY,
- SILC_ID_CHANNEL, &list))
+ if (!silc_idcache_get_all(conn->channel_cache, &list))
return;
silc_idcache_list_first(list, &id_cache);
silc_idcache_list_free(list);
}
-/* Parses mode mask and returns the mode as string. */
+/* Registers failure timeout to process the received failure packet
+ with timeout. */
-char *silc_client_chmode(unsigned int mode)
+void silc_client_process_failure(SilcClient client,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
{
- char string[20];
-
- if (!mode)
- return NULL;
-
- memset(string, 0, sizeof(string));
-
- if (mode & SILC_CHANNEL_MODE_PRIVATE)
- strncat(string, "p", 1);
-
- if (mode & SILC_CHANNEL_MODE_SECRET)
- strncat(string, "s", 1);
-
- if (mode & SILC_CHANNEL_MODE_PRIVKEY)
- strncat(string, "k", 1);
-
- if (mode & SILC_CHANNEL_MODE_INVITE)
- strncat(string, "i", 1);
+ uint32 failure = 0;
- if (mode & SILC_CHANNEL_MODE_TOPIC)
- strncat(string, "t", 1);
-
- if (mode & SILC_CHANNEL_MODE_ULIMIT)
- strncat(string, "l", 1);
-
- if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
- strncat(string, "a", 1);
-
- /* Rest of mode is ignored */
+ if (sock->protocol) {
+ if (packet->buffer->len >= 4)
+ SILC_GET32_MSB(failure, packet->buffer->data);
- return strdup(string);
+ /* Notify application */
+ client->ops->failure(client, sock->user_data, sock->protocol,
+ (void *)failure);
+ }
}
-/* Parses channel user mode mask and returns te mode as string */
+/* A timeout callback for the re-key. We will be the initiator of the
+ re-key protocol. */
-char *silc_client_chumode(unsigned int mode)
+SILC_TASK_CALLBACK(silc_client_rekey_callback)
{
- char string[4];
-
- if (!mode)
- return NULL;
-
- memset(string, 0, sizeof(string));
-
- if (mode & SILC_CHANNEL_UMODE_CHANFO)
- strncat(string, "f", 1);
+ SilcSocketConnection sock = (SilcSocketConnection)context;
+ SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+ SilcClient client = (SilcClient)conn->rekey->context;
+ SilcProtocol protocol;
+ SilcClientRekeyInternalContext *proto_ctx;
- if (mode & SILC_CHANNEL_UMODE_CHANOP)
- strncat(string, "o", 1);
+ SILC_LOG_DEBUG(("Start"));
- return strdup(string);
+ /* Allocate internal protocol context. This is sent as context
+ to the protocol. */
+ proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+ proto_ctx->client = (void *)client;
+ proto_ctx->sock = silc_socket_dup(sock);
+ proto_ctx->responder = FALSE;
+ proto_ctx->pfs = conn->rekey->pfs;
+
+ /* Perform rekey protocol. Will call the final callback after the
+ protocol is over. */
+ silc_protocol_alloc(SILC_PROTOCOL_CLIENT_REKEY,
+ &protocol, proto_ctx, silc_client_rekey_final);
+ sock->protocol = protocol;
+
+ /* Run the protocol */
+ silc_protocol_execute(protocol, client->timeout_queue, 0, 0);
+
+ /* Re-register re-key timeout */
+ silc_task_register(client->timeout_queue, sock->sock,
+ silc_client_rekey_callback,
+ context, conn->rekey->timeout, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
}
-/* Parses channel user mode and returns it as special mode character. */
+/* The final callback for the REKEY protocol. This will actually take the
+ new key material into use. */
-char *silc_client_chumode_char(unsigned int mode)
+SILC_TASK_CALLBACK(silc_client_rekey_final)
{
- char string[4];
-
- if (!mode)
- return NULL;
-
- memset(string, 0, sizeof(string));
-
- if (mode & SILC_CHANNEL_UMODE_CHANFO)
- strncat(string, "*", 1);
-
- if (mode & SILC_CHANNEL_UMODE_CHANOP)
- strncat(string, "@", 1);
-
- return strdup(string);
-}
-
-/* Failure timeout callback. If this is called then we will immediately
- process the received failure. We always process the failure with timeout
- since we do not want to blindly trust to received failure packets.
- This won't be called (the timeout is cancelled) if the failure was
- bogus (it is bogus if remote does not close the connection after sending
- the failure). */
+ SilcProtocol protocol = (SilcProtocol)context;
+ SilcClientRekeyInternalContext *ctx =
+ (SilcClientRekeyInternalContext *)protocol->context;
+ SilcClient client = (SilcClient)ctx->client;
+ SilcSocketConnection sock = ctx->sock;
-SILC_TASK_CALLBACK_GLOBAL(silc_client_failure_callback)
-{
- SilcClientFailureContext *f = (SilcClientFailureContext *)context;
+ SILC_LOG_DEBUG(("Start"));
- if (f->sock->protocol) {
- f->sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
- f->sock->protocol->execute(f->client->timeout_queue, 0,
- f->sock->protocol, f->sock->sock, 0, 0);
-
- /* Notify application */
- f->client->ops->failure(f->client, f->sock->user_data, f->sock->protocol,
- (void *)f->failure);
+ if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+ protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
+ /* Error occured during protocol */
+ silc_protocol_cancel(protocol, client->timeout_queue);
+ silc_protocol_free(protocol);
+ sock->protocol = NULL;
+ if (ctx->packet)
+ silc_packet_context_free(ctx->packet);
+ if (ctx->ske)
+ silc_ske_free(ctx->ske);
+ silc_socket_free(ctx->sock);
+ silc_free(ctx);
+ return;
}
- silc_free(f);
-}
-
-/* Registers failure timeout to process the received failure packet
- with timeout. */
-
-void silc_client_process_failure(SilcClient client,
- SilcSocketConnection sock,
- SilcPacketContext *packet)
-{
- SilcClientFailureContext *f;
- unsigned int failure = 0;
-
- if (sock->protocol) {
- if (packet->buffer->len >= 4)
- SILC_GET32_MSB(failure, packet->buffer->data);
-
- f = silc_calloc(1, sizeof(*f));
- f->client = client;
- f->sock = sock;
- f->failure = failure;
-
- /* We will wait 5 seconds to process this failure packet */
- silc_task_register(client->timeout_queue, sock->sock,
- silc_client_failure_callback, (void *)f, 5, 0,
- SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
- }
+ /* Cleanup */
+ silc_protocol_free(protocol);
+ sock->protocol = NULL;
+ if (ctx->packet)
+ silc_packet_context_free(ctx->packet);
+ if (ctx->ske)
+ silc_ske_free(ctx->ske);
+ silc_socket_free(ctx->sock);
+ silc_free(ctx);
}