PageRenderTime 58ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/rtmfp.js

http://github.com/OpenRTMFP/ArcusNode
JavaScript | 1217 lines | 757 code | 201 blank | 259 comment | 150 complexity | 31e2d52d4873baf78c1dc0b237f04e05 MD5 | raw file
Possible License(s): GPL-3.0
  1. /**
  2. * RTMFP - The Protocol
  3. *
  4. * Copyright 2011 OpenRTMFP
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License received along this program for more
  15. * details (or else see http://www.gnu.org/licenses/).
  16. *
  17. * This file is a part of ArcusNode.
  18. */
  19. var net = require('net');
  20. var Packet = require('./packet.js');
  21. var AMF = require('./amf.js');
  22. var AMF0 = require('./amf0.js');
  23. var rtmfp = require('../build/Release/rtmfp.node');
  24. //Packet Markers
  25. var RTMFP_MARKER_HANDSHAKE = 0x0b,
  26. RTMFP_MARKER_MESSAGE_1 = 0x0d,
  27. RTMFP_MARKER_MESSAGE_2 = 0x8d,
  28. RTMFP_MARKER_MESSAGE_3 = 0x89,
  29. RTMFP_MARKER_MESSAGE_4 = 0x09,
  30. RTMFP_MARKER_RESPONSE_1 = 0x4e,
  31. RTMFP_MARKER_RESPONSE_2 = 0x4a,
  32. RTMFP_VALID_MARKERS = [0x8d, 0x8e, 0x8a, 0x0d, 0x0b, 0x89, 0x09, 0x49, 0x4e, 0x4a, 0x4d, 0x0e, 0x0a];
  33. RTMFP_VALID_MESSAGES = [0x10, 0x11, 0x30, 0x38, 0x51, 0x01, 0x41, 0x0c, 0x4c, 0x18, 0x71, 0x70, 0x78, 0x5e];
  34. RTMFP_MSG_EMPTY = 0x00;
  35. RTMFP_MSG_AMF_WITH_HANDLER = 0x14;
  36. RTMFP_MSG_AMF_METHOD = 0x11;
  37. RTMFP_MSG_AMF = 0x0f;
  38. RTMFP_MSG_AUDIO = 0x08;
  39. RTMFP_MSG_VIDEO = 0x09;
  40. RTMFP_MSG_RAW_DATA = 0x04;
  41. /**
  42. * RTMFP
  43. * TODO: handle connect and address message as command
  44. * TODO: the deltaNack in a command is an index for a command flow
  45. */
  46. var RTMFP = module.exports = function(){
  47. var _rtmfp = new rtmfp.RTMFP();
  48. var _epoch = (function(){
  49. var d = new Date();
  50. return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(), d.getUTCMilliseconds());
  51. })();
  52. /**
  53. * Read RTMFP Messages from a packet and return message objects in an array
  54. */
  55. this.readPacket = function(pkt){
  56. messages = [];
  57. pkt.pos(6);
  58. var marker = pkt.readInt8();
  59. if (RTMFP_VALID_MARKERS.indexOf(marker) == -1){
  60. throw new Error('Invalid packet marker ' + marker);
  61. }
  62. var time1 = pkt.readInt16();
  63. var time2 = 0;
  64. var message = null;
  65. //with echo time
  66. if ((marker | 0xF0) == 0xFD || marker == 0x4e || marker == 0x0e) {
  67. time2 = pkt.readInt16();
  68. }
  69. while(pkt.available() > 0 && RTMFP_VALID_MESSAGES.indexOf(pkt.peek()) != -1)
  70. {
  71. message = this.readMessage(pkt, message);
  72. if(message)
  73. {
  74. var now = _timeNow();
  75. message.receivedTime = now;
  76. message.sentTime = time1;
  77. message.echoTime = time2;
  78. if(time2 > 0)
  79. message.latency = now - time2;
  80. messages.push(message);
  81. }
  82. }
  83. return messages;
  84. };
  85. /**
  86. * Read a single message from a packet
  87. */
  88. this.readMessage = function(pkt, lastMessage){
  89. var type = pkt.readInt8();
  90. //Ensure packet bytes
  91. if(pkt.available() < 2)
  92. return null;
  93. var messageSize = pkt.readInt16();
  94. //Ensure packet bytes
  95. if(pkt.available() < messageSize){
  96. return null;
  97. }
  98. //Clip packet to size
  99. var clippedPkt = new Packet(pkt.readBytes(messageSize), messageSize);
  100. var message = null;
  101. switch(type)
  102. {
  103. //
  104. // FORWARD_REQUEST
  105. //
  106. case 0x71:
  107. message = { type: RTMFP.FORWARD_REQUEST };
  108. message.tag = clippedPkt.readBytes(clippedPkt.readInt8(), true);
  109. message.endpoints = [];
  110. //TODO: read IPv6 also
  111. while(clippedPkt.available() > 0 && clippedPkt.readInt8() == 1) {
  112. message.endpoints.push(this.readRawIpv4(clippedPkt));
  113. }
  114. break;
  115. //
  116. // Message: HANDSHAKE_REQUEST / RENDEZVOUZ_REQUEST
  117. //
  118. case 0x30:
  119. var msgLength;
  120. //Two times the message length
  121. clippedPkt.readU29();
  122. msgLength = clippedPkt.readU29();
  123. var handshakeType = clippedPkt.readInt8();
  124. //Rendevouz
  125. if(handshakeType == 0x0f)
  126. {
  127. if(clippedPkt.available() > 32){
  128. message = { type: RTMFP.RENDEZVOUZ_REQUEST };
  129. message.peerIds = [clippedPkt.readBytes(32, true)];
  130. //Read more than one peer id
  131. while(clippedPkt.available() > 36 && clippedPkt.peek() == 0x11){
  132. clippedPkt.skip(1);
  133. if(clippedPkt.readInt16() == 34 && clippedPkt.readInt16() == 11){
  134. message.peerIds.push(clippedPkt.readBytes(32, true));
  135. } else {
  136. throw new Error('Tried to read more than one peer id for rendezvouz but failed. ' + pkt.toString());
  137. }
  138. }
  139. }
  140. }
  141. else if(handshakeType == 0x0a)
  142. {
  143. if(clippedPkt.available() >= msgLength - 1){
  144. message = { type: RTMFP.HANDSHAKE_REQUEST };
  145. //URL connected to
  146. message.url = clippedPkt.readBytes(msgLength - 1).toString('ascii');
  147. }
  148. }
  149. if(message != null && clippedPkt.available() >= 16){
  150. message.tag = clippedPkt.readBytes(16, true);
  151. } else {
  152. message = null;
  153. }
  154. if(message == null){
  155. throw new Error('Tried to read malicious handshake packet: ' + pkt.toString());
  156. }
  157. break;
  158. //
  159. // HANDSHAKE_RESPONSE
  160. //
  161. case 0x70:
  162. message = { type: RTMFP.HANDSHAKE_RESPONSE };
  163. message.tag = clippedPkt.readBytes(clippedPkt.readInt8(), true);
  164. message.cookie = clippedPkt.readBytes(clippedPkt.readInt8());
  165. message.certificate = clippedPkt.readBytes(clippedPkt.size() - clippedPkt.pos());
  166. break;
  167. //
  168. // KEY_RESPONSE
  169. //
  170. case 0x78:
  171. message = { type: RTMFP.KEY_RESPONSE };
  172. message.connectionId = clippedPkt.readInt32();
  173. clippedPkt.skip(1);
  174. message.signature = clippedPkt.readBytes(clippedPkt.readInt8());
  175. message.publicKey = clippedPkt.readBytes(clippedPkt.size() - clippedPkt.pos() - 1);
  176. break;
  177. //
  178. // Message: KEY_REQUEST
  179. //
  180. case 0x38:
  181. message = { type: RTMFP.KEY_REQUEST };
  182. message.connectionId = clippedPkt.readInt32();
  183. var cookie_size = clippedPkt.readInt8();
  184. if(cookie_size != 64)
  185. {
  186. throw new Error('COOKIE SIZE != 64');
  187. }
  188. message.cookie = clippedPkt.readBytes(cookie_size);
  189. var keySize = clippedPkt.readU29();
  190. var pos = clippedPkt.pos();
  191. message.clientSignature = clippedPkt.readBytes(4);
  192. message.publicKey = clippedPkt.readBytes(keySize - 4);
  193. clippedPkt.pos(pos);
  194. var keyPlusSig = clippedPkt.readBytes(keySize);
  195. var certificate_size = clippedPkt.readInt8();
  196. if(certificate_size != 76)
  197. {
  198. throw new Error('handshake client certificate size exceeded!');
  199. }
  200. message.clientCertificate = clippedPkt.readBytes(certificate_size);
  201. //Compute the client peer id
  202. if(!keyPlusSig || keySize == 0){
  203. throw new Error('Cannot compute peer id without correct arguments');
  204. }
  205. message.peerId = _rtmfp.computePeerId(keyPlusSig, keySize);
  206. break;
  207. //
  208. // Message: KEEPALIVE_REQUEST
  209. //
  210. case 0x01:
  211. message = { type: RTMFP.KEEPALIVE_REQUEST };
  212. break;
  213. //
  214. // Message: KEEPALIVE_RESPONSE
  215. //
  216. case 0x41 :
  217. message = { type: RTMFP.KEEPALIVE_RESPONSE };
  218. break;
  219. //
  220. // Message: NET_CONNECTION_CLOSE
  221. //
  222. case 0x0c:
  223. case 0x4c:
  224. message = { type: RTMFP.NET_CONNECTION_CLOSE };
  225. break;
  226. //
  227. // Message: ACK and NACK
  228. //
  229. case 0x51:
  230. var flow = clippedPkt.readInt8();
  231. var ackMarker = clippedPkt.readInt8();
  232. if(ackMarker == 0xFF) //happens after response is resend many times...
  233. {
  234. ackMarker = clippedPkt.readInt8();
  235. }
  236. message = { type: (ackMarker == 0x7f) ? RTMFP.ACK : RTMFP.NOT_ACK };
  237. message.flow = flow;
  238. message.stage = clippedPkt.readInt8();
  239. break;
  240. //
  241. // Message: NC_FAILED_REQUEST
  242. // raises "NetConnection.Connect.Failed" on client
  243. //
  244. case 0x5e:
  245. message = { type: RTMFP.NC_FAILED_REQUEST };
  246. message.flow = clippedPkt.readInt8();
  247. break;
  248. //
  249. // Message: UNKNOWN
  250. //
  251. case 0x18:
  252. throw new Error('UNHANDLED MESSAGE TYPE 0x18');
  253. break;
  254. //
  255. // Message: FLOW (RPC || GROUP)
  256. // TODO: handle 91 2E F8 CE 3B 05 09 E2 B6 10 00 04 03 03 02 01
  257. // -> Read correct headers:
  258. // MESSAGE_HEADER 0x80
  259. // MESSAGE_WITH_AFTERPART 0x10
  260. // MESSAGE_WITH_BEFOREPART 0x20
  261. // MESSAGE_ABANDONMENT 0x02
  262. // MESSAGE_END 0x01
  263. //
  264. case 0x10 :
  265. case 0x11 :
  266. message = {};
  267. message.flag = clippedPkt.readInt8(); // 0x80 extended header, 0x00 non extended header
  268. //Sometimes 11 00 01 03 is appended to Group Join, don't know why...
  269. //TODO: should be fixed with correct header reading: MESSAGE_ABANDONMENT 0x02 && MESSAGE_END 0x01
  270. if(messageSize == 1 || clippedPkt.available() < 3) {
  271. return null;
  272. }
  273. if(type == 0x11 && lastMessage != null) {
  274. message.flow = lastMessage.flow;
  275. message.stage = lastMessage.stage + 1;
  276. message.delta = lastMessage.delta + 1;
  277. } else {
  278. message.flow = clippedPkt.readInt8();
  279. message.stage = clippedPkt.readInt8();
  280. message.delta = clippedPkt.readInt8();
  281. }
  282. //Read message header
  283. if(message.flag == 0x80 || message.flag == 0x83) {
  284. var headPartSize = 0;
  285. message.header = [];
  286. while((headPartSize = clippedPkt.readInt8()) != 0){
  287. message.header.push(clippedPkt.readBytes(headPartSize, true));
  288. }
  289. }
  290. //Flag 0x03 is connect failed answer/retry without header (when connection request was acknowledged)
  291. //Flag 0x83 is connect failed answer/retry WITH header (if connection request was NOT acknowledged)
  292. if(message.flow == 0x02 && (message.flag == 0x83 || message.flag == 0x03)){
  293. message.type = RTMFP.MESSAGE_RESEND;
  294. return message;
  295. }
  296. //Handle Flow 0x02 (Method)
  297. if(message.flow == 0x02)
  298. {
  299. //Read Message Type
  300. var messageType = clippedPkt.readInt8();
  301. switch(messageType){
  302. // 0x14
  303. case RTMFP_MSG_AMF_WITH_HANDLER:
  304. message.handler = clippedPkt.readInt32();
  305. message.commandName = AMF0.readString(clippedPkt);
  306. message.commandHandle = AMF0.readNumber(clippedPkt);
  307. break;
  308. case RTMFP_MSG_AMF_METHOD:
  309. message.handler = clippedPkt.readInt32();
  310. clippedPkt.skip(1); //0x00
  311. message.commandName = AMF0.readString(clippedPkt);
  312. message.commandHandle = AMF0.readNumber(clippedPkt);
  313. break;
  314. // Yet unhandled types
  315. case RTMFP_MSG_RAW_DATA:
  316. case RTMFP_MSG_EMPTY:
  317. case RTMFP_MSG_AMF:
  318. case RTMFP_MSG_AUDIO:
  319. case RTMFP_MSG_VIDEO:
  320. throw new Error('Unhandled message type ', messageType);
  321. break;
  322. //Exit with error on unrecognized message type
  323. default:
  324. throw new Error('Unrecognized message type ', messageType);
  325. }
  326. switch(message.commandName) {
  327. //Handle NetConnection
  328. case 'connect':
  329. message.type = RTMFP.NET_CONNECTION_REQUEST;
  330. //Read AMF Data
  331. message.commandData = AMF.readAMF0(clippedPkt);
  332. break;
  333. //Handle endpoints for NetConnection
  334. case 'setPeerInfo':
  335. message.type = RTMFP.NET_CONNECTION_ADDRESSES;
  336. clippedPkt.skip(1);
  337. //TODO: read AMF0 strings and handle as endpoints outside
  338. message.endpoints = [];
  339. while(clippedPkt.available() > 3 && clippedPkt.readInt8() == 0x02)
  340. {
  341. message.endpoints.push(this.readAddress(clippedPkt));
  342. }
  343. break;
  344. //read command object and return it with message
  345. default:
  346. //Read AMF Data
  347. message.type = RTMFP.COMMAND;
  348. message.commandData = AMF.readAMF0(clippedPkt);
  349. break;
  350. }
  351. }
  352. //NetGroup stage 1
  353. else if(message.flow > 0x02 && message.stage == 0x01)
  354. {
  355. message.type = RTMFP.NET_GROUP_JOIN;
  356. clippedPkt.skip(1); //0x01
  357. var gidLength = clippedPkt.readU29();
  358. message.groupId = clippedPkt.readBytes(gidLength, true);
  359. }
  360. //NetGroup stage 2
  361. else if(message.flow > 0x02 && message.stage == 0x02)
  362. {
  363. message.type = RTMFP.NET_GROUP_LEAVE;
  364. }
  365. break;
  366. default:
  367. throw new Error('Unhandled Message: ', pkt.toString());
  368. break;
  369. }
  370. return message;
  371. };
  372. /**
  373. * Writes a message to a packet
  374. * TODO: rename to writeMessage() ???
  375. */
  376. this.writePacket = function(pkt, message){
  377. pkt.pos(6);
  378. switch(message.type)
  379. {
  380. //
  381. // HANDSHAKE_RESPONSE
  382. //
  383. case RTMFP.HANDSHAKE_RESPONSE:
  384. pkt.writeInt8(RTMFP_MARKER_HANDSHAKE);
  385. pkt.writeInt16(_timeNow());
  386. pkt.writeInt8(0x70);
  387. pkt.writeInt16(16 + message.cookie.length + 77 + 2);
  388. pkt.writeInt8(16);
  389. pkt.writeBuffer(message.tag);
  390. pkt.writeInt8(message.cookie.length);
  391. pkt.writeBuffer(message.cookie);
  392. pkt.writeBytes([0x01,0x0A,0x41,0x0E])
  393. if(message.certificate.length != 64){
  394. throw new Error('Incorrect certificate for handshake response');
  395. }
  396. pkt.writeBuffer(message.certificate);
  397. pkt.writeBytes([0x02,0x15,0x02,0x02,0x15,0x05,0x02,0x15,0x0E])
  398. break;
  399. //
  400. // HANDSHAKE_REQUEST
  401. //
  402. case RTMFP.HANDSHAKE_REQUEST:
  403. pkt.writeInt8(RTMFP_MARKER_HANDSHAKE);
  404. pkt.writeInt16(_timeNow());
  405. pkt.writeInt8(0x30);
  406. pkt.writeInt16(message.tag.length + message.url.length + 3);
  407. //write url size
  408. pkt.writeU29(message.url.length + 2);
  409. pkt.writeU29(message.url.length + 1);
  410. pkt.writeInt8(0x0a);
  411. pkt.writeString(message.url);
  412. pkt.writeBuffer(message.tag);
  413. break;
  414. //
  415. // FORWARD_REQUEST
  416. //
  417. case RTMFP.FORWARD_REQUEST:
  418. pkt.writeInt8(RTMFP_MARKER_HANDSHAKE);
  419. pkt.writeInt16(_timeNow());
  420. pkt.writeInt8(0x71);
  421. var sizePos = pkt.pos();
  422. pkt.skip(2); //size placeholder
  423. pkt.writeInt8(16);
  424. if(!message.tag){
  425. throw new Error('Need a tag from handshake to write forward request');
  426. }
  427. pkt.writeBuffer(message.tag);
  428. for(var i = 0; i < message.endpoints.length; i++) {
  429. this.writeAddress(pkt, message.endpoints[i], false);
  430. }
  431. //write size finally
  432. writeSize(pkt, sizePos, pkt.size() - sizePos - 2);
  433. break;
  434. //
  435. // KEY_RESPONSE
  436. //
  437. case RTMFP.KEY_RESPONSE:
  438. pkt.writeInt8(RTMFP_MARKER_HANDSHAKE);
  439. pkt.writeInt16(_timeNow());
  440. pkt.writeInt8(0x78);
  441. var nonce = this.createServerNonce(message.publicKey);
  442. pkt.writeInt16(nonce.length + 7);
  443. pkt.writeInt32(message.connectionId);
  444. pkt.writeU29(nonce.length);
  445. pkt.writeBuffer(nonce);
  446. pkt.writeInt8(0x58);
  447. break;
  448. //
  449. // KEY_REQUEST
  450. //
  451. case RTMFP.KEY_REQUEST:
  452. pkt.writeInt8(RTMFP_MARKER_HANDSHAKE);
  453. pkt.writeInt16(_timeNow());
  454. pkt.writeInt8(0x38);
  455. var sizePos = pkt.pos();
  456. pkt.skip(2); //size placeholder
  457. pkt.writeInt32(message.connectionId);
  458. pkt.writeInt8(message.cookie.length);
  459. pkt.writeBuffer(message.cookie);
  460. //TODO: write real keysize and signature
  461. pkt.writeBytes([0x81, 0x04, 0x81, 0x02, 0x1d, 0x02]);
  462. pkt.writeBuffer(message.publicKey);
  463. var nonce = this.createClientNonce(message.certificate);
  464. pkt.writeU29(nonce.length);
  465. pkt.writeBuffer(nonce);
  466. pkt.writeInt8(0x58);
  467. //write size finally
  468. writeSize(pkt, sizePos, pkt.size() - sizePos - 2);
  469. break;
  470. //
  471. // NET_CONNECTION_REQUEST
  472. //
  473. case RTMFP.NET_CONNECTION_REQUEST:
  474. if(!message.echoTime){
  475. pkt.writeInt8(RTMFP_MARKER_MESSAGE_3);
  476. pkt.writeInt16(_timeNow());
  477. } else {
  478. pkt.writeInt8(RTMFP_MARKER_MESSAGE_2);
  479. pkt.writeInt16(_timeNow());
  480. pkt.writeInt16(message.echoTime);
  481. }
  482. pkt.writeInt8(0x10);
  483. var sizePos = pkt.pos();
  484. pkt.skip(2); //size placeholder
  485. pkt.writeInt8(0x80); //header flag
  486. pkt.writeBytes([0x02, 0x01, 0x01]); //hardcoded for testing flow, stage, command index
  487. pkt.writeBytes([0x05, 0x00, 0x54, 0x43, 0x04, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01]); //hardcoded header
  488. AMF0.writeString(pkt, 'connect'); //commandName
  489. AMF0.writeNumber(pkt, 1); //commandHandle
  490. AMF0.writeObject(pkt, {
  491. app: message.app, //rtmfp://127.0.0.1/test <- app/devkey
  492. objectEncoding: 3,
  493. swfUrl: undefined,
  494. pageUrl: undefined,
  495. tcUrl: message.url,
  496. flashVer: 'WIN 10,2,159,1',
  497. fpad: false,
  498. capabilities: 235,
  499. audioCodecs: 3191,
  500. videoCodecs: 252,
  501. videoFunction: 1
  502. });
  503. //write size finally
  504. writeSize(pkt, sizePos, pkt.size() - sizePos - 2);
  505. break;
  506. //
  507. // NC_SUCCESS, NC_REJECTED
  508. //
  509. case RTMFP.NC_REJECTED_RESPONSE:
  510. case RTMFP.NC_SUCCESS_RESPONSE:
  511. //Todo: check if response was sent multiple times and after
  512. //30 sec send response without echo time (message.sentTime and message.echoTime)
  513. //TODO: No echo time if latency > 30 sec
  514. pkt.writeInt8(RTMFP_MARKER_RESPONSE_1); //response with echo time
  515. pkt.writeInt16(_timeNow());
  516. pkt.writeInt16(message.sentTime);
  517. // Prepare response
  518. pkt.writeInt8(0x10);
  519. var sizePos = pkt.pos();
  520. pkt.skip(2); //size placeholder
  521. //Write header flag
  522. if(typeof message.header !== 'undefined' && Array.isArray(message.header)){
  523. pkt.writeInt8(0x80);
  524. } else if(typeof message.header === 'undefined'){
  525. pkt.writeInt8(0x00);
  526. } else {
  527. throw new Error('Message header needs to be an array with header values');
  528. }
  529. pkt.writeInt8(message.flow);
  530. pkt.writeInt8(message.stage);
  531. pkt.writeInt8(0x01); //TODO: stage of the last flow message that was sent
  532. //Write Header
  533. if(message.flag == 0x80 || message.flag == 0x83) {
  534. for(var hp = 0; hp < message.header.length; hp++){
  535. pkt.writeInt8(message.header[hp].length);
  536. pkt.writeBuffer(message.header[hp]);
  537. }
  538. //Close Header
  539. pkt.writeInt8(0x00);
  540. }
  541. //Write Message Type
  542. pkt.writeInt8(RTMFP_MSG_AMF_WITH_HANDLER);
  543. pkt.writeInt32(0);
  544. AMF0.writeString(pkt, '_result');
  545. AMF0.writeNumber(pkt, message.commandHandle);
  546. AMF0.writeNull(pkt);
  547. //Write rejected or success status object
  548. var responseObject = {
  549. objectEncoding: 3,
  550. level: 'status'
  551. };
  552. if(message.type == RTMFP.NC_REJECTED_RESPONSE){
  553. responseObject.description = message.description || 'Connection rejected';
  554. responseObject.code = 'NetConnection.Connect.Rejected';
  555. } else if(message.type == RTMFP.NC_SUCCESS_RESPONSE){
  556. responseObject.description = message.description || 'Connection succeeded';
  557. responseObject.code = 'NetConnection.Connect.Success';
  558. }
  559. AMF0.writeObject(pkt, responseObject);
  560. //write size finally
  561. writeSize(pkt, sizePos, pkt.size() - sizePos - 2);
  562. break;
  563. //
  564. // Response: COMMAND_RESULT
  565. // Response: COMMAND_ERROR
  566. //
  567. case RTMFP.COMMAND_RESULT:
  568. case RTMFP.COMMAND_ERROR:
  569. //TODO: No echo time if latency > 30 sec
  570. pkt.writeInt8(RTMFP_MARKER_RESPONSE_1); //response with echo time
  571. pkt.writeInt16(_timeNow());
  572. pkt.writeInt16(message.sentTime);
  573. // Prepare response
  574. pkt.writeInt8(0x10);
  575. var sizePos = pkt.pos();
  576. pkt.skip(2); //size placeholder
  577. pkt.writeInt8(message.flag);
  578. pkt.writeInt8(message.flow);
  579. pkt.writeInt8(message.stage);
  580. pkt.writeInt8(0x01); // 0x01 flag to ack previous command
  581. pkt.writeInt8(0x14);
  582. pkt.writeInt32(0x00);
  583. AMF0.writeString(pkt, (message.type === RTMFP.COMMAND_RESULT) ? '_result' : '_error');
  584. AMF0.writeNumber(pkt, message.commandHandle);
  585. //AMF0 NULL MARKER to close header
  586. AMF0.writeNull(pkt);
  587. if(message.type === RTMFP.COMMAND_RESULT) {
  588. //write response AMF
  589. if(typeof message.commandData !== 'undefined'){
  590. AMF0.writeValue(pkt, message.commandData);
  591. }
  592. } else if(message.type === RTMFP.COMMAND_ERROR) {
  593. var statusObject = {
  594. level: 'error',
  595. code: 'NetConnection.Call.Failed'
  596. };
  597. if(typeof message.statusDescription === 'string') {
  598. statusObject.description = message.statusDescription;
  599. }
  600. //write response AMF
  601. AMF0.writeObject(pkt, statusObject);
  602. }
  603. //write size finally
  604. writeSize(pkt, sizePos, pkt.size() - sizePos - 2);
  605. break;
  606. //
  607. // Response: NET_CONNECTION_ADDRESSES
  608. //
  609. case RTMFP.NET_CONNECTION_ADDRESSES:
  610. //TODO: No echo time if latency > 30 sec
  611. pkt.writeInt8(RTMFP_MARKER_RESPONSE_1); //response with echo time
  612. pkt.writeInt16(_timeNow());
  613. pkt.writeInt16(message.sentTime);
  614. if(message.stage == 0x02)
  615. {
  616. pkt.writeInt8(0x10);
  617. pkt.writeInt16(0x13);
  618. pkt.writeBytes([0x00, 0x02, 0x02, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29]);
  619. pkt.writeInt32(message.serverKeepalive);
  620. pkt.writeInt32(message.clientKeepalive);
  621. }
  622. break;
  623. //
  624. // Response: NET_CONNECTION_CLOSE
  625. //
  626. case RTMFP.NET_CONNECTION_CLOSE:
  627. //form close message
  628. pkt.writeInt8(RTMFP_MARKER_RESPONSE_2); //response without echo time
  629. pkt.writeInt16(_timeNow());
  630. pkt.writeInt8(0x0c);
  631. pkt.writeInt16(0);
  632. break;
  633. //
  634. // Response: NET_GROUP_JOIN
  635. //
  636. case RTMFP.NET_GROUP_JOIN:
  637. //TODO: No echo time if latency > 30 sec
  638. pkt.writeInt8(RTMFP_MARKER_RESPONSE_1); //response with echo time
  639. pkt.writeInt16(_timeNow());
  640. pkt.writeInt16(message.sentTime);
  641. this.writeAck(pkt, message.flow, message.stage, true);
  642. //If group exists we have peer ids to add
  643. if(message.peers && message.peers.length > 0)
  644. {
  645. pkt.writeInt8(0x10);
  646. var sizePos = pkt.pos();
  647. pkt.skip(2); //size placeholder
  648. //Write header flag
  649. if(typeof message.header !== 'undefined' && Array.isArray(message.header)){
  650. pkt.writeInt8(0x80);
  651. } else if(typeof message.header === 'undefined'){
  652. pkt.writeInt8(0x00);
  653. } else {
  654. throw new Error('Message header needs to be an array with header values');
  655. }
  656. pkt.writeInt8(message.flow);
  657. pkt.writeInt8(message.stage);
  658. pkt.writeInt8(message.delta);
  659. //Write Header
  660. if(message.flag == 0x80 || message.flag == 0x83) {
  661. for(var hp = 0; hp < message.header.length; hp++){
  662. pkt.writeInt8(message.header[hp].length);
  663. pkt.writeBuffer(message.header[hp]);
  664. }
  665. //Close Header
  666. pkt.writeInt8(0x00);
  667. }
  668. pkt.writeInt8(0x0b);
  669. pkt.writeBuffer(message.peers[0].peerId);
  670. //write size finally
  671. writeSize(pkt, sizePos, pkt.size() - sizePos - 2);
  672. //Append additional peer ids
  673. for(var i = 1; i < message.peers.length; i++)
  674. {
  675. pkt.writeInt8(0x11);
  676. pkt.writeInt16(0x22);
  677. //Header Flag
  678. pkt.writeInt8(0x00);
  679. pkt.writeInt8(0x0b);
  680. pkt.writeBuffer(message.peers[i].peerId);
  681. }
  682. }
  683. break;
  684. //
  685. // Response: NET_GROUP_LEAVE
  686. //
  687. case RTMFP.NET_GROUP_LEAVE:
  688. //TODO: No echo time if latency > 30 sec
  689. pkt.writeInt8(RTMFP_MARKER_RESPONSE_1); //response with echo time
  690. pkt.writeInt16(_timeNow());
  691. pkt.writeInt16(message.sentTime);
  692. this.writeAck(pkt, message.flow, message.stage, true);
  693. break;
  694. //
  695. // Response: RENDEZVOUZ
  696. //
  697. case RTMFP.RENDEZVOUZ_RESPONSE:
  698. pkt.writeInt8(RTMFP_MARKER_HANDSHAKE);
  699. pkt.writeInt16(_timeNow());
  700. pkt.writeInt8(0x71);
  701. var sizePos = pkt.pos();
  702. pkt.skip(2); //size placeholder
  703. pkt.writeInt8(message.tag.length);
  704. pkt.writeBuffer(message.tag);
  705. var publicFlag = true;
  706. for(var i = 0; i < message.endpoints.length; i++)
  707. {
  708. //TODO: get public/private flag from address itself
  709. this.writeAddress(pkt, message.endpoints[i], publicFlag);
  710. publicFlag = false;
  711. }
  712. //write size finally
  713. writeSize(pkt, sizePos, pkt.size() - sizePos - 2);
  714. break;
  715. //
  716. // Response: RENDEZVOUZ_NEWCOMER
  717. //
  718. case RTMFP.RENDEZVOUZ_NEWCOMER:
  719. //TODO: use a general function to write the correct time depending on echoTime
  720. pkt.writeInt8(RTMFP_MARKER_RESPONSE_1); //echo time
  721. pkt.writeInt16(_timeNow());
  722. pkt.writeInt16(message.echoTime);
  723. pkt.writeInt8(0x0f);
  724. var sizePos = pkt.pos();
  725. pkt.skip(2); //size placeholder
  726. //TODO: write real size 2x U29
  727. pkt.writeBytes([0x22, 0x21, 0x0f]);
  728. //Write Peer Id (of the peer we send the newcomer message to)
  729. if(!message.peerId || message.peerId.length != 32){
  730. throw new Error('Peer id for newcomer request mandatory and has to be 32 bytes.');
  731. }
  732. pkt.writeBuffer(message.peerId);
  733. //TODO: get public/private marker from address itself
  734. this.writeAddress(pkt, message.address, message.pp);
  735. pkt.writeBuffer(message.tag);
  736. //write size finally
  737. writeSize(pkt, sizePos, pkt.size() - sizePos - 2);
  738. break;
  739. //
  740. // Response: KEEPALIVE_REQUEST
  741. //
  742. case RTMFP.KEEPALIVE_REQUEST:
  743. break;
  744. //
  745. // Response: KEEPALIVE
  746. //
  747. case RTMFP.KEEPALIVE_RESPONSE:
  748. //TODO: No echo time if latency > 30 sec
  749. pkt.writeInt8(RTMFP_MARKER_RESPONSE_1); //response with echo time
  750. pkt.writeInt16(_timeNow());
  751. pkt.writeInt16(message.sentTime);
  752. pkt.writeInt8(0x41);
  753. pkt.writeInt16(0x0);
  754. break;
  755. //
  756. // Response: ACK
  757. //
  758. case RTMFP.ACK:
  759. pkt.writeInt8(RTMFP_MARKER_RESPONSE_1); //response with echo time
  760. pkt.writeInt16(_timeNow());
  761. pkt.writeInt16(message.sentTime);
  762. this.writeAck(pkt, message.flow, message.stage, true);
  763. break;
  764. //
  765. // Response: NACK
  766. //
  767. case RTMFP.NOT_ACK:
  768. pkt.writeInt8(RTMFP_MARKER_RESPONSE_1); //response with echo time
  769. pkt.writeInt16(_timeNow());
  770. pkt.writeInt16(message.sentTime);
  771. this.writeAck(pkt, message.flow, message.stage, false);
  772. break;
  773. //
  774. // NC_FAILED_RESPONSE
  775. //
  776. case RTMFP.NC_FAILED_RESPONSE:
  777. pkt.writeInt8(0x8d); //response with echo time
  778. pkt.writeInt16(_timeNow());
  779. pkt.writeInt16(message.echoTime);
  780. pkt.writeInt8(0x10);
  781. pkt.writeInt16(4);
  782. pkt.writeInt8(3);
  783. pkt.writeInt8(message.flow);
  784. pkt.writeInt8(message.stage);
  785. pkt.writeInt8(0);
  786. break;
  787. //
  788. // NC_FAILED_REQUEST
  789. //
  790. case RTMFP.NC_FAILED_REQUEST:
  791. pkt.writeInt8(0x4a); //response without echo time
  792. pkt.writeInt16(_timeNow());
  793. pkt.writeInt8(0x5e);
  794. pkt.writeInt16(2);
  795. pkt.writeInt8(message.flow);
  796. pkt.writeInt8(0);
  797. break;
  798. //
  799. // Response: UNKNOWN
  800. //
  801. case RTMFP.UNKNOWN:
  802. //Do nothing
  803. break;
  804. }
  805. return pkt;
  806. };
  807. /**
  808. * Writes packet size to given position and returns to current write position
  809. */
  810. var writeSize = function(pkt, pos, size) {
  811. var lastPos = pkt.pos();
  812. pkt.pos(pos);
  813. pkt.writeInt16( pkt.size() - pos - 2);
  814. pkt.pos(lastPos);
  815. };
  816. /**
  817. * Writes ack/nack to the packet
  818. */
  819. this.writeAck = function(pkt, flow, stage, ack)
  820. {
  821. // Write Acknowledgment
  822. pkt.writeInt8(0x51);
  823. pkt.writeInt16(3);
  824. pkt.writeInt8(flow);
  825. pkt.writeInt8((ack) ? 0x7f : 0x00);
  826. pkt.writeInt8(stage);
  827. return true;
  828. }
  829. /**
  830. * Read a byte encoded address/port endpoint
  831. *
  832. * @param {Packet} pkt
  833. * @return {endpoint}
  834. */
  835. this.readRawIpv4 = function(pkt){
  836. var endpoint = {};
  837. endpoint.address = pkt.readInt8() + '.' + pkt.readInt8() + '.' + pkt.readInt8() + '.' + pkt.readInt8();
  838. endpoint.port = pkt.readInt16();
  839. return endpoint;
  840. };
  841. function ipv6ToBytes(ip){
  842. var parts = ip.split(':');
  843. for(k in parts){
  844. if(parts[k] === '0'){
  845. parts[k] = '0000';
  846. }
  847. var mx = 4 - parts[k].length;
  848. for(var z = 0; z < mx; z++){
  849. parts[k] = '0' + parts[k];
  850. }
  851. }
  852. return new Buffer(parts.join(''), 'hex');
  853. };
  854. /**
  855. * Reads an IP address and port combination from a packet
  856. */
  857. this.readAddress = function(pkt) {
  858. var rawAddress = pkt.readBytes(pkt.readInt16()).toString();
  859. var colonPos = rawAddress.lastIndexOf(':');
  860. var endpoint = { address: rawAddress.substr(0, colonPos)};
  861. if(endpoint.address.substr(0, 1) == '['){
  862. endpoint.address = ipv6ToBytes(endpoint.address.substr(1, endpoint.address.length - 2));
  863. endpoint.is_IPv6 = true;
  864. }
  865. endpoint.port = rawAddress.substr(colonPos + 1)
  866. return endpoint;
  867. };
  868. /**
  869. * Writes an IP address and port combination to a packet
  870. */
  871. this.writeAddress = function(pkt, endpoint, isPublic) {
  872. //validate endpoints (has address and port)
  873. if(!endpoint.address || !endpoint.port) {
  874. throw new Error('An endpoint needs an address and a port');
  875. }
  876. if(endpoint.is_IPv6)
  877. {
  878. //IPv6
  879. pkt.writeInt8(isPublic ? 0x82 : 0x81);
  880. pkt.writeBuffer(endpoint.address);
  881. } else {
  882. //IPv4
  883. pkt.writeInt8(isPublic ? 0x02 : 0x01);
  884. var ipParts = endpoint.address.split('.');
  885. for(k in ipParts){
  886. pkt.writeInt8(ipParts[k]);
  887. }
  888. }
  889. pkt.writeInt16(endpoint.port);
  890. };
  891. /**
  892. * Reads the connection id from the packet and sets the buffer position to 4
  893. */
  894. this.decodePacket = function(pkt){
  895. pkt.pos(0);
  896. var connection_id = 0;
  897. for(i = 0; i < 3; ++i)
  898. connection_id ^= pkt.readInt32();
  899. pkt.pos(4);
  900. return connection_id;
  901. };
  902. /**
  903. * Adds the connection id to the packet
  904. */
  905. this.encodePacket = function(pkt, connectionId){
  906. pkt.pos(4);
  907. var encodedId = pkt.readInt32() ^ pkt.readInt32() ^ connectionId;
  908. pkt.pos(0);
  909. pkt.writeInt32(encodedId);
  910. };
  911. /**
  912. * Get the checksum for the packet
  913. */
  914. this.packetChecksum = function(pkt) {
  915. var sum = 0, pos = pkt.pos();
  916. pkt.pos(6);
  917. while(pkt.available() > 0)
  918. sum += (pkt.available() == 1) ? pkt.readInt8() : pkt.readInt16();
  919. pkt.pos(pos);
  920. return _rtmfp.finishChecksum(sum);
  921. };
  922. /**
  923. * Decrypts the packet with the given key,
  924. * returns true if the decrypted packet matches the checksum.
  925. */
  926. this.decryptPacket = function(pkt, key) {
  927. if(!pkt || !key){
  928. throw new Error('Cannot decrypt with either missing packet or key');
  929. }
  930. _rtmfp.decryptBuffer(pkt.buffer(), key, 4);
  931. pkt.pos(4);
  932. var check = pkt.readInt16();
  933. var comp = this.packetChecksum(pkt);
  934. return (check == comp);
  935. };
  936. /**
  937. * Encrypts the packet with the given key and adds the checksum.
  938. */
  939. this.encryptPacket = function(pkt, key){
  940. //Ensure pkt and key are given, otherwise we risk crashing the C Module
  941. if(!pkt || !key){
  942. throw new Error('Cannot encrypt with either missing packet or key');
  943. }
  944. //Add padding bytes to the end
  945. var paddingBytesLength = _rtmfp.paddingLength(pkt.size());
  946. pkt.pos(pkt.size());
  947. for(i = 0; i < paddingBytesLength; i++){
  948. pkt.writeInt8(0xFF);
  949. }
  950. //Write Checksum
  951. pkt.pos(4);
  952. var comp = this.packetChecksum(pkt);
  953. pkt.writeInt16(comp);
  954. pkt.pos(4);
  955. var check = pkt.readInt16();
  956. _rtmfp.encryptBuffer(pkt.buffer(), pkt.size(), key, 4);
  957. };
  958. /**
  959. * Create server nonce part
  960. */
  961. this.createServerNonce = function(publicKey){
  962. if(!publicKey){
  963. throw new Error('Public Key needs to be a Buffer');
  964. }
  965. var serverNonce = new Packet(new Buffer(11 + publicKey.length));
  966. serverNonce.writeBytes([0x03,0x1a,0x00,0x00,0x02,0x1e,0x00,0x81,0x02,0x0d,0x02]);
  967. serverNonce.writeBuffer(publicKey);
  968. return serverNonce.buffer();
  969. };
  970. /**
  971. * Create client nonce part
  972. */
  973. this.createClientNonce = function(certificate){
  974. if(!certificate || certificate.length != 64){
  975. throw new Error('Certificate needs to be a Buffer');
  976. }
  977. var clientNonce = new Packet(new Buffer(76));
  978. clientNonce.writeBytes([0x02, 0x1d, 0x02, 0x41, 0x0e]);
  979. clientNonce.writeBuffer(certificate);
  980. clientNonce.writeBytes([0x03, 0x1a, 0x02, 0x0a, 0x02, 0x1e, 0x02]);
  981. return clientNonce.buffer();
  982. };
  983. var _timeNow = function() {
  984. var d = new Date();
  985. return Math.round((Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(), d.getUTCMilliseconds()) - _epoch) / 4);
  986. };
  987. };
  988. //statics
  989. RTMFP.UNKNOWN = 0x00;
  990. RTMFP.RENDEZVOUZ_REQUEST = 0x01;
  991. RTMFP.RENDEZVOUZ_RESPONSE = 0x02;
  992. RTMFP.RENDEZVOUZ_NEWCOMER = 0x03;
  993. RTMFP.HANDSHAKE_REQUEST = 0x04;
  994. RTMFP.HANDSHAKE_RESPONSE = 0x05;
  995. RTMFP.KEY_REQUEST = 0x06;
  996. RTMFP.KEY_RESPONSE = 0x07;
  997. RTMFP.KEEPALIVE_REQUEST = 0x08;
  998. RTMFP.KEEPALIVE_RESPONSE = 0x09;
  999. RTMFP.NET_CONNECTION_CLOSE = 0x0A;
  1000. RTMFP.NET_CONNECTION_REQUEST = 0x0B;
  1001. RTMFP.NC_SUCCESS_RESPONSE = 0x0C;
  1002. RTMFP.NET_GROUP_JOIN = 0x0D;
  1003. RTMFP.NET_GROUP_LEAVE = 0x0E;
  1004. RTMFP.NET_CONNECTION_ADDRESSES = 0x0F;
  1005. RTMFP.ACK = 0x10;
  1006. RTMFP.NOT_ACK = 0x11;
  1007. RTMFP.NC_FAILED_REQUEST = 0x12;
  1008. RTMFP.NC_FAILED_RESPONSE = 0x13;
  1009. RTMFP.COMMAND = 0x14;
  1010. RTMFP.COMMAND_RESULT = 0x15;
  1011. RTMFP.COMMAND_ERROR = 0x16;
  1012. RTMFP.FORWARD_REQUEST = 0x17;
  1013. RTMFP.FORWARD_RESPONSE = 0x18;
  1014. RTMFP.MESSAGE_RESEND = 0x19;
  1015. RTMFP.NC_REJECTED_RESPONSE = 0x1A;
  1016. RTMFP.SYMETRIC_KEY = new Buffer('Adobe Systems 02');