From 47202578e057ec6a3b43aa4a8205abbb9ba564aa Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Thu, 29 Jun 2006 21:20:38 +0000 Subject: [PATCH] Added silc_packet_stream_link, silc_packet_stream_unlink. Added silc_packet_wait_init, _uninit, silc_packet_wait. --- lib/silccore/silcpacket.c | 187 +++++++++++++++++++++++++++++++++++--- lib/silccore/silcpacket.h | 96 ++++++++++++++++++- 2 files changed, 268 insertions(+), 15 deletions(-) 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; +} diff --git a/lib/silccore/silcpacket.h b/lib/silccore/silcpacket.h index f7c6853c..6f2c15a7 100644 --- a/lib/silccore/silcpacket.h +++ b/lib/silccore/silcpacket.h @@ -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 @@ -789,6 +789,100 @@ SilcBool silc_packet_send_ext(SilcPacketStream stream, const unsigned char *data, SilcUInt32 data_len, SilcCipher cipher, SilcHmac hmac); +/****f* silccore/SilcPacketAPI/silc_packet_wait + * + * SYNOPSIS + * + * void *silc_packet_wait_init(SilcPacketStream stream, ...); + * + * DESCRIPTION + * + * Initializes a packet waiter for the packet stream `stream' and + * for the variable argument list of packet types. The function + * silc_packet_wait can be used to block the thread until a packet + * has been received. This function is used to initialize the waiting + * and to give the list of packet types that caller wish to receive. + * The variable argument list must end with -1. To receive all + * packets use SILC_PACKET_ANY. Returns a context that must be given + * to the silc_packet_wait function as argument. Returns NULL on + * error. To uninitialize the waiting call silc_packet_wait_uninit. + * + * EXAMPLE + * + * void *waiter; + * + * // Will wait for private message packets + * waiter = silc_packet_wait_init(stream, + * SILC_PACKET_PRIVATE_MESSAGE, -1); + * + * + ***/ +void *silc_packet_wait_init(SilcPacketStream stream, ...); + +/****f* silccore/SilcPacketAPI/silc_packet_wait + * + * SYNOPSIS + * + * void silc_packet_wait_uninit(void *waiter, SilcPacketStream stream); + * + * DESCRIPTION + * + * Uninitializes the waiting context. + * + ***/ +void silc_packet_wait_uninit(void *waiter, SilcPacketStream stream); + +/****f* silccore/SilcPacketAPI/silc_packet_wait + * + * SYNOPSIS + * + * int silc_packet_wait(void *waiter, int timeout, + * SilcPacket *return_packet) + * + * DESCRIPTION + * + * A special function that can be used to wait for a packet to arrive. + * This function will block the calling process or thread until either + * a packet is received into the `return_packet' pointer or the specified + * timeout value `timeout', which is in milliseconds, will expire. If + * the timeout is 0, no timeout exist. Before calling this function the + * silc_packet_wait_init must be called. The caller is responsible for + * freeing the returned packet with silc_packet_free. + * + * This function can be used for example from a thread that wants to + * block until SILC packet has been received. + * + * Returns 1 when packet was received, 0 if timeout occurred and -1 if + * error occurred. + * + * EXAMPLE + * + * static int foo_read_data(FooContext c) + * { + * SilcPacket packet; + * void *waiter; + * ... + * + * // Will wait for private message packets + * if (c->initialized == FALSE) { + * waiter = silc_packet_wait_init(stream, + * SILC_PACKET_PRIVATE_MESSAGE, -1); + * c->initialized = TRUE; + * } + * + * ... + * // Wait here until packet is received + * if ((silc_packet_wait(waiter, 0, &packet)) != -1) + * return -1; + * + * ... process packet ... + * + * return 1; + * } + * + ***/ +int silc_packet_wait(void *waiter, int timeout, SilcPacket *return_packet); + /****f* silccore/SilcPacketAPI/silc_packet_free * * SYNOPSIS -- 2.24.0