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 //     http://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 /// L2CAP CoC client bridge: connects to a BLE device, then waits for an inbound
16 /// TCP connection on a specified port number. When a TCP client connects, an
17 /// L2CAP CoC channel connection to the BLE device is established, and the data
18 /// is bridged in both directions, with flow control.
19 /// When the TCP connection is closed by the client, the L2CAP CoC channel is
20 /// disconnected, but the connection to the BLE device remains, ready for a new
21 /// TCP client to connect.
22 /// When the L2CAP CoC channel is closed, the TCP connection is closed as well.
23 use crate::cli::l2cap::{
24     inject_py_event_loop, proxy_l2cap_rx_to_tcp_tx, proxy_tcp_rx_to_l2cap_tx, BridgeData,
25 };
26 use bumble::wrapper::{
27     device::{Connection, Device},
28     hci::HciConstant,
29 };
30 use futures::executor::block_on;
31 use owo_colors::OwoColorize;
32 use pyo3::{PyResult, Python};
33 use std::{net::SocketAddr, sync::Arc};
34 use tokio::{
35     join,
36     net::{TcpListener, TcpStream},
37     sync::{mpsc, Mutex},
38 };
39 
40 pub struct Args {
41     pub psm: u16,
42     pub max_credits: Option<u16>,
43     pub mtu: Option<u16>,
44     pub mps: Option<u16>,
45     pub bluetooth_address: String,
46     pub tcp_host: String,
47     pub tcp_port: u16,
48 }
49 
start(args: &Args, device: &mut Device) -> PyResult<()>50 pub async fn start(args: &Args, device: &mut Device) -> PyResult<()> {
51     println!(
52         "{}",
53         format!("### Connecting to {}...", args.bluetooth_address).yellow()
54     );
55     let mut ble_connection = device.connect(&args.bluetooth_address).await?;
56     ble_connection.on_disconnection(|_py, reason| {
57         let disconnection_info = match HciConstant::error_name(reason) {
58             Ok(info_string) => info_string,
59             Err(py_err) => format!("failed to get disconnection error name ({})", py_err),
60         };
61         println!(
62             "{} {}",
63             "@@@ Bluetooth disconnection: ".red(),
64             disconnection_info,
65         );
66         Ok(())
67     })?;
68 
69     // Start the TCP server.
70     let listener = TcpListener::bind(format!("{}:{}", args.tcp_host, args.tcp_port))
71         .await
72         .expect("failed to bind tcp to address");
73     println!(
74         "{}",
75         format!(
76             "### Listening for TCP connections on port {}",
77             args.tcp_port
78         )
79         .magenta()
80     );
81 
82     let psm = args.psm;
83     let max_credits = args.max_credits;
84     let mtu = args.mtu;
85     let mps = args.mps;
86     let ble_connection = Arc::new(Mutex::new(ble_connection));
87     // spawn thread to handle incoming tcp connections
88     tokio::spawn(inject_py_event_loop(async move {
89         while let Ok((tcp_stream, addr)) = listener.accept().await {
90             let ble_connection = ble_connection.clone();
91             // spawn thread to handle this specific tcp connection
92             if let Ok(future) = inject_py_event_loop(proxy_data_between_tcp_and_l2cap(
93                 ble_connection,
94                 tcp_stream,
95                 addr,
96                 psm,
97                 max_credits,
98                 mtu,
99                 mps,
100             )) {
101                 tokio::spawn(future);
102             }
103         }
104     })?);
105     Ok(())
106 }
107 
proxy_data_between_tcp_and_l2cap( ble_connection: Arc<Mutex<Connection>>, tcp_stream: TcpStream, addr: SocketAddr, psm: u16, max_credits: Option<u16>, mtu: Option<u16>, mps: Option<u16>, ) -> PyResult<()>108 async fn proxy_data_between_tcp_and_l2cap(
109     ble_connection: Arc<Mutex<Connection>>,
110     tcp_stream: TcpStream,
111     addr: SocketAddr,
112     psm: u16,
113     max_credits: Option<u16>,
114     mtu: Option<u16>,
115     mps: Option<u16>,
116 ) -> PyResult<()> {
117     println!("{}", format!("<<< TCP connection from {}", addr).magenta());
118     println!(
119         "{}",
120         format!(">>> Opening L2CAP channel on PSM = {}", psm).yellow()
121     );
122 
123     let mut l2cap_channel = match ble_connection
124         .lock()
125         .await
126         .open_l2cap_channel(psm, max_credits, mtu, mps)
127         .await
128     {
129         Ok(channel) => channel,
130         Err(e) => {
131             println!("{}", format!("!!! Connection failed: {e}").red());
132             // TCP stream will get dropped after returning, automatically shutting it down.
133             return Err(e);
134         }
135     };
136     let channel_info = l2cap_channel
137         .debug_string()
138         .unwrap_or_else(|e| format!("failed to get l2cap channel info ({e})"));
139 
140     println!("{}{}", "*** L2CAP channel: ".cyan(), channel_info);
141 
142     let (l2cap_to_tcp_tx, l2cap_to_tcp_rx) = mpsc::channel::<BridgeData>(10);
143 
144     // Set l2cap callback (`set_sink`) for when data is received.
145     let l2cap_to_tcp_tx_clone = l2cap_to_tcp_tx.clone();
146     l2cap_channel
147         .set_sink(move |_py, sdu| {
148             block_on(l2cap_to_tcp_tx_clone.send(BridgeData::Data(sdu.into())))
149                 .expect("failed to channel data to tcp");
150             Ok(())
151         })
152         .expect("failed to set sink for l2cap connection");
153 
154     // Set l2cap callback for when the channel is closed.
155     l2cap_channel
156         .on_close(move |_py| {
157             println!("{}", "*** L2CAP channel closed".red());
158             block_on(l2cap_to_tcp_tx.send(BridgeData::CloseSignal))
159                 .expect("failed to channel close signal to tcp");
160             Ok(())
161         })
162         .expect("failed to set on_close callback for l2cap channel");
163 
164     let l2cap_channel = Arc::new(Mutex::new(Some(l2cap_channel)));
165     let (tcp_reader, tcp_writer) = tcp_stream.into_split();
166 
167     // Do tcp stuff when something happens on the l2cap channel.
168     let handle_l2cap_data_future =
169         proxy_l2cap_rx_to_tcp_tx(l2cap_to_tcp_rx, tcp_writer, l2cap_channel.clone());
170 
171     // Do l2cap stuff when something happens on tcp.
172     let handle_tcp_data_future = proxy_tcp_rx_to_l2cap_tx(tcp_reader, l2cap_channel.clone(), true);
173 
174     let (handle_l2cap_result, handle_tcp_result) =
175         join!(handle_l2cap_data_future, handle_tcp_data_future);
176 
177     if let Err(e) = handle_l2cap_result {
178         println!("!!! Error: {e}");
179     }
180 
181     if let Err(e) = handle_tcp_result {
182         println!("!!! Error: {e}");
183     }
184 
185     Python::with_gil(|_| {
186         // Must hold GIL at least once while/after dropping for Python heap object to ensure
187         // de-allocation.
188         drop(l2cap_channel);
189     });
190 
191     Ok(())
192 }
193