1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 use std::sync::{Arc, Mutex};
16 use std::{collections::HashMap, io::Cursor, net::TcpStream};
17
18 use bytes::Bytes;
19 use http::Request;
20 use log::{error, info, warn};
21 use netsim_proto::common::ChipKind;
22 use tungstenite::{protocol::Role, Message, WebSocket};
23
24 use crate::devices::chip;
25 use crate::devices::devices_handler::{add_chip, remove_chip};
26 use crate::http_server::server_response::ResponseWritable;
27 use crate::wireless;
28 use crate::wireless::packet::{register_transport, unregister_transport, Response};
29
30 use super::h4;
31
32 // This feature is enabled only for CMake builds
33 #[cfg(feature = "local_ssl")]
34 use crate::openssl;
35
36 /// Generate Sec-Websocket-Accept value from given Sec-Websocket-Key value
generate_websocket_accept(websocket_key: String) -> String37 fn generate_websocket_accept(websocket_key: String) -> String {
38 let concat = websocket_key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
39 let hashed = openssl::sha::sha1(concat.as_bytes());
40 data_encoding::BASE64.encode(&hashed)
41 }
42
43 /// Handler for websocket server connection
handle_websocket(request: &Request<Vec<u8>>, param: &str, writer: ResponseWritable)44 pub fn handle_websocket(request: &Request<Vec<u8>>, param: &str, writer: ResponseWritable) {
45 if param != "bt" {
46 writer.put_error(404, "netsim websocket currently supports bt, uri=/v1/websocket/bt");
47 }
48 let websocket_accept = match request.headers().get("Sec-Websocket-Key") {
49 Some(key) => match key.to_str() {
50 Ok(key_str) => generate_websocket_accept(key_str.to_string()),
51 Err(_) => {
52 writer.put_error(
53 404,
54 "The HeaderValue of Sec-Websocket-Key cannot be converted to str",
55 );
56 return;
57 }
58 },
59 None => {
60 writer.put_error(404, "Missing Sec-Websocket-Key in header");
61 return;
62 }
63 };
64 writer.put_ok_switch_protocol(
65 "websocket",
66 vec![("Sec-WebSocket-Accept".to_string(), websocket_accept)],
67 )
68 }
69
70 struct WebSocketTransport {
71 websocket_writer: Arc<Mutex<WebSocket<TcpStream>>>,
72 }
73
74 impl Response for WebSocketTransport {
response(&mut self, packet: Bytes, packet_type: u8)75 fn response(&mut self, packet: Bytes, packet_type: u8) {
76 let mut buffer = Vec::new();
77 buffer.push(packet_type);
78 buffer.extend(packet);
79 if let Err(err) = self
80 .websocket_writer
81 .lock()
82 .expect("Failed to acquire lock on WebSocket")
83 .send(Message::Binary(buffer))
84 {
85 error!("{err}");
86 };
87 }
88 }
89
90 /// Run websocket transport for packet flow in netsim
run_websocket_transport(stream: TcpStream, queries: HashMap<&str, &str>)91 pub fn run_websocket_transport(stream: TcpStream, queries: HashMap<&str, &str>) {
92 let chip_create_params = chip::CreateParams {
93 kind: ChipKind::BLUETOOTH,
94 address: queries.get("address").unwrap_or(&"").to_string(),
95 name: Some(format!("websocket-{}", stream.peer_addr().unwrap())),
96 manufacturer: "Google".to_string(),
97 product_name: "Google".to_string(),
98 bt_properties: None,
99 };
100 #[cfg(not(test))]
101 let wireless_create_params =
102 wireless::CreateParam::Bluetooth(wireless::bluetooth::CreateParams {
103 address: chip_create_params.address.clone(),
104 bt_properties: None,
105 });
106 #[cfg(test)]
107 let wireless_create_params = wireless::CreateParam::Mock(wireless::mocked::CreateParams {
108 chip_kind: ChipKind::BLUETOOTH,
109 });
110 // Add Chip
111 let result = match add_chip(
112 &stream.peer_addr().unwrap().port().to_string(),
113 queries
114 .get("name")
115 .unwrap_or(&format!("websocket-device-{}", stream.peer_addr().unwrap()).as_str()),
116 &chip_create_params,
117 &wireless_create_params,
118 ) {
119 Ok(chip_result) => chip_result,
120 Err(err) => {
121 warn!("{err}");
122 return;
123 }
124 };
125
126 // Create websocket_writer to handle packet responses, write pong or close messages
127 let websocket_writer = Arc::new(Mutex::new(WebSocket::from_raw_socket(
128 stream.try_clone().unwrap(),
129 Role::Server,
130 None,
131 )));
132 // Websocket reader
133 let mut websocket_reader = WebSocket::from_raw_socket(stream, Role::Server, None);
134
135 // Sending cloned websocket into packet dispatcher
136 register_transport(
137 result.chip_id,
138 Box::new(WebSocketTransport { websocket_writer: websocket_writer.clone() }),
139 );
140
141 // Running Websocket server
142 loop {
143 let packet_msg =
144 match websocket_reader.read().map_err(|_| "Failed to read Websocket message") {
145 Ok(message) => message,
146 Err(err) => {
147 error!("{err}");
148 break;
149 }
150 };
151 if packet_msg.is_binary() {
152 let mut cursor = Cursor::new(packet_msg.into_data());
153 match h4::read_h4_packet(&mut cursor) {
154 Ok(packet) => {
155 wireless::handle_request(result.chip_id, &packet.payload, packet.h4_type);
156 }
157 Err(error) => {
158 error!(
159 "netsimd: end websocket reader {}: {:?}",
160 websocket_reader.get_ref().peer_addr().unwrap(),
161 error
162 );
163 break;
164 }
165 }
166 } else if packet_msg.is_ping() {
167 if let Err(err) = websocket_writer
168 .lock()
169 .expect("Failed to acquire lock on WebSocket")
170 .send(Message::Pong(packet_msg.into_data()))
171 {
172 error!("{err}");
173 }
174 } else if packet_msg.is_close() {
175 if let Message::Close(close_frame) = packet_msg {
176 if let Err(err) = websocket_writer
177 .lock()
178 .expect("Failed to acquire lock on WebSocket")
179 .close(close_frame)
180 .map_err(|_| "Failed to close Websocket")
181 {
182 error!("{err}");
183 }
184 }
185 break;
186 }
187 }
188
189 // unregister before remove_chip because facade may re-use facade_id
190 // on an intertwining create_chip and the unregister here might remove
191 // the recently added chip creating a disconnected transport.
192 unregister_transport(result.chip_id);
193
194 if let Err(err) = remove_chip(result.device_id, result.chip_id) {
195 warn!("{err}");
196 };
197 info!("Removed chip: device_id: {}, chip_id: {}", result.device_id, result.chip_id);
198 }
199