pharos_server/
admission.rs1use pharos_proto::{Class, Message, MessageBuilder, Opcode, Rcode};
2
3pub enum Admission<'a> {
5 Query(Message<'a>),
6 Respond,
7 Drop,
8}
9
10pub fn admit<'a>(wire: &'a [u8], allow_non_in: bool, response: &mut Vec<u8>) -> Admission<'a> {
12 if wire.len() < 12 {
14 return Admission::Drop;
15 }
16 let flags = u16::from_be_bytes([wire[2], wire[3]]);
17 if flags & 0x8000 != 0 {
19 return Admission::Drop;
20 }
21 let opcode = (flags >> 11) & 15;
22 if opcode != 0 && opcode != 4 {
23 header_error(wire, response, Rcode::NOTIMP, true);
24 return Admission::Respond;
25 }
26 let count = |i| u16::from_be_bytes([wire[i], wire[i + 1]]);
27 if count(4) != 1 || count(6) > 1 || count(8) > 1 || count(10) > 2 {
29 header_error(wire, response, Rcode::FORMERR, false);
30 return Admission::Respond;
31 }
32 let Ok(query) = Message::parse(wire) else {
33 header_error(wire, response, Rcode::FORMERR, false);
34 return Admission::Respond;
35 };
36 let Some(question) = query.questions().next() else {
37 header_error(wire, response, Rcode::SERVFAIL, false);
38 return Admission::Respond;
39 };
40 if !allow_non_in && question.qclass != Class::IN {
41 write_error(query, response, Rcode::REFUSED);
42 return Admission::Respond;
43 }
44 if query
46 .badvers_response(response)
47 .expect("valid BADVERS Response")
48 .is_some()
49 {
50 return Admission::Respond;
51 }
52 Admission::Query(query)
53}
54
55fn header_error(wire: &[u8], response: &mut Vec<u8>, rcode: Rcode, keep_opcode: bool) {
56 let flags = u16::from_be_bytes([wire[2], wire[3]]);
57 let flags = 0x8000 | (flags & 0x03b0) | if keep_opcode { flags & 0x7800 } else { 0 } | rcode.0;
58 response.clear();
59 response.extend_from_slice(&wire[..2]);
60 response.extend_from_slice(&flags.to_be_bytes());
61 response.extend_from_slice(&[0; 8]);
62}
63
64pub(crate) fn write_error(query: Message<'_>, response: &mut Vec<u8>, rcode: Rcode) {
65 let flags = 0x8000
67 | (query.flags() & 0x7800)
68 | if query.opcode() == Opcode::QUERY {
69 query.flags() & 0x0110
70 } else {
71 0
72 }
73 | (rcode.0 & 15);
74 let mut builder = MessageBuilder::new(response, usize::from(u16::MAX), query.id(), flags)
75 .expect("valid maximum");
76 if let Some(question) = query.questions().next() {
77 let _ = builder.question(question.name, question.qtype, question.qclass);
78 }
79 let _ = builder.finish();
80}