updates.
authorPekka Riikonen <priikone@silcnet.org>
Fri, 16 Feb 2001 00:33:23 +0000 (00:33 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Fri, 16 Feb 2001 00:33:23 +0000 (00:33 +0000)
22 files changed:
CHANGES
TODO
apps/silc/client_ops.c
apps/silc/local_command.c
apps/silcd/command.c
apps/silcd/idlist.c
apps/silcd/idlist.h
apps/silcd/packet_send.c
apps/silcd/packet_send.h
apps/silcd/server.c
apps/silcd/server.h
apps/silcd/server_internal.h
apps/silcd/testi2.conf
doc/draft-riikonen-silc-pp-01.nroff
lib/silcclient/client.c
lib/silcclient/command_reply.c
lib/silccore/silcpacket.c
lib/silccore/silcpacket.h
lib/silccore/silcsockconn.c
lib/silccore/silcsockconn.h
lib/silcutil/silctask.c
lib/silcutil/silctask.h

diff --git a/CHANGES b/CHANGES
index b9c7fdddbed1951075da91424c55ebd451381e71..e50f864b919e8653f790bf58bd211c3c525c9f71 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,70 @@
+Thu Feb 15 20:07:37 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * 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 <priikone@poseidon.pspt.fi>
 
        * Added new function silc_server_remove_clients_by_server to
diff --git a/TODO b/TODO
index e60dfdc2f2ee69bf7a929c9760d6ae7a11d4e539..e8bbcf8cade0b6dbf36050b18369d8591275ca3f 100644 (file)
--- 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.
index ae20153b4756212d52f089c16a18f88f784f1d4f..4097e9f8454ef75604547821dd0afcbe62f90903 100644 (file)
@@ -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);
index 56e36da4a9f6bdc2071b1071c9d4184cd3579ab1..7a9316a5d4468d399f8375f06e0afc434b949d4b 100644 (file)
@@ -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);
index ce9e60435fee009a72f8d2144005c16a0472081d..6f34122867a862bef6bb1a4e820339003e6dd4c4 100644 (file)
@@ -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 {
 
   }
index c1354143fb3c67c69a20c7ec7525fc711b7cce84..1d6b9540a320563df5f4162e84703773618df480 100644 (file)
@@ -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
index 6b865eb957a0dfd9d41711020683c836beab9c6a..cbdf64f8dd45d0229b09ca9dcf6e1422ffb27b09 100644 (file)
@@ -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);
index 2ecca8b5971b92d03235f8b35b87504208c03770..83fef72fad60cc225d3ad6be3e78b8d7e2b1e1c6 100644 (file)
@@ -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);
+}
index 17b4a64daae751f0b81684a9a87cc60d7815d938..cfd7937842f568b23d4c9bca8c7d278f4e522c75 100644 (file)
@@ -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
index 6fd86d7d6b27f499c7437ff8bb209cf1e5c26667..6a6733c6a39c8b19e2cab17b0c1bfbadfd94b666 100644 (file)
@@ -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);
+}
index c6d27d75b59cacded60525c1e484f514e9b45272..3a041fc63712446b7fefef3ef5bc7cb23d70b380 100644 (file)
@@ -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
index 63ca697a9e479d4b51237ab37261258b3cda6d9f..346162798eda045b8fb42bc5dfe5a4363234eccc 100644 (file)
@@ -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
index fe6247a1d0a8b9ff75a693d83810e3924412a3a0..35f54730a3ecf478eca431e08f08df91aa359faf 100644 (file)
@@ -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]
index f3860e95170b1c4ba5ee62ab6f53a823227f76af..110daec46c99e9e2e1dc932d60bde7a4c7aed475 100644 (file)
@@ -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.
 
index 1cd84f2f6b9d34cdce3173a63226e475a107a46f..fd17296d46772b06367d5b1ad416904939e35c3f 100644 (file)
@@ -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);
index 007e283eab36234b0dc63cd92dd8bff2c4858074..48e1a70525601f693d390ec3a752295a7162f704 100644 (file)
@@ -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);
 
index 9c5ed4759534ca76f5923128a826b81e34beab8c..5b9d33e30f3d3de88fc78cac4d09f9641ee3b39b 100644 (file)
@@ -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)
index bdd4e4d676b3e15e69bbb927a6ca66d966017297..1a22736e95e9ca9a31741b728fcfc50f5ae155f5 100644 (file)
@@ -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 */
index 3262cfc4153f63b81b6d4ddc049a3e6f8b3693a1..4f5e60d23bc6fa83e8781617fb658d460f5f846c 100644 (file)
   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);
+}
index 291a5a46e798546d964f12d42421446cc8bb98e5..9b190d1bb157cdf20bf4a4e6711546ebd647c19b 100644 (file)
 #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
index 9aec2507e5ab1ce9f6158434ed378a4538481dc9..152487e48e6bf82533e060b63db6671f16fb63a6 100644 (file)
@@ -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;
index e07e6e9439166abe2e49c8ae5c5ef643dc53f052..cb353e4f385ef8430efc23752f3a9b1ef2327df6 100644 (file)
@@ -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);