Client written and working, but now need to implement pointer for names, non-consuming data stream
This commit is contained in:
parent
d781deceee
commit
a8fe789836
@ -1,5 +1,5 @@
|
||||
# trust-dns
|
||||
A Rust based DNS server
|
||||
A Rust based DNS client and server
|
||||
|
||||
# Goals
|
||||
|
||||
@ -21,6 +21,7 @@ All parsers complete.
|
||||
Todo: Serializers.
|
||||
Todo: Operations.
|
||||
|
||||
- EDNS http://tools.ietf.org/html/rfc2671
|
||||
- Support DNS Update RFC 2136.
|
||||
- DNSSEC Resource Records RFC 4034
|
||||
- DNSSec protocol RFC 4035
|
||||
|
@ -1,6 +1,3 @@
|
||||
pub mod rr;
|
||||
pub mod op;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
}
|
||||
pub mod udp;
|
||||
|
@ -128,6 +128,19 @@ impl Header {
|
||||
pub fn name_server_count(&mut self, name_server_count: u16) -> &mut Self { self.name_server_count = name_server_count; self }
|
||||
pub fn additional_count(&mut self, additional_count: u16) -> &mut Self { self.additional_count = additional_count; self }
|
||||
|
||||
pub fn get_id(&self) -> u16 { self.id }
|
||||
pub fn get_message_type(&self) -> MessageType { self.message_type }
|
||||
pub fn get_op_code(&self) -> OpCode { self.op_code }
|
||||
pub fn is_authoritative(&self) -> bool { self.authoritative }
|
||||
pub fn is_truncated(&self) -> bool { self.truncation }
|
||||
pub fn is_recursion_desired(&self) -> bool { self.recursion_desired }
|
||||
pub fn is_recursion_available(&self) -> bool {self.recursion_available }
|
||||
pub fn get_response_code(&self) -> ResponseCode { self.response_code }
|
||||
pub fn get_query_count(&self) -> u16 { self.query_count }
|
||||
pub fn get_answer_count(&self) -> u16 { self.answer_count }
|
||||
pub fn get_name_server_count(&self) -> u16 { self.name_server_count }
|
||||
pub fn get_additional_count(&self) -> u16 { self.additional_count }
|
||||
|
||||
/// This is a specialized clone which clones all the fields but the counts
|
||||
/// handy for setting the count fields before sending over the wire.
|
||||
pub fn clone(&self, query_count: u16, answer_count: u16, name_server_count: u16, additional_count: u16) -> Self {
|
||||
@ -191,19 +204,6 @@ impl Header {
|
||||
util::write_u16_to(buf, self.name_server_count);
|
||||
util::write_u16_to(buf, self.additional_count);
|
||||
}
|
||||
|
||||
pub fn get_id(&self) -> u16 { self.id }
|
||||
pub fn get_message_type(&self) -> MessageType { self.message_type }
|
||||
pub fn get_op_code(&self) -> OpCode { self.op_code }
|
||||
pub fn is_authoritative(&self) -> bool { self.authoritative }
|
||||
pub fn is_truncated(&self) -> bool { self.truncation }
|
||||
pub fn is_recursion_desired(&self) -> bool { self.recursion_desired }
|
||||
pub fn is_recursion_available(&self) -> bool {self.recursion_available }
|
||||
pub fn get_response_code(&self) -> ResponseCode { self.response_code }
|
||||
pub fn get_query_count(&self) -> u16 { self.query_count }
|
||||
pub fn get_answer_count(&self) -> u16 { self.answer_count }
|
||||
pub fn get_name_server_count(&self) -> u16 { self.name_server_count }
|
||||
pub fn get_additional_count(&self) -> u16 { self.additional_count }
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -65,6 +65,20 @@ impl Message {
|
||||
pub fn add_name_server(&mut self, record: Record) -> &mut Self { self.name_servers.push(record); self }
|
||||
pub fn add_additional(&mut self, record: Record) -> &mut Self { self.additionals.push(record); self }
|
||||
|
||||
pub fn get_id(&self) -> u16 { self.header.get_id() }
|
||||
pub fn get_message_type(&self) -> MessageType { self.header.get_message_type() }
|
||||
pub fn get_op_code(&self) -> OpCode { self.header.get_op_code() }
|
||||
pub fn is_authoritative(&self) -> bool { self.header.is_authoritative() }
|
||||
pub fn is_truncated(&self) -> bool { self.header.is_truncated() }
|
||||
pub fn is_recursion_desired(&self) -> bool { self.header.is_recursion_desired() }
|
||||
pub fn is_recursion_available(&self) -> bool { self.header.is_recursion_available() }
|
||||
pub fn get_response_code(&self) -> ResponseCode { self.header.get_response_code() }
|
||||
pub fn get_queries(&self) -> &Vec<Query> { &self.queries }
|
||||
pub fn get_answers(&self) -> &Vec<Record> { &self.answers }
|
||||
pub fn get_name_servers(&self) -> &Vec<Record> { &self.name_servers }
|
||||
pub fn get_additional(&self) -> &Vec<Record> { &self.additionals }
|
||||
|
||||
|
||||
/// this is necessary to match the counts in the header from the record sections
|
||||
/// this happens implicitly on write_to, so no need to call before write_to
|
||||
pub fn update_counts(&mut self) -> &mut Self {
|
||||
|
@ -8,7 +8,7 @@ pub enum DNSClass {
|
||||
CH, // 3 Chaos (CH)
|
||||
HS, // 4 Hesiod (HS)
|
||||
NONE, // 254 QCLASS NONE
|
||||
ANY, // 255 QCLASS * (ANY)
|
||||
// ANY, // 255 QCLASS * (ANY)
|
||||
}
|
||||
|
||||
impl DNSClass {
|
||||
@ -42,7 +42,7 @@ impl From<DNSClass> for &'static str {
|
||||
DNSClass::CH => "CH",
|
||||
DNSClass::HS => "HS",
|
||||
DNSClass::NONE => "NONE",
|
||||
DNSClass::ANY => "ANY",
|
||||
// DNSClass::ANY => "ANY",
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -66,8 +66,8 @@ impl<'a> From<&'a str> for DNSClass {
|
||||
"CH" => DNSClass::CH,
|
||||
"HS" => DNSClass::HS,
|
||||
"NONE" => DNSClass::NONE,
|
||||
"ANY" => DNSClass::ANY,
|
||||
"*" => DNSClass::ANY,
|
||||
// "ANY" => DNSClass::ANY,
|
||||
// "*" => DNSClass::ANY,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
@ -92,7 +92,7 @@ impl From<DNSClass> for u16 {
|
||||
DNSClass::CH => 3,
|
||||
DNSClass::HS => 4,
|
||||
DNSClass::NONE => 254,
|
||||
DNSClass::ANY => 255,
|
||||
// DNSClass::ANY => 255,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -116,7 +116,7 @@ impl From<u16> for DNSClass {
|
||||
3 => DNSClass::CH,
|
||||
4 => DNSClass::HS,
|
||||
254 => DNSClass::NONE,
|
||||
255 => DNSClass::ANY,
|
||||
// 255 => DNSClass::ANY,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use std::ops::Index;
|
||||
|
||||
use super::util;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Name {
|
||||
labels: Vec<String>
|
||||
}
|
||||
|
@ -6,42 +6,42 @@ use super::util;
|
||||
pub enum RecordType {
|
||||
A, // 1 RFC 1035[1] IPv4 Address record
|
||||
AAAA, // 28 RFC 3596[2] IPv6 address record
|
||||
AFSDB, // 18 RFC 1183 AFS database record
|
||||
APL, // 42 RFC 3123 Address Prefix List
|
||||
CAA, // 257 RFC 6844 Certification Authority Authorization
|
||||
CDNSKEY, // 60 RFC 7344 Child DNSKEY
|
||||
CDS, // 59 RFC 7344 Child DS
|
||||
CERT, // 37 RFC 4398 Certificate record
|
||||
// AFSDB, // 18 RFC 1183 AFS database record
|
||||
// APL, // 42 RFC 3123 Address Prefix List
|
||||
// CAA, // 257 RFC 6844 Certification Authority Authorization
|
||||
// CDNSKEY, // 60 RFC 7344 Child DNSKEY
|
||||
// CDS, // 59 RFC 7344 Child DS
|
||||
// CERT, // 37 RFC 4398 Certificate record
|
||||
CNAME, // 5 RFC 1035[1] Canonical name record
|
||||
DHCID, // 49 RFC 4701 DHCP identifier
|
||||
DLV, // 32769 RFC 4431 DNSSEC Lookaside Validation record
|
||||
DNAME, // 39 RFC 2672 Delegation Name
|
||||
DNSKEY, // 48 RFC 4034 DNS Key record
|
||||
DS, // 43 RFC 4034 Delegation signer
|
||||
HIP, // 55 RFC 5205 Host Identity Protocol
|
||||
IPSECKEY, // 45 RFC 4025 IPsec Key
|
||||
KEY, // 25 RFC 2535[3] and RFC 2930[4] Key record
|
||||
KX, // 36 RFC 2230 Key eXchanger record
|
||||
LOC, // 29 RFC 1876 Location record
|
||||
// DHCID, // 49 RFC 4701 DHCP identifier
|
||||
// DLV, // 32769 RFC 4431 DNSSEC Lookaside Validation record
|
||||
// DNAME, // 39 RFC 2672 Delegation Name
|
||||
// DNSKEY, // 48 RFC 4034 DNS Key record
|
||||
// DS, // 43 RFC 4034 Delegation signer
|
||||
// HIP, // 55 RFC 5205 Host Identity Protocol
|
||||
// IPSECKEY, // 45 RFC 4025 IPsec Key
|
||||
// KEY, // 25 RFC 2535[3] and RFC 2930[4] Key record
|
||||
// KX, // 36 RFC 2230 Key eXchanger record
|
||||
// LOC, // 29 RFC 1876 Location record
|
||||
MX, // 15 RFC 1035[1] Mail exchange record
|
||||
NAPTR, // 35 RFC 3403 Naming Authority Pointer
|
||||
// NAPTR, // 35 RFC 3403 Naming Authority Pointer
|
||||
NS, // 2 RFC 1035[1] Name server record
|
||||
NSEC, // 47 RFC 4034 Next-Secure record
|
||||
NSEC3, // 50 RFC 5155 NSEC record version 3
|
||||
NSEC3PARAM, // 51 RFC 5155 NSEC3 parameters
|
||||
// NSEC, // 47 RFC 4034 Next-Secure record
|
||||
// NSEC3, // 50 RFC 5155 NSEC record version 3
|
||||
// NSEC3PARAM, // 51 RFC 5155 NSEC3 parameters
|
||||
PTR, // 12 RFC 1035[1] Pointer record
|
||||
RRSIG, // 46 RFC 4034 DNSSEC signature
|
||||
RP, // 17 RFC 1183 Responsible person
|
||||
SIG, // 24 RFC 2535 Signature
|
||||
// RRSIG, // 46 RFC 4034 DNSSEC signature
|
||||
// RP, // 17 RFC 1183 Responsible person
|
||||
// SIG, // 24 RFC 2535 Signature
|
||||
SOA, // 6 RFC 1035[1] and RFC 2308[9] Start of [a zone of] authority record
|
||||
SRV, // 33 RFC 2782 Service locator
|
||||
SSHFP, // 44 RFC 4255 SSH Public Key Fingerprint
|
||||
TA, // 32768 N/A DNSSEC Trust Authorities
|
||||
TKEY, // 249 RFC 2930 Secret key record
|
||||
TLSA, // 52 RFC 6698 TLSA certificate association
|
||||
TSIG, // 250 RFC 2845 Transaction Signature
|
||||
// SRV, // 33 RFC 2782 Service locator
|
||||
// SSHFP, // 44 RFC 4255 SSH Public Key Fingerprint
|
||||
// TA, // 32768 N/A DNSSEC Trust Authorities
|
||||
// TKEY, // 249 RFC 2930 Secret key record
|
||||
// TLSA, // 52 RFC 6698 TLSA certificate association
|
||||
// TSIG, // 250 RFC 2845 Transaction Signature
|
||||
TXT, // 16 RFC 1035[1] Text record
|
||||
ANY, // * 255 RFC 1035[1] All cached records, aka ANY
|
||||
// ANY, // * 255 RFC 1035[1] All cached records, aka ANY
|
||||
AXFR, // 252 RFC 1035[1] Authoritative Zone Transfer
|
||||
IXFR, // 251 RFC 1996 Incremental Zone Transfer
|
||||
OPT, // 41 RFC 6891 Option
|
||||
@ -81,7 +81,6 @@ impl From<RecordType> for &'static str {
|
||||
RecordType::CNAME => "CNAME",
|
||||
RecordType::NS => "NS",
|
||||
RecordType::SOA => "SOA",
|
||||
RecordType::ANY => "ANY",
|
||||
_ => panic!("unsupported RecordType: {:?}", rt),
|
||||
}
|
||||
}
|
||||
@ -107,8 +106,6 @@ impl<'a> From<&'a str> for RecordType {
|
||||
"CNAME" => RecordType::CNAME,
|
||||
"NS" => RecordType::NS,
|
||||
"SOA" => RecordType::SOA,
|
||||
"ANY" => RecordType::ANY,
|
||||
"*" => RecordType::ANY,
|
||||
_ => panic!("unsupported RecordType: {:?}", str),
|
||||
}
|
||||
}
|
||||
@ -134,7 +131,6 @@ impl From<RecordType> for u16 {
|
||||
RecordType::CNAME => 5,
|
||||
RecordType::NS => 2,
|
||||
RecordType::SOA => 6,
|
||||
RecordType::ANY => 255,
|
||||
_ => panic!("unsupported RecordType: {:?}", rt),
|
||||
}
|
||||
}
|
||||
@ -160,7 +156,6 @@ impl From<u16> for RecordType {
|
||||
5 => RecordType::CNAME,
|
||||
2 => RecordType::NS,
|
||||
6 => RecordType::SOA,
|
||||
255 => RecordType::ANY,
|
||||
_ => panic!("unsupported RecordType: {:?}", value),
|
||||
}
|
||||
}
|
||||
|
@ -96,6 +96,13 @@ impl Record {
|
||||
pub fn ttl(&mut self, ttl: i32) -> &mut Self { self.ttl = ttl; self }
|
||||
pub fn rdata(&mut self, rdata: RData) -> &mut Self { self.rdata = rdata; self }
|
||||
|
||||
pub fn get_name(&self) -> &domain::Name { &self.name_labels }
|
||||
pub fn get_rr_type(&self) -> RecordType { self.rr_type }
|
||||
pub fn get_dns_class(&self) -> DNSClass { self.dns_class }
|
||||
pub fn get_ttl(&self) -> i32 { self.ttl }
|
||||
pub fn get_rdata(&self) -> &RData { &self.rdata }
|
||||
|
||||
|
||||
/// parse a resource record line example:
|
||||
/// WARNING: the record_bytes is 100% consumed and destroyed in this parsing process
|
||||
pub fn parse(data: &mut Vec<u8>) -> Record {
|
||||
|
104
src/udp/client.rs
Normal file
104
src/udp/client.rs
Normal file
@ -0,0 +1,104 @@
|
||||
use std::net::*; // we need almost everything in here...
|
||||
use std::cell::Cell;
|
||||
use std::io;
|
||||
|
||||
use super::super::rr::resource::Record;
|
||||
use super::super::rr::dns_class::DNSClass;
|
||||
use super::super::rr::record_type::RecordType;
|
||||
use super::super::rr::domain;
|
||||
use super::super::op::message::Message;
|
||||
use super::super::op::header::{Header, MessageType};
|
||||
use super::super::op::op_code::OpCode;
|
||||
use super::super::op::query::Query;
|
||||
|
||||
pub struct Client {
|
||||
socket: UdpSocket,
|
||||
name_server: SocketAddrV4,
|
||||
next_id: Cell<u16>,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
/// name_server to connect to with default port 53
|
||||
pub fn new(name_server: Ipv4Addr) -> io::Result<Client> {
|
||||
Self::with_port(name_server, 53)
|
||||
}
|
||||
|
||||
/// name_server to connect to, port is the port number that server is listening on (default 53)
|
||||
pub fn with_port(name_server: Ipv4Addr, port: u16) -> io::Result<Client> {
|
||||
// client binds to all addresses...
|
||||
// TODO when the socket_opts interfaces stabilize, need to add timeouts, ttl, etc.
|
||||
let socket = try!(UdpSocket::bind(SocketAddrV4::new(Ipv4Addr::new(0,0,0,0),0)));
|
||||
Ok(Client { socket: socket, name_server: SocketAddrV4::new(name_server, port), next_id: Cell::new(4096) })
|
||||
}
|
||||
|
||||
/// send a DNS query to the name_server specified in Clint.
|
||||
///
|
||||
/// ```
|
||||
/// use std::net::*;
|
||||
///
|
||||
/// use trust_dns::rr::dns_class::DNSClass;
|
||||
/// use trust_dns::rr::record_type::RecordType;
|
||||
/// use trust_dns::rr::domain;
|
||||
/// use trust_dns::rr::record_data::RData;
|
||||
/// use trust_dns::udp::client::Client;
|
||||
///
|
||||
/// let name = domain::Name::with_labels(vec!["www".to_string(), "example".to_string(), "com".to_string()]);
|
||||
/// let client = Client::new(("8.8.8.8").parse().unwrap()).unwrap();
|
||||
/// let response = client.query(name.clone(), DNSClass::IN, RecordType::A).unwrap();
|
||||
///
|
||||
/// let record = &response.get_answers()[0];
|
||||
/// assert_eq!(record.get_name(), &name);
|
||||
/// assert_eq!(record.get_rr_type(), RecordType::A);
|
||||
/// assert_eq!(record.get_dns_class(), DNSClass::IN);
|
||||
///
|
||||
/// if let &RData::A{ ref address } = record.get_rdata() {
|
||||
/// assert_eq!(address, &Ipv4Addr::new(93,184,216,34))
|
||||
/// } else {
|
||||
/// assert!(false);
|
||||
/// }
|
||||
///
|
||||
/// ```
|
||||
pub fn query(&self, name: domain::Name, query_class: DNSClass, query_type: RecordType) -> Result<Message, ()> {
|
||||
// build the message
|
||||
let mut message: Message = Message::new();
|
||||
let id = self.next_id();
|
||||
message.id(id).message_type(MessageType::Query).op_code(OpCode::Query).recursion_desired(true);
|
||||
|
||||
// add the query
|
||||
let mut query: Query = Query::new();
|
||||
query.name(name).query_class(query_class).query_type(query_type);
|
||||
message.add_query(query);
|
||||
|
||||
// get the message bytes and send the query
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
message.write_to(&mut buf);
|
||||
|
||||
// TODO proper error handling
|
||||
// TODO when the socket_opts interfaces stabilize, need to add timeouts, ttl, etc.
|
||||
let bytes_sent = self.socket.send_to(&buf, self.name_server).unwrap();
|
||||
assert_eq!(bytes_sent, buf.len()); // TODO, proper error...
|
||||
|
||||
//----------------------------
|
||||
// now listen for the response
|
||||
//----------------------------
|
||||
|
||||
// the max buffer size we'll except is 4k
|
||||
let mut buf = [0u8; 4096];
|
||||
let (bytes_recv, remote) = self.socket.recv_from(&mut buf).unwrap();
|
||||
|
||||
// TODO change parsers to use Read or something else, so that we don't need to copy here.
|
||||
let mut resp_bytes = buf.to_vec();
|
||||
resp_bytes.truncate(bytes_recv);
|
||||
resp_bytes.reverse();
|
||||
let response = Message::parse(&mut resp_bytes); // TODO, change all parses to return Results...
|
||||
|
||||
assert_eq!(response.get_id(), id); // TODO, better error...
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
fn next_id(&self) -> u16 {
|
||||
let id = self.next_id.get();
|
||||
self.next_id.set(id + 1);
|
||||
id
|
||||
}
|
||||
}
|
1
src/udp/mod.rs
Normal file
1
src/udp/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod client;
|
Loading…
Reference in New Issue
Block a user