Line data Source code
1 : /**
2 : * @file test_domain_user_info.c
3 : * @brief Unit tests for domain_resolve_username (US-09 v1).
4 : */
5 :
6 : #include "test_helpers.h"
7 : #include "domain/read/user_info.h"
8 : #include "tl_serial.h"
9 : #include "tl_registry.h"
10 : #include "mock_socket.h"
11 : #include "mock_crypto.h"
12 : #include "mtproto_session.h"
13 : #include "transport.h"
14 : #include "api_call.h"
15 :
16 : #include <stdlib.h>
17 : #include <string.h>
18 :
19 2 : static void build_fake_encrypted_response(const uint8_t *payload, size_t plen,
20 : uint8_t *out, size_t *out_len) {
21 2 : TlWriter w; tl_writer_init(&w);
22 2 : uint8_t zeros24[24] = {0}; tl_write_raw(&w, zeros24, 24);
23 2 : uint8_t header[32] = {0};
24 2 : uint32_t plen32 = (uint32_t)plen;
25 2 : memcpy(header + 28, &plen32, 4);
26 2 : tl_write_raw(&w, header, 32);
27 2 : tl_write_raw(&w, payload, plen);
28 2 : size_t enc = w.len - 24;
29 2 : if (enc % 16 != 0) {
30 2 : uint8_t pad[16] = {0}; tl_write_raw(&w, pad, 16 - (enc % 16));
31 : }
32 2 : out[0] = (uint8_t)(w.len / 4);
33 2 : memcpy(out + 1, w.data, w.len);
34 2 : *out_len = 1 + w.len;
35 2 : tl_writer_free(&w);
36 2 : }
37 :
38 3 : static void fix_session(MtProtoSession *s) {
39 3 : mtproto_session_init(s);
40 3 : s->session_id = 0; /* match the zero session_id in fake encrypted frames */
41 3 : uint8_t k[256] = {0}; mtproto_session_set_auth_key(s, k);
42 3 : mtproto_session_set_salt(s, 0xFFEEDDCCBBAA9988ULL);
43 3 : }
44 3 : static void fix_transport(Transport *t) {
45 3 : transport_init(t); t->fd = 42; t->connected = 1; t->dc_id = 1;
46 3 : }
47 3 : static void fix_cfg(ApiConfig *cfg) {
48 3 : api_config_init(cfg); cfg->api_id = 12345; cfg->api_hash = "deadbeef";
49 3 : }
50 :
51 : /* Build a minimal contacts.resolvedPeer response where peer is a channel,
52 : * chats contains a single channel with access_hash, and users is empty. */
53 1 : static size_t make_channel_response(uint8_t *buf, size_t max,
54 : int64_t channel_id, int64_t hash) {
55 1 : TlWriter w; tl_writer_init(&w);
56 1 : tl_write_uint32(&w, TL_contacts_resolvedPeer);
57 1 : tl_write_uint32(&w, TL_peerChannel);
58 1 : tl_write_int64 (&w, channel_id);
59 :
60 1 : tl_write_uint32(&w, TL_vector);
61 1 : tl_write_uint32(&w, 1);
62 1 : tl_write_uint32(&w, TL_channel);
63 1 : tl_write_uint32(&w, 1u << 13); /* flags: access_hash present */
64 1 : tl_write_uint32(&w, 0); /* flags2 */
65 1 : tl_write_int64 (&w, channel_id);
66 1 : tl_write_int64 (&w, hash);
67 :
68 1 : tl_write_uint32(&w, TL_vector);
69 1 : tl_write_uint32(&w, 0);
70 :
71 1 : size_t n = w.len < max ? w.len : max;
72 1 : memcpy(buf, w.data, n);
73 1 : tl_writer_free(&w);
74 1 : return n;
75 : }
76 :
77 1 : static void test_resolve_channel(void) {
78 1 : mock_socket_reset(); mock_crypto_reset();
79 :
80 : uint8_t payload[256];
81 1 : size_t plen = make_channel_response(payload, sizeof(payload),
82 : 1001234567890LL, 0xABCDEF1234567890LL);
83 1 : uint8_t resp[1024]; size_t rlen = 0;
84 1 : build_fake_encrypted_response(payload, plen, resp, &rlen);
85 1 : mock_socket_set_response(resp, rlen);
86 :
87 : MtProtoSession s; Transport t; ApiConfig cfg;
88 1 : fix_session(&s); fix_transport(&t); fix_cfg(&cfg);
89 :
90 1 : ResolvedPeer r = {0};
91 1 : int rc = domain_resolve_username(&cfg, &s, &t, "@channel_example", &r);
92 1 : ASSERT(rc == 0, "resolve: must succeed");
93 1 : ASSERT(r.kind == RESOLVED_KIND_CHANNEL, "kind=channel");
94 1 : ASSERT(r.id == 1001234567890LL, "id matches");
95 1 : ASSERT(r.have_hash == 1, "access_hash present");
96 1 : ASSERT(r.access_hash == (int64_t)0xABCDEF1234567890LL, "access_hash value");
97 1 : ASSERT(strcmp(r.username, "channel_example") == 0, "username echoed");
98 : }
99 :
100 1 : static void test_resolve_rpc_error(void) {
101 1 : mock_socket_reset(); mock_crypto_reset();
102 :
103 1 : TlWriter w; tl_writer_init(&w);
104 1 : tl_write_uint32(&w, TL_rpc_error);
105 1 : tl_write_int32(&w, 400);
106 1 : tl_write_string(&w, "USERNAME_INVALID");
107 1 : uint8_t payload[64]; memcpy(payload, w.data, w.len);
108 1 : size_t plen = w.len; tl_writer_free(&w);
109 :
110 1 : uint8_t resp[512]; size_t rlen = 0;
111 1 : build_fake_encrypted_response(payload, plen, resp, &rlen);
112 1 : mock_socket_set_response(resp, rlen);
113 :
114 : MtProtoSession s; Transport t; ApiConfig cfg;
115 1 : fix_session(&s); fix_transport(&t); fix_cfg(&cfg);
116 :
117 1 : ResolvedPeer r = {0};
118 1 : int rc = domain_resolve_username(&cfg, &s, &t, "@bogus", &r);
119 1 : ASSERT(rc != 0, "RPC error must propagate");
120 : }
121 :
122 1 : static void test_resolve_null_args(void) {
123 : ResolvedPeer r;
124 1 : ASSERT(domain_resolve_username(NULL, NULL, NULL, NULL, &r) == -1, "nulls");
125 1 : ApiConfig cfg; fix_cfg(&cfg);
126 1 : MtProtoSession s; fix_session(&s);
127 1 : Transport t; fix_transport(&t);
128 1 : ASSERT(domain_resolve_username(&cfg, &s, &t, NULL, &r) == -1, "null name");
129 : }
130 :
131 1 : void run_domain_user_info_tests(void) {
132 1 : RUN_TEST(test_resolve_channel);
133 1 : RUN_TEST(test_resolve_rpc_error);
134 1 : RUN_TEST(test_resolve_null_args);
135 1 : }
|