198 lines
6.3 KiB
C
198 lines
6.3 KiB
C
#include "mozen_tunnel.h"
|
|
#include <string.h>
|
|
// ·Ö°ü
|
|
|
|
/*
|
|
* Initialize tunnel
|
|
* tunnel: tunnel context
|
|
* prot: protocol context
|
|
* mtu: maximum transmission unit
|
|
* user_context: user context
|
|
* returns: none
|
|
*/
|
|
void mozen_tunnel_init(mozen_tunnel_t *tunnel, mozen_protocol_t *prot, uint16_t mtu, void *user_context)
|
|
{
|
|
if (!tunnel) return;
|
|
memset(tunnel, 0, sizeof(mozen_tunnel_t));
|
|
tunnel->prot = prot;
|
|
tunnel->mtu = mtu;
|
|
tunnel->user_context = user_context;
|
|
}
|
|
|
|
/*
|
|
* Send a response to the tunnel command
|
|
* tunnel: tunnel context
|
|
* target_cmd: command id
|
|
* is_ok: true if the command was successful, false otherwise
|
|
* returns: none
|
|
*/
|
|
void mozen_tunnel_set_callbacks(mozen_tunnel_t *tunnel,
|
|
mozen_tunnel_on_stream_start_t on_start,
|
|
mozen_tunnel_on_stream_data_t on_data,
|
|
mozen_tunnel_on_stream_finish_t on_finish)
|
|
{
|
|
if (!tunnel) return;
|
|
tunnel->on_start = on_start;
|
|
tunnel->on_data = on_data;
|
|
tunnel->on_finish = on_finish;
|
|
}
|
|
|
|
/*
|
|
* Send a response to the tunnel command
|
|
* tunnel: tunnel context
|
|
* target_cmd: command id
|
|
* is_ok: true if the command was successful, false otherwise
|
|
* returns: none
|
|
*/
|
|
static void mozen_tunnel_send_response(mozen_tunnel_t *tunnel, uint8_t target_cmd, bool is_ok)
|
|
{
|
|
if (!tunnel || !tunnel->prot) return;
|
|
|
|
uint8_t res_buf[sizeof(mozen_tunnel_header_t) + 2];
|
|
mozen_tunnel_header_t *res_header = (mozen_tunnel_header_t *)res_buf;
|
|
|
|
res_header->tunnel_type = TUNNEL_TYPE_DATA_TRANSFER;
|
|
res_header->target_cmd = target_cmd;
|
|
res_buf[sizeof(mozen_tunnel_header_t)] = is_ok ? 'o' : 'N';
|
|
res_buf[sizeof(mozen_tunnel_header_t) + 1] = is_ok ? 'k' : 'G';
|
|
// tunnel messages go through IRQ buffer context to avoid disrupting main buffer?
|
|
// The previous implementation used the default send context.
|
|
mozen_protocol_send_frame(tunnel->prot, MOZEN_TUNNEL_COMMAND_ID, res_buf, sizeof(res_buf), true);
|
|
}
|
|
|
|
/*
|
|
* Handle a received tunnel frame
|
|
* tunnel: tunnel context
|
|
* data: received data
|
|
* length: received data length
|
|
* returns: none
|
|
*/
|
|
static void mozen_tunnel_abort(mozen_tunnel_t *tunnel)
|
|
{
|
|
if (tunnel->is_active)
|
|
{
|
|
if (tunnel->on_finish)
|
|
{
|
|
tunnel->on_finish(tunnel->user_context, false);
|
|
}
|
|
tunnel->is_active = false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Handle a received tunnel frame
|
|
* tunnel: tunnel context
|
|
* data: received data
|
|
* length: received data length
|
|
* returns: none
|
|
*/
|
|
void mozen_tunnel_handle_command(mozen_tunnel_t *tunnel, const uint8_t *data, uint16_t length)
|
|
{
|
|
if (!tunnel || length < sizeof(mozen_tunnel_header_t)) return;
|
|
|
|
const mozen_tunnel_header_t *tunnel_header = (const mozen_tunnel_header_t *)data;
|
|
const uint8_t *payload = data + sizeof(mozen_tunnel_header_t);
|
|
uint16_t payload_len = length - sizeof(mozen_tunnel_header_t);
|
|
|
|
switch (tunnel_header->tunnel_type)
|
|
{
|
|
case TUNNEL_TYPE_HANDSHAKE: // handshake request
|
|
{
|
|
if (payload_len < sizeof(uint32_t) + sizeof(uint16_t)) return;
|
|
|
|
mozen_tunnel_abort(tunnel); // Abort any ongoing stream
|
|
|
|
tunnel->is_active = true;
|
|
tunnel->target_cmd = tunnel_header->target_cmd;
|
|
|
|
memcpy(&tunnel->total_data_len, payload, sizeof(uint32_t));
|
|
memcpy(&tunnel->total_packets, payload + sizeof(uint32_t), sizeof(uint16_t));
|
|
|
|
tunnel->next_packet_index = 1;
|
|
tunnel->received_data_len = 0;
|
|
|
|
// Prepare and send handshake response
|
|
uint8_t res_buf[sizeof(mozen_tunnel_handshake_res_t)];
|
|
mozen_tunnel_handshake_res_t *res = (mozen_tunnel_handshake_res_t *)res_buf;
|
|
res->header.tunnel_type = TUNNEL_TYPE_RESPONSE;
|
|
res->header.target_cmd = tunnel_header->target_cmd;
|
|
res->status = 0x6B6F; // 'ok' (little-endian: 'o' is 0x6F, 'k' is 0x6B)
|
|
res->mtu = tunnel->mtu;
|
|
|
|
if (tunnel->prot)
|
|
{
|
|
mozen_protocol_send_frame(tunnel->prot, MOZEN_TUNNEL_COMMAND_ID, (uint8_t *)res, sizeof(mozen_tunnel_handshake_res_t), true);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case TUNNEL_TYPE_DATA_TRANSFER: // data transfer
|
|
{
|
|
if (!tunnel->is_active || tunnel_header->target_cmd != tunnel->target_cmd) return;
|
|
if (payload_len < sizeof(uint16_t)) return;
|
|
|
|
uint16_t packet_index;
|
|
memcpy(&packet_index, payload, sizeof(uint16_t));
|
|
|
|
if (packet_index != tunnel->next_packet_index)
|
|
{
|
|
mozen_tunnel_send_response(tunnel, tunnel->target_cmd, false);
|
|
mozen_tunnel_abort(tunnel);
|
|
return;
|
|
}
|
|
|
|
const uint8_t *chunk_data = payload + sizeof(uint16_t);
|
|
uint16_t chunk_len = payload_len - sizeof(uint16_t);
|
|
|
|
bool accepted = true;
|
|
|
|
if (packet_index == 1)
|
|
{
|
|
if (tunnel->on_start)
|
|
{
|
|
accepted = tunnel->on_start(tunnel->user_context, tunnel->target_cmd, chunk_data, chunk_len);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (tunnel->on_data)
|
|
{
|
|
accepted = tunnel->on_data(tunnel->user_context, chunk_data, chunk_len);
|
|
}
|
|
}
|
|
|
|
if (!accepted)
|
|
{
|
|
mozen_tunnel_send_response(tunnel, tunnel->target_cmd, false);
|
|
mozen_tunnel_abort(tunnel);
|
|
return;
|
|
}
|
|
|
|
tunnel->received_data_len += chunk_len;
|
|
tunnel->next_packet_index++;
|
|
|
|
mozen_tunnel_send_response(tunnel, tunnel->target_cmd, true);
|
|
|
|
if (tunnel->next_packet_index > tunnel->total_packets)
|
|
{
|
|
if (tunnel->received_data_len == tunnel->total_data_len)
|
|
{
|
|
if (tunnel->on_finish)
|
|
{
|
|
tunnel->on_finish(tunnel->user_context, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (tunnel->on_finish)
|
|
{
|
|
tunnel->on_finish(tunnel->user_context, false);
|
|
}
|
|
}
|
|
tunnel->is_active = false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|