| 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 | |