client.c
- Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+ Author: Pekka Riikonen <priikone@silcnet.org>
Copyright (C) 1997 - 2001 Pekka Riikonen
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,
+ uint32 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
if (params)
memcpy(new_client->params, params, sizeof(*params));
+ if (!new_client->params->task_max)
+ new_client->params->task_max = 200;
+
if (!new_client->params->rekey_secs)
new_client->params->rekey_secs = 3600;
+ if (!new_client->params->connauth_request_secs)
+ new_client->params->connauth_request_secs = 2;
+
+ new_client->params->
+ nickname_format[sizeof(new_client->params->nickname_format) - 1] = 0;
+
return new_client;
}
silc_client_protocols_register();
/* Initialize the scheduler */
- client->schedule = silc_schedule_init(&client->io_queue,
- &client->timeout_queue,
- &client->generic_queue, 5000);
+ client->schedule = silc_schedule_init(client->params->task_max ?
+ client->params->task_max : 200);
if (!client->schedule)
return FALSE;
{
SILC_LOG_DEBUG(("Stopping client"));
- /* 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(client->schedule);
silc_schedule_uninit(client->schedule);
silc_schedule(client->schedule);
}
+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
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;
/* 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;
/* Register task that will receive the async connect and will
read the result. */
- ctx->task = silc_task_register(ctx->client->io_queue, sock,
- silc_client_connect_to_server_start,
- (void *)ctx, 0, 0,
- SILC_TASK_FD,
- SILC_TASK_PRI_NORMAL);
- silc_task_reset_iotype(ctx->task, SILC_TASK_WRITE);
- silc_schedule_set_listen_fd(ctx->client->schedule, sock, ctx->task->iomask);
+ ctx->task = silc_schedule_task_add(ctx->client->schedule, sock,
+ silc_client_connect_to_server_start,
+ (void *)ctx, 0, 0,
+ SILC_TASK_FD,
+ SILC_TASK_PRI_NORMAL);
+ silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE);
ctx->sock = sock;
used only if the application performed the connecting outside the library.
The library however may use this internally. */
-int silc_client_start_key_exchange(SilcClient client,
- SilcClientConnection conn,
- int fd)
+bool silc_client_start_key_exchange(SilcClient client,
+ SilcClientConnection conn,
+ int fd)
{
SilcProtocol protocol;
SilcClientKEInternalContext *proto_ctx;
/* 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 = strdup(client->username);
conn->sock->hostname = conn->remote_host;
conn->sock->ip = strdup(conn->remote_host);
silc_client_connect_to_server_second);
if (!protocol) {
client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
- "Error: Could not start authentication protocol");
+ "Error: Could not start key exchange protocol");
return FALSE;
}
conn->sock->protocol = protocol;
SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(fd);
/* Execute the protocol */
- silc_protocol_execute(protocol, client->timeout_queue, 0, 0);
+ silc_protocol_execute(protocol, client->schedule, 0, 0);
return TRUE;
}
/* Unregister old connection try */
silc_schedule_unset_listen_fd(client->schedule, fd);
silc_net_close_connection(fd);
- silc_task_unregister(client->io_queue, ctx->task);
+ silc_schedule_task_del(client->schedule, ctx->task);
/* Try again */
silc_client_connect_to_server_internal(ctx);
ctx->host, strerror(opt));
silc_schedule_unset_listen_fd(client->schedule, fd);
silc_net_close_connection(fd);
- silc_task_unregister(client->io_queue, ctx->task);
+ silc_schedule_task_del(client->schedule, ctx->task);
silc_free(ctx);
/* Notify application of failure */
}
silc_schedule_unset_listen_fd(client->schedule, fd);
- silc_task_unregister(client->io_queue, ctx->task);
+ silc_schedule_task_del(client->schedule, ctx->task);
silc_free(ctx);
if (!silc_client_start_key_exchange(client, conn, fd)) {
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->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->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,
+ uint32 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_calloc(auth_data_len, sizeof(*auth_data));
+ memcpy(proto_ctx->auth_data, 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->timeout_queue, 0, 0);
+ silc_protocol_execute(proto_ctx->sock->protocol, client->schedule, 0, 0);
}
/* Finalizes the connection to the remote SILC server. This is called
/* Register re-key timeout */
conn->rekey->timeout = client->params->rekey_secs;
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_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_protocol_free(protocol);
if (ctx->auth_data)
SilcClient client = (SilcClient)parser_context->context;
/* Parse the packet */
- silc_task_register(client->timeout_queue, parser_context->sock->sock,
- silc_client_packet_parse_real,
- (void *)parser_context, 0, 1,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
+ 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);
}
/* Parses the packet type and calls what ever routines the packet type
* success message is for whatever protocol is executing currently.
*/
if (sock->protocol)
- silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
+ silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
break;
case SILC_PACKET_FAILURE:
/*
break;
/* Let the protocol handle the packet */
- silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
+ silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
} else {
SILC_LOG_ERROR(("Received Key Exchange packet but no key exchange "
"protocol active, packet dropped."));
proto_ctx->packet = silc_packet_context_dup(packet);
/* Let the protocol handle the packet */
- silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
+ silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
} else {
SilcClientKEInternalContext *proto_ctx =
(SilcClientKEInternalContext *)sock->protocol->context;
break;
/* Let the protocol handle the packet */
- silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
+ silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
}
} else {
SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange "
proto_ctx->packet = silc_packet_context_dup(packet);
/* Let the protocol handle the packet */
- silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
+ silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
} else {
SilcClientKEInternalContext *proto_ctx =
(SilcClientKEInternalContext *)sock->protocol->context;
break;
/* Let the protocol handle the packet */
- silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
+ silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
}
} else {
SILC_LOG_ERROR(("Received Key Exchange 2 packet but no key exchange "
/* Let the protocol handle the packet */
if (proto_ctx->responder == FALSE)
- silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
+ silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
else
/* Let the protocol handle the packet */
- silc_protocol_execute(sock->protocol, client->timeout_queue,
+ silc_protocol_execute(sock->protocol, client->schedule,
0, 100000);
} else {
SILC_LOG_ERROR(("Received Re-key done packet but no re-key "
}
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;
+
default:
SILC_LOG_DEBUG(("Incorrect packet type %d, packet dropped", type));
break;
silc_schedule_unset_listen_fd(client->schedule, sock->sock);
/* Unregister all tasks */
- silc_task_unregister_by_fd(client->io_queue, sock->sock);
- silc_task_unregister_by_fd(client->timeout_queue, sock->sock);
+ 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);
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;
+ silc_protocol_execute_final(sock->protocol, client->schedule);
/* 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);
+ silc_protocol_execute_final(sock->protocol, client->schedule);
sock->protocol = NULL;
}
}
/* Free everything */
if (del && sock->user_data) {
- /* XXX Free all client entries and channel entries. */
+ /* Free all cache entries */
+ SilcIDCacheList list;
+ SilcIDCacheEntry entry;
+ bool ret;
+
+ if (silc_idcache_get_all(conn->client_cache, &list)) {
+ ret = silc_idcache_list_first(list, &entry);
+ while (ret) {
+ silc_client_del_client(client, conn, entry->context);
+ ret = silc_idcache_list_next(list, &entry);
+ }
+ silc_idcache_list_free(list);
+ }
+
+ if (silc_idcache_get_all(conn->channel_cache, &list)) {
+ ret = silc_idcache_list_first(list, &entry);
+ while (ret) {
+ silc_client_del_channel(client, conn, entry->context);
+ ret = silc_idcache_list_next(list, &entry);
+ }
+ silc_idcache_list_free(list);
+ }
+
+ if (silc_idcache_get_all(conn->server_cache, &list)) {
+ ret = silc_idcache_list_first(list, &entry);
+ while (ret) {
+ silc_client_del_server(client, conn, entry->context);
+ ret = silc_idcache_list_next(list, &entry);
+ }
+ silc_idcache_list_free(list);
+ }
/* Clear ID caches */
if (conn->client_cache)
if (conn->server_cache)
silc_idcache_del_all(conn->server_cache);
- /* Free data */
+ /* Free data (my ID is freed in above silc_client_del_client) */
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)
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);
- }
+ if (!conn->local_entry->username)
+ conn->local_entry->username = strdup(client->username);
+ if (!conn->local_entry->hostname)
+ conn->local_entry->hostname = strdup(client->hostname);
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,
+ silc_idcache_add(conn->client_cache, strdup(conn->nickname), conn->local_id,
(void *)conn->local_entry, FALSE);
/* Issue INFO command to fetch the real server name and server information
silc_list_init(channel->clients, struct SilcChannelUserStruct, next);
/* Put it to the ID cache */
- silc_idcache_add(conn->channel_cache, channel_name, (void *)channel->id,
- (void *)channel, FALSE);
+ silc_idcache_add(conn->channel_cache, channel->channel_name,
+ (void *)channel->id, (void *)channel, FALSE);
return channel;
}
sock->protocol = protocol;
/* Run the protocol */
- silc_protocol_execute(protocol, client->timeout_queue, 0, 0);
+ silc_protocol_execute(protocol, client->schedule, 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);
+ silc_schedule_task_add(client->schedule, sock->sock,
+ 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
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_cancel(protocol, client->schedule);
silc_protocol_free(protocol);
sock->protocol = NULL;
if (ctx->packet)
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;
+ uint16 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->params->connauth_request_secs, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+}