updates.
[silc.git] / lib / silccore / silcchannel.c
index 54409ec1ae1e9b792a2084b7202bd63b3750ac70..1dc0f438b6250116926a12280e3c88994ff9f8fa 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 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
@@ -17,7 +17,8 @@
   GNU General Public License for more details.
 
 */
-/* Channel Payload and Channel Key Payload implementations. */
+/* Channel Payload, Channel Message Payload and Channel Key Payload 
+   implementations. */
 /* $Id$ */
 
 #include "silcincludes.h"
 
 /******************************************************************************
 
-                          Channel Message Payload
+                              Channel Payload
 
 ******************************************************************************/
 
 /* Channel Message Payload structure. Contents of this structure is parsed
    from SILC packets. */
 struct SilcChannelPayloadStruct {
+  unsigned short name_len;
+  unsigned char *channel_name;
+  unsigned short id_len;
+  unsigned char *channel_id;
+  unsigned int mode;
+};
+
+/* Parses channel payload returning new channel payload structure. */
+
+SilcChannelPayload silc_channel_payload_parse(SilcBuffer buffer)
+{
+  SilcChannelPayload new;
+  int ret;
+
+  SILC_LOG_DEBUG(("Parsing channel payload"));
+
+  new = silc_calloc(1, sizeof(*new));
+
+  /* Parse the Channel Payload. Ignore the padding. */
+  ret = silc_buffer_unformat(buffer,
+                            SILC_STR_UI16_NSTRING_ALLOC(&new->channel_name, 
+                                                        &new->name_len),
+                            SILC_STR_UI16_NSTRING_ALLOC(&new->channel_id, 
+                                                        &new->id_len),
+                            SILC_STR_UI_INT(&new->mode),
+                            SILC_STR_END);
+  if (ret == -1)
+    goto err;
+
+  if ((new->name_len < 1 || new->name_len > buffer->len) ||
+      (new->id_len < 1 || new->id_len > buffer->len)) {
+    SILC_LOG_ERROR(("Incorrect channel payload in packet, packet dropped"));
+    goto err;
+  }
+
+  return new;
+
+ err:
+  silc_channel_payload_free(new);
+  return NULL;
+}
+
+/* Parses list of channel payloads returning list of payloads. */
+
+SilcDList silc_channel_payload_parse_list(SilcBuffer buffer)
+{
+  SilcDList list;
+  SilcChannelPayload new;
+  int len, ret;
+
+  SILC_LOG_DEBUG(("Parsing channel payload list"));
+
+  list = silc_dlist_init();
+
+  while (buffer->len) {
+    new = silc_calloc(1, sizeof(*new));
+    ret = silc_buffer_unformat(buffer,
+                              SILC_STR_UI16_NSTRING_ALLOC(&new->channel_name, 
+                                                          &new->name_len),
+                              SILC_STR_UI16_NSTRING_ALLOC(&new->channel_id, 
+                                                          &new->id_len),
+                              SILC_STR_UI_INT(&new->mode),
+                              SILC_STR_END);
+    if (ret == -1)
+      goto err;
+
+    if ((new->name_len < 1 || new->name_len > buffer->len) ||
+       (new->id_len < 1 || new->id_len > buffer->len)) {
+      SILC_LOG_ERROR(("Incorrect channel payload in packet, packet dropped"));
+      goto err;
+    }
+
+    len = 2 + new->name_len + 2 + new->id_len + 4;
+    if (buffer->len < len)
+      break;
+    silc_buffer_pull(buffer, len);
+
+    silc_dlist_add(list, new);
+  }
+  
+  return list;
+
+ err:
+  silc_channel_payload_list_free(list);
+  return NULL;
+}
+
+/* Encode new channel payload and returns it as buffer. */
+
+SilcBuffer silc_channel_payload_encode(unsigned char *channel_name,
+                                      unsigned short channel_name_len,
+                                      unsigned char *channel_id,
+                                      unsigned int channel_id_len,
+                                      unsigned int mode)
+{
+  SilcBuffer buffer;
+
+  SILC_LOG_DEBUG(("Encoding message payload"));
+
+  buffer = silc_buffer_alloc(2 + channel_name_len + 2 + channel_id_len + 4);
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+
+  /* Encode the Channel Payload */
+  silc_buffer_format(buffer, 
+                    SILC_STR_UI_SHORT(channel_name_len),
+                    SILC_STR_UI_XNSTRING(channel_name, channel_name_len),
+                    SILC_STR_UI_SHORT(channel_id_len),
+                    SILC_STR_UI_XNSTRING(channel_id, channel_id_len),
+                    SILC_STR_UI_INT(mode),
+                    SILC_STR_END);
+
+  return buffer;
+}
+
+/* Free's Channel Payload */
+
+void silc_channel_payload_free(SilcChannelPayload payload)
+{
+  silc_free(payload->channel_name);
+  silc_free(payload->channel_id);
+  silc_free(payload);
+}
+
+/* Free's list of Channel Payloads */
+
+void silc_channel_payload_list_free(SilcDList list)
+{
+  SilcChannelPayload entry;
+
+  silc_dlist_start(list);
+  while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
+    silc_free(entry->channel_name);
+    silc_free(entry->channel_id);
+    silc_free(entry);
+    silc_dlist_del(list, entry);
+  }
+
+  silc_dlist_uninit(list);
+}
+
+/* Return the channel name */
+
+unsigned char *silc_channel_get_name(SilcChannelPayload payload,
+                                    unsigned int *channel_name_len)
+{
+  if (channel_name_len)
+    *channel_name_len = payload->name_len;
+
+  return payload->channel_name;
+}
+
+/* Return the channel ID */
+
+unsigned char *silc_channel_get_id(SilcChannelPayload payload,
+                                  unsigned int *channel_id_len)
+{
+  if (channel_id_len)
+    *channel_id_len = payload->id_len;
+
+  return payload->channel_id;
+}
+
+/* Return the channel ID as parsed ID. */
+
+SilcChannelID *silc_channel_get_id_parse(SilcChannelPayload payload)
+{
+  return silc_id_str2id(payload->channel_id, payload->id_len,
+                       SILC_ID_CHANNEL);
+}
+
+/* Return the mode. The mode is arbitrary. It can be the mode of the
+   channel or perhaps the mode of the client on the channel.  The protocol
+   dictates what the usage of the mode is in different circumstances. */
+
+unsigned int silc_channel_get_mode(SilcChannelPayload payload)
+{
+  return payload->mode;
+}
+
+/******************************************************************************
+
+                          Channel Message Payload
+
+******************************************************************************/
+
+/* Channel Message Payload structure. Contents of this structure is parsed
+   from SILC packets. */
+struct SilcChannelMessagePayloadStruct {
+  unsigned short flags;
   unsigned short data_len;
   unsigned char *data;
   unsigned char *mac;
   unsigned char *iv;
 };
 
