diff --git a/.gitignore b/.gitignore index 4a8a2dea..2fda19c5 100644 --- a/.gitignore +++ b/.gitignore @@ -362,3 +362,4 @@ MigrationBackup/ # Fody - auto-generated XML schema FodyWeavers.xsd /hacktv_updsrv/ServiceLogPost/1626307222_warning_812bf30600b002bb +/hacktv_updsrv/ServiceVault/wtv-music/midi diff --git a/README.md b/README.md index 18da1414..1106352b 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,12 @@ This open source server is in alpha status. Use at your own risk. - Can encrypt and decrypt SECURE ON and arbitrary encrypted data - Can handle psuedo encryption (box sends SECURE ON but does not encrypt) - Can handle client "relogin" and "reconnect" events +- Can now use `.async.js` files with asynchronous requests +- Can handle more than one box at a time ### Current issues: -- Probably can't handle more than one box at a time +- May not run on non-development Windows machines (VS2019 with node and python) +- Breaks when two different boxes have the same SSID (spoofing, won't fix (production did it too)) - Power cycling box and re-connecting via ConnectSetup may invalidate encryption until server is restarted - wtv-update:/update does not yet function as intended @@ -23,10 +26,12 @@ This open source server is in alpha status. Use at your own risk. - (probably not) url tokenizer ### How To Use: -- Install [node.js](https://nodejs.org/en/download/) +- Install [node.js](https://nodejs.org/en/download/). Be sure to say `Yes` when asked about `Chocolatey`. +- If you have trouble running it on Windows, try a Linux machine, Windows may need a full development enviroment or extra steps. - Download a snapshot (either of master, or of any commit/branch/relase/tag etc) - Extract zip somewhere and enter that directory with a command prompt -- Run `npm install` +- Enter `hacktv_updsrv` subdirectory +- Verify you are in the same directory as `app.js`, then tun `npm install` - Check any configuration (services.json) - Run `node app.js` - Test with a WebTV Viewer or connect with a real box diff --git a/hacktv_updsrv/ServiceVault/wtv-1800/finish-prereg.js b/hacktv_updsrv/ServiceVault/wtv-1800/finish-prereg.js index 4f51374d..67275dd8 100644 --- a/hacktv_updsrv/ServiceVault/wtv-1800/finish-prereg.js +++ b/hacktv_updsrv/ServiceVault/wtv-1800/finish-prereg.js @@ -1,7 +1,10 @@ -if (socket_session_data[socket.id].ssid != null && !sec_session[socket_session_data[socket.id].ssid]) { - sec_session[socket_session_data[socket.id].ssid] = new WTVSec(); - sec_session[socket_session_data[socket.id].ssid].IssueChallenge(); - sec_session[socket_session_data[socket.id].ssid].set_incarnation(request_headers['wtv-incarnation']); +if (socket_session_data[socket.id].ssid != null && !getSessionData(socket_session_data[socket.id].ssid, 'wtvsec_login')) { + var wtvsec_login = new WTVSec(); + wtvsec_login.IssueChallenge(); + wtvsec_login.set_incarnation(request_headers['wtv-incarnation']); + setSessionData(socket_session_data[socket.id].ssid, 'wtvsec_login', wtvsec_login) +} else { + var wtvsec_login = getSessionData(socket_session_data[socket.id].ssid, 'wtvsec_login') } var contype = "text/tellyscript"; @@ -11,12 +14,12 @@ var skip_tellyscript = false; // if relogin, skip tellyscript if (query['relogin']) { contype = "text/html"; // skip tellyscript - sec_session[socket_session_data[socket.id].ssid].ticket_b64 = null; // clear old ticket + wtvsec_login.ticket_b64 = null; // clear old ticket } headers = `200 OK Connection: Keep-Alive -wtv-initial-key: ` + issueWTVInitialKey(socket) + ` +wtv-initial-key: ` + wtvsec_login.challenge_key.toString(CryptoJS.enc.Base64) + ` Content-Type: `+ contype + ` wtv-service: reset ` + getServiceString('wtv-1800') + ` diff --git a/hacktv_updsrv/ServiceVault/wtv-flashrom/willie.async.js b/hacktv_updsrv/ServiceVault/wtv-flashrom/willie.async.js new file mode 100644 index 00000000..aa379a3d --- /dev/null +++ b/hacktv_updsrv/ServiceVault/wtv-flashrom/willie.async.js @@ -0,0 +1,35 @@ +// willie is just a graphical frontend to a list of ROMs +// the rest of the scripts should work if you manually link to a ROM, and actually have it. + + +var proxy_query = ''; +if (query['flash']) delete query['flash']; +if (query['vflash']) delete query['vflash']; + +for (const [key, value] of Object.entries(query)) { + proxy_query += "&" + key + "=" + value; +} + +console.log(proxy_query); + +var options = { + host: "wtv.zefie.com", + path: "/willie.php?pflash=" + getSessionData(socket_session_data[socket.id].ssid, 'wtv-client-rom-type') + proxy_query, + method: 'GET' +} + + +headers = "200 OK\nContent-type: text/html"; +const req = http.request(options, function (res) { + data = ''; + console.log(` * Upstream HTTP StatusCode: ${res.statusCode}`) + + res.on('data', d => { + data += d; + }) + + res.on('end', function () { + sendToClient(socket, headers, data); + }); +}); +req.end(); diff --git a/hacktv_updsrv/ServiceVault/wtv-flashrom/willie.js b/hacktv_updsrv/ServiceVault/wtv-flashrom/willie.js deleted file mode 100644 index 2e5eef92..00000000 --- a/hacktv_updsrv/ServiceVault/wtv-flashrom/willie.js +++ /dev/null @@ -1,20 +0,0 @@ -// willie is just a graphical frontend to a list of ROMs -// the rest of the scripts should work if you manually link to a ROM, and actually have it. - - -const options = new URL('http://wtv.zefie.com/willie.php?flash='+getSessionData(socket_session_data[socket.id].ssid, 'wtv-client-rom-type')) -var data_ready = false; -data = ''; -const req = http.request(options, res => { - console.log(`statusCode: ${res.statusCode}`) - - res.on('data', d => { - data += d; - }) - - res.on('end', function () { - data_ready = true; - }); -}); - -headers = "200 OK\nContent-type: text/html"; \ No newline at end of file diff --git a/hacktv_updsrv/ServiceVault/wtv-head-waiter/finalize-security.js b/hacktv_updsrv/ServiceVault/wtv-head-waiter/finalize-security.js index ec678b8c..ba53496f 100644 --- a/hacktv_updsrv/ServiceVault/wtv-head-waiter/finalize-security.js +++ b/hacktv_updsrv/ServiceVault/wtv-head-waiter/finalize-security.js @@ -5,10 +5,10 @@ if (socket_session_data[socket.id].ssid !== null) { if (request_headers['wtv-ticket']) { if (request_headers['wtv-ticket'].length > 8) { DecodeTicket(request_headers['wtv-ticket']); - sec_session[socket_session_data[socket.id].ssid].ticket_b64 = request_headers['wtv-ticket']; + socket_session_data[socket.id].wtvsec.ticket_b64 = request_headers['wtv-ticket']; //socket_session_data[socket.id].secure == true; } - } else if (sec_session[socket_session_data[socket.id].ssid].ticket_b64 == null) { + } else if (socket_session_data[socket.id].wtvsec.ticket_b64 == null) { // TODO: client should have a ticket and send it back by now, if not we should handle this correctly } } @@ -16,7 +16,7 @@ if (socket_session_data[socket.id].ssid !== null) { headers = `200 OK Connection: Keep-Alive wtv-encrypted: true -wtv-ticket: `+sec_session[socket_session_data[socket.id].ssid].ticket_b64+` +wtv-ticket: `+socket_session_data[socket.id].wtvsec.ticket_b64+` wtv-expire-all: htv- wtv-home-url: wtv-home:/home? wtv-visit: wtv-home:/splash? diff --git a/hacktv_updsrv/ServiceVault/wtv-head-waiter/login-stage-two.js b/hacktv_updsrv/ServiceVault/wtv-head-waiter/login-stage-two.js index 5174ebae..47cebbad 100644 --- a/hacktv_updsrv/ServiceVault/wtv-head-waiter/login-stage-two.js +++ b/hacktv_updsrv/ServiceVault/wtv-head-waiter/login-stage-two.js @@ -1,22 +1,32 @@ var challenge_response, challenge_header = ''; var gourl; +if (socket_session_data[socket.id].ssid != null && !getSessionData(socket_session_data[socket.id].ssid, 'wtvsec_login')) { + var wtvsec_login = new WTVSec(); + wtvsec_login = new WTVSec(); + wtvsec_login.IssueChallenge(); + wtvsec_login.set_incarnation(request_headers['wtv-incarnation']); + setSessionData(socket_session_data[socket.id].ssid, 'wtvsec_login', wtvsec_login) +} else { + var wtvsec_login = getSessionData(socket_session_data[socket.id].ssid, 'wtvsec_login') +} + if (socket_session_data[socket.id].ssid !== null) { - if (sec_session[socket_session_data[socket.id].ssid].ticket_b64 == null) { + if (wtvsec_login.ticket_b64 == null) { if (request_headers['wtv-ticket']) { if (request_headers['wtv-ticket'].length > 8) { - sec_session[socket_session_data[socket.id].ssid].DecodeTicket(request_headers['wtv-ticket']); - sec_session[socket_session_data[socket.id].ssid].ticket_b64 = request_headers['wtv-ticket']; + wtvsec_login.DecodeTicket(request_headers['wtv-ticket']); + wtvsec_login.ticket_b64 = request_headers['wtv-ticket']; //socket_session_data[socket.id].secure = true; } } else { - challenge_response = sec_session[socket_session_data[socket.id].ssid].challenge_response; + challenge_response = wtvsec_login.challenge_response; var client_challenge_response = request_headers['wtv-challenge-response'] || null; if (challenge_response && client_challenge_response) { //if (challenge_response.toString(CryptoJS.enc.Base64).substring(0,85) == client_challenge_response.substring(0,85)) { if (challenge_response.toString(CryptoJS.enc.Base64) == client_challenge_response) { console.log(" * wtv-challenge-response success for "+socket_session_data[socket.id].ssid); - sec_session[socket_session_data[socket.id].ssid].PrepareTicket(); + wtvsec_login.PrepareTicket(); //socket_session_data[socket.id].secure = true; } else { console.log(" * wtv-challenge-response FAILED for " + socket_session_data[socket.id].ssid); @@ -56,7 +66,7 @@ wtv-visit: client:closeallpanels wtv-expire-all: client:closeallpanels wtv-offline-user-list: `+offline_user_list+` wtv-bypass-proxy: true -wtv-ticket: `+ sec_session[socket_session_data[socket.id].ssid].ticket_b64 + ` +wtv-ticket: `+ wtvsec_login.ticket_b64 + ` wtv-messagewatch-checktimeoffset: off wtv-input-timeout: 14400 wtv-connection-timeout: 90 @@ -71,6 +81,8 @@ wtv-noback-all: wtv- wtv-service: reset `+ getServiceString('all') + ` wtv-boot-url: wtv-1800:/preregister?relogin=true +wtv-ssl-certs-download-url: wtv-head-waiter:/ssl-cert.der +wtv-ssl-certs-checksum: 473926DC1B11F635A6B920953FDCDE6A wtv-user-name: `+ nickname + ` wtv-human-name: `+ nickname + ` wtv-irc-nick: `+ nickname + ` diff --git a/hacktv_updsrv/ServiceVault/wtv-head-waiter/login.js b/hacktv_updsrv/ServiceVault/wtv-head-waiter/login.js index 8c8ead24..be4cf9e3 100644 --- a/hacktv_updsrv/ServiceVault/wtv-head-waiter/login.js +++ b/hacktv_updsrv/ServiceVault/wtv-head-waiter/login.js @@ -1,26 +1,32 @@ var challenge_response, challenge_header = ''; if (socket_session_data[socket.id].ssid !== null) { + var wtvsec_login = getSessionData(socket_session_data[socket.id].ssid, 'wtvsec_login'); if (request_headers['wtv-ticket']) { - if (sec_session[socket_session_data[socket.id].ssid].ticket_b64 == null) { + if (wtvsec_login.ticket_b64 == null) { if (request_headers['wtv-ticket'].length > 8) { - sec_session[socket_session_data[socket.id].ssid].DecodeTicket(request_headers['wtv-ticket']); - sec_session[socket_session_data[socket.id].ssid].ticket_b64 = request_headers['wtv-ticket']; + wtvsec_login.DecodeTicket(request_headers['wtv-ticket']); + wtvsec_login.ticket_b64 = request_headers['wtv-ticket']; } } } else { - challenge_response = sec_session[socket_session_data[socket.id].ssid].challenge_response; - var client_challenge_response = request_headers['wtv-challenge-response'] || null; - if (challenge_response && client_challenge_response) { - if (challenge_response.toString(CryptoJS.enc.Base64).substring(0,85) == client_challenge_response.substring(0,85)) { - console.log(" * wtv-challenge-response success for "+socket_session_data[socket.id].ssid); - sec_session[socket_session_data[socket.id].ssid].PrepareTicket(); + if (wtvsec_login) { + challenge_response = wtvsec_login.challenge_response; + var client_challenge_response = request_headers['wtv-challenge-response'] || null; + if (challenge_response && client_challenge_response) { + if (challenge_response.toString(CryptoJS.enc.Base64).substring(0, 85) == client_challenge_response.substring(0, 85)) { + console.log(" * wtv-challenge-response success for " + socket_session_data[socket.id].ssid); + wtvsec_login.PrepareTicket(); + } else { + challenge_header = "wtv-challenge: " + wtvsec_login.IssueChallenge(); + } } else { - challenge_header = "wtv-challenge: "+issueWTVChallenge(socket); + challenge_header = "wtv-challenge: " + wtvsec_login.IssueChallenge(); } } else { - challenge_header = "wtv-challenge: "+issueWTVChallenge(socket); - } + wtvsec_login = new WTVSec(); + + } } } @@ -42,15 +48,27 @@ if (request_headers) { }); } */ -headers = `200 OK + +if (challenge_header != '') { + headers = `200 OK Connection: Keep-Alive Expires: Wed, 09 Oct 1991 22:00:00 GMT wtv-expire-all: wtv-head-waiter: -`+getServiceString('wtv-log')+` +`+ getServiceString('wtv-log') + ` wtv-log-url: wtv-log:/log -`+challenge_header+` +`+ challenge_header + ` wtv-relogin-url: wtv-1800:/preregister?relogin=true wtv-reconnect-url: wtv-1800:/preregister?reconnect=true wtv-visit: wtv-head-waiter:/login-stage-two? Content-type: text/html`; -data = ''; + data = ''; +} else { + headers = `200 OK +Connection: Keep-Alive +Expires: Wed, 09 Oct 1991 22:00:00 GMT +wtv-expire-all: wtv-head-waiter: +wtv-expire-all: wtv-1800: +wtv-visit: wtv-1800:/preregister?relogin=true +Content-type: text/html`; + +} \ No newline at end of file diff --git a/hacktv_updsrv/ServiceVault/wtv-head-waiter/ssl-cert.der b/hacktv_updsrv/ServiceVault/wtv-head-waiter/ssl-cert.der new file mode 100644 index 00000000..b4b9da93 Binary files /dev/null and b/hacktv_updsrv/ServiceVault/wtv-head-waiter/ssl-cert.der differ diff --git a/hacktv_updsrv/ServiceVault/wtv-home/home.js b/hacktv_updsrv/ServiceVault/wtv-home/home.js index dfc5acae..7fe374f2 100644 --- a/hacktv_updsrv/ServiceVault/wtv-home/home.js +++ b/hacktv_updsrv/ServiceVault/wtv-home/home.js @@ -12,32 +12,37 @@ if (getSessionData(socket_session_data[socket.id].ssid, 'box-does-psuedo-encrypt data =` -Home for minsrv +Home for minisrv -

