You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
458 lines
17 KiB
458 lines
17 KiB
#include "pkt_normalizer.h" |
|
#include "u_async.h" |
|
#include "settings.h" |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <stdint.h> |
|
#include <stdio.h> |
|
|
|
static void packer_handler(ll_queue_t* q, ll_entry_t* unused, void* arg); |
|
static void unpacker_handler(ll_queue_t* q, ll_entry_t* unused, void* arg); |
|
static void send_buf(pn_struct* pn); |
|
static int get_header(uint8_t* header, size_t L); |
|
static void fragment_timeout_cb(void* arg); |
|
static void set_fragment_timeout(pn_struct* pn); |
|
static void cancel_fragment_timeout(pn_struct* pn); |
|
|
|
pn_struct* pkt_normalizer_init(int is_packer) { |
|
pn_struct* pn = malloc(sizeof(pn_struct)); |
|
if (!pn) return NULL; |
|
|
|
pn->input = queue_new(); |
|
if (!pn->input) { |
|
free(pn); |
|
return NULL; |
|
} |
|
pn->output = queue_new(); |
|
if (!pn->output) { |
|
queue_free(pn->input); |
|
free(pn); |
|
return NULL; |
|
} |
|
pn->is_packer = is_packer; |
|
|
|
if (is_packer) { |
|
pn->u.packer.cap = settings.max_fragment_size; |
|
pn->u.packer.buf = malloc(pn->u.packer.cap); |
|
if (!pn->u.packer.buf) { |
|
queue_free(pn->input); |
|
queue_free(pn->output); |
|
free(pn); |
|
return NULL; |
|
} |
|
pn->u.packer.len = 0; |
|
pn->u.packer.error_count = 0; |
|
queue_set_callback(pn->input, packer_handler, pn); |
|
} else { |
|
pn->u.unpacker.buf = NULL; |
|
pn->u.unpacker.len = 0; |
|
pn->u.unpacker.total_len = 0; |
|
pn->u.unpacker.cap = 0; |
|
pn->u.unpacker.error_count = 0; |
|
pn->u.unpacker.in_fragment = 0; |
|
pn->u.unpacker.fragment_timeout_id = NULL; |
|
queue_set_callback(pn->input, unpacker_handler, pn); |
|
} |
|
return pn; |
|
} |
|
|
|
void pkt_normalizer_deinit(pn_struct* pn) { |
|
if (!pn) return; |
|
queue_free(pn->input); |
|
queue_free(pn->output); |
|
if (pn->is_packer) { |
|
free(pn->u.packer.buf); |
|
} else { |
|
cancel_fragment_timeout(pn); |
|
free(pn->u.unpacker.buf); |
|
} |
|
free(pn); |
|
} |
|
|
|
pkt_normalizer_pair* pkt_normalizer_pair_init(void) { |
|
pkt_normalizer_pair* pair = malloc(sizeof(pkt_normalizer_pair)); |
|
if (!pair) return NULL; |
|
|
|
pair->packer = pkt_normalizer_init(1); |
|
if (!pair->packer) { |
|
free(pair); |
|
return NULL; |
|
} |
|
pair->unpacker = pkt_normalizer_init(0); |
|
if (!pair->unpacker) { |
|
pkt_normalizer_deinit(pair->packer); |
|
free(pair); |
|
return NULL; |
|
} |
|
return pair; |
|
} |
|
|
|
void pkt_normalizer_pair_deinit(pkt_normalizer_pair* pair) { |
|
if (!pair) return; |
|
pkt_normalizer_deinit(pair->packer); |
|
pkt_normalizer_deinit(pair->unpacker); |
|
free(pair); |
|
} |
|
|
|
static int get_header(uint8_t* header, size_t L) { |
|
if (L > 3839) return -1; |
|
if (L <= 239) { |
|
header[0] = (uint8_t)L; |
|
return 1; |
|
} else { |
|
uint8_t high = (uint8_t)(L >> 8); |
|
if (high > 14) return -1; |
|
header[0] = 0xF0 + high; |
|
header[1] = (uint8_t)(L & 0xFF); |
|
return 2; |
|
} |
|
} |
|
|
|
/* Сбросить состояние сборки фрагментов */ |
|
static void reset_fragment_state(pn_struct* pn) { |
|
if (!pn->is_packer) { |
|
cancel_fragment_timeout(pn); |
|
pn->u.unpacker.len = 0; |
|
pn->u.unpacker.total_len = 0; |
|
pn->u.unpacker.in_fragment = 0; |
|
} |
|
} |
|
|
|
/* Таймаут для сборки фрагментов */ |
|
static void fragment_timeout_cb(void* arg) { |
|
pn_struct* pn = (pn_struct*)arg; |
|
if (!pn || pn->is_packer) return; |
|
|
|
pn->u.unpacker.fragment_timeout_id = NULL; |
|
pn->u.unpacker.error_count++; |
|
reset_fragment_state(pn); |
|
} |
|
|
|
/* Установить таймаут для текущей сборки фрагментов */ |
|
static void set_fragment_timeout(pn_struct* pn) { |
|
if (!pn || pn->is_packer) return; |
|
|
|
cancel_fragment_timeout(pn); |
|
pn->u.unpacker.fragment_timeout_id = uasync_set_timeout( |
|
PKT_NORMALIZER_FRAGMENT_TIMEOUT, pn, fragment_timeout_cb); |
|
} |
|
|
|
/* Отменить таймаут сборки фрагментов */ |
|
static void cancel_fragment_timeout(pn_struct* pn) { |
|
if (!pn || pn->is_packer) return; |
|
|
|
if (pn->u.unpacker.fragment_timeout_id) { |
|
uasync_cancel_timeout(pn->u.unpacker.fragment_timeout_id); |
|
pn->u.unpacker.fragment_timeout_id = NULL; |
|
} |
|
} |
|
|
|
static void send_buf(pn_struct* pn) { |
|
if (pn->u.packer.len == 0) return; |
|
size_t payload_len = pn->u.packer.len; |
|
printf("[PN DEBUG] send_buf: packer len=%zu, output queue count=%d\n", payload_len, queue_entry_count(pn->output)); |
|
ll_entry_t* out = queue_entry_new(2 + payload_len); |
|
if (!out) return; |
|
uint8_t* d = ll_entry_data(out); |
|
*(uint16_t*)d = (uint16_t)payload_len; |
|
memcpy(d + 2, pn->u.packer.buf, payload_len); |
|
queue_entry_put(pn->output, out); |
|
pn->u.packer.len = 0; |
|
} |
|
|
|
static void packer_handler(ll_queue_t* q, ll_entry_t* unused, void* arg) { |
|
(void)unused; |
|
pn_struct* pn = arg; |
|
size_t max = (size_t)settings.max_fragment_size; |
|
|
|
ll_entry_t* entry = queue_entry_get(q); |
|
if (!entry) { |
|
queue_resume_callback(q); |
|
return; |
|
} |
|
|
|
size_t L = ll_entry_size(entry); |
|
uint8_t* data = ll_entry_data(entry); |
|
uint8_t header[2]; |
|
int hsize = get_header(header, L); |
|
size_t needed = (size_t)hsize + L; |
|
|
|
if (hsize < 0 || needed > max) { |
|
// Fragment |
|
if (pn->u.packer.len > 0) { |
|
send_buf(pn); |
|
} |
|
size_t remaining = L; |
|
size_t pos = 0; |
|
int fragment_count = 0; |
|
while (remaining > 0) { |
|
size_t chunk; |
|
size_t payload_len; |
|
ll_entry_t* fout; |
|
uint8_t* fd; |
|
uint8_t frag_header[2]; |
|
int frag_hsize; |
|
|
|
if (fragment_count == 0) { |
|
// Первый фрагмент: FF + общая длина (2 байта) |
|
chunk = remaining > (max - 5) ? (max - 5) : remaining; // 2+1+2+chunk <= max |
|
payload_len = 1 + 2 + chunk; // FF + total_len + data |
|
fout = queue_entry_new(2 + payload_len); |
|
if (!fout) break; |
|
fd = ll_entry_data(fout); |
|
*(uint16_t*)fd = (uint16_t)payload_len; |
|
fd += 2; |
|
*fd++ = 0xFF; |
|
*fd++ = (uint8_t)(L >> 8); // старший байт общей длины |
|
*fd++ = (uint8_t)(L & 0xFF); // младший байт общей длины |
|
} else { |
|
// Не первый фрагмент |
|
if (remaining <= max - 3) { |
|
// Это последний возможный фрагмент (помещается в один пакет с префиксом FE) |
|
// Пытаемся отправить как обычный блок |
|
frag_hsize = get_header(frag_header, remaining); |
|
if (frag_hsize > 0 && (size_t)frag_hsize + remaining + 2 <= max) { |
|
// Успешно: обычный блок |
|
payload_len = frag_hsize + remaining; |
|
chunk = remaining; |
|
fout = queue_entry_new(2 + payload_len); |
|
if (!fout) break; |
|
fd = ll_entry_data(fout); |
|
*(uint16_t*)fd = (uint16_t)payload_len; |
|
fd += 2; |
|
memcpy(fd, frag_header, frag_hsize); |
|
fd += frag_hsize; |
|
} else { |
|
// Не удалось отправить как обычный блок - ошибка |
|
pn->u.packer.error_count++; |
|
// Отправляем как FE (нарушение спецификации) |
|
chunk = remaining > (max - 3) ? (max - 3) : remaining; |
|
payload_len = 1 + chunk; // FE + data |
|
fout = queue_entry_new(2 + payload_len); |
|
if (!fout) break; |
|
fd = ll_entry_data(fout); |
|
*(uint16_t*)fd = (uint16_t)payload_len; |
|
fd += 2; |
|
*fd++ = 0xFE; |
|
} |
|
} else { |
|
// Промежуточный фрагмент, отправляем как FE |
|
chunk = remaining > (max - 3) ? (max - 3) : remaining; |
|
payload_len = 1 + chunk; // FE + data |
|
fout = queue_entry_new(2 + payload_len); |
|
if (!fout) break; |
|
fd = ll_entry_data(fout); |
|
*(uint16_t*)fd = (uint16_t)payload_len; |
|
fd += 2; |
|
*fd++ = 0xFE; |
|
} |
|
} |
|
memcpy(fd, data + pos, chunk); |
|
queue_entry_put(pn->output, fout); |
|
pos += chunk; |
|
remaining -= chunk; |
|
fragment_count++; |
|
} |
|
} else { |
|
if (pn->u.packer.len + needed > max) { |
|
send_buf(pn); |
|
} |
|
// Add to buffer |
|
uint8_t* p = pn->u.packer.buf + pn->u.packer.len; |
|
memcpy(p, header, (size_t)hsize); |
|
memcpy(p + hsize, data, L); |
|
pn->u.packer.len += needed; |
|
} |
|
|
|
queue_entry_free(entry); |
|
|
|
if (pn->u.packer.len > 0) { |
|
send_buf(pn); |
|
} |
|
|
|
queue_resume_callback(q); |
|
} |
|
|
|
int pkt_normalizer_get_error_count(const pn_struct* pn) { |
|
if (!pn) return 0; |
|
if (pn->is_packer) { |
|
return pn->u.packer.error_count; |
|
} |
|
return pn->u.unpacker.error_count; |
|
} |
|
|
|
void pkt_normalizer_reset_error_count(pn_struct* pn) { |
|
if (!pn) return; |
|
if (pn->is_packer) { |
|
pn->u.packer.error_count = 0; |
|
} else { |
|
pn->u.unpacker.error_count = 0; |
|
} |
|
} |
|
|
|
void pkt_normalizer_flush(pn_struct* pn) { |
|
if (!pn || !pn->is_packer) return; |
|
if (pn->u.packer.len > 0) { |
|
send_buf(pn); |
|
} |
|
} |
|
|
|
static void unpacker_handler(ll_queue_t* q, ll_entry_t* unused, void* arg) { |
|
(void)unused; |
|
pn_struct* pn = arg; |
|
while (queue_entry_count(q) > 0) { |
|
ll_entry_t* entry = queue_entry_get(q); |
|
uint8_t* data = ll_entry_data(entry); |
|
size_t total = ll_entry_size(entry); |
|
uint16_t payload_len = *(uint16_t*)data; |
|
if (total != 2 + (size_t)payload_len) { |
|
queue_entry_free(entry); |
|
continue; |
|
} |
|
uint8_t* cg = data + 2; |
|
size_t cg_pos = 0; |
|
size_t cg_len = (size_t)payload_len; |
|
while (cg_pos < cg_len) { |
|
uint8_t byte = cg[cg_pos++]; |
|
|
|
if (byte == 0xFF) { |
|
/* Начало нового фрагментированного пакета */ |
|
if (pn->u.unpacker.in_fragment) { |
|
/* Не завершен предыдущий фрагмент - ошибка */ |
|
pn->u.unpacker.error_count++; |
|
reset_fragment_state(pn); |
|
} |
|
/* Проверить, что есть 2 байта для общей длины */ |
|
if (cg_pos + 2 > cg_len) { |
|
pn->u.unpacker.error_count++; |
|
goto err; |
|
} |
|
/* Прочитать общую длину */ |
|
uint16_t total_len = ((uint16_t)cg[cg_pos] << 8) | cg[cg_pos + 1]; |
|
cg_pos += 2; |
|
size_t chunk_len = cg_len - cg_pos; |
|
/* Выделить буфер при необходимости */ |
|
size_t new_len = chunk_len; |
|
if (new_len > pn->u.unpacker.cap) { |
|
size_t new_cap = pn->u.unpacker.cap ? pn->u.unpacker.cap * 2 : 4096; |
|
if (new_cap < new_len) new_cap = new_len; |
|
pn->u.unpacker.buf = realloc(pn->u.unpacker.buf, new_cap); |
|
pn->u.unpacker.cap = new_cap; |
|
} |
|
memcpy(pn->u.unpacker.buf, cg + cg_pos, chunk_len); |
|
pn->u.unpacker.len = chunk_len; |
|
pn->u.unpacker.total_len = total_len; |
|
pn->u.unpacker.in_fragment = 1; |
|
set_fragment_timeout(pn); |
|
cg_pos += chunk_len; |
|
/* Проверить, не собрали ли уже весь пакет */ |
|
if (pn->u.unpacker.len >= pn->u.unpacker.total_len) { |
|
if (pn->u.unpacker.len == pn->u.unpacker.total_len) { |
|
ll_entry_t* out = queue_entry_new(pn->u.unpacker.total_len); |
|
if (out) { |
|
memcpy(ll_entry_data(out), pn->u.unpacker.buf, pn->u.unpacker.total_len); |
|
queue_entry_put(pn->output, out); |
|
} |
|
} else { |
|
/* Слишком много данных - ошибка */ |
|
pn->u.unpacker.error_count++; |
|
} |
|
reset_fragment_state(pn); |
|
} |
|
continue; |
|
} |
|
|
|
if (byte == 0xFE) { |
|
/* Продолжение фрагментированного пакета */ |
|
if (!pn->u.unpacker.in_fragment) { |
|
/* Не было начала фрагмента - ошибка */ |
|
pn->u.unpacker.error_count++; |
|
goto err; |
|
} |
|
size_t chunk_len = cg_len - cg_pos; |
|
size_t new_len = pn->u.unpacker.len + chunk_len; |
|
if (new_len > pn->u.unpacker.cap) { |
|
size_t new_cap = pn->u.unpacker.cap ? pn->u.unpacker.cap * 2 : 4096; |
|
if (new_cap < new_len) new_cap = new_len; |
|
pn->u.unpacker.buf = realloc(pn->u.unpacker.buf, new_cap); |
|
pn->u.unpacker.cap = new_cap; |
|
} |
|
memcpy(pn->u.unpacker.buf + pn->u.unpacker.len, cg + cg_pos, chunk_len); |
|
pn->u.unpacker.len = new_len; |
|
cg_pos += chunk_len; |
|
set_fragment_timeout(pn); |
|
/* Проверить, не собрали ли уже весь пакет */ |
|
if (pn->u.unpacker.len >= pn->u.unpacker.total_len) { |
|
if (pn->u.unpacker.len == pn->u.unpacker.total_len) { |
|
ll_entry_t* out = queue_entry_new(pn->u.unpacker.total_len); |
|
if (out) { |
|
memcpy(ll_entry_data(out), pn->u.unpacker.buf, pn->u.unpacker.total_len); |
|
queue_entry_put(pn->output, out); |
|
} |
|
} else { |
|
/* Слишком много данных - ошибка */ |
|
pn->u.unpacker.error_count++; |
|
} |
|
reset_fragment_state(pn); |
|
} |
|
continue; |
|
} |
|
|
|
/* Обычная запись (не фрагмент) */ |
|
size_t L; |
|
if (byte <= 0xEF) { |
|
L = byte; |
|
} else if (byte >= 0xF0 && byte <= 0xFE) { |
|
if (cg_pos >= cg_len) goto err; |
|
uint8_t ext = cg[cg_pos++]; |
|
L = ((size_t)(byte - 0xF0) << 8) | ext; |
|
} else { |
|
/* Недопустимый байт */ |
|
goto err; |
|
} |
|
|
|
if (cg_pos + L > cg_len) goto err; |
|
|
|
if (pn->u.unpacker.in_fragment) { |
|
/* Это последний фрагмент в виде обычной записи */ |
|
size_t new_len = pn->u.unpacker.len + L; |
|
if (new_len > pn->u.unpacker.cap) { |
|
size_t new_cap = pn->u.unpacker.cap ? pn->u.unpacker.cap * 2 : 4096; |
|
if (new_cap < new_len) new_cap = new_len; |
|
pn->u.unpacker.buf = realloc(pn->u.unpacker.buf, new_cap); |
|
pn->u.unpacker.cap = new_cap; |
|
} |
|
memcpy(pn->u.unpacker.buf + pn->u.unpacker.len, cg + cg_pos, L); |
|
pn->u.unpacker.len = new_len; |
|
cg_pos += L; |
|
set_fragment_timeout(pn); |
|
|
|
/* Проверить, собрали ли весь пакет */ |
|
if (pn->u.unpacker.len >= pn->u.unpacker.total_len) { |
|
if (pn->u.unpacker.len == pn->u.unpacker.total_len) { |
|
ll_entry_t* out = queue_entry_new(pn->u.unpacker.total_len); |
|
if (out) { |
|
memcpy(ll_entry_data(out), pn->u.unpacker.buf, pn->u.unpacker.total_len); |
|
queue_entry_put(pn->output, out); |
|
} |
|
} else { |
|
/* Слишком много данных - ошибка */ |
|
pn->u.unpacker.error_count++; |
|
} |
|
reset_fragment_state(pn); |
|
} |
|
} else { |
|
/* Обычная запись (не часть фрагмента) */ |
|
ll_entry_t* out = queue_entry_new(L); |
|
if (out) { |
|
memcpy(ll_entry_data(out), cg + cg_pos, L); |
|
queue_entry_put(pn->output, out); |
|
} |
|
cg_pos += L; |
|
} |
|
} |
|
err: |
|
queue_entry_free(entry); |
|
} |
|
queue_resume_callback(q); |
|
}
|
|
|