

Discover more from hrbrmstr's Daily Drop
QuickJS
I 💙 programming languages of all flavours (yes, I even have a pre-revelation, two-sizes-too-small Grinch 🖤 for Python), and some of the most fun I've had programming these past few decades has involved language and compiler design/implementation. I'd venture to say that most coders have no idea just how much clang
, gcc
, rustc
, et al., are doing under the covers for them.
It should also, hopefully, come as no surprise that I also am a big fan of compiling code into system-specific binaries vs execute scripts. Scripting languages have their place — I mean, I do use R on the daily to get real work done — but nothing beats a lovely Mach-O 64-bit executable arm64 binary.
JavaScript is not exactly a new programming language, and I often find the hieroglyphic nature of it annoying, but it is certainly ubiquitous, and fairly easy to learn and use. It is most certainly a scripting language, but — as we've seen in previous editions — it is also possible to compile JavaScript code. Previous examples, here, have been demonstrated turning JavaScript code into Wasm binaries, but Wasm is not the only option.
Enter Fabrice Bellard's QuickJS [GH], "a small and embeddable Javascript engine. It supports the ECMAScript 2020 specification including modules, asynchronous generators, proxies and BigInt; and optionally supports mathematical extensions such as big decimal floating-point numbers (BigDecimal), big binary floating-point numbers (BigFloat) and operator overloading."
If you've seen the reference to "ECMAScript" before but just got on with your day and never bothered to see why it is associated with JavaScript, start here for some background.
QuickJS works a bit like Embedded R in that it ships a whole JavaScript engine in the binary, garbage collector and all.
macOS folk can brew install quickjs
to start playing with it. Stick:
console.log("Hello World");
in, say, hello.js
, and do:
$ qjs hello.js
to run the simple JS "Hello, World" program. (qjs
also works as a REPL).
You can turn that into a system binary with qjsc
:
$ qjsc -o hello-js hello.js
On arm64 Ventura, that compiles to a 323,381 byte binary, which is an order of magnitude more than it's pure C cousin (which comes in around 33,428). Note that there's a way to transpile JavaScript to C code with qjsc
and then use some clang
optimizations to get that binary size closer to 200K. Here's what the transpiled hello.js
looks like:
/* File generated automatically by the QuickJS compiler. */
#include "quickjs-libc.h"
const uint32_t qjsc_hello_size = 78;
const uint8_t qjsc_hello[78] = {
0x02, 0x04, 0x0e, 0x63, 0x6f, 0x6e, 0x73, 0x6f,
0x6c, 0x65, 0x06, 0x6c, 0x6f, 0x67, 0x16, 0x48,
0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72,
0x6c, 0x64, 0x10, 0x68, 0x65, 0x6c, 0x6c, 0x6f,
0x2e, 0x6a, 0x73, 0x0e, 0x00, 0x06, 0x00, 0xa0,
0x01, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x14,
0x01, 0xa2, 0x01, 0x00, 0x00, 0x00, 0x38, 0xe1,
0x00, 0x00, 0x00, 0x42, 0xe2, 0x00, 0x00, 0x00,
0x04, 0xe3, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00,
0xcd, 0x28, 0xc8, 0x03, 0x01, 0x00,
};
static JSContext *JS_NewCustomContext(JSRuntime *rt)
{
JSContext *ctx = JS_NewContextRaw(rt);
if (!ctx)
return NULL;
JS_AddIntrinsicBaseObjects(ctx);
JS_AddIntrinsicBigInt(ctx);
return ctx;
}
int main(int argc, char **argv)
{
JSRuntime *rt;
JSContext *ctx;
rt = JS_NewRuntime();
js_std_set_worker_new_context_func(JS_NewCustomContext);
js_std_init_handlers(rt);
ctx = JS_NewCustomContext(rt);
js_std_add_helpers(ctx, argc, argv);
js_std_eval_binary(ctx, qjsc_hello, qjsc_hello_size, 0);
js_std_loop(ctx);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
return 0;
}
QuickJS also comes with batteries included, meaning a small built-in standard library with C library wrappers, meaning you get networking and filesystem access (etc.).
There's also a C++ wrapper for QuickJS, if you're more comfortable working in C++.
Because QuickJS supports a fairly modern JavaScript standard, many npm packages also work with it.
QuickJS is not the only JavaScript compiler in town. There's also (non-comprehensive list):
Note that NectarJS doesn't just embed a JS engine and some bytecode into a binary. It's a "proper" compiler, though the NectarJS binary for the same "Hello, World" example is nearly 500K vs 200-300K.
If you make anything with QuickJS, drop a note in the comments letting folks know what/where it is and how it went!
cmux & sslh
When folks ask me what I do for work, I often say "count IP addresses" (it depends on who is asking). That's not far from the truth, even in my paint-still-drying new job at GreyNoise. We listen for connections to our honeypot fleet and do our best to intuit intent — "benign", "malicious", "just checking", "wtheck?", "dunno" — and then usually count up the IPs associated with each intent (we do much more than that, but you hopefully get the idea).
Connections to services happen on ports. In TCP-land, possible port values range between 0-65535. Anything can be sent to a port. Just because you have a web server listening for TLS connections on port 443 (the standard HTTPS port) does not mean someone won't, say, attempt to shove a plaintext email message at it. Many a Fortune 500 web application has been felled by folks sending unexpected packets at it.
Most honeypots stand up traditional services on well-known ports; i.e. web servers on 80/443, SSH on 22, etc. this is fairly constraining, and can cause them to miss some, er, interesting, connections. Wouldn't it be great if we could intuit some part of the intent closer to the initial connection?
Well, one can perform said dark magic if you have a fast enough protocol multiplexer handler. Enter Soheil Hassas Yeganeh's cmux
and Yves Rutschle's sslh
.
cmux
is a Golang-based library that can handle gRPC, SSH, HTTPS, HTTP, Go RPC, and — well — anything else you can wire up. It does require Golang coding, but has some excellent documentation and examples
sslh
is written in C and handles HTTP, TLS/SSL (including SNI and ALPN), SSH, OpenVPN, tinc, XMPP, and SOCKS5 out of the box (you can add more to this as well). It also has more "batteries included" than cmux
, in that you can do something like:
$ docker run \
--rm \
-it \
sslh:latest \
--listen=0.0.0.0:443 \
--ssh=hostname-to-fwd-ssh-to:22 \
--tls=hostname-to-fwd-https-to:443
and have HTTPS connections flow to one host/port and SSH connections flow to another. Note that this is also a great way to bypass outbound firewall filters if no HTTPS proxy is being used. Go forth and exfiltrate!
Sand Batteries
This BBC article is hot off the presses and, well, is a BBC article, so it's pretty accessible (meaning you don't have to put up with me blathering much).
TLDR: store heat in giant sand silos and use said heat for heating things like buildings:
Low-cost electricity warms the sand up to 500°C by resistive heating (the same process that makes electric fires work).
This generates hot air which is circulated in the sand by means of a heat exchanger.
Sand is a very effective medium for storing heat and loses little over time. The developers say that their device could keep sand at 500°C for several months.
So when energy prices are higher, the battery discharges the hot air which warms water for the district heating system which is then pumped around homes, offices and even the local swimming pool.
The BBC article focuses on a Finnish company's real-world project. It also mentions a similar initiative by the U.S. National Renewable Energy Laboratory.
I'd say both efforts sound pretty cool.
FIN
The 2022 Tour de France — the only sport I even mildly follow — is in full swing! Check out the new bikes & equipment. While I 💛 my Felt FR4, I gotta say the Trek Madone SLR is an amazing piece of technology. ☮
2022-07-05.01
Well, I literally only just realized that my "clever" markdown->substack workflow wasn't translating code-block styled links. I'll make sure to not make that mistake moving forward.