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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
use std::collections::VecDeque;
use std::ffi::CString;
use std::io;

pub trait ToC<C> {
    unsafe fn to_c(&self, result: *mut C, buffer: &mut CBuffer) -> std::io::Result<()>;
}

#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum NssStatus {
    TryAgain = -2,
    Unavail = -1,
    NotFound = 0,
    Success = 1,
    Return = 2,
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Response<R> {
    TryAgain,
    Unavail,
    NotFound,
    Success(R),
    Return,
}

impl<R> Response<R> {
    pub fn to_status(&self) -> NssStatus {
        use NssStatus::*;
        match self {
            Self::Success(..) => Success,
            Self::TryAgain => TryAgain,
            Self::Unavail => Unavail,
            Self::NotFound => NotFound,
            Self::Return => Return,
        }
    }

    pub unsafe fn to_c<C>(
        &self,
        result: *mut C,
        buf: *mut libc::c_char,
        buflen: libc::size_t,
        errnop: *mut libc::c_int,
    ) -> NssStatus
    where
        R: ToC<C>,
    {
        if let Self::Success(entity) = self {
            let mut buffer = CBuffer::new(buf as *mut libc::c_void, buflen);
            buffer.clear();

            match entity.to_c(result, &mut buffer) {
                Ok(()) => {
                    *errnop = 0;
                    self.to_status()
                }
                Err(e) => match e.raw_os_error() {
                    Some(e) => {
                        *errnop = e;
                        Self::TryAgain.to_status()
                    }
                    None => {
                        *errnop = libc::ENOENT;
                        Self::Unavail.to_status()
                    }
                },
            }
        } else {
            self.to_status()
        }
    }
}

pub struct Iterator<T> {
    items: Option<VecDeque<T>>,
}

impl<T> Iterator<T> {
    pub fn new() -> Self {
        Iterator { items: None }
    }
    pub fn open(&mut self, items: Vec<T>) -> NssStatus {
        self.items = Some(VecDeque::from(items));
        NssStatus::Success
    }

    pub fn next(&mut self) -> Response<T> {
        match self.items {
            Some(ref mut items) => match items.pop_front() {
                Some(entity) => Response::Success(entity),
                None => Response::NotFound,
            },
            None => Response::Unavail,
        }
    }

    pub fn close(&mut self) -> NssStatus {
        self.items = None;
        NssStatus::Success
    }
}

pub struct CBuffer {
    start: *mut libc::c_void,
    pos: *mut libc::c_void,
    free: libc::size_t,
    len: libc::size_t,
}

impl CBuffer {
    pub fn new(ptr: *mut libc::c_void, len: libc::size_t) -> Self {
        CBuffer {
            start: ptr,
            pos: ptr,
            free: len,
            len,
        }
    }

    pub unsafe fn clear(&mut self) {
        libc::memset(self.start, 0, self.len);
    }

    pub unsafe fn write_str(&mut self, string: &str) -> io::Result<*mut libc::c_char> {
        // Capture start address
        let str_start = self.pos;

        // Convert string
        let cstr = CString::new(string).expect("Failed to convert string");
        let ptr = cstr.as_ptr();
        let len = libc::strlen(ptr);

        // Ensure we have enough capacity
        if self.free < len + 1 {
            return Err(io::Error::from_raw_os_error(libc::ERANGE));
        }

        // Copy string
        libc::memcpy(self.pos, ptr as *mut libc::c_void, len);
        self.pos = self.pos.offset(len as isize + 1);
        self.free -= len as usize + 1;

        // Return start of string
        Ok(str_start as *mut libc::c_char)
    }

    pub unsafe fn write_strs<S: AsRef<str>>(
        &mut self,
        strings: &[S],
    ) -> io::Result<*mut *mut libc::c_char> {
        let ptr_size = std::mem::size_of::<*mut libc::c_char>() as isize;

        let vec_start =
            self.reserve(ptr_size * (strings.len() as isize + 1))? as *mut *mut libc::c_char;
        let mut pos = vec_start;

        // Write strings
        for s in strings {
            *pos = self.write_str(s.as_ref())?;
            pos = pos.offset(1);
        }

        libc::memset(pos as *mut libc::c_void, 0, ptr_size as usize);

        Ok(vec_start)
    }

    pub unsafe fn reserve(&mut self, len: isize) -> io::Result<*mut libc::c_char> {
        let start = self.pos;

        // Ensure we have enough capacity
        if self.free < len as usize {
            return Err(io::Error::from_raw_os_error(libc::ERANGE));
        }

        // Reserve space
        self.pos = self.pos.offset(len as isize);
        self.free -= len as usize;

        Ok(start as *mut libc::c_char)
    }
}