X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=lib%2Fsilcclient%2Fclient.c;h=af4de6513b719cd065905a6c275af92d8381c40c;hp=617682e17030e23d5c42587e99725b79f8166c50;hb=a818c5b5411bbc4436d1c5f011236985c96bb787;hpb=e5d8d3db6caa344b3d419b884556c21b15e7d123 diff --git a/lib/silcclient/client.c b/lib/silcclient/client.c index 617682e1..af4de651 100644 --- a/lib/silcclient/client.c +++ b/lib/silcclient/client.c @@ -1,16 +1,15 @@ /* - client.c + client.c Author: Pekka Riikonen - 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 @@ -19,25 +18,26 @@ */ /* $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, - uint32 auth_data_len, void *context); + 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 @@ -53,24 +53,28 @@ SilcClient silc_client_alloc(SilcClientOperations *ops, 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->params->task_max) - new_client->params->task_max = 200; + if (!new_client->internal->params->task_max) + new_client->internal->params->task_max = 200; - if (!new_client->params->rekey_secs) - new_client->params->rekey_secs = 3600; + if (!new_client->internal->params->rekey_secs) + new_client->internal->params->rekey_secs = 3600; - if (!new_client->params->connauth_request_secs) - new_client->params->connauth_request_secs = 2; + if (!new_client->internal->params->connauth_request_secs) + new_client->internal->params->connauth_request_secs = 2; - new_client->params-> - nickname_format[sizeof(new_client->params->nickname_format) - 1] = 0; + new_client->internal->params-> + nickname_format[sizeof(new_client->internal-> + params->nickname_format) - 1] = 0; return new_client; } @@ -83,8 +87,9 @@ void silc_client_free(SilcClient 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); } } @@ -98,11 +103,11 @@ int silc_client_init(SilcClient client) SILC_LOG_DEBUG(("Initializing client")); /* Initialize hash functions for client to use */ - silc_hash_alloc("md5", &client->md5hash); - silc_hash_alloc("sha1", &client->sha1hash); + silc_hash_alloc("md5", &client->internal->md5hash); + silc_hash_alloc("sha1", &client->internal->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(); @@ -113,11 +118,15 @@ int silc_client_init(SilcClient client) 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); if (!client->schedule) return FALSE; + /* Register commands */ + silc_client_commands_register(client); + return TRUE; } @@ -132,6 +141,7 @@ void silc_client_stop(SilcClient client) silc_schedule_uninit(client->schedule); silc_client_protocols_unregister(); + silc_client_commands_unregister(client); SILC_LOG_DEBUG(("Client stopped")); } @@ -148,6 +158,20 @@ void silc_client_run(SilcClient client) 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) { @@ -182,18 +206,20 @@ SilcClientConnection silc_client_add_connection(SilcClient client, conn->remote_port = port; conn->context = context; conn->pending_commands = silc_dlist_init(); + conn->ftp_sessions = silc_dlist_init(); /* 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; } @@ -204,12 +230,19 @@ void silc_client_del_connection(SilcClient client, SilcClientConnection 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); + silc_dlist_uninit(conn->ftp_sessions); silc_free(conn); - client->conns[i] = NULL; + + client->internal->conns[i] = NULL; } } @@ -221,24 +254,28 @@ 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; + 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. */ @@ -247,13 +284,13 @@ void silc_client_del_socket(SilcClient client, SilcSocketConnection sock) { 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; } } @@ -305,8 +342,8 @@ int silc_client_connect_to_server(SilcClient client, int port, conn = silc_client_add_connection(client, 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. */ @@ -324,33 +361,26 @@ int silc_client_connect_to_server(SilcClient client, int port, 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. */ -bool 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); - /* 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); + SILC_LOG_DEBUG(("Start")); - conn->nickname = strdup(client->username); - conn->sock->hostname = conn->remote_host; - conn->sock->ip = strdup(conn->remote_host); + /* 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->ip) + conn->sock->ip = strdup(conn->remote_host); conn->sock->port = conn->remote_port; /* Allocate internal Key Exchange context. This is sent to the @@ -369,9 +399,11 @@ bool silc_client_start_key_exchange(SilcClient client, &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 key exchange 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, FALSE); + return; } conn->sock->protocol = protocol; @@ -382,11 +414,53 @@ bool silc_client_start_key_exchange(SilcClient client, 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, FALSE); + if (ctx->packet) + silc_packet_context_free(ctx->packet); + silc_free(ctx); } /* Start of the connection to the remote server. This is called after @@ -407,12 +481,12 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_start) 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); @@ -424,16 +498,16 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_start) 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, FALSE); silc_client_del_connection(client, conn); } return; @@ -443,10 +517,7 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_start) 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 @@ -477,8 +548,9 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_second) 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; } @@ -490,7 +562,8 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_second) 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 @@ -512,20 +585,22 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_second) /* 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); + 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->ops->get_auth_method client operation - to resolve the authentication method. We will continue the executiong - of the protocol in 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, - uint32 auth_data_len, void *context) + SilcUInt32 auth_data_len, void *context) { SilcClientConnAuthInternalContext *proto_ctx = (SilcClientConnAuthInternalContext *)context; @@ -537,8 +612,7 @@ void silc_client_resolve_auth_method(bool success, 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 = silc_memdup(auth_data, auth_data_len); proto_ctx->auth_data_len = auth_data_len; } @@ -582,8 +656,9 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final) 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; } @@ -614,7 +689,7 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final) 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, @@ -732,15 +807,15 @@ SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process) /* 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); + if (sock == conn->sock && sock->type != SILC_SOCKET_TYPE_CLIENT) + client->internal->ops->disconnect(client, conn); silc_client_close_connection(client, sock, conn); return; } SILC_LOG_DEBUG(("EOF from connection %d", sock->sock)); - if (sock == conn->sock) - client->ops->disconnect(client, conn); + if (sock == conn->sock && sock->type != SILC_SOCKET_TYPE_CLIENT) + client->internal->ops->disconnect(client, conn); silc_client_close_connection(client, sock, conn); return; } @@ -748,102 +823,75 @@ SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process) /* 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); - - if (ret < 0) - goto out; + ret = silc_packet_parse_special(packet, conn->receive_key); - 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 @@ -1012,7 +1060,8 @@ void silc_client_packet_parse_type(SilcClient client, 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, @@ -1039,7 +1088,7 @@ void silc_client_packet_parse_type(SilcClient client, */ 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) @@ -1131,10 +1180,12 @@ void silc_client_packet_send(SilcClient client, SilcCipher cipher, SilcHmac hmac, unsigned char *data, - uint32 data_len, + SilcUInt32 data_len, int force_send) { SilcPacketContext packetdata; + int block_len; + SilcUInt32 sequence = 0; if (!sock) return; @@ -1153,8 +1204,13 @@ void silc_client_packet_send(SilcClient client, 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; @@ -1180,7 +1236,7 @@ void silc_client_packet_send(SilcClient client, } packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + packetdata.src_id_len + packetdata.dst_id_len; - packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen); + packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen, block_len); /* Prepare outgoing data buffer for packet sending */ silc_packet_send_prepare(sock, @@ -1199,13 +1255,14 @@ void silc_client_packet_send(SilcClient client, silc_buffer_put(sock->outbuf, data, data_len); /* Create the outgoing packet */ - silc_packet_assemble(&packetdata); + silc_packet_assemble(&packetdata, cipher); /* Encrypt the packet */ if (cipher) - silc_packet_encrypt(cipher, hmac, sock->outbuf, sock->outbuf->len); + silc_packet_encrypt(cipher, hmac, sequence, sock->outbuf, + sock->outbuf->len); - SILC_LOG_HEXDUMP(("Packet, len %d", sock->outbuf->len), + SILC_LOG_HEXDUMP(("Packet (%d), len %d", sequence, sock->outbuf->len), sock->outbuf->data, sock->outbuf->len); /* Now actually send the packet */ @@ -1242,6 +1299,8 @@ void silc_client_close_connection(SilcClient client, { int del = FALSE; + SILC_LOG_DEBUG(("Start")); + if (!sock || (sock && conn->sock == sock)) del = TRUE; if (!sock) @@ -1311,13 +1370,14 @@ void silc_client_close_connection(SilcClient client, /* 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 (my ID is freed in above silc_client_del_client) */ + /* 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_data) @@ -1326,13 +1386,23 @@ void silc_client_close_connection(SilcClient client, 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->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); + 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); + memset(conn, 0, sizeof(*conn)); silc_client_del_connection(client, conn); } @@ -1340,6 +1410,22 @@ void silc_client_close_connection(SilcClient client, silc_socket_free(sock); } +/* 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(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. */ @@ -1352,13 +1438,18 @@ void silc_client_disconnected_by_server(SilcClient client, 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); + msg = silc_memdup(message->data, message->len); + client->internal->ops->say(client, sock->user_data, + SILC_CLIENT_MESSAGE_AUDIT, msg); silc_free(msg); 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. @@ -1370,12 +1461,24 @@ void silc_client_error_by_server(SilcClient client, { 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)); +} + /* Processes the received new Client ID from server. Old Client ID is deleted from cache and new one is added. */ @@ -1385,6 +1488,7 @@ void silc_client_receive_new_id(SilcClient client, { SilcClientConnection conn = (SilcClientConnection)sock->user_data; int connecting = FALSE; + SilcClientID *client_id = silc_id_payload_get_id(idp); SilcBuffer sidp; if (!conn->local_entry) @@ -1392,6 +1496,12 @@ void silc_client_receive_new_id(SilcClient client, /* 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); } @@ -1401,7 +1511,7 @@ void silc_client_receive_new_id(SilcClient client, 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);; @@ -1413,94 +1523,61 @@ void silc_client_receive_new_id(SilcClient client, 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); + if (!conn->local_entry->server) + conn->local_entry->server = strdup(conn->remote_host); conn->local_entry->id = conn->local_id; conn->local_entry->valid = TRUE; - - /* Put it to the ID cache */ - 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 - 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); + 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->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) { + /* Send NICK command if the nickname was set by the application (and is + not same as the username). Send this with little timeout. */ + if (client->nickname && strcmp(client->nickname, client->username)) + silc_schedule_task_add(client->schedule, 0, + silc_client_send_auto_nick, conn, + 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); + + /* Issue INFO command to fetch the real server name and server information + and other stuff. */ + silc_client_command_register(client, SILC_COMMAND_INFO, NULL, NULL, + silc_client_command_reply_info_i, 0, + ++conn->cmd_ident); + sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER); + silc_client_command_send(client, conn, SILC_COMMAND_INFO, + conn->cmd_ident, 1, 2, sidp->data, sidp->len); + silc_buffer_free(sidp); + + /* Notify application of successful connection. We do it here now that + we've received the Client ID and are allowed to send traffic. */ + client->internal->ops->connect(client, conn, TRUE); + } } -/* 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. @@ -1513,35 +1590,20 @@ void silc_client_replace_from_channels(SilcClient client, 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 @@ -1551,15 +1613,15 @@ void silc_client_process_failure(SilcClient client, 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); } } @@ -1653,7 +1715,7 @@ void silc_client_connection_auth_request(SilcClient client, SilcPacketContext *packet) { SilcClientConnection conn = (SilcClientConnection)sock->user_data; - uint16 conn_type, auth_meth; + SilcUInt16 conn_type, auth_meth; int ret; /* If we haven't send our request then ignore this one. */ @@ -1743,34 +1805,7 @@ silc_client_request_authentication_method(SilcClient client, connauth->timeout = silc_schedule_task_add(client->schedule, conn->sock->sock, silc_client_request_authentication_method_timeout, - conn, client->params->connauth_request_secs, 0, + conn, + client->internal->params->connauth_request_secs, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); } - -/* Called when file transfer packet is received. This will parse the - packet and give it to the file transfer protocol. */ - -void silc_client_ftp(SilcClient client, - SilcSocketConnection sock, - SilcPacketContext *packet) -{ - SilcClientConnection conn = (SilcClientConnection)sock->user_data; - uint8 type; - int ret; - - /* Parse the payload */ - ret = silc_buffer_unformat(packet->buffer, - SILC_STR_UI_CHAR(&type), - SILC_STR_END); - if (ret == -1) - return; - - /* We support only type number 1 (== SFTP) */ - if (type != 1) - return; - - silc_buffer_pull(packet->buffer, 1); - - /* Give it to the file transfer protocol processor. */ - //silc_sftp_client_receive_process(xxx, sock, packet); -}