LCOV - code coverage report
Current view: top level - tests/functional - test_mt_server_smoke.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 79 79
Test Date: 2026-04-20 19:54:22 Functions: 100.0 % 5 5

            Line data    Source code
       1              : /**
       2              :  * @file test_mt_server_smoke.c
       3              :  * @brief End-to-end proof that the mock Telegram server round-trips
       4              :  *        real AES-IGE + SHA-256 against the production client code.
       5              :  *
       6              :  * Wires up:
       7              :  *   mock_socket (in-memory transport)
       8              :  *     ↕
       9              :  *   production rpc_send_encrypted / rpc_recv_encrypted (real crypto)
      10              :  *     ↕
      11              :  *   mock_tel_server (real crypto on the other side)
      12              :  */
      13              : 
      14              : #include "test_helpers.h"
      15              : 
      16              : #include "mock_socket.h"
      17              : #include "mock_tel_server.h"
      18              : 
      19              : #include "mtproto_rpc.h"
      20              : #include "mtproto_session.h"
      21              : #include "transport.h"
      22              : #include "app/session_store.h"
      23              : #include "tl_serial.h"
      24              : 
      25              : #include <stdio.h>
      26              : #include <stdlib.h>
      27              : #include <string.h>
      28              : #include <unistd.h>
      29              : 
      30              : #define CRC_ping     0x7abe77ecU   /* ping#7abe77ec ping_id:long = Pong; */
      31              : #define CRC_pong     0x347773c5U   /* pong#347773c5 msg_id:long ping_id:long = Pong; */
      32              : 
      33            4 : static void with_tmp_home(const char *tag) {
      34              :     char tmp[256];
      35            4 :     snprintf(tmp, sizeof(tmp), "/tmp/tg-cli-mt-server-%s", tag);
      36              :     char bin[512];
      37            4 :     snprintf(bin, sizeof(bin), "%s/.config/tg-cli/session.bin", tmp);
      38            4 :     (void)unlink(bin);
      39            4 :     setenv("HOME", tmp, 1);
      40            4 : }
      41              : 
      42            2 : static void on_ping(MtRpcContext *ctx) {
      43              :     /* ping body = CRC(4) ping_id:long — echo back pong with the same id. */
      44            2 :     if (ctx->req_body_len < 4 + 8) return;
      45            2 :     uint64_t ping_id = 0;
      46           18 :     for (int i = 0; i < 8; ++i) {
      47           16 :         ping_id |= ((uint64_t)ctx->req_body[4 + i]) << (i * 8);
      48              :     }
      49              : 
      50              :     TlWriter w;
      51            2 :     tl_writer_init(&w);
      52            2 :     tl_write_uint32(&w, CRC_pong);
      53            2 :     tl_write_uint64(&w, ctx->req_msg_id);
      54            2 :     tl_write_uint64(&w, ping_id);
      55            2 :     mt_server_reply_result(ctx, w.data, w.len);
      56            2 :     tl_writer_free(&w);
      57              : }
      58              : 
      59            2 : static void test_ping_pong_roundtrip(void) {
      60            2 :     with_tmp_home("ping-pong");
      61            2 :     mt_server_init();
      62            2 :     mt_server_reset();
      63              : 
      64              :     uint8_t auth_key[256];
      65            2 :     uint64_t salt = 0, sid = 0;
      66            2 :     ASSERT(mt_server_seed_session(2, auth_key, &salt, &sid) == 0,
      67              :            "seed session writes disk");
      68            2 :     mt_server_expect(CRC_ping, on_ping, NULL);
      69              : 
      70              :     /* Load that same session into our own MtProtoSession — the production
      71              :      * auth_flow would do this; here we drive session_store directly. */
      72              :     MtProtoSession s;
      73            2 :     mtproto_session_init(&s);
      74            2 :     int dc_id = 0;
      75            2 :     ASSERT(session_store_load(&s, &dc_id) == 0, "session loaded");
      76            2 :     ASSERT(dc_id == 2, "home DC is 2");
      77              : 
      78              :     Transport t;
      79            2 :     transport_init(&t);
      80            2 :     ASSERT(transport_connect(&t, "127.0.0.1", 443) == 0, "fake connect");
      81              : 
      82              :     /* Send a ping. */
      83              :     TlWriter req;
      84            2 :     tl_writer_init(&req);
      85            2 :     tl_write_uint32(&req, CRC_ping);
      86            2 :     tl_write_uint64(&req, 0x1234567890ABCDEFULL);
      87            2 :     ASSERT(rpc_send_encrypted(&s, &t, req.data, req.len, 1) == 0,
      88              :            "send encrypted ping");
      89            2 :     tl_writer_free(&req);
      90              : 
      91              :     /* Read back pong. */
      92              :     uint8_t reply[1024];
      93            2 :     size_t reply_len = 0;
      94            2 :     ASSERT(rpc_recv_encrypted(&s, &t, reply, sizeof(reply), &reply_len) == 0,
      95              :            "recv encrypted pong");
      96            2 :     ASSERT(reply_len >= 4 + 8 + 4 + 8 + 8, "reply big enough for rpc_result+pong");
      97              : 
      98              :     /* rpc_result#f35c6d01 req_msg_id:long result:Object */
      99            2 :     TlReader r = tl_reader_init(reply, reply_len);
     100            2 :     uint32_t crc_result = tl_read_uint32(&r);
     101            2 :     ASSERT(crc_result == 0xf35c6d01U, "outer is rpc_result");
     102            2 :     tl_read_uint64(&r); /* req_msg_id */
     103            2 :     uint32_t crc_pong = tl_read_uint32(&r);
     104            2 :     ASSERT(crc_pong == CRC_pong, "inner is pong");
     105            2 :     tl_read_uint64(&r); /* msg_id echo */
     106            2 :     uint64_t returned_ping_id = tl_read_uint64(&r);
     107            2 :     ASSERT(returned_ping_id == 0x1234567890ABCDEFULL, "ping_id round-trips");
     108              : 
     109            2 :     ASSERT(mt_server_rpc_call_count() == 1, "exactly one RPC dispatched");
     110              : 
     111            2 :     transport_close(&t);
     112            2 :     mt_server_reset();
     113              : }
     114              : 
     115            2 : static void test_unknown_crc_returns_rpc_error(void) {
     116            2 :     with_tmp_home("no-handler");
     117            2 :     mt_server_init();
     118            2 :     mt_server_reset();
     119              : 
     120            2 :     ASSERT(mt_server_seed_session(2, NULL, NULL, NULL) == 0, "seed");
     121              :     /* no handler registered */
     122              : 
     123              :     MtProtoSession s;
     124            2 :     mtproto_session_init(&s);
     125            2 :     int dc_id = 0;
     126            2 :     ASSERT(session_store_load(&s, &dc_id) == 0, "load");
     127              : 
     128              :     Transport t;
     129            2 :     transport_init(&t);
     130            2 :     transport_connect(&t, "127.0.0.1", 443);
     131              : 
     132              :     /* Send an unregistered CRC (e.g. help.getConfig#c4f9186b). */
     133              :     TlWriter req;
     134            2 :     tl_writer_init(&req);
     135            2 :     tl_write_uint32(&req, 0xc4f9186bU);
     136            2 :     rpc_send_encrypted(&s, &t, req.data, req.len, 1);
     137            2 :     tl_writer_free(&req);
     138              : 
     139              :     uint8_t reply[512];
     140            2 :     size_t reply_len = 0;
     141            2 :     ASSERT(rpc_recv_encrypted(&s, &t, reply, sizeof(reply), &reply_len) == 0,
     142              :            "recv reply");
     143            2 :     uint64_t req_msg_id = 0;
     144            2 :     const uint8_t *inner = NULL;
     145            2 :     size_t inner_len = 0;
     146            2 :     ASSERT(rpc_unwrap_result(reply, reply_len, &req_msg_id, &inner, &inner_len) == 0,
     147              :            "unwrap rpc_result");
     148              :     RpcError err;
     149            2 :     ASSERT(rpc_parse_error(inner, inner_len, &err) == 0,
     150              :            "inner is rpc_error");
     151            2 :     ASSERT(err.error_code == 500, "500 status");
     152            2 :     ASSERT(strstr(err.error_msg, "NO_HANDLER") != NULL,
     153              :            "error message says NO_HANDLER");
     154              : 
     155            2 :     transport_close(&t);
     156            2 :     mt_server_reset();
     157              : }
     158              : 
     159            2 : void run_mt_server_smoke_tests(void) {
     160            2 :     RUN_TEST(test_ping_pong_roundtrip);
     161            2 :     RUN_TEST(test_unknown_crc_returns_rpc_error);
     162            2 : }
        

Generated by: LCOV version 2.0-1