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

            Line data    Source code
       1              : /**
       2              :  * @file test_dc_session_cache_skip.c
       3              :  * @brief TEST-21 — cached DC session skips DH handshake on second open.
       4              :  *
       5              :  * Validates US-15 acceptance criterion:
       6              :  *   "Cached foreign sessions skip the full handshake on every subsequent
       7              :  *    request."
       8              :  *
       9              :  * Strategy:
      10              :  *   1. Seed the home DC-2 session and a DC-4 foreign session on disk via
      11              :  *      mt_server_seed_session + mt_server_seed_extra_dc.  This simulates
      12              :  *      a prior run that performed the full DH handshake + auth.importAuth
      13              :  *      and persisted the resulting key.
      14              :  *   2. Call dc_session_open(4) — the fast path (session_store_load_dc)
      15              :  *      must be taken; zero DH-handshake CRCs are expected.
      16              :  *   3. Close the session and call dc_session_open(4) a second time — the
      17              :  *      persisted key is still on disk; again zero DH CRCs expected.
      18              :  *
      19              :  * DH handshake CRCs tracked:
      20              :  *   req_pq_multi       0xbe7e8ef1
      21              :  *   req_pq             0x60469778
      22              :  *   req_DH_params      0xd712e4be
      23              :  *   set_client_DH_params 0xf5045f1f
      24              :  */
      25              : 
      26              : #include "test_helpers.h"
      27              : #include "mock_tel_server.h"
      28              : #include "mock_socket.h"
      29              : 
      30              : #include "app/dc_session.h"
      31              : #include "app/session_store.h"
      32              : #include "transport.h"
      33              : #include "mtproto_session.h"
      34              : 
      35              : #include <stdlib.h>
      36              : #include <string.h>
      37              : #include <sys/stat.h>
      38              : #include <unistd.h>
      39              : 
      40              : /* TL CRCs for MTProto DH-handshake messages (client → server). */
      41              : #define CRC_req_pq_multi         0xbe7e8ef1U
      42              : #define CRC_req_pq               0x60469778U
      43              : #define CRC_req_DH_params        0xd712e4beU
      44              : #define CRC_set_client_DH_params 0xf5045f1fU
      45              : 
      46            6 : static void with_tmp_home(const char *tag) {
      47              :     char tmp[256];
      48            6 :     snprintf(tmp, sizeof(tmp), "/tmp/tg-cli-ft-dcsess-%s", tag);
      49              :     /* Ensure config dir exists (mkdir -p equivalent via three mkdir calls). */
      50              :     char cfg_dir[512];
      51            6 :     snprintf(cfg_dir, sizeof(cfg_dir), "%s/.config", tmp);
      52            6 :     (void)mkdir(tmp, 0700);
      53            6 :     (void)mkdir(cfg_dir, 0700);
      54            6 :     snprintf(cfg_dir, sizeof(cfg_dir), "%s/.config/tg-cli", tmp);
      55            6 :     (void)mkdir(cfg_dir, 0700);
      56              :     /* Wipe any leftover session file from a prior run. */
      57              :     char bin[600];
      58            6 :     snprintf(bin, sizeof(bin), "%s/session.bin", cfg_dir);
      59            6 :     (void)unlink(bin);
      60            6 :     setenv("HOME", tmp, 1);
      61            6 : }
      62              : 
      63              : /** Assert no DH-handshake frame was sent for the last dc_session_open call. */
      64            6 : static void assert_no_handshake_crcs(void) {
      65            6 :     ASSERT(mt_server_request_crc_count(CRC_req_pq_multi) == 0,
      66              :            "no req_pq_multi (0xbe7e8ef1) — DH handshake NOT triggered");
      67            6 :     ASSERT(mt_server_request_crc_count(CRC_req_pq) == 0,
      68              :            "no req_pq (0x60469778) — DH handshake NOT triggered");
      69            6 :     ASSERT(mt_server_request_crc_count(CRC_req_DH_params) == 0,
      70              :            "no req_DH_params (0xd712e4be) — DH handshake NOT triggered");
      71            6 :     ASSERT(mt_server_request_crc_count(CRC_set_client_DH_params) == 0,
      72              :            "no set_client_DH_params (0xf5045f1f) — DH handshake NOT triggered");
      73              : }
      74              : 
      75              : /**
      76              :  * FT-21a — first dc_session_open on DC 4 with a pre-seeded session.
      77              :  *
      78              :  * dc_session_open must load from session_store (fast path) and open the
      79              :  * transport without sending any DH-handshake frames.
      80              :  */
      81            2 : static void test_dc4_first_open_uses_cache(void) {
      82            2 :     with_tmp_home("first");
      83            2 :     mt_server_init();
      84            2 :     mt_server_reset();
      85              : 
      86              :     /* Seed home DC 2 and foreign DC 4 — simulates a prior successful run. */
      87            2 :     ASSERT(mt_server_seed_session(2, NULL, NULL, NULL) == 0,
      88              :            "seed home DC 2");
      89            2 :     ASSERT(mt_server_seed_extra_dc(4) == 0,
      90              :            "seed DC 4 session on disk");
      91              : 
      92              :     DcSession sess;
      93            2 :     ASSERT(dc_session_open(4, &sess) == 0,
      94              :            "dc_session_open(4) succeeds via cached key");
      95            2 :     ASSERT(sess.dc_id == 4,     "dc_id is 4");
      96            2 :     ASSERT(sess.authorized == 1, "marked authorized (key was cached)");
      97              : 
      98              :     /* Core assertion: no DH handshake frames crossed the wire. */
      99            2 :     assert_no_handshake_crcs();
     100              : 
     101            2 :     dc_session_close(&sess);
     102            2 :     mt_server_reset();
     103              : }
     104              : 
     105              : /**
     106              :  * FT-21b — second dc_session_open on DC 4: session persisted by prior call.
     107              :  *
     108              :  * After FT-21a the DC-4 key is still in session.bin.  A second open must
     109              :  * again take the fast path (zero handshake RPCs).
     110              :  */
     111            2 : static void test_dc4_second_open_skips_handshake(void) {
     112            2 :     with_tmp_home("second");
     113            2 :     mt_server_init();
     114            2 :     mt_server_reset();
     115              : 
     116              :     /* Seed home DC 2 and DC 4. */
     117            2 :     ASSERT(mt_server_seed_session(2, NULL, NULL, NULL) == 0,
     118              :            "seed home DC 2");
     119            2 :     ASSERT(mt_server_seed_extra_dc(4) == 0,
     120              :            "seed DC 4 session on disk");
     121              : 
     122              :     /* First open — fast path, establishes session + closes. */
     123              :     DcSession sess1;
     124            2 :     ASSERT(dc_session_open(4, &sess1) == 0, "first dc_session_open ok");
     125            2 :     dc_session_close(&sess1);
     126              : 
     127              :     /* Reset CRC counters so the second open is measured in isolation. */
     128            2 :     mt_server_reset();
     129              :     /* Re-seed so the mock can handle the new transport connection. */
     130            2 :     ASSERT(mt_server_seed_session(2, NULL, NULL, NULL) == 0,
     131              :            "re-seed DC 2 after reset");
     132              :     /* Note: mt_server_reset wipes seeded flag but session.bin persists on
     133              :      * disk because HOME is still the same tmpdir. The server only needs to
     134              :      * know the key for encrypted-frame decryption; mt_server_seed_session
     135              :      * restores g_srv.auth_key to the same deterministic bytes. */
     136              : 
     137              :     /* Second open — must skip handshake. */
     138              :     DcSession sess2;
     139            2 :     ASSERT(dc_session_open(4, &sess2) == 0,
     140              :            "second dc_session_open(4) succeeds via cached key");
     141            2 :     ASSERT(sess2.authorized == 1, "still marked authorized");
     142              : 
     143              :     /* Core assertion: second open sent zero DH frames. */
     144            2 :     assert_no_handshake_crcs();
     145              : 
     146            2 :     dc_session_close(&sess2);
     147            2 :     mt_server_reset();
     148              : }
     149              : 
     150              : /**
     151              :  * FT-21c — dc_session_open on DC 4 sets authorized=1 when key is cached.
     152              :  *
     153              :  * Verifies that the fast-path branch in dc_session.c sets out->authorized=1
     154              :  * so that callers need not run auth.importAuthorization again.
     155              :  */
     156            2 : static void test_dc4_cache_hit_authorized_flag(void) {
     157            2 :     with_tmp_home("authflag");
     158            2 :     mt_server_init();
     159            2 :     mt_server_reset();
     160              : 
     161            2 :     ASSERT(mt_server_seed_session(2, NULL, NULL, NULL) == 0,
     162              :            "seed DC 2");
     163            2 :     ASSERT(mt_server_seed_extra_dc(4) == 0,
     164              :            "seed DC 4");
     165              : 
     166              :     DcSession sess;
     167            2 :     ASSERT(dc_session_open(4, &sess) == 0, "dc_session_open ok");
     168            2 :     ASSERT(sess.authorized == 1,
     169              :            "authorized flag is 1 when key loaded from cache");
     170            2 :     assert_no_handshake_crcs();
     171              : 
     172            2 :     dc_session_close(&sess);
     173            2 :     mt_server_reset();
     174              : }
     175              : 
     176            2 : void run_dc_session_cache_skip_tests(void) {
     177            2 :     RUN_TEST(test_dc4_first_open_uses_cache);
     178            2 :     RUN_TEST(test_dc4_second_open_skips_handshake);
     179            2 :     RUN_TEST(test_dc4_cache_hit_authorized_flag);
     180            2 : }
        

Generated by: LCOV version 2.0-1