From 703baf105592a00525aee5c818f20689d3a5ee81 Mon Sep 17 00:00:00 2001 From: Marshall Pierce Date: Fri, 13 Mar 2026 15:37:55 -0600 Subject: [PATCH] Update MSRV to 1.75.0 and fix all rustc and clippy warnings --- .gitignore | 1 + CHANGELOG.md | 8 +++ Cargo.toml | 1 + examples/sync_tcp_client.rs | 11 ++-- examples/sync_udp_client.rs | 11 ++-- rust-toolchain.toml | 2 + src/builder.rs | 4 +- src/enums.rs | 14 ++--- src/lib.rs | 1 + src/name.rs | 12 ++--- src/parser.rs | 105 ++++++++++++++++++------------------ src/rdata/cname.rs | 2 +- src/rdata/mx.rs | 18 +++---- src/rdata/soa.rs | 9 ++-- src/rdata/srv.rs | 22 ++++---- src/rdata/txt.rs | 8 +-- 16 files changed, 118 insertions(+), 111 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 rust-toolchain.toml diff --git a/.gitignore b/.gitignore index 4cd31f5..d089c2c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /.vagga /target /Cargo.lock +/.idea \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..5ad57de --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +# Next + +- Update MSRV to 1.75.0, currently the older of rustc in Ubuntu LTS (Noble Numbat, 1.75.0) and Debian Stable (trixie, 1.85.0) + - Small syntactic tweaks to address all compiler and clippy warnings + +# 0.8.0 + +Current version when changelog started \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 455c3bc..53445b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,3 +25,4 @@ serde_derive = { version = "1.0", optional = true } [dev-dependencies] matches = "0.1.2" +itertools = "0.14.0" diff --git a/examples/sync_tcp_client.rs b/examples/sync_tcp_client.rs index 732e8d3..e8be91c 100644 --- a/examples/sync_tcp_client.rs +++ b/examples/sync_tcp_client.rs @@ -26,7 +26,7 @@ fn main() { process::exit(code); } -fn resolve(name: &str) -> Result<(), Box> { +fn resolve(name: &str) -> Result<(), Box> { let mut conn = TcpStream::connect("127.0.0.1:53")?; let mut builder = Builder::new_query(1, true); builder.add_question(name, false, QueryType::A, QueryClass::IN); @@ -62,15 +62,12 @@ fn resolve(name: &str) -> Result<(), Box> { if pkt.header.response_code != ResponseCode::NoError { return Err(pkt.header.response_code.into()); } - if pkt.answers.len() == 0 { + if pkt.answers.is_empty() { return Err("No records received".into()); } for ans in pkt.answers { - match ans.data { - RData::A(Record(ip)) => { - println!("{}", ip); - } - _ => {} // ignore + if let RData::A(Record(ip)) = ans.data { + println!("{}", ip); } } Ok(()) diff --git a/examples/sync_udp_client.rs b/examples/sync_udp_client.rs index f8bf426..e9ed4a3 100644 --- a/examples/sync_udp_client.rs +++ b/examples/sync_udp_client.rs @@ -25,7 +25,7 @@ fn main() { process::exit(code); } -fn resolve(name: &str) -> Result<(), Box> { +fn resolve(name: &str) -> Result<(), Box> { let sock = UdpSocket::bind("127.0.0.1:0")?; sock.connect("127.0.0.1:53")?; let mut builder = Builder::new_query(1, true); @@ -38,15 +38,12 @@ fn resolve(name: &str) -> Result<(), Box> { if pkt.header.response_code != ResponseCode::NoError { return Err(pkt.header.response_code.into()); } - if pkt.answers.len() == 0 { + if pkt.answers.is_empty() { return Err("No records received".into()); } for ans in pkt.answers { - match ans.data { - RData::A(Record(ip)) => { - println!("{}", ip); - } - _ => {} // ignore + if let RData::A(Record(ip)) = ans.data { + println!("{}", ip); } } Ok(()) diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..4dd8e5c --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.75.0" \ No newline at end of file diff --git a/src/builder.rs b/src/builder.rs index 22aff0b..362bc76 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -19,7 +19,7 @@ impl Builder { pub fn new_query(id: u16, recursion: bool) -> Builder { let mut buf = Vec::with_capacity(512); let head = Header { - id: id, + id, query: true, opcode: Opcode::StandardQuery, authoritative: false, @@ -36,7 +36,7 @@ impl Builder { }; buf.extend([0u8; 12].iter()); head.write(&mut buf[..12]); - Builder { buf: buf } + Builder { buf } } /// Adds a question to the packet /// diff --git a/src/enums.rs b/src/enums.rs index 9f58ddf..bf6ef24 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -167,10 +167,10 @@ impl From for Opcode { } } } -impl Into for Opcode { - fn into(self) -> u16 { +impl From for u16 { + fn from(value: Opcode) -> u16 { use self::Opcode::*; - match self { + match value { StandardQuery => 0, InverseQuery => 1, ServerStatusRequest => 2, @@ -189,15 +189,15 @@ impl From for ResponseCode { 3 => NameError, 4 => NotImplemented, 5 => Refused, - 6...15 => Reserved(code), + 6..=15 => Reserved(code), x => panic!("Invalid response code {}", x), } } } -impl Into for ResponseCode { - fn into(self) -> u8 { +impl From for u8 { + fn from(value: ResponseCode) -> u8 { use self::ResponseCode::*; - match self { + match value { NoError => 0, FormatError => 1, ServerFailure => 2, diff --git a/src/lib.rs b/src/lib.rs index e89c005..1fd0bff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,7 @@ extern crate byteorder; #[cfg(test)] #[macro_use] extern crate matches; #[macro_use(quick_error)] extern crate quick_error; #[cfg(feature = "with-serde")] #[macro_use] extern crate serde_derive; +#[cfg(test)] extern crate itertools; mod enums; mod structs; diff --git a/src/name.rs b/src/name.rs index 8763259..3079f7c 100644 --- a/src/name.rs +++ b/src/name.rs @@ -55,7 +55,7 @@ impl<'a> Name<'a> { // Set value for return_pos which is the pos in the original // data buffer that should be used to return after validating // the offsetted labels. - if let None = return_pos { + if return_pos.is_none() { return_pos = Some(pos); } @@ -85,9 +85,9 @@ impl<'a> Name<'a> { byte = parse_data[pos]; } if let Some(return_pos) = return_pos { - return Ok(Name {labels: &data[..return_pos+2], original: original}); + Ok(Name {labels: &data[..return_pos+2], original }) } else { - return Ok(Name {labels: &data[..pos+1], original: original }); + Ok(Name {labels: &data[..pos+1], original }) } } /// Number of bytes serialized name occupies @@ -109,16 +109,16 @@ impl<'a> fmt::Display for Name<'a> { let off = (BigEndian::read_u16(&data[pos..pos+2]) & !0b1100_0000_0000_0000) as usize; if pos != 0 { - try!(fmt.write_char('.')); + fmt.write_char('.')?; } return fmt::Display::fmt( &Name::scan(&original[off..], original).unwrap(), fmt) } else if byte & 0b1100_0000 == 0 { if pos != 0 { - try!(fmt.write_char('.')); + fmt.write_char('.')?; } let end = pos + byte as usize + 1; - try!(fmt.write_str(from_utf8(&data[pos+1..end]).unwrap())); + fmt.write_str(from_utf8(&data[pos + 1..end]).unwrap())?; pos = end; continue; } else { diff --git a/src/parser.rs b/src/parser.rs index 0718798..6a3f266 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -12,58 +12,58 @@ impl<'a> Packet<'a> { /// Parse a full DNS Packet and return a structure that has all the /// data borrowed from the passed buffer. pub fn parse(data: &[u8]) -> Result { - let header = try!(Header::parse(data)); + let header = Header::parse(data)?; let mut offset = Header::size(); let mut questions = Vec::with_capacity(header.questions as usize); for _ in 0..header.questions { - let name = try!(Name::scan(&data[offset..], data)); + let name = Name::scan(&data[offset..], data)?; offset += name.byte_len(); if offset + 4 > data.len() { return Err(Error::UnexpectedEOF); } - let qtype = try!(QueryType::parse( - BigEndian::read_u16(&data[offset..offset+2]))); + let qtype = QueryType::parse( + BigEndian::read_u16(&data[offset..offset + 2]))?; offset += 2; - let (prefer_unicast, qclass) = try!(parse_qclass_code( - BigEndian::read_u16(&data[offset..offset+2]))); + let (prefer_unicast, qclass) = parse_qclass_code( + BigEndian::read_u16(&data[offset..offset + 2]))?; offset += 2; questions.push(Question { qname: name, - qtype: qtype, - prefer_unicast: prefer_unicast, - qclass: qclass, + qtype, + prefer_unicast, + qclass, }); } let mut answers = Vec::with_capacity(header.answers as usize); for _ in 0..header.answers { - answers.push(try!(parse_record(data, &mut offset))); + answers.push(parse_record(data, &mut offset)?); } let mut nameservers = Vec::with_capacity(header.nameservers as usize); for _ in 0..header.nameservers { - nameservers.push(try!(parse_record(data, &mut offset))); + nameservers.push(parse_record(data, &mut offset)?); } let mut additional = Vec::with_capacity(header.additional as usize); let mut opt = None; for _ in 0..header.additional { if offset + 3 <= data.len() && data[offset..offset+3] == OPT_RR_START { if opt.is_none() { - opt = Some(try!(parse_opt_record(data, &mut offset))); + opt = Some(parse_opt_record(data, &mut offset)?); } else { return Err(Error::AdditionalOPT); } } else { - additional.push(try!(parse_record(data, &mut offset))); + additional.push(parse_record(data, &mut offset)?); } } Ok(Packet { - header: header, - questions: questions, - answers: answers, - nameservers: nameservers, - additional: additional, - opt: opt, + header, + questions, + answers, + nameservers, + additional, + opt, }) } } @@ -72,7 +72,7 @@ fn parse_qclass_code(value: u16) -> Result<(bool, QueryClass), Error> { let prefer_unicast = value & 0x8000 == 0x8000; let qclass_code = value & 0x7FFF; - let qclass = try!(QueryClass::parse(qclass_code)); + let qclass = QueryClass::parse(qclass_code)?; Ok((prefer_unicast, qclass)) } @@ -80,23 +80,23 @@ fn parse_class_code(value: u16) -> Result<(bool, Class), Error> { let is_unique = value & 0x8000 == 0x8000; let class_code = value & 0x7FFF; - let cls = try!(Class::parse(class_code)); + let cls = Class::parse(class_code)?; Ok((is_unique, cls)) } // Generic function to parse answer, nameservers, and additional records. fn parse_record<'a>(data: &'a [u8], offset: &mut usize) -> Result, Error> { - let name = try!(Name::scan(&data[*offset..], data)); + let name = Name::scan(&data[*offset..], data)?; *offset += name.byte_len(); if *offset + 10 > data.len() { return Err(Error::UnexpectedEOF); } - let typ = try!(Type::parse( - BigEndian::read_u16(&data[*offset..*offset+2]))); + let typ = Type::parse( + BigEndian::read_u16(&data[*offset..*offset + 2]))?; *offset += 2; let class_code = BigEndian::read_u16(&data[*offset..*offset+2]); - let (multicast_unique, cls) = try!(parse_class_code(class_code)); + let (multicast_unique, cls) = parse_class_code(class_code)?; *offset += 2; let mut ttl = BigEndian::read_u32(&data[*offset..*offset+4]); @@ -109,15 +109,15 @@ fn parse_record<'a>(data: &'a [u8], offset: &mut usize) -> Result data.len() { return Err(Error::UnexpectedEOF); } - let data = try!(RData::parse(typ, - &data[*offset..*offset+rdlen], data)); + let data = RData::parse(typ, + &data[*offset..*offset + rdlen], data)?; *offset += rdlen; Ok(ResourceRecord { - name: name, - multicast_unique: multicast_unique, - cls: cls, - ttl: ttl, - data: data, + name, + multicast_unique, + cls, + ttl, + data, }) } @@ -127,8 +127,8 @@ fn parse_opt_record<'a>(data: &'a [u8], offset: &mut usize) -> Result, E return Err(Error::UnexpectedEOF); } *offset += 1; - let typ = try!(Type::parse( - BigEndian::read_u16(&data[*offset..*offset+2]))); + let typ = Type::parse( + BigEndian::read_u16(&data[*offset..*offset + 2]))?; if typ != Type::OPT { return Err(Error::InvalidType(typ as u16)); } @@ -146,16 +146,16 @@ fn parse_opt_record<'a>(data: &'a [u8], offset: &mut usize) -> Result, E if *offset + rdlen > data.len() { return Err(Error::UnexpectedEOF); } - let data = try!(RData::parse(typ, - &data[*offset..*offset+rdlen], data)); + let data = RData::parse(typ, + &data[*offset..*offset + rdlen], data)?; *offset += rdlen; Ok(Opt { - udp: udp, - extrcode: extrcode, - version: version, - flags: flags, - data: data, + udp, + extrcode, + version, + flags, + data, }) } @@ -163,6 +163,7 @@ fn parse_opt_record<'a>(data: &'a [u8], offset: &mut usize) -> Result, E mod test { use std::net::Ipv4Addr; + use itertools::Itertools; use {Packet, Header}; use Opcode::*; use ResponseCode::NoError; @@ -228,7 +229,7 @@ mod test { assert_eq!(&packet.questions[0].qname.to_string()[..], "example.com"); assert_eq!(packet.answers.len(), 1); assert_eq!(&packet.answers[0].name.to_string()[..], "example.com"); - assert_eq!(packet.answers[0].multicast_unique, false); + assert!(!packet.answers[0].multicast_unique); assert_eq!(packet.answers[0].cls, C::IN); assert_eq!(packet.answers[0].ttl, 1272); match packet.answers[0].data { @@ -248,7 +249,7 @@ mod test { let packet = Packet::parse(response).unwrap(); assert_eq!(packet.answers.len(), 1); - assert_eq!(packet.answers[0].multicast_unique, true); + assert!(packet.answers[0].multicast_unique); assert_eq!(packet.answers[0].cls, C::IN); } @@ -352,7 +353,7 @@ mod test { assert_eq!(packet.questions[0].qclass, QC::IN); assert_eq!(&packet.questions[0].qname.to_string()[..], "google.com"); assert_eq!(packet.answers.len(), 6); - let ips = vec![ + let ips = [ Ipv4Addr::new(64, 233, 164, 100), Ipv4Addr::new(64, 233, 164, 139), Ipv4Addr::new(64, 233, 164, 113), @@ -360,13 +361,13 @@ mod test { Ipv4Addr::new(64, 233, 164, 101), Ipv4Addr::new(64, 233, 164, 138), ]; - for i in 0..6 { - assert_eq!(&packet.answers[i].name.to_string()[..], "google.com"); - assert_eq!(packet.answers[i].cls, C::IN); - assert_eq!(packet.answers[i].ttl, 239); - match packet.answers[i].data { + for (answer, ip) in packet.answers.iter().zip_eq(ips.iter()) { + assert_eq!(&answer.name.to_string()[..], "google.com"); + assert_eq!(answer.cls, C::IN); + assert_eq!(answer.ttl, 239); + match answer.data { RData::A(addr) => { - assert_eq!(addr.0, ips[i]); + assert_eq!(addr.0, *ip); } ref x => panic!("Wrong rdata {:?}", x), } @@ -397,7 +398,7 @@ mod test { assert_eq!(packet.questions.len(), 1); assert_eq!(packet.questions[0].qtype, QT::SRV); assert_eq!(packet.questions[0].qclass, QC::IN); - assert_eq!(packet.questions[0].prefer_unicast, false); + assert!(!packet.questions[0].prefer_unicast); assert_eq!(&packet.questions[0].qname.to_string()[..], "_xmpp-server._tcp.gmail.com"); assert_eq!(packet.answers.len(), 0); @@ -412,7 +413,7 @@ mod test { assert_eq!(packet.questions.len(), 1); assert_eq!(packet.questions[0].qtype, QT::A); assert_eq!(packet.questions[0].qclass, QC::IN); - assert_eq!(packet.questions[0].prefer_unicast, true); + assert!(packet.questions[0].prefer_unicast); } #[test] diff --git a/src/rdata/cname.rs b/src/rdata/cname.rs index 0dcb469..c8dcb99 100644 --- a/src/rdata/cname.rs +++ b/src/rdata/cname.rs @@ -80,7 +80,7 @@ mod test { ref x => panic!("Wrong rdata {:?}", x), } - let ips = vec![ + let ips = [ Ipv4Addr::new(104, 16, 103, 204), Ipv4Addr::new(104, 16, 107, 204), Ipv4Addr::new(104, 16, 104, 204), diff --git a/src/rdata/mx.rs b/src/rdata/mx.rs index 33c5c41..4bd3a55 100644 --- a/src/rdata/mx.rs +++ b/src/rdata/mx.rs @@ -25,7 +25,7 @@ impl<'a> super::Record<'a> for Record<'a> { #[cfg(test)] mod test { - + use itertools::Itertools; use {Packet, Header}; use Opcode::*; use ResponseCode::NoError; @@ -68,22 +68,22 @@ mod test { assert_eq!(&packet.questions[0].qname.to_string()[..], "gmail.com"); assert_eq!(packet.answers.len(), 5); - let items = vec![ + let items = [ ( 5, "gmail-smtp-in.l.google.com"), (10, "alt1.gmail-smtp-in.l.google.com"), (40, "alt4.gmail-smtp-in.l.google.com"), (20, "alt2.gmail-smtp-in.l.google.com"), (30, "alt3.gmail-smtp-in.l.google.com"), ]; - for i in 0..5 { - assert_eq!(&packet.answers[i].name.to_string()[..], + for (answer, item) in packet.answers.iter().zip_eq(items.iter()) { + assert_eq!(&answer.name.to_string()[..], "gmail.com"); - assert_eq!(packet.answers[i].cls, C::IN); - assert_eq!(packet.answers[i].ttl, 1148); - match *&packet.answers[i].data { + assert_eq!(answer.cls, C::IN); + assert_eq!(answer.ttl, 1148); + match answer.data { RData::MX( Record { preference, exchange }) => { - assert_eq!(preference, items[i].0); - assert_eq!(exchange.to_string(), (items[i].1).to_string()); + assert_eq!(preference, item.0); + assert_eq!(exchange.to_string(), item.1.to_string()); } ref x => panic!("Wrong rdata {:?}", x), } diff --git a/src/rdata/soa.rs b/src/rdata/soa.rs index e6d5f17..b28ae63 100644 --- a/src/rdata/soa.rs +++ b/src/rdata/soa.rs @@ -14,21 +14,20 @@ pub struct Record<'a> { } impl<'a> super::Record<'a> for Record<'a> { - const TYPE: isize = 6; fn parse(rdata: &'a [u8], original: &'a [u8]) -> super::RDataResult<'a> { let mut pos = 0; - let primary_name_server = try!(Name::scan(rdata, original)); + let primary_name_server = Name::scan(rdata, original)?; pos += primary_name_server.byte_len(); - let mailbox = try!(Name::scan(&rdata[pos..], original)); + let mailbox = Name::scan(&rdata[pos..], original)?; pos += mailbox.byte_len(); if rdata[pos..].len() < 20 { return Err(Error::WrongRdataLength); } let record = Record { primary_ns: primary_name_server, - mailbox: mailbox, + mailbox, serial: BigEndian::read_u32(&rdata[pos..(pos+4)]), refresh: BigEndian::read_u32(&rdata[(pos+4)..(pos+8)]), retry: BigEndian::read_u32(&rdata[(pos+8)..(pos+12)]), @@ -83,7 +82,7 @@ mod test { assert_eq!(packet.nameservers.len(), 1); assert_eq!(&packet.nameservers[0].name.to_string()[..], "youtube.com"); assert_eq!(packet.nameservers[0].cls, C::IN); - assert_eq!(packet.nameservers[0].multicast_unique, false); + assert!(!packet.nameservers[0].multicast_unique); assert_eq!(packet.nameservers[0].ttl, 10800); match packet.nameservers[0].data { RData::SOA(ref soa_rec) => { diff --git a/src/rdata/srv.rs b/src/rdata/srv.rs index dbc151d..0d12a08 100644 --- a/src/rdata/srv.rs +++ b/src/rdata/srv.rs @@ -29,7 +29,7 @@ impl<'a> super::Record<'a> for Record<'a> { #[cfg(test)] mod test { - + use itertools::Itertools; use {Packet, Header}; use Opcode::*; use ResponseCode::NoError; @@ -76,24 +76,24 @@ mod test { assert_eq!(&packet.questions[0].qname.to_string()[..], "_xmpp-server._tcp.gmail.com"); assert_eq!(packet.answers.len(), 5); - let items = vec![ + let items = [ (5, 0, 5269, "xmpp-server.l.google.com"), (20, 0, 5269, "alt3.xmpp-server.l.google.com"), (20, 0, 5269, "alt1.xmpp-server.l.google.com"), (20, 0, 5269, "alt2.xmpp-server.l.google.com"), (20, 0, 5269, "alt4.xmpp-server.l.google.com"), ]; - for i in 0..5 { - assert_eq!(&packet.answers[i].name.to_string()[..], + for (answer, item) in packet.answers.iter().zip_eq(items.iter()) { + assert_eq!(&answer.name.to_string()[..], "_xmpp-server._tcp.gmail.com"); - assert_eq!(packet.answers[i].cls, C::IN); - assert_eq!(packet.answers[i].ttl, 900); - match *&packet.answers[i].data { + assert_eq!(answer.cls, C::IN); + assert_eq!(answer.ttl, 900); + match answer.data { RData::SRV(Record { priority, weight, port, target }) => { - assert_eq!(priority, items[i].0); - assert_eq!(weight, items[i].1); - assert_eq!(port, items[i].2); - assert_eq!(target.to_string(), (items[i].3).to_string()); + assert_eq!(priority, item.0); + assert_eq!(weight, item.1); + assert_eq!(port, item.2); + assert_eq!(target.to_string(), item.3.to_string()); } ref x => panic!("Wrong rdata {:?}", x), } diff --git a/src/rdata/txt.rs b/src/rdata/txt.rs index 8f5f5fc..01fe4ea 100644 --- a/src/rdata/txt.rs +++ b/src/rdata/txt.rs @@ -13,14 +13,14 @@ pub struct RecordIter<'a> { impl<'a> Iterator for RecordIter<'a> { type Item = &'a [u8]; fn next(&mut self) -> Option<&'a [u8]> { - if self.bytes.len() >= 1 { + if !self.bytes.is_empty() { let len = self.bytes[0] as usize; - debug_assert!(self.bytes.len() >= len+1); + debug_assert!(self.bytes.len() > len); let (head, tail) = self.bytes[1..].split_at(len); self.bytes = tail; return Some(head); } - return None; + None } } @@ -104,7 +104,7 @@ mod test { assert_eq!(&packet.questions[0].qname.to_string()[..], "facebook.com"); assert_eq!(packet.answers.len(), 1); assert_eq!(&packet.answers[0].name.to_string()[..], "facebook.com"); - assert_eq!(packet.answers[0].multicast_unique, false); + assert!(!packet.answers[0].multicast_unique); assert_eq!(packet.answers[0].cls, C::IN); assert_eq!(packet.answers[0].ttl, 86333); match packet.answers[0].data {