1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// SPDX-License-Identifier: MIT

use std::convert::TryFrom;

use netlink_packet_utils::{
    buffer,
    traits::{Emitable, Parseable},
    DecodeError,
};

use crate::constants::*;

pub const UNIX_REQUEST_LEN: usize = 24;

buffer!(UnixRequestBuffer(UNIX_REQUEST_LEN) {
    // The address family; it should be set to `AF_UNIX`
    family: (u8, 0),
    // This field should be set to `0`
    protocol: (u8, 1),
    // This field should be set to `0`
    pad: (u16, 2..4),
    // This is a bit mask that defines a filter of sockets
    // states. Only those sockets whose states are in this mask will
    // be reported. Ignored when querying for an individual
    // socket. Supported values are:
    //
    // ```no_rust
    // 1 << UNIX_ESTABLISHED
    // 1 << UNIX_LISTEN
    // ```
    state_flags: (u32, 4..8),
    // This is an inode number when querying for an individual
    // socket. Ignored when querying for a list of sockets.
    inode: (u32, 8..12),
    // This is a set of flags defining what kind of information to
    // report. Supported values are the `UDIAG_SHOW_*` constants.
    show_flags: (u32, 12..16),
    // This is an array of opaque identifiers that could be used
    // along with udiag_ino to specify an individual socket. It is
    // ignored when querying for a list of sockets, as well as when
    // all its elements are set to `0xff`.
    cookie: (slice, 16..UNIX_REQUEST_LEN),
});

/// The request for UNIX domain sockets
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct UnixRequest {
    /// This is a bit mask that defines a filter of sockets states.
    ///
    /// Only those sockets whose states are in this mask will be reported.
    /// Ignored when querying for an individual socket.
    pub state_flags: StateFlags,
    /// This is an inode number when querying for an individual socket.
    ///
    /// Ignored when querying for a list of sockets.
    pub inode: u32,
    /// This is a set of flags defining what kind of information to report.
    ///
    /// Each requested kind of information is reported back as a netlink
    /// attribute
    pub show_flags: ShowFlags,
    /// This is an opaque identifiers that could be used to specify an
    /// individual socket.
    pub cookie: [u8; 8],
}

bitflags! {
    /// Bitmask that defines a filter of UNIX socket states
    pub struct StateFlags: u32 {
        const ESTABLISHED = 1 << TCP_ESTABLISHED;
        const LISTEN = 1 << TCP_LISTEN;
    }
}

bitflags! {
    /// Bitmask that defines what kind of information to
    /// report. Supported values are the `UDIAG_SHOW_*` constants.
    pub struct ShowFlags: u32 {
        const NAME = UDIAG_SHOW_NAME;
        const VFS = UDIAG_SHOW_VFS;
        const PEER = UDIAG_SHOW_PEER;
        const ICONS = UDIAG_SHOW_ICONS;
        const RQLEN = UDIAG_SHOW_RQLEN;
        const MEMINFO = UDIAG_SHOW_MEMINFO;
    }
}

impl<'a, T: AsRef<[u8]> + 'a> Parseable<UnixRequestBuffer<&'a T>>
    for UnixRequest
{
    fn parse(buf: &UnixRequestBuffer<&'a T>) -> Result<Self, DecodeError> {
        Ok(Self {
            state_flags: StateFlags::from_bits_truncate(buf.state_flags()),
            inode: buf.inode(),
            show_flags: ShowFlags::from_bits_truncate(buf.show_flags()),
            // Unwrapping is safe because UnixRequestBuffer::cookie()
            // returns a slice of exactly 8 bytes.
            cookie: TryFrom::try_from(buf.cookie()).unwrap(),
        })
    }
}

impl Emitable for UnixRequest {
    fn buffer_len(&self) -> usize {
        UNIX_REQUEST_LEN
    }

    fn emit(&self, buf: &mut [u8]) {
        let mut buffer = UnixRequestBuffer::new(buf);
        buffer.set_family(AF_UNIX);
        buffer.set_protocol(0);
        buffer.set_state_flags(self.state_flags.bits());
        buffer.set_inode(self.inode);
        buffer.set_pad(0);
        buffer.set_show_flags(self.show_flags.bits());
        buffer.cookie_mut().copy_from_slice(&self.cookie[..]);
    }
}