+ SILC_LOG_DEBUG(("Freeing packet %p", packet));
+
+#if defined(SILC_DEBUG)
+ /* Check for double free */
+ assert(packet->stream != NULL);
+#endif /* SILC_DEBUG */
+
+ silc_mutex_lock(stream->engine->lock);
+
+ packet->stream = NULL;
+ packet->src_id = packet->dst_id = NULL;
+ silc_buffer_reset(&packet->buffer);
+
+ /* Put the packet back to freelist */
+ silc_list_add(stream->engine->packet_pool, packet);
+
+ silc_mutex_unlock(stream->engine->lock);
+}
+
+/* Creates streamer */
+
+SilcStream silc_packet_streamer_create(SilcPacketStream stream,
+ SilcPacketType packet_type,
+ SilcPacketFlags packet_flags)
+{
+ /* XXX TODO */
+ return NULL;
+}
+
+/* Destroyes streamer */
+
+void silc_packet_streamer_destroy(SilcStream stream)
+{
+
+}
+
+
+/****************************** Packet Sending ******************************/
+
+/* Prepare outgoing data buffer for packet sending. Returns the
+ pointer to that buffer into the `packet'. */
+
+static SilcBool silc_packet_send_prepare(SilcPacketStream stream,
+ SilcUInt32 totlen,
+ SilcHmac hmac,
+ SilcBuffer packet)
+{
+ unsigned char *oldptr;
+ unsigned int mac_len = hmac ? silc_hmac_len(hmac) : 0;
+
+ totlen += mac_len;
+
+ /* Allocate more space if needed */
+ if (silc_buffer_taillen(&stream->outbuf) < totlen) {
+ if (!silc_buffer_realloc(&stream->outbuf,
+ silc_buffer_truelen(&stream->outbuf) + totlen))
+ return FALSE;
+ }
+
+ /* Pull data area for the new packet, and return pointer to the start of
+ the data area and save the pointer in to the `packet'. MAC is pulled
+ later after it's computed. */
+ oldptr = silc_buffer_pull_tail(&stream->outbuf, totlen);
+ silc_buffer_set(packet, oldptr, totlen);
+ silc_buffer_push_tail(packet, mac_len);
+
+ return TRUE;
+}
+
+/* Internal routine to send packet */
+
+static SilcBool silc_packet_send_raw(SilcPacketStream stream,
+ SilcPacketType type,
+ SilcPacketFlags flags,
+ SilcIdType src_id_type,
+ unsigned char *src_id,
+ SilcUInt32 src_id_len,
+ SilcIdType dst_id_type,
+ unsigned char *dst_id,
+ SilcUInt32 dst_id_len,
+ const unsigned char *data,
+ SilcUInt32 data_len,
+ SilcCipher cipher,
+ SilcHmac hmac)
+{
+ unsigned char tmppad[SILC_PACKET_MAX_PADLEN];
+ int block_len = (cipher ? silc_cipher_get_block_len(cipher) : 0);
+ int i, enclen, truelen, padlen;
+ SilcBufferStruct packet;
+
+ SILC_LOG_DEBUG(("Sending packet %s (%d) flags %d, src %d dst %d,"
+ "data len %d", silc_get_packet_name(type), stream->send_psn,
+ flags, src_id_type, dst_id_type, data_len));
+
+ /* Get the true length of the packet. This is saved as payload length
+ into the packet header. This does not include the length of the
+ padding. */
+ data_len = SILC_PACKET_DATALEN(data_len, (SILC_PACKET_HEADER_LEN +
+ src_id_len + dst_id_len));
+ enclen = truelen = (data_len + SILC_PACKET_HEADER_LEN +
+ src_id_len + dst_id_len);
+
+ /* We automatically figure out the packet structure from the packet
+ type and flags, and calculate correct length. Private messages with
+ private keys and channel messages are special packets as their
+ payload is encrypted already. */
+ if ((type == SILC_PACKET_PRIVATE_MESSAGE &&
+ flags & SILC_PACKET_FLAG_PRIVMSG_KEY) ||
+ type == SILC_PACKET_CHANNEL_MESSAGE) {
+
+ /* Padding is calculated from header + IDs */
+ SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
+ src_id_len +
+ dst_id_len), block_len, padlen);
+
+ /* Length to encrypt, header + IDs + padding. */
+ enclen = SILC_PACKET_HEADER_LEN + src_id_len + dst_id_len + padlen;
+ } else {
+
+ /* Padding is calculated from true length of the packet */
+ if (flags & SILC_PACKET_FLAG_LONG_PAD)
+ SILC_PACKET_PADLEN_MAX(truelen, block_len, padlen);
+ else
+ SILC_PACKET_PADLEN(truelen, block_len, padlen);
+
+ enclen += padlen;
+ }
+
+ /* Remove implementation specific flags */
+ flags &= ~(SILC_PACKET_FLAG_LONG_PAD);
+
+ /* Get random padding */
+ for (i = 0; i < padlen; i++) tmppad[i] =
+ silc_rng_get_byte_fast(stream->engine->rng);
+
+ silc_mutex_lock(stream->lock);
+
+ /* Get packet pointer from the outgoing buffer */
+ if (!silc_packet_send_prepare(stream, truelen + padlen, hmac, &packet)) {
+ silc_mutex_unlock(stream->lock);
+ return FALSE;
+ }
+
+ /* Create the packet. This creates the SILC header, adds padding, and
+ the actual packet data. */
+ i = silc_buffer_format(&packet,
+ SILC_STR_UI_SHORT(truelen),
+ SILC_STR_UI_CHAR(flags),
+ SILC_STR_UI_CHAR(type),
+ SILC_STR_UI_CHAR(padlen),
+ SILC_STR_UI_CHAR(0),
+ SILC_STR_UI_CHAR(src_id_len),
+ SILC_STR_UI_CHAR(dst_id_len),
+ SILC_STR_UI_CHAR(src_id_type),
+ SILC_STR_UI_XNSTRING(src_id, src_id_len),
+ SILC_STR_UI_CHAR(dst_id_type),
+ SILC_STR_UI_XNSTRING(dst_id, dst_id_len),
+ SILC_STR_UI_XNSTRING(tmppad, padlen),
+ SILC_STR_UI_XNSTRING(data, data_len),
+ SILC_STR_END);
+ if (i < 0) {
+ silc_mutex_unlock(stream->lock);
+ return FALSE;
+ }
+
+ SILC_LOG_HEXDUMP(("Assembled packet, len %d", silc_buffer_len(&packet)),
+ packet.data, silc_buffer_len(&packet));
+
+ /* Encrypt the packet */
+ if (cipher)
+ if (!silc_cipher_encrypt(cipher, packet.data, packet.data,
+ enclen, NULL)) {
+ SILC_LOG_ERROR(("Packet encryption failed"));
+ silc_mutex_unlock(stream->lock);
+ return FALSE;
+ }
+
+ /* Compute HMAC */
+ if (hmac) {
+ unsigned char psn[4];
+ SilcUInt32 mac_len;
+
+ /* MAC is computed from the entire encrypted packet data, and put
+ to the end of the packet. */
+ silc_hmac_init(hmac);
+ SILC_PUT32_MSB(stream->send_psn, psn);
+ silc_hmac_update(hmac, psn, 4);
+ silc_hmac_update(hmac, packet.data, silc_buffer_len(&packet));
+ silc_hmac_final(hmac, packet.tail, &mac_len);
+ silc_buffer_pull_tail(&packet, mac_len);
+ stream->send_psn++;
+ }
+
+ /* Write the packet to the stream */
+ while (silc_buffer_len(&stream->outbuf) > 0) {
+ i = silc_stream_write(stream->stream, stream->outbuf.data,
+ silc_buffer_len(&stream->outbuf));
+ if (i == 0) {
+ /* EOS */
+ silc_buffer_reset(&stream->outbuf);
+ silc_mutex_unlock(stream->lock);
+ SILC_PACKET_CALLBACK_EOS(stream);
+ return FALSE;