1 // Copyright 2021, The Android Open Source Project
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 //! This connects to "rootcanal" and provides a simulated
16 //! Nfc chip as well as a simulated environment.
17
18 use bytes::{BufMut, BytesMut};
19 use log::{debug, Level};
20 use logger::{self, Config};
21 use nfc_packets::nci;
22 use nfc_packets::nci::{CommandChild, NciChild};
23 use nfc_packets::nci::{
24 ConfigParams, ConfigStatus, GetConfigResponseBuilder, NciVersion, ParamIds,
25 ResetNotificationBuilder, ResetResponseBuilder, ResetTrigger, ResetType,
26 SetConfigResponseBuilder,
27 };
28 use nfc_packets::nci::{InitResponseBuilder, NfccFeatures, RfInterface};
29 use nfc_packets::nci::{NciMsgType, NciPacket, Packet, PacketBoundaryFlag};
30 use std::collections::HashMap;
31 use std::convert::TryInto;
32 use std::mem::size_of_val;
33 use std::sync::Arc;
34 use thiserror::Error;
35 use tokio::io;
36 use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader, ErrorKind};
37 use tokio::net::TcpListener;
38 use tokio::sync::RwLock;
39
40 /// Result type
41 type Result<T> = std::result::Result<T, RootcanalError>;
42
43 #[derive(Debug, Error)]
44 enum RootcanalError {
45 #[error("Termination request")]
46 TerminateTask,
47 #[error("Socket error")]
48 IoError(#[from] io::Error),
49 #[error("Unsupported command packet")]
50 UnsupportedCommand,
51 #[error("Packet did not parse correctly")]
52 InvalidPacket,
53 #[error("Packet type not supported")]
54 UnsupportedPacket,
55 }
56
57 /// Provides storage for internal configuration parameters
58 #[derive(Clone)]
59 pub struct InternalConfiguration {
60 map: Arc<RwLock<HashMap<ParamIds, Vec<u8>>>>,
61 }
62
63 impl InternalConfiguration {
64 /// InternalConfiguration constructor
new() -> Self65 pub async fn new() -> Self {
66 let ic = InternalConfiguration { map: Arc::new(RwLock::new(HashMap::new())) };
67 let mut map = ic.map.write().await;
68 map.insert(ParamIds::LfT3tMax, vec![0x10u8]);
69 drop(map);
70 ic
71 }
72
73 /// Set a configuration parameter
set(&mut self, parameter: ParamIds, value: Vec<u8>)74 pub async fn set(&mut self, parameter: ParamIds, value: Vec<u8>) {
75 self.map.write().await.insert(parameter, value);
76 }
77
78 /// Gets a parameter value or None
get(&mut self, parameter: ParamIds) -> Option<Vec<u8>>79 pub async fn get(&mut self, parameter: ParamIds) -> Option<Vec<u8>> {
80 self.map.read().await.get(¶meter).map(|v| (*v).clone())
81 }
82
83 /// Clears the allocated storage
clear(&mut self)84 pub async fn clear(&mut self) {
85 self.map.write().await.clear();
86 }
87 }
88
89 const TERMINATION: u8 = 4u8;
90
91 #[tokio::main]
main() -> io::Result<()>92 async fn main() -> io::Result<()> {
93 logger::init(Config::default().with_tag_on_device("nfc-rc").with_min_level(Level::Trace));
94
95 let listener = TcpListener::bind("127.0.0.1:54323").await?;
96
97 for _ in 0..2 {
98 let (mut sock, _) = listener.accept().await?;
99
100 tokio::spawn(async move {
101 let (rd, mut wr) = sock.split();
102 let mut rd = BufReader::new(rd);
103 let config = InternalConfiguration::new().await;
104 loop {
105 if let Err(e) = process(config.clone(), &mut rd, &mut wr).await {
106 match e {
107 RootcanalError::TerminateTask => break,
108 RootcanalError::IoError(e) => {
109 if e.kind() == ErrorKind::UnexpectedEof {
110 break;
111 }
112 }
113 _ => panic!("Communication error: {:?}", e),
114 }
115 }
116 }
117 })
118 .await?;
119 }
120 Ok(())
121 }
122
process<R, W>(config: InternalConfiguration, reader: &mut R, writer: &mut W) -> Result<()> where R: AsyncReadExt + Unpin, W: AsyncWriteExt + Unpin,123 async fn process<R, W>(config: InternalConfiguration, reader: &mut R, writer: &mut W) -> Result<()>
124 where
125 R: AsyncReadExt + Unpin,
126 W: AsyncWriteExt + Unpin,
127 {
128 let mut buffer = BytesMut::with_capacity(1024);
129 let len: usize = reader.read_u16().await?.into();
130 buffer.resize(len, 0);
131 reader.read_exact(&mut buffer).await?;
132 let frozen = buffer.freeze();
133 debug!("{:?}", &frozen);
134 let pkt_type = (frozen[0] >> 5) & 0x7;
135 debug!("packet {} received len={}", &pkt_type, &len);
136 if pkt_type == NciMsgType::Command as u8 {
137 match NciPacket::parse(&frozen) {
138 Ok(p) => command_response(config, writer, p).await,
139 Err(_) => Err(RootcanalError::InvalidPacket),
140 }
141 } else if pkt_type == TERMINATION {
142 Err(RootcanalError::TerminateTask)
143 } else {
144 Err(RootcanalError::UnsupportedPacket)
145 }
146 }
147
148 const MAX_PAYLOAD: u8 = 255;
149
command_response<W>( mut config: InternalConfiguration, out: &mut W, cmd: NciPacket, ) -> Result<()> where W: AsyncWriteExt + Unpin,150 async fn command_response<W>(
151 mut config: InternalConfiguration,
152 out: &mut W,
153 cmd: NciPacket,
154 ) -> Result<()>
155 where
156 W: AsyncWriteExt + Unpin,
157 {
158 let pbf = PacketBoundaryFlag::CompleteOrFinal;
159 let gid = 0u8;
160 let mut status = nci::Status::Ok;
161 match cmd.specialize() {
162 NciChild::Command(cmd) => match cmd.specialize() {
163 CommandChild::ResetCommand(rst) => {
164 write_nci(out, (ResetResponseBuilder { gid, pbf, status }).build()).await?;
165 write_nci(
166 out,
167 (ResetNotificationBuilder {
168 gid,
169 pbf,
170 trigger: ResetTrigger::ResetCommand,
171 config_status: if rst.get_reset_type() == ResetType::KeepConfig {
172 ConfigStatus::ConfigKept
173 } else {
174 ConfigStatus::ConfigReset
175 },
176 nci_version: NciVersion::Version20,
177 manufacturer_id: 0,
178 mfsi: Vec::new(),
179 })
180 .build(),
181 )
182 .await
183 }
184 CommandChild::InitCommand(_) => {
185 let nfcc_feat = [0u8; 5];
186 let rf_int = [0u8; 2];
187 write_nci(
188 out,
189 (InitResponseBuilder {
190 gid,
191 pbf,
192 status,
193 nfcc_features: NfccFeatures::parse(&nfcc_feat).unwrap(),
194 max_log_conns: 0,
195 max_rout_tbls_size: 0x0000,
196 max_ctrl_payload: MAX_PAYLOAD,
197 max_data_payload: MAX_PAYLOAD,
198 num_of_credits: 0,
199 max_nfcv_rf_frame_sz: 64,
200 rf_interface: vec![RfInterface::parse(&rf_int).unwrap(); 1],
201 })
202 .build(),
203 )
204 .await
205 }
206 CommandChild::SetConfigCommand(sc) => {
207 for cp in sc.get_params() {
208 if cp.valm.len() > 251 {
209 status = nci::Status::InvalidParam;
210 break;
211 }
212 config.set(cp.paramid, cp.valm.clone()).await;
213 }
214 write_nci(
215 out,
216 (SetConfigResponseBuilder { gid, pbf, status, paramids: Vec::new() }).build(),
217 )
218 .await
219 }
220 CommandChild::GetConfigCommand(gc) => {
221 let mut cpv: Vec<ConfigParams> = Vec::new();
222 for paramid in gc.get_paramids() {
223 let mut cp = ConfigParams { paramid: paramid.pids, valm: Vec::new() };
224 if status == nci::Status::Ok {
225 if let Some(val) = config.get(paramid.pids).await {
226 cp.valm = val;
227 } else {
228 status = nci::Status::InvalidParam;
229 cpv.clear();
230 }
231 } else if config.get(paramid.pids).await.is_some() {
232 continue;
233 }
234 cpv.push(cp);
235 // The Status field takes a byte
236 if size_of_val(&*cpv) > (MAX_PAYLOAD - 1).into() {
237 cpv.pop();
238 if status == nci::Status::Ok {
239 status = nci::Status::MessageSizeExceeded;
240 }
241 break;
242 }
243 }
244 write_nci(out, (GetConfigResponseBuilder { gid, pbf, status, params: cpv }).build())
245 .await
246 }
247 _ => Err(RootcanalError::UnsupportedCommand),
248 },
249 _ => Err(RootcanalError::InvalidPacket),
250 }
251 }
252
write_nci<W, T>(writer: &mut W, rsp: T) -> Result<()> where W: AsyncWriteExt + Unpin, T: Into<NciPacket>,253 async fn write_nci<W, T>(writer: &mut W, rsp: T) -> Result<()>
254 where
255 W: AsyncWriteExt + Unpin,
256 T: Into<NciPacket>,
257 {
258 let pkt = rsp.into();
259 let b = pkt.to_bytes();
260 let mut data = BytesMut::with_capacity(b.len() + 2);
261 data.put_u16(b.len().try_into().unwrap());
262 data.extend(b);
263 let frozen = data.freeze();
264 writer.write_all(frozen.as_ref()).await?;
265 debug!("command written");
266 Ok(())
267 }
268