├── .gitignore ├── Cargo.toml ├── LICENSE ├── PKGBUILD ├── README.md ├── build.sh ├── dnrs.install ├── dnrs.service └── src ├── language ├── lexer │ ├── lex.rs │ ├── mod.rs │ └── token.rs ├── mod.rs └── parser │ ├── mod.rs │ └── parser.rs ├── main.rs └── structs ├── header.rs ├── mod.rs ├── question.rs ├── request.rs └── response.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | dns -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dnrs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1", features = ["net", "rt", "rt-multi-thread", "macros"] } 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 VeroniDeev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: Veroni zoubheir@gmail.com 2 | 3 | pkgname="dnrs" 4 | pkgver="1.1.3" 5 | pkgrel=1 6 | pkgdesc="Light dns server written in rust" 7 | arch=('x86_64') 8 | url="https://github.com/VeroniDeev/dnrs" 9 | license=('MIT') 10 | depends=('glibc') 11 | makedepends=('cargo') 12 | source=("$pkgname-$pkgver.tar.gz::https://github.com/VeroniDeev/$pkgname/archive/v$pkgver.tar.gz") 13 | sha256sums=('SKIP') 14 | install=dnrs.install 15 | 16 | build() { 17 | cd "$srcdir/$pkgname-$pkgver" 18 | cargo build --release 19 | } 20 | 21 | package() { 22 | install -d "$pkgdir/etc/dnrs/dns" 23 | 24 | cd "$srcdir/$pkgname-$pkgver" 25 | install -Dm755 "target/release/$pkgname" "$pkgdir/etc/dnrs/$pkgname" 26 | install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" 27 | install -Dm644 README.md "$pkgdir/usr/share/doc/$pkgname/README.md" 28 | 29 | install -Dm644 dnrs.service "$pkgdir/usr/lib/systemd/system/dnrs.service" 30 | } 31 | 32 | post_install() { 33 | echo "Don't forget to configure your DNS server by modifying the files in /etc/dnrs/dns" 34 | } 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dnrs 2 | #### Light dns server written in rust 3 | **Dnrs** is a dns server written in rust that aims to modernize the approach to writing configuration files and to be lightweight. 4 | 5 | **Dnrs** is a mix between dns and rs from rust. 6 | 7 | ## Badges 8 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/VeroniDeev/dnrs/blob/main/LICENSE) 9 | 10 | # Table of contents 11 | 12 | 1. [Requirement](#requirement) 13 | 2. [Installation](#installation) 14 | 3. [Setup](#setup) 15 | 4. [Documentation](#documentation) 16 | 17 | # Requirement 18 | 19 | For **dnrs** to work perfectly, you just need to install rust and have a Linux system. 20 | 21 | # Installation 22 | 23 | ### For the moment, dnrs installation is only available on Arch Linux with the AUR package. Here's how to proceed: 24 | 25 | Updating the aur repository 26 | ```sh 27 | yay -Syy 28 | ``` 29 | 30 | Install **dnrs** 31 | ```sh 32 | yay -S dnrs 33 | ``` 34 | 35 | ### For all linux systems other than Arch, here's how to configure it easily 36 | 37 | Clone the repository 38 | ```sh 39 | git clone https://github.com/VeroniDeev/dnrs 40 | ``` 41 | 42 | Go to the folder 43 | ```sh 44 | cd dnrs 45 | ``` 46 | 47 | Set build.sh as executable then run 48 | ```sh 49 | chmod +x config.sh && ./build.sh 50 | ``` 51 | 52 | # Setup 53 | 54 | ### Now before running dnrs you need to configure the systemd 55 | 56 | Enable dnrs.service 57 | ```sh 58 | sudo systemctl enable dnrs.service 59 | ``` 60 | 61 | Start dnrs.service 62 | ```sh 63 | sudo systemctl start dnrs.service 64 | ``` 65 | 66 | Check the status 67 | ```sh 68 | sudo systemctl status dnrs.service 69 | ``` 70 | If you see an error, don't hesitate to open an [issue](https://github.com/VeroniDeev/dnrs/issues) 71 | 72 | # Documentation 73 | You can find the documentation at this [link](https://dnrs.zoubheir.com). 74 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | echo "Creating dnrs user and group..." 2 | getent group dnrs >/dev/null 2>&1 || groupadd -r dnrs 3 | getent passwd dnrs >/dev/null 2>&1 || useradd -r -g dnrs -d /etc/dnrs -s /bin/false -c "DNRS user" dnrs 4 | echo "User and group dnrs created." 5 | 6 | cargo build --release 7 | 8 | install -d "/etc/dnrs/dns" 9 | 10 | install -Dm755 "target/release/dnrs" "/etc/dnrs/dnrs" 11 | install -Dm644 LICENSE "/usr/share/licenses/dnrs/LICENSE" 12 | install -Dm644 README.md "/usr/share/doc/dnrs/README.md" 13 | 14 | install -Dm644 dnrs.service "/usr/lib/systemd/system/dnrs.service" -------------------------------------------------------------------------------- /dnrs.install: -------------------------------------------------------------------------------- 1 | post_install() { 2 | echo "Creating dnrs user and group..." 3 | getent group dnrs >/dev/null 2>&1 || groupadd -r dnrs 4 | getent passwd dnrs >/dev/null 2>&1 || useradd -r -g dnrs -d /etc/dnrs -s /bin/false -c "DNRS user" dnrs 5 | echo "User and group dnrs created." 6 | } 7 | 8 | pre_remove() { 9 | echo "Removing dnrs user and group..." 10 | userdel dnrs >/dev/null 2>&1 || true 11 | groupdel dnrs >/dev/null 2>&1 || true 12 | echo "User and group dnrs removed." 13 | } 14 | 15 | post_upgrade() { 16 | post_install 17 | } 18 | -------------------------------------------------------------------------------- /dnrs.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Light dns server written in rust 3 | After=network.target 4 | 5 | [Service] 6 | ExecStart=/etc/dnrs/dnrs 7 | Restart=on-failure 8 | User=dnrs 9 | Group=dnrs 10 | AmbientCapabilities=CAP_NET_BIND_SERVICE 11 | 12 | [Install] 13 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /src/language/lexer/lex.rs: -------------------------------------------------------------------------------- 1 | use crate::structs::question::Qtype; 2 | 3 | use super::token::Token; 4 | 5 | pub struct Lexer { 6 | line: usize, 7 | column: usize, 8 | pub tokens: Vec, 9 | pub source: String, 10 | } 11 | 12 | impl Lexer { 13 | pub fn new(source: String) -> Self { 14 | Self { 15 | line: 1, 16 | column: 1, 17 | tokens: Vec::new(), 18 | source, 19 | } 20 | } 21 | 22 | pub fn lexer(&mut self) { 23 | let mut token: String = String::new(); 24 | let mut openbrace: bool = false; 25 | 26 | for c in self.source.chars() { 27 | let char: String = c.to_string(); 28 | self.column += 1; 29 | 30 | if openbrace && char != "\"" { 31 | token.push(c); 32 | continue; 33 | } else if openbrace && char == "\"" { 34 | self.tokens.push(Token::ValueString(token.clone())); 35 | self.tokens.push(Token::Guillemet); 36 | openbrace = false; 37 | token = String::new(); 38 | continue; 39 | } 40 | 41 | match char.as_str() { 42 | ":" => { 43 | if !token.is_empty() { 44 | self.tokens.push(Token::Identifier(token)); 45 | } 46 | self.tokens.push(Token::Colons); 47 | token = String::new(); 48 | } 49 | "=" => { 50 | self.tokens.push(Token::Equals); 51 | token = String::new(); 52 | } 53 | "{" => self.tokens.push(Token::OpenBrace), 54 | "}" => self.tokens.push(Token::CloseBrace), 55 | "\"" => { 56 | self.tokens.push(Token::Guillemet); 57 | openbrace = true; 58 | continue; 59 | } 60 | " " => { 61 | if !token.is_empty() { 62 | let token_type = self.check_type(token.clone()); 63 | self.tokens.push(token_type); 64 | } 65 | token = String::new(); 66 | } 67 | "\t" => {} 68 | "\n" => { 69 | if !token.is_empty() { 70 | let token_type = self.check_type(token.clone()); 71 | self.tokens.push(token_type); 72 | } 73 | token = String::new(); 74 | self.column = 1; 75 | self.line += 1; 76 | } 77 | _ => token.push(c), 78 | } 79 | 80 | match token.as_str() { 81 | "domain" => { 82 | self.tokens.push(Token::Identifier(token)); 83 | token = String::new(); 84 | } 85 | "ttl" => { 86 | self.tokens.push(Token::Identifier(token)); 87 | token = String::new(); 88 | } 89 | "type" => { 90 | self.tokens.push(Token::Identifier(token)); 91 | token = String::new(); 92 | } 93 | "subdomain" => { 94 | self.tokens.push(Token::Identifier(token)); 95 | token = String::new(); 96 | } 97 | "ip" => { 98 | self.tokens.push(Token::Identifier(token)); 99 | token = String::new(); 100 | } 101 | _ => {} 102 | } 103 | } 104 | } 105 | 106 | fn check_type(&self, token: String) -> Token { 107 | if let Ok(val) = token.parse::() { 108 | return Token::ValueInt(val); 109 | } else { 110 | return Token::Qtype(Qtype::with_string(token)); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/language/lexer/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod lex; 2 | pub mod token; 3 | -------------------------------------------------------------------------------- /src/language/lexer/token.rs: -------------------------------------------------------------------------------- 1 | use crate::structs::question::Qtype; 2 | 3 | #[derive(Debug, PartialEq, Clone)] 4 | pub enum Token { 5 | Identifier(String), 6 | Equals, 7 | OpenBrace, 8 | Colons, 9 | CloseBrace, 10 | Guillemet, 11 | ValueString(String), 12 | ValueInt(i32), 13 | Qtype(Qtype), 14 | } 15 | 16 | impl Token { 17 | pub fn as_identifier(&self) -> Option { 18 | match self { 19 | Self::Identifier(value) => return Some(value.clone()), 20 | _ => None, 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/language/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::BTreeMap, 3 | fs::{self, ReadDir}, 4 | }; 5 | 6 | use self::{ 7 | lexer::lex::Lexer, 8 | parser::parser::{Literal, ParseError, Parser}, 9 | }; 10 | 11 | pub mod lexer; 12 | pub mod parser; 13 | 14 | fn read_file(path: String) -> String { 15 | let source: String = fs::read_to_string(path).unwrap(); 16 | 17 | return source; 18 | } 19 | 20 | pub fn file_finder(domain: String) -> Option { 21 | let paths: ReadDir = fs::read_dir("/etc/dnrs/dns").unwrap(); 22 | 23 | for path in paths { 24 | let path_string: String = path.unwrap().path().display().to_string(); 25 | 26 | let source: String = read_file(path_string); 27 | 28 | if let Some(_) = source.find(&domain) { 29 | return Some(source); 30 | } 31 | } 32 | 33 | return None; 34 | } 35 | 36 | pub fn analyse(source: String) -> Result, ParseError> { 37 | let mut lexer: Lexer = Lexer::new(source); 38 | lexer.lexer(); 39 | 40 | let mut parser: Parser = Parser::new(lexer.tokens.clone()); 41 | 42 | match parser.parser() { 43 | Ok(_) => Ok(parser.properties), 44 | Err(err) => Err(err), 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/language/parser/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod parser; 2 | -------------------------------------------------------------------------------- /src/language/parser/parser.rs: -------------------------------------------------------------------------------- 1 | use crate::{language::lexer::token::Token, structs::question::Qtype}; 2 | use std::collections::BTreeMap; 3 | 4 | #[derive(Debug)] 5 | pub enum ParseError { 6 | UnexpectedToken(String), 7 | UnexpectedEndOfInput, 8 | InvalidLiteral(String), 9 | MissingToken(String), 10 | InvalidSyntax(String), 11 | } 12 | 13 | #[derive(Debug, Clone)] 14 | pub enum Literal { 15 | Int(i32), 16 | Str(String), 17 | Qtype(Qtype), 18 | Object(Box>), 19 | } 20 | 21 | impl Literal { 22 | pub fn as_int(&self) -> Option { 23 | match self { 24 | Literal::Int(value) => return Some(value.clone()), 25 | _ => return None, 26 | } 27 | } 28 | 29 | pub fn as_str(&self) -> Option { 30 | match self { 31 | Literal::Str(value) => return Some(value.clone()), 32 | _ => return None, 33 | } 34 | } 35 | 36 | pub fn as_qtype(&self) -> Option { 37 | match self { 38 | Literal::Qtype(value) => return Some(value.clone()), 39 | _ => return None, 40 | } 41 | } 42 | 43 | pub fn as_object(&self) -> Option>> { 44 | match self { 45 | Literal::Object(value) => return Some(value.clone()), 46 | _ => return None, 47 | } 48 | } 49 | } 50 | 51 | pub struct Parser { 52 | index: usize, 53 | tokens: Vec, 54 | pub properties: BTreeMap, 55 | } 56 | 57 | impl Parser { 58 | pub fn new(tokens: Vec) -> Self { 59 | Self { 60 | index: 0, 61 | tokens, 62 | properties: BTreeMap::new(), 63 | } 64 | } 65 | 66 | pub fn parser(&mut self) -> Result<(), ParseError> { 67 | while self.index < self.tokens.len() { 68 | match self.parse_statement() { 69 | Ok(_) => {} 70 | Err(err) => return Err(err), 71 | } 72 | } 73 | Ok(()) 74 | } 75 | 76 | fn parse_statement(&mut self) -> Result<(), ParseError> { 77 | match self.tokens[self.index] { 78 | Token::Identifier(_) => match self.parse_expression() { 79 | Ok(_) => {} 80 | Err(err) => return Err(err), 81 | }, 82 | _ => { 83 | return Err(ParseError::MissingToken(String::from( 84 | "We are exept to receive a name", 85 | ))) 86 | } 87 | } 88 | self.index += 1; 89 | Ok(()) 90 | } 91 | 92 | fn parse_expression(&mut self) -> Result<(), ParseError> { 93 | let identifier: String = self.tokens[self.index].as_identifier().unwrap(); 94 | self.index += 1; 95 | 96 | match self.tokens[self.index] { 97 | Token::Equals | Token::Colons => { 98 | self.index += 1; 99 | } 100 | _ => { 101 | return Err(ParseError::MissingToken(String::from( 102 | "We are except to receive a equal or colons", 103 | ))) 104 | } 105 | } 106 | 107 | match &self.tokens[self.index] { 108 | Token::ValueInt(value) => { 109 | self.properties 110 | .insert(identifier, Literal::Int(value.clone())); 111 | } 112 | 113 | Token::Qtype(value) => { 114 | self.properties 115 | .insert(identifier, Literal::Qtype(value.clone())); 116 | } 117 | 118 | Token::Guillemet => { 119 | self.index += 1; 120 | 121 | match &self.tokens[self.index] { 122 | Token::ValueString(value) => { 123 | self.properties 124 | .insert(identifier, Literal::Str(value.clone())); 125 | 126 | self.index += 1; 127 | } 128 | _ => { 129 | return Err(ParseError::InvalidLiteral(String::from( 130 | "We are expect to receive a string", 131 | ))) 132 | } 133 | } 134 | 135 | if self.tokens[self.index] != Token::Guillemet {} 136 | } 137 | 138 | Token::OpenBrace => match self.parse_block() { 139 | Ok(object) => { 140 | self.properties 141 | .insert(identifier, Literal::Object(Box::new(object))); 142 | } 143 | Err(err) => return Err(err), 144 | }, 145 | 146 | _ => { 147 | return Err(ParseError::UnexpectedToken(String::from( 148 | "Unexpected value", 149 | ))) 150 | } 151 | } 152 | 153 | Ok(()) 154 | } 155 | 156 | fn parse_block(&mut self) -> Result, ParseError> { 157 | let mut identifier: String = String::new(); 158 | self.index += 1; 159 | let mut literal: BTreeMap = BTreeMap::new(); 160 | let mut is_brace_closed = false; 161 | 162 | while let Some(token) = self.tokens.get(self.index) { 163 | match token { 164 | Token::OpenBrace => match self.parse_block() { 165 | Ok(object) => { 166 | literal.insert(identifier.clone(), Literal::Object(Box::new(object))); 167 | } 168 | Err(_) => { 169 | return Err(ParseError::InvalidSyntax(String::from("Invalid syntax"))); 170 | } 171 | }, 172 | 173 | Token::Identifier(value) => { 174 | identifier = value.clone(); 175 | } 176 | 177 | Token::Colons => {} 178 | 179 | Token::ValueInt(value) => { 180 | literal.insert(identifier.clone(), Literal::Int(value.clone())); 181 | } 182 | 183 | Token::Qtype(value) => { 184 | literal.insert(identifier.clone(), Literal::Qtype(value.clone())); 185 | } 186 | 187 | Token::CloseBrace => { 188 | is_brace_closed = true; 189 | break; 190 | } 191 | 192 | _ => {} 193 | } 194 | 195 | self.index += 1; 196 | } 197 | 198 | if is_brace_closed { 199 | Ok(literal) 200 | } else { 201 | Err(ParseError::MissingToken(String::from( 202 | "We are expecting a closing brace for the object", 203 | ))) 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use structs::{request::Request, response::Response}; 2 | use tokio::net::UdpSocket; 3 | 4 | pub mod language; 5 | pub mod structs; 6 | 7 | #[tokio::main] 8 | async fn main() { 9 | let server: UdpSocket = UdpSocket::bind("0.0.0.0:53").await.unwrap(); 10 | let mut buf: [u8; 1024] = [0; 1024]; 11 | 12 | loop { 13 | let (len, addr) = server.recv_from(&mut buf).await.unwrap(); 14 | 15 | let mut data: Vec = buf.to_vec(); 16 | data.resize(len, 0); 17 | 18 | let mut request: Request = Request::new(); 19 | request.with_bytes(data); 20 | 21 | let mut response: Response = Response::from(request); 22 | let data_response: Vec = response.create_byte_response(); 23 | 24 | server.send_to(&data_response, addr).await.unwrap(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/structs/header.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub struct Header { 3 | pub id: u16, 4 | pub flags: u16, 5 | pub qdcount: u16, 6 | pub ancount: u16, 7 | pub nscount: u16, 8 | pub arcount: u16, 9 | } 10 | 11 | impl Header { 12 | pub fn new() -> Self { 13 | Self { 14 | id: 0, 15 | flags: 0, 16 | qdcount: 0, 17 | ancount: 0, 18 | nscount: 0, 19 | arcount: 0, 20 | } 21 | } 22 | pub fn with_bytes(bytes: Vec) -> Self { 23 | Self { 24 | id: u16::from_be_bytes([bytes[0], bytes[1]]), 25 | flags: u16::from_be_bytes([bytes[2], bytes[3]]), 26 | qdcount: u16::from_be_bytes([bytes[4], bytes[5]]), 27 | ancount: u16::from_be_bytes([bytes[6], bytes[7]]), 28 | nscount: u16::from_be_bytes([bytes[8], bytes[9]]), 29 | arcount: u16::from_be_bytes([bytes[10], bytes[10]]), 30 | } 31 | } 32 | 33 | pub fn to_bytes(&self) -> Vec { 34 | let mut bytes: Vec = Vec::new(); 35 | 36 | bytes.extend_from_slice(&self.id.to_be_bytes()); 37 | bytes.extend_from_slice(&self.flags.to_be_bytes()); 38 | bytes.extend_from_slice(&self.qdcount.to_be_bytes()); 39 | bytes.extend_from_slice(&self.ancount.to_be_bytes()); 40 | bytes.extend_from_slice(&self.nscount.to_be_bytes()); 41 | bytes.extend_from_slice(&self.arcount.to_be_bytes()); 42 | 43 | return bytes; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/structs/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod header; 2 | pub mod question; 3 | pub mod request; 4 | pub mod response; 5 | -------------------------------------------------------------------------------- /src/structs/question.rs: -------------------------------------------------------------------------------- 1 | use std::str::Split; 2 | 3 | #[derive(Debug, PartialEq, Clone)] 4 | pub enum Qtype { 5 | A = 1, 6 | NS = 2, 7 | MD = 3, 8 | MF = 4, 9 | CNAME = 5, 10 | SOA = 6, 11 | MB = 7, 12 | MG = 8, 13 | MR = 9, 14 | NULL = 10, 15 | WKS = 11, 16 | PTR = 12, 17 | HINFO = 13, 18 | MINFO = 14, 19 | MX = 15, 20 | TXT = 16, 21 | AAAA = 28, 22 | Unknow, 23 | } 24 | 25 | impl Qtype { 26 | fn with_bytes(bytes: u16) -> Self { 27 | match bytes { 28 | 1 => Self::A, 29 | 2 => Self::NS, 30 | 3 => Self::MD, 31 | 4 => Self::MF, 32 | 5 => Self::CNAME, 33 | 6 => Self::SOA, 34 | 7 => Self::MB, 35 | 8 => Self::MG, 36 | 9 => Self::MR, 37 | 10 => Self::NULL, 38 | 11 => Self::WKS, 39 | 12 => Self::PTR, 40 | 13 => Self::HINFO, 41 | 14 => Self::MINFO, 42 | 15 => Self::MX, 43 | 16 => Self::TXT, 44 | 28 => Self::AAAA, 45 | _ => Self::Unknow, 46 | } 47 | } 48 | 49 | pub fn with_string(string: String) -> Self { 50 | match string.as_str() { 51 | "A" => Self::A, 52 | "NS" => Self::NS, 53 | "MD" => Self::MD, 54 | "MF" => Self::MF, 55 | "CNAME" => Self::CNAME, 56 | "SOA" => Self::SOA, 57 | "MB" => Self::MB, 58 | "MG" => Self::MG, 59 | "MR" => Self::MR, 60 | "NULL" => Self::NULL, 61 | "WKS" => Self::WKS, 62 | "PTR" => Self::PTR, 63 | "HINFO" => Self::HINFO, 64 | "MINFO" => Self::MINFO, 65 | "MX" => Self::MX, 66 | "TXT" => Self::TXT, 67 | "AAAA" => Self::AAAA, 68 | _ => Self::Unknow, 69 | } 70 | } 71 | 72 | pub fn to_bytes(&self) -> u16 { 73 | match self { 74 | Self::A => 0x1, 75 | Self::NS => 0x2, 76 | Self::MD => 0x3, 77 | Self::MF => 0x4, 78 | Self::CNAME => 0x5, 79 | Self::SOA => 0x6, 80 | Self::MB => 0x7, 81 | Self::MG => 0x8, 82 | Self::MR => 0x9, 83 | Self::NULL => 0xA, 84 | Self::WKS => 0xB, 85 | Self::PTR => 0xC, 86 | Self::HINFO => 0xD, 87 | Self::MINFO => 0xE, 88 | Self::MX => 0xF, 89 | Self::TXT => 0x10, 90 | Self::AAAA => 0x1C, 91 | // TODO 92 | Self::Unknow => 0x99, 93 | } 94 | } 95 | } 96 | 97 | #[derive(Debug)] 98 | pub enum Qclass { 99 | IN = 1, 100 | Unknow, 101 | } 102 | 103 | impl Qclass { 104 | fn with_bytes(bytes: u16) -> Self { 105 | match bytes { 106 | 1 => Self::IN, 107 | _ => Self::Unknow, 108 | } 109 | } 110 | 111 | pub fn to_bytes(&self) -> u16 { 112 | match self { 113 | Self::IN => 0x1, 114 | // TODO 115 | Self::Unknow => 0x99, 116 | } 117 | } 118 | } 119 | 120 | #[derive(Debug)] 121 | pub struct Question { 122 | pub qname: String, 123 | pub qtype: Qtype, 124 | pub qclass: Qclass, 125 | } 126 | 127 | impl Question { 128 | pub fn new() -> Self { 129 | Self { 130 | qname: String::new(), 131 | qtype: Qtype::Unknow, 132 | qclass: Qclass::Unknow, 133 | } 134 | } 135 | 136 | pub fn to_bytes(&self) -> Vec { 137 | let domain_splitted: Split<&str> = self.qname.split("."); 138 | let mut domain_bytes: Vec = Vec::new(); 139 | 140 | for domain_split in domain_splitted { 141 | domain_bytes.push(domain_split.len().try_into().unwrap()); 142 | 143 | for c in domain_split.chars() { 144 | domain_bytes.push(c as u8); 145 | } 146 | } 147 | domain_bytes.push(0x0); 148 | 149 | domain_bytes.extend_from_slice(&self.qtype.to_bytes().to_be_bytes()); 150 | domain_bytes.extend_from_slice(&self.qclass.to_bytes().to_be_bytes()); 151 | 152 | return domain_bytes; 153 | } 154 | 155 | pub fn with_bytes(bytes: Vec) -> Self { 156 | let bytes_len: usize = bytes.len(); 157 | let mut bytes_clone: Vec = bytes.clone(); 158 | 159 | Self { 160 | qname: decode_string(&mut bytes_clone), 161 | qtype: Qtype::with_bytes(u16::from_be_bytes([ 162 | bytes[bytes_len - 4], 163 | bytes[bytes_len - 3], 164 | ])), 165 | qclass: Qclass::with_bytes(u16::from_be_bytes([ 166 | bytes[bytes_len - 2], 167 | bytes[bytes_len - 1], 168 | ])), 169 | } 170 | } 171 | } 172 | 173 | fn decode_string(bytes: &mut Vec) -> String { 174 | let mut result: String = String::new(); 175 | 176 | loop { 177 | let last_index: usize = (bytes[0] + 1).try_into().unwrap(); 178 | let name: String = String::from_utf8_lossy(&bytes[1..last_index]).to_string(); 179 | 180 | result += &name; 181 | 182 | bytes.drain(0..last_index); 183 | 184 | if bytes[0] == 0x00 { 185 | break; 186 | } 187 | 188 | result += "."; 189 | } 190 | 191 | return result; 192 | } 193 | -------------------------------------------------------------------------------- /src/structs/request.rs: -------------------------------------------------------------------------------- 1 | use super::{header::Header, question::Question}; 2 | 3 | #[derive(Debug)] 4 | pub struct Request { 5 | pub header: Header, 6 | pub question: Question, 7 | } 8 | 9 | impl Request { 10 | pub fn new() -> Self { 11 | Self { 12 | header: Header::new(), 13 | question: Question::new(), 14 | } 15 | } 16 | pub fn with_bytes(&mut self, bytes: Vec) { 17 | let header_bytes: Vec = bytes[0..12].to_vec(); 18 | self.header = Header::with_bytes(header_bytes); 19 | 20 | let question_bytes: Vec = bytes[12..].to_vec(); 21 | self.question = Question::with_bytes(question_bytes); 22 | } 23 | 24 | pub fn to_bytes(&self) -> Vec { 25 | let mut bytes: Vec = Vec::new(); 26 | 27 | bytes.extend(self.header.to_bytes()); 28 | bytes.extend(self.question.to_bytes()); 29 | 30 | return bytes; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/structs/response.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::BTreeMap, str::Split}; 2 | 3 | use crate::language::{analyse, file_finder, parser::parser::Literal}; 4 | 5 | use super::{question::Qtype, request::Request}; 6 | 7 | #[derive(Debug)] 8 | pub struct Response { 9 | pub request: Request, 10 | } 11 | 12 | impl Response { 13 | pub fn from(request: Request) -> Self { 14 | Self { request } 15 | } 16 | 17 | fn domain_to_bytes(&self, domain: String) -> Vec { 18 | let domain_splitted: Split<&str> = domain.split("."); 19 | let mut domain_bytes: Vec = Vec::new(); 20 | 21 | for domain_split in domain_splitted { 22 | domain_bytes.push(domain_split.len().try_into().unwrap()); 23 | 24 | for c in domain_split.chars() { 25 | domain_bytes.push(c as u8); 26 | } 27 | } 28 | domain_bytes.push(0x0); 29 | 30 | return domain_bytes; 31 | } 32 | 33 | fn ip_to_bytes(&self, ip_array: Vec<&str>) -> Vec { 34 | let mut ip_bytes: Vec = Vec::new(); 35 | 36 | for ip in ip_array { 37 | ip_bytes.push(ip.parse::().unwrap()); 38 | } 39 | 40 | return ip_bytes; 41 | } 42 | 43 | pub fn create_byte_response(&mut self) -> Vec { 44 | let domain: String = self.request.question.qname.clone(); 45 | 46 | let source: Option = file_finder(domain.clone()); 47 | 48 | if source == None { 49 | self.request.header.flags = 0x8183; 50 | return self.request.to_bytes(); 51 | } 52 | 53 | let analyse: BTreeMap = analyse(source.unwrap()).unwrap(); 54 | 55 | let ttl: i32 = analyse.get("ttl").unwrap().clone().as_int().unwrap(); 56 | let dns_type: Qtype = analyse.get("type").unwrap().clone().as_qtype().unwrap(); 57 | let ip: String = analyse.get("ip").unwrap().clone().as_str().unwrap(); 58 | 59 | self.request.header.flags = 0x8180; 60 | 61 | if dns_type != self.request.question.qtype { 62 | return self.request.to_bytes(); 63 | } 64 | 65 | self.request.header.ancount = 0x0001; 66 | let mut array: Vec = Vec::new(); 67 | 68 | let domain_array: Vec = self.domain_to_bytes(domain); 69 | array.extend(domain_array); 70 | 71 | array.extend_from_slice(&dns_type.to_bytes().to_be_bytes()); 72 | array.extend_from_slice(&self.request.question.qclass.to_bytes().to_be_bytes()); 73 | array.extend_from_slice(&ttl.to_be_bytes()); 74 | 75 | let ip_array: Vec<&str> = ip.split(".").collect(); 76 | let ip_size: u16 = ip_array.len().try_into().unwrap(); 77 | 78 | array.extend_from_slice(&ip_size.to_be_bytes()); 79 | array.extend(self.ip_to_bytes(ip_array)); 80 | 81 | let mut bytes: Vec = self.request.to_bytes(); 82 | 83 | bytes.extend(array); 84 | 85 | return bytes; 86 | } 87 | } 88 | --------------------------------------------------------------------------------