| use std::alloc; |
| use std::mem; |
| use std::ptr; |
| use std::slice; |
| use winapi::ctypes; |
| use winapi::shared::sspi; |
| |
| // This is manually calculated here rather than using `size_of::<SEC_APPLICATION_PROTOCOL_LIST>()`, |
| // as the latter is 2 bytes too large because it accounts for padding at the end of the struct for |
| // alignment requirements, which is irrelevant in actual usage because there is a variable-length |
| // array at the end of the struct. |
| const SEC_APPLICATION_PROTOCOL_LIST_HEADER_SIZE: usize = |
| mem::size_of::<u32>() + mem::size_of::<ctypes::c_ushort>(); |
| const SEC_APPLICATION_PROTOCOL_HEADER_SIZE: usize = |
| mem::size_of::<ctypes::c_ulong>() + SEC_APPLICATION_PROTOCOL_LIST_HEADER_SIZE; |
| |
| pub struct AlpnList { |
| layout: alloc::Layout, |
| memory: ptr::NonNull<u8>, |
| } |
| |
| impl Drop for AlpnList { |
| fn drop(&mut self) { |
| unsafe { |
| // Safety: `self.memory` was allocated with `self.layout` and is non-null. |
| alloc::dealloc(self.memory.as_ptr(), self.layout); |
| } |
| } |
| } |
| |
| impl AlpnList { |
| pub fn new(protos: &[Vec<u8>]) -> Self { |
| // ALPN wire format is each ALPN preceded by its length as a byte. |
| let mut alpn_wire_format = Vec::with_capacity( |
| protos.iter().map(Vec::len).sum::<usize>() + protos.len(), |
| ); |
| for alpn in protos { |
| alpn_wire_format.push(alpn.len() as u8); |
| alpn_wire_format.extend(alpn); |
| } |
| |
| let size = SEC_APPLICATION_PROTOCOL_HEADER_SIZE + alpn_wire_format.len(); |
| let layout = alloc::Layout::from_size_align( |
| size, |
| mem::align_of::<sspi::SEC_APPLICATION_PROTOCOLS>(), |
| ).unwrap(); |
| |
| unsafe { |
| // Safety: `layout` is guaranteed to have non-zero size. |
| let memory = match ptr::NonNull::new(alloc::alloc(layout)) { |
| Some(ptr) => ptr, |
| None => alloc::handle_alloc_error(layout), |
| }; |
| |
| // Safety: `memory` was created from `layout`. |
| let buf = slice::from_raw_parts_mut(memory.as_ptr(), layout.size()); |
| let protocols = &mut *(buf.as_mut_ptr() as *mut sspi::SEC_APPLICATION_PROTOCOLS); |
| protocols.ProtocolListsSize = |
| (SEC_APPLICATION_PROTOCOL_LIST_HEADER_SIZE + alpn_wire_format.len()) as ctypes::c_ulong; |
| |
| let protocol = &mut *protocols.ProtocolLists.as_mut_ptr(); |
| protocol.ProtoNegoExt = sspi::SecApplicationProtocolNegotiationExt_ALPN; |
| protocol.ProtocolListSize = alpn_wire_format.len() as ctypes::c_ushort; |
| |
| let protocol_list_offset = protocol.ProtocolList.as_ptr() as usize - buf.as_ptr() as usize; |
| let protocol_list = &mut buf[protocol_list_offset..]; |
| protocol_list.copy_from_slice(&alpn_wire_format); |
| |
| Self { |
| layout, |
| memory, |
| } |
| } |
| } |
| } |
| |
| impl std::ops::Deref for AlpnList { |
| type Target = [u8]; |
| |
| fn deref(&self) -> &Self::Target { |
| unsafe { |
| // Safety: `self.memory` was created from `self.layout`. |
| slice::from_raw_parts(self.memory.as_ptr(), self.layout.size()) |
| } |
| } |
| } |
| |
| impl std::ops::DerefMut for AlpnList { |
| fn deref_mut(&mut self) -> &mut Self::Target { |
| unsafe { |
| // Safety: `self.memory` was created from `self.layout`. |
| slice::from_raw_parts_mut(self.memory.as_ptr(), self.layout.size()) |
| } |
| } |
| } |