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);
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]);
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_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);
}