Mercurial > crates > nonstick
comparison src/libpam/response.rs @ 77:351bdc13005e
Update the libpam module to work with the new structure.
| author | Paul Fisher <paul@pfish.zone> |
|---|---|
| date | Sun, 08 Jun 2025 01:03:46 -0400 |
| parents | c30811b4afae |
| children |
comparison
equal
deleted
inserted
replaced
| 76:e58d24849e82 | 77:351bdc13005e |
|---|---|
| 1 //! Types used when dealing with PAM conversations. | 1 //! Types used to communicate data from the application to the module. |
| 2 | 2 |
| 3 use crate::conv::BinaryData; | 3 use crate::conv::BorrowedBinaryData; |
| 4 use crate::libpam::conversation::OwnedMessage; | |
| 4 use crate::libpam::memory; | 5 use crate::libpam::memory; |
| 5 use crate::libpam::memory::{CBinaryData, Immovable, NulError, TooBigError}; | 6 use crate::libpam::memory::{CBinaryData, Immovable}; |
| 6 use crate::Response; | 7 use crate::{ErrorCode, Result}; |
| 7 use std::ffi::{c_int, c_void, CStr}; | 8 use std::ffi::{c_int, c_void, CStr}; |
| 8 use std::ops::{Deref, DerefMut}; | 9 use std::ops::{Deref, DerefMut}; |
| 9 use std::result::Result as StdResult; | |
| 10 use std::str::Utf8Error; | |
| 11 use std::{iter, mem, ptr, slice}; | 10 use std::{iter, mem, ptr, slice}; |
| 12 | 11 |
| 13 #[repr(transparent)] | 12 #[repr(transparent)] |
| 14 #[derive(Debug)] | 13 #[derive(Debug)] |
| 15 pub struct RawTextResponse(RawResponse); | 14 pub struct TextAnswer(Answer); |
| 16 | 15 |
| 17 impl RawTextResponse { | 16 impl TextAnswer { |
| 18 /// Interprets the provided `RawResponse` as a text response. | 17 /// Interprets the provided `Answer` as a text answer. |
| 19 /// | 18 /// |
| 20 /// # Safety | 19 /// # Safety |
| 21 /// | 20 /// |
| 22 /// It's up to you to provide a response that is a `RawTextResponse`. | 21 /// It's up to you to provide an answer that is a `TextAnswer`. |
| 23 pub unsafe fn upcast(from: &mut RawResponse) -> &mut Self { | 22 pub unsafe fn upcast(from: &mut Answer) -> &mut Self { |
| 24 // SAFETY: We're provided a valid reference. | 23 // SAFETY: We're provided a valid reference. |
| 25 &mut *(from as *mut RawResponse).cast::<Self>() | 24 &mut *(from as *mut Answer).cast::<Self>() |
| 26 } | 25 } |
| 27 | 26 |
| 28 /// Fills in the provided `RawResponse` with the given text. | 27 /// Converts the `Answer` to a `TextAnswer` with the given text. |
| 29 /// | 28 fn fill(dest: &mut Answer, text: &str) -> Result<()> { |
| 30 /// You are responsible for calling [`free`](Self::free_contents) | 29 let allocated = memory::malloc_str(text)?; |
| 31 /// on the pointer you get back when you're done with it. | 30 dest.free_contents(); |
| 32 pub fn fill(dest: &mut RawResponse, text: impl AsRef<str>) -> StdResult<&mut Self, NulError> { | 31 dest.data = allocated.cast(); |
| 33 dest.data = memory::malloc_str(text)?.cast(); | 32 Ok(()) |
| 34 // SAFETY: We just filled this in so we know it's a text response. | 33 } |
| 35 Ok(unsafe { Self::upcast(dest) }) | 34 |
| 36 } | 35 /// Gets the string stored in this answer. |
| 37 | 36 pub fn contents(&self) -> Result<&str> { |
| 38 /// Gets the string stored in this response. | |
| 39 pub fn contents(&self) -> StdResult<&str, Utf8Error> { | |
| 40 if self.0.data.is_null() { | 37 if self.0.data.is_null() { |
| 41 Ok("") | 38 Ok("") |
| 42 } else { | 39 } else { |
| 43 // SAFETY: This data is either passed from PAM (so we are forced to | 40 // SAFETY: This data is either passed from PAM (so we are forced |
| 44 // trust it) or was created by us in TextResponseInner::alloc. | 41 // to trust it) or was created by us in TextAnswerInner::alloc. |
| 45 // In either case, it's going to be a valid null-terminated string. | 42 // In either case, it's going to be a valid null-terminated string. |
| 46 unsafe { CStr::from_ptr(self.0.data.cast()) }.to_str() | 43 unsafe { CStr::from_ptr(self.0.data.cast()) } |
| 47 } | 44 .to_str() |
| 48 } | 45 .map_err(|_| ErrorCode::ConversationError) |
| 49 | 46 } |
| 50 /// Releases memory owned by this response. | 47 } |
| 48 | |
| 49 /// Zeroes out the answer data, frees it, and points our data to `null`. | |
| 50 /// | |
| 51 /// When this `TextAnswer` is part of an [`Answers`], | |
| 52 /// this is optional (since that will perform the `free`), | |
| 53 /// but it will clear potentially sensitive data. | |
| 51 pub fn free_contents(&mut self) { | 54 pub fn free_contents(&mut self) { |
| 52 // SAFETY: We know we own this data. | 55 // SAFETY: We own this data and know it's valid. |
| 56 // If it's null, this is a no-op. | |
| 53 // After we're done, it will be null. | 57 // After we're done, it will be null. |
| 54 unsafe { | 58 unsafe { |
| 55 memory::zero_c_string(self.0.data); | 59 memory::zero_c_string(self.0.data); |
| 56 libc::free(self.0.data); | 60 libc::free(self.0.data); |
| 57 self.0.data = ptr::null_mut() | 61 self.0.data = ptr::null_mut() |
| 58 } | 62 } |
| 59 } | 63 } |
| 60 } | 64 } |
| 61 | 65 |
| 62 /// A [`RawResponse`] with [`CBinaryData`] in it. | 66 /// A [`Answer`] with [`CBinaryData`] in it. |
| 63 #[repr(transparent)] | 67 #[repr(transparent)] |
| 64 #[derive(Debug)] | 68 #[derive(Debug)] |
| 65 pub struct RawBinaryResponse(RawResponse); | 69 pub struct BinaryAnswer(Answer); |
| 66 | 70 |
| 67 impl RawBinaryResponse { | 71 impl BinaryAnswer { |
| 68 /// Interprets the provided `RawResponse` as a binary response. | 72 /// Interprets the provided [`Answer`] as a binary answer. |
| 69 /// | 73 /// |
| 70 /// # Safety | 74 /// # Safety |
| 71 /// | 75 /// |
| 72 /// It's up to you to provide a response that is a `RawBinaryResponse`. | 76 /// It's up to you to provide an answer that is a `BinaryAnswer`. |
| 73 pub unsafe fn upcast(from: &mut RawResponse) -> &mut Self { | 77 pub unsafe fn upcast(from: &mut Answer) -> &mut Self { |
| 74 // SAFETY: We're provided a valid reference. | 78 // SAFETY: We're provided a valid reference. |
| 75 &mut *(from as *mut RawResponse).cast::<Self>() | 79 &mut *(from as *mut Answer).cast::<Self>() |
| 76 } | 80 } |
| 77 | 81 |
| 78 /// Fills in a `RawResponse` with the provided binary data. | 82 /// Fills in a [`Answer`] with the provided binary data. |
| 79 /// | 83 /// |
| 80 /// The `data_type` is a tag you can use for whatever. | 84 /// The `data_type` is a tag you can use for whatever. |
| 81 /// It is passed through PAM unchanged. | 85 /// It is passed through PAM unchanged. |
| 82 /// | 86 /// |
| 83 /// The referenced data is copied to the C heap. We do not take ownership. | 87 /// The referenced data is copied to the C heap. |
| 84 /// You are responsible for calling [`free`](Self::free_contents) | 88 /// We do not take ownership of the original data. |
| 85 /// on the pointer you get back when you're done with it. | 89 pub fn fill(dest: &mut Answer, data: BorrowedBinaryData) -> Result<()> { |
| 86 pub fn fill<'a>( | 90 let allocated = CBinaryData::alloc(data.data(), data.data_type())?; |
| 87 dest: &'a mut RawResponse, | 91 dest.free_contents(); |
| 88 data: &[u8], | 92 dest.data = allocated.cast(); |
| 89 data_type: u8, | 93 Ok(()) |
| 90 ) -> StdResult<&'a mut Self, TooBigError> { | 94 } |
| 91 dest.data = CBinaryData::alloc(data, data_type)?.cast(); | 95 |
| 92 // SAFETY: We just filled this in, so we know it's binary. | 96 /// Gets the binary data in this answer. |
| 93 Ok(unsafe { Self::upcast(dest) }) | 97 pub fn data(&self) -> Option<&CBinaryData> { |
| 94 } | 98 // SAFETY: We either got this data from PAM or allocated it ourselves. |
| 95 | 99 // Either way, we trust that it is either valid data or null. |
| 96 /// Gets the binary data in this response. | |
| 97 pub fn data(&self) -> &[u8] { | |
| 98 self.contents().map(CBinaryData::contents).unwrap_or(&[]) | |
| 99 } | |
| 100 | |
| 101 /// Gets the `data_type` tag that was embedded with the message. | |
| 102 pub fn data_type(&self) -> u8 { | |
| 103 self.contents().map(CBinaryData::data_type).unwrap_or(0) | |
| 104 } | |
| 105 | |
| 106 fn contents(&self) -> Option<&CBinaryData> { | |
| 107 // SAFETY: This was either something we got from PAM (in which case | |
| 108 // we trust it), or something that was created with | |
| 109 // BinaryResponseInner::alloc. In both cases, it points to valid data. | |
| 110 unsafe { self.0.data.cast::<CBinaryData>().as_ref() } | 100 unsafe { self.0.data.cast::<CBinaryData>().as_ref() } |
| 111 } | 101 } |
| 112 | 102 |
| 113 pub fn to_owned(&self) -> BinaryData { | 103 /// Zeroes out the answer data, frees it, and points our data to `null`. |
| 114 BinaryData::new(self.data().into(), self.data_type()) | 104 /// |
| 115 } | 105 /// When this `TextAnswer` is part of an [`Answers`], |
| 116 | 106 /// this is optional (since that will perform the `free`), |
| 117 /// Releases memory owned by this response. | 107 /// but it will clear potentially sensitive data. |
| 118 pub fn free_contents(&mut self) { | 108 pub fn zero_contents(&mut self) { |
| 119 // SAFETY: We know that our data pointer is either valid or null. | 109 // SAFETY: We know that our data pointer is either valid or null. |
| 120 // Once we're done, it's null and the response is safe. | 110 // Once we're done, it's null and the answer is safe. |
| 121 unsafe { | 111 unsafe { |
| 122 let data_ref = self.0.data.cast::<CBinaryData>().as_mut(); | 112 let data_ref = self.0.data.cast::<CBinaryData>().as_mut(); |
| 123 if let Some(d) = data_ref { | 113 if let Some(d) = data_ref { |
| 124 d.zero_contents() | 114 d.zero_contents() |
| 125 } | 115 } |
| 127 self.0.data = ptr::null_mut() | 117 self.0.data = ptr::null_mut() |
| 128 } | 118 } |
| 129 } | 119 } |
| 130 } | 120 } |
| 131 | 121 |
| 132 /// Generic version of response data. | 122 /// Generic version of answer data. |
| 133 /// | 123 /// |
| 134 /// This has the same structure as [`RawBinaryResponse`] | 124 /// This has the same structure as [`BinaryAnswer`] |
| 135 /// and [`RawTextResponse`]. | 125 /// and [`TextAnswer`]. |
| 136 #[repr(C)] | 126 #[repr(C)] |
| 137 #[derive(Debug)] | 127 #[derive(Debug)] |
| 138 pub struct RawResponse { | 128 pub struct Answer { |
| 139 /// Pointer to the data returned in a response. | 129 /// Pointer to the data returned in an answer. |
| 140 /// For most responses, this will be a [`CStr`], but for responses to | 130 /// For most answers, this will be a [`CStr`], but for answers to |
| 141 /// [`MessageStyle::BinaryPrompt`]s, this will be [`CBinaryData`] | 131 /// [`MessageStyle::BinaryPrompt`]s, this will be [`CBinaryData`] |
| 142 /// (a Linux-PAM extension). | 132 /// (a Linux-PAM extension). |
| 143 data: *mut c_void, | 133 data: *mut c_void, |
| 144 /// Unused. | 134 /// Unused. |
| 145 return_code: c_int, | 135 return_code: c_int, |
| 146 _marker: Immovable, | 136 _marker: Immovable, |
| 147 } | 137 } |
| 148 | 138 |
| 149 /// A contiguous block of responses. | 139 impl Answer { |
| 140 /// Frees the contents of this answer. | |
| 141 /// | |
| 142 /// After this is done, this answer's `data` will be `null`, | |
| 143 /// which is a valid (empty) state. | |
| 144 fn free_contents(&mut self) { | |
| 145 // SAFETY: We have either an owned valid pointer, or null. | |
| 146 // We can free our owned pointer, and `free(null)` is a no-op. | |
| 147 unsafe { | |
| 148 libc::free(self.data); | |
| 149 self.data = ptr::null_mut(); | |
| 150 } | |
| 151 } | |
| 152 } | |
| 153 | |
| 154 /// An owned, contiguous block of [`Answer`]s. | |
| 150 #[derive(Debug)] | 155 #[derive(Debug)] |
| 151 pub struct OwnedResponses { | 156 pub struct Answers { |
| 152 base: *mut RawResponse, | 157 base: *mut Answer, |
| 153 count: usize, | 158 count: usize, |
| 154 } | 159 } |
| 155 | 160 |
| 156 impl OwnedResponses { | 161 impl Answers { |
| 157 /// Allocates an owned list of responses on the C heap. | 162 /// Allocates an owned list of answers on the C heap. |
| 158 fn alloc(count: usize) -> Self { | 163 fn alloc(count: usize) -> Self { |
| 159 OwnedResponses { | 164 Answers { |
| 160 // SAFETY: We are doing allocation here. | 165 // SAFETY: We are doing allocation here. |
| 161 base: unsafe { libc::calloc(count, size_of::<RawResponse>()) }.cast(), | 166 base: unsafe { libc::calloc(count, size_of::<Answer>()) }.cast(), |
| 162 count, | 167 count, |
| 163 } | 168 } |
| 164 } | 169 } |
| 165 | 170 |
| 166 pub fn build(value: &[Response]) -> StdResult<Self, FillError> { | 171 pub fn build(value: Vec<OwnedMessage>) -> Result<Self> { |
| 167 let mut outputs = OwnedResponses::alloc(value.len()); | 172 let mut outputs = Answers::alloc(value.len()); |
| 168 // If we fail in here after allocating OwnedResponses, | 173 // Even if we fail during this process, we still end up freeing |
| 169 // we still free all memory, even though we don't zero it first. | 174 // all allocated answer memory. |
| 170 // This is an acceptable level of risk. | 175 for (input, output) in iter::zip(value, outputs.iter_mut()) { |
| 171 for (input, output) in iter::zip(value.iter(), outputs.iter_mut()) { | |
| 172 match input { | 176 match input { |
| 173 Response::NoResponse => { | 177 OwnedMessage::MaskedPrompt(p) => TextAnswer::fill(output, p.answer()?.unsecure())?, |
| 174 RawTextResponse::fill(output, "")?; | 178 OwnedMessage::Prompt(p) => TextAnswer::fill(output, &(p.answer()?))?, |
| 175 } | 179 OwnedMessage::BinaryPrompt(p) => BinaryAnswer::fill(output, (&p.answer()?).into())?, |
| 176 Response::Text(data) => { | 180 OwnedMessage::Error(p) => TextAnswer::fill(output, p.answer().map(|_| "")?)?, |
| 177 RawTextResponse::fill(output, data)?; | 181 OwnedMessage::Info(p) => TextAnswer::fill(output, p.answer().map(|_| "")?)?, |
| 178 } | 182 OwnedMessage::RadioPrompt(p) => TextAnswer::fill(output, &(p.answer()?))?, |
| 179 Response::MaskedText(data) => { | |
| 180 RawTextResponse::fill(output, data.unsecure())?; | |
| 181 } | |
| 182 Response::Binary(data) => { | |
| 183 RawBinaryResponse::fill(output, data.data(), data.data_type())?; | |
| 184 } | |
| 185 } | 183 } |
| 186 } | 184 } |
| 187 Ok(outputs) | 185 Ok(outputs) |
| 188 } | 186 } |
| 189 | 187 |
| 190 /// Converts this into a `*RawResponse` for passing to PAM. | 188 /// Converts this into a `*Answer` for passing to PAM. |
| 191 /// | 189 /// |
| 192 /// The pointer "owns" its own data (i.e., this will not be dropped). | 190 /// The pointer "owns" its own data (i.e., this will not be dropped). |
| 193 pub fn into_ptr(self) -> *mut RawResponse { | 191 pub fn into_ptr(self) -> *mut Answer { |
| 194 let ret = self.base; | 192 let ret = self.base; |
| 195 mem::forget(self); | 193 mem::forget(self); |
| 196 ret | 194 ret |
| 197 } | 195 } |
| 198 | 196 |
| 199 /// Takes ownership of a list of responses allocated on the C heap. | 197 /// Takes ownership of a list of answers allocated on the C heap. |
| 200 /// | 198 /// |
| 201 /// # Safety | 199 /// # Safety |
| 202 /// | 200 /// |
| 203 /// It's up to you to make sure you pass a valid pointer. | 201 /// It's up to you to make sure you pass a valid pointer. |
| 204 pub unsafe fn from_c_heap(base: *mut RawResponse, count: usize) -> Self { | 202 pub unsafe fn from_c_heap(base: *mut Answer, count: usize) -> Self { |
| 205 OwnedResponses { base, count } | 203 Answers { base, count } |
| 206 } | 204 } |
| 207 } | 205 } |
| 208 | 206 |
| 209 #[derive(Debug, thiserror::Error)] | 207 impl Deref for Answers { |
| 210 #[error("error converting responses: {0}")] | 208 type Target = [Answer]; |
| 211 pub enum FillError { | |
| 212 NulError(#[from] NulError), | |
| 213 TooBigError(#[from] TooBigError), | |
| 214 } | |
| 215 | |
| 216 impl Deref for OwnedResponses { | |
| 217 type Target = [RawResponse]; | |
| 218 fn deref(&self) -> &Self::Target { | 209 fn deref(&self) -> &Self::Target { |
| 219 // SAFETY: This is the memory we manage ourselves. | 210 // SAFETY: This is the memory we manage ourselves. |
| 220 unsafe { slice::from_raw_parts(self.base, self.count) } | 211 unsafe { slice::from_raw_parts(self.base, self.count) } |
| 221 } | 212 } |
| 222 } | 213 } |
| 223 | 214 |
| 224 impl DerefMut for OwnedResponses { | 215 impl DerefMut for Answers { |
| 225 fn deref_mut(&mut self) -> &mut Self::Target { | 216 fn deref_mut(&mut self) -> &mut Self::Target { |
| 226 // SAFETY: This is the memory we manage ourselves. | 217 // SAFETY: This is the memory we manage ourselves. |
| 227 unsafe { slice::from_raw_parts_mut(self.base, self.count) } | 218 unsafe { slice::from_raw_parts_mut(self.base, self.count) } |
| 228 } | 219 } |
| 229 } | 220 } |
| 230 | 221 |
| 231 impl Drop for OwnedResponses { | 222 impl Drop for Answers { |
| 232 fn drop(&mut self) { | 223 fn drop(&mut self) { |
| 233 // SAFETY: We allocated this ourselves, or it was provided to us by PAM. | 224 // SAFETY: We allocated this ourselves, or it was provided to us by PAM. |
| 234 unsafe { | 225 unsafe { |
| 235 for resp in self.iter_mut() { | 226 for answer in self.iter_mut() { |
| 236 libc::free(resp.data) | 227 answer.free_contents() |
| 237 } | 228 } |
| 238 libc::free(self.base.cast()) | 229 libc::free(self.base.cast()) |
| 239 } | 230 } |
| 240 } | 231 } |
| 241 } | 232 } |
| 242 | 233 |
| 243 #[cfg(test)] | 234 #[cfg(test)] |
| 244 mod tests { | 235 mod tests { |
| 245 use super::{BinaryData, OwnedResponses, RawBinaryResponse, RawTextResponse, Response}; | 236 use super::{Answers, BinaryAnswer, TextAnswer, BorrowedBinaryData}; |
| 237 use crate::BinaryData; | |
| 238 use crate::conv::{BinaryQAndA, ErrorMsg, InfoMsg, MaskedQAndA, QAndA, RadioQAndA}; | |
| 239 use crate::libpam::conversation::OwnedMessage; | |
| 246 | 240 |
| 247 #[test] | 241 #[test] |
| 248 fn test_round_trip() { | 242 fn test_round_trip() { |
| 249 let responses = [ | 243 let binary_msg = { |
| 250 Response::Binary(BinaryData::new(vec![1, 2, 3], 99)), | 244 let qa = BinaryQAndA::new(&[], 0); |
| 251 Response::Text("whats going on".to_owned()), | 245 qa.set_answer(Ok(BinaryData::new(vec![1, 2, 3], 99))); |
| 252 Response::MaskedText("well then".into()), | 246 OwnedMessage::BinaryPrompt(qa) |
| 253 Response::NoResponse, | 247 }; |
| 254 Response::Text("bogus".to_owned()), | 248 |
| 249 macro_rules! answered { | |
| 250 ($typ:ty, $msg:path, $data:expr) => { | |
| 251 {let qa = <$typ>::new(""); | |
| 252 qa.set_answer(Ok($data)); $msg(qa)} | |
| 253 } | |
| 254 } | |
| 255 | |
| 256 | |
| 257 let answers = vec![ | |
| 258 binary_msg, | |
| 259 answered!(QAndA, OwnedMessage::Prompt, "whats going on".to_owned()), | |
| 260 answered!(MaskedQAndA, OwnedMessage::MaskedPrompt, "well then".into()), | |
| 261 answered!(ErrorMsg, OwnedMessage::Error, ()), | |
| 262 answered!(InfoMsg, OwnedMessage::Info, ()), | |
| 263 answered!(RadioQAndA, OwnedMessage::RadioPrompt, "beep boop".to_owned()), | |
| 255 ]; | 264 ]; |
| 256 let sent = OwnedResponses::build(&responses).unwrap(); | 265 let n = answers.len(); |
| 257 let heap_resps = sent.into_ptr(); | 266 let sent = Answers::build(answers).unwrap(); |
| 258 let mut received = unsafe { OwnedResponses::from_c_heap(heap_resps, 5) }; | 267 let heap_answers = sent.into_ptr(); |
| 268 let mut received = unsafe { Answers::from_c_heap(heap_answers, n) }; | |
| 259 | 269 |
| 260 let assert_text = |want, raw| { | 270 let assert_text = |want, raw| { |
| 261 let up = unsafe { RawTextResponse::upcast(raw) }; | 271 let up = unsafe { TextAnswer::upcast(raw) }; |
| 262 assert_eq!(want, up.contents().unwrap()); | 272 assert_eq!(want, up.contents().unwrap()); |
| 263 up.free_contents(); | 273 up.free_contents(); |
| 264 assert_eq!("", up.contents().unwrap()); | 274 assert_eq!("", up.contents().unwrap()); |
| 265 }; | 275 }; |
| 266 let assert_bin = |want_data: &[u8], want_type, raw| { | 276 let assert_bin = |want_data: &[u8], want_type, raw| { |
| 267 let up = unsafe { RawBinaryResponse::upcast(raw) }; | 277 let up = unsafe { BinaryAnswer::upcast(raw) }; |
| 268 assert_eq!(want_data, up.data()); | 278 assert_eq!(BinaryData::new(want_data.into(), want_type), up.data().into()); |
| 269 assert_eq!(want_type, up.data_type()); | 279 up.zero_contents(); |
| 270 up.free_contents(); | 280 assert_eq!(BinaryData::default(), up.data().into()); |
| 271 let empty: [u8; 0] = []; | |
| 272 assert_eq!(&empty, up.data()); | |
| 273 assert_eq!(0, up.data_type()); | |
| 274 }; | 281 }; |
| 275 if let [zero, one, two, three, four] = &mut received[..] { | 282 if let [zero, one, two, three, four, five] = &mut received[..] { |
| 276 assert_bin(&[1, 2, 3], 99, zero); | 283 assert_bin(&[1, 2, 3], 99, zero); |
| 277 assert_text("whats going on", one); | 284 assert_text("whats going on", one); |
| 278 assert_text("well then", two); | 285 assert_text("well then", two); |
| 279 assert_text("", three); | 286 assert_text("", three); |
| 280 assert_text("bogus", four); | 287 assert_text("", four); |
| 288 assert_text("beep boop", five); | |
| 281 } else { | 289 } else { |
| 282 panic!("wrong size!") | 290 panic!("received wrong size {len}!", len = received.len()) |
| 283 } | 291 } |
| 284 } | 292 } |
| 285 | 293 |
| 286 #[test] | 294 #[test] |
| 287 fn test_text_response() { | 295 fn test_text_answer() { |
| 288 let mut responses = OwnedResponses::alloc(2); | 296 let mut answers = Answers::alloc(2); |
| 289 let text = RawTextResponse::fill(&mut responses[0], "hello").unwrap(); | 297 let zeroth = &mut answers[0]; |
| 290 let data = text.contents().expect("valid"); | 298 TextAnswer::fill(zeroth, "hello").unwrap(); |
| 299 let zeroth_text = unsafe { TextAnswer::upcast(zeroth) }; | |
| 300 let data = zeroth_text.contents().expect("valid"); | |
| 291 assert_eq!("hello", data); | 301 assert_eq!("hello", data); |
| 292 text.free_contents(); | 302 zeroth_text.free_contents(); |
| 293 text.free_contents(); | 303 zeroth_text.free_contents(); |
| 294 RawTextResponse::fill(&mut responses[1], "hell\0").expect_err("should error; contains nul"); | 304 TextAnswer::fill(&mut answers[1], "hell\0").expect_err("should error; contains nul"); |
| 295 } | 305 } |
| 296 | 306 |
| 297 #[test] | 307 #[test] |
| 298 fn test_binary_response() { | 308 fn test_binary_answer() { |
| 299 let mut responses = OwnedResponses::alloc(1); | 309 let mut answers = Answers::alloc(1); |
| 300 let real_data = [1, 2, 3, 4, 5, 6, 7, 8]; | 310 let real_data = BinaryData::new(vec![1, 2, 3, 4, 5, 6, 7, 8], 9); |
| 301 let resp = RawBinaryResponse::fill(&mut responses[0], &real_data, 7) | 311 let answer = &mut answers[0]; |
| 302 .expect("alloc should succeed"); | 312 BinaryAnswer::fill(answer, (&real_data).into()).expect("alloc should succeed"); |
| 303 let data = resp.data(); | 313 let bin_answer = unsafe { BinaryAnswer::upcast(answer) }; |
| 304 assert_eq!(&real_data, data); | 314 assert_eq!(real_data, bin_answer.data().into()); |
| 305 assert_eq!(7, resp.data_type()); | 315 answer.free_contents(); |
| 306 resp.free_contents(); | 316 answer.free_contents(); |
| 307 resp.free_contents(); | |
| 308 } | 317 } |
| 309 | 318 |
| 310 #[test] | 319 #[test] |
| 311 #[ignore] | 320 #[ignore] |
| 312 fn test_binary_response_too_big() { | 321 fn test_binary_answer_too_big() { |
| 313 let big_data: Vec<u8> = vec![0xFFu8; 10_000_000_000]; | 322 let big_data: Vec<u8> = vec![0xFFu8; 10_000_000_000]; |
| 314 let mut responses = OwnedResponses::alloc(1); | 323 let mut answers = Answers::alloc(1); |
| 315 RawBinaryResponse::fill(&mut responses[0], &big_data, 0).expect_err("this is too big!"); | 324 BinaryAnswer::fill(&mut answers[0], BorrowedBinaryData::new(&big_data, 100)) |
| 316 } | 325 .expect_err("this is too big!"); |
| 317 } | 326 } |
| 327 } |
