mtprotoSender.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. const MtProtoPlainSender = require("./mtprotoPlainSender").MtProtoPlainSender;
  2. const Helpers = require("../utils/Helpers");
  3. /**
  4. * MTProto Mobile Protocol sender (https://core.telegram.org/mtproto/description)
  5. */
  6. class MtProtoSender {
  7. constructor(transport, session) {
  8. this.transport = transport;
  9. this.session = session;
  10. this.needConfirmation = Array(); // Message IDs that need confirmation
  11. this.onUpdateHandlers = Array();
  12. }
  13. /**
  14. * Disconnects
  15. */
  16. disconnect() {
  17. this.setListenForUpdates(false);
  18. this.transport.close();
  19. }
  20. /**
  21. * Adds an update handler (a method with one argument, the received
  22. * TLObject) that is fired when there are updates available
  23. * @param handler {function}
  24. */
  25. addUpdateHandler(handler) {
  26. let firstHandler = Boolean(this.onUpdateHandlers.length);
  27. this.onUpdateHandlers.push(handler);
  28. // If this is the first added handler,
  29. // we must start receiving updates
  30. if (firstHandler) {
  31. this.setListenForUpdates(true);
  32. }
  33. }
  34. /**
  35. * Removes an update handler (a method with one argument, the received
  36. * TLObject) that is fired when there are updates available
  37. * @param handler {function}
  38. */
  39. removeUpdateHandler(handler) {
  40. let index = this.onUpdateHandlers.indexOf(handler);
  41. if (index !== -1) this.onUpdateHandlers.splice(index, 1);
  42. if (!Boolean(this.onUpdateHandlers.length)) {
  43. this.setListenForUpdates(false);
  44. }
  45. }
  46. /**
  47. *
  48. * @param confirmed {boolean}
  49. * @returns {number}
  50. */
  51. generateSequence(confirmed) {
  52. if (confirmed) {
  53. let result = this.session.sequence * 2 + 1;
  54. this.session.sequence += 1;
  55. return result;
  56. } else {
  57. return this.session.sequence * 2;
  58. }
  59. }
  60. /**
  61. * Sends the specified MTProtoRequest, previously sending any message
  62. * which needed confirmation. This also pauses the updates thread
  63. * @param request {MtProtoPlainSender}
  64. * @param resend
  65. */
  66. send(request, resend = false) {
  67. let buffer;
  68. //If any message needs confirmation send an AckRequest first
  69. if (Boolean(this.needConfirmation.length)) {
  70. let msgsAck = MsgsAck(this.needConfirmation);
  71. buffer = msgsAck.onSend();
  72. this.sendPacket(buffer, msgsAck);
  73. this.needConfirmation.length = 0;
  74. }
  75. //Finally send our packed request
  76. buffer = request.on_send();
  77. this.sendPacket(buffer, request);
  78. //And update the saved session
  79. this.session.save();
  80. }
  81. receive(request) {
  82. try {
  83. //Try until we get an update
  84. while (!request.confirmReceive()) {
  85. let {seq, body} = this.transport.receive();
  86. let {message, remoteMsgId, remoteSequence} = this.decodeMsg(body);
  87. this.processMsg(remoteMsgId, remoteSequence, message, request);
  88. }
  89. } catch (e) {
  90. }
  91. }
  92. // region Low level processing
  93. /**
  94. * Sends the given packet bytes with the additional
  95. * information of the original request.
  96. * @param packet
  97. * @param request
  98. */
  99. sendPacket(packet, request) {
  100. request.msgId = this.session.getNewMsgId();
  101. // First Calculate plainText to encrypt it
  102. let first = Buffer.alloc(8);
  103. let second = Buffer.alloc(8);
  104. let third = Buffer.alloc(8);
  105. let forth = Buffer.alloc(4);
  106. let fifth = Buffer.alloc(4);
  107. first.writeBigUInt64LE(this.session.salt, 0);
  108. second.writeBigUInt64LE(this.session.id, 0);
  109. third.writeBigUInt64LE(request.msgId, 0);
  110. forth.writeInt32LE(this.generateSequence(request.confirmed), 0);
  111. fifth.writeInt32LE(packet.length, 0);
  112. let plain = Buffer.concat([
  113. first,
  114. second,
  115. third,
  116. forth,
  117. fifth,
  118. packet
  119. ]);
  120. let msgKey = Helpers.calcMsgKey(plain);
  121. let {key, iv} = Helpers.calcKey(this.session.authKey.key, msgKey, true);
  122. let cipherText = AES.encryptIge(plain, key, iv);
  123. //And then finally send the encrypted packet
  124. first = Buffer.alloc(8);
  125. first.writeUInt32LE(this.session.authKey.keyId, 0);
  126. let cipher = Buffer.concat([
  127. first,
  128. msgKey,
  129. cipherText,
  130. ]);
  131. this.transport.send(cipher);
  132. }
  133. decodeMsg(body) {
  134. if (body.length < 8) {
  135. throw Error("Can't decode packet");
  136. }
  137. let offset = 8;
  138. let msgKey = body.readIntLE(offset, 16);
  139. offset += 16;
  140. let {key, iv} = Helpers.calcKey(this.session.authKey.key, msgKey, false);
  141. let plainText = AES.decryptIge(body.readIntLE(offset, body.length - offset), key, iv);
  142. offset = 0;
  143. let remoteSalt = plainText.readBigInt64LE(offset);
  144. offset += 8;
  145. let remoteSessionId = plainText.readBigInt64LE(offset);
  146. offset += 8;
  147. let remoteSequence = plainText.readBigInt64LE(offset);
  148. offset += 8;
  149. let remoteMsgId = plainText.readInt32LE(offset);
  150. offset += 4;
  151. let msgLen = plainText.readInt32LE(offset);
  152. offset += 4;
  153. let message = plainText.readIntLE(offset, msgLen);
  154. return {message, remoteMsgId, remoteSequence}
  155. }
  156. processMsg(msgId, sequence, reader, offset, request = undefined) {
  157. this.needConfirmation.push(msgId);
  158. let code = reader.readUInt32LE(offset);
  159. offset -= 4;
  160. // The following codes are "parsed manually"
  161. if (code === 0xf35c6d01) { //rpc_result, (response of an RPC call, i.e., we sent a request)
  162. return this.handleRpcResult(msgId, sequence, reader, request);
  163. }
  164. if (code === 0x73f1f8dc) { //msg_container
  165. return this.handlerContainer(msgId, sequence, reader, request);
  166. }
  167. if (code === 0x3072cfa1) { //gzip_packed
  168. return this.handlerGzipPacked(msgId, sequence, reader, request);
  169. }
  170. if (code === 0xedab447b) { //bad_server_salt
  171. return this.handleBadServerSalt(msgId, sequence, reader, request);
  172. }
  173. if (code === 0xa7eff811) { //bad_msg_notification
  174. return this.handleBadMsgNotification(msgId, sequence, reader);
  175. }
  176. /**
  177. * If the code is not parsed manually, then it was parsed by the code generator!
  178. * In this case, we will simply treat the incoming TLObject as an Update,
  179. * if we can first find a matching TLObject
  180. */
  181. if (tlobjects.contains(code)) {
  182. return this.handleUpdate(msgId, sequence, reader);
  183. }
  184. console.log("Unknown message");
  185. return false;
  186. }
  187. // region Message handling
  188. handleUpdate(msgId, sequence, reader) {
  189. let tlobject = Helpers.tgReadObject(reader);
  190. for (let handler of this.onUpdateHandlers) {
  191. handler(tlobject);
  192. }
  193. return Float32Array
  194. }
  195. handleContainer(msgId, sequence, reader, offset, request) {
  196. let code = reader.readUInt32LE(offset);
  197. offset += 4;
  198. let size = reader.readInt32LE(offset);
  199. offset += 4;
  200. for (let i = 0; i < size; i++) {
  201. let innerMsgId = reader.readBigUInt64LE(offset);
  202. offset += 8;
  203. let innerSequence = reader.readBigInt64LE(offset);
  204. offset += 8;
  205. let innerLength = reader.readInt32LE(offset);
  206. offset += 4;
  207. if (!this.processMsg(innerMsgId, sequence, reader, request)) {
  208. offset += innerLength;
  209. }
  210. }
  211. return false;
  212. }
  213. handleBadServerSalt(msgId, sequence, reader, offset, request) {
  214. let code = reader.readUInt32LE(offset);
  215. offset += 4;
  216. let badMsgId = reader.readUInt32LE(offset);
  217. offset += 4;
  218. let badMsgSeqNo = reader.readInt32LE(offset);
  219. offset += 4;
  220. let errorCode = reader.readInt32LE(offset);
  221. offset += 4;
  222. let newSalt = reader.readUInt32LE(offset);
  223. offset += 4;
  224. this.session.salt = newSalt;
  225. if (!request) {
  226. throw Error("Tried to handle a bad server salt with no request specified");
  227. }
  228. //Resend
  229. this.send(request, true);
  230. return true;
  231. }
  232. handleBadMsgNotification(msgId, sequence, reader, offset) {
  233. let code = reader.readUInt32LE(offset);
  234. offset += 4;
  235. let requestId = reader.readUInt32LE(offset);
  236. offset += 4;
  237. let requestSequence = reader.readInt32LE(offset);
  238. offset += 4;
  239. let errorCode = reader.readInt32LE(offset);
  240. return BadMessageError(errorCode);
  241. }
  242. handleRpcResult(msgId, sequence, reader, offset, request) {
  243. if (!request) {
  244. throw Error("RPC results should only happen after a request was sent");
  245. }
  246. let code = reader.readUInt32LE(offset);
  247. offset += 4;
  248. let requestId = reader.readUInt32LE(offset);
  249. offset += 4;
  250. let innerCode = reader.readUInt32LE(offset);
  251. offset += 4;
  252. if (requestId === request.msgId) {
  253. request.confirmReceived = true;
  254. }
  255. if (innerCode === 0x2144ca19) { // RPC Error
  256. // TODO add rpc logic
  257. throw Error("error");
  258. } else {
  259. // TODO
  260. }
  261. }
  262. handleGzipPacked(msgId, sequence, reader, offset, request) {
  263. // TODO
  264. }
  265. setListenForUpdates(enabled) {
  266. if (enabled) {
  267. console.log("Enabled updates");
  268. } else {
  269. console.log("Disabled updates");
  270. }
  271. }
  272. updatesListenMethod() {
  273. while (true) {
  274. let {seq, body} = this.transport.receive();
  275. let {message, remoteMsgId, remoteSequence} = this.decodeMsg(body);
  276. this.processMsg(remoteMsgId, remoteSequence, message);
  277. }
  278. }
  279. }