telegram-purple/mtproto-client.c

1735 lines
52 KiB
C

/*
This file is part of telegram-client.
Telegram-client is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Telegram-client 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this telegram-client. If not, see <http://www.gnu.org/licenses/>.
Copyright Nikolay Durov, Andrey Lopatin 2012-2013
Copyright Vitaly Valtman 2013
*/
#define _FILE_OFFSET_BITS 64
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#if defined(__FreeBSD__) || defined(__OpenBSD__)
#include <sys/endian.h>
#endif
#include <sys/types.h>
#include <netdb.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/sha.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <poll.h>
#include "telegram.h"
#include "net.h"
#include "include.h"
#include "queries.h"
#include "loop.h"
#include "structures.h"
#include "binlog.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-client.h"
#define MAX_NET_RES (1L << 16)
int log_level = 2;
int verbosity = 0;
int allow_weak_random = 0;
int disable_auto_accept = 0;
int rpc_execute (struct connection *c, int op, int len);
int rpc_becomes_ready (struct connection *c);
int rpc_close (struct connection *c);
struct connection_methods auth_methods = {
.execute = rpc_execute,
.ready = rpc_becomes_ready,
.close = rpc_close
};
long long precise_time;
double get_utime (int clock_id) {
struct timespec T;
my_clock_gettime (clock_id, &T);
double res = T.tv_sec + (double) T.tv_nsec * 1e-9;
if (clock_id == CLOCK_REALTIME) {
precise_time = (long long) (res * (1LL << 32));
}
return res;
}
void secure_random (void *s, int l) {
if (RAND_bytes (s, l) < 0) {
if (allow_weak_random) {
RAND_pseudo_bytes (s, l);
} else {
assert (0 && "End of random. If you want, you can start with -w");
}
}
}
#define STATS_BUFF_SIZE (64 << 10)
int stats_buff_len;
char stats_buff[STATS_BUFF_SIZE];
#define MAX_RESPONSE_SIZE (1L << 24)
char Response[MAX_RESPONSE_SIZE];
int Response_len;
/*
*
* STATE MACHINE
*
*/
#define TG_SERVER_PUBKEY_FILENAME "/etc/telegram-purple/server.pub"
char *rsa_public_key_name = 0;
RSA *pubKey;
long long pk_fingerprint;
static int rsa_load_public_key (const char *public_key_name) {
pubKey = NULL;
FILE *f = fopen (public_key_name, "r");
if (f == NULL) {
debug ( "Couldn't open public key file: %s\n", public_key_name);
return -1;
}
pubKey = PEM_read_RSAPublicKey (f, NULL, NULL, NULL);
fclose (f);
if (pubKey == NULL) {
debug ( "PEM_read_RSAPublicKey returns NULL.\n");
return -1;
}
debug ( "public key '%s' loaded successfully\n", rsa_public_key_name);
return 0;
}
int auth_work_start (struct connection *c);
/*
*
* UNAUTHORIZED (DH KEY EXCHANGE) PROTOCOL PART
*
*/
int encrypt_packet_buffer (struct mtproto_connection *self) {
return pad_rsa_encrypt (self, (char *) self->packet_buffer, (self->packet_ptr - self->packet_buffer) * 4, (char *) self->encrypt_buffer,
ENCRYPT_BUFFER_INTS * 4, pubKey->n, pubKey->e);
}
int encrypt_packet_buffer_aes_unauth (struct mtproto_connection *self, const char server_nonce[16], const char hidden_client_nonce[32]) {
init_aes_unauth (self, server_nonce, hidden_client_nonce, AES_ENCRYPT);
return pad_aes_encrypt (self, (char *) self->packet_buffer, (self->packet_ptr - self->packet_buffer) * 4,
(char *) self->encrypt_buffer, ENCRYPT_BUFFER_INTS * 4);
}
int rpc_send_packet (struct connection *c) {
debug("rpc_send_packet()\n");
struct mtproto_connection *self = c->mtconnection;
int len = (self->packet_ptr - self->packet_buffer) * 4;
c->out_packet_num ++;
long long next_msg_id = (long long) ((1LL << 32) * get_utime (CLOCK_REALTIME)) & -4;
if (next_msg_id <= self->unenc_msg_header.out_msg_id) {
self->unenc_msg_header.out_msg_id += 4;
} else {
self->unenc_msg_header.out_msg_id = next_msg_id;
}
self->unenc_msg_header.msg_len = len;
int total_len = len + 20;
assert (total_len > 0 && !(total_len & 0xfc000003));
total_len >>= 2;
if (total_len < 0x7f) {
assert (write_out (c, &total_len, 1) == 1);
} else {
total_len = (total_len << 8) | 0x7f;
assert (write_out (c, &total_len, 4) == 4);
}
write_out (c, &self->unenc_msg_header, 20);
write_out (c, self->packet_buffer, len);
flush_out (c);
self->total_packets_sent ++;
self->total_data_sent += total_len;
return 1;
}
int rpc_send_message (struct connection *c, void *data, int len) {
struct mtproto_connection *self = c->mtconnection;
//debug("rpc_send_message(...)\n");
assert (len > 0 && !(len & 0xfc000003));
int total_len = len >> 2;
if (total_len < 0x7f) {
assert (write_out (c, &total_len, 1) == 1);
} else {
total_len = (total_len << 8) | 0x7f;
assert (write_out (c, &total_len, 4) == 4);
}
c->out_packet_num ++;
assert (write_out (c, data, len) == len);
flush_out (c);
self->total_packets_sent ++;
self->total_data_sent += total_len;
return 1;
}
int send_req_pq_packet (struct connection *c) {
struct mtproto_connection *self = c->mtconnection;
assert (self->c_state == st_init);
secure_random (self->nonce, 16);
self->unenc_msg_header.out_msg_id = 0;
clear_packet (self);
out_int (self, CODE_req_pq);
out_ints (self, (int *)self->nonce, 4);
rpc_send_packet (c);
self->c_state = st_reqpq_sent;
return 1;
}
unsigned long long gcd (unsigned long long a, unsigned long long b) {
return b ? gcd (b, a % b) : a;
}
//typedef unsigned int uint128_t __attribute__ ((mode(TI)));
int process_respq_answer (struct connection *c, char *packet, int len) {
debug ( "process_respq_answer(), len=%d\n", len);
struct mtproto_connection *self = c->mtconnection;
int i;
assert (len >= 76);
assert (!*(long long *) packet);
assert (*(int *) (packet + 16) == len - 20);
assert (!(len & 3));
assert (*(int *) (packet + 20) == CODE_resPQ);
assert (!memcmp (packet + 24, self->nonce, 16));
memcpy (self->server_nonce, packet + 40, 16);
char *from = packet + 56;
int clen = *from++;
assert (clen <= 8);
self->what = 0;
for (i = 0; i < clen; i++) {
self->what = (self->what << 8) + (unsigned char)*from++;
}
while (((unsigned long)from) & 3) ++from;
self->p1 = 0, self->p2 = 0;
debug ( "%lld received\n", self->what);
int it = 0;
unsigned long long g = 0;
for (i = 0; i < 3 || it < 1000; i++) {
int q = ((lrand48() & 15) + 17) % self->what;
unsigned long long x = (long long)lrand48 () % (self->what - 1) + 1, y = x;
int lim = 1 << (i + 18);
int j;
for (j = 1; j < lim; j++) {
++it;
unsigned long long a = x, b = x, c = q;
while (b) {
if (b & 1) {
c += a;
if (c >= self->what) {
c -= self->what;
}
}
a += a;
if (a >= self->what) {
a -= self->what;
}
b >>= 1;
}
x = c;
unsigned long long z = x < y ? self->what + x - y : x - y;
g = gcd (z, self->what);
if (g != 1) {
break;
}
if (!(j & (j - 1))) {
y = x;
}
}
if (g > 1 && g < self->what) break;
}
assert (g > 1 && g < self->what);
self->p1 = g;
self->p2 = self->what / g;
if (self->p1 > self->p2) {
unsigned t = self->p1; self->p1 = self->p2; self->p2 = t;
}
debug ( "Calculated primes: self->p1 = %d, self->p2 = %d, %d iterations\n", self->p1, self->p2, it);
/// ++p1; ///
assert (*(int *) (from) == CODE_vector);
int fingerprints_num = *(int *)(from + 4);
assert (fingerprints_num >= 1 && fingerprints_num <= 64 && len == fingerprints_num * 8 + 8 + (from - packet));
long long *fingerprints = (long long *) (from + 8);
debug("Got %d fingerprints\n", fingerprints_num);
for (i = 0; i < fingerprints_num; i++) {
if (fingerprints[i] == pk_fingerprint) {
debug ( "found our public key at position %d\n", i);
break;
}
}
if (i == fingerprints_num) {
debug ( "fatal: don't have any matching keys (%016llx expected)\n", pk_fingerprint);
exit (2);
}
// create inner part (P_Q_inner_data)
clear_packet (self);
self->packet_ptr += 5;
out_int (self, CODE_p_q_inner_data);
out_cstring (self, packet + 57, clen);
//out_int (0x0f01); // pq=15
if (self->p1 < 256) {
clen = 1;
} else if (self->p1 < 65536) {
clen = 2;
} else if (self->p1 < 16777216) {
clen = 3;
} else {
clen = 4;
}
self->p1 = __builtin_bswap32 (self->p1);
out_cstring (self, (char *)&self->p1 + 4 - clen, clen);
self->p1 = __builtin_bswap32 (self->p1);
if (self->p2 < 256) {
clen = 1;
} else if (self->p2 < 65536) {
clen = 2;
} else if (self->p2 < 16777216) {
clen = 3;
} else {
clen = 4;
}
self->p2 = __builtin_bswap32 (self->p2);
out_cstring (self, (char *)&self->p2 + 4 - clen, clen);
self->p2 = __builtin_bswap32 (self->p2);
//out_int (0x0301); // p=3
//out_int (0x0501); // q=5
out_ints (self, (int *) self->nonce, 4);
out_ints (self, (int *) self->server_nonce, 4);
secure_random (self->new_nonce, 32);
out_ints (self, (int *) self->new_nonce, 8);
sha1 ((unsigned char *) (self->packet_buffer + 5), (self->packet_ptr - self->packet_buffer - 5) * 4, (unsigned char *) self->packet_buffer);
int l = encrypt_packet_buffer (self);
clear_packet (self);
out_int (self, CODE_req_DH_params);
out_ints (self, (int *) self->nonce, 4);
out_ints (self, (int *) self->server_nonce, 4);
//out_int (0x0301); // p=3
//out_int (0x0501); // q=5
if (self->p1 < 256) {
clen = 1;
} else if (self->p1 < 65536) {
clen = 2;
} else if (self->p1 < 16777216) {
clen = 3;
} else {
clen = 4;
}
self->p1 = __builtin_bswap32 (self->p1);
out_cstring (self, (char *)&self->p1 + 4 - clen, clen);
self->p1 = __builtin_bswap32 (self->p1);
if (self->p2 < 256) {
clen = 1;
} else if (self->p2 < 65536) {
clen = 2;
} else if (self->p2 < 16777216) {
clen = 3;
} else {
clen = 4;
}
self->p2 = __builtin_bswap32 (self->p2);
out_cstring (self, (char *)&self->p2 + 4 - clen, clen);
self->p2 = __builtin_bswap32 (self->p2);
out_long (self, pk_fingerprint);
out_cstring (self, (char *) self->encrypt_buffer, l);
self->c_state = st_reqdh_sent;
return rpc_send_packet (c);
}
int check_prime (struct mtproto_connection *self, BIGNUM *p) {
int r = BN_is_prime (p, BN_prime_checks, 0, self->BN_ctx, 0);
ensure (r >= 0);
return r;
}
int check_DH_params (struct mtproto_connection *self, BIGNUM *p, int g) {
if (g < 2 || g > 7) { return -1; }
BIGNUM t;
BN_init (&t);
BN_init (&self->dh_g);
ensure (BN_set_word (&self->dh_g, 4 * g));
ensure (BN_mod (&t, p, &self->dh_g, self->BN_ctx));
int x = BN_get_word (&t);
assert (x >= 0 && x < 4 * g);
BN_free (&self->dh_g);
switch (g) {
case 2:
if (x != 7) { return -1; }
break;
case 3:
if (x % 3 != 2 ) { return -1; }
break;
case 4:
break;
case 5:
if (x % 5 != 1 && x % 5 != 4) { return -1; }
break;
case 6:
if (x != 19 && x != 23) { return -1; }
break;
case 7:
if (x % 7 != 3 && x % 7 != 5 && x % 7 != 6) { return -1; }
break;
}
if (!check_prime (self, p)) { return -1; }
BIGNUM b;
BN_init (&b);
ensure (BN_set_word (&b, 2));
ensure (BN_div (&t, 0, p, &b, self->BN_ctx));
if (!check_prime (self, &t)) { return -1; }
BN_free (&b);
BN_free (&t);
return 0;
}
int check_g (unsigned char p[256], BIGNUM *g) {
static unsigned char s[256];
memset (s, 0, 256);
assert (BN_num_bytes (g) <= 256);
BN_bn2bin (g, s);
int ok = 0;
int i;
for (i = 0; i < 64; i++) {
if (s[i]) {
ok = 1;
break;
}
}
if (!ok) { return -1; }
ok = 0;
for (i = 0; i < 64; i++) {
if (s[255 - i]) {
ok = 1;
break;
}
}
if (!ok) { return -1; }
ok = 0;
for (i = 0; i < 64; i++) {
if (s[i] < p[i]) {
ok = 1;
break;
} else if (s[i] > p[i]) {
debug ("i = %d (%d %d)\n", i, (int)s[i], (int)p[i]);
return -1;
}
}
if (!ok) { return -1; }
return 0;
}
int check_g_bn (BIGNUM *p, BIGNUM *g) {
static unsigned char s[256];
memset (s, 0, 256);
assert (BN_num_bytes (p) <= 256);
BN_bn2bin (p, s);
return check_g (s, g);
}
int process_dh_answer (struct connection *c, char *packet, int len) {
debug ( "process_dh_answer(), len=%d\n", len);
struct mtproto_connection *self = c->mtconnection;
if (len < 116) {
debug ( "%u * %u = %llu", self->p1, self->p2, self->what);
}
assert (len >= 116);
assert (!*(long long *) packet);
assert (*(int *) (packet + 16) == len - 20);
assert (!(len & 3));
assert (*(int *) (packet + 20) == (int)CODE_server_DH_params_ok);
assert (!memcmp (packet + 24, self->nonce, 16));
assert (!memcmp (packet + 40, self->server_nonce, 16));
init_aes_unauth (self, self->server_nonce, self->new_nonce, AES_DECRYPT);
self->in_ptr = (int *)(packet + 56);
self->in_end = (int *)(packet + len);
int l = prefetch_strlen (self);
assert (l > 0);
l = pad_aes_decrypt (self, fetch_str (self, l), l, (char *) self->decrypt_buffer, DECRYPT_BUFFER_INTS * 4 - 16);
assert (self->in_ptr == self->in_end);
assert (l >= 60);
assert (self->decrypt_buffer[5] == (int)CODE_server_DH_inner_data);
assert (!memcmp (self->decrypt_buffer + 6, self->nonce, 16));
assert (!memcmp (self->decrypt_buffer + 10, self->server_nonce, 16));
int g = self->decrypt_buffer[14];
self->in_ptr = self->decrypt_buffer + 15;
self->in_end = self->decrypt_buffer + (l >> 2);
BN_init (&self->dh_prime);
BN_init (&self->g_a);
assert (fetch_bignum (self, &self->dh_prime) > 0);
assert (fetch_bignum (self, &self->g_a) > 0);
assert (check_g_bn (&self->dh_prime, &self->g_a) >= 0);
int server_time = *self->in_ptr++;
assert (self->in_ptr <= self->in_end);
assert (check_DH_params (self, &self->dh_prime, g) >= 0);
static char sha1_buffer[20];
sha1 ((unsigned char *) self->decrypt_buffer + 20, (self->in_ptr - self->decrypt_buffer - 5) * 4, (unsigned char *) sha1_buffer);
assert (!memcmp (self->decrypt_buffer, sha1_buffer, 20));
assert ((char *) self->in_end - (char *) self->in_ptr < 16);
GET_DC(c)->server_time_delta = server_time - time (0);
GET_DC(c)->server_time_udelta = server_time - get_utime (CLOCK_MONOTONIC);
//debug ( "server time is %d, delta = %d\n", server_time, server_time_delta);
// Build set_client_DH_params answer
clear_packet (self);
self->packet_ptr += 5;
out_int (self, CODE_client_DH_inner_data);
out_ints (self, (int *) self->nonce, 4);
out_ints (self, (int *) self->server_nonce, 4);
out_long (self, 0LL);
BN_init (&self->dh_g);
ensure (BN_set_word (&self->dh_g, g));
secure_random (self->s_power, 256);
BIGNUM *dh_power = BN_bin2bn ((unsigned char *)self->s_power, 256, 0);
ensure_ptr (dh_power);
BIGNUM *y = BN_new ();
ensure_ptr (y);
ensure (BN_mod_exp (y, &self->dh_g, dh_power, &self->dh_prime, self->BN_ctx));
out_bignum (self, y);
BN_free (y);
BN_init (&self->auth_key_num);
ensure (BN_mod_exp (&self->auth_key_num, &self->g_a, dh_power, &self->dh_prime, self->BN_ctx));
l = BN_num_bytes (&self->auth_key_num);
assert (l >= 250 && l <= 256);
assert (BN_bn2bin (&self->auth_key_num, (unsigned char *)GET_DC(c)->auth_key));
memset (GET_DC(c)->auth_key + l, 0, 256 - l);
BN_free (dh_power);
BN_free (&self->auth_key_num);
BN_free (&self->dh_g);
BN_free (&self->g_a);
BN_free (&self->dh_prime);
//hexdump (auth_key, auth_key + 256);
sha1 ((unsigned char *) (self->packet_buffer + 5), (self->packet_ptr - self->packet_buffer - 5) * 4, (unsigned char *) self->packet_buffer);
//hexdump ((char *)packet_buffer, (char *)packet_ptr);
l = encrypt_packet_buffer_aes_unauth (self, self->server_nonce, self->new_nonce);
clear_packet (self);
out_int (self, CODE_set_client_DH_params);
out_ints (self, (int *) self->nonce, 4);
out_ints (self, (int *) self->server_nonce, 4);
out_cstring (self, (char *) self->encrypt_buffer, l);
self->c_state = st_client_dh_sent;
return rpc_send_packet (c);
}
int process_auth_complete (struct connection *c, char *packet, int len) {
debug ( "process_auth_complete(), len=%d\n", len);
struct mtproto_connection *self = c->mtconnection;
assert (len == 72);
assert (!*(long long *) packet);
assert (*(int *) (packet + 16) == len - 20);
assert (!(len & 3));
assert (*(int *) (packet + 20) == CODE_dh_gen_ok);
assert (!memcmp (packet + 24, self->nonce, 16));
assert (!memcmp (packet + 40, self->server_nonce, 16));
static unsigned char tmp[44], sha1_buffer[20];
memcpy (tmp, self->new_nonce, 32);
tmp[32] = 1;
//GET_DC(c)->auth_key_id = *(long long *)(sha1_buffer + 12);
bl_do_set_auth_key_id (c->instance, GET_DC(c)->id, (unsigned char *)GET_DC(c)->auth_key);
sha1 ((unsigned char *)GET_DC(c)->auth_key, 256, sha1_buffer);
memcpy (tmp + 33, sha1_buffer, 8);
sha1 (tmp, 41, sha1_buffer);
assert (!memcmp (packet + 56, sha1_buffer + 4, 16));
GET_DC(c)->server_salt = *(long long *)self->server_nonce ^ *(long long *)self->new_nonce;
debug ( "auth_key_id=%016llx\n", GET_DC(c)->auth_key_id);
//kprintf ("OK\n");
//c->status = conn_error;
//sleep (1);
self->c_state = st_authorized;
//return 1;
debug ( "Auth success\n");
self->auth_success ++;
GET_DC(c)->flags |= 1;
return 1;
}
/*
*
* AUTHORIZED (MAIN) PROTOCOL PART
*
*/
double get_server_time (struct 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;
}
long long generate_next_msg_id (struct mtproto_connection *self, struct dc *DC) {
long long next_id = (long long) (get_server_time (DC) * (1LL << 32)) & -4;
if (next_id <= self->client_last_msg_id) {
next_id = self->client_last_msg_id += 4;
} else {
self->client_last_msg_id = next_id;
}
return next_id;
}
void init_enc_msg (struct mtproto_connection *self, struct session *S, int useful) {
struct dc *DC = S->dc;
assert (DC->auth_key_id);
self->enc_msg.auth_key_id = DC->auth_key_id;
// assert (DC->server_salt);
self->enc_msg.server_salt = DC->server_salt;
if (!S->session_id) {
secure_random (&S->session_id, 8);
}
self->enc_msg.session_id = S->session_id;
//enc_msg.auth_key_id2 = auth_key_id;
self->enc_msg.msg_id = generate_next_msg_id (self, DC);
//enc_msg.msg_id -= 0x10000000LL * (lrand48 () & 15);
//kprintf ("message id %016llx\n", enc_msg.msg_id);
self->enc_msg.seq_no = S->seq_no;
if (useful) {
self->enc_msg.seq_no |= 1;
}
S->seq_no += 2;
};
int aes_encrypt_message (struct mtproto_connection *self, struct dc *DC, 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);
//printf ("enc_len is %d\n", enc_len);
if (verbosity >= 2) {
debug ( "sending message with sha1 %08x\n", *(int *)sha1_buffer);
}
memcpy (enc->msg_key, sha1_buffer + 4, 16);
init_aes_auth (self, DC->auth_key, enc->msg_key, AES_ENCRYPT);
//hexdump ((char *)enc, (char *)enc + enc_len + 24);
return pad_aes_encrypt (self, (char *) &enc->server_salt, enc_len, (char *) &enc->server_salt, MAX_MESSAGE_INTS * 4 + (MINSZ - UNENCSZ));
}
long long encrypt_send_message (struct mtproto_connection *self, int *msg, int msg_ints, int useful) {
struct connection *c = self->connection;
//debug("encrypt_send_message(...)\n");
struct dc *DC = GET_DC(c);
struct session *S = c->session;
assert (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 (self->enc_msg.message, msg, msg_ints * 4);
self->enc_msg.msg_len = msg_ints * 4;
} else {
if ((self->enc_msg.msg_len & 0x80000003) || self->enc_msg.msg_len > MAX_MESSAGE_INTS * 4 - 16) {
return -1;
}
}
init_enc_msg (self, S, useful);
//hexdump ((char *)msg, (char *)msg + (msg_ints * 4));
int l = aes_encrypt_message (self, DC, &self->enc_msg);
//hexdump ((char *)&enc_msg, (char *)&enc_msg + l + 24);
assert (l > 0);
rpc_send_message (c, &self->enc_msg, l + UNENCSZ);
return self->client_last_msg_id;
}
int auth_work_start (struct connection *c UU) {
return 1;
}
void rpc_execute_answer (struct connection *c, long long msg_id UU);
void fetch_pts (struct mtproto_connection *self) {
int p = fetch_int (self);
if (p <= self->pts) { return; }
if (p != self->pts + 1) {
if (self->pts) {
//debug ("Hole in pts p = %d, pts = %d\n", p, pts);
// get difference should be here
self->pts = p;
} else {
self->pts = p;
}
} else {
self->pts ++;
}
bl_do_set_pts (self->bl, self, self->pts);
}
void fetch_qts (struct mtproto_connection *self) {
int p = fetch_int (self);
if (p <= self->qts) { return; }
if (p != self->qts + 1) {
if (self->qts) {
//debug ("Hole in qts\n");
// get difference should be here
self->qts = p;
} else {
self->qts = p;
}
} else {
self->qts ++;
}
bl_do_set_qts (self->bl, self, self->qts);
}
void fetch_date (struct mtproto_connection *self) {
int p = fetch_int (self);
if (p > self->last_date) {
self->last_date = p;
bl_do_set_date (self->bl, self, self->last_date);
}
}
void fetch_seq (struct mtproto_connection *self) {
int x = fetch_int (self);
if (x > self->seq + 1) {
debug ("Hole in seq: seq = %d, x = %d\n", self->seq, x);
//do_get_difference ();
//seq = x;
} else if (x == self->seq + 1) {
self->seq = x;
bl_do_set_seq (self->bl, self, self->seq);
}
}
void work_update_binlog (struct mtproto_connection *self) {
struct binlog *bl = self->bl;
unsigned op = fetch_int (self);
switch (op) {
case CODE_update_user_name:
{
peer_id_t user_id = MK_USER (fetch_int (self));
peer_t *UC = user_chat_get (bl, user_id);
if (UC) {
struct tgl_user *U = &UC->user;
if (U->first_name) { tfree_str (U->first_name); }
if (U->last_name) { tfree_str (U->last_name); }
if (U->print_name) {
peer_delete_name (bl, UC);
tfree_str (U->print_name);
}
U->first_name = fetch_str_dup (self);
U->last_name = fetch_str_dup (self);
U->print_name = create_print_name (bl, U->id, U->first_name, U->last_name, 0, 0);
peer_insert_name (bl, (void *)U);
} else {
fetch_skip_str (self);
fetch_skip_str (self);
}
}
break;
case CODE_update_user_photo:
{
peer_id_t user_id = MK_USER (fetch_int (self));
peer_t *UC = user_chat_get (bl, user_id);
fetch_date (self);
if (UC) {
struct tgl_user *U = &UC->user;
unsigned y = fetch_int (self);
if (y == CODE_user_profile_photo_empty) {
U->photo_id = 0;
U->photo_big.dc = -2;
U->photo_small.dc = -2;
} else {
assert (y == CODE_user_profile_photo);
U->photo_id = fetch_long (self);
fetch_file_location (self, &U->photo_small);
fetch_file_location (self, &U->photo_big);
}
} else {
struct file_location t;
unsigned y = fetch_int (self);
if (y == CODE_user_profile_photo_empty) {
} else {
assert (y == CODE_user_profile_photo);
fetch_long (self); // photo_id
fetch_file_location (self, &t);
fetch_file_location (self, &t);
}
}
fetch_bool (self);
}
break;
default:
assert (0);
}
}
void work_update (struct mtproto_connection *self, long long msg_id UU) {
struct connection *c = self->connection;
struct telegram *tg = c->instance;
struct binlog *bl = self->bl;
unsigned op = fetch_int (self);
debug("work_update(): OP:%d\n", op);
switch (op) {
case CODE_update_new_message:
{
debug ("CODE_update_new_message\n");
struct message *M UU = fetch_alloc_message (self, tg);
assert (M);
fetch_pts (self);
self->unread_messages ++;
event_update_new_message (tg, M);
//print_message (M);
//update_prompt ();
break;
};
case CODE_update_message_i_d:
{
debug ("CODE_update_message\n");
int id = fetch_int (self); // id
int new = fetch_long (self); // random_id
struct message *M = message_get (bl, new);
if (M) {
bl_do_set_msg_id (self->bl, self, M, id);
}
}
break;
case CODE_update_read_messages:
{
debug ("CODE_update_read_message\n");
assert (fetch_int (self) == (int)CODE_vector);
int n = fetch_int (self);
int i;
for (i = 0; i < n; i++) {
int id = fetch_int (self);
struct message *M = message_get (bl, id);
if (M) {
bl_do_set_unread (self->bl, self, M, 0);
}
}
fetch_pts (self);
}
break;
case CODE_update_user_typing:
{
debug ("CODE_update_user_typing\n");
peer_id_t id = MK_USER (fetch_int (self));
peer_t *U UU = user_chat_get (bl, id);
event_update_user_typing (tg, U);
}
break;
case CODE_update_chat_user_typing:
{
peer_id_t chat_id = MK_CHAT (fetch_int (self));
peer_id_t id = MK_USER (fetch_int (self));
peer_t *C = user_chat_get (bl, chat_id);
peer_t *U = user_chat_get (bl, id);
event_update_chat_user_typing(tg, C, U, 0);
}
break;
case CODE_update_user_status:
{
peer_id_t user_id = MK_USER (fetch_int (self));
peer_t *U = user_chat_get (bl, user_id);
if (U) {
fetch_user_status (self, &U->user.status);
event_update_user_status(tg, U);
} else {
struct user_status t;
fetch_user_status (self, &t);
}
}
break;
case CODE_update_user_name:
{
peer_id_t user_id = MK_USER (fetch_int (self));
peer_t *UC = user_chat_get (bl, user_id);
if (UC && (UC->flags & FLAG_CREATED)) {
int l1 = prefetch_strlen (self);
char *f = fetch_str (self, l1);
int l2 = prefetch_strlen (self);
char *l = fetch_str (self, l2);
struct tgl_user *U = &UC->user;
bl_do_set_user_real_name (self->bl, self, U, f, l1, l, l2);
} else {
fetch_skip_str (self);
fetch_skip_str (self);
}
event_update_user_name (tg, UC);
}
break;
case CODE_update_user_photo:
{
peer_id_t user_id = MK_USER (fetch_int (self));
peer_t *UC = user_chat_get (bl, user_id);
fetch_date (self);
if (UC && (UC->flags & FLAG_CREATED)) {
struct tgl_user *U = &UC->user;
unsigned y = fetch_int (self);
long long photo_id;
struct file_location big;
struct file_location small;
memset (&big, 0, sizeof (big));
memset (&small, 0, sizeof (small));
if (y == CODE_user_profile_photo_empty) {
photo_id = 0;
big.dc = -2;
small.dc = -2;
} else {
assert (y == CODE_user_profile_photo);
photo_id = fetch_long (self);
fetch_file_location (self, &small);
fetch_file_location (self, &big);
}
bl_do_set_user_profile_photo (self->bl, self, U, photo_id, &big, &small);
} else {
struct file_location t;
unsigned y = fetch_int (self);
if (y == CODE_user_profile_photo_empty) {
} else {
assert (y == CODE_user_profile_photo);
fetch_long (self); // photo_id
fetch_file_location (self, &t);
fetch_file_location (self, &t);
}
}
event_update_user_photo(tg, UC);
fetch_bool (self);
}
break;
case CODE_update_restore_messages:
{
assert (fetch_int (self) == CODE_vector);
int n = fetch_int (self);
fetch_skip (self, n);
fetch_pts (self);
}
break;
case CODE_update_delete_messages:
{
assert (fetch_int (self) == CODE_vector);
int n = fetch_int (self);
fetch_skip (self, n);
fetch_pts (self);
}
break;
case CODE_update_chat_participants:
{
unsigned x = fetch_int (self);
assert (x == CODE_chat_participants || x == CODE_chat_participants_forbidden);
peer_id_t chat_id = MK_CHAT (fetch_int (self));
int n = 0;
peer_t *C = user_chat_get (bl, chat_id);
if (C && (C->flags & FLAG_CREATED)) {
if (x == CODE_chat_participants) {
bl_do_set_chat_admin (self->bl, self, &C->chat, fetch_int (self));
assert (fetch_int (self) == CODE_vector);
n = fetch_int (self);
struct chat_user *users = talloc (12 * n);
int i;
for (i = 0; i < n; i++) {
assert (fetch_int (self) == (int)CODE_chat_participant);
users[i].user_id = fetch_int (self);
users[i].inviter_id = fetch_int (self);
users[i].date = fetch_int (self);
}
int version = fetch_int (self);
bl_do_set_chat_participants (self->bl, self, &C->chat, version, n, users);
}
} else {
if (x == CODE_chat_participants) {
fetch_int (self); // admin_id
assert (fetch_int (self) == CODE_vector);
n = fetch_int (self);
fetch_skip (self, n * 4);
fetch_int (self); // version
}
}
if (C) {
event_update_chat_participants(tg, C);
}
}
break;
case CODE_update_contact_registered:
{
peer_id_t user_id = MK_USER (fetch_int (self));
peer_t *U = user_chat_get (bl, user_id);
fetch_int (self); // date
event_update_user_registered(tg, U);
}
break;
case CODE_update_contact_link:
{
peer_id_t user_id = MK_USER (fetch_int (self));
peer_t *U UU = user_chat_get (bl, user_id);
unsigned t = fetch_int (self);
assert (t == CODE_contacts_my_link_empty || t == CODE_contacts_my_link_requested || t == CODE_contacts_my_link_contact);
if (t == CODE_contacts_my_link_requested) {
fetch_bool (self); // has_phone
}
t = fetch_int (self);
assert (t == CODE_contacts_foreign_link_unknown || t == CODE_contacts_foreign_link_requested || t == CODE_contacts_foreign_link_mutual);
if (t == CODE_contacts_foreign_link_requested) {
fetch_bool (self); // has_phone
}
}
break;
case CODE_update_activation:
{
peer_id_t user_id = MK_USER (fetch_int (self));
peer_t *U UU = user_chat_get (bl, user_id);
}
break;
case CODE_update_new_authorization:
{
fetch_long (self); // auth_key_id
fetch_int (self); // date
char *s = fetch_str_dup (self);
char *location = fetch_str_dup (self);
event_update_auth_new(tg, location);
tfree_str (s);
tfree_str (location);
}
break;
case CODE_update_new_geo_chat_message:
{
struct message *M = fetch_alloc_geo_message (self, tg);
self->unread_messages ++;
event_update_new_message (self->instance, M);
}
break;
case CODE_update_new_encrypted_message:
{
struct message *M UU = fetch_alloc_encrypted_message (self, tg);
self->unread_messages ++;
event_update_new_message (self->instance, M);
fetch_qts (self);
}
break;
case CODE_update_encryption:
{
struct secret_chat *E = fetch_alloc_encrypted_chat (self);
debug ("Secret chat state = %d\n", E->state);
if (E->state == sc_request && !disable_auto_accept) {
do_accept_encr_chat_request (tg, E);
}
fetch_int (self); // date
}
break;
case CODE_update_encrypted_chat_typing:
{
peer_id_t id = MK_ENCR_CHAT (fetch_int (self));
peer_t *P = user_chat_get (bl, id);
event_update_user_typing(tg, P);
}
break;
case CODE_update_encrypted_messages_read:
{
peer_id_t id = MK_ENCR_CHAT (fetch_int (self)); // chat_id
fetch_int (self); // max_date
fetch_int (self); // date
peer_t *P = user_chat_get (bl, id);
int x = -1;
if (P && P->last) {
x = 0;
struct message *M = P->last;
while (M && (!M->out || M->unread)) {
if (M->out) {
M->unread = 0;
x ++;
}
M = M->next;
}
}
if (log_level >= 1) {
}
}
break;
case CODE_update_chat_participant_add:
{
peer_id_t chat_id = MK_CHAT (fetch_int (self));
peer_id_t user_id = MK_USER (fetch_int (self));
peer_id_t inviter_id = MK_USER (fetch_int (self));
int version = fetch_int (self);
peer_t *C = user_chat_get (bl, chat_id);
if (C && (C->flags & FLAG_CREATED)) {
bl_do_chat_add_user (self->bl, self, &C->chat, version, get_peer_id (user_id), get_peer_id (inviter_id), time (0));
}
event_update_chat_add_participant(tg, C, user_id, inviter_id);
}
break;
case CODE_update_chat_participant_delete:
{
peer_id_t chat_id = MK_CHAT (fetch_int (self));
peer_id_t user_id = MK_USER (fetch_int (self));
int version = fetch_int (self);
peer_t *C = user_chat_get (bl, chat_id);
if (C && (C->flags & FLAG_CREATED)) {
bl_do_chat_del_user (self->bl, self, &C->chat, version, get_peer_id (user_id));
}
event_update_chat_del_participant(tg, C, user_id, 0);
}
break;
case CODE_update_dc_options:
{
assert (fetch_int (self) == CODE_vector);
int n = fetch_int (self);
assert (n >= 0);
int i;
for (i = 0; i < n; i++) {
fetch_dc_option (tg);
}
}
break;
default:
debug ("Unknown update type %08x\n", op);
;
}
}
void work_update_short (struct connection *c, long long msg_id) {
debug ("work_update_short\n");
struct mtproto_connection *self = c->mtconnection;
assert (fetch_int (self) == CODE_update_short);
work_update (self, msg_id);
fetch_date (self);
}
void work_updates (struct connection *c, long long msg_id) {
debug ("work_updates(\n)");
struct mtproto_connection *self = c->mtconnection;
assert (fetch_int (c->mtconnection) == CODE_updates);
assert (fetch_int (c->mtconnection) == CODE_vector);
int n = fetch_int (c->mtconnection);
int i;
for (i = 0; i < n; i++) {
work_update (c->mtconnection, msg_id);
}
assert (fetch_int (c->mtconnection) == CODE_vector);
n = fetch_int (c->mtconnection);
for (i = 0; i < n; i++) {
fetch_alloc_user (self);
}
assert (fetch_int (c->mtconnection) == CODE_vector);
n = fetch_int (c->mtconnection);
for (i = 0; i < n; i++) {
fetch_alloc_chat (self);
}
bl_do_set_date (self->bl, self, fetch_int (c->mtconnection));
bl_do_set_seq (self->bl, self, fetch_int (c->mtconnection));
}
void work_update_short_message (struct connection *c UU, long long msg_id UU) {
debug ("work_update_short_message(\n)");
struct mtproto_connection *self = c->mtconnection;
assert (fetch_int (c->mtconnection) == (int)CODE_update_short_message);
struct message *M = fetch_alloc_message_short (self, c->instance);
c->mtconnection->unread_messages ++;
event_update_new_message (self->instance, M);
if (M->date > c->mtconnection->last_date) {
c->mtconnection->last_date = M->date;
}
}
void work_update_short_chat_message (struct connection *c, long long msg_id UU) {
debug ("work_update_chat_message(\n)");
struct mtproto_connection *self = c->mtconnection;
assert (fetch_int (self) == CODE_update_short_chat_message);
struct message *M = fetch_alloc_message_short_chat (self, c->instance);
c->mtconnection->unread_messages ++;
event_update_new_message (self->instance, M);
if (M->date > c->mtconnection->last_date) {
c->mtconnection->last_date = M->date;
}
}
void work_container (struct connection *c, long long msg_id UU) {
debug ( "work_container: msg_id = %lld\n", msg_id);
assert (fetch_int (c->mtconnection) == CODE_msg_container);
int n = fetch_int (c->mtconnection);
int i;
for (i = 0; i < n; i++) {
long long id = fetch_long (c->mtconnection);
//int seqno = fetch_int ();
fetch_int (c->mtconnection); // seq_no
if (id & 1) {
insert_msg_id (c->session, id);
}
int bytes = fetch_int (c->mtconnection);
int *t = c->mtconnection->in_end;
c->mtconnection->in_end = c->mtconnection->in_ptr + (bytes / 4);
rpc_execute_answer (c, id);
assert (c->mtconnection->in_ptr == c->mtconnection->in_end);
c->mtconnection->in_end = t;
}
}
void work_new_session_created (struct connection *c, long long msg_id UU) {
debug ( "work_new_session_created: msg_id = %lld\n", msg_id);
assert (fetch_int (c->mtconnection) == (int)CODE_new_session_created);
fetch_long (c->mtconnection); // first message id
//DC->session_id = fetch_long ();
fetch_long (c->mtconnection); // unique_id
GET_DC(c)->server_salt = fetch_long (c->mtconnection);
debug ("new server_salt = %lld\n", GET_DC(c)->server_salt);
}
void work_msgs_ack (struct connection *c UU, long long msg_id UU) {
debug ( "work_msgs_ack: msg_id = %lld\n", msg_id);
assert (fetch_int (c->mtconnection) == CODE_msgs_ack);
assert (fetch_int (c->mtconnection) == CODE_vector);
int n = fetch_int (c->mtconnection);
int i;
for (i = 0; i < n; i++) {
long long id = fetch_long (c->mtconnection);
debug ("ack for %lld\n", id);
query_ack (c->instance, id);
}
}
void work_rpc_result (struct connection *c UU, long long msg_id UU) {
debug ( "work_rpc_result: msg_id = %lld\n", msg_id);
assert (fetch_int (c->mtconnection) == (int)CODE_rpc_result);
long long id = fetch_long (c->mtconnection);
int op = prefetch_int (c->mtconnection);
if (op == CODE_rpc_error) {
query_error (c->instance, id);
} else {
query_result (c->instance, id);
}
}
#define MAX_PACKED_SIZE (1 << 24)
void work_packed (struct connection *c, long long msg_id) {
debug ("work_packet()\n");
assert (fetch_int (c->mtconnection) == CODE_gzip_packed);
static int in_gzip;
static int buf[MAX_PACKED_SIZE >> 2];
assert (!in_gzip);
in_gzip = 1;
int l = prefetch_strlen (c->mtconnection);
char *s = fetch_str (c->mtconnection, l);
int total_out = tinflate (s, l, buf, MAX_PACKED_SIZE);
int *end = c->mtconnection->in_ptr;
int *eend = c->mtconnection->in_end;
//assert (total_out % 4 == 0);
c->mtconnection->in_ptr = buf;
c->mtconnection->in_end = c->mtconnection->in_ptr + total_out / 4;
if (verbosity >= 4) {
debug ( "Unzipped data: ");
hexdump_in (c->mtconnection);
}
rpc_execute_answer (c, msg_id);
c->mtconnection->in_ptr = end;
c->mtconnection->in_end = eend;
in_gzip = 0;
}
void work_bad_server_salt (struct connection *c UU, long long msg_id UU) {
debug ("work_bad_server_salt()\n");
assert (fetch_int (c->mtconnection) == (int)CODE_bad_server_salt);
long long id = fetch_long (c->mtconnection);
query_restart (c->instance, id);
fetch_int (c->mtconnection); // seq_no
fetch_int (c->mtconnection); // error_code
long long new_server_salt = fetch_long (c->mtconnection);
GET_DC(c)->server_salt = new_server_salt;
}
void work_pong (struct connection *c UU, long long msg_id UU) {
debug ("work_pong()\n");
assert (fetch_int (c->mtconnection) == CODE_pong);
fetch_long (c->mtconnection); // msg_id
fetch_long (c->mtconnection); // ping_id
}
void work_detailed_info (struct connection *c UU, long long msg_id UU) {
debug ("work_detailed_info()\n");
assert (fetch_int (c->mtconnection) == CODE_msg_detailed_info);
fetch_long (c->mtconnection); // msg_id
fetch_long (c->mtconnection); // answer_msg_id
fetch_int (c->mtconnection); // bytes
fetch_int (c->mtconnection); // status
}
void work_new_detailed_info (struct connection *c UU, long long msg_id UU) {
debug ("work_new_detailed_info()\n");
assert (fetch_int (c->mtconnection) == (int)CODE_msg_new_detailed_info);
fetch_long (c->mtconnection); // answer_msg_id
fetch_int (c->mtconnection); // bytes
fetch_int (c->mtconnection); // status
}
void work_updates_to_long (struct connection *c UU, long long msg_id UU) {
assert (fetch_int (c->mtconnection) == (int)CODE_updates_too_long);
debug ("updates to long... Getting difference\n");
do_get_difference (c->instance, 0);
}
void work_bad_msg_notification (struct connection *c UU, long long msg_id UU) {
assert (fetch_int (c->mtconnection) == (int)CODE_bad_msg_notification);
long long m1 = fetch_long (c->mtconnection);
int s = fetch_int (c->mtconnection);
int e = fetch_int (c->mtconnection);
debug ("bad_msg_notification: msg_id = %lld, seq = %d, error = %d\n", m1, s, e);
}
void rpc_execute_answer (struct connection *c, long long msg_id UU) {
if (verbosity >= 5) {
debug ("rpc_execute_answer: fd=%d\n", c->fd);
hexdump_in (c->mtconnection);
}
int op = prefetch_int (c->mtconnection);
switch (op) {
case CODE_msg_container:
work_container (c, msg_id);
return;
case CODE_new_session_created:
work_new_session_created (c, msg_id);
return;
case CODE_msgs_ack:
work_msgs_ack (c, msg_id);
return;
case CODE_rpc_result:
work_rpc_result (c, msg_id);
return;
case CODE_update_short:
work_update_short (c, msg_id);
return;
case CODE_updates:
work_updates (c, msg_id);
return;
case CODE_update_short_message:
work_update_short_message (c, msg_id);
return;
case CODE_update_short_chat_message:
work_update_short_chat_message (c, msg_id);
return;
case CODE_gzip_packed:
work_packed (c, msg_id);
return;
case CODE_bad_server_salt:
work_bad_server_salt (c, msg_id);
return;
case CODE_pong:
work_pong (c, msg_id);
return;
case CODE_msg_detailed_info:
work_detailed_info (c, msg_id);
return;
case CODE_msg_new_detailed_info:
work_new_detailed_info (c, msg_id);
return;
case CODE_updates_too_long:
work_updates_to_long (c, msg_id);
return;
case CODE_bad_msg_notification:
work_bad_msg_notification (c, msg_id);
return;
}
debug ( "Unknown message: \n");
hexdump_in (c->mtconnection);
c->mtconnection->in_ptr = c->mtconnection->in_end; // Will not fail due to assertion in_ptr == in_end
}
int process_rpc_message (struct connection *c UU, struct encrypted_message *enc, int len) {
const int MINSZ = offsetof (struct encrypted_message, message);
const int UNENCSZ = offsetof (struct encrypted_message, server_salt);
debug ( "process_rpc_message(), len=%d\n", len);
assert (len >= MINSZ && (len & 15) == (UNENCSZ & 15));
struct dc *DC = GET_DC(c);
assert (enc->auth_key_id == DC->auth_key_id);
assert (DC->auth_key_id);
init_aes_auth (c->mtconnection, DC->auth_key + 8, enc->msg_key, AES_DECRYPT);
int l = pad_aes_decrypt (c->mtconnection, (char *)&enc->server_salt, len - UNENCSZ, (char *)&enc->server_salt, len - UNENCSZ);
assert (l == len - UNENCSZ);
//assert (enc->auth_key_id2 == enc->auth_key_id);
assert (!(enc->msg_len & 3) && enc->msg_len > 0 && enc->msg_len <= len - MINSZ && len - MINSZ - enc->msg_len <= 12);
static unsigned char sha1_buffer[20];
sha1 ((void *)&enc->server_salt, enc->msg_len + (MINSZ - UNENCSZ), sha1_buffer);
assert (!memcmp (&enc->msg_key, sha1_buffer + 4, 16));
//assert (enc->server_salt == server_salt); //in fact server salt can change
if (DC->server_salt != enc->server_salt) {
DC->server_salt = enc->server_salt;
//write_auth_file ();
}
int this_server_time = enc->msg_id >> 32LL;
if (!DC->server_time_delta) {
DC->server_time_delta = this_server_time - get_utime (CLOCK_REALTIME);
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) {
debug ("salt = %lld, session_id = %lld, msg_id = %lld, 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));
c->mtconnection->in_ptr = enc->message;
c->mtconnection->in_end = c->mtconnection->in_ptr + (enc->msg_len / 4);
hexdump_in (c->mtconnection);
}
assert (this_server_time >= st - 300 && this_server_time <= st + 30);
//assert (enc->msg_id > server_last_msg_id && (enc->msg_id & 3) == 1);
debug ( "received mesage id %016llx\n", enc->msg_id);
hexdump_in (c->mtconnection);
c->mtconnection->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);
++c->mtconnection->good_messages;
c->mtconnection->in_ptr = enc->message;
c->mtconnection->in_end = c->mtconnection->in_ptr + (enc->msg_len / 4);
if (enc->msg_id & 1) {
insert_msg_id (c->session, enc->msg_id);
}
assert (c->session->session_id == enc->session_id);
rpc_execute_answer (c, enc->msg_id);
assert (c->mtconnection->in_ptr == c->mtconnection->in_end);
return 0;
}
int rpc_execute (struct connection *c, int op, int len) {
debug ("outbound rpc connection #%d : received rpc answer %d with %d content bytes\n", c->fd, op, len);
struct mtproto_connection *self = c->mtconnection;
struct telegram *instance = c->instance;
/*
if (op < 0) {
assert (read_in (c, Response, Response_len) == Response_len);
return 0;
}
*/
if (len >= MAX_RESPONSE_SIZE/* - 12*/ || len < 0/*12*/) {
debug ( "answer too long (%d bytes), skipping\n", len);
return 0;
}
int Response_len = len;
if (verbosity >= 2) {
debug ("Response_len = %d\n", Response_len);
}
assert (read_in (c, Response, Response_len) == Response_len);
Response[Response_len] = 0;
if (verbosity >= 2) {
debug ( "have %d Response bytes\n", Response_len);
}
int o = c->mtconnection->c_state;
if (GET_DC(c)->flags & 1) { o = st_authorized;}
switch (o) {
case st_reqpq_sent:
process_respq_answer (c, Response/* + 8*/, Response_len/* - 12*/);
return 0;
case st_reqdh_sent:
process_dh_answer (c, Response/* + 8*/, Response_len/* - 12*/);
return 0;
case st_client_dh_sent:
process_auth_complete (c, Response/* + 8*/, Response_len/* - 12*/);
self->queries_num --;
debug ("queries_num=%d\n", c->mtconnection->queries_num);
if (self->on_ready) {
self->on_ready(self, self->on_ready_data);
}
return 0;
case st_authorized:
if (op < 0 && op >= -999) {
debug ("Server error %d\n", op);
char code[12] = {0};
snprintf (code, 12, "%d", op);
c->mtconnection->c_state = st_error;
telegram_change_state (instance, STATE_ERROR, code);
} else {
process_rpc_message (c, (void *)(Response/* + 8*/), Response_len/* - 12*/);
}
return 0;
default:
debug ( "fatal: cannot receive answer in state %d\n", c->mtconnection->c_state);
exit (2);
}
return 0;
}
int tc_close (struct connection *c, int who) {
debug ( "outbound http connection #%d : closing by %d\n", c->fd, who);
return 0;
}
int tc_becomes_ready (struct connection *c) {
debug ( "outbound connection #%d becomes ready\n", c->fd);
char byte = 0xef;
assert (write_out (c, &byte, 1) == 1);
flush_out (c);
int o = c->mtconnection->c_state;
if (GET_DC(c)->flags & 1) {
o = st_authorized;
}
switch (o) {
case st_init:
c->mtconnection->queries_num ++;
debug ("queries_num=%d\n", c->mtconnection->queries_num);
send_req_pq_packet (c);
break;
case st_authorized:
c->mtconnection->on_ready(c->mtconnection, c->mtconnection->on_ready_data);
//telegram_change_state (c->instance, STATE_AUTHORIZED, NULL);
break;
default:
debug ( "c_state = %d\n", c->mtconnection->c_state);
assert (0);
}
return 0;
}
int rpc_becomes_ready (struct connection *c) {
return tc_becomes_ready (c);
}
int rpc_close (struct connection *c) {
return tc_close (c, 0);
}
int auth_is_success (struct mtproto_connection *m) {
return m->auth_success;
}
#define RANDSEED_PASSWORD_FILENAME NULL
#define RANDSEED_PASSWORD_LENGTH 0
void on_start (struct mtproto_connection *self) {
prng_seed (self, RANDSEED_PASSWORD_FILENAME, RANDSEED_PASSWORD_LENGTH);
if (rsa_public_key_name) {
if (rsa_load_public_key (rsa_public_key_name) < 0) {
perror ("rsa_load_public_key");
exit (1);
}
} else {
if (rsa_load_public_key (TG_SERVER_PUBKEY_FILENAME) < 0
&& rsa_load_public_key ("/etc/" PROG_NAME "/server.pub") < 0) {
perror ("rsa_load_public_key");
exit (1);
}
}
pk_fingerprint = compute_rsa_key_fingerprint (pubKey);
}
struct connection_methods mtproto_methods = {
.execute = rpc_execute,
.ready = rpc_becomes_ready,
.close = rpc_close
};
/**
* Create a new struct mtproto_connection connection using the giving datacenter for authorization and
* session handling
*/
struct mtproto_connection *mtproto_new(struct dc *DC, int fd, struct telegram *tg)
{
struct mtproto_connection *mtp = talloc0(sizeof(struct mtproto_connection));
tg->Cs[tg->cs++] = mtp;
mtp->instance = tg;
mtp->packet_buffer = mtp->__packet_buffer + 16;
mtp->connection = fd_create_connection(DC, fd, tg, &mtproto_methods, mtp);
assert (tg->bl);
mtp->bl = tg->bl;
return mtp;
}
/**
* Connect to the network
*/
void mtproto_connect(struct mtproto_connection *c)
{
on_start(c);
start_ping_timer (c->connection);
c->connection->methods->ready(c->connection);
}
/**
* Mark the connection for destruction, stop all timers and initiate
* cleanup tasks
*/
void mtproto_close(struct mtproto_connection *mtp) {
debug ("closing mtproto_connection...\n");
mtp->destroy = 1;
// send all pending acks on this connection so the server won't
// resend messages. We might not be able to send the acknowledgements later
// in case the session is switched and this DC is not reachable anymore
if (mtp->connection) {
if (mtp->connection->session && mtp->connection->session->ack_tree) {
struct session *S = mtp->connection->session;
send_all_acks (S);
mtp->instance->config->on_output(mtp->handle);
S->c = 0;
}
stop_ping_timer (mtp->connection);
}
}
/**
* Close the underlying file descriptor
*/
void mtproto_destroy (struct mtproto_connection *self) {
debug("destroying mtproto_connection: %p\n", self);
self->instance->config->proxy_close_cb(self->handle);
fd_close_connection(self->connection);
tfree(self, sizeof(struct mtproto_connection));
}
void mtproto_close_foreign (struct telegram *instance)
{
int i;
for (i = 0; i < 100; i++) {
struct mtproto_connection * c = instance->Cs[i];
if (c &&
!c->destroy &&
c->connection->session->dc->id != instance->auth.dc_working_num) {
debug ("closing connection for working_dc=%d, dc=%d\n",
instance->auth.dc_working_num, c->connection->session->dc->id);
mtproto_close (c);
}
}
}
/**
* Free all destroyed connections
*/
void mtproto_free_closed (struct telegram *tg, int force) {
int i;
for (i = 0; i < 100; i++) {
if (tg->Cs[i] == NULL) continue;
struct mtproto_connection *c = tg->Cs[i];
debug ("checking mtproto_connection %d: c_state:%d destroy:%d, quries_num:%d\n",
i, c->c_state, c->destroy, c->queries_num);
if (c->destroy == 0) continue;
if (!force && c->connection->out_bytes > 0) {
debug ("still %d bytes ouput left, skipping connection...\n", c->connection->out_bytes);
continue;
}
mtproto_destroy (c);
if (tg->connection == c) {
tg->connection = NULL;
}
tg->Cs[i] = NULL;
}
}