1 | /* |
2 | This file is part of Mtproto-proxy Library. |
3 | |
4 | Mtproto-proxy Library is free software: you can redistribute it and/or modify |
5 | it under the terms of the GNU Lesser General Public License as published by |
6 | the Free Software Foundation, either version 2 of the License, or |
7 | (at your option) any later version. |
8 | |
9 | Mtproto-proxy Library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | GNU Lesser General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Lesser General Public License |
15 | along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>. |
16 | |
17 | Copyright 2010-2013 Vkontakte Ltd |
18 | 2010-2013 Nikolai Durov |
19 | 2010-2013 Andrey Lopatin |
20 | 2013 Vitaliy Valtman |
21 | |
22 | Copyright 2014-2018 Telegram Messenger Inc |
23 | 2015-2016 Vitaly Valtman |
24 | 2016-2018 Nikolai Durov |
25 | */ |
26 | |
27 | #define _FILE_OFFSET_BITS 64 |
28 | |
29 | #include <assert.h> |
30 | #include <string.h> |
31 | #include <stdio.h> |
32 | #include <stdlib.h> |
33 | #include <time.h> |
34 | #include <unistd.h> |
35 | |
36 | #include "crc32.h" |
37 | #include "crc32c.h" |
38 | #include "common/sha256.h" |
39 | #include "net/net-events.h" |
40 | #include "kprintf.h" |
41 | #include "precise-time.h" |
42 | #include "net/net-connections.h" |
43 | #include "net/net-tcp-rpc-ext-server.h" |
44 | #include "net/net-tcp-connections.h" |
45 | #include "net/net-thread.h" |
46 | |
47 | #include "rpc-const.h" |
48 | |
49 | #include "net/net-crypto-aes.h" |
50 | //#include "net/net-config.h" |
51 | |
52 | #include "vv/vv-io.h" |
53 | /* |
54 | * |
55 | * EXTERNAL RPC SERVER INTERFACE |
56 | * |
57 | */ |
58 | |
59 | int tcp_rpcs_compact_parse_execute (connection_job_t c); |
60 | |
61 | conn_type_t ct_tcp_rpc_ext_server = { |
62 | .magic = CONN_FUNC_MAGIC, |
63 | .flags = C_RAWMSG, |
64 | .title = "rpc_ext_server" , |
65 | .init_accepted = tcp_rpcs_init_accepted_nohs, |
66 | .parse_execute = tcp_rpcs_compact_parse_execute, |
67 | .close = tcp_rpcs_close_connection, |
68 | .flush = tcp_rpc_flush, |
69 | .write_packet = tcp_rpc_write_packet_compact, |
70 | .connected = server_failed, |
71 | .wakeup = tcp_rpcs_wakeup, |
72 | .alarm = tcp_rpcs_alarm, |
73 | .crypto_init = aes_crypto_ctr128_init, |
74 | .crypto_free = aes_crypto_free, |
75 | .crypto_encrypt_output = cpu_tcp_aes_crypto_ctr128_encrypt_output, |
76 | .crypto_decrypt_input = cpu_tcp_aes_crypto_ctr128_decrypt_input, |
77 | .crypto_needed_output_bytes = cpu_tcp_aes_crypto_ctr128_needed_output_bytes, |
78 | }; |
79 | |
80 | int tcp_rpcs_default_execute (connection_job_t c, int op, struct raw_message *msg); |
81 | |
82 | static unsigned char ext_secret[16][16]; |
83 | static int ext_secret_cnt = 0; |
84 | |
85 | void tcp_rpcs_set_ext_secret(unsigned char secret[16]) { |
86 | assert (ext_secret_cnt < 16); |
87 | memcpy (ext_secret[ext_secret_cnt ++], secret, 16); |
88 | } |
89 | |
90 | /* |
91 | struct tcp_rpc_server_functions default_tcp_rpc_ext_server = { |
92 | .execute = tcp_rpcs_default_execute, |
93 | .check_ready = server_check_ready, |
94 | .flush_packet = tcp_rpc_flush_packet, |
95 | .rpc_wakeup = tcp_rpcs_do_wakeup, |
96 | .rpc_alarm = tcp_rpcs_do_wakeup, |
97 | .rpc_check_perm = tcp_rpcs_default_check_perm, |
98 | .rpc_init_crypto = tcp_rpcs_init_crypto, |
99 | .rpc_ready = server_noop, |
100 | }; |
101 | */ |
102 | |
103 | int tcp_rpcs_compact_parse_execute (connection_job_t C) { |
104 | struct tcp_rpc_data *D = TCP_RPC_DATA (C); |
105 | if (D->crypto_flags & RPCF_COMPACT_OFF) { |
106 | return tcp_rpcs_parse_execute (C); |
107 | } |
108 | |
109 | struct connection_info *c = CONN_INFO (C); |
110 | int len; |
111 | |
112 | vkprintf (4, "%s. in_total_bytes = %d\n" , __func__, c->in.total_bytes); |
113 | |
114 | while (1) { |
115 | if (c->flags & C_ERROR) { |
116 | return NEED_MORE_BYTES; |
117 | } |
118 | if (c->flags & C_STOPPARSE) { |
119 | return NEED_MORE_BYTES; |
120 | } |
121 | len = c->in.total_bytes; |
122 | if (len <= 0) { |
123 | return NEED_MORE_BYTES; |
124 | } |
125 | |
126 | int min_len = (D->flags & RPC_F_MEDIUM) ? 4 : 1; |
127 | |
128 | if (len < min_len + 8) { |
129 | return min_len + 8 - len; |
130 | } |
131 | |
132 | int packet_len = 0; |
133 | assert (rwm_fetch_lookup (&c->in, &packet_len, 4) == 4); |
134 | |
135 | if (D->in_packet_num == -3) { |
136 | vkprintf (1, "trying to determine connection type\n" ); |
137 | #if __ALLOW_UNOBFS__ |
138 | if ((packet_len & 0xff) == 0xef) { |
139 | D->flags |= RPC_F_COMPACT; |
140 | assert (rwm_skip_data (&c->in, 1) == 1); |
141 | D->in_packet_num = 0; |
142 | vkprintf (1, "Short type\n" ); |
143 | continue; |
144 | } |
145 | if (packet_len == 0xeeeeeeee) { |
146 | D->flags |= RPC_F_MEDIUM; |
147 | assert (rwm_skip_data (&c->in, 4) == 4); |
148 | D->in_packet_num = 0; |
149 | vkprintf (1, "Medium type\n" ); |
150 | continue; |
151 | } |
152 | |
153 | // http |
154 | if ((packet_len == *(int *)"HEAD" || packet_len == *(int *)"POST" || packet_len == *(int *)"GET " || packet_len == *(int *)"OPTI" ) && TCP_RPCS_FUNC(C)->http_fallback_type) { |
155 | D->crypto_flags |= RPCF_COMPACT_OFF; |
156 | vkprintf (1, "HTTP type\n" ); |
157 | return tcp_rpcs_parse_execute (C); |
158 | } |
159 | |
160 | int tmp[2]; |
161 | assert (rwm_fetch_lookup (&c->in, &tmp, 8) == 8); |
162 | if (!tmp[1]) { |
163 | D->crypto_flags |= RPCF_COMPACT_OFF; |
164 | vkprintf (1, "Long type\n" ); |
165 | return tcp_rpcs_parse_execute (C); |
166 | } |
167 | #endif |
168 | |
169 | if (len < 64) { |
170 | #if __ALLOW_UNOBFS__ |
171 | vkprintf (1, "random 64-byte header: first 0x%08x 0x%08x, need %d more bytes to distinguish\n" , tmp[0], tmp[1], 64 - len); |
172 | #else |
173 | vkprintf (1, "\"random\" 64-byte header: have %d bytes, need %d more bytes to distinguish\n" , len, 64 - len); |
174 | #endif |
175 | return 64 - len; |
176 | } |
177 | |
178 | unsigned char random_header[64]; |
179 | unsigned char k[48]; |
180 | assert (rwm_fetch_lookup (&c->in, random_header, 64) == 64); |
181 | |
182 | unsigned char random_header_sav[64]; |
183 | memcpy (random_header_sav, random_header, 64); |
184 | |
185 | struct aes_key_data key_data; |
186 | |
187 | int ok = 0; |
188 | int secret_id; |
189 | for (secret_id = 0; secret_id < 1 || secret_id < ext_secret_cnt; secret_id++) { |
190 | if (ext_secret_cnt > 0) { |
191 | memcpy (k, random_header + 8, 32); |
192 | memcpy (k + 32, ext_secret[secret_id], 16); |
193 | sha256 (k, 48, key_data.read_key); |
194 | } else { |
195 | memcpy (key_data.read_key, random_header + 8, 32); |
196 | } |
197 | memcpy (key_data.read_iv, random_header + 40, 16); |
198 | |
199 | int i; |
200 | for (i = 0; i < 32; i++) { |
201 | key_data.write_key[i] = random_header[55 - i]; |
202 | } |
203 | for (i = 0; i < 16; i++) { |
204 | key_data.write_iv[i] = random_header[23 - i]; |
205 | } |
206 | |
207 | if (ext_secret_cnt > 0) { |
208 | memcpy (k, key_data.write_key, 32); |
209 | sha256 (k, 48, key_data.write_key); |
210 | } |
211 | |
212 | aes_crypto_ctr128_init (C, &key_data, sizeof (key_data)); |
213 | assert (c->crypto); |
214 | struct aes_crypto *T = c->crypto; |
215 | |
216 | T->read_aeskey.type->ctr128_crypt (&T->read_aeskey, random_header, random_header, 64, T->read_iv, T->read_ebuf, &T->read_num); |
217 | unsigned tag = *(unsigned *)(random_header + 56); |
218 | |
219 | if (tag == 0xeeeeeeee || tag == 0xefefefef) { |
220 | assert (rwm_skip_data (&c->in, 64) == 64); |
221 | rwm_union (&c->in_u, &c->in); |
222 | rwm_init (&c->in, 0); |
223 | // T->read_pos = 64; |
224 | D->in_packet_num = 0; |
225 | switch (tag) { |
226 | case 0xeeeeeeee: |
227 | D->flags |= RPC_F_MEDIUM | RPC_F_EXTMODE2; |
228 | break; |
229 | case 0xefefefef: |
230 | D->flags |= RPC_F_COMPACT | RPC_F_EXTMODE2; |
231 | break; |
232 | } |
233 | assert (c->type->crypto_decrypt_input (C) >= 0); |
234 | |
235 | int target = *(short *)(random_header + 60); |
236 | D->extra_int4 = target; |
237 | vkprintf (1, "tcp opportunistic encryption mode detected, tag = %08x, target=%d\n" , tag, target); |
238 | ok = 1; |
239 | break; |
240 | } else { |
241 | aes_crypto_free (C); |
242 | memcpy (random_header, random_header_sav, 64); |
243 | } |
244 | } |
245 | |
246 | if (ok) { |
247 | continue; |
248 | } |
249 | |
250 | if (ext_secret_cnt > 0) { |
251 | vkprintf (1, "invalid \"random\" 64-byte header, entering global skip mode\n" ); |
252 | return (-1 << 28); |
253 | } |
254 | |
255 | #if __ALLOW_UNOBFS__ |
256 | vkprintf (1, "short type with 64-byte header: first 0x%08x 0x%08x\n" , tmp[0], tmp[1]); |
257 | D->flags |= RPC_F_COMPACT | RPC_F_EXTMODE1; |
258 | D->in_packet_num = 0; |
259 | |
260 | assert (len >= 64); |
261 | assert (rwm_skip_data (&c->in, 64) == 64); |
262 | continue; |
263 | #else |
264 | vkprintf (1, "invalid \"random\" 64-byte header, entering global skip mode\n" ); |
265 | return (-1 << 28); |
266 | #endif |
267 | } |
268 | |
269 | int packet_len_bytes = 4; |
270 | if (D->flags & RPC_F_MEDIUM) { |
271 | // packet len in `medium` mode |
272 | if (D->crypto_flags & RPCF_QUICKACK) { |
273 | D->flags = (D->flags & ~RPC_F_QUICKACK) | (packet_len & RPC_F_QUICKACK); |
274 | packet_len &= ~RPC_F_QUICKACK; |
275 | } |
276 | } else { |
277 | // packet len in `compact` mode |
278 | if (packet_len & 0x80) { |
279 | D->flags |= RPC_F_QUICKACK; |
280 | packet_len &= ~0x80; |
281 | } else { |
282 | D->flags &= ~RPC_F_QUICKACK; |
283 | } |
284 | if ((packet_len & 0xff) == 0x7f) { |
285 | packet_len = ((unsigned) packet_len >> 8); |
286 | if (packet_len < 0x7f) { |
287 | vkprintf (1, "error while parsing compact packet: got length %d in overlong encoding\n" , packet_len); |
288 | fail_connection (C, -1); |
289 | return 0; |
290 | } |
291 | } else { |
292 | packet_len &= 0x7f; |
293 | packet_len_bytes = 1; |
294 | } |
295 | packet_len <<= 2; |
296 | } |
297 | |
298 | if (packet_len <= 0 || (packet_len & 0xc0000003)) { |
299 | vkprintf (1, "error while parsing packet: bad packet length %d\n" , packet_len); |
300 | fail_connection (C, -1); |
301 | return 0; |
302 | } |
303 | |
304 | if ((packet_len > TCP_RPCS_FUNC(C)->max_packet_len && TCP_RPCS_FUNC(C)->max_packet_len > 0)) { |
305 | vkprintf (1, "error while parsing packet: bad packet length %d\n" , packet_len); |
306 | fail_connection (C, -1); |
307 | return 0; |
308 | } |
309 | |
310 | if (len < packet_len + packet_len_bytes) { |
311 | return packet_len + packet_len_bytes - len; |
312 | } |
313 | |
314 | assert (rwm_skip_data (&c->in, packet_len_bytes) == packet_len_bytes); |
315 | |
316 | struct raw_message msg; |
317 | int packet_type; |
318 | |
319 | rwm_split_head (&msg, &c->in, packet_len); |
320 | |
321 | assert (rwm_fetch_lookup (&msg, &packet_type, 4) == 4); |
322 | |
323 | if (D->in_packet_num < 0) { |
324 | assert (D->in_packet_num == -3); |
325 | D->in_packet_num = 0; |
326 | } |
327 | |
328 | if (verbosity > 2) { |
329 | fprintf (stderr, "received packet from connection %d (length %d, num %d, type %08x)\n" , c->fd, packet_len, D->in_packet_num, packet_type); |
330 | rwm_dump (&msg); |
331 | } |
332 | |
333 | int res = -1; |
334 | |
335 | /* main case */ |
336 | c->last_response_time = precise_now; |
337 | if (packet_type == RPC_PING) { |
338 | res = tcp_rpcs_default_execute (C, packet_type, &msg); |
339 | } else { |
340 | res = TCP_RPCS_FUNC(C)->execute (C, packet_type, &msg); |
341 | } |
342 | if (res <= 0) { |
343 | rwm_free (&msg); |
344 | } |
345 | |
346 | D->in_packet_num++; |
347 | } |
348 | return NEED_MORE_BYTES; |
349 | } |
350 | |
351 | /* |
352 | * |
353 | * END (EXTERNAL RPC SERVER) |
354 | * |
355 | */ |
356 | |