From: Pekka Riikonen Date: Fri, 16 Feb 2001 00:33:23 +0000 (+0000) Subject: updates. X-Git-Tag: SILC.0.1~219 X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=commitdiff_plain;h=6175840fb524397cecae2d70b4befa8d377a652d updates. --- diff --git a/CHANGES b/CHANGES index b9c7fddd..e50f864b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,70 @@ +Thu Feb 15 20:07:37 EET 2001 Pekka Riikonen + + * Added new packet type SILC_PACKET_HEARTBEAT that is used to + send keepalive packets. The packet can be sent by clients, + servers and routers. + + Added function silc_socket_set_heartbeat into the file + lib/silccore/silcsockconn.[ch] to set the heartbeat timeout. + If not set, the heartbeat is not performed. The actual + heartbeat is implemented in the low level socket connection + library. However, application is responsible of actually + sending the packet. + + Added silc_server_send_heartbeat to send the actual heartbeat + packet into silcd/packet_send.[ch]. Server now performs + keepalive with all connections. + + * Added silc_task_get_first function into lib/silcutil/silctask.c + to return the timeout task with shortest timeout. There was a bug + in task unregistration that caused problems. TODO has been + updated to include that task system must be rewritten. + + * The client library will now resolve the client information when + receiving JOIN notify from server for client that we know but + have incomplete information. + + * Rewrote parts of silc_server_remove_from_channels and + silc_server_remove_from_one_channel as they did not remove the + channel in some circumstances even though they should've. + + * Encryption problem encountered in server: + + The LEAVE command used to send the Channel Key packet to the + router immediately after generating it. However, the code + had earlier sent Remove Channel user packet but not immediately, + ie. it was put to queue. The order of packets in the router + was that Channel Key packet was first and Remove Channel User + packet was second, even though they were encrypted in the + reverse order. For this reason, MAC check failed. Now, this + is fixed by not sending the Channel Key packet immediately but + putting it to queue. However, this is more fundamental problem: + packets that are in queue should actually not be encrypted + because packets that are sent immediately gets encrypted + actually with wrong IV (and thus MAC check fails). So, packets + that are in queue should be encrypted when they are sent to + the wire and not when they put to the queue. + + However, the problem is that the current system has not been + designed to work that way. Instead, the packet is encrypted + as soon as possible and left to the queue. The queue is then + just purged into wire. There won't be any fixes for this + any time soon. So, the current semantic for packet sending + is as follows: + + o If you send packet to remote host and do not force the send + (the packet will be in queue) then all subsequent packets to the + same remote host must also be put to the queue. Only after the + queue has been purged is it safe again to force the packet + send immediately. + + o If you send all packets immediately then it safe to send + any of subsequent packets through the queue, however, after + the first packet is put to queue then any subsequent packets + must also be put to the queue. + + Follow these rules and everything works fine. + Thu Feb 15 14:24:32 EET 2001 Pekka Riikonen * Added new function silc_server_remove_clients_by_server to diff --git a/TODO b/TODO index e60dfdc2..e8bbcf8c 100644 --- a/TODO +++ b/TODO @@ -153,6 +153,10 @@ TODO In SILC Server TODO In SILC Libraries ====================== + o Rewrite the task system. I made it too complex and too "neat" and + it really should be rewritten. We don't need priorities really, one + priority is enough. This will simplify a lot the task system. + o Implement PFS (Perfect Forward Secrecy) flag in SKE (and in client and server, actually). If PFS is set, re-key must cause new key exchange. This is required by the SILC protocol. diff --git a/apps/silc/client_ops.c b/apps/silc/client_ops.c index ae20153b..4097e9f8 100644 --- a/apps/silc/client_ops.c +++ b/apps/silc/client_ops.c @@ -49,7 +49,7 @@ void silc_channel_message(SilcClient client, SilcClientConnection conn, char *sender, char *channel_name, char *msg) { /* Message from client */ - if (!strcmp(conn->current_channel->channel_name, channel_name)) + if (conn && !strcmp(conn->current_channel->channel_name, channel_name)) silc_print(client, "<%s> %s", sender, msg); else silc_print(client, "<%s:%s> %s", sender, channel_name, msg); diff --git a/apps/silc/local_command.c b/apps/silc/local_command.c index 56e36da4..7a9316a5 100644 --- a/apps/silc/local_command.c +++ b/apps/silc/local_command.c @@ -20,6 +20,9 @@ /* * $Id$ * $Log$ + * Revision 1.4 2001/02/16 00:33:23 priikone + * updates. + * * Revision 1.3 2001/02/14 15:31:33 priikone * Do not allow several server connections. * @@ -207,6 +210,7 @@ SILC_CLIENT_LCMD_FUNC(server) port = 706; } +#if 0 if (conn && conn->remote_host) { if (!strcmp(hostname, conn->remote_host) && port == conn->remote_port) { silc_say(client, conn, "You are already connected to that server"); @@ -217,6 +221,7 @@ SILC_CLIENT_LCMD_FUNC(server) cmd->client->ops->disconnect(cmd->client, cmd->conn); silc_client_close_connection(cmd->client, cmd->conn->sock); } +#endif /* Connect asynchronously to not to block user interface */ silc_client_connect_to_server(cmd->client, port, hostname, NULL); diff --git a/apps/silcd/command.c b/apps/silcd/command.c index ce9e6043..6f341228 100644 --- a/apps/silcd/command.c +++ b/apps/silcd/command.c @@ -2819,7 +2819,7 @@ SILC_SERVER_CMD_FUNC(leave) silc_server_packet_send(server, cmd->server->router->connection, SILC_PACKET_CHANNEL_KEY, 0, packet->data, - packet->len, TRUE); + packet->len, FALSE); } else { } diff --git a/apps/silcd/idlist.c b/apps/silcd/idlist.c index c1354143..1d6b9540 100644 --- a/apps/silcd/idlist.c +++ b/apps/silcd/idlist.c @@ -504,15 +504,16 @@ silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode, /* Free channel entry. This free's everything. */ -void silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry) +int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry) { if (entry) { SilcChannelClientEntry chl; /* Remove from cache */ if (entry->id) - silc_idcache_del_by_id(id_list->channels, SILC_ID_CHANNEL, - (void *)entry->id); + if (!silc_idcache_del_by_id(id_list->channels, SILC_ID_CHANNEL, + (void *)entry->id)) + return FALSE; /* Free data */ if (entry->channel_name) @@ -535,6 +536,8 @@ void silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry) silc_free(chl); } } + + return TRUE; } /* Finds channel by channel name. Channel names are unique and they diff --git a/apps/silcd/idlist.h b/apps/silcd/idlist.h index 6b865eb9..cbdf64f8 100644 --- a/apps/silcd/idlist.h +++ b/apps/silcd/idlist.h @@ -496,7 +496,7 @@ SilcChannelEntry silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode, SilcChannelID *id, SilcServerEntry router, SilcCipher channel_key); -void silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry); +int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry); SilcChannelEntry silc_idlist_find_channel_by_name(SilcIDList id_list, char *name, SilcIDCacheEntry *ret_entry); diff --git a/apps/silcd/packet_send.c b/apps/silcd/packet_send.c index 2ecca8b5..83fef72f 100644 --- a/apps/silcd/packet_send.c +++ b/apps/silcd/packet_send.c @@ -1207,3 +1207,12 @@ void silc_server_send_set_mode(SilcServer server, silc_buffer_free(packet); } + +/* Send the heartbeat packet. */ + +void silc_server_send_heartbeat(SilcServer server, + SilcSocketConnection sock) +{ + silc_server_packet_send(server, sock, SILC_PACKET_HEARTBEAT, 0, + NULL, 0, FALSE); +} diff --git a/apps/silcd/packet_send.h b/apps/silcd/packet_send.h index 17b4a64d..cfd79378 100644 --- a/apps/silcd/packet_send.h +++ b/apps/silcd/packet_send.h @@ -148,5 +148,7 @@ void silc_server_send_set_mode(SilcServer server, int broadcast, int mode_type, unsigned int mode_mask, unsigned int argc, ...); +void silc_server_send_heartbeat(SilcServer server, + SilcSocketConnection sock); #endif diff --git a/apps/silcd/server.c b/apps/silcd/server.c index 6fd86d7d..6a6733c6 100644 --- a/apps/silcd/server.c +++ b/apps/silcd/server.c @@ -557,6 +557,7 @@ SILC_TASK_CALLBACK(silc_server_connect_router) silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket); server->sockets[sock] = newsocket; newsocket->hostname = strdup(sconn->remote_host); + newsocket->ip = strdup(sconn->remote_host); newsocket->port = sconn->remote_port; sconn->sock = newsocket; @@ -783,6 +784,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final) SilcSocketConnection sock = ctx->sock; SilcServerEntry id_entry; SilcBuffer packet; + SilcServerHBContext hb_context; unsigned char *id_string; SILC_LOG_DEBUG(("Start")); @@ -849,6 +851,15 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final) server->router = id_entry; server->router->data.registered = TRUE; + /* Perform keepalive. The `hb_context' will be freed automatically + when finally calling the silc_socket_free function. XXX hardcoded + timeout!! */ + hb_context = silc_calloc(1, sizeof(*hb_context)); + hb_context->server = server; + silc_socket_set_heartbeat(sock, 600, hb_context, + silc_server_perform_heartbeat, + server->timeout_queue); + out: /* Free the temporary connection data context */ if (sconn) @@ -1042,6 +1053,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) (SilcServerConnAuthInternalContext *)protocol->context; SilcServer server = (SilcServer)ctx->server; SilcSocketConnection sock = ctx->sock; + SilcServerHBContext hb_context; void *id_entry = NULL; SILC_LOG_DEBUG(("Start")); @@ -1160,6 +1172,15 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) /* Connection has been fully established now. Everything is ok. */ SILC_LOG_DEBUG(("New connection authenticated")); + /* Perform keepalive. The `hb_context' will be freed automatically + when finally calling the silc_socket_free function. XXX hardcoded + timeout!! */ + hb_context = silc_calloc(1, sizeof(*hb_context)); + hb_context->server = server; + silc_socket_set_heartbeat(sock, 600, hb_context, + silc_server_perform_heartbeat, + server->timeout_queue); + silc_protocol_free(protocol); if (ctx->packet) silc_packet_context_free(ctx->packet); @@ -1717,6 +1738,13 @@ void silc_server_packet_parse_type(SilcServer server, silc_server_set_mode(server, sock, packet); break; + case SILC_PACKET_HEARTBEAT: + /* + * Received heartbeat. + */ + SILC_LOG_DEBUG(("Heartbeat packet")); + break; + default: SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type)); break; @@ -1961,7 +1989,7 @@ void silc_server_remove_from_channels(SilcServer server, { SilcChannelEntry channel; SilcChannelClientEntry chl; - SilcBuffer chidp, clidp; + SilcBuffer clidp; SILC_LOG_DEBUG(("Start")); @@ -1975,40 +2003,45 @@ void silc_server_remove_from_channels(SilcServer server, silc_list_start(client->channels); while ((chl = silc_list_get(client->channels)) != SILC_LIST_END) { channel = chl->channel; - chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL); - /* Remove from list */ + /* Remove channel from client's channel list */ silc_list_del(client->channels, chl); - /* If this client is last one on the channel the channel - is removed all together. */ - if (silc_list_count(channel->user_list) < 2) { + /* Remove channel if there is no users anymore */ + if (server->server_type == SILC_ROUTER && + silc_list_count(channel->user_list) < 2) { + if (!silc_idlist_del_channel(server->local_list, channel)) + silc_idlist_del_channel(server->global_list, channel); + server->stat.my_channels--; + continue; + } - /* However, if the channel has marked global users then the - channel is not created locally, and this does not remove the - channel globally from SILC network, in this case we will - notify that this client has left the channel. */ + /* Remove client from channel's client list */ + silc_list_del(channel->user_list, chl); + silc_free(chl); + server->stat.my_chanclients--; + + /* If there is not at least one local user on the channel then we don't + need the channel entry anymore, we can remove it safely. */ + if (server->server_type == SILC_SERVER && + !silc_server_channel_has_local(channel)) { + /* Notify about leaving client if this channel has global users. */ if (channel->global_users) silc_server_send_notify_to_channel(server, NULL, channel, FALSE, SILC_NOTIFY_TYPE_SIGNOFF, 1, clidp->data, clidp->len); - - silc_idlist_del_channel(server->local_list, channel); + + if (!silc_idlist_del_channel(server->local_list, channel)) + silc_idlist_del_channel(server->global_list, channel); server->stat.my_channels--; continue; } - /* Remove from list */ - silc_list_del(channel->user_list, chl); - silc_free(chl); - server->stat.my_chanclients--; - /* Send notify to channel about client leaving SILC and thus the entire channel. */ silc_server_send_notify_to_channel(server, NULL, channel, FALSE, SILC_NOTIFY_TYPE_SIGNOFF, 1, clidp->data, clidp->len); - silc_buffer_free(chidp); } silc_buffer_free(clidp); @@ -2043,25 +2076,20 @@ int silc_server_remove_from_one_channel(SilcServer server, ch = chl->channel; - /* Remove from list */ + /* Remove channel from client's channel list */ silc_list_del(client->channels, chl); - /* If this client is last one on the channel the channel - is removed all together. */ - if (silc_list_count(channel->user_list) < 2) { - /* Notify about leaving client if this channel has global users. */ - if (notify && channel->global_users) - silc_server_send_notify_to_channel(server, NULL, channel, FALSE, - SILC_NOTIFY_TYPE_LEAVE, 1, - clidp->data, clidp->len); - - silc_idlist_del_channel(server->local_list, channel); + /* Remove channel if there is no users anymore */ + if (server->server_type == SILC_ROUTER && + silc_list_count(channel->user_list) < 2) { + if (!silc_idlist_del_channel(server->local_list, channel)) + silc_idlist_del_channel(server->global_list, channel); silc_buffer_free(clidp); server->stat.my_channels--; return FALSE; } - /* Remove from list */ + /* Remove client from channel's client list */ silc_list_del(channel->user_list, chl); silc_free(chl); server->stat.my_chanclients--; @@ -2072,11 +2100,18 @@ int silc_server_remove_from_one_channel(SilcServer server, !silc_server_channel_has_global(channel)) channel->global_users = FALSE; - /* If tehre is not at least one local user on the channel then we don't + /* If there is not at least one local user on the channel then we don't need the channel entry anymore, we can remove it safely. */ if (server->server_type == SILC_SERVER && !silc_server_channel_has_local(channel)) { - silc_idlist_del_channel(server->local_list, channel); + /* Notify about leaving client if this channel has global users. */ + if (notify && channel->global_users) + silc_server_send_notify_to_channel(server, NULL, channel, FALSE, + SILC_NOTIFY_TYPE_LEAVE, 1, + clidp->data, clidp->len); + + if (!silc_idlist_del_channel(server->local_list, channel)) + silc_idlist_del_channel(server->global_list, channel); silc_buffer_free(clidp); server->stat.my_channels--; return FALSE; @@ -2309,3 +2344,19 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server, return channel; } + +/* Heartbeat callback. This function is set as argument for the + silc_socket_set_heartbeat function. The library will call this function + at the set time interval. */ + +void silc_server_perform_heartbeat(SilcSocketConnection sock, + void *hb_context) +{ + SilcServerHBContext hb = (SilcServerHBContext)hb_context; + + SILC_LOG_DEBUG(("Sending heartbeat to %s (%s)", sock->hostname, + sock->ip)); + + /* Send the heartbeat */ + silc_server_send_heartbeat(hb->server, sock); +} diff --git a/apps/silcd/server.h b/apps/silcd/server.h index c6d27d75..3a041fc6 100644 --- a/apps/silcd/server.h +++ b/apps/silcd/server.h @@ -117,5 +117,7 @@ void silc_server_create_channel_key(SilcServer server, SilcChannelEntry silc_server_save_channel_key(SilcServer server, SilcBuffer key_payload, SilcChannelEntry channel); +void silc_server_perform_heartbeat(SilcSocketConnection sock, + void *hb_context); #endif diff --git a/apps/silcd/server_internal.h b/apps/silcd/server_internal.h index 63ca697a..34616279 100644 --- a/apps/silcd/server_internal.h +++ b/apps/silcd/server_internal.h @@ -144,6 +144,11 @@ struct SilcServerStruct { #endif }; +/* Server's heartbeat context */ +typedef struct { + SilcServer server; +} *SilcServerHBContext; + /* Macros */ /* Registers generic task for file descriptor for reading from network and diff --git a/apps/silcd/testi2.conf b/apps/silcd/testi2.conf index fe6247a1..35f54730 100644 --- a/apps/silcd/testi2.conf +++ b/apps/silcd/testi2.conf @@ -19,10 +19,10 @@ nobody:nobody Mun huone:Mun servo:Pekka Riikonen:priikone@poseidon.pspt.fi [ServerInfo] -lassi.kuo.fi.ssh.com:10.2.1.7:Kuopio, Finland:1334 +lassi.kuo.fi.ssh.com:212.146.8.245:Kuopio, Finland:1334 [ListenPort] -10.2.1.7:10.2.1.7:1334 +212.146.8.245:212.146.8.245:1334 [Logging] infologfile:silcd2.log:10000 @@ -43,10 +43,10 @@ errorlogfile:silcd2.log:10000 [AdminConnection] [ServerConnection] -10.2.1.7:passwd:priikone:1333:1:1 +212.146.8.245:passwd:priikone:1333:1:1 [RouterConnection] -10.2.1.7:passwd:priikone:1335:1:1:0 +212.146.8.245:passwd:priikone:1335:1:1:0 [DenyConnection] [RedirectClient] diff --git a/doc/draft-riikonen-silc-pp-01.nroff b/doc/draft-riikonen-silc-pp-01.nroff index f3860e95..110daec4 100644 --- a/doc/draft-riikonen-silc-pp-01.nroff +++ b/doc/draft-riikonen-silc-pp-01.nroff @@ -766,7 +766,15 @@ List of SILC Packet types are defined as follows. Payload of the packet: See section 2.3.27 Set Mode Payload - 32 - 199 + 32 SILC_PACKET_HEARTBEAT + + This packet is used by clients, servers and routers to keep the + connection alive. It is recommended that all servers implement + keepalive actions and perform it to both direction in a link. + This packet does not have a payload. + + + 33 - 199 Currently undefined commands. diff --git a/lib/silcclient/client.c b/lib/silcclient/client.c index 1cd84f2f..fd17296d 100644 --- a/lib/silcclient/client.c +++ b/lib/silcclient/client.c @@ -850,6 +850,13 @@ void silc_client_packet_parse_type(SilcClient client, break; } + case SILC_PACKET_HEARTBEAT: + /* + * Received heartbeat packet + */ + SILC_LOG_DEBUG(("Heartbeat packet")); + break; + default: SILC_LOG_DEBUG(("Incorrect packet type %d, packet dropped", type)); break; @@ -1394,6 +1401,20 @@ void silc_client_notify_by_server(SilcClient client, goto out; } + /* If nickname or username hasn't been resolved, do so */ + if (!client_entry->nickname || !client_entry->username) { + SilcPacketContext *p = silc_packet_context_dup(packet); + SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT); + silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, + SILC_IDLIST_IDENT, 1, + 3, idp->data, idp->len); + p->context = (void *)client; + p->sock = sock; + silc_client_command_pending(conn, SILC_COMMAND_WHOIS, SILC_IDLIST_IDENT, + silc_client_notify_by_server_pending, p); + goto out; + } + /* Get channel entry */ channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL); diff --git a/lib/silcclient/command_reply.c b/lib/silcclient/command_reply.c index 007e283e..48e1a705 100644 --- a/lib/silcclient/command_reply.c +++ b/lib/silcclient/command_reply.c @@ -709,21 +709,21 @@ SILC_CLIENT_CMD_REPLY_FUNC(ping) "Ping reply from %s: %d second%s", conn->ping[i].dest_name, diff, diff == 1 ? "" : "s"); - + conn->ping[i].start_time = 0; silc_free(conn->ping[i].dest_id); conn->ping[i].dest_id = NULL; silc_free(conn->ping[i].dest_name); conn->ping[i].dest_name = NULL; - - /* Notify application */ - COMMAND_REPLY((ARGS)); break; } } silc_free(id); + /* Notify application */ + COMMAND_REPLY((ARGS)); + /* Execute any pending command callbacks */ SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_PING); diff --git a/lib/silccore/silcpacket.c b/lib/silccore/silcpacket.c index 9c5ed475..5b9d33e3 100644 --- a/lib/silccore/silcpacket.c +++ b/lib/silccore/silcpacket.c @@ -106,11 +106,6 @@ void silc_packet_encrypt(SilcCipher cipher, SilcHmac hmac, { unsigned char mac[32]; - if (cipher) { - SILC_LOG_DEBUG(("Encrypting packet, cipher %s, len %d (%d)", - cipher->cipher->name, len, len - 2)); - } - /* Compute HMAC. This assumes that HMAC is created from the entire data area thus this uses the length found in buffer, not the length sent as argument. */ @@ -122,9 +117,12 @@ void silc_packet_encrypt(SilcCipher cipher, SilcHmac hmac, /* Encrypt the data area of the packet. 2 bytes of the packet are not encrypted. */ - if (cipher) + if (cipher) { + SILC_LOG_DEBUG(("Encrypting packet, cipher %s, len %d (%d)", + cipher->cipher->name, len, len - 2)); cipher->cipher->encrypt(cipher->context, buffer->data + 2, buffer->data + 2, len - 2, cipher->iv); + } /* Pull the HMAC into the visible data area in the buffer */ if (hmac) diff --git a/lib/silccore/silcpacket.h b/lib/silccore/silcpacket.h index bdd4e4d6..1a22736e 100644 --- a/lib/silccore/silcpacket.h +++ b/lib/silccore/silcpacket.h @@ -223,6 +223,7 @@ typedef void (*SilcPacketParserCallback)(SilcPacketParserContext #define SILC_PACKET_REKEY 29 #define SILC_PACKET_REKEY_DONE 30 #define SILC_PACKET_SET_MODE 31 /* Set mode */ +#define SILC_PACKET_HEARTBEAT 32 /* Heartbeat */ /* #define SILC_PACKET_MAX 255 */ /* Macros */ diff --git a/lib/silccore/silcsockconn.c b/lib/silccore/silcsockconn.c index 3262cfc4..4f5e60d2 100644 --- a/lib/silccore/silcsockconn.c +++ b/lib/silccore/silcsockconn.c @@ -17,20 +17,7 @@ GNU General Public License for more details. */ -/* - * $Id$ - * $Log$ - * Revision 1.3 2001/02/11 14:09:34 priikone - * Code auditing weekend results and fixes committing. - * - * Revision 1.2 2000/07/05 06:06:35 priikone - * Global cosmetic change. - * - * Revision 1.1.1.1 2000/06/27 11:36:55 priikone - * Imported from internal CVS/Added Log headers. - * - * - */ +/* $Id$ */ #include "silcincludes.h" @@ -61,6 +48,55 @@ void silc_socket_free(SilcSocketConnection sock) if (sock) { silc_buffer_free(sock->inbuf); silc_buffer_free(sock->outbuf); + if (sock->hb) { + silc_task_unregister(sock->hb->timeout_queue, sock->hb->hb_task); + silc_free(sock->hb->hb_context); + silc_free(sock->hb); + } + + memset(sock, 'F', sizeof(*sock)); silc_free(sock); } } + +/* Internal timeout callback to perform heartbeat */ + +SILC_TASK_CALLBACK(silc_socket_heartbeat) +{ + SilcSocketConnectionHB hb = (SilcSocketConnectionHB)context; + + if (!hb->heartbeat) + return; + + if (hb->hb_callback) + hb->hb_callback(hb->sock, hb->hb_context); + + hb->hb_task = silc_task_register(hb->timeout_queue, hb->sock->sock, + silc_socket_heartbeat, + context, hb->heartbeat, 0, + SILC_TASK_TIMEOUT, + SILC_TASK_PRI_LOW); +} + +/* Sets the heartbeat timeout and prepares the socket for performing + heartbeat in `heartbeat' intervals (seconds). */ + +void silc_socket_set_heartbeat(SilcSocketConnection sock, + unsigned long heartbeat, + void *hb_context, + SilcSocketConnectionHBCb hb_callback, + void *timeout_queue) +{ + SilcSocketConnectionHB hb = silc_calloc(1, sizeof(*hb)); + + hb->heartbeat = heartbeat; + hb->hb_context = hb_context; + hb->hb_callback = hb_callback; + hb->timeout_queue = timeout_queue; + hb->sock = sock; + hb->hb_task = silc_task_register(timeout_queue, sock->sock, + silc_socket_heartbeat, + (void *)hb, heartbeat, 0, + SILC_TASK_TIMEOUT, + SILC_TASK_PRI_LOW); +} diff --git a/lib/silccore/silcsockconn.h b/lib/silccore/silcsockconn.h index 291a5a46..9b190d1b 100644 --- a/lib/silccore/silcsockconn.h +++ b/lib/silccore/silcsockconn.h @@ -21,6 +21,10 @@ #ifndef SILCSOCKCONN_H #define SILCSOCKCONN_H +/* Forward declarations */ +typedef struct SilcSocketConnectionStruct *SilcSocketConnection; +typedef struct SilcSocketConnectionHB *SilcSocketConnectionHB; + /* Socket types. These identifies the socket connection. */ typedef enum { SILC_SOCKET_TYPE_UNKNOWN = 0, @@ -36,6 +40,12 @@ typedef enum { #define SILC_SF_DISCONNECTING 3 #define SILC_SF_DISCONNECTED 4 +/* Heartbeat callback function. This is the function in the application + that this library will call when it is time to send the keepalive + packet SILC_PACKET_HEARTBEAT. */ +typedef void (*SilcSocketConnectionHBCb)(SilcSocketConnection sock, + void *context); + /* SILC Socket Connection object. @@ -91,8 +101,12 @@ typedef enum { inbuf buffer and outgoing data after encryption is put to the outbuf buffer. + SilcSocketConnectionHB hb + + The heartbeat context. If NULL, heartbeat is not performed. + */ -typedef struct { +struct SilcSocketConnectionStruct { int sock; SilcSocketType type; void *user_data; @@ -105,9 +119,19 @@ typedef struct { SilcBuffer inbuf; SilcBuffer outbuf; -} SilcSocketConnectionObject; -typedef SilcSocketConnectionObject *SilcSocketConnection; + SilcSocketConnectionHB hb; +}; + +/* Heartbeat context */ +struct SilcSocketConnectionHB { + unsigned long heartbeat; + SilcSocketConnectionHBCb hb_callback; + void *hb_context; + void *timeout_queue; + SilcTask hb_task; + SilcSocketConnection sock; +}; /* Macros */ @@ -136,5 +160,10 @@ typedef SilcSocketConnectionObject *SilcSocketConnection; void silc_socket_alloc(int sock, SilcSocketType type, void *user_data, SilcSocketConnection *new_socket); void silc_socket_free(SilcSocketConnection sock); +void silc_socket_set_heartbeat(SilcSocketConnection sock, + unsigned long heartbeat, + void *hb_context, + SilcSocketConnectionHBCb hb_callback, + void *timeout_queue); #endif diff --git a/lib/silcutil/silctask.c b/lib/silcutil/silctask.c index 9aec2507..152487e4 100644 --- a/lib/silcutil/silctask.c +++ b/lib/silcutil/silctask.c @@ -151,6 +151,57 @@ SilcTask silc_task_add(SilcTaskQueue queue, SilcTask new, return new; } +#if 0 +void dump_tasks(SilcTaskQueue queue) +{ + SilcTask first, prev; + + if (!queue->task) + return; + + first = queue->task; + + fprintf(stderr, "\nqueue->task:\t%p\t%d\n", queue->task, + queue->task->timeout.tv_sec); + + prev = first->prev; + while (1) { + if (first == prev) + break; + + fprintf(stderr, "task :\t%p\t%d\n", prev, prev->timeout.tv_sec); + + prev = prev->prev; + } + fprintf(stderr, "\n"); +} +#endif + +/* Return the timeout task with smallest timeout. */ + +static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first) +{ + SilcTask prev, task; + + prev = first->prev; + + if (first == prev) + return first; + + task = first; + while (1) { + if (first == prev) + break; + + if (silc_task_timeout_compare(&prev->timeout, &task->timeout)) + task = prev; + + prev = prev->prev; + } + + return task; +} + /* Adds a timeout task into the task queue. This function is used by silc_task_register function. Returns a pointer to the registered task. Timeout tasks are sorted by their timeout value in ascending @@ -316,6 +367,10 @@ SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask new, return NULL; } +#if 0 + dump_tasks(queue); +#endif + return new; } @@ -443,12 +498,17 @@ int silc_task_remove(SilcTaskQueue queue, SilcTask task) if (prev == old && next == old) queue->task = NULL; if (queue->task == old) - queue->task = next; + queue->task = silc_task_get_first(queue, next); + /*queue->task = next;*/ + +#if 0 + dump_tasks(queue); +#endif silc_free(old); return TRUE; } - old = old->next; + old = old->prev; if (old == first) return FALSE; diff --git a/lib/silcutil/silctask.h b/lib/silcutil/silctask.h index e07e6e94..cb353e4f 100644 --- a/lib/silcutil/silctask.h +++ b/lib/silcutil/silctask.h @@ -332,7 +332,6 @@ typedef SilcTaskQueueObject *SilcTaskQueue; #define SILC_TASK_CALLBACK(func) \ static void func(void *qptr, int type, void *context, int fd) - /* Prototypes */ void silc_task_queue_alloc(SilcTaskQueue *new, int valid); void silc_task_queue_free(SilcTaskQueue old);