#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "bytestring.h"
#include "dtls12_internal.h"
#define DTLS12_HANDSHAKE_MSG_HEADER_LEN 12
#define DTLS12_HANDSHAKE_MSG_INITIAL_LEN 256
#define DTLS12_HANDSHAKE_MSG_FRAGMENT_LENGTH_OFFSET 9
struct dtls12_handshake_msg {
uint8_t msg_type;
uint32_t msg_len;
uint16_t msg_seq;
uint32_t fragment_offset;
uint32_t fragment_len;
uint8_t *fragment_data;
size_t fragment_data_len;
int fragment_pending;
uint8_t *data;
size_t data_len;
CBS cbs;
CBB cbb;
};
struct dtls12_handshake_msg *
dtls12_handshake_msg_new(void)
{
struct dtls12_handshake_msg *msg = NULL;
if ((msg = calloc(1, sizeof(struct dtls12_handshake_msg))) == NULL)
return NULL;
return msg;
}
void
dtls12_handshake_msg_free(struct dtls12_handshake_msg *msg)
{
if (msg == NULL)
return;
CBB_cleanup(&msg->cbb);
freezero(msg->data, msg->data_len);
freezero(msg->fragment_data, msg->fragment_data_len);
freezero(msg, sizeof(struct dtls12_handshake_msg));
}
void
dtls12_handshake_msg_data(struct dtls12_handshake_msg *msg, CBS *cbs)
{
CBS_init(cbs, msg->data, msg->data_len);
}
uint8_t
dtls12_handshake_msg_type(struct dtls12_handshake_msg *msg)
{
return msg->msg_type;
}
int
dtls12_handshake_msg_content(struct dtls12_handshake_msg *msg, CBS *cbs)
{
dtls12_handshake_msg_data(msg, cbs);
return CBS_skip(cbs, DTLS12_HANDSHAKE_MSG_HEADER_LEN);
}
int
dtls12_handshake_msg_start(struct dtls12_handshake_msg *msg, CBB *body,
uint8_t msg_type, size_t msg_seq)
{
msg->msg_type = msg_type;
msg->msg_seq = msg_seq;
msg->msg_len = 0;
msg->fragment_offset = 0;
if (!CBB_init(&msg->cbb, DTLS12_HANDSHAKE_MSG_INITIAL_LEN))
return 0;
if (!CBB_add_u8(&msg->cbb, msg->msg_type))
return 0;
if (!CBB_add_u24(&msg->cbb, msg->msg_len))
return 0;
if (!CBB_add_u16(&msg->cbb, msg->msg_seq))
return 0;
if (!CBB_add_u24(&msg->cbb, msg->fragment_offset))
return 0;
if (!CBB_add_u24_length_prefixed(&msg->cbb, body))
return 0;
return 1;
}
int
dtls12_handshake_msg_finish(struct dtls12_handshake_msg *msg)
{
CBS cbs;
if (!CBB_finish(&msg->cbb, &msg->data, &msg->data_len))
return 0;
CBS_init(&cbs, msg->data, msg->data_len);
if (!CBS_skip(&cbs, DTLS12_HANDSHAKE_MSG_FRAGMENT_LENGTH_OFFSET))
return 0;
if (!CBS_get_u24(&cbs, &msg->msg_len))
return 0;
if (!CBB_init_fixed(&msg->cbb, msg->data, msg->data_len))
return 0;
if (!CBB_add_u8(&msg->cbb, msg->msg_type))
return 0;
if (!CBB_add_u24(&msg->cbb, msg->msg_len))
return 0;
if (!CBB_finish(&msg->cbb, NULL, NULL))
return 0;
dtls12_handshake_msg_fragment_reset(msg);
return 1;
}
int
dtls12_handshake_msg_fragment_reset(struct dtls12_handshake_msg *msg)
{
freezero(msg->fragment_data, msg->fragment_data_len);
msg->fragment_data = NULL;
msg->fragment_data_len = 0;
msg->fragment_offset = 0;
msg->fragment_pending = 1;
return dtls12_handshake_msg_content(msg, &msg->cbs);
}
int
dtls12_handshake_msg_fragment_build(struct dtls12_handshake_msg *msg,
size_t max_fragment_len, CBS *cbs)
{
CBB body;
CBS_init(cbs, NULL, 0);
if (msg->fragment_offset > msg->msg_len)
return 0;
if (msg->msg_len - msg->fragment_offset > CBS_len(&msg->cbs))
return 0;
freezero(msg->fragment_data, msg->fragment_data_len);
msg->fragment_data = NULL;
msg->fragment_data_len = 0;
if ((msg->fragment_len = CBS_len(&msg->cbs)) > max_fragment_len)
msg->fragment_len = max_fragment_len;
if (!CBB_init(&msg->cbb, DTLS12_HANDSHAKE_MSG_INITIAL_LEN))
goto err;
if (!CBB_add_u8(&msg->cbb, msg->msg_type))
goto err;
if (!CBB_add_u24(&msg->cbb, msg->msg_len))
goto err;
if (!CBB_add_u16(&msg->cbb, msg->msg_seq))
goto err;
if (!CBB_add_u24(&msg->cbb, msg->fragment_offset))
goto err;
if (!CBB_add_u24_length_prefixed(&msg->cbb, &body))
goto err;
if (!CBB_add_bytes(&body, CBS_data(&msg->cbs), msg->fragment_len))
goto err;
if (!CBB_finish(&msg->cbb, &msg->fragment_data, &msg->fragment_data_len))
goto err;
CBS_init(cbs, msg->fragment_data, msg->fragment_data_len);
return 1;
err:
CBB_cleanup(&msg->cbb);
return 0;
}
int
dtls12_handshake_msg_fragment_next(struct dtls12_handshake_msg *msg)
{
if (msg->fragment_offset > msg->msg_len)
return 0;
if (msg->msg_len - msg->fragment_offset < msg->fragment_len)
return 0;
if (!CBS_skip(&msg->cbs, msg->fragment_len))
return 0;
msg->fragment_offset += msg->fragment_len;
msg->fragment_pending = (CBS_len(&msg->cbs) > 0);
return 1;
}
int
dtls12_handshake_msg_fragment_pending(struct dtls12_handshake_msg *msg)
{
return msg->fragment_pending;
}