From a5806e858147f42c8661a1332eded933089ae433 Mon Sep 17 00:00:00 2001 From: zefie Date: Sun, 25 Jul 2021 03:19:19 -0400 Subject: [PATCH] v0.9.6 - update: add initial wtv-capability-flags decoding, as well as wtv-tricks:/info demonstration - update: rename classes - minor update: quirky 'Special Thanks' in each custom class. - minor update: notice about Initial Shared Key and multiple minisrvs - update: wtv-music:/demo/index: update wtv-star image paths - update: app.js: fix unencrypted post - update: app.js: improve buffering and cleanup in attempt to fix occasional 'double-up' bug - update: info.js: remove debug dump of capabilities - Update: add test.js, syntax-testing script for `npm test` - Update: wtv-chat:/home experimental nick change page thanks to MattMan (chat still giving issues on real boxes, works in Viewer) - Update: README.md: Add ways to support the project --- README.md | 6 + .../ServiceVault/wtv-chat/home.html | 123 ++++++++++++++ .../ServiceVault/wtv-chat/images/htv_chat.gif | Bin 0 -> 3744 bytes .../ServiceVault/wtv-chat/images/htv_chat.jpg | Bin 0 -> 2051 bytes .../ServiceVault/wtv-home/home.js | 7 +- .../ServiceVault/wtv-music/demo/index.html | 10 +- .../wtv-tricks/images/About_bg.jpg | Bin 0 -> 2240 bytes .../ServiceVault/wtv-tricks/info.js | 148 +++++++++++++++++ zefie_wtvp_minisrv/WTVClientCapabilities.js | 150 ++++++++++++++++++ ...ession_data.js => WTVClientSessionData.js} | 13 +- zefie_wtvp_minisrv/{wtvsec.js => WTVSec.js} | 16 +- zefie_wtvp_minisrv/app.js | 87 ++++++++-- zefie_wtvp_minisrv/package.json | 5 +- zefie_wtvp_minisrv/test.js | 33 ++++ zefie_wtvp_minisrv/zefie_wtvp_minisrv.njsproj | 8 +- 15 files changed, 573 insertions(+), 33 deletions(-) create mode 100644 zefie_wtvp_minisrv/ServiceVault/wtv-chat/home.html create mode 100644 zefie_wtvp_minisrv/ServiceVault/wtv-chat/images/htv_chat.gif create mode 100644 zefie_wtvp_minisrv/ServiceVault/wtv-chat/images/htv_chat.jpg create mode 100644 zefie_wtvp_minisrv/ServiceVault/wtv-tricks/images/About_bg.jpg create mode 100644 zefie_wtvp_minisrv/WTVClientCapabilities.js rename zefie_wtvp_minisrv/{session_data.js => WTVClientSessionData.js} (66%) rename zefie_wtvp_minisrv/{wtvsec.js => WTVSec.js} (95%) create mode 100644 zefie_wtvp_minisrv/test.js diff --git a/README.md b/README.md index 5e88c245..c5e0ea95 100644 --- a/README.md +++ b/README.md @@ -44,3 +44,9 @@ This open source server is in alpha status. Use at your own risk. - Test with a WebTV Viewer or connect with a real box - To connect with a real box, you will need to open ports in your firewall and have a way to connect your WebTV (and preferably reroute 10.0.0.1 to the server) - See [ServiceVault.md](ServiceVault.md) for a brief introduction to how the service files work + +### How to Support the Project +- [Report Bugs](https://github.com/zefie/zefie_wtvp_minisrv/issues) +- [Add a Feature and send a Pull Request](https://github.com/zefie/zefie_wtvp_minisrv/pulls) +- Write and submit better documentation than I created (see Pull Request above) +- [Support financially on Patreon](https://www.patreon.com/zefie) \ No newline at end of file diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-chat/home.html b/zefie_wtvp_minisrv/ServiceVault/wtv-chat/home.html new file mode 100644 index 00000000..2f14e323 --- /dev/null +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-chat/home.html @@ -0,0 +1,123 @@ + + + +Chat Home + + + + + + + +
+ + + +
+ +
+ + + + + +
+ + + + +
+
+ + + + + + + +
+
+
+
+ + + + + + + +
+ +
+ +
+ + + + +
+ +
+
+ + +
+ + +
   + + + + +
