1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <errno.h>
8 #include <stdbool.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <strings.h>
13 
14 #include <zephyr/kernel.h>
15 #include <zephyr/logging/log.h>
16 #include <zephyr/net/http/service.h>
17 #include <zephyr/sys/base64.h>
18 #include <mbedtls/sha1.h>
19 #include <zephyr/net/websocket.h>
20 
21 LOG_MODULE_DECLARE(net_http_server, CONFIG_NET_HTTP_SERVER_LOG_LEVEL);
22 
23 #include "headers/server_internal.h"
24 
25 #if !defined(ZEPHYR_USER_AGENT)
26 #define ZEPHYR_USER_AGENT "Zephyr"
27 #endif
28 
29 /* From RFC 6455 chapter 4.2.2 */
30 #define WS_MAGIC "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
31 
32 /* Handle upgrade from HTTP/1.1 to Websocket, see RFC 6455
33  */
handle_http1_to_websocket_upgrade(struct http_client_ctx * client)34 int handle_http1_to_websocket_upgrade(struct http_client_ctx *client)
35 {
36 	static const char switching_protocols[] =
37 		"HTTP/1.1 101 Switching Protocols\r\n"
38 		"Connection: Upgrade\r\n"
39 		"Upgrade: websocket\r\n"
40 		"Sec-WebSocket-Accept: ";
41 	char key_accept[HTTP_SERVER_WS_MAX_SEC_KEY_LEN + sizeof(WS_MAGIC)];
42 	char accept[20];
43 	char tmp[64];
44 	size_t key_len;
45 	size_t olen;
46 	int ret;
47 
48 	key_len = MIN(sizeof(key_accept) - 1, sizeof(client->ws_sec_key));
49 	strncpy(key_accept, client->ws_sec_key, key_len);
50 	key_len = strlen(key_accept);
51 
52 	olen = MIN(sizeof(key_accept) - 1 - key_len, sizeof(WS_MAGIC) - 1);
53 	strncpy(key_accept + key_len, WS_MAGIC, olen);
54 
55 	mbedtls_sha1(key_accept, olen + key_len, accept);
56 
57 	ret = base64_encode(tmp, sizeof(tmp) - 1, &olen, accept, sizeof(accept));
58 	if (ret) {
59 		if (ret == -ENOMEM) {
60 			NET_DBG("[%p] Too short buffer olen %zd", client, olen);
61 		}
62 
63 		goto error;
64 	}
65 
66 	ret = http_server_sendall(client, switching_protocols,
67 				  sizeof(switching_protocols) - 1);
68 	if (ret < 0) {
69 		NET_DBG("Cannot write to socket (%d)", ret);
70 		goto error;
71 	}
72 
73 	client->http1_headers_sent = true;
74 
75 	ret = http_server_sendall(client, tmp, strlen(tmp));
76 	if (ret < 0) {
77 		NET_DBG("Cannot write to socket (%d)", ret);
78 		goto error;
79 	}
80 
81 	ret = snprintk(tmp, sizeof(tmp), "\r\nUser-Agent: %s\r\n\r\n",
82 		       ZEPHYR_USER_AGENT);
83 	if (ret < 0 || ret >= sizeof(tmp)) {
84 		goto error;
85 	}
86 
87 	ret = http_server_sendall(client, tmp, strlen(tmp));
88 	if (ret < 0) {
89 		NET_DBG("Cannot write to socket (%d)", ret);
90 		goto error;
91 	}
92 
93 	/* Only after the complete HTTP1 payload has been processed, switch
94 	 * to Websocket.
95 	 */
96 	if (client->parser_state == HTTP1_MESSAGE_COMPLETE_STATE) {
97 		struct http_resource_detail_websocket *ws_detail;
98 		struct http_request_ctx request_ctx;
99 		int ws_sock;
100 		char *params;
101 		size_t params_len;
102 
103 		ws_detail = (struct http_resource_detail_websocket *)client->current_detail;
104 
105 		ret = ws_sock = websocket_register(client->fd,
106 						   ws_detail->data_buffer,
107 						   ws_detail->data_buffer_len);
108 		if (ret < 0) {
109 			NET_DBG("Cannot register websocket (%d)", ret);
110 			goto error;
111 		}
112 
113 		memset(&request_ctx, 0, sizeof(request_ctx));
114 		params = &client->url_buffer[client->current_detail->path_len];
115 		params_len = strlen(params);
116 		populate_request_ctx(&request_ctx, params, params_len, &client->header_capture_ctx);
117 
118 		ret = ws_detail->cb(ws_sock, &request_ctx, ws_detail->user_data);
119 		http_server_release_client(client);
120 
121 		if (ret < 0) {
122 			NET_DBG("WS connection failed (%d)", ret);
123 			websocket_unregister(ws_sock);
124 			goto error;
125 		}
126 	}
127 
128 	return 0;
129 
130 error:
131 	return ret;
132 }
133