X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilccore%2Fsilcpacket.c;h=6de9eb089a91860680de499226a1a3189e2077bd;hb=9905799a86c606304fd7df2cd401de1740a272a1;hp=c19b9d08aadffb6490ae55566c25a118370c91a6;hpb=a4168d3cc19e363d8317a585aed0a9f8b8ad8c1f;p=silc.git diff --git a/lib/silccore/silcpacket.c b/lib/silccore/silcpacket.c index c19b9d08..6de9eb08 100644 --- a/lib/silccore/silcpacket.c +++ b/lib/silccore/silcpacket.c @@ -25,51 +25,71 @@ /************************** Types and definitions ***************************/ +/* Per scheduler (which usually means per thread) data. We put per scheduler + data here for accessing without locking. SILC Schedule dictates that + tasks are dispatched in one thread, hence the per scheduler context. */ +typedef struct { + SilcSchedule schedule; /* The scheduler */ + SilcPacketEngine engine; /* Packet engine */ + SilcBufferStruct inbuf; /* Data input buffer */ + SilcUInt32 stream_count; /* Number of streams using this */ +} *SilcPacketEngineContext; + /* Packet engine */ struct SilcPacketEngineStruct { + SilcMutex lock; /* Engine lock */ SilcRng rng; /* RNG for engine */ + SilcHashTable contexts; /* Per scheduler contexts */ SilcPacketCallbacks *callbacks; /* Packet callbacks */ void *callback_context; /* Context for callbacks */ SilcList streams; /* All streams in engine */ SilcList packet_pool; /* Free list for received packets */ - SilcMutex lock; /* Engine lock */ - SilcBool local_is_router; + SilcHashTable udp_remote; /* UDP remote streams, or NULL */ + unsigned int local_is_router : 1; }; -/* Packet procesor context */ +/* Packet processor context */ typedef struct SilcPacketProcessStruct { - SilcInt32 priority; /* Priority */ SilcPacketType *types; /* Packets to process */ SilcPacketCallbacks *callbacks; /* Callbacks or NULL */ void *callback_context; + SilcInt32 priority; /* Priority */ } *SilcPacketProcess; +/* UDP remote stream tuple */ +typedef struct { + char *remote_ip; /* Remote IP address */ + SilcUInt16 remote_port; /* Remote port */ +} *SilcPacketRemoteUDP; + /* Packet stream */ struct SilcPacketStreamStruct { struct SilcPacketStreamStruct *next; - SilcPacketEngine engine; /* Packet engine */ + SilcPacketEngineContext sc; /* Per scheduler context */ SilcStream stream; /* Underlaying stream */ SilcMutex lock; /* Stream lock */ - SilcDList process; /* Packet processors, it set */ - SilcHashTable streamers; /* Valid if streamers exist */ + SilcDList process; /* Packet processors, or NULL */ + SilcPacketRemoteUDP remote_udp; /* UDP remote stream tuple, or NULL */ void *stream_context; /* Stream context */ - SilcBufferStruct inbuf; /* In buffer */ SilcBufferStruct outbuf; /* Out buffer */ - SilcUInt32 send_psn; /* Sending sequence */ - SilcCipher send_key; /* Sending key */ - SilcHmac send_hmac; /* Sending HMAC */ - SilcUInt32 receive_psn; /* Receiving sequence */ - SilcCipher receive_key; /* Receiving key */ - SilcHmac receive_hmac; /* Receiving HMAC */ + SilcCipher send_key[2]; /* Sending key */ + SilcHmac send_hmac[2]; /* Sending HMAC */ + SilcCipher receive_key[2]; /* Receiving key */ + SilcHmac receive_hmac[2]; /* Receiving HMAC */ unsigned char *src_id; /* Source ID */ unsigned char *dst_id; /* Destination ID */ + SilcUInt32 send_psn; /* Sending sequence */ + SilcUInt32 receive_psn; /* Receiving sequence */ + SilcAtomic8 refcnt; /* Reference counter */ + SilcUInt8 sid; /* Security ID, set if IV included */ unsigned int src_id_len : 6; unsigned int src_id_type : 2; unsigned int dst_id_len : 6; unsigned int dst_id_type : 2; - SilcUInt8 refcnt; /* Reference counter */ unsigned int is_router : 1; /* Set if router stream */ unsigned int destroyed : 1; /* Set if destroyed */ + unsigned int iv_included : 1; /* Set if IV included */ + unsigned int udp : 1; /* UDP remote stream */ }; /* Initial size of stream buffers */ @@ -78,10 +98,9 @@ struct SilcPacketStreamStruct { /* Header length without source and destination ID's. */ #define SILC_PACKET_HEADER_LEN 10 -/* Minimum length of SILC Packet Header. This much is decrypted always - when packet is received to be able to get all the relevant data out - from the header. */ +/* Minimum length of SILC Packet Header. */ #define SILC_PACKET_MIN_HEADER_LEN 16 +#define SILC_PACKET_MIN_HEADER_LEN_IV 32 + 1 /* Maximum padding length */ #define SILC_PACKET_MAX_PADLEN 128 @@ -92,9 +111,6 @@ struct SilcPacketStreamStruct { /* Minimum packet length */ #define SILC_PACKET_MIN_LEN (SILC_PACKET_HEADER_LEN + 1) - -/* Macros */ - /* Returns true length of the packet. */ #define SILC_PACKET_LENGTH(__packetdata, __ret_truelen, __ret_paddedlen) \ do { \ @@ -132,127 +148,288 @@ do { \ /* EOS callback */ #define SILC_PACKET_CALLBACK_EOS(s) \ do { \ - (s)->engine->callbacks->eos((s)->engine, s, \ - (s)->engine->callback_context, \ - (s)->stream_context); \ + (s)->sc->engine->callbacks->eos((s)->sc->engine, s, \ + (s)->sc->engine->callback_context, \ + (s)->stream_context); \ } while(0) /* Error callback */ #define SILC_PACKET_CALLBACK_ERROR(s, err) \ do { \ - (s)->engine->callbacks->error((s)->engine, s, err, \ - (s)->engine->callback_context, \ - (s)->stream_context); \ + (s)->sc->engine->callbacks->error((s)->sc->engine, s, err, \ + (s)->sc->engine->callback_context, \ + (s)->stream_context); \ } while(0) +static SilcBool silc_packet_dispatch(SilcPacket packet); +static void silc_packet_read_process(SilcPacketStream stream); +static inline SilcBool silc_packet_send_raw(SilcPacketStream stream, + SilcPacketType type, + SilcPacketFlags flags, + SilcIdType src_id_type, + unsigned char *src_id, + SilcUInt32 src_id_len, + SilcIdType dst_id_type, + unsigned char *dst_id, + SilcUInt32 dst_id_len, + const unsigned char *data, + SilcUInt32 data_len, + SilcCipher cipher, + SilcHmac hmac); /************************ Static utility functions **************************/ -static void silc_packet_read_process(SilcPacketStream stream); +/* Injects packet to new stream created with silc_packet_stream_add_remote. */ -/* Our stream IO notifier callback. */ +SILC_TASK_CALLBACK(silc_packet_stream_inject_packet) +{ + SilcPacket packet = context; + SilcPacketStream stream = packet->stream; -static void silc_packet_stream_io(SilcStream stream, SilcStreamStatus status, - void *context) + SILC_LOG_DEBUG(("Injecting packet %p to stream %p", packet, packet->stream)); + + silc_mutex_lock(stream->lock); + if (!stream->destroyed) + silc_packet_dispatch(packet); + silc_mutex_unlock(stream->lock); + silc_packet_stream_unref(stream); +} + +/* Write data to the stream. Must be called with ps->lock locked. Unlocks + the lock inside this function, unless no_unlock is TRUE. Unlocks always + in case it returns FALSE. */ + +static inline SilcBool silc_packet_stream_write(SilcPacketStream ps, + SilcBool no_unlock) { - SilcPacketStream ps = context; - int ret; + SilcStream stream; + SilcBool connected; + int i; - silc_mutex_lock(ps->lock); + if (ps->udp) + stream = ((SilcPacketStream)ps->stream)->stream; + else + stream = ps->stream; + + if (ps->udp && silc_socket_stream_is_udp(stream, &connected)) { + if (!connected) { + /* Connectionless UDP stream */ + while (silc_buffer_len(&ps->outbuf) > 0) { + i = silc_net_udp_send(stream, ps->remote_udp->remote_ip, + ps->remote_udp->remote_port, + ps->outbuf.data, silc_buffer_len(&ps->outbuf)); + if (silc_unlikely(i == -2)) { + /* Error */ + silc_buffer_reset(&ps->outbuf); + SILC_PACKET_CALLBACK_ERROR(ps, SILC_PACKET_ERR_WRITE); + return FALSE; + } - if (ps->destroyed) { - silc_mutex_unlock(ps->lock); - return; + if (silc_unlikely(i == -1)) { + /* Cannot write now, write later. */ + if (!no_unlock) + silc_mutex_unlock(ps->lock); + return TRUE; + } + + /* Wrote data */ + silc_buffer_pull(&ps->outbuf, i); + } + + silc_buffer_reset(&ps->outbuf); + if (!no_unlock) + silc_mutex_unlock(ps->lock); + + return TRUE; + } } - switch (status) { + /* Write the data to the stream */ + while (silc_buffer_len(&ps->outbuf) > 0) { + i = silc_stream_write(stream, ps->outbuf.data, + silc_buffer_len(&ps->outbuf)); + if (silc_unlikely(i == 0)) { + /* EOS */ + silc_buffer_reset(&ps->outbuf); + silc_mutex_unlock(ps->lock); + SILC_PACKET_CALLBACK_EOS(ps); + return FALSE; + } - case SILC_STREAM_CAN_WRITE: - if (!silc_buffer_headlen(&ps->outbuf)) { + if (silc_unlikely(i == -2)) { + /* Error */ + silc_buffer_reset(&ps->outbuf); silc_mutex_unlock(ps->lock); - return; + SILC_PACKET_CALLBACK_ERROR(ps, SILC_PACKET_ERR_WRITE); + return FALSE; } - SILC_LOG_DEBUG(("Writing pending data to stream")); + if (silc_unlikely(i == -1)) { + /* Cannot write now, write later. */ + if (!no_unlock) + silc_mutex_unlock(ps->lock); + return TRUE; + } - /* Write pending data to stream */ - while (silc_buffer_len(&ps->outbuf) > 0) { - ret = silc_stream_write(ps->stream, ps->outbuf.data, - silc_buffer_len(&ps->outbuf)); - if (ret == 0) { - /* EOS */ - silc_buffer_reset(&ps->outbuf); + /* Wrote data */ + silc_buffer_pull(&ps->outbuf, i); + } + + silc_buffer_reset(&ps->outbuf); + if (!no_unlock) + silc_mutex_unlock(ps->lock); + + return TRUE; +} + +/* Reads data from stream. Must be called with ps->lock locked. If this + returns FALSE the lock has been unlocked. If this returns packet stream + to `ret_ps' its lock has been acquired and `ps' lock has been unlocked. + It is returned if the stream is UDP and remote UDP stream exists for + the sender of the packet. */ + +static inline SilcBool silc_packet_stream_read(SilcPacketStream ps, + SilcPacketStream *ret_ps) +{ + SilcStream stream; + SilcBuffer inbuf; + SilcBool connected; + int ret; + + stream = ps->stream; + inbuf = &ps->sc->inbuf; + + if (silc_socket_stream_is_udp(stream, &connected)) { + if (!connected) { + /* Connectionless UDP stream, read one UDP packet */ + char remote_ip[64], tuple[64]; + int remote_port; + SilcPacketStream remote; + + ret = silc_net_udp_receive(stream, remote_ip, sizeof(remote_ip), + &remote_port, inbuf->tail, + silc_buffer_taillen(inbuf)); + + if (silc_unlikely(ret < 0)) { silc_mutex_unlock(ps->lock); - SILC_PACKET_CALLBACK_EOS(ps); - return; - } + if (ret == -1) { + /* Cannot read now, do it later. */ + silc_buffer_pull(inbuf, silc_buffer_len(inbuf)); + return FALSE; + } - if (ret == -2) { /* Error */ - silc_buffer_reset(&ps->outbuf); - silc_mutex_unlock(ps->lock); - SILC_PACKET_CALLBACK_ERROR(ps, SILC_PACKET_ERR_WRITE); - return; + silc_buffer_reset(inbuf); + SILC_PACKET_CALLBACK_ERROR(ps, SILC_PACKET_ERR_READ); + return FALSE; } - if (ret == -1) { - /* Cannot write now, write later. */ + /* See if remote packet stream exist for this sender */ + silc_snprintf(tuple, sizeof(tuple), "%d%s", remote_port, remote_ip); + silc_mutex_lock(ps->sc->engine->lock); + if (silc_hash_table_find(ps->sc->engine->udp_remote, tuple, NULL, + (void *)&remote)) { + silc_mutex_unlock(ps->sc->engine->lock); + SILC_LOG_DEBUG(("UDP packet from %s:%d for stream %p", remote_ip, + remote_port, remote)); silc_mutex_unlock(ps->lock); - return; + silc_mutex_lock(remote->lock); + *ret_ps = remote; + return TRUE; + } + silc_mutex_unlock(ps->sc->engine->lock); + + /* Unknown sender */ + if (!ps->remote_udp) { + ps->remote_udp = silc_calloc(1, sizeof(*ps->remote_udp)); + if (silc_unlikely(!ps->remote_udp)) { + silc_mutex_unlock(ps->lock); + SILC_PACKET_CALLBACK_ERROR(ps, SILC_PACKET_ERR_NO_MEMORY); + return FALSE; + } } - /* Wrote data */ - silc_buffer_pull(&ps->outbuf, ret); + /* Save sender IP and port */ + silc_free(ps->remote_udp->remote_ip); + ps->remote_udp->remote_ip = strdup(remote_ip); + ps->remote_udp->remote_port = remote_port; + + silc_buffer_pull_tail(inbuf, ret); + return TRUE; + } + } + + /* Read data from the stream */ + ret = silc_stream_read(stream, inbuf->tail, silc_buffer_taillen(inbuf)); + if (silc_unlikely(ret <= 0)) { + silc_mutex_unlock(ps->lock); + if (ret == 0) { + /* EOS */ + silc_buffer_reset(inbuf); + SILC_PACKET_CALLBACK_EOS(ps); + return FALSE; } - silc_buffer_reset(&ps->outbuf); + if (ret == -1) { + /* Cannot read now, do it later. */ + silc_buffer_pull(inbuf, silc_buffer_len(inbuf)); + return FALSE; + } + + /* Error */ + silc_buffer_reset(inbuf); + SILC_PACKET_CALLBACK_ERROR(ps, SILC_PACKET_ERR_READ); + return FALSE; + } + + silc_buffer_pull_tail(inbuf, ret); + return TRUE; +} + +/* Our stream IO notifier callback. */ + +static void silc_packet_stream_io(SilcStream stream, SilcStreamStatus status, + void *context) +{ + SilcPacketStream remote = NULL, ps = context; + silc_mutex_lock(ps->lock); + + if (silc_unlikely(ps->destroyed)) { silc_mutex_unlock(ps->lock); - break; + return; + } + switch (status) { case SILC_STREAM_CAN_READ: SILC_LOG_DEBUG(("Reading data from stream")); - /* Make sure we have fair amount of free space in inbuf */ - if (silc_buffer_taillen(&ps->inbuf) < SILC_PACKET_DEFAULT_SIZE) - if (!silc_buffer_realloc(&ps->inbuf, silc_buffer_truelen(&ps->inbuf) + - SILC_PACKET_DEFAULT_SIZE * 2)) { - silc_mutex_unlock(ps->lock); - return; - } - /* Read data from stream */ - ret = silc_stream_read(ps->stream, ps->inbuf.tail, - silc_buffer_taillen(&ps->inbuf)); - - if (ret == 0) { - /* EOS */ - silc_buffer_reset(&ps->inbuf); - silc_mutex_unlock(ps->lock); - SILC_PACKET_CALLBACK_EOS(ps); + if (!silc_packet_stream_read(ps, &remote)) return; - } - if (ret == -2) { - /* Error */ - silc_buffer_reset(&ps->inbuf); + /* Now process the data */ + silc_packet_stream_ref(ps); + if (!remote) { + silc_packet_read_process(ps); silc_mutex_unlock(ps->lock); - SILC_PACKET_CALLBACK_ERROR(ps, SILC_PACKET_ERR_READ); - return; + } else { + silc_packet_read_process(remote); + silc_mutex_unlock(remote->lock); } + silc_packet_stream_unref(ps); + break; - if (ret == -1) { - /* Cannot read now, do it later. */ - silc_buffer_pull(&ps->inbuf, silc_buffer_len(&ps->inbuf)); + case SILC_STREAM_CAN_WRITE: + SILC_LOG_DEBUG(("Writing pending data to stream")); + + if (silc_unlikely(!silc_buffer_headlen(&ps->outbuf))) { silc_mutex_unlock(ps->lock); return; } - /* Now process the data */ - silc_buffer_pull_tail(&ps->inbuf, ret); - silc_packet_read_process(ps); - - silc_mutex_unlock(ps->lock); + /* Write pending data to stream */ + silc_packet_stream_write(ps, FALSE); break; default: @@ -280,13 +457,13 @@ static SilcPacket silc_packet_alloc(SilcPacketEngine engine) silc_mutex_unlock(engine->lock); packet = silc_calloc(1, sizeof(*packet)); - if (!packet) + if (silc_unlikely(!packet)) return NULL; SILC_LOG_DEBUG(("Allocating new packet %p", packet)); tmp = silc_malloc(SILC_PACKET_DEFAULT_SIZE); - if (!tmp) { + if (silc_unlikely(!tmp)) { silc_free(packet); return NULL; } @@ -306,6 +483,25 @@ static SilcPacket silc_packet_alloc(SilcPacketEngine engine) return packet; } +/* UDP remote stream hash table destructor */ + +static void silc_packet_engine_hash_destr(void *key, void *context, + void *user_context) +{ + silc_free(key); +} + +/* Per scheduler context hash table destructor */ + +static void silc_packet_engine_context_destr(void *key, void *context, + void *user_context) +{ + SilcPacketEngineContext sc = context; + silc_buffer_clear(&sc->inbuf); + silc_buffer_purge(&sc->inbuf); + silc_free(sc); +} + /******************************** Packet API ********************************/ @@ -332,6 +528,14 @@ silc_packet_engine_start(SilcRng rng, SilcBool router, if (!engine) return NULL; + engine->contexts = silc_hash_table_alloc(0, silc_hash_ptr, NULL, NULL, NULL, + silc_packet_engine_context_destr, + engine, TRUE); + if (!engine->contexts) { + silc_free(engine); + return NULL; + } + engine->rng = rng; engine->local_is_router = router; engine->callbacks = callbacks; @@ -343,12 +547,16 @@ silc_packet_engine_start(SilcRng rng, SilcBool router, silc_list_init(engine->packet_pool, struct SilcPacketStruct, next); for (i = 0; i < 5; i++) { packet = silc_calloc(1, sizeof(*packet)); - if (!packet) + if (!packet) { + silc_packet_engine_stop(engine); return NULL; + } tmp = silc_malloc(SILC_PACKET_DEFAULT_SIZE); - if (!tmp) + if (!tmp) { + silc_packet_engine_stop(engine); return NULL; + } silc_buffer_set(&packet->buffer, tmp, SILC_PACKET_DEFAULT_SIZE); silc_buffer_reset(&packet->buffer); @@ -392,35 +600,165 @@ SilcPacketStream silc_packet_stream_create(SilcPacketEngine engine, if (!ps) return NULL; - ps->engine = engine; ps->stream = stream; - ps->refcnt++; + silc_atomic_init8(&ps->refcnt, 1); + silc_mutex_alloc(&ps->lock); - /* Allocate buffers */ - tmp = silc_malloc(SILC_PACKET_DEFAULT_SIZE); - if (!tmp) - return NULL; - silc_buffer_set(&ps->inbuf, tmp, SILC_PACKET_DEFAULT_SIZE); - silc_buffer_reset(&ps->inbuf); + /* Allocate out buffer */ tmp = silc_malloc(SILC_PACKET_DEFAULT_SIZE); - if (!tmp) + if (!tmp) { + silc_packet_stream_destroy(ps); return NULL; + } silc_buffer_set(&ps->outbuf, tmp, SILC_PACKET_DEFAULT_SIZE); silc_buffer_reset(&ps->outbuf); /* Initialize packet procesors list */ ps->process = silc_dlist_init(); + if (!ps->process) { + silc_packet_stream_destroy(ps); + return NULL; + } + + silc_mutex_lock(engine->lock); + + /* Add per scheduler context */ + if (!silc_hash_table_find(engine->contexts, schedule, NULL, + (void *)&ps->sc)) { + ps->sc = silc_calloc(1, sizeof(*ps->sc)); + if (!ps->sc) { + silc_packet_stream_destroy(ps); + silc_mutex_unlock(engine->lock); + return NULL; + } + ps->sc->engine = engine; + ps->sc->schedule = schedule; + + /* Allocate data input buffer */ + tmp = silc_malloc(SILC_PACKET_DEFAULT_SIZE * 31); + if (!tmp) { + 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); + + /* Add to per scheduler context hash table */ + if (!silc_hash_table_add(engine->contexts, schedule, ps->sc)) { + silc_buffer_purge(&ps->sc->inbuf); + silc_free(ps->sc); + ps->sc = NULL; + silc_packet_stream_destroy(ps); + silc_mutex_unlock(engine->lock); + return NULL; + } + } + ps->sc->stream_count++; - /* Set IO notifier callback */ + /* Add the packet stream to engine */ + silc_list_add(engine->streams, ps); + + /* If this is UDP stream, allocate UDP remote stream hash table */ + if (!engine->udp_remote && silc_socket_stream_is_udp(stream, NULL)) + engine->udp_remote = silc_hash_table_alloc(0, silc_hash_string, NULL, + silc_hash_string_compare, NULL, + silc_packet_engine_hash_destr, + NULL, TRUE); + silc_mutex_unlock(engine->lock); + + /* Set IO notifier callback. This schedules this stream for I/O. */ silc_stream_set_notifier(ps->stream, schedule, silc_packet_stream_io, ps); + return ps; +} + +/* Add new remote packet stream for UDP packet streams */ + +SilcPacketStream silc_packet_stream_add_remote(SilcPacketStream stream, + const char *remote_ip, + SilcUInt16 remote_port, + SilcPacket packet) +{ + SilcPacketEngine engine = stream->sc->engine; + SilcPacketStream ps; + char *tuple; + void *tmp; + + SILC_LOG_DEBUG(("Adding UDP remote %s:%d to packet stream %p", + remote_ip, remote_port, stream)); + + if (!stream || !remote_ip || !remote_port) + return NULL; + + if (!silc_socket_stream_is_udp(stream->stream, NULL)) { + SILC_LOG_ERROR(("Stream is not UDP stream, cannot add remote IP")); + return NULL; + } + + ps = silc_calloc(1, sizeof(*ps)); + if (!ps) + return NULL; + ps->sc = stream->sc; + + silc_atomic_init8(&ps->refcnt, 1); silc_mutex_alloc(&ps->lock); - /* Add to engine */ + /* Set the UDP packet stream as underlaying stream */ + silc_packet_stream_ref(stream); + ps->stream = (SilcStream)stream; + ps->udp = TRUE; + + /* Allocate out buffer */ + tmp = silc_malloc(SILC_PACKET_DEFAULT_SIZE); + if (!tmp) { + silc_packet_stream_destroy(ps); + return NULL; + } + silc_buffer_set(&ps->outbuf, tmp, SILC_PACKET_DEFAULT_SIZE); + silc_buffer_reset(&ps->outbuf); + + /* Initialize packet procesors list */ + ps->process = silc_dlist_init(); + if (!ps->process) { + silc_packet_stream_destroy(ps); + return NULL; + } + + /* Add to engine with this IP and port pair */ + tuple = silc_format("%d%s", remote_port, remote_ip); silc_mutex_lock(engine->lock); - silc_list_add(engine->streams, ps); + if (!tuple || !silc_hash_table_add(engine->udp_remote, tuple, ps)) { + silc_mutex_unlock(engine->lock); + silc_packet_stream_destroy(ps); + return NULL; + } silc_mutex_unlock(engine->lock); + /* Save remote IP and port pair */ + ps->remote_udp = silc_calloc(1, sizeof(*ps->remote_udp)); + if (!ps->remote_udp) { + silc_packet_stream_destroy(ps); + return NULL; + } + ps->remote_udp->remote_port = remote_port; + ps->remote_udp->remote_ip = strdup(remote_ip); + if (!ps->remote_udp->remote_ip) { + silc_packet_stream_destroy(ps); + return NULL; + } + + if (packet) { + /* Inject packet to the new stream */ + packet->stream = ps; + silc_packet_stream_ref(ps); + silc_schedule_task_add_timeout(silc_stream_get_schedule(stream->stream), + silc_packet_stream_inject_packet, packet, + 0, 0); + } + return ps; } @@ -431,30 +769,64 @@ void silc_packet_stream_destroy(SilcPacketStream stream) if (!stream) return; - if (stream->refcnt > 1) { + if (silc_atomic_get_int8(&stream->refcnt) > 1) { stream->destroyed = TRUE; return; } SILC_LOG_DEBUG(("Destroying packet stream %p", stream)); - /* Delete from engine */ - silc_mutex_lock(stream->engine->lock); - silc_list_del(stream->engine->streams, stream); - silc_mutex_unlock(stream->engine->lock); + if (!stream->udp) { + /* Delete from engine */ + silc_mutex_lock(stream->sc->engine->lock); + silc_list_del(stream->sc->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_mutex_unlock(stream->sc->engine->lock); + + /* Destroy the underlaying stream */ + if (stream->stream) + silc_stream_destroy(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); + + silc_free(stream->remote_udp->remote_ip); + silc_free(stream->remote_udp); + + /* Unreference the underlaying packet stream */ + silc_packet_stream_unref((SilcPacketStream)stream->stream); + } /* Clear and free buffers */ - silc_buffer_clear(&stream->inbuf); silc_buffer_clear(&stream->outbuf); - silc_buffer_purge(&stream->inbuf); silc_buffer_purge(&stream->outbuf); - /* XXX */ + if (stream->process) { + SilcPacketProcess p; + silc_dlist_start(stream->process); + while ((p = silc_dlist_get(stream->process))) { + silc_free(p->types); + silc_free(p); + silc_dlist_del(stream->process, p); + } + silc_dlist_uninit(stream->process); + } - /* Destroy the underlaying stream */ - silc_stream_destroy(stream->stream); + /* XXX */ - silc_dlist_uninit(stream->process); + silc_atomic_uninit8(&stream->refcnt); silc_mutex_free(stream->lock); silc_free(stream); } @@ -466,6 +838,12 @@ void silc_packet_stream_set_router(SilcPacketStream stream) stream->is_router = TRUE; } +/* Mark to include IV in ciphertext */ + +void silc_packet_stream_set_iv_included(SilcPacketStream stream) +{ + stream->iv_included = TRUE; +} /* Links `callbacks' to `stream' for specified packet types */ @@ -497,8 +875,10 @@ static SilcBool silc_packet_stream_link_va(SilcPacketStream stream, if (!stream->process) { stream->process = silc_dlist_init(); - if (!stream->process) + if (!stream->process) { + silc_mutex_unlock(stream->lock); return FALSE; + } } /* According to priority set the procesor to correct position. First @@ -579,6 +959,7 @@ void silc_packet_stream_unlink(SilcPacketStream stream, if (p->callbacks == callbacks && p->callback_context == callback_context) { silc_dlist_del(stream->process, p); + silc_free(p->types); silc_free(p); break; } @@ -593,23 +974,40 @@ void silc_packet_stream_unlink(SilcPacketStream stream, silc_packet_stream_unref(stream); } +/* Returns TRUE if stream is UDP stream */ + +SilcBool silc_packet_stream_is_udp(SilcPacketStream stream) +{ + return stream->udp || silc_socket_stream_is_udp(stream->stream, NULL); +} + +/* Return packet sender IP and port for UDP packet stream */ + +SilcBool silc_packet_get_sender(SilcPacket packet, + const char **sender_ip, + SilcUInt16 *sender_port) +{ + if (!packet->stream->remote_udp) + return FALSE; + + *sender_ip = packet->stream->remote_udp->remote_ip; + *sender_port = packet->stream->remote_udp->remote_port; + + return TRUE; +} + /* Reference packet stream */ void silc_packet_stream_ref(SilcPacketStream stream) { - silc_mutex_lock(stream->lock); - stream->refcnt++; - silc_mutex_unlock(stream->lock); + silc_atomic_add_int8(&stream->refcnt, 1); } /* Unreference packet stream */ void silc_packet_stream_unref(SilcPacketStream stream) { - silc_mutex_lock(stream->lock); - stream->refcnt--; - silc_mutex_unlock(stream->lock); - if (stream->refcnt == 0) + if (silc_atomic_sub_int8(&stream->refcnt, 1) == 0) silc_packet_stream_destroy(stream); } @@ -617,7 +1015,7 @@ void silc_packet_stream_unref(SilcPacketStream stream) SilcPacketEngine silc_packet_get_engine(SilcPacketStream stream) { - return stream->engine; + return stream->sc->engine; } /* Set application context for packet stream */ @@ -640,71 +1038,116 @@ void *silc_packet_get_context(SilcPacketStream stream) return context; } -/* Return underlaying stream */ +/* Change underlaying stream */ -SilcStream silc_packet_stream_get_stream(SilcPacketStream stream) +void silc_packet_stream_set_stream(SilcPacketStream ps, + SilcStream stream) { - return stream->stream; + if (ps->stream) + silc_stream_set_notifier(ps->stream, ps->sc->schedule, NULL, NULL); + ps->stream = stream; + silc_stream_set_notifier(ps->stream, ps->sc->schedule, silc_packet_stream_io, + ps); } -/* Set ciphers for packet stream */ +/* Return underlaying stream */ -void silc_packet_set_ciphers(SilcPacketStream stream, SilcCipher send, - SilcCipher receive) +SilcStream silc_packet_stream_get_stream(SilcPacketStream stream) { - SILC_LOG_DEBUG(("Setting new ciphers to packet stream")); - silc_mutex_lock(stream->lock); - stream->send_key = send; - stream->receive_key = receive; - silc_mutex_unlock(stream->lock); + return stream->stream; } -/* Return current ciphers from packet stream */ +/* Set keys. */ -SilcBool silc_packet_get_ciphers(SilcPacketStream stream, SilcCipher *send, - SilcCipher *receive) +SilcBool silc_packet_set_keys(SilcPacketStream stream, SilcCipher send_key, + SilcCipher receive_key, SilcHmac send_hmac, + SilcHmac receive_hmac, SilcBool rekey) { - if (!stream->send_key && !stream->receive_key) - return FALSE; - - silc_mutex_lock(stream->lock); - - if (send) - *send = stream->send_key; - if (receive) - *receive = stream->receive_key; + SILC_LOG_DEBUG(("Setting new keys to packet stream %p", stream)); + + /* If doing rekey, send REKEY_DONE packet */ + if (rekey) { + /* This will take stream lock. */ + if (!silc_packet_send_raw(stream, SILC_PACKET_REKEY_DONE, 0, + stream->src_id_type, stream->src_id, + stream->src_id_len, stream->dst_id_type, + stream->dst_id, stream->dst_id_len, + NULL, 0, stream->send_key[0], + stream->send_hmac[0])) + return FALSE; - silc_mutex_unlock(stream->lock); + /* Write the packet to the stream */ + if (!silc_packet_stream_write(stream, TRUE)) + return FALSE; + } else { + silc_mutex_lock(stream->lock); + } - return TRUE; -} + /* In case IV Included is set, save the old keys */ + if (stream->iv_included) { + if (stream->send_key[1] && send_key) { + silc_cipher_free(stream->send_key[1]); + stream->send_key[1] = stream->send_key[0]; + } + if (stream->receive_key[1] && receive_key) { + silc_cipher_free(stream->receive_key[1]); + stream->receive_key[1] = stream->receive_key[0]; + } + if (stream->send_hmac[1] && send_hmac) { + silc_hmac_free(stream->send_hmac[1]); + stream->send_hmac[1] = stream->send_hmac[0]; + } + if (stream->receive_hmac[1] && receive_hmac) { + silc_hmac_free(stream->receive_hmac[1]); + stream->receive_hmac[1] = stream->receive_hmac[0]; + } + } else { + if (stream->send_key[0] && send_key) + silc_cipher_free(stream->send_key[0]); + if (stream->send_key[1] && receive_key) + silc_cipher_free(stream->receive_key[0]); + if (stream->send_hmac[0] && send_hmac) + silc_hmac_free(stream->send_hmac[0]); + if (stream->receive_hmac[0] && receive_hmac) + silc_hmac_free(stream->receive_hmac[0]); + } -/* Set HMACs for packet stream */ + /* Set keys */ + if (send_key) + stream->send_key[0] = send_key; + if (receive_key) + stream->receive_key[0] = receive_key; + if (send_hmac) + stream->send_hmac[0] = send_hmac; + if (receive_hmac) + stream->receive_hmac[0] = receive_hmac; -void silc_packet_set_hmacs(SilcPacketStream stream, SilcHmac send, - SilcHmac receive) -{ - SILC_LOG_DEBUG(("Setting new HMACs to packet stream")); - silc_mutex_lock(stream->lock); - stream->send_hmac = send; - stream->receive_hmac = receive; silc_mutex_unlock(stream->lock); + return TRUE; } -/* Return current HMACs from packet stream */ +/* Return current ciphers from packet stream */ -SilcBool silc_packet_get_hmacs(SilcPacketStream stream, SilcHmac *send, - SilcHmac *receive) +SilcBool silc_packet_get_keys(SilcPacketStream stream, + SilcCipher *send_key, + SilcCipher *receive_key, + SilcHmac *send_hmac, + SilcHmac *receive_hmac) { - if (!stream->send_hmac && !stream->receive_hmac) + if (!stream->send_key[0] && !stream->receive_key[0] && + !stream->send_hmac[0] && !stream->receive_hmac[0]) return FALSE; silc_mutex_lock(stream->lock); - if (send) - *send = stream->send_hmac; - if (receive) - *receive = stream->receive_hmac; + if (send_key) + *send_key = stream->send_key[0]; + if (receive_key) + *receive_key = stream->receive_key[0]; + if (send_hmac) + *send_hmac = stream->send_hmac[0]; + if (receive_hmac) + *receive_hmac = stream->receive_hmac[0]; silc_mutex_unlock(stream->lock); @@ -762,6 +1205,19 @@ SilcBool silc_packet_set_ids(SilcPacketStream stream, return TRUE; } +/* Adds Security ID (SID) */ + +SilcBool silc_packet_set_sid(SilcPacketStream stream, SilcUInt8 sid) +{ + if (!stream->iv_included) + return FALSE; + + SILC_LOG_DEBUG(("Set packet stream %p SID to %d", stream, sid)); + + stream->sid = sid; + return TRUE; +} + /* Free packet */ void silc_packet_free(SilcPacket packet) @@ -770,50 +1226,32 @@ void silc_packet_free(SilcPacket packet) SILC_LOG_DEBUG(("Freeing packet %p", packet)); -#if defined(SILC_DEBUG) /* Check for double free */ - assert(packet->stream != NULL); -#endif /* SILC_DEBUG */ - - silc_mutex_lock(stream->engine->lock); + SILC_ASSERT(packet->stream != NULL); packet->stream = NULL; packet->src_id = packet->dst_id = NULL; silc_buffer_reset(&packet->buffer); - /* Put the packet back to freelist */ - silc_list_add(stream->engine->packet_pool, packet); - - silc_mutex_unlock(stream->engine->lock); -} - -/* Creates streamer */ + silc_mutex_lock(stream->sc->engine->lock); -SilcStream silc_packet_streamer_create(SilcPacketStream stream, - SilcPacketType packet_type, - SilcPacketFlags packet_flags) -{ - /* XXX TODO */ - return NULL; -} - -/* Destroyes streamer */ - -void silc_packet_streamer_destroy(SilcStream stream) -{ + /* Put the packet back to freelist */ + silc_list_add(stream->sc->engine->packet_pool, packet); + if (silc_list_count(stream->sc->engine->packet_pool) == 1) + silc_list_start(stream->sc->engine->packet_pool); + silc_mutex_unlock(stream->sc->engine->lock); } - /****************************** Packet Sending ******************************/ /* Prepare outgoing data buffer for packet sending. Returns the pointer to that buffer into the `packet'. */ -static SilcBool silc_packet_send_prepare(SilcPacketStream stream, - SilcUInt32 totlen, - SilcHmac hmac, - SilcBuffer packet) +static inline SilcBool silc_packet_send_prepare(SilcPacketStream stream, + SilcUInt32 totlen, + SilcHmac hmac, + SilcBuffer packet) { unsigned char *oldptr; unsigned int mac_len = hmac ? silc_hmac_len(hmac) : 0; @@ -821,7 +1259,7 @@ static SilcBool silc_packet_send_prepare(SilcPacketStream stream, totlen += mac_len; /* Allocate more space if needed */ - if (silc_buffer_taillen(&stream->outbuf) < totlen) { + if (silc_unlikely(silc_buffer_taillen(&stream->outbuf) < totlen)) { if (!silc_buffer_realloc(&stream->outbuf, silc_buffer_truelen(&stream->outbuf) + totlen)) return FALSE; @@ -837,28 +1275,65 @@ static SilcBool silc_packet_send_prepare(SilcPacketStream stream, return TRUE; } -/* Internal routine to send packet */ - -static SilcBool silc_packet_send_raw(SilcPacketStream stream, - SilcPacketType type, - SilcPacketFlags flags, - SilcIdType src_id_type, - unsigned char *src_id, - SilcUInt32 src_id_len, - SilcIdType dst_id_type, - unsigned char *dst_id, - SilcUInt32 dst_id_len, - const unsigned char *data, - SilcUInt32 data_len, - SilcCipher cipher, - SilcHmac hmac) +/* Increments counter when encrypting in counter mode. */ + +static inline void silc_packet_send_ctr_increment(SilcPacketStream stream, + SilcCipher cipher, + unsigned char *ret_iv) { - unsigned char tmppad[SILC_PACKET_MAX_PADLEN]; + unsigned char *iv = silc_cipher_get_iv(cipher); + SilcUInt32 pc; + + /* Increment packet counter */ + SILC_GET32_MSB(pc, iv + 8); + pc++; + SILC_PUT32_MSB(pc, iv + 8); + + /* Reset block counter */ + memset(iv + 12, 0, 4); + + /* If IV Included flag, return the 64-bit IV for inclusion in packet */ + if (stream->iv_included) { + /* Get new nonce */ + ret_iv[0] = silc_rng_get_byte_fast(stream->sc->engine->rng); + 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_LOG_HEXDUMP(("IV"), ret_iv, 8); + + /* Set new nonce to counter block */ + memcpy(iv + 4, ret_iv, 4); + } + + SILC_LOG_HEXDUMP(("Counter Block"), iv, 16); +} + +/* Internal routine to assemble outgoing packet. Assembles and encryptes + the packet. The silc_packet_stream_write needs to be called to send it + after this returns TRUE. */ + +static inline SilcBool silc_packet_send_raw(SilcPacketStream stream, + SilcPacketType type, + SilcPacketFlags flags, + SilcIdType src_id_type, + unsigned char *src_id, + SilcUInt32 src_id_len, + SilcIdType dst_id_type, + unsigned char *dst_id, + SilcUInt32 dst_id_len, + const unsigned char *data, + SilcUInt32 data_len, + SilcCipher cipher, + SilcHmac hmac) +{ + unsigned char tmppad[SILC_PACKET_MAX_PADLEN], iv[33], psn[4]; int block_len = (cipher ? silc_cipher_get_block_len(cipher) : 0); - int i, enclen, truelen, padlen; + int i, enclen, truelen, padlen = 0, ivlen = 0, psnlen = 0; + SilcBool ctr; SilcBufferStruct packet; - SILC_LOG_DEBUG(("Sending packet %s (%d) flags %d, src %d dst %d," + SILC_LOG_DEBUG(("Sending packet %s (%d) flags %d, src %d dst %d, " "data len %d", silc_get_packet_name(type), stream->send_psn, flags, src_id_type, dst_id_type, data_len)); @@ -870,6 +1345,27 @@ static SilcBool silc_packet_send_raw(SilcPacketStream stream, enclen = truelen = (data_len + SILC_PACKET_HEADER_LEN + src_id_len + dst_id_len); + /* If using CTR mode, increment the counter */ + ctr = (cipher && silc_cipher_get_mode(cipher) == SILC_CIPHER_MODE_CTR); + if (ctr) { + silc_packet_send_ctr_increment(stream, cipher, iv + 1); + + /* If IV is included, the SID, IV and sequence number is added to packet */ + if (stream->iv_included && cipher) { + psnlen = sizeof(psn); + ivlen = 8 + 1; + iv[0] = stream->sid; + } + } else { + /* If IV is included, the SID, IV and sequence number is added to packet */ + if (stream->iv_included && cipher) { + psnlen = sizeof(psn); + ivlen = block_len + 1; + iv[0] = stream->sid; + memcpy(iv + 1, silc_cipher_get_iv(cipher), block_len); + } + } + /* We automatically figure out the packet structure from the packet type and flags, and calculate correct length. Private messages with private keys and channel messages are special packets as their @@ -879,21 +1375,22 @@ static SilcBool silc_packet_send_raw(SilcPacketStream stream, type == SILC_PACKET_CHANNEL_MESSAGE) { /* Padding is calculated from header + IDs */ - SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN + - src_id_len + - dst_id_len), block_len, padlen); + if (!ctr) + SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN + src_id_len + dst_id_len + + psnlen), block_len, padlen); /* Length to encrypt, header + IDs + padding. */ - enclen = SILC_PACKET_HEADER_LEN + src_id_len + dst_id_len + padlen; + enclen = (SILC_PACKET_HEADER_LEN + src_id_len + dst_id_len + + padlen + psnlen); } else { /* Padding is calculated from true length of the packet */ if (flags & SILC_PACKET_FLAG_LONG_PAD) - SILC_PACKET_PADLEN_MAX(truelen, block_len, padlen); - else - SILC_PACKET_PADLEN(truelen, block_len, padlen); + SILC_PACKET_PADLEN_MAX(truelen + psnlen, block_len, padlen); + else if (!ctr) + SILC_PACKET_PADLEN(truelen + psnlen, block_len, padlen); - enclen += padlen; + enclen += padlen + psnlen; } /* Remove implementation specific flags */ @@ -901,19 +1398,24 @@ static SilcBool silc_packet_send_raw(SilcPacketStream stream, /* Get random padding */ for (i = 0; i < padlen; i++) tmppad[i] = - silc_rng_get_byte_fast(stream->engine->rng); + silc_rng_get_byte_fast(stream->sc->engine->rng); silc_mutex_lock(stream->lock); /* Get packet pointer from the outgoing buffer */ - if (!silc_packet_send_prepare(stream, truelen + padlen, hmac, &packet)) { + if (silc_unlikely(!silc_packet_send_prepare(stream, truelen + padlen + ivlen + + psnlen, hmac, &packet))) { silc_mutex_unlock(stream->lock); return FALSE; } + SILC_PUT32_MSB(stream->send_psn, psn); + /* Create the packet. This creates the SILC header, adds padding, and the actual packet data. */ i = silc_buffer_format(&packet, + SILC_STR_DATA(iv, ivlen), + SILC_STR_DATA(psn, psnlen), SILC_STR_UI_SHORT(truelen), SILC_STR_UI_CHAR(flags), SILC_STR_UI_CHAR(type), @@ -922,25 +1424,26 @@ static SilcBool silc_packet_send_raw(SilcPacketStream stream, SILC_STR_UI_CHAR(src_id_len), SILC_STR_UI_CHAR(dst_id_len), SILC_STR_UI_CHAR(src_id_type), - SILC_STR_UI_XNSTRING(src_id, src_id_len), + SILC_STR_DATA(src_id, src_id_len), SILC_STR_UI_CHAR(dst_id_type), - SILC_STR_UI_XNSTRING(dst_id, dst_id_len), - SILC_STR_UI_XNSTRING(tmppad, padlen), - SILC_STR_UI_XNSTRING(data, data_len), + SILC_STR_DATA(dst_id, dst_id_len), + SILC_STR_DATA(tmppad, padlen), + SILC_STR_DATA(data, data_len), SILC_STR_END); - if (i < 0) { + if (silc_unlikely(i < 0)) { silc_mutex_unlock(stream->lock); return FALSE; } SILC_LOG_HEXDUMP(("Assembled packet, len %d", silc_buffer_len(&packet)), - packet.data, silc_buffer_len(&packet)); + silc_buffer_data(&packet), silc_buffer_len(&packet)); /* Encrypt the packet */ - if (cipher) { + if (silc_likely(cipher)) { SILC_LOG_DEBUG(("Encrypting packet")); - if (!silc_cipher_encrypt(cipher, packet.data, packet.data, - enclen, NULL)) { + if (silc_unlikely(!silc_cipher_encrypt(cipher, packet.data + ivlen, + packet.data + ivlen, enclen, + NULL))) { SILC_LOG_ERROR(("Packet encryption failed")); silc_mutex_unlock(stream->lock); return FALSE; @@ -948,53 +1451,19 @@ static SilcBool silc_packet_send_raw(SilcPacketStream stream, } /* Compute HMAC */ - if (hmac) { - unsigned char psn[4]; + if (silc_likely(hmac)) { SilcUInt32 mac_len; /* MAC is computed from the entire encrypted packet data, and put to the end of the packet. */ silc_hmac_init(hmac); - SILC_PUT32_MSB(stream->send_psn, psn); - silc_hmac_update(hmac, psn, 4); + silc_hmac_update(hmac, psn, sizeof(psn)); silc_hmac_update(hmac, packet.data, silc_buffer_len(&packet)); silc_hmac_final(hmac, packet.tail, &mac_len); silc_buffer_pull_tail(&packet, mac_len); stream->send_psn++; } - /* Write the packet to the stream */ - while (silc_buffer_len(&stream->outbuf) > 0) { - i = silc_stream_write(stream->stream, stream->outbuf.data, - silc_buffer_len(&stream->outbuf)); - if (i == 0) { - /* EOS */ - silc_buffer_reset(&stream->outbuf); - silc_mutex_unlock(stream->lock); - SILC_PACKET_CALLBACK_EOS(stream); - return FALSE; - } - - if (i == -2) { - /* Error */ - silc_buffer_reset(&stream->outbuf); - silc_mutex_unlock(stream->lock); - SILC_PACKET_CALLBACK_ERROR(stream, SILC_PACKET_ERR_WRITE); - return FALSE; - } - - if (i == -1) { - /* Cannot write now, write later. */ - silc_mutex_unlock(stream->lock); - return TRUE; - } - - /* Wrote data */ - silc_buffer_pull(&stream->outbuf, i); - } - silc_buffer_reset(&stream->outbuf); - - silc_mutex_unlock(stream->lock); return TRUE; } @@ -1004,16 +1473,21 @@ SilcBool silc_packet_send(SilcPacketStream stream, SilcPacketType type, SilcPacketFlags flags, const unsigned char *data, SilcUInt32 data_len) { - return silc_packet_send_raw(stream, type, flags, - stream->src_id_type, - stream->src_id, - stream->src_id_len, - stream->dst_id_type, - stream->dst_id, - stream->dst_id_len, - data, data_len, - stream->send_key, - stream->send_hmac); + SilcBool ret; + + ret = silc_packet_send_raw(stream, type, flags, + stream->src_id_type, + stream->src_id, + stream->src_id_len, + stream->dst_id_type, + stream->dst_id, + stream->dst_id_len, + data, data_len, + stream->send_key[0], + stream->send_hmac[0]); + + /* Write the packet to the stream */ + return ret ? silc_packet_stream_write(stream, FALSE) : FALSE; } /* Sends a packet, extended routine */ @@ -1027,6 +1501,7 @@ SilcBool silc_packet_send_ext(SilcPacketStream stream, { unsigned char src_id_data[32], dst_id_data[32]; SilcUInt32 src_id_len, dst_id_len; + SilcBool ret; if (src_id) if (!silc_id_id2str(src_id, src_id_type, src_id_data, @@ -1037,31 +1512,90 @@ SilcBool silc_packet_send_ext(SilcPacketStream stream, sizeof(dst_id_data), &dst_id_len)) return FALSE; - return silc_packet_send_raw(stream, type, flags, - src_id_type, - src_id_data, - src_id_len, - dst_id_type, - dst_id_data, - dst_id_len, - data, data_len, - cipher, - hmac); + ret = silc_packet_send_raw(stream, type, flags, + src_id ? src_id_type : stream->src_id_type, + src_id ? src_id_data : stream->src_id, + src_id ? src_id_len : stream->src_id_len, + dst_id ? dst_id_type : stream->dst_id_type, + dst_id ? dst_id_data : stream->dst_id, + dst_id ? dst_id_len : stream->dst_id_len, + data, data_len, + cipher ? cipher : stream->send_key[0], + hmac ? hmac : stream->send_hmac[0]); + + /* Write the packet to the stream */ + return ret ? silc_packet_stream_write(stream, FALSE) : FALSE; +} + +/* Sends packet after formatting the arguments to buffer */ + +SilcBool silc_packet_send_va(SilcPacketStream stream, + SilcPacketType type, SilcPacketFlags flags, ...) +{ + SilcBufferStruct buf; + SilcBool ret; + va_list va; + + va_start(va, flags); + + memset(&buf, 0, sizeof(buf)); + if (silc_buffer_format_vp(&buf, va) < 0) { + va_end(va); + return FALSE; + } + + ret = silc_packet_send(stream, type, flags, silc_buffer_data(&buf), + silc_buffer_len(&buf)); + + silc_buffer_purge(&buf); + va_end(va); + + return ret; } +/* Sends packet after formatting the arguments to buffer, extended routine */ + +SilcBool silc_packet_send_va_ext(SilcPacketStream stream, + SilcPacketType type, SilcPacketFlags flags, + SilcIdType src_id_type, void *src_id, + SilcIdType dst_id_type, void *dst_id, + SilcCipher cipher, SilcHmac hmac, ...) +{ + SilcBufferStruct buf; + SilcBool ret; + va_list va; + + va_start(va, hmac); + + memset(&buf, 0, sizeof(buf)); + if (silc_buffer_format_vp(&buf, va) < 0) { + va_end(va); + return FALSE; + } + + ret = silc_packet_send_ext(stream, type, flags, src_id_type, src_id, + dst_id_type, dst_id, silc_buffer_data(&buf), + silc_buffer_len(&buf), cipher, hmac); + + silc_buffer_purge(&buf); + va_end(va); + + return TRUE; +} /***************************** Packet Receiving *****************************/ /* Checks MAC in the packet. Returns TRUE if MAC is Ok. */ -static SilcBool silc_packet_check_mac(SilcHmac hmac, - const unsigned char *data, - SilcUInt32 data_len, - const unsigned char *packet_mac, - SilcUInt32 sequence) +static inline SilcBool silc_packet_check_mac(SilcHmac hmac, + const unsigned char *data, + SilcUInt32 data_len, + const unsigned char *packet_mac, + const unsigned char *packet_seq, + SilcUInt32 sequence) { /* Check MAC */ - if (hmac) { + if (silc_likely(hmac)) { unsigned char mac[32], psn[4]; SilcUInt32 mac_len; @@ -1069,13 +1603,18 @@ static SilcBool silc_packet_check_mac(SilcHmac hmac, /* Compute HMAC of packet */ silc_hmac_init(hmac); - SILC_PUT32_MSB(sequence, psn); - silc_hmac_update(hmac, psn, 4); + + if (!packet_seq) { + SILC_PUT32_MSB(sequence, psn); + silc_hmac_update(hmac, psn, 4); + } else + silc_hmac_update(hmac, packet_seq, 4); + silc_hmac_update(hmac, data, data_len); silc_hmac_final(hmac, mac, &mac_len); /* Compare the MAC's */ - if (memcmp(packet_mac, mac, mac_len)) { + if (silc_unlikely(memcmp(packet_mac, mac, mac_len))) { SILC_LOG_DEBUG(("MAC failed")); return FALSE; } @@ -1086,26 +1625,51 @@ static SilcBool silc_packet_check_mac(SilcHmac hmac, return TRUE; } +/* Increments/sets counter when decrypting in counter mode. */ + +static inline void silc_packet_receive_ctr_increment(SilcPacketStream stream, + unsigned char *iv, + unsigned char *packet_iv) +{ + SilcUInt32 pc; + + /* 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); + } + + /* Reset block counter */ + memset(iv + 12, 0, 4); + + SILC_LOG_HEXDUMP(("Counter Block"), iv, 16); +} + /* Decrypts SILC packet. Handles both normal and special packet decryption. Return 0 when packet is normal and 1 when it it special, -1 on error. */ -static int silc_packet_decrypt(SilcCipher cipher, SilcHmac hmac, - SilcUInt32 sequence, SilcBuffer buffer, - SilcBool normal) +static inline int silc_packet_decrypt(SilcCipher cipher, SilcHmac hmac, + SilcUInt32 sequence, SilcBuffer buffer, + SilcBool normal) { if (normal == TRUE) { - if (cipher) { + if (silc_likely(cipher)) { /* Decrypt rest of the packet */ SILC_LOG_DEBUG(("Decrypting the packet")); - if (!silc_cipher_decrypt(cipher, buffer->data, buffer->data, - silc_buffer_len(buffer), NULL)) + if (silc_unlikely(!silc_cipher_decrypt(cipher, buffer->data, + buffer->data, + silc_buffer_len(buffer), NULL))) return -1; } return 0; } else { /* Decrypt rest of the header plus padding */ - if (cipher) { + if (silc_likely(cipher)) { SilcUInt16 len; SilcUInt32 block_len = silc_cipher_get_block_len(cipher); @@ -1119,13 +1683,13 @@ static int silc_packet_decrypt(SilcCipher cipher, SilcHmac hmac, block_len); silc_buffer_pull(buffer, block_len); - if (len > silc_buffer_len(buffer)) { + if (silc_unlikely(len > silc_buffer_len(buffer))) { SILC_LOG_ERROR(("Garbage in header of packet, bad packet length, " "packet dropped")); return -1; } - if (!silc_cipher_decrypt(cipher, buffer->data, buffer->data, - len, NULL)) + if (silc_unlikely(!silc_cipher_decrypt(cipher, buffer->data, + buffer->data, len, NULL))) return -1; } @@ -1137,52 +1701,59 @@ static int silc_packet_decrypt(SilcCipher cipher, SilcHmac hmac, parsed. The buffer sent must be already decrypted before calling this function. */ -static SilcBool silc_packet_parse(SilcPacket packet) +static inline SilcBool silc_packet_parse(SilcPacket packet) { SilcBuffer buffer = &packet->buffer; SilcUInt8 padlen = (SilcUInt8)buffer->data[4]; SilcUInt8 src_id_len, dst_id_len, src_id_type, dst_id_type; - int len, ret; + int ret; SILC_LOG_DEBUG(("Parsing incoming packet")); /* Parse the buffer. This parses the SILC header of the packet. */ - len = silc_buffer_unformat(buffer, + ret = silc_buffer_unformat(buffer, + SILC_STR_ADVANCE, SILC_STR_OFFSET(6), SILC_STR_UI_CHAR(&src_id_len), SILC_STR_UI_CHAR(&dst_id_len), SILC_STR_UI_CHAR(&src_id_type), SILC_STR_END); - if (len == -1) { - SILC_LOG_ERROR(("Malformed packet header, packet dropped")); + if (silc_unlikely(ret == -1)) { + if (!packet->stream->udp && + !silc_socket_stream_is_udp(packet->stream->stream, NULL)) + SILC_LOG_ERROR(("Malformed packet header, packet dropped")); return FALSE; } - if (src_id_len > SILC_PACKET_MAX_ID_LEN || - dst_id_len > SILC_PACKET_MAX_ID_LEN) { - SILC_LOG_ERROR(("Bad ID lengths in packet (%d and %d)", - packet->src_id_len, packet->dst_id_len)); + if (silc_unlikely(src_id_len > SILC_PACKET_MAX_ID_LEN || + dst_id_len > SILC_PACKET_MAX_ID_LEN)) { + if (!packet->stream->udp && + !silc_socket_stream_is_udp(packet->stream->stream, NULL)) + SILC_LOG_ERROR(("Bad ID lengths in packet (%d and %d)", + packet->src_id_len, packet->dst_id_len)); return FALSE; } ret = silc_buffer_unformat(buffer, - SILC_STR_OFFSET(len), - SILC_STR_UI_XNSTRING(&packet->src_id, - src_id_len), + SILC_STR_ADVANCE, + SILC_STR_DATA(&packet->src_id, src_id_len), SILC_STR_UI_CHAR(&dst_id_type), - SILC_STR_UI_XNSTRING(&packet->dst_id, - dst_id_len), + SILC_STR_DATA(&packet->dst_id, dst_id_len), SILC_STR_OFFSET(padlen), SILC_STR_END); - if (ret == -1) { - SILC_LOG_ERROR(("Malformed packet header, packet dropped")); + if (silc_unlikely(ret == -1)) { + if (!packet->stream->udp && + !silc_socket_stream_is_udp(packet->stream->stream, NULL)) + SILC_LOG_ERROR(("Malformed packet header, packet dropped")); return FALSE; } - if (src_id_type > SILC_ID_CHANNEL || - dst_id_type > SILC_ID_CHANNEL) { - SILC_LOG_ERROR(("Bad ID types in packet (%d and %d)", - src_id_type, dst_id_type)); + if (silc_unlikely(src_id_type > SILC_ID_CHANNEL || + dst_id_type > SILC_ID_CHANNEL)) { + if (!packet->stream->udp && + !silc_socket_stream_is_udp(packet->stream->stream, NULL)) + SILC_LOG_ERROR(("Bad ID types in packet (%d and %d)", + src_id_type, dst_id_type)); return FALSE; } @@ -1191,12 +1762,9 @@ static SilcBool silc_packet_parse(SilcPacket packet) packet->src_id_type = src_id_type; packet->dst_id_type = dst_id_type; - SILC_LOG_HEXDUMP(("Parsed packet, len %d", silc_buffer_len(buffer)), - buffer->data, silc_buffer_len(buffer)); - - /* Pull SILC header and padding from packet to get the data payload */ - silc_buffer_pull(buffer, SILC_PACKET_HEADER_LEN + - packet->src_id_len + packet->dst_id_len + padlen); + SILC_LOG_HEXDUMP(("Parsed packet, len %d", silc_buffer_headlen(buffer) + + silc_buffer_len(buffer)), buffer->head, + silc_buffer_headlen(buffer) + silc_buffer_len(buffer)); SILC_LOG_DEBUG(("Incoming packet type: %d (%s)", packet->type, silc_get_packet_name(packet->type))); @@ -1204,37 +1772,29 @@ static SilcBool silc_packet_parse(SilcPacket packet) return TRUE; } -/* Dispatch packet to application. Called with stream->lock locked. */ +/* Dispatch packet to application. Called with stream->lock locked. + Returns FALSE if the stream was destroyed while dispatching a packet. */ -static void silc_packet_dispatch(SilcPacket packet) +static SilcBool silc_packet_dispatch(SilcPacket packet) { SilcPacketStream stream = packet->stream; SilcPacketProcess p; SilcBool default_sent = FALSE; SilcPacketType *pt; - /* Parse the packet */ - if (!silc_packet_parse(packet)) { - silc_mutex_unlock(packet->stream->lock); - SILC_PACKET_CALLBACK_ERROR(stream, SILC_PACKET_ERR_MALFORMED); - silc_mutex_lock(packet->stream->lock); - silc_packet_free(packet); - return; - } - /* Dispatch packet to all packet processors that want it */ - if (!stream->process) { + if (silc_likely(!stream->process)) { /* Send to default processor as no others exist */ SILC_LOG_DEBUG(("Dispatching packet to default callbacks")); - silc_mutex_unlock(packet->stream->lock); - if (!stream->engine->callbacks-> - packet_receive(stream->engine, stream, packet, - stream->engine->callback_context, - stream->stream_context)) + silc_mutex_unlock(stream->lock); + if (silc_unlikely(!stream->sc->engine->callbacks-> + packet_receive(stream->sc->engine, stream, packet, + stream->sc->engine->callback_context, + stream->stream_context))) silc_packet_free(packet); - silc_mutex_lock(packet->stream->lock); - return; + silc_mutex_lock(stream->lock); + return stream->destroyed == FALSE; } silc_dlist_start(stream->process); @@ -1245,43 +1805,43 @@ static void silc_packet_dispatch(SilcPacket packet) if (!default_sent && p->priority <= 0) { SILC_LOG_DEBUG(("Dispatching packet to default callbacks")); default_sent = TRUE; - silc_mutex_unlock(packet->stream->lock); - if (stream->engine->callbacks-> - packet_receive(stream->engine, stream, packet, - stream->engine->callback_context, + 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(packet->stream->lock); - return; + silc_mutex_lock(stream->lock); + return stream->destroyed == FALSE; } - silc_mutex_lock(packet->stream->lock); + silc_mutex_lock(stream->lock); } /* Send to processor */ if (!p->types) { /* Send all packet types */ SILC_LOG_DEBUG(("Dispatching packet to %p callbacks", p->callbacks)); - silc_mutex_unlock(packet->stream->lock); - if (p->callbacks->packet_receive(stream->engine, stream, packet, + silc_mutex_unlock(stream->lock); + if (p->callbacks->packet_receive(stream->sc->engine, stream, packet, p->callback_context, stream->stream_context)) { - silc_mutex_lock(packet->stream->lock); - return; + silc_mutex_lock(stream->lock); + return stream->destroyed == FALSE; } - silc_mutex_lock(packet->stream->lock); + 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(packet->stream->lock); - if (p->callbacks->packet_receive(stream->engine, stream, packet, + silc_mutex_unlock(stream->lock); + if (p->callbacks->packet_receive(stream->sc->engine, stream, packet, p->callback_context, stream->stream_context)) { - silc_mutex_lock(packet->stream->lock); - return; + silc_mutex_lock(stream->lock); + return stream->destroyed == FALSE; } - silc_mutex_lock(packet->stream->lock); + silc_mutex_lock(stream->lock); break; } } @@ -1290,19 +1850,20 @@ static void silc_packet_dispatch(SilcPacket packet) 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(packet->stream->lock); - if (stream->engine->callbacks-> - packet_receive(stream->engine, stream, packet, - stream->engine->callback_context, + 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(packet->stream->lock); - return; + silc_mutex_lock(stream->lock); + return stream->destroyed == FALSE; } - silc_mutex_lock(packet->stream->lock); + 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 @@ -1310,86 +1871,149 @@ static void silc_packet_dispatch(SilcPacket packet) 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; + 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]; - SilcBool normal = TRUE; + 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(&stream->inbuf) > 0) { - - if (silc_buffer_len(&stream->inbuf) < SILC_PACKET_MIN_HEADER_LEN) { + 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 (stream->receive_hmac) - mac_len = silc_hmac_len(stream->receive_hmac); + 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 (stream->receive_key) { - block_len = silc_cipher_get_block_len(stream->receive_key); - memcpy(iv, silc_cipher_get_iv(stream->receive_key), block_len); - silc_cipher_decrypt(stream->receive_key, stream->inbuf.data, - tmp, block_len, iv); + 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 = stream->inbuf.data; + header = inbuf->data; } /* Get packet length and full packet length with padding */ SILC_PACKET_LENGTH(header, packetlen, paddedlen); /* Sanity checks */ - if (packetlen < SILC_PACKET_MIN_LEN) { - SILC_LOG_ERROR(("Received too short packet")); + 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(&stream->inbuf); + silc_buffer_reset(inbuf); return; } - if (silc_buffer_len(&stream->inbuf) < paddedlen + mac_len) { + 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(&stream->inbuf))); + paddedlen + mac_len - silc_buffer_len(inbuf))); memset(tmp, 0, sizeof(tmp)); return; } /* Check MAC of the packet */ - if (!silc_packet_check_mac(stream->receive_hmac, stream->inbuf.data, - paddedlen, stream->inbuf.data + paddedlen, - stream->receive_psn)) { + 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(&stream->inbuf); + silc_buffer_reset(inbuf); return; } /* Get packet */ - packet = silc_packet_alloc(stream->engine); - if (!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(&stream->inbuf); + silc_buffer_reset(inbuf); return; } + packet->stream = stream; /* Allocate more space to packet buffer, if needed */ - if (silc_buffer_truelen(&packet->buffer) < paddedlen) { + if (silc_unlikely(silc_buffer_truelen(&packet->buffer) < paddedlen)) { if (!silc_buffer_realloc(&packet->buffer, silc_buffer_truelen(&packet->buffer) + (paddedlen - @@ -1399,7 +2023,7 @@ static void silc_packet_read_process(SilcPacketStream stream) silc_mutex_lock(stream->lock); silc_packet_free(packet); memset(tmp, 0, sizeof(tmp)); - silc_buffer_reset(&stream->inbuf); + silc_buffer_reset(inbuf); return; } } @@ -1408,7 +2032,7 @@ static void silc_packet_read_process(SilcPacketStream stream) packet->flags = (SilcPacketFlags)header[2]; packet->type = (SilcPacketType)header[3]; - if (stream->engine->local_is_router) { + if (stream->sc->engine->local_is_router) { if (packet->type == SILC_PACKET_PRIVATE_MESSAGE && (packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY)) normal = FALSE; @@ -1425,20 +2049,21 @@ static void silc_packet_read_process(SilcPacketStream stream) } SILC_LOG_HEXDUMP(("Incoming packet (%d) len %d", - stream->receive_psn, paddedlen + mac_len), - stream->inbuf.data, paddedlen + mac_len); + 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); - silc_buffer_pull(&packet->buffer, block_len); - silc_buffer_put(&packet->buffer, stream->inbuf.data + block_len, - paddedlen - block_len); - if (stream->receive_key) { - silc_cipher_set_iv(stream->receive_key, iv); - ret = silc_packet_decrypt(stream->receive_key, stream->receive_hmac, - stream->receive_psn, &packet->buffer, normal); - if (ret < 0) { + 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); @@ -1452,14 +2077,24 @@ static void silc_packet_read_process(SilcPacketStream stream) 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(&stream->inbuf, paddedlen + mac_len); + 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 */ - packet->stream = stream; - silc_packet_dispatch(packet); + if (!silc_packet_dispatch(packet)) + break; } - silc_buffer_reset(&stream->inbuf); + silc_buffer_reset(inbuf); } @@ -1484,7 +2119,7 @@ typedef struct { SilcMutex wait_lock; SilcCond wait_cond; SilcList packet_queue; - SilcBool waiting; + unsigned int stopped : 1; } *SilcPacketWait; /* Packet wait receive callback */ @@ -1501,7 +2136,7 @@ silc_packet_wait_packet_receive(SilcPacketEngine engine, /* Signal the waiting thread for a new packet */ silc_mutex_lock(pw->wait_lock); - if (!pw->waiting) { + if (silc_unlikely(pw->stopped)) { silc_mutex_unlock(pw->wait_lock); return FALSE; } @@ -1557,11 +2192,18 @@ void *silc_packet_wait_init(SilcPacketStream stream, ...) /* Uninitialize packet waiting */ -void silc_packet_wait_uninit(void *context, SilcPacketStream stream) +void silc_packet_wait_uninit(void *waiter, SilcPacketStream stream) { - SilcPacketWait pw = context; + SilcPacketWait pw = waiter; SilcPacket packet; + /* Signal any threads to stop waiting */ + silc_mutex_lock(pw->wait_lock); + pw->stopped = TRUE; + silc_cond_broadcast(pw->wait_cond); + silc_mutex_unlock(pw->wait_lock); + + /* Re-acquire lock and free resources */ silc_mutex_lock(pw->wait_lock); silc_packet_stream_unlink(stream, &silc_packet_wait_cbs, pw); @@ -1571,7 +2213,6 @@ void silc_packet_wait_uninit(void *context, SilcPacketStream stream) 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); @@ -1579,17 +2220,21 @@ void silc_packet_wait_uninit(void *context, SilcPacketStream stream) /* Blocks thread until a packet has been received. */ -int silc_packet_wait(void *context, int timeout, SilcPacket *return_packet) +int silc_packet_wait(void *waiter, int timeout, SilcPacket *return_packet) { - SilcPacketWait pw = context; + SilcPacketWait pw = waiter; 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) + while (silc_list_count(pw->packet_queue) == 0) { + if (silc_unlikely(pw->stopped)) { + silc_mutex_unlock(pw->wait_lock); + return -1; + } ret = silc_cond_timedwait(pw->wait_cond, pw->wait_lock, timeout); + } /* Return packet */ silc_list_start(pw->packet_queue);