diff options
Diffstat (limited to 'protocols/Telegram/tgl/mtproto-client.c')
-rw-r--r-- | protocols/Telegram/tgl/mtproto-client.c | 1489 |
1 files changed, 1489 insertions, 0 deletions
diff --git a/protocols/Telegram/tgl/mtproto-client.c b/protocols/Telegram/tgl/mtproto-client.c new file mode 100644 index 0000000000..37530b5afb --- /dev/null +++ b/protocols/Telegram/tgl/mtproto-client.c @@ -0,0 +1,1489 @@ +/* + This file is part of tgl-library + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + Copyright Nikolay Durov, Andrey Lopatin 2012-2013 + Vitaly Valtman 2013-2015 +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define _FILE_OFFSET_BITS 64 + +#include <assert.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#if defined(WIN32) || defined(_WIN32) +#include <io.h> +#include <stdint.h> +#include <winsock2.h> +#include <ws2tcpip.h> +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#undef WIN32_LEAN_AND_MEAN +#else +#include <unistd.h> +#include <netdb.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <poll.h> +#endif +#include <fcntl.h> +#if defined(__FreeBSD__) || defined(__OpenBSD__) +#include <sys/endian.h> +#endif +#include <sys/types.h> +#include <openssl/rand.h> +#include <openssl/rsa.h> +#include <openssl/pem.h> +#include <openssl/sha.h> + +//#include "telegram.h" +#include "queries.h" +//#include "loop.h" +#include "tgl-structures.h" +#include "tgl-binlog.h" +#include "auto.h" +#include "auto/auto-types.h" +#include "auto/auto-skip.h" +#include "tgl.h" +#include "mtproto-client.h" +#include "tools.h" +#include "tree.h" +#include "updates.h" +#include "mtproto-utils.h" +#include "auto.h" +#include "tgl-methods-in.h" + +#if defined(__FreeBSD__) +#define __builtin_bswap32(x) bswap32(x) +#endif + +#if defined(__OpenBSD__) +#define __builtin_bswap32(x) __swap32gen(x) +#endif + +#define sha1 SHA1 + +#include "mtproto-common.h" + +#define MAX_NET_RES (1L << 16) +//extern int log_level; + +static long long generate_next_msg_id (struct tgl_state *TLS, struct tgl_dc *DC, struct tgl_session *S); +static double get_server_time (struct tgl_dc *DC); + +#if !defined(HAVE___BUILTIN_BSWAP32) && !defined(__FreeBSD__) && !defined(__OpenBSD__) +static inline unsigned __builtin_bswap32(unsigned x) { + return ((x << 24) & 0xff000000 ) | + ((x << 8) & 0x00ff0000 ) | + ((x >> 8) & 0x0000ff00 ) | + ((x >> 24) & 0x000000ff ); +} +#endif + +// for statistic only +static int total_packets_sent; +static long long total_data_sent; + + +static int rpc_execute (struct tgl_state *TLS, struct connection *c, int op, int len); +static int rpc_becomes_ready (struct tgl_state *TLS, struct connection *c); +static int rpc_close (struct tgl_state *TLS, struct connection *c); + +static double get_utime (int clock_id) { + struct timespec T; + tgl_my_clock_gettime (clock_id, &T); + return T.tv_sec + (double) T.tv_nsec * 1e-9; +} + + +#define MAX_RESPONSE_SIZE (1L << 24) + +static RSA *rsa_load_public_key (struct tgl_state *TLS, const char *public_key_name) { +#if defined(_MSC_VER) && _MSC_VER >= 1400 + FILE * f = NULL; + errno_t err = fopen_s(&f, public_key_name, "r"); + if (err != 0) { +#else + FILE *f = fopen (public_key_name, "r"); + if (f == NULL) { +#endif + vlogprintf (E_WARNING, "Couldn't open public key file: %s\n", public_key_name); + return NULL; + } + RSA *res = PEM_read_RSAPublicKey (f, NULL, NULL, NULL); + fclose (f); + if (res == NULL) { + vlogprintf (E_WARNING, "PEM_read_RSAPublicKey returns NULL.\n"); + return NULL; + } + + vlogprintf (E_NOTICE, "public key '%s' loaded successfully\n", public_key_name); + + return res; +} + + + + +/* + * + * UNAUTHORIZED (DH KEY EXCHANGE) PROTOCOL PART + * + */ + +#define ENCRYPT_BUFFER_INTS 16384 +static int encrypt_buffer[ENCRYPT_BUFFER_INTS]; + +#define DECRYPT_BUFFER_INTS 16384 +static int decrypt_buffer[ENCRYPT_BUFFER_INTS]; + +static int encrypt_packet_buffer (struct tgl_state *TLS, struct tgl_dc *DC) { + RSA *key = TLS->rsa_key_loaded[DC->rsa_key_idx]; + return tgl_pad_rsa_encrypt (TLS, (char *) packet_buffer, (packet_ptr - packet_buffer) * 4, (char *) encrypt_buffer, ENCRYPT_BUFFER_INTS * 4, key->n, key->e); +} + +static int encrypt_packet_buffer_aes_unauth (const char server_nonce[16], const char hidden_client_nonce[32]) { + tgl_init_aes_unauth (server_nonce, hidden_client_nonce, AES_ENCRYPT); + return tgl_pad_aes_encrypt ((char *) packet_buffer, (packet_ptr - packet_buffer) * 4, (char *) encrypt_buffer, ENCRYPT_BUFFER_INTS * 4); +} + +// +// Used in unauthorized part of protocol +// +static int rpc_send_packet (struct tgl_state *TLS, struct connection *c) { + static struct { + long long auth_key_id; + long long out_msg_id; + int msg_len; + } unenc_msg_header; + + int len = (packet_ptr - packet_buffer) * 4; + TLS->net_methods->incr_out_packet_num (c); + + struct tgl_dc *DC = TLS->net_methods->get_dc (c); + struct tgl_session *S = TLS->net_methods->get_session (c); + + unenc_msg_header.out_msg_id = generate_next_msg_id (TLS, DC, S); + unenc_msg_header.msg_len = len; + + int total_len = len + 20; + assert (total_len > 0 && !(total_len & 0xfc000003)); + total_len >>= 2; + vlogprintf (E_DEBUG, "writing packet: total_len = %d, len = %d\n", total_len, len); + if (total_len < 0x7f) { + assert (TLS->net_methods->write_out (c, &total_len, 1) == 1); + } else { + total_len = (total_len << 8) | 0x7f; + assert (TLS->net_methods->write_out (c, &total_len, 4) == 4); + } + TLS->net_methods->write_out (c, &unenc_msg_header, 20); + TLS->net_methods->write_out (c, packet_buffer, len); + TLS->net_methods->flush_out (c); + + total_packets_sent ++; + total_data_sent += total_len; + return 1; +} + +static int rpc_send_message (struct tgl_state *TLS, struct connection *c, void *data, int len) { + assert (len > 0 && !(len & 0xfc000003)); + + int total_len = len >> 2; + if (total_len < 0x7f) { + assert (TLS->net_methods->write_out (c, &total_len, 1) == 1); + } else { + total_len = (total_len << 8) | 0x7f; + assert (TLS->net_methods->write_out (c, &total_len, 4) == 4); + } + + TLS->net_methods->incr_out_packet_num (c); + assert (TLS->net_methods->write_out (c, data, len) == len); + TLS->net_methods->flush_out (c); + + total_packets_sent ++; + total_data_sent += total_len; + return 1; +} + +// +// State machine. See description at +// https://core.telegram.org/mtproto/auth_key +// + + +static int check_unauthorized_header (struct tgl_state *TLS) { + long long auth_key_id = fetch_long (); + if (auth_key_id) { + vlogprintf (E_ERROR, "ERROR: auth_key_id should be NULL\n"); + return -1; + } + fetch_long (); // msg_id + int len = fetch_int (); + if (len != 4 * (in_end - in_ptr)) { + vlogprintf (E_ERROR, "ERROR: length mismatch\n"); + return -1; + } + return 0; +} + +/* {{{ REQ_PQ */ +// req_pq#60469778 nonce:int128 = ResPQ +static int send_req_pq_packet (struct tgl_state *TLS, struct connection *c) { + struct tgl_dc *DC = TLS->net_methods->get_dc (c); + assert (DC->state == st_init); + + tglt_secure_random (DC->nonce, 16); + clear_packet (); + out_int (CODE_req_pq); + out_ints ((int *)DC->nonce, 4); + rpc_send_packet (TLS, c); + + DC->state = st_reqpq_sent; + return 1; +} + +// req_pq#60469778 nonce:int128 = ResPQ +static int send_req_pq_temp_packet (struct tgl_state *TLS, struct connection *c) { + struct tgl_dc *DC = TLS->net_methods->get_dc (c); + assert (DC->state == st_authorized); + + tglt_secure_random (DC->nonce, 16); + clear_packet (); + out_int (CODE_req_pq); + out_ints ((int *)DC->nonce, 4); + rpc_send_packet (TLS, c); + + DC->state = st_reqpq_sent_temp; + return 1; +} +/* }}} */ + +/* {{{ REQ DH */ +// req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:string q:string public_key_fingerprint:long encrypted_data:string = Server_DH_Params; +// p_q_inner_data#83c95aec pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 = P_Q_inner_data; +// p_q_inner_data_temp#3c6a84d4 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 expires_in:int = P_Q_inner_data; +static void send_req_dh_packet (struct tgl_state *TLS, struct connection *c, BIGNUM *pq, int temp_key) { + struct tgl_dc *DC = TLS->net_methods->get_dc (c); + + BIGNUM *p = BN_new (); + BIGNUM *q = BN_new (); + assert (bn_factorize (pq, p, q) >= 0); + + clear_packet (); + packet_ptr += 5; + out_int (temp_key ? CODE_p_q_inner_data_temp : CODE_p_q_inner_data); + + out_bignum (pq); + out_bignum (p); + out_bignum (q); + + out_ints ((int *) DC->nonce, 4); + out_ints ((int *) DC->server_nonce, 4); + tglt_secure_random (DC->new_nonce, 32); + out_ints ((int *) DC->new_nonce, 8); + if (temp_key) { + out_int (TLS->temp_key_expire_time); + } + sha1 ((unsigned char *) (packet_buffer + 5), (packet_ptr - packet_buffer - 5) * 4, (unsigned char *) packet_buffer); + + int l = encrypt_packet_buffer (TLS, DC); + + clear_packet (); + out_int (CODE_req_DH_params); + out_ints ((int *) DC->nonce, 4); + out_ints ((int *) DC->server_nonce, 4); + out_bignum (p); + out_bignum (q); + + out_long (TLS->rsa_key_fingerprint[DC->rsa_key_idx]); + out_cstring ((char *) encrypt_buffer, l); + + BN_free (p); + BN_free (q); + DC->state = temp_key ? st_reqdh_sent_temp : st_reqdh_sent; + rpc_send_packet (TLS, c); +} +/* }}} */ + +/* {{{ SEND DH PARAMS */ +// set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:string = Set_client_DH_params_answer; +// client_DH_inner_data#6643b654 nonce:int128 server_nonce:int128 retry_id:long g_b:string = Client_DH_Inner_Data +static void send_dh_params (struct tgl_state *TLS, struct connection *c, BIGNUM *dh_prime, BIGNUM *g_a, int g, int temp_key) { + struct tgl_dc *DC = TLS->net_methods->get_dc (c); + + clear_packet (); + packet_ptr += 5; + out_int (CODE_client_DH_inner_data); + out_ints ((int *) DC->nonce, 4); + out_ints ((int *) DC->server_nonce, 4); + out_long (0); + + BIGNUM *dh_g = BN_new (); + ensure (BN_set_word (dh_g, g)); + + static unsigned char s_power[256]; + tglt_secure_random (s_power, 256); + BIGNUM *dh_power = BN_bin2bn ((unsigned char *)s_power, 256, 0); + ensure_ptr (dh_power); + + BIGNUM *y = BN_new (); + ensure_ptr (y); + ensure (BN_mod_exp (y, dh_g, dh_power, dh_prime, TLS->BN_ctx)); + out_bignum (y); + BN_free (y); + + BIGNUM *auth_key_num = BN_new (); + ensure (BN_mod_exp (auth_key_num, g_a, dh_power, dh_prime, TLS->BN_ctx)); + int l = BN_num_bytes (auth_key_num); + assert (l >= 250 && l <= 256); + assert (BN_bn2bin (auth_key_num, (unsigned char *)(temp_key ? DC->temp_auth_key : DC->auth_key))); + if (l < 256) { + char *key = temp_key ? DC->temp_auth_key : DC->auth_key; + memmove (key + 256 - l, key, l); + memset (key, 0, 256 - l); + } + + BN_free (dh_power); + BN_free (auth_key_num); + BN_free (dh_g); + + sha1 ((unsigned char *) (packet_buffer + 5), (packet_ptr - packet_buffer - 5) * 4, (unsigned char *) packet_buffer); + + l = encrypt_packet_buffer_aes_unauth (DC->server_nonce, DC->new_nonce); + + clear_packet (); + out_int (CODE_set_client_DH_params); + out_ints ((int *) DC->nonce, 4); + out_ints ((int *) DC->server_nonce, 4); + out_cstring ((char *) encrypt_buffer, l); + + DC->state = temp_key ? st_client_dh_sent_temp : st_client_dh_sent;; + rpc_send_packet (TLS, c); +} +/* }}} */ + +/* {{{ RECV RESPQ */ +// resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector long = ResPQ +static int process_respq_answer (struct tgl_state *TLS, struct connection *c, char *packet, int len, int temp_key) { + assert (!(len & 3)); + in_ptr = (int *)packet; + in_end = in_ptr + (len / 4); + if (check_unauthorized_header (TLS) < 0) { + return -1; + } + + int *in_save = in_ptr; + if (skip_type_any (TYPE_TO_PARAM (res_p_q)) < 0 || in_ptr != in_end) { + vlogprintf (E_ERROR, "can not parse req_p_q answer\n"); + return -1; + } + in_ptr = in_save; + + struct tgl_dc *DC = TLS->net_methods->get_dc (c); + + assert (fetch_int() == CODE_res_p_q); + + static int tmp[4]; + fetch_ints (tmp, 4); + if (memcmp (tmp, DC->nonce, 16)) { + vlogprintf (E_ERROR, "nonce mismatch\n"); + return -1; + } + fetch_ints (DC->server_nonce, 4); + + BIGNUM *pq = BN_new (); + assert (fetch_bignum (pq) >= 0); + + assert (fetch_int () == CODE_vector); + int fingerprints_num = fetch_int (); + assert (fingerprints_num >= 0); + DC->rsa_key_idx = -1; + + int i; + for (i = 0; i < fingerprints_num; i++) { + int j; + long long fprint = fetch_long (); + for (j = 0; j < TLS->rsa_key_num; j++) { + if (TLS->rsa_key_loaded[j]) { + if (fprint == TLS->rsa_key_fingerprint[j]) { + DC->rsa_key_idx = j; + break; + } + } + } + } + assert (in_ptr == in_end); + if (DC->rsa_key_idx == -1) { + vlogprintf (E_ERROR, "fatal: don't have any matching keys\n"); + return -1; + } + + send_req_dh_packet (TLS, c, pq, temp_key); + + BN_free (pq); + return 1; +} +/* }}} */ + +/* {{{ RECV DH */ +// server_DH_params_fail#79cb045d nonce:int128 server_nonce:int128 new_nonce_hash:int128 = Server_DH_Params; +// server_DH_params_ok#d0e8075c nonce:int128 server_nonce:int128 encrypted_answer:string = Server_DH_Params; +// server_DH_inner_data#b5890dba nonce:int128 server_nonce:int128 g:int dh_prime:string g_a:string server_time:int = Server_DH_inner_data; +static int process_dh_answer (struct tgl_state *TLS, struct connection *c, char *packet, int len, int temp_key) { + assert (!(len & 3)); + in_ptr = (int *)packet; + in_end = in_ptr + (len / 4); + if (check_unauthorized_header (TLS) < 0) { + return -1; + } + + int *in_save = in_ptr; + if (skip_type_any (TYPE_TO_PARAM (server_d_h_params)) < 0 || in_ptr != in_end) { + vlogprintf (E_ERROR, "can not parse server_DH_params answer\n"); + return -1; + } + in_ptr = in_save; + + struct tgl_dc *DC = TLS->net_methods->get_dc (c); + + unsigned op = fetch_int (); + assert (op == CODE_server__d_h_params_ok || op == CODE_server__d_h_params_fail); + + int tmp[4]; + fetch_ints (tmp, 4); + if (memcmp (tmp, DC->nonce, 16)) { + vlogprintf (E_ERROR, "nonce mismatch\n"); + return -1; + } + assert (!memcmp (tmp, DC->nonce, 16)); + fetch_ints (tmp, 4); + if (memcmp (tmp, DC->server_nonce, 16)) { + vlogprintf (E_ERROR, "nonce mismatch\n"); + return -1; + } + assert (!memcmp (tmp, DC->server_nonce, 16)); + + if (op == CODE_server__d_h_params_fail) { + vlogprintf (E_ERROR, "DH params fail\n"); + return -1; + } + + tgl_init_aes_unauth (DC->server_nonce, DC->new_nonce, AES_DECRYPT); + + int l = prefetch_strlen (); + assert (l >= 0); + if (!l) { + vlogprintf (E_ERROR, "non-empty encrypted part expected\n"); + return -1; + } + l = tgl_pad_aes_decrypt (fetch_str (l), l, (char *) decrypt_buffer, DECRYPT_BUFFER_INTS * 4 - 16); + assert (in_ptr == in_end); + + in_ptr = decrypt_buffer + 5; + in_end = decrypt_buffer + (l >> 2); + if (skip_type_any (TYPE_TO_PARAM (server_d_h_inner_data)) < 0) { + vlogprintf (E_ERROR, "can not parse server_DH_inner_data answer\n"); + return -1; + } + in_ptr = decrypt_buffer + 5; + + assert (fetch_int () == (int)CODE_server_DH_inner_data); + fetch_ints (tmp, 4); + if (memcmp (tmp, DC->nonce, 16)) { + vlogprintf (E_ERROR, "nonce mismatch\n"); + return -1; + } + assert (!memcmp (tmp, DC->nonce, 16)); + fetch_ints (tmp, 4); + if (memcmp (tmp, DC->server_nonce, 16)) { + vlogprintf (E_ERROR, "nonce mismatch\n"); + return -1; + } + assert (!memcmp (tmp, DC->server_nonce, 16)); + int g = fetch_int (); + + BIGNUM *dh_prime = BN_new (); + BIGNUM *g_a = BN_new (); + assert (fetch_bignum (dh_prime) > 0); + assert (fetch_bignum (g_a) > 0); + + if (tglmp_check_DH_params (TLS, dh_prime, g) < 0) { + vlogprintf (E_ERROR, "bad DH params\n"); + return -1; + } + if (tglmp_check_g_a (TLS, dh_prime, g_a) < 0) { + vlogprintf (E_ERROR, "bad dh_prime\n"); + return -1; + } + + int server_time = fetch_int (); + assert (in_ptr <= in_end); + + static char sha1_buffer[20]; + sha1 ((unsigned char *) decrypt_buffer + 20, (in_ptr - decrypt_buffer - 5) * 4, (unsigned char *) sha1_buffer); + if (memcmp (decrypt_buffer, sha1_buffer, 20)) { + vlogprintf (E_ERROR, "bad encrypted message SHA1\n"); + return -1; + } + if ((char *) in_end - (char *) in_ptr >= 16) { + vlogprintf (E_ERROR, "too much padding\n"); + return -1; + } + + DC->server_time_delta = server_time - get_utime (CLOCK_REALTIME); + DC->server_time_udelta = server_time - get_utime (CLOCK_MONOTONIC); + + send_dh_params (TLS, c, dh_prime, g_a, g, temp_key); + + BN_free (dh_prime); + BN_free (g_a); + + return 1; +} +/* }}} */ + +static void create_temp_auth_key (struct tgl_state *TLS, struct connection *c) { + assert (TLS->enable_pfs); + send_req_pq_temp_packet (TLS, c); +} + +int tglmp_encrypt_inner_temp (struct tgl_state *TLS, struct connection *c, int *msg, int msg_ints, int useful, void *data, long long msg_id); +static long long msg_id_override; +static void mpc_on_get_config (struct tgl_state *TLS, void *extra, int success); +static void bind_temp_auth_key (struct tgl_state *TLS, struct connection *c); + +/* {{{ RECV AUTH COMPLETE */ + +// dh_gen_ok#3bcbf734 nonce:int128 server_nonce:int128 new_nonce_hash1:int128 = Set_client_DH_params_answer; +// dh_gen_retry#46dc1fb9 nonce:int128 server_nonce:int128 new_nonce_hash2:int128 = Set_client_DH_params_answer; +// dh_gen_fail#a69dae02 nonce:int128 server_nonce:int128 new_nonce_hash3:int128 = Set_client_DH_params_answer; +static int process_auth_complete (struct tgl_state *TLS, struct connection *c, char *packet, int len, int temp_key) { + struct tgl_dc *DC = TLS->net_methods->get_dc (c); + + assert (!(len & 3)); + in_ptr = (int *)packet; + in_end = in_ptr + (len / 4); + if (check_unauthorized_header (TLS) < 0) { + return -1; + } + + int *in_save = in_ptr; + if (skip_type_any (TYPE_TO_PARAM (set_client_d_h_params_answer)) < 0 || in_ptr != in_end) { + vlogprintf (E_ERROR, "can not parse server_DH_params answer\n"); + return -1; + } + in_ptr = in_save; + + unsigned op = fetch_int (); + assert (op == CODE_dh_gen_ok || op == CODE_dh_gen_retry || op == CODE_dh_gen_fail); + + int tmp[4]; + fetch_ints (tmp, 4); + if (memcmp (DC->nonce, tmp, 16)) { + vlogprintf (E_ERROR, "nonce mismatch\n"); + return -1; + } + fetch_ints (tmp, 4); + if (memcmp (DC->server_nonce, tmp, 16)) { + vlogprintf (E_ERROR, "nonce mismatch\n"); + return -1; + } + if (op != CODE_dh_gen_ok) { + vlogprintf (E_ERROR, "something bad. Retry regen\n"); + return -1; + } + + fetch_ints (tmp, 4); + + static unsigned char th[44], sha1_buffer[20]; + memcpy (th, DC->new_nonce, 32); + th[32] = 1; + if (!temp_key) { + sha1 ((unsigned char *)DC->auth_key, 256, sha1_buffer); + } else { + sha1 ((unsigned char *)DC->temp_auth_key, 256, sha1_buffer); + } + memcpy (th + 33, sha1_buffer, 8); + sha1 (th, 41, sha1_buffer); + if (memcmp (tmp, sha1_buffer + 4, 16)) { + vlogprintf (E_ERROR, "hash mismatch\n"); + return -1; + } + + if (!temp_key) { + bl_do_set_auth_key (TLS, DC->id, (unsigned char *)DC->auth_key); + sha1 ((unsigned char *)DC->auth_key, 256, sha1_buffer); + } else { + sha1 ((unsigned char *)DC->temp_auth_key, 256, sha1_buffer); + DC->temp_auth_key_id = *(long long *)(sha1_buffer + 12); + } + + DC->server_salt = *(long long *)DC->server_nonce ^ *(long long *)DC->new_nonce; + + DC->state = st_authorized; + + vlogprintf (E_DEBUG, "Auth success\n"); + if (temp_key) { + bind_temp_auth_key (TLS, c); + } else { + DC->flags |= 1; + if (TLS->enable_pfs) { + create_temp_auth_key (TLS, c); + } else { + DC->temp_auth_key_id = DC->auth_key_id; + memcpy (DC->temp_auth_key, DC->auth_key, 256); + DC->flags |= 2; + if (!(DC->flags & 4)) { + tgl_do_help_get_config_dc (TLS, DC, mpc_on_get_config, DC); + } + } + } + + return 1; +} +/* }}} */ + +static void bind_temp_auth_key (struct tgl_state *TLS, struct connection *c) { + struct tgl_dc *DC = TLS->net_methods->get_dc (c); + if (DC->temp_auth_key_bind_query_id) { + tglq_query_delete (TLS, DC->temp_auth_key_bind_query_id); + } + struct tgl_session *S = TLS->net_methods->get_session (c); + long long msg_id = generate_next_msg_id (TLS, DC, S); + + clear_packet (); + out_int (CODE_bind_auth_key_inner); + long long rand; + tglt_secure_random (&rand, 8); + out_long (rand); + out_long (DC->temp_auth_key_id); + out_long (DC->auth_key_id); + + if (!S->session_id) { + tglt_secure_random (&S->session_id, 8); + } + out_long (S->session_id); + int expires = (int)time (0) + DC->server_time_delta + TLS->temp_key_expire_time; + out_int (expires); + + static int data[1000]; + int len = tglmp_encrypt_inner_temp (TLS, c, packet_buffer, packet_ptr - packet_buffer, 0, data, msg_id); + msg_id_override = msg_id; + DC->temp_auth_key_bind_query_id = msg_id; + tgl_do_send_bind_temp_key (TLS, DC, rand, expires, (void *)data, len, msg_id); + msg_id_override = 0; +} + +/* + * + * AUTHORIZED (MAIN) PROTOCOL PART + * + */ + +static struct encrypted_message enc_msg; + +static double get_server_time (struct tgl_dc *DC) { + //if (!DC->server_time_udelta) { + // DC->server_time_udelta = get_utime (CLOCK_REALTIME) - get_utime (CLOCK_MONOTONIC); + //} + return get_utime (CLOCK_MONOTONIC) + DC->server_time_udelta; +} + +static long long generate_next_msg_id (struct tgl_state *TLS, struct tgl_dc *DC, struct tgl_session *S) { + long long next_id = (long long) (get_server_time (DC) * (1LL << 32)) & -4; + if (next_id <= S->last_msg_id) { + next_id = S->last_msg_id += 4; + } else { + S->last_msg_id = next_id; + } + return next_id; +} + +static void init_enc_msg (struct tgl_state *TLS, struct tgl_session *S, int useful) { + struct tgl_dc *DC = S->dc; + assert (DC->state == st_authorized); + assert (DC->temp_auth_key_id); + vlogprintf (E_DEBUG, "temp_auth_key_id = 0x%016llx, auth_key_id = 0x%016llx\n", DC->temp_auth_key_id, DC->auth_key_id); + enc_msg.auth_key_id = DC->temp_auth_key_id; + enc_msg.server_salt = DC->server_salt; + if (!S->session_id) { + tglt_secure_random (&S->session_id, 8); + } + enc_msg.session_id = S->session_id; + enc_msg.msg_id = msg_id_override ? msg_id_override : generate_next_msg_id (TLS, DC, S); + enc_msg.seq_no = S->seq_no; + if (useful) { + enc_msg.seq_no |= 1; + } + S->seq_no += 2; +}; + +static void init_enc_msg_inner_temp (struct tgl_dc *DC, long long msg_id) { + enc_msg.auth_key_id = DC->auth_key_id; + tglt_secure_random (&enc_msg.server_salt, 8); + tglt_secure_random (&enc_msg.session_id, 8); + enc_msg.msg_id = msg_id; + enc_msg.seq_no = 0; +}; + + +static int aes_encrypt_message (struct tgl_state *TLS, char *key, struct encrypted_message *enc) { + unsigned char sha1_buffer[20]; + const int MINSZ = offsetof (struct encrypted_message, message); + const int UNENCSZ = offsetof (struct encrypted_message, server_salt); + + int enc_len = (MINSZ - UNENCSZ) + enc->msg_len; + assert (enc->msg_len >= 0 && enc->msg_len <= MAX_MESSAGE_INTS * 4 - 16 && !(enc->msg_len & 3)); + sha1 ((unsigned char *) &enc->server_salt, enc_len, sha1_buffer); + vlogprintf (E_DEBUG, "sending message with sha1 %08x\n", *(int *)sha1_buffer); + memcpy (enc->msg_key, sha1_buffer + 4, 16); + tgl_init_aes_auth (key, enc->msg_key, AES_ENCRYPT); + return tgl_pad_aes_encrypt ((char *) &enc->server_salt, enc_len, (char *) &enc->server_salt, MAX_MESSAGE_INTS * 4 + (MINSZ - UNENCSZ)); +} + +long long tglmp_encrypt_send_message (struct tgl_state *TLS, struct connection *c, int *msg, int msg_ints, int flags) { + struct tgl_dc *DC = TLS->net_methods->get_dc (c); + struct tgl_session *S = TLS->net_methods->get_session (c); + assert (S); + if (!(DC->flags & 4) && !(flags & 2)) { + return generate_next_msg_id (TLS, DC, S); + } + + const int UNENCSZ = offsetof (struct encrypted_message, server_salt); + if (msg_ints <= 0 || msg_ints > MAX_MESSAGE_INTS - 4) { + return -1; + } + if (msg) { + memcpy (enc_msg.message, msg, msg_ints * 4); + enc_msg.msg_len = msg_ints * 4; + } else { + if ((enc_msg.msg_len & 0x80000003) || enc_msg.msg_len > MAX_MESSAGE_INTS * 4 - 16) { + return -1; + } + } + init_enc_msg (TLS, S, flags & 1); + + int l = aes_encrypt_message (TLS, DC->temp_auth_key, &enc_msg); + assert (l > 0); + rpc_send_message (TLS, c, &enc_msg, l + UNENCSZ); + + return S->last_msg_id; +} + +int tglmp_encrypt_inner_temp (struct tgl_state *TLS, struct connection *c, int *msg, int msg_ints, int useful, void *data, long long msg_id) { + struct tgl_dc *DC = TLS->net_methods->get_dc (c); + struct tgl_session *S = TLS->net_methods->get_session (c); + assert (S); + + const int UNENCSZ = offsetof (struct encrypted_message, server_salt); + if (msg_ints <= 0 || msg_ints > MAX_MESSAGE_INTS - 4) { + return -1; + } + memcpy (enc_msg.message, msg, msg_ints * 4); + enc_msg.msg_len = msg_ints * 4; + + init_enc_msg_inner_temp (DC, msg_id); + + int l = aes_encrypt_message (TLS, DC->auth_key, &enc_msg); + assert (l > 0); + //rpc_send_message (c, &enc_msg, l + UNENCSZ); + memcpy (data, &enc_msg, l + UNENCSZ); + + return l + UNENCSZ; +} + +static int rpc_execute_answer (struct tgl_state *TLS, struct connection *c, long long msg_id); + +static int work_container (struct tgl_state *TLS, struct connection *c, long long msg_id) { + vlogprintf (E_DEBUG, "work_container: msg_id = %"_PRINTF_INT64_"d\n", msg_id); + assert (fetch_int () == CODE_msg_container); + int n = fetch_int (); + int i; + for (i = 0; i < n; i++) { + long long id = fetch_long (); + //int seqno = fetch_int (); + fetch_int (); // seq_no + if (id & 1) { + tgln_insert_msg_id (TLS, TLS->net_methods->get_session (c), id); + } + int bytes = fetch_int (); + int *t = in_end; + in_end = in_ptr + (bytes / 4); + int r = rpc_execute_answer (TLS, c, id); + if (r < 0) { return -1; } + assert (in_ptr == in_end); + in_end = t; + } + return 0; +} + +static int work_new_session_created (struct tgl_state *TLS, struct connection *c, long long msg_id) { + vlogprintf (E_DEBUG, "work_new_session_created: msg_id = %"_PRINTF_INT64_"d\n", msg_id); + assert (fetch_int () == (int)CODE_new_session_created); + fetch_long (); // first message id + fetch_long (); // unique_id + TLS->net_methods->get_dc (c)->server_salt = fetch_long (); + if (TLS->started && !(TLS->locks & TGL_LOCK_DIFF) && (TLS->DC_working->flags & TGLDCF_LOGGED_IN)) { + tgl_do_get_difference (TLS, 0, 0, 0); + } + return 0; +} + +static int work_msgs_ack (struct tgl_state *TLS, struct connection *c, long long msg_id) { + vlogprintf (E_DEBUG, "work_msgs_ack: msg_id = %"_PRINTF_INT64_"d\n", msg_id); + assert (fetch_int () == CODE_msgs_ack); + assert (fetch_int () == CODE_vector); + int n = fetch_int (); + int i; + for (i = 0; i < n; i++) { + long long id = fetch_long (); + vlogprintf (E_DEBUG + 1, "ack for %"_PRINTF_INT64_"d\n", id); + tglq_query_ack (TLS, id); + } + return 0; +} + +static int work_rpc_result (struct tgl_state *TLS, struct connection *c, long long msg_id) { + vlogprintf (E_DEBUG, "work_rpc_result: msg_id = %"_PRINTF_INT64_"d\n", msg_id); + assert (fetch_int () == (int)CODE_rpc_result); + long long id = fetch_long (); + int op = prefetch_int (); + if (op == CODE_rpc_error) { + return tglq_query_error (TLS, id); + } else { + return tglq_query_result (TLS, id); + } +} + +#define MAX_PACKED_SIZE (1 << 24) +static int work_packed (struct tgl_state *TLS, struct connection *c, long long msg_id) { + assert (fetch_int () == CODE_gzip_packed); + static int in_gzip; + static int buf[MAX_PACKED_SIZE >> 2]; + assert (!in_gzip); + in_gzip = 1; + + int l = prefetch_strlen (); + char *s = fetch_str (l); + + int total_out = tgl_inflate (s, l, buf, MAX_PACKED_SIZE); + int *end = in_ptr; + int *eend = in_end; + //assert (total_out % 4 == 0); + in_ptr = buf; + in_end = in_ptr + total_out / 4; + int r = rpc_execute_answer (TLS, c, msg_id); + in_ptr = end; + in_end = eend; + in_gzip = 0; + return r; +} + +static int work_bad_server_salt (struct tgl_state *TLS, struct connection *c, long long msg_id) { + assert (fetch_int () == (int)CODE_bad_server_salt); + long long id = fetch_long (); + tglq_query_restart (TLS, id); + fetch_int (); // seq_no + fetch_int (); // error_code + long long new_server_salt = fetch_long (); + TLS->net_methods->get_dc (c)->server_salt = new_server_salt; + return 0; +} + +static int work_pong (struct tgl_state *TLS, struct connection *c, long long msg_id) { + assert (fetch_int () == CODE_pong); + fetch_long (); // msg_id + fetch_long (); // ping_id + return 0; +} + +static int work_detailed_info (struct tgl_state *TLS, struct connection *c, long long msg_id) { + assert (fetch_int () == CODE_msg_detailed_info); + fetch_long (); // msg_id + fetch_long (); // answer_msg_id + fetch_int (); // bytes + fetch_int (); // status + return 0; +} + +static int work_new_detailed_info (struct tgl_state *TLS, struct connection *c, long long msg_id) { + assert (fetch_int () == (int)CODE_msg_new_detailed_info); + fetch_long (); // answer_msg_id + fetch_int (); // bytes + fetch_int (); // status + return 0; +} + +static int work_bad_msg_notification (struct tgl_state *TLS, struct connection *c, long long msg_id) { + assert (fetch_int () == (int)CODE_bad_msg_notification); + long long m1 = fetch_long (); + int s = fetch_int (); + int e = fetch_int (); + vlogprintf (E_NOTICE, "bad_msg_notification: msg_id = %"_PRINTF_INT64_"d, seq = %d, error = %d\n", m1, s, e); + switch (e) { + // Too low msg id + case 16: + tglq_regen_query (TLS, m1); + break; + // Too high msg id + case 17: + tglq_regen_query (TLS, m1); + break; + default: + vlogprintf (E_NOTICE, "bad_msg_notification: msg_id = %"_PRINTF_INT64_"d, seq = %d, error = %d\n", m1, s, e); + break; + } + + return -1; +} + +static int rpc_execute_answer (struct tgl_state *TLS, struct connection *c, long long msg_id) { + int op = prefetch_int (); + switch (op) { + case CODE_msg_container: + return work_container (TLS, c, msg_id); + case CODE_new_session_created: + return work_new_session_created (TLS, c, msg_id); + case CODE_msgs_ack: + return work_msgs_ack (TLS, c, msg_id); + case CODE_rpc_result: + return work_rpc_result (TLS, c, msg_id); + case CODE_update_short: + case CODE_updates: + case CODE_update_short_message: + case CODE_update_short_chat_message: + case CODE_updates_too_long: + tglu_work_any_updates (TLS); + return 0; + case CODE_gzip_packed: + return work_packed (TLS, c, msg_id); + case CODE_bad_server_salt: + return work_bad_server_salt (TLS, c, msg_id); + case CODE_pong: + return work_pong (TLS, c, msg_id); + case CODE_msg_detailed_info: + return work_detailed_info (TLS, c, msg_id); + case CODE_msg_new_detailed_info: + return work_new_detailed_info (TLS, c, msg_id); + case CODE_bad_msg_notification: + return work_bad_msg_notification (TLS, c, msg_id); + } + vlogprintf (E_WARNING, "Unknown message: %08x\n", op); + in_ptr = in_end; // Will not fail due to assertion in_ptr == in_end + return 0; +} + +static struct mtproto_methods mtproto_methods; +void tgls_free_session (struct tgl_state *TLS, struct tgl_session *S); +/* +static char *get_ipv6 (struct tgl_state *TLS, int num) { + static char res[1<< 10]; + if (TLS->test_mode) { + switch (num) { + case 1: + strcpy (res, TG_SERVER_TEST_IPV6_1); + break; + case 2: + strcpy (res, TG_SERVER_TEST_IPV6_2); + break; + case 3: + strcpy (res, TG_SERVER_TEST_IPV6_3); + break; + default: + assert (0); + } + } else { + switch (num) { + case 1: + strcpy (res, TG_SERVER_IPV6_1); + break; + case 2: + strcpy (res, TG_SERVER_IPV6_2); + break; + case 3: + strcpy (res, TG_SERVER_IPV6_3); + break; + case 4: + strcpy (res, TG_SERVER_IPV6_4); + break; + case 5: + strcpy (res, TG_SERVER_IPV6_5); + break; + default: + assert (0); + } + } + return res; +} +*/ + +static void create_session_connect (struct tgl_state *TLS, struct tgl_session *S) { + struct tgl_dc *DC = S->dc; + + if (TLS->ipv6_enabled) { + S->c = TLS->net_methods->create_connection (TLS, DC->options[1]->ip, DC->options[1]->port, S, DC, &mtproto_methods); + } else { + S->c = TLS->net_methods->create_connection (TLS, DC->options[0]->ip, DC->options[0]->port, S, DC, &mtproto_methods); + } +} + +static void fail_connection (struct tgl_state *TLS, struct connection *c) { + struct tgl_session *S = TLS->net_methods->get_session (c); + TLS->net_methods->free (c); + create_session_connect (TLS, S); +} + +static void fail_session (struct tgl_state *TLS, struct tgl_session *S) { + vlogprintf (E_NOTICE, "failing session %"_PRINTF_INT64_"d\n", S->session_id); + struct tgl_dc *DC = S->dc; + tgls_free_session (TLS, S); + DC->sessions[0] = NULL; + tglmp_dc_create_session (TLS, DC); +} + +static int process_rpc_message (struct tgl_state *TLS, struct connection *c, struct encrypted_message *enc, int len) { + const int MINSZ = offsetof (struct encrypted_message, message); + const int UNENCSZ = offsetof (struct encrypted_message, server_salt); + vlogprintf (E_DEBUG, "process_rpc_message(), len=%d\n", len); + if (len < MINSZ || (len & 15) != (UNENCSZ & 15)) { + vlogprintf (E_WARNING, "Incorrect packet from server. Closing connection\n"); + fail_connection (TLS, c); + return -1; + } + assert (len >= MINSZ && (len & 15) == (UNENCSZ & 15)); + struct tgl_dc *DC = TLS->net_methods->get_dc (c); + if (enc->auth_key_id != DC->temp_auth_key_id && enc->auth_key_id != DC->auth_key_id) { + vlogprintf (E_WARNING, "received msg from dc %d with auth_key_id %"_PRINTF_INT64_"d (perm_auth_key_id %"_PRINTF_INT64_"d temp_auth_key_id %"_PRINTF_INT64_"d). Dropping\n", + DC->id, enc->auth_key_id, DC->auth_key_id, DC->temp_auth_key_id); + return 0; + } + if (enc->auth_key_id == DC->temp_auth_key_id) { + assert (enc->auth_key_id == DC->temp_auth_key_id); + assert (DC->temp_auth_key_id); + tgl_init_aes_auth (DC->temp_auth_key + 8, enc->msg_key, AES_DECRYPT); + } else { + assert (enc->auth_key_id == DC->auth_key_id); + assert (DC->auth_key_id); + tgl_init_aes_auth (DC->auth_key + 8, enc->msg_key, AES_DECRYPT); + } + + int l = tgl_pad_aes_decrypt ((char *)&enc->server_salt, len - UNENCSZ, (char *)&enc->server_salt, len - UNENCSZ); + assert (l == len - UNENCSZ); + + if (!(!(enc->msg_len & 3) && enc->msg_len > 0 && enc->msg_len <= len - MINSZ && len - MINSZ - enc->msg_len <= 12)) { + vlogprintf (E_WARNING, "Incorrect packet from server. Closing connection\n"); + fail_connection (TLS, c); + return -1; + } + assert (!(enc->msg_len & 3) && enc->msg_len > 0 && enc->msg_len <= len - MINSZ && len - MINSZ - enc->msg_len <= 12); + + struct tgl_session *S = TLS->net_methods->get_session (c); + if (!S || S->session_id != enc->session_id) { + vlogprintf (E_WARNING, "Message to bad session. Drop.\n"); + return 0; + } + + static unsigned char sha1_buffer[20]; + sha1 ((void *)&enc->server_salt, enc->msg_len + (MINSZ - UNENCSZ), sha1_buffer); + if (memcmp (&enc->msg_key, sha1_buffer + 4, 16)) { + vlogprintf (E_WARNING, "Incorrect packet from server. Closing connection\n"); + fail_connection (TLS, c); + return -1; + } + assert (!memcmp (&enc->msg_key, sha1_buffer + 4, 16)); + + int this_server_time = enc->msg_id >> 32LL; + if (!S->received_messages) { + DC->server_time_delta = this_server_time - get_utime (CLOCK_REALTIME); + if (DC->server_time_udelta) { + vlogprintf (E_WARNING, "adjusting CLOCK_MONOTONIC delta to %lf\n", + DC->server_time_udelta - this_server_time + get_utime (CLOCK_MONOTONIC)); + } + DC->server_time_udelta = this_server_time - get_utime (CLOCK_MONOTONIC); + } + + double st = get_server_time (DC); + if (this_server_time < st - 300 || this_server_time > st + 30) { + vlogprintf (E_WARNING, "bad msg time: salt = %"_PRINTF_INT64_"d, session_id = %"_PRINTF_INT64_"d, msg_id = %"_PRINTF_INT64_"d, seq_no = %d, st = %lf, now = %lf\n", enc->server_salt, enc->session_id, enc->msg_id, enc->seq_no, st, get_utime (CLOCK_REALTIME)); + fail_session (TLS, S); + return -1; + } + S->received_messages ++; + + if (DC->server_salt != enc->server_salt) { + DC->server_salt = enc->server_salt; + } + + assert (this_server_time >= st - 300 && this_server_time <= st + 30); + //assert (enc->msg_id > server_last_msg_id && (enc->msg_id & 3) == 1); + vlogprintf (E_DEBUG, "received mesage id %016llx\n", enc->msg_id); + //server_last_msg_id = enc->msg_id; + + //*(long long *)(longpoll_query + 3) = *(long long *)((char *)(&enc->msg_id) + 0x3c); + //*(long long *)(longpoll_query + 5) = *(long long *)((char *)(&enc->msg_id) + 0x3c); + + assert (l >= (MINSZ - UNENCSZ) + 8); + //assert (enc->message[0] == CODE_rpc_result && *(long long *)(enc->message + 1) == client_last_msg_id); + + in_ptr = enc->message; + in_end = in_ptr + (enc->msg_len / 4); + + if (enc->msg_id & 1) { + tgln_insert_msg_id (TLS, S, enc->msg_id); + } + assert (S->session_id == enc->session_id); + + if (rpc_execute_answer (TLS, c, enc->msg_id) < 0) { + fail_session (TLS, S); + return -1; + } + assert (in_ptr == in_end); + return 0; +} + + +static int rpc_execute (struct tgl_state *TLS, struct connection *c, int op, int len) { + struct tgl_dc *DC = TLS->net_methods->get_dc (c); + + if (len >= MAX_RESPONSE_SIZE/* - 12*/ || len < 0/*12*/) { + vlogprintf (E_WARNING, "answer too long (%d bytes), skipping\n", len); + return 0; + } + + int Response_len = len; + + static char Response[MAX_RESPONSE_SIZE]; + vlogprintf (E_DEBUG, "Response_len = %d\n", Response_len); + assert (TLS->net_methods->read_in (c, Response, Response_len) == Response_len); + +#if !defined(WIN32) || !defined(_WIN32) + setsockopt(c->fd, IPPROTO_TCP, TCP_QUICKACK, (int[]) { 0 }, 4); +#endif +#if !defined(__MACH__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined (__CYGWIN__) +// setsockopt (c->fd, IPPROTO_TCP, TCP_QUICKACK, (int[]){0}, 4); +#endif + int o = DC->state; + //if (DC->flags & 1) { o = st_authorized;} + if (o != st_authorized) { + vlogprintf (E_DEBUG, "%s: state = %d\n", __func__, o); + } + switch (o) { + case st_reqpq_sent: + process_respq_answer (TLS, c, Response/* + 8*/, Response_len/* - 12*/, 0); + return 0; + case st_reqdh_sent: + process_dh_answer (TLS, c, Response/* + 8*/, Response_len/* - 12*/, 0); + return 0; + case st_client_dh_sent: + process_auth_complete (TLS, c, Response/* + 8*/, Response_len/* - 12*/, 0); + return 0; + case st_reqpq_sent_temp: + process_respq_answer (TLS, c, Response/* + 8*/, Response_len/* - 12*/, 1); + return 0; + case st_reqdh_sent_temp: + process_dh_answer (TLS, c, Response/* + 8*/, Response_len/* - 12*/, 1); + return 0; + case st_client_dh_sent_temp: + process_auth_complete (TLS, c, Response/* + 8*/, Response_len/* - 12*/, 1); + return 0; + case st_authorized: + if (op < 0 && op >= -999) { + vlogprintf (E_WARNING, "Server error %d\n", op); + } else { + return process_rpc_message (TLS, c, (void *)(Response/* + 8*/), Response_len/* - 12*/); + } + return 0; + default: + vlogprintf (E_ERROR, "fatal: cannot receive answer in state %d\n", DC->state); + exit (2); + } + + return 0; +} + + +static int tc_close (struct tgl_state *TLS, struct connection *c, int who) { + vlogprintf (E_DEBUG, "outbound rpc connection from dc #%d : closing by %d\n", TLS->net_methods->get_dc(c)->id, who); + return 0; +} + +static void mpc_on_get_config (struct tgl_state *TLS, void *extra, int success) { + assert (success); + struct tgl_dc *DC = extra; + DC->flags |= 4; +} + +static int tc_becomes_ready (struct tgl_state *TLS, struct connection *c) { + vlogprintf (E_NOTICE, "outbound rpc connection from dc #%d becomed ready\n", TLS->net_methods->get_dc(c)->id); + //char byte = 0xef; + //assert (TLS->net_methods->write_out (c, &byte, 1) == 1); + //TLS->net_methods->flush_out (c); + + struct tgl_dc *DC = TLS->net_methods->get_dc (c); + if (DC->flags & 1) { DC->state = st_authorized; } + int o = DC->state; + if (o == st_authorized && !TLS->enable_pfs) { + DC->temp_auth_key_id = DC->auth_key_id; + memcpy (DC->temp_auth_key, DC->auth_key, 256); + DC->flags |= 2; + } + switch (o) { + case st_init: + send_req_pq_packet (TLS, c); + break; + case st_authorized: + if (!(DC->flags & 2)) { + assert (TLS->enable_pfs); + if (!DC->temp_auth_key_id) { + assert (!DC->temp_auth_key_id); + create_temp_auth_key (TLS, c); + } else { + bind_temp_auth_key (TLS, c); + } + } else if (!(DC->flags & 4)) { + tgl_do_help_get_config_dc (TLS, DC, mpc_on_get_config, DC); + } + break; + default: + vlogprintf (E_DEBUG, "c_state = %d\n", DC->state); + DC->state = st_init; // previous connection was reset + send_req_pq_packet (TLS, c); + break; + } + return 0; +} + +static int rpc_becomes_ready (struct tgl_state *TLS, struct connection *c) { + return tc_becomes_ready (TLS, c); +} + +static int rpc_close (struct tgl_state *TLS, struct connection *c) { + return tc_close (TLS, c, 0); +} + + +#define RANDSEED_PASSWORD_FILENAME NULL +#define RANDSEED_PASSWORD_LENGTH 0 +void tglmp_on_start (struct tgl_state *TLS) { + tgl_prng_seed (TLS, RANDSEED_PASSWORD_FILENAME, RANDSEED_PASSWORD_LENGTH); + + int i; + int ok = 0; + for (i = 0; i < TLS->rsa_key_num; i++) { + char *key = TLS->rsa_key_list[i]; + RSA *res = rsa_load_public_key (TLS, key); + if (!res) { + vlogprintf (E_WARNING, "Can not load key %s\n", key); + TLS->rsa_key_loaded[i] = NULL; + } else { + ok = 1; + TLS->rsa_key_loaded[i] = res; + TLS->rsa_key_fingerprint[i] = tgl_do_compute_rsa_key_fingerprint (res); + } + } + + if (!ok) { + vlogprintf (E_ERROR, "No public keys found\n"); + exit (1); + } +} + +void tgl_dc_authorize (struct tgl_state *TLS, struct tgl_dc *DC) { + //c_state = 0; + if (!DC->sessions[0]) { + tglmp_dc_create_session (TLS, DC); + } + vlogprintf (E_DEBUG, "Starting authorization for DC #%d\n", DC->id); + //net_loop (0, auth_ok); +} + +#define long_cmp(a,b) ((a) > (b) ? 1 : (a) == (b) ? 0 : -1) +DEFINE_TREE(long,long long,long_cmp,0) + +static int send_all_acks (struct tgl_state *TLS, struct tgl_session *S) { + clear_packet (); + out_int (CODE_msgs_ack); + out_int (CODE_vector); + out_int (tree_count_long (S->ack_tree)); + while (S->ack_tree) { + long long x = tree_get_min_long (S->ack_tree); + out_long (x); + S->ack_tree = tree_delete_long (S->ack_tree, x); + } + tglmp_encrypt_send_message (TLS, S->c, packet_buffer, packet_ptr - packet_buffer, 0); + return 0; +} + +static void send_all_acks_gateway (struct tgl_state *TLS, void *arg) { + send_all_acks (TLS, arg); +} + + +void tgln_insert_msg_id (struct tgl_state *TLS, struct tgl_session *S, long long id) { + if (!S->ack_tree) { + TLS->timer_methods->insert (S->ev, ACK_TIMEOUT); + } + if (!tree_lookup_long (S->ack_tree, id)) { + S->ack_tree = tree_insert_long (S->ack_tree, id, lrand48 ()); + } +} + +//extern struct tgl_dc *DC_list[]; + + +static void regen_temp_key_gw (struct tgl_state *TLS, void *arg) { + tglmp_regenerate_temp_auth_key (TLS, arg); +} + +struct tgl_dc *tglmp_alloc_dc (struct tgl_state *TLS, int flags, int id, char *ip, int port) { + //assert (!TLS->DC_list[id]); + + if (!TLS->DC_list[id]) { + struct tgl_dc *DC = talloc0 (sizeof (*DC)); + DC->id = id; + TLS->DC_list[id] = DC; + if (id > TLS->max_dc_num) { + TLS->max_dc_num = id; + } + DC->ev = TLS->timer_methods->alloc (TLS, regen_temp_key_gw, DC); + TLS->timer_methods->insert (DC->ev, 0); + } + + struct tgl_dc *DC = TLS->DC_list[id]; + + struct tgl_dc_option *O = DC->options[flags & 3]; + + struct tgl_dc_option *O2 = O; + while (O2) { + if (!strcmp (O2->ip, ip)) { + tfree_str (ip); + return DC; + } + O2 = O2->next; + } + + struct tgl_dc_option *T = talloc (sizeof (*T)); + T->ip = ip; + T->port = port; + T->next = O; + DC->options[flags & 3] = T; + + + return DC; +} + +static struct mtproto_methods mtproto_methods = { + .execute = rpc_execute, + .ready = rpc_becomes_ready, + .close = rpc_close +}; + +void tglmp_dc_create_session (struct tgl_state *TLS, struct tgl_dc *DC) { + struct tgl_session *S = talloc0 (sizeof (*S)); + assert (RAND_pseudo_bytes ((unsigned char *) &S->session_id, 8) >= 0); + S->dc = DC; + //S->c = TLS->net_methods->create_connection (TLS, DC->ip, DC->port, S, DC, &mtproto_methods); + + create_session_connect (TLS, S); + S->ev = TLS->timer_methods->alloc (TLS, send_all_acks_gateway, S); + assert (!DC->sessions[0]); + DC->sessions[0] = S; +} + +void tgl_do_send_ping (struct tgl_state *TLS, struct connection *c) { + int x[3]; + x[0] = CODE_ping; + *(long long *)(x + 1) = lrand48 () * (1ll << 32) + lrand48 (); + tglmp_encrypt_send_message (TLS, c, x, 3, 0); +} + +void tgl_dc_iterator (struct tgl_state *TLS, void (*iterator)(struct tgl_dc *DC)) { + int i; + for (i = 0; i <= TLS->max_dc_num; i++) { + iterator (TLS->DC_list[i]); + } +} + +void tgl_dc_iterator_ex (struct tgl_state *TLS, void (*iterator)(struct tgl_dc *DC, void *extra), void *extra) { + int i; + for (i = 0; i <= TLS->max_dc_num; i++) { + iterator (TLS->DC_list[i], extra); + } +} + + +void tglmp_regenerate_temp_auth_key (struct tgl_state *TLS, struct tgl_dc *DC) { + DC->flags &= ~6; + DC->temp_auth_key_id = 0; + memset (DC->temp_auth_key, 0, 256); + + if (!DC->sessions[0]) { + tgl_dc_authorize (TLS, DC); + return; + } + + + struct tgl_session *S = DC->sessions[0]; + tglt_secure_random (&S->session_id, 8); + S->seq_no = 0; + + TLS->timer_methods->remove (S->ev); + S->ack_tree = tree_clear_long (S->ack_tree); + + if (DC->state != st_authorized) { + return; + } + + if (S->c) { + create_temp_auth_key (TLS, S->c); + } +} + +void tgls_free_session (struct tgl_state *TLS, struct tgl_session *S) { + S->ack_tree = tree_clear_long (S->ack_tree); + if (S->ev) { TLS->timer_methods->free (S->ev); } + if (S->c) { + TLS->net_methods->free (S->c); + } + tfree (S, sizeof (*S)); +} + +void tgls_free_dc (struct tgl_state *TLS, struct tgl_dc *DC) { + //if (DC->ip) { tfree_str (DC->ip); } + + struct tgl_session *S = DC->sessions[0]; + if (S) { tgls_free_session (TLS, S); } + + if (DC->ev) { TLS->timer_methods->free (DC->ev); } + tfree (DC, sizeof (*DC)); +} + +void tgls_free_pubkey (struct tgl_state *TLS) { + int i; + for (i = 0; i < TLS->rsa_key_num; i++) { + if (TLS->rsa_key_loaded[i]) { + RSA_free (TLS->rsa_key_loaded[i]); + TLS->rsa_key_loaded[i] = NULL; + } + } +} |