Merged from silc_1_0_branch.
[silc.git] / apps / silcd / packet_send.c
index 0860b68e1ea6c357acca50d1f72e7eac19cf2742..8e3ab0f248742ce3a308a3074059467eb3aec13b 100644 (file)
@@ -1,16 +1,15 @@
 /*
 
-  packet_send.c
+  packet_send.c 
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  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
@@ -39,19 +38,27 @@ int silc_server_packet_send_real(SilcServer server,
   if (SILC_IS_DISCONNECTING(sock))
     return -1;
 
-  /* If rekey protocol is active we must assure that all packets are
-     sent through packet queue. */
-  if (SILC_SERVER_IS_REKEY(sock))
-    force_send = FALSE;
-
-  /* If outbound data is already pending do not force send */
-  if (SILC_IS_OUTBUF_PENDING(sock))
-    force_send = FALSE;
-
   /* Send the packet */
-  ret = silc_packet_send(sock, force_send);
-  if (ret != -2)
+  ret = silc_packet_send(sock, FALSE);
+  if (ret != -2) {
+    if (ret == -1) {
+      SILC_LOG_ERROR(("Error sending packet to connection "
+                     "%s:%d [%s]", sock->hostname, sock->port,
+                     (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                      sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                      sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                      "Router")));
+
+      SILC_SET_DISCONNECTING(sock);
+      if (sock->user_data)
+       silc_server_free_sock_user_data(server, sock, NULL);
+      silc_server_close_connection(server, sock);
+      return ret;
+    }
+
+    server->stat.packets_sent++;
     return ret;
+  }
 
   /* Mark that there is some outgoing data available for this connection. 
      This call sets the connection both for input and output (the input
@@ -78,23 +85,31 @@ void silc_server_packet_send(SilcServer server,
                             SilcPacketType type, 
                             SilcPacketFlags flags,
                             unsigned char *data, 
-                            uint32 data_len,
+                            SilcUInt32 data_len,
                             bool force_send)
 {
   void *dst_id = NULL;
   SilcIdType dst_id_type = SILC_ID_NONE;
-  SilcIDListData idata = (SilcIDListData)sock->user_data;
+  SilcIDListData idata;
 
   if (!sock)
     return;
 
+  idata = (SilcIDListData)sock->user_data;
+
   /* If disconnecting, ignore the data */
   if (SILC_IS_DISCONNECTING(sock))
     return;
 