+ + +
+Chat Home +
+ + +
+
+
+ + + + + + + +
+
+ +yo yo yo yo + +enter your nick... + + +
+ + + + +
+
+ + +
+
+ + + +
+ + + + + + + +
+
+ + diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-chat/images/htv_chat.gif b/zefie_wtvp_minisrv/ServiceVault/wtv-chat/images/htv_chat.gif new file mode 100644 index 0000000000000000000000000000000000000000..7b19a7d967c79f8935b16c821df5fc30f76b917b GIT binary patch literal 3744 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
- +
@@ -35,7 +35,7 @@ WebTV Music Index
- +
@@ -48,7 +48,7 @@ WebTV Music Index
- +
@@ -74,7 +74,7 @@ WebTV Music Index
- +
@@ -87,7 +87,7 @@ WebTV Music Index
- +
diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-tricks/images/About_bg.jpg b/zefie_wtvp_minisrv/ServiceVault/wtv-tricks/images/About_bg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dbfb1d9e29c022e7313d58e03c2ff9f232921d98 GIT binary patch literal 2240 zcmbW0dstKV8OP7bNdj^!ffEdI)yOr8pj;9_CN@wGBoQb%AyA^T5I~ZsTm=MmPY?<< zWhfUB1CN6c?hhge-d1a=q7|evQo0M73paVSw2I&Cp~L7BPJzDo|e8VJvG10 zgCR&uTJP^07HWJ7*#sx2%Tk~e6$C-B@d;$Rm=3|9rKzLwHdGART3XpyS=icG+t^WT z?P*Lp&B1}@?dQh+JXzm=+GeE`1p1JtSvJS4W za9Bxmq`SJgGg)j7*Uvv-W8lZ3SXek;AQZ<)V&guImnJ2r>`0X>l-YZ7a`X1)7ZeqL zUQ+tSA$4U{_0gJRwZ}EuCS7w&Yg>Cq@9Dn&GiT48zwqtlD}z_BeRq9GKRz)z^~3Zn z!~K~Dvp+uk>Cxl4=PwpszIy%ho44;^2zigQO!O-r3g96Si3rim!~+xZfDj6iwAPz! z7c4SM%(Qp%DKtkzDjItRRyh0ODjF$)J5&VASCCZt88{|@Nj|3$P6w9EruMg%e% zAS(n0S3@Y!N6=4|U6&0V?!vRyK8}rM!gvMaaa3h3^`>UtQRhH|NWuu6^S*!ABn3c43pu!&d zxc&M8LrYFTTjB$1W?a@JgGJ@MG~@bSE7BK7DLCdki_WwUSt|u=uE*$v6{mv=+g!-q zy%L3;igk+XNbW*gYM=9lZbz<)6ozwZ8$9G;=i4V)6kgJovTrz(yVlxm zihro7ScskU_q25QK-g(9>l6M$;%Kj#uKcK~SyZ`MDP=#*Zl%WS`&@;pYxpSoRY!@a zFiuCKMkV4Tzj-X6Tt0}{S%-u&{BH!!#~NOwRh9g^sTBZVi1N8Aj(VohXqi3~3iV2EqjK9CFGiW(YXnb{kDU9GF|}(fsXtw?T3J1cNVh-C?J4re!TIxPauQxci5|Vtw1#uy zv;OVgR{H#qhSW6}8tttZQdEO0UVYQ~di_q~`ldahfLzqE;z;ni{gEV zcl*wiJ!2P+WMFv(yWicDmfZY4AW^b%G;cjO_UPXB_&Kv9HvJu;e$n+5O`|urlNC!9 z$_pPyHac?;jkrZdjP0K|AxV{$yQ!J&G+d955{huvAQnZgBHY0frhX}HqpgSBe@wH^ zW>0tKE>IJ)wxafDRyW771_AA}$e##&3HC4P| zpZ=P?mE|;6yQ6Akb${sf@H%m3eKRVG_R^}EumW>Mp=Zj_UG!q{gdS-ceY!cxF$hyc zWNw=}?td(!^xlPyQE^<|>KglXr)RXeBX+-$6E_t2gGu*|7eb%eS+vm(Z+C`Z?vxq#z@ z&ruOS5gdvEaA^1##(<|zIwx4PT~sDaw~&i{7R2Z8^w+JLIT`L2hr-C=+Hqz08m=1? z@d2NBFerpE6sPq82kh~MNOXtFC3f%=w0<&LF*n^pnz{9`YV)MUCG<~^n2jKrC&Ew& z$clxbeJUx7*+6P0*KOgImdqT>Z(9hH(}tMZ+Jm4qLCwM#01CANYauStA*(P{Y%2;^ zGGVz^&0A&$(;b^QBA#`#PFrhf0OhhV!503KJ(I;n5paAg0yLJxEV=dw3C(M|xU@GO zm?vbZM3^h&2^K;E%Yc3**mN2paAt{p_((?py?G>5v@9}n5RFC@3YJ2J>qUG*P1QwD z(Ae<)q^8=%dKQQSAeEG_X2Nv4>b`2zR=zQ%NkN+F&wr>|gn5}t9MsIe@d=CT zF|CN%K-8+Gi_RAPc)EHrV6dIV5TUYWX)rJj!oXK0X#elwLa7kU0;%N_BE$PQ=(kL+ MYt}7x`M`MVUsHZ + + +${minisrv_config.config.service_name} Info + + + + + + +
+
+
+ +

