+ /* According to priority set the procesor to correct position. First
+ entry has the highest priority */
+ silc_dlist_start(stream->process);
+ while ((e = silc_dlist_get(stream->process)) != SILC_LIST_END) {
+ if (p->priority > e->priority) {
+ silc_dlist_insert(stream->process, p);
+ break;
+ }
+ }
+ if (!e)
+ silc_dlist_add(stream->process, p);
+
+ /* Get packet types to process */
+ i = 1;
+ while (1) {
+ packet_type = va_arg(ap, SilcInt32);
+
+ if (packet_type == SILC_PACKET_ANY)
+ break;
+
+ if (packet_type == -1)
+ break;
+
+ p->types = silc_realloc(p->types, sizeof(*p->types) * (i + 1));
+ 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;
+
+ 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,
+ const 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,
+ const SilcPacketCallbacks *callbacks,
+ void *callback_context)
+{
+ SilcPacketProcess p;
+
+ SILC_LOG_DEBUG(("Unlinking callbacks %p from stream %p",
+ callbacks, stream));
+
+ silc_mutex_lock(stream->lock);
+
+ silc_dlist_start(stream->process);
+ while ((p = silc_dlist_get(stream->process)) != SILC_LIST_END)
+ if (p->callbacks == callbacks &&
+ p->callback_context == callback_context) {
+ silc_dlist_del(stream->process, p);
+ silc_free(p->types);
+ silc_free(p);
+ break;
+ }
+
+ if (!silc_dlist_count(stream->process)) {
+ silc_dlist_uninit(stream->process);
+ stream->process = NULL;
+ }
+
+ silc_mutex_unlock(stream->lock);
+
+ 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_atomic_add_int32(&stream->refcnt, 1);
+ SILC_LOG_DEBUG(("Stream %p, refcnt %d->%d", stream,
+ silc_atomic_get_int32(&stream->refcnt) - 1,
+ silc_atomic_get_int32(&stream->refcnt)));
+}
+
+/* Unreference packet stream */
+
+void silc_packet_stream_unref(SilcPacketStream stream)
+{
+ SILC_LOG_DEBUG(("Stream %p, refcnt %d->%d", stream,
+ 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_int32(&stream->refcnt, 1);
+ silc_packet_stream_destroy(stream);
+}
+
+/* Return engine */
+
+SilcPacketEngine silc_packet_get_engine(SilcPacketStream stream)
+{
+ return stream->sc->engine;
+}
+
+/* Set application context for packet stream */
+
+void silc_packet_set_context(SilcPacketStream stream, void *stream_context)
+{
+ silc_mutex_lock(stream->lock);
+ stream->stream_context = stream_context;
+ silc_mutex_unlock(stream->lock);
+}
+
+/* Return application context from packet stream */
+
+void *silc_packet_get_context(SilcPacketStream stream)
+{
+ void *context;
+ silc_mutex_lock(stream->lock);
+ context = stream->stream_context;
+ silc_mutex_unlock(stream->lock);
+ return context;
+}
+
+/* Change underlaying stream */
+
+void silc_packet_stream_set_stream(SilcPacketStream ps,
+ SilcStream 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);
+}
+
+/* Return underlaying stream */
+
+SilcStream silc_packet_stream_get_stream(SilcPacketStream stream)
+{
+ return stream->stream;
+}
+
+/* Set keys. */
+
+SilcBool silc_packet_set_keys(SilcPacketStream stream, SilcCipher send_key,
+ SilcCipher receive_key, SilcHmac send_hmac,
+ SilcHmac receive_hmac, SilcBool rekey)
+{
+ 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;
+
+ /* Write the packet to the stream */
+ if (!silc_packet_stream_write(stream, TRUE))
+ return FALSE;
+ } else {
+ silc_mutex_lock(stream->lock);
+ }
+
+ /* 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->receive_key[0] && receive_key)
+ silc_cipher_free(stream->receive_key[0]);
+ if (stream->send_hmac[0] && send_hmac)
+ silc_hmac_free(stream->send_hmac[0]);
+ if (stream->receive_hmac[0] && receive_hmac)
+ silc_hmac_free(stream->receive_hmac[0]);
+ }
+
+ /* 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;
+
+ silc_mutex_unlock(stream->lock);
+ return TRUE;
+}
+
+/* Return current ciphers from packet stream */
+
+SilcBool silc_packet_get_keys(SilcPacketStream stream,
+ SilcCipher *send_key,
+ SilcCipher *receive_key,
+ SilcHmac *send_hmac,
+ SilcHmac *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_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);
+
+ return TRUE;
+}
+
+/* Set SILC IDs to packet stream */
+
+SilcBool silc_packet_set_ids(SilcPacketStream stream,
+ SilcIdType src_id_type, const void *src_id,
+ SilcIdType dst_id_type, const void *dst_id)
+{
+ SilcUInt32 len;
+ unsigned char tmp[32];
+ void *tmp_id;
+
+ if (!src_id && !dst_id)
+ return FALSE;
+
+ silc_mutex_lock(stream->lock);
+
+ if (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;
+ }
+ 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_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;
+ }
+ 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;
+ }
+
+ silc_mutex_unlock(stream->lock);
+
+ return TRUE;
+}
+
+/* Return IDs from the packet stream */
+
+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)
+ if (!silc_id_str2id2(stream->src_id, stream->src_id_len,
+ stream->src_id_type, src_id))
+ return FALSE;
+
+ 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;
+}
+
+/* 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)
+{
+ SilcPacketStream stream = packet->stream;
+
+ SILC_LOG_DEBUG(("Freeing packet %p", packet));
+
+ /* Check for double free */
+ SILC_ASSERT(packet->stream != NULL);
+
+ packet->stream = NULL;
+ packet->src_id = packet->dst_id = NULL;
+ silc_buffer_reset(&packet->buffer);
+
+ silc_mutex_lock(stream->sc->engine->lock);
+
+ /* 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 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;
+
+ totlen += mac_len;
+
+ /* Allocate more space if needed */
+ if (silc_unlikely(silc_buffer_taillen(&stream->outbuf) < totlen)) {
+ if (!silc_buffer_realloc(&stream->outbuf,
+ silc_buffer_truelen(&stream->outbuf) + totlen))
+ return FALSE;
+ }
+
+ /* Pull data area for the new packet, and return pointer to the start of
+ the data area and save the pointer in to the `packet'. MAC is pulled
+ later after it's computed. */
+ oldptr = silc_buffer_pull_tail(&stream->outbuf, totlen);
+ silc_buffer_set(packet, oldptr, totlen);
+ silc_buffer_push_tail(packet, mac_len);
+
+ return TRUE;
+}
+
+/* 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 *iv = silc_cipher_get_iv(cipher);
+ SilcUInt32 pc1, pc2;
+
+ /* 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];
+
+ /* 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, 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
+ 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 = 0, ivlen = 0, psnlen = 0;
+ SilcBool ctr;
+ SilcBufferStruct packet;
+
+ 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));
+
+ /* Get the true length of the packet. This is saved as payload length
+ into the packet header. This does not include the length of the
+ padding. */
+ data_len = SILC_PACKET_DATALEN(data_len, (SILC_PACKET_HEADER_LEN +
+ src_id_len + dst_id_len));
+ 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
+ payload is encrypted already. */
+ if (type == SILC_PACKET_PRIVATE_MESSAGE &&
+ flags & SILC_PACKET_FLAG_PRIVMSG_KEY) {
+ /* Padding is calculated from header + IDs */
+ 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 + psnlen);
+
+ } else if (type == SILC_PACKET_CHANNEL_MESSAGE) {
+ if (stream->sc->engine->local_is_router && stream->is_router) {
+ /* Channel messages between routers are encrypted as normal packets.
+ Padding is calculated from true length of the packet. */
+ if (!ctr)
+ SILC_PACKET_PADLEN(truelen + psnlen, block_len, padlen);
+
+ enclen += padlen + psnlen;
+ } else {
+ /* Padding is calculated from header + IDs */
+ 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 + psnlen);
+ }
+ } else {
+ /* Padding is calculated from true length of the packet */
+ if (flags & SILC_PACKET_FLAG_LONG_PAD)
+ SILC_PACKET_PADLEN_MAX(truelen + psnlen, block_len, padlen);
+ else if (!ctr)
+ SILC_PACKET_PADLEN(truelen + psnlen, block_len, padlen);
+
+ enclen += padlen + psnlen;
+ }
+
+ /* Remove implementation specific flags */
+ flags &= ~(SILC_PACKET_FLAG_LONG_PAD);
+
+ /* Get random padding */
+ for (i = 0; i < padlen; i++) tmppad[i] =
+ silc_rng_get_byte_fast(stream->sc->engine->rng);
+
+ silc_mutex_lock(stream->lock);
+
+ /* Get packet pointer from the outgoing buffer */
+ 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),
+ SILC_STR_UI_CHAR(padlen),
+ SILC_STR_UI_CHAR(0),
+ SILC_STR_UI_CHAR(src_id_len),
+ SILC_STR_UI_CHAR(dst_id_len),
+ SILC_STR_UI_CHAR(src_id_type),
+ SILC_STR_DATA(src_id, src_id_len),
+ SILC_STR_UI_CHAR(dst_id_type),
+ SILC_STR_DATA(dst_id, dst_id_len),
+ SILC_STR_DATA(tmppad, padlen),
+ SILC_STR_DATA(data, data_len),
+ SILC_STR_END);
+ if (silc_unlikely(i < 0)) {
+ silc_mutex_unlock(stream->lock);
+ return FALSE;
+ }
+
+ SILC_LOG_HEXDUMP(("Assembled packet, len %d", silc_buffer_len(&packet)),
+ silc_buffer_data(&packet), silc_buffer_len(&packet));
+
+ /* Encrypt the packet */
+ if (silc_likely(cipher)) {
+ SILC_LOG_DEBUG(("Encrypting packet"));
+ silc_cipher_set_iv(cipher, 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;
+ }
+ }
+
+ /* Compute HMAC */
+ 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_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++;
+ }
+
+ return TRUE;
+}
+
+/* Sends a packet */
+
+SilcBool silc_packet_send(SilcPacketStream stream,
+ SilcPacketType type, SilcPacketFlags flags,
+ const unsigned char *data, SilcUInt32 data_len)
+{
+ 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 */
+
+SilcBool silc_packet_send_ext(SilcPacketStream stream,
+ SilcPacketType type, SilcPacketFlags flags,
+ SilcIdType src_id_type, void *src_id,
+ SilcIdType dst_id_type, void *dst_id,
+ const unsigned char *data, SilcUInt32 data_len,
+ SilcCipher cipher, SilcHmac hmac)
+{
+ 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,
+ sizeof(src_id_data), &src_id_len))
+ return FALSE;
+ if (dst_id)
+ if (!silc_id_id2str(dst_id, dst_id_type, dst_id_data,
+ sizeof(dst_id_data), &dst_id_len))
+ return FALSE;
+
+ 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 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 (silc_likely(hmac)) {
+ unsigned char mac[32], psn[4];
+ SilcUInt32 mac_len;
+
+ SILC_LOG_DEBUG(("Verifying MAC"));
+
+ /* Compute HMAC of packet */
+ silc_hmac_init(hmac);
+
+ 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 (silc_unlikely(memcmp(packet_mac, mac, mac_len))) {
+ SILC_LOG_DEBUG(("MAC failed"));
+ return FALSE;
+ }
+
+ SILC_LOG_DEBUG(("MAC is Ok"));
+ }
+
+ 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 pc1, pc2;
+
+ /* If IV Included flag, set the IV from packet to block counter. */
+ if (stream->iv_included) {
+ memcpy(iv + 4, packet_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);