Skip to main content

wayk_proto/message/
status.rs

1use crate::{
2    error::*,
3    serialization::{Decode, Encode},
4};
5use core::{convert::TryFrom, fmt};
6use num_derive::{FromPrimitive, ToPrimitive};
7use std::io::{Cursor, Write};
8
9// NSTATUS
10
11#[derive(Debug, Clone)]
12pub struct NowStatusBuilder<CodeType> {
13    severity: SeverityLevel,
14    status_type: StatusType,
15    code: CodeType,
16}
17
18impl<CodeType> NowStatusBuilder<CodeType>
19where
20    CodeType: num::ToPrimitive,
21{
22    pub fn severity<V: Into<SeverityLevel>>(self, value: V) -> Self {
23        Self {
24            severity: value.into(),
25            ..self
26        }
27    }
28
29    pub fn status_type<V: Into<StatusType>>(self, value: V) -> Self {
30        Self {
31            status_type: value.into(),
32            ..self
33        }
34    }
35
36    pub fn build(self) -> NowStatus<CodeType> {
37        let repr = ((self.severity as u32) << 30)
38            + ((self.status_type as u32) << 16)
39            + num::ToPrimitive::to_u32(&self.code).unwrap(); // should not panic.
40
41        NowStatus {
42            repr,
43            severity: self.severity,
44            status_type: self.status_type,
45            code: self.code,
46        }
47    }
48}
49
50#[derive(Debug, PartialEq, Clone)]
51pub struct NowStatus<CodeType> {
52    repr: u32,
53
54    // cache
55    severity: SeverityLevel,
56    status_type: StatusType,
57    code: CodeType,
58}
59
60impl<CodeType> Encode for NowStatus<CodeType> {
61    fn encoded_len(&self) -> usize {
62        std::mem::size_of::<u32>()
63    }
64
65    fn encode_into<W: Write>(&self, writer: &mut W) -> Result<()> {
66        self.repr.encode_into(writer)
67    }
68}
69
70impl<CodeType> Decode<'_> for NowStatus<CodeType>
71where
72    CodeType: num::FromPrimitive,
73{
74    fn decode_from(cursor: &mut Cursor<&[u8]>) -> Result<Self> {
75        let repr = u32::decode_from(cursor)?;
76        Self::from_u32(repr)
77    }
78}
79
80impl<CodeType> TryFrom<u32> for NowStatus<CodeType>
81where
82    CodeType: num::FromPrimitive,
83{
84    type Error = ProtoError;
85
86    fn try_from(repr: u32) -> Result<Self> {
87        Ok(NowStatus {
88            repr,
89            severity: num::FromPrimitive::from_u32(repr >> 30)
90                .chain(ProtoErrorKind::Decoding(stringify!(NowStatus)))
91                .or_desc("couldn't parse severity")?,
92            status_type: num::FromPrimitive::from_u32((repr & 0x00FF_0000) >> 16)
93                .chain(ProtoErrorKind::Decoding(stringify!(NowStatus)))
94                .or_desc("couldn't parse status type")?,
95            code: num::FromPrimitive::from_u32(repr & 0x0000_FFFF)
96                .chain(ProtoErrorKind::Decoding(stringify!(NowStatus)))
97                .or_desc("couldn't parse status code")?,
98        })
99    }
100}
101
102impl<CodeType> Into<u32> for NowStatus<CodeType> {
103    fn into(self) -> u32 {
104        self.repr
105    }
106}
107
108impl<CodeType> PartialEq<u32> for NowStatus<CodeType> {
109    fn eq(&self, other: &u32) -> bool {
110        self.repr == *other
111    }
112}
113
114impl<CodeType> Default for NowStatus<CodeType>
115where
116    CodeType: num::FromPrimitive + num::ToPrimitive,
117{
118    fn default() -> Self {
119        // should not panic... Code 0 should be "Success" for any CodeType.
120        NowStatus::builder(<CodeType as num::FromPrimitive>::from_u8(0).unwrap()).build()
121    }
122}
123
124impl<CodeType> NowStatus<CodeType>
125where
126    CodeType: num::FromPrimitive,
127{
128    pub fn from_u32(repr: u32) -> Result<Self> {
129        Self::try_from(repr)
130    }
131}
132
133impl<CodeType> NowStatus<CodeType> {
134    pub fn builder<V: Into<CodeType>>(code: V) -> NowStatusBuilder<CodeType> {
135        NowStatusBuilder {
136            severity: SeverityLevel::Info,
137            status_type: StatusType::None,
138            code: code.into(),
139        }
140    }
141}
142
143impl<CodeType> NowStatus<CodeType>
144where
145    CodeType: Copy,
146{
147    pub fn severity(&self) -> SeverityLevel {
148        self.severity
149    }
150
151    pub fn status_type(&self) -> StatusType {
152        self.status_type
153    }
154
155    pub fn code(&self) -> CodeType {
156        self.code
157    }
158
159    pub fn as_u32(&self) -> u32 {
160        self.repr
161    }
162}
163
164impl<CodeType> fmt::Display for NowStatus<CodeType>
165where
166    CodeType: fmt::Display,
167{
168    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169        write!(f, "{} {}: {}", self.status_type, self.severity, self.code)
170    }
171}
172
173#[derive(Encode, Decode, FromPrimitive, Debug, PartialEq, Clone, Copy)]
174#[repr(u8)]
175pub enum SeverityLevel {
176    Info = 0,
177    Warn = 1,
178    Error = 2,
179    Fatal = 3,
180}
181
182impl fmt::Display for SeverityLevel {
183    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184        write!(f, "{:?}", self)
185    }
186}
187
188#[derive(Encode, Decode, FromPrimitive, Debug, PartialEq, Clone, Copy)]
189#[repr(u8)]
190pub enum StatusType {
191    None = 0,
192    Disconnect = 1,
193    Connect = 2,
194    Security = 3,
195    Handshake = 21,
196    Negotiate = 22,
197    Auth = 23,
198    Associate = 24,
199    Capabilities = 25,
200    Channel = 26,
201    Clipboard = 0x81,
202    FileTransfer = 0x82,
203    Exec = 0x83,
204}
205
206impl fmt::Display for StatusType {
207    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208        write!(f, "{:?}", self)
209    }
210}
211
212// Common
213
214#[derive(Encode, Decode, FromPrimitive, ToPrimitive, Debug, PartialEq, Clone, Copy)]
215#[repr(u16)]
216enum StatusCode {
217    Success = 0x0000,
218    Failure = 0xFFFF,
219}
220
221// NSTATUS_DISCONNECT_TYPE
222
223#[derive(Encode, Decode, FromPrimitive, ToPrimitive, Debug, PartialEq, Clone, Copy)]
224#[repr(u16)]
225pub enum DisconnectStatusCode {
226    Success = StatusCode::Success as u16,
227    Failure = StatusCode::Failure as u16,
228    ByLocalUser = 1,
229    ByRemoteUser = 2,
230    ByLocalSystem = 3,
231    ByRemoteSystem = 4,
232    SystemShutdown = 5,
233    SystemReboot = 6,
234    LocalLogoff = 7,
235    RemoteLogoff = 8,
236    ByOtherConnection = 9,
237    LogonTimeout = 10,
238    LogonCancelled = 11,
239    IdleTimeout = 12,
240    AlreadyActive = 13,
241    LicenseRequired = 14,
242}
243
244impl fmt::Display for DisconnectStatusCode {
245    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
246        match self {
247            Self::Success => write!(f, "disconnected with success"),
248            Self::Failure => write!(f, "disconnection with unknown failure"),
249            Self::ByLocalUser => write!(f, "disconnected by local user."),
250            Self::ByRemoteUser => write!(f, "disconnected by remote user."),
251            Self::ByLocalSystem => write!(f, "disconnected by local system."),
252            Self::ByRemoteSystem => write!(f, "disconnected by remote system."),
253            Self::SystemShutdown => write!(f, "disconnected because of system shutdown."),
254            Self::SystemReboot => write!(f, "disconnected because of system reboot."),
255            Self::LocalLogoff => write!(f, "disconnected because of local logoff."),
256            Self::RemoteLogoff => write!(f, "disconnected because of remote logoff."),
257            Self::ByOtherConnection => write!(f, "disconnected by another connexion."),
258            Self::LogonTimeout => write!(f, "disconnected because of logon timeout."),
259            Self::LogonCancelled => write!(f, "disconnected because the logon was canceled."),
260            Self::IdleTimeout => write!(f, "disconnected because of idle timeout."),
261            Self::AlreadyActive => write!(f, "disconnected because another connection is already active."),
262            Self::LicenseRequired => write!(f, "disconnected because a license is required."),
263        }
264    }
265}
266
267// NSTATUS_CONNECT_TYPE
268
269#[derive(Encode, Decode, FromPrimitive, ToPrimitive, Debug, PartialEq, Clone, Copy)]
270#[repr(u16)]
271pub enum ConnectStatusCode {
272    Success = StatusCode::Success as u16,
273    Failure = StatusCode::Failure as u16,
274    Unresolved = 1,
275    Unreachable = 2,
276    Refused = 3,
277    Loopback = 4,
278    Concurrent = 5,
279    Unauthorized = 6,
280}
281
282impl fmt::Display for ConnectStatusCode {
283    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
284        match self {
285            Self::Success => write!(f, "connection succeeded"),
286            Self::Failure => write!(f, "connection failed"),
287            Self::Unresolved => write!(f, "connection unresolved"),
288            Self::Unreachable => write!(f, "sharer unreachable"),
289            Self::Refused => write!(f, "sharer refused connection"),
290            Self::Loopback => write!(f, "connection loopback"),
291            Self::Concurrent => write!(f, "concurrent connection"),
292            Self::Unauthorized => write!(f, "unauthorized connection"),
293        }
294    }
295}
296
297// NSTATUS_SECURITY_TYPE
298
299#[derive(Encode, Decode, FromPrimitive, ToPrimitive, Debug, PartialEq, Clone, Copy)]
300#[repr(u16)]
301pub enum SecurityStatusCode {
302    Success = StatusCode::Success as u16,
303    Failure = StatusCode::Failure as u16,
304    TLSHandshake = 1,
305    TLSClientCert = 2,
306    TLSServerCert = 3,
307}
308
309impl fmt::Display for SecurityStatusCode {
310    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
311        match self {
312            Self::Success => write!(f, "success"),
313            Self::Failure => write!(f, "unknown failure"),
314            Self::TLSHandshake => write!(f, "TLS failed"),
315            Self::TLSClientCert => write!(f, "bad client TLS certificate"),
316            Self::TLSServerCert => write!(f, "bad server TLS certificate"),
317        }
318    }
319}
320
321// NSTATUS_HANDSHAKE_TYPE
322
323#[derive(Encode, Decode, FromPrimitive, ToPrimitive, Debug, PartialEq, Clone, Copy)]
324#[repr(u16)]
325pub enum HandshakeStatusCode {
326    Success = StatusCode::Success as u16,
327    Failure = StatusCode::Failure as u16,
328    Incompatible = 1,
329}
330
331impl fmt::Display for HandshakeStatusCode {
332    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
333        match self {
334            Self::Success => write!(f, "handshake succeeded"),
335            Self::Failure => write!(f, "handshaked failed"),
336            Self::Incompatible => write!(f, "version is incompatible"),
337        }
338    }
339}
340
341// NSTATUS_NEGOTIATE_TYPE
342
343#[derive(Encode, Decode, FromPrimitive, ToPrimitive, Debug, PartialEq, Clone, Copy)]
344#[repr(u16)]
345pub enum NegotiateStatusCode {
346    Success = StatusCode::Success as u16,
347    Failure = StatusCode::Failure as u16,
348}
349
350impl fmt::Display for NegotiateStatusCode {
351    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
352        match self {
353            Self::Success => write!(f, "negotiation succeeded"),
354            Self::Failure => write!(f, "negotiation failed"),
355        }
356    }
357}
358
359// NSTATUS_AUTH_TYPE
360
361#[derive(Encode, Decode, FromPrimitive, ToPrimitive, Debug, PartialEq, Clone, Copy)]
362#[repr(u16)]
363pub enum AuthStatusCode {
364    Success = StatusCode::Success as u16,
365    Failure = StatusCode::Failure as u16,
366    Timeout = 1,
367    Cancelled = 2,
368    AccountDisabled = 3,
369    AccountExpired = 4,
370    AccountRestriction = 5,
371    InvalidLogonHours = 6,
372    InvalidWorkstation = 7,
373    PasswordExpired = 8,
374    PasswordMustChange = 9,
375}
376
377impl fmt::Display for AuthStatusCode {
378    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
379        match self {
380            Self::Success => write!(f, "authentication succeeded"),
381            Self::Failure => write!(f, "authentication failed"),
382            Self::Timeout => write!(f, "timed out"),
383            Self::Cancelled => write!(f, "cancelled"),
384            Self::AccountDisabled => write!(f, "account disabled"),
385            Self::AccountExpired => write!(f, "account expired"),
386            Self::AccountRestriction => write!(f, "account restricted"),
387            Self::InvalidLogonHours => write!(f, "invalid logon hours"),
388            Self::InvalidWorkstation => write!(f, "invalid workstation"),
389            Self::PasswordExpired => write!(f, "password expired"),
390            Self::PasswordMustChange => write!(f, "password must change"),
391        }
392    }
393}
394
395// NSTATUS_ASSOCIATE_TYPE
396
397#[derive(Encode, Decode, FromPrimitive, ToPrimitive, Debug, PartialEq, Clone, Copy)]
398#[repr(u16)]
399pub enum AssociateStatusCode {
400    Success = StatusCode::Success as u16,
401    Failure = StatusCode::Failure as u16,
402}
403
404impl fmt::Display for AssociateStatusCode {
405    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
406        match self {
407            Self::Success => write!(f, "association succeeded"),
408            Self::Failure => write!(f, "association failed"),
409        }
410    }
411}
412
413// NSTATUS_CAPABILITIES_TYPE
414
415#[derive(Encode, Decode, FromPrimitive, ToPrimitive, Debug, PartialEq, Clone, Copy)]
416#[repr(u16)]
417pub enum CapabilitiesStatusCode {
418    Success = StatusCode::Success as u16,
419    Failure = StatusCode::Failure as u16,
420}
421
422impl fmt::Display for CapabilitiesStatusCode {
423    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
424        match self {
425            Self::Failure => write!(f, "capabilities negotiation failed"),
426            Self::Success => write!(f, "capabilities negotiation succeeded"),
427        }
428    }
429}
430
431// NSTATUS_CHANNEL_TYPE
432
433#[derive(Encode, Decode, FromPrimitive, ToPrimitive, Debug, PartialEq, Clone, Copy)]
434#[repr(u16)]
435pub enum ChannelStatusCode {
436    Success = StatusCode::Success as u16,
437    Failure = StatusCode::Failure as u16,
438}
439
440impl fmt::Display for ChannelStatusCode {
441    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
442        match self {
443            Self::Success => write!(f, "success"),
444            Self::Failure => write!(f, "failure"),
445        }
446    }
447}
448
449// NSTATUS_CLIPBOARD_TYPE
450
451#[derive(Encode, Decode, FromPrimitive, ToPrimitive, Debug, PartialEq, Clone, Copy)]
452#[repr(u16)]
453pub enum ClipboardStatusCode {
454    Success = StatusCode::Success as u16,
455    Failure = StatusCode::Failure as u16,
456}
457
458impl fmt::Display for ClipboardStatusCode {
459    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
460        match self {
461            Self::Success => write!(f, "success"),
462            Self::Failure => write!(f, "failure"),
463        }
464    }
465}
466
467// NSTATUS_FILE_TRANSFER_TYPE
468
469#[derive(Encode, Decode, FromPrimitive, ToPrimitive, Debug, PartialEq, Clone, Copy)]
470#[repr(u16)]
471pub enum FileTransferStatusCode {
472    Success = StatusCode::Success as u16,
473    Failure = StatusCode::Failure as u16,
474}
475
476impl fmt::Display for FileTransferStatusCode {
477    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
478        match self {
479            FileTransferStatusCode::Success => write!(f, "success"),
480            FileTransferStatusCode::Failure => write!(f, "failure"),
481        }
482    }
483}
484
485// NSTATUS_EXEC_TYPE (Remote Execution)
486
487#[derive(Encode, Decode, FromPrimitive, ToPrimitive, Debug, PartialEq, Clone, Copy)]
488#[repr(u16)]
489pub enum ExecStatusCode {
490    Success = StatusCode::Success as u16,
491    Failure = StatusCode::Failure as u16,
492    FileNotFound = 1,
493    InvalidExecutable = 2,
494    AccessDenied = 3,
495}
496
497impl fmt::Display for ExecStatusCode {
498    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
499        match self {
500            ExecStatusCode::Success => write!(f, "execution success"),
501            ExecStatusCode::Failure => write!(f, "execution failed"),
502            ExecStatusCode::FileNotFound => write!(f, "file not found"),
503            ExecStatusCode::InvalidExecutable => write!(f, "invalid executable"),
504            ExecStatusCode::AccessDenied => write!(f, "access denied"),
505        }
506    }
507}
508
509#[cfg(test)]
510mod tests {
511    use super::*;
512    use num;
513
514    #[test]
515    fn integer_conversion() {
516        let status = num::FromPrimitive::from_u8(3);
517        match status {
518            Some(SeverityLevel::Fatal) => { /* success */ }
519            _ => panic!("wrong status value"),
520        }
521    }
522
523    #[test]
524    fn parse_from_u32() {
525        let nstatus = NowStatus::<AuthStatusCode>::try_from(0x8017_ffff).unwrap();
526        assert_eq!(nstatus.severity(), SeverityLevel::Error);
527        assert_eq!(nstatus.code(), AuthStatusCode::Failure);
528        assert_eq!(nstatus.status_type(), StatusType::Auth);
529    }
530
531    #[test]
532    fn repr_building() {
533        let nstatus = NowStatus::<AuthStatusCode>::builder(AuthStatusCode::Failure)
534            .severity(SeverityLevel::Error)
535            .status_type(StatusType::Auth)
536            .build();
537        assert_eq!(nstatus.as_u32(), 0x8017_ffff);
538    }
539}