a bare-bones limbo server in rust (mirror of https://github.com/xoogware/crawlspace)
at protocol-breakout 114 lines 3.5 kB view raw
1/* 2 * Copyright (c) 2024 Andrew Brower. 3 * This file is part of Crawlspace. 4 * 5 * Crawlspace is free software: you can redistribute it and/or 6 * modify it under the terms of the GNU Affero General Public 7 * License as published by the Free Software Foundation, either 8 * version 3 of the License, or (at your option) any later version. 9 * 10 * Crawlspace is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Affero General Public License for more details. 14 * 15 * You should have received a copy of the GNU Affero General Public 16 * License along with Crawlspace. If not, see 17 * <https://www.gnu.org/licenses/>. 18 */ 19 20use crate::{ 21 ErrorKind::{self, InvalidData}, 22 Read, Write, 23}; 24 25use super::VarInt; 26 27#[derive(Debug)] 28pub struct Bounded<T, const BOUND: usize = 32767>(pub T); 29 30impl<const BOUND: usize> Read for Bounded<String, BOUND> { 31 fn read(r: &mut impl std::io::Read) -> Result<Self, ErrorKind> { 32 let len = VarInt::read(r)?.0; 33 if len < 0 { 34 return Err(InvalidData( 35 "tried to decode string with negative length".to_string(), 36 )); 37 } 38 39 let len = len as usize; 40 41 let mut buf = vec![0; len]; 42 r.read_exact(&mut buf)?; 43 let content = String::from_utf8(buf) 44 .map_err(|_| ErrorKind::InvalidData("invalid utf8 string data".to_string()))?; 45 let utf16_len = content.encode_utf16().count(); 46 47 if utf16_len > BOUND { 48 return Err(InvalidData(format!( 49 "utf-16 encoded string exceeds {BOUND} chars (is {utf16_len})" 50 ))); 51 } 52 53 Ok(Bounded(content)) 54 } 55} 56 57impl<'a, const BOUND: usize> Write for Bounded<String, BOUND> { 58 fn write(&self, w: &mut impl std::io::Write) -> Result<(), ErrorKind> { 59 let len = self.0.encode_utf16().count(); 60 61 if len > BOUND { 62 return Err(InvalidData(format!( 63 "length of string {len} exceeds bound {BOUND}" 64 ))); 65 }; 66 67 VarInt(self.0.len() as i32).write(w)?; 68 Ok(w.write_all(self.0.as_bytes())?) 69 } 70} 71 72impl Write for str { 73 fn write(&self, w: &mut impl std::io::Write) -> Result<(), ErrorKind> { 74 VarInt(self.len() as i32).write(w)?; 75 Ok(w.write_all(self.as_bytes())?) 76 } 77} 78 79#[derive(Debug)] 80pub struct Rest<T, const BOUND: usize = 32767>(pub T); 81 82impl<'a, const BOUND: usize> Read for Rest<String, BOUND> { 83 fn read(r: &mut impl std::io::Read) -> Result<Self, ErrorKind> { 84 let mut buf = Vec::new(); 85 r.read_to_end(&mut buf)?; 86 87 let content = String::from_utf8(buf) 88 .map_err(|_| InvalidData("Rest was not valid UTF-8".to_string()))?; 89 90 let utf16_len = content.encode_utf16().count(); 91 92 if utf16_len > BOUND { 93 return Err(InvalidData(format!( 94 "utf-16 encoded string exceeds {BOUND} chars (is {utf16_len})" 95 ))); 96 }; 97 98 Ok(Rest(content)) 99 } 100} 101 102impl<'a, const BOUND: usize> Write for Rest<String, BOUND> { 103 fn write(&self, w: &mut impl std::io::Write) -> Result<(), ErrorKind> { 104 let len = self.0.encode_utf16().count(); 105 106 if len > BOUND { 107 return Err(InvalidData(format!( 108 "length of string {len} exceeds bound {BOUND}" 109 ))); 110 }; 111 112 Ok(w.write_all(self.0.as_bytes())?) 113 } 114}