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
58struct 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
66char *config_filename;
67
68static 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
113void 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
121void 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
127void 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
144conn_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
196static 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
206static 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
217struct 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
227void 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
235static 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
247int 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
354static 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
358int 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