Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 1997 - 2002 Pekka Riikonen
+ Copyright (C) 1997 - 2005 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
*/
/*
- * Server packet routines to send packets.
+ * Server packet routines to send packets.
*/
/* $Id$ */
int ret;
/* If disconnecting, ignore the data */
- if (SILC_IS_DISCONNECTING(sock))
+ if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(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;
+ /* Send the packet */
+ ret = silc_packet_send(sock, FALSE);
+ if (ret != -2) {
+ if (ret == -1) {
+ SILC_SET_CONNECTION_FOR_INPUT(server->schedule, sock->sock);
+ SILC_UNSET_OUTBUF_PENDING(sock);
+ silc_buffer_clear(sock->outbuf);
+
+ 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")));
+
+ if (sock->user_data) {
+ /* If backup then mark that resuming will not be allowed */
+ if (server->server_type == SILC_ROUTER && !server->backup_router &&
+ sock->type == SILC_SOCKET_TYPE_SERVER) {
+ SilcServerEntry server_entry = sock->user_data;
+ if (server_entry->server_type == SILC_BACKUP_ROUTER)
+ server->backup_closed = TRUE;
+ }
- /* If outbound data is already pending do not force send */
- if (SILC_IS_OUTBUF_PENDING(sock))
- force_send = FALSE;
+ silc_server_free_sock_user_data(server, sock, NULL);
+ }
+ SILC_SET_DISCONNECTING(sock);
+ silc_server_close_connection(server, sock);
+ return ret;
+ }
- /* Send the packet */
- ret = silc_packet_send(sock, force_send);
- if (ret != -2)
+ server->stat.packets_sent++;
return ret;
+ }
- /* Mark that there is some outgoing data available for this connection.
+ /* Mark that there is some outgoing data available for this connection.
This call sets the connection both for input and output (the input
- is set always and this call keeps the input setting, actually).
+ is set always and this call keeps the input setting, actually).
Actual data sending is performed by silc_server_packet_process. */
SILC_SET_CONNECTION_FOR_OUTPUT(server->schedule, sock->sock);
/* Assembles a new packet to be sent out to network. This doesn't actually
send the packet but creates the packet and fills the outgoing data
- buffer and marks the packet ready to be sent to network. However, If
- argument force_send is TRUE the packet is sent immediately and not put
+ buffer and marks the packet ready to be sent to network. However, If
+ argument force_send is TRUE the packet is sent immediately and not put
to queue. Normal case is that the packet is not sent immediately. */
void silc_server_packet_send(SilcServer server,
- SilcSocketConnection sock,
- SilcPacketType type,
+ SilcSocketConnection sock,
+ SilcPacketType type,
SilcPacketFlags flags,
- unsigned char *data,
+ unsigned char *data,
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))
+ if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(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 && type != SILC_PACKET_KEY_EXCHANGE_1
+ && type != SILC_PACKET_KEY_EXCHANGE_2) ||
+ (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) {
/* Assembles a new packet to be sent out to network. This doesn't actually
send the packet but creates the packet and fills the outgoing data
- buffer and marks the packet ready to be sent to network. However, If
- argument force_send is TRUE the packet is sent immediately and not put
- to queue. Normal case is that the packet is not sent immediately.
+ buffer and marks the packet ready to be sent to network. However, If
+ argument force_send is TRUE the packet is sent immediately and not put
+ to queue. Normal case is that the packet is not sent immediately.
Destination information is sent as argument for this function. */
void silc_server_packet_send_dest(SilcServer server,
- SilcSocketConnection sock,
- SilcPacketType type,
+ SilcSocketConnection sock,
+ SilcPacketType type,
SilcPacketFlags flags,
void *dst_id,
SilcIdType dst_id_type,
- unsigned char *data,
+ unsigned char *data,
SilcUInt32 data_len,
bool force_send)
{
SilcPacketContext packetdata;
const SilcBufferStruct packet;
- SilcIDListData idata = (SilcIDListData)sock->user_data;
+ SilcIDListData idata;
SilcCipher cipher = NULL;
SilcHmac hmac = NULL;
SilcUInt32 sequence = 0;
int block_len = 0;
/* If disconnecting, ignore the data */
- if (SILC_IS_DISCONNECTING(sock))
+ if (!sock || SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock))
return;
- /* If entry is disabled do not sent anything. */
- if (idata && idata->status & SILC_IDLIST_STATUS_DISABLED)
+ idata = (SilcIDListData)sock->user_data;
+
+ /* 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 && type != SILC_PACKET_KEY_EXCHANGE_1
+ && type != SILC_PACKET_KEY_EXCHANGE_2) ||
+ (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);
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.src_id_len +
packetdata.dst_id_len));
- packetdata.truelen = data_len + SILC_PACKET_HEADER_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
send the packet but creates the packet and fills the outgoing data
- buffer and marks the packet ready to be sent to network. However, If
- argument force_send is TRUE the packet is sent immediately and not put
- to queue. Normal case is that the packet is not sent immediately.
+ buffer and marks the packet ready to be sent to network. However, If
+ argument force_send is TRUE the packet is sent immediately and not put
+ to queue. Normal case is that the packet is not sent immediately.
The source and destination information is sent as argument for this
function. */
void silc_server_packet_send_srcdest(SilcServer server,
- SilcSocketConnection sock,
- SilcPacketType type,
+ SilcSocketConnection sock,
+ SilcPacketType type,
SilcPacketFlags flags,
void *src_id,
SilcIdType src_id_type,
void *dst_id,
SilcIdType dst_id_type,
- unsigned char *data,
+ unsigned char *data,
SilcUInt32 data_len,
bool force_send)
{
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. 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 && type != SILC_PACKET_KEY_EXCHANGE_1
+ && type != SILC_PACKET_KEY_EXCHANGE_2) ||
+ (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;
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.src_id_len +
dst_id_len));
- packetdata.truelen = data_len + SILC_PACKET_HEADER_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
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
/* 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;
}
silc_packet_encrypt(idata->send_key, idata->hmac_send, idata->psn_send++,
(SilcBuffer)&p, p.len);
- SILC_LOG_HEXDUMP(("Routed packet (%d), len %d", idata->psn_send - 1,
+ SILC_LOG_HEXDUMP(("Routed packet (%d), len %d", idata->psn_send - 1,
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
clients (for server locally connected, and for router local cell). */
void silc_server_packet_send_clients(SilcServer server,
- SilcClientEntry *clients,
- SilcUInt32 clients_count,
- SilcPacketType type,
+ SilcHashTable clients,
+ SilcPacketType type,
SilcPacketFlags flags,
bool route,
- unsigned char *data,
+ unsigned char *data,
SilcUInt32 data_len,
bool force_send)
{
SilcSocketConnection sock = NULL;
+ SilcHashTableList htl;
SilcClientEntry client = NULL;
SilcServerEntry *routed = NULL;
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. */
- if (server->server_type == SILC_ROUTER && client->router &&
+ if (server->server_type == SILC_ROUTER && client->router &&
((!route && client->router->router == server->id_entry) || route)) {
/* Check if we have sent the packet to this route already */
client->id, SILC_ID_CLIENT,
data, data_len, force_send);
}
-
+ silc_hash_table_list_reset(&htl);
silc_free(routed);
}
return;
data_len = SILC_PACKET_DATALEN(data_len, (SILC_PACKET_HEADER_LEN +
- packet->src_id_len +
+ packet->src_id_len +
packet->dst_id_len));
- packet->truelen = data_len + SILC_PACKET_HEADER_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);
+ 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
}
if (channel_message)
- silc_packet_encrypt(cipher, hmac, sequence, (SilcBuffer)&p,
- SILC_PACKET_HEADER_LEN + packet->src_id_len +
+ 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, (SilcBuffer)&p, p.len);
-
+
SILC_LOG_HEXDUMP(("Channel packet (%d), len %d", sequence, p.len),
p.data, p.len);
silc_server_packet_send_real(server, sock, force_send);
}
-/* This routine is used by the server to send packets to channel. The
+/* This routine is used by the server to send packets to channel. The
packet sent with this function is distributed to all clients on
the channel. Usually this is used to send notify messages to the
- channel, things like notify about new user joining to the channel.
+ channel, things like notify about new user joining to the channel.
If `route' is FALSE then the packet is sent only locally and will not
be routed anywhere (for router locally means cell wide). If `sender'
is provided then the packet is not sent to that connection since it
SilcChannelEntry channel,
SilcPacketType type,
bool route,
+ bool send_to_clients,
unsigned char *data,
SilcUInt32 data_len,
bool force_send)
/* 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;
router = server->router;
sock = (SilcSocketConnection)router->connection;
idata = (SilcIDListData)router;
-
+
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,
+ idata->send_key,
+ idata->hmac_send,
idata->psn_send++,
- data, data_len, FALSE,
+ data, data_len, FALSE,
force_send);
}
}
- routed = silc_calloc(silc_hash_table_count(channel->user_list),
+ 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. */
/* 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. */
- if (server->server_type == SILC_ROUTER && client->router &&
+ if (server->server_type == SILC_ROUTER && client->router &&
((!route && client->router->router == server->id_entry) || route)) {
/* Check if we have sent the packet to this route already */
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,
- idata->hmac_send,
+ idata->send_key,
+ idata->hmac_send,
idata->psn_send++,
- data, data_len, FALSE,
+ data, data_len, FALSE,
force_send);
/* Mark this route routed already */
continue;
}
- if (client->router)
+ if (client->router || !send_to_clients)
continue;
/* Send to locally connected client */
/* Get data used in packet header encryption, keys and stuff. */
sock = (SilcSocketConnection)client->connection;
idata = (SilcIDListData)client;
-
+
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,
- idata->hmac_send,
- idata->psn_send++,
- data, data_len, FALSE,
+ idata->send_key,
+ idata->hmac_send,
+ idata->psn_send++,
+ 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);
unsigned char *data,
unsigned int data_len)
{
+ SilcUInt32 mac_len, iv_len;
+ unsigned char iv[SILC_CIPHER_MAX_IV_SIZE];
+ SilcUInt16 totlen, len;
+
/* 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);
+ !(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(channel->iv, data + (data_len - iv_len), iv_len);
- silc_channel_message_payload_encrypt(data, data_len - iv_len - mac_len,
- data_len, channel->iv, iv_len,
- channel->channel_key, channel->hmac);
+ totlen = 2;
+ SILC_GET16_MSB(len, data + totlen);
+ totlen += 2 + len;
+ if (totlen + iv_len + mac_len + 2 > data_len) {
+ SILC_LOG_WARNING(("Corrupted channel message, cannot relay it"));
+ return FALSE;
+ }
+ SILC_GET16_MSB(len, data + totlen);
+ totlen += 2 + len;
+ if (totlen + iv_len + mac_len > data_len) {
+ SILC_LOG_WARNING(("Corrupted channel message, cannot relay it"));
+ return FALSE;
+ }
+
+ memcpy(iv, data + (data_len - iv_len - mac_len), iv_len);
+ silc_message_payload_encrypt(data, totlen, data_len - mac_len,
+ iv, iv_len, channel->channel_key,
+ channel->hmac);
}
return TRUE;
/* This routine is explicitly used to relay messages to some channel.
Packets sent with this function we have received earlier and are
totally encrypted. This just sends the packet to all clients on
- the channel. If the sender of the packet is someone on the channel
+ the channel. If the sender of the packet is someone on the channel
the message will not be sent to that client. The SILC Packet header
is encrypted with the session key shared between us and the client.
MAC is also computed before encrypting the header. Rest of the
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
channel key. If the channel key does not exist, then we know we
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,
- idata->hmac_send,
- idata->psn_send++,
- data, data_len, TRUE,
+ idata->send_key,
+ idata->hmac_send,
+ idata->psn_send++,
+ data, data_len, TRUE,
force_send);
}
}
- routed = silc_calloc(silc_hash_table_count(channel->user_list),
+ routed = silc_calloc(silc_hash_table_count(channel->user_list),
sizeof(*routed));
/* Assure we won't route the message back to the sender's way. */
if (sender_sock && sock == sender_sock)
continue;
- SILC_LOG_DEBUG(("Relaying packet to client ID(%s) %s (%s)",
+ SILC_LOG_DEBUG(("Relaying packet to client ID(%s) %s (%s)",
silc_id_render(client->id, SILC_ID_CLIENT),
sock->hostname, sock->ip));
/* Mark this route routed already. */
routed[routed_count++] = client->router;
-
+
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
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
and re-encrypt it */
- if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY) &&
+ if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY) &&
channel->channel_key) {
unsigned char tmp[SILC_PACKET_MAX_LEN];
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,
if (!sock || (sender_sock && sock == sender_sock))
continue;
- SILC_LOG_DEBUG(("Sending packet to client ID(%s) %s (%s)",
+ SILC_LOG_DEBUG(("Sending packet to client ID(%s) %s (%s)",
silc_id_render(client->id, SILC_ID_CLIENT),
sock->hostname, sock->ip));
/* Send the packet */
silc_server_packet_send_to_channel_real(server, sock, &packetdata,
- idata->send_key,
- idata->hmac_send,
- idata->psn_send++,
- data, data_len, TRUE,
+ idata->send_key,
+ idata->hmac_send,
+ idata->psn_send++,
+ data, data_len, TRUE,
force_send);
}
/* This function is used to send packets strictly to all local clients
on a particular channel. This is used for example to distribute new
- channel key to all our locally connected clients on the channel.
+ channel key to all our locally connected clients on the channel.
The packets are always encrypted with the session key shared between
the client, this means these are not _to the channel_ but _to the client_
on the channel. */
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;
+ 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,
SilcBuffer buffer = packet->buffer;
const SilcBufferStruct p;
- silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len
+ 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_packet_encrypt(cipher, hmac, sequence, (SilcBuffer)&p, buffer->len);
} else {
/* Key exist so encrypt just header and send it */
- silc_packet_encrypt(cipher, hmac, sequence, (SilcBuffer)&p,
- SILC_PACKET_HEADER_LEN + packet->src_id_len +
+ silc_packet_encrypt(cipher, hmac, sequence, (SilcBuffer)&p,
+ SILC_PACKET_HEADER_LEN + packet->src_id_len +
packet->dst_id_len + packet->padlen);
}
/* 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);
}
}
-/* Sends error message. Error messages may or may not have any
+/* Sends error message. Error messages may or may not have any
implications. */
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,
+ silc_server_packet_send(server, sock, SILC_PACKET_ERROR, 0,
buf, strlen(buf), FALSE);
}
va_start(ap, argc);
packet = silc_notify_payload_encode(type, argc, ap);
- silc_server_packet_send(server, sock, SILC_PACKET_NOTIFY,
+ silc_server_packet_send(server, sock, SILC_PACKET_NOTIFY,
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->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);
SilcBuffer packet;
packet = silc_notify_payload_encode_args(type, argc, args);
- silc_server_packet_send(server, sock, SILC_PACKET_NOTIFY,
+ silc_server_packet_send(server, sock, SILC_PACKET_NOTIFY,
broadcast ? SILC_PACKET_FLAG_BROADCAST : 0,
packet->data, packet->len, FALSE);
silc_buffer_free(packet);
idp1 = silc_id_payload_encode((void *)old_id, SILC_ID_CLIENT);
idp2 = silc_id_payload_encode((void *)new_id, SILC_ID_CLIENT);
- silc_server_send_notify(server, sock, broadcast,
+ silc_server_send_notify(server, sock, broadcast,
SILC_NOTIFY_TYPE_NICK_CHANGE,
3, idp1->data, idp1->len, idp2->data, idp2->len,
nickname, nickname ? strlen(nickname) : 0);
SilcChannelEntry channel,
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 channel_pubkeys)
{
- SilcBuffer idp;
- unsigned char mode[4];
+ SilcBuffer idp, fkey = NULL;
+ unsigned char mode[4], ulimit[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);
+ if (channel->mode & SILC_CHANNEL_MODE_ULIMIT)
+ SILC_PUT32_MSB(channel->user_limit, ulimit);
silc_server_send_notify_dest(server, sock, broadcast, (void *)channel->id,
SILC_ID_CHANNEL, SILC_NOTIFY_TYPE_CMODE_CHANGE,
- 5, idp->data, idp->len,
+ 8, idp->data, idp->len,
mode, 4,
cipher, cipher ? strlen(cipher) : 0,
hmac, hmac ? strlen(hmac) : 0,
- passphrase, passphrase ?
- strlen(passphrase) : 0);
+ passphrase, passphrase ?
+ strlen(passphrase) : 0,
+ fkey ? fkey->data : NULL, fkey ? fkey->len : 0,
+ channel_pubkeys ? channel_pubkeys->data : NULL,
+ channel_pubkeys ? channel_pubkeys->len : 0,
+ mode_mask & SILC_CHANNEL_MODE_ULIMIT ?
+ ulimit : NULL,
+ mode_mask & SILC_CHANNEL_MODE_ULIMIT ?
+ sizeof(ulimit) : 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. */
SilcChannelEntry channel,
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_ID_CHANNEL,
+ 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);
}
silc_server_send_notify_dest(server, sock, broadcast,
(void *)channel->id, SILC_ID_CHANNEL,
SILC_NOTIFY_TYPE_TOPIC_SET,
- topic ? 2 : 1,
- idp->data, idp->len,
+ topic ? 2 : 1,
+ idp->data, idp->len,
topic, topic ? strlen(topic) : 0);
silc_buffer_free(idp);
}
silc_server_send_notify(server, sock, broadcast,
SILC_NOTIFY_TYPE_UMODE_CHANGE, 2,
- idp->data, idp->len,
+ idp->data, idp->len,
mode, 4);
silc_buffer_free(idp);
}
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);
}
SilcClientEntry watcher,
SilcClientEntry client,
const char *nickname,
- SilcNotifyType type)
+ SilcNotifyType type,
+ SilcPublicKey public_key)
{
- SilcBuffer idp;
+ SilcBuffer idp, pkp = NULL;
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);
+ if (public_key)
+ pkp = silc_pkcs_public_key_payload_encode(public_key);
silc_server_send_notify_dest(server, sock, FALSE, watcher->id,
SILC_ID_CLIENT, SILC_NOTIFY_TYPE_WATCH,
- 4, idp->data, idp->len,
- nickname, strlen(nickname),
- mode, sizeof(mode),
+ 5, idp->data, idp->len,
+ nickname, nickname ? strlen(nickname) : 0,
+ mode, sizeof(mode),
type != SILC_NOTIFY_TYPE_NONE ?
- n : NULL, sizeof(n));
+ n : NULL, sizeof(n),
+ pkp ? pkp->data : NULL,
+ pkp ? pkp->len : 0);
silc_buffer_free(idp);
+ silc_buffer_free(pkp);
}
/* Sends notify message destined to specific entity. */
va_start(ap, argc);
packet = silc_notify_payload_encode(type, argc, ap);
- silc_server_packet_send_dest(server, sock, SILC_PACKET_NOTIFY,
+ silc_server_packet_send_dest(server, sock, SILC_PACKET_NOTIFY,
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);
}
-/* Sends notify message to a channel. The notify message sent is
+/* Sends notify message to a channel. The notify message sent is
distributed to all clients on the channel. If `route_notify' is TRUE
then the notify may be routed to primary route or to some other routers.
If FALSE it is assured that the notify is sent only locally. If `sender'
SilcSocketConnection sender,
SilcChannelEntry channel,
bool route_notify,
+ bool send_to_clients,
SilcNotifyType type,
SilcUInt32 argc, ...)
{
va_start(ap, argc);
packet = silc_notify_payload_encode(type, argc, ap);
- silc_server_packet_send_to_channel(server, sender, channel,
+ silc_server_packet_send_to_channel(server, sender, channel,
SILC_PACKET_NOTIFY, route_notify,
+ send_to_clients,
packet->data, packet->len, FALSE);
silc_buffer_free(packet);
va_end(ap);
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);
silc_hash_table_list(channel->user_list, &htl2);
while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
c = chl2->client;
-
+
if (sender && c == sender)
continue;
break;
if (k < routed_count)
continue;
-
+
/* Get data used in packet header encryption, keys and stuff. */
sock = (SilcSocketConnection)c->router->connection;
idata = (SilcIDListData)c->router;
/* Send the packet */
silc_server_packet_send_to_channel_real(server, sock, &packetdata,
- idata->send_key,
- idata->hmac_send,
- idata->psn_send++,
- data, data_len, FALSE,
+ idata->send_key,
+ idata->hmac_send,
+ idata->psn_send++,
+ data, data_len, FALSE,
force_send);
-
+
silc_free(packetdata.dst_id);
/* We want to make sure that the packet is routed to same router
/* Send to locally connected client */
if (c) {
-
+
/* Get data used in packet header encryption, keys and stuff. */
sock = (SilcSocketConnection)c->connection;
idata = (SilcIDListData)c;
-
+
if (!sock)
continue;
/* Send the packet */
silc_server_packet_send_to_channel_real(server, sock, &packetdata,
- idata->send_key,
- idata->hmac_send,
- idata->psn_send++,
- data, data_len, FALSE,
+ idata->send_key,
+ idata->hmac_send,
+ idata->psn_send++,
+ data, data_len, FALSE,
force_send);
silc_free(packetdata.dst_id);
/* Make sure that we send the notify only once per client. */
- sent_clients = silc_realloc(sent_clients, sizeof(*sent_clients) *
+ sent_clients = silc_realloc(sent_clients, sizeof(*sent_clients) *
(sent_clients_count + 1));
sent_clients[sent_clients_count++] = c;
}
/* Sends New ID Payload to remote end. The packet is used to distribute
information about new registered clients, servers, channel etc. usually
- to routers so that they can keep these information up to date.
+ to routers so that they can keep these information up to date.
If the argument `broadcast' is TRUE then the packet is sent as
broadcast packet. */
void silc_server_send_new_id(SilcServer server,
SilcSocketConnection sock,
bool broadcast,
- void *id, SilcIdType id_type,
+ void *id, SilcIdType id_type,
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,
+ 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);
}
/* Send New Channel Payload to notify about newly created channel in the
- SILC network. Router uses this to notify other routers in the network
+ SILC network. Router uses this to notify other routers in the network
about new channel. This packet is broadcasted by router. */
void silc_server_send_new_channel(SilcServer server,
SilcSocketConnection sock,
bool broadcast,
char *channel_name,
- void *channel_id,
+ void *channel_id,
SilcUInt32 channel_id_len,
SilcUInt32 mode)
{
unsigned char *cid;
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)
packet = silc_channel_payload_encode(channel_name, name_len,
cid, channel_id_len, mode);
- silc_server_packet_send(server, sock, SILC_PACKET_NEW_CHANNEL,
- broadcast ? SILC_PACKET_FLAG_BROADCAST : 0,
+ silc_server_packet_send(server, sock, SILC_PACKET_NEW_CHANNEL,
+ 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);
}
sends this to the local server who sent the join command in case where
the channel did not exist yet. Both normal and router servers uses this
also to send this to locally connected clients on the channel. This
- must not be broadcasted packet. Routers do not send this to each other.
+ must not be broadcasted packet. Routers do not send this to each other.
If `sender is provided then the packet is not sent to that connection since
it originally came from it. */
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_server_packet_send_to_channel(server, sender, channel,
SILC_PACKET_CHANNEL_KEY,
- route, packet->data, packet->len,
+ route, TRUE, packet->data, packet->len,
FALSE);
silc_buffer_free(packet);
silc_free(chid);
/* Generic function to send any command. The arguments must be sent already
encoded into correct form in correct order. */
-void silc_server_send_command(SilcServer server,
+void silc_server_send_command(SilcServer server,
SilcSocketConnection sock,
- SilcCommand command,
+ SilcCommand command,
SilcUInt16 ident,
SilcUInt32 argc, ...)
{
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);
}
/* Generic function to send any command reply. The arguments must be sent
already encoded into correct form in correct order. */
-void silc_server_send_command_reply(SilcServer server,
+void silc_server_send_command_reply(SilcServer server,
SilcSocketConnection sock,
- SilcCommand command,
+ SilcCommand command,
SilcStatus status,
SilcStatus error,
SilcUInt16 ident,
/* Generic function to send any command reply. The arguments must be sent
already encoded into correct form in correct order. */
-void silc_server_send_dest_command_reply(SilcServer server,
+void silc_server_send_dest_command_reply(SilcServer server,
SilcSocketConnection sock,
void *dst_id,
SilcIdType dst_id_type,
- SilcCommand command,
+ SilcCommand command,
SilcStatus status,
SilcStatus error,
SilcUInt16 ident,
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);
+ dst_id, dst_id_type, packet->data,
+ packet->len, FALSE);
silc_buffer_free(packet);
va_end(ap);
}
{
const SilcBufferStruct p;
- silc_buffer_push(packet->buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len
+ 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)) {
/* Re-encrypt packet */
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
+ 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. */
}
/* Purge the outgoing packet queue to the network if there is data. This
- function can be used to empty the packet queue. It is guaranteed that
- after this function returns the outgoing data queue is empty. */
+ function can be used to empty the packet queue. */
void silc_server_packet_queue_purge(SilcServer server,
SilcSocketConnection sock)
{
- if (sock && SILC_IS_OUTBUF_PENDING(sock) &&
- (SILC_IS_DISCONNECTED(sock) == FALSE)) {
+ if (sock && SILC_IS_OUTBUF_PENDING(sock) &&
+ !(SILC_IS_DISCONNECTING(sock)) && !(SILC_IS_DISCONNECTED(sock))) {
+ int ret;
+
+ SILC_LOG_DEBUG(("Purging outgoing queue"));
+
server->stat.packets_sent++;
- silc_packet_send(sock, TRUE);
+ ret = silc_packet_send(sock, TRUE);
+ if (ret == -2) {
+ if (sock->outbuf && sock->outbuf->len > 0) {
+ /* Couldn't send all data, put the queue back up, we'll send
+ rest later. */
+ SILC_LOG_DEBUG(("Could not purge immediately, sending rest later"));
+ SILC_SET_CONNECTION_FOR_OUTPUT(server->schedule, sock->sock);
+ SILC_SET_OUTBUF_PENDING(sock);
+ return;
+ }
+ } else if (ret == -1) {
+ SILC_LOG_ERROR(("Error purging packet queue, packets dropped"));
+ }
+
+ /* Purged all data */
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);
+}