+ /* Send to processor */
+ if (!p->types) {
+ /* Send all packet types */
+ SILC_LOG_DEBUG(("Dispatching packet to %p callbacks", p->callbacks));
+ silc_mutex_unlock(stream->lock);
+ if (p->callbacks->packet_receive(stream->sc->engine, stream, packet,
+ p->callback_context,
+ stream->stream_context)) {
+ silc_mutex_lock(stream->lock);
+ return stream->destroyed == FALSE;
+ }
+ silc_mutex_lock(stream->lock);
+ } else {
+ /* Send specific types */
+ for (pt = p->types; *pt; pt++) {
+ if (*pt != packet->type)
+ continue;
+ SILC_LOG_DEBUG(("Dispatching packet to %p callbacks", p->callbacks));
+ silc_mutex_unlock(stream->lock);
+ if (p->callbacks->packet_receive(stream->sc->engine, stream, packet,
+ p->callback_context,
+ stream->stream_context)) {
+ silc_mutex_lock(stream->lock);
+ return stream->destroyed == FALSE;
+ }
+ silc_mutex_lock(stream->lock);
+ break;
+ }
+ }
+ }
+
+ if (!default_sent) {
+ /* Send to default processor as it has not been sent yet */
+ SILC_LOG_DEBUG(("Dispatching packet to default callbacks"));
+ silc_mutex_unlock(stream->lock);
+ if (stream->sc->engine->callbacks->
+ packet_receive(stream->sc->engine, stream, packet,
+ stream->sc->engine->callback_context,
+ stream->stream_context)) {
+ silc_mutex_lock(stream->lock);
+ return stream->destroyed == FALSE;
+ }
+ silc_mutex_lock(stream->lock);
+ }
+
+ /* If we got here, no one wanted the packet, so drop it */
+ silc_packet_free(packet);
+ return stream->destroyed == FALSE;
+}
+
+/* Process incoming data and parse packets. Called with stream->lock
+ locked. */
+
+static void silc_packet_read_process(SilcPacketStream stream)
+{
+ SilcBuffer inbuf = &stream->sc->inbuf;
+ SilcCipher cipher;
+ SilcHmac hmac;
+ SilcPacket packet;
+ SilcUInt8 sid;
+ SilcUInt16 packetlen;
+ SilcUInt32 paddedlen, mac_len, block_len, ivlen, psnlen;
+ unsigned char tmp[SILC_PACKET_MIN_HEADER_LEN], *header;
+ unsigned char iv[SILC_CIPHER_MAX_IV_SIZE], *packet_seq = NULL;
+ SilcBool normal;
+ int ret;
+
+ /* Parse the packets from the data */
+ while (silc_buffer_len(inbuf) > 0) {
+ ivlen = psnlen = 0;
+ cipher = stream->receive_key[0];
+ hmac = stream->receive_hmac[0];
+ normal = FALSE;
+
+ if (silc_unlikely(silc_buffer_len(inbuf) <
+ (stream->iv_included ? SILC_PACKET_MIN_HEADER_LEN_IV :
+ SILC_PACKET_MIN_HEADER_LEN))) {
+ SILC_LOG_DEBUG(("Partial packet in queue, waiting for the rest"));
+ return;
+ }
+
+ if (silc_likely(hmac))
+ mac_len = silc_hmac_len(hmac);
+ else
+ mac_len = 0;
+
+ /* Decrypt first block of the packet to get the length field out */
+ if (silc_likely(cipher)) {
+ block_len = silc_cipher_get_block_len(cipher);
+
+ if (stream->iv_included) {
+ /* SID, IV and sequence number is included in the ciphertext */
+ sid = (SilcUInt8)inbuf->data[0];
+
+ if (silc_cipher_get_mode(cipher) == SILC_CIPHER_MODE_CTR) {
+ /* Set the CTR mode IV from packet to counter block */
+ memcpy(iv, silc_cipher_get_iv(cipher), block_len);
+ silc_packet_receive_ctr_increment(stream, iv, inbuf->data + 1);
+ ivlen = 8 + 1;
+ } else {
+ /* Get IV from packet */
+ memcpy(iv, inbuf->data + 1, block_len);
+ ivlen = block_len + 1;
+ }
+ psnlen = 4;
+
+ /* Check SID, and get correct decryption key */
+ if (sid != stream->sid) {
+ /* If SID is recent get the previous key and use it */
+ if (sid > 0 && stream->sid > 0 && stream->sid - 1 == sid &&
+ stream->receive_key[1] && !stream->receive_hmac[1]) {
+ cipher = stream->receive_key[1];
+ hmac = stream->receive_hmac[1];
+ } else {
+ /* The SID is unknown, drop rest of the data in buffer */
+ SILC_LOG_DEBUG(("Unknown Security ID %d in packet, expected %d",
+ sid, stream->sid));
+ silc_mutex_unlock(stream->lock);
+ SILC_PACKET_CALLBACK_ERROR(stream, SILC_PACKET_ERR_UNKNOWN_SID);
+ silc_mutex_lock(stream->lock);
+ silc_buffer_reset(inbuf);
+ return;
+ }
+ }
+ } else {
+ memcpy(iv, silc_cipher_get_iv(cipher), block_len);
+
+ /* If using CTR mode, increment the counter */
+ if (silc_cipher_get_mode(cipher) == SILC_CIPHER_MODE_CTR)
+ silc_packet_receive_ctr_increment(stream, iv, NULL);
+ }
+
+ silc_cipher_decrypt(cipher, inbuf->data + ivlen, tmp,
+ block_len, iv);
+
+ header = tmp;
+ if (stream->iv_included) {
+ /* Take sequence number from packet */
+ packet_seq = header;
+ header += 4;
+ }
+ } else {
+ /* Unencrypted packet */
+ block_len = SILC_PACKET_MIN_HEADER_LEN;
+ header = inbuf->data;
+ }
+
+ /* Get packet length and full packet length with padding */
+ SILC_PACKET_LENGTH(header, packetlen, paddedlen);
+
+ /* Sanity checks */
+ if (silc_unlikely(packetlen < SILC_PACKET_MIN_LEN)) {
+ if (!stream->udp && !silc_socket_stream_is_udp(stream->stream, NULL))
+ SILC_LOG_ERROR(("Received too short packet"));
+ silc_mutex_unlock(stream->lock);
+ SILC_PACKET_CALLBACK_ERROR(stream, SILC_PACKET_ERR_MALFORMED);
+ silc_mutex_lock(stream->lock);
+ memset(tmp, 0, sizeof(tmp));
+ silc_buffer_reset(inbuf);
+ return;
+ }
+
+ if (silc_buffer_len(inbuf) < paddedlen + ivlen + mac_len) {
+ SILC_LOG_DEBUG(("Received partial packet, waiting for the rest "
+ "(%d bytes)",
+ paddedlen + mac_len - silc_buffer_len(inbuf)));
+ memset(tmp, 0, sizeof(tmp));
+ return;
+ }
+
+ /* Check MAC of the packet */
+ if (silc_unlikely(!silc_packet_check_mac(hmac, inbuf->data,
+ paddedlen + ivlen,
+ inbuf->data + ivlen +
+ paddedlen, packet_seq,
+ stream->receive_psn))) {
+ silc_mutex_unlock(stream->lock);
+ SILC_PACKET_CALLBACK_ERROR(stream, SILC_PACKET_ERR_MAC_FAILED);
+ silc_mutex_lock(stream->lock);
+ memset(tmp, 0, sizeof(tmp));
+ silc_buffer_reset(inbuf);
+ return;
+ }
+
+ /* Get packet */
+ packet = silc_packet_alloc(stream->sc->engine);
+ if (silc_unlikely(!packet)) {
+ silc_mutex_unlock(stream->lock);
+ SILC_PACKET_CALLBACK_ERROR(stream, SILC_PACKET_ERR_NO_MEMORY);
+ silc_mutex_lock(stream->lock);
+ memset(tmp, 0, sizeof(tmp));
+ silc_buffer_reset(inbuf);
+ return;
+ }
+ packet->stream = stream;
+
+ /* Allocate more space to packet buffer, if needed */
+ if (silc_unlikely(silc_buffer_truelen(&packet->buffer) < paddedlen)) {
+ if (!silc_buffer_realloc(&packet->buffer,
+ silc_buffer_truelen(&packet->buffer) +
+ (paddedlen -
+ silc_buffer_truelen(&packet->buffer)))) {
+ silc_mutex_unlock(stream->lock);
+ SILC_PACKET_CALLBACK_ERROR(stream, SILC_PACKET_ERR_NO_MEMORY);
+ silc_mutex_lock(stream->lock);
+ silc_packet_free(packet);
+ memset(tmp, 0, sizeof(tmp));
+ silc_buffer_reset(inbuf);
+ return;
+ }
+ }
+
+ /* Parse packet header */
+ packet->flags = (SilcPacketFlags)header[2];
+ packet->type = (SilcPacketType)header[3];
+
+ if (stream->sc->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 + ivlen + mac_len),
+ inbuf->data, paddedlen + ivlen + 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 - psnlen);
+ silc_buffer_pull(&packet->buffer, block_len - psnlen);
+ silc_buffer_put(&packet->buffer, (inbuf->data + ivlen +
+ psnlen + (block_len - psnlen)),
+ paddedlen - ivlen - psnlen - (block_len - psnlen));
+ if (silc_likely(cipher)) {
+ silc_cipher_set_iv(cipher, iv);
+ ret = silc_packet_decrypt(cipher, hmac, stream->receive_psn,
+ &packet->buffer, normal);
+ if (silc_unlikely(ret < 0)) {
+ silc_mutex_unlock(stream->lock);
+ SILC_PACKET_CALLBACK_ERROR(stream, SILC_PACKET_ERR_DECRYPTION_FAILED);
+ silc_mutex_lock(stream->lock);
+ 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(inbuf, paddedlen + mac_len);
+
+ /* Parse the packet */
+ if (silc_unlikely(!silc_packet_parse(packet))) {
+ silc_mutex_unlock(stream->lock);
+ SILC_PACKET_CALLBACK_ERROR(stream, SILC_PACKET_ERR_MALFORMED);
+ silc_mutex_lock(stream->lock);
+ silc_packet_free(packet);
+ memset(tmp, 0, sizeof(tmp));
+ return;
+ }
+
+ /* Dispatch the packet to application */
+ if (!silc_packet_dispatch(packet))
+ break;