X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=lib%2Fsilccore%2Fsilcpacket.c;h=26f721f56f0d34448725a4c7a1e43673702e1429;hp=57bf5148d712613903f83381b48a9759da409ba7;hb=805fddcf6431e784f9f77114782a90c9d12f9cbe;hpb=6cda49040217e0a1a547cce09662dc683c38b3fd diff --git a/lib/silccore/silcpacket.c b/lib/silccore/silcpacket.c index 57bf5148..26f721f5 100644 --- a/lib/silccore/silcpacket.c +++ b/lib/silccore/silcpacket.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2007 Pekka Riikonen + Copyright (C) 1997 - 2008 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 @@ -40,7 +40,7 @@ struct SilcPacketEngineStruct { SilcMutex lock; /* Engine lock */ SilcRng rng; /* RNG for engine */ SilcHashTable contexts; /* Per scheduler contexts */ - SilcPacketCallbacks *callbacks; /* Packet callbacks */ + const SilcPacketCallbacks *callbacks; /* Packet callbacks */ void *callback_context; /* Context for callbacks */ SilcList streams; /* All streams in engine */ SilcList packet_pool; /* Free list for received packets */ @@ -51,7 +51,7 @@ struct SilcPacketEngineStruct { /* Packet processor context */ typedef struct SilcPacketProcessStruct { SilcPacketType *types; /* Packets to process */ - SilcPacketCallbacks *callbacks; /* Callbacks or NULL */ + const SilcPacketCallbacks *callbacks; /* Callbacks or NULL */ void *callback_context; SilcInt32 priority; /* Priority */ } *SilcPacketProcess; @@ -81,7 +81,7 @@ struct SilcPacketStreamStruct { unsigned char *dst_id; /* Destination ID */ SilcUInt32 send_psn; /* Sending sequence */ SilcUInt32 receive_psn; /* Receiving sequence */ - SilcAtomic8 refcnt; /* Reference counter */ + SilcAtomic32 refcnt; /* Reference counter */ SilcUInt8 sid; /* Security ID, set if IV included */ unsigned int src_id_len : 6; unsigned int src_id_type : 2; @@ -306,7 +306,7 @@ static inline SilcBool silc_packet_stream_read(SilcPacketStream ps, inbuf = silc_dlist_get(ps->sc->inbufs); if (!inbuf) { /* Allocate new data input buffer */ - inbuf = silc_buffer_alloc(SILC_PACKET_DEFAULT_SIZE * 31); + inbuf = silc_buffer_alloc(SILC_PACKET_DEFAULT_SIZE * 65); if (!inbuf) { silc_mutex_unlock(ps->lock); return FALSE; @@ -336,7 +336,6 @@ static inline SilcBool silc_packet_stream_read(SilcPacketStream ps, silc_mutex_unlock(ps->lock); if (ret == -1) { /* Cannot read now, do it later. */ - silc_buffer_pull(inbuf, silc_buffer_len(inbuf)); return FALSE; } @@ -394,7 +393,6 @@ static inline SilcBool silc_packet_stream_read(SilcPacketStream ps, if (ret == -1) { /* Cannot read now, do it later. */ - silc_buffer_pull(inbuf, silc_buffer_len(inbuf)); return FALSE; } @@ -542,7 +540,7 @@ static void silc_packet_engine_context_destr(void *key, void *context, SilcPacketEngine silc_packet_engine_start(SilcRng rng, SilcBool router, - SilcPacketCallbacks *callbacks, + const SilcPacketCallbacks *callbacks, void *callback_context) { SilcPacketEngine engine; @@ -561,7 +559,8 @@ 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, + engine->contexts = silc_hash_table_alloc(NULL, 0, silc_hash_ptr, + NULL, NULL, NULL, silc_packet_engine_context_destr, engine, TRUE); if (!engine->contexts) { @@ -623,6 +622,25 @@ void silc_packet_engine_stop(SilcPacketEngine engine) silc_free(engine); } +static const char * const packet_error[] = { + "Cannot read from stream", + "Cannot write to stream", + "Packet MAC failed", + "Packet decryption failed", + "Unknown SID", + "Packet is malformed", + "System out of memory", +}; + +/* Return packet error string */ + +const char *silc_packet_error_string(SilcPacketError error) +{ + if (error < SILC_PACKET_ERR_READ || error > SILC_PACKET_ERR_NO_MEMORY) + return ""; + return packet_error[error]; +} + /* Return list of packet streams in the engine */ SilcDList silc_packet_engine_get_streams(SilcPacketEngine engine) @@ -636,13 +654,28 @@ SilcDList silc_packet_engine_get_streams(SilcPacketEngine engine) silc_mutex_lock(engine->lock); silc_list_start(engine->streams); - while ((ps = silc_list_get(engine->streams))) + while ((ps = silc_list_get(engine->streams))) { + silc_packet_stream_ref(ps); silc_dlist_add(list, ps); + } silc_mutex_unlock(engine->lock); return list; } +/* Free list returned by silc_packet_engine_get_streams */ + +void silc_packet_engine_free_streams_list(SilcDList streams) +{ + SilcPacketStream ps; + + silc_dlist_start(streams); + while ((ps = silc_dlist_get(streams))) + silc_packet_stream_unref(ps); + + silc_dlist_uninit(streams); +} + /* Create new packet stream */ SilcPacketStream silc_packet_stream_create(SilcPacketEngine engine, @@ -663,7 +696,7 @@ SilcPacketStream silc_packet_stream_create(SilcPacketEngine engine, return NULL; ps->stream = stream; - silc_atomic_init8(&ps->refcnt, 1); + silc_atomic_init32(&ps->refcnt, 1); silc_mutex_alloc(&ps->lock); /* Allocate out buffer */ @@ -689,20 +722,20 @@ SilcPacketStream silc_packet_stream_create(SilcPacketEngine engine, (void *)&ps->sc)) { ps->sc = silc_calloc(1, sizeof(*ps->sc)); if (!ps->sc) { - silc_packet_stream_destroy(ps); silc_mutex_unlock(engine->lock); + silc_packet_stream_destroy(ps); return NULL; } ps->sc->engine = engine; ps->sc->schedule = schedule; /* Allocate data input buffer */ - inbuf = silc_buffer_alloc(SILC_PACKET_DEFAULT_SIZE * 31); + inbuf = silc_buffer_alloc(SILC_PACKET_DEFAULT_SIZE * 65); if (!inbuf) { silc_free(ps->sc); ps->sc = NULL; - silc_packet_stream_destroy(ps); silc_mutex_unlock(engine->lock); + silc_packet_stream_destroy(ps); return NULL; } silc_buffer_reset(inbuf); @@ -712,8 +745,8 @@ SilcPacketStream silc_packet_stream_create(SilcPacketEngine engine, silc_buffer_free(inbuf); silc_free(ps->sc); ps->sc = NULL; - silc_packet_stream_destroy(ps); silc_mutex_unlock(engine->lock); + silc_packet_stream_destroy(ps); return NULL; } silc_dlist_add(ps->sc->inbufs, inbuf); @@ -724,8 +757,8 @@ SilcPacketStream silc_packet_stream_create(SilcPacketEngine engine, silc_dlist_del(ps->sc->inbufs, inbuf); silc_free(ps->sc); ps->sc = NULL; - silc_packet_stream_destroy(ps); silc_mutex_unlock(engine->lock); + silc_packet_stream_destroy(ps); return NULL; } } @@ -736,10 +769,10 @@ SilcPacketStream silc_packet_stream_create(SilcPacketEngine engine, /* 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); + engine->udp_remote = + silc_hash_table_alloc(NULL, 0, silc_hash_string_case, NULL, + silc_hash_string_case_compare, NULL, + silc_packet_engine_hash_destr, NULL, TRUE); silc_mutex_unlock(engine->lock); @@ -751,6 +784,8 @@ SilcPacketStream silc_packet_stream_create(SilcPacketEngine engine, return NULL; } + SILC_LOG_DEBUG(("Created packet stream %p", ps)); + return ps; } @@ -782,7 +817,7 @@ SilcPacketStream silc_packet_stream_add_remote(SilcPacketStream stream, return NULL; ps->sc = stream->sc; - silc_atomic_init8(&ps->refcnt, 1); + silc_atomic_init32(&ps->refcnt, 1); silc_mutex_alloc(&ps->lock); /* Set the UDP packet stream as underlaying stream */ @@ -850,9 +885,13 @@ void silc_packet_stream_destroy(SilcPacketStream stream) if (!stream) return; - if (silc_atomic_sub_int8(&stream->refcnt, 1) > 0) { + if (silc_atomic_sub_int32(&stream->refcnt, 1) > 0) { + if (stream->destroyed) + return; stream->destroyed = TRUE; + SILC_LOG_DEBUG(("Marking packet stream %p destroyed", stream)); + /* Close the underlaying stream */ if (!stream->udp && stream->stream) silc_stream_close(stream->stream); @@ -863,17 +902,18 @@ void silc_packet_stream_destroy(SilcPacketStream stream) if (!stream->udp) { /* Delete from engine */ - 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) { + 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 */ stream->sc->stream_count--; if (!stream->sc->stream_count) silc_hash_table_del(engine->contexts, stream->sc->schedule); + + silc_mutex_unlock(engine->lock); } - silc_mutex_unlock(engine->lock); /* Destroy the underlaying stream */ if (stream->stream) @@ -933,7 +973,7 @@ void silc_packet_stream_destroy(SilcPacketStream stream) silc_free(stream->src_id); silc_free(stream->dst_id); - silc_atomic_uninit8(&stream->refcnt); + silc_atomic_uninit32(&stream->refcnt); silc_mutex_free(stream->lock); silc_free(stream); } @@ -962,7 +1002,7 @@ void silc_packet_stream_set_iv_included(SilcPacketStream stream) /* Links `callbacks' to `stream' for specified packet types */ static SilcBool silc_packet_stream_link_va(SilcPacketStream stream, - SilcPacketCallbacks *callbacks, + const SilcPacketCallbacks *callbacks, void *callback_context, int priority, va_list ap) { @@ -991,6 +1031,7 @@ static SilcBool silc_packet_stream_link_va(SilcPacketStream stream, stream->process = silc_dlist_init(); if (!stream->process) { silc_mutex_unlock(stream->lock); + silc_free(p); return FALSE; } } @@ -1040,7 +1081,7 @@ static SilcBool silc_packet_stream_link_va(SilcPacketStream stream, /* Links `callbacks' to `stream' for specified packet types */ SilcBool silc_packet_stream_link(SilcPacketStream stream, - SilcPacketCallbacks *callbacks, + const SilcPacketCallbacks *callbacks, void *callback_context, int priority, ...) { @@ -1058,7 +1099,7 @@ SilcBool silc_packet_stream_link(SilcPacketStream stream, /* Unlinks `callbacks' from `stream'. */ void silc_packet_stream_unlink(SilcPacketStream stream, - SilcPacketCallbacks *callbacks, + const SilcPacketCallbacks *callbacks, void *callback_context) { SilcPacketProcess p; @@ -1114,10 +1155,10 @@ SilcBool silc_packet_get_sender(SilcPacket packet, void silc_packet_stream_ref(SilcPacketStream stream) { - silc_atomic_add_int8(&stream->refcnt, 1); + silc_atomic_add_int32(&stream->refcnt, 1); SILC_LOG_DEBUG(("Stream %p, refcnt %d->%d", stream, - silc_atomic_get_int8(&stream->refcnt) - 1, - silc_atomic_get_int8(&stream->refcnt))); + silc_atomic_get_int32(&stream->refcnt) - 1, + silc_atomic_get_int32(&stream->refcnt))); } /* Unreference packet stream */ @@ -1125,11 +1166,11 @@ void silc_packet_stream_ref(SilcPacketStream stream) void silc_packet_stream_unref(SilcPacketStream stream) { SILC_LOG_DEBUG(("Stream %p, refcnt %d->%d", stream, - silc_atomic_get_int8(&stream->refcnt), - silc_atomic_get_int8(&stream->refcnt) - 1)); - if (silc_atomic_sub_int8(&stream->refcnt, 1) > 0) + silc_atomic_get_int32(&stream->refcnt), + silc_atomic_get_int32(&stream->refcnt) - 1)); + if (silc_atomic_sub_int32(&stream->refcnt, 1) > 0) return; - silc_atomic_add_int8(&stream->refcnt, 1); + silc_atomic_add_int32(&stream->refcnt, 1); silc_packet_stream_destroy(stream); } @@ -1284,40 +1325,45 @@ SilcBool silc_packet_set_ids(SilcPacketStream stream, { SilcUInt32 len; unsigned char tmp[32]; + void *tmp_id; if (!src_id && !dst_id) return FALSE; - SILC_LOG_DEBUG(("Setting new IDs to packet stream")); - silc_mutex_lock(stream->lock); if (src_id) { - silc_free(stream->src_id); + SILC_LOG_DEBUG(("Setting source ID to packet stream %p", stream)); + if (!silc_id_id2str(src_id, src_id_type, tmp, sizeof(tmp), &len)) { silc_mutex_unlock(stream->lock); return FALSE; } - stream->src_id = silc_memdup(tmp, len); - if (!stream->src_id) { + tmp_id = silc_memdup(tmp, len); + if (!tmp_id) { silc_mutex_unlock(stream->lock); return FALSE; } + silc_free(stream->src_id); + stream->src_id = tmp_id; stream->src_id_type = src_id_type; stream->src_id_len = len; } if (dst_id) { - silc_free(stream->dst_id); + SILC_LOG_DEBUG(("Setting destination ID to packet stream %p", stream)); + if (!silc_id_id2str(dst_id, dst_id_type, tmp, sizeof(tmp), &len)) { silc_mutex_unlock(stream->lock); return FALSE; } - stream->dst_id = silc_memdup(tmp, len); - if (!stream->dst_id) { + tmp_id = silc_memdup(tmp, len); + if (!tmp_id) { silc_mutex_unlock(stream->lock); return FALSE; } + silc_free(stream->dst_id); + stream->dst_id = tmp_id; stream->dst_id_type = dst_id_type; stream->dst_id_len = len; } @@ -1333,39 +1379,21 @@ SilcBool silc_packet_get_ids(SilcPacketStream stream, SilcBool *src_id_set, SilcID *src_id, SilcBool *dst_id_set, SilcID *dst_id) { - if (src_id && stream->src_id) { - (*src_id).type = stream->src_id_type; - switch (stream->src_id_type) { - case SILC_ID_CLIENT: - (*src_id).u.client_id = *(SilcClientID *)stream->src_id; - break; - case SILC_ID_SERVER: - (*src_id).u.server_id = *(SilcServerID *)stream->src_id; - break; - case SILC_ID_CHANNEL: - (*src_id).u.channel_id = *(SilcChannelID *)stream->src_id; - break; - } - if (src_id_set) - *src_id_set = TRUE; - } + if (src_id && stream->src_id) + if (!silc_id_str2id2(stream->src_id, stream->src_id_len, + stream->src_id_type, src_id)) + return FALSE; - if (dst_id && stream->dst_id) { - (*dst_id).type = stream->dst_id_type; - switch (stream->dst_id_type) { - case SILC_ID_CLIENT: - (*dst_id).u.client_id = *(SilcClientID *)stream->dst_id; - break; - case SILC_ID_SERVER: - (*dst_id).u.server_id = *(SilcServerID *)stream->dst_id; - break; - case SILC_ID_CHANNEL: - (*dst_id).u.channel_id = *(SilcChannelID *)stream->dst_id; - break; - } - if (dst_id_set) - *dst_id_set = TRUE; - } + if (stream->src_id && src_id_set) + *src_id_set = TRUE; + + if (dst_id && stream->dst_id) + if (!silc_id_str2id2(stream->dst_id, stream->dst_id_len, + stream->dst_id_type, dst_id)) + return FALSE; + + if (stream->dst_id && dst_id_set) + *dst_id_set = TRUE; return TRUE; } @@ -1449,14 +1477,6 @@ static inline void silc_packet_send_ctr_increment(SilcPacketStream stream, unsigned char *iv = silc_cipher_get_iv(cipher); SilcUInt32 pc1, pc2; - /* 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); @@ -1467,17 +1487,30 @@ static inline void silc_packet_send_ctr_increment(SilcPacketStream stream, 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(pc2, ret_iv + 4); + + /* Increment 32-bit packet counter */ + SILC_GET32_MSB(pc1, iv + 8); + pc1++; + SILC_PUT32_MSB(pc1, ret_iv + 4); + SILC_LOG_HEXDUMP(("IV"), ret_iv, 8); - /* Set new nonce to counter block */ - memcpy(iv + 4, ret_iv, 4); + /* Set new IV to counter block */ + memcpy(iv + 4, ret_iv, 8); + } else { + /* 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); } SILC_LOG_HEXDUMP(("Counter Block"), iv, 16); } -/* Internal routine to assemble outgoing packet. Assembles and encryptes +/* Internal routine to assemble outgoing packet. Assembles and encrypts the packet. The silc_packet_stream_write needs to be called to send it after this returns TRUE. */ @@ -1586,6 +1619,12 @@ static inline SilcBool silc_packet_send_raw(SilcPacketStream stream, silc_mutex_lock(stream->lock); + if (silc_unlikely(stream->destroyed)) { + SILC_LOG_DEBUG(("Stream %p is destroyed, cannot send packet", stream)); + silc_mutex_unlock(stream->lock); + return FALSE; + } + /* Get packet pointer from the outgoing buffer */ if (silc_unlikely(!silc_packet_send_prepare(stream, truelen + padlen + ivlen + psnlen, hmac, &packet))) { @@ -1954,8 +1993,8 @@ static inline SilcBool silc_packet_parse(SilcPacket packet) 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))); + SILC_LOG_DEBUG(("Incoming packet type: %d (%s), flags %d", packet->type, + silc_get_packet_name(packet->type), packet->flags)); return TRUE; } @@ -2312,7 +2351,7 @@ silc_packet_wait_packet_receive(SilcPacketEngine engine, void *stream_context); /* Packet waiting callbacks */ -static SilcPacketCallbacks silc_packet_wait_cbs = +static const SilcPacketCallbacks silc_packet_wait_cbs = { silc_packet_wait_packet_receive, NULL, NULL }; @@ -2322,6 +2361,9 @@ typedef struct { SilcMutex wait_lock; SilcCond wait_cond; SilcList packet_queue; + unsigned char id[28]; + unsigned int id_type : 2; + unsigned int id_len : 5; unsigned int stopped : 1; } *SilcPacketWait; @@ -2336,6 +2378,13 @@ silc_packet_wait_packet_receive(SilcPacketEngine engine, { SilcPacketWait pw = callback_context; + /* If source ID is specified check for it */ + if (pw->id_len) { + if (pw->id_type != packet->src_id_type || + memcmp(pw->id, packet->src_id, pw->id_len)) + return FALSE; + } + /* Signal the waiting thread for a new packet */ silc_mutex_lock(pw->wait_lock); @@ -2354,7 +2403,8 @@ silc_packet_wait_packet_receive(SilcPacketEngine engine, /* Initialize packet waiting */ -void *silc_packet_wait_init(SilcPacketStream stream, ...) +void *silc_packet_wait_init(SilcPacketStream stream, + const SilcID *source_id, ...) { SilcPacketWait pw; SilcBool ret; @@ -2376,7 +2426,7 @@ void *silc_packet_wait_init(SilcPacketStream stream, ...) } /* Link to the packet stream for the requested packet types */ - va_start(ap, stream); + va_start(ap, source_id); ret = silc_packet_stream_link_va(stream, &silc_packet_wait_cbs, pw, 10000000, ap); va_end(ap); @@ -2390,6 +2440,14 @@ void *silc_packet_wait_init(SilcPacketStream stream, ...) /* Initialize packet queue */ silc_list_init(pw->packet_queue, struct SilcPacketStruct, next); + if (source_id) { + SilcUInt32 id_len; + silc_id_id2str(SILC_ID_GET_ID(*source_id), source_id->type, pw->id, + sizeof(pw->id), &id_len); + pw->id_type = source_id->type; + pw->id_len = id_len; + } + return (void *)pw; } @@ -2482,7 +2540,7 @@ typedef struct { } *SilcPacketWrapperStream; /* Packet wrapper callbacks */ -static SilcPacketCallbacks silc_packet_wrap_cbs = +static const SilcPacketCallbacks silc_packet_wrap_cbs = { silc_packet_wrap_packet_receive, NULL, NULL }; @@ -2725,7 +2783,7 @@ SilcStream silc_packet_stream_wrap(SilcPacketStream stream, if (pws->blocking) { /* Blocking mode. Use packet waiter to do the thing. */ - pws->waiter = silc_packet_wait_init(pws->stream, pws->type, -1); + pws->waiter = silc_packet_wait_init(pws->stream, NULL, pws->type, -1); if (!pws->waiter) { silc_free(pws); return NULL;