| 1 | /* |
| 2 | Copyright 2018-2021 Intel Corporation |
| 3 | |
| 4 | Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | you may not use this file except in compliance with the License. |
| 6 | You may obtain a copy of the License at |
| 7 | |
| 8 | http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | |
| 10 | Unless required by applicable law or agreed to in writing, software |
| 11 | distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | See the License for the specific language governing permissions and |
| 14 | limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | /** |
| 18 | * @description APF/CIRA Client for Duktape |
| 19 | * @author Joko Sastriawan & Ylian Saint-Hilaire |
| 20 | * @copyright Intel Corporation 2020-2021 |
| 21 | * @license Apache-2.0 |
| 22 | * @version v0.0.2 |
| 23 | */ |
| 24 | |
| 25 | function CreateAPFClient(parent, args) { |
| 26 | if ((args.clientuuid == null) || (args.clientuuid.length != 36)) return null; // Require a UUID if this exact length |
| 27 | |
| 28 | var obj = {}; |
| 29 | obj.parent = parent; |
| 30 | obj.args = args; |
| 31 | obj.http = require('http'); |
| 32 | obj.net = require('net'); |
| 33 | obj.forwardClient = null; |
| 34 | obj.downlinks = {}; |
| 35 | obj.pfwd_idx = 0; |
| 36 | obj.timer = null; // Keep alive timer |
| 37 | |
| 38 | // obj.onChannelClosed |
| 39 | // obj.onJsonControl |
| 40 | |
| 41 | // Function copied from common.js |
| 42 | function ReadInt(v, p) { return (v.charCodeAt(p) * 0x1000000) + (v.charCodeAt(p + 1) << 16) + (v.charCodeAt(p + 2) << 8) + v.charCodeAt(p + 3); }; // We use "*0x1000000" instead of "<<24" because the shift converts the number to signed int32. |
| 43 | function IntToStr(v) { return String.fromCharCode((v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); }; |
| 44 | function hex2rstr(d) { var r = '', m = ('' + d).match(/../g), t; while (t = m.shift()) { r += String.fromCharCode('0x' + t); } return r; }; |
| 45 | function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }; // Convert decimal to hex |
| 46 | function rstr2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } return r; }; // Convert a raw string to a hex string |
| 47 | function d2h(d) { return (d / 256 + 1 / 512).toString(16).substring(2, 4); } |
| 48 | function buf2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += d2h(input[i]); } return r; }; |
| 49 | function Debug(str) { if (obj.parent.debug) { console.log(str); } } |
| 50 | function guidToStr(g) { return g.substring(6, 8) + g.substring(4, 6) + g.substring(2, 4) + g.substring(0, 2) + "-" + g.substring(10, 12) + g.substring(8, 10) + "-" + g.substring(14, 16) + g.substring(12, 14) + "-" + g.substring(16, 20) + "-" + g.substring(20); } |
| 51 | function strToGuid(s) { s = s.replace(/-/g, ''); var ret = s.substring(6, 8) + s.substring(4, 6) + s.substring(2, 4) + s.substring(0, 2) + s.substring(10, 12) + s.substring(8, 10) + s.substring(14, 16) + s.substring(12, 14) + s.substring(16, 20) + s.substring(20); return ret; } |
| 52 | function binzerostring(len) { var res = ''; for (var l = 0; l < len; l++) { res += String.fromCharCode(0 & 0xFF); } return res; } |
| 53 | |
| 54 | // CIRA state |
| 55 | var CIRASTATE = { |
| 56 | INITIAL: 0, |
| 57 | PROTOCOL_VERSION_SENT: 1, |
| 58 | AUTH_SERVICE_REQUEST_SENT: 2, |
| 59 | AUTH_REQUEST_SENT: 3, |
| 60 | PFWD_SERVICE_REQUEST_SENT: 4, |
| 61 | GLOBAL_REQUEST_SENT: 5, |
| 62 | FAILED: -1 |
| 63 | } |
| 64 | obj.cirastate = CIRASTATE.INITIAL; |
| 65 | |
| 66 | // REDIR state |
| 67 | var REDIR_TYPE = { |
| 68 | REDIR_UNKNOWN: 0, |
| 69 | REDIR_SOL: 1, |
| 70 | REDIR_KVM: 2, |
| 71 | REDIR_IDER: 3 |
| 72 | } |
| 73 | |
| 74 | // redirection start command |
| 75 | obj.RedirectStartSol = String.fromCharCode(0x10, 0x00, 0x00, 0x00, 0x53, 0x4F, 0x4C, 0x20); |
| 76 | obj.RedirectStartKvm = String.fromCharCode(0x10, 0x01, 0x00, 0x00, 0x4b, 0x56, 0x4d, 0x52); |
| 77 | obj.RedirectStartIder = String.fromCharCode(0x10, 0x00, 0x00, 0x00, 0x49, 0x44, 0x45, 0x52); |
| 78 | |
| 79 | // Intel AMT forwarded port list for non-TLS mode |
| 80 | //var pfwd_ports = [16992, 623, 16994, 5900]; |
| 81 | var pfwd_ports = [ 16992, 16993 ]; |
| 82 | |
| 83 | // protocol definitions |
| 84 | var APFProtocol = { |
| 85 | UNKNOWN: 0, |
| 86 | DISCONNECT: 1, |
| 87 | SERVICE_REQUEST: 5, |
| 88 | SERVICE_ACCEPT: 6, |
| 89 | USERAUTH_REQUEST: 50, |
| 90 | USERAUTH_FAILURE: 51, |
| 91 | USERAUTH_SUCCESS: 52, |
| 92 | GLOBAL_REQUEST: 80, |
| 93 | REQUEST_SUCCESS: 81, |
| 94 | REQUEST_FAILURE: 82, |
| 95 | CHANNEL_OPEN: 90, |
| 96 | CHANNEL_OPEN_CONFIRMATION: 91, |
| 97 | CHANNEL_OPEN_FAILURE: 92, |
| 98 | CHANNEL_WINDOW_ADJUST: 93, |
| 99 | CHANNEL_DATA: 94, |
| 100 | CHANNEL_CLOSE: 97, |
| 101 | PROTOCOLVERSION: 192, |
| 102 | KEEPALIVE_REQUEST: 208, |
| 103 | KEEPALIVE_REPLY: 209, |
| 104 | KEEPALIVE_OPTIONS_REQUEST: 210, |
| 105 | KEEPALIVE_OPTIONS_REPLY: 211, |
| 106 | JSON_CONTROL: 250 // This is a Mesh specific command that sends JSON to and from the MPS server. |
| 107 | } |
| 108 | |
| 109 | var APFDisconnectCode = { |
| 110 | HOST_NOT_ALLOWED_TO_CONNECT: 1, |
| 111 | PROTOCOL_ERROR: 2, |
| 112 | KEY_EXCHANGE_FAILED: 3, |
| 113 | RESERVED: 4, |
| 114 | MAC_ERROR: 5, |
| 115 | COMPRESSION_ERROR: 6, |
| 116 | SERVICE_NOT_AVAILABLE: 7, |
| 117 | PROTOCOL_VERSION_NOT_SUPPORTED: 8, |
| 118 | HOST_KEY_NOT_VERIFIABLE: 9, |
| 119 | CONNECTION_LOST: 10, |
| 120 | BY_APPLICATION: 11, |
| 121 | TOO_MANY_CONNECTIONS: 12, |
| 122 | AUTH_CANCELLED_BY_USER: 13, |
| 123 | NO_MORE_AUTH_METHODS_AVAILABLE: 14, |
| 124 | INVALID_CREDENTIALS: 15, |
| 125 | CONNECTION_TIMED_OUT: 16, |
| 126 | BY_POLICY: 17, |
| 127 | TEMPORARILY_UNAVAILABLE: 18 |
| 128 | } |
| 129 | |
| 130 | var APFChannelOpenFailCodes = { |
| 131 | ADMINISTRATIVELY_PROHIBITED: 1, |
| 132 | CONNECT_FAILED: 2, |
| 133 | UNKNOWN_CHANNEL_TYPE: 3, |
| 134 | RESOURCE_SHORTAGE: 4, |
| 135 | } |
| 136 | |
| 137 | var APFChannelOpenFailureReasonCode = { |
| 138 | AdministrativelyProhibited: 1, |
| 139 | ConnectFailed: 2, |
| 140 | UnknownChannelType: 3, |
| 141 | ResourceShortage: 4, |
| 142 | } |
| 143 | |
| 144 | obj.onSecureConnect = function onSecureConnect(resp, ws, head) { |
| 145 | Debug("APF Secure WebSocket connected."); |
| 146 | //console.log(JSON.stringify(resp)); |
| 147 | obj.forwardClient.tag = { accumulator: [] }; |
| 148 | obj.forwardClient.ws = ws; |
| 149 | obj.forwardClient.ws.on('end', function () { |
| 150 | Debug("APF: Connection is closing."); |
| 151 | if (obj.timer != null) { clearInterval(obj.timer); obj.timer = null; } |
| 152 | if (obj.onChannelClosed) { obj.onChannelClosed(obj); } |
| 153 | }); |
| 154 | |
| 155 | obj.forwardClient.ws.on('data', function (data) { |
| 156 | obj.forwardClient.tag.accumulator += hex2rstr(buf2hex(data)); |
| 157 | try { |
| 158 | var len = 0; |
| 159 | do { |
| 160 | len = ProcessData(obj.forwardClient); |
| 161 | if (len > 0) { obj.forwardClient.tag.accumulator = obj.forwardClient.tag.accumulator.slice(len); } |
| 162 | if (obj.cirastate == CIRASTATE.FAILED) { |
| 163 | Debug("APF: in a failed state, destroying socket."); |
| 164 | obj.forwardClient.ws.end(); |
| 165 | } |
| 166 | } while (len > 0); |
| 167 | } catch (ex) { Debug(ex); } |
| 168 | }); |
| 169 | |
| 170 | obj.forwardClient.ws.on('error', function (e) { |
| 171 | Debug("APF: Connection error, ending connecting."); |
| 172 | if (obj.timer != null) { clearInterval(obj.timer); obj.timer = null; } |
| 173 | }); |
| 174 | |
| 175 | obj.state = CIRASTATE.INITIAL; |
| 176 | if ((typeof obj.args.conntype == 'number') && (obj.args.conntype != 0)) { |
| 177 | SendJsonControl(obj.forwardClient.ws, { action: 'connType', value: obj.args.conntype }); |
| 178 | if (obj.args.meiState != null) { SendJsonControl(obj.forwardClient.ws, { action: 'meiState', value: obj.args.meiState }); } |
| 179 | } |
| 180 | SendProtocolVersion(obj.forwardClient.ws, obj.args.clientuuid); |
| 181 | SendServiceRequest(obj.forwardClient.ws, 'auth@amt.intel.com'); |
| 182 | } |
| 183 | |
| 184 | obj.updateMeiState = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'meiState', value: state }); } |
| 185 | obj.sendMeiDeactivationState = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'deactivate', value: state }); } |
| 186 | obj.sendStartTlsHostConfigResponse = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'startTlsHostConfig', value: state }); } |
| 187 | obj.sendStopConfigurationResponse = function (state) { SendJsonControl(obj.forwardClient.ws, { action: 'stopConfiguration', value: state }); } |
| 188 | |
| 189 | function SendJsonControl(socket, o) { |
| 190 | var data = JSON.stringify(o) |
| 191 | socket.write(String.fromCharCode(APFProtocol.JSON_CONTROL) + IntToStr(data.length) + data); |
| 192 | Debug("APF: Send JSON control: " + data); |
| 193 | } |
| 194 | |
| 195 | function SendProtocolVersion(socket, uuid) { |
| 196 | var data = String.fromCharCode(APFProtocol.PROTOCOLVERSION) + IntToStr(1) + IntToStr(0) + IntToStr(0) + hex2rstr(strToGuid(uuid)) + binzerostring(64); |
| 197 | socket.write(data); |
| 198 | Debug("APF: Send protocol version 1 0 " + uuid); |
| 199 | obj.cirastate = CIRASTATE.PROTOCOL_VERSION_SENT; |
| 200 | } |
| 201 | |
| 202 | function SendServiceRequest(socket, service) { |
| 203 | var data = String.fromCharCode(APFProtocol.SERVICE_REQUEST) + IntToStr(service.length) + service; |
| 204 | socket.write(data); |
| 205 | Debug("APF: Send service request " + service); |
| 206 | if (service == 'auth@amt.intel.com') { |
| 207 | obj.cirastate = CIRASTATE.AUTH_SERVICE_REQUEST_SENT; |
| 208 | } else if (service == 'pfwd@amt.intel.com') { |
| 209 | obj.cirastate = CIRASTATE.PFWD_SERVICE_REQUEST_SENT; |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | function SendUserAuthRequest(socket, user, pass) { |
| 214 | var service = "pfwd@amt.intel.com"; |
| 215 | var data = String.fromCharCode(APFProtocol.USERAUTH_REQUEST) + IntToStr(user.length) + user + IntToStr(service.length) + service; |
| 216 | //password auth |
| 217 | data += IntToStr(8) + 'password'; |
| 218 | data += binzerostring(1) + IntToStr(pass.length) + pass; |
| 219 | socket.write(data); |
| 220 | Debug("APF: Send username password authentication to MPS"); |
| 221 | obj.cirastate = CIRASTATE.AUTH_REQUEST_SENT; |
| 222 | } |
| 223 | |
| 224 | function SendGlobalRequestPfwd(socket, amthostname, amtport) { |
| 225 | var tcpipfwd = 'tcpip-forward'; |
| 226 | var data = String.fromCharCode(APFProtocol.GLOBAL_REQUEST) + IntToStr(tcpipfwd.length) + tcpipfwd + binzerostring(1, 1); |
| 227 | data += IntToStr(amthostname.length) + amthostname + IntToStr(amtport); |
| 228 | socket.write(data); |
| 229 | Debug("APF: Send tcpip-forward " + amthostname + ":" + amtport); |
| 230 | obj.cirastate = CIRASTATE.GLOBAL_REQUEST_SENT; |
| 231 | } |
| 232 | |
| 233 | function SendKeepAliveRequest(socket) { |
| 234 | socket.write(String.fromCharCode(APFProtocol.KEEPALIVE_REQUEST) + IntToStr(255)); |
| 235 | Debug("APF: Send keepalive request"); |
| 236 | } |
| 237 | |
| 238 | function SendKeepAliveReply(socket, cookie) { |
| 239 | socket.write(String.fromCharCode(APFProtocol.KEEPALIVE_REPLY) + IntToStr(cookie)); |
| 240 | Debug("APF: Send keepalive reply"); |
| 241 | } |
| 242 | |
| 243 | function ProcessData(socket) { |
| 244 | var cmd = socket.tag.accumulator.charCodeAt(0); |
| 245 | var len = socket.tag.accumulator.length; |
| 246 | var data = socket.tag.accumulator; |
| 247 | if (len == 0) { return 0; } |
| 248 | |
| 249 | // Respond to MPS according to obj.cirastate |
| 250 | switch (cmd) { |
| 251 | case APFProtocol.SERVICE_ACCEPT: { |
| 252 | var slen = ReadInt(data, 1), service = data.substring(5, 6 + slen); |
| 253 | Debug("APF: Service request to " + service + " accepted."); |
| 254 | if (service == 'auth@amt.intel.com') { |
| 255 | if (obj.cirastate >= CIRASTATE.AUTH_SERVICE_REQUEST_SENT) { |
| 256 | SendUserAuthRequest(socket.ws, obj.args.mpsuser, obj.args.mpspass); |
| 257 | } |
| 258 | } else if (service == 'pfwd@amt.intel.com') { |
| 259 | if (obj.cirastate >= CIRASTATE.PFWD_SERVICE_REQUEST_SENT) { |
| 260 | SendGlobalRequestPfwd(socket.ws, obj.args.clientname, pfwd_ports[obj.pfwd_idx++]); |
| 261 | } |
| 262 | } |
| 263 | return 5 + slen; |
| 264 | } |
| 265 | case APFProtocol.REQUEST_SUCCESS: { |
| 266 | if (len >= 5) { |
| 267 | var port = ReadInt(data, 1); |
| 268 | Debug("APF: Request to port forward " + port + " successful."); |
| 269 | // iterate to pending port forward request |
| 270 | if (obj.pfwd_idx < pfwd_ports.length) { |
| 271 | SendGlobalRequestPfwd(socket.ws, obj.args.clientname, pfwd_ports[obj.pfwd_idx++]); |
| 272 | } else { |
| 273 | // no more port forward, now setup timer to send keep alive |
| 274 | Debug("APF: Start keep alive for every " + obj.args.mpskeepalive + " ms."); |
| 275 | obj.timer = setInterval(function () { |
| 276 | SendKeepAliveRequest(obj.forwardClient.ws); |
| 277 | }, obj.args.mpskeepalive);// |
| 278 | } |
| 279 | return 5; |
| 280 | } |
| 281 | Debug("APF: Request successful."); |
| 282 | return 1; |
| 283 | } |
| 284 | case APFProtocol.USERAUTH_SUCCESS: { |
| 285 | Debug("APF: User Authentication successful"); |
| 286 | // Send Pfwd service request |
| 287 | SendServiceRequest(socket.ws, 'pfwd@amt.intel.com'); |
| 288 | return 1; |
| 289 | } |
| 290 | case APFProtocol.USERAUTH_FAILURE: { |
| 291 | Debug("APF: User Authentication failed"); |
| 292 | obj.cirastate = CIRASTATE.FAILED; |
| 293 | return 14; |
| 294 | } |
| 295 | case APFProtocol.KEEPALIVE_REQUEST: { |
| 296 | Debug("APF: Keep Alive Request with cookie: " + ReadInt(data, 1)); |
| 297 | SendKeepAliveReply(socket.ws, ReadInt(data, 1)); |
| 298 | return 5; |
| 299 | } |
| 300 | case APFProtocol.KEEPALIVE_REPLY: { |
| 301 | Debug("APF: Keep Alive Reply with cookie: " + ReadInt(data, 1)); |
| 302 | return 5; |
| 303 | } |
| 304 | // Channel management |
| 305 | case APFProtocol.CHANNEL_OPEN: { |
| 306 | // Parse CHANNEL OPEN request |
| 307 | var p_res = parseChannelOpen(data); |
| 308 | Debug("APF: CHANNEL_OPEN request: " + JSON.stringify(p_res)); |
| 309 | // Check if target port is in pfwd_ports |
| 310 | if (pfwd_ports.indexOf(p_res.target_port) >= 0) { |
| 311 | // Connect socket to that port |
| 312 | var chan = obj.net.createConnection({ host: obj.args.clientaddress, port: p_res.target_port }, function () { |
| 313 | //require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "CHANNEL_OPEN-open" }); |
| 314 | // obj.downlinks[p_res.sender_chan].setEncoding('binary');//assume everything is binary, not interpreting |
| 315 | SendChannelOpenConfirm(socket.ws, p_res); |
| 316 | }); |
| 317 | |
| 318 | // Setup flow control |
| 319 | chan.maxInWindow = p_res.window_size; // Oddly, we are using the same window size as the other side. |
| 320 | chan.curInWindow = 0; |
| 321 | |
| 322 | chan.on('data', function (ddata) { |
| 323 | // Relay data to fordwardclient |
| 324 | // TODO: Implement flow control |
| 325 | SendChannelData(socket.ws, p_res.sender_chan, ddata); |
| 326 | }); |
| 327 | |
| 328 | chan.on('error', function (e) { |
| 329 | //Debug("Downlink connection error: " + e); |
| 330 | SendChannelOpenFailure(socket.ws, p_res); |
| 331 | }); |
| 332 | |
| 333 | chan.on('end', function () { |
| 334 | var chan = obj.downlinks[p_res.sender_chan]; |
| 335 | if (chan != null) { |
| 336 | Debug("Socket ends."); |
| 337 | try { SendChannelClose(socket.ws, p_res.sender_chan); } catch (ex) { } |
| 338 | delete obj.downlinks[p_res.sender_chan]; |
| 339 | } |
| 340 | }); |
| 341 | |
| 342 | obj.downlinks[p_res.sender_chan] = chan; |
| 343 | } else { |
| 344 | // Not a supported port, fail the connection |
| 345 | SendChannelOpenFailure(socket.ws, p_res); |
| 346 | } |
| 347 | return p_res.len; |
| 348 | } |
| 349 | case APFProtocol.CHANNEL_OPEN_CONFIRMATION: { |
| 350 | Debug("APF: CHANNEL_OPEN_CONFIRMATION"); |
| 351 | return 17; |
| 352 | } |
| 353 | case APFProtocol.CHANNEL_CLOSE: { |
| 354 | var rcpt_chan = ReadInt(data, 1); |
| 355 | Debug("APF: CHANNEL_CLOSE: " + rcpt_chan); |
| 356 | try { obj.downlinks[rcpt_chan].end(); } catch (ex) { } |
| 357 | return 5; |
| 358 | } |
| 359 | case APFProtocol.CHANNEL_DATA: { |
| 360 | Debug("APF: CHANNEL_DATA: " + JSON.stringify(rstr2hex(data))); |
| 361 | var rcpt_chan = ReadInt(data, 1); |
| 362 | var chan_data_len = ReadInt(data, 5); |
| 363 | var chan_data = data.substring(9, 9 + chan_data_len); |
| 364 | var chan = obj.downlinks[rcpt_chan]; |
| 365 | if (chan != null) { |
| 366 | chan.curInWindow += chan_data_len; |
| 367 | try { |
| 368 | chan.write(Buffer.from(chan_data, 'binary'), function () { |
| 369 | Debug("Write completed."); |
| 370 | // If the incoming window is over half used, send an adjust. |
| 371 | if (this.curInWindow > (this.maxInWindow / 2)) { SendChannelWindowAdjust(socket.ws, rcpt_chan, this.curInWindow); this.curInWindow = 0; } |
| 372 | }); |
| 373 | } catch (ex) { Debug("Cannot forward data to downlink socket."); } |
| 374 | } |
| 375 | return 9 + chan_data_len; |
| 376 | } |
| 377 | case APFProtocol.CHANNEL_WINDOW_ADJUST: { |
| 378 | Debug("APF: CHANNEL_WINDOW_ADJUST"); |
| 379 | return 9; |
| 380 | } |
| 381 | case APFProtocol.JSON_CONTROL: { |
| 382 | Debug("APF: JSON_CONTROL"); |
| 383 | var len = ReadInt(data, 1); |
| 384 | if (obj.onJsonControl) { var o = null; try { o = JSON.parse(data.substring(5, 5 + len)); } catch (ex) { } if (o != null) { obj.onJsonControl(o); } } |
| 385 | return 5 + len; |
| 386 | } |
| 387 | default: { |
| 388 | Debug("CMD: " + cmd + " is not implemented."); |
| 389 | obj.cirastate = CIRASTATE.FAILED; |
| 390 | return 0; |
| 391 | } |
| 392 | } |
| 393 | } |
| 394 | |
| 395 | function parseChannelOpen(data) { |
| 396 | var result = { cmd: APFProtocol.CHANNEL_OPEN }; |
| 397 | var chan_type_slen = ReadInt(data, 1); |
| 398 | result.chan_type = data.substring(5, 5 + chan_type_slen); |
| 399 | result.sender_chan = ReadInt(data, 5 + chan_type_slen); |
| 400 | result.window_size = ReadInt(data, 9 + chan_type_slen); |
| 401 | var c_len = ReadInt(data, 17 + chan_type_slen); |
| 402 | result.target_address = data.substring(21 + chan_type_slen, 21 + chan_type_slen + c_len); |
| 403 | result.target_port = ReadInt(data, 21 + chan_type_slen + c_len); |
| 404 | var o_len = ReadInt(data, 25 + chan_type_slen + c_len); |
| 405 | result.origin_address = data.substring(29 + chan_type_slen + c_len, 29 + chan_type_slen + c_len + o_len); |
| 406 | result.origin_port = ReadInt(data, 29 + chan_type_slen + c_len + o_len); |
| 407 | result.len = 33 + chan_type_slen + c_len + o_len; |
| 408 | return result; |
| 409 | } |
| 410 | |
| 411 | function SendChannelOpenFailure(socket, chan_data) { |
| 412 | socket.write(String.fromCharCode(APFProtocol.CHANNEL_OPEN_FAILURE) + IntToStr(chan_data.sender_chan) + IntToStr(2) + IntToStr(0) + IntToStr(0)); |
| 413 | Debug("APF: Send ChannelOpenFailure"); |
| 414 | } |
| 415 | |
| 416 | function SendChannelOpenConfirm(socket, chan_data) { |
| 417 | socket.write(String.fromCharCode(APFProtocol.CHANNEL_OPEN_CONFIRMATION) + IntToStr(chan_data.sender_chan) + IntToStr(chan_data.sender_chan) + IntToStr(chan_data.window_size) + IntToStr(0xFFFFFFFF)); |
| 418 | Debug("APF: Send ChannelOpenConfirmation"); |
| 419 | } |
| 420 | |
| 421 | function SendChannelWindowAdjust(socket, chan, size) { |
| 422 | socket.write(String.fromCharCode(APFProtocol.CHANNEL_WINDOW_ADJUST) + IntToStr(chan) + IntToStr(size)); |
| 423 | Debug("APF: Send ChannelWindowAdjust, channel: " + chan + ", size: " + size); |
| 424 | } |
| 425 | |
| 426 | function SendChannelData(socket, chan, data) { |
| 427 | socket.write(Buffer.concat([Buffer.from(String.fromCharCode(APFProtocol.CHANNEL_DATA) + IntToStr(chan) + IntToStr(data.length), 'binary'), data])); |
| 428 | Debug("APF: Send ChannelData: " + data.toString('hex')); |
| 429 | } |
| 430 | |
| 431 | function SendChannelClose(socket, chan) { |
| 432 | socket.write(String.fromCharCode(APFProtocol.CHANNEL_CLOSE) + IntToStr(chan)); |
| 433 | Debug("APF: Send ChannelClose "); |
| 434 | } |
| 435 | |
| 436 | obj.connect = function () { |
| 437 | if (obj.forwardClient != null) { |
| 438 | try { obj.forwardClient.ws.end(); } catch (ex) { Debug(ex); } |
| 439 | //obj.forwardClient = null; |
| 440 | } |
| 441 | obj.cirastate = CIRASTATE.INITIAL; |
| 442 | obj.pfwd_idx = 0; |
| 443 | |
| 444 | //obj.forwardClient = new obj.ws(obj.args.mpsurl, obj.tlsoptions); |
| 445 | //obj.forwardClient.on("open", obj.onSecureConnect); |
| 446 | |
| 447 | var wsoptions = obj.http.parseUri(obj.args.mpsurl); |
| 448 | wsoptions.rejectUnauthorized = 0; |
| 449 | obj.forwardClient = obj.http.request(wsoptions); |
| 450 | obj.forwardClient.upgrade = obj.onSecureConnect; |
| 451 | obj.forwardClient.end(); // end request, trigger completion of HTTP request |
| 452 | } |
| 453 | |
| 454 | obj.disconnect = function () { try { obj.forwardClient.ws.end(); } catch (ex) { Debug(ex); } } |
| 455 | |
| 456 | return obj; |
| 457 | } |
| 458 | |
| 459 | module.exports = CreateAPFClient; |