diff --git a/src/dm/dm_core.c b/src/dm/dm_core.c index d27c8fb3..fe48f544 100644 --- a/src/dm/dm_core.c +++ b/src/dm/dm_core.c @@ -296,6 +296,16 @@ static int dm_build_body(struct dm_state* dm, const struct dm_conv* c, uint64_t } uint8_t ct_len = (uint8_t)strnlen(ct, 255); + const uint8_t* media = NULL; + if (!strcmp(ct, "file")) { + if (data_len <= DM_MEDIA_DESC_SIZE) { + DEBUG_ERROR(DEBUG_CATEGORY_DM, "%s: file message lacks descriptor/name", DM_ID); + return -1; + } + media = data; + data += DM_MEDIA_DESC_SIZE; + data_len -= DM_MEDIA_DESC_SIZE; + } uint64_t conv_num = (uint64_t)strtoull(c->conv_id, NULL, 10); uint8_t nonce[DM_NONCE_SIZE]; dm_build_nonce(conv_num, dm->inst->node_id, seq, nonce); @@ -311,7 +321,7 @@ static int dm_build_body(struct dm_state* dm, const struct dm_conv* c, uint64_t } uint64_t author = dm->inst->node_id; - size_t body_len = DM_MSG_FIXED_HDR + ct_len + 2 + enc_len + DM_MSG_SIG_SIZE; + size_t body_len = DM_MSG_FIXED_HDR + ct_len + 2 + enc_len + (media ? DM_MEDIA_DESC_SIZE : 0) + DM_MSG_SIG_SIZE; uint8_t* body = u_malloc(body_len); if (!body) { u_free(enc); return -1; } size_t off = 0; @@ -324,6 +334,7 @@ static int dm_build_body(struct dm_state* dm, const struct dm_conv* c, uint64_t uint16_t el = (uint16_t)enc_len; memcpy(body + off, &el, 2); off += 2; memcpy(body + off, enc, enc_len); off += enc_len; + if (media) { memcpy(body + off, media, DM_MEDIA_DESC_SIZE); off += DM_MEDIA_DESC_SIZE; } /* подпись по всем полям до sig */ if (sc_ed25519_sign(dm->inst->my_ed25519_privkey, body, off, body + off) != SC_OK) { DEBUG_ERROR(DEBUG_CATEGORY_DM, "%s: message signing failed", DM_ID); @@ -349,6 +360,12 @@ int dm_verify_message(struct UTUN_INSTANCE* inst, uint64_t recipient, const uint memcpy(&author, msg + 24, 8); if (!recipient || !author || author == recipient || !seq || seq > INT64_MAX || conv != dm_derive_conv_id(author, recipient)) goto invalid; + const uint8_t* media = dm_message_media(msg, len); + if (media) { + uint64_t size; + memcpy(&size, media + 16, 8); + if (size > DM_MEDIA_MAX_SIZE) goto invalid; + } uint8_t x[32], ed[32]; if (dm_node_pubkeys(&view, author, x, ed) != 0 || sc_ed25519_verify(ed, msg, len - DM_MSG_SIG_SIZE, msg + len - DM_MSG_SIG_SIZE) != SC_OK) goto invalid; @@ -453,7 +470,7 @@ int dm_accept_message(struct UTUN_INSTANCE* inst, const uint8_t* msg, size_t len uint64_t ts; memcpy(&ts, msg + 16, 8); if (ts > c.last_ts) c.last_ts = ts; - if (inserted < 0 || dm_conv_save(dm, &c) != 0 || dm_exec(dm, "COMMIT") != 0) { + if (inserted < 0 || dm_conv_save(dm, &c) != 0 || dm_media_track(inst, 0, msg, len) != 0 || dm_exec(dm, "COMMIT") != 0) { dm_exec(dm, "ROLLBACK"); return -1; } @@ -734,7 +751,8 @@ int dm_send(struct UTUN_INSTANCE* inst, const char* conv_id, const char* content if (dm_exec(dm, "BEGIN IMMEDIATE") != 0) { u_free(body); return -1; } c.last_out_seq = seq; if (ts > c.last_ts) c.last_ts = ts; - if (dm_store_message(dm, conv_id, 1, body, len) != 1 || dm_conv_save(dm, &c) != 0) goto rollback; + if (dm_store_message(dm, conv_id, 1, body, len) != 1 || dm_conv_save(dm, &c) != 0 || + dm_media_track(inst, 1, body, len) != 0) goto rollback; int rc = sqlite3_prepare_v2(dm->db, "INSERT INTO dm_outbox(conv_id,seq,body) VALUES(?,?,?)", -1, &st, NULL); if (rc == SQLITE_OK) { sqlite3_bind_text(st, 1, conv_id, -1, SQLITE_STATIC); diff --git a/src/dm/dm_core.h b/src/dm/dm_core.h index bf4dfaf7..5a510f7c 100644 --- a/src/dm/dm_core.h +++ b/src/dm/dm_core.h @@ -17,6 +17,7 @@ #include #include #include +#include "dm_media.h" struct UTUN_INSTANCE; @@ -25,7 +26,8 @@ extern "C" { #endif /* ── Каноническое тело DM-сообщения (общий wire-формат dm_core и dm_mailbox) ── - * [conv_id:8][seq:8][ts:8][author:8][ct_len:1][ct:ct_len][data_len:2][data_enc:data_len][sig:64] + * [conv_id:8][seq:8][ts:8][author:8][ct_len:1][ct:ct_len][data_len:2][data_enc:data_len][media?:56][sig:64] + * media присутствует только для ct="file", подписан вместе с сообщением. * data_enc — AES-256-CCM (шифротекст + tag), sig — Ed25519 автора по всем полям до sig. */ #define DM_MSG_FIXED_HDR 33 /* conv_id+seq+ts+author(32) + ct_len(1) — до ct */ #define DM_MSG_SIG_SIZE 64 @@ -41,7 +43,8 @@ static inline int dm_msg_len(const uint8_t* msg, size_t avail, size_t* out_len) uint16_t data_len; memcpy(&data_len, msg + DM_MSG_FIXED_HDR + ct_len, 2); if (data_len < 16 || data_len > DM_DATA_MAX + 16) return 0; - size_t total = (size_t)DM_MSG_FIXED_HDR + ct_len + 2 + data_len + DM_MSG_SIG_SIZE; + size_t media_len = ct_len == 4 && !memcmp(msg + DM_MSG_FIXED_HDR, "file", 4) ? DM_MEDIA_DESC_SIZE : 0; + size_t total = (size_t)DM_MSG_FIXED_HDR + ct_len + 2 + data_len + media_len + DM_MSG_SIG_SIZE; if (avail < total) return 0; *out_len = total; return 1; @@ -75,7 +78,8 @@ int dm_start(struct UTUN_INSTANCE* inst, uint64_t peer_node_id, /* Отправить сообщение в беседу. Шифрует, подписывает, сохраняет локально и * доставляет (прямое соединение при доступности пира, иначе mailbox). - * conv_id — строковое представление (как в dm_conversations.conv_id). */ + * conv_id — строковое представление (как в dm_conversations.conv_id). + * Для ct="file" data=[descriptor:56][basename:utf8], только после подготовки dm_media. */ int dm_send(struct UTUN_INSTANCE* inst, const char* conv_id, const char* content_type, const uint8_t* data, uint32_t data_len);