diff options
| author | ame <[email protected]> | 2026-02-15 04:08:16 -0600 |
|---|---|---|
| committer | ame <[email protected]> | 2026-02-15 04:08:16 -0600 |
| commit | db2611fcad18f73572dd1b344e4197536086be53 (patch) | |
| tree | 8d6df833110e57fa7d77753571acfda2ebb23f95 /src/net/websocket.c | |
| parent | 0a909a9dc5879e592d92c6eedeb59da8cf503392 (diff) | |
ssl server support, websocket upgrades, and net changes
Diffstat (limited to 'src/net/websocket.c')
| -rw-r--r-- | src/net/websocket.c | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/src/net/websocket.c b/src/net/websocket.c new file mode 100644 index 0000000..af37f32 --- /dev/null +++ b/src/net/websocket.c @@ -0,0 +1,225 @@ +#include "websocket.h" +#include "common.h" +#include "util.h" +#include "../lua.h" +#include "../hash/sha01.h" +#include "../encode/base64.h" +#include <limits.h> +#include <ctype.h> + +//why????? +const char* magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + +//calloc or zero {out} +void hex_decode(char* in, char* out, uint64_t* outlen){ + uint64_t len = 0; + for(int i = 0; in[i] != '\0'; i++, len++){ + int value = 0; + char c = in[i]; + if(c >= '0' && c <= '9') + value = c - '0'; + else if(c >= 'A' && c <= 'F') + value = 10 + (c - 'A'); + else if(c >= 'a' && c <= 'f') + value = 10 + (c - 'a'); + + out[(i/2)] += value << (((i + 1) % 2) * 4); + //out[i/2] += strtoul((char[]){in[i], in[i+1]}, 0, 16); + } + + *outlen = len/2; +} + +struct ws_frame_info ws_frame_decode(char* buffer){ + struct ws_frame_info frame_info = {.fin = (buffer[0] >> 7) & 1, .rsv1 = (buffer[0] >> 6) & 1, + .rsv2 = (buffer[0] >> 5) & 1, .rsv3 = (buffer[0] >> 4) & 1, .opcode = buffer[0] & 0b1111, + .mask = (buffer[1] >> 7) & 1, .payload = buffer[1] & 0b1111111}; + return frame_info; +} + +void ws_frame_build(struct ws_frame_info frame, uint64_t extended, char* mkey, str* out){ + uint8_t d = (frame.fin << 7) | (frame.rsv1 << 6) | (frame.rsv2 << 5) | (frame.rsv3 << 4) | frame.opcode; + str_pushc(out, d); + frame.payload = extended; + if(extended > 125) frame.payload = 126; + if(extended > UINT16_MAX) frame.payload = 127; + d = (frame.mask << 7) | frame.payload; + str_pushc(out, d); + + if(frame.payload == 126){ + uint16_t sh = extended; + for(int i = 2; i != 0; i--) + str_pushc(out, ((uint8_t*)&sh)[i - 1]); + } else if(frame.payload == 127){ + for(int i = 8; i != 0; i--) + str_pushc(out, ((uint8_t*)&extended)[i - 1]); + } + + if(frame.mask){ + str_pushl(out, mkey, 4); + } +} + +#define BUFFER_LEN 4096 + +int ws_read(struct net_data* data, struct ws_frame_info* frame_info){ + char buffer[BUFFER_LEN] = {0}; + int total_len = 0; + int len; + + for(; (len = net_ctx_read(data, buffer + total_len, 2 - total_len)) > 0;){ + total_len += len; + if(total_len >= 2) break; + } + + if(len < 0 || total_len <= 0) return -1; + + uint64_t payload = 0; + *frame_info = ws_frame_decode(buffer); + memset(buffer, 0, total_len); + total_len = 0; + + if(frame_info->payload <= 125) payload = frame_info->payload; + else if(frame_info->payload == 126) { + for(; (len = net_ctx_read(data, buffer + total_len, 2 - total_len)) > 0;){ + total_len += len; + if(total_len >= 2) break; + } + + if(len < 0) return -1; + + payload = (buffer[0] & 0xff) << 8 | (buffer[1] & 0xff); + } else { + for(; (len = net_ctx_read(data, buffer + total_len, 8 - total_len)) > 0;){ + total_len += len; + if(total_len >= 8) break; + } + + if(len < 0) return -1; + + payload = ((uint64_t)buffer[0] & 0xff) << 56 | ((uint64_t)buffer[1] & 0xff) << 48 | ((uint64_t)buffer[2] & 0xff) << 40 | + ((uint64_t)buffer[3] & 0xff) << 32 | (buffer[4] & 0xff) << 24 | (buffer[5] & 0xff) << 16 | (buffer[6] & 0xff) << 8 | (buffer[7] & 0xff); + } + + total_len = 0; + uint8_t mask[4] = {0}; + if(frame_info->mask){ + for(; (len = net_ctx_read(data, buffer + total_len, 4 - total_len)) > 0;){ + total_len += len; + if(total_len >= 4) break; + } + mask[0] = buffer[0]; + mask[1] = buffer[1]; + mask[2] = buffer[2]; + mask[3] = buffer[3]; + } + + uint64_t i = 0; + memset(buffer, 0, BUFFER_LEN); + for(; data->buffer->len != payload && (len = net_ctx_read(data, buffer, lesser(payload - data->buffer->len, BUFFER_LEN))) > 0;){ + if(frame_info->mask){ + for(int z = 0; z != len; z++,i++) + buffer[z] ^= mask[i % 4]; + } + str_pushl(data->buffer, buffer, len); + memset(buffer, 0, len); + } + + if(len < 0) return -1; + + return 1; +} + +int l_ws_read(lua_State* L){ + lua_getfield(L, 1, "_ws"); + struct net_data* data = lua_touserdata(L, -1); + struct ws_frame_info frame = {}; + int c = ws_read(data, &frame); + if(c == -1){ + luaI_error(L, -1, "SSL_read error"); + str_clear(data->buffer); + } + + lua_newtable(L); + int idx = lua_gettop(L); + luaI_tsetsl(L, idx, "content", data->buffer->c, data->buffer->len); + luaI_tseti(L, idx, "opcode", frame.opcode); + + str_clear(data->buffer); + return 1; +} + +int l_ws_write(lua_State* L){ + lua_getfield(L, 1, "_ws"); + struct net_data* data = lua_touserdata(L, -1); + struct ws_frame_info frame = {}; + frame.rsv1 = 0; + frame.fin = 1; + frame.mask = 0; + frame.opcode = 0b0001; + + uint64_t len; + const char* s = lua_tolstring(L, 2, &len); + + str* f = str_init(""); + ws_frame_build(frame, len, NULL, f); + str_pushl(f, s, len); + write(data->sock, f->c, f->len); + str_free(f); + + return 0; +} + +int l_websocket_upgrade(lua_State* L){ + int res_idx = 1; + int req_idx = 2; + + lua_getfield(L, res_idx, "_"); + struct net_data* ctx = lua_touserdata(L, -1); + + lua_getfield(L, req_idx, "sec-websocket-key"); + const char* wskey = luaL_checklstring(L, -1, NULL); + str* newkey = str_init(wskey); + str_push(newkey, magic); + char* sha = calloc(1512, sizeof * sha); + printf("%s\n", newkey->c); + sha1((uint8_t*)newkey->c, newkey->len, sha); + printf("%s\n", sha); + char* bin = calloc(512, sizeof * bin); + uint64_t len; + hex_decode(sha, bin, &len); + char* b64 = calloc(512 * 3, sizeof * b64); + en_base64(bin, len, b64); + + char* upgrade = calloc(8192, sizeof * upgrade); + sprintf(upgrade, "HTTP/1.1 101 Switching Protocols\r\n" + "upgrade: websocket\r\n" + "connection: upgrade\r\n" + "sec-websocket-accept: %s\r\n" + "\r\n", b64); + + printf("%s\n", upgrade); + net_ctx_write(ctx, upgrade, strlen(upgrade)); + free(upgrade); + free(sha); + free(bin); + free(b64); + str_free(newkey); + + struct net_data *data = calloc(1, sizeof * data); + data->sock = ctx->sock; + data->ssl = ctx->ssl; + data->ctx = ctx->ctx; + data->buffer = str_init(""); + + luaI_tsetnil(L, req_idx, "roll"); + luaI_tsetlud(L, res_idx, "_ws", data); +#warning "missing ws commands" + luaI_tsetcf(L, res_idx, "send", l_ws_write); + //luaI_tsetcf(L, res_idx, "write", ); + //luaI_tsetcf(L, res_idx, "sendfile", ); + luaI_tsetcf(L, res_idx, "read", l_ws_read); + //luaI_tsetcf(L, res_idx, "close", ); + + return 1; +} |
