diff --git a/src/civetweb.c b/src/civetweb.c index af0ebd1a..955b2c84 100755 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -8259,46 +8259,16 @@ SHA1Final(unsigned char digest[20], SHA1_CTX *context) static int -send_websocket_handshake(struct mg_connection *conn) +send_websocket_handshake(struct mg_connection *conn, const char *websock_key) { static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; const char *protocol = NULL; char buf[100], sha[20], b64_sha[sizeof(sha) * 2]; SHA1_CTX sha_ctx; int truncated; - const char *websock_key = mg_get_header(conn, "Sec-WebSocket-Key"); - - if (websock_key) { - /* RFC standard version: - * https://tools.ietf.org/html/rfc6455 */ - - /* Reply for Sec-WebSocket-Accept */ - mg_snprintf( - conn, &truncated, buf, sizeof(buf), "%s%s", websock_key, magic); - - } else { - /* hixie draft version: - * http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76 */ - const char *key1 = mg_get_header(conn, "Sec-WebSocket-Key1"); - const char *key2 = mg_get_header(conn, "Sec-WebSocket-Key2"); - char key3[8]; - - if ((!key1) || (!key2)) { - return 0; - } - - /* This version uses 8 byte body data in a GET request */ - conn->content_len = 8; - if ((!key1) || (!key2) || (8 != mg_read(conn, key3, 8))) { - return 0; - } - } - - const char *host = mg_get_header(conn, "Host"); - if (!host) { - return 0; - } + /* Calculate Sec-WebSocket-Accept reply from Sec-WebSocket-Key. */ + mg_snprintf(conn, &truncated, buf, sizeof(buf), "%s%s", websock_key, magic); if (truncated) { conn->must_close = 1; return 0; @@ -8621,6 +8591,7 @@ handle_websocket_request(struct mg_connection *conn, mg_websocket_close_handler ws_close_handler, void *cbData) { + const char *websock_key = mg_get_header(conn, "Sec-WebSocket-Key"); const char *version = mg_get_header(conn, "Sec-WebSocket-Version"); int lua_websock = 0; @@ -8629,17 +8600,55 @@ handle_websocket_request(struct mg_connection *conn, #endif /* Step 1: Check websocket protocol version. */ + /* Step 1.1: Check Sec-WebSocket-Key. */ + if (!websock_key) { + /* The RFC standard version (https://tools.ietf.org/html/rfc6455) + * requires a Sec-WebSocket-Key header. + */ + /* It could be the hixie draft version + * (http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76). + */ + const char *key1 = mg_get_header(conn, "Sec-WebSocket-Key1"); + const char *key2 = mg_get_header(conn, "Sec-WebSocket-Key2"); + char key3[8]; + + if ((key1 != NULL) && (key2 != NULL)) { + /* This version uses 8 byte body data in a GET request */ + conn->content_len = 8; + if (8 == mg_read(conn, key3, 8)) { + /* This is the hixie version */ + send_http_error(conn, + 426, + "%s", + "Protocol upgrade to RFC 6455 required"); + return; + } + } + /* This is an unknown version */ + send_http_error(conn, 400, "%s", "Malformed websocket request"); + return; + } + + /* Step 1.2: Check websocket protocol version. */ + /* The RFC version (https://tools.ietf.org/html/rfc6455) is 13. */ if (version == NULL || strcmp(version, "13") != 0) { /* Reject wrong versions */ send_http_error(conn, 426, "%s", "Protocol upgrade required"); return; } + /* Step 1.3: Check Host. */ + const char *host = mg_get_header(conn, "Host"); + if (!host) { + return 0; + } + /* Step 2: If a callback is responsible, call it. */ if (is_callback_resource) { if (ws_connect_handler != NULL && ws_connect_handler(conn, cbData) != 0) { - /* C callback has returned non-zero, do not proceed with handshake. + /* C callback has returned non-zero, do not proceed with + * handshake. */ /* Note that C callbacks are no longer called when Lua is * responsible, so C can no longer filter callbacks for Lua. */ @@ -8680,7 +8689,7 @@ handle_websocket_request(struct mg_connection *conn, } /* Step 5: The websocket connection has been accepted */ - if (!send_websocket_handshake(conn)) { + if (!send_websocket_handshake(conn, websock_key)) { send_http_error(conn, 500, "%s", "Websocket handshake failed"); return; } @@ -8728,7 +8737,8 @@ is_websocket_protocol(const struct mg_connection *conn) upgrade = mg_get_header(conn, "Upgrade"); if (upgrade == NULL) { - return 0; /* fail early, don't waste time checking other header fields + return 0; /* fail early, don't waste time checking other header + * fields */ } if (!mg_strcasestr(upgrade, "websocket")) { @@ -8747,7 +8757,7 @@ is_websocket_protocol(const struct mg_connection *conn) * "Sec-WebSocket-Version" are also required. * Don't check them here, since even an unsupported websocket protocol * request still IS a websocket request (in contrast to a standard HTTP - * request). It will fail later in the websocket handshake. + * request). It will fail later in handle_websocket_request. */ return 1; @@ -9400,7 +9410,8 @@ handle_request(struct mg_connection *conn) ri->local_uri, uri_len, (char *)ri->local_uri, uri_len + 1, 0); } - /* 1.3. clean URIs, so a path like allowed_dir/../forbidden_file is not + /* 1.3. clean URIs, so a path like allowed_dir/../forbidden_file is + * not * possible */ remove_double_dots_and_double_slashes((char *)ri->local_uri); @@ -9451,9 +9462,11 @@ handle_request(struct mg_connection *conn) /* request not yet handled by a handler or redirect, so the request * is processed here */ - /* 5. interpret the url to find out how the request must be handled */ + /* 5. interpret the url to find out how the request must be handled + */ /* 5.1. first test, if the request targets the regular http(s):// - * protocol namespace or the websocket ws(s):// protocol namespace. */ + * protocol namespace or the websocket ws(s):// protocol namespace. + */ is_websocket_request = is_websocket_protocol(conn); /* 5.2. check if the request will be handled by a callback */ @@ -9465,7 +9478,8 @@ handle_request(struct mg_connection *conn) &ws_data_handler, &ws_close_handler, &callback_data)) { - /* 5.2.1. A callback will handle this request. All requests handled + /* 5.2.1. A callback will handle this request. All requests + * handled * by a callback have to be considered as requests to a script * resource. */ is_callback_resource = 1; @@ -9507,7 +9521,8 @@ handle_request(struct mg_connection *conn) } #if !defined(NO_FILES) - /* 6.1.2. Check if put authorization for static files is available. + /* 6.1.2. Check if put authorization for static files is + * available. */ if (!is_authorized_for_put(conn)) { send_authorization_request(conn); @@ -9532,15 +9547,18 @@ handle_request(struct mg_connection *conn) if (!is_websocket_request) { i = callback_handler(conn, callback_data); if (i > 0) { - /* Do nothing, callback has served the request. Store the - * return value as status code for the log and discard all + /* Do nothing, callback has served the request. Store + * the + * return value as status code for the log and discard + * all * data from the client not used by the callback. */ conn->status_code = i; discard_unread_request_data(conn); } else { /* TODO (high): what if the handler did NOT handle the * request */ - /* The last version did handle this as a file request, but + /* The last version did handle this as a file request, + * but * since a file request is not always a script resource, * the authorization check might be different */ interpret_uri(conn, @@ -9553,7 +9571,8 @@ handle_request(struct mg_connection *conn) &is_put_or_delete_request); callback_handler = NULL; - /* TODO (very low): goto is deprecated but for the moment, + /* TODO (very low): goto is deprecated but for the + * moment, * a goto is simpler than some curious loop. */ /* The situation "callback does not handle the request" * needs to be reconsidered anyway. */ @@ -9607,7 +9626,8 @@ handle_request(struct mg_connection *conn) #endif #if defined(NO_FILES) - /* 9a. In case the server uses only callbacks, this uri is unknown. + /* 9a. In case the server uses only callbacks, this uri is + * unknown. * Then, all request handling ends here. */ send_http_error(conn, 404, "%s", "Not Found"); @@ -9751,13 +9771,15 @@ handle_file_based_request(struct mg_connection *conn, strlen( conn->ctx->config[LUA_SERVER_PAGE_EXTENSIONS]), path) > 0) { - /* Lua server page: an SSI like page containing mostly plain html code + /* Lua server page: an SSI like page containing mostly plain html + * code * plus some tags with server generated contents. */ handle_lsp_request(conn, path, file, NULL); } else if (match_prefix(conn->ctx->config[LUA_SCRIPT_EXTENSIONS], strlen(conn->ctx->config[LUA_SCRIPT_EXTENSIONS]), path) > 0) { - /* Lua in-server module script: a CGI like script used to generate the + /* Lua in-server module script: a CGI like script used to generate + * the * entire reply. */ mg_exec_lua_script(conn, path, NULL); #endif @@ -9839,7 +9861,8 @@ parse_port_string(const struct vec *vec, struct socket *so) && mg_inet_pton( AF_INET6, buf, &so->lsa.sin6, sizeof(so->lsa.sin6))) { /* IPv6 address, examples: see above */ - /* so->lsa.sin6.sin6_family = AF_INET6; already set by mg_inet_pton */ + /* so->lsa.sin6.sin6_family = AF_INET6; already set by mg_inet_pton + */ so->lsa.sin6.sin6_port = htons((uint16_t)port); #endif } else if (sscanf(vec->ptr, "%u%n", &port, &len) == 1) { @@ -9851,7 +9874,8 @@ parse_port_string(const struct vec *vec, struct socket *so) len = 0; } - /* sscanf and the option splitting code ensure the following condition */ + /* sscanf and the option splitting code ensure the following condition + */ if ((len < 0) && ((unsigned)len > (unsigned)vec->len)) { return 0; } @@ -10163,7 +10187,8 @@ log_access(const struct mg_connection *conn) /* Verify given socket address against the ACL. - * Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed. */ + * Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed. + */ static int check_acl(struct mg_context *ctx, uint32_t remote_ip) { @@ -10283,7 +10308,8 @@ ssl_id_callback(void) struct mg_workerTLS *tls = (struct mg_workerTLS *)pthread_getspecific(sTlsKey); if (tls == NULL) { - /* SSL called from an unknown thread: Create some thread index. */ + /* SSL called from an unknown thread: Create some thread index. + */ tls = (struct mg_workerTLS *)mg_malloc(sizeof(struct mg_workerTLS)); tls->is_master = -2; /* -2 means "3rd party thread" */ tls->thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max); @@ -10611,13 +10637,13 @@ set_ssl_option(struct mg_context *ctx) ca_file = ctx->config[SSL_CA_FILE]; if (SSL_CTX_load_verify_locations(ctx->ssl_ctx, ca_file, ca_path) != 1) { - mg_cry( - fc(ctx), - "SSL_CTX_load_verify_locations error: %s " - "ssl_verify_peer requires setting " - "either ssl_ca_path or ssl_ca_file. Is any of them present in " - "the .conf file?", - ssl_error()); + mg_cry(fc(ctx), + "SSL_CTX_load_verify_locations error: %s " + "ssl_verify_peer requires setting " + "either ssl_ca_path or ssl_ca_file. Is any of them " + "present in " + "the .conf file?", + ssl_error()); return 0; } @@ -10768,7 +10794,8 @@ close_socket_gracefully(struct mg_connection *conn) return; } - /* Set linger option to avoid socket hanging out after close. This prevent + /* Set linger option to avoid socket hanging out after close. This + * prevent * ephemeral port exhaust problem under high QPS. */ linger.l_onoff = 1; linger.l_linger = 1; @@ -10789,7 +10816,8 @@ close_socket_gracefully(struct mg_connection *conn) set_non_blocking_mode(conn->client.sock); #if defined(_WIN32) - /* Read and discard pending incoming data. If we do not do that and close + /* Read and discard pending incoming data. If we do not do that and + * close * the socket, the data in the send buffer may be discarded. This * behaviour is seen on Windows, when client keeps sending data * when server decides to close the connection; then when client @@ -10832,7 +10860,8 @@ close_connection(struct mg_connection *conn) #ifndef NO_SSL if (conn->ssl != NULL) { - /* Run SSL_shutdown twice to ensure completly close SSL connection */ + /* Run SSL_shutdown twice to ensure completly close SSL connection + */ SSL_shutdown(conn->ssl); SSL_free(conn->ssl); conn->ssl = NULL; @@ -10963,10 +10992,12 @@ mg_connect_client_impl(const struct mg_client_options *client_options, /* TODO: Check ssl_verify_peer and ssl_ca_path here. SSL_CTX_set_verify call is needed to switch off server - * certificate checking, which is off by default in OpenSSL and on + * certificate checking, which is off by default in OpenSSL and + on * in yaSSL. */ - // TODO: SSL_CTX_set_verify(conn->client_ssl_ctx, SSL_VERIFY_PEER, + // TODO: SSL_CTX_set_verify(conn->client_ssl_ctx, + // SSL_VERIFY_PEER, // verify_ssl_server); if (client_options->client_cert) { @@ -11120,7 +11151,8 @@ get_rel_url_at_current_server(const char *uri, const struct mg_connection *conn) char *hostend = NULL; char *portbegin, *portend; - /* DNS is case insensitive, so use case insensitive string compare here */ + /* DNS is case insensitive, so use case insensitive string compare here + */ domain = conn->ctx->config[AUTHENTICATION_DOMAIN]; if (!domain) { return 0; @@ -11203,7 +11235,8 @@ getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err) conn->request_len = read_request(NULL, conn, conn->buf, conn->buf_size, &conn->data_len); - /* assert(conn->request_len < 0 || conn->data_len >= conn->request_len); */ + /* assert(conn->request_len < 0 || conn->data_len >= conn->request_len); + */ if (conn->request_len >= 0 && conn->data_len < conn->request_len) { mg_snprintf(conn, NULL, /* No truncation check for ebuf */ @@ -11626,10 +11659,12 @@ process_new_connection(struct mg_connection *conn) ri->remote_user = NULL; } - /* NOTE(lsm): order is important here. should_keep_alive() call is + /* NOTE(lsm): order is important here. should_keep_alive() call + * is * using parsed request, which will be invalid after memmove's * below. - * Therefore, memorize should_keep_alive() result now for later use + * Therefore, memorize should_keep_alive() result now for later + * use * in loop exit condition. */ keep_alive = conn->ctx->stop_flag == 0 && keep_alive_enabled && conn->content_len >= 0 && should_keep_alive(conn); @@ -11731,7 +11766,8 @@ worker_thread_run(void *thread_func_param) conn->ctx = ctx; conn->request_info.user_data = ctx->user_data; /* Allocate a mutex for this connection to allow communication both - * within the request handler and from elsewhere in the application */ + * within the request handler and from elsewhere in the application + */ (void)pthread_mutex_init(&conn->mutex, &pthread_mutex_attr); /* Call consume_socket() even when ctx->stop_flag > 0, to let it @@ -11879,9 +11915,11 @@ accept_new_connection(const struct socket *listener, struct mg_context *ctx) strerror(ERRNO)); } - /* Set TCP keep-alive. This is needed because if HTTP-level keep-alive + /* Set TCP keep-alive. This is needed because if HTTP-level + * keep-alive * is enabled, and client resets the connection, server won't get - * TCP FIN or RST and will keep the connection open forever. With TCP + * TCP FIN or RST and will keep the connection open forever. With + * TCP * keep-alive, next keep-alive handshake will figure out that the * client is down and will close the server end. * Thanks to Igor Klopov who suggested the patch. */ @@ -12052,7 +12090,8 @@ free_context(struct mg_context *ctx) ctx->callbacks.exit_context(ctx); } - /* All threads exited, no sync is needed. Destroy thread mutex and condvars + /* All threads exited, no sync is needed. Destroy thread mutex and + * condvars */ (void)pthread_mutex_destroy(&ctx->thread_mutex); (void)pthread_cond_destroy(&ctx->thread_cond); @@ -12233,7 +12272,8 @@ mg_start(const struct mg_callbacks *callbacks, #endif if (0 != pthread_key_create(&sTlsKey, tls_dtor)) { - /* Fatal error - abort start. However, this situation should never + /* Fatal error - abort start. However, this situation should + * never * occur in practice. */ mg_atomic_dec(&sTlsInit); mg_cry(fc(ctx), "Cannot initialize thread local storage");