client create semantics added

This commit is contained in:
Benjamin Fry 2016-05-30 01:28:25 -07:00
parent 8d51f3b6f8
commit 520cf3d73e
11 changed files with 362 additions and 92 deletions

View File

@ -9,6 +9,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- DNSKEYs auto inserted for added private keys
- New mocked network client tests, to verify zone signing
- NSEC record creation for zone, with tests
- SIG0 validation for Authentication on for dynamic updates
### Fixed
- Added loop on TCP accept requests
@ -24,6 +25,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- All authorities default to IN DNSCLASS now (none others currently supported)
- Cleaned up the Signer interface to support zone signing
- Simplified RData variant implementations
- Improved ENDS and SIG0 parsing on Message deserialization
## 0.5.3 2016-04-07
### Fixed

View File

@ -17,9 +17,10 @@ use std::collections::BTreeMap;
use std::cmp::Ordering;
use chrono::offset::utc::UTC;
use openssl::crypto::pkey::Role;
use ::authority::{UpdateResult, ZoneType, RRSet};
use ::op::{UpdateMessage, ResponseCode, Query};
use ::op::{Message, UpdateMessage, ResponseCode, Query};
use ::rr::{DNSClass, Name, RData, Record, RecordType};
use ::rr::rdata::{NSEC, SIG};
use ::rr::dnssec::Signer;
@ -111,7 +112,7 @@ impl Authority {
}
#[cfg(test)]
fn set_allow_update(&mut self, allow_update: bool) {
pub fn set_allow_update(&mut self, allow_update: bool) {
self.allow_update = allow_update;
}
@ -186,8 +187,9 @@ impl Authority {
self.lookup(&self.origin, RecordType::NS, is_secure)
}
/// [RFC 2136](https://tools.ietf.org/html/rfc2136), DNS Update, April 1997
///
/// ```text
/// RFC 2136 DNS Update April 1997
///
/// 3.2 - Process Prerequisite Section
///
@ -348,8 +350,9 @@ impl Authority {
Ok(())
}
/// [RFC 2136](https://tools.ietf.org/html/rfc2136), DNS Update, April 1997
///
/// ```text
/// RFC 2136 DNS Update April 1997
///
/// 3.3 - Check Requestor's Permissions
///
@ -369,7 +372,7 @@ impl Authority {
/// and restore the zone to its original state before answering the
/// requestor.
/// ```
fn authorize(&self, update_message: &UpdateMessage) -> UpdateResult<()> {
fn authorize(&self, update_message: &Message) -> UpdateResult<()> {
// 3.3.3 - Pseudocode for Permission Checking
//
// if (security policy exists)
@ -386,20 +389,53 @@ impl Authority {
}
// verify sig0, currently the only authorization that is accepted.
let sig0: &[Record] = update_message.get_sig0();
if !sig0.is_empty() {
info!("attempted update rejected due to missing SIG0: {:?}", update_message);
return Err(ResponseCode::Refused);
}
let sig0s: &[Record] = update_message.get_sig0();
debug!("authorizing with: {:?}", sig0s);
if !sig0s.is_empty() && sig0s.iter()
.filter_map(|sig0| if let &RData::SIG(ref sig) = sig0.get_rdata() { Some(sig) } else { None })
.any(|sig| {
let name = sig.get_signer_name();
let keys = self.lookup(name, RecordType::KEY, false);
debug!("found keys {:?}", keys);
keys.iter()
.filter_map(|rr_set| if let &RData::KEY(ref key) = rr_set.get_rdata() { Some(key) } else { None })
.any(|key| {
let pkey = key.get_algorithm().public_key_from_vec(key.get_public_key());
if let Err(error) = pkey {
warn!("public key {:?} of {} could not be used: {}", key, name, error);
return false
}
let pkey = pkey.unwrap();
if pkey.can(Role::Verify) {
let signer: Signer = Signer::new_verifier(*key.get_algorithm(), pkey, sig.get_signer_name().clone());
if signer.verify_message(update_message, sig.get_sig()) {
info!("verified sig: {:?} with key: {:?}", sig, key);
true
} else {
debug!("did not verify sig: {:?} with key: {:?}", sig, key);
false
}
} else {
warn!("{}: can not be used to verify", name);
false
}
})
}) {
return Ok(());
} else {
warn!("no sig0 matched registered records: id {}", update_message.get_id());
}
// getting here, we will always default to rejecting the request
// the code will only ever explcitly return authrorized actions.
Err(ResponseCode::Refused)
}
/// [RFC 2136](https://tools.ietf.org/html/rfc2136), DNS Update, April 1997
///
/// ```text
/// RFC 2136 DNS Update April 1997
///
/// 3.4 - Process Update Section
///
@ -480,8 +516,9 @@ impl Authority {
/// Updates the specified records according to the update section.
///
/// [RFC 2136](https://tools.ietf.org/html/rfc2136), DNS Update, April 1997
///
/// ```text
/// RFC 2136 DNS Update April 1997
///
/// 3.4.2.6 - Table Of Metavalues Used In Update Section
///
@ -650,8 +687,9 @@ impl Authority {
/// Takes the UpdateMessage, extracts the Records, and applies the changes to the record set.
///
/// [RFC 2136](https://tools.ietf.org/html/rfc2136), DNS Update, April 1997
///
/// ```text
/// RFC 2136 DNS Update April 1997
///
/// 3.4 - Process Update Section
///
@ -704,7 +742,7 @@ impl Authority {
///
/// true if any of additions, updates or deletes were made to the zone, false otherwise. Err is
/// returned in the case of bad data, etc.
pub fn update(&mut self, update: &UpdateMessage) -> UpdateResult<bool> {
pub fn update(&mut self, update: &Message) -> UpdateResult<bool> {
// the spec says to authorize after prereqs, seems better to auth first.
try!(self.authorize(update));
try!(self.verify_prerequisites(update.get_pre_requisites()));

View File

@ -175,14 +175,16 @@ impl Catalog {
/// # Arguments
///
/// * `request` - an update message
pub fn update(&self, update: &UpdateMessage) -> Message {
pub fn update(&self, update: &Message) -> Message {
let mut response: Message = Message::new();
response.id(update.get_id());
response.op_code(OpCode::Update);
response.message_type(MessageType::Response);
let zones: &[Query] = update.get_zones();
if zones.len() != 1 || zones[0].get_query_type() != RecordType::SOA {
// TODO: allow SOA updates to create subzones more easily... (not RFC compliant)
if zones.len() != 1 || zones[0].get_query_type() == RecordType::SOA {
response.response_code(ResponseCode::FormErr);
return response;
}

View File

@ -16,6 +16,7 @@ use std::cell::{Cell, RefCell};
use std::collections::HashSet;
use std::sync::Arc as Rc;
use chrono::UTC;
use data_encoding::base32hex;
use openssl::crypto::pkey::Role;
@ -23,7 +24,7 @@ use ::error::*;
use ::rr::{DNSClass, RecordType, Record, RData};
use ::rr::domain;
use ::rr::dnssec::{Signer, TrustAnchor};
use ::op::{ Message, MessageType, OpCode, Query, Edns, ResponseCode };
use ::op::{ Message, MessageType, OpCode, Query, Edns, ResponseCode, UpdateMessage };
use ::serialize::binary::*;
use ::client::ClientConnection;
@ -490,8 +491,6 @@ impl<C: ClientConnection> Client<C> {
fn inner_query(&self, name: &domain::Name, query_class: DNSClass, query_type: RecordType, secure: bool) -> ClientResult<Message> {
debug!("querying: {} {:?}", name, query_type);
// TODO: this isn't DRY, duplicate code with the TCP client
// build the message
let mut message: Message = Message::new();
let id = self.next_id();
@ -517,6 +516,78 @@ impl<C: ClientConnection> Client<C> {
query.name(name.clone()).query_class(query_class).query_type(query_type);
message.add_query(query);
self.send_message(&message)
}
/// Sends a record to create on the server, this will fail if the record exists.
///
/// [RFC 2136](https://tools.ietf.org/html/rfc2136), DNS Update, April 1997
///
/// ```text
/// 2.4.3 - RRset Does Not Exist
///
/// No RRs with a specified NAME and TYPE (in the zone and class denoted
/// by the Zone Section) can exist.
///
/// For this prerequisite, a requestor adds to the section a single RR
/// whose NAME and TYPE are equal to that of the RRset whose nonexistence
/// is required. The RDLENGTH of this record is zero (0), and RDATA
/// field is therefore empty. CLASS must be specified as NONE in order
/// to distinguish this condition from a valid RR whose RDLENGTH is
/// naturally zero (0) (for example, the NULL RR). TTL must be specified
/// as zero (0).
/// ```
///
/// # Arguments
///
/// * `name` - the record name to create
/// * `zone` - the zone name (must match an SOA) to update
/// * `class` - the DNS class
/// * `record_data` - the RData to associate to the new record
/// * `signer` - the signer with private key to use to sign the request
///
/// The update must go to a zone authority (i.e. the server used in the ClientConnection)
pub fn create(&self,
record: Record,
zone_origin: domain::Name,
signer: &Signer) -> ClientResult<Message> {
assert!(zone_origin.zone_of(record.get_name()));
// for updates, the query section is used for the zone
let mut zone: Query = Query::new();
zone.name(zone_origin).query_class(record.get_dns_class()).query_type(record.get_rr_type());
// build the message
let mut message: Message = Message::new();
message.id(self.next_id()).message_type(MessageType::Query).op_code(OpCode::Update).recursion_desired(false);
message.add_zone(zone);
let mut prerequisite = Record::with(record.get_name().clone(), record.get_rr_type(), 0);
prerequisite.dns_class(DNSClass::NONE);
message.add_pre_requisite(prerequisite);
message.add_update(record);
// Extended dns
let mut edns: Edns = Edns::new();
// if secure {
// edns.set_dnssec_ok(true);
// message.authentic_data(true);
// message.checking_disabled(false);
// }
edns.set_max_payload(1500);
edns.set_version(0);
message.set_edns(edns);
// after all other updates to the message, sign it.
message.sign(signer, UTC::now().timestamp() as u32);
self.send_message(&message)
}
fn send_message(&self, message: &Message) -> ClientResult<Message> {
// get the message bytes and send the query
let mut buffer: Vec<u8> = Vec::with_capacity(512);
{
@ -530,7 +601,7 @@ impl<C: ClientConnection> Client<C> {
let mut decoder = BinDecoder::new(&resp_buffer);
let response = try_rethrow!(ClientError::DecodeError, Message::read(&mut decoder));
if response.get_id() != id { return Err(ClientError::IncorrectMessageId{ loc: error_loc!(), got: response.get_id(), expect: id }); }
if response.get_id() != message.get_id() { return Err(ClientError::IncorrectMessageId{ loc: error_loc!(), got: response.get_id(), expect: message.get_id() }); }
Ok(response)
}
@ -546,12 +617,15 @@ impl<C: ClientConnection> Client<C> {
mod test {
use std::net::*;
use chrono::Duration;
use openssl::crypto::pkey::PKey;
use ::authority::Catalog;
use ::authority::authority_tests::{create_example, create_secure_example};
use ::client::{Client, ClientConnection, TestClientConnection};
use ::op::ResponseCode;
use ::rr::{DNSClass, RecordType, domain, RData};
use ::rr::dnssec::TrustAnchor;
use ::rr::{DNSClass, Record, RecordType, domain, RData};
use ::rr::dnssec::{Algorithm, Signer, TrustAnchor};
#[cfg(feature = "ftest")]
use ::tcp::TcpClientConnection;
#[cfg(feature = "ftest")]
@ -782,4 +856,47 @@ mod test {
// let response = response.unwrap();
// assert_eq!(response.get_response_code(), ResponseCode::NXDomain);
// }
#[test]
fn test_create() {
use ::rr::rdata::DNSKEY;
let mut authority = create_example();
authority.set_allow_update(true);
let mut catalog = Catalog::new();
let origin = authority.get_origin().clone();
let mut pkey = PKey::new();
pkey.gen(512);
let signer = Signer::new(Algorithm::RSASHA256,
pkey,
domain::Name::with_labels(vec!["trusted".to_string(), "example".to_string(), "com".to_string()]),
0,
0);
// insert the KEY for the trusted.example.com
let mut auth_key = Record::with(domain::Name::with_labels(vec!["trusted".to_string(), "example".to_string(), "com".to_string()]),
RecordType::KEY,
Duration::minutes(5).num_seconds() as u32);
auth_key.rdata(RData::KEY(DNSKEY::new(false, false, false, signer.get_algorithm(), signer.get_public_key())));
authority.upsert(auth_key, 0);
catalog.upsert(authority.get_origin().clone(), authority);
let client = Client::new(TestClientConnection::new(&catalog));
// add a record
let mut record = Record::with(domain::Name::with_labels(vec!["new".to_string(), "example".to_string(), "com".to_string()]),
RecordType::A,
Duration::minutes(5).num_seconds() as u32);
record.rdata(RData::A(Ipv4Addr::new(100,10,100,10)));
let result = client.create(record.clone(), origin, &signer).expect("create failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
let result = client.query(record.get_name(), record.get_dns_class(), record.get_rr_type()).expect("query failed");
assert_eq!(result.get_response_code(), ResponseCode::NoError);
assert_eq!(result.get_answers().len(), 1);
assert_eq!(result.get_answers()[0], record);
}
}

View File

@ -38,6 +38,7 @@ pub enum DecodeError {
IncorrectRDataLengthRead(usize, usize),
BadPublicKey,
SslError(SslError),
MoreThanOneEdns,
}
impl fmt::Debug for DecodeError {
@ -66,6 +67,7 @@ impl fmt::Display for DecodeError {
DecodeError::IncorrectRDataLengthRead(ref read, ref rdata_length) => write!(f, "IncorrectRDataLengthRead read: {}, expected: {}", read, rdata_length),
DecodeError::BadPublicKey => write!(f, "BadPublicKey"),
DecodeError::SslError(ref err) => err.fmt(f),
DecodeError::MoreThanOneEdns => write!(f, "More than one EDNS record is already set"),
}
}
}
@ -90,6 +92,7 @@ impl Error for DecodeError {
DecodeError::IncorrectRDataLengthRead(..) => "IncorrectRDataLengthRead",
DecodeError::BadPublicKey => "BadPublicKey",
DecodeError::SslError(ref err) => err.description(),
DecodeError::MoreThanOneEdns => "More than one EDNS record is already set",
}
}

View File

@ -23,7 +23,7 @@ use ::rr::resource::Record;
use ::rr::domain::Name;
use ::rr::{RData, RecordType, DNSClass};
use ::rr::rdata::SIG;
use ::serialize::binary::*;
use ::serialize::binary::{BinEncoder, BinDecoder, BinSerializable, EncodeMode};
use ::error::*;
use ::rr::dnssec::Signer;
@ -258,18 +258,19 @@ impl Message {
/// this happens implicitly on write_to, so no need to call before write_to
#[cfg(test)]
pub fn update_counts(&mut self) -> &mut Self {
self.header = self.update_header_counts(true, true);
self.header = self.update_header_counts(true);
self
}
fn update_header_counts(&self, include_edns: bool, include_sig0: bool) -> Header {
fn update_header_counts(&self, include_sig0: bool) -> Header {
assert!(self.queries.len() <= u16::max_value() as usize);
assert!(self.answers.len() <= u16::max_value() as usize);
assert!(self.name_servers.len() <= u16::max_value() as usize);
assert!(self.additionals.len() + self.sig0.len() <= u16::max_value() as usize);
let mut additional_count = self.additionals.len();
if include_edns && self.edns.is_some() { additional_count += 1 }
if self.edns.is_some() { additional_count += 1 }
if include_sig0 { additional_count += self.sig0.len() };
self.header.clone(
@ -279,12 +280,39 @@ impl Message {
additional_count as u16)
}
fn read_records(decoder: &mut BinDecoder, count: usize) -> DecodeResult<Vec<Record>> {
fn read_records(decoder: &mut BinDecoder, count: usize, is_additional: bool) -> DecodeResult<(Vec<Record>, Option<Edns>, Vec<Record>)> {
let mut records: Vec<Record> = Vec::with_capacity(count);
let mut edns: Option<Edns> = None;
let mut sig0s: Vec<Record> = Vec::with_capacity(if is_additional { 1 } else { 0 });
// sig0 must be last, once this is set, disable.
let mut saw_sig0 = false;
for _ in 0 .. count {
records.push(try!(Record::read(decoder)))
let record = try!(Record::read(decoder));
if !is_additional {
if saw_sig0 { return Err(DecodeError::Sig0NotLast) } // SIG0 must be last
records.push(record)
} else {
match record.get_rr_type() {
RecordType::SIG => {
saw_sig0 = true;
sig0s.push(record);
},
RecordType::OPT => {
if saw_sig0 { return Err(DecodeError::Sig0NotLast) } // SIG0 must be last
if edns.is_some() { return Err(DecodeError::MoreThanOneEdns) }
edns = Some((&record).into());
},
_ => {
if saw_sig0 { return Err(DecodeError::Sig0NotLast) } // SIG0 must be last
records.push(record);
}
}
}
}
Ok(records)
Ok((records, edns, sig0s))
}
fn emit_records(encoder: &mut BinEncoder, records: &Vec<Record>) -> EncodeResult {
@ -339,6 +367,7 @@ impl UpdateMessage for Message {
// TODO: where's the 'right' spot for this function
fn sign(&mut self, signer: &Signer, inception_time: u32) {
debug!("signing message: {:?}", self);
let signature: Vec<u8> = signer.sign_message(self);
let key_tag: u16 = signer.calculate_key_tag();
@ -360,6 +389,7 @@ impl UpdateMessage for Message {
let expiration_time: u32 = inception_time + (5 * 60); // +5 minutes in seconds
sig0.rr_type(RecordType::SIG);
sig0.rdata(
RData::SIG(SIG::new(
// type covered in SIG(0) is 0 which is what makes this SIG0 vs a standard SIG
@ -379,6 +409,10 @@ impl UpdateMessage for Message {
signature,
)
));
debug!("sig0: {:?}", sig0);
self.add_sig0(sig0);
}
}
@ -396,47 +430,11 @@ impl BinSerializable<Message> for Message {
// get all counts before header moves
let answer_count = header.get_answer_count() as usize;
let name_server_count = header.get_name_server_count() as usize;
let mut additional_count = header.get_additional_count() as usize;
let additional_count = header.get_additional_count() as usize;
let answers: Vec<Record> = try!(Self::read_records(decoder, answer_count));
let name_servers: Vec<Record> = try!(Self::read_records(decoder, name_server_count));
let mut additionals: Vec<Record> = try!(Self::read_records(decoder, additional_count));
let mut sig0: Vec<Record> = Vec::new();
let mut edns: Option<Edns> = None;
// get the sig0's and remove from from the additional section, and decrement the counts
// this will allow for the Message to be verified directly.
// TODO: this would be cleaner as a recursive function...
loop {
// TODO: make a function is_a() on Record for Type to RData Validation
if let Some(record) = additionals.last() {
if record.get_rr_type() != RecordType::SIG {
// no more sig0's break
break;
}
} else {
// nothing in the list
break;
}
// we're only getting here if the SIG0 record is what was found
let record = additionals.pop().unwrap();
assert!(record.get_rr_type() == RecordType::SIG);
sig0.push(record);
additional_count -= 1;
}
// edns goes from the other direction, but unlike sig0, edns does not pop off the stack
// will loop through them all to verify that there is only one OPT record
for r in additionals.iter() {
match r.get_rr_type() {
RecordType::OPT => edns = Some(r.into()),
RecordType::SIG => return Err(DecodeError::Sig0NotLast),
_ =>(),
}
}
additionals.shrink_to_fit();
let (answers, _, _) = try!(Self::read_records(decoder, answer_count, false));
let (name_servers, _, _) = try!(Self::read_records(decoder, name_server_count, false));
let (additionals, edns, sig0) = try!(Self::read_records(decoder, additional_count, true));
Ok(Message {
header: header,
@ -451,8 +449,8 @@ impl BinSerializable<Message> for Message {
fn emit(&self, encoder: &mut BinEncoder) -> EncodeResult {
// clone the header to set the counts lazily
let include_sig0: bool = encoder.mode() != EncodeMode::Verify;
try!(self.update_header_counts(include_sig0, include_sig0).emit(encoder));
let include_sig0: bool = encoder.mode() != EncodeMode::Signing;
try!(self.update_header_counts(include_sig0).emit(encoder));
for q in &self.queries {
try!(q.emit(encoder));
@ -465,14 +463,15 @@ impl BinSerializable<Message> for Message {
try!(Self::emit_records(encoder, &self.name_servers));
try!(Self::emit_records(encoder, &self.additionals));
if let Some(edns) = self.get_edns() {
// need to commit the error code
try!(Record::from(edns).emit(encoder));
}
// this is a little hacky, but if we are Verifying a signature, i.e. the original Message
// then the SIG0 records should not be encoded and the edns record (if it exists) is already
// part of the additionals section.
if include_sig0 {
if let Some(edns) = self.get_edns() {
// need to commit the error code
try!(Record::from(edns).emit(encoder));
}
try!(Self::emit_records(encoder, &self.sig0));
}
Ok(())

View File

@ -21,7 +21,7 @@ use openssl::crypto::pkey::{PKey, Role};
use ::op::Message;
use ::rr::dnssec::{Algorithm, DigestType};
use ::rr::{DNSClass, Name, Record, RecordType, RData};
use ::serialize::binary::{BinEncoder, BinSerializable};
use ::serialize::binary::{BinEncoder, BinSerializable, EncodeMode};
use ::rr::rdata::{sig, DNSKEY};
@ -123,11 +123,7 @@ impl Signer {
pub fn calculate_key_tag(&self) -> u16 {
let mut ac: usize = 0;
// TODO This might need to be the RAW key, as opposed to the DER formatted public key
// would need to extract the RAW public key: https://en.wikipedia.org/wiki/X.690#DER_encoding
// TODO use insert i with known sizes for optimized loop unrolling.
for (i,k) in self.pkey.save_pub().iter().enumerate() {
for (i,k) in self.get_public_key().iter().enumerate() {
ac += if i & 0x0001 == 0x0001 { *k as usize } else { (*k as usize) << 8 };
}
@ -142,13 +138,19 @@ impl Signer {
let mut buf: Vec<u8> = Vec::with_capacity(512);
{
let mut encoder: BinEncoder = BinEncoder::new(&mut buf);
let mut encoder: BinEncoder = BinEncoder::with_mode(&mut buf, EncodeMode::Signing);
message.emit(&mut encoder).unwrap(); // coding error if this panics (i think?)
}
DigestType::from(self.algorithm).hash(&buf)
}
/// Signs the given message, returning the signature bytes.
///
/// # Arguments
///
/// * `message` - the message to sign
///
/// ```text
/// 4.1.8.1 Calculating Transaction and Request SIGs
///
@ -199,7 +201,23 @@ impl Signer {
pub fn sign_message(&self, message: &Message) -> Vec<u8> {
assert!(self.pkey.can(Role::Sign)); // this is bad code, not expected in regular runtime
let hash = self.hash_message(message);
self.pkey.sign_with_hash(&hash, DigestType::from(self.algorithm).to_hash())
self.sign(&hash)
}
/// Verifies a message with the against the given signature
///
/// # Arguments
///
/// `message` - the message to verify
/// `signature` - the signature to use for validation
///
/// # Return value
///
/// `true` if the message could be validated against the signature, `false` otherwise
pub fn verify_message(&self, message: &Message, signature: &[u8]) -> bool {
assert!(self.pkey.can(Role::Verify)); // this is bad code, not expected in regular runtime
let hash = self.hash_message(message);
self.verify(&hash, signature)
}
// RFC 4035 DNSSEC Protocol Modifications March 2005
@ -564,6 +582,40 @@ impl Signer {
}
}
#[test]
fn test_sign_and_verify_message_sig0() {
use ::rr::Name;
use ::op::{Message, Query, UpdateMessage};
let origin: Name = Name::parse("example.com.", None).unwrap();
let mut question: Message = Message::new();
let mut query: Query = Query::new();
query.name(origin.clone());
question.add_query(query);
let mut pkey = PKey::new();
pkey.gen(512);
let signer = Signer::new(Algorithm::RSASHA256, pkey, Name::root(), u32::max_value(), 0);
let sig = signer.sign_message(&question);
println!("sig: {:?}", sig);
assert!(!sig.is_empty());
assert!(signer.verify_message(&question, &sig));
// now test that the sig0 record works correctly.
assert!(question.get_sig0().is_empty());
question.sign(&signer, 0);
assert!(!question.get_sig0().is_empty());
let sig = signer.sign_message(&question);
println!("sig after sign: {:?}", sig);
if let &RData::SIG(ref sig) = question.get_sig0()[0].get_rdata() {
assert!(signer.verify_message(&question, sig.get_sig()));
}
}
#[test]
fn test_hash_rrset() {
use ::rr::{Name, RecordType};

View File

@ -221,6 +221,51 @@ pub enum RData {
// digest algorithm is SHA-1, which produces a 20 octet digest.
DS(DS),
// RFC 2535 DNS Security Extensions March 1999
//
// 3.1 KEY RDATA format
//
// The RDATA for a KEY RR consists of flags, a protocol octet, the
// algorithm number octet, and the public key itself. The format is as
// follows:
//
//
//
//
//
//
//
//
//
//
//
//
//
// 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | flags | protocol | algorithm |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | /
// / public key /
// / /
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
//
// The KEY RR is not intended for storage of certificates and a separate
// certificate RR has been developed for that purpose, defined in [RFC
// 2538].
//
// The meaning of the KEY RR owner name, flags, and protocol octet are
// described in Sections 3.1.1 through 3.1.5 below. The flags and
// algorithm must be examined before any data following the algorithm
// octet as they control the existence and format of any following data.
// The algorithm and public key fields are described in Section 3.2.
// The format of the public key is algorithm dependent.
//
// KEY RRs do not specify their validity period but their authenticating
// SIG RR(s) do as described in Section 4 below.
KEY(DNSKEY),
// 3.3.9. MX RDATA format
//
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
@ -601,6 +646,7 @@ impl RData {
RecordType::ANY => panic!("parsing ANY doesn't make sense"),
RecordType::AXFR => panic!("parsing AXFR doesn't make sense"),
RecordType::CNAME => RData::CNAME(try!(rdata::name::parse(tokens, origin))),
RecordType::KEY => panic!("KEY should be dynamically generated"),
RecordType::DNSKEY => panic!("DNSKEY should be dynamically generated"),
RecordType::DS => panic!("DS should be dynamically generated"),
RecordType::IXFR => panic!("parsing IXFR doesn't make sense"),
@ -640,6 +686,7 @@ impl RData {
rt @ RecordType::ANY => return Err(DecodeError::UnknownRecordTypeValue(rt.into())),
rt @ RecordType::AXFR => return Err(DecodeError::UnknownRecordTypeValue(rt.into())),
RecordType::CNAME => {debug!("reading CNAME"); RData::CNAME(try!(rdata::name::read(decoder))) },
RecordType::KEY => {debug!("reading KEY"); RData::KEY(try!(rdata::dnskey::read(decoder, rdata_length))) },
RecordType::DNSKEY => {debug!("reading DNSKEY"); RData::DNSKEY(try!(rdata::dnskey::read(decoder, rdata_length))) },
RecordType::DS => {debug!("reading DS"); RData::DS(try!(rdata::ds::read(decoder, rdata_length))) },
rt @ RecordType::IXFR => return Err(DecodeError::UnknownRecordTypeValue(rt.into())),
@ -672,6 +719,7 @@ impl RData {
RData::AAAA(ref address) => rdata::aaaa::emit(encoder, address),
RData::CNAME(ref name) => rdata::name::emit(encoder, name),
RData::DS(ref ds) => rdata::ds::emit(encoder, ds),
RData::KEY(ref key) => rdata::dnskey::emit(encoder, key),
RData::DNSKEY(ref dnskey) => rdata::dnskey::emit(encoder, dnskey),
RData::MX(ref mx) => rdata::mx::emit(encoder, mx),
RData::NULL(ref null) => rdata::null::emit(encoder, null),
@ -698,6 +746,7 @@ impl<'a> From<&'a RData> for RecordType {
RData::AAAA(..) => RecordType::AAAA,
RData::CNAME(..) => RecordType::CNAME,
RData::DS(..) => RecordType::DS,
RData::KEY(..) => RecordType::KEY,
RData::DNSKEY(..) => RecordType::DNSKEY,
RData::MX(..) => RecordType::MX,
RData::NS(..) => RecordType::NS,

View File

@ -46,7 +46,7 @@ pub enum RecordType {
// HIP, // 55 RFC 5205 Host Identity Protocol
// IPSECKEY, // 45 RFC 4025 IPsec Key
IXFR, // 251 RFC 1996 Incremental Zone Transfer
// KEY, // 25 RFC 2535[3] and RFC 2930[4] Key record
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
@ -115,6 +115,7 @@ impl RecordType {
5 => Ok(RecordType::CNAME),
48 => Ok(RecordType::DNSKEY),
43 => Ok(RecordType::DS),
25 => Ok(RecordType::KEY),
15 => Ok(RecordType::MX),
2 => Ok(RecordType::NS),
47 => Ok(RecordType::NSEC),
@ -171,6 +172,7 @@ impl From<RecordType> for &'static str {
RecordType::DNSKEY => "DNSKEY",
RecordType::DS => "DS",
RecordType::IXFR => "IXFR",
RecordType::KEY => "KEY",
RecordType::MX => "MX",
RecordType::NULL => "NULL",
RecordType::NS => "NS",
@ -205,6 +207,7 @@ impl From<RecordType> for u16 {
RecordType::ANY => 255,
RecordType::AXFR => 252,
RecordType::CNAME => 5,
RecordType::KEY => 25,
RecordType::DNSKEY => 48,
RecordType::DS => 43,
RecordType::IXFR => 251,

View File

@ -16,7 +16,6 @@
//! resource record implementation
use std::net::Ipv4Addr;
use std::sync::Arc as Rc;
use std::cmp::Ordering;
@ -94,11 +93,10 @@ pub struct Record {
}
impl Record {
/**
* Creates a not very useful empty record, use the setters to build a more useful object
* There are no optional elements in this object, defaults are an empty name, type A, class IN,
* ttl of 0 and the 0.0.0.0 ip address.
*/
/// Creates a default record, use the setters to build a more useful object.
///
/// There are no optional elements in this object, defaults are an empty name, type A, class IN,
/// ttl of 0 and the 0.0.0.0 ip address.
pub fn new() -> Record {
Record {
// TODO: these really should all be Optionals, I was lazy.
@ -106,10 +104,17 @@ impl Record {
rr_type: RecordType::A,
dns_class: DNSClass::IN,
ttl: 0,
rdata: RData::A(Ipv4Addr::new(0,0,0,0))
rdata: RData::NULL(NULL::new())
}
}
/// Create a record with the specified initial values.
///
/// # Arguments
///
/// * `name` - name of the resource records
/// * `rr_type` - the record type
/// * `ttl` - time-to-live is the amount of time this record should be cached before refreshing
pub fn with(name: domain::Name, rr_type: RecordType, ttl: u32) -> Record {
Record {
name_labels: name,

View File

@ -180,4 +180,4 @@ impl<'a> BinEncoder<'a> {
/// In the Verify mode there maybe some things which are encoded differently, e.g. SIG0 records
/// should not be included in the additional count and not in the encoded data when in Verify
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum EncodeMode { Verify, Normal }
pub enum EncodeMode { Signing, Normal }