1pub mod dir_cmds;
4pub mod file_cmds;
5pub mod session;
6pub mod trans2_cmds;
7
8use smb_server_proto::types::Status;
9use smb_server_proto_smb1::consts;
10use smb_server_proto_smb1::header::RespBody;
11
12pub use crate::dispatch::IoCtx;
13use crate::dispatch::{IoContext, ReqView};
14use crate::state::{next_uid, Session};
15
16pub async fn dispatch_one<'a>(
18 io: &mut IoContext<'a>,
19 req: &ReqView<'a>,
20 bodies: &mut Vec<RespBody>,
21) -> Result<Status, Status> {
22
23 let needs_session = !matches!(
25 req.hdr.command,
26 consts::COM_NEGOTIATE
27 | consts::COM_SESSION_SETUP_ANDX
28 | consts::COM_ECHO
29 | consts::COM_PROCESS_EXIT
30 | consts::COM_NT_CANCEL
31 );
32
33 if io.conn.session.is_none() && !io.conn.auth_pending && io.server.allow_guest && needs_session {
36 let uid = if io.conn.uid != 0 { io.conn.uid } else { next_uid() };
37 io.conn.uid = uid;
38 io.conn.session = Some(Session { user: "nobody".into(), guest: true, trees: Vec::new() });
39 }
40 if needs_session && io.conn.session.is_none() && !io.conn.auth_pending {
41 return Err(Status::ACCESS_DENIED);
42 }
43
44 let needs_tree = needs_session
45 && !matches!(req.hdr.command, consts::COM_TREE_CONNECT_ANDX | consts::COM_LOGOFF_ANDX);
46 if needs_tree && !io.conn.trees.contains_key(&req.hdr.tid) {
47 return Err(Status::INVALID_HANDLE);
48 }
49
50 match req.hdr.command {
51 consts::COM_NEGOTIATE => session::negotiate(io, req, bodies),
52 consts::COM_SESSION_SETUP_ANDX => session::setup(io, req, bodies),
53 consts::COM_TREE_CONNECT_ANDX => session::tree_connect(io, req, bodies),
54 consts::COM_TREE_DISCONNECT => {
55 let tid = req.hdr.tid;
56 io.conn.trees.remove(&tid);
57 if let Some(s) = io.conn.session.as_mut() {
58 s.trees.retain(|t| *t != tid);
59 }
60 *bodies = vec![RespBody::new(consts::COM_TREE_DISCONNECT, Vec::new(), Vec::new())];
61 Ok(Status::SUCCESS)
62 }
63 consts::COM_LOGOFF_ANDX => {
64 for fid in io.conn.handles.keys().copied().collect::<Vec<_>>() {
65 if let Some(h) = io.conn.handles.remove(&fid) {
66 let vfs = share_vfs(io, req.hdr.tid);
67 let _ = vfs.close(h).await;
68 }
69 }
70 io.conn.session = None;
71 io.conn.auth_pending = false;
72 *bodies =
73 vec![RespBody::new(consts::COM_LOGOFF_ANDX, vec![consts::ANDX_NONE, 0, 0, 0], Vec::new())];
74 Ok(Status::SUCCESS)
75 }
76 consts::COM_ECHO => session::echo(req, bodies),
77 consts::COM_QUERY_INFORMATION_DISK => file_cmds::query_disk(io, req, bodies).await,
78 consts::COM_NT_CREATE_ANDX => file_cmds::nt_create(io, req, bodies).await,
79 consts::COM_READ_ANDX => file_cmds::read_andx(io, req, bodies).await,
80 consts::COM_WRITE_ANDX => file_cmds::write_andx(io, req, bodies).await,
81 consts::COM_CLOSE => file_cmds::close(io, req, bodies).await,
82 consts::COM_FLUSH => file_cmds::flush(io, req, bodies).await,
83 consts::COM_SEEK => file_cmds::seek(io, req, bodies).await,
84 consts::COM_LOCKING_ANDX
85 | consts::COM_LOCK_BYTE_RANGE
86 | consts::COM_UNLOCK_BYTE_RANGE => file_cmds::locking(io, req, bodies).await,
87 consts::COM_CREATE_DIRECTORY => dir_cmds::mkdir(io, req, bodies).await,
88 consts::COM_DELETE_DIRECTORY => dir_cmds::rmdir(io, req, bodies).await,
89 consts::COM_CHECK_DIRECTORY => dir_cmds::check_dir(io, req, bodies).await,
90 consts::COM_DELETE => dir_cmds::delete(io, req, bodies).await,
91 consts::COM_RENAME => dir_cmds::rename(io, req, bodies).await,
92 consts::COM_NT_RENAME => dir_cmds::rename(io, req, bodies).await,
93 consts::COM_QUERY_INFORMATION => dir_cmds::query_info_legacy(io, req, bodies).await,
94 consts::COM_SET_INFORMATION => dir_cmds::set_info_legacy(io, req, bodies).await,
95 consts::COM_PROCESS_EXIT => {
96 for (_, h) in std::mem::take(&mut io.conn.handles) {
97 let vfs = share_vfs_any(io);
98 let _ = vfs.close(h).await;
99 }
100 *bodies = vec![RespBody::new(consts::COM_PROCESS_EXIT, Vec::new(), Vec::new())];
101 Ok(Status::SUCCESS)
102 }
103 consts::COM_TRANSACTION2 => trans2_cmds::dispatch_trans2(io, req, bodies).await,
104 _ => Err(Status::INVALID_DEVICE_REQUEST),
105 }
106}
107
108pub fn dir_cmds_split(pattern: &str) -> (String, String) {
110 crate::cmds::dir_cmds::split_pattern_pub(pattern)
111}
112
113pub use crate::cmds::dir_cmds::split_pattern_pub as split_pattern;
114
115pub fn wildcard(name: &str, pat: &str) -> bool {
117 smb_server_backend_posix::wildcard_match(name, pat)
118}
119
120pub fn share_vfs(io: &IoContext<'_>, tid: u16) -> std::sync::Arc<dyn smb_server_vfs::Vfs> {
124 static IPC: std::sync::OnceLock<smb_server_vfs_stub::IpcVfs> = std::sync::OnceLock::new();
125 match io.conn.trees.get(&tid).and_then(|n| io.server.shares.get(n)) {
126 Some(share) => share.vfs.clone(),
127 None => std::sync::Arc::new(IPC.get_or_init(smb_server_vfs_stub::IpcVfs::default).clone())
128 as std::sync::Arc<dyn smb_server_vfs::Vfs>,
129 }
130 }
131
132fn share_vfs_any(_io: &IoContext) -> &'static dyn smb_server_vfs::Vfs {
133 static IPC: std::sync::OnceLock<smb_server_vfs_stub::IpcVfs> = std::sync::OnceLock::new();
134 IPC.get_or_init(smb_server_vfs_stub::IpcVfs::default) as &dyn smb_server_vfs::Vfs
135}
136
137mod smb_server_vfs_stub {
139 #[derive(Debug, Default, Clone)]
141 pub struct IpcVfs;
142
143 #[async_trait::async_trait(?Send)]
144 impl smb_server_vfs::Vfs for IpcVfs {
145 async fn create(
146 &self,
147 _: &str,
148 _: bool,
149 _: u32,
150 _: u32,
151 _: u32,
152 _: u32,
153 ) -> smb_server_vfs::VfsResult<(Box<smb_server_vfs::OpenFile>, smb_server_vfs::FileMeta, u32)> {
154 Err(smb_server_vfs::VfsError::AccessDenied)
155 }
156 async fn read(
157 &self,
158 _: &mut smb_server_vfs::OpenFile,
159 _: u64,
160 _: usize,
161 ) -> smb_server_vfs::VfsResult<Vec<u8>> {
162 Err(smb_server_vfs::VfsError::AccessDenied)
163 }
164 async fn write(
165 &self,
166 _: &mut smb_server_vfs::OpenFile,
167 _: u64,
168 _: &[u8],
169 _: bool,
170 ) -> smb_server_vfs::VfsResult<u64> {
171 Err(smb_server_vfs::VfsError::AccessDenied)
172 }
173 async fn seek(
174 &self,
175 _: &mut smb_server_vfs::OpenFile,
176 _: u16,
177 _: i64,
178 ) -> smb_server_vfs::VfsResult<u64> {
179 Err(smb_server_vfs::VfsError::NotSupported)
180 }
181 async fn flush(&self, _: &mut smb_server_vfs::OpenFile) -> smb_server_vfs::VfsResult<()> {
182 Ok(())
183 }
184 async fn flush_all(&self) -> smb_server_vfs::VfsResult<()> {
185 Ok(())
186 }
187 async fn close(&self, _: Box<smb_server_vfs::OpenFile>) -> smb_server_vfs::VfsResult<()> {
188 Ok(())
189 }
190 async fn mkdir(&self, _: &str) -> smb_server_vfs::VfsResult<()> {
191 Err(smb_server_vfs::VfsError::AccessDenied)
192 }
193 async fn rmdir(&self, _: &str) -> smb_server_vfs::VfsResult<()> {
194 Err(smb_server_vfs::VfsError::AccessDenied)
195 }
196 async fn check_dir(&self, _: &str) -> smb_server_vfs::VfsResult<()> {
197 Err(smb_server_vfs::VfsError::NotFound)
198 }
199 async fn unlink(&self, _: &str) -> smb_server_vfs::VfsResult<()> {
200 Err(smb_server_vfs::VfsError::AccessDenied)
201 }
202 async fn delete_pattern(&self, _: &str, _: &str) -> smb_server_vfs::VfsResult<bool> {
203 Err(smb_server_vfs::VfsError::AccessDenied)
204 }
205 async fn rename(&self, _: &str, _: &str) -> smb_server_vfs::VfsResult<()> {
206 Err(smb_server_vfs::VfsError::AccessDenied)
207 }
208 async fn list(&self, _: &str) -> smb_server_vfs::VfsResult<Vec<smb_server_vfs::Entry>> {
209 Err(smb_server_vfs::VfsError::AccessDenied)
210 }
211 async fn stat(&self, _: &str) -> smb_server_vfs::VfsResult<smb_server_vfs::FileMeta> {
212 Err(smb_server_vfs::VfsError::NotFound)
213 }
214 async fn set_info_open(
215 &self,
216 _: &mut smb_server_vfs::OpenFile,
217 _: &smb_server_vfs::SetOp,
218 ) -> smb_server_vfs::VfsResult<()> {
219 Err(smb_server_vfs::VfsError::AccessDenied)
220 }
221 async fn set_info_path(
222 &self,
223 _: &str,
224 _: &smb_server_vfs::SetOp,
225 ) -> smb_server_vfs::VfsResult<()> {
226 Err(smb_server_vfs::VfsError::AccessDenied)
227 }
228 async fn query_disk(&self) -> smb_server_vfs::VfsResult<(u32, u32, u16, u16)> {
229 Err(smb_server_vfs::VfsError::NotSupported)
230 }
231 }
232}