wrap some rr fields in flags/enum types with some derived help, add [c]dnskey tag calculation
This commit is contained in:
parent
155b34a17a
commit
f5a6ce44c7
@ -65,6 +65,14 @@ impl DnsTextData for HexShortBlob {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for HexShortBlob {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
// 16-bit length, uses decimal length + base64 encoding (if length > 0)
|
||||
// for text representation.
|
||||
//
|
||||
@ -116,6 +124,14 @@ impl DnsTextData for Base64LongBlob {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for Base64LongBlob {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// No length byte (or restriction), just all data to end of record. uses
|
||||
/// base64 encoding for text representation, whitespace allowed, padding
|
||||
/// required.
|
||||
@ -154,6 +170,14 @@ impl DnsTextData for Base64RemainingBlob {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for Base64RemainingBlob {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// No length byte (or restriction), just all data to end of record. uses
|
||||
/// hex encoding for text representation, whitespace allowed.
|
||||
///
|
||||
@ -187,6 +211,14 @@ impl DnsTextData for HexRemainingBlob {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for HexRemainingBlob {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// No length byte (or restriction), just all data to end of record. uses
|
||||
/// hex encoding for text representation, whitespace allowed.
|
||||
///
|
||||
@ -226,3 +258,11 @@ impl DnsTextData for HexRemainingBlobNotEmpty {
|
||||
write!(f, "{}", HEXLOWER_PERMISSIVE_ALLOW_WS.encode(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for HexRemainingBlobNotEmpty {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
9
lib/dnsbox-base/src/common_types/caa.rs
Normal file
9
lib/dnsbox-base/src/common_types/caa.rs
Normal file
@ -0,0 +1,9 @@
|
||||
use crate::ser::{packet::DnsPacketData, text::DnsTextData};
|
||||
|
||||
#[dnsbox_derive::native_flags(u8)]
|
||||
#[derive(DnsPacketData, DnsTextData)]
|
||||
/// Flags for the CAA RR
|
||||
pub enum CaaFlags {
|
||||
/// Issuer Critical Flag
|
||||
CRITICAL = 0x01, // bit "7"
|
||||
}
|
125
lib/dnsbox-base/src/common_types/dnssec.rs
Normal file
125
lib/dnsbox-base/src/common_types/dnssec.rs
Normal file
@ -0,0 +1,125 @@
|
||||
use crate::ser::{packet::DnsPacketData, text::DnsTextData};
|
||||
|
||||
// https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml
|
||||
#[dnsbox_derive::native_enum(u8)]
|
||||
#[derive(DnsPacketData, DnsTextData)]
|
||||
pub enum DnsSecAlgorithm {
|
||||
/// Delete DS
|
||||
// [RFC4034][RFC4398][RFC8078]
|
||||
DELETE = 0,
|
||||
/// RSA/MD5 (deprecated, see 5)
|
||||
// [RFC3110][RFC4034]
|
||||
RSAMD5 = 1,
|
||||
/// Diffie-Hellman
|
||||
// [RFC2539][proposed standard]
|
||||
DH = 2,
|
||||
/// DSA/SHA1
|
||||
// [RFC3755][proposed standard][RFC2536][proposed standard][Federal Information Processing Standards Publication (FIPS PUB) 186, Digital Signature Standard, 18 May 1994.][Federal Information Processing Standards Publication (FIPS PUB) 180-1, Secure Hash Standard, 17 April 1995. (Supersedes FIPS PUB 180 dated 11 May 1993.)]
|
||||
DSA = 3,
|
||||
|
||||
// Reserved: 4 [RFC6725]
|
||||
|
||||
/// RSA/SHA-1
|
||||
// [RFC3110][RFC4034]
|
||||
RSASHA1 = 5,
|
||||
/// DSA-NSEC3-SHA1
|
||||
// [RFC5155][proposed standard]
|
||||
DSA_NSEC3_SHA1 = 6,
|
||||
/// RSASHA1-NSEC3-SHA1
|
||||
// [RFC5155][proposed standard]
|
||||
RSASHA1_NSEC3_SHA1 = 7,
|
||||
/// RSA/SHA-256
|
||||
// [RFC5702][proposed standard]
|
||||
RSASHA256 = 8,
|
||||
|
||||
// Reserved: 9 [RFC6725]
|
||||
|
||||
/// RSA/SHA-512
|
||||
// [RFC5702][proposed standard]
|
||||
RSASHA512 = 10,
|
||||
|
||||
// Reserved: 11 [RFC6725]
|
||||
|
||||
/// GOST R 34.10-2001
|
||||
// [RFC5933][standards track]
|
||||
ECC_GOST = 12,
|
||||
/// ECDSA Curve P-256 with SHA-256
|
||||
// [RFC6605][standards track]
|
||||
ECDSAP256SHA256 = 13,
|
||||
/// ECDSA Curve P-384 with SHA-384
|
||||
// [RFC6605][standards track]
|
||||
ECDSAP384SHA384 = 14,
|
||||
/// Ed25519
|
||||
// [RFC8080][standards track]
|
||||
ED25519 = 15,
|
||||
/// Ed448
|
||||
// [RFC8080][standards track]
|
||||
ED448 = 16,
|
||||
/// Reserved for Indirect Keys
|
||||
// [RFC4034][proposed standard]
|
||||
INDIRECT = 252,
|
||||
/// private algorithm
|
||||
// [RFC4034]
|
||||
PRIVATEDNS = 253,
|
||||
/// private algorithm OID
|
||||
// [RFC4034]
|
||||
PRIVATEOID = 254,
|
||||
|
||||
// Reserved: 255 [RFC4034][proposed standard]
|
||||
}
|
||||
|
||||
#[dnsbox_derive::native_flags(u16)]
|
||||
#[derive(DnsPacketData, DnsTextData)]
|
||||
/// Flags for the DNSKEY RR
|
||||
pub enum DnskeyFlags {
|
||||
ZONE_KEY = 0x0100, // bit "7"
|
||||
/// secure entry point, SEP
|
||||
SECURE_ENTRY_POINT = 0x0001, // bit "15"
|
||||
}
|
||||
|
||||
#[dnsbox_derive::native_enum(u8)]
|
||||
#[derive(DnsPacketData, DnsTextData)]
|
||||
/// Protocol for the DNSKEY RR (only DNSSEC(3) is valid for DNSKEY)
|
||||
pub enum DnskeyProtocol {
|
||||
// reserved: 0x00
|
||||
TLS = 0x01,
|
||||
EMAIL = 0x02,
|
||||
DNSSEC = 0x03,
|
||||
IPSEC = 0x04,
|
||||
ALL = 0xff,
|
||||
}
|
||||
|
||||
// https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml
|
||||
#[dnsbox_derive::native_enum(u8)]
|
||||
#[derive(DnsPacketData, DnsTextData)]
|
||||
pub enum DnsSecDigestAlgorithm {
|
||||
// Reserved: 0 [RFC3658]
|
||||
SHA1 = 0x01, // [RFC3658]
|
||||
SHA256 = 0x02, // [RFC4509]
|
||||
GOST_R_34_11_94 = 0x03, // [RFC5933]
|
||||
SHA384 = 0x04, // [RFC6605]
|
||||
}
|
||||
|
||||
// https://www.iana.org/assignments/dnssec-nsec3-parameters/dnssec-nsec3-parameters.xhtml
|
||||
#[dnsbox_derive::native_flags(u8)]
|
||||
#[derive(DnsPacketData, DnsTextData)]
|
||||
/// Flags for the NSEC3 RR
|
||||
pub enum Nsec3Flags {
|
||||
OPT_OUT = 0x01, // bit "7"
|
||||
}
|
||||
|
||||
// https://www.iana.org/assignments/dnssec-nsec3-parameters/dnssec-nsec3-parameters.xhtml
|
||||
#[dnsbox_derive::native_flags(u8)]
|
||||
#[derive(DnsPacketData, DnsTextData)]
|
||||
/// Flags for the NSEC3PARAM RR
|
||||
pub enum Nsec3ParamFlags {
|
||||
// reserved: Nsec3Flags::OPT_OUT bit "7" (0x01)
|
||||
}
|
||||
|
||||
// https://www.iana.org/assignments/dnssec-nsec3-parameters/dnssec-nsec3-parameters.xhtml
|
||||
#[dnsbox_derive::native_enum(u8)]
|
||||
#[derive(DnsPacketData, DnsTextData)]
|
||||
pub enum Nsec3Algorithm {
|
||||
// Reserved: 0 [RFC5155]
|
||||
SHA1 = 0x01, // [RFC5155]
|
||||
}
|
@ -3,20 +3,26 @@ pub mod classes;
|
||||
pub mod name;
|
||||
pub mod text;
|
||||
pub mod types;
|
||||
mod caa;
|
||||
mod dnssec;
|
||||
mod eui;
|
||||
mod nsec;
|
||||
mod nxt;
|
||||
mod sig;
|
||||
mod sshfp;
|
||||
mod time;
|
||||
mod uri;
|
||||
|
||||
pub use self::binary::{HexShortBlob, Base64LongBlob, Base64RemainingBlob, HexRemainingBlob, HexRemainingBlobNotEmpty};
|
||||
pub use self::classes::Class;
|
||||
pub use self::caa::{CaaFlags};
|
||||
pub use self::dnssec::{DnsSecAlgorithm, DnskeyFlags, DnskeyProtocol, DnsSecDigestAlgorithm, Nsec3Flags, Nsec3ParamFlags, Nsec3Algorithm};
|
||||
pub use self::eui::{EUI48Addr, EUI64Addr};
|
||||
pub use self::name::{DnsName, DnsCanonicalName, DnsCompressedName};
|
||||
pub use self::nsec::{NsecTypeBitmap, NextHashedOwnerName};
|
||||
pub use self::nxt::NxtTypeBitmap;
|
||||
pub use self::sig::OptionalTTL;
|
||||
pub use self::sshfp::{SshFpAlgorithm, SshFpType};
|
||||
pub use self::text::{ShortText, LongText, UnquotedShortText, RemainingText};
|
||||
pub use self::time::{Time, TimeStrict, Time48};
|
||||
pub use self::types::Type;
|
||||
|
25
lib/dnsbox-base/src/common_types/sshfp.rs
Normal file
25
lib/dnsbox-base/src/common_types/sshfp.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use crate::ser::{packet::DnsPacketData, text::DnsTextData};
|
||||
|
||||
// https://www.iana.org/assignments/dns-sshfp-rr-parameters/dns-sshfp-rr-parameters.xml
|
||||
|
||||
#[dnsbox_derive::native_enum(u8)]
|
||||
#[derive(DnsPacketData, DnsTextData)]
|
||||
pub enum SshFpAlgorithm {
|
||||
// Reserved: 0
|
||||
|
||||
// [RFC4255]
|
||||
RSA = 1,
|
||||
// [RFC4255]
|
||||
DSA = 2,
|
||||
// [RFC6594]
|
||||
ECDSA = 3,
|
||||
// [RFC7479]
|
||||
ED25519 = 4,
|
||||
}
|
||||
|
||||
#[dnsbox_derive::native_enum(u8)]
|
||||
#[derive(DnsPacketData, DnsTextData)]
|
||||
pub enum SshFpType {
|
||||
SHA1 = 1,
|
||||
SHA256 = 2,
|
||||
}
|
@ -179,7 +179,7 @@ pub struct NSAP_PTR {
|
||||
#[RRClass(ANY)]
|
||||
pub struct SIG {
|
||||
pub rr_type: Type,
|
||||
pub algorithm: u8,
|
||||
pub algorithm: DnsSecAlgorithm,
|
||||
pub labels: u8,
|
||||
// RFC says this can be omitted in text form if it is the same as
|
||||
// the TTL on the SIG record. not supported to be omitted here
|
||||
@ -196,8 +196,8 @@ pub struct SIG {
|
||||
#[RRClass(ANY)]
|
||||
pub struct KEY {
|
||||
pub flags: u16,
|
||||
pub protocol: u8,
|
||||
pub algorithm: u8,
|
||||
pub protocol: DnskeyProtocol,
|
||||
pub algorithm: DnsSecAlgorithm,
|
||||
pub public_key: Base64RemainingBlob,
|
||||
}
|
||||
|
||||
@ -274,9 +274,10 @@ pub struct KX {
|
||||
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
|
||||
// #[RRClass(?)]
|
||||
pub struct CERT {
|
||||
// https://www.iana.org/assignments/cert-rr-types/cert-rr-types.xhtml
|
||||
pub cert_type: u16,
|
||||
pub key_tag: u16,
|
||||
pub algorithm: u8,
|
||||
pub algorithm: DnsSecAlgorithm,
|
||||
pub certificate: Base64RemainingBlob,
|
||||
}
|
||||
|
||||
@ -304,16 +305,16 @@ pub struct DNAME {
|
||||
#[RRClass(ANY)]
|
||||
pub struct DS {
|
||||
pub key_tag: u16,
|
||||
pub algorithm: u8,
|
||||
pub digest_type: u8,
|
||||
pub algorithm: DnsSecAlgorithm,
|
||||
pub digest_type: DnsSecDigestAlgorithm,
|
||||
pub digest: HexRemainingBlob,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
|
||||
// #[RRClass(?)]
|
||||
pub struct SSHFP {
|
||||
pub algorithm: u8,
|
||||
pub fingerprint_type: u8,
|
||||
pub algorithm: SshFpAlgorithm,
|
||||
pub fingerprint_type: SshFpType,
|
||||
// RFC 4255 doesn't specify whether whitespace is allowed.
|
||||
// `HexRemainingBlob` allows whitespace.
|
||||
pub fingerprint: HexRemainingBlob,
|
||||
@ -325,7 +326,7 @@ pub use super::weird_structs::IPSECKEY;
|
||||
#[RRClass(ANY)]
|
||||
pub struct RRSIG {
|
||||
pub rr_type: Type,
|
||||
pub algorithm: u8,
|
||||
pub algorithm: DnsSecAlgorithm,
|
||||
pub labels: u8,
|
||||
pub original_ttl: u32,
|
||||
pub signature_expiration: Time,
|
||||
@ -345,12 +346,55 @@ pub struct NSEC {
|
||||
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
|
||||
#[RRClass(ANY)]
|
||||
pub struct DNSKEY {
|
||||
pub flags: u16,
|
||||
pub protocol: u8,
|
||||
pub algorithm: u8,
|
||||
pub flags: DnskeyFlags,
|
||||
pub protocol: DnskeyProtocol, // should always be DNSSEC (3) according to RFC 4034
|
||||
pub algorithm: DnsSecAlgorithm,
|
||||
pub public_key: Base64RemainingBlob,
|
||||
}
|
||||
|
||||
impl DNSKEY {
|
||||
fn alg1_tag(&self) -> u16 {
|
||||
let key: &[u8] = &self.public_key;
|
||||
if key.is_empty() { return 0; } // not enough data
|
||||
let pkey;
|
||||
if 0 == key[0] {
|
||||
// two-byte length encoding of exponent
|
||||
if key.len() < 3 { return 0; } // not enough data
|
||||
let explen = ((key[1] as u16) << 8) + (key[2] as u16);
|
||||
if explen < 256 { return 0; } // should have used shorter length encoding
|
||||
if key.len() < 3 + (explen as usize) { return 0; } // not enough data
|
||||
pkey = &key[3 + (explen as usize)..];
|
||||
} else {
|
||||
// one-byte length encoding of exponent
|
||||
let explen = key[0];
|
||||
if key.len() < 1 + (explen as usize) { return 0; } // not enough data
|
||||
pkey = &key[1 + (explen as usize)..];
|
||||
}
|
||||
if pkey.len() < 3 { return 0; } // not enough data
|
||||
((pkey[pkey.len() - 3] as u16) << 8) + (pkey[pkey.len() - 3] as u16)
|
||||
}
|
||||
|
||||
/// calculate key tag
|
||||
pub fn tag(&self) -> u16 {
|
||||
if self.algorithm == DnsSecAlgorithm::RSAMD5 { return self.alg1_tag(); }
|
||||
|
||||
let mut sum = 0u32;
|
||||
|
||||
sum += self.flags.0 as u32;
|
||||
sum += (self.protocol.0 as u32) << 8;
|
||||
sum += self.algorithm.0 as u32;
|
||||
|
||||
let key: &[u8] = &self.public_key;
|
||||
for i in 0..key.len() {
|
||||
let v = key[i] as u32;
|
||||
let v = if 0 == i & 1 { v << 8 } else { v };
|
||||
sum = sum.wrapping_add(v);
|
||||
}
|
||||
|
||||
sum.wrapping_add(sum >> 16) as u16
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
|
||||
#[RRClass(IN)]
|
||||
pub struct DHCID {
|
||||
@ -360,8 +404,8 @@ pub struct DHCID {
|
||||
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
|
||||
#[RRClass(ANY)]
|
||||
pub struct NSEC3 {
|
||||
pub hash_algorithm: u8,
|
||||
pub flags: u8,
|
||||
pub hash_algorithm: Nsec3Algorithm,
|
||||
pub flags: Nsec3Flags,
|
||||
pub iterations: u16,
|
||||
pub salt: HexShortBlob,
|
||||
pub next_hashed: NextHashedOwnerName,
|
||||
@ -371,8 +415,8 @@ pub struct NSEC3 {
|
||||
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
|
||||
#[RRClass(ANY)]
|
||||
pub struct NSEC3PARAM {
|
||||
pub hash_algorithm: u8,
|
||||
pub flags: u8,
|
||||
pub hash_algorithm: Nsec3Algorithm,
|
||||
pub flags: Nsec3ParamFlags,
|
||||
pub iterations: u16,
|
||||
pub salt: HexShortBlob,
|
||||
}
|
||||
@ -413,7 +457,7 @@ pub struct NINFO {
|
||||
pub struct RKEY {
|
||||
pub flags: u16,
|
||||
pub protocol: u8,
|
||||
pub algorithm: u8,
|
||||
pub algorithm: DnsSecAlgorithm,
|
||||
pub public_key: Base64RemainingBlob,
|
||||
}
|
||||
|
||||
@ -425,20 +469,31 @@ pub struct RKEY {
|
||||
#[RRClass(ANY)]
|
||||
pub struct CDS {
|
||||
pub key_tag: u16,
|
||||
pub algorithm: u8,
|
||||
pub digest_type: u8,
|
||||
pub algorithm: DnsSecAlgorithm,
|
||||
pub digest_type: DnsSecDigestAlgorithm,
|
||||
pub digest: HexRemainingBlob,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
|
||||
#[RRClass(ANY)]
|
||||
pub struct CDNSKEY {
|
||||
pub flags: u16,
|
||||
pub protocol: u8,
|
||||
pub algorithm: u8,
|
||||
pub flags: DnskeyFlags,
|
||||
pub protocol: DnskeyProtocol,
|
||||
pub algorithm: DnsSecAlgorithm,
|
||||
pub public_key: Base64RemainingBlob,
|
||||
}
|
||||
|
||||
impl CDNSKEY {
|
||||
pub fn tag(&self) -> u16 {
|
||||
DNSKEY {
|
||||
flags: self.flags,
|
||||
protocol: self.protocol,
|
||||
algorithm: self.algorithm,
|
||||
public_key: self.public_key.clone(),
|
||||
}.tag()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
|
||||
#[RRClass(ANY)]
|
||||
pub struct OPENPGPKEY {
|
||||
@ -545,7 +600,7 @@ pub struct URI {
|
||||
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
|
||||
// #[RRClass(?)]
|
||||
pub struct CAA {
|
||||
pub flags: u8,
|
||||
pub flags: CaaFlags,
|
||||
pub tag: UnquotedShortText,
|
||||
pub value: RemainingText,
|
||||
}
|
||||
@ -559,8 +614,8 @@ pub struct CAA {
|
||||
#[RRClass(ANY)]
|
||||
pub struct DLV {
|
||||
pub key_tag: u16,
|
||||
pub algorithm: u8,
|
||||
pub digest_type: u8,
|
||||
pub algorithm: DnsSecAlgorithm,
|
||||
pub digest_type: DnsSecDigestAlgorithm,
|
||||
pub digest: HexRemainingBlob,
|
||||
}
|
||||
|
||||
|
@ -9,11 +9,14 @@ extern crate proc_macro2;
|
||||
mod rrdata;
|
||||
mod dns_packet_data;
|
||||
mod dns_text_data;
|
||||
mod native_enum;
|
||||
mod native_flags;
|
||||
|
||||
decl_derive!([DnsPacketData] => dns_packet_data::derive);
|
||||
decl_derive!([DnsTextData] => dns_text_data::derive);
|
||||
decl_derive!([RRData, attributes(RRTypeName, RRClass)] => rrdata::rrdata_derive);
|
||||
|
||||
decl_attribute!([native_enum] => native_enum::attribute_native_enum);
|
||||
decl_attribute!([native_flags] => native_flags::attribute_native_flags);
|
||||
|
||||
fn attr_get_single_list_arg(attr_meta: &syn::Meta) -> proc_macro2::TokenStream {
|
||||
match attr_meta {
|
||||
|
88
lib/dnsbox-derive/src/native_enum.rs
Normal file
88
lib/dnsbox-derive/src/native_enum.rs
Normal file
@ -0,0 +1,88 @@
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
pub fn attribute_native_enum(
|
||||
native_type: TokenStream,
|
||||
structure: synstructure::Structure,
|
||||
) -> TokenStream {
|
||||
let ast = structure.ast();
|
||||
let in_attrs = ast.attrs.iter().map(|a| quote!{#a}).collect::<TokenStream>();
|
||||
let in_vis = &ast.vis;
|
||||
let name = &ast.ident;
|
||||
|
||||
let known_name = syn::Ident::new(&format!("{}Known", name), proc_macro2::Span::call_site());
|
||||
|
||||
let enumdata = match &ast.data {
|
||||
syn::Data::Enum(de) => de,
|
||||
_ => panic!("not an enum"),
|
||||
};
|
||||
let known_enum;
|
||||
|
||||
{
|
||||
let variants = &enumdata.variants;
|
||||
let doc_str = format!("Known enum variants of [`{}`]\n\n[`{}`]: struct.{}.html\n", name, name, name);
|
||||
known_enum = quote! {
|
||||
#[doc = #doc_str]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
#[repr(#native_type)]
|
||||
#[allow(non_camel_case_types)]
|
||||
#in_vis enum #known_name {
|
||||
#variants
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut consts = TokenStream::new();
|
||||
let mut convert = TokenStream::new();
|
||||
for variant in &enumdata.variants {
|
||||
let variant_attrs = variant.attrs.iter().map(|a| quote!{#a}).collect::<TokenStream>();
|
||||
let disc_name = &variant.ident;
|
||||
let disc = variant.discriminant.as_ref().map(|(_, d)| quote!{#d}).unwrap_or_else(|| quote!{
|
||||
#known_name::#disc_name as #native_type
|
||||
});
|
||||
consts.extend(quote! {
|
||||
#variant_attrs
|
||||
pub const #disc_name: Self = #name(#disc);
|
||||
});
|
||||
convert.extend(quote! {
|
||||
Self::#disc_name => #known_name::#disc_name,
|
||||
});
|
||||
}
|
||||
|
||||
quote! {
|
||||
#known_enum
|
||||
|
||||
#in_attrs
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#in_vis struct #name(pub #native_type);
|
||||
|
||||
impl #name {
|
||||
#consts
|
||||
|
||||
/// Try converting to known enum values
|
||||
pub fn into_known(self) -> Option<#known_name> {
|
||||
Some(
|
||||
#[allow(unreachable_patterns)]
|
||||
match self {
|
||||
#convert
|
||||
_ => return None,
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for #name {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
match self.into_known() {
|
||||
Some(v) => v.fmt(f),
|
||||
None => f.debug_tuple(stringify!(#name)).field(&self.0).finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<#known_name> for #name {
|
||||
fn from(v: #known_name) -> Self {
|
||||
#name(v as #native_type)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
136
lib/dnsbox-derive/src/native_flags.rs
Normal file
136
lib/dnsbox-derive/src/native_flags.rs
Normal file
@ -0,0 +1,136 @@
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
pub fn attribute_native_flags(
|
||||
native_type: TokenStream,
|
||||
structure: synstructure::Structure,
|
||||
) -> TokenStream {
|
||||
let ast = structure.ast();
|
||||
let in_attrs = ast.attrs.iter().map(|a| quote!{#a}).collect::<TokenStream>();
|
||||
let in_vis = &ast.vis;
|
||||
let name = &ast.ident;
|
||||
|
||||
let hidden_impl_mod = syn::Ident::new(&format!("_{}_hidden_native_flags", name), proc_macro2::Span::call_site());
|
||||
|
||||
let enumdata = match &ast.data {
|
||||
syn::Data::Enum(de) => de,
|
||||
_ => panic!("not an enum"),
|
||||
};
|
||||
|
||||
let mut consts = TokenStream::new();
|
||||
let mut known_mask = TokenStream::new();
|
||||
let mut dbg = TokenStream::new();
|
||||
for variant in &enumdata.variants {
|
||||
let variant_attrs = variant.attrs.iter().map(|a| quote!{#a}).collect::<TokenStream>();
|
||||
let disc_name = &variant.ident;
|
||||
let disc = variant.discriminant.as_ref().map(|(_, d)| d).expect("all variants need explicit bitmask discriminants");
|
||||
consts.extend(quote! {
|
||||
#variant_attrs
|
||||
pub const #disc_name: Flag = Flag { mask: #disc };
|
||||
});
|
||||
known_mask.extend(quote!{
|
||||
| #disc
|
||||
});
|
||||
dbg.extend(quote! {
|
||||
if self & Self::#disc_name {
|
||||
if !first { f.write_str(" | ")?; }
|
||||
first = false;
|
||||
f.write_str(stringify!(#disc_name))?;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let bitops = quote! {
|
||||
impl core::ops::BitAnd<Flag> for super::#name {
|
||||
type Output = bool;
|
||||
|
||||
fn bitand(self, rhs: Flag) -> Self::Output {
|
||||
rhs.mask == self.0 & rhs.mask
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::BitAnd<Flag> for &super::#name {
|
||||
type Output = bool;
|
||||
|
||||
fn bitand(self, rhs: Flag) -> Self::Output {
|
||||
rhs.mask == self.0 & rhs.mask
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::BitOr for Flag {
|
||||
type Output = super::#name;
|
||||
|
||||
fn bitor(self, rhs: Flag) -> Self::Output {
|
||||
super::#name(self.mask | rhs.mask)
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::BitOr<Flag> for super::#name {
|
||||
type Output = super::#name;
|
||||
|
||||
fn bitor(self, rhs: Flag) -> Self::Output {
|
||||
super::#name(self.0 | rhs.mask)
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::BitOr<super::#name> for Flag {
|
||||
type Output = super::#name;
|
||||
|
||||
fn bitor(self, rhs: super::#name) -> Self::Output {
|
||||
super::#name(self.mask | rhs.0)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
quote! {
|
||||
#in_attrs
|
||||
///
|
||||
/// The `Flag` constants can be used to check if a flag is set
|
||||
/// by using bitwise and: `flags & flag`.
|
||||
///
|
||||
/// They can be converted to and combined with bitwise or to set
|
||||
/// flags. Their bitmask is available as public struct member
|
||||
/// `mask`.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#in_vis struct #name(pub #native_type);
|
||||
|
||||
mod #hidden_impl_mod {
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub struct Flag {
|
||||
pub mask: #native_type,
|
||||
}
|
||||
|
||||
const known_mask: #native_type = 0 #known_mask;
|
||||
|
||||
impl super::#name {
|
||||
pub fn new_flag(mask: #native_type) -> Flag {
|
||||
Flag { mask }
|
||||
}
|
||||
|
||||
#consts
|
||||
}
|
||||
|
||||
#bitops
|
||||
|
||||
impl From<Flag> for super::#name {
|
||||
fn from(v: Flag) -> Self {
|
||||
Self(v.mask)
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for super::#name {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
let mut first = true;
|
||||
let unknown_bits = self.0 & !known_mask;
|
||||
#dbg
|
||||
if unknown_bits != 0 {
|
||||
if !first { f.write_str(" | ")?; }
|
||||
write!(f, "unknown(0x{:x})", unknown_bits)?;
|
||||
} else if first {
|
||||
f.write_str("(empty)")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user