-  /* If entry is disabled do not sent anything. */
-  if (idata && idata->status & SILC_IDLIST_STATUS_DISABLED)
+  /* If entry is disabled do not sent anything.  Allow hearbeat and
+     rekeys, though */
+  if ((idata && idata->status & SILC_IDLIST_STATUS_DISABLED &&
+       type != SILC_PACKET_HEARTBEAT && type != SILC_PACKET_REKEY && 
+       type != SILC_PACKET_REKEY_DONE) ||
+      sock->user_data == server->id_entry) {
+    SILC_LOG_DEBUG(("Connection is disabled"));
     return;
+  }
 
   /* Get data used in the packet sending, keys and stuff */
   switch(sock->type) {
@@ -133,27 +148,34 @@ void silc_server_packet_send_dest(SilcServer server,
                                  void *dst_id,
                                  SilcIdType dst_id_type,
                                  unsigned char *data, 
-                                 uint32 data_len,
+                                 SilcUInt32 data_len,
                                  bool force_send)
 {
   SilcPacketContext packetdata;
-  SilcIDListData idata = (SilcIDListData)sock->user_data;
+  const SilcBufferStruct packet;
+  SilcIDListData idata;
   SilcCipher cipher = NULL;
   SilcHmac hmac = NULL;
-  uint32 sequence = 0;
+  SilcUInt32 sequence = 0;
   unsigned char *dst_id_data = NULL;
-  uint32 dst_id_len = 0;
+  SilcUInt32 dst_id_len = 0;
   int block_len = 0;
 
   /* If disconnecting, ignore the data */
-  if (SILC_IS_DISCONNECTING(sock))
+  if (!sock || SILC_IS_DISCONNECTING(sock))
     return;
 
+  idata = (SilcIDListData)sock->user_data;
+
   /* If entry is disabled do not sent anything. */
-  if (idata && idata->status & SILC_IDLIST_STATUS_DISABLED)
+  if ((idata && idata->status & SILC_IDLIST_STATUS_DISABLED) ||
+      sock->user_data == server->id_entry) {
+    SILC_LOG_DEBUG(("Connection is disabled"));
     return;
+  }
 
-  SILC_LOG_DEBUG(("Sending packet, type %d", type));
+  SILC_LOG_DEBUG(("Sending %s packet (forced=%s)",
+                 silc_get_packet_name(type), force_send ? "yes" : "no"));
 
   if (dst_id) {
     dst_id_data = silc_id_id2str(dst_id, dst_id_type);
@@ -164,55 +186,54 @@ void silc_server_packet_send_dest(SilcServer server,
     cipher = idata->send_key;
     hmac = idata->hmac_send;
     sequence = idata->psn_send++;
-    block_len = silc_cipher_get_block_len(cipher);
+    if (cipher)
+      block_len = silc_cipher_get_block_len(cipher);
+
+    /* Check for mandatory rekey */
+    if (sequence == SILC_SERVER_REKEY_THRESHOLD)
+      silc_schedule_task_add(server->schedule, sock->sock,
+                            silc_server_rekey_callback, sock, 0, 1,
+                            SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
   }
 
   /* Set the packet context pointers */
   packetdata.type = type;
   packetdata.flags = flags;
-  packetdata.src_id = silc_id_id2str(server->id, server->id_type);
-  packetdata.src_id_len = silc_id_get_len(server->id, server->id_type);
-  packetdata.src_id_type = server->id_type;
+  packetdata.src_id = silc_id_id2str(server->id, SILC_ID_SERVER);
+  packetdata.src_id_len = silc_id_get_len(server->id, SILC_ID_SERVER);
+  packetdata.src_id_type = SILC_ID_SERVER;
   packetdata.dst_id = dst_id_data;
   packetdata.dst_id_len = dst_id_len;
   packetdata.dst_id_type = dst_id_type;
+  data_len = SILC_PACKET_DATALEN(data_len, (SILC_PACKET_HEADER_LEN +
+                                           packetdata.src_id_len + 
+                                           packetdata.dst_id_len));
   packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
     packetdata.src_id_len + dst_id_len;
-  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen, block_len);
-
-  /* Prepare outgoing data buffer for packet sending */
-  silc_packet_send_prepare(sock, 
-                          SILC_PACKET_HEADER_LEN +
-                          packetdata.src_id_len + 
-                          packetdata.dst_id_len,
-                          packetdata.padlen,
-                          data_len);
-
-  SILC_LOG_DEBUG(("Putting data to outgoing buffer, len %d", data_len));
-
-  packetdata.buffer = sock->outbuf;
-
-  /* Put the data to the buffer */
-  if (data && data_len)
-    silc_buffer_put(sock->outbuf, data, data_len);
+  if (type == SILC_PACKET_CONNECTION_AUTH)
+    SILC_PACKET_PADLEN_MAX(packetdata.truelen, block_len, packetdata.padlen);
+  else
+    SILC_PACKET_PADLEN(packetdata.truelen, block_len, packetdata.padlen);
 
   /* Create the outgoing packet */
-  silc_packet_assemble(&packetdata, cipher);
+  if (!silc_packet_assemble(&packetdata, NULL, cipher, hmac, sock,
+                            data, data_len, (const SilcBuffer)&packet)) {
+    SILC_LOG_ERROR(("Cannot assemble packet"));
+    goto out;
+  }
 
   /* Encrypt the packet */
-  silc_packet_encrypt(cipher, hmac, sequence, sock->outbuf, sock->outbuf->len);
+  silc_packet_encrypt(cipher, hmac, sequence, (SilcBuffer)&packet, packet.len);
 
-  SILC_LOG_HEXDUMP(("Outgoing packet (%d), len %d", sequence, 
-                   sock->outbuf->len),
-                  sock->outbuf->data, sock->outbuf->len);
+  SILC_LOG_HEXDUMP(("Outgoing packet (%d), len %d", sequence, packet.len),
+                  packet.data, packet.len);
 
   /* Now actually send the packet */
   silc_server_packet_send_real(server, sock, force_send);
 
-  if (packetdata.src_id)
-    silc_free(packetdata.src_id);
-  if (packetdata.dst_id)
-    silc_free(packetdata.dst_id);
+ out:
+  silc_free(packetdata.src_id);
+  silc_free(packetdata.dst_id);
 }
 
 /* Assembles a new packet to be sent out to network. This doesn't actually
@@ -232,25 +253,49 @@ void silc_server_packet_send_srcdest(SilcServer server,
                                     void *dst_id,
                                     SilcIdType dst_id_type,
                                     unsigned char *data, 
-                                    uint32 data_len,
+                                    SilcUInt32 data_len,
                                     bool force_send)
 {
   SilcPacketContext packetdata;
+  const SilcBufferStruct packet;
   SilcIDListData idata;
   SilcCipher cipher = NULL;
   SilcHmac hmac = NULL;
-  uint32 sequence = 0;
+  SilcUInt32 sequence = 0;
   unsigned char *dst_id_data = NULL;
-  uint32 dst_id_len = 0;
+  SilcUInt32 dst_id_len = 0;
   unsigned char *src_id_data = NULL;
-  uint32 src_id_len = 0;
+  SilcUInt32 src_id_len = 0;
   int block_len = 0;
 
-  SILC_LOG_DEBUG(("Sending packet, type %d", type));
+  SILC_LOG_DEBUG(("Sending %s packet", silc_get_packet_name(type)));
+
+  if (!sock)
+    return;
 
   /* Get data used in the packet sending, keys and stuff */
   idata = (SilcIDListData)sock->user_data;
 
+  /* If entry is disabled do not sent anything. */
+  if ((idata && idata->status & SILC_IDLIST_STATUS_DISABLED) ||
+      sock->user_data == server->id_entry) {
+    SILC_LOG_DEBUG(("Connection is disabled"));
+    return;
+  }
+
+  if (idata) {
+    cipher = idata->send_key;
+    hmac = idata->hmac_send;
+    sequence = idata->psn_send++;
+    block_len = silc_cipher_get_block_len(cipher);
+
+    /* Check for mandatory rekey */
+    if (sequence == SILC_SERVER_REKEY_THRESHOLD)
+      silc_schedule_task_add(server->schedule, sock->sock,
+                            silc_server_rekey_callback, sock, 0, 1,
+                            SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+  }
+
   if (dst_id) {
     dst_id_data = silc_id_id2str(dst_id, dst_id_type);
     dst_id_len = silc_id_get_len(dst_id, dst_id_type);
@@ -261,13 +306,6 @@ void silc_server_packet_send_srcdest(SilcServer server,
     src_id_len = silc_id_get_len(src_id, src_id_type);
   }
 
-  if (idata) {
-    cipher = idata->send_key;
-    hmac = idata->hmac_send;
-    sequence = idata->psn_send++;
-    block_len = silc_cipher_get_block_len(cipher);
-  }
-
   /* Set the packet context pointers */
   packetdata.type = type;
   packetdata.flags = flags;
@@ -277,43 +315,32 @@ void silc_server_packet_send_srcdest(SilcServer server,
   packetdata.dst_id = dst_id_data;
   packetdata.dst_id_len = dst_id_len;
   packetdata.dst_id_type = dst_id_type;
+  data_len = SILC_PACKET_DATALEN(data_len, (SILC_PACKET_HEADER_LEN +
+                                           packetdata.src_id_len + 
+                                           dst_id_len));
   packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
     packetdata.src_id_len + dst_id_len;
-  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen, block_len);
-
-  /* Prepare outgoing data buffer for packet sending */
-  silc_packet_send_prepare(sock, 
-                          SILC_PACKET_HEADER_LEN +
-                          packetdata.src_id_len + 
-                          packetdata.dst_id_len,
-                          packetdata.padlen,
-                          data_len);
-
-  SILC_LOG_DEBUG(("Putting data to outgoing buffer, len %d", data_len));
-
-  packetdata.buffer = sock->outbuf;
-
-  /* Put the data to the buffer */
-  if (data && data_len)
-    silc_buffer_put(sock->outbuf, data, data_len);
+  SILC_PACKET_PADLEN(packetdata.truelen, block_len, packetdata.padlen);
 
   /* Create the outgoing packet */
-  silc_packet_assemble(&packetdata, cipher);
+  if (!silc_packet_assemble(&packetdata, NULL, cipher, hmac, sock, data,
+                            data_len, (const SilcBuffer)&packet)) {
+    SILC_LOG_ERROR(("Cannot assemble packe"));
+    goto out;
+  }
 
   /* Encrypt the packet */
-  silc_packet_encrypt(cipher, hmac, sequence, sock->outbuf, sock->outbuf->len);
+  silc_packet_encrypt(cipher, hmac, sequence, (SilcBuffer)&packet, packet.len);
 
-  SILC_LOG_HEXDUMP(("Outgoing packet (%d), len %d", sequence, 
-                   sock->outbuf->len),
-                  sock->outbuf->data, sock->outbuf->len);
+  SILC_LOG_HEXDUMP(("Outgoing packet (%d), len %d", sequence, packet.len),
+                   packet.data, packet.len);
 
   /* Now actually send the packet */
   silc_server_packet_send_real(server, sock, force_send);
 
-  if (packetdata.src_id)
-    silc_free(packetdata.src_id);
-  if (packetdata.dst_id)
-    silc_free(packetdata.dst_id);
+ out:
+  silc_free(packetdata.src_id);
+  silc_free(packetdata.dst_id);
 }
 
 /* Broadcast received packet to our primary route. This function is used
@@ -329,27 +356,42 @@ void silc_server_packet_broadcast(SilcServer server,
   SilcIDListData idata;
   void *id;
 
+  if (!sock)
+    return;
+
   SILC_LOG_DEBUG(("Broadcasting received broadcast packet"));
 
   /* If the packet is originated from our primary route we are
      not allowed to send the packet. */
   id = silc_id_str2id(packet->src_id, packet->src_id_len, packet->src_id_type);
   if (id && !SILC_ID_SERVER_COMPARE(id, server->router->id)) {
+    const SilcBufferStruct p;
+
     idata = (SilcIDListData)sock->user_data;
 
     silc_buffer_push(buffer, buffer->data - buffer->head);
-    silc_packet_send_prepare(sock, 0, 0, buffer->len); 
-    silc_buffer_put(sock->outbuf, buffer->data, buffer->len);
+    if (!silc_packet_send_prepare(sock, 0, 0, buffer->len, idata->hmac_send,
+                                  (const SilcBuffer)&p)) {
+      SILC_LOG_ERROR(("Cannot send packet"));
+      silc_free(id);
+      return;
+    }
+    silc_buffer_put((SilcBuffer)&p, buffer->data, buffer->len);
     silc_packet_encrypt(idata->send_key, idata->hmac_send, idata->psn_send++,
-                       sock->outbuf, sock->outbuf->len);
+                       (SilcBuffer)&p, p.len);
 
     SILC_LOG_HEXDUMP(("Broadcasted packet (%d), len %d", idata->psn_send - 1,
-                     sock->outbuf->len),
-                    sock->outbuf->data, sock->outbuf->len);
+                     p.len), p.data, p.len);
 
     /* Now actually send the packet */
     silc_server_packet_send_real(server, sock, TRUE);
     silc_free(id);
+
+    /* Check for mandatory rekey */
+    if (idata->psn_send == SILC_SERVER_REKEY_THRESHOLD)
+      silc_schedule_task_add(server->schedule, sock->sock,
+                            silc_server_rekey_callback, sock, 0, 1,
+                            SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
     return;
   }
 
@@ -366,6 +408,7 @@ void silc_server_packet_route(SilcServer server,
                              SilcPacketContext *packet)
 {
   SilcBuffer buffer = packet->buffer;
+  const SilcBufferStruct p;
   SilcIDListData idata;
 
   SILC_LOG_DEBUG(("Routing received packet"));
@@ -373,17 +416,26 @@ void silc_server_packet_route(SilcServer server,
   idata = (SilcIDListData)sock->user_data;
 
   silc_buffer_push(buffer, buffer->data - buffer->head);
-  silc_packet_send_prepare(sock, 0, 0, buffer->len); 
-  silc_buffer_put(sock->outbuf, buffer->data, buffer->len);
+  if (!silc_packet_send_prepare(sock, 0, 0, buffer->len, idata->hmac_send,
+                                (const SilcBuffer)&p)) {
+    SILC_LOG_ERROR(("Cannot send packet"));
+    return;
+  }
+  silc_buffer_put((SilcBuffer)&p, buffer->data, buffer->len);
   silc_packet_encrypt(idata->send_key, idata->hmac_send, idata->psn_send++,
-                     sock->outbuf, sock->outbuf->len);
+                     (SilcBuffer)&p, p.len);
 
   SILC_LOG_HEXDUMP(("Routed packet (%d), len %d", idata->psn_send - 1, 
-                   sock->outbuf->len),
-                  sock->outbuf->data, sock->outbuf->len);
+                  p.len), p.data, p.len);
 
   /* Now actually send the packet */
   silc_server_packet_send_real(server, sock, TRUE);
