juegos

Juegos de terminal de Pancho: Catan (TUI, GUI, web, servidor, PicoCalc), ajedrez, calculadora y minijuegos
git clone https://git.lu3dhn.xyz/juegos.git
Log | Files | Refs

ws.c (5522B)


      1 /* ws.c - WebSocket (RFC 6455) sobre buffers. Solo lo que necesita el servidor de
      2  * lineas: handshake, desenmascarar frames del cliente y envolver respuestas. */
      3 #include <stdbool.h>
      4 #include <stdio.h>
      5 #include <string.h>
      6 #include "sha1.h"
      7 #include "ws.h"
      8 
      9 #define WS_MAX_FRAME (1 << 20)     /* mas que esto es un cliente roto o malicioso */
     10 
     11 static int lc(int ch) { return ch >= 'A' && ch <= 'Z' ? ch + 32 : ch; }
     12 
     13 /* busca una cabecera (sin distinguir mayusculas) y copia su valor sin espacios */
     14 static int header(const char *req, int n, const char *name, char *out, int cap)
     15 {
     16     int nl = (int)strlen(name);
     17     for (int i = 0; i + nl + 1 < n; i++) {
     18         if (i > 0 && req[i - 1] != '\n') continue;
     19         int k = 0;
     20         while (k < nl && lc(req[i + k]) == lc(name[k])) k++;
     21         if (k < nl || req[i + nl] != ':') continue;
     22         int p = i + nl + 1;
     23         while (p < n && (req[p] == ' ' || req[p] == '\t')) p++;
     24         int o = 0;
     25         while (p < n && req[p] != '\r' && req[p] != '\n' && o < cap - 1) out[o++] = req[p++];
     26         while (o > 0 && (out[o - 1] == ' ' || out[o - 1] == '\t')) o--;
     27         out[o] = 0;
     28         return o;
     29     }
     30     return -1;
     31 }
     32 
     33 int ws_handshake(const char *req, int n, int *consumed, char *resp, int cap)
     34 {
     35     const char *end = 0;
     36     for (int i = 0; i + 3 < n; i++)
     37         if (req[i] == '\r' && req[i + 1] == '\n' && req[i + 2] == '\r' && req[i + 3] == '\n') { end = req + i + 4; break; }
     38     if (!end) return 0;
     39     int len = (int)(end - req);
     40     *consumed = len;
     41     char key[128], proto[128], upgrade[32] = "";
     42     header(req, len, "Upgrade", upgrade, sizeof upgrade);
     43     for (char *p = upgrade; *p; p++) *p = (char)(*p | 32);
     44     bool ok = n >= 4 && !memcmp(req, "GET ", 4) && !strcmp(upgrade, "websocket")
     45               && header(req, len, "Sec-WebSocket-Key", key, sizeof key) > 0;
     46     if (!ok) {
     47         snprintf(resp, (size_t)cap, "HTTP/1.1 400 Bad Request\r\nConnection: close\r\nContent-Length: 0\r\n\r\n");
     48         return -1;
     49     }
     50     /* Accept = base64(sha1(key + GUID)) */
     51     char cat[200];
     52     snprintf(cat, sizeof cat, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", key);
     53     uint8_t dig[20];
     54     sha1(cat, strlen(cat), dig);
     55     char accept[32];
     56     base64_encode(dig, 20, accept, sizeof accept);
     57     /* el primer subprotocolo ofrecido, si hay (el cliente emscripten manda "binary");
     58      * la lista puede traer espacios alrededor de las comas */
     59     char sub[160] = "";
     60     if (header(req, len, "Sec-WebSocket-Protocol", proto, sizeof proto) > 0) {
     61         char *p = proto;
     62         while (*p == ' ' || *p == ',') p++;
     63         char *e = p;
     64         while (*e && *e != ',' && *e != ' ') e++;
     65         *e = 0;
     66         if (*p) snprintf(sub, sizeof sub, "Sec-WebSocket-Protocol: %s\r\n", p);
     67     }
     68     int r = snprintf(resp, (size_t)cap,
     69                      "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n"
     70                      "Sec-WebSocket-Accept: %s\r\n%s\r\n", accept, sub);
     71     return r >= cap ? -1 : r;
     72 }
     73 
     74 int ws_unwrap(const uint8_t *buf, int n, char *out, int cap, int *outlen, int *ctl, uint8_t ping[WS_CTL_MAX], int *pinglen)
     75 {
     76     int pos = 0, o = 0;
     77     *ctl = 0;
     78     *pinglen = 0;
     79     while (pos + 2 <= n) {
     80         const uint8_t *f = buf + pos;
     81         int op = f[0] & 0x0F, fin = f[0] & 0x80, masked = f[1] & 0x80, hdr = 2;
     82         uint64_t len = f[1] & 0x7F;
     83         if (len == 126) {
     84             if (pos + 4 > n) break;
     85             len = (uint64_t)f[2] << 8 | f[3];
     86             hdr = 4;
     87         } else if (len == 127) {
     88             if (pos + 10 > n) break;
     89             len = 0;
     90             for (int i = 0; i < 8; i++) len = len << 8 | f[2 + i];
     91             hdr = 10;
     92         }
     93         if (!masked || len > WS_MAX_FRAME || (f[0] & 0x70)) return -1;    /* el cliente siempre enmascara; sin extensiones */
     94         bool data = op == WS_CONT || op == WS_TEXT || op == WS_BINARY;
     95         bool control = op == WS_CLOSE || op == WS_PING || op == WS_PONG;
     96         if (!data && !control) return -1;
     97         if (control && (!fin || len > WS_CTL_MAX)) return -1;
     98         if (data && len > (uint64_t)cap) return -1;                        /* nunca va a entrar */
     99         if (data && len > (uint64_t)(cap - o)) break;                      /* entra, pero en la proxima: primero vaciar out */
    100         hdr += 4;
    101         if ((uint64_t)(n - pos) < (uint64_t)hdr + len) break;             /* frame incompleto */
    102         const uint8_t *mask = f + hdr - 4, *pay = f + hdr;
    103         if (data) {
    104             for (uint64_t i = 0; i < len; i++) out[o++] = (char)(pay[i] ^ mask[i & 3]);
    105         } else {
    106             *ctl |= 1 << op;
    107             if (op == WS_PING) {
    108                 *pinglen = (int)len;
    109                 for (uint64_t i = 0; i < len; i++) ping[i] = (uint8_t)(pay[i] ^ mask[i & 3]);
    110             }
    111         }
    112         pos += hdr + (int)len;
    113         if (op == WS_CLOSE) break;                                          /* lo que siga ya no importa */
    114     }
    115     *outlen = o;
    116     return pos;
    117 }
    118 
    119 int ws_wrap(int opcode, const void *data, int n, uint8_t *out, int cap)
    120 {
    121     int hdr = n < 126 ? 2 : n < 65536 ? 4 : 10;
    122     if (n < 0 || hdr + n > cap) return -1;
    123     out[0] = (uint8_t)(0x80 | opcode);
    124     if (hdr == 2) out[1] = (uint8_t)n;
    125     else if (hdr == 4) { out[1] = 126; out[2] = (uint8_t)(n >> 8); out[3] = (uint8_t)n; }
    126     else {
    127         out[1] = 127;
    128         for (int i = 0; i < 8; i++) out[2 + i] = (uint8_t)((uint64_t)n >> (8 * (7 - i)));
    129     }
    130     if (n) memcpy(out + hdr, data, (size_t)n);
    131     return hdr + n;
    132 }