summaryrefslogtreecommitdiff
path: root/packet
diff options
context:
space:
mode:
Diffstat (limited to 'packet')
-rw-r--r--packet/Cargo.lock7
-rw-r--r--packet/Cargo.toml6
-rw-r--r--packet/src/buffer.rs192
-rw-r--r--packet/src/header.rs102
-rw-r--r--packet/src/lib.rs156
-rw-r--r--packet/src/query.rs33
-rw-r--r--packet/src/question.rs31
-rw-r--r--packet/src/record.rs227
-rw-r--r--packet/src/result.rs22
9 files changed, 0 insertions, 776 deletions
diff --git a/packet/Cargo.lock b/packet/Cargo.lock
deleted file mode 100644
index 3f0df66..0000000
--- a/packet/Cargo.lock
+++ /dev/null
@@ -1,7 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 3
-
-[[package]]
-name = "packet"
-version = "0.1.0"
diff --git a/packet/Cargo.toml b/packet/Cargo.toml
deleted file mode 100644
index 2e3797f..0000000
--- a/packet/Cargo.toml
+++ /dev/null
@@ -1,6 +0,0 @@
-[package]
-name = "packet"
-version = "0.1.0"
-edition = "2021"
-
-[dependencies]
diff --git a/packet/src/buffer.rs b/packet/src/buffer.rs
deleted file mode 100644
index 4394705..0000000
--- a/packet/src/buffer.rs
+++ /dev/null
@@ -1,192 +0,0 @@
-use super::Result;
-
-pub struct PacketBuffer {
- pub buf: [u8; 512],
- pub pos: usize,
-}
-
-impl PacketBuffer {
- pub fn new() -> Self {
- Self {
- buf: [0; 512],
- pos: 0,
- }
- }
-
- pub fn pos(&self) -> usize {
- self.pos
- }
-
- pub fn step(&mut self, steps: usize) -> Result<()> {
- self.pos += steps;
-
- Ok(())
- }
-
- pub fn seek(&mut self, pos: usize) -> Result<()> {
- self.pos = pos;
-
- Ok(())
- }
-
- pub fn read(&mut self) -> Result<u8> {
- if self.pos >= 512 {
- return Err("End of buffer".into());
- }
- let res = self.buf[self.pos];
- self.pos += 1;
-
- Ok(res)
- }
-
- pub fn get(&mut self, pos: usize) -> Result<u8> {
- if pos >= 512 {
- return Err("End of buffer".into());
- }
- Ok(self.buf[pos])
- }
-
- pub fn get_range(&mut self, start: usize, len: usize) -> Result<&[u8]> {
- if start + len >= 512 {
- return Err("End of buffer".into());
- }
- Ok(&self.buf[start..start + len])
- }
-
- pub fn read_u16(&mut self) -> Result<u16> {
- let res = ((self.read()? as u16) << 8) | (self.read()? as u16);
-
- Ok(res)
- }
-
- pub fn read_u32(&mut self) -> Result<u32> {
- let res = ((self.read()? as u32) << 24)
- | ((self.read()? as u32) << 16)
- | ((self.read()? as u32) << 8)
- | (self.read()? as u32);
-
- Ok(res)
- }
-
- pub fn read_qname(&mut self, outstr: &mut String) -> Result<()> {
- let mut pos = self.pos();
- let mut jumped = false;
-
- let mut delim = "";
- let max_jumps = 5;
- let mut jumps_performed = 0;
- loop {
- // Dns Packets are untrusted data, so we need to be paranoid. Someone
- // can craft a packet with a cycle in the jump instructions. This guards
- // against such packets.
- if jumps_performed > max_jumps {
- return Err(format!("Limit of {max_jumps} jumps exceeded").into());
- }
-
- let len = self.get(pos)?;
-
- // A two byte sequence, where the two highest bits of the first byte is
- // set, represents a offset relative to the start of the buffer. We
- // handle this by jumping to the offset, setting a flag to indicate
- // that we shouldn't update the shared buffer position once done.
- if (len & 0xC0) == 0xC0 {
- // When a jump is performed, we only modify the shared buffer
- // position once, and avoid making the change later on.
- if !jumped {
- self.seek(pos + 2)?;
- }
-
- let b2 = self.get(pos + 1)? as u16;
- let offset = (((len as u16) ^ 0xC0) << 8) | b2;
- pos = offset as usize;
- jumped = true;
- jumps_performed += 1;
- continue;
- }
-
- pos += 1;
-
- // Names are terminated by an empty label of length 0
- if len == 0 {
- break;
- }
-
- outstr.push_str(delim);
-
- let str_buffer = self.get_range(pos, len as usize)?;
- outstr.push_str(&String::from_utf8_lossy(str_buffer).to_lowercase());
-
- delim = ".";
-
- pos += len as usize;
- }
-
- if !jumped {
- self.seek(pos)?;
- }
-
- Ok(())
- }
-
- pub fn write(&mut self, val: u8) -> Result<()> {
- if self.pos >= 512 {
- return Err("End of buffer".into());
- }
- self.buf[self.pos] = val;
- self.pos += 1;
- Ok(())
- }
-
- pub fn write_u8(&mut self, val: u8) -> Result<()> {
- self.write(val)?;
-
- Ok(())
- }
-
- pub fn write_u16(&mut self, val: u16) -> Result<()> {
- self.write((val >> 8) as u8)?;
- self.write((val & 0xFF) as u8)?;
-
- Ok(())
- }
-
- pub fn write_u32(&mut self, val: u32) -> Result<()> {
- self.write(((val >> 24) & 0xFF) as u8)?;
- self.write(((val >> 16) & 0xFF) as u8)?;
- self.write(((val >> 8) & 0xFF) as u8)?;
- self.write((val & 0xFF) as u8)?;
-
- Ok(())
- }
-
- pub fn write_qname(&mut self, qname: &str) -> Result<()> {
- for label in qname.split('.') {
- let len = label.len();
- if len > 0x34 {
- return Err("Single label exceeds 63 characters of length".into());
- }
-
- self.write_u8(len as u8)?;
- for b in label.as_bytes() {
- self.write_u8(*b)?;
- }
- }
-
- self.write_u8(0)?;
-
- Ok(())
- }
-
- pub fn set(&mut self, pos: usize, val: u8) -> Result<()> {
- self.buf[pos] = val;
-
- Ok(())
- }
-
- pub fn set_u16(&mut self, pos: usize, val: u16) -> Result<()> {
- self.set(pos, (val >> 8) as u8)?;
- self.set(pos + 1, (val & 0xFF) as u8)?;
-
- Ok(())
- }
-} \ No newline at end of file
diff --git a/packet/src/header.rs b/packet/src/header.rs
deleted file mode 100644
index b2bf1a1..0000000
--- a/packet/src/header.rs
+++ /dev/null
@@ -1,102 +0,0 @@
-use super::{buffer::PacketBuffer, Result, result::ResultCode};
-
-#[derive(Clone, Debug)]
-pub struct DnsHeader {
- pub id: u16, // 16 bits
-
- pub recursion_desired: bool, // 1 bit
- pub truncated_message: bool, // 1 bit
- pub authoritative_answer: bool, // 1 bit
- pub opcode: u8, // 4 bits
- pub response: bool, // 1 bit
-
- pub rescode: ResultCode, // 4 bits
- pub checking_disabled: bool, // 1 bit
- pub authed_data: bool, // 1 bit
- pub z: bool, // 1 bit
- pub recursion_available: bool, // 1 bit
-
- pub questions: u16, // 16 bits
- pub answers: u16, // 16 bits
- pub authoritative_entries: u16, // 16 bits
- pub resource_entries: u16, // 16 bits
-}
-
-impl DnsHeader {
- pub fn new() -> Self {
- Self {
- id: 0,
-
- recursion_desired: false,
- truncated_message: false,
- authoritative_answer: false,
- opcode: 0,
- response: false,
-
- rescode: ResultCode::NOERROR,
- checking_disabled: false,
- authed_data: false,
- z: false,
- recursion_available: false,
-
- questions: 0,
- answers: 0,
- authoritative_entries: 0,
- resource_entries: 0,
- }
- }
-
- pub fn read(&mut self, buffer: &mut PacketBuffer) -> Result<()> {
- self.id = buffer.read_u16()?;
-
- let flags = buffer.read_u16()?;
- let a = (flags >> 8) as u8;
- let b = (flags & 0xFF) as u8;
- self.recursion_desired = (a & (1 << 0)) > 0;
- self.truncated_message = (a & (1 << 1)) > 0;
- self.authoritative_answer = (a & (1 << 2)) > 0;
- self.opcode = (a >> 3) & 0x0F;
- self.response = (a & (1 << 7)) > 0;
-
- self.rescode = ResultCode::from_num(b & 0x0F);
- self.checking_disabled = (b & (1 << 4)) > 0;
- self.authed_data = (b & (1 << 5)) > 0;
- self.z = (b & (1 << 6)) > 0;
- self.recursion_available = (b & (1 << 7)) > 0;
-
- self.questions = buffer.read_u16()?;
- self.answers = buffer.read_u16()?;
- self.authoritative_entries = buffer.read_u16()?;
- self.resource_entries = buffer.read_u16()?;
-
- // Return the constant header size
- Ok(())
- }
-
- pub fn write(&self, buffer: &mut PacketBuffer) -> Result<()> {
- buffer.write_u16(self.id)?;
-
- buffer.write_u8(
- (self.recursion_desired as u8)
- | ((self.truncated_message as u8) << 1)
- | ((self.authoritative_answer as u8) << 2)
- | (self.opcode << 3)
- | ((self.response as u8) << 7),
- )?;
-
- buffer.write_u8(
- (self.rescode as u8)
- | ((self.checking_disabled as u8) << 4)
- | ((self.authed_data as u8) << 5)
- | ((self.z as u8) << 6)
- | ((self.recursion_available as u8) << 7),
- )?;
-
- buffer.write_u16(self.questions)?;
- buffer.write_u16(self.answers)?;
- buffer.write_u16(self.authoritative_entries)?;
- buffer.write_u16(self.resource_entries)?;
-
- Ok(())
- }
-} \ No newline at end of file
diff --git a/packet/src/lib.rs b/packet/src/lib.rs
deleted file mode 100644
index c7a8eb9..0000000
--- a/packet/src/lib.rs
+++ /dev/null
@@ -1,156 +0,0 @@
-use std::net::IpAddr;
-
-use self::{header::DnsHeader, question::DnsQuestion, record::DnsRecord, query::QueryType};
-
-type Error = Box<dyn std::error::Error>;
-pub type Result<T> = std::result::Result<T, Error>;
-
-mod buffer;
-mod header;
-mod query;
-mod question;
-mod record;
-mod result;
-
-#[derive(Clone, Debug)]
-pub struct Packet {
- pub header: DnsHeader,
- pub questions: Vec<DnsQuestion>,
- pub answers: Vec<DnsRecord>,
- pub authorities: Vec<DnsRecord>,
- pub resources: Vec<DnsRecord>,
-}
-
-pub use buffer::PacketBuffer;
-pub use result::ResultCode;
-
-pub use query::QueryType as PacketType;
-pub use question::DnsQuestion as PacketQuestion;
-
-impl Packet {
- pub fn new() -> Self {
- Self {
- header: DnsHeader::new(),
- questions: Vec::new(),
- answers: Vec::new(),
- authorities: Vec::new(),
- resources: Vec::new(),
- }
- }
-
- pub fn from_buffer(buffer: &mut PacketBuffer) -> Result<Self> {
- let mut result = Self::new();
- result.header.read(buffer)?;
-
- for _ in 0..result.header.questions {
- let mut question = DnsQuestion::new("".to_string(), QueryType::UNKNOWN(0));
- question.read(buffer)?;
- result.questions.push(question);
- }
-
- for _ in 0..result.header.answers {
- let rec = DnsRecord::read(buffer)?;
- result.answers.push(rec);
- }
- for _ in 0..result.header.authoritative_entries {
- let rec = DnsRecord::read(buffer)?;
- result.authorities.push(rec);
- }
- for _ in 0..result.header.resource_entries {
- let rec = DnsRecord::read(buffer)?;
- result.resources.push(rec);
- }
-
- Ok(result)
- }
-
- pub fn write(&mut self, buffer: &mut PacketBuffer) -> Result<()> {
- self.header.questions = self.questions.len() as u16;
- self.header.answers = self.answers.len() as u16;
- self.header.authoritative_entries = self.authorities.len() as u16;
- self.header.resource_entries = self.resources.len() as u16;
-
- self.header.write(buffer)?;
-
- for question in &self.questions {
- question.write(buffer)?;
- }
- for rec in &self.answers {
- rec.write(buffer)?;
- }
- for rec in &self.authorities {
- rec.write(buffer)?;
- }
- for rec in &self.resources {
- rec.write(buffer)?;
- }
-
- Ok(())
- }
-
- /// It's useful to be able to pick a random A record from a packet. When we
- /// get multiple IP's for a single name, it doesn't matter which one we
- /// choose, so in those cases we can now pick one at random.
- pub fn get_random_a(&self) -> Option<IpAddr> {
- self.answers
- .iter()
- .filter_map(|record| match record {
- DnsRecord::A { addr, .. } => Some(IpAddr::V4(*addr)),
- DnsRecord::AAAA { addr, .. } => Some(IpAddr::V6(*addr)),
- _ => None,
- })
- .next()
- }
-
- /// A helper function which returns an iterator over all name servers in
- /// the authorities section, represented as (domain, host) tuples
- fn get_ns<'a>(&'a self, qname: &'a str) -> impl Iterator<Item = (&'a str, &'a str)> {
- self.authorities
- .iter()
- // In practice, these are always NS records in well formed packages.
- // Convert the NS records to a tuple which has only the data we need
- // to make it easy to work with.
- .filter_map(|record| match record {
- DnsRecord::NS { domain, host, .. } => Some((domain.as_str(), host.as_str())),
- _ => None,
- })
- // Discard servers which aren't authoritative to our query
- .filter(move |(domain, _)| qname.ends_with(*domain))
- }
-
- /// We'll use the fact that name servers often bundle the corresponding
- /// A records when replying to an NS query to implement a function that
- /// returns the actual IP for an NS record if possible.
- pub fn get_resolved_ns(&self, qname: &str) -> Option<IpAddr> {
- // Get an iterator over the nameservers in the authorities section
- self.get_ns(qname)
- // Now we need to look for a matching A record in the additional
- // section. Since we just want the first valid record, we can just
- // build a stream of matching records.
- .flat_map(|(_, host)| {
- self.resources
- .iter()
- // Filter for A records where the domain match the host
- // of the NS record that we are currently processing
- .filter_map(move |record| match record {
- DnsRecord::A { domain, addr, .. } if domain == host => Some(IpAddr::V4(*addr)),
- DnsRecord::AAAA { domain, addr, .. } if domain == host => Some(IpAddr::V6(*addr)),
- _ => None,
- })
- })
- // Finally, pick the first valid entry
- .next()
- }
-
- /// However, not all name servers are as that nice. In certain cases there won't
- /// be any A records in the additional section, and we'll have to perform *another*
- /// lookup in the midst. For this, we introduce a method for returning the host
- /// name of an appropriate name server.
- pub fn get_unresolved_ns<'a>(&'a self, qname: &'a str) -> Option<&'a str> {
- // Get an iterator over the nameservers in the authorities section
- self.get_ns(qname)
- .map(|(_, host)| host)
- // Finally, pick the first valid entry
- .next()
- }
-} \ No newline at end of file
diff --git a/packet/src/query.rs b/packet/src/query.rs
deleted file mode 100644
index 8804d15..0000000
--- a/packet/src/query.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-#[derive(PartialEq, Eq, Debug, Clone, Hash, Copy)]
-pub enum QueryType {
- UNKNOWN(u16),
- A, // 1
- NS, // 2
- CNAME, // 5
- MX, // 15
- AAAA, // 28
-}
-
-impl QueryType {
- pub fn to_num(&self) -> u16 {
- match *self {
- Self::UNKNOWN(x) => x,
- Self::A => 1,
- Self::NS => 2,
- Self::CNAME => 5,
- Self::MX => 15,
- Self::AAAA => 28,
- }
- }
-
- pub fn from_num(num: u16) -> Self {
- match num {
- 1 => Self::A,
- 2 => Self::NS,
- 5 => Self::CNAME,
- 15 => Self::MX,
- 28 => Self::AAAA,
- _ => Self::UNKNOWN(num),
- }
- }
-} \ No newline at end of file
diff --git a/packet/src/question.rs b/packet/src/question.rs
deleted file mode 100644
index 076de00..0000000
--- a/packet/src/question.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-use super::{buffer::PacketBuffer, query::QueryType, Result};
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct DnsQuestion {
- pub name: String,
- pub qtype: QueryType,
-}
-
-impl DnsQuestion {
- pub fn new(name: String, qtype: QueryType) -> Self {
- Self { name, qtype }
- }
-
- pub fn read(&mut self, buffer: &mut PacketBuffer) -> Result<()> {
- buffer.read_qname(&mut self.name)?;
- self.qtype = QueryType::from_num(buffer.read_u16()?); // qtype
- let _ = buffer.read_u16()?; // class
-
- Ok(())
- }
-
- pub fn write(&self, buffer: &mut PacketBuffer) -> Result<()> {
- buffer.write_qname(&self.name)?;
-
- let typenum = self.qtype.to_num();
- buffer.write_u16(typenum)?;
- buffer.write_u16(1)?;
-
- Ok(())
- }
-} \ No newline at end of file
diff --git a/packet/src/record.rs b/packet/src/record.rs
deleted file mode 100644
index c7ff1ac..0000000
--- a/packet/src/record.rs
+++ /dev/null
@@ -1,227 +0,0 @@
-use std::net::{Ipv4Addr, Ipv6Addr};
-
-use super::{query::QueryType, buffer::PacketBuffer, Result};
-
-#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
-#[allow(dead_code)]
-pub enum DnsRecord {
- UNKNOWN {
- domain: String,
- qtype: u16,
- data_len: u16,
- ttl: u32,
- }, // 0
- A {
- domain: String,
- addr: Ipv4Addr,
- ttl: u32,
- }, // 1
- NS {
- domain: String,
- host: String,
- ttl: u32,
- }, // 2
- CNAME {
- domain: String,
- host: String,
- ttl: u32,
- }, // 5
- MX {
- domain: String,
- priority: u16,
- host: String,
- ttl: u32,
- }, // 15
- AAAA {
- domain: String,
- addr: Ipv6Addr,
- ttl: u32,
- }, // 28
-}
-
-impl DnsRecord {
- pub fn read(buffer: &mut PacketBuffer) -> Result<Self> {
- let mut domain = String::new();
- buffer.read_qname(&mut domain)?;
-
- let qtype_num = buffer.read_u16()?;
- let qtype = QueryType::from_num(qtype_num);
- let _ = buffer.read_u16()?;
- let ttl = buffer.read_u32()?;
- let data_len = buffer.read_u16()?;
-
- match qtype {
- QueryType::A => {
- let raw_addr = buffer.read_u32()?;
- let addr = Ipv4Addr::new(
- ((raw_addr >> 24) & 0xFF) as u8,
- ((raw_addr >> 16) & 0xFF) as u8,
- ((raw_addr >> 8) & 0xFF) as u8,
- (raw_addr & 0xFF) as u8,
- );
-
- Ok(Self::A { domain, addr, ttl })
- }
- QueryType::AAAA => {
- let raw_addr1 = buffer.read_u32()?;
- let raw_addr2 = buffer.read_u32()?;
- let raw_addr3 = buffer.read_u32()?;
- let raw_addr4 = buffer.read_u32()?;
- let addr = Ipv6Addr::new(
- ((raw_addr1 >> 16) & 0xFFFF) as u16,
- (raw_addr1 & 0xFFFF) as u16,
- ((raw_addr2 >> 16) & 0xFFFF) as u16,
- (raw_addr2 & 0xFFFF) as u16,
- ((raw_addr3 >> 16) & 0xFFFF) as u16,
- (raw_addr3 & 0xFFFF) as u16,
- ((raw_addr4 >> 16) & 0xFFFF) as u16,
- (raw_addr4 & 0xFFFF) as u16,
- );
-
- Ok(Self::AAAA { domain, addr, ttl })
- }
- QueryType::NS => {
- let mut ns = String::new();
- buffer.read_qname(&mut ns)?;
-
- Ok(Self::NS {
- domain,
- host: ns,
- ttl,
- })
- }
- QueryType::CNAME => {
- let mut cname = String::new();
- buffer.read_qname(&mut cname)?;
-
- Ok(Self::CNAME {
- domain,
- host: cname,
- ttl,
- })
- }
- QueryType::MX => {
- let priority = buffer.read_u16()?;
- let mut mx = String::new();
- buffer.read_qname(&mut mx)?;
-
- Ok(Self::MX {
- domain,
- priority,
- host: mx,
- ttl,
- })
- }
- QueryType::UNKNOWN(_) => {
- buffer.step(data_len as usize)?;
-
- Ok(Self::UNKNOWN {
- domain,
- qtype: qtype_num,
- data_len,
- ttl,
- })
- }
- }
- }
-
- pub fn write(&self, buffer: &mut PacketBuffer) -> Result<usize> {
- let start_pos = buffer.pos();
-
- match *self {
- Self::A {
- ref domain,
- ref addr,
- ttl,
- } => {
- buffer.write_qname(domain)?;
- buffer.write_u16(QueryType::A.to_num())?;
- buffer.write_u16(1)?;
- buffer.write_u32(ttl)?;
- buffer.write_u16(4)?;
-
- let octets = addr.octets();
- buffer.write_u8(octets[0])?;
- buffer.write_u8(octets[1])?;
- buffer.write_u8(octets[2])?;
- buffer.write_u8(octets[3])?;
- }
- Self::NS {
- ref domain,
- ref host,
- ttl,
- } => {
- buffer.write_qname(domain)?;
- buffer.write_u16(QueryType::NS.to_num())?;
- buffer.write_u16(1)?;
- buffer.write_u32(ttl)?;
-
- let pos = buffer.pos();
- buffer.write_u16(0)?;
-
- buffer.write_qname(host)?;
-
- let size = buffer.pos() - (pos + 2);
- buffer.set_u16(pos, size as u16)?;
- }
- Self::CNAME {
- ref domain,
- ref host,
- ttl,
- } => {
- buffer.write_qname(domain)?;
- buffer.write_u16(QueryType::CNAME.to_num())?;
- buffer.write_u16(1)?;
- buffer.write_u32(ttl)?;
-
- let pos = buffer.pos();
- buffer.write_u16(0)?;
-
- buffer.write_qname(host)?;
-
- let size = buffer.pos() - (pos + 2);
- buffer.set_u16(pos, size as u16)?;
- }
- Self::MX {
- ref domain,
- priority,
- ref host,
- ttl,
- } => {
- buffer.write_qname(domain)?;
- buffer.write_u16(QueryType::MX.to_num())?;
- buffer.write_u16(1)?;
- buffer.write_u32(ttl)?;
-
- let pos = buffer.pos();
- buffer.write_u16(0)?;
-
- buffer.write_u16(priority)?;
- buffer.write_qname(host)?;
-
- let size = buffer.pos() - (pos + 2);
- buffer.set_u16(pos, size as u16)?;
- }
- Self::AAAA {
- ref domain,
- ref addr,
- ttl,
- } => {
- buffer.write_qname(domain)?;
- buffer.write_u16(QueryType::AAAA.to_num())?;
- buffer.write_u16(1)?;
- buffer.write_u32(ttl)?;
- buffer.write_u16(16)?;
-
- for octet in &addr.segments() {
- buffer.write_u16(*octet)?;
- }
- }
- Self::UNKNOWN { .. } => {
- println!("Skipping record: {self:?}");
- }
- }
-
- Ok(buffer.pos() - start_pos)
- }
-} \ No newline at end of file
diff --git a/packet/src/result.rs b/packet/src/result.rs
deleted file mode 100644
index f33bd7d..0000000
--- a/packet/src/result.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum ResultCode {
- NOERROR = 0,
- FORMERR = 1,
- SERVFAIL = 2,
- NXDOMAIN = 3,
- NOTIMP = 4,
- REFUSED = 5,
-}
-
-impl ResultCode {
- pub fn from_num(num: u8) -> Self {
- match num {
- 1 => Self::FORMERR,
- 2 => Self::SERVFAIL,
- 3 => Self::NXDOMAIN,
- 4 => Self::NOTIMP,
- 5 => Self::REFUSED,
- 0 | _ => Self::NOERROR,
- }
- }
-} \ No newline at end of file