Welcome to `+ z_title + `

+

Welcome to `+ z_title + `

Encryption Status: `+cryptstatus+`

` -if (socket_session_data[socket.id].secure) { - data += 'Encryption Key (Server): ' + sec_session[socket.id].session_key2.toString(CryptoJS.enc.Hex)+'
'; - data += 'Encryption Key (Client): ' + sec_session[socket.id].session_key1.toString(CryptoJS.enc.Hex)+'


'; -} data += `

Working stuff

client:relog (direct)
-Clear Cache
+Clear Cache
+
+ + +

zefie's server only

Music Collection
+Matt Test

Test Stuff (probably broken)

-HackTV Updater Test
+HackTV Updater Test
Ultra Willies
Disconnect and go to HackTV Home
diff --git a/hacktv_updsrv/ServiceVault/wtv-home/splash.txt b/hacktv_updsrv/ServiceVault/wtv-home/splash.txt index 123e8313..c6e7f1b8 100644 --- a/hacktv_updsrv/ServiceVault/wtv-home/splash.txt +++ b/hacktv_updsrv/ServiceVault/wtv-home/splash.txt @@ -1,6 +1,6 @@ 200 OK Connection: Keep-Alive -wtv-expire-all: htv- +wtv-expire-all: wtv- Content-type: text/html diff --git a/hacktv_updsrv/ServiceVault/wtv-home/test.ram b/hacktv_updsrv/ServiceVault/wtv-home/test.ram new file mode 100644 index 00000000..949393d4 --- /dev/null +++ b/hacktv_updsrv/ServiceVault/wtv-home/test.ram @@ -0,0 +1 @@ +pnm://192.168.11.188:7070/clients/zefie/wtvrom/Daybreak.ra \ No newline at end of file diff --git a/hacktv_updsrv/ServiceVault/wtv-tricks/access.js b/hacktv_updsrv/ServiceVault/wtv-tricks/access.js new file mode 100644 index 00000000..4c9342fc --- /dev/null +++ b/hacktv_updsrv/ServiceVault/wtv-tricks/access.js @@ -0,0 +1,11 @@ +// Allow URL access outside our trusted minisrv + +if (query['url']) var url = unescape(query['url']); +else var url = "client:showalert?message=Please%20provide%20a%20%3Furl%3D%20with%20the%20url%20you%20would%20like%20to%20access.&buttonlabel1=Okay&buttonacction1=client:donothing" + +headers = `200 OK +wtv-visit: `+url+` +Content-type: text/html` + + +data = ''; \ No newline at end of file diff --git a/hacktv_updsrv/ServiceVault/wtv-tricks/blastcache.js b/hacktv_updsrv/ServiceVault/wtv-tricks/blastcache.js new file mode 100644 index 00000000..6269d576 --- /dev/null +++ b/hacktv_updsrv/ServiceVault/wtv-tricks/blastcache.js @@ -0,0 +1,23 @@ +headers = `200 OK +wtv-expire-all: wtv- +wtv-expire-all: http +Content-type: text/html` + +var visit_url = null; + +if (request_headers['Referer']) visit_url = request_headers['Referer']; +else if (query['return_to']) visit_url = query['return_to']; +else visit_url = "client:goback"; + +data = ` + + + +Successfully expired service URL cache
+Any previously cached pages should be reloaded from the network.

