diff --git a/build/Makefile b/build/Makefile index 1322b829c2f0812f5401ddee03116655d9ddcba8..79c410942aa2f09a6570431694be5f93dd877744 100644 --- a/build/Makefile +++ b/build/Makefile @@ -30,7 +30,7 @@ SOURCES = src/internal.h src/util.c src/string.c src/parse_date.c \ src/options.c src/crypto.c src/auth.c src/win32.c src/unix.c \ src/mg_printf.c src/ssl.c src/http_client.c src/mime.c \ src/directory.c src/log.c src/parse_http.c src/io.c src/cgi.c \ - src/upload.c src/mongoose.c src/lua.c + src/upload.c src/websocket.c src/mongoose.c src/lua.c TINY_SOURCES = ../mongoose.c main.c LUA_SOURCES = $(TINY_SOURCES) lua_5.2.1.c diff --git a/build/src/mongoose.c b/build/src/mongoose.c index 6c448358b17272a450ae887c7360a43e55eae919..d8d4f6c27f224468f2f325f46a6c7b80a1a6d3fa 100644 --- a/build/src/mongoose.c +++ b/build/src/mongoose.c @@ -647,313 +647,6 @@ static void handle_propfind(struct mg_connection *conn, const char *path, conn->num_bytes_sent += mg_printf(conn, "%s\n", "</d:multistatus>"); } -#if defined(USE_WEBSOCKET) - -// START OF SHA-1 code -// Copyright(c) By Steve Reid <steve@edmweb.com> -#define SHA1HANDSOFF -#if defined(__sun) -#include "solarisfixes.h" -#endif - -union char64long16 { unsigned char c[64]; uint32_t l[16]; }; - -#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) - -static uint32_t blk0(union char64long16 *block, int i) { - // Forrest: SHA expect BIG_ENDIAN, swap if LITTLE_ENDIAN - if (!is_big_endian()) { - block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | - (rol(block->l[i], 8) & 0x00FF00FF); - } - return block->l[i]; -} - -#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ - ^block->l[(i+2)&15]^block->l[i&15],1)) -#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(block, i)+0x5A827999+rol(v,5);w=rol(w,30); -#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); -#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); -#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); -#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); - -typedef struct { - uint32_t state[5]; - uint32_t count[2]; - unsigned char buffer[64]; -} SHA1_CTX; - -static void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) { - uint32_t a, b, c, d, e; - union char64long16 block[1]; - - memcpy(block, buffer, 64); - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); - R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); - R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); - R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); - R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); - R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); - R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); - R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); - R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); - R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); - R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); - R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); - R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); - R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); - R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); - R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); - R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); - R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); - R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); - R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - a = b = c = d = e = 0; - memset(block, '\0', sizeof(block)); -} - -static void SHA1Init(SHA1_CTX* context) { - context->state[0] = 0x67452301; - context->state[1] = 0xEFCDAB89; - context->state[2] = 0x98BADCFE; - context->state[3] = 0x10325476; - context->state[4] = 0xC3D2E1F0; - context->count[0] = context->count[1] = 0; -} - -static void SHA1Update(SHA1_CTX* context, const unsigned char* data, - uint32_t len) { - uint32_t i, j; - - j = context->count[0]; - if ((context->count[0] += len << 3) < j) - context->count[1]++; - context->count[1] += (len>>29); - j = (j >> 3) & 63; - if ((j + len) > 63) { - memcpy(&context->buffer[j], data, (i = 64-j)); - SHA1Transform(context->state, context->buffer); - for ( ; i + 63 < len; i += 64) { - SHA1Transform(context->state, &data[i]); - } - j = 0; - } - else i = 0; - memcpy(&context->buffer[j], &data[i], len - i); -} - -static void SHA1Final(unsigned char digest[20], SHA1_CTX* context) { - unsigned i; - unsigned char finalcount[8], c; - - for (i = 0; i < 8; i++) { - finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] - >> ((3-(i & 3)) * 8) ) & 255); - } - c = 0200; - SHA1Update(context, &c, 1); - while ((context->count[0] & 504) != 448) { - c = 0000; - SHA1Update(context, &c, 1); - } - SHA1Update(context, finalcount, 8); - for (i = 0; i < 20; i++) { - digest[i] = (unsigned char) - ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); - } - memset(context, '\0', sizeof(*context)); - memset(&finalcount, '\0', sizeof(finalcount)); -} -// END OF SHA1 CODE - -static void base64_encode(const unsigned char *src, int src_len, char *dst) { - static const char *b64 = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - int i, j, a, b, c; - - for (i = j = 0; i < src_len; i += 3) { - a = src[i]; - b = i + 1 >= src_len ? 0 : src[i + 1]; - c = i + 2 >= src_len ? 0 : src[i + 2]; - - dst[j++] = b64[a >> 2]; - dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; - if (i + 1 < src_len) { - dst[j++] = b64[(b & 15) << 2 | (c >> 6)]; - } - if (i + 2 < src_len) { - dst[j++] = b64[c & 63]; - } - } - while (j % 4 != 0) { - dst[j++] = '='; - } - dst[j++] = '\0'; -} - -void mg_websocket_handshake(struct mg_connection *conn) { - static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - char buf[100], sha[20], b64_sha[sizeof(sha) * 2]; - SHA1_CTX sha_ctx; - - mg_snprintf(buf, sizeof(buf), "%s%s", - mg_get_header(conn, "Sec-WebSocket-Key"), magic); - SHA1Init(&sha_ctx); - SHA1Update(&sha_ctx, (unsigned char *) buf, strlen(buf)); - SHA1Final((unsigned char *) sha, &sha_ctx); - base64_encode((unsigned char *) sha, sizeof(sha), b64_sha); - mg_printf(conn, "%s%s%s", - "HTTP/1.1 101 Switching Protocols\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Accept: ", b64_sha, "\r\n\r\n"); -} - -int mg_websocket_read(struct mg_connection *conn, int *bits, char **data) { - // Pointer to the beginning of the portion of the incoming websocket message - // queue. The original websocket upgrade request is never removed, - // so the queue begins after it. - unsigned char *buf = (unsigned char *) conn->buf + conn->request_len; - int n, stop = 0; - size_t i, len, mask_len, data_len, header_len, body_len; - char mask[4]; - - assert(conn->content_len == 0); - - // Loop continuously, reading messages from the socket, invoking the callback, - // and waiting repeatedly until an error occurs. - while (!stop) { - header_len = 0; - // body_len is the length of the entire queue in bytes - // len is the length of the current message - // data_len is the length of the current message's data payload - // header_len is the length of the current message's header - if ((body_len = conn->data_len - conn->request_len) >= 2) { - len = buf[1] & 127; - mask_len = buf[1] & 128 ? 4 : 0; - if (len < 126 && body_len >= mask_len) { - data_len = len; - header_len = 2 + mask_len; - } else if (len == 126 && body_len >= 4 + mask_len) { - header_len = 4 + mask_len; - data_len = ((((int) buf[2]) << 8) + buf[3]); - } else if (body_len >= 10 + mask_len) { - header_len = 10 + mask_len; - data_len = (((uint64_t) htonl(* (uint32_t *) &buf[2])) << 32) + - htonl(* (uint32_t *) &buf[6]); - } - } - - // Data layout is as follows: - // conn->buf buf - // v v frame1 | frame2 - // |---------------------|----------------|--------------|------- - // | |<--header_len-->|<--data_len-->| - // |<-conn->request_len->|<-----body_len----------->| - // |<-------------------conn->data_len------------->| - - if (header_len > 0) { - // Allocate space to hold websocket payload - if ((*data = malloc(data_len)) == NULL) { - // Allocation failed, exit the loop and then close the connection - // TODO: notify user about the failure - data_len = 0; - break; - } - - // Save mask and bits, otherwise it may be clobbered by memmove below - *bits = buf[0]; - memcpy(mask, buf + header_len - mask_len, mask_len); - - // Read frame payload into the allocated buffer. - assert(body_len >= header_len); - if (data_len + header_len > body_len) { - len = body_len - header_len; - memcpy(*data, buf + header_len, len); - // TODO: handle pull error - pull_all(NULL, conn, *data + len, data_len - len); - conn->data_len = conn->request_len; - } else { - len = data_len + header_len; - memcpy(*data, buf + header_len, data_len); - memmove(buf, buf + len, body_len - len); - conn->data_len -= len; - } - - // Apply mask if necessary - if (mask_len > 0) { - for (i = 0; i < data_len; i++) { - (*data)[i] ^= mask[i % 4]; - } - } - - return data_len; - } else { - // Buffering websocket request - if ((n = pull(NULL, conn, conn->buf + conn->data_len, - conn->buf_size - conn->data_len)) <= 0) { - break; - } - conn->data_len += n; - } - } - - return 0; -} - -int mg_websocket_write(struct mg_connection* conn, int opcode, - const char *data, size_t data_len) { - unsigned char *copy; - size_t copy_len = 0; - int retval = -1; - - if ((copy = (unsigned char *) malloc(data_len + 10)) == NULL) { - return -1; - } - - copy[0] = 0x80 + (opcode & 0x0f); - - // Frame format: http://tools.ietf.org/html/rfc6455#section-5.2 - if (data_len < 126) { - // Inline 7-bit length field - copy[1] = data_len; - memcpy(copy + 2, data, data_len); - copy_len = 2 + data_len; - } else if (data_len <= 0xFFFF) { - // 16-bit length field - copy[1] = 126; - * (uint16_t *) (copy + 2) = htons(data_len); - memcpy(copy + 4, data, data_len); - copy_len = 4 + data_len; - } else { - // 64-bit length field - copy[1] = 127; - * (uint32_t *) (copy + 2) = htonl((uint64_t) data_len >> 32); - * (uint32_t *) (copy + 6) = htonl(data_len & 0xffffffff); - memcpy(copy + 10, data, data_len); - copy_len = 10 + data_len; - } - - // Not thread safe - if (copy_len > 0) { - retval = mg_write(conn, copy, copy_len); - } - free(copy); - - return retval; -} -#endif // !USE_WEBSOCKET - static int isbyte(int n) { return n >= 0 && n <= 255; } diff --git a/mongoose.c b/mongoose.c index a26c2c50c1c43845aaae183ad58eba573e72335b..366fe4208c4e3cdd9d8e1fddd2b9f351c9988be0 100644 --- a/mongoose.c +++ b/mongoose.c @@ -3541,247 +3541,555 @@ FILE *mg_upload(struct mg_connection *conn, const char *destination_dir, -static int call_user(int type, struct mg_connection *conn, void *p) { - if (conn != NULL && conn->ctx != NULL) { - conn->event.user_data = conn->ctx->user_data; - conn->event.type = type; - conn->event.event_param = p; - conn->event.request_info = &conn->request_info; - conn->event.conn = conn; - } - return conn == NULL || conn->ctx == NULL || conn->ctx->event_handler == NULL ? - 0 : conn->ctx->event_handler(&conn->event); -} +#if defined(USE_WEBSOCKET) -static FILE *mg_fopen(const char *path, const char *mode) { -#ifdef _WIN32 - wchar_t wbuf[PATH_MAX], wmode[20]; - to_unicode(path, wbuf, ARRAY_SIZE(wbuf)); - MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, ARRAY_SIZE(wmode)); - return _wfopen(wbuf, wmode); -#else - return fopen(path, mode); +// START OF SHA-1 code +// Copyright(c) By Steve Reid <steve@edmweb.com> +#define SHA1HANDSOFF +#if defined(__sun) +#include "solarisfixes.h" #endif -} -// Print error message to the opened error log stream. -static void cry(struct mg_connection *conn, const char *fmt, ...) { - char buf[MG_BUF_LEN], src_addr[IP_ADDR_STR_LEN]; - va_list ap; - FILE *fp; - time_t timestamp; +union char64long16 { unsigned char c[64]; uint32_t l[16]; }; - va_start(ap, fmt); - (void) vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) - // Do not lock when getting the callback value, here and below. - // I suppose this is fine, since function cannot disappear in the - // same way string option can. - if (call_user(MG_EVENT_LOG, conn, buf) == 0) { - fp = conn->ctx == NULL || conn->ctx->config[ERROR_LOG_FILE] == NULL ? NULL : - fopen(conn->ctx->config[ERROR_LOG_FILE], "a+"); +static uint32_t blk0(union char64long16 *block, int i) { + // Forrest: SHA expect BIG_ENDIAN, swap if LITTLE_ENDIAN + if (!is_big_endian()) { + block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | + (rol(block->l[i], 8) & 0x00FF00FF); + } + return block->l[i]; +} - if (fp != NULL) { - flockfile(fp); - timestamp = time(NULL); +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(block, i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); - sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa); - fprintf(fp, "[%010lu] [error] [client %s] ", (unsigned long) timestamp, - src_addr); +typedef struct { + uint32_t state[5]; + uint32_t count[2]; + unsigned char buffer[64]; +} SHA1_CTX; - if (conn->request_info.request_method != NULL) { - fprintf(fp, "%s %s: ", conn->request_info.request_method, - conn->request_info.uri); - } +static void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) { + uint32_t a, b, c, d, e; + union char64long16 block[1]; - fprintf(fp, "%s", buf); - fputc('\n', fp); - funlockfile(fp); - fclose(fp); - } - } + memcpy(block, buffer, 64); + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + a = b = c = d = e = 0; + memset(block, '\0', sizeof(block)); } -const char *mg_version(void) { - return MONGOOSE_VERSION; +static void SHA1Init(SHA1_CTX* context) { + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; } -// HTTP 1.1 assumes keep alive if "Connection:" header is not set -// This function must tolerate situations when connection info is not -// set up, for example if request parsing failed. -static int should_keep_alive(const struct mg_connection *conn) { - const char *http_version = conn->request_info.http_version; - const char *header = mg_get_header(conn, "Connection"); - if (conn->must_close || - conn->status_code == 401 || - mg_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0 || - (header != NULL && mg_strcasecmp(header, "keep-alive") != 0) || - (header == NULL && http_version && strcmp(http_version, "1.1"))) { - return 0; - } - return 1; -} +static void SHA1Update(SHA1_CTX* context, const unsigned char* data, + uint32_t len) { + uint32_t i, j; -static const char *suggest_connection_header(const struct mg_connection *conn) { - return should_keep_alive(conn) ? "keep-alive" : "close"; + j = context->count[0]; + if ((context->count[0] += len << 3) < j) + context->count[1]++; + context->count[1] += (len>>29); + j = (j >> 3) & 63; + if ((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + SHA1Transform(context->state, &data[i]); + } + j = 0; + } + else i = 0; + memcpy(&context->buffer[j], &data[i], len - i); } -static void send_http_error(struct mg_connection *conn, int status, - const char *reason, const char *fmt, ...) { - char buf[MG_BUF_LEN]; - va_list ap; - int len = 0; - - conn->status_code = status; - buf[0] = '\0'; - - // Errors 1xx, 204 and 304 MUST NOT send a body - if (status > 199 && status != 204 && status != 304) { - len = mg_snprintf(buf, sizeof(buf), "Error %d: %s", status, reason); - buf[len++] = '\n'; +static void SHA1Final(unsigned char digest[20], SHA1_CTX* context) { + unsigned i; + unsigned char finalcount[8], c; - va_start(ap, fmt); - len += mg_vsnprintf(buf + len, sizeof(buf) - len, fmt, ap); - va_end(ap); + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); } - DEBUG_TRACE(("[%s]", buf)); - - if (call_user(MG_HTTP_ERROR, conn, (void *) (long) status) == 0) { - mg_printf(conn, "HTTP/1.1 %d %s\r\n" - "Content-Length: %d\r\n" - "Connection: %s\r\n\r\n", status, reason, len, - suggest_connection_header(conn)); - conn->num_bytes_sent += mg_printf(conn, "%s", buf); + c = 0200; + SHA1Update(context, &c, 1); + while ((context->count[0] & 504) != 448) { + c = 0000; + SHA1Update(context, &c, 1); + } + SHA1Update(context, finalcount, 8); + for (i = 0; i < 20; i++) { + digest[i] = (unsigned char) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); } + memset(context, '\0', sizeof(*context)); + memset(&finalcount, '\0', sizeof(finalcount)); } +// END OF SHA1 CODE -// Return 1 if real file has been found, 0 otherwise -static int convert_uri_to_file_name(struct mg_connection *conn, char *buf, - size_t buf_len, struct file *filep) { - struct vec a, b; - const char *rewrite, *uri = conn->request_info.uri, - *root = conn->ctx->config[DOCUMENT_ROOT]; - char *p; - int match_len; - char gz_path[PATH_MAX]; - char const* accept_encoding; - - // No filesystem access - if (root == NULL) { - return 0; - } +static void base64_encode(const unsigned char *src, int src_len, char *dst) { + static const char *b64 = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + int i, j, a, b, c; - // Using buf_len - 1 because memmove() for PATH_INFO may shift part - // of the path one byte on the right. - // If document_root is NULL, leave the file empty. - mg_snprintf(buf, buf_len - 1, "%s%s", root, uri); + for (i = j = 0; i < src_len; i += 3) { + a = src[i]; + b = i + 1 >= src_len ? 0 : src[i + 1]; + c = i + 2 >= src_len ? 0 : src[i + 2]; - rewrite = conn->ctx->config[REWRITE]; - while ((rewrite = next_option(rewrite, &a, &b)) != NULL) { - if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) { - mg_snprintf(buf, buf_len - 1, "%.*s%s", (int) b.len, b.ptr, - uri + match_len); - break; + dst[j++] = b64[a >> 2]; + dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; + if (i + 1 < src_len) { + dst[j++] = b64[(b & 15) << 2 | (c >> 6)]; + } + if (i + 2 < src_len) { + dst[j++] = b64[c & 63]; } } - - if (mg_stat(buf, filep)) { - return 1; + while (j % 4 != 0) { + dst[j++] = '='; } + dst[j++] = '\0'; +} - // if we can't find the actual file, look for the file - // with the same name but a .gz extension. If we find it, - // use that and set the gzipped flag in the file struct - // to indicate that the response need to have the content- - // encoding: gzip header - // we can only do this if the browser declares support - if ((accept_encoding = mg_get_header(conn, "Accept-Encoding")) != NULL) { - if (strstr(accept_encoding,"gzip") != NULL) { - snprintf(gz_path, sizeof(gz_path), "%s.gz", buf); - if (mg_stat(gz_path, filep)) { - filep->gzipped = 1; - return 1; +void mg_websocket_handshake(struct mg_connection *conn) { + static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + char buf[100], sha[20], b64_sha[sizeof(sha) * 2]; + SHA1_CTX sha_ctx; + + mg_snprintf(buf, sizeof(buf), "%s%s", + mg_get_header(conn, "Sec-WebSocket-Key"), magic); + SHA1Init(&sha_ctx); + SHA1Update(&sha_ctx, (unsigned char *) buf, strlen(buf)); + SHA1Final((unsigned char *) sha, &sha_ctx); + base64_encode((unsigned char *) sha, sizeof(sha), b64_sha); + mg_printf(conn, "%s%s%s", + "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: ", b64_sha, "\r\n\r\n"); +} + +int mg_websocket_read(struct mg_connection *conn, int *bits, char **data) { + // Pointer to the beginning of the portion of the incoming websocket message + // queue. The original websocket upgrade request is never removed, + // so the queue begins after it. + unsigned char *buf = (unsigned char *) conn->buf + conn->request_len; + int n, stop = 0; + size_t i, len, mask_len, data_len, header_len, body_len; + char mask[4]; + + assert(conn->content_len == 0); + + // Loop continuously, reading messages from the socket, invoking the callback, + // and waiting repeatedly until an error occurs. + while (!stop) { + header_len = 0; + // body_len is the length of the entire queue in bytes + // len is the length of the current message + // data_len is the length of the current message's data payload + // header_len is the length of the current message's header + if ((body_len = conn->data_len - conn->request_len) >= 2) { + len = buf[1] & 127; + mask_len = buf[1] & 128 ? 4 : 0; + if (len < 126 && body_len >= mask_len) { + data_len = len; + header_len = 2 + mask_len; + } else if (len == 126 && body_len >= 4 + mask_len) { + header_len = 4 + mask_len; + data_len = ((((int) buf[2]) << 8) + buf[3]); + } else if (body_len >= 10 + mask_len) { + header_len = 10 + mask_len; + data_len = (((uint64_t) htonl(* (uint32_t *) &buf[2])) << 32) + + htonl(* (uint32_t *) &buf[6]); } } - } - // Support PATH_INFO for CGI scripts. - for (p = buf + strlen(root == NULL ? "" : root); *p != '\0'; p++) { - if (*p == '/') { - *p = '\0'; - if (match_prefix(conn->ctx->config[CGI_EXTENSIONS], - strlen(conn->ctx->config[CGI_EXTENSIONS]), buf) > 0 && - mg_stat(buf, filep)) { - // Shift PATH_INFO block one character right, e.g. - // "/x.cgi/foo/bar\x00" => "/x.cgi\x00/foo/bar\x00" - // conn->path_info is pointing to the local variable "path" declared - // in handle_request(), so PATH_INFO is not valid after - // handle_request returns. - conn->path_info = p + 1; - memmove(p + 2, p + 1, strlen(p + 1) + 1); // +1 is for trailing \0 - p[1] = '/'; - return 1; + // Data layout is as follows: + // conn->buf buf + // v v frame1 | frame2 + // |---------------------|----------------|--------------|------- + // | |<--header_len-->|<--data_len-->| + // |<-conn->request_len->|<-----body_len----------->| + // |<-------------------conn->data_len------------->| + + if (header_len > 0) { + // Allocate space to hold websocket payload + if ((*data = malloc(data_len)) == NULL) { + // Allocation failed, exit the loop and then close the connection + // TODO: notify user about the failure + data_len = 0; + break; + } + + // Save mask and bits, otherwise it may be clobbered by memmove below + *bits = buf[0]; + memcpy(mask, buf + header_len - mask_len, mask_len); + + // Read frame payload into the allocated buffer. + assert(body_len >= header_len); + if (data_len + header_len > body_len) { + len = body_len - header_len; + memcpy(*data, buf + header_len, len); + // TODO: handle pull error + pull_all(NULL, conn, *data + len, data_len - len); + conn->data_len = conn->request_len; } else { - *p = '/'; + len = data_len + header_len; + memcpy(*data, buf + header_len, data_len); + memmove(buf, buf + len, body_len - len); + conn->data_len -= len; + } + + // Apply mask if necessary + if (mask_len > 0) { + for (i = 0; i < data_len; i++) { + (*data)[i] ^= mask[i % 4]; + } + } + + return data_len; + } else { + // Buffering websocket request + if ((n = pull(NULL, conn, conn->buf + conn->data_len, + conn->buf_size - conn->data_len)) <= 0) { + break; } + conn->data_len += n; } } return 0; } -static void gmt_time_string(char *buf, size_t buf_len, time_t *t) { - strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t)); +int mg_websocket_write(struct mg_connection* conn, int opcode, + const char *data, size_t data_len) { + unsigned char *copy; + size_t copy_len = 0; + int retval = -1; + + if ((copy = (unsigned char *) malloc(data_len + 10)) == NULL) { + return -1; + } + + copy[0] = 0x80 + (opcode & 0x0f); + + // Frame format: http://tools.ietf.org/html/rfc6455#section-5.2 + if (data_len < 126) { + // Inline 7-bit length field + copy[1] = data_len; + memcpy(copy + 2, data, data_len); + copy_len = 2 + data_len; + } else if (data_len <= 0xFFFF) { + // 16-bit length field + copy[1] = 126; + * (uint16_t *) (copy + 2) = htons(data_len); + memcpy(copy + 4, data, data_len); + copy_len = 4 + data_len; + } else { + // 64-bit length field + copy[1] = 127; + * (uint32_t *) (copy + 2) = htonl((uint64_t) data_len >> 32); + * (uint32_t *) (copy + 6) = htonl(data_len & 0xffffffff); + memcpy(copy + 10, data, data_len); + copy_len = 10 + data_len; + } + + // Not thread safe + if (copy_len > 0) { + retval = mg_write(conn, copy, copy_len); + } + free(copy); + + return retval; } +#endif // !USE_WEBSOCKET -static void construct_etag(char *buf, size_t buf_len, - const struct file *filep) { - snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"", - (unsigned long) filep->modification_time, filep->size); + +static int call_user(int type, struct mg_connection *conn, void *p) { + if (conn != NULL && conn->ctx != NULL) { + conn->event.user_data = conn->ctx->user_data; + conn->event.type = type; + conn->event.event_param = p; + conn->event.request_info = &conn->request_info; + conn->event.conn = conn; + } + return conn == NULL || conn->ctx == NULL || conn->ctx->event_handler == NULL ? + 0 : conn->ctx->event_handler(&conn->event); } -static void fclose_on_exec(FILE *fp) { - if (fp != NULL) { -#ifndef _WIN32 - fcntl(fileno(fp), F_SETFD, FD_CLOEXEC); +static FILE *mg_fopen(const char *path, const char *mode) { +#ifdef _WIN32 + wchar_t wbuf[PATH_MAX], wmode[20]; + to_unicode(path, wbuf, ARRAY_SIZE(wbuf)); + MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, ARRAY_SIZE(wmode)); + return _wfopen(wbuf, wmode); +#else + return fopen(path, mode); #endif - } } -static void handle_file_request(struct mg_connection *conn, const char *path, - struct file *filep) { - char date[64], lm[64], etag[64], range[64]; - const char *msg = "OK", *hdr; - time_t curtime = time(NULL); - int64_t cl, r1, r2; - struct vec mime_vec; - int n; - char gz_path[PATH_MAX]; - char const* encoding = ""; +// Print error message to the opened error log stream. +static void cry(struct mg_connection *conn, const char *fmt, ...) { + char buf[MG_BUF_LEN], src_addr[IP_ADDR_STR_LEN]; + va_list ap; FILE *fp; + time_t timestamp; - get_mime_type(conn->ctx, path, &mime_vec); - cl = filep->size; - conn->status_code = 200; - range[0] = '\0'; + va_start(ap, fmt); + (void) vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); - // if this file is in fact a pre-gzipped file, rewrite its filename - // it's important to rewrite the filename after resolving - // the mime type from it, to preserve the actual file's type - if (filep->gzipped) { - snprintf(gz_path, sizeof(gz_path), "%s.gz", path); - path = gz_path; - encoding = "Content-Encoding: gzip\r\n"; - } + // Do not lock when getting the callback value, here and below. + // I suppose this is fine, since function cannot disappear in the + // same way string option can. + if (call_user(MG_EVENT_LOG, conn, buf) == 0) { + fp = conn->ctx == NULL || conn->ctx->config[ERROR_LOG_FILE] == NULL ? NULL : + fopen(conn->ctx->config[ERROR_LOG_FILE], "a+"); - if ((fp = mg_fopen(path, "rb")) == NULL) { - send_http_error(conn, 500, http_500_error, - "fopen(%s): %s", path, strerror(ERRNO)); - return; + if (fp != NULL) { + flockfile(fp); + timestamp = time(NULL); + + sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa); + fprintf(fp, "[%010lu] [error] [client %s] ", (unsigned long) timestamp, + src_addr); + + if (conn->request_info.request_method != NULL) { + fprintf(fp, "%s %s: ", conn->request_info.request_method, + conn->request_info.uri); + } + + fprintf(fp, "%s", buf); + fputc('\n', fp); + funlockfile(fp); + fclose(fp); + } } +} - fclose_on_exec(fp); +const char *mg_version(void) { + return MONGOOSE_VERSION; +} + +// HTTP 1.1 assumes keep alive if "Connection:" header is not set +// This function must tolerate situations when connection info is not +// set up, for example if request parsing failed. +static int should_keep_alive(const struct mg_connection *conn) { + const char *http_version = conn->request_info.http_version; + const char *header = mg_get_header(conn, "Connection"); + if (conn->must_close || + conn->status_code == 401 || + mg_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0 || + (header != NULL && mg_strcasecmp(header, "keep-alive") != 0) || + (header == NULL && http_version && strcmp(http_version, "1.1"))) { + return 0; + } + return 1; +} + +static const char *suggest_connection_header(const struct mg_connection *conn) { + return should_keep_alive(conn) ? "keep-alive" : "close"; +} + +static void send_http_error(struct mg_connection *conn, int status, + const char *reason, const char *fmt, ...) { + char buf[MG_BUF_LEN]; + va_list ap; + int len = 0; + + conn->status_code = status; + buf[0] = '\0'; + + // Errors 1xx, 204 and 304 MUST NOT send a body + if (status > 199 && status != 204 && status != 304) { + len = mg_snprintf(buf, sizeof(buf), "Error %d: %s", status, reason); + buf[len++] = '\n'; + + va_start(ap, fmt); + len += mg_vsnprintf(buf + len, sizeof(buf) - len, fmt, ap); + va_end(ap); + } + DEBUG_TRACE(("[%s]", buf)); + + if (call_user(MG_HTTP_ERROR, conn, (void *) (long) status) == 0) { + mg_printf(conn, "HTTP/1.1 %d %s\r\n" + "Content-Length: %d\r\n" + "Connection: %s\r\n\r\n", status, reason, len, + suggest_connection_header(conn)); + conn->num_bytes_sent += mg_printf(conn, "%s", buf); + } +} + +// Return 1 if real file has been found, 0 otherwise +static int convert_uri_to_file_name(struct mg_connection *conn, char *buf, + size_t buf_len, struct file *filep) { + struct vec a, b; + const char *rewrite, *uri = conn->request_info.uri, + *root = conn->ctx->config[DOCUMENT_ROOT]; + char *p; + int match_len; + char gz_path[PATH_MAX]; + char const* accept_encoding; + + // No filesystem access + if (root == NULL) { + return 0; + } + + // Using buf_len - 1 because memmove() for PATH_INFO may shift part + // of the path one byte on the right. + // If document_root is NULL, leave the file empty. + mg_snprintf(buf, buf_len - 1, "%s%s", root, uri); + + rewrite = conn->ctx->config[REWRITE]; + while ((rewrite = next_option(rewrite, &a, &b)) != NULL) { + if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) { + mg_snprintf(buf, buf_len - 1, "%.*s%s", (int) b.len, b.ptr, + uri + match_len); + break; + } + } + + if (mg_stat(buf, filep)) { + return 1; + } + + // if we can't find the actual file, look for the file + // with the same name but a .gz extension. If we find it, + // use that and set the gzipped flag in the file struct + // to indicate that the response need to have the content- + // encoding: gzip header + // we can only do this if the browser declares support + if ((accept_encoding = mg_get_header(conn, "Accept-Encoding")) != NULL) { + if (strstr(accept_encoding,"gzip") != NULL) { + snprintf(gz_path, sizeof(gz_path), "%s.gz", buf); + if (mg_stat(gz_path, filep)) { + filep->gzipped = 1; + return 1; + } + } + } + + // Support PATH_INFO for CGI scripts. + for (p = buf + strlen(root == NULL ? "" : root); *p != '\0'; p++) { + if (*p == '/') { + *p = '\0'; + if (match_prefix(conn->ctx->config[CGI_EXTENSIONS], + strlen(conn->ctx->config[CGI_EXTENSIONS]), buf) > 0 && + mg_stat(buf, filep)) { + // Shift PATH_INFO block one character right, e.g. + // "/x.cgi/foo/bar\x00" => "/x.cgi\x00/foo/bar\x00" + // conn->path_info is pointing to the local variable "path" declared + // in handle_request(), so PATH_INFO is not valid after + // handle_request returns. + conn->path_info = p + 1; + memmove(p + 2, p + 1, strlen(p + 1) + 1); // +1 is for trailing \0 + p[1] = '/'; + return 1; + } else { + *p = '/'; + } + } + } + + return 0; +} + +static void gmt_time_string(char *buf, size_t buf_len, time_t *t) { + strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t)); +} + +static void construct_etag(char *buf, size_t buf_len, + const struct file *filep) { + snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"", + (unsigned long) filep->modification_time, filep->size); +} + +static void fclose_on_exec(FILE *fp) { + if (fp != NULL) { +#ifndef _WIN32 + fcntl(fileno(fp), F_SETFD, FD_CLOEXEC); +#endif + } +} + +static void handle_file_request(struct mg_connection *conn, const char *path, + struct file *filep) { + char date[64], lm[64], etag[64], range[64]; + const char *msg = "OK", *hdr; + time_t curtime = time(NULL); + int64_t cl, r1, r2; + struct vec mime_vec; + int n; + char gz_path[PATH_MAX]; + char const* encoding = ""; + FILE *fp; + + get_mime_type(conn->ctx, path, &mime_vec); + cl = filep->size; + conn->status_code = 200; + range[0] = '\0'; + + // if this file is in fact a pre-gzipped file, rewrite its filename + // it's important to rewrite the filename after resolving + // the mime type from it, to preserve the actual file's type + if (filep->gzipped) { + snprintf(gz_path, sizeof(gz_path), "%s.gz", path); + path = gz_path; + encoding = "Content-Encoding: gzip\r\n"; + } + + if ((fp = mg_fopen(path, "rb")) == NULL) { + send_http_error(conn, 500, http_500_error, + "fopen(%s): %s", path, strerror(ERRNO)); + return; + } + + fclose_on_exec(fp); // If Range: header specified, act accordingly r1 = r2 = 0; @@ -4188,313 +4496,6 @@ static void handle_propfind(struct mg_connection *conn, const char *path, conn->num_bytes_sent += mg_printf(conn, "%s\n", "</d:multistatus>"); } -#if defined(USE_WEBSOCKET) - -// START OF SHA-1 code -// Copyright(c) By Steve Reid <steve@edmweb.com> -#define SHA1HANDSOFF -#if defined(__sun) -#include "solarisfixes.h" -#endif - -union char64long16 { unsigned char c[64]; uint32_t l[16]; }; - -#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) - -static uint32_t blk0(union char64long16 *block, int i) { - // Forrest: SHA expect BIG_ENDIAN, swap if LITTLE_ENDIAN - if (!is_big_endian()) { - block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | - (rol(block->l[i], 8) & 0x00FF00FF); - } - return block->l[i]; -} - -#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ - ^block->l[(i+2)&15]^block->l[i&15],1)) -#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(block, i)+0x5A827999+rol(v,5);w=rol(w,30); -#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); -#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); -#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); -#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); - -typedef struct { - uint32_t state[5]; - uint32_t count[2]; - unsigned char buffer[64]; -} SHA1_CTX; - -static void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) { - uint32_t a, b, c, d, e; - union char64long16 block[1]; - - memcpy(block, buffer, 64); - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); - R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); - R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); - R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); - R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); - R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); - R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); - R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); - R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); - R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); - R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); - R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); - R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); - R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); - R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); - R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); - R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); - R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); - R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); - R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - a = b = c = d = e = 0; - memset(block, '\0', sizeof(block)); -} - -static void SHA1Init(SHA1_CTX* context) { - context->state[0] = 0x67452301; - context->state[1] = 0xEFCDAB89; - context->state[2] = 0x98BADCFE; - context->state[3] = 0x10325476; - context->state[4] = 0xC3D2E1F0; - context->count[0] = context->count[1] = 0; -} - -static void SHA1Update(SHA1_CTX* context, const unsigned char* data, - uint32_t len) { - uint32_t i, j; - - j = context->count[0]; - if ((context->count[0] += len << 3) < j) - context->count[1]++; - context->count[1] += (len>>29); - j = (j >> 3) & 63; - if ((j + len) > 63) { - memcpy(&context->buffer[j], data, (i = 64-j)); - SHA1Transform(context->state, context->buffer); - for ( ; i + 63 < len; i += 64) { - SHA1Transform(context->state, &data[i]); - } - j = 0; - } - else i = 0; - memcpy(&context->buffer[j], &data[i], len - i); -} - -static void SHA1Final(unsigned char digest[20], SHA1_CTX* context) { - unsigned i; - unsigned char finalcount[8], c; - - for (i = 0; i < 8; i++) { - finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] - >> ((3-(i & 3)) * 8) ) & 255); - } - c = 0200; - SHA1Update(context, &c, 1); - while ((context->count[0] & 504) != 448) { - c = 0000; - SHA1Update(context, &c, 1); - } - SHA1Update(context, finalcount, 8); - for (i = 0; i < 20; i++) { - digest[i] = (unsigned char) - ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); - } - memset(context, '\0', sizeof(*context)); - memset(&finalcount, '\0', sizeof(finalcount)); -} -// END OF SHA1 CODE - -static void base64_encode(const unsigned char *src, int src_len, char *dst) { - static const char *b64 = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - int i, j, a, b, c; - - for (i = j = 0; i < src_len; i += 3) { - a = src[i]; - b = i + 1 >= src_len ? 0 : src[i + 1]; - c = i + 2 >= src_len ? 0 : src[i + 2]; - - dst[j++] = b64[a >> 2]; - dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; - if (i + 1 < src_len) { - dst[j++] = b64[(b & 15) << 2 | (c >> 6)]; - } - if (i + 2 < src_len) { - dst[j++] = b64[c & 63]; - } - } - while (j % 4 != 0) { - dst[j++] = '='; - } - dst[j++] = '\0'; -} - -void mg_websocket_handshake(struct mg_connection *conn) { - static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - char buf[100], sha[20], b64_sha[sizeof(sha) * 2]; - SHA1_CTX sha_ctx; - - mg_snprintf(buf, sizeof(buf), "%s%s", - mg_get_header(conn, "Sec-WebSocket-Key"), magic); - SHA1Init(&sha_ctx); - SHA1Update(&sha_ctx, (unsigned char *) buf, strlen(buf)); - SHA1Final((unsigned char *) sha, &sha_ctx); - base64_encode((unsigned char *) sha, sizeof(sha), b64_sha); - mg_printf(conn, "%s%s%s", - "HTTP/1.1 101 Switching Protocols\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Accept: ", b64_sha, "\r\n\r\n"); -} - -int mg_websocket_read(struct mg_connection *conn, int *bits, char **data) { - // Pointer to the beginning of the portion of the incoming websocket message - // queue. The original websocket upgrade request is never removed, - // so the queue begins after it. - unsigned char *buf = (unsigned char *) conn->buf + conn->request_len; - int n, stop = 0; - size_t i, len, mask_len, data_len, header_len, body_len; - char mask[4]; - - assert(conn->content_len == 0); - - // Loop continuously, reading messages from the socket, invoking the callback, - // and waiting repeatedly until an error occurs. - while (!stop) { - header_len = 0; - // body_len is the length of the entire queue in bytes - // len is the length of the current message - // data_len is the length of the current message's data payload - // header_len is the length of the current message's header - if ((body_len = conn->data_len - conn->request_len) >= 2) { - len = buf[1] & 127; - mask_len = buf[1] & 128 ? 4 : 0; - if (len < 126 && body_len >= mask_len) { - data_len = len; - header_len = 2 + mask_len; - } else if (len == 126 && body_len >= 4 + mask_len) { - header_len = 4 + mask_len; - data_len = ((((int) buf[2]) << 8) + buf[3]); - } else if (body_len >= 10 + mask_len) { - header_len = 10 + mask_len; - data_len = (((uint64_t) htonl(* (uint32_t *) &buf[2])) << 32) + - htonl(* (uint32_t *) &buf[6]); - } - } - - // Data layout is as follows: - // conn->buf buf - // v v frame1 | frame2 - // |---------------------|----------------|--------------|------- - // | |<--header_len-->|<--data_len-->| - // |<-conn->request_len->|<-----body_len----------->| - // |<-------------------conn->data_len------------->| - - if (header_len > 0) { - // Allocate space to hold websocket payload - if ((*data = malloc(data_len)) == NULL) { - // Allocation failed, exit the loop and then close the connection - // TODO: notify user about the failure - data_len = 0; - break; - } - - // Save mask and bits, otherwise it may be clobbered by memmove below - *bits = buf[0]; - memcpy(mask, buf + header_len - mask_len, mask_len); - - // Read frame payload into the allocated buffer. - assert(body_len >= header_len); - if (data_len + header_len > body_len) { - len = body_len - header_len; - memcpy(*data, buf + header_len, len); - // TODO: handle pull error - pull_all(NULL, conn, *data + len, data_len - len); - conn->data_len = conn->request_len; - } else { - len = data_len + header_len; - memcpy(*data, buf + header_len, data_len); - memmove(buf, buf + len, body_len - len); - conn->data_len -= len; - } - - // Apply mask if necessary - if (mask_len > 0) { - for (i = 0; i < data_len; i++) { - (*data)[i] ^= mask[i % 4]; - } - } - - return data_len; - } else { - // Buffering websocket request - if ((n = pull(NULL, conn, conn->buf + conn->data_len, - conn->buf_size - conn->data_len)) <= 0) { - break; - } - conn->data_len += n; - } - } - - return 0; -} - -int mg_websocket_write(struct mg_connection* conn, int opcode, - const char *data, size_t data_len) { - unsigned char *copy; - size_t copy_len = 0; - int retval = -1; - - if ((copy = (unsigned char *) malloc(data_len + 10)) == NULL) { - return -1; - } - - copy[0] = 0x80 + (opcode & 0x0f); - - // Frame format: http://tools.ietf.org/html/rfc6455#section-5.2 - if (data_len < 126) { - // Inline 7-bit length field - copy[1] = data_len; - memcpy(copy + 2, data, data_len); - copy_len = 2 + data_len; - } else if (data_len <= 0xFFFF) { - // 16-bit length field - copy[1] = 126; - * (uint16_t *) (copy + 2) = htons(data_len); - memcpy(copy + 4, data, data_len); - copy_len = 4 + data_len; - } else { - // 64-bit length field - copy[1] = 127; - * (uint32_t *) (copy + 2) = htonl((uint64_t) data_len >> 32); - * (uint32_t *) (copy + 6) = htonl(data_len & 0xffffffff); - memcpy(copy + 10, data, data_len); - copy_len = 10 + data_len; - } - - // Not thread safe - if (copy_len > 0) { - retval = mg_write(conn, copy, copy_len); - } - free(copy); - - return retval; -} -#endif // !USE_WEBSOCKET - static int isbyte(int n) { return n >= 0 && n <= 255; }