chore: initial sanitized public snapshot
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use super::DhtBencodeValue;
|
||||
|
||||
/// Encodes one DHT bencode value into a byte buffer.
|
||||
fn dht_encode_value(value: &DhtBencodeValue, out: &mut Vec<u8>) {
|
||||
match value {
|
||||
DhtBencodeValue::Int(number) => {
|
||||
out.push(b'i');
|
||||
out.extend_from_slice(number.to_string().as_bytes());
|
||||
out.push(b'e');
|
||||
}
|
||||
DhtBencodeValue::Bytes(bytes) => {
|
||||
out.extend_from_slice(bytes.len().to_string().as_bytes());
|
||||
out.push(b':');
|
||||
out.extend_from_slice(bytes);
|
||||
}
|
||||
DhtBencodeValue::List(values) => {
|
||||
out.push(b'l');
|
||||
for item in values {
|
||||
dht_encode_value(item, out);
|
||||
}
|
||||
out.push(b'e');
|
||||
}
|
||||
DhtBencodeValue::Dict(values) => {
|
||||
out.push(b'd');
|
||||
for (key, value) in values {
|
||||
out.extend_from_slice(key.len().to_string().as_bytes());
|
||||
out.push(b':');
|
||||
out.extend_from_slice(key);
|
||||
dht_encode_value(value, out);
|
||||
}
|
||||
out.push(b'e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Encodes a DHT dictionary into canonical bencode bytes.
|
||||
pub(super) fn dht_encode_dict(dict: &BTreeMap<Vec<u8>, DhtBencodeValue>) -> Vec<u8> {
|
||||
let mut out = Vec::new();
|
||||
dht_encode_value(&DhtBencodeValue::Dict(dict.clone()), &mut out);
|
||||
out
|
||||
}
|
||||
|
||||
/// Parses one DHT bencode value and returns the decoded value plus next index.
|
||||
pub(super) fn dht_parse_value(
|
||||
input: &[u8],
|
||||
index: usize,
|
||||
) -> Result<(DhtBencodeValue, usize), String> {
|
||||
match input.get(index).copied() {
|
||||
Some(b'i') => dht_parse_int(input, index + 1),
|
||||
Some(b'l') => dht_parse_list(input, index + 1),
|
||||
Some(b'd') => dht_parse_dict(input, index + 1),
|
||||
Some(byte) if byte.is_ascii_digit() => dht_parse_bytes(input, index),
|
||||
Some(_) => Err("invalid dht bencode value".to_owned()),
|
||||
None => Err("unexpected end of dht bencode input".to_owned()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses one DHT bencode integer starting at `index`.
|
||||
fn dht_parse_int(input: &[u8], index: usize) -> Result<(DhtBencodeValue, usize), String> {
|
||||
let mut cursor = index;
|
||||
while cursor < input.len() && input[cursor] != b'e' {
|
||||
cursor += 1;
|
||||
}
|
||||
if cursor >= input.len() {
|
||||
return Err("unterminated dht integer".to_owned());
|
||||
}
|
||||
let text = std::str::from_utf8(&input[index..cursor])
|
||||
.map_err(|_| "invalid dht integer bytes".to_owned())?;
|
||||
let value = text
|
||||
.parse::<i64>()
|
||||
.map_err(|_| "invalid dht integer value".to_owned())?;
|
||||
Ok((DhtBencodeValue::Int(value), cursor + 1))
|
||||
}
|
||||
|
||||
/// Parses one DHT bencode list starting at `index`.
|
||||
fn dht_parse_list(input: &[u8], index: usize) -> Result<(DhtBencodeValue, usize), String> {
|
||||
let mut values = Vec::new();
|
||||
let mut cursor = index;
|
||||
while cursor < input.len() {
|
||||
if input[cursor] == b'e' {
|
||||
return Ok((DhtBencodeValue::List(values), cursor + 1));
|
||||
}
|
||||
let (value, next) = dht_parse_value(input, cursor)?;
|
||||
values.push(value);
|
||||
cursor = next;
|
||||
}
|
||||
Err("unterminated dht list".to_owned())
|
||||
}
|
||||
|
||||
/// Parses one DHT bencode dictionary starting at `index`.
|
||||
fn dht_parse_dict(input: &[u8], index: usize) -> Result<(DhtBencodeValue, usize), String> {
|
||||
let mut map = BTreeMap::new();
|
||||
let mut cursor = index;
|
||||
while cursor < input.len() {
|
||||
if input[cursor] == b'e' {
|
||||
return Ok((DhtBencodeValue::Dict(map), cursor + 1));
|
||||
}
|
||||
let (key, key_end) = dht_parse_bytes_raw(input, cursor)?;
|
||||
let (value, value_end) = dht_parse_value(input, key_end)?;
|
||||
map.insert(key, value);
|
||||
cursor = value_end;
|
||||
}
|
||||
Err("unterminated dht dictionary".to_owned())
|
||||
}
|
||||
|
||||
/// Parses one DHT bencode byte string starting at `index`.
|
||||
fn dht_parse_bytes(input: &[u8], index: usize) -> Result<(DhtBencodeValue, usize), String> {
|
||||
let (bytes, next) = dht_parse_bytes_raw(input, index)?;
|
||||
Ok((DhtBencodeValue::Bytes(bytes), next))
|
||||
}
|
||||
|
||||
/// Parses one raw DHT bencode byte string and returns its bytes plus next index.
|
||||
fn dht_parse_bytes_raw(input: &[u8], index: usize) -> Result<(Vec<u8>, usize), String> {
|
||||
let mut cursor = index;
|
||||
while cursor < input.len() && input[cursor].is_ascii_digit() {
|
||||
cursor += 1;
|
||||
}
|
||||
if cursor == index || cursor >= input.len() || input[cursor] != b':' {
|
||||
return Err("invalid dht byte string".to_owned());
|
||||
}
|
||||
let length = std::str::from_utf8(&input[index..cursor])
|
||||
.map_err(|_| "invalid dht byte string length".to_owned())?
|
||||
.parse::<usize>()
|
||||
.map_err(|_| "invalid dht byte string length".to_owned())?;
|
||||
let start = cursor + 1;
|
||||
let end = start.saturating_add(length);
|
||||
if end > input.len() {
|
||||
return Err("truncated dht byte string".to_owned());
|
||||
}
|
||||
Ok((input[start..end].to_vec(), end))
|
||||
}
|
||||
|
||||
/// Looks up a raw byte-string field inside a DHT dictionary.
|
||||
pub(super) fn dht_dict_get_bytes(
|
||||
dict: &BTreeMap<Vec<u8>, DhtBencodeValue>,
|
||||
key: &[u8],
|
||||
) -> Option<Vec<u8>> {
|
||||
match dict.get(key) {
|
||||
Some(DhtBencodeValue::Bytes(bytes)) => Some(bytes.clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Looks up an integer field inside a DHT dictionary.
|
||||
pub(super) fn dht_dict_get_int(
|
||||
dict: &BTreeMap<Vec<u8>, DhtBencodeValue>,
|
||||
key: &[u8],
|
||||
) -> Option<i64> {
|
||||
match dict.get(key) {
|
||||
Some(DhtBencodeValue::Int(value)) => Some(*value),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Looks up a nested dictionary field inside a DHT dictionary.
|
||||
pub(super) fn dht_dict_get_dict<'a>(
|
||||
dict: &'a BTreeMap<Vec<u8>, DhtBencodeValue>,
|
||||
key: &[u8],
|
||||
) -> Option<&'a BTreeMap<Vec<u8>, DhtBencodeValue>> {
|
||||
match dict.get(key) {
|
||||
Some(DhtBencodeValue::Dict(value)) => Some(value),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Looks up a list field inside a DHT dictionary.
|
||||
pub(super) fn dht_dict_get_list(
|
||||
dict: &BTreeMap<Vec<u8>, DhtBencodeValue>,
|
||||
key: &[u8],
|
||||
) -> Option<Vec<DhtBencodeValue>> {
|
||||
match dict.get(key) {
|
||||
Some(DhtBencodeValue::List(values)) => Some(values.clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user