+Not loading? Click here. + +`; \ No newline at end of file diff --git a/hacktv_updsrv/ServiceVault/wtv-tricks/blastcache.txt b/hacktv_updsrv/ServiceVault/wtv-tricks/blastcache.txt deleted file mode 100644 index a26fd7cd..00000000 --- a/hacktv_updsrv/ServiceVault/wtv-tricks/blastcache.txt +++ /dev/null @@ -1,17 +0,0 @@ -200 OK -wtv-noback-all: wtv- -wtv-expire-all: wtv- -Content-type: text/html - - - - - -Successfully expired service URL cache
-Any previously cached pages should be reloaded from the network.

-Go Home - - \ No newline at end of file diff --git a/hacktv_updsrv/app.js b/hacktv_updsrv/app.js index b85c1d27..85b7ed9e 100644 --- a/hacktv_updsrv/app.js +++ b/hacktv_updsrv/app.js @@ -16,6 +16,14 @@ var ports = []; var service_vault_dir = __dirname + "/ServiceVault"; +String.prototype.reverse = function () { + var splitString = this.split(""); + var reverseArray = splitString.reverse(); + var joinArray = reverseArray.join(""); + return joinArray; +} + + function getServiceString(service) { if (service === "all") { var out = ""; @@ -33,16 +41,11 @@ function getServiceString(service) { } var ssid_data = new Array(); -var sec_session = new Array(); var socket_buffer = new Array(); var socket_session_data = new Array(); var script_processing_timeout = 10; // seconds -var overrides = new Array(); -//overrides['initial_key'] = "CC5rWmRUE0o="; -//overrides['challenge'] = "0kjyqIYAu0ziFBbSERN6DGaZ6S0fT+DBUCtpHCJ4lpuM7CbXdAm+x83BIDoJYztd1Z+5KFZ7ghmb3LJCT/6mhWUYkqqKOyfPRW8ZIdbICK/CV+Kxm8EUjRXZSk/97tsmFpH3hcCJ7C2TBw+TX38uQQ=="; - function getSessionData(ssid, key = null) { if (typeof (ssid_data[ssid]) === 'undefined') return null; if (key == null) return ssid_data[ssid]; @@ -69,25 +72,8 @@ function getFile(path, deps = false) { return null; } - -function issueWTVInitialKey(socket) { - if (overrides['initial_key']) { - sec_session[socket_session_data[socket.id].ssid].initial_shared_key = CryptoJS.enc.Base64.parse(overrides['initial_key']); - sec_session[socket_session_data[socket.id].ssid].current_shared_key = CryptoJS.enc.Base64.parse(overrides['initial_key']); - sec_session[socket_session_data[socket.id].ssid].challenge_key = CryptoJS.enc.Base64.parse(overrides['initial_key']); - return overrides['initial_key']; - } else { - return sec_session[socket_session_data[socket.id].ssid].challenge_key.toString(CryptoJS.enc.Base64); - } -} - -function issueWTVChallenge(socket) { - if (overrides['challenge']) { - sec_session[socket_session_data[socket.id].ssid].challenge_response = sec_session[socket_session_data[socket.id].ssid].ProcessChallenge(overrides['challenge']); - return overrides['challenge']; - } else { - return sec_session[socket_session_data[socket.id].ssid].IssueChallenge(); - } +function getFileExt(path) { + return path.reverse().split(".")[0].reverse(); } function doErrorPage(code) { @@ -116,15 +102,18 @@ function doErrorPage(code) { function getConType(path) { // custom contype for flashrom - if (path.indexOf("wtv-flashrom") && (path.substring(path.length - 4, path.length) == ".rom" || path.substring(path.length - 5, path.length) == ".brom")) { + if (path.indexOf("wtv-flashrom") && (getFileExt(path).toLowerCase() == "rom" || getFileExt(path).toLowerCase() == "brom")) { return "binary/x-wtv-flashblock"; - } + } else if (getFileExt(path).toLowerCase() == "rmf") { + return "audio/x-rmf"; + } return mime.lookup(path); } async function processPath(socket, path, request_headers = new Array(), query = new Array(), service_name) { var headers, data = null; var request_is_direct_file = false; + var request_is_async_js = false; path = path.replace(/\\/g, "/"); try { try { @@ -140,9 +129,12 @@ async function processPath(socket, path, request_headers = new Array(), query = // file exists, read it and return it console.log(" * Found " + path + " to handle request (Direct File Mode) [Socket " + socket.id +"]"); var contype = getConType(path); - data = new Uint8Array(fs.readFileSync(path)).buffer; + request_is_async_js = true; headers = "200 OK\n" headers += "Content-Type: " + contype; + fs.readFile(path, null, function (err, data) { + sendToClient(socket, headers, data); + }); } else if (fs.existsSync(path + ".txt")) { // raw text format, entire payload expected (headers and content) console.log(" * Found " + path + ".txt to handle request (Raw TXT Mode) [Socket " + socket.id +"]"); @@ -160,16 +152,31 @@ async function processPath(socket, path, request_headers = new Array(), query = } else { headers = fdat; } + } else if (fs.existsSync(path + ".async.js")) { + // asynchronous js scripting, process with vars, must manually call sendToClient(socket, headers, data); + // (hint: socket is already defined) + // loaded script will have r/w access to any JavaScript vars this function does. + // any query args are in an array named 'query' + request_is_async_js = true; + console.log(" * Found " + path + ".async.js to handle request (Async JS Interpreter mode) [Socket " + socket.id + "]"); + // expose var service_dir for script path to the root of the wtv-service + var service_dir = service_vault_dir.replace(/\\/g, "/") + "/" + service_name; + socket_session_data[socket.id].starttime = Math.floor(new Date().getTime() / 1000); + fs.readFile(path + ".async.js", "utf-8", function (err, data) { + eval(data); + }); } else if (fs.existsSync(path + ".js")) { - // js scripting, process with vars, must set 'headers' and 'data' appropriately. + // synchronous js scripting, process with vars, must set 'headers' and 'data' appropriately. // loaded script will have r/w access to any JavaScript vars this function does. // any query args are in an array named 'query' console.log(" * Found " + path + ".js to handle request (JS Interpreter mode) [Socket " + socket.id + "]"); - var service_dir = service_vault_dir.replace(/\\/g,"/") + "/" + service_name; + // expose var service_dir for script path to the root of the wtv-service + var service_dir = service_vault_dir.replace(/\\/g, "/") + "/" + service_name; socket_session_data[socket.id].starttime = Math.floor(new Date().getTime() / 1000); var jscript_eval = fs.readFileSync(path + ".js").toString(); eval(jscript_eval); - } else if (fs.existsSync(path + ".html")) { + } + else if (fs.existsSync(path + ".html")) { // Standard HTML with no headers, WTV Style console.log(" * Found " + path + ".html to handle request (HTML Mode) [Socket " + socket.id +"]"); data = fs.readFileSync(path + ".html").toString(); @@ -184,16 +191,12 @@ async function processPath(socket, path, request_headers = new Array(), query = // 'headers' and 'data' should both be set with content by this point! - if (headers != null) { - if (typeof headers !== "string") { - headers = headers.toString(); - } - } else { + if (headers == null && !request_is_async_js) { var errpage = doErrorPage(400); headers = errpage[0]; data = errpage[1]; console.log(" * Scripting or Data error: Headers were not defined. (headers,data) as follows:") - console.log(headers,data) + console.log(socket.id,headers,data) } if (data === null) { data = ''; @@ -204,7 +207,9 @@ async function processPath(socket, path, request_headers = new Array(), query = data = errpage[1] + "

The interpreter said:
" + e.toString() + "
"; console.log(e); } - return new Array(headers, data); + if (!request_is_async_js) { + sendToClient(socket, headers, data); + } } async function processURL(socket, request_headers) { @@ -225,14 +230,13 @@ async function processURL(socket, request_headers) { query[k] = qraw[i].split("=")[1]; } } - console.log(" * Request query:", query); } } else { shortURL = request_headers['request_url']; } - if (shortURL.indexOf(':/') >= 0) { + if (shortURL.indexOf(':/') >= 0 && shortURL.indexOf('://') < 0) { var ssid = socket_session_data[socket.id].ssid; if (ssid == null) { ssid = request_headers['wtv-client-serial-number']; @@ -245,81 +249,77 @@ async function processURL(socket, request_headers) { reqverb = "Psuedo-encrypted " + reqverb; } if (ssid != null) { - console.log(" * "+reqverb+" for " + request_headers['request_url'] + " from WebTV SSID " + ssid, 'on', socket.id); + console.log(" * " + reqverb + " for " + request_headers['request_url'] + " from WebTV SSID " + ssid, 'on', socket.id); } else { - console.log(" * "+reqverb+" for " + request_headers['request_url'], 'on', socket.id); + console.log(" * " + reqverb + " for " + request_headers['request_url'], 'on', socket.id); } // assume webtv since there is a :/ in the GET var service_name = shortURL.split(':/')[0]; var urlToPath = service_vault_dir.replace(/\\/g, "/") + "/" + service_name + "/" + shortURL.split(':/')[1]; - console.log(request_headers); - var result = await processPath(socket, urlToPath, request_headers, query, service_name); - - if (result[0] == null) { - // error processing path - var errpage = doErrorPage(404); - headers = errpage[0]; - data = errpage[1]; - } else { - headers = result[0]; - data = result[1]; - } + console.log(" * Incoming headers on socket ID", socket.id, request_headers); + processPath(socket, urlToPath, request_headers, query, service_name); + } else if (shortURL.indexOf('http://') >= 0) { + doHTTPProxy(socket, request_headers); } else { - switch (shortURL) { - default: - var errpage = doErrorPage(200); - headers = errpage[0]; - data = errpage[1]; - break; + // error reading headers (no request_url provided) + var errpage = doErrorPage(400); + headers = errpage[0]; + data = errpage[1] + socket_session_data[socket.id].close_me = true; + sendToClient(socket, headers, data); + } + } +} + +async function doHTTPProxy(socket, headers_obj) { + console.log(socket.id, headers_obj); +} + +async function headerStringToObj(headers, response = false) { + var inc_headers = 0; + var headers_obj = new Array(); + var headers_obj_pre = headers.split("\n"); + headers_obj_pre.forEach(function (d) { + if (/^SECURE ON/.test(d) && !response) { + headers_obj['secure'] = true; + //socket_session_data[socket.id].secure_headers = true; + } else if (/^([0-9]{3}) $/.test(d.substring(0, 4)) && response) { + headers_obj['http_response'] = d.replace("\r", ""); + } else if (/^(GET |PUT |POST)$/.test(d.substring(0, 4)) && !response) { + headers_obj['request'] = d.replace("\r", ""); + headers_obj['request_url'] = decodeURI(d.split(' ')[1]).replace("\r", ""); + } else if (d.indexOf(":") > 0) { + var d_split = d.split(':'); + var header_name = d_split[0]; + if (headers_obj[header_name] != null) { + header_name = header_name + "_" + inc_headers; + inc_headers++; + } + d_split.shift(); + d = d_split.join(':'); + headers_obj[header_name] = (d).replace("\r", ""); + if (headers_obj[header_name].substring(0, 1) == " ") { + headers_obj[header_name] = headers_obj[header_name].substring(1); } } - } else { - // error reading headers (no request_url provided) - var errpage = doErrorPage(400); - headers = errpage[0]; - data = errpage[1] - socket_session_data[socket.id].close_me = true; - } - - // headers to object - if (typeof headers != 'object') { - var headers_obj = {}; - var inc_headers = 1; - headers.split('\n').forEach(function (d) { - if (d.length > 0) { - if (d.indexOf(":") > 0 && !/^([0-9]{3} )/.test(d.substring(0, 4))) { - var d = d.split(':'); - var header_name = d[0]; - d.shift(); - if (headers_obj[header_name] != null) { - header_name = header_name + "_" + inc_headers; - inc_headers++; - } - headers_obj[header_name] = d.join(':').replace("\r", ""); - if (headers_obj[header_name].substring(0, 1) == " ") { - headers_obj[header_name] = headers_obj[header_name].substring(1); - } - } else if (/^([0-9]{3} )/.test(d.substring(0, 4))) { - headers_obj['http_response'] = d.replace("\r", ""); - } - } - }); - } else { - header_obj = headers; + }); + return headers_obj; +} + +async function sendToClient(socket, headers_obj, data) { + var headers = ""; + if (typeof (headers_obj) === 'string') { + // string to header object + headers_obj = await headerStringToObj(headers_obj, true); } + // add Connection header if missing, default to Keep-Alive if (!headers_obj['Connection']) { headers_obj['Connection'] = "Keep-Alive"; - headers_obj = moveObjectElement('Connection','http_response', headers_obj); + headers_obj = moveObjectElement('Connection', 'http_response', headers_obj); } - if (request_headers['psuedo-encryption'] || getSessionData(socket_session_data[socket.id].ssid, 'box-does-psuedo-encryption')) { - request_headers['psuedo-encryption'] = true; - headers_obj['wtv-encrypted'] = true; - headers_obj = moveObjectElement('wtv-encrypted', 'Connection', headers_obj); - } - - // set wtv-encrypted and put it near the top of the headers (unknown if needed) + // encrypt if needed if (socket_session_data[socket.id].secure == true) { var clen = null; if (typeof data.length !== 'undefined') { @@ -327,23 +327,17 @@ async function processURL(socket, request_headers) { } else if (typeof data.byteLength !== 'undefined') { clen = data.byteLength; } - headers_obj['wtv-encrypted'] = true; + headers_obj['wtv-encrypted'] = 'true'; headers_obj = moveObjectElement('wtv-encrypted', 'Connection', headers_obj); if (clen > 0) { console.log(" * Encrypting response to client ...") - var data_type = typeof (data); - if (typeof (data) === 'string') { - data = CryptoJS.enc.Utf8.parse(data); - } - if (data.constructor === ArrayBuffer) { - data = CryptoJS.lib.WordArray.create(data); - } - var enc_data = sec_session[socket.id].Encrypt(1,data); + var enc_data = socket_session_data[socket.id].wtvsec.Encrypt(1, data); data = enc_data; } } - if (!headers_obj["Content-length"] && !headers_obj["Content-length"]) { + // set content-length after encryption + if (!headers_obj["Content-length"] && !headers_obj["Content-Length"]) { if (typeof data.length !== 'undefined') { headers_obj['Content-Length'] = data.length; } else if (typeof data.byteLength !== 'undefined') { @@ -351,8 +345,8 @@ async function processURL(socket, request_headers) { } } - headers = ""; - console.log(headers_obj); + // header object to string + console.log(" * Outgoing headers on socket ID", socket.id, headers_obj); Object.keys(headers_obj).forEach(function (k) { if (k == "http_response") { headers += headers_obj[k] + "\r\n"; @@ -362,27 +356,32 @@ async function processURL(socket, request_headers) { headers += j + ": " + headers_obj[k] + "\n"; } else { headers += k + ": " + headers_obj[k] + "\n"; - } + } } }); + + + // send to client var toClient = null; if (typeof data == 'string') { toClient = headers + "\n" + data; socket.write(toClient); } else if (typeof data == 'object') { if (socket_session_data[socket.id].secure_headers == true) { - var enc_headers = sec_session[socket.id].Encrypt(1,headers+"\n"); + // encrypt headers + var enc_headers = socket_session_data[socket.id].wtvsec.Encrypt(1, headers + "\n"); socket.write(new Uint8Array(concatArrayBuffer(enc_headers, data))); } else { socket.write(new Uint8Array(concatArrayBuffer(Buffer.from(headers + "\n"), data))); } } + socket_session_data[socket.id].buffer = null; + if (socket_session_data[socket.id].close_me) socket.end(); if (headers_obj['Connection']) { if (headers_obj['Connection'].toLowerCase() == "close") { socket.destroy(); } } - } function concatArrayBuffer(buffer1, buffer2) { @@ -419,16 +418,16 @@ function headersAreStandard(string, verbose) { // the test will see the binary compressed/enrypted data as ASCII, so a generic "isAscii" // is not suffuicent. This checks for characters expected in unecrypted headers, and returns // true only if every character in the string matches the regex. Once we know the string is binary - // we can better process it with the raw base64 data in processHeaders() below. - var test = /^([A-Za-z0-9\+\/\=\-\.\,\ \;\:\?\&\r\n\(\)\%\<\>\_]{8,})$/.test(string); + // we can better process it with the raw base64 data in processRequest() below. + var test = /^([A-Za-z0-9\+\/\=\-\.\,\ \"\;\:\?\&\r\n\(\)\%\<\>\_]{8,})$/.test(string); if (verbose) { if (zdebug) console.log(" # Request is ascii: " + test); if (zdebug) console.log(" # Request is SECURE ON: " + /^SECURE ON/.test(string)); } return test; - } +} -async function processHeaders(socket, data_hex, returnHeadersBeforeSecure = false, encryptedRequest = false) { +async function processRequest(socket, data_hex, returnHeadersBeforeSecure = false, encryptedRequest = false) { var url = ""; var data = CryptoJS.enc.Latin1.stringify(CryptoJS.enc.Hex.parse(data_hex)); @@ -440,40 +439,24 @@ async function processHeaders(socket, data_hex, returnHeadersBeforeSecure = fals } else { data = data.split("\n\n")[0]; } - if (headersAreStandard(data, (!returnHeadersBeforeSecure && !encryptedRequest))) { - data.split('\n').forEach(function (d) { - if (d.length > 0) { - if (/^SECURE ON/.test(d)) { - headers['secure'] = true; - //socket_session_data[socket.id].secure_headers = true; - } - if (d.indexOf(":") > 0 && d.indexOf(":/") == -1) { - headers[d.split(':')[0]] = (d.split(':')[1]).replace("\r", ""); - if (headers[d.split(':')[0]].substring(0, 1) == " ") { - headers[d.split(':')[0]] = headers[d.split(':')[0]].substring(1); - } - } else if (/^(GET |PUT |POST)$/.test(d.substring(0, 4))) { - headers['request'] = d.replace("\r", ""); - headers['request_url'] = (d.split(' ')[1]).replace("\r", ""); - } - } - }); + if (headersAreStandard(data)) { + headers = await headerStringToObj(data); } else if (!returnHeadersBeforeSecure) { // if its a POST request, assume its a binary blob and not encrypted (dangerous) if (!encryptedRequest) { // its not a POST and it 1failed the headersAreStandard test, so we think this is an encrypted blob if (socket_session_data[socket.id].secure != true) { // first time so reroll sessions - sec_session[socket.id] = new WTVSec(); - sec_session[socket.id].IssueChallenge(); - if (zdebug) console.log(" # [ UNEXPECTED BINARY BLOCK ] First sign of encryption, re-creating RC4 sessions for socket id",socket.id); - sec_session[socket.id].SecureOn(); + if (zdebug) console.log(" # [ UNEXPECTED BINARY BLOCK ] First sign of encryption, re-creating RC4 sessions for socket id", socket.id); + socket_session_data[socket.id].wtvsec = new WTVSec(); + socket_session_data[socket.id].wtvsec.IssueChallenge(); + socket_session_data[socket.id].wtvsec.SecureOn(); socket_session_data[socket.id].secure = true; - } + } var enc_data = CryptoJS.enc.Hex.parse(data_hex.substring(header_length * 2)); if (enc_data.sigBytes > 0) { - var dec_data = CryptoJS.lib.WordArray.create(sec_session[socket.id].Decrypt(0,enc_data)); - var secure_headers = await processHeaders(socket, dec_data.toString(CryptoJS.enc.Hex), true, true); + var dec_data = CryptoJS.lib.WordArray.create(socket_session_data[socket.id].wtvsec.Decrypt(0, enc_data)); + var secure_headers = await processRequest(socket, dec_data.toString(CryptoJS.enc.Hex), true, true); headers['encrypted'] = true; Object.keys(secure_headers).forEach(function (k, v) { headers[k] = secure_headers[k]; @@ -491,8 +474,8 @@ async function processHeaders(socket, data_hex, returnHeadersBeforeSecure = fals } } if (headers['wtv-incarnation'] != null) { - if (sec_session[socket.id]) { - sec_session[socket.id].set_incarnation(headers['wtv-incarnation']); + if (socket_session_data[socket.id].wtvsec) { + socket_session_data[socket.id].wtvsec.set_incarnation(headers['wtv-incarnation']); } else { setSessionData(socket_session_data[socket.id].ssid, 'incarnation', headers['wtv-incarnation']) } @@ -504,19 +487,19 @@ async function processHeaders(socket, data_hex, returnHeadersBeforeSecure = fals } if (headers['secure'] === true) { - if (!sec_session[socket.id]) { + if (!socket_session_data[socket.id].wtvsec) { console.log(" * Starting new WTVSec instance on socket", socket.id); - sec_session[socket.id] = new WTVSec(); - sec_session[socket.id].DecodeTicket(headers['wtv-ticket']); - sec_session[socket.id].ticket_b64 = headers['wtv-ticket']; + socket_session_data[socket.id].wtvsec = new WTVSec(); + socket_session_data[socket.id].wtvsec.DecodeTicket(headers['wtv-ticket']); + socket_session_data[socket.id].wtvsec.ticket_b64 = headers['wtv-ticket']; if (getSessionData(socket_session_data[socket.id].ssid, 'incarnation')) { - sec_session[socket.id].incarnation = getSessionData(socket_session_data[socket.id].ssid, 'incarnation'); + socket_session_data[socket.id].wtvsec.incarnation = getSessionData(socket_session_data[socket.id].ssid, 'incarnation'); } - sec_session[socket.id].SecureOn(); + socket_session_data[socket.id].wtvsec.SecureOn(); } if (socket_session_data[socket.id].secure != true) { // first time so reroll sessions - if (zdebug) console.log(" # [ SECURE ON BLOCK ("+socket.id+")]"); + if (zdebug) console.log(" # [ SECURE ON BLOCK (" + socket.id + ")]"); socket_session_data[socket.id].secure = true; } if (!headers['request_url']) { @@ -533,16 +516,16 @@ async function processHeaders(socket, data_hex, returnHeadersBeforeSecure = fals if (headersAreStandard(enc_data.toString(CryptoJS.enc.Latin1), (!returnHeadersBeforeSecure && !encryptedRequest))) { // some builds (like our targeted 3833), send SECURE ON but then unencrypted headers if (zdebug) console.log(" # Psuedo-encrypted Request (SECURE ON)", "on", socket.id); - // don't actually encrypt output + // don't actually encrypt output headers['psuedo-encryption'] = true; setSessionData(socket_session_data[socket.id].ssid, 'box-does-psuedo-encryption', true); socket_session_data[socket.id].secure = false; - var secure_headers = await processHeaders(socket, enc_data.toString(CryptoJS.enc.Hex), true); + var secure_headers = await processRequest(socket, enc_data.toString(CryptoJS.enc.Hex), true); } else { // SECURE ON and detected encrypted data setSessionData(socket_session_data[socket.id].ssid, 'box-does-psuedo-encryption', false); - var dec_data = CryptoJS.lib.WordArray.create(sec_session[socket.id].Decrypt(0, enc_data)) - var secure_headers = await processHeaders(socket, dec_data.toString(CryptoJS.enc.Hex), true); + var dec_data = CryptoJS.lib.WordArray.create(socket_session_data[socket.id].wtvsec.Decrypt(0, enc_data)) + var secure_headers = await processRequest(socket, dec_data.toString(CryptoJS.enc.Hex), true); if (zdebug) console.log(" # Encrypted Request (SECURE ON)", "on", socket.id); } // Merge new headers into existing headers object @@ -552,17 +535,31 @@ async function processHeaders(socket, data_hex, returnHeadersBeforeSecure = fals } } } - headers = await checkForPostData(socket, headers, data, data_hex); - return headers; + headers = await checkForPostData(socket, headers, data); + if (!headers['request_url']) { + // still no url, likely lost encryption stream, tell client to relog + socket_session_data[socket.id].secure = false; + headers = `200 OK +Connection: Keep-Alive +Expires: Wed, 09 Oct 1991 22:00:00 GMT +wtv-expire-all: wtv-head-waiter: +wtv-expire-all: wtv-1800: +wtv-visit: client:relog +Content-type: text/html`; + data = ''; + delete socket_session_data[socket.id].wtvsec; + sendToClient(socket, headers, data); + } else { + processURL(socket, headers); + } } else { // socket error, terminate it. socket.destroy(); } } - return null; } -async function checkForPostData(socket, headers, data, data_hex) { +async function checkForPostData(socket, headers, data) { if (headers['request']) { if (headers['request'].substring(0, 4) == "POST") { if (data_hex.indexOf("0d0a0d0a") != -1) { @@ -573,7 +570,7 @@ async function checkForPostData(socket, headers, data, data_hex) { var header_length = data.length + 2; } if (socket_session_data[socket.id].secure == true) { - var enc_data = CryptoJS.enc.Hex.parse(data_hex.substring(header_length * 2)); + var enc_data = CryptoJS.enc.Hex.parse(socket_session_data[socket.id].buffer.toString(CryptoJS.enc.Hex).substring(header_length * 2)); if (enc_data.sigBytes > 0) { if (headersAreStandard(enc_data.toString(CryptoJS.enc.Latin1))) { // some builds (like our targeted 3833), send SECURE ON but then unencrypted headers @@ -582,17 +579,17 @@ async function checkForPostData(socket, headers, data, data_hex) { headers['psuedo-encryption'] = true; setSessionData(socket_session_data[socket.id].ssid, 'box-does-psuedo-encryption', true); socket_session_data[socket.id].secure = false; - headers['post_data'] = await processHeaders(socket, enc_data.toString(CryptoJS.enc.Hex), true); + headers['post_data'] = await processRequest(socket, enc_data.toString(CryptoJS.enc.Hex), true); } else { // SECURE ON and detected encrypted data setSessionData(socket_session_data[socket.id].ssid, 'box-does-psuedo-encryption', false); - headers['post_data'] = CryptoJS.lib.WordArray.create(sec_session[socket.id].Decrypt(0, enc_data)) + headers['post_data'] = CryptoJS.lib.WordArray.create(socket_session_data[socket.id].wtvsec.Decrypt(0, enc_data)) if (zdebug) console.log(" # Encrypted POST Content (SECURE ON)", "on", socket.id); } } } else { if (zdebug) console.log(" # Unencrypted POST Content", "on", socket.id); - headers['post_data'] = CryptoJS.enc.Hex.parse(data_hex.substring(header_length * 2)); + headers['post_data'] = CryptoJS.enc.Hex.parse(socket_session_data[socket.id].buffer.toString(CryptoJS.enc.Hex).substring(header_length * 2)); } } } @@ -602,36 +599,35 @@ async function checkForPostData(socket, headers, data, data_hex) { async function cleanupSocket(socket) { try { console.log(" * Destroying old WTVSec instance on disconnected socket", socket.id); - delete socket_buffer[socket.id]; + delete socket_session_data[socket.id].buffer; + + delete socket_session_data[socket.id].wtvsec; delete socket_session_data[socket.id]; - delete sec_session[socket.id]; socket.end(); } catch (e) { console.log(" # Could not clean up socket data for socket ID", socket.id, e); } -}; +} async function handleSocket(socket) { // create unique socket id with client address and port - socket.id = parseInt(crc16.checkSum(Buffer.from(String(socket.remoteAddress) + String(socket.remotePort), "utf8")).toString("hex"),16); + socket.id = parseInt(crc16.checkSum(Buffer.from(String(socket.remoteAddress) + String(socket.remotePort), "utf8")).toString("hex"), 16); socket_session_data[socket.id] = []; socket.setEncoding('hex'); //set data encoding (either 'ascii', 'utf8', or 'base64') socket.on('data', function (data_hex) { socket.setTimeout(300); - if (socket_buffer[socket.id]) { - socket_buffer[socket.id].concat(CryptoJS.enc.Hex.parse(data_hex)); + if (socket_session_data[socket.id].buffer) { + socket_session_data[socket.id].buffer.concat(CryptoJS.enc.Hex.parse(data_hex)); } else { - socket_buffer[socket.id] = CryptoJS.enc.Hex.parse(data_hex); + socket_session_data[socket.id].buffer = CryptoJS.enc.Hex.parse(data_hex); } }); socket.on('timeout', async function () { socket.setTimeout(0); - var phead = await processHeaders(this, socket_buffer[socket.id].toString(CryptoJS.enc.Hex)); - processURL(this, phead); - if (socket_session_data[socket.id].close_me) socket.end(); - socket_buffer[socket.id] = null; + // start the async chain + processRequest(this, socket_session_data[socket.id].buffer.toString(CryptoJS.enc.Hex)); }); socket.on('error', (err) => { @@ -657,9 +653,10 @@ Object.keys(services_configured.services).forEach(function (k) { if (!services_configured.services[k].host) { services_configured.services[k].host = service_ip; } - if (services_configured.services[k].port) { + if (services_configured.services[k].port && !services_configured.services[k].nobind) { ports.push(services_configured.services[k].port); } + services_configured.services[k].toString = function () { var outstr = "wtv-service: name=" + this.name + " host=" + this.host + " port=" + this.port; if (this.flags) outstr += " flags=" + this.flags; @@ -671,7 +668,7 @@ Object.keys(services_configured.services).forEach(function (k) { } return outstr; } - console.log(" * Configured Service", k, "on port", services_configured.services[k].port); + console.log(" * Configured Service", k, "on Port", services_configured.services[k].port, "- Host", services_configured.services[k].host, "- Bind Port:", !services_configured.services[k].nobind); }) var initstring = ''; diff --git a/hacktv_updsrv/hacktv_updsrv.njsproj b/hacktv_updsrv/hacktv_updsrv.njsproj index 44cc8b5b..ce1ce0b2 100644 --- a/hacktv_updsrv/hacktv_updsrv.njsproj +++ b/hacktv_updsrv/hacktv_updsrv.njsproj @@ -36,11 +36,11 @@ - + - + diff --git a/hacktv_updsrv/package.json b/hacktv_updsrv/package.json index 3be989a9..c5dd664a 100644 --- a/hacktv_updsrv/package.json +++ b/hacktv_updsrv/package.json @@ -1,6 +1,6 @@ { "name": "hacktv_updsrv", - "version": "0.6.1", + "version": "0.7.0", "description": "hacktv_updsrv", "main": "app.js", "author": { diff --git a/hacktv_updsrv/services.json b/hacktv_updsrv/services.json index e2b3db09..0d5fb469 100644 --- a/hacktv_updsrv/services.json +++ b/hacktv_updsrv/services.json @@ -42,6 +42,10 @@ "port": 1656, "flags": "0x04", "connections": 3 + }, + "http": { + "port": 1650, + "connections": 3 } } } diff --git a/hacktv_updsrv/wtvsec.js b/hacktv_updsrv/wtvsec.js index 485b1b2e..7cc8c863 100644 --- a/hacktv_updsrv/wtvsec.js +++ b/hacktv_updsrv/wtvsec.js @@ -264,7 +264,12 @@ class WTVSec { if (!this.RC4Session[session_id]) { this.NewRC4Session(session_id); } - return this.RC4Session[session_id].update(Buffer.from(this.wordArrayToUint8Array(data))); + if (data.words) { + data = new Buffer.from(this.wordArrayToUint8Array(data)); + } else if (data.constructor === ArrayBuffer) { + data = new Buffer.from(data); + } + return this.RC4Session[session_id].update(data); } Decrypt(keynum, data) { @@ -283,7 +288,12 @@ class WTVSec { if (!this.RC4Session[session_id]) { this.NewRC4Session(session_id); } - return this.RC4Session[session_id].update(Buffer.from(this.wordArrayToUint8Array(data))); + if (data.words) { + data = new Buffer.from(this.wordArrayToUint8Array(data)); + } else if (data.constructor === ArrayBuffer) { + data = new Buffer.from(data); + } + return this.RC4Session[session_id].update(data); } Test() {