ssize_t ws_close(wsh_t *wsh, int16_t reason) { if (wsh->down) { return -1; } wsh->down = 1; if (reason && wsh->sock != ws_sock_invalid) { uint16_t *u16; uint8_t fr[4] = {WSOC_CLOSE | 0x80, 2, 0}; u16 = (uint16_t *) &fr[2]; *u16 = htons((int16_t)reason); ws_raw_write(wsh, fr, 4); } restore_socket(wsh->sock); if (wsh->close_sock && wsh->sock != ws_sock_invalid) { close(wsh->sock); } wsh->sock = ws_sock_invalid; return reason * -1; }
ssize_t ws_write_frame(wsh_t *wsh, ws_opcode_t oc, void *data, size_t bytes) { uint8_t hdr[14] = { 0 }; size_t hlen = 2; uint8_t *bp; ssize_t raw_ret = 0; if (wsh->down) { return -1; } //printf("WRITE[%ld]-----------------------------:\n[%s]\n-----------------------------------\n", bytes, (char *) data); hdr[0] = (uint8_t)(oc | 0x80); if (bytes < 126) { hdr[1] = (uint8_t)bytes; } else if (bytes < 0x10000) { uint16_t *u16; hdr[1] = 126; hlen += 2; u16 = (uint16_t *) &hdr[2]; *u16 = htons((uint16_t) bytes); } else { uint64_t *u64; hdr[1] = 127; hlen += 8; u64 = (uint64_t *) &hdr[2]; *u64 = htonl(bytes); } if (wsh->write_buffer_len < (hlen + bytes + 1)) { void *tmp; wsh->write_buffer_len = hlen + bytes + 1; if ((tmp = realloc(wsh->write_buffer, wsh->write_buffer_len))) { wsh->write_buffer = tmp; } else { abort(); } } bp = (uint8_t *) wsh->write_buffer; memcpy(bp, (void *) &hdr[0], hlen); memcpy(bp + hlen, data, bytes); raw_ret = ws_raw_write(wsh, bp, (hlen + bytes)); if (raw_ret != (ssize_t) (hlen + bytes)) { return raw_ret; } return bytes; }
int inline ws_send(struct tcp_connection *con, int fd, int op, char *body, unsigned int len) { /* * we need this buffer to mask the message sent to the client * since we cannot modify the buffer - it might be readonly */ static char *body_buf = 0; static unsigned char hdr_buf[WS_MAX_HDR_LEN]; static struct iovec v[2] = { {hdr_buf, 0}, {0, 0}}; unsigned int mask = rand(); /* FIN + OPCODE */ hdr_buf[0] = WS_BIT_FIN | (op & WS_MASK_OPCODE); if (len == 0) { hdr_buf[1] = 0; /* don't have any data, send only the heeader */ return ws_raw_write(con, fd, (char *)hdr_buf, WS_MIN_HDR_LEN); } else if (len < WS_EXT_LEN) { hdr_buf[1] = len; v[0].iov_len = WS_MIN_HDR_LEN; } else if (len < WS_MAX_ELEN) { v[0].iov_len = WS_MIN_HDR_LEN + WS_ELEN_SIZE; hdr_buf[1] = WS_EXT_LEN; *(uint16_t *)(hdr_buf + WS_MIN_HDR_LEN) = htons(len); } else { v[0].iov_len = WS_MIN_HDR_LEN + WS_ELENC_SIZE; hdr_buf[1] = WS_EXTC_LEN; /* len can't be larger than 32 bits long */ *(uint64_t *)(hdr_buf + WS_MIN_HDR_LEN) = htonl(len); } if (WS_TYPE(con) == WS_CLIENT) { /* set the mask in the message */ *(uint32_t *)(v[0].iov_base + v[0].iov_len) = mask; v[0].iov_len += WS_MASK_SIZE; /* also indicate that the message is masked */ hdr_buf[1] |= WS_BIT_MASK; body_buf = body_buf ? pkg_realloc(body_buf, len) : pkg_malloc(len); if (!body_buf) { LM_ERR("oom for body buffer\n"); return -1; } memcpy(body_buf, body, len); ws_mask(body_buf, len, mask); v[1].iov_base = body_buf; } else { v[1].iov_base = body; } v[1].iov_len = len; return ws_raw_writev(con, fd, v, 2); }
ssize_t ws_write_frame(wsh_t *wsh, ws_opcode_t oc, void *data, size_t bytes) { uint8_t hdr[14] = { 0 }; size_t hlen = 2; if (wsh->down) { return -1; } //printf("WRITE[%ld]-----------------------------:\n[%s]\n-----------------------------------\n", bytes, (char *) data); hdr[0] = (uint8_t)(oc | 0x80); if (bytes < 126) { hdr[1] = (uint8_t)bytes; } else if (bytes < 0x10000) { uint16_t *u16; hdr[1] = 126; hlen += 2; u16 = (uint16_t *) &hdr[2]; *u16 = htons((uint16_t) bytes); } else { uint64_t *u64; hdr[1] = 127; hlen += 8; u64 = (uint64_t *) &hdr[2]; *u64 = htonl(bytes); } if (ws_raw_write(wsh, (void *) &hdr[0], hlen) != (ssize_t)hlen) { return -1; } if (ws_raw_write(wsh, data, bytes) != (ssize_t)bytes) { return -2; } return bytes; }
int ws_handshake(wsh_t *wsh) { char key[256] = ""; char version[5] = ""; char proto[256] = ""; char proto_buf[384] = ""; char input[256] = ""; unsigned char output[SHA1_HASH_SIZE] = ""; char b64[256] = ""; char respond[512] = ""; ssize_t bytes; char *p, *e = 0; if (wsh->sock == ws_sock_invalid) { return -3; } while((bytes = ws_raw_read(wsh, wsh->buffer + wsh->datalen, wsh->buflen - wsh->datalen, WS_BLOCK)) > 0) { wsh->datalen += bytes; if (strstr(wsh->buffer, "\r\n\r\n") || strstr(wsh->buffer, "\n\n")) { break; } } if (bytes > sizeof(wsh->buffer) -1) { goto err; } *(wsh->buffer + wsh->datalen) = '\0'; if (strncasecmp(wsh->buffer, "GET ", 4)) { goto err; } p = wsh->buffer + 4; e = strchr(p, ' '); if (!e) { goto err; } wsh->uri = malloc((e-p) + 1); strncpy(wsh->uri, p, e-p); *(wsh->uri + (e-p)) = '\0'; cheezy_get_var(wsh->buffer, "Sec-WebSocket-Key", key, sizeof(key)); cheezy_get_var(wsh->buffer, "Sec-WebSocket-Version", version, sizeof(version)); cheezy_get_var(wsh->buffer, "Sec-WebSocket-Protocol", proto, sizeof(proto)); if (!*key) { goto err; } snprintf(input, sizeof(input), "%s%s", key, WEBSOCKET_GUID); sha1_digest(output, input); b64encode((unsigned char *)output, SHA1_HASH_SIZE, (unsigned char *)b64, sizeof(b64)); if (*proto) { snprintf(proto_buf, sizeof(proto_buf), "Sec-WebSocket-Protocol: %s\r\n", proto); } snprintf(respond, sizeof(respond), "HTTP/1.1 101 Switching Protocols\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" "Sec-WebSocket-Accept: %s\r\n" "%s\r\n", b64, proto_buf); respond[511] = 0; if (ws_raw_write(wsh, respond, strlen(respond)) != (ssize_t)strlen(respond)) { goto err; } wsh->handshake = 1; return 0; err: if (!wsh->stay_open) { snprintf(respond, sizeof(respond), "HTTP/1.1 400 Bad Request\r\n" "Sec-WebSocket-Version: 13\r\n\r\n"); respond[511] = 0; ws_raw_write(wsh, respond, strlen(respond)); ws_close(wsh, WS_NONE); } return -1; }