+/* Actually sends the packet. This flushes the connections outgoing data
+ buffer. If data is sent directly to the network this returns the bytes
+ written, if error occured this returns -1 and if the data could not
+ be written directly to the network at this time this returns -2, in
+ which case the data should be queued by the caller and sent at some
+ later time. If `force_send' is TRUE this attempts to write the data
+ directly to the network, if FALSE, this returns -2. */
+
+int silc_packet_send(SilcSocketConnection sock, int force_send)
+{
+ /* Send now if forced to do so */
+ if (force_send == TRUE) {
+ int ret;
+
+ SILC_LOG_DEBUG(("Forcing packet send, packet sent immediately"));
+
+ /* Write to network */
+ ret = silc_packet_write(sock->sock, sock->outbuf);
+
+ if (ret == -1)
+ SILC_LOG_ERROR(("Error sending packet, dropped"));
+ if (ret != -2)
+ return ret;
+
+ SILC_LOG_DEBUG(("Could not force the send, packet put to queue"));
+ }
+
+ SILC_LOG_DEBUG(("Packet in queue"));
+
+ return -2;
+}
+
+/* Encrypts a packet. This also creates HMAC of the packet before
+ encryption and adds the HMAC at the end of the buffer. This assumes
+ that there is enough free space at the end of the buffer to add the
+ computed HMAC. This is the normal way of encrypting packets, if some
+ other process of HMAC computing and encryption is needed this function
+ cannot be used. */
+
+void silc_packet_encrypt(SilcCipher cipher, SilcHmac hmac,
+ SilcBuffer buffer, unsigned int len)
+{
+ unsigned char mac[32];
+
+ if (cipher) {
+ SILC_LOG_DEBUG(("Encrypting packet, cipher %s, len %d (%d)",
+ cipher->cipher->name, len, len - 2));
+ }
+
+ /* Compute HMAC. This assumes that HMAC is created from the entire
+ data area thus this uses the length found in buffer, not the length
+ sent as argument. */
+ if (hmac) {
+ silc_hmac_make(hmac, buffer->data, buffer->len, mac);
+ silc_buffer_put_tail(buffer, mac, hmac->hash->hash->hash_len);
+ memset(mac, 0, sizeof(mac));
+ }
+
+ /* Encrypt the data area of the packet. 2 bytes of the packet
+ are not encrypted. */
+ if (cipher)
+ cipher->cipher->encrypt(cipher->context, buffer->data + 2,
+ buffer->data + 2, len - 2, cipher->iv);
+
+ /* Pull the HMAC into the visible data area in the buffer */
+ if (hmac)
+ silc_buffer_pull_tail(buffer, hmac->hash->hash->hash_len);
+}
+
+/* Assembles a new packet to be ready for send out. The buffer sent as
+ argument must include the data to be sent and it must not be encrypted.
+ The packet also must have enough free space so that the SILC header
+ and padding maybe added to the packet. The packet is encrypted after
+ this function has returned.
+
+ The buffer sent as argument should be something like following:
+
+ --------------------------------------------
+ | head | data | tail |
+ --------------------------------------------
+ ^ ^
+ 58 bytes x bytes
+
+ So that the SILC header and 1 - 16 bytes of padding can fit to
+ the buffer. After assembly the buffer might look like this:
+
+ --------------------------------------------
+ | data | |
+ --------------------------------------------
+ ^ ^
+ Start of assembled packet
+
+ Packet construct is as follows (* = won't be encrypted):
+
+ x bytes SILC Header
+ 2 bytes Payload length (*)
+ 1 byte Flags
+ 1 byte Packet type
+ 1 byte Source ID Type
+ 2 bytes Source ID Length
+ x bytes Source ID
+ 1 byte Destination ID Type
+ 2 bytes Destination ID Length
+ x bytes Destination ID
+
+ 1 - 16 bytes Padding
+
+ x bytes Data payload
+
+ All fields in the packet will be authenticated by MAC. The MAC is
+ not computed here, it must be computed differently before encrypting
+ the packet.
+
+*/
+
+void silc_packet_assemble(SilcPacketContext *ctx)
+{
+ unsigned char tmppad[SILC_PACKET_MAX_PADLEN];
+ int i;
+
+ SILC_LOG_DEBUG(("Assembling outgoing packet"));
+
+ /* 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. */
+ if (!ctx->truelen)
+ ctx->truelen = ctx->buffer->len + SILC_PACKET_HEADER_LEN +
+ ctx->src_id_len + ctx->dst_id_len;
+
+ /* Calculate the length of the padding. The padding is calculated from
+ the data that will be encrypted. As protocol states 3 first bytes
+ of the packet are not encrypted they are not included in the
+ padding calculation. */
+ if (!ctx->padlen)
+ ctx->padlen = SILC_PACKET_PADLEN(ctx->truelen);
+
+ /* Put the start of the data section to the right place. */
+ silc_buffer_push(ctx->buffer, SILC_PACKET_HEADER_LEN +
+ ctx->src_id_len + ctx->dst_id_len + ctx->padlen);
+
+ /* Get random padding */
+#if 1
+ for (i = 0; i < ctx->padlen; i++)
+ tmppad[i] = silc_rng_get_byte(ctx->rng);
+#else
+ /* XXX: For testing - to be removed */
+ memset(tmppad, 65, sizeof(tmppad));
+#endif
+
+ /* Create the packet. This creates the SILC header and adds padding,
+ rest of the buffer remains as it is. */
+ silc_buffer_format(ctx->buffer,
+ SILC_STR_UI_SHORT(ctx->truelen),
+ SILC_STR_UI_CHAR(ctx->flags),
+ SILC_STR_UI_CHAR(ctx->type),
+ SILC_STR_UI_SHORT(ctx->src_id_len),
+ SILC_STR_UI_SHORT(ctx->dst_id_len),
+ SILC_STR_UI_CHAR(ctx->src_id_type),
+ SILC_STR_UI_XNSTRING(ctx->src_id, ctx->src_id_len),
+ SILC_STR_UI_CHAR(ctx->dst_id_type),
+ SILC_STR_UI_XNSTRING(ctx->dst_id, ctx->dst_id_len),
+ SILC_STR_UI_XNSTRING(tmppad, ctx->padlen),
+ SILC_STR_END);
+
+ SILC_LOG_HEXDUMP(("Assembled packet, len %d", ctx->buffer->len),
+ ctx->buffer->data, ctx->buffer->len);
+
+ SILC_LOG_DEBUG(("Outgoing packet assembled"));
+}
+
+/* Prepare outgoing data buffer for packet sending. This moves the data
+ area so that new packet may be added into it. If needed this allocates
+ more space to the buffer. This handles directly the connection's
+ outgoing buffer in SilcSocketConnection object. */
+
+void silc_packet_send_prepare(SilcSocketConnection sock,
+ unsigned int header_len,
+ unsigned int padlen,
+ unsigned int data_len)
+{
+ int totlen, oldlen;
+
+ totlen = header_len + padlen + data_len;
+
+ /* Prepare the outgoing buffer for packet sending. */
+ if (!sock->outbuf) {
+ /* Allocate new buffer. This is done only once per connection. */
+ SILC_LOG_DEBUG(("Allocating outgoing data buffer"));
+
+ sock->outbuf = silc_buffer_alloc(SILC_PACKET_DEFAULT_SIZE);
+ silc_buffer_pull_tail(sock->outbuf, totlen);
+ silc_buffer_pull(sock->outbuf, header_len + padlen);
+ } else {
+ if (SILC_IS_OUTBUF_PENDING(sock)) {
+ /* There is some pending data in the buffer. */
+
+ /* Allocate more space if needed */
+ if ((sock->outbuf->end - sock->outbuf->tail) < data_len) {
+ SILC_LOG_DEBUG(("Reallocating outgoing data buffer"));
+ sock->outbuf = silc_buffer_realloc(sock->outbuf,
+ sock->outbuf->truelen + totlen);
+ }
+
+ oldlen = sock->outbuf->len;
+ silc_buffer_pull_tail(sock->outbuf, totlen);
+ silc_buffer_pull(sock->outbuf, header_len + padlen + oldlen);
+ } else {
+ /* Buffer is free for use */
+ silc_buffer_clear(sock->outbuf);
+ silc_buffer_pull_tail(sock->outbuf, totlen);
+ silc_buffer_pull(sock->outbuf, header_len + padlen);
+ }
+ }
+}
+
+/******************************************************************************
+
+ Packet Reception Routines
+
+******************************************************************************/
+