typedef struct {
SilcSchedule schedule; /* The scheduler */
SilcPacketEngine engine; /* Packet engine */
- SilcBufferStruct inbuf; /* Data input buffer */
+ SilcDList inbufs; /* Data inbut buffer list */
SilcUInt32 stream_count; /* Number of streams using this */
} *SilcPacketEngineContext;
SilcPacketRemoteUDP remote_udp; /* UDP remote stream tuple, or NULL */
void *stream_context; /* Stream context */
SilcBufferStruct outbuf; /* Out buffer */
+ SilcBuffer inbuf; /* Inbuf from inbuf list or NULL */
SilcCipher send_key[2]; /* Sending key */
SilcHmac send_hmac[2]; /* Sending HMAC */
SilcCipher receive_key[2]; /* Receiving key */
static inline SilcBool silc_packet_stream_read(SilcPacketStream ps,
SilcPacketStream *ret_ps)
{
- SilcStream stream;
+ SilcStream stream = ps->stream;
SilcBuffer inbuf;
SilcBool connected;
int ret;
- stream = ps->stream;
- inbuf = &ps->sc->inbuf;
+ /* Get inbuf. If there is already some data for this stream in the buffer
+ we already have it. Otherwise get the current one from list, it will
+ include the data. */
+ inbuf = ps->inbuf;
+ if (!inbuf) {
+ silc_dlist_start(ps->sc->inbufs);
+ inbuf = silc_dlist_get(ps->sc->inbufs);
+ if (!inbuf) {
+ /* Allocate new data input buffer */
+ inbuf = silc_buffer_alloc(SILC_PACKET_DEFAULT_SIZE * 31);
+ if (!inbuf) {
+ silc_mutex_unlock(ps->lock);
+ return FALSE;
+ }
+ silc_buffer_reset(inbuf);
+ silc_dlist_add(ps->sc->inbufs, inbuf);
+ }
+ }
/* Make sure there is enough room to read */
if (SILC_PACKET_DEFAULT_SIZE * 2 > silc_buffer_taillen(inbuf))
void *user_context)
{
SilcPacketEngineContext sc = context;
- silc_buffer_clear(&sc->inbuf);
- silc_buffer_purge(&sc->inbuf);
+ SilcBuffer buffer;
+
+ silc_dlist_start(sc->inbufs);
+ while ((buffer = silc_dlist_get(sc->inbufs))) {
+ silc_buffer_clear(buffer);
+ silc_buffer_free(buffer);
+ silc_dlist_del(sc->inbufs, buffer);
+ }
+
+ silc_dlist_uninit(sc->inbufs);
silc_free(sc);
}
void silc_packet_engine_stop(SilcPacketEngine engine)
{
+ SilcPacket packet;
SILC_LOG_DEBUG(("Stopping packet engine"));
if (!engine)
return;
- /* XXX */
+ /* Free packet free list */
+ silc_list_start(engine->packet_pool);
+ while ((packet = silc_list_get(engine->packet_pool))) {
+ silc_buffer_purge(&packet->buffer);
+ silc_free(packet);
+ }
+ silc_hash_table_free(engine->contexts);
+ silc_mutex_free(engine->lock);
silc_free(engine);
}
SilcStream stream)
{
SilcPacketStream ps;
+ SilcBuffer inbuf;
void *tmp;
SILC_LOG_DEBUG(("Creating new packet stream"));
ps->sc->schedule = schedule;
/* Allocate data input buffer */
- tmp = silc_malloc(SILC_PACKET_DEFAULT_SIZE * 31);
- if (!tmp) {
+ inbuf = silc_buffer_alloc(SILC_PACKET_DEFAULT_SIZE * 31);
+ if (!inbuf) {
+ silc_free(ps->sc);
+ ps->sc = NULL;
+ silc_packet_stream_destroy(ps);
+ silc_mutex_unlock(engine->lock);
+ return NULL;
+ }
+ silc_buffer_reset(inbuf);
+
+ ps->sc->inbufs = silc_dlist_init();
+ if (!ps->sc->inbufs) {
+ silc_buffer_free(inbuf);
silc_free(ps->sc);
ps->sc = NULL;
silc_packet_stream_destroy(ps);
silc_mutex_unlock(engine->lock);
return NULL;
}
- silc_buffer_set(&ps->sc->inbuf, tmp, SILC_PACKET_DEFAULT_SIZE * 31);
- silc_buffer_reset(&ps->sc->inbuf);
+ silc_dlist_add(ps->sc->inbufs, inbuf);
/* Add to per scheduler context hash table */
if (!silc_hash_table_add(engine->contexts, schedule, ps->sc)) {
- silc_buffer_purge(&ps->sc->inbuf);
+ silc_buffer_free(inbuf);
+ silc_dlist_del(ps->sc->inbufs, inbuf);
silc_free(ps->sc);
ps->sc = NULL;
silc_packet_stream_destroy(ps);
void silc_packet_stream_destroy(SilcPacketStream stream)
{
+ SilcPacketEngine engine;
+
if (!stream)
return;
if (!stream->udp) {
/* Delete from engine */
- silc_mutex_lock(stream->sc->engine->lock);
- silc_list_del(stream->sc->engine->streams, stream);
+ engine = stream->sc->engine;
+ silc_mutex_lock(engine->lock);
+ silc_list_del(engine->streams, stream);
/* Remove per scheduler context, if it is not used anymore */
if (stream->sc) {
stream->sc->stream_count--;
if (!stream->sc->stream_count)
- silc_hash_table_del(stream->sc->engine->contexts,
- stream->sc->schedule);
+ silc_hash_table_del(engine->contexts, stream->sc->schedule);
}
- silc_mutex_unlock(stream->sc->engine->lock);
+ silc_mutex_unlock(engine->lock);
/* Destroy the underlaying stream */
if (stream->stream)
} else {
/* Delete from UDP remote hash table */
char tuple[64];
- silc_snprintf(tuple, sizeof(tuple), "%d%s", stream->remote_udp->remote_port,
- stream->remote_udp->remote_ip);
- silc_mutex_lock(stream->sc->engine->lock);
- silc_hash_table_del(stream->sc->engine->udp_remote, tuple);
- silc_mutex_unlock(stream->sc->engine->lock);
+ engine = stream->sc->engine;
+ silc_snprintf(tuple, sizeof(tuple), "%d%s",
+ stream->remote_udp->remote_port,
+ stream->remote_udp->remote_ip);
+ silc_mutex_lock(engine->lock);
+ silc_hash_table_del(engine->udp_remote, tuple);
+ silc_mutex_unlock(engine->lock);
silc_free(stream->remote_udp->remote_ip);
silc_free(stream->remote_udp);
silc_dlist_uninit(stream->process);
}
- /* XXX */
+ /* Destroy ciphers and HMACs */
+ if (stream->send_key[0])
+ silc_cipher_free(stream->send_key[0]);
+ if (stream->receive_key[0])
+ silc_cipher_free(stream->receive_key[0]);
+ if (stream->send_hmac[0])
+ silc_hmac_free(stream->send_hmac[0]);
+ if (stream->receive_hmac[0])
+ silc_hmac_free(stream->receive_hmac[0]);
+ if (stream->send_key[1])
+ silc_cipher_free(stream->send_key[1]);
+ if (stream->receive_key[1])
+ silc_cipher_free(stream->receive_key[1]);
+ if (stream->send_hmac[1])
+ silc_hmac_free(stream->send_hmac[1]);
+ if (stream->receive_hmac[1])
+ silc_hmac_free(stream->receive_hmac[1]);
+
+ /* Free IDs */
+ silc_free(stream->src_id);
+ silc_free(stream->dst_id);
silc_atomic_uninit8(&stream->refcnt);
silc_mutex_free(stream->lock);
} else {
if (stream->send_key[0] && send_key)
silc_cipher_free(stream->send_key[0]);
- if (stream->send_key[1] && receive_key)
+ if (stream->receive_key[0] && receive_key)
silc_cipher_free(stream->receive_key[0]);
if (stream->send_hmac[0] && send_hmac)
silc_hmac_free(stream->send_hmac[0]);
unsigned char *ret_iv)
{
unsigned char *iv = silc_cipher_get_iv(cipher);
- SilcUInt32 pc;
+ SilcUInt32 pc1, pc2;
- /* Increment packet counter */
- SILC_GET32_MSB(pc, iv + 8);
- pc++;
- SILC_PUT32_MSB(pc, iv + 8);
+ /* Increment 64-bit packet counter */
+ SILC_GET32_MSB(pc1, iv + 4);
+ SILC_GET32_MSB(pc2, iv + 8);
+ if (++pc2 == 0)
+ ++pc1;
+ SILC_PUT32_MSB(pc1, iv + 4);
+ SILC_PUT32_MSB(pc2, iv + 8);
/* Reset block counter */
memset(iv + 12, 0, 4);
ret_iv[1] = ret_iv[0] + iv[4];
ret_iv[2] = ret_iv[0] ^ ret_iv[1];
ret_iv[3] = ret_iv[0] + ret_iv[2];
- SILC_PUT32_MSB(pc, ret_iv + 4);
+ SILC_PUT32_MSB(pc2, ret_iv + 4);
SILC_LOG_HEXDUMP(("IV"), ret_iv, 8);
/* Set new nonce to counter block */
/* Encrypt the packet */
if (silc_likely(cipher)) {
SILC_LOG_DEBUG(("Encrypting packet"));
+ silc_cipher_set_iv(cipher, NULL);
if (silc_unlikely(!silc_cipher_encrypt(cipher, packet.data + ivlen,
packet.data + ivlen, enclen,
NULL))) {
unsigned char *iv,
unsigned char *packet_iv)
{
- SilcUInt32 pc;
+ SilcUInt32 pc1, pc2;
/* If IV Included flag, set the IV from packet to block counter. */
if (stream->iv_included) {
memcpy(iv + 4, packet_iv, 8);
} else {
- /* Increment packet counter */
- SILC_GET32_MSB(pc, iv + 8);
- pc++;
- SILC_PUT32_MSB(pc, iv + 8);
+ /* Increment 64-bit packet counter. */
+ SILC_GET32_MSB(pc1, iv + 4);
+ SILC_GET32_MSB(pc2, iv + 8);
+ if (++pc2 == 0)
+ ++pc1;
+ SILC_PUT32_MSB(pc1, iv + 4);
+ SILC_PUT32_MSB(pc2, iv + 8);
}
/* Reset block counter */
static void silc_packet_read_process(SilcPacketStream stream)
{
- SilcBuffer inbuf = &stream->sc->inbuf;
+ SilcBuffer inbuf;
SilcCipher cipher;
SilcHmac hmac;
SilcPacket packet;
SilcBool normal;
int ret;
+ /* Get inbuf. If there is already some data for this stream in the buffer
+ we already have it. Otherwise get the current one from list, it will
+ include the data. */
+ inbuf = stream->inbuf;
+ if (!inbuf) {
+ silc_dlist_start(stream->sc->inbufs);
+ inbuf = silc_dlist_get(stream->sc->inbufs);
+ }
+
/* Parse the packets from the data */
while (silc_buffer_len(inbuf) > 0) {
ivlen = psnlen = 0;
(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"));
+ silc_dlist_del(stream->sc->inbufs, inbuf);
+ stream->inbuf = inbuf;
return;
}
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;
+ goto out;
}
}
} else {
silc_packet_receive_ctr_increment(stream, iv, NULL);
}
- silc_cipher_decrypt(cipher, inbuf->data + ivlen, tmp,
- block_len, iv);
+ if (silc_cipher_get_mode(cipher) == SILC_CIPHER_MODE_CTR)
+ silc_cipher_set_iv(cipher, NULL);
+ silc_cipher_decrypt(cipher, inbuf->data + ivlen, tmp, block_len, iv);
header = tmp;
if (stream->iv_included) {
SILC_PACKET_CALLBACK_ERROR(stream, SILC_PACKET_ERR_MALFORMED);
silc_mutex_lock(stream->lock);
memset(tmp, 0, sizeof(tmp));
- silc_buffer_reset(inbuf);
- return;
+ goto out;
}
if (silc_buffer_len(inbuf) < paddedlen + ivlen + mac_len) {
"(%d bytes)",
paddedlen + mac_len - silc_buffer_len(inbuf)));
memset(tmp, 0, sizeof(tmp));
+ silc_dlist_del(stream->sc->inbufs, inbuf);
+ stream->inbuf = inbuf;
return;
}
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;
+ goto out;
}
/* Get packet */
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;
+ goto out;
}
packet->stream = stream;
silc_mutex_lock(stream->lock);
silc_packet_free(packet);
memset(tmp, 0, sizeof(tmp));
- silc_buffer_reset(inbuf);
- return;
+ goto out;
}
}
silc_mutex_lock(stream->lock);
silc_packet_free(packet);
memset(tmp, 0, sizeof(tmp));
- return;
+ goto out;
}
stream->receive_psn++;
silc_mutex_lock(stream->lock);
silc_packet_free(packet);
memset(tmp, 0, sizeof(tmp));
- return;
+ goto out;
}
/* Dispatch the packet to application */
break;
}
+ out:
+ /* Add inbuf back to free list, if we owned it. */
+ if (stream->inbuf) {
+ silc_dlist_add(stream->sc->inbufs, inbuf);
+ stream->inbuf = NULL;
+ }
+
silc_buffer_reset(inbuf);
}
if (read_more && !pws->blocking) {
/* More data will be available (in blocking mode not supported). */
silc_buffer_pull(&packet->buffer, len);
- silc_list_insert(pws->in_queue, NULL, packet);
+ silc_list_insert(pws->in_queue, NULL, packet);
silc_schedule_task_add_timeout(pws->stream->sc->schedule,
silc_packet_wrap_read_more, pws, 0, 0);
pws->read_more = TRUE;
}
} else {
/* Non-blocking mode */
- if (!silc_mutex_alloc(&pws->lock)) {
- silc_free(pws);
- return NULL;
- }
-
+ silc_mutex_alloc(&pws->lock);
silc_list_init(pws->in_queue, struct SilcPacketStruct, next);
}