pull/5/head
parent
9693c990a2
commit
1dc7ce9a05
@ -0,0 +1,23 @@
|
||||
[package]
|
||||
name = "rust-dnscrypt-server"
|
||||
version = "0.1.0"
|
||||
authors = ["Frank Denis <github@pureftpd.org>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bincode = "1.1.4"
|
||||
byteorder = "1.3.2"
|
||||
clap = { version="2.33.0", features=["suggestions", "wrap_help", "nightly"] }
|
||||
derivative = "1.0.3"
|
||||
env_logger = "0.6.2"
|
||||
failure = "0.1.5"
|
||||
futures-preview = { version = "=0.3.0-alpha.18", features = ["compat", "async-await", "nightly", "io-compat", "cfg-target-has-atomic"] }
|
||||
jemallocator = "0.3.2"
|
||||
libsodium-sys="0.2.3"
|
||||
log = "0.4.8"
|
||||
tokio = "=0.2.0-alpha.4"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
panic = "abort"
|
||||
opt-level = 3
|
@ -1,2 +1,5 @@
|
||||
# rust-dnscrypt-server
|
||||
|
||||
A new DNSCrypt server proxy implementation in Rust.
|
||||
|
||||
# *** WIP - Nothing to see yet ***
|
||||
|
@ -0,0 +1,88 @@
|
||||
use crate::errors::*;
|
||||
|
||||
use libsodium_sys::*;
|
||||
use std::ptr;
|
||||
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Default)]
|
||||
pub struct Signature(
|
||||
#[derivative(Default(value = "[0u8; crypto_sign_BYTES as usize]"))]
|
||||
[u8; crypto_sign_BYTES as usize],
|
||||
);
|
||||
|
||||
impl Signature {
|
||||
pub fn as_bytes(&self) -> &[u8; crypto_sign_BYTES as usize] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: [u8; crypto_sign_BYTES as usize]) -> Self {
|
||||
Signature(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Default)]
|
||||
pub struct SignSK(
|
||||
#[derivative(Default(value = "[0u8; crypto_sign_SECRETKEYBYTES as usize]"))]
|
||||
[u8; crypto_sign_SECRETKEYBYTES as usize],
|
||||
);
|
||||
|
||||
impl SignSK {
|
||||
pub fn as_bytes(&self) -> &[u8; crypto_sign_SECRETKEYBYTES as usize] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: [u8; crypto_sign_SECRETKEYBYTES as usize]) -> Self {
|
||||
SignSK(bytes)
|
||||
}
|
||||
|
||||
pub fn sign(&self, bytes: &[u8]) -> Signature {
|
||||
let mut signature = Signature::default();
|
||||
let ret = unsafe {
|
||||
crypto_sign_detached(
|
||||
signature.0.as_mut_ptr(),
|
||||
ptr::null_mut(),
|
||||
bytes.as_ptr(),
|
||||
bytes.len() as _,
|
||||
self.as_bytes().as_ptr(),
|
||||
)
|
||||
};
|
||||
assert_eq!(ret, 0);
|
||||
signature
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SignPK([u8; crypto_sign_PUBLICKEYBYTES as usize]);
|
||||
|
||||
impl SignPK {
|
||||
pub fn as_bytes(&self) -> &[u8; crypto_sign_PUBLICKEYBYTES as usize] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: [u8; crypto_sign_PUBLICKEYBYTES as usize]) -> Self {
|
||||
SignPK(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Debug, Default)]
|
||||
pub struct SignKeyPair {
|
||||
#[derivative(Debug = "ignore")]
|
||||
pub sk: SignSK,
|
||||
pub pk: SignPK,
|
||||
}
|
||||
|
||||
impl SignKeyPair {
|
||||
pub fn new() -> Self {
|
||||
let mut kp = SignKeyPair::default();
|
||||
unsafe { crypto_sign_keypair(kp.pk.0.as_mut_ptr(), kp.sk.0.as_mut_ptr()) };
|
||||
kp
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init() -> Result<(), Error> {
|
||||
let res = unsafe { sodium_init() };
|
||||
ensure!(res >= 0, "Unable to initialize libsodium");
|
||||
Ok(())
|
||||
}
|
@ -0,0 +1,307 @@
|
||||
use crate::dnscrypt_certs::*;
|
||||
use crate::errors::*;
|
||||
|
||||
use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
|
||||
|
||||
const DNS_MAX_HOSTNAME_LEN: usize = 256;
|
||||
const DNS_MAX_INDIRECTIONS: usize = 16;
|
||||
const DNS_HEADER_SIZE: usize = 12;
|
||||
const DNS_OFFSET_FLAGS: usize = 2;
|
||||
const DNS_FLAGS_TC: u16 = 2u16 << 8;
|
||||
const DNS_FLAGS_QR: u16 = 128u16 << 8;
|
||||
const DNS_FLAGS_RA: u16 = 128;
|
||||
const DNS_MAX_PACKET_SIZE: usize = 65_533;
|
||||
const DNS_OFFSET_QUESTION: usize = DNS_HEADER_SIZE;
|
||||
const DNS_TYPE_OPT: u16 = 41;
|
||||
const DNS_TYPE_TXT: u16 = 16;
|
||||
const DNS_CLASS_INET: u16 = 1;
|
||||
|
||||
#[inline]
|
||||
fn qdcount(packet: &[u8]) -> u16 {
|
||||
BigEndian::read_u16(&packet[4..])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ancount(packet: &[u8]) -> u16 {
|
||||
BigEndian::read_u16(&packet[6..])
|
||||
}
|
||||
|
||||
fn ancount_inc(packet: &mut [u8]) -> Result<(), Error> {
|
||||
let mut ancount = ancount(packet);
|
||||
ensure!(ancount < 0xffff, "Too many answer records");
|
||||
ancount += 1;
|
||||
BigEndian::write_u16(&mut packet[6..], ancount);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn nscount(packet: &[u8]) -> u16 {
|
||||
BigEndian::read_u16(&packet[8..])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn arcount(packet: &[u8]) -> u16 {
|
||||
BigEndian::read_u16(&packet[10..])
|
||||
}
|
||||
|
||||
fn arcount_inc(packet: &mut [u8]) -> Result<(), Error> {
|
||||
let mut arcount = arcount(packet);
|
||||
ensure!(arcount < 0xffff, "Too many additional records");
|
||||
arcount += 1;
|
||||
BigEndian::write_u16(&mut packet[10..], arcount);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn authoritative_response(packet: &mut [u8]) {
|
||||
let current_flags = BigEndian::read_u16(&packet[DNS_OFFSET_FLAGS..]);
|
||||
BigEndian::write_u16(
|
||||
&mut packet[DNS_OFFSET_FLAGS..],
|
||||
current_flags | DNS_FLAGS_QR | DNS_FLAGS_RA,
|
||||
);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn truncate(packet: &mut [u8]) {
|
||||
let current_flags = BigEndian::read_u16(&packet[DNS_OFFSET_FLAGS..]);
|
||||
BigEndian::write_u16(
|
||||
&mut packet[DNS_OFFSET_FLAGS..],
|
||||
current_flags | DNS_FLAGS_TC | DNS_FLAGS_QR | DNS_FLAGS_RA,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn qname(packet: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
assert!(std::usize::MAX > 0xffff);
|
||||
ensure!(qdcount(packet) == 1, "Unexpected query count");
|
||||
let packet_len = packet.len();
|
||||
let mut offset = DNS_HEADER_SIZE;
|
||||
let mut qname = Vec::with_capacity(DNS_MAX_HOSTNAME_LEN);
|
||||
let mut indirections = 0;
|
||||
loop {
|
||||
ensure!(offset < packet_len, "Short packet");
|
||||
match packet[offset] as usize {
|
||||
label_len if label_len & 0xc0 == 0xc0 => {
|
||||
ensure!(packet_len - offset > 1, "Short packet");
|
||||
let new_offset = (BigEndian::read_u16(&packet[offset..]) & 0x3fff) as usize;
|
||||
indirections += 1;
|
||||
ensure!(
|
||||
new_offset >= DNS_HEADER_SIZE
|
||||
&& new_offset != offset
|
||||
&& indirections < DNS_MAX_INDIRECTIONS,
|
||||
"Too many indirections"
|
||||
);
|
||||
offset = new_offset;
|
||||
}
|
||||
0 => {
|
||||
if qname.is_empty() {
|
||||
qname.push(b'.')
|
||||
}
|
||||
break;
|
||||
}
|
||||
label_len => {
|
||||
ensure!(packet_len - offset > 1, "Short packet");
|
||||
offset += 1;
|
||||
ensure!(packet_len - offset > label_len, "Short packet");
|
||||
if !qname.is_empty() {
|
||||
qname.push(b'.')
|
||||
}
|
||||
ensure!(
|
||||
qname.len() < DNS_MAX_HOSTNAME_LEN - label_len,
|
||||
"Name too long"
|
||||
);
|
||||
qname.extend_from_slice(&packet[offset..offset + label_len]);
|
||||
offset += label_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(qname)
|
||||
}
|
||||
|
||||
fn skip_name(packet: &[u8], offset: usize) -> Result<usize, Error> {
|
||||
let packet_len = packet.len();
|
||||
ensure!(offset < packet_len - 1, "Short packet");
|
||||
let mut qname_len: usize = 0;
|
||||
let mut offset = offset;
|
||||
loop {
|
||||
let label_len = match packet[offset] as usize {
|
||||
label_len if label_len & 0xc0 == 0xc0 => {
|
||||
ensure!(packet_len - offset >= 2, "Incomplete offset");
|
||||
offset += 2;
|
||||
break;
|
||||
}
|
||||
label_len => label_len,
|
||||
} as usize;
|
||||
ensure!(
|
||||
packet_len - offset - 1 > label_len,
|
||||
"Malformed packet with an out-of-bounds name"
|
||||
);
|
||||
qname_len += label_len + 1;
|
||||
ensure!(qname_len <= DNS_MAX_HOSTNAME_LEN, "Name too long");
|
||||
offset += label_len + 1;
|
||||
if label_len == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(offset)
|
||||
}
|
||||
|
||||
fn traverse_rrs<F: FnMut(usize) -> Result<(), Error>>(
|
||||
packet: &[u8],
|
||||
mut offset: usize,
|
||||
rrcount: u16,
|
||||
mut cb: F,
|
||||
) -> Result<usize, Error> {
|
||||
let packet_len = packet.len();
|
||||
for _ in 0..rrcount {
|
||||
offset = skip_name(packet, offset)?;
|
||||
ensure!(packet_len - offset >= 10, "Short packet");
|
||||
cb(offset)?;
|
||||
let rdlen = BigEndian::read_u16(&packet[offset + 8..]) as usize;
|
||||
offset += 10;
|
||||
ensure!(
|
||||
packet_len - offset <= rdlen,
|
||||
"Record length would exceed packet length"
|
||||
);
|
||||
offset += rdlen;
|
||||
}
|
||||
Ok(offset)
|
||||
}
|
||||
|
||||
fn traverse_rrs_mut<F: FnMut(&mut [u8], usize) -> Result<(), Error>>(
|
||||
packet: &mut [u8],
|
||||
mut offset: usize,
|
||||
rrcount: u16,
|
||||
mut cb: F,
|
||||
) -> Result<usize, Error> {
|
||||
let packet_len = packet.len();
|
||||
for _ in 0..rrcount {
|
||||
offset = skip_name(packet, offset)?;
|
||||
ensure!(packet_len - offset >= 10, "Short packet");
|
||||
cb(packet, offset)?;
|
||||
let rdlen = BigEndian::read_u16(&packet[offset + 8..]) as usize;
|
||||
offset += 10;
|
||||
ensure!(
|
||||
packet_len - offset <= rdlen,
|
||||
"Record length would exceed packet length"
|
||||
);
|
||||
offset += rdlen;
|
||||
}
|
||||
Ok(offset)
|
||||
}
|
||||
|
||||
pub fn min_ttl(packet: &[u8], min_ttl: u32, max_ttl: u32, failure_ttl: u32) -> Result<u32, Error> {
|
||||
ensure!(qdcount(packet) == 1, "Unsupported number of questions");
|
||||
let packet_len = packet.len();
|
||||
ensure!(packet_len > DNS_OFFSET_QUESTION, "Short packet");
|
||||
ensure!(packet_len <= DNS_MAX_PACKET_SIZE, "Large packet");
|
||||
let mut offset = skip_name(packet, DNS_OFFSET_QUESTION)?;
|
||||
assert!(offset > DNS_OFFSET_QUESTION);
|
||||
ensure!(packet_len - offset > 4, "Short packet");
|
||||
offset += 4;
|
||||
let (ancount, nscount, arcount) = (ancount(packet), nscount(packet), arcount(packet));
|
||||
let rrcount = ancount + nscount + arcount;
|
||||
let mut found_min_ttl = if rrcount > 0 { max_ttl } else { failure_ttl };
|
||||
|
||||
offset = traverse_rrs(packet, offset, rrcount, |offset| {
|
||||
let qtype = BigEndian::read_u16(&packet[offset..]);
|
||||
let ttl = BigEndian::read_u32(&packet[offset + 4..]);
|
||||
if qtype != DNS_TYPE_OPT && ttl < found_min_ttl {
|
||||
found_min_ttl = ttl;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
if found_min_ttl < min_ttl {
|
||||
found_min_ttl = min_ttl;
|
||||
}
|
||||
ensure!(packet_len == offset, "Garbage after packet");
|
||||
Ok(found_min_ttl)
|
||||
}
|
||||
|
||||
fn add_edns_section(packet: &mut Vec<u8>, max_payload_size: u16) -> Result<(), Error> {
|
||||
let opt_rr: [u8; 11] = [
|
||||
0,
|
||||
(DNS_TYPE_OPT >> 8) as u8,
|
||||
DNS_TYPE_OPT as u8,
|
||||
(max_payload_size >> 8) as u8,
|
||||
max_payload_size as u8,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
];
|
||||
ensure!(
|
||||
DNS_MAX_PACKET_SIZE - packet.len() >= opt_rr.len(),
|
||||
"Packet would be too large to add a new record"
|
||||
);
|
||||
arcount_inc(packet)?;
|
||||
packet.extend(&opt_rr);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_edns_max_payload_size(packet: &mut Vec<u8>, max_payload_size: u16) -> Result<(), Error> {
|
||||
ensure!(qdcount(packet) == 1, "Unsupported number of questions");
|
||||
let packet_len = packet.len();
|
||||
ensure!(packet_len > DNS_OFFSET_QUESTION, "Short packet");
|
||||
ensure!(packet_len <= DNS_MAX_PACKET_SIZE, "Large packet");
|
||||
|
||||
let mut offset = skip_name(packet, DNS_OFFSET_QUESTION)?;
|
||||
assert!(offset > DNS_OFFSET_QUESTION);
|
||||
ensure!(packet_len - offset <= 4, "Short packet");
|
||||
offset += 4;
|
||||
let (ancount, nscount, arcount) = (ancount(packet), nscount(packet), arcount(packet));
|
||||
offset = traverse_rrs(packet, offset, ancount + nscount, |_offset| Ok(()))?;
|
||||
let mut edns_payload_set = false;
|
||||
|
||||
traverse_rrs_mut(packet, offset, arcount, |packet, offset| {
|
||||
let qtype = BigEndian::read_u16(&packet[offset..]);
|
||||
if qtype == DNS_TYPE_OPT {
|
||||
ensure!(!edns_payload_set, "Duplicate OPT RR found");
|
||||
BigEndian::write_u16(&mut packet[offset + 2..], max_payload_size);
|
||||
edns_payload_set = true;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
if edns_payload_set {
|
||||
return Ok(());
|
||||
}
|
||||
add_edns_section(packet, max_payload_size)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn serve_certificates<'t>(
|
||||
client_packet: &[u8],
|
||||
expected_qname: &str,
|
||||
dnscrypt_certs: impl IntoIterator<Item = &'t DNSCryptCert>,
|
||||
) -> Result<Option<Vec<u8>>, Error> {
|
||||
let offset = skip_name(client_packet, DNS_HEADER_SIZE)?;
|
||||
ensure!(client_packet.len() - offset >= 4, "Short packet");
|
||||
let qtype = BigEndian::read_u16(&client_packet[offset..]);
|
||||
let qclass = BigEndian::read_u16(&client_packet[offset + 2..]);
|
||||
if qtype != DNS_TYPE_TXT || qclass != DNS_CLASS_INET {
|
||||
return Ok(None);
|
||||
}
|
||||
let qname_v = qname(&client_packet)?;
|
||||
let qname = std::str::from_utf8(&qname_v)?;
|
||||
if !qname.eq_ignore_ascii_case(expected_qname) {
|
||||
return Ok(None);
|
||||
}
|
||||
let mut packet = (&client_packet[..offset + 4]).to_vec();
|
||||
authoritative_response(&mut packet);
|
||||
|
||||
for dnscrypt_cert in dnscrypt_certs {
|
||||
let cert_bin = dnscrypt_cert.as_bytes();
|
||||
ensure!(cert_bin.len() <= 0xff, "Certificate too long");
|
||||
ancount_inc(&mut packet)?;
|
||||
packet.write_u16::<BigEndian>(0xc000 + DNS_HEADER_SIZE as u16)?;
|
||||
packet.write_u16::<BigEndian>(DNS_TYPE_TXT)?;
|
||||
packet.write_u16::<BigEndian>(DNS_CLASS_INET)?;
|
||||
packet.write_u32::<BigEndian>(28800)?;
|
||||
packet.write_u16::<BigEndian>(1 + cert_bin.len() as u16)?;
|
||||
packet.write_u8(cert_bin.len() as u8)?;
|
||||
packet.extend_from_slice(&cert_bin[..]);
|
||||
ensure!(packet.len() < DNS_MAX_PACKET_SIZE, "Packet too large");
|
||||
}
|
||||
Ok(Some(packet))
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
use crate::crypto::*;
|
||||
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
use std::mem;
|
||||
use std::slice;
|
||||
use std::time::SystemTime;
|
||||
|
||||
fn now() -> u32 {
|
||||
SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs() as u32
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[repr(C, packed)]
|
||||
pub struct DNSCryptCertInner {
|
||||
resolver_pk: [u8; 32],
|
||||
client_magic: [u8; 8],
|
||||
serial: [u8; 8],
|
||||
ts_start: [u8; 4],
|
||||
ts_end: [u8; 4],
|
||||
}
|
||||
|
||||
impl DNSCryptCertInner {
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
unsafe { slice::from_raw_parts(self as *const _ as *const u8, mem::size_of_val(self)) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Debug, Default)]
|
||||
#[repr(C, packed)]
|
||||
pub struct DNSCryptCert {
|
||||
cert_magic: [u8; 4],
|
||||
es_version: [u8; 2],
|
||||
minor_version: [u8; 2],
|
||||
#[derivative(Debug = "ignore", Default(value = "[0u8; 64]"))]
|
||||
signature: [u8; 64],
|
||||
inner: DNSCryptCertInner,
|
||||
}
|
||||
|
||||
impl DNSCryptCert {
|
||||
pub fn new(resolver_kp: &SignKeyPair) -> Self {
|
||||
let ts_start = now();
|
||||
let ts_end = ts_start + 86400;
|
||||
|
||||
let mut dnscrypt_cert = DNSCryptCert::default();
|
||||
|
||||
let dnscrypt_cert_inner = &mut dnscrypt_cert.inner;
|
||||
dnscrypt_cert_inner
|
||||
.resolver_pk
|
||||
.copy_from_slice(resolver_kp.pk.as_bytes());
|
||||
dnscrypt_cert_inner
|
||||
.client_magic
|
||||
.copy_from_slice(&dnscrypt_cert_inner.resolver_pk[..8]);
|
||||
BigEndian::write_u64(&mut dnscrypt_cert_inner.serial, 1);
|
||||
BigEndian::write_u32(&mut dnscrypt_cert_inner.ts_start, ts_start);
|
||||
BigEndian::write_u32(&mut dnscrypt_cert_inner.ts_end, ts_end);
|
||||
|
||||
BigEndian::write_u32(&mut dnscrypt_cert.cert_magic, 0x44_4e_53_43);
|
||||
BigEndian::write_u16(&mut dnscrypt_cert.es_version, 2);
|
||||
BigEndian::write_u16(&mut dnscrypt_cert.minor_version, 0);
|
||||
|
||||
dnscrypt_cert.signature.copy_from_slice(
|
||||
resolver_kp
|
||||
.sk
|
||||
.sign(dnscrypt_cert_inner.as_bytes())
|
||||
.as_bytes(),
|
||||
);
|
||||
dnscrypt_cert
|
||||
}
|
||||
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
unsafe { slice::from_raw_parts(self as *const _ as *const u8, mem::size_of_val(self)) }
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
pub use failure::{bail, ensure, Error};
|
||||
use std::io;
|
||||
use std::net::AddrParseError;
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum ProxyError {
|
||||
#[fail(display = "Internal error: [{}]", _0)]
|
||||
InternalError(String),
|
||||
#[fail(display = "I/O error: [{}]", _0)]
|
||||
Io(#[cause] io::Error),
|
||||
#[fail(display = "Unable to parse address: [{}]", _0)]
|
||||
AddrParseError(#[cause] AddrParseError),
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
use crate::crypto::*;
|
||||
use crate::dnscrypt_certs::*;
|
||||
|
||||
use std::sync::Arc;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Globals {
|
||||
pub runtime: Arc<Runtime>,
|
||||
pub resolver_kp: SignKeyPair,
|
||||
pub dnscrypt_certs: Vec<DNSCryptCert>,
|
||||
}
|
||||
|
||||
impl Globals {
|
||||
pub fn new(
|
||||
runtime: Arc<Runtime>,
|
||||
resolver_kp: SignKeyPair,
|
||||
dnscrypt_certs: Vec<DNSCryptCert>,
|
||||
) -> Self {
|
||||
Globals {
|
||||
runtime,
|
||||
resolver_kp,
|
||||
dnscrypt_certs,
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
#![allow(clippy::assertions_on_constants)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(unused_variables)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
|
||||
|
||||
#[macro_use]
|
||||
extern crate clap;
|
||||
#[macro_use]
|
||||
extern crate derivative;
|
||||
#[macro_use]
|
||||
extern crate failure;
|
||||
|
||||
mod config;
|
||||
mod crypto;
|
||||
mod dns;
|
||||
mod dnscrypt_certs;
|
||||
mod errors;
|
||||
mod globals;
|
||||
|
||||
use crypto::*;
|
||||
use dns::*;
|
||||
use dnscrypt_certs::*;
|
||||
use errors::*;
|
||||
use globals::*;
|
||||
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
use clap::Arg;
|
||||
use failure::{bail, ensure};
|
||||
use futures::prelude::*;
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use tokio::net::{TcpListener, UdpSocket};
|
||||
use tokio::prelude::*;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
const DNSCRYPT_QUERY_MIN_SIZE: usize = 12;
|
||||
const DNSCRYPT_QUERY_MAX_SIZE: usize = 512;
|
||||
|
||||
async fn tcp_acceptor(globals: Arc<Globals>, tcp_listener: TcpListener) -> Result<(), Error> {
|
||||
let mut tcp_listener = tcp_listener.incoming();
|
||||
while let Some(client) = tcp_listener.next().await {
|
||||
let mut client = match client {
|
||||
Ok(client) => client,
|
||||
Err(_) => continue,
|
||||
};
|
||||
let mut binlen = [0u8, 0];
|
||||
client.read_exact(&mut binlen).await?;
|
||||
let packet_len = BigEndian::read_u16(&binlen) as usize;
|
||||
ensure!(
|
||||
(DNSCRYPT_QUERY_MIN_SIZE..=DNSCRYPT_QUERY_MAX_SIZE).contains(&packet_len),
|
||||
"Unexpected query size"
|
||||
);
|
||||
let mut packet = vec![0u8; packet_len];
|
||||
client.read_exact(&mut packet).await?;
|
||||
dbg!(packet);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn udp_acceptor(globals: Arc<Globals>, mut udp_listener: UdpSocket) -> Result<(), Error> {
|
||||
loop {
|
||||
let mut packet = vec![0u8; DNSCRYPT_QUERY_MAX_SIZE];
|
||||
let (packet_len, client_addr) = udp_listener.recv_from(&mut packet).await?;
|
||||
dbg!(&packet);
|
||||
let mut packet = &mut packet[..packet_len];
|
||||
if let Some(synth_packet) =
|
||||
serve_certificates(&packet, "2.dnscrypt.example.com", &globals.dnscrypt_certs)?
|
||||
{
|
||||
let _ = udp_listener.send_to(&synth_packet, client_addr).await;
|
||||
continue;
|
||||
}
|
||||
truncate(&mut packet);
|
||||
let _ = udp_listener.send_to(&packet, client_addr).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn start(globals: Arc<Globals>, runtime: Arc<Runtime>) -> Result<(), Error> {
|
||||
let socket_addr: SocketAddr = "127.0.0.1:5300".parse()?;
|
||||
let tcp_listener = TcpListener::bind(&socket_addr).await?;
|
||||
let udp_listener = UdpSocket::bind(&socket_addr).await?;
|
||||
runtime.spawn(tcp_acceptor(globals.clone(), tcp_listener).map(|_| {}));
|
||||
runtime.spawn(udp_acceptor(globals.clone(), udp_listener).map(|_| {}));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
env_logger::init();
|
||||
crypto::init()?;
|
||||
|
||||
let _matches = app_from_crate!().get_matches();
|
||||
|
||||
let resolver_kp = SignKeyPair::new();
|
||||
let dnscrypt_cert = DNSCryptCert::new(&resolver_kp);
|
||||
|
||||
let runtime = Arc::new(Runtime::new()?);
|
||||
let globals = Arc::new(Globals::new(
|
||||
runtime.clone(),
|
||||
resolver_kp,
|
||||
vec![dnscrypt_cert],
|
||||
));
|
||||
runtime.spawn(start(globals, runtime.clone()).map(|_| ()));
|
||||
runtime.block_on(future::pending::<()>());
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue