1 | /* |
2 | This file is part of MTProto-Server |
3 | |
4 | MTProto-Server is free software: you can redistribute it and/or modify |
5 | it under the terms of the GNU 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-Server 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 General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU General Public License |
15 | along with MTProto-Server. If not, see <http://www.gnu.org/licenses/>. |
16 | |
17 | This program is released under the GPL with the additional exemption |
18 | that compiling, linking, and/or using OpenSSL is allowed. |
19 | You are free to remove this exemption from derived works. |
20 | |
21 | Copyright 2012-2018 Nikolai Durov |
22 | 2012-2014 Andrey Lopatin |
23 | 2014-2018 Telegram Messenger Inc |
24 | */ |
25 | #define _FILE_OFFSET_BITS 64 |
26 | |
27 | #include <assert.h> |
28 | #include <string.h> |
29 | #include <stdio.h> |
30 | #include <stdlib.h> |
31 | #include <time.h> |
32 | #include <arpa/inet.h> |
33 | #include <fcntl.h> |
34 | #include <netinet/in.h> |
35 | #include <sys/socket.h> |
36 | #include <unistd.h> |
37 | #include <sys/mman.h> |
38 | |
39 | #include "md5.h" |
40 | #include "resolver.h" |
41 | #include "net/net-events.h" |
42 | #include "kprintf.h" |
43 | #include "precise-time.h" |
44 | #include "net/net-connections.h" |
45 | #include "net/net-crypto-aes.h" |
46 | #include "mtproto-common.h" |
47 | #include "mtproto-config.h" |
48 | #include "engine/engine.h" |
49 | #include "common/parse-config.h" |
50 | #include "common/server-functions.h" |
51 | |
52 | /* |
53 | * |
54 | * CONFIGURATION PARSER |
55 | * |
56 | */ |
57 | |
58 | struct mf_config Config[2], *CurConf = Config, *NextConf = Config + 1; |
59 | |
60 | |
61 | //#define MAX_CONFIG_SIZE (1 << 20) |
62 | |
63 | //char config_buff[MAX_CONFIG_SIZE+4], *config_filename, *cfg_start, *cfg_end, *cfg_cur; |
64 | //int config_bytes, cfg_lno, cfg_lex = -1; |
65 | |
66 | char *config_filename; |
67 | |
68 | static int cfg_getlex_ext (void) { |
69 | switch (cfg_skipspc()) { |
70 | case ';': |
71 | case ':': |
72 | case '{': |
73 | case '}': |
74 | return cfg_lex = *cfg_cur++; |
75 | case 'm': |
76 | if (!memcmp (cfg_cur, "min_connections" , 15)) { |
77 | cfg_cur += 15; |
78 | return cfg_lex = 'x'; |
79 | } |
80 | if (!memcmp (cfg_cur, "max_connections" , 15)) { |
81 | cfg_cur += 15; |
82 | return cfg_lex = 'X'; |
83 | } |
84 | break; |
85 | case 'p': |
86 | if (!memcmp (cfg_cur, "proxy_for" , 9)) { |
87 | cfg_cur += 9; |
88 | return cfg_lex = 'Y'; |
89 | } else if (!memcmp (cfg_cur, "proxy" , 5)) { |
90 | cfg_cur += 5; |
91 | return cfg_lex = 'y'; |
92 | } |
93 | break; |
94 | case 't': |
95 | if (!memcmp (cfg_cur, "timeout" , 7)) { |
96 | cfg_cur += 7; |
97 | return cfg_lex = 't'; |
98 | } |
99 | break; |
100 | case 'd': |
101 | if (!memcmp (cfg_cur, "default" , 7)) { |
102 | cfg_cur += 7; |
103 | return cfg_lex = 'D'; |
104 | } |
105 | break; |
106 | case 0: |
107 | return cfg_lex = 0; |
108 | } |
109 | return cfg_lex = -1; |
110 | } |
111 | |
112 | |
113 | void forget_cluster_targets (struct mf_group_stats *GS, struct mf_cluster *MFC, int do_destroy_targets) { |
114 | if (MFC->cluster_targets) { |
115 | MFC->cluster_targets = 0; |
116 | } |
117 | MFC->targets_num = MFC->write_targets_num = 0; |
118 | MFC->targets_allocated = 0; |
119 | } |
120 | |
121 | void clear_mf_cluster (struct mf_group_stats *GS, struct mf_cluster *MFC, int do_destroy_targets) { |
122 | forget_cluster_targets (GS, MFC, do_destroy_targets); |
123 | MFC->flags = 0; |
124 | GS->tot_clusters --; |
125 | } |
126 | |
127 | void clear_config (struct mf_config *MC, int do_destroy_targets) { |
128 | int j; |
129 | if (do_destroy_targets) { |
130 | for (j = 0; j < MC->tot_targets; j++) { |
131 | vkprintf (1, "destroying target %s:%d\n" , inet_ntoa (CONN_TARGET_INFO(MC->targets[j])->target), CONN_TARGET_INFO(MC->targets[j])->port); |
132 | destroy_target (JOB_REF_PASS (MC->targets[j])); |
133 | } |
134 | memset (MC->targets, 0, MC->tot_targets * sizeof (conn_target_job_t)); |
135 | } |
136 | for (j = 0; j < MC->auth_clusters; j++) { |
137 | clear_mf_cluster (&MC->auth_stats, &MC->auth_cluster[j], do_destroy_targets); |
138 | } |
139 | MC->tot_targets = 0; |
140 | MC->auth_clusters = 0; |
141 | memset (&MC->auth_stats, 0, sizeof (struct mf_group_stats)); |
142 | } |
143 | |
144 | conn_target_job_t *cfg_parse_server_port (struct mf_config *MC, int flags) { |
145 | if (MC->tot_targets >= MAX_CFG_TARGETS) { |
146 | syntax ("too many targets (%d)" , MC->tot_targets); |
147 | return 0; |
148 | } |
149 | |
150 | struct hostent *h = cfg_gethost (); |
151 | if (!h) { |
152 | return 0; |
153 | } |
154 | |
155 | if (h->h_addrtype == AF_INET) { |
156 | default_cfg_ct.target = *((struct in_addr *) h->h_addr); |
157 | memset (default_cfg_ct.target_ipv6, 0, 16); |
158 | } else if (h->h_addrtype == AF_INET6) { |
159 | default_cfg_ct.target.s_addr = 0; |
160 | memcpy (default_cfg_ct.target_ipv6, h->h_addr, 16); |
161 | } else { |
162 | syntax ("cannot resolve hostname" ); |
163 | return 0; |
164 | } |
165 | |
166 | //*(cfg_cur += l) = c; |
167 | cfg_getlex_ext (); |
168 | if (expect_lexem (':') < 0) { |
169 | return 0; |
170 | } |
171 | default_cfg_ct.port = cfg_getint(); |
172 | if (!default_cfg_ct.port) { |
173 | syntax ("port number expected" ); |
174 | return 0; |
175 | } |
176 | |
177 | if (default_cfg_ct.port <= 0 || default_cfg_ct.port >= 0x10000) { |
178 | syntax ("port number %d out of range" , default_cfg_ct.port); |
179 | return 0; |
180 | } |
181 | |
182 | default_cfg_ct.min_connections = MC->min_connections; |
183 | default_cfg_ct.max_connections = MC->max_connections; |
184 | default_cfg_ct.reconnect_timeout = 1.0 + 0.1 * drand48 (); |
185 | |
186 | if ((flags & 1)) { |
187 | int was_created = -1; |
188 | conn_target_job_t D = create_target (&default_cfg_ct, &was_created); |
189 | MC->targets[MC->tot_targets] = D; |
190 | vkprintf (3, "new target %p created (%d): ip %s, port %d\n" , D, was_created, inet_ntoa (default_cfg_ct.target), default_cfg_ct.port); |
191 | } |
192 | return &MC->targets[MC->tot_targets++]; |
193 | } |
194 | |
195 | |
196 | static void init_old_mf_cluster (struct mf_group_stats *GS, struct mf_cluster *MFC, conn_target_job_t *targets, int targets_num, int flags, int cluster_id) { |
197 | MFC->flags = flags; |
198 | MFC->targets_num = targets_num; |
199 | MFC->write_targets_num = targets_num; |
200 | MFC->targets_allocated = 0; |
201 | MFC->cluster_targets = targets; |
202 | MFC->cluster_id = cluster_id; |
203 | GS->tot_clusters ++; |
204 | } |
205 | |
206 | static int extend_old_mf_cluster (struct mf_cluster *MFC, conn_target_job_t *target, int cluster_id) { |
207 | if (MFC->cluster_targets + MFC->targets_num != target) { |
208 | return 0; |
209 | } |
210 | if (MFC->cluster_id != cluster_id) { |
211 | return 0; |
212 | } |
213 | MFC->write_targets_num = ++(MFC->targets_num); |
214 | return 1; |
215 | } |
216 | |
217 | struct mf_cluster *mf_cluster_lookup (struct mf_config *MC, int cluster_id, int force) { |
218 | int i; |
219 | for (i = 0; i < MC->auth_clusters; i++) { |
220 | if (MC->auth_cluster[i].cluster_id == cluster_id) { |
221 | return &(MC->auth_cluster[i]); |
222 | } |
223 | } |
224 | return force ? MC->default_cluster : 0; |
225 | } |
226 | |
227 | void dump_mf_cluster (struct mf_cluster *MFC) { |
228 | int i; |
229 | kprintf ("Current state of cluster `%s` (N=%d, M=%d, alloc=%d):\n" , "(nil)" , MFC->targets_num, MFC->write_targets_num, MFC->targets_allocated); |
230 | for (i = 0; i < MFC->targets_num; i++) { |
231 | kprintf ("Target #%d [%c]: %s:%d\n" , i, i < MFC->write_targets_num ? 'W' : 'R', show_ip (ntohl (CONN_TARGET_INFO(MFC->cluster_targets[i])->target.s_addr)), CONN_TARGET_INFO(MFC->cluster_targets[i])->port); |
232 | } |
233 | } |
234 | |
235 | static void preinit_config (struct mf_config *MC) { |
236 | MC->tot_targets = 0; |
237 | MC->auth_clusters = 0; |
238 | MC->min_connections = default_cfg_min_connections; |
239 | MC->max_connections = default_cfg_max_connections; |
240 | MC->timeout = 0.3; |
241 | MC->default_cluster_id = 0; |
242 | MC->default_cluster = 0; |
243 | } |
244 | |
245 | // flags = 0 -- syntax check only (first pass), flags = 1 -- create targets and points as well (second pass) |
246 | // flags: +2 = allow proxies, +4 = allow proxies only, +16 = do not load file |
247 | int parse_config (struct mf_config *MC, int flags, int config_fd) { |
248 | conn_target_job_t *targ_ptr; |
249 | int have_proxy = 0; |
250 | |
251 | assert (flags & 4); |
252 | |
253 | if (!(flags & 17)) { |
254 | if (load_config (config_filename, config_fd) < 0) { |
255 | return -2; |
256 | } |
257 | } |
258 | |
259 | reset_config (); |
260 | |
261 | preinit_config (MC); |
262 | |
263 | while (cfg_skipspc ()) { |
264 | int t, target_dc = 0; |
265 | switch (t = cfg_getlex_ext ()) { |
266 | case 't': |
267 | MC->timeout = cfg_getint (); |
268 | if (MC->timeout < 10 || MC->timeout > 30000) { |
269 | Syntax ("invalid timeout" ); |
270 | } |
271 | MC->timeout /= 1000; |
272 | break; |
273 | case 'D': |
274 | case 'Y': { |
275 | long long targ_dc = cfg_getint_signed_zero(); |
276 | if (targ_dc < -0x8000 || targ_dc >= 0x8000) { |
277 | Syntax ("invalid target id (integer -32768..32767 expected)" , targ_dc); |
278 | } |
279 | if (t == 'D') { |
280 | MC->default_cluster_id = targ_dc; |
281 | break; |
282 | } |
283 | if (*cfg_cur != ' ' && *cfg_cur != 9) { |
284 | Syntax ("space expected after target id" ); |
285 | } |
286 | cfg_skspc (); |
287 | target_dc = targ_dc; |
288 | } |
289 | case 'y': { |
290 | have_proxy |= 1; |
291 | if (MC->auth_clusters >= MAX_CFG_CLUSTERS) { |
292 | Syntax ("too many auth clusters" , MC->auth_clusters); |
293 | } |
294 | targ_ptr = cfg_parse_server_port (MC, flags); |
295 | if (!targ_ptr) { |
296 | return -1; |
297 | } |
298 | struct mf_cluster *MFC = mf_cluster_lookup (MC, target_dc, 0); |
299 | if (!MFC) { |
300 | vkprintf (3, "-> added target to new auth_cluster #%d\n" , MC->auth_clusters); |
301 | if (flags & 1) { |
302 | init_old_mf_cluster (&MC->auth_stats, &MC->auth_cluster[MC->auth_clusters], targ_ptr, 1, 1, target_dc); |
303 | } else { |
304 | MC->auth_cluster[MC->auth_clusters].cluster_id = target_dc; |
305 | } |
306 | MC->auth_clusters ++; |
307 | } else if (MFC == &MC->auth_cluster[MC->auth_clusters - 1]) { |
308 | vkprintf (3, "-> added target to old auth_cluster #%d\n" , MC->auth_clusters - 1); |
309 | if (flags & 1) { |
310 | if (!extend_old_mf_cluster (MFC, targ_ptr, target_dc)) { |
311 | Syntax ("IMPOSSIBLE" ); |
312 | } |
313 | } |
314 | } else { |
315 | Syntax ("proxies for dc %d intermixed" , target_dc); |
316 | } |
317 | break; |
318 | } |
319 | case 'X': |
320 | MC->max_connections = cfg_getint (); |
321 | if (MC->max_connections < MC->min_connections || MC->max_connections > 1000) { |
322 | Syntax ("invalid max connections" ); |
323 | } |
324 | break; |
325 | case 'x': |
326 | MC->min_connections = cfg_getint (); |
327 | if (MC->min_connections < 1 || MC->min_connections > MC->max_connections) { |
328 | Syntax ("invalid min connections" ); |
329 | } |
330 | break; |
331 | case 0: |
332 | break; |
333 | default: |
334 | Syntax ("'proxy <ip>:<port>;' expected" ); |
335 | } |
336 | if (!t) { |
337 | break; |
338 | } |
339 | cfg_getlex_ext (); |
340 | Expect (';'); |
341 | } |
342 | |
343 | if (have_proxy != 1) { |
344 | Syntax ("expected to find a mtproto-proxy configuration with `proxy' directives" ); |
345 | } |
346 | MC->have_proxy = have_proxy & 1; |
347 | if (!MC->auth_clusters) { |
348 | Syntax ("no MTProto next proxy servers defined to forward queries to" ); |
349 | } |
350 | MC->default_cluster = mf_cluster_lookup (MC, MC->default_cluster_id, 0); |
351 | return 0; |
352 | } |
353 | |
354 | static int need_reload_config = 0; |
355 | |
356 | |
357 | // flags: +1 = create targets and connections, +2 = allow proxies, +4 = allow proxies only, +16 = do not re-load file itself, +32 = preload config + perform syntax check, do not apply |
358 | int do_reload_config (int flags) { |
359 | int res; |
360 | need_reload_config = 0; |
361 | |
362 | int fd = -1; |
363 | assert (flags & 4); |
364 | |
365 | if (!(flags & 16)) { |
366 | fd = open (config_filename, O_RDONLY); |
367 | if (fd < 0) { |
368 | kprintf ("cannot re-read config file %s: %m\n" , config_filename); |
369 | return -1; |
370 | } |
371 | |
372 | res = kdb_load_hosts (); |
373 | |
374 | if (res > 0) { |
375 | vkprintf (1, "/etc/hosts changed, reloaded\n" ); |
376 | } |
377 | } |
378 | |
379 | res = parse_config (NextConf, flags & -2, fd); |
380 | |
381 | if (fd >= 0) { |
382 | close (fd); |
383 | } |
384 | |
385 | // clear_config (NextConf); |
386 | |
387 | if (res < 0) { |
388 | kprintf ("error while re-reading config file %s, new configuration NOT applied\n" , config_filename); |
389 | return res; |
390 | } |
391 | |
392 | if ((flags & 32)) { |
393 | return 0; |
394 | } |
395 | |
396 | res = parse_config (NextConf, flags | 1, -1); |
397 | |
398 | if (res < 0) { |
399 | clear_config (NextConf, 0); |
400 | kprintf ("fatal error while re-reading config file %s\n" , config_filename); |
401 | exit (-res); |
402 | } |
403 | |
404 | struct mf_config *tmp = CurConf; |
405 | CurConf = NextConf; |
406 | NextConf = tmp; |
407 | |
408 | clear_config (NextConf, 1); |
409 | |
410 | if (flags & 1) { |
411 | create_all_outbound_connections (); |
412 | } |
413 | |
414 | CurConf->config_loaded_at = now ? now : time (0); |
415 | CurConf->config_bytes = config_bytes; |
416 | CurConf->config_md5_hex = malloc (33); |
417 | md5_hex_config (CurConf->config_md5_hex); |
418 | CurConf->config_md5_hex[32] = 0; |
419 | |
420 | kprintf ("configuration file %s re-read successfully (%d bytes parsed), new configuration active\n" , config_filename, config_bytes); |
421 | |
422 | return 0; |
423 | } |
424 | |
425 | |