Support for SILC 1.2 INVITE and BAN commands. Client supports
[silc.git] / apps / silcd / packet_send.c
index 18c4011d78832fd33513f9b97a0c66556ccaba15..0d97aeafd33ecabab76994248887f38c9ee996d0 100644 (file)
@@ -1,6 +1,6 @@
 /*
 
-  packet_send.c
+  packet_send.c 
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
@@ -8,9 +8,8 @@
 
   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
@@ -96,9 +95,15 @@ void silc_server_packet_send(SilcServer server,
   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) {
@@ -157,10 +162,14 @@ void silc_server_packet_send_dest(SilcServer server,
   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 %s packet", silc_get_packet_name(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);
@@ -171,7 +180,14 @@ 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 */
@@ -188,7 +204,10 @@ void silc_server_packet_send_dest(SilcServer server,
                                            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);
+  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 */
   if (!silc_packet_assemble(&packetdata, NULL, cipher, hmac, sock,
@@ -207,10 +226,8 @@ void silc_server_packet_send_dest(SilcServer server,
   silc_server_packet_send_real(server, sock, force_send);
 
  out:
-  if (packetdata.src_id)
-    silc_free(packetdata.src_id);
-  if (packetdata.dst_id)
-    silc_free(packetdata.dst_id);
+  silc_free(packetdata.src_id);
+  silc_free(packetdata.dst_id);
 }
 
 /* Assembles a new packet to be sent out to network. This doesn't actually
@@ -253,6 +270,26 @@ void silc_server_packet_send_srcdest(SilcServer server,
   /* 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);
@@ -263,13 +300,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;
@@ -284,7 +314,7 @@ void silc_server_packet_send_srcdest(SilcServer server,
                                            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);
+  SILC_PACKET_PADLEN(packetdata.truelen, block_len, packetdata.padlen);
 
   /* Create the outgoing packet */
   if (!silc_packet_assemble(&packetdata, NULL, cipher, hmac, sock, data,
@@ -303,10 +333,8 @@ void silc_server_packet_send_srcdest(SilcServer server,
   silc_server_packet_send_real(server, sock, force_send);
 
  out:
-  if (packetdata.src_id)
-    silc_free(packetdata.src_id);
-  if (packetdata.dst_id)
-    silc_free(packetdata.dst_id);
+  silc_free(packetdata.src_id);
+  silc_free(packetdata.dst_id);
 }
 
 /* Broadcast received packet to our primary route. This function is used
@@ -352,6 +380,12 @@ void silc_server_packet_broadcast(SilcServer server,
     /* 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;
   }
 
@@ -390,6 +424,12 @@ void silc_server_packet_route(SilcServer server,
 
   /* 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
@@ -413,7 +453,11 @@ void silc_server_packet_send_clients(SilcServer server,
   bool gone = FALSE;
   int k;
 
-  SILC_LOG_DEBUG(("Sending packet to list of clients"));
+  if (!silc_hash_table_count(clients))
+    return;
+
+  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);
@@ -497,11 +541,11 @@ silc_server_packet_send_to_channel_real(SilcServer server,
 
   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);
+    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
@@ -596,7 +640,8 @@ void silc_server_packet_send_to_channel(SilcServer server,
     goto out;
   }
 
-  SILC_LOG_DEBUG(("Sending packet to channel %s", channel->channel_name));
+  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));
@@ -636,6 +681,10 @@ void silc_server_packet_send_to_channel(SilcServer server,
        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, 
@@ -661,6 +710,10 @@ void silc_server_packet_send_to_channel(SilcServer server,
     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, 
@@ -706,10 +759,9 @@ silc_server_packet_relay_to_channel_encrypt(SilcServer server,
       return FALSE;
     }
 
-    memcpy(iv, data + (data_len - iv_len), iv_len);
-    silc_channel_message_payload_encrypt(data, data_len - iv_len - mac_len,
-                                        data_len, iv, iv_len,
-                                        channel->channel_key, channel->hmac);
+    memcpy(iv, data + (data_len - iv_len - mac_len), iv_len);
+    silc_message_payload_encrypt(data, data_len - iv_len, iv, iv_len,
+                                channel->channel_key, channel->hmac);
   }
 
   return TRUE;
@@ -782,7 +834,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, 
@@ -858,6 +910,20 @@ void silc_server_packet_relay_to_channel(SilcServer server,
          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"));
 
        /* If private key mode is not set then decrypt the packet
@@ -871,8 +937,9 @@ void silc_server_packet_relay_to_channel(SilcServer server,
          memcpy(tmp, data, data_len);
 
          /* Decrypt the channel message (we don't check the MAC) */
-         silc_channel_message_payload_decrypt(tmp, data_len,
-                                              channel->channel_key, NULL);
+         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,
@@ -956,8 +1023,8 @@ void silc_server_packet_send_local_channel(SilcServer server,
   /* 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;
+    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,
@@ -1006,6 +1073,12 @@ void silc_server_send_private_message(SilcServer server,
 
   /* 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 */
@@ -1024,6 +1097,7 @@ void silc_server_send_motd(SilcServer server,
     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);
@@ -1042,7 +1116,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, 
@@ -1197,14 +1271,13 @@ void silc_server_send_notify_cmode(SilcServer server,
                                   const char *passphrase,
                                   SilcPublicKey founder_key)
 {
-  SilcBuffer idp;
-  unsigned char mode[4], *key = NULL;
-  SilcUInt32 key_len = 0;
+  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)
-    key = silc_pkcs_public_key_encode(founder_key, &key_len);
+    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,
@@ -1214,8 +1287,8 @@ void silc_server_send_notify_cmode(SilcServer server,
                               hmac, hmac ? strlen(hmac) : 0,
                               passphrase, passphrase ? 
                               strlen(passphrase) : 0,
-                              key, key_len);
-  silc_free(key);
+                              fkey ? fkey->data : NULL, fkey ? fkey->len : 0);
+  silc_buffer_free(fkey),
   silc_buffer_free(idp);
 }
 
@@ -1232,15 +1305,14 @@ void silc_server_send_notify_cumode(SilcServer server,
                                    SilcClientID *target,
                                    SilcPublicKey founder_key)
 {
-  SilcBuffer idp1, idp2;
-  unsigned char mode[4], *key = NULL;
-  SilcUInt32 key_len = 0;
+  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)
-    key = silc_pkcs_public_key_encode(founder_key, &key_len);
+    fkey = silc_pkcs_public_key_payload_encode(founder_key);
 
   silc_server_send_notify_dest(server, sock, broadcast, (void *)channel->id,
                               SILC_ID_CHANNEL, 
@@ -1248,8 +1320,8 @@ void silc_server_send_notify_cumode(SilcServer server,
                               idp1->data, idp1->len,
                               mode, 4,
                               idp2->data, idp2->len,
-                              key, key_len);
-  silc_free(key);
+                              fkey ? fkey->data : NULL, fkey ? fkey->len : 0);
+  silc_buffer_free(fkey);
   silc_buffer_free(idp1);
   silc_buffer_free(idp2);
 }
@@ -1382,7 +1454,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;
 
@@ -1390,8 +1463,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);
 }
 
@@ -1405,7 +1478,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;
 
@@ -1416,8 +1490,8 @@ 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);
 }
@@ -1468,6 +1542,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);
 }
@@ -1730,19 +1814,23 @@ void silc_server_send_channel_key(SilcServer server,
   SilcBuffer packet;
   unsigned char *chid;
   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,
@@ -1768,7 +1856,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);
 }
@@ -1819,7 +1907,7 @@ void silc_server_send_dest_command_reply(SilcServer server,
                                                 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);
 }
@@ -1863,6 +1951,12 @@ void silc_server_relay_packet(SilcServer server,
 
   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. */
@@ -1902,3 +1996,183 @@ void silc_server_packet_queue_purge(SilcServer server,
     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);
+}