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);
- dst_id_len = silc_id_get_len(dst_id_type);
+ dst_id_len = silc_id_get_len(dst_id, dst_id_type);
}
/* 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_SERVER_LEN;
+ packetdata.src_id_len = silc_id_get_len(server->id, server->id_type);
packetdata.src_id_type = server->id_type;
packetdata.dst_id = dst_id_data;
packetdata.dst_id_len = dst_id_len;
SilcIdType dst_id_type,
unsigned char *data,
uint32 data_len,
- int force_send)
+ bool force_send)
{
SilcPacketContext packetdata;
SilcIDListData idata;
if (dst_id) {
dst_id_data = silc_id_id2str(dst_id, dst_id_type);
- dst_id_len = silc_id_get_len(dst_id_type);
+ dst_id_len = silc_id_get_len(dst_id, dst_id_type);
}
if (src_id) {
src_id_data = silc_id_id2str(src_id, src_id_type);
- src_id_len = silc_id_get_len(src_id_type);
+ src_id_len = silc_id_get_len(src_id, src_id_type);
}
/* Set the packet context pointers */
/* If the packet is originated from our primary route we are
not allowed to send the packet. */
id = silc_id_str2id(packet->src_id, packet->src_id_len, packet->src_id_type);
- if (id && SILC_ID_SERVER_COMPARE(id, server->router->id)) {
+ if (id && !SILC_ID_SERVER_COMPARE(id, server->router->id)) {
idata = (SilcIDListData)sock->user_data;
silc_buffer_push(buffer, buffer->data - buffer->head);
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.flags = 0;
packetdata.type = type;
packetdata.src_id = silc_id_id2str(server->id, SILC_ID_SERVER);
- packetdata.src_id_len = SILC_ID_SERVER_LEN;
+ 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_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;
/* 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.
SilcChannelEntry channel,
void *sender,
SilcIdType sender_type,
+ 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 = silc_id_id2str(sender, sender_type);
- packetdata.src_id_len = silc_id_get_len(sender_type);
+ 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_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) {
/* If sender is one on the channel do not send it the packet. */
- if (!found && !SILC_ID_CLIENT_COMPARE(client->id, sender)) {
+ if (!found && sender_type == SILC_ID_CLIENT &&
+ SILC_ID_CLIENT_COMPARE(client->id, sender)) {
found = TRUE;
continue;
}
/* Sender maybe server as well so we want to make sure that
we won't send the message to the server it came from. */
- if (!found && !SILC_ID_SERVER_COMPARE(client->router->id, sender)) {
+ if (!found && SILC_ID_SERVER_COMPARE(client->router->id, sender)) {
found = TRUE;
continue;
}
sock = (SilcSocketConnection)client->router->connection;
idata = (SilcIDListData)client->router;
+ /* Do not send to the sender. Check first whether the true
+ sender's router is same as this client's router. Also check
+ if the sender socket is the same as this client's router
+ socket. */
+ if (sender_entry &&
+ ((SilcClientEntry)sender_entry)->router == client->router)
+ continue;
if (sender_sock && sock == sender_sock)
continue;
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 */
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
SilcSocketConnection sock,
int broadcast,
SilcChannelID *old_id,
- SilcChannelID *new_id,
- uint32 id_len)
+ SilcChannelID *new_id)
{
SilcBuffer idp1, idp2;
SilcSocketConnection sock,
int broadcast,
SilcClientID *old_id,
- SilcClientID *new_id,
- uint32 id_len)
+ SilcClientID *new_id)
{
SilcBuffer idp1, idp2;
SilcSocketConnection sock,
int broadcast,
SilcChannelEntry channel,
- SilcClientID *client_id,
- uint32 client_id_len)
+ SilcClientID *client_id)
{
SilcBuffer idp1, idp2;
SilcSocketConnection sock,
int broadcast,
SilcChannelEntry channel,
- SilcClientID *client_id,
- uint32 client_id_len)
+ SilcClientID *client_id)
{
SilcBuffer idp;
SilcChannelEntry channel,
uint32 mode_mask,
void *id, SilcIdType id_type,
- uint32 id_len,
char *cipher, char *hmac)
{
SilcBuffer idp;
int broadcast,
SilcChannelEntry channel,
uint32 mode_mask,
- SilcClientID *client_id,
- uint32 client_id_len,
- SilcClientID *target,
- uint32 target_len)
+ void *id, SilcIdType id_type,
+ SilcClientID *target)
{
SilcBuffer idp1, idp2;
unsigned char mode[4];
- idp1 = silc_id_payload_encode((void *)client_id, SILC_ID_CLIENT);
+ 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);
SilcSocketConnection sock,
int broadcast,
SilcClientID *client_id,
- uint32 client_id_len,
char *message)
{
SilcBuffer idp;
int broadcast,
SilcChannelEntry channel,
SilcClientID *client_id,
- uint32 client_id_len,
char *topic)
{
SilcBuffer idp;
int broadcast,
SilcChannelEntry channel,
SilcClientID *client_id,
- uint32 client_id_len,
char *comment)
{
SilcBuffer idp;
SilcSocketConnection sock,
int broadcast,
SilcClientID *client_id,
- uint32 client_id_len,
char *comment)
{
SilcBuffer idp;
SilcSocketConnection sock,
int broadcast,
SilcClientID *client_id,
- uint32 client_id_len,
uint32 mode_mask)
{
SilcBuffer idp;
int broadcast,
SilcChannelEntry channel,
SilcClientID *client_id,
- uint32 client_id_len,
char *add, char *del)
{
SilcBuffer idp, idp2;
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.flags = 0;
packetdata.type = SILC_PACKET_NOTIFY;
packetdata.src_id = silc_id_id2str(server->id, SILC_ID_SERVER);
- packetdata.src_id_len = SILC_ID_SERVER_LEN;
+ 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)
idata = (SilcIDListData)c->router;
packetdata.dst_id = silc_id_id2str(c->router->id, SILC_ID_SERVER);
- packetdata.dst_id_len = SILC_ID_SERVER_LEN;
+ packetdata.dst_id_len = silc_id_get_len(c->router->id, SILC_ID_SERVER);
packetdata.dst_id_type = SILC_ID_SERVER;
packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN +
packetdata.src_id_len + packetdata.dst_id_len;
idata = (SilcIDListData)c;
packetdata.dst_id = silc_id_id2str(c->id, SILC_ID_CLIENT);
- packetdata.dst_id_len = SILC_ID_CLIENT_LEN;
+ packetdata.dst_id_len = silc_id_get_len(c->id, SILC_ID_CLIENT);
packetdata.dst_id_type = SILC_ID_CLIENT;
packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN +
packetdata.src_id_len + packetdata.dst_id_len;
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);
}
/* Encode channel key packet */
tmp_len = strlen(channel->channel_key->cipher->name);
- packet = silc_channel_key_payload_encode(SILC_ID_CHANNEL_LEN, chid, tmp_len,
+ packet = silc_channel_key_payload_encode(silc_id_get_len(channel->id,
+ SILC_ID_CHANNEL),
+ 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);
+ }
+}