/*
- packet_send.c
+ packet_send.c
Author: Pekka Riikonen <priikone@silcnet.org>
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
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);
+ 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;
}
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) {
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);
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.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,
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
/* 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);
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;
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,
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
/* 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;
}
/* 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
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);
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
goto out;
}
- SILC_LOG_DEBUG(("Sending %s packet to channel %s",
+ 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),
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
same channel key. */
if (server->server_type == SILC_ROUTER &&
sock->type == SILC_SOCKET_TYPE_ROUTER &&
- !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY) &&
- channel->channel_key) {
- SilcUInt32 mac_len = silc_hmac_len(channel->hmac);
- SilcUInt32 iv_len = silc_cipher_get_block_len(channel->channel_key);
- unsigned char iv[SILC_CIPHER_MAX_IV_SIZE];
+ !(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 (data_len <= mac_len + iv_len) {
SILC_LOG_WARNING(("Corrupted channel message, cannot relay it"));
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, data_len,
+ iv, iv_len, channel->channel_key,
+ channel->hmac);
}
return TRUE;
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,
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
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,
/* 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,
/* 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 */
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);
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,
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,
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);
}
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,
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);
}
SilcSocketConnection sock,
bool broadcast,
SilcChannelEntry channel,
- char *add, char *del)
+ unsigned char *action,
+ SilcBuffer list)
{
SilcBuffer idp;
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);
}
bool broadcast,
SilcChannelEntry channel,
SilcClientID *client_id,
- char *add, char *del)
+ unsigned char *action,
+ SilcBuffer list)
{
SilcBuffer idp, idp2;
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);
}
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);
}
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,
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);
}
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);
}
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. */
(SILC_IS_DISCONNECTED(sock) == FALSE)) {
server->stat.packets_sent++;
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);
+}