X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilccore%2Fsilcpacket.c;h=5f3c55d092ba0c94c75f41ba55c1d2488bc38fcd;hb=47202578e057ec6a3b43aa4a8205abbb9ba564aa;hp=bea26e774325834672e75954eea541a102513517;hpb=c38ab4e44415abc8e8c754ed8b1c0d86639c5387;p=silc.git diff --git a/lib/silccore/silcpacket.c b/lib/silccore/silcpacket.c index bea26e77..5f3c55d0 100644 --- a/lib/silccore/silcpacket.c +++ b/lib/silccore/silcpacket.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2005 Pekka Riikonen + Copyright (C) 1997 - 2006 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -211,8 +211,8 @@ static void silc_packet_stream_io(SilcStream stream, SilcStreamStatus status, break; case SILC_STREAM_CAN_READ: - /* Packet receiving can only happen in one thread, so locking is not - required in packet receiving procedure. */ + /* Packet receiving can only happen in one thread for one SilcPacketStream, + so locking is not required in packet receiving procedure. */ silc_mutex_unlock(ps->lock); SILC_LOG_DEBUG(("Reading data from stream")); @@ -463,14 +463,14 @@ void silc_packet_stream_set_router(SilcPacketStream stream) stream->is_router = TRUE; } + /* Links `callbacks' to `stream' for specified packet types */ -SilcBool silc_packet_stream_link(SilcPacketStream stream, - SilcPacketCallbacks *callbacks, - void *callback_context, - int priority, ...) +static SilcBool silc_packet_stream_link_va(SilcPacketStream stream, + SilcPacketCallbacks *callbacks, + void *callback_context, + int priority, va_list ap) { - va_list ap; SilcPacketProcess p, e; SilcInt32 packet_type; int i; @@ -510,10 +510,7 @@ SilcBool silc_packet_stream_link(SilcPacketStream stream, if (!e) silc_dlist_add(stream->process, p); - silc_mutex_unlock(stream->lock); - /* Get packet types to process */ - va_start(ap, priority); i = 1; while (1) { packet_type = va_arg(ap, SilcInt32); @@ -525,21 +522,42 @@ SilcBool silc_packet_stream_link(SilcPacketStream stream, break; p->types = silc_realloc(p->types, sizeof(*p->types) * (i + 1)); - if (!p->types) + if (!p->types) { + silc_mutex_unlock(stream->lock); return FALSE; + } p->types[i - 1] = (SilcPacketType)packet_type; i++; } if (p->types) p->types[i - 1] = 0; - va_end(ap); + + silc_mutex_unlock(stream->lock); silc_packet_stream_ref(stream); return TRUE; } +/* Links `callbacks' to `stream' for specified packet types */ + +SilcBool silc_packet_stream_link(SilcPacketStream stream, + SilcPacketCallbacks *callbacks, + void *callback_context, + int priority, ...) +{ + va_list ap; + SilcBool ret; + + va_start(ap, priority); + ret = silc_packet_stream_link_va(stream, callbacks, callback_context, + priority, ap); + va_end(ap); + + return ret; +} + /* Unlinks `callbacks' from `stream'. */ void silc_packet_stream_unlink(SilcPacketStream stream, @@ -886,13 +904,15 @@ static SilcBool silc_packet_send_raw(SilcPacketStream stream, packet.data, silc_buffer_len(&packet)); /* Encrypt the packet */ - if (cipher) + if (cipher) { + SILC_LOG_DEBUG(("Encrypting packet")); 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) { @@ -1378,3 +1398,142 @@ static void silc_packet_read_process(SilcPacketStream stream) silc_buffer_reset(&stream->inbuf); } + + +/****************************** Packet Waiting ******************************/ + +/* Packet wait receive callback */ +static SilcBool +silc_packet_wait_packet_receive(SilcPacketEngine engine, + SilcPacketStream stream, + SilcPacket packet, + void *callback_context, + void *stream_context); + +/* Packet waiting callbacks */ +static SilcPacketCallbacks silc_packet_wait_cbs = +{ + silc_packet_wait_packet_receive, NULL, NULL +}; + +/* Packet waiting context */ +typedef struct { + SilcMutex wait_lock; + SilcCond wait_cond; + SilcList packet_queue; + SilcBool waiting; +} *SilcPacketWait; + +/* Packet wait receive callback */ + +static SilcBool +silc_packet_wait_packet_receive(SilcPacketEngine engine, + SilcPacketStream stream, + SilcPacket packet, + void *callback_context, + void *stream_context) +{ + SilcPacketWait pw = callback_context; + + /* Signal the waiting thread for a new packet */ + silc_mutex_lock(pw->wait_lock); + + if (!pw->waiting) { + silc_mutex_unlock(pw->wait_lock); + return FALSE; + } + + silc_list_add(pw->packet_queue, packet); + silc_cond_broadcast(pw->wait_cond); + + silc_mutex_unlock(pw->wait_lock); + + return TRUE; +} + +/* Initialize packet waiting */ + +void *silc_packet_wait_init(SilcPacketStream stream, ...) +{ + SilcPacketWait pw; + SilcBool ret; + va_list ap; + + pw = silc_calloc(1, sizeof(*pw)); + if (!pw) + return NULL; + + /* Allocate mutex and conditional variable */ + if (!silc_mutex_alloc(&pw->wait_lock)) { + silc_free(pw); + return NULL; + } + if (!silc_cond_alloc(&pw->wait_cond)) { + silc_mutex_free(pw->wait_lock); + silc_free(pw); + return NULL; + } + + /* Link to the packet stream for the requested packet types */ + va_start(ap, stream); + ret = silc_packet_stream_link_va(stream, &silc_packet_wait_cbs, pw, + 10000000, ap); + va_end(ap); + if (!ret) { + silc_cond_free(pw->wait_cond); + silc_mutex_free(pw->wait_lock); + silc_free(pw); + return NULL; + } + + /* Initialize packet queue */ + silc_list_init(pw->packet_queue, struct SilcPacketStruct, next); + + return (void *)pw; +} + +/* Uninitialize packet waiting */ + +void silc_packet_wait_uninit(void *context, SilcPacketStream stream) +{ + SilcPacketWait pw = context; + SilcPacket packet; + + silc_mutex_lock(pw->wait_lock); + silc_packet_stream_unlink(stream, &silc_packet_wait_cbs, pw); + + /* Free any remaining packets */ + silc_list_start(pw->packet_queue); + while ((packet = silc_list_get(pw->packet_queue)) != SILC_LIST_END) + silc_packet_free(packet); + + silc_mutex_unlock(pw->wait_lock); + + silc_cond_free(pw->wait_cond); + silc_mutex_free(pw->wait_lock); + silc_free(pw); +} + +/* Blocks thread until a packet has been received. */ + +int silc_packet_wait(void *context, int timeout, SilcPacket *return_packet) +{ + SilcPacketWait pw = context; + SilcBool ret = FALSE; + + silc_mutex_lock(pw->wait_lock); + + /* Wait here until packet has arrived */ + pw->waiting = TRUE; + while (silc_list_count(pw->packet_queue) == 0) + ret = silc_cond_timedwait(pw->wait_cond, pw->wait_lock, timeout); + + /* Return packet */ + silc_list_start(pw->packet_queue); + *return_packet = silc_list_get(pw->packet_queue); + silc_list_del(pw->packet_queue, *return_packet); + + silc_mutex_unlock(pw->wait_lock); + + return ret == TRUE ? 1 : 0; +}