--- deliantra/server/socket/lowlevel.C 2012/10/29 23:55:57 1.85 +++ deliantra/server/socket/lowlevel.C 2012/11/06 15:11:16 1.88 @@ -202,7 +202,7 @@ && (!(pkt->flags & PF_PLAYING) || state == ST_PLAYING); } -// HACK: some commands currently should be executed +// HACK: some commands currently should be executed // even when the player is frozen. this hack detects // those commands. it should be folded into may_execute, // but kept seperate to emphasise the hack aspect, i.e. @@ -312,6 +312,18 @@ return true; } +void +client::inbuf_handle () +{ + if (!handle_packet ()) + return; + + while (handle_packet ()) + ; + + flush (); +} + // callback called when socket is either readable or writable void client::socket_cb (iow &w, int revents) @@ -340,11 +352,122 @@ if (!amount) { // input buffer full - socket_ev.poll (socket_ev.poll () & ~EV_READ); + LOG (llevError, "input buffer overflow."); + destroy (); return; } - amount = read (fd, inbuf + inbuf_len, amount); + if (ws_version) + { + if (ws_inbuf_len + 2048 > ws_inbuf_alloc) + ws_inbuf = (uint8 *)realloc (ws_inbuf, ws_inbuf_alloc += 4096); + + int len = read (fd, ws_inbuf + ws_inbuf_len, ws_inbuf_alloc - ws_inbuf_len); + + if (len > 0) + { + ws_inbuf_len += len; + + if (ws_inbuf_len < 2 + 4) // 6 is minimum length: op, len, mask + return; + + int d = 2; + int o = ws_inbuf [0] & 15; + int l = ws_inbuf [1] & 127; + + if (l == 126) + { + l = (ws_inbuf [2] << 8) | ws_inbuf [3]; + d += 2; + } + else if (l == 127) + { + if (ws_inbuf_len < 2 + 8) + return; + + // we don't do extra long frames, if a browser wants to send >2**32 bytes, + // there are bigger issues to fix. + l = (ws_inbuf [6] << 24) + | (ws_inbuf [7] << 16) + | (ws_inbuf [8] << 8) + | ws_inbuf [9]; + d += 8; + } + + // we only continue if we have a complete frame + if (ws_inbuf_len < d + 4 + l) + return; + + switch (o) + { + case 0: o = ws_inbuf_type; break; // continuation + case 1: ws_inbuf_type = 1; break; // utf-8 + case 2: ws_inbuf_type = 2; break; // binary + } + + if (l > amount) + { + // input buffer full + LOG (llevError, "input buffer overflow (ws)."); + destroy (); + return; + } + + for (int i = 0; i < l; ++i) + inbuf [inbuf_len + i] = ws_inbuf [d + 4 + i] ^ ws_inbuf [d + (i & 3)]; + + // remove frame + ws_inbuf_len -= d + 4 + l; + memmove (ws_inbuf, ws_inbuf + d + 4 + l, ws_inbuf_len); + + switch (o) + { + case 1: // utf-8 + { + uint8 *a = inbuf + inbuf_len; + uint8 *b = a; + uint8 *c = a + l; + + for (; a < c; ++a, ++b) + { + *b = *a; + + if (*a >= 0x80) + *b = (a [0] & 0x1f) << 6 | (a [1] & 0x3f), ++a; + } + + l -= a - b; + } + break; + case 2: // binary + break; + + case 9: // ping + { + // send pong - we assume ping messages are <64k + // as we can't handle >10k at the moment anyway. + uint8 hdr [] = { 0x8a, 126, l >> 8, l }; + send (hdr, sizeof (hdr)); + send (inbuf + inbuf_len, l); + } + return; + + case 10: // pong + return; + + case 8: // close + default: + destroy (); + return; + } + + amount = l; + } + else + amount = -1; + } + else + amount = read (fd, inbuf + inbuf_len, amount); if (!amount) { @@ -365,14 +488,7 @@ else { inbuf_len += amount; - - if (handle_packet ()) - { - while (handle_packet ()) - ; - - flush (); - } + inbuf_handle (); } } } @@ -452,12 +568,69 @@ if (!sl.length ()) return; - assert (sl.hdrlen == 2); + if (ws_version == 8) + { + static uint8 buf [MAXSOCKBUF * 2 + 4]; + + uint8 *b = buf + 4; + for (uint8 *a = sl.buf_ + sl.hdrlen; a < sl.cur; ++a) + { + if (*a < 0x80) + *b++ = *a; + else + { + *b++ = 0xc0 | ((*a >> 6) & 0x1f); + *b++ = 0x80 | ( *a & 0x3f); + } + } + + assert (b - buf < sizeof (buf)); + + int l = b - (buf + 4); - sl.buf_ [0] = sl.length () >> 8; - sl.buf_ [1] = sl.length () ; + if (l < 126) + { + buf [2] = 0x81; + buf [3] = l; + + send (buf + 2, l + 2); + } + else + { + buf [0] = 0x81; + buf [1] = 126; + buf [2] = l >> 8; + buf [3] = l; + + send (buf, l + 4); + } + } + else if (ws_version == 13) + { + int l = sl.length (); - send (sl.buf_, sl.length () + sl.hdrlen); + if (l < 126) + { + uint8 hdr [] = { 0x82, 126, l >> 8, l }; + send (hdr, sizeof (hdr)); + } + else + { + uint8 hdr [] = { 0x82, l }; + send (hdr, sizeof (hdr)); + } + + send (sl.buf_ + sl.hdrlen, l); + } + else + { + assert (sl.hdrlen == 2); + + sl.buf_ [0] = sl.length () >> 8; + sl.buf_ [1] = sl.length () ; + + send (sl.buf_, sl.length () + sl.hdrlen); + } } void