packet_send.c
- Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+ Author: Pekka Riikonen <priikone@silcnet.org>
Copyright (C) 1997 - 2001 Pekka Riikonen
int silc_server_packet_send_real(SilcServer server,
SilcSocketConnection sock,
- int force_send)
+ bool force_send)
{
int ret;
+ /* If disconnecting, ignore the data */
+ if (SILC_IS_DISCONNECTING(sock))
+ return -1;
+
/* If rekey protocol is active we must assure that all packets are
sent through packet queue. */
if (SILC_SERVER_IS_REKEY(sock))
force_send = FALSE;
+ /* If outbound data is already pending do not force send */
+ if (SILC_IS_OUTBUF_PENDING(sock))
+ force_send = FALSE;
+
/* Send the packet */
ret = silc_packet_send(sock, force_send);
if (ret != -2)
This call sets the connection both for input and output (the input
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(sock->sock);
+ SILC_SET_CONNECTION_FOR_OUTPUT(server->schedule, sock->sock);
/* Mark to socket that data is pending in outgoing buffer. This flag
is needed if new data is added to the buffer before the earlier
SilcPacketFlags flags,
unsigned char *data,
uint32 data_len,
- int force_send)
+ bool force_send)
{
void *dst_id = NULL;
SilcIdType dst_id_type = SILC_ID_NONE;
+ SilcIDListData idata = (SilcIDListData)sock->user_data;
if (!sock)
return;
+ /* If disconnecting, ignore the data */
+ if (SILC_IS_DISCONNECTING(sock))
+ return;
+
+ /* If entry is disabled do not sent anything. */
+ if (idata && idata->status & SILC_IDLIST_STATUS_DISABLED)
+ return;
+
/* Get data used in the packet sending, keys and stuff */
switch(sock->type) {
case SILC_SOCKET_TYPE_CLIENT:
- dst_id = ((SilcClientEntry)sock->user_data)->id;
- dst_id_type = SILC_ID_CLIENT;
+ if (sock->user_data) {
+ dst_id = ((SilcClientEntry)sock->user_data)->id;
+ dst_id_type = SILC_ID_CLIENT;
+ }
break;
case SILC_SOCKET_TYPE_SERVER:
case SILC_SOCKET_TYPE_ROUTER:
- dst_id = ((SilcServerEntry)sock->user_data)->id;
- dst_id_type = SILC_ID_SERVER;
+ if (sock->user_data) {
+ dst_id = ((SilcServerEntry)sock->user_data)->id;
+ dst_id_type = SILC_ID_SERVER;
+ }
break;
default:
break;
SilcIdType dst_id_type,
unsigned char *data,
uint32 data_len,
- int force_send)
+ bool force_send)
{
SilcPacketContext packetdata;
- SilcIDListData idata;
+ SilcIDListData idata = (SilcIDListData)sock->user_data;
SilcCipher cipher = NULL;
SilcHmac hmac = NULL;
unsigned char *dst_id_data = NULL;
uint32 dst_id_len = 0;
- SILC_LOG_DEBUG(("Sending packet, type %d", type));
+ /* If disconnecting, ignore the data */
+ if (SILC_IS_DISCONNECTING(sock))
+ return;
- /* Get data used in the packet sending, keys and stuff */
- idata = (SilcIDListData)sock->user_data;
+ /* If entry is disabled do not sent anything. */
+ if (idata && idata->status & SILC_IDLIST_STATUS_DISABLED)
+ return;
+
+ SILC_LOG_DEBUG(("Sending packet, type %d", type));
if (dst_id) {
dst_id_data = silc_id_id2str(dst_id, dst_id_type);
SilcIdType dst_id_type,
unsigned char *data,
uint32 data_len,
- int force_send)
+ bool force_send)
{
SilcPacketContext packetdata;
SilcIDListData idata;
unsigned char *data,
uint32 data_len,
int channel_message,
- int force_send)
+ bool force_send)
{
packet->truelen = data_len + SILC_PACKET_HEADER_LEN +
packet->src_id_len + packet->dst_id_len;
packet->buffer = sock->outbuf;
/* Put the data to buffer, assemble and encrypt the packet. The packet
- is encrypted with normal session key shared with the client. */
+ is encrypted with normal session key shared with the client, unless
+ the `channel_message' is TRUE. */
silc_buffer_put(sock->outbuf, data, data_len);
silc_packet_assemble(packet);
if (channel_message)
SilcSocketConnection sender,
SilcChannelEntry channel,
SilcPacketType type,
- unsigned char route,
+ bool route,
unsigned char *data,
uint32 data_len,
- int force_send)
+ bool force_send)
{
SilcSocketConnection sock = NULL;
SilcPacketContext packetdata;
SilcClientEntry client = NULL;
SilcServerEntry *routed = NULL;
SilcChannelClientEntry chl;
+ SilcHashTableList htl;
SilcIDListData idata;
uint32 routed_count = 0;
/* This doesn't send channel message packets */
- if (type == SILC_PACKET_CHANNEL_MESSAGE)
- return;
+ assert(type != SILC_PACKET_CHANNEL_MESSAGE);
SILC_LOG_DEBUG(("Sending packet to channel"));
packetdata.src_id_len = silc_id_get_len(server->id, SILC_ID_SERVER);
packetdata.src_id_type = SILC_ID_SERVER;
packetdata.dst_id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
- packetdata.dst_id_len = SILC_ID_CHANNEL_LEN;
- packetdata.dst_id_type = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
+ packetdata.dst_id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
+ packetdata.dst_id_type = SILC_ID_CHANNEL;
packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN +
packetdata.src_id_len + packetdata.dst_id_len;
packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
/* If there are global users in the channel we will send the message
first to our router for further routing. */
- if (route && server->server_type == SILC_SERVER && !server->standalone &&
+ if (route && server->server_type != SILC_ROUTER && !server->standalone &&
channel->global_users) {
SilcServerEntry router;
}
/* Send the message to clients on the channel's client list. */
- silc_list_start(channel->user_list);
- while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+ silc_hash_table_list(channel->user_list, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
client = chl->client;
/* If client has router set it is not locally connected client and
then we'll need to encrypt it with the channel key. This is called
from the silc_server_packet_relay_to_channel. */
-static void
+static bool
silc_server_packet_relay_to_channel_encrypt(SilcServer server,
SilcSocketConnection sock,
SilcChannelEntry channel,
channel->channel_key) {
SilcBuffer chp;
uint32 iv_len, i;
- uint16 data_len, flags;
+ uint16 dlen, flags;
iv_len = silc_cipher_get_block_len(channel->channel_key);
if (channel->iv[0] == '\0')
/* Encode new payload. This encrypts it also. */
SILC_GET16_MSB(flags, data);
- SILC_GET16_MSB(data_len, data + 2);
- chp = silc_channel_message_payload_encode(flags, data_len,
- data + 4,
+ SILC_GET16_MSB(dlen, data + 2);
+
+ if (dlen > data_len) {
+ SILC_LOG_WARNING(("Corrupted channel message, cannot relay it"));
+ return FALSE;
+ }
+
+ chp = silc_channel_message_payload_encode(flags, dlen, data + 4,
iv_len, channel->iv,
channel->channel_key,
channel->hmac);
memcpy(data, chp->data, chp->len);
silc_buffer_free(chp);
}
+
+ return TRUE;
}
/* This routine is explicitly used to relay messages to some channel.
void *sender_entry,
unsigned char *data,
uint32 data_len,
- int force_send)
+ bool force_send)
{
bool found = FALSE;
SilcSocketConnection sock = NULL;
SilcChannelClientEntry chl;
uint32 routed_count = 0;
SilcIDListData idata;
+ SilcHashTableList htl;
SILC_LOG_DEBUG(("Relaying packet to channel"));
+ /* 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
+ don't have a single local user on the channel. */
+ if (!silc_server_packet_relay_to_channel_encrypt(server, sender_sock,
+ channel, data,
+ data_len))
+ return;
+
/* Set the packet context pointers. */
packetdata.flags = 0;
packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
packetdata.src_id_len = silc_id_get_len(sender, sender_type);
packetdata.src_id_type = sender_type;
packetdata.dst_id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
- packetdata.dst_id_len = SILC_ID_CHANNEL_LEN;
- packetdata.dst_id_type = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
+ packetdata.dst_id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
+ packetdata.dst_id_type = SILC_ID_CHANNEL;
packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
packetdata.src_id_len +
packetdata.dst_id_len));
- /* 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
- don't have a single local user on the channel. */
- silc_server_packet_relay_to_channel_encrypt(server, sender_sock,
- channel, data,
- data_len);
-
/* If there are global users in the channel we will send the message
first to our router for further routing. */
- if (server->server_type == SILC_SERVER && !server->standalone &&
+ if (server->server_type != SILC_ROUTER && !server->standalone &&
channel->global_users) {
SilcServerEntry router;
}
/* Send the message to clients on the channel's client list. */
- silc_list_start(channel->user_list);
- while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+ silc_hash_table_list(channel->user_list, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
client = chl->client;
if (client) {
channel keys are cell specific and we have different channel
key than the remote router has. */
if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
+ 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 */
memcpy(tmp, data, data_len);
/* Decrypt the channel message (we don't check the MAC) */
- /* XXX this could be optimized and removed all together by
- taking a copy of the original data before encrypting it
- and thus would not required decrypting. */
if (channel->channel_key &&
!silc_channel_message_payload_decrypt(tmp, data_len,
channel->channel_key,
SilcPacketFlags flags,
unsigned char *data,
uint32 data_len,
- int force_send)
+ bool force_send)
{
SilcChannelClientEntry chl;
+ SilcHashTableList htl;
SilcSocketConnection sock = NULL;
SILC_LOG_DEBUG(("Start"));
/* Send the message to clients on the channel's client list. */
- silc_list_start(channel->user_list);
- while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+ 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;
SilcBuffer buffer = packet->buffer;
/* Re-encrypt and send if private messge key does not exist */
- if ((packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY) == FALSE) {
+ if (!(packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY)) {
silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len
+ packet->dst_id_len + packet->padlen);
silc_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)
+ silc_server_backup_send(server, NULL, SILC_PACKET_NOTIFY, 0,
+ packet->data, packet->len, FALSE, TRUE);
+
silc_buffer_free(packet);
+ va_end(ap);
}
/* Sends notify message and gets the arguments from the `args' Argument
dest_id, dest_id_type,
packet->data, packet->len, FALSE);
silc_buffer_free(packet);
+ va_end(ap);
}
/* Sends notify message to a channel. The notify message sent is
SILC_PACKET_NOTIFY, route_notify,
packet->data, packet->len, FALSE);
silc_buffer_free(packet);
+ va_end(ap);
}
/* Send notify message to all channels the client has joined. It is quaranteed
uint32 sent_clients_count = 0;
SilcServerEntry *routed = NULL;
uint32 routed_count = 0;
+ SilcHashTableList htl, htl2;
SilcChannelEntry channel;
SilcChannelClientEntry chl, chl2;
SilcIDListData idata;
SilcBuffer packet;
unsigned char *data;
uint32 data_len;
- int force_send = FALSE;
+ bool force_send = FALSE;
va_list ap;
SILC_LOG_DEBUG(("Start"));
- if (!silc_list_count(client->channels))
+ if (!silc_hash_table_count(client->channels))
return;
va_start(ap, argc);
packetdata.src_id_len = silc_id_get_len(server->id, SILC_ID_SERVER);
packetdata.src_id_type = SILC_ID_SERVER;
- silc_list_start(client->channels);
- while ((chl = silc_list_get(client->channels)) != SILC_LIST_END) {
+ silc_hash_table_list(client->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
channel = chl->channel;
/* Send the message to all clients on the channel's client list. */
- silc_list_start(channel->user_list);
- while ((chl2 = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+ silc_hash_table_list(channel->user_list, &htl2);
+ while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
c = chl2->client;
if (sender && c == sender)
if (sent_clients_count)
silc_free(sent_clients);
silc_free(packetdata.src_id);
+ va_end(ap);
}
/* Sends New ID Payload to remote end. The packet is used to distribute
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);
}
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)
+ silc_server_backup_send(server, NULL, SILC_PACKET_NEW_CHANNEL, 0,
+ packet->data, packet->len, FALSE, TRUE);
+
silc_free(cid);
silc_buffer_free(packet);
}
chid, tmp_len,
channel->channel_key->cipher->name,
channel->key_len / 8, channel->key);
-
silc_server_packet_send_to_channel(server, sender, channel,
SILC_PACKET_CHANNEL_KEY,
route, packet->data, packet->len, FALSE);
void silc_server_send_command(SilcServer server,
SilcSocketConnection sock,
SilcCommand command,
+ uint16 ident,
uint32 argc, ...)
{
SilcBuffer packet;
va_start(ap, argc);
- packet = silc_command_payload_encode_vap(command, 0, argc, ap);
+ 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);
silc_buffer_free(packet);
+ va_end(ap);
}
/* Send the heartbeat packet. */
SilcCipher cipher,
SilcHmac hmac,
SilcPacketContext *packet,
- int force_send)
+ bool force_send)
{
silc_buffer_push(packet->buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len
+ packet->dst_id_len + packet->padlen);
0, packet->data, packet->len, FALSE);
silc_buffer_free(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. */
+
+void silc_server_packet_queue_purge(SilcServer server,
+ SilcSocketConnection sock)
+{
+ if (sock && SILC_IS_OUTBUF_PENDING(sock) &&
+ (SILC_IS_DISCONNECTED(sock) == FALSE)) {
+ server->stat.packets_sent++;
+
+ if (sock->outbuf->data - sock->outbuf->head)
+ silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+
+ silc_packet_send(sock, TRUE);
+
+ SILC_SET_CONNECTION_FOR_INPUT(server->schedule, sock->sock);
+ SILC_UNSET_OUTBUF_PENDING(sock);
+ silc_buffer_clear(sock->outbuf);
+ }
+}