1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 extern crate libc;
6 
7 use std::ffi::{CString, OsString};
8 use std::io;
9 use std::io::{Read, Write};
10 use std::os::unix::prelude::*;
11 
12 use crate::consts::{CID_BROADCAST, MAX_HID_RPT_SIZE};
13 use crate::platform::uhid;
14 use crate::u2ftypes::{U2FDevice, U2FDeviceInfo};
15 use crate::util::from_unix_result;
16 
17 #[derive(Debug)]
18 pub struct Device {
19     path: OsString,
20     fd: libc::c_int,
21     cid: [u8; 4],
22     dev_info: Option<U2FDeviceInfo>,
23 }
24 
25 impl Device {
new(path: OsString) -> io::Result<Self>26     pub fn new(path: OsString) -> io::Result<Self> {
27         let cstr = CString::new(path.as_bytes())?;
28         let fd = unsafe { libc::open(cstr.as_ptr(), libc::O_RDWR) };
29         let fd = from_unix_result(fd)?;
30         Ok(Self {
31             path,
32             fd,
33             cid: CID_BROADCAST,
34             dev_info: None,
35         })
36     }
37 
is_u2f(&self) -> bool38     pub fn is_u2f(&self) -> bool {
39         uhid::is_u2f_device(self.fd)
40     }
41 }
42 
43 impl Drop for Device {
drop(&mut self)44     fn drop(&mut self) {
45         // Close the fd, ignore any errors.
46         let _ = unsafe { libc::close(self.fd) };
47     }
48 }
49 
50 impl PartialEq for Device {
eq(&self, other: &Device) -> bool51     fn eq(&self, other: &Device) -> bool {
52         self.path == other.path
53     }
54 }
55 
56 impl Read for Device {
read(&mut self, buf: &mut [u8]) -> io::Result<usize>57     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
58         let bufp = buf.as_mut_ptr() as *mut libc::c_void;
59         let rv = unsafe { libc::read(self.fd, bufp, buf.len()) };
60         from_unix_result(rv as usize)
61     }
62 }
63 
64 impl Write for Device {
write(&mut self, buf: &[u8]) -> io::Result<usize>65     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
66         let report_id = buf[0] as i64;
67         // Skip report number when not using numbered reports.
68         let start = if report_id == 0x0 { 1 } else { 0 };
69         let data = &buf[start..];
70 
71         let data_ptr = data.as_ptr() as *const libc::c_void;
72         let rv = unsafe { libc::write(self.fd, data_ptr, data.len()) };
73         from_unix_result(rv as usize + 1)
74     }
75 
76     // USB HID writes don't buffer, so this will be a nop.
flush(&mut self) -> io::Result<()>77     fn flush(&mut self) -> io::Result<()> {
78         Ok(())
79     }
80 }
81 
82 impl U2FDevice for Device {
get_cid<'a>(&'a self) -> &'a [u8; 4]83     fn get_cid<'a>(&'a self) -> &'a [u8; 4] {
84         &self.cid
85     }
86 
set_cid(&mut self, cid: [u8; 4])87     fn set_cid(&mut self, cid: [u8; 4]) {
88         self.cid = cid;
89     }
90 
in_rpt_size(&self) -> usize91     fn in_rpt_size(&self) -> usize {
92         MAX_HID_RPT_SIZE
93     }
94 
out_rpt_size(&self) -> usize95     fn out_rpt_size(&self) -> usize {
96         MAX_HID_RPT_SIZE
97     }
98 
get_property(&self, _prop_name: &str) -> io::Result<String>99     fn get_property(&self, _prop_name: &str) -> io::Result<String> {
100         Err(io::Error::new(io::ErrorKind::Other, "Not implemented"))
101     }
102 
get_device_info(&self) -> U2FDeviceInfo103     fn get_device_info(&self) -> U2FDeviceInfo {
104         // unwrap is okay, as dev_info must have already been set, else
105         // a programmer error
106         self.dev_info.clone().unwrap()
107     }
108 
set_device_info(&mut self, dev_info: U2FDeviceInfo)109     fn set_device_info(&mut self, dev_info: U2FDeviceInfo) {
110         self.dev_info = Some(dev_info);
111     }
112 }
113