LCOV - code coverage report
Current view: top level - src/domain/write - send.c (source / functions) Coverage Total Hit
Test: coverage-functional.info Lines: 68.8 % 77 53
Test Date: 2026-04-20 19:54:24 Functions: 100.0 % 3 3

            Line data    Source code
       1              : /* SPDX-License-Identifier: GPL-3.0-or-later */
       2              : /* Copyright 2026 Peter Csaszar */
       3              : 
       4              : /**
       5              :  * @file domain/write/send.c
       6              :  * @brief messages.sendMessage — first write-capable domain.
       7              :  */
       8              : 
       9              : #include "domain/write/send.h"
      10              : 
      11              : #include "tl_serial.h"
      12              : #include "tl_registry.h"
      13              : #include "mtproto_rpc.h"
      14              : #include "crypto.h"
      15              : #include "logger.h"
      16              : 
      17              : #include <stdlib.h>
      18              : #include <string.h>
      19              : 
      20              : #define CRC_messages_sendMessage 0x0d9d75a4u
      21              : 
      22              : /* messages.Updates constructors we may see in the response. We only need
      23              :  * to recognise them enough to return success + optionally extract the
      24              :  * outgoing message id from updateShortSentMessage. */
      25              : #define CRC_updateShortSentMessage  0x9015e101u
      26              : #define CRC_updates                 TL_updates
      27              : #define CRC_updatesCombined         TL_updatesCombined
      28              : #define CRC_updateShort             TL_updateShort
      29              : 
      30            7 : static int write_input_peer(TlWriter *w, const HistoryPeer *p) {
      31            7 :     switch (p->kind) {
      32            6 :     case HISTORY_PEER_SELF:
      33            6 :         tl_write_uint32(w, TL_inputPeerSelf);
      34            6 :         return 0;
      35            1 :     case HISTORY_PEER_USER:
      36            1 :         tl_write_uint32(w, TL_inputPeerUser);
      37            1 :         tl_write_int64 (w, p->peer_id);
      38            1 :         tl_write_int64 (w, p->access_hash);
      39            1 :         return 0;
      40            0 :     case HISTORY_PEER_CHAT:
      41            0 :         tl_write_uint32(w, TL_inputPeerChat);
      42            0 :         tl_write_int64 (w, p->peer_id);
      43            0 :         return 0;
      44            0 :     case HISTORY_PEER_CHANNEL:
      45            0 :         tl_write_uint32(w, TL_inputPeerChannel);
      46            0 :         tl_write_int64 (w, p->peer_id);
      47            0 :         tl_write_int64 (w, p->access_hash);
      48            0 :         return 0;
      49            0 :     default:
      50            0 :         return -1;
      51              :     }
      52              : }
      53              : 
      54              : #define CRC_inputReplyToMessage 0x22c0f6d5u
      55              : 
      56            9 : int domain_send_message_reply(const ApiConfig *cfg,
      57              :                                MtProtoSession *s, Transport *t,
      58              :                                const HistoryPeer *peer,
      59              :                                const char *message,
      60              :                                int32_t reply_to_msg_id,
      61              :                                int32_t *msg_id_out,
      62              :                                RpcError *err) {
      63            9 :     if (!cfg || !s || !t || !peer || !message) return -1;
      64            9 :     size_t mlen = strlen(message);
      65            9 :     if (mlen == 0 || mlen > 4096) {
      66            2 :         logger_log(LOG_ERROR, "send: message length %zu out of bounds", mlen);
      67            2 :         return -1;
      68              :     }
      69              : 
      70              :     /* random_id — must be uniformly random 64-bit. */
      71            7 :     uint8_t rand_buf[8] = {0};
      72            7 :     int64_t random_id = 0;
      73            7 :     if (crypto_rand_bytes(rand_buf, sizeof(rand_buf)) == 0) {
      74            7 :         memcpy(&random_id, rand_buf, 8);
      75              :     } else {
      76              :         /* Fall back to non-crypto randomness; not security-critical. */
      77            0 :         random_id = (int64_t)rand() << 32 | (int64_t)rand();
      78              :     }
      79              : 
      80            7 :     TlWriter w; tl_writer_init(&w);
      81            7 :     tl_write_uint32(&w, CRC_messages_sendMessage);
      82            7 :     uint32_t flags = (reply_to_msg_id > 0) ? 1u : 0u; /* flags.0 = reply_to */
      83            7 :     tl_write_uint32(&w, flags);
      84            7 :     if (write_input_peer(&w, peer) != 0) {
      85            0 :         tl_writer_free(&w);
      86            0 :         return -1;
      87              :     }
      88            7 :     if (reply_to_msg_id > 0) {
      89              :         /* inputReplyToMessage#22c0f6d5 flags:# reply_to_msg_id:int
      90              :          *                              top_msg_id:flags.0?int
      91              :          *                              reply_to_peer_id:flags.1?InputPeer
      92              :          *                              quote_text:flags.2?string ... */
      93            1 :         tl_write_uint32(&w, CRC_inputReplyToMessage);
      94            1 :         tl_write_uint32(&w, 0);                    /* inner flags */
      95            1 :         tl_write_int32 (&w, reply_to_msg_id);
      96              :     }
      97            7 :     tl_write_string(&w, message);
      98            7 :     tl_write_int64 (&w, random_id);
      99              : 
     100              :     uint8_t query[8192];
     101            7 :     if (w.len > sizeof(query)) {
     102            0 :         tl_writer_free(&w);
     103            0 :         logger_log(LOG_ERROR, "send: query too large (%zu)", w.len);
     104            0 :         return -1;
     105              :     }
     106            7 :     memcpy(query, w.data, w.len);
     107            7 :     size_t qlen = w.len;
     108            7 :     tl_writer_free(&w);
     109              : 
     110            7 :     uint8_t resp[8192]; size_t resp_len = 0;
     111            7 :     if (api_call(cfg, s, t, query, qlen, resp, sizeof(resp), &resp_len) != 0) {
     112            0 :         logger_log(LOG_ERROR, "send: api_call failed");
     113            0 :         return -1;
     114              :     }
     115            7 :     if (resp_len < 4) return -1;
     116              : 
     117              :     uint32_t top;
     118            7 :     memcpy(&top, resp, 4);
     119            7 :     if (top == TL_rpc_error) {
     120            2 :         if (err) rpc_parse_error(resp, resp_len, err);
     121            2 :         return -1;
     122              :     }
     123              : 
     124            5 :     if (msg_id_out) *msg_id_out = 0;
     125              : 
     126              :     /* updateShortSentMessage#9015e101 flags:# out:flags.1?true id:int
     127              :      *   pts:int pts_count:int date:int media:flags.9?MessageMedia
     128              :      *   entities:flags.7?Vector<MessageEntity> ttl_period:flags.25?int */
     129            5 :     if (top == CRC_updateShortSentMessage) {
     130            5 :         TlReader r = tl_reader_init(resp, resp_len);
     131            5 :         tl_read_uint32(&r);                 /* crc */
     132            5 :         tl_read_uint32(&r);                 /* flags */
     133            5 :         if (tl_reader_ok(&r) && r.len - r.pos >= 4) {
     134            5 :             int32_t id = tl_read_int32(&r);
     135            5 :             if (msg_id_out) *msg_id_out = id;
     136              :         }
     137            5 :         return 0;
     138              :     }
     139              : 
     140              :     /* For updates / updatesCombined / updateShort we just accept success.
     141              :      * Message-id extraction from the full Updates envelope is future work. */
     142            0 :     if (top == CRC_updates || top == CRC_updatesCombined
     143            0 :         || top == CRC_updateShort) {
     144            0 :         return 0;
     145              :     }
     146              : 
     147            0 :     logger_log(LOG_WARN, "send: unexpected top 0x%08x — assuming success", top);
     148            0 :     return 0;
     149              : }
     150              : 
     151            8 : int domain_send_message(const ApiConfig *cfg,
     152              :                          MtProtoSession *s, Transport *t,
     153              :                          const HistoryPeer *peer,
     154              :                          const char *message,
     155              :                          int32_t *msg_id_out,
     156              :                          RpcError *err) {
     157            8 :     return domain_send_message_reply(cfg, s, t, peer, message, 0,
     158              :                                        msg_id_out, err);
     159              : }
        

Generated by: LCOV version 2.0-1