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 	ret = http_server_sendall(client, tmp, strlen(tmp));
74 	if (ret < 0) {
75 		NET_DBG("Cannot write to socket (%d)", ret);
76 		goto error;
77 	}
78 
79 	ret = snprintk(tmp, sizeof(tmp), "\r\nUser-Agent: %s\r\n\r\n",
80 		       ZEPHYR_USER_AGENT);
81 	if (ret < 0 || ret >= sizeof(tmp)) {
82 		goto error;
83 	}
84 
85 	ret = http_server_sendall(client, tmp, strlen(tmp));
86 	if (ret < 0) {
87 		NET_DBG("Cannot write to socket (%d)", ret);
88 		goto error;
89 	}
90 
91 	/* Only after the complete HTTP1 payload has been processed, switch
92 	 * to Websocket.
93 	 */
94 	if (client->parser_state == HTTP1_MESSAGE_COMPLETE_STATE) {
95 		struct http_resource_detail_websocket *ws_detail;
96 		int ws_sock;
97 
98 		ws_detail = (struct http_resource_detail_websocket *)client->current_detail;
99 
100 		ret = ws_sock = websocket_register(client->fd,
101 						   ws_detail->data_buffer,
102 						   ws_detail->data_buffer_len);
103 		if (ret < 0) {
104 			NET_DBG("Cannot register websocket (%d)", ret);
105 			goto error;
106 		}
107 
108 		http_server_release_client(client);
109 
110 		ret = ws_detail->cb(ws_sock, ws_detail->user_data);
111 		if (ret < 0) {
112 			NET_DBG("WS connection failed (%d)", ret);
113 			websocket_unregister(ws_sock);
114 			goto error;
115 		}
116 	}
117 
118 	return 0;
119 
120 error:
121 	return ret;
122 }
123