--- a/net/websocket/frames.lua Thu Oct 15 13:43:03 2020 +0100
+++ b/net/websocket/frames.lua Thu Oct 15 14:25:09 2020 +0100
@@ -12,12 +12,11 @@
local bit = require "util.bitcompat";
local band = bit.band;
local bor = bit.bor;
-local bxor = bit.bxor;
local lshift = bit.lshift;
local rshift = bit.rshift;
-local unpack = table.unpack or unpack; -- luacheck: ignore 113
+local sbit = require "util.strbitop";
+local sxor = sbit.sxor;
-local t_concat = table.concat;
local s_char= string.char;
local s_pack = string.pack;
local s_unpack = string.unpack;
@@ -106,7 +105,7 @@
end
if result.MASK then
- result.key = { frame:byte(length_bytes+3, length_bytes+6) };
+ result.key = frame:sub(length_bytes+3, length_bytes+6);
end
return result, header_length;
@@ -115,19 +114,7 @@
-- XORs the string `str` with the array of bytes `key`
-- TODO: optimize
local function apply_mask(str, key, from, to)
- from = from or 1
- if from < 0 then from = #str + from + 1 end -- negative indices
- to = to or #str
- if to < 0 then to = #str + to + 1 end -- negative indices
- local key_len = #key
- local counter = 0;
- local data = {};
- for i = from, to do
- local key_index = counter%key_len + 1;
- counter = counter + 1;
- data[counter] = s_char(bxor(key[key_index], str:byte(i)));
- end
- return t_concat(data);
+ return sxor(str:sub(from or 1, to or -1), key);
end
local function parse_frame_body(frame, header, pos)
@@ -174,15 +161,12 @@
local key = ""
if desc.MASK then
- local key_a = desc.key
- if key_a then
- key = s_char(unpack(key_a, 1, 4));
- else
+ key = desc.key
+ if not key then
key = random_bytes(4);
- key_a = {key:byte(1,4)};
end
b2 = bor(b2, 0x80);
- data = apply_mask(data, key_a);
+ data = apply_mask(data, key);
end
return s_char(b1, b2) .. length_extra .. key .. data
--- a/spec/net_websocket_frames_spec.lua Thu Oct 15 13:43:03 2020 +0100
+++ b/spec/net_websocket_frames_spec.lua Thu Oct 15 14:25:09 2020 +0100
@@ -32,16 +32,25 @@
["RSV2"] = false;
["RSV3"] = false;
};
- masked_data = {
+ with_mask = {
["opcode"] = 0;
["length"] = 5;
["data"] = "hello";
+ ["key"] = " \0 \0";
["FIN"] = true;
["MASK"] = true;
["RSV1"] = false;
["RSV2"] = false;
["RSV3"] = false;
- ["key"] = { 0x20, 0x20, 0x20, 0x20, };
+ };
+ empty_with_mask = {
+ ["opcode"] = 0;
+ ["key"] = " \0 \0";
+ ["FIN"] = true;
+ ["MASK"] = true;
+ ["RSV1"] = false;
+ ["RSV2"] = false;
+ ["RSV3"] = false;
};
ping = {
["opcode"] = 0x9;
@@ -71,7 +80,8 @@
assert.equal("\0\0", build(test_frames.simple_empty));
assert.equal("\0\5hello", build(test_frames.simple_data));
assert.equal("\128\0", build(test_frames.simple_fin));
- assert.equal("\128\133 HELLO", build(test_frames.masked_data));
+ assert.equal("\128\133 \0 \0HeLlO", build(test_frames.with_mask))
+ assert.equal("\128\128 \0 \0", build(test_frames.empty_with_mask))
assert.equal("\137\4ping", build(test_frames.ping));
assert.equal("\138\4pong", build(test_frames.pong));
end);
@@ -83,7 +93,7 @@
assert.same(test_frames.simple_empty, parse("\0\0"));
assert.same(test_frames.simple_data, parse("\0\5hello"));
assert.same(test_frames.simple_fin, parse("\128\0"));
- assert.same(test_frames.masked_data, parse("\128\133 HELLO"));
+ assert.same(test_frames.with_mask, parse("\128\133 \0 \0HeLlO"));
assert.same(test_frames.ping, parse("\137\4ping"));
assert.same(test_frames.pong, parse("\138\4pong"));
end);
--- a/util-src/GNUmakefile Thu Oct 15 13:43:03 2020 +0100
+++ b/util-src/GNUmakefile Thu Oct 15 14:25:09 2020 +0100
@@ -7,7 +7,7 @@
TARGET?=../util/
ALL=encodings.so hashes.so net.so pposix.so signal.so table.so \
- ringbuffer.so time.so poll.so compat.so
+ ringbuffer.so time.so poll.so compat.so strbitop.so
ifdef RANDOM
ALL+=crand.so
--- a/util-src/makefile Thu Oct 15 13:43:03 2020 +0100
+++ b/util-src/makefile Thu Oct 15 14:25:09 2020 +0100
@@ -6,7 +6,7 @@
TARGET?=../util/
ALL=encodings.so hashes.so net.so pposix.so signal.so table.so \
- ringbuffer.so time.so poll.so compat.so
+ ringbuffer.so time.so poll.so compat.so strbitop.so
.ifdef $(RANDOM)
ALL+=crand.so
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/util-src/strbitop.c Thu Oct 15 14:25:09 2020 +0100
@@ -0,0 +1,91 @@
+/*
+ * This project is MIT licensed. Please see the
+ * COPYING file in the source package for more information.
+ *
+ * Copyright (C) 2016 Kim Alvefur
+ */
+
+#include <lua.h>
+#include <lauxlib.h>
+
+#if (LUA_VERSION_NUM == 501)
+#define luaL_setfuncs(L, R, N) luaL_register(L, NULL, R)
+#endif
+
+/* TODO Deduplicate code somehow */
+
+int strop_and(lua_State* L) {
+ luaL_Buffer buf;
+ size_t a, b, i;
+ const char* str_a = luaL_checklstring(L, 1, &a);
+ const char* str_b = luaL_checklstring(L, 2, &b);
+
+ luaL_buffinit(L, &buf);
+
+ if(a == 0 || b == 0) {
+ lua_settop(L, 1);
+ return 1;
+ }
+
+ for(i = 0; i < a; i++) {
+ luaL_addchar(&buf, str_a[i] & str_b[i % b]);
+ }
+
+ luaL_pushresult(&buf);
+ return 1;
+}
+
+int strop_or(lua_State* L) {
+ luaL_Buffer buf;
+ size_t a, b, i;
+ const char* str_a = luaL_checklstring(L, 1, &a);
+ const char* str_b = luaL_checklstring(L, 2, &b);
+
+ luaL_buffinit(L, &buf);
+
+ if(a == 0 || b == 0) {
+ lua_settop(L, 1);
+ return 1;
+ }
+
+ for(i = 0; i < a; i++) {
+ luaL_addchar(&buf, str_a[i] | str_b[i % b]);
+ }
+
+ luaL_pushresult(&buf);
+ return 1;
+}
+
+int strop_xor(lua_State* L) {
+ luaL_Buffer buf;
+ size_t a, b, i;
+ const char* str_a = luaL_checklstring(L, 1, &a);
+ const char* str_b = luaL_checklstring(L, 2, &b);
+
+ luaL_buffinit(L, &buf);
+
+ if(a == 0 || b == 0) {
+ lua_settop(L, 1);
+ return 1;
+ }
+
+ for(i = 0; i < a; i++) {
+ luaL_addchar(&buf, str_a[i] ^ str_b[i % b]);
+ }
+
+ luaL_pushresult(&buf);
+ return 1;
+}
+
+LUA_API int luaopen_util_strbitop(lua_State *L) {
+ luaL_Reg exports[] = {
+ { "sand", strop_and },
+ { "sor", strop_or },
+ { "sxor", strop_xor },
+ { NULL, NULL }
+ };
+
+ lua_newtable(L);
+ luaL_setfuncs(L, exports, 0);
+ return 1;
+}