Browse Source

Fix first requests with new transport

Fix resend websocket messages after close
master
morethanwords 4 years ago
parent
commit
df74c2b7e7
  1. 116
      src/lib/mtproto/authorizer.ts
  2. 4
      src/lib/mtproto/mtproto_config.ts
  3. 2
      src/lib/mtproto/mtprotoworker.ts
  4. 73
      src/lib/mtproto/networker.ts
  5. 4
      src/lib/mtproto/rsaKeysManager.ts
  6. 77
      src/lib/mtproto/transports/websocket.ts

116
src/lib/mtproto/authorizer.ts

@ -1,6 +1,6 @@
import { TLSerialization, TLDeserialization } from "./tl_utils"; import { TLSerialization, TLDeserialization } from "./tl_utils";
import dcConfigurator from "./dcConfigurator"; import dcConfigurator from "./dcConfigurator";
import { dT, bytesToHex, bytesCmp, bytesFromHex, bytesXor } from "../bin_utils"; import { bytesToHex, bytesCmp, bytesFromHex, bytesXor } from "../bin_utils";
import rsaKeysManager from "./rsaKeysManager"; import rsaKeysManager from "./rsaKeysManager";
import timeManager from "./timeManager"; import timeManager from "./timeManager";
@ -9,6 +9,8 @@ import { BigInteger } from "jsbn";
import CryptoWorker from "../crypto/cryptoworker"; import CryptoWorker from "../crypto/cryptoworker";
import { logger, LogLevels } from "../polyfill";
/* let fNewNonce: any = bytesFromHex('8761970c24cb2329b5b2459752c502f3057cb7e8dbab200e526e8767fdc73b3c').reverse(); /* let fNewNonce: any = bytesFromHex('8761970c24cb2329b5b2459752c502f3057cb7e8dbab200e526e8767fdc73b3c').reverse();
let fNonce: any = bytesFromHex('b597720d11faa5914ef485c529cde414').reverse(); let fNonce: any = bytesFromHex('b597720d11faa5914ef485c529cde414').reverse();
let fResult: any = new Uint8Array(bytesFromHex('000000000000000001b473a0661b285e480000006324160514e4cd29c585f44e91a5fa110d7297b5c0c4134c84893db5715ecd56af5ed618082182053cc5de91cd00000015c4b51c02000000a5b7f709355fc30b216be86c022bb4c3')); let fResult: any = new Uint8Array(bytesFromHex('000000000000000001b473a0661b285e480000006324160514e4cd29c585f44e91a5fa110d7297b5c0c4134c84893db5715ecd56af5ed618082182053cc5de91cd00000015c4b51c02000000a5b7f709355fc30b216be86c022bb4c3'));
@ -59,7 +61,10 @@ export class Authorizer {
[dcID: number]: Promise<AuthOptions> [dcID: number]: Promise<AuthOptions>
} = {}; } = {};
private log: ReturnType<typeof logger>;
constructor() { constructor() {
this.log = logger(`AUTHORIZER`/* , LogLevels.error | LogLevels.log */);
} }
public mtpSendPlainRequest(dcID: number, requestArray: Uint8Array) { public mtpSendPlainRequest(dcID: number, requestArray: Uint8Array) {
@ -94,10 +99,10 @@ export class Authorizer {
transport: transport transport: transport
}; };
console.log(dT(), 'mtpSendPlainRequest: creating requestPromise'); this.log('mtpSendPlainRequest: creating requestPromise');
return transport.send(resultArray).then(result => { return transport.send(resultArray).then(result => {
console.log(dT(), 'mtpSendPlainRequest: in good sector', result); this.log('mtpSendPlainRequest: in good sector', result);
if(!result || !result.byteLength) { if(!result || !result.byteLength) {
return Promise.reject(baseError); return Promise.reject(baseError);
@ -109,17 +114,17 @@ export class Authorizer {
let deserializer = new TLDeserialization(result, {mtproto: true}); let deserializer = new TLDeserialization(result, {mtproto: true});
let auth_key_id = deserializer.fetchLong('auth_key_id'); let auth_key_id = deserializer.fetchLong('auth_key_id');
if(auth_key_id != 0) console.error('auth_key_id != 0', auth_key_id); if(auth_key_id != 0) this.log.error('auth_key_id != 0', auth_key_id);
let msg_id = deserializer.fetchLong('msg_id'); let msg_id = deserializer.fetchLong('msg_id');
if(msg_id == 0) console.error('msg_id == 0', msg_id); if(msg_id == 0) this.log.error('msg_id == 0', msg_id);
let msg_len = deserializer.fetchInt('msg_len'); let msg_len = deserializer.fetchInt('msg_len');
if(!msg_len) console.error('no msg_len', msg_len); if(!msg_len) this.log.error('no msg_len', msg_len);
return deserializer; return deserializer;
} catch(e) { } catch(e) {
console.error('mtpSendPlainRequest: deserialization went bad', e); this.log.error('mtpSendPlainRequest: deserialization went bad', e);
let error = Object.assign(baseError, {originalError: e}); let error = Object.assign(baseError, {originalError: e});
throw error; throw error;
} }
@ -142,15 +147,14 @@ export class Authorizer {
// need // need
rsaKeysManager.prepare().then(() => {}); rsaKeysManager.prepare().then(() => {});
console.log(dT(), 'Send req_pq', auth.nonce.hex); this.log('Send req_pq', auth.nonce.hex);
try { try {
var deserializer = await this.mtpSendPlainRequest(auth.dcID, request.getBytes(true)); var deserializer = await this.mtpSendPlainRequest(auth.dcID, request.getBytes(true));
} catch(error) { } catch(error) {
console.error(dT(), 'req_pq error', error.message); this.log.error('req_pq error', error.message);
throw error; throw error;
} }
var response = deserializer.fetchObject('ResPQ'); var response = deserializer.fetchObject('ResPQ');
if(response._ != 'resPQ') { if(response._ != 'resPQ') {
@ -158,7 +162,7 @@ export class Authorizer {
} }
if(!bytesCmp(auth.nonce, response.nonce)) { if(!bytesCmp(auth.nonce, response.nonce)) {
console.log(auth.nonce, response.nonce); this.log.error(auth.nonce, response.nonce);
throw new Error('[MT] resPQ nonce mismatch'); throw new Error('[MT] resPQ nonce mismatch');
} }
@ -167,7 +171,7 @@ export class Authorizer {
auth.pq = response.pq; auth.pq = response.pq;
auth.fingerprints = response.server_public_key_fingerprints; auth.fingerprints = response.server_public_key_fingerprints;
console.log(dT(), 'Got ResPQ', bytesToHex(auth.serverNonce), bytesToHex(auth.pq), auth.fingerprints); this.log('Got ResPQ', bytesToHex(auth.serverNonce), bytesToHex(auth.pq), auth.fingerprints);
let publicKey = await rsaKeysManager.select(auth.fingerprints); let publicKey = await rsaKeysManager.select(auth.fingerprints);
if(!publicKey) { if(!publicKey) {
@ -176,19 +180,19 @@ export class Authorizer {
auth.publicKey = publicKey; auth.publicKey = publicKey;
console.log(dT(), 'PQ factorization start', auth.pq); this.log('PQ factorization start', auth.pq);
try { try {
var pAndQ = await CryptoWorker.factorize(auth.pq); var pAndQ = await CryptoWorker.factorize(auth.pq);
} catch(error) { } catch(error) {
console.error('worker error factorize', error); this.log.error('worker error factorize', error);
throw error; throw error;
} }
auth.p = pAndQ[0]; auth.p = pAndQ[0];
auth.q = pAndQ[1]; auth.q = pAndQ[1];
console.log(dT(), 'PQ factorization done', pAndQ); this.log('PQ factorization done', pAndQ);
/* let p = new Uint32Array(new Uint8Array(auth.p).buffer)[0]; /* let p = new Uint32Array(new Uint8Array(auth.p).buffer)[0];
let q = new Uint32Array(new Uint8Array(auth.q).buffer)[0]; let q = new Uint32Array(new Uint8Array(auth.q).buffer)[0];
console.log(dT(), 'PQ factorization done', pAndQ, p.toString(16), q.toString(16)); */ console.log(dT(), 'PQ factorization done', pAndQ, p.toString(16), q.toString(16)); */
@ -253,18 +257,18 @@ export class Authorizer {
let requestBytes = request.getBytes(true); let requestBytes = request.getBytes(true);
console.log(dT(), 'Send req_DH_params', req_DH_params/* , requestBytes.hex */); this.log('Send req_DH_params', req_DH_params/* , requestBytes.hex */);
try { try {
var deserializer = await this.mtpSendPlainRequest(auth.dcID, requestBytes); var deserializer = await this.mtpSendPlainRequest(auth.dcID, requestBytes);
} catch(error) { } catch(error) {
console.log(dT(), 'Send req_DH_params FAIL!', error); this.log('Send req_DH_params FAIL!', error);
throw error; throw error;
} }
var response = deserializer.fetchObject('Server_DH_Params', 'RESPONSE'); var response = deserializer.fetchObject('Server_DH_Params', 'RESPONSE');
console.log(dT(), 'Sent req_DH_params, response:', response); this.log('Sent req_DH_params, response:', response);
if(response._ != 'server_DH_params_fail' && response._ != 'server_DH_params_ok') { if(response._ != 'server_DH_params_fail' && response._ != 'server_DH_params_ok') {
throw new Error('[MT] Server_DH_Params response invalid: ' + response._); throw new Error('[MT] Server_DH_Params response invalid: ' + response._);
@ -292,7 +296,7 @@ export class Authorizer {
try { try {
await this.mtpDecryptServerDhDataAnswer(auth, response.encrypted_answer); await this.mtpDecryptServerDhDataAnswer(auth, response.encrypted_answer);
} catch(e) { } catch(e) {
console.error(dT(), 'mtpDecryptServerDhDataAnswer FAILED!', e); this.log.error('mtpDecryptServerDhDataAnswer FAILED!', e);
throw e; throw e;
} }
@ -342,7 +346,7 @@ export class Authorizer {
throw new Error('[MT] server_DH_inner_data serverNonce mismatch'); throw new Error('[MT] server_DH_inner_data serverNonce mismatch');
} }
console.log(dT(), 'Done decrypting answer'); this.log('Done decrypting answer');
auth.g = response.g; auth.g = response.g;
auth.dhPrime = response.dh_prime; auth.dhPrime = response.dh_prime;
auth.gA = response.g_a; auth.gA = response.g_a;
@ -362,13 +366,13 @@ export class Authorizer {
} }
public mtpVerifyDhParams(g: number, dhPrime: any, gA: any) { public mtpVerifyDhParams(g: number, dhPrime: any, gA: any) {
console.log(dT(), 'Verifying DH params'); this.log('Verifying DH params');
var dhPrimeHex = bytesToHex(dhPrime); var dhPrimeHex = bytesToHex(dhPrime);
if(g != 3 || dhPrimeHex !== 'c71caeb9c6b1c9048e6c522f70f13f73980d40238e3e21c14934d037563d930f48198a0aa7c14058229493d22530f4dbfa336f6e0ac925139543aed44cce7c3720fd51f69458705ac68cd4fe6b6b13abdc9746512969328454f18faf8c595f642477fe96bb2a941d5bcd1d4ac8cc49880708fa9b378e3c4f3a9060bee67cf9a4a4a695811051907e162753b56b0f6b410dba74d8a84b2a14b3144e0ef1284754fd17ed950d5965b4b9dd46582db1178d169c6bc465b0d6ff9ca3928fef5b9ae4e418fc15e83ebea0f87fa9ff5eed70050ded2849f47bf959d956850ce929851f0d8115f635b105ee2e4e15d04b2454bf6f4fadf034b10403119cd8e3b92fcc5b') { if(g != 3 || dhPrimeHex !== 'c71caeb9c6b1c9048e6c522f70f13f73980d40238e3e21c14934d037563d930f48198a0aa7c14058229493d22530f4dbfa336f6e0ac925139543aed44cce7c3720fd51f69458705ac68cd4fe6b6b13abdc9746512969328454f18faf8c595f642477fe96bb2a941d5bcd1d4ac8cc49880708fa9b378e3c4f3a9060bee67cf9a4a4a695811051907e162753b56b0f6b410dba74d8a84b2a14b3144e0ef1284754fd17ed950d5965b4b9dd46582db1178d169c6bc465b0d6ff9ca3928fef5b9ae4e418fc15e83ebea0f87fa9ff5eed70050ded2849f47bf959d956850ce929851f0d8115f635b105ee2e4e15d04b2454bf6f4fadf034b10403119cd8e3b92fcc5b') {
// The verified value is from https://core.telegram.org/mtproto/security_guidelines // The verified value is from https://core.telegram.org/mtproto/security_guidelines
throw new Error('[MT] DH params are not verified: unknown dhPrime'); throw new Error('[MT] DH params are not verified: unknown dhPrime');
} }
console.log(dT(), 'dhPrime cmp OK'); this.log('dhPrime cmp OK');
var gABigInt = new BigInteger(bytesToHex(gA), 16); var gABigInt = new BigInteger(bytesToHex(gA), 16);
var dhPrimeBigInt = new BigInteger(dhPrimeHex, 16); var dhPrimeBigInt = new BigInteger(dhPrimeHex, 16);
@ -380,7 +384,7 @@ export class Authorizer {
if(gABigInt.compareTo(dhPrimeBigInt.subtract(BigInteger.ONE)) >= 0) { if(gABigInt.compareTo(dhPrimeBigInt.subtract(BigInteger.ONE)) >= 0) {
throw new Error('[MT] DH params are not verified: gA >= dhPrime - 1'); throw new Error('[MT] DH params are not verified: gA >= dhPrime - 1');
} }
console.log(dT(), '1 < gA < dhPrime-1 OK'); this.log('1 < gA < dhPrime-1 OK');
var two = new BigInteger(/* null */''); var two = new BigInteger(/* null */'');
@ -393,7 +397,7 @@ export class Authorizer {
if(gABigInt.compareTo(dhPrimeBigInt.subtract(twoPow)) >= 0) { if(gABigInt.compareTo(dhPrimeBigInt.subtract(twoPow)) >= 0) {
throw new Error('[MT] DH params are not verified: gA > dhPrime - 2^{2048-64}'); throw new Error('[MT] DH params are not verified: gA > dhPrime - 2^{2048-64}');
} }
console.log(dT(), '2^{2048-64} < gA < dhPrime-2^{2048-64} OK'); this.log('2^{2048-64} < gA < dhPrime-2^{2048-64} OK');
return true; return true;
} }
@ -433,7 +437,7 @@ export class Authorizer {
encrypted_data: encryptedData encrypted_data: encryptedData
}); });
console.log(dT(), 'Send set_client_DH_params'); this.log('Send set_client_DH_params');
try { try {
var deserializer = await this.mtpSendPlainRequest(auth.dcID, request.getBytes(true)); var deserializer = await this.mtpSendPlainRequest(auth.dcID, request.getBytes(true));
@ -466,43 +470,43 @@ export class Authorizer {
authKeyAux = authKeyHash.slice(0, 8), authKeyAux = authKeyHash.slice(0, 8),
authKeyID = authKeyHash.slice(-8); authKeyID = authKeyHash.slice(-8);
console.log(dT(), 'Got Set_client_DH_params_answer', response._, authKey); this.log('Got Set_client_DH_params_answer', response._, authKey);
switch(response._) { switch(response._) {
case 'dh_gen_ok': case 'dh_gen_ok':
var newNonceHash1 = (await CryptoWorker.sha1Hash(auth.newNonce.concat([1], authKeyAux))).slice(-16); var newNonceHash1 = (await CryptoWorker.sha1Hash(auth.newNonce.concat([1], authKeyAux))).slice(-16);
//var newNonceHash1 = sha1BytesSync(auth.newNonce.concat([1], authKeyAux)).slice(-16); //var newNonceHash1 = sha1BytesSync(auth.newNonce.concat([1], authKeyAux)).slice(-16);
if(!bytesCmp(newNonceHash1, response.new_nonce_hash1)) { if(!bytesCmp(newNonceHash1, response.new_nonce_hash1)) {
throw new Error('[MT] Set_client_DH_params_answer new_nonce_hash1 mismatch'); throw new Error('[MT] Set_client_DH_params_answer new_nonce_hash1 mismatch');
} }
var serverSalt = bytesXor(auth.newNonce.slice(0, 8), auth.serverNonce.slice(0, 8)); var serverSalt = bytesXor(auth.newNonce.slice(0, 8), auth.serverNonce.slice(0, 8));
console.log('Auth successfull!', authKeyID, authKey, serverSalt); this.log('Auth successfull!', authKeyID, authKey, serverSalt);
auth.authKeyID = authKeyID; auth.authKeyID = authKeyID;
auth.authKey = authKey; auth.authKey = authKey;
auth.serverSalt = serverSalt; auth.serverSalt = serverSalt;
return auth; return auth;
break; break;
case 'dh_gen_retry': case 'dh_gen_retry':
//var newNonceHash2 = sha1BytesSync(auth.newNonce.concat([2], authKeyAux)).slice(-16); //var newNonceHash2 = sha1BytesSync(auth.newNonce.concat([2], authKeyAux)).slice(-16);
var newNonceHash2 = (await CryptoWorker.sha1Hash(auth.newNonce.concat([2], authKeyAux))).slice(-16); var newNonceHash2 = (await CryptoWorker.sha1Hash(auth.newNonce.concat([2], authKeyAux))).slice(-16);
if(!bytesCmp(newNonceHash2, response.new_nonce_hash2)) { if(!bytesCmp(newNonceHash2, response.new_nonce_hash2)) {
throw new Error('[MT] Set_client_DH_params_answer new_nonce_hash2 mismatch'); throw new Error('[MT] Set_client_DH_params_answer new_nonce_hash2 mismatch');
} }
return this.mtpSendSetClientDhParams(auth); return this.mtpSendSetClientDhParams(auth);
case 'dh_gen_fail': case 'dh_gen_fail':
//var newNonceHash3 = sha1BytesSync(auth.newNonce.concat([3], authKeyAux)).slice(-16); //var newNonceHash3 = sha1BytesSync(auth.newNonce.concat([3], authKeyAux)).slice(-16);
var newNonceHash3 = (await CryptoWorker.sha1Hash(auth.newNonce.concat([3], authKeyAux))).slice(-16); var newNonceHash3 = (await CryptoWorker.sha1Hash(auth.newNonce.concat([3], authKeyAux))).slice(-16);
if(!bytesCmp(newNonceHash3, response.new_nonce_hash3)) { if(!bytesCmp(newNonceHash3, response.new_nonce_hash3)) {
throw new Error('[MT] Set_client_DH_params_answer new_nonce_hash3 mismatch'); throw new Error('[MT] Set_client_DH_params_answer new_nonce_hash3 mismatch');
} }
throw new Error('[MT] Set_client_DH_params_answer fail'); throw new Error('[MT] Set_client_DH_params_answer fail');
} }
} }

4
src/lib/mtproto/mtproto_config.ts

@ -9,7 +9,7 @@ export const App = {
export const Modes = { export const Modes = {
test: location.search.indexOf('test=1') > 0/* || true */, test: location.search.indexOf('test=1') > 0/* || true */,
debug: location.search.indexOf('debug=1') > 0, debug: location.search.indexOf('debug=1') > 0,
http: location.search.indexOf('http=1') > 0, http: false, //location.search.indexOf('http=1') > 0,
ssl: location.search.indexOf('ssl=1') > 0 || location.protocol == 'https:' && location.search.indexOf('ssl=0') == -1, ssl: true, // location.search.indexOf('ssl=1') > 0 || location.protocol == 'https:' && location.search.indexOf('ssl=0') == -1,
multipleConnections: true multipleConnections: true
}; };

2
src/lib/mtproto/mtprotoworker.ts

@ -121,7 +121,7 @@ class ApiManagerProxy extends CryptoWorkerMethods {
stopTime?: number, stopTime?: number,
rawError?: any rawError?: any
} = {}): Promise<any> { } = {}): Promise<any> {
console.log('will invokeApi:', method, params, options); //console.log('will invokeApi:', method, params, options);
return this.performTaskWorker('invokeApi', method, params, options); return this.performTaskWorker('invokeApi', method, params, options);
} }

73
src/lib/mtproto/networker.ts

@ -12,7 +12,7 @@ import NetworkerFactory from './networkerFactory';
import dcConfigurator from './dcConfigurator'; import dcConfigurator from './dcConfigurator';
import Socket from './transports/websocket'; import Socket from './transports/websocket';
import HTTP from './transports/http'; import HTTP from './transports/http';
import { logger } from '../polyfill'; import { logger, LogLevels } from '../polyfill';
import { Modes, App } from './mtproto_config'; import { Modes, App } from './mtproto_config';
import { InvokeApiOptions } from '../../types'; import { InvokeApiOptions } from '../../types';
@ -79,8 +79,6 @@ class MTPNetworker {
private onOnlineCb = this.checkConnection.bind(this); private onOnlineCb = this.checkConnection.bind(this);
private debug = false;
private lastResendReq: { private lastResendReq: {
req_msg_id: string, req_msg_id: string,
resend_msg_ids: Array<string> resend_msg_ids: Array<string>
@ -99,6 +97,11 @@ class MTPNetworker {
this.log = logger('NET-' + dcID + (this.upload ? '-U' : '')); this.log = logger('NET-' + dcID + (this.upload ? '-U' : ''));
this.log('constructor'/* , this.authKey, this.authKeyID, this.serverSalt */); this.log('constructor'/* , this.authKey, this.authKeyID, this.serverSalt */);
/* // Test resend after bad_server_salt
if(this.dcID == 1 && this.upload) {
this.serverSalt[0] = 0;
} */
this.updateSession(); this.updateSession();
// if(!NetworkerFactory.offlineInited) { // if(!NetworkerFactory.offlineInited) {
@ -268,7 +271,7 @@ class MTPNetworker {
if(Modes.debug/* || true */) { if(Modes.debug/* || true */) {
this.log('Api call', method, message, params, options); this.log('Api call', method, message, params, options);
} else { } else {
//////this.log('Api call', method); this.log('Api call', method, params, options);
} }
return this.pushMessage(message, options); return this.pushMessage(message, options);
@ -344,28 +347,28 @@ class MTPNetworker {
} }
public pushResend(messageID: string, delay = 0) { public pushResend(messageID: string, delay = 0) {
var value = delay ? Date.now() + delay : 0; const value = delay ? Date.now() + delay : 0;
var sentMessage = this.sentMessages[messageID]; const sentMessage = this.sentMessages[messageID];
if(sentMessage.container) { if(sentMessage.container) {
for(var i = 0; i < sentMessage.inner.length; i++) { for(let i = 0, length = sentMessage.inner.length; i < length; i++) {
this.pendingMessages[sentMessage.inner[i]] = value; this.pendingMessages[sentMessage.inner[i]] = value;
} }
} else { } else {
this.pendingMessages[messageID] = value; this.pendingMessages[messageID] = value;
} }
// this.log('Resend due', messageID, this.pendingMessages) this.log('Resend due', messageID, this.pendingMessages);
this.scheduleRequest(delay); this.scheduleRequest(delay);
} }
public async getMsgKey(dataWithPadding: ArrayBuffer, isOut: boolean) { public async getMsgKey(dataWithPadding: ArrayBuffer, isOut: boolean) {
var authKey = this.authKeyUint8; const authKey = this.authKeyUint8;
var x = isOut ? 0 : 8 const x = isOut ? 0 : 8
var msgKeyLargePlain = bufferConcat(authKey.subarray(88 + x, 88 + x + 32), dataWithPadding); const msgKeyLargePlain = bufferConcat(authKey.subarray(88 + x, 88 + x + 32), dataWithPadding);
let msgKeyLarge = await CryptoWorker.sha256Hash(msgKeyLargePlain); const msgKeyLarge = await CryptoWorker.sha256Hash(msgKeyLargePlain);
var msgKey = new Uint8Array(msgKeyLarge).subarray(8, 24); const msgKey = new Uint8Array(msgKeyLarge).subarray(8, 24);
return msgKey; return msgKey;
}; };
@ -513,7 +516,7 @@ class MTPNetworker {
/* for(var i = 0; i < this.pendingResends.length; i++) { /* for(var i = 0; i < this.pendingResends.length; i++) {
resendMsgIDs.push(this.pendingResends[i]); resendMsgIDs.push(this.pendingResends[i]);
} */ } */
// this.log('resendReq messages', resendMsgIDs) this.log('resendReq messages', resendMsgIDs);
this.wrapMtpMessage({ this.wrapMtpMessage({
_: 'msg_resend_req', _: 'msg_resend_req',
msg_ids: resendMsgIDs msg_ids: resendMsgIDs
@ -729,7 +732,7 @@ class MTPNetworker {
public sendEncryptedRequest(message: any, options: any = {}) { public sendEncryptedRequest(message: any, options: any = {}) {
var self = this; var self = this;
this.debug && this.log('Send encrypted', message, options, this.authKeyID); this.log.debug('Send encrypted', message, options, this.authKeyID);
// console.trace() // console.trace()
var data = new TLSerialization({ var data = new TLSerialization({
startMaxLength: message.body.length + 2048 startMaxLength: message.body.length + 2048
@ -740,7 +743,7 @@ class MTPNetworker {
data.storeLong(message.msg_id, 'message_id'); data.storeLong(message.msg_id, 'message_id');
data.storeInt(message.seq_no, 'seq_no'); data.storeInt(message.seq_no, 'seq_no');
data.storeInt(message.body.length, 'message_data_length'); data.storeInt(message.body.length, 'message_data_length');
data.storeRawBytes(message.body, 'message_data'); data.storeRawBytes(message.body, 'message_data');
@ -756,7 +759,7 @@ class MTPNetworker {
// this.log('auth_key_id', bytesToHex(self.authKeyID)) // this.log('auth_key_id', bytesToHex(self.authKeyID))
return this.getEncryptedMessage(dataWithPadding).then((encryptedResult) => { return this.getEncryptedMessage(dataWithPadding).then((encryptedResult) => {
this.debug && this.log('Got encrypted out message', encryptedResult); this.log.debug('Got encrypted out message', encryptedResult);
let request = new TLSerialization({ let request = new TLSerialization({
startMaxLength: encryptedResult.bytes.byteLength + 256 startMaxLength: encryptedResult.bytes.byteLength + 256
@ -796,7 +799,7 @@ class MTPNetworker {
} }
public parseResponse(responseBuffer: Uint8Array) { public parseResponse(responseBuffer: Uint8Array) {
this.debug && this.log('Start parsing response'/* , responseBuffer */); this.log.debug('Start parsing response'/* , responseBuffer */);
let self = this; let self = this;
let deserializer = new TLDeserialization(responseBuffer); let deserializer = new TLDeserialization(responseBuffer);
@ -1016,6 +1019,18 @@ class MTPNetworker {
}; };
} }
/**
* только для сокета, возможно это будет неправильно работать, но в тесте сработало правильно
*/
public resend() {
for(let id in this.sentMessages) {
const msg = this.sentMessages[id];
if(msg.body) {
this.pushResend(id);
}
}
}
public processMessage(message: any, messageID: string, sessionID: Uint8Array | number[]) { public processMessage(message: any, messageID: string, sessionID: Uint8Array | number[]) {
var msgidInt = parseInt(messageID/* .toString(10) */.substr(0, -10), 10); var msgidInt = parseInt(messageID/* .toString(10) */.substr(0, -10), 10);
if(msgidInt % 2) { if(msgidInt % 2) {
@ -1023,7 +1038,7 @@ class MTPNetworker {
return; return;
} }
this.debug && this.log('process message', message, messageID, sessionID); this.log.debug('process message', message, messageID, sessionID);
switch(message._) { switch(message._) {
case 'msg_container': case 'msg_container':
@ -1040,10 +1055,20 @@ class MTPNetworker {
this.log(message.bad_msg_id, message.bad_msg_seqno); this.log(message.bad_msg_id, message.bad_msg_seqno);
throw new Error('[MT] Bad server salt for invalid message'); throw new Error('[MT] Bad server salt for invalid message');
} }
this.applyServerSalt(message.new_server_salt); this.applyServerSalt(message.new_server_salt);
this.pushResend(message.bad_msg_id); this.pushResend(message.bad_msg_id);
this.ackMessage(messageID); this.ackMessage(messageID);
/* // simulate disconnect
try {
this.log('networker state:', this);
// @ts-ignore
this.transport.ws.close(1000);
} catch(err) {
this.log.error('transport', this.transport, err);
} */
break; break;
case 'bad_msg_notification': case 'bad_msg_notification':
@ -1084,7 +1109,7 @@ class MTPNetworker {
case 'new_session_created': case 'new_session_created':
this.ackMessage(messageID); this.ackMessage(messageID);
this.log('new_session_created in my head'); this.log.debug('new_session_created', message);
//this.updateSession(); //this.updateSession();
this.processMessageAck(message.first_msg_id); this.processMessageAck(message.first_msg_id);
@ -1150,7 +1175,7 @@ class MTPNetworker {
} else { } else {
if(deferred) { if(deferred) {
if(Modes.debug) { if(Modes.debug) {
this.debug && this.log('Rpc response', message.result); this.log.debug('Rpc response', message.result);
} else { } else {
var dRes = message.result._; var dRes = message.result._;
if(!dRes) { if(!dRes) {
@ -1160,7 +1185,7 @@ class MTPNetworker {
dRes = message.result; dRes = message.result;
} }
} }
this.debug && this.log('Rpc response', dRes, sentMessage); this.log.debug('Rpc response', dRes, sentMessage);
} }
sentMessage.deferred.resolve(message.result); sentMessage.deferred.resolve(message.result);
@ -1179,7 +1204,7 @@ class MTPNetworker {
default: default:
this.ackMessage(messageID); this.ackMessage(messageID);
this.debug && this.log('Update', message); this.log.debug('Update', message);
if(NetworkerFactory.updatesProcessor !== null) { if(NetworkerFactory.updatesProcessor !== null) {
NetworkerFactory.updatesProcessor(message, true); NetworkerFactory.updatesProcessor(message, true);

4
src/lib/mtproto/rsaKeysManager.ts

@ -113,7 +113,7 @@ export class RSAKeysManager {
})).then(() => { })).then(() => {
this.prepared = true; this.prepared = true;
console.log('[MT] Prepared keys'); //console.log('[MT] Prepared keys');
this.preparePromise = null; this.preparePromise = null;
}); });
} }
@ -130,7 +130,7 @@ export class RSAKeysManager {
fingerprintHex = new Array(16 - fingerprintHex.length).fill('0').join('') + fingerprintHex; fingerprintHex = new Array(16 - fingerprintHex.length).fill('0').join('') + fingerprintHex;
} }
console.log(fingerprintHex, this.publicKeysParsed); //console.log(fingerprintHex, this.publicKeysParsed);
if(foundKey = this.publicKeysParsed[fingerprintHex]) { if(foundKey = this.publicKeysParsed[fingerprintHex]) {
return Object.assign({ return Object.assign({
fingerprint: fingerprints[i] fingerprint: fingerprints[i]

77
src/lib/mtproto/transports/websocket.ts

@ -5,7 +5,7 @@ import {CTR} from '@cryptography/aes';
//import abridgetPacketCodec from './abridged'; //import abridgetPacketCodec from './abridged';
import intermediatePacketCodec from './intermediate'; import intermediatePacketCodec from './intermediate';
import {MTPNetworker} from '../networker'; import {MTPNetworker} from '../networker';
import { logger } from '../../polyfill'; import { logger, LogLevels } from '../../polyfill';
import { bytesFromWordss } from '../../bin_utils'; import { bytesFromWordss } from '../../bin_utils';
import { Codec } from './codec'; import { Codec } from './codec';
@ -108,9 +108,14 @@ export class Obfuscation {
} }
export default class Socket extends MTTransport { export default class Socket extends MTTransport {
ws: WebSocket | undefined; ws: WebSocket;
pending: Array<{resolve?: any, reject?: any, body?: Uint8Array}> = []; pending: Array<Partial<{
resolve: any,
reject: any,
body: Uint8Array,
bodySent: boolean
}>> = [];
connected = false; connected = false;
@ -122,14 +127,12 @@ export default class Socket extends MTTransport {
log: ReturnType<typeof logger>; log: ReturnType<typeof logger>;
debug = false;
codec = intermediatePacketCodec; codec = intermediatePacketCodec;
constructor(dcID: number, url: string) { constructor(dcID: number, url: string) {
super(dcID, url); super(dcID, url);
this.log = logger(`WS-${dcID}`); this.log = logger(`WS-${dcID}`, LogLevels.log | LogLevels.error);
this.log('constructor'); this.log('constructor');
@ -142,7 +145,7 @@ export default class Socket extends MTTransport {
this.ws.removeEventListener('close', this.handleClose); this.ws.removeEventListener('close', this.handleClose);
this.ws.removeEventListener('message', this.handleMessage); this.ws.removeEventListener('message', this.handleMessage);
this.ws.close(1000); this.ws.close(1000);
} }
this.ws = new WebSocket(this.url, 'binary'); this.ws = new WebSocket(this.url, 'binary');
this.ws.binaryType = 'arraybuffer'; this.ws.binaryType = 'arraybuffer';
@ -161,20 +164,34 @@ export default class Socket extends MTTransport {
}; };
handleClose = (event: CloseEvent) => { handleClose = (event: CloseEvent) => {
this.log('closed', event); this.log('closed', event, this.pending);
this.connected = false; this.connected = false;
this.pending.length = 0; //this.pending.length = 0;
if(this.networker) { /* if(this.networker) {
this.networker.resend();
this.networker.cleanupSent(); this.networker.cleanupSent();
} } */
this.log('trying to reconnect...'); this.log('trying to reconnect...');
this.connect(); this.connect();
for(let pending of this.pending) {
if(pending.bodySent) {
pending.bodySent = false;
}
}
if(this.networker) {
this.ws.addEventListener('open', () => {
this.networker.resend();
this.networker.cleanupSent();
}, {once: true});
}
}; };
handleMessage = (event: MessageEvent) => { handleMessage = (event: MessageEvent) => {
this.debug && this.log('<-', 'handleMessage', event); this.log.debug('<-', 'handleMessage', event);
let data = this.obfuscation.decode(new Uint8Array(event.data)); let data = this.obfuscation.decode(new Uint8Array(event.data));
data = this.codec.readPacket(data); data = this.codec.readPacket(data);
@ -182,9 +199,9 @@ export default class Socket extends MTTransport {
if(this.networker) { // authenticated! if(this.networker) { // authenticated!
//this.pending = this.pending.filter(p => p.body); // clear pending //this.pending = this.pending.filter(p => p.body); // clear pending
this.debug && this.log('redirecting to networker'); this.log.debug('redirecting to networker');
return this.networker.parseResponse(data).then(response => { return this.networker.parseResponse(data).then(response => {
this.debug && this.log('redirecting to networker response:', response); this.log.debug('redirecting to networker response:', response);
this.networker.processMessage(response.response, response.messageID, response.sessionID); this.networker.processMessage(response.response, response.messageID, response.sessionID);
}); });
} }
@ -192,14 +209,14 @@ export default class Socket extends MTTransport {
//console.log('got hex:', data.hex); //console.log('got hex:', data.hex);
let pending = this.pending.shift(); let pending = this.pending.shift();
if(!pending) { if(!pending) {
return this.log('no pending for res:', data.hex); return this.log.debug('no pending for res:', data.hex);
} }
pending.resolve(data); pending.resolve(data);
}; };
send = (body: Uint8Array) => { send = (body: Uint8Array) => {
this.debug && this.log('-> body length to pending:', body.length); this.log.debug('-> body length to pending:', body.length);
if(this.networker) { if(this.networker) {
this.pending.push({body}); this.pending.push({body});
@ -221,26 +238,28 @@ export default class Socket extends MTTransport {
return; return;
} }
let length = this.pending.length; //this.log.error('Pending length:', this.pending.length);
const length = this.pending.length;
for(let i = length - 1; i >= 0; --i) { for(let i = length - 1; i >= 0; --i) {
let pending = this.pending[i]; const pending = this.pending[i];
let {body} = pending; const {body, bodySent} = pending;
if(body) { if(body && !bodySent) {
let toEncode = this.codec.encodePacket(body); const toEncode = this.codec.encodePacket(body);
//console.log('send before obf:', /* body.hex, nonce.hex, */ toEncode.hex);
let enc = this.obfuscation.encode(toEncode);
//console.log('send after obf:', enc.hex);
this.debug && this.log('-> body length to send:', enc.length); //this.log('send before obf:', /* body.hex, nonce.hex, */ toEncode.hex);
const enc = this.obfuscation.encode(toEncode);
//this.log('send after obf:', enc.hex);
this.log.debug('-> body length to send:', enc.length);
this.ws.send(enc); this.ws.send(enc);
if(!pending.resolve) { // remove if no response needed if(!pending.resolve) { // remove if no response needed
this.pending.splice(i, 1); this.pending.splice(i, 1);
} else {
pending.bodySent = true;
} }
delete pending.body; //delete pending.body;
} }
} }
} }

Loading…
Cancel
Save