+
+  /* Check for mandatory rekey */
+  if (idata->psn_send == SILC_SERVER_REKEY_THRESHOLD)
+    silc_schedule_task_add(server->schedule, sock->sock,
+                          silc_server_rekey_callback, sock, 0, 1,
+                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 }
 
 /* This routine can be used to send a packet to table of clients provided
@@ -391,28 +443,31 @@ void silc_server_packet_route(SilcServer server,
    clients (for server locally connected, and for router local cell). */
 
 void silc_server_packet_send_clients(SilcServer server,
-                                    SilcClientEntry *clients,
-                                    uint32 clients_count,
+                                    SilcHashTable clients,
                                     SilcPacketType type, 
                                     SilcPacketFlags flags,
                                     bool route,
                                     unsigned char *data, 
-                                    uint32 data_len,
+                                    SilcUInt32 data_len,
                                     bool force_send)
 {
   SilcSocketConnection sock = NULL;
+  SilcHashTableList htl;
   SilcClientEntry client = NULL;
   SilcServerEntry *routed = NULL;
-  uint32 routed_count = 0;
+  SilcUInt32 routed_count = 0;
   bool gone = FALSE;
-  int i, k;
+  int k;
 
-  SILC_LOG_DEBUG(("Sending packet to list of clients"));
+  if (!silc_hash_table_count(clients))
+    return;
 
-  /* Send to all clients in table */
-  for (i = 0; i < clients_count; i++) {
-    client = clients[i];
+  SILC_LOG_DEBUG(("Sending packet to %d clients",
+                 silc_hash_table_count(clients)));
 
+  /* Send to all clients in table */
+  silc_hash_table_list(clients, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void **)&client)) {
     /* If client has router set it is not locally connected client and
        we will route the message to the router set in the client. Though,
        send locally connected server in all cases. */
@@ -450,11 +505,14 @@ void silc_server_packet_send_clients(SilcServer server,
 
     /* Send to locally connected client */
     sock = (SilcSocketConnection)client->connection;
+    if (!sock)
+      continue;
+
     silc_server_packet_send_dest(server, sock, type, flags,
                                 client->id, SILC_ID_CLIENT,
                                 data, data_len, force_send);
   }
-
+  silc_hash_table_list_reset(&htl);
   silc_free(routed);
 }
 
@@ -469,54 +527,50 @@ silc_server_packet_send_to_channel_real(SilcServer server,
                                        SilcPacketContext *packet,
                                        SilcCipher cipher,
                                        SilcHmac hmac,
-                                       uint32 sequence,
+                                       SilcUInt32 sequence,
                                        unsigned char *data,
-                                       uint32 data_len,
+                                       SilcUInt32 data_len,
                                        bool channel_message,
                                        bool force_send)
 {
   int block_len;
+  const SilcBufferStruct p;
 
   if (!sock)
     return;
 
+  data_len = SILC_PACKET_DATALEN(data_len, (SILC_PACKET_HEADER_LEN +
+                                           packet->src_id_len + 
+                                           packet->dst_id_len));
   packet->truelen = data_len + SILC_PACKET_HEADER_LEN + 
     packet->src_id_len + packet->dst_id_len;
 
   block_len = cipher ? silc_cipher_get_block_len(cipher) : 0;
   if (channel_message)
-    packet->padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
-                                        packet->src_id_len +
-                                        packet->dst_id_len), block_len);
+    SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
+                       packet->src_id_len +
+                       packet->dst_id_len), block_len, packet->padlen);
   else
-    packet->padlen = SILC_PACKET_PADLEN(packet->truelen, block_len);
-
-  /* Prepare outgoing data buffer for packet sending */
-  silc_packet_send_prepare(sock, 
-                          SILC_PACKET_HEADER_LEN +
-                          packet->src_id_len + 
-                          packet->dst_id_len,
-                          packet->padlen,
-                          data_len);
-
-  packet->buffer = sock->outbuf;
+    SILC_PACKET_PADLEN(packet->truelen, block_len, packet->padlen);
 
   /* Put the data to buffer, assemble and encrypt the packet. The packet
      is encrypted with normal session key shared with the client, unless
      the `channel_message' is TRUE. */
-  silc_buffer_put(sock->outbuf, data, data_len);
-  silc_packet_assemble(packet, cipher);
+  if (!silc_packet_assemble(packet, NULL, cipher, hmac, sock, data,
+                            data_len, (const SilcBuffer)&p)) {
+    SILC_LOG_ERROR(("Cannot assemble packet"));
+    return;
+  }
+
   if (channel_message)
