+static void silc_packet_read_process(SilcPacketStream stream)
+{
+ SilcPacket packet;
+ SilcUInt16 packetlen;
+ SilcUInt32 paddedlen, mac_len, block_len;
+ unsigned char tmp[SILC_PACKET_MIN_HEADER_LEN], *header;
+ unsigned char iv[SILC_CIPHER_MAX_IV_SIZE];
+ SilcBool normal = TRUE;
+ int ret;
+
+ /* Parse the packets from the data */
+ while (silc_buffer_len(&stream->inbuf) > 0) {
+
+ if (silc_buffer_len(&stream->inbuf) < SILC_PACKET_MIN_HEADER_LEN) {
+ SILC_LOG_DEBUG(("Partial packet in queue, waiting for the rest"));
+ return;
+ }
+
+ if (stream->receive_hmac)
+ mac_len = silc_hmac_len(stream->receive_hmac);
+ else
+ mac_len = 0;
+
+ /* Decrypt first block of the packet to get the length field out */
+ if (stream->receive_key) {
+ block_len = silc_cipher_get_block_len(stream->receive_key);
+ memcpy(iv, silc_cipher_get_iv(stream->receive_key), block_len);
+ silc_cipher_decrypt(stream->receive_key, stream->inbuf.data,
+ tmp, block_len, iv);
+ header = tmp;
+ } else {
+ block_len = SILC_PACKET_MIN_HEADER_LEN;
+ header = stream->inbuf.data;
+ }
+
+ /* Get packet length and full packet length with padding */
+ SILC_PACKET_LENGTH(header, packetlen, paddedlen);
+
+ /* Sanity checks */
+ if (packetlen < SILC_PACKET_MIN_LEN) {
+ SILC_LOG_ERROR(("Received too short packet"));
+ SILC_PACKET_CALLBACK_ERROR(stream, SILC_PACKET_ERR_MALFORMED);
+ memset(tmp, 0, sizeof(tmp));
+ silc_buffer_reset(&stream->inbuf);
+ return;
+ }
+
+ if (silc_buffer_len(&stream->inbuf) < paddedlen + mac_len) {
+ SILC_LOG_DEBUG(("Received partial packet, waiting for the rest "
+ "(%d bytes)",
+ paddedlen + mac_len - silc_buffer_len(&stream->inbuf)));
+ memset(tmp, 0, sizeof(tmp));
+ return;
+ }
+
+ /* Check MAC of the packet */
+ if (!silc_packet_check_mac(stream->receive_hmac, stream->inbuf.data,
+ paddedlen, stream->inbuf.data + paddedlen,
+ stream->receive_psn)) {
+ SILC_PACKET_CALLBACK_ERROR(stream, SILC_PACKET_ERR_MAC_FAILED);
+ memset(tmp, 0, sizeof(tmp));
+ silc_buffer_reset(&stream->inbuf);
+ return;
+ }
+
+ /* Get packet */
+ packet = silc_packet_alloc(stream->engine);
+ if (!packet) {
+ SILC_PACKET_CALLBACK_ERROR(stream, SILC_PACKET_ERR_NO_MEMORY);
+ memset(tmp, 0, sizeof(tmp));
+ silc_buffer_reset(&stream->inbuf);
+ return;
+ }
+
+ /* Allocate more space to packet buffer, if needed */
+ if (silc_buffer_truelen(&packet->buffer) < paddedlen) {
+ if (!silc_buffer_realloc(&packet->buffer,
+ silc_buffer_truelen(&packet->buffer) +
+ (paddedlen -
+ silc_buffer_truelen(&packet->buffer)))) {
+ SILC_PACKET_CALLBACK_ERROR(stream, SILC_PACKET_ERR_NO_MEMORY);
+ silc_packet_free(packet);
+ memset(tmp, 0, sizeof(tmp));
+ silc_buffer_reset(&stream->inbuf);
+ return;
+ }
+ }
+
+ /* Parse packet header */
+ packet->flags = (SilcPacketFlags)header[2];
+ packet->type = (SilcPacketType)header[3];
+
+ if (stream->engine->local_is_router) {
+ if (packet->type == SILC_PACKET_PRIVATE_MESSAGE &&
+ (packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY))
+ normal = FALSE;
+ else if (packet->type != SILC_PACKET_CHANNEL_MESSAGE ||
+ (packet->type == SILC_PACKET_CHANNEL_MESSAGE &&
+ stream->is_router == TRUE))
+ normal = TRUE;
+ } else {
+ if (packet->type == SILC_PACKET_PRIVATE_MESSAGE &&
+ (packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY))
+ normal = FALSE;
+ else if (packet->type != SILC_PACKET_CHANNEL_MESSAGE)
+ normal = TRUE;
+ }
+
+ SILC_LOG_HEXDUMP(("Incoming packet (%d) len %d",
+ stream->receive_psn, paddedlen + mac_len),
+ stream->inbuf.data, paddedlen + mac_len);
+
+ /* Put the decrypted part, and rest of the encrypted data, and decrypt */
+ silc_buffer_pull_tail(&packet->buffer, paddedlen);
+ silc_buffer_put(&packet->buffer, header, block_len);
+ silc_buffer_pull(&packet->buffer, block_len);
+ silc_buffer_put(&packet->buffer, stream->inbuf.data + block_len,
+ paddedlen - block_len);
+ if (stream->receive_key) {
+ silc_cipher_set_iv(stream->receive_key, iv);
+ ret = silc_packet_decrypt(stream->receive_key, stream->receive_hmac,
+ stream->receive_psn, &packet->buffer, normal);
+ if (ret < 0) {
+ SILC_PACKET_CALLBACK_ERROR(stream, SILC_PACKET_ERR_DECRYPTION_FAILED);
+ silc_packet_free(packet);
+ memset(tmp, 0, sizeof(tmp));
+ return;
+ }
+
+ stream->receive_psn++;
+ }
+ silc_buffer_push(&packet->buffer, block_len);
+
+ /* Pull the packet from inbuf thus we'll get the next one in the inbuf. */
+ silc_buffer_pull(&stream->inbuf, paddedlen + mac_len);
+
+ /* Dispatch the packet to application */
+ packet->stream = stream;
+ silc_packet_dispatch(packet);
+ }