From 5b6d06241adc0073087421766125baf3c9a36025 Mon Sep 17 00:00:00 2001 From: zefie Date: Thu, 22 Jul 2021 00:45:19 -0400 Subject: [PATCH] v0.9.4 - feature: add SSID whitelist/blacklist as well as IP based SSID auth - Add user_config_README.md - Update user_config.example.json - Update README.md - update: wtv-1800:/finish-prereg: send tellyscript on relogin if tellyscripts are enabled, and wtv-script-id == 0 (no tellyscript) - Allow definition of custom ServiceVault paths - feature: app.js: add `bind_ip` option to specify an IP to bind to, instead of `0.0.0.0` - feature: custom service logos and better wtv-home:/splash --- README.md | 16 +- ServiceVault.md | 17 ++- user_config_README.md | 85 +++++++++++ .../ServiceVault/wtv-1800/finish-prereg.js | 20 ++- .../ServiceVault/wtv-flashrom/get-by-path.js | 2 +- .../ServiceVault/wtv-flashrom/get-lc2-page.js | 4 +- .../wtv-flashrom/lc2-download-complete.js | 2 +- .../wtv-flashrom/lc2-download-failed.js | 2 +- .../wtv-head-waiter/login-stage-two.js | 4 +- .../ServiceVault/wtv-home/home.js | 4 +- .../ServiceVault/wtv-home/splash.js | 36 +++++ .../ServiceVault/wtv-home/splash.txt | 26 ---- .../wtv-star/images/HackTVLogo.gif | Bin 16880 -> 0 bytes .../ServiceVault/wtv-star/images/MSNLogo.gif | Bin 0 -> 1801 bytes .../wtv-star/images/splash_logo_hacktv.gif | Bin 0 -> 8969 bytes .../wtv-star/images/splash_logo_msn.gif | Bin 0 -> 5122 bytes .../{DealerDemo.html => DealerDemo.js} | 9 +- .../ServiceVault/wtv-update/sync.js | 4 +- zefie_wtvp_minisrv/app.js | 142 +++++++++++++++--- zefie_wtvp_minisrv/config.json | 17 ++- zefie_wtvp_minisrv/package-lock.json | 2 +- zefie_wtvp_minisrv/package.json | 2 +- zefie_wtvp_minisrv/user_config.example.json | 34 ++++- zefie_wtvp_minisrv/zefie_wtvp_minisrv.njsproj | 5 +- 24 files changed, 336 insertions(+), 97 deletions(-) create mode 100644 user_config_README.md create mode 100644 zefie_wtvp_minisrv/ServiceVault/wtv-home/splash.js delete mode 100644 zefie_wtvp_minisrv/ServiceVault/wtv-home/splash.txt delete mode 100644 zefie_wtvp_minisrv/ServiceVault/wtv-star/images/HackTVLogo.gif create mode 100644 zefie_wtvp_minisrv/ServiceVault/wtv-star/images/MSNLogo.gif create mode 100644 zefie_wtvp_minisrv/ServiceVault/wtv-star/images/splash_logo_hacktv.gif create mode 100644 zefie_wtvp_minisrv/ServiceVault/wtv-star/images/splash_logo_msn.gif rename zefie_wtvp_minisrv/ServiceVault/wtv-update/{DealerDemo.html => DealerDemo.js} (92%) diff --git a/README.md b/README.md index 68d9c232..5e88c245 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ This open source server is in alpha status. Use at your own risk. - wtv-update:/sync for Download-o-Rama style file downloading ### Current issues: -- Occasionally, in certain circumstances, a specific SSID may be unable to reconnect to the server until the server is restarted +- ~~Occasionally, in certain circumstances, a specific SSID may be unable to reconnect to the server until the server is restarted~~ *Hopefully fixed in [v0.9.3](https://github.com/zefie/zefie_wtvp_minisrv/releases/tag/v0.9.3)* - Mis-configuring wtv-update:/sync DiskMaps may cause units to delete contents of partitions (need more info) ### Won't fix: @@ -24,25 +24,23 @@ This open source server is in alpha status. Use at your own risk. - No intentions to support user accounts, registration, or any form of database system ### Feature Todo: -- ~~(maybe) implement HTTP proxy (needs to be able to defluff most of the web, think retro WAP converter)~~ ***Done*** -- ~~(maybe) enable "internet mode" (let user outside of minisrv)~~ ***Done*** -- ~~Flashrom flashing functionality (at least for LC2 and higher)~~ ***Done*** +- ~~Implement HTTP proxy (needs to be able to defluff most of the web, think retro WAP converter)~~ ***Done [v0.7.1](https://github.com/zefie/zefie_wtvp_minisrv/releases/tag/v0.7.1)*** +- ~~Flashrom flashing functionality (at least for LC2 and higher)~~ ***Done [v0.8.0](https://github.com/zefie/zefie_wtvp_minisrv/releases/tag/v0.8.0)*** +- ~~SSID/IP black/whitelisting (including tying SSID to an IP or multiple IPs)~~ ***Done [v0.9.4](https://github.com/zefie/zefie_wtvp_minisrv/releases/tag/v0.9.4)*** - Flashrom flashing for bf0app old classic (need donor unit) -- SSID/IP black/whitelisting (including tying SSID to an IP or multiple IPs) - wtv-lzpf support -- (maybe) Proper wtv-star (generic service outage page) support (maybe useful for allowing a unit to multiple sub-minisrvs). - (maybe) wtvchat stuff -- (probably not) url tokenizer (eg wtv-token-blabla, was mostly to secure service URLs from unintended access, which this server does not aim to do) ### How To Use: - 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 - Enter `zefie_wtvp_minisrv` subdirectory - Verify you are in the same directory as `app.js`, then run `npm install` -- Check any configuration, and modify to your liking. Especally `service_ip` (config can be found in `services.json`) +- Check any configuration. Create your override `user_config.json`. Especally `service_ip`. See [user_config_README.md](user_config_README.md) and [user_config.example.json](zefie_wtvp_minisrv/user_config.example.json) for more information. + - **Note:** The intended use is for all custom config to be in `user_config.json` and any custom service files to go in `UserServiceVault`. If you do not care about potential issues with future `git pull`, and will manually add new upstream `config.json` entries, you could use the standard `ServiceVault` and `config.json` - Run `node app.js` +- If you have trouble running it on Windows, try a Linux machine, Windows may need a full development enviroment or extra steps. - 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 diff --git a/ServiceVault.md b/ServiceVault.md index a1d7b0bb..4dc99c59 100644 --- a/ServiceVault.md +++ b/ServiceVault.md @@ -1,14 +1,17 @@ ## Brief ServiceVault Explanation +In `user_config.json`, or `config.json`, under the `config` section: +``` + "ServiceVaults": [ + "UserServiceVault", + "ServiceVault" + ], +``` + +The `ServiceVaults` entry is an array of Service Vaults to check, in order of priority (topmost = check first). If the path is not absolute, the the server will look in the current directory (of `app.js`). An absolute path can be specified in Linux (`/home/zefie/ServiceVault`) or Windows (`C:\\Users\\zefie\\ServiceVault` or `C:/Users/zefie/ServiceVault`) format. + The server will scan configured ServiceVaults in order of priority and look for files within them. -Currently only 2 Service Vaults are supported, the `User Server Vault` and the `Service Vault`. - -The paths to the Service Vaults are user configurable in `user_config.json` or `config.json` - -The `User Service Vault` has priority, and files found in that Service Vault will be loaded, even if the -file exists in the `Service Vault`. - Within the Service Vaults, the server looks for a subdirectory named after the wtv-service URL requested. The server will then look for files in sequential order when requesting a URL, stopping at the first match. diff --git a/user_config_README.md b/user_config_README.md new file mode 100644 index 00000000..79d36bf4 --- /dev/null +++ b/user_config_README.md @@ -0,0 +1,85 @@ +# user_config.json Quick Guide +`user_config.json` is an override file, you do not need to redefine everything that is in `config.json`, just override the values you wish to. You must use the same structure as `config.json`, but can override any value. + +--- + +### `config` section +Some values are available that are not defined in `config.json` by default. I will attempt to cover them here. +``` + "service_logo": "WebTVLogoJewel.gif", + "service_splash_logo": "file://ROM/images/SplashLogo1.gif" +``` +You can set the image to be loaded in the top left in place of the WebTV or MSN logo, as well as the main Splash image shown on login. +If an absolute path (`wtv-url:/`, `file://` url, or `http(s)://` url) is not passed, the server will search for the specified filename in `wtv-star/images` of any Service Vault. You'll want to keep the filesizes low. +``` + "ssid_block_list": [ + "8100000000000000", + "8100000000000010" + ] +``` +This would ban the SSIDs `8100000000000000` and `8100000000000010` from the service, but allow all other SSIDs to connect. +``` + "ssid_ip_allow_list": { + "8100000000000000": [ + "192.168.1.0/24", + "127.0.0.1" + ] + } +``` +This would allow `8100000000000000` to connect, despite being on the block list, if it was connecting from the 192.168.1.0/24 Subnet, or from 127.0.0.1. +``` + "ssid_allow_list": [ + "8100000000000020", + "8100000000000030" + ] +``` +This would allow only the SSIDs `8100000000000020` and `8100000000000030` to use the service, and block all other SSIDs. Note that if you add an SSID/IP combo to the `ssid_ip_allow_list`, it will allow the SSID even if it is not in the whitelist. This is useful to allow a leaked SSID but only from trusted hosts. + +--- +### `service` section + +``` + "wtv-1800" { + "send_tellyscripts": true, + "send_tellyscript_ssid_whitelist": [ + "8100000000000000" + ] + } +``` +This override would enable sending of tellyscripts, but only to the box with SSID `8100000000000000`. The `send_tellyscript_ssid_whitelist` parameter is optional, and if not defined while `send_tellyscripts` is true, the server will simply send tellyscripts to all clients. +``` + "wtv-log": { + "write_logs_to_disk": true + } +``` +By default the wtv-log:/log service discards any submitted data from the WebTV units. You can override this by setting `write_logs_to_disk` to true, then it will save to the directory named `ServiceLogPost` in the same directory as `app.js`. + +``` + "wtv-some-custom-service": { + "port": 1609, + "connections": 1 + } +``` +You can easily define a custom service in your `user_config.json` + +``` + "wtv-tricks": { + "service_ip": "192.168.1.8", + "port": 1702, + "nobind": true + } +``` +The `wtv-tricks` example above shows how you could point a service to another minisrv. +``` + "wtv-1800": { + "port": 1715 + } +``` +The `wtv-1800` example above shows how you could override the default port for a service. + +``` + "wtv-music": { + "disabled": true + } +``` +The `wtv-music` example above shows how you could disable a default service without modifying `config.json` diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-1800/finish-prereg.js b/zefie_wtvp_minisrv/ServiceVault/wtv-1800/finish-prereg.js index 3ee2a2a0..b7e3f32c 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-1800/finish-prereg.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-1800/finish-prereg.js @@ -14,21 +14,29 @@ if (socket.ssid != null) { if (ssid_sessions[socket.ssid].data_store.wtvsec_login) { var prereg_contype = "text/html"; - // if relogin, skip tellyscript - if (request_headers.query.relogin) { // skip tellyscript + if (request_headers.query.relogin) { // relogin ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_b64 = null; // clear old ticket } - // if relogin, skip tellyscript + // if relogin and wtv-script-id != 0, skip tellyscript var romtype, file_path = null; - if (!request_headers.query.relogin && minisrv_config.config.send_tellyscripts) { - var romtype = ssid_sessions[socket.ssid].get("wtv-client-rom-type"); + var send_tellyscript = true; + var wtv_script_id = parseInt(ssid_sessions[socket.ssid].get("wtv-script-id")); + if (request_headers.query.relogin && wtv_script_id != 0) send_tellyscript = false; + if (send_tellyscript && minisrv_config.services[service_name].send_tellyscripts) { + if (minisrv_config.services[service_name].send_tellyscript_ssid_whitelist) { + var send_telly_to_ssid = (minisrv_config.services[service_name].send_tellyscript_ssid_whitelist.findIndex(element => element == socket.ssid) != -1) + if (send_telly_to_ssid) romtype = ssid_sessions[socket.ssid].get("wtv-client-rom-type"); + } else { + romtype = ssid_sessions[socket.ssid].get("wtv-client-rom-type"); + } } switch (romtype) { case "US-LC2-disk-0MB-8MB": prereg_contype = "text/tellyscript"; - if (ssid_sessions[socket.ssid].get("wtv-open-access")) var file_path = __dirname + "/ServiceDeps/premade_tellyscripts/LC2/LC2_OISP_5555732_56k.tok"; + // if wtv-open-access: true then client expects OpenISP + if (ssid_sessions[socket.ssid].get("wtv-open-access") == "true") var file_path = __dirname + "/ServiceDeps/premade_tellyscripts/LC2/LC2_OISP_5555732_56k.tok"; else var file_path = __dirname + "/ServiceDeps/premade_tellyscripts/LC2/LC2_WTV_18006138199_56k.tok"; break; diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/get-by-path.js b/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/get-by-path.js index ccaf410c..fcc973e9 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/get-by-path.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/get-by-path.js @@ -29,7 +29,7 @@ if (request_headers.query.raw) { var flashrom_file_path = null; Object.keys(service_vaults).forEach(function (g) { if (flashrom_file_path != null) return; - flashrom_file_path = service_vaults[g].path + "/" + service_name + "/" + request_path; + flashrom_file_path = service_vaults[g] + "/" + service_name + "/" + request_path; if (!fs.existsSync(flashrom_file_path)) flashrom_file_path = null; }); if (minisrv_config.services[service_name].use_zefie_server && !flashrom_file_path) { diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/get-lc2-page.js b/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/get-lc2-page.js index 5f1e2050..e7e66602 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/get-lc2-page.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/get-lc2-page.js @@ -29,7 +29,7 @@ if (!request_headers.query.path) { var flashrom_file_path = null; Object.keys(service_vaults).forEach(function (g) { if (flashrom_file_path != null) return; - flashrom_file_path = service_vaults[g].path + "/" + service_name + "/" + request_path; + flashrom_file_path = service_vaults[g] + "/" + service_name + "/" + request_path; if (!fs.existsSync(flashrom_file_path)) flashrom_file_path = null; }); @@ -123,7 +123,7 @@ hspace=0 vspace=0 fontsize="large">
- + diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/lc2-download-complete.js b/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/lc2-download-complete.js index 06db1356..de414b98 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/lc2-download-complete.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/lc2-download-complete.js @@ -23,7 +23,7 @@ hspace=0 vspace=0 fontsize="large">
- + diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/lc2-download-failed.js b/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/lc2-download-failed.js index 6a5a5c52..e1667a42 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/lc2-download-failed.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/lc2-download-failed.js @@ -51,7 +51,7 @@ data = `
- + diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-head-waiter/login-stage-two.js b/zefie_wtvp_minisrv/ServiceVault/wtv-head-waiter/login-stage-two.js index 5ad309ac..608ba163 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-head-waiter/login-stage-two.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-head-waiter/login-stage-two.js @@ -50,9 +50,9 @@ Content-type: text/html`; } else { var namerand = Math.floor(Math.random() * 100000); - var nickname = 'HackTVUsr_' + namerand; + var nickname = minisrv_config.config.service_name+'_Usr_' + namerand; var userid = '1'+ Math.floor(Math.random() * 1000000000000000000); - var offline_user_list = CryptoJS.enc.Latin1.parse("\n\t\n").toString(CryptoJS.enc.Base64); + var offline_user_list = CryptoJS.enc.Latin1.parse("\n\t\n").toString(CryptoJS.enc.Base64); data = ''; headers = `200 OK Connection: Keep-Alive diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-home/home.js b/zefie_wtvp_minisrv/ServiceVault/wtv-home/home.js index 51e21954..d6a8cbab 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-home/home.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-home/home.js @@ -44,6 +44,6 @@ data += `width=250 height=10 bgcolor=#444444 text=#ffdd33 cursor=#cc9933 select ` -if (fs.existsSync(service_vaults[0].path + "/" + service_name + "/home.zefie.html")) { - data += fs.readFileSync(service_vaults[0].path + "/" + service_name + "/home.zefie.html", { 'encoding': 'utf8' }); +if (fs.existsSync(service_vaults[0] + "/" + service_name + "/home.zefie.html")) { + data += fs.readFileSync(service_vaults[0] + "/" + service_name + "/home.zefie.html", { 'encoding': 'utf8' }); } \ No newline at end of file diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-home/splash.js b/zefie_wtvp_minisrv/ServiceVault/wtv-home/splash.js new file mode 100644 index 00000000..7de72b69 --- /dev/null +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-home/splash.js @@ -0,0 +1,36 @@ +headers = `200 OK +Connection: Keep-Alive +wtv-expire-all: wtv- +wtv-expire-all: http +Content-type: text/html` + +data = ` + + +Engaging zefie... + + + + +
+ +
+ +


+


+


+ +
+Mini service +
+zefie minisrv v`+ minisrv_config.version; +if (getGitRevision()) { + data += ` (git ` + getGitRevision().substring(0,8) + `)`; +} +data += ` +
&rate; +
+

+ + +`; \ No newline at end of file diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-home/splash.txt b/zefie_wtvp_minisrv/ServiceVault/wtv-home/splash.txt deleted file mode 100644 index 1267aa4d..00000000 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-home/splash.txt +++ /dev/null @@ -1,26 +0,0 @@ -200 OK -Connection: Keep-Alive -wtv-expire-all: wtv- -wtv-expire-all: http -Content-type: text/html - - -Engaging zefie... - - - - - - - -
- -
- -
-
- - diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-star/images/HackTVLogo.gif b/zefie_wtvp_minisrv/ServiceVault/wtv-star/images/HackTVLogo.gif deleted file mode 100644 index 7df200353d74824c577df2a10f948bad8c60df38..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16880 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 z 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 diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-star/images/MSNLogo.gif b/zefie_wtvp_minisrv/ServiceVault/wtv-star/images/MSNLogo.gif new file mode 100644 index 0000000000000000000000000000000000000000..59b3829d26941a8190ccc2230e23f42dc86f712b GIT binary patch literal 1801 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 Hfc5_Y{K74> literal 0 HcmV?d00001 diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-star/images/splash_logo_msn.gif b/zefie_wtvp_minisrv/ServiceVault/wtv-star/images/splash_logo_msn.gif new file mode 100644 index 0000000000000000000000000000000000000000..e69ad2a0149a44aba586e20f380141cde20650cc GIT binary patch literal 5122 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

HwyA1Ip$TJcPsg23jNTCB9 z_CqFsgrBqx?s9wZ`f8LFO1R1DQ%j(*B}7Svh9feN(1~7H75e@N4}bHy7kdu^{m)JB8Am`A>m{Neir{agzz8Gf z9KH1!U{_vPk=N+Xy+BaD8w^c*T%&KXsHGcH?lqo{A9(Yeg6129m1&D8D*VcikVv?? z#bLS8F5j0TKlweHr8=VetHNW|tLklv>NDp>nParrV=<5`Tg%a?4_6TH2Qn*0J|m51&We>N@lbFo8hc@NoNR7FAwNaRq}9$ zMQM(m4t+G$Ht;?y)}*a}@^Q>d+ur7TN?%{dKhY%^500A`Sen`<#B}b)I4PPU{A0WM zlwV0SEA5?qG@%q}a_vx}_i>rQ#(?rKx@%VNC!D@?)BL>pR+Ym!|2&)y!yTkTs|h2D z5Gpw=JZobqj*}LZt$OgQ-ZO*XJL7w{PqX3;AWRr-%WFwolcA8|wl~Teyueti^e421 k%>MY-09ak_wS@Kocx@}7VZ^HOjGs67ZEM)ClQ2NzKX%rZRR910 literal 0 HcmV?d00001 diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-update/DealerDemo.html b/zefie_wtvp_minisrv/ServiceVault/wtv-update/DealerDemo.js similarity index 92% rename from zefie_wtvp_minisrv/ServiceVault/wtv-update/DealerDemo.html rename to zefie_wtvp_minisrv/ServiceVault/wtv-update/DealerDemo.js index 263a2a20..ecf03f93 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-update/DealerDemo.html +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-update/DealerDemo.js @@ -1,4 +1,7 @@ - +headers = `200 OK +Content-Type: text/html` + +data = `

- + @@ -45,4 +48,4 @@
- +` \ No newline at end of file diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-update/sync.js b/zefie_wtvp_minisrv/ServiceVault/wtv-update/sync.js index bf987f34..df0682fc 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-update/sync.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-update/sync.js @@ -103,7 +103,7 @@ function processGroup(diskmap_primary_group, diskmap_group_data, diskmap_subgrou var post_match_file = null; Object.keys(service_vaults).forEach(function (g) { if (post_match_file != null) return; - post_match_file = service_vaults[g].path + "/" + service_name + "/" + diskmap_group_data.files[k].location; + post_match_file = service_vaults[g] + "/" + service_name + "/" + diskmap_group_data.files[k].location; if (!fs.existsSync(post_match_file)) post_match_file = null; }); @@ -136,7 +136,7 @@ if (request_headers.query.diskmap && request_headers.query.group && request_head var diskmap_json_file = null; Object.keys(service_vaults).forEach(function (g) { if (diskmap_json_file != null) return; - diskmap_json_file = service_vaults[g].path + "/" + service_name + "/" + diskmap_dir + request_headers.query.diskmap + ".json"; + diskmap_json_file = service_vaults[g] + "/" + service_name + "/" + diskmap_dir + request_headers.query.diskmap + ".json"; if (!fs.existsSync(diskmap_json_file)) diskmap_json_file = null; }); diff --git a/zefie_wtvp_minisrv/app.js b/zefie_wtvp_minisrv/app.js index 9758b6c7..238a9099 100644 --- a/zefie_wtvp_minisrv/app.js +++ b/zefie_wtvp_minisrv/app.js @@ -89,14 +89,14 @@ async function processPath(socket, service_vault_file_path, request_headers = ne try { service_vaults.forEach(function (service_vault_dir) { if (service_vault_found) return; - service_vault_file_path = service_vault_dir.path + "/" + service_path.replace(/\\/g, "/"); + service_vault_file_path = service_vault_dir + "/" + service_path.replace(/\\/g, "/"); if (fs.existsSync(service_vault_file_path)) { // file exists, read it and return it service_vault_found = true; request_is_async = true; - if (!zquiet) console.log(" * Found " + service_vault_file_path + " in " + service_vault_dir.name +" to handle request (Direct File Mode) [Socket " + socket.id + "]"); + if (!zquiet) console.log(" * Found " + service_vault_file_path + " to handle request (Direct File Mode) [Socket " + socket.id + "]"); var contype = getConType(service_vault_file_path); headers = "200 OK\n" headers += "Content-Type: " + contype; @@ -106,7 +106,7 @@ async function processPath(socket, service_vault_file_path, request_headers = ne } else if (fs.existsSync(service_vault_file_path + ".txt")) { // raw text format, entire payload expected (headers and content) service_vault_found = true; - if (!zquiet) console.log(" * Found " + service_vault_file_path + ".txt in " + service_vault_dir.name +" to handle request (Raw TXT Mode) [Socket " + socket.id + "]"); + if (!zquiet) console.log(" * Found " + service_vault_file_path + ".txt to handle request (Raw TXT Mode) [Socket " + socket.id + "]"); request_is_async = true; fs.readFile(service_vault_file_path + ".txt", 'Utf-8', function (err, file_raw) { if (file_raw.indexOf("\n\n") > 0) { @@ -137,9 +137,9 @@ async function processPath(socket, service_vault_file_path, request_headers = ne // In Asynchronous mode, you are expected to call sendToClient(socket,headers,data) by the end of your script // `socket` is already defined and should be passed-through. service_vault_found = true; - if (!zquiet) console.log(" * Found " + service_vault_file_path + ".js in " + service_vault_dir.name + " to handle request (JS Interpreter mode) [Socket " + socket.id + "]"); + if (!zquiet) console.log(" * Found " + service_vault_file_path + ".js to handle request (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.path.replace(/\\/g, "/") + "/" + service_name; + var service_dir = service_vault_dir.replace(/\\/g, "/") + "/" + service_name; socket_sessions[socket.id].starttime = Math.floor(new Date().getTime() / 1000); var jscript_eval = fs.readFileSync(service_vault_file_path + ".js").toString(); eval(jscript_eval); @@ -148,7 +148,7 @@ async function processPath(socket, service_vault_file_path, request_headers = ne else if (fs.existsSync(service_vault_file_path + ".html")) { // Standard HTML with no headers, WTV Style service_vault_found = true; - if (!zquiet) console.log(" * Found " + service_vault_file_path + ".html in " + service_vault_dir.name +" to handle request (HTML Mode) [Socket " + socket.id + "]"); + if (!zquiet) console.log(" * Found " + service_vault_file_path + ".html to handle request (HTML Mode) [Socket " + socket.id + "]"); request_is_async = true; headers = "200 OK\n" headers += "Content-Type: text/html" @@ -186,7 +186,7 @@ async function processPath(socket, service_vault_file_path, request_headers = ne } function filterSSID(obj) { - if (minisrv_config.config.hide_ssid_in_logs) { + if (minisrv_config.config.hide_ssid_in_logs === true) { if (typeof (obj) == "string") { if (obj.substr(0, 8) == "MSTVSIMU") { return obj.substr(0, 10) + ('*').repeat(10) + obj.substr(20); @@ -598,7 +598,86 @@ async function processRequest(socket, data_hex, returnHeadersBeforeSecure = fals ssid_sessions[socket.ssid] = new ClientSessionData(); } if (!ssid_sessions[socket.ssid].data_store.sockets) ssid_sessions[socket.ssid].data_store.sockets = new Array(); - ssid_sessions[socket.ssid].data_store.sockets.push(socket.id); + ssid_sessions[socket.ssid].data_store.sockets.push(socket.id); + } + + var ip2long = function (ip) { + var components; + + if (components = ip.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/)) { + var iplong = 0; + var power = 1; + for (var i = 4; i >= 1; i -= 1) { + iplong += power * parseInt(components[i]); + power *= 256; + } + return iplong; + } + else return -1; + }; + + var isInSubnet = function (ip, subnet) { + var mask, base_ip, long_ip = ip2long(ip); + if ((mask = subnet.match(/^(.*?)\/(\d{1,2})$/)) && ((base_ip = ip2long(mask[1])) >= 0)) { + var freedom = Math.pow(2, 32 - parseInt(mask[2])); + return (long_ip > base_ip) && (long_ip < base_ip + freedom - 1); + } + else return false; + }; + + var rejectSSIDConnection = function (ssid, blacklist) { + if (blacklist) console.log(" * Request from SSID", filterSSID(ssid), "(" + socket.remoteAddr + "), but that SSID is in the blacklist, rejecting."); + else console.log(" * Request from SSID", filterSSID(socket.ssid), "(" + socket.remoteAddress + "), but that SSID is not in the whitelist, rejecting."); + + var errpage = doErrorPage(401, "Access to this service is denied."); + headers = errpage[0]; + data = errpage[1]; + socket_sessions[socket.id].close_me = true; + } + + var checkSSIDIPWhitelist = function (ssid, blacklist) { + var ssid_access_list_ip_override = false; + if (minisrv_config.config.ssid_ip_allow_list) { + if (minisrv_config.config.ssid_ip_allow_list[socket.ssid]) { + Object.keys(minisrv_config.config.ssid_ip_allow_list[socket.ssid]).forEach(function (k) { + if (minisrv_config.config.ssid_ip_allow_list[socket.ssid][k].indexOf('/') > 0) { + if (isInSubnet(socket.remoteAddress, minisrv_config.config.ssid_ip_allow_list[socket.ssid][k])) { + // remoteAddr is in allowed subnet + ssid_access_list_ip_override = true; + } + } else { + if (socket.remoteAddress == minisrv_config.config.ssid_ip_allow_list[socket.ssid][k]) { + // remoteAddr directly matches IP + ssid_access_list_ip_override = true; + } + } + }); + if (!ssid_access_list_ip_override) rejectSSIDConnection(socket.ssid, blacklist); + } else { + rejectSSIDConnection(socket.ssid, blacklist); + } + } else { + rejectSSIDConnection(socket.ssid, blacklist); + } + if (ssid_access_list_ip_override && zdebug) console.log(" * Request from disallowed SSID", filterSSID(ssid), "was allowed due to IP address whitelist"); + } + + // process whitelist first + if (socket.ssid && minisrv_config.config.ssid_allow_list) { + var ssid_is_in_whitelist = minisrv_config.config.ssid_allow_list.findIndex(element => element == socket.ssid); + if (ssid_is_in_whitelist == -1) { + // no whitelist match, but lets see if the remoteAddress is allowed + checkSSIDIPWhitelist(socket.ssid, false); + } + } + + // now check blacklist + if (socket.ssid && minisrv_config.config.ssid_block_list) { + var ssid_is_in_blacklist = minisrv_config.config.ssid_block_list.findIndex(element => element == socket.ssid); + if (ssid_is_in_blacklist != -1) { + // blacklist match, but lets see if the remoteAddress is allowed + checkSSIDIPWhitelist(socket.ssid, true); + } } @@ -756,7 +835,7 @@ async function cleanupSocket(socket) { delete socket_sessions[socket.id]; } if (socket.ssid) { - var socket_array_index = ssid_sessions[socket.ssid].data_store.sockets.findIndex(element => element = socket.id); + var socket_array_index = ssid_sessions[socket.ssid].data_store.sockets.findIndex(element => element == socket.id); if (socket_array_index != -1) { ssid_sessions[socket.ssid].data_store.sockets.splice(socket_array_index,1); } @@ -849,6 +928,19 @@ function returnAbsolsutePath(path) { } +function getGitRevision() { + try { + const rev = fs.readFileSync(__dirname.replace(/\\/g, "/") + '/../.git/HEAD').toString().trim(); + if (rev.indexOf(':') === -1) { + return rev; + } else { + return fs.readFileSync(__dirname.replace(/\\/g, "/") + '/../.git/' + rev.substring(5)).toString().trim(); + } + } catch (e) { + return null; + } +} + // SERVER START var z_title = "zefie's wtv minisrv v" + require('./package.json').version; @@ -886,11 +978,15 @@ if (throw_me) { throw ("An error has occured while reading the configuration files."); } -if (minisrv_config.config.UserServiceVault) service_vaults.push({ "path": returnAbsolsutePath(minisrv_config.config.UserServiceVault), "name": "User Service Vault" }); -service_vaults.push({ "path": returnAbsolsutePath(minisrv_config.config.ServiceVault), "name": "Service Vault" }); -Object.keys(service_vaults).forEach(function (k) { - console.log(" * Using", service_vaults[k].name, "at", service_vaults[k].path); -}); +if (minisrv_config.config.ServiceVaults) { + Object.keys(minisrv_config.config.ServiceVaults).forEach(function (k) { + var service_vault = returnAbsolsutePath(minisrv_config.config.ServiceVaults[k]); + service_vaults.push(service_vault); + console.log(" * Configured Service Vault at", service_vault, "with priority",(parseInt(k)+1)); + }) +} else { + throw ("ERROR: No Service Vaults defined!"); +} var service_ip = minisrv_config.config.service_ip; Object.keys(minisrv_config.services).forEach(function (k) { @@ -917,7 +1013,13 @@ Object.keys(minisrv_config.services).forEach(function (k) { } console.log(" * Configured Service", k, "on Port", minisrv_config.services[k].port, "- Host", minisrv_config.services[k].host, "- Bind Port:", !minisrv_config.services[k].nobind); }) -if (minisrv_config.config.hide_ssid_in_logs) console.log(" * Masking SSIDs in the console for security"); +if (minisrv_config.config.hide_ssid_in_logs) console.log(" * Masking SSIDs in console logs for security"); +else console.log(" * Full SSIDs will be shown in console logs"); + +if (minisrv_config.config.service_logo.indexOf(':') == -1) minisrv_config.config.service_logo = "wtv-star:/images/" + minisrv_config.config.service_logo; +if (minisrv_config.config.service_splash_logo.indexOf(':') == -1) minisrv_config.config.service_splash_logo = "wtv-star:/images/" + minisrv_config.config.service_splash_logo; + +minisrv_config.version = require('./package.json').version; // defaults var zdebug = false; @@ -964,17 +1066,19 @@ ports.sort(); // de-duplicate ports in case user configured multiple services on same port const bind_ports = [...new Set(ports)] - +if (!minisrv_config.config.bind_ip) minisrv_config.config.bind_ip = "0.0.0.0"; bind_ports.forEach(function (v) { try { var server = net.createServer(handleSocket); - server.listen(v, '0.0.0.0'); + server.listen(v, minisrv_config.config.bind_ip); initstring += v + ", "; } catch (e) { - throw ("Could not bind to port", v, e.toString()); + throw ("Could not bind to port", v, "on", minisrv_config.config.bind_ip, e.toString()); } }); initstring = initstring.substring(0, initstring.length - 2); -console.log(" * Started server on ports " + initstring + "... Service IP is " + service_ip); +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/config.json b/zefie_wtvp_minisrv/config.json index 5f175b8f..34804678 100644 --- a/zefie_wtvp_minisrv/config.json +++ b/zefie_wtvp_minisrv/config.json @@ -1,19 +1,22 @@ { "config": { "service_ip": "0.0.0.0", - "ServiceVault": "ServiceVault", - "UserServiceVault": "UserServiceVault", - "service_name": "HackTV", - "send_tellyscripts": false, + "ServiceVaults": [ + "UserServiceVault", + "ServiceVault" + ], + "service_name": "MSNTV", + "service_logo": "MSNLogo.gif", + "service_splash_logo": "splash_logo_msn.gif", "hide_ssid_in_logs": true, - "socket_timeout": 350, + "socket_timeout": 350, "verbosity": 2 }, "services": { "wtv-1800": { - "host": null, "port": 1615, - "connections": 1 + "connections": 1, + "send_tellyscripts": false }, "wtv-star": { "port": 1603, diff --git a/zefie_wtvp_minisrv/package-lock.json b/zefie_wtvp_minisrv/package-lock.json index 16578ae8..e807ee3e 100644 --- a/zefie_wtvp_minisrv/package-lock.json +++ b/zefie_wtvp_minisrv/package-lock.json @@ -1,6 +1,6 @@ { "name": "zefie_wtvp_minisrv", - "version": "0.9.1", + "version": "0.9.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/zefie_wtvp_minisrv/package.json b/zefie_wtvp_minisrv/package.json index 4d9dba7f..b8b8d4b1 100644 --- a/zefie_wtvp_minisrv/package.json +++ b/zefie_wtvp_minisrv/package.json @@ -1,6 +1,6 @@ { "name": "zefie_wtvp_minisrv", - "version": "0.9.3", + "version": "0.9.4", "description": "WebTV Service (WTVP) Emulation Server", "main": "app.js", "homepage": "https://github.com/zefie/zefie_wtvp_minisrv", diff --git a/zefie_wtvp_minisrv/user_config.example.json b/zefie_wtvp_minisrv/user_config.example.json index b6e9f49a..48bce41c 100644 --- a/zefie_wtvp_minisrv/user_config.example.json +++ b/zefie_wtvp_minisrv/user_config.example.json @@ -1,12 +1,36 @@ { "config": { - "service_ip": "192.168.11.8", - "service_name": "MyWebTV", - "send_tellyscripts": true, + "service_ip": "192.168.1.8", + "service_name": "WebTV", + "service_logo": "WebTVLogoJewel.gif", + "service_splash_logo": "file://ROM/images/SplashLogo1.gif", + "ServiceVaults": [ + "UserServiceVault", + "ServiceVault", + "C:\\users\\zefie\\webtv\\ServiceVault", + "C:/Users/zefie/webtv/ServiceVault2", + "/home/zefie/webtv/ServiceVault" + ], + "ssid_block_list": [ + "8100000000000000", + "8100000000000010" + ], + "ssid_ip_allow_list": { + "8100000000000000": [ + "192.168.1.0/24", + "127.0.0.1" + ] + }, "hide_ssid_in_logs": true, - "verbosity": 0 + "verbosity": 2 }, "services": { + "wtv-1800": { + "send_tellyscripts": true, + "send_tellyscript_ssid_whitelist": [ + "8100000000000000" + ] + }, "wtv-log": { "write_logs_to_disk": true }, @@ -15,7 +39,7 @@ "connections": 1 }, "wtv-tricks": { - "service_ip": "192.168.11.8", + "service_ip": "192.168.1.8", "port": 1702, "nobind": true }, diff --git a/zefie_wtvp_minisrv/zefie_wtvp_minisrv.njsproj b/zefie_wtvp_minisrv/zefie_wtvp_minisrv.njsproj index ca2a9579..f64c45c9 100644 --- a/zefie_wtvp_minisrv/zefie_wtvp_minisrv.njsproj +++ b/zefie_wtvp_minisrv/zefie_wtvp_minisrv.njsproj @@ -189,7 +189,7 @@ Code - + @@ -197,11 +197,12 @@ - + Code + Code