-    silc_packet_encrypt(cipher, hmac, sequence, sock->outbuf
+    silc_packet_encrypt(cipher, hmac, sequence, (SilcBuffer)&p
                        SILC_PACKET_HEADER_LEN + packet->src_id_len + 
                        packet->dst_id_len + packet->padlen);
   else
-    silc_packet_encrypt(cipher, hmac, sequence, sock->outbuf, 
-                       sock->outbuf->len);
+    silc_packet_encrypt(cipher, hmac, sequence, (SilcBuffer)&p, p.len);
     
-  SILC_LOG_HEXDUMP(("Channel packet (%d), len %d", sequence, 
-                   sock->outbuf->len),
-                  sock->outbuf->data, sock->outbuf->len);
+  SILC_LOG_HEXDUMP(("Channel packet (%d), len %d", sequence, p.len),
+                  p.data, p.len);
 
   /* Now actually send the packet */
   silc_server_packet_send_real(server, sock, force_send);
@@ -538,7 +592,7 @@ void silc_server_packet_send_to_channel(SilcServer server,
                                        SilcPacketType type,
                                        bool route,
                                        unsigned char *data,
-                                       uint32 data_len,
+                                       SilcUInt32 data_len,
                                        bool force_send)
 {
   SilcSocketConnection sock = NULL;
@@ -548,15 +602,13 @@ void silc_server_packet_send_to_channel(SilcServer server,
   SilcChannelClientEntry chl;
   SilcHashTableList htl;
   SilcIDListData idata;
-  uint32 routed_count = 0;
+  SilcUInt32 routed_count = 0;
   bool gone = FALSE;
   int k;
 
   /* This doesn't send channel message packets */
   assert(type != SILC_PACKET_CHANNEL_MESSAGE);
   
-  SILC_LOG_DEBUG(("Sending packet to channel"));
-
   /* Set the packet context pointers. */
   packetdata.flags = 0;
   packetdata.type = type;
@@ -580,7 +632,6 @@ void silc_server_packet_send_to_channel(SilcServer server,
     
     if (sock != sender) {
       SILC_LOG_DEBUG(("Sending packet to router for routing"));
-      
       silc_server_packet_send_to_channel_real(server, sock, &packetdata,
                                              idata->send_key, 
                                              idata->hmac_send, 
@@ -590,12 +641,20 @@ void silc_server_packet_send_to_channel(SilcServer server,
     }
   }
 
+  if (!silc_hash_table_count(channel->user_list)) {
+    SILC_LOG_DEBUG(("Channel %s is empty", channel->channel_name));
+    goto out;
+  }
+
+  SILC_LOG_DEBUG(("Sending %s to channel %s",
+                 silc_get_packet_name(type), channel->channel_name));
+
   routed = silc_calloc(silc_hash_table_count(channel->user_list), 
                       sizeof(*routed));
 
   /* Send the message to clients on the channel's client list. */
   silc_hash_table_list(channel->user_list, &htl);
-  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+  while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
     client = chl->client;
     if (!client)
       continue;
@@ -620,13 +679,18 @@ void silc_server_packet_send_to_channel(SilcServer server,
       if (sender && sock == sender)
        continue;
 
-      /* Route only once to router */
+      /* Route only once to router. Protocol prohibits sending channel
+        messages to more than one router. */
       if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
        if (gone)
          continue;
        gone = TRUE;
       }
 
+      SILC_LOG_DEBUG(("Sending packet to client %s",
+                     client->nickname ? client->nickname :
+                     (unsigned char *)""));
+
       /* Send the packet */
       silc_server_packet_send_to_channel_real(server, sock, &packetdata,
                                              idata->send_key, 
@@ -649,9 +713,13 @@ void silc_server_packet_send_to_channel(SilcServer server,
     sock = (SilcSocketConnection)client->connection;
     idata = (SilcIDListData)client;
     
-    if (sender && sock == sender)
+    if (!sock || (sender && sock == sender))
       continue;
 
+    SILC_LOG_DEBUG(("Sending packet to client %s",
+                   client->nickname ? client->nickname :
+                   (unsigned char *)""));
+
     /* Send the packet */
     silc_server_packet_send_to_channel_real(server, sock, &packetdata,
                                            idata->send_key, 
@@ -660,7 +728,9 @@ void silc_server_packet_send_to_channel(SilcServer server,
                                            data, data_len, FALSE, 
                                            force_send);
   }
+  silc_hash_table_list_reset(&htl);
 
+ out:
   silc_free(routed);
   silc_free(packetdata.src_id);
   silc_free(packetdata.dst_id);
@@ -677,6 +747,9 @@ silc_server_packet_relay_to_channel_encrypt(SilcServer server,
                                            unsigned char *data,
                                            unsigned int data_len)
 {
+  SilcUInt32 mac_len, iv_len;
+  unsigned char iv[SILC_CIPHER_MAX_IV_SIZE];
+
   /* If we are router and the packet came from router and private key
      has not been set for the channel then we must encrypt the packet
      as it was decrypted with the session key shared between us and the
@@ -684,34 +757,27 @@ silc_server_packet_relay_to_channel_encrypt(SilcServer server,
      same channel key. */
   if (server->server_type == SILC_ROUTER &&
       sock->type == SILC_SOCKET_TYPE_ROUTER &&
-      !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY) &&
-      channel->channel_key) {
-    SilcBuffer chp;
-    uint32 iv_len, i;
-    uint16 dlen, flags;
+      !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY) && channel->key) {
+
+    /* If we are backup router and remote is our primary router and
+       we are currently doing backup resuming protocol we must not
+       re-encrypt message with session key. */
+    if (server->backup_router && SILC_SERVER_IS_BACKUP(sock) &&
+       SILC_PRIMARY_ROUTE(server) == sock)
+      return TRUE;
 
+    mac_len = silc_hmac_len(channel->hmac);
     iv_len = silc_cipher_get_block_len(channel->channel_key);
-    if (channel->iv[0] == '\0')
-      for (i = 0; i < iv_len; i++) channel->iv[i] = 
-                                    silc_rng_get_byte(server->rng);
-    else
-      silc_hash_make(server->md5hash, channel->iv, iv_len, channel->iv);
-    
-    /* Encode new payload. This encrypts it also. */
-    SILC_GET16_MSB(flags, data);
-    SILC_GET16_MSB(dlen, data + 2);
 
-    if (dlen > data_len) {
+    if (data_len <= mac_len + iv_len) {
       SILC_LOG_WARNING(("Corrupted channel message, cannot relay it"));
       return FALSE;
     }
 
-    chp = silc_channel_message_payload_encode(flags, dlen, data + 4,
-                                             iv_len, channel->iv,
-                                             channel->channel_key,
-                                             channel->hmac);
-    memcpy(data, chp->data, chp->len);
-    silc_buffer_free(chp);
+    memcpy(iv, data + (data_len - iv_len - mac_len), iv_len);
+    silc_message_payload_encrypt(data, data_len - iv_len, data_len,
+                                iv, iv_len, channel->channel_key,
+                                channel->hmac);
   }
 
   return TRUE;
@@ -729,26 +795,28 @@ silc_server_packet_relay_to_channel_encrypt(SilcServer server,
 void silc_server_packet_relay_to_channel(SilcServer server,
                                         SilcSocketConnection sender_sock,
                                         SilcChannelEntry channel,
-                                        void *sender
+                                        void *sender_id,
                                         SilcIdType sender_type,
-                                        void *sender_entry,
+                                        SilcClientEntry sender_entry,
                                         unsigned char *data,
-                                        uint32 data_len,
+                                        SilcUInt32 data_len,
                                         bool force_send)
 {
-  bool found = FALSE;
   SilcSocketConnection sock = NULL;
   SilcPacketContext packetdata;
   SilcClientEntry client = NULL;
   SilcServerEntry *routed = NULL;
-  SilcChannelClientEntry chl;
-  uint32 routed_count = 0;
+  SilcChannelClientEntry chl, chl_sender;
+  SilcUInt32 routed_count = 0;
   SilcIDListData idata;
   SilcHashTableList htl;
   bool gone = FALSE;
   int k;
 
-  SILC_LOG_DEBUG(("Relaying packet to channel"));
+  if (!silc_server_client_on_channel(sender_entry, channel, &chl_sender))
+    return;
+
+  SILC_LOG_DEBUG(("Relaying packet to channel %s", channel->channel_name));
 
   /* This encrypts the packet, if needed. It will be encrypted if
      it came from the router thus it needs to be encrypted with the
@@ -762,8 +830,8 @@ void silc_server_packet_relay_to_channel(SilcServer server,
   /* Set the packet context pointers. */
   packetdata.flags = 0;
   packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
-  packetdata.src_id = silc_id_id2str(sender, sender_type);
-  packetdata.src_id_len = silc_id_get_len(sender, sender_type);
+  packetdata.src_id = silc_id_id2str(sender_id, sender_type);
+  packetdata.src_id_len = silc_id_get_len(sender_id, sender_type);
   packetdata.src_id_type = sender_type;
   packetdata.dst_id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
   packetdata.dst_id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
@@ -782,7 +850,7 @@ void silc_server_packet_relay_to_channel(SilcServer server,
       sock = (SilcSocketConnection)router->connection;
       idata = (SilcIDListData)router;
 
-      SILC_LOG_DEBUG(("Sending channel message to router for routing"));
+      SILC_LOG_DEBUG(("Sending message to router for routing"));
 
       silc_server_packet_send_to_channel_real(server, sock, &packetdata,
                                              idata->send_key, 
@@ -796,59 +864,45 @@ void silc_server_packet_relay_to_channel(SilcServer server,
   routed = silc_calloc(silc_hash_table_count(channel->user_list), 
                       sizeof(*routed));
 
-  /* Mark that to the route the original sender if from is not routed */
-  if (sender_type == SILC_ID_CLIENT) {
-    client = (SilcClientEntry)sender_entry;
-    if (client->router) {
-      routed[routed_count++] = client->router;
-      SILC_LOG_DEBUG(("************* router %s", 
-                     silc_id_render(client->router->id, SILC_ID_SERVER)));
-    }
-  }
+  /* Assure we won't route the message back to the sender's way. */
+  if (sender_entry->router)
+    routed[routed_count++] = sender_entry->router;
 
   /* Send the message to clients on the channel's client list. */
   silc_hash_table_list(channel->user_list, &htl);
-  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+  while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
     client = chl->client;
-    if (!client)
+    if (!client || client == sender_entry)
       continue;
 
-    /* Do not send to the sender */
-    if (!found && client == sender_entry) {
-      found = TRUE;
+    /* Check whether message sending is blocked */
+    if (chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES)
+      continue;
+    if (chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS &&
+       !(chl_sender->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+       !(chl_sender->mode & SILC_CHANNEL_UMODE_CHANFO))
+      continue;
+    if (chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS &&
+       sender_entry->mode & SILC_UMODE_ROBOT)
       continue;
-    }
 
     /* If the client has set router it means that it is not locally
        connected client and we will route the packet further. */
     if (server->server_type == SILC_ROUTER && client->router) {
 
-      /* Sender maybe server as well so we want to make sure that
-        we won't send the message to the server it came from. */
-      if (!found && SILC_ID_SERVER_COMPARE(client->router->id, sender)) {
-       found = TRUE;
-       routed[routed_count++] = client->router;
-       continue;
-      }
-
       /* Check if we have sent the packet to this route already */
       for (k = 0; k < routed_count; k++)
        if (routed[k] == client->router)
          break;
       if (k < routed_count)
        continue;
-       
+
       /* Get data used in packet header encryption, keys and stuff. */
       sock = (SilcSocketConnection)client->router->connection;
       idata = (SilcIDListData)client->router;
 
-      /* Do not send to the sender. Check first whether the true
-        sender's router is same as this client's router. Also check
-        if the sender socket is the same as this client's router
+      /* Check if the sender socket is the same as this client's router
         socket. */
-      if (sender_entry &&
-         ((SilcClientEntry)sender_entry)->router == client->router)
-       continue;
       if (sender_sock && sock == sender_sock)
        continue;
 
@@ -859,64 +913,75 @@ void silc_server_packet_relay_to_channel(SilcServer server,
       /* Mark this route routed already. */
       routed[routed_count++] = client->router;
        
-      /* If the remote connection is router then we'll decrypt the
-        channel message and re-encrypt it with the session key shared
-        between us and the remote router. This is done because the
-        channel keys are cell specific and we have different channel
-        key than the remote router has. */
       if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
+       /* The remote connection is router then we'll decrypt the
+          channel message and re-encrypt it with the session key shared
+          between us and the remote router. This is done because the
+          channel keys are cell specific and we have different channel
+          key than the remote router has. */
+
+       /* Route only once to router. Protocol prohibits sending channel
+          messages to more than one router. */
        if (gone)
          continue;
+       gone = TRUE;
+
+       /* If we are backup router and remote is our primary router and
+          we are currently doing backup resuming protocol we must not
+          re-encrypt message with session key. */
+       if (server->backup_router && SILC_SERVER_IS_BACKUP(sock) &&
+           SILC_PRIMARY_ROUTE(server) == sock) {
+         silc_server_packet_send_to_channel_real(server, sock, &packetdata,
+                                                 idata->send_key,
+                                                 idata->hmac_send,
+                                                 idata->psn_send++,
+                                                 data, data_len, TRUE,
+                                                 force_send);
+         continue;
+       }
 
        SILC_LOG_DEBUG(("Remote is router, encrypt with session key"));
-       gone = TRUE;
 
        /* If private key mode is not set then decrypt the packet
           and re-encrypt it */
-       if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
-         unsigned char *tmp = silc_calloc(data_len, sizeof(*data));
+       if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY) && 
+           channel->channel_key) {
+         unsigned char tmp[SILC_PACKET_MAX_LEN];
+
+         if (data_len > SILC_PACKET_MAX_LEN)
+           data_len = SILC_PACKET_MAX_LEN;
          memcpy(tmp, data, data_len);
 
          /* Decrypt the channel message (we don't check the MAC) */
-         if (channel->channel_key &&
-             !silc_channel_message_payload_decrypt(tmp, data_len, 
-                                                   channel->channel_key,
-                                                   NULL)) {
-           memset(tmp, 0, data_len);
-           silc_free(tmp);
-           continue;
-         }
+         silc_message_payload_decrypt(tmp, data_len, FALSE, FALSE,
+                                      channel->channel_key,
+                                      channel->hmac, FALSE);
 
          /* Now re-encrypt and send it to the router */
-         silc_server_packet_send_srcdest(server, sock, 
+         silc_server_packet_send_srcdest(server, sock,
                                          SILC_PACKET_CHANNEL_MESSAGE, 0,
-                                         sender, sender_type,
+                                         sender_id, sender_type,
                                          channel->id, SILC_ID_CHANNEL,
                                          tmp, data_len, force_send);
-
-         /* Free the copy of the channel message */
-         memset(tmp, 0, data_len);
-         silc_free(tmp);
        } else {
          /* Private key mode is set, we don't have the channel key, so
             just re-encrypt the entire packet and send it to the router. */
-         silc_server_packet_send_srcdest(server, sock, 
+         silc_server_packet_send_srcdest(server, sock,
                                          SILC_PACKET_CHANNEL_MESSAGE, 0,
-                                         sender, sender_type,
+                                         sender_id, sender_type,
                                          channel->id, SILC_ID_CHANNEL,
                                          data, data_len, force_send);
        }
-       continue;
+      } else {
+       /* Send the packet to normal server */
+       silc_server_packet_send_to_channel_real(server, sock, &packetdata,
+                                               idata->send_key,
+                                               idata->hmac_send,
+                                               idata->psn_send++,
+                                               data, data_len, TRUE,
+                                               force_send);
       }
 
-      /* Send the packet (to normal server) */
-      silc_server_packet_send_to_channel_real(server, sock, &packetdata,
-                                             idata->send_key, 
-                                             idata->hmac_send, 
-                                             idata->psn_send++, 
-                                             data, data_len, TRUE, 
-                                             force_send);
-
       continue;
     }
 
@@ -927,7 +992,7 @@ void silc_server_packet_relay_to_channel(SilcServer server,
     sock = (SilcSocketConnection)client->connection;
     idata = (SilcIDListData)client;
 
-    if (sender_sock && sock == sender_sock)
+    if (!sock || (sender_sock && sock == sender_sock))
       continue;
 
     SILC_LOG_DEBUG(("Sending packet to client ID(%s) %s (%s)", 
@@ -943,6 +1008,7 @@ void silc_server_packet_relay_to_channel(SilcServer server,
                                            force_send);
   }
 
+  silc_hash_table_list_reset(&htl);
   silc_free(routed);
   silc_free(packetdata.src_id);
   silc_free(packetdata.dst_id);
@@ -960,20 +1026,21 @@ void silc_server_packet_send_local_channel(SilcServer server,
                                           SilcPacketType type,
                                           SilcPacketFlags flags,
                                           unsigned char *data,
-                                          uint32 data_len,
+                                          SilcUInt32 data_len,
                                           bool force_send)
 {
   SilcChannelClientEntry chl;
   SilcHashTableList htl;
   SilcSocketConnection sock = NULL;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Send packet to local clients on channel %s",
+                 channel->channel_name));
 
   /* Send the message to clients on the channel's client list. */
   silc_hash_table_list(channel->user_list, &htl);
-  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
-    if (chl->client && !chl->client->router) {
-      sock = (SilcSocketConnection)chl->client->connection;
+  while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
+    if (chl->client && SILC_IS_LOCAL(chl->client)) {
+      sock = chl->client->connection;
 
       /* Send the packet to the client */
       silc_server_packet_send_dest(server, sock, type, flags, chl->client->id,
@@ -981,6 +1048,7 @@ void silc_server_packet_send_local_channel(SilcServer server,
                                   force_send);
     }
   }
+  silc_hash_table_list_reset(&htl);
 }
 
 /* Routine used to send (relay, route) private messages to some destination.
@@ -993,39 +1061,40 @@ void silc_server_send_private_message(SilcServer server,
                                      SilcSocketConnection dst_sock,
                                      SilcCipher cipher,
                                      SilcHmac hmac,
-                                     uint32 sequence,
+                                     SilcUInt32 sequence,
                                      SilcPacketContext *packet)
 {
   SilcBuffer buffer = packet->buffer;
+  const SilcBufferStruct p;
+
+  silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
+                  + packet->dst_id_len + packet->padlen);
+  if (!silc_packet_send_prepare(dst_sock, 0, 0, buffer->len, hmac,
+                                (const SilcBuffer)&p)) {
+    SILC_LOG_ERROR(("Cannot send packet"));
+    return;
+  }
+  silc_buffer_put((SilcBuffer)&p, buffer->data, buffer->len);
 
   /* Re-encrypt and send if private messge key does not exist */
   if (!(packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY)) {
-
-    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
-                    + packet->dst_id_len + packet->padlen);
-    silc_packet_send_prepare(dst_sock, 0, 0, buffer->len);
-    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
-
     /* Re-encrypt packet */
-    silc_packet_encrypt(cipher, hmac, sequence, dst_sock->outbuf, buffer->len);
-
-    /* Send the packet */
-    silc_server_packet_send_real(server, dst_sock, FALSE);
-
+    silc_packet_encrypt(cipher, hmac, sequence, (SilcBuffer)&p, buffer->len);
   } else {
     /* Key exist so encrypt just header and send it */
-    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
-                    + packet->dst_id_len + packet->padlen);
-    silc_packet_send_prepare(dst_sock, 0, 0, buffer->len);
-    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
-
-    /* Encrypt header */
-    silc_packet_encrypt(cipher, hmac, sequence, dst_sock->outbuf, 
+    silc_packet_encrypt(cipher, hmac, sequence, (SilcBuffer)&p, 
                        SILC_PACKET_HEADER_LEN + packet->src_id_len + 
                        packet->dst_id_len + packet->padlen);
-
-    silc_server_packet_send_real(server, dst_sock, FALSE);
   }
+
+  /* Send the packet */
+  silc_server_packet_send_real(server, dst_sock, FALSE);
+
+  /* Check for mandatory rekey */
+  if (sequence == SILC_SERVER_REKEY_THRESHOLD)
+    silc_schedule_task_add(server->schedule, dst_sock->sock,
+                          silc_server_rekey_callback, dst_sock, 0, 1,
+                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 }
 
 /* Sends current motd to client */
@@ -1033,16 +1102,18 @@ void silc_server_send_private_message(SilcServer server,
 void silc_server_send_motd(SilcServer server,
                           SilcSocketConnection sock)
 {
-  char *motd;
-  uint32 motd_len;
+  char *motd, *motd_file = NULL;
+  SilcUInt32 motd_len;
 
-  if (server->config && server->config->motd && 
-      server->config->motd->motd_file) {
+  if (server->config)
+    motd_file = server->config->server_info->motd_file;
 
-    motd = silc_file_readfile(server->config->motd->motd_file, &motd_len);
+  if (motd_file) {
+    motd = silc_file_readfile(motd_file, &motd_len);
     if (!motd)
       return;
 
+    motd[motd_len] = 0;
     silc_server_send_notify(server, sock, FALSE, SILC_NOTIFY_TYPE_MOTD, 1,
                            motd, motd_len);
     silc_free(motd);
@@ -1061,7 +1132,7 @@ void silc_server_send_error(SilcServer server,
 
   memset(buf, 0, sizeof(buf));
   va_start(ap, fmt);
-  vsprintf(buf, fmt, ap);
+  vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
   va_end(ap);
 
   silc_server_packet_send(server, sock, SILC_PACKET_ERROR, 0, 
@@ -1077,7 +1148,7 @@ void silc_server_send_notify(SilcServer server,
                             SilcSocketConnection sock,
                             bool broadcast,
                             SilcNotifyType type,
-                            uint32 argc, ...)
+                            SilcUInt32 argc, ...)
 {
   va_list ap;
   SilcBuffer packet;
@@ -1090,9 +1161,10 @@ void silc_server_send_notify(SilcServer server,
                          packet->data, packet->len, FALSE);
 
   /* Send to backup routers if this is being broadcasted to primary
-     router. */
-  if (server->router && server->router->connection &&
-      sock == server->router->connection && broadcast)
+     router.  The silc_server_backup_send checks further whether to
+     actually send it or not. */
+  if ((broadcast && sock && sock == SILC_PRIMARY_ROUTE(server)) ||
+      (broadcast && !sock && !SILC_PRIMARY_ROUTE(server)))
     silc_server_backup_send(server, NULL, SILC_PACKET_NOTIFY, 0,
                            packet->data, packet->len, FALSE, TRUE);
 
@@ -1107,7 +1179,7 @@ void silc_server_send_notify_args(SilcServer server,
                                  SilcSocketConnection sock,
                                  bool broadcast,
                                  SilcNotifyType type,
-                                 uint32 argc,
+                                 SilcUInt32 argc,
                                  SilcBuffer args)
 {
   SilcBuffer packet;
@@ -1147,7 +1219,8 @@ void silc_server_send_notify_nick_change(SilcServer server,
                                         SilcSocketConnection sock,
                                         bool broadcast,
                                         SilcClientID *old_id,
-                                        SilcClientID *new_id)
+                                        SilcClientID *new_id,
+                                        const char *nickname)
 {
   SilcBuffer idp1, idp2;
 
@@ -1156,7 +1229,8 @@ void silc_server_send_notify_nick_change(SilcServer server,
 
   silc_server_send_notify(server, sock, broadcast, 
                          SILC_NOTIFY_TYPE_NICK_CHANGE,
-                         2, idp1->data, idp1->len, idp2->data, idp2->len);
+                         3, idp1->data, idp1->len, idp2->data, idp2->len,
+                         nickname, nickname ? strlen(nickname) : 0);
   silc_buffer_free(idp1);
   silc_buffer_free(idp2);
 }
@@ -1207,29 +1281,34 @@ void silc_server_send_notify_cmode(SilcServer server,
                                   SilcSocketConnection sock,
                                   bool broadcast,
                                   SilcChannelEntry channel,
-                                  uint32 mode_mask,
+                                  SilcUInt32 mode_mask,
                                   void *id, SilcIdType id_type,
-                                  char *cipher, char *hmac,
-                                  char *passphrase)
+                                  const char *cipher, const char *hmac,
+                                  const char *passphrase,
+                                  SilcPublicKey founder_key)
 {
-  SilcBuffer idp;
+  SilcBuffer idp, fkey = NULL;
   unsigned char mode[4];
 
   idp = silc_id_payload_encode((void *)id, id_type);
   SILC_PUT32_MSB(mode_mask, mode);
+  if (founder_key)
+    fkey = silc_pkcs_public_key_payload_encode(founder_key);
 
   silc_server_send_notify_dest(server, sock, broadcast, (void *)channel->id,
                               SILC_ID_CHANNEL, SILC_NOTIFY_TYPE_CMODE_CHANGE,
-                              5, idp->data, idp->len,
+                              6, idp->data, idp->len,
                               mode, 4,
                               cipher, cipher ? strlen(cipher) : 0,
                               hmac, hmac ? strlen(hmac) : 0,
                               passphrase, passphrase ? 
-                              strlen(passphrase) : 0);
+                              strlen(passphrase) : 0,
+                              fkey ? fkey->data : NULL, fkey ? fkey->len : 0);
+  silc_buffer_free(fkey),
   silc_buffer_free(idp);
 }
 
-/* Sends CUMODE_CHANGE notify type. This tells that `client_id' changed the
+/* Sends CUMODE_CHANGE notify type. This tells that `id' changed the
    `target' client's mode on `channel'. The notify packet is always
    destined to the channel. */
 
@@ -1237,23 +1316,28 @@ void silc_server_send_notify_cumode(SilcServer server,
                                    SilcSocketConnection sock,
                                    bool broadcast,
                                    SilcChannelEntry channel,
-                                   uint32 mode_mask,
+                                   SilcUInt32 mode_mask,
                                    void *id, SilcIdType id_type,
-                                   SilcClientID *target)
+                                   SilcClientID *target,
+                                   SilcPublicKey founder_key)
 {
-  SilcBuffer idp1, idp2;
+  SilcBuffer idp1, idp2, fkey = NULL;
   unsigned char mode[4];
 
   idp1 = silc_id_payload_encode((void *)id, id_type);
   idp2 = silc_id_payload_encode((void *)target, SILC_ID_CLIENT);
   SILC_PUT32_MSB(mode_mask, mode);
+  if (founder_key)
+    fkey = silc_pkcs_public_key_payload_encode(founder_key);
 
   silc_server_send_notify_dest(server, sock, broadcast, (void *)channel->id,
                               SILC_ID_CHANNEL, 
-                              SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3
+                              SILC_NOTIFY_TYPE_CUMODE_CHANGE, 4
                               idp1->data, idp1->len,
                               mode, 4,
-                              idp2->data, idp2->len);
+                              idp2->data, idp2->len,
+                              fkey ? fkey->data : NULL, fkey ? fkey->len : 0);
+  silc_buffer_free(fkey);
   silc_buffer_free(idp1);
   silc_buffer_free(idp2);
 }
@@ -1338,16 +1422,21 @@ void silc_server_send_notify_killed(SilcServer server,
                                    SilcSocketConnection sock,
                                    bool broadcast,
                                    SilcClientID *client_id,
-                                   char *comment)
+                                   const char *comment,
+                                   void *killer, SilcIdType killer_type)
 {
-  SilcBuffer idp;
+  SilcBuffer idp1;
+  SilcBuffer idp2;
 
-  idp = silc_id_payload_encode((void *)client_id, SILC_ID_CLIENT);
+  idp1 = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+  idp2 = silc_id_payload_encode(killer, killer_type);
   silc_server_send_notify_dest(server, sock, broadcast, (void *)client_id,
                               SILC_ID_CLIENT, SILC_NOTIFY_TYPE_KILLED,
-                              comment ? 2 : 1, idp->data, idp->len,
-                              comment, comment ? strlen(comment) : 0);
-  silc_buffer_free(idp);
+                              3, idp1->data, idp1->len,
+                              comment, comment ? strlen(comment) : 0,
+                              idp2->data, idp2->len);
+  silc_buffer_free(idp1);
+  silc_buffer_free(idp2);
 }
 
 /* Sends UMODE_CHANGE notify type. This tells that `client_id' client's
@@ -1358,7 +1447,7 @@ void silc_server_send_notify_umode(SilcServer server,
                                   SilcSocketConnection sock,
                                   bool broadcast,
                                   SilcClientID *client_id,
-                                  uint32 mode_mask)
+                                  SilcUInt32 mode_mask)
 {
   SilcBuffer idp;
   unsigned char mode[4];
@@ -1381,7 +1470,8 @@ void silc_server_send_notify_ban(SilcServer server,
                                 SilcSocketConnection sock,
                                 bool broadcast,
                                 SilcChannelEntry channel,
-                                char *add, char *del)
+                                unsigned char *action,
+                                SilcBuffer list)
 {
   SilcBuffer idp;
 
@@ -1389,8 +1479,8 @@ void silc_server_send_notify_ban(SilcServer server,
   silc_server_send_notify(server, sock, broadcast,
                          SILC_NOTIFY_TYPE_BAN, 3,
                          idp->data, idp->len,
-                         add, add ? strlen(add) : 0,
-                         del, del ? strlen(del) : 0);
+                         action ? action : NULL, action ? 1 : 0,
+                         list ? list->data : NULL, list ? list->len : 0);
   silc_buffer_free(idp);
 }
 
@@ -1404,7 +1494,8 @@ void silc_server_send_notify_invite(SilcServer server,
                                    bool broadcast,
                                    SilcChannelEntry channel,
                                    SilcClientID *client_id,
-                                   char *add, char *del)
+                                   unsigned char *action,
+                                   SilcBuffer list)
 {
   SilcBuffer idp, idp2;
 
@@ -1415,12 +1506,38 @@ void silc_server_send_notify_invite(SilcServer server,
                          idp->data, idp->len,
                          channel->channel_name, strlen(channel->channel_name),
                          idp2->data, idp2->len,
-                         add, add ? strlen(add) : 0,
-                         del, del ? strlen(del) : 0);
+                         action ? action : NULL, action ? 1 : 0,
+                         list ? list->data : NULL, list ? list->len : 0);
   silc_buffer_free(idp);
   silc_buffer_free(idp2);
 }
 
+/* Sends WATCH notify type. This tells that the `client' was watched and
+   its status in the network has changed. */
+
+void silc_server_send_notify_watch(SilcServer server,
+                                  SilcSocketConnection sock,
+                                  SilcClientEntry watcher,
+                                  SilcClientEntry client,
+                                  const char *nickname,
+                                  SilcNotifyType type)
+{
+  SilcBuffer idp;
+  unsigned char mode[4], n[2];
+
+  idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+  SILC_PUT16_MSB(type, n);
+  SILC_PUT32_MSB(client->mode, mode);
+  silc_server_send_notify_dest(server, sock, FALSE, watcher->id,
+                              SILC_ID_CLIENT, SILC_NOTIFY_TYPE_WATCH,
+                              4, idp->data, idp->len,
+                              nickname, nickname ? strlen(nickname) : 0,
+                              mode, sizeof(mode), 
+                              type != SILC_NOTIFY_TYPE_NONE ?
+                              n : NULL, sizeof(n));
+  silc_buffer_free(idp);
+}
+
 /* Sends notify message destined to specific entity. */
 
 void silc_server_send_notify_dest(SilcServer server,
@@ -1429,7 +1546,7 @@ void silc_server_send_notify_dest(SilcServer server,
                                  void *dest_id,
                                  SilcIdType dest_id_type,
                                  SilcNotifyType type,
-                                 uint32 argc, ...)
+                                 SilcUInt32 argc, ...)
 {
   va_list ap;
   SilcBuffer packet;
@@ -1441,6 +1558,16 @@ void silc_server_send_notify_dest(SilcServer server,
                               broadcast ? SILC_PACKET_FLAG_BROADCAST : 0,
                               dest_id, dest_id_type,
                               packet->data, packet->len, FALSE);
+
+  /* Send to backup routers if this is being broadcasted to primary
+     router.  The silc_server_backup_send checks further whether to
+     actually send it or not. */
+  if ((broadcast && sock && sock == SILC_PRIMARY_ROUTE(server)) ||
+      (broadcast && !sock && !SILC_PRIMARY_ROUTE(server)))
+    silc_server_backup_send_dest(server, NULL, SILC_PACKET_NOTIFY, 0,
+                                dest_id, dest_id_type,
+                                packet->data, packet->len, FALSE, TRUE);
+
   silc_buffer_free(packet);
   va_end(ap);
 }
@@ -1457,7 +1584,7 @@ void silc_server_send_notify_to_channel(SilcServer server,
                                        SilcChannelEntry channel,
                                        bool route_notify,
                                        SilcNotifyType type,
-                                       uint32 argc, ...)
+                                       SilcUInt32 argc, ...)
 {
   va_list ap;
   SilcBuffer packet;
@@ -1483,30 +1610,32 @@ void silc_server_send_notify_on_channels(SilcServer server,
                                         SilcClientEntry sender,
                                         SilcClientEntry client,
                                         SilcNotifyType type,
-                                        uint32 argc, ...)
+                                        SilcUInt32 argc, ...)
 {
   int k;
   SilcSocketConnection sock = NULL;
   SilcPacketContext packetdata;
   SilcClientEntry c;
   SilcClientEntry *sent_clients = NULL;
-  uint32 sent_clients_count = 0;
+  SilcUInt32 sent_clients_count = 0;
   SilcServerEntry *routed = NULL;
-  uint32 routed_count = 0;
+  SilcUInt32 routed_count = 0;
   SilcHashTableList htl, htl2;
   SilcChannelEntry channel;
   SilcChannelClientEntry chl, chl2;
   SilcIDListData idata;
   SilcBuffer packet;
   unsigned char *data;
-  uint32 data_len;
+  SilcUInt32 data_len;
   bool force_send = FALSE;
   va_list ap;
 
-  SILC_LOG_DEBUG(("Start"));
-
-  if (!silc_hash_table_count(client->channels))
+  if (!silc_hash_table_count(client->channels)) {
+    SILC_LOG_DEBUG(("Client is not joined to any channels"));
     return;
+  }
+
+  SILC_LOG_DEBUG(("Sending notify to joined channels"));
 
   va_start(ap, argc);
   packet = silc_notify_payload_encode(type, argc, ap);
@@ -1521,12 +1650,12 @@ void silc_server_send_notify_on_channels(SilcServer server,
   packetdata.src_id_type = SILC_ID_SERVER;
 
   silc_hash_table_list(client->channels, &htl);
-  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+  while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
     channel = chl->channel;
 
     /* Send the message to all clients on the channel's client list. */
     silc_hash_table_list(channel->user_list, &htl2);
-    while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
+    while (silc_hash_table_get(&htl2, NULL, (void **)&chl2)) {
       c = chl2->client;
       
       if (sender && c == sender)
@@ -1593,6 +1722,9 @@ void silc_server_send_notify_on_channels(SilcServer server,
        sock = (SilcSocketConnection)c->connection;
        idata = (SilcIDListData)c;
        
+        if (!sock)
+          continue;
+
        packetdata.dst_id = silc_id_id2str(c->id, SILC_ID_CLIENT);
        packetdata.dst_id_len = silc_id_get_len(c->id, SILC_ID_CLIENT);
        packetdata.dst_id_type = SILC_ID_CLIENT;
@@ -1613,8 +1745,10 @@ void silc_server_send_notify_on_channels(SilcServer server,
        sent_clients[sent_clients_count++] = c;
       }
     }
+    silc_hash_table_list_reset(&htl2);
   }
 
+  silc_hash_table_list_reset(&htl);
   silc_free(routed);
   silc_free(sent_clients);
   silc_free(packetdata.src_id);
@@ -1632,24 +1766,16 @@ void silc_server_send_new_id(SilcServer server,
                             SilcSocketConnection sock,
                             bool broadcast,
                             void *id, SilcIdType id_type, 
-                            uint32 id_len)
+                            SilcUInt32 id_len)
 {
   SilcBuffer idp;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Sending new ID"));
 
   idp = silc_id_payload_encode(id, id_type);
   silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 
                          broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
                          idp->data, idp->len, FALSE);
-
-  /* Send to backup routers if this is being broadcasted to primary
-     router. */
-  if (server->router && server->router->connection &&
-      sock == server->router->connection && broadcast)
-    silc_server_backup_send(server, NULL, SILC_PACKET_NEW_ID, 0,
-                           idp->data, idp->len, FALSE, TRUE);
-
   silc_buffer_free(idp);
 }
 
@@ -1662,14 +1788,14 @@ void silc_server_send_new_channel(SilcServer server,
                                  bool broadcast,
                                  char *channel_name,
                                  void *channel_id, 
-                                 uint32 channel_id_len,
-                                 uint32 mode)
+                                 SilcUInt32 channel_id_len,
+                                 SilcUInt32 mode)
 {
   SilcBuffer packet;
   unsigned char *cid;
-  uint32 name_len = strlen(channel_name);
+  SilcUInt32 name_len = strlen(channel_name);
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Sending new channel"));
 
   cid = silc_id_id2str(channel_id, SILC_ID_CHANNEL);
   if (!cid)
@@ -1683,14 +1809,6 @@ void silc_server_send_new_channel(SilcServer server,
                          broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
                          packet->data, packet->len, FALSE);
 
-  /* Send to backup routers if this is being broadcasted to primary
-     router. */
-  if (server->server_type == SILC_ROUTER &&
-      server->router && server->router->connection &&
-      sock == server->router->connection && broadcast)
-    silc_server_backup_send(server, NULL, SILC_PACKET_NEW_CHANNEL, 0,
-                           packet->data, packet->len, FALSE, TRUE);
-
   silc_free(cid);
   silc_buffer_free(packet);
 }
@@ -1711,20 +1829,24 @@ void silc_server_send_channel_key(SilcServer server,
 {
   SilcBuffer packet;
   unsigned char *chid;
-  uint32 tmp_len;
+  SilcUInt32 tmp_len;
+  const char *cipher;
  
   SILC_LOG_DEBUG(("Sending key to channel %s", channel->channel_name));
  
   chid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
   if (!chid)
     return;
+
+  if (!channel->channel_key)
+    return;
  
   /* Encode channel key packet */
-  tmp_len = strlen(channel->channel_key->cipher->name);
+  cipher = silc_cipher_get_name(channel->channel_key);
+  tmp_len = strlen(cipher);
   packet = silc_channel_key_payload_encode(silc_id_get_len(channel->id,
                                                           SILC_ID_CHANNEL),
-                                          chid, tmp_len,
-                                           channel->channel_key->cipher->name,
+                                          chid, tmp_len, cipher,
                                            channel->key_len / 8, channel->key);
   silc_server_packet_send_to_channel(server, sender, channel, 
                                     SILC_PACKET_CHANNEL_KEY,
@@ -1740,8 +1862,8 @@ void silc_server_send_channel_key(SilcServer server,
 void silc_server_send_command(SilcServer server, 
                              SilcSocketConnection sock,
                              SilcCommand command, 
-                             uint16 ident,
-                             uint32 argc, ...)
+                             SilcUInt16 ident,
+                             SilcUInt32 argc, ...)
 {
   SilcBuffer packet;
   va_list ap;
@@ -1750,7 +1872,7 @@ void silc_server_send_command(SilcServer server,
 
   packet = silc_command_payload_encode_vap(command, ident, argc, ap);
   silc_server_packet_send(server, sock, SILC_PACKET_COMMAND, 0,
-                         packet->data, packet->len, TRUE);
+                         packet->data, packet->len, FALSE);
   silc_buffer_free(packet);
   va_end(ap);
 }
@@ -1761,17 +1883,18 @@ void silc_server_send_command(SilcServer server,
 void silc_server_send_command_reply(SilcServer server, 
                                    SilcSocketConnection sock,
                                    SilcCommand command, 
-                                   SilcCommandStatus status,
-                                   uint16 ident,
-                                   uint32 argc, ...)
+                                   SilcStatus status,
+                                   SilcStatus error,
+                                   SilcUInt16 ident,
+                                   SilcUInt32 argc, ...)
 {
   SilcBuffer packet;
   va_list ap;
 
   va_start(ap, argc);
 
-  packet = silc_command_reply_payload_encode_vap(command, status, ident, 
-                                                argc, ap);
+  packet = silc_command_reply_payload_encode_vap(command, status, error,
+                                                ident, argc, ap);
   silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0,
                          packet->data, packet->len, TRUE);
   silc_buffer_free(packet);
@@ -1786,20 +1909,21 @@ void silc_server_send_dest_command_reply(SilcServer server,
                                         void *dst_id,
                                         SilcIdType dst_id_type,
                                         SilcCommand command, 
-                                        SilcCommandStatus status,
-                                        uint16 ident,
-                                        uint32 argc, ...)
+                                        SilcStatus status,
+                                        SilcStatus error,
+                                        SilcUInt16 ident,
+                                        SilcUInt32 argc, ...)
 {
   SilcBuffer packet;
   va_list ap;
 
   va_start(ap, argc);
 
-  packet = silc_command_reply_payload_encode_vap(command, status, ident, 
-                                                argc, ap);
+  packet = silc_command_reply_payload_encode_vap(command, status, error,
+                                                ident, argc, ap);
   silc_server_packet_send_dest(server, sock, SILC_PACKET_COMMAND_REPLY, 0,
                               dst_id, dst_id_type, packet->data, 
-                              packet->len, TRUE);
+                              packet->len, FALSE);
   silc_buffer_free(packet);
   va_end(ap);
 }
@@ -1820,32 +1944,42 @@ void silc_server_relay_packet(SilcServer server,
                              SilcSocketConnection dst_sock,
                              SilcCipher cipher,
                              SilcHmac hmac,
-                             uint32 sequence,
+                             SilcUInt32 sequence,
                              SilcPacketContext *packet,
                              bool force_send)
 {
+  const SilcBufferStruct p;
+
   silc_buffer_push(packet->buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
                   + packet->dst_id_len + packet->padlen);
+  if (!silc_packet_send_prepare(dst_sock, 0, 0, packet->buffer->len, hmac,
+                                (const SilcBuffer)&p)) {
+    SILC_LOG_ERROR(("Cannot send packet"));
+    return;
+  }
+  silc_buffer_put((SilcBuffer)&p, packet->buffer->data, packet->buffer->len);
 
-  silc_packet_send_prepare(dst_sock, 0, 0, packet->buffer->len);
-  silc_buffer_put(dst_sock->outbuf, packet->buffer->data, packet->buffer->len);
-  
   /* Re-encrypt packet */
-  silc_packet_encrypt(cipher, hmac, sequence, dst_sock->outbuf, 
-                     packet->buffer->len);
+  silc_packet_encrypt(cipher, hmac, sequence, (SilcBuffer)&p, p.len);
   
   /* Send the packet */
   silc_server_packet_send_real(server, dst_sock, force_send);
 
   silc_buffer_pull(packet->buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
                   + packet->dst_id_len + packet->padlen);
+
+  /* Check for mandatory rekey */
+  if (sequence == SILC_SERVER_REKEY_THRESHOLD)
+    silc_schedule_task_add(server->schedule, dst_sock->sock,
+                          silc_server_rekey_callback, dst_sock, 0, 1,
+                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 }
 
 /* Routine used to send the connection authentication packet. */
 
 void silc_server_send_connection_auth_request(SilcServer server,
                                              SilcSocketConnection sock,
-                                             uint16 conn_type,
+                                             SilcUInt16 conn_type,
                                              SilcAuthMethod auth_meth)
 {
   SilcBuffer packet;
@@ -1872,14 +2006,188 @@ void silc_server_packet_queue_purge(SilcServer server,
   if (sock && SILC_IS_OUTBUF_PENDING(sock) && 
       (SILC_IS_DISCONNECTED(sock) == FALSE)) {
     server->stat.packets_sent++;
-
-    if (sock->outbuf->data - sock->outbuf->head)
-      silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
-
     silc_packet_send(sock, TRUE);
-
-    SILC_SET_CONNECTION_FOR_INPUT(server->schedule, sock->sock);
     SILC_UNSET_OUTBUF_PENDING(sock);
     silc_buffer_clear(sock->outbuf);
   }
 }
+
+/* Send packet to clients that are known to be operators.  If server
+   is router and `route' is TRUE then the packet would go to all operators
+   in the SILC network.  If `route' is FALSE then only local operators
+   (local for server and cell wide for router).  If `local' is TRUE then
+   only locally connected operators receive the packet.  If `local' is
+   TRUE then `route' is ignored.  If server is normal server and `route'
+   is FALSE it is equivalent to `local' being TRUE. */
+
+void silc_server_send_opers(SilcServer server,
+                           SilcPacketType type,
+                           SilcPacketFlags flags,
+                           bool route, bool local,
+                           unsigned char *data, 
+                           SilcUInt32 data_len,
+                           bool force_send)
+{
+  SilcIDCacheList list = NULL;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcClientEntry client = NULL;
+  SilcSocketConnection sock;
+  SilcServerEntry *routed = NULL;
+  SilcUInt32 routed_count = 0;
+  bool gone = FALSE;
+  int k;
+
+  SILC_LOG_DEBUG(("Sending %s packet to operators",
+                 silc_get_packet_name(type)));
+
+  /* If local was requested send only locally connected operators. */
+  if (local || (server->server_type == SILC_SERVER && !route)) {
+    if (!silc_idcache_get_all(server->local_list->clients, &list) ||
+       !silc_idcache_list_first(list, &id_cache))
+      return;
+    while (id_cache) {
+      client = (SilcClientEntry)id_cache->context;
+      if (!client->router && SILC_IS_LOCAL(client) &&
+         (client->mode & SILC_UMODE_SERVER_OPERATOR ||
+          client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
+
+       /* Send the packet to locally connected operator */
+       silc_server_packet_send_dest(server, client->connection, type, flags,
+                                    client->id, SILC_ID_CLIENT,
+                                    data, data_len, force_send);
+      }
+
+      if (!silc_idcache_list_next(list, &id_cache))
+       break;
+    }
+    silc_idcache_list_free(list);
+    return;
+  }
+
+  if (!silc_idcache_get_all(server->local_list->clients, &list) ||
+      !silc_idcache_list_first(list, &id_cache))
+    return;
+  while (id_cache) {
+    client = (SilcClientEntry)id_cache->context;
+    if (!(client->mode & SILC_UMODE_SERVER_OPERATOR) &&
+       !(client->mode & SILC_UMODE_ROUTER_OPERATOR))
+      goto next;
+
+    if (server->server_type != SILC_SERVER && client->router && 
+       ((!route && client->router->router == server->id_entry) || route)) {
+
+      /* Check if we have sent the packet to this route already */
+      for (k = 0; k < routed_count; k++)
+       if (routed[k] == client->router)
+         break;
+      if (k < routed_count)
+       goto next;
+
+      /* Route only once to router */
+      sock = (SilcSocketConnection)client->router->connection;
+      if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
+       if (gone)
+         goto next;
+       gone = TRUE;
+      }
+
+      /* Send the packet */
+      silc_server_packet_send_dest(server, sock, type, flags,
+                                  client->id, SILC_ID_CLIENT,
+                                  data, data_len, force_send);
+
+      /* Mark this route routed already */
+      routed = silc_realloc(routed, sizeof(*routed) * (routed_count + 1));
+      routed[routed_count++] = client->router;
+      goto next;
+    }
+
+    if (client->router || !client->connection)
+      goto next;
+
+    /* Send to locally connected client */
+    sock = (SilcSocketConnection)client->connection;
+    silc_server_packet_send_dest(server, sock, type, flags,
+                                client->id, SILC_ID_CLIENT,
+                                data, data_len, force_send);
+
+  next:
+    if (!silc_idcache_list_next(list, &id_cache))
+      break;
+  }
+  silc_idcache_list_free(list);
+
+  if (!silc_idcache_get_all(server->global_list->clients, &list) ||
+      !silc_idcache_list_first(list, &id_cache))
+    return;
+  while (id_cache) {
+    client = (SilcClientEntry)id_cache->context;
+    if (!(client->mode & SILC_UMODE_SERVER_OPERATOR) &&
+       !(client->mode & SILC_UMODE_ROUTER_OPERATOR))
+      goto nextg;
+
+    if (server->server_type != SILC_SERVER && client->router && 
+       ((!route && client->router->router == server->id_entry) || route)) {
+
+      /* Check if we have sent the packet to this route already */
+      for (k = 0; k < routed_count; k++)
+       if (routed[k] == client->router)
+         break;
+      if (k < routed_count)
+       goto nextg;
+
+      /* Route only once to router */
+      sock = (SilcSocketConnection)client->router->connection;
+      if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
+       if (gone)
+         goto nextg;
+       gone = TRUE;
+      }
+
+      /* Send the packet */
+      silc_server_packet_send_dest(server, sock, type, flags,
+                                  client->id, SILC_ID_CLIENT,
+                                  data, data_len, force_send);
+
+      /* Mark this route routed already */
+      routed = silc_realloc(routed, sizeof(*routed) * (routed_count + 1));
+      routed[routed_count++] = client->router;
+      goto nextg;
+    }
+
+    if (client->router || !client->connection)
+      goto nextg;
+
+    /* Send to locally connected client */
+    sock = (SilcSocketConnection)client->connection;
+    silc_server_packet_send_dest(server, sock, type, flags,
+                                client->id, SILC_ID_CLIENT,
+                                data, data_len, force_send);
+
+  nextg:
+    if (!silc_idcache_list_next(list, &id_cache))
+      break;
+  }
+  silc_idcache_list_free(list);
+  silc_free(routed);
+}
+
+/* Send a notify packet to operators */
+
+void silc_server_send_opers_notify(SilcServer server,
+                                  bool route,
+                                  bool local,
+                                  SilcNotifyType type,
+                                  SilcUInt32 argc, ...)
+{
+  va_list ap;
+  SilcBuffer packet;
+
+  va_start(ap, argc);
+  packet = silc_notify_payload_encode(type, argc, ap);
+  silc_server_send_opers(server, SILC_PACKET_NOTIFY, 0,
+                        route, local, packet->data, packet->len,
+                        FALSE);
+  silc_buffer_free(packet);
+  va_end(ap);
+}