${minisrv_config.config.service_name} Info

+ +
+ + + + + + + + + + + + + + +
+
Connected to: + + Mini Service +
Service: + + ${z_title} +
Client: + + &vers; (Build ${wtv_system_version} [${client_label}]) +
Boot: + + &wtv-bootvers; (Build ${wtv_client_bootrom_version} [${boot_client_label}]) +
+ Silicon serial ID: + + ${wtv_client_serial_number} +
Connected at: + + &rate; +
Client IP number: + + ${socket.remoteAddress} +
+
ROM type: + + ${wtv_client_rom_type} +
Modem f/w (when available): + + &modem; +`; + if (ssid_sessions[socket.ssid].get("wtv-need-upgrade")) { + data += `
Mini-browser: + + Yes +`; + } + data += ` +
Chip version: + + ${wtv_system_chipversion_str} (TODO) +
SysConfig: + + 0x${wtv_system_sysconfig_hex.toUpperCase()} +
+ + + + +
+
Client capabilities: + + +
+ +`; + + + // start loop + + Object.keys(capabilities_table).forEach(function (k) { + data += ` +
${capabilities_table[k][1]} + + `; + if (client_caps[capabilities_table[k][0]]) data += "True\n"; + else data += "False\n"; + }); + +// end loop + +data += ` +
+ +
+
+
+${wtv_system_sysconfig_str}
+
+
+ + `; +} else { + var errpage = doErrorPage(400); + headers = errpage[0]; + data = errpage[1]; +} \ No newline at end of file diff --git a/zefie_wtvp_minisrv/WTVClientCapabilities.js b/zefie_wtvp_minisrv/WTVClientCapabilities.js new file mode 100644 index 00000000..587586cb --- /dev/null +++ b/zefie_wtvp_minisrv/WTVClientCapabilities.js @@ -0,0 +1,150 @@ +class WTVClientCapabilities { + + /***********************************\ + |* Special Thanks to: *| + |* Outatyme *| + |* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *| + |* For the binary information *| + |* about capability flags *| + \***********************************/ + + capabilities = null; + capabilities_table = null; + + + constructor(wtv_capability_flags = null) { + // [ flag_name, friendly_flag_name ] + // so far we assume the reversed bit order = the order on wtv-tricks:/info (production service) + // also speculation that `client-has-relogin-function` is forced true on the service side + // (this script does not do that, also note that LC2 MiniBrowser does not support client:relog) + // None of this is 100% for certain yet (except the bitfield stuff), do not trust as verbatim, more testing needed + + var capabilities_table = [ + ["client-can-do-muzac", "Can Do Muzac"], + ["client-can-do-chat", "Can Chat"], + ["client-can-do-openISP", "Can do OpenISP"], + ["client-can-receive-compressed-data", "Can receive compressed data"], + ["client-can-display-spotads1", "Can show Spotads1"], + ["client-can-print", "Can Print"], + ["client-can-do-macromedia-flash1", "Can do Macromedia Flash1"], + ["client-can-do-javascript", "Can do JavaScript"], + ["client-can-do-videoflash", "Can do VideoFlash"], + ["client-can-do-videoads", "Can do VideoAds"], + ["client-has-disk", "Has Disk"], + ["client-supports-classical-service", "Supports Classical"], + ["client-open-isp-settings-valid", "OISP settings valid"], + ["client-can-tell-valid-open-isp", "Can tell OISP settings valid"], + ["client-has-tuner", "Has Tuner"], + ["client-can-data-download", "Can data download"], + ["client-supports-approx-content-len", "Supports approximate content length"], + ["client-has-built-in-printer-port", "Has built-in printer port"], + ["client-has-tv-experience", "Has TV experience"], + ["client-can-handle-proxy-bypass", "Can handle proxy bypass"], + ["client-can-handle-download-v2", "Can handle Download protocol 2"], + ["client-has-relogin-function", "Has Relogin function"], + ["client-can-display-spotads2", "Can display spotads2"], + ["client-can-display-30-sec-video-ads", "Can display 30 second video ads"], + ["client-supports-etude-service", "Supports Etude"], + ["client-can-do-av-capture", "Can do AV capture"], + ["client-can-do-disconnected-email", "Can do disconnected email"], + ["client-can-do-macromedia-flash2", "Can do Macromedia Flash2"], + ["client-has-memory-size-bit1-set", "Memory size bit1 set"], + ["client-has-memory-size-bit2-set", "Memory size bit2 set"], + ["client-has-memory-size-bit3-set", "Memory size bit3 set"], + ["client-can-do-rmf", "Can do RMF"], + ["client-can-do-png", "Can do PNG"], + ["client-does-broadband-data-dowload", "Supports broadband download"], + ["client-has-softmodem", "Has Softmodem"], + ["client-can-do-preparsed-epg", "Can do pre-parsed EPG"], + ["client-supports-funk-e-service", "Supports Funk-e"], + ["client-wants-dial-script", "Wants dial script"], + ["client-upgrade-visits-not-needed", "Upgrade visits not needed"], + ["client-uses-flexible-videoad-paths", "Uses flexible videoad paths"], + ["client-non-production-build", "Non-production build"], + ["client-can-download-printer-drivers", "Can download printer drivers"], + ["client-supports-hiphop-service", "Supports HipHop"], + ["client-can-use-messenger", "Can use MSN Messenger"], + ["client-uses-third-party-billing", "Uses 3rd-party billing"], + ["client-can-do-offlineads", "Can do offline ads"], + ["client-has-no-dialin-support", "Has no dialin support"], + ["client-has-ssl-support-for-wtvp", "Has SSL support for WTVP"], + ["client-can-do-audio-capture", "Can do audio capture"], + ["client-can-do-metered-pricing", "Can do Metered Pricing"], + ["client-negotiates-user-agent", "Can Negotiate User-Agent"], + ["client-can-do-element-logging", "Can do Unsupported Element Logging"], + ["client-supports-jazz-security", "Supports Jazz security"], + ["client-supports-MSN-service", "Supports MSN service"], + ["client-supports-notify-port-header", "Supports notify port header"], + ["client-supports-messenger-update-light", "Supports MSN Messenger update light"], + ["client-supports-MSN-chat", "Supports MSN Chat"], + ["client-supports-MSN-chat-findu", "Supports MSN Chat FindU"], + ["client-supports-MSN-messenger-CVR", "Supports MSN Messenger CVR"], + ["client-supports-MSN-messenger-MSNP8", "Supports MSN Messenger MSNP8"], + ["client-supports-MSN-chat-R9C", "Supports MSN Chat R9C"] + ]; + + this.capabilities_table = capabilities_table; + + var capabilities = new Array(); + + // might want to pass without a flag to get the table + if (wtv_capability_flags != null) { + + // define function to convert hex string to binary string (0s & 1s) + var hex2bin = function (hex) { + var binary = ""; + var remainingSize = hex.length; + for (var p = 0; p < hex.length / 8; p++) { + //In case remaining hex length (or initial) is not multiple of 8 + var blockSize = remainingSize < 8 ? remainingSize : 8; + + binary += parseInt(hex.substr(p * 8, blockSize), 16).toString(2); + + remainingSize -= blockSize; + } + return binary; + } + + // Add .reverse() to strings for ease of processing + if (!String.prototype.reverse) { + String.prototype.reverse = function () { + var splitString = this.split(""); + var reverseArray = splitString.reverse(); + var joinArray = reverseArray.join(""); + return joinArray; + } + } + + // convert wtv_capability_flags to binary string, reverse the string, and split into array containing each character; + var bitfield = hex2bin(wtv_capability_flags).reverse().split(""); + + // only add to the capabilities array if the result is true + var add = function (flag_name, flag) { + if (flag) capabilities[flag_name] = flag; + } + + // process bitfield and set capabilities + Object.keys(bitfield).forEach(function (k) { + // Convert binary to boolean, 0 to false, 1 to true + var bitfield_result = (bitfield[k] == "1") + + // set flags based on position of bit + add(capabilities_table[k][0], bitfield_result); + }); + + this.capabilities = capabilities; + return capabilities; + } + } + + get(key = null) { + if (typeof (this.capabilities) === 'undefined') return null; + else if (key === null) return this.capabilities; + else if (this.capabilities[key]) return this.capabilities[key]; + else return null; + } + +} + + +module.exports = WTVClientCapabilities; \ No newline at end of file diff --git a/zefie_wtvp_minisrv/session_data.js b/zefie_wtvp_minisrv/WTVClientSessionData.js similarity index 66% rename from zefie_wtvp_minisrv/session_data.js rename to zefie_wtvp_minisrv/WTVClientSessionData.js index f6e14679..7bec66c7 100644 --- a/zefie_wtvp_minisrv/session_data.js +++ b/zefie_wtvp_minisrv/WTVClientSessionData.js @@ -1,4 +1,13 @@ -class ClientSessionData { +class WTVClientSessionData { + + /***********************************\ + |* Special Thanks to: *| + |* No one *| + |* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *| + |* There is literally nothing *| + |* special about this class *| + \***********************************/ + data_store = null; constructor() { @@ -25,4 +34,4 @@ class ClientSessionData { } -module.exports = ClientSessionData; \ No newline at end of file +module.exports = WTVClientSessionData; \ No newline at end of file diff --git a/zefie_wtvp_minisrv/wtvsec.js b/zefie_wtvp_minisrv/WTVSec.js similarity index 95% rename from zefie_wtvp_minisrv/wtvsec.js rename to zefie_wtvp_minisrv/WTVSec.js index 147bc03d..99206228 100644 --- a/zefie_wtvp_minisrv/wtvsec.js +++ b/zefie_wtvp_minisrv/WTVSec.js @@ -2,9 +2,21 @@ const CryptoJS = require('crypto-js'); const endianness = require('endianness'); var crypto = require('crypto'); +/***********************************\ +|* Special Thanks to: *| +|* eMac (Eric MacDonald) *| +|* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *| +|* For the encryption/decryption *| +|* information and process *| +\***********************************/ + class WTVSec { - //initial_shared_key = CryptoJS.lib.WordArray.random(8); - initial_shared_key_b64 = "CC5rWmRUE0o="; // You can change this but it doesn't mean much for security. Just make sure its static. 8 bytes base64 encoded. + // Initial Shared Key, in Base64 Format + // You can change this but it doesn't mean much for security. Just make sure its static. 8 bytes base64 encoded. + // If you intend to link multiple minisrv's together, they must all share the same Initial Shared Key. + + initial_shared_key_b64 = "CC5rWmRUE0o="; + initial_shared_key = null; current_shared_key = null; challenge_key = null; diff --git a/zefie_wtvp_minisrv/app.js b/zefie_wtvp_minisrv/app.js index fddcc4e1..f0e09753 100644 --- a/zefie_wtvp_minisrv/app.js +++ b/zefie_wtvp_minisrv/app.js @@ -9,8 +9,9 @@ const net = require('net'); const CryptoJS = require('crypto-js'); const mime = require('mime-types'); const { crc16 } = require('easy-crc'); -var WTVSec = require('./wtvsec.js'); -var ClientSessionData = require('./session_data.js'); +var WTVSec = require('./WTVSec.js'); +var WTVClientCapabilities = require('./WTVClientCapabilities.js'); +var WTVClientSessionData = require('./WTVClientSessionData.js'); // Where we store our session information var ssid_sessions = new Array(); @@ -20,11 +21,13 @@ var ports = []; // add .reverse() feature to all JavaScript Strings in this application // works for service vault scripts too. -String.prototype.reverse = function () { - var splitString = this.split(""); - var reverseArray = splitString.reverse(); - var joinArray = reverseArray.join(""); - return joinArray; +if (!String.prototype.reverse) { + String.prototype.reverse = function () { + var splitString = this.split(""); + var reverseArray = splitString.reverse(); + var joinArray = reverseArray.join(""); + return joinArray; + } } function getServiceString(service) { @@ -509,7 +512,16 @@ async function sendToClient(socket, headers_obj, data) { } if (zquiet) console.log(" * Sent" + verbosity_mod + " " + headers_obj.http_response + " to client (Content-Type:", headers_obj['Content-Type'], "~", headers_obj['Content-Length'], "bytes)"); } - socket_sessions[socket.id].buffer = null; + + if (socket_sessions[socket.id].expecting_post_data) delete socket_sessions[socket.id].expecting_post_data; + if (socket_sessions[socket.id].header_buffer) delete socket_sessions[socket.id].header_buffer; + if (socket_sessions[socket.id].secure_buffer) delete socket_sessions[socket.id].secure_buffer; + if (socket_sessions[socket.id].buffer) delete socket_sessions[socket.id].buffer; + if (socket_sessions[socket.id].headers) delete socket_sessions[socket.id].headers; + if (socket_sessions[socket.id].post_data) delete socket_sessions[socket.id].post_data; + if (socket_sessions[socket.id].post_data_length) delete socket_sessions[socket.id].post_data_length; + if (socket_sessions[socket.id].post_data_percents_shown) delete socket_sessions[socket.id].post_data_percents_shown; + if (socket_sessions[socket.id].close_me) socket.end(); if (headers_obj["Connection"]) { if (headers_obj["Connection"].toLowerCase() == "close" && wtv_connection_close == "true") { @@ -561,7 +573,7 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq // This function sucks and needs to be rewritten - var headers = null; + var headers = new Array(); if (socket_sessions[socket.id]) { if (socket_sessions[socket.id].headers) { headers = socket_sessions[socket.id].headers; @@ -577,7 +589,7 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq data = data.split("\n\n")[0]; } if (headersAreStandard(data)) { - if (headers != null) { + if (headers.length != 0) { var new_header_obj = headerStringToObj(data); Object.keys(new_header_obj).forEach(function (k, v) { headers[k] = new_header_obj[k]; @@ -621,10 +633,12 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq } } + if (!headers) return; + if (headers["wtv-client-serial-number"] != null) { socket.ssid = headers["wtv-client-serial-number"]; if (!ssid_sessions[socket.ssid]) { - ssid_sessions[socket.ssid] = new ClientSessionData(); + ssid_sessions[socket.ssid] = new WTVClientSessionData(); } if (!ssid_sessions[socket.ssid].data_store.sockets) ssid_sessions[socket.ssid].data_store.sockets = new Set(); ssid_sessions[socket.ssid].data_store.sockets.add(socket); @@ -709,6 +723,15 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq } } + // Passed Security + + if (headers["wtv-capability-flags"] != null) { + if (!ssid_sessions[socket.ssid]) { + ssid_sessions[socket.ssid] = new WTVClientSessionData(); + } + if (!ssid_sessions[socket.ssid].data_store.capabilities) ssid_sessions[socket.ssid].data_store.capabilities = new WTVClientCapabilities(headers["wtv-capability-flags"]); + } + // log all client wtv- headers to the SessionData for that SSID // this way we can pull up client info such as wtv-client-rom-type or wtv-system-sysconfig @@ -782,8 +805,23 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq // SECURE ON and detected encrypted data ssid_sessions[socket.ssid].set("box-does-psuedo-encryption", false); var dec_data = CryptoJS.lib.WordArray.create(socket_sessions[socket.id].wtvsec.Decrypt(0, enc_data)) - var secure_headers = await processRequest(socket, dec_data.toString(CryptoJS.enc.Hex), true, true); + if (!socket_sessions[socket.id].secure_buffer) socket_sessions[socket.id].secure_buffer = ""; + socket_sessions[socket.id].secure_buffer += dec_data.toString(CryptoJS.enc.Hex); + var secure_headers = null; + if (headers['request']) { + if (headers['request'] == "GET") { + if (socket_sessions[socket.id].secure_buffer.indexOf("0d0a0d0a") || socket_sessions[socket.id].secure_buffer.indexOf("0a0a")) { + secure_headers = await processRequest(socket, socket_sessions[socket.id].secure_buffer, true, true); + } + } else { + secure_headers = await processRequest(socket, socket_sessions[socket.id].secure_buffer, true, true); + } + } else { + secure_headers = await processRequest(socket, socket_sessions[socket.id].secure_buffer, true, true); + } if (!secure_headers) return; + + delete socket_sessions[socket.id].secure_buffer; if (zdebug) console.log(" # Encrypted Request (SECURE ON)", "on", socket.id); if (zshowheaders) console.log(secure_headers); if (!secure_headers.request) { @@ -839,11 +877,14 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq } if (socket_sessions[socket.id].post_data.length == (socket_sessions[socket.id].post_data_length * 2)) { // got all expected data + if (socket_sessions[socket.id].expecting_post_data) delete socket_sessions[socket.id].expecting_post_data; console.log(" * Incoming", post_string, "request on", socket.id, "from", filterSSID(socket.ssid), "to", headers['request_url'], "(got all expected", socket_sessions[socket.id].post_data_length, "bytes of data from client already)"); headers.post_data = CryptoJS.enc.Hex.parse(socket_sessions[socket.id].post_data); + if (socket_sessions[socket.id].headers) delete socket_sessions[socket.id].headers; processURL(socket, headers); } else { // expecting more data (see below) + socket_sessions[socket.id].expecting_post_data = true; console.log(" * Incoming", post_string, "request on", socket.id, "from", filterSSID(socket.ssid), "to", headers['request_url'], "(expecting", socket_sessions[socket.id].post_data_length, "bytes of data from client...)"); } if (socket_sessions[socket.id].post_data.length > (socket_sessions[socket.id].post_data_length * 2)) { @@ -909,6 +950,7 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq } if (socket_sessions[socket.id].post_data.length == (socket_sessions[socket.id].post_data_length * 2)) { // got all expected data + if (socket_sessions[socket.id].expecting_post_data) delete socket_sessions[socket.id].expecting_post_data; headers.post_data = CryptoJS.enc.Hex.parse(socket_sessions[socket.id].post_data); if (socket_sessions[socket.id].secure == true) { if (zdebug) console.log(" # Encrypted POST Content (SECURE ON)", "on", socket.id, "[", headers.post_data.sigBytes, "bytes ]"); @@ -922,6 +964,7 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq return; } if (socket_sessions[socket.id].post_data.length > (socket_sessions[socket.id].post_data_length * 2)) { + if (socket_sessions[socket.id].expecting_post_data) delete socket_sessions[socket.id].expecting_post_data; // got too much data ? ... should not ever reach this code var errpage = doErrorPage(400, "Received too much data in POST request
Got " + (socket_sessions[socket.id].post_data.length / 2) + ", expected " + socket_sessions[socket.id].post_data_length); headers = errpage[0]; @@ -956,7 +999,18 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq } if (!socket_sessions[socket.id].secure_buffer) socket_sessions[socket.id].secure_buffer = ""; socket_sessions[socket.id].secure_buffer += dec_data.toString(CryptoJS.enc.Hex); - var secure_headers = await processRequest(socket, socket_sessions[socket.id].secure_buffer, true, true); + var secure_headers = null; + if (headers['request']) { + if (headers['request'] == "GET") { + if (socket_sessions[socket.id].secure_buffer.indexOf("0d0a0d0a") || socket_sessions[socket.id].secure_buffer.indexOf("0a0a")) { + secure_headers = await processRequest(socket, socket_sessions[socket.id].secure_buffer, true, true); + } + } else { + var secure_headers = await processRequest(socket, socket_sessions[socket.id].secure_buffer, true, true); + } + } else { + var secure_headers = await processRequest(socket, socket_sessions[socket.id].secure_buffer, true, true); + } if (secure_headers) { delete socket_sessions[socket.id].secure_buffer; if (!headers) headers = new Array(); @@ -1016,18 +1070,18 @@ async function handleSocket(socket) { socket.setEncoding('hex'); //set data encoding (Text: 'ascii', 'utf8' ~ Binary: 'hex', 'base64' (do not trust 'binary' encoding)) socket.setTimeout(600000); socket.on('data', function (data_hex) { - if (!socket_sessions[socket.id].secure) { + if (!socket_sessions[socket.id].secure && !socket_sessions[socket.id].expecting_post_data) { // buffer unencrypted data until we see the classic double-newline, or get blank if (!socket_sessions[socket.id].header_buffer) socket_sessions[socket.id].header_buffer = ""; socket_sessions[socket.id].header_buffer += data_hex; - var header_buffer_text = CryptoJS.enc.Hex.parse(socket_sessions[socket.id].header_buffer).toString(CryptoJS.enc.Latin1); - if (header_buffer_text.indexOf("\r\n\r\n") != -1 || header_buffer_text.indexOf("\n\n") != -1 || header_buffer_text == "") { + if (socket_sessions[socket.id].header_buffer.indexOf("0d0a0d0a") != -1 || socket_sessions[socket.id].header_buffer.indexOf("0a0a") != -1) { data_hex = socket_sessions[socket.id].header_buffer; delete socket_sessions[socket.id].header_buffer; processRequest(this, data_hex); } } else { // stream encrypted requests through the processor + if (socket_sessions[socket.id].header_buffer) delete socket_sessions[socket.id].header_buffer; processRequest(this, data_hex); } }); @@ -1238,4 +1292,3 @@ initstring = initstring.substring(0, initstring.length - 2); console.log(" * Started server on ports " + initstring + "...") var listening_ip_string = (minisrv_config.config.bind_ip != "0.0.0.0") ? "IP: " + minisrv_config.config.bind_ip : "all interfaces"; console.log(" * Listening on", listening_ip_string,"~","Service IP:", service_ip); - diff --git a/zefie_wtvp_minisrv/package.json b/zefie_wtvp_minisrv/package.json index 14ecdc61..5286c4aa 100644 --- a/zefie_wtvp_minisrv/package.json +++ b/zefie_wtvp_minisrv/package.json @@ -1,10 +1,13 @@ { "name": "zefie_wtvp_minisrv", - "version": "0.9.5", + "version": "0.9.6", "description": "WebTV Service (WTVP) Emulation Server", "main": "app.js", "homepage": "https://github.com/zefie/zefie_wtvp_minisrv", "license": "GPL3", + "scripts": { + "test": "node test.js" + }, "author": { "name": "zefie", "email": "zefie@zefie.net", diff --git a/zefie_wtvp_minisrv/test.js b/zefie_wtvp_minisrv/test.js new file mode 100644 index 00000000..49c0d926 --- /dev/null +++ b/zefie_wtvp_minisrv/test.js @@ -0,0 +1,33 @@ +const { promisify } = require('util'); +const { resolve } = require('path'); +const fs = require('fs'); +const readdir = promisify(fs.readdir); +const stat = promisify(fs.stat); +const { exec } = require("child_process"); +var path = require('path'); + +async function getFiles(dir) { + const subdirs = await readdir(dir); + const files = await Promise.all(subdirs.map(async (subdir) => { + const res = resolve(dir, subdir); + return (await stat(res)).isDirectory() ? getFiles(res) : res; + })); + return files.reduce((a, f) => a.concat(f), []); +} + +getFiles(__dirname) + .then(files => { + files.forEach(function (file) { + if (path.extname(file) == ".js" && file.indexOf("node_modules") == -1) { + console.log(" * Checking syntax of", file.replace(__dirname + path.sep, "." + path.sep)); + exec("node --check \"" + file + "\"", (error, stdout, stderr) => { + if (stderr.length > 0) { + console.log(`${stderr}`); + return; + } + }); + } + }); + }) + .catch(e => console.error(e)); + diff --git a/zefie_wtvp_minisrv/zefie_wtvp_minisrv.njsproj b/zefie_wtvp_minisrv/zefie_wtvp_minisrv.njsproj index 02be7160..2c248647 100644 --- a/zefie_wtvp_minisrv/zefie_wtvp_minisrv.njsproj +++ b/zefie_wtvp_minisrv/zefie_wtvp_minisrv.njsproj @@ -202,12 +202,16 @@ - + + Code - + + Code + + Code