-/* Parses channel payload returning new channel payload structure. This
-   also decrypts it and checks the MAC. */
+/* Decrypts the channel message payload. */
 
-SilcChannelPayload silc_channel_payload_parse(SilcBuffer buffer,
-                                             SilcCipher cipher,
-                                             SilcHmac hmac)
+int silc_channel_message_payload_decrypt(unsigned char *data,
+                                        size_t data_len,
+                                        SilcCipher cipher,
+                                        SilcHmac hmac)
 {
-  SilcChannelPayload new;
-  int ret;
   unsigned int iv_len, mac_len;
-  unsigned char *mac, mac2[32];
-
-  SILC_LOG_DEBUG(("Parsing channel payload"));
+  unsigned char *end, *mac, mac2[32];
 
   /* Decrypt the channel message. First push the IV out of the packet.
      The IV is used in the decryption process. Then decrypt the message.
      After decyprtion, take the MAC from the decrypted packet, compute MAC
      and compare the MACs.  If they match, the decryption was successfull
      and we have the channel message ready to be displayed. */
+  end = data + data_len;
 
-  /* Push the IV out of the packet (it will be in buffer->tail) */
+  /* Push the IV out of the packet */
   iv_len = silc_cipher_get_block_len(cipher);
-  silc_buffer_push_tail(buffer, iv_len);
 
   /* Decrypt the channel message */
-  silc_cipher_decrypt(cipher, buffer->data, buffer->data,
-                     buffer->len, buffer->tail);
+  silc_cipher_decrypt(cipher, data, data, data_len - iv_len, (end - iv_len));
 
   /* Take the MAC */
-  mac_len = silc_hmac_len(hmac);
-  silc_buffer_push_tail(buffer, mac_len);
-  mac = buffer->tail;
-
-  /* Check the MAC of the message */
-  SILC_LOG_DEBUG(("Checking channel message MACs"));
-  silc_hmac_make(hmac, buffer->data, buffer->len, mac2, &mac_len);
-  if (memcmp(mac, mac2, mac_len)) {
-    SILC_LOG_DEBUG(("Channel message MACs does not match"));
-    return NULL;
+  if (hmac) {
+    mac_len = silc_hmac_len(hmac);
+    mac = (end - iv_len - mac_len);
+
+    /* Check the MAC of the message */
+    SILC_LOG_DEBUG(("Checking channel message MACs"));
+    silc_hmac_make(hmac, data, (data_len - iv_len - mac_len), mac2, &mac_len);
+    if (memcmp(mac, mac2, mac_len)) {
+      SILC_LOG_DEBUG(("Channel message MACs does not match"));
+      return FALSE;
+    }
+    SILC_LOG_DEBUG(("MAC is Ok"));
   }
-  SILC_LOG_DEBUG(("MAC is Ok"));
-  silc_buffer_pull_tail(buffer, iv_len + mac_len);
+
+  return TRUE;
+}
+
+/* Parses channel message payload returning new channel payload structure.
+   This also decrypts it and checks the MAC. */
+
+SilcChannelMessagePayload 
+silc_channel_message_payload_parse(SilcBuffer buffer,
+                                  SilcCipher cipher,
+                                  SilcHmac hmac)
+{
+  SilcChannelMessagePayload new;
+  int ret;
+  unsigned int iv_len, mac_len;
+
+  SILC_LOG_DEBUG(("Parsing channel message payload"));
+
+  /* Decrypt the payload */
+  ret = silc_channel_message_payload_decrypt(buffer->data, buffer->len,
+                                    cipher, hmac);
+  if (ret == FALSE)
+    return NULL;
+
+  iv_len = silc_cipher_get_block_len(cipher);
+  mac_len = silc_hmac_len(hmac);
 
   new = silc_calloc(1, sizeof(*new));
 
-  /* Parse the Channel Payload. Ignore the padding. */
+  /* Parse the Channel Message Payload. Ignore the padding. */
   ret = silc_buffer_unformat(buffer,
-                            SILC_STR_UI16_NSTRING(&new->data, 
-                                                  &new->data_len),
+                            SILC_STR_UI_SHORT(&new->flags),
+                            SILC_STR_UI16_NSTRING_ALLOC(&new->data, 
+                                                        &new->data_len),
                             SILC_STR_UI16_NSTRING(NULL, NULL),
                             SILC_STR_UI_XNSTRING(&new->mac, mac_len),
                             SILC_STR_UI_XNSTRING(&new->iv, iv_len),
@@ -95,29 +307,30 @@ SilcChannelPayload silc_channel_payload_parse(SilcBuffer buffer,
     goto err;
 
   if (new->data_len < 1 || new->data_len > buffer->len) {
-    SILC_LOG_ERROR(("Incorrect channel payload in packet, packet dropped"));
+    SILC_LOG_ERROR(("Incorrect channel messaeg payload in packet, "
+                   "packet dropped"));
     goto err;
   }
 
   return new;
 
  err:
-  silc_free(new);
+  silc_channel_message_payload_free(new);
   return NULL;
 }
 
-/* Encodes channel payload into a buffer and returns it. This is used 
-   to add channel payload into a packet. As the channel payload is
+/* Encodes channel message payload into a buffer and returns it. This is used 
+   to add channel message payload into a packet. As the channel payload is
    encrypted separately from other parts of the packet padding must
    be applied to the payload. */
 
-SilcBuffer silc_channel_payload_encode(unsigned short data_len,
-                                      unsigned char *data,
-                                      unsigned short iv_len,
-                                      unsigned char *iv,
-                                      SilcCipher cipher,
-                                      SilcHmac hmac,
-                                      SilcRng rng)
+SilcBuffer silc_channel_message_payload_encode(unsigned short flags,
+                                              unsigned short data_len,
+                                              unsigned char *data,
+                                              unsigned short iv_len,
+                                              unsigned char *iv,
+                                              SilcCipher cipher,
+                                              SilcHmac hmac)
 {
   int i;
   SilcBuffer buffer;
@@ -125,12 +338,12 @@ SilcBuffer silc_channel_payload_encode(unsigned short data_len,
   unsigned char pad[SILC_PACKET_MAX_PADLEN];
   unsigned char mac[32];
 
-  SILC_LOG_DEBUG(("Encoding channel payload"));
+  SILC_LOG_DEBUG(("Encoding channel message payload"));
 
   /* Calculate length of padding. IV is not included into the calculation
      since it is not encrypted. */
   mac_len = silc_hmac_len(hmac);
-  len = 4 + data_len + mac_len;
+  len = 6 + data_len + mac_len;
   pad_len = SILC_PACKET_PADLEN((len + 2));
 
   /* Allocate channel payload buffer */
@@ -138,11 +351,12 @@ SilcBuffer silc_channel_payload_encode(unsigned short data_len,
   buffer = silc_buffer_alloc(len);
 
   /* Generate padding */
-  for (i = 0; i < pad_len; i++) pad[i] = silc_rng_get_byte(rng);
+  for (i = 0; i < pad_len; i++) pad[i] = silc_rng_global_get_byte();
 
-  /* Encode the Channel Payload */
-  silc_buffer_pull_tail(buffer, 4 + data_len + pad_len);
+  /* Encode the Channel Message Payload */
+  silc_buffer_pull_tail(buffer, 6 + data_len + pad_len);
   silc_buffer_format(buffer, 
+                    SILC_STR_UI_SHORT(flags),
                     SILC_STR_UI_SHORT(data_len),
                     SILC_STR_UI_XNSTRING(data, data_len),
                     SILC_STR_UI_SHORT(pad_len),
@@ -154,12 +368,12 @@ SilcBuffer silc_channel_payload_encode(unsigned short data_len,
 
   /* Put rest of the data to the payload */
   silc_buffer_pull_tail(buffer, mac_len + iv_len);
-  silc_buffer_pull(buffer, 4 + data_len + pad_len);
+  silc_buffer_pull(buffer, 6 + data_len + pad_len);
   silc_buffer_format(buffer, 
                     SILC_STR_UI_XNSTRING(mac, mac_len),
                     SILC_STR_UI_XNSTRING(iv, iv_len),
                     SILC_STR_END);
-  silc_buffer_push(buffer, 4 + data_len + pad_len);
+  silc_buffer_push(buffer, 6 + data_len + pad_len);
 
   /* Encrypt payload of the packet. This is encrypted with the channel key. */
   silc_cipher_encrypt(cipher, buffer->data, buffer->data, 
@@ -171,18 +385,29 @@ SilcBuffer silc_channel_payload_encode(unsigned short data_len,
   return buffer;
 }
 
-/* Free's Channel Payload */
+/* Free's Channel Message Payload */
 
-void silc_channel_payload_free(SilcChannelPayload payload)
+void silc_channel_message_payload_free(SilcChannelMessagePayload payload)
 {
-  if (payload)
-    silc_free(payload);
+  if (payload->data) {
+    memset(payload->data, 0, payload->data_len);
+    silc_free(payload->data);
+  }
+  silc_free(payload);
+}
+
+/* Return flags */
+
+unsigned short 
+silc_channel_message_get_flags(SilcChannelMessagePayload payload)
+{
+  return payload->flags;
 }
 
 /* Return data */
 
-unsigned char *silc_channel_get_data(SilcChannelPayload payload,
-                                    unsigned int *data_len)
+unsigned char *silc_channel_message_get_data(SilcChannelMessagePayload payload,
+                                            unsigned int *data_len)
 {
   if (data_len)
     *data_len = payload->data_len;
@@ -192,14 +417,14 @@ unsigned char *silc_channel_get_data(SilcChannelPayload payload,
 
 /* Return MAC. The caller knows the length of the MAC */
 
-unsigned char *silc_channel_get_mac(SilcChannelPayload payload)
+unsigned char *silc_channel_mesage_get_mac(SilcChannelMessagePayload payload)
 {
   return payload->mac;
 }
 
 /* Return IV. The caller knows the length of the IV */
 
-unsigned char *silc_channel_get_iv(SilcChannelPayload payload)
+unsigned char *silc_channel_message_get_iv(SilcChannelMessagePayload payload)
 {
   return payload->iv;
 }
@@ -301,10 +526,8 @@ SilcBuffer silc_channel_key_payload_encode(unsigned short id_len,
 void silc_channel_key_payload_free(SilcChannelKeyPayload payload)
 {
   if (payload) {
-    if (payload->id)
-      silc_free(payload->id);
-    if (payload->cipher)
-      silc_free(payload->cipher);
+    silc_free(payload->id);
+    silc_free(payload->cipher);
     if (payload->key) {
       memset(payload->key, 0, payload->key_len);
       silc_free(payload->key);