├── .github └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── async-dns ├── CHANGELOG.md ├── Cargo.toml ├── README.md ├── benches │ └── comparison.rs ├── examples │ └── lookup.rs ├── src │ ├── lib.rs │ ├── unix.rs │ └── windows.rs └── tests │ └── lookup.rs └── dns-protocol ├── CHANGELOG.md ├── Cargo.toml ├── README.md ├── examples └── nslookup.rs └── src ├── lib.rs └── ser.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | schedule: 9 | - cron: '0 2 * * *' 10 | 11 | env: 12 | RUSTFLAGS: -D warnings 13 | RUST_BACKTRACE: 1 14 | 15 | jobs: 16 | test: 17 | runs-on: ${{ matrix.os }} 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | os: [ubuntu-latest, windows-latest, macos-latest] 22 | rust: [nightly, stable] 23 | steps: 24 | - uses: actions/checkout@v3 25 | - name: Install Rust 26 | run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} 27 | - run: cargo build --all --all-features --all-targets 28 | - name: Run cargo check (without dev-dependencies to catch missing feature flags) 29 | if: startsWith(matrix.rust, 'nightly') 30 | run: cargo check -Z features=dev_dep 31 | - run: cargo test 32 | 33 | # Copied from: https://github.com/rust-lang/stacker/pull/19/files 34 | windows_gnu: 35 | runs-on: windows-latest 36 | strategy: 37 | matrix: 38 | rust: [nightly] 39 | target: 40 | - x86_64-pc-windows-gnu 41 | steps: 42 | - uses: actions/checkout@v4 43 | - name: Install Rust 44 | # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. 45 | run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} 46 | - run: rustup target add ${{ matrix.target }} 47 | - run: cargo build --target ${{ matrix.target }} --all --all-features --all-targets 48 | - run: cargo test --target ${{ matrix.target }} 49 | 50 | cross: 51 | runs-on: ${{ matrix.os }} 52 | strategy: 53 | fail-fast: false 54 | matrix: 55 | os: [ubuntu-latest, macos-latest] 56 | steps: 57 | - uses: actions/checkout@v4 58 | - name: Install Rust 59 | run: rustup update stable 60 | - name: Install cross 61 | uses: taiki-e/install-action@cross 62 | # We don't test BSDs, since we already test them in Cirrus. 63 | - name: Android 64 | if: startsWith(matrix.os, 'ubuntu') 65 | run: cross test --target arm-linux-androideabi 66 | - name: iOS 67 | if: startsWith(matrix.os, 'macos') 68 | run: | 69 | rustup target add aarch64-apple-ios 70 | cross build --target aarch64-apple-ios 71 | - name: Linux x32 72 | if: startsWith(matrix.os, 'ubuntu') 73 | run: | 74 | rustup target add x86_64-unknown-linux-gnux32 75 | cross check --target x86_64-unknown-linux-gnux32 76 | - name: Fuchsia 77 | if: startsWith(matrix.os, 'ubuntu') 78 | run: | 79 | rustup target add x86_64-unknown-fuchsia 80 | cargo build --target x86_64-unknown-fuchsia 81 | - name: illumos 82 | if: startsWith(matrix.os, 'ubuntu') 83 | run: | 84 | rustup target add x86_64-unknown-illumos 85 | cargo build --target x86_64-unknown-illumos 86 | 87 | msrv: 88 | runs-on: ubuntu-latest 89 | strategy: 90 | matrix: 91 | # When updating this, the reminder to update the minimum supported 92 | # Rust version in Cargo.toml and .clippy.toml. 93 | ad_rust: ['1.63'] 94 | dp_rust: ['1.63'] 95 | steps: 96 | - uses: actions/checkout@v3 97 | - name: Install Rust for async-dns 98 | run: rustup update ${{ matrix.ad_rust }} && rustup default ${{ matrix.ad_rust }} 99 | - run: cargo build 100 | - name: Install Rust for dns-protocol 101 | run: rustup update ${{ matrix.dp_rust }} && rustup default ${{ matrix.dp_rust }} 102 | - run: cargo build -p dns-protocol 103 | 104 | clippy: 105 | runs-on: ubuntu-latest 106 | steps: 107 | - uses: actions/checkout@v3 108 | - name: Install Rust 109 | run: rustup update stable 110 | - run: cargo clippy --all-features --all-targets 111 | 112 | fmt: 113 | runs-on: ubuntu-latest 114 | steps: 115 | - uses: actions/checkout@v3 116 | - name: Install Rust 117 | run: rustup update stable 118 | - run: cargo fmt --all --check 119 | 120 | security_audit: 121 | runs-on: ubuntu-latest 122 | steps: 123 | - uses: actions/checkout@v3 124 | - uses: actions-rs/audit-check@v1 125 | with: 126 | token: ${{ secrets.GITHUB_TOKEN }} 127 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | permissions: 4 | contents: write 5 | 6 | on: 7 | push: 8 | tags: 9 | - v[0-9]+.* 10 | 11 | jobs: 12 | create-release: 13 | if: github.repository_owner == 'rust-windowing' 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: taiki-e/create-gh-release-action@v1 18 | with: 19 | changelog: CHANGELOG.md 20 | branch: master 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} 23 | - name: Publish to crates.io 24 | run: cargo publish --token ${{ secrets.CRATES_IO_API_TOKEN }} 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | target/ -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "dns-protocol", 4 | "async-dns" 5 | ] -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 2 | 3 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Asynchronous DNS in Rust 2 | 3 | This workspace contains two crates of note: 4 | 5 | - [`async-dns`], which provides an asynchronous DNS resolver based on the [`smol`] runtime. 6 | - [`dns-protocol`], which provides a `no_std` implementation of the DNS protocol. 7 | 8 | [`async-dns`]: async-dns 9 | [`dns-protocol`]: dns-protocol 10 | [`smol`]: https://crates.io/crates/smol -------------------------------------------------------------------------------- /async-dns/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v0.1.1 2 | 3 | - Update dependencies. 4 | - Fix a bug in the Windows implementation. 5 | -------------------------------------------------------------------------------- /async-dns/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "async-dns" 3 | version = "0.1.1" 4 | edition = "2018" 5 | authors = ["John Nunley "] 6 | description = "A simple async DNS resolver" 7 | license = "MIT OR Apache-2.0" 8 | homepage = "https://github.com/notgull/async-dns/tree/master/async-dns#readme" 9 | repository = "https://github.com/notgull/async-dns/tree/master/async-dns" 10 | rust-version = "1.63" 11 | keywords = ["dns", "async"] 12 | categories = ["network-programming", "asynchronous"] 13 | 14 | [[bench]] 15 | name = "comparison" 16 | harness = false 17 | 18 | [dependencies] 19 | cfg-if = "1.0.0" 20 | 21 | [target.'cfg(windows)'.dependencies] 22 | async-channel = "2" 23 | 24 | [target.'cfg(windows)'.dependencies.windows-sys] 25 | version = "0.52" 26 | features = [ 27 | "Win32_NetworkManagement_Dns", 28 | "Win32_Foundation" 29 | ] 30 | 31 | [target.'cfg(unix)'.dependencies] 32 | async-executor = "1" 33 | async-fs = "2" 34 | async-io = "2" 35 | dns-protocol = "0.1" 36 | futures-lite = "2" 37 | fastrand = "2" 38 | memchr = "2.5.0" 39 | 40 | [dev-dependencies] 41 | blocking = "1" 42 | criterion = "0.4.0" 43 | 44 | [target.'cfg(windows)'.dev-dependencies] 45 | # we use async-io::block_on in tests 46 | async-io = "2" 47 | -------------------------------------------------------------------------------- /async-dns/README.md: -------------------------------------------------------------------------------- 1 | # async-dns 2 | 3 | This crate provides asynchronous DNS lookups. 4 | 5 | In asynchronous Rust code, it is necessary to resolve URL names using DNS. In most cases, this is done by calling [`getaddrinfo`] on a blocking threadpool. However, since DNS is a UDP-based protocol, it doesn't make much sense to block on a thread when fully asynchronous options are available. 6 | 7 | [`getaddrinfo`]: https://man7.org/linux/man-pages/man3/gai_strerror.3.html 8 | 9 | This crate provides a fully asynchronous alternative, based on the following mechanisms: 10 | 11 | * On Windows, it uses the [`DnsQueryEx`] function, which allows for non-blocking DNS queries. 12 | * On Unix, it uses a custom implementation of DNS provided by the [`dns-protocol`] crate. [`async-fs`] is used to read files and [`async-io`] is used for the actual UDP packets. 13 | 14 | It returns the list of addresses that it found to be associated with the given name. 15 | 16 | # License 17 | 18 | Dual licensed under the MIT and Apache 2.0 licenses. 19 | 20 | [`DnsQueryEx`]: https://docs.microsoft.com/en-us/windows/win32/api/windns/nf-windns-dnsqueryex 21 | [`dns-protocol`]: https://crates.io/crates/dns-protocol 22 | [`async-fs`]: https://crates.io/crates/async-fs 23 | [`async-io`]: https://crates.io/crates/async-io -------------------------------------------------------------------------------- /async-dns/benches/comparison.rs: -------------------------------------------------------------------------------- 1 | //! Comparison of `async-dns` with a regular blocking lookup. 2 | 3 | use criterion::{black_box, criterion_group, criterion_main, Criterion}; 4 | 5 | fn async_dns_lookup(b: &mut Criterion) { 6 | b.bench_function("async-dns", |b| { 7 | b.iter(|| { 8 | async_io::block_on(async { 9 | // Perform the lookup. 10 | let ips = async_dns::lookup("google.com").await.unwrap(); 11 | black_box(ips.collect::>()); 12 | }); 13 | }); 14 | }); 15 | } 16 | 17 | fn blocking_lookup(b: &mut Criterion) { 18 | b.bench_function("blocking", |b| { 19 | b.iter(|| { 20 | async_io::block_on(async { 21 | let ips = blocking::unblock(|| { 22 | std::net::ToSocketAddrs::to_socket_addrs("google.com:53") 23 | .unwrap() 24 | .collect::>() 25 | }) 26 | .await; 27 | black_box(ips); 28 | }); 29 | }); 30 | }); 31 | } 32 | 33 | criterion_group! { 34 | benches, 35 | async_dns_lookup, 36 | blocking_lookup, 37 | } 38 | 39 | criterion_main!(benches); 40 | -------------------------------------------------------------------------------- /async-dns/examples/lookup.rs: -------------------------------------------------------------------------------- 1 | //! Preform an asynchronous namespace lookup. 2 | 3 | use std::env; 4 | use std::process; 5 | 6 | fn main() { 7 | async_io::block_on(async { 8 | // Get the arguments/name to lookup. 9 | let mut args = env::args(); 10 | let program_name = args.next().unwrap(); 11 | 12 | let name = match args.next() { 13 | Some(name) => name, 14 | None => { 15 | eprintln!("Usage: {} ", program_name); 16 | process::exit(1); 17 | } 18 | }; 19 | 20 | // Perform the lookup. 21 | let ips = async_dns::lookup(&name).await.unwrap(); 22 | 23 | // Print the results. 24 | println!("{}:", name); 25 | for ip in ips { 26 | println!(" - {}", &ip.ip_address); 27 | } 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /async-dns/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Asynchronous DNS lookups. 2 | //! 3 | //! This crate provides asynchronous DNS lookups. It uses the following mechanisms 4 | //! to resolve hostnames: 5 | //! 6 | //! - On `cfg(unix)`, it uses a custom implementation based on [`async-fs`] for reading 7 | //! files, [`async-io`] for communication with the server, and [`dns-protocol`] for the 8 | //! protocol implementation. 9 | //! - On `cfg(windows)`, it uses the [`DnsQueryEx`] function to make asynchronous queries. 10 | //! 11 | //! [`DnsQueryEx`]: https://docs.microsoft.com/en-us/windows/win32/api/windns/nf-windns-dnsqueryex 12 | //! [`async-fs`]: https://crates.io/crates/async-fs 13 | //! [`async-io`]: https://crates.io/crates/async-io 14 | //! [`dns-protocol`]: https://crates.io/crates/dns-protocol 15 | 16 | // Non-windows platforms use no unsafe code. 17 | #![cfg_attr(not(windows), forbid(unsafe_code))] 18 | #![forbid(missing_docs, future_incompatible)] 19 | 20 | cfg_if::cfg_if! { 21 | if #[cfg(unix)] { 22 | mod unix; 23 | use unix as sys; 24 | } else if #[cfg(windows)] { 25 | mod windows; 26 | use windows as sys; 27 | } else { 28 | compile_error! { 29 | "async-dns does not support this platform" 30 | } 31 | } 32 | } 33 | 34 | use std::io; 35 | use std::iter::FusedIterator; 36 | use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; 37 | 38 | /// Preform a DNS lookup, retrieving the IP addresses and other necessary information. 39 | pub async fn lookup(name: &str) -> io::Result> { 40 | // Try to parse the name as an IP address. 41 | if let Ok(ip) = name.parse::() { 42 | return Ok(OneOrMany::One(Some(AddressInfo { 43 | ip_address: ip.into(), 44 | }))); 45 | } 46 | 47 | if let Ok(ip) = name.parse::() { 48 | return Ok(OneOrMany::One(Some(AddressInfo { 49 | ip_address: ip.into(), 50 | }))); 51 | } 52 | 53 | // Perform the actual DNS lookup. 54 | sys::lookup(name) 55 | .await 56 | .map(|v| OneOrMany::Many(v.into_iter())) 57 | } 58 | 59 | /// Information about an address. 60 | #[non_exhaustive] 61 | pub struct AddressInfo { 62 | /// The IP address of the system. 63 | pub ip_address: IpAddr, 64 | } 65 | 66 | /// Either an iterator or a single value. 67 | enum OneOrMany { 68 | One(Option), 69 | Many(I), 70 | } 71 | 72 | impl> Iterator for OneOrMany { 73 | type Item = AddressInfo; 74 | 75 | fn next(&mut self) -> Option { 76 | match self { 77 | OneOrMany::One(v) => v.take(), 78 | OneOrMany::Many(v) => v.next(), 79 | } 80 | } 81 | 82 | fn size_hint(&self) -> (usize, Option) { 83 | match self { 84 | OneOrMany::One(v) => (v.is_some() as usize, Some(v.is_some() as usize)), 85 | OneOrMany::Many(v) => v.size_hint(), 86 | } 87 | } 88 | 89 | fn fold(self, init: B, mut f: F) -> B 90 | where 91 | Self: Sized, 92 | F: FnMut(B, Self::Item) -> B, 93 | { 94 | match self { 95 | OneOrMany::One(v) => { 96 | if let Some(v) = v { 97 | f(init, v) 98 | } else { 99 | init 100 | } 101 | } 102 | OneOrMany::Many(v) => v.fold(init, f), 103 | } 104 | } 105 | } 106 | 107 | impl> FusedIterator for OneOrMany {} 108 | 109 | impl> ExactSizeIterator for OneOrMany {} 110 | 111 | impl> DoubleEndedIterator for OneOrMany { 112 | fn next_back(&mut self) -> Option { 113 | match self { 114 | OneOrMany::One(v) => v.take(), 115 | OneOrMany::Many(v) => v.next_back(), 116 | } 117 | } 118 | } 119 | 120 | fn _assert_threadsafe() { 121 | fn _assertion(_: F) {} 122 | _assertion(lookup("foobar")); 123 | } 124 | -------------------------------------------------------------------------------- /async-dns/src/unix.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of `lookup` for Unix systems. 2 | //! 3 | //! This is largely based on the lookup system used in musl libc. Main differences: 4 | //! 5 | //! - Files are read asynchronously. 6 | //! - We check for AAAA addresses after checking for A addresses. 7 | //! - Instead of manually waiting for sockets to become readable, we use several sockets 8 | //! spawned on different tasks and polled using an executor. 9 | //! - We use a more structured DNS protocol implementation instead of messy raw byte manipulation. 10 | //! - The `memchr` crate is used to optimize certain operations. 11 | 12 | use super::AddressInfo; 13 | 14 | use async_executor::Executor; 15 | use async_fs::File; 16 | use async_io::{Async, Timer}; 17 | 18 | use dns_protocol::{Flags, Message, Question, ResourceRecord, ResourceType}; 19 | 20 | use futures_lite::{future, io::BufReader, prelude::*}; 21 | use memchr::memmem; 22 | 23 | use std::cmp; 24 | use std::convert::TryInto; 25 | use std::fmt; 26 | use std::io; 27 | use std::net::{IpAddr, SocketAddr, TcpStream, UdpSocket}; 28 | use std::sync::Arc; 29 | use std::time::Duration; 30 | 31 | /// An executor used to spawn tasks for simultaneous DNS lookups. 32 | /// 33 | /// This is unlikely to ever be initialized, since it is only used when we 34 | /// have more than one nameserver at a time to deal with. 35 | static EXECUTOR: Executor<'static> = Executor::new(); 36 | 37 | pub(super) async fn lookup(name: &str) -> io::Result> { 38 | // We may be able to use the /etc/hosts resolver. 39 | if let Some(addr) = from_hosts(name).await? { 40 | return Ok(vec![addr]); 41 | } 42 | 43 | // Otherwise, we need to use the manual resolver. 44 | let resolv = ResolvConf::load().await?; 45 | dns_with_search(name, &resolv).await 46 | } 47 | 48 | /// Try parsing the name from the "hosts" file. 49 | async fn from_hosts(name: &str) -> io::Result> { 50 | // Open the hosts file. 51 | let file = File::open("/etc/hosts").await?; 52 | let mut file = BufReader::new(file); 53 | 54 | // Create a searcher for the name. 55 | let searcher = memmem::Finder::new(name.as_bytes()); 56 | 57 | // Search for the line in the file. 58 | let mut buf = String::new(); 59 | 60 | loop { 61 | let n = file.read_line(&mut buf).await?; 62 | 63 | // If we read nothing, we reached the end of the file. 64 | if n == 0 { 65 | return Ok(None); 66 | } 67 | 68 | // Pop the newline from the end, if any. 69 | if buf.ends_with('\n') { 70 | buf.pop(); 71 | } 72 | 73 | // If the line has a comment, remove it. 74 | if let Some(n) = memchr::memchr(b'#', buf.as_bytes()) { 75 | buf.truncate(n); 76 | } 77 | 78 | // The "hosts" file may contain our name. 79 | if let Some(index) = searcher.find(buf.as_bytes()) { 80 | // Get the IP address at the start. 81 | let ip_addr = match buf[..index].split_whitespace().next() { 82 | Some(ip_addr) => ip_addr, 83 | None => continue, 84 | }; 85 | 86 | // Parse the IP address. 87 | if let Ok(ip_addr) = ip_addr.parse() { 88 | return Ok(Some(AddressInfo { 89 | ip_address: ip_addr, 90 | })); 91 | } 92 | } 93 | 94 | buf.clear(); 95 | } 96 | } 97 | 98 | /// Preform a DNS lookup, considering the search variable. 99 | async fn dns_with_search(mut name: &str, resolv: &ResolvConf) -> io::Result> { 100 | // See if we should just use global scope. 101 | let num_dots = memchr::Memchr::new(b'.', name.as_bytes()).count(); 102 | let global_scope = num_dots >= resolv.ndots as usize || name.ends_with('.'); 103 | 104 | // Remove the dots from the end of `name`, if needed. 105 | if name.ends_with('.') { 106 | name = &name[..name.len() - 1]; 107 | 108 | // Raise an error if name still ends with a dot. 109 | if name.ends_with('.') { 110 | return Err(io::Error::new( 111 | io::ErrorKind::InvalidInput, 112 | "name ends with a dot", 113 | )); 114 | } 115 | } 116 | 117 | if global_scope { 118 | if let Some(search) = resolv.search.as_ref() { 119 | // Try the name with the search domains. 120 | let mut buffer = String::from(name); 121 | buffer.push('.'); 122 | let name_end = buffer.len(); 123 | 124 | // Try the name with the search domains. 125 | for domain in search.split_whitespace() { 126 | buffer.truncate(name_end); 127 | buffer.push_str(domain); 128 | 129 | if let Ok(addrs) = dns_lookup(&buffer, resolv).await { 130 | if !addrs.is_empty() { 131 | return Ok(addrs); 132 | } 133 | } 134 | } 135 | } 136 | } 137 | 138 | // Preform a DNS search on just the name. 139 | dns_lookup(name, resolv).await 140 | } 141 | 142 | /// Preform a manual lookup for the name. 143 | async fn dns_lookup(name: &str, resolv: &ResolvConf) -> io::Result> { 144 | match resolv.name_servers.len() { 145 | 0 => { 146 | // No nameservers, so we can't do anything. 147 | Ok(vec![]) 148 | } 149 | 1 => { 150 | // Just poll the one nameserver. 151 | let addr = resolv.name_servers[0]; 152 | query_name_and_nameserver(name, addr, resolv).await 153 | } 154 | _ => { 155 | // Use an executor to poll futures in parallel. 156 | let mut tasks = Vec::with_capacity(resolv.name_servers.len()); 157 | let name = Arc::::from(name.to_string().into_boxed_str()); 158 | let resolv = Arc::new(resolv.clone()); 159 | 160 | for ns in resolv.name_servers.iter().copied() { 161 | let name = name.clone(); 162 | let resolv = resolv.clone(); 163 | 164 | tasks.push( 165 | EXECUTOR 166 | .spawn(async move { query_name_and_nameserver(&name, ns, &resolv).await }), 167 | ); 168 | } 169 | 170 | // Poll until every task is complete. 171 | EXECUTOR 172 | .run(async move { 173 | let mut info = Vec::with_capacity(tasks.len()); 174 | for task in tasks { 175 | info.append(&mut task.await?); 176 | } 177 | Ok(info) 178 | }) 179 | .await 180 | } 181 | } 182 | } 183 | 184 | /// Poll for the name on the given nameserver. 185 | async fn query_name_and_nameserver( 186 | name: &str, 187 | nameserver: IpAddr, 188 | resolv: &ResolvConf, 189 | ) -> io::Result> { 190 | // Try to poll for an IPv4 address first. 191 | let mut addrs = 192 | query_question_and_nameserver(Question::new(name, ResourceType::A, 1), nameserver, resolv) 193 | .await?; 194 | 195 | // If we didn't get any addresses, try an IPv6 address. 196 | if addrs.is_empty() { 197 | addrs = query_question_and_nameserver( 198 | Question::new(name, ResourceType::AAAA, 1), 199 | nameserver, 200 | resolv, 201 | ) 202 | .await?; 203 | } 204 | 205 | Ok(addrs) 206 | } 207 | 208 | /// Poll for a DNS response on the given nameserver. 209 | async fn query_question_and_nameserver( 210 | question: Question<'_>, 211 | nameserver: IpAddr, 212 | resolv: &ResolvConf, 213 | ) -> io::Result> { 214 | // Create the DNS query. 215 | // I'd like to use two questions at once, but at least the DNS system I use just drops the packet. 216 | let id = fastrand::u16(..); 217 | let mut questions = [question]; 218 | let message = Message::new( 219 | id, 220 | Flags::standard_query(), 221 | &mut questions, 222 | &mut [], 223 | &mut [], 224 | &mut [], 225 | ); 226 | 227 | // Serialize it to a buffer. 228 | let mut stack_buffer = [0; 512]; 229 | let mut heap_buffer; 230 | let needed = message.space_needed(); 231 | 232 | // Use the stack if we can, but switch to the heap if it's not enough. 233 | let buf = if needed > stack_buffer.len() { 234 | heap_buffer = vec![0; needed]; 235 | heap_buffer.as_mut_slice() 236 | } else { 237 | &mut stack_buffer[..] 238 | }; 239 | 240 | let len = message 241 | .write(buf) 242 | .map_err(|err| io::Error::new(io::ErrorKind::Other, ErrWrap(err)))?; 243 | 244 | // The query may be too large, so we need to use TCP. 245 | if len <= 512 { 246 | if let Some(addrs) = question_with_udp(id, &buf[..len], nameserver, resolv).await? { 247 | return Ok(addrs); 248 | } 249 | } 250 | 251 | // We were unable to complete the query over UDP, use TCP instead. 252 | question_with_tcp(id, &buf[..len], nameserver).await 253 | } 254 | 255 | /// Query a nameserver for the given question, using the UDP protocol. 256 | /// 257 | /// Returns `None` if the UDP query failed and TCP should be used instead. 258 | async fn question_with_udp( 259 | id: u16, 260 | query: &[u8], 261 | nameserver: IpAddr, 262 | resolv: &ResolvConf, 263 | ) -> io::Result>> { 264 | const RECORD_BUFSIZE: usize = 16; 265 | 266 | /// The result of waiting for a packet on a fixed timeout. 267 | enum WaitResult { 268 | /// The packet was received. 269 | Packet { len: usize }, 270 | /// The timeout expired. 271 | TimedOut, 272 | } 273 | 274 | let mut addrs = vec![]; 275 | 276 | // Write the query to the nameserver address. 277 | let socket = Async::::bind(([0, 0, 0, 0], 0))?; 278 | let foreign_addr = SocketAddr::new(nameserver, 53); 279 | 280 | // UDP queries are limited to 512 bytes. 281 | let mut buf = [0; 512]; 282 | 283 | for _ in 0..resolv.attempts { 284 | socket.send_to(query, foreign_addr).await?; 285 | 286 | // Wait for `timeout` seconds for a response. 287 | let timeout = Timer::after(Duration::from_secs(resolv.timeout.into())); 288 | let timeout = async move { 289 | timeout.await; 290 | io::Result::Ok(WaitResult::TimedOut) 291 | }; 292 | let read_packet = async { 293 | let len = socket.recv(&mut buf).await?; 294 | io::Result::Ok(WaitResult::Packet { len }) 295 | }; 296 | 297 | let result = future::or(read_packet, timeout).await?; 298 | 299 | // Get the length of the packet we're reading. 300 | let len = match result { 301 | WaitResult::Packet { len } => len, 302 | WaitResult::TimedOut => { 303 | // Try again. Use yield_now() to give other tasks time if we're in an executor. 304 | future::yield_now().await; 305 | continue; 306 | } 307 | }; 308 | 309 | // Buffers for DNS results. 310 | let mut q_buf = [Question::default(); 1]; 311 | let mut answers = [ResourceRecord::default(); RECORD_BUFSIZE]; 312 | let mut authority = [ResourceRecord::default(); RECORD_BUFSIZE]; 313 | let mut additional = [ResourceRecord::default(); RECORD_BUFSIZE]; 314 | 315 | // Parse the packet. 316 | let message = Message::read( 317 | &buf[..len], 318 | &mut q_buf, 319 | &mut answers, 320 | &mut authority, 321 | &mut additional, 322 | ) 323 | .map_err(|err| io::Error::new(io::ErrorKind::Other, ErrWrap(err)))?; 324 | 325 | // Check the ID. 326 | if message.id() != id { 327 | // Try again. 328 | future::yield_now().await; 329 | continue; 330 | } 331 | 332 | // If the reply was truncated, it's too large for UDP. 333 | if message.flags().truncated() { 334 | return Ok(None); 335 | } 336 | 337 | // Parse the resulting answer. 338 | parse_answers(&message, &mut addrs); 339 | 340 | // We got a response, so we're done. 341 | return Ok(Some(addrs)); 342 | } 343 | 344 | // We did not receive a response. 345 | Ok(None) 346 | } 347 | 348 | /// Query a nameserver for the given question, using the TCP protocol. 349 | #[cold] 350 | async fn question_with_tcp( 351 | id: u16, 352 | query: &[u8], 353 | nameserver: IpAddr, 354 | ) -> io::Result> { 355 | const RECORD_BUFSIZE: usize = 16; 356 | 357 | if query.len() > u16::MAX as usize { 358 | return Err(io::Error::new( 359 | io::ErrorKind::Other, 360 | "query too large for TCP", 361 | )); 362 | } 363 | 364 | // Open the socket to the server. 365 | let mut socket = Async::::connect((nameserver, 53)).await?; 366 | 367 | // Write the length of the query. 368 | let len_bytes = (query.len() as u16).to_be_bytes(); 369 | socket.write_all(&len_bytes).await?; 370 | 371 | // Write the query. 372 | socket.write_all(query).await?; 373 | 374 | // Read the length of the response. 375 | let mut len_bytes = [0; 2]; 376 | socket.read_exact(&mut len_bytes).await?; 377 | let len = u16::from_be_bytes(len_bytes) as usize; 378 | 379 | // Read the response. 380 | let mut stack_buffer = [0; 1024]; 381 | let mut heap_buffer; 382 | let buf = if len > stack_buffer.len() { 383 | // Initialize the heap buffer and return a pointer to it. 384 | heap_buffer = vec![0; len]; 385 | heap_buffer.as_mut_slice() 386 | } else { 387 | &mut stack_buffer 388 | }; 389 | 390 | socket.read_exact(buf).await?; 391 | 392 | // Parse the response. 393 | let mut q_buf = [Question::default(); 1]; 394 | let mut answers = [ResourceRecord::default(); RECORD_BUFSIZE]; 395 | let mut authority = [ResourceRecord::default(); RECORD_BUFSIZE]; 396 | let mut additional = [ResourceRecord::default(); RECORD_BUFSIZE]; 397 | 398 | let message = Message::read( 399 | &buf[..len], 400 | &mut q_buf, 401 | &mut answers, 402 | &mut authority, 403 | &mut additional, 404 | ) 405 | .map_err(|err| io::Error::new(io::ErrorKind::Other, ErrWrap(err)))?; 406 | 407 | if message.id() != id { 408 | return Err(io::Error::new( 409 | io::ErrorKind::Other, 410 | "invalid ID in response", 411 | )); 412 | } 413 | 414 | // Parse the answers as address info. 415 | let mut addrs = vec![]; 416 | parse_answers(&message, &mut addrs); 417 | Ok(addrs) 418 | } 419 | 420 | /// Append address information to the vector, given the DNS response. 421 | fn parse_answers(response: &Message<'_, '_>, addrs: &mut Vec) { 422 | addrs.extend(response.answers().iter().filter_map(|answer| { 423 | let data = answer.data(); 424 | 425 | // Parse the data as an IP address. 426 | match data.len() { 427 | 4 => { 428 | let data: [u8; 4] = data.try_into().unwrap(); 429 | Some(AddressInfo { 430 | ip_address: IpAddr::V4(data.into()), 431 | }) 432 | } 433 | 16 => { 434 | let data: [u8; 16] = data.try_into().unwrap(); 435 | Some(AddressInfo { 436 | ip_address: IpAddr::V6(data.into()), 437 | }) 438 | } 439 | _ => None, 440 | } 441 | })); 442 | } 443 | 444 | /// Structural form of `resolv.conf`. 445 | #[derive(Clone)] 446 | struct ResolvConf { 447 | /// The list of name servers. 448 | name_servers: Vec, 449 | 450 | /// Maximum number of segments in the domain name. 451 | ndots: u8, 452 | 453 | /// Maximum timeout in seconds. 454 | timeout: u8, 455 | 456 | /// Maximum number of retries. 457 | attempts: u8, 458 | 459 | /// The search domain to use. 460 | search: Option, 461 | } 462 | 463 | impl ResolvConf { 464 | /// Load the current configuration from /etc/resolv.conf. 465 | async fn load() -> io::Result { 466 | // Open the file. 467 | let file = File::open("/etc/resolv.conf").await?; 468 | let mut file = BufReader::new(file); 469 | 470 | // Default configuration values. 471 | let mut config = ResolvConf { 472 | name_servers: vec![], 473 | ndots: 1, 474 | timeout: 5, 475 | attempts: 2, 476 | search: None, 477 | }; 478 | 479 | // Begin reading lines. 480 | let mut buf = String::new(); 481 | 482 | loop { 483 | // Read a line. 484 | buf.clear(); 485 | let n = file.read_line(&mut buf).await?; 486 | 487 | // If we read nothing, we reached the end of the file. 488 | if n == 0 { 489 | break; 490 | } 491 | 492 | // Pop the newline from the end, if any. 493 | if buf.ends_with('\n') { 494 | buf.pop(); 495 | } 496 | 497 | // If there is a comment, remove it. 498 | if let Some(n) = memchr::memchr(b'#', buf.as_bytes()) { 499 | buf.truncate(n); 500 | if buf.is_empty() { 501 | continue; 502 | } 503 | } 504 | 505 | if let Some(ns) = buf.strip_prefix("nameserver") { 506 | // Parse the IP address. 507 | if let Ok(ip_addr) = ns.trim().parse() { 508 | config.name_servers.push(ip_addr); 509 | } 510 | 511 | continue; 512 | } else if let Some(options) = buf.strip_prefix("options") { 513 | // Try to find the options. 514 | if let Some(ndots_index) = memmem::find(options.as_bytes(), b"ndots:") { 515 | // Parse the number of dots. 516 | if let Ok(ndots) = options[ndots_index + 6..].trim().parse() { 517 | config.ndots = cmp::min(ndots, 15); 518 | } 519 | 520 | continue; 521 | } else if let Some(timeout_index) = memmem::find(options.as_bytes(), b"timeout:") { 522 | // Parse the timeout. 523 | if let Ok(timeout) = options[timeout_index + 8..].trim().parse() { 524 | config.timeout = cmp::min(timeout, 60); 525 | } 526 | 527 | continue; 528 | } else if let Some(attempts_index) = memmem::find(options.as_bytes(), b"attempts:") 529 | { 530 | // Parse the number of attempts. 531 | if let Ok(attempts) = options[attempts_index + 9..].trim().parse() { 532 | config.attempts = cmp::min(attempts, 10); 533 | } 534 | 535 | continue; 536 | } 537 | } 538 | 539 | // See if we have a search domain. 540 | let search = match buf.strip_prefix("search") { 541 | Some(search) => search, 542 | None => match buf.strip_prefix("domain") { 543 | Some(search) => search, 544 | None => continue, 545 | }, 546 | }; 547 | 548 | // Parse the search domain. 549 | config.search = Some(search.trim().to_string()); 550 | } 551 | 552 | Ok(config) 553 | } 554 | } 555 | 556 | /// Wraps `dns_protocol::Error` so that outside types can't access it, preventing `dns_protocol` from being a public dependency. 557 | struct ErrWrap(dns_protocol::Error); 558 | 559 | impl From for ErrWrap { 560 | fn from(err: dns_protocol::Error) -> Self { 561 | ErrWrap(err) 562 | } 563 | } 564 | 565 | impl fmt::Debug for ErrWrap { 566 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 567 | fmt::Debug::fmt(&self.0, f) 568 | } 569 | } 570 | 571 | impl fmt::Display for ErrWrap { 572 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 573 | fmt::Display::fmt(&self.0, f) 574 | } 575 | } 576 | 577 | impl std::error::Error for ErrWrap {} 578 | -------------------------------------------------------------------------------- /async-dns/src/windows.rs: -------------------------------------------------------------------------------- 1 | //! Windows strategy, using the DNS API. 2 | 3 | use super::AddressInfo; 4 | use windows_sys::Win32::Foundation as found; 5 | use windows_sys::Win32::NetworkManagement::Dns as dns; 6 | 7 | use std::ffi::c_void; 8 | use std::io; 9 | use std::mem; 10 | use std::net::{IpAddr, Ipv6Addr}; 11 | use std::process::abort; 12 | 13 | pub(super) async fn lookup(name: &str) -> io::Result> { 14 | // Query IPv4 addresses. 15 | let mut addrs = dns_query(name, dns::DNS_TYPE_A).await?; 16 | 17 | // If there are no IPv4 addreses, query IPv6 addresses. 18 | if addrs.is_empty() { 19 | addrs = dns_query(name, dns::DNS_TYPE_AAAA).await?; 20 | } 21 | 22 | Ok(addrs) 23 | } 24 | 25 | /// Preform a DNS query for the given DNS type. 26 | async fn dns_query(name: &str, query_type: u16) -> io::Result> { 27 | // If this future is dropped, we need to cancel the query. 28 | struct CancelDns(Option); 29 | 30 | impl CancelDns { 31 | fn defuse(&mut self) { 32 | self.0 = None; 33 | } 34 | } 35 | 36 | impl Drop for CancelDns { 37 | fn drop(&mut self) { 38 | if let Some(cancel) = self.0.take() { 39 | unsafe { 40 | dns::DnsCancelQuery(&cancel); 41 | } 42 | } 43 | } 44 | } 45 | 46 | // Create a channel to receive the results. 47 | let (send, recv) = async_channel::bounded(1); 48 | let mut query_results = QueryResultsSlot(mem::MaybeUninit::uninit()); 49 | 50 | // Make the actual DNS query. 51 | let handle = make_query( 52 | name, 53 | query_type, 54 | &mut query_results.0, 55 | move |result: &mut dns::DNS_QUERY_RESULT| { 56 | // Parse the results. 57 | let mut current = result.pQueryRecords; 58 | 59 | if !current.is_null() { 60 | let mut addrs = Vec::new(); 61 | 62 | loop { 63 | // Parse the current record. 64 | let record = unsafe { &mut *current }; 65 | 66 | match record.wType { 67 | dns::DNS_TYPE_A => { 68 | // It's an IPv4 Address 69 | let ip_addr = unsafe { record.Data.A.IpAddress }; 70 | 71 | let addr_info = AddressInfo { 72 | ip_address: IpAddr::V4(ip_addr.to_be_bytes().into()), 73 | }; 74 | 75 | addrs.push(addr_info); 76 | } 77 | dns::DNS_TYPE_AAAA => { 78 | // It's an IPv6 Address 79 | let ip_addr = unsafe { record.Data.AAAA.Ip6Address.IP6Qword }; 80 | 81 | let left = ip_addr[0].to_be_bytes(); 82 | let right = ip_addr[1].to_be_bytes(); 83 | 84 | let ip_addr = Ipv6Addr::new( 85 | u16::from_be_bytes([left[0], left[1]]), 86 | u16::from_be_bytes([left[2], left[3]]), 87 | u16::from_be_bytes([left[4], left[5]]), 88 | u16::from_be_bytes([left[6], left[7]]), 89 | u16::from_be_bytes([right[0], right[1]]), 90 | u16::from_be_bytes([right[2], right[3]]), 91 | u16::from_be_bytes([right[4], right[5]]), 92 | u16::from_be_bytes([right[6], right[7]]), 93 | ); 94 | 95 | let addr_info = AddressInfo { 96 | ip_address: IpAddr::V6(ip_addr), 97 | }; 98 | 99 | addrs.push(addr_info); 100 | } 101 | _ => { 102 | // Not our concern, ignore it. 103 | } 104 | } 105 | 106 | // Move to the next record. 107 | if record.pNext.is_null() { 108 | break; 109 | } else { 110 | current = record.pNext; 111 | } 112 | } 113 | 114 | // Free the results. 115 | unsafe { 116 | dns::DnsFree(result.pQueryRecords as *const _, dns::DnsFreeRecordList); 117 | } 118 | 119 | // Send the results. 120 | let _ = send.try_send(Ok(addrs)); 121 | } else { 122 | // No available records. 123 | let _ = send.try_send(Ok(vec![])); 124 | } 125 | }, 126 | )?; 127 | let mut guard = CancelDns(handle); 128 | 129 | // Wait for the request to complete. 130 | let res = recv 131 | .recv() 132 | .await 133 | .map_err(|_| io::Error::new(io::ErrorKind::Other, "channel closed unexpectedly"))?; 134 | 135 | // Now that the future has ended, don't cancel the query. 136 | guard.defuse(); 137 | 138 | res 139 | } 140 | 141 | /// Actually make the DNS call, using the given function as a callback. 142 | fn make_query( 143 | name: &str, 144 | query_type: u16, 145 | immediate_results: &mut mem::MaybeUninit, 146 | complete: F, 147 | ) -> io::Result> 148 | where 149 | F: FnOnce(&mut dns::DNS_QUERY_RESULT), 150 | { 151 | // Win32 callback function for the completion. 152 | unsafe extern "system" fn dns_completion_callback( 153 | closure: *const c_void, 154 | results: *mut dns::DNS_QUERY_RESULT, 155 | ) where 156 | F: FnOnce(&mut dns::DNS_QUERY_RESULT), 157 | { 158 | // "closure" is a `Box` in reality. 159 | let _guard = NoPanic; 160 | let closure = Box::from_raw(closure as *mut F); 161 | (closure)(&mut *results); 162 | mem::forget(_guard); 163 | } 164 | 165 | // Box `complete` and store it on the heap. 166 | let complete = Box::new(complete); 167 | 168 | // Convert "name" to a wide string. 169 | let mut name = name.encode_utf16().collect::>(); 170 | name.push(0); 171 | 172 | // Create the initial request. 173 | let request = dns::DNS_QUERY_REQUEST { 174 | Version: dns::DNS_QUERY_REQUEST_VERSION1, 175 | QueryName: name.as_ptr(), 176 | QueryType: query_type, 177 | QueryOptions: 0, 178 | pQueryCompletionCallback: Some(dns_completion_callback::), 179 | pQueryContext: Box::into_raw(complete) as *mut c_void, 180 | InterfaceIndex: 0, 181 | pDnsServerList: std::ptr::null_mut(), 182 | }; 183 | 184 | // Create space for the results. 185 | immediate_results.write(dns::DNS_QUERY_RESULT { 186 | Version: dns::DNS_QUERY_REQUEST_VERSION1, 187 | ..unsafe { mem::zeroed() } 188 | }); 189 | let mut cancel_handle = mem::MaybeUninit::::uninit(); 190 | 191 | // The first field of immediate_results must be version 1. 192 | 193 | // Call the function proper. 194 | let res = unsafe { 195 | dns::DnsQueryEx( 196 | &request, 197 | immediate_results.as_mut_ptr(), 198 | cancel_handle.as_mut_ptr(), 199 | ) 200 | }; 201 | 202 | const ERROR_SUCCESS: i32 = found::ERROR_SUCCESS as i32; 203 | 204 | // Determine what the result is. 205 | match res { 206 | ERROR_SUCCESS => { 207 | // The query was successful and it completed immediately. 208 | // Get the closure back and run it. 209 | let closure = unsafe { Box::from_raw(request.pQueryContext as *mut F) }; 210 | (closure)(unsafe { immediate_results.assume_init_mut() }); 211 | Ok(None) 212 | } 213 | found::DNS_REQUEST_PENDING => { 214 | // The request is pending. We should now wait for it. 215 | Ok(Some(unsafe { cancel_handle.assume_init() })) 216 | } 217 | err => { 218 | // The request failed. The closure will not be called, so dealloc it. 219 | drop(unsafe { Box::from_raw(request.pQueryContext as *mut F) }); 220 | 221 | println!("got error code: {err:}"); 222 | 223 | // This may be a DNS error. 224 | if matches!(err, 0x2329..=0x26B2) { 225 | Err(io::Error::new( 226 | io::ErrorKind::Other, 227 | format!("DNS error: {}", err - 0x2328), 228 | )) 229 | } else { 230 | // Otherwise, it's a Win32 error. 231 | Err(io::Error::from_raw_os_error(err)) 232 | } 233 | } 234 | } 235 | } 236 | 237 | /// If a panic occurs, abort the process. 238 | struct NoPanic; 239 | 240 | impl Drop for NoPanic { 241 | fn drop(&mut self) { 242 | abort(); 243 | } 244 | } 245 | 246 | struct QueryResultsSlot(mem::MaybeUninit); 247 | 248 | unsafe impl Send for QueryResultsSlot {} 249 | unsafe impl Sync for QueryResultsSlot {} 250 | -------------------------------------------------------------------------------- /async-dns/tests/lookup.rs: -------------------------------------------------------------------------------- 1 | //! lookup.rs example adapted into a test. 2 | //! 3 | //! Will work as long as google.com exists. 4 | 5 | #[test] 6 | fn google_lookup() { 7 | async_io::block_on(async { 8 | // Perform the lookup. 9 | let ips = async_dns::lookup("google.com").await.unwrap(); 10 | assert_ne!(ips.count(), 0); 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /dns-protocol/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v0.1.1 2 | 3 | - Fix a parsing bug in the record implementation. (#2) 4 | -------------------------------------------------------------------------------- /dns-protocol/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dns-protocol" 3 | version = "0.1.2" 4 | edition = "2018" 5 | authors = ["John Nunley "] 6 | description = "A DNS protocol implementation in Rust" 7 | license = "MIT OR Apache-2.0" 8 | homepage = "https://github.com/notgull/async-dns/tree/master/dns-protocol#readme" 9 | repository = "https://github.com/notgull/async-dns" 10 | rust-version = "1.63" 11 | 12 | [dependencies] 13 | memchr = { version = "2.5.0", default-features = false } 14 | 15 | [features] 16 | default = ["std"] 17 | std = [] 18 | -------------------------------------------------------------------------------- /dns-protocol/README.md: -------------------------------------------------------------------------------- 1 | # dns-protocol 2 | 3 | This crate provides a `no_std` implementation of the DNS protocol. 4 | 5 | In order to make it trivial for others to build implementations of the DNS protocol, this crate provides a [sans-I/O] implementation of the protocol. This means that it doesn't provide any I/O functionality, but instead provides a way to parse and serialize DNS messages. 6 | 7 | In addition, this crate is not only `no_std`, but also `alloc`-free. This means that it can be used in environments where `alloc` is not available, such as embedded systems. It also has no unsafe code. 8 | 9 | However, there is a catch. Since this system does not allocate, the user is responsible for providing a buffer to parse DNS messages into. This means that the user must know the maximum size of a DNS message that they will be parsing. This is a reasonable assumption, since DNS messages are limited to 512 bytes in the common case. 10 | 11 | [sans-I/O]: https://sans-io.readthedocs.io/en/latest/ 12 | 13 | ## MSRV 14 | 15 | The Minimum Supported Rust Version (MSRV) for this crate is Rust 1.63. Any change to this MSRV will be accompanied by a minor version bump. 16 | 17 | ## License 18 | 19 | Dual licensed under the MIT and Apache 2.0 licenses. 20 | -------------------------------------------------------------------------------- /dns-protocol/examples/nslookup.rs: -------------------------------------------------------------------------------- 1 | //! Synchronous implementation of `nslookup`, using `dns-protocol`. 2 | //! 3 | //! Only works on Unix. 4 | 5 | use std::env; 6 | use std::fs; 7 | use std::io::{prelude::*, BufReader}; 8 | use std::net::Ipv4Addr; 9 | use std::net::{IpAddr, UdpSocket}; 10 | use std::process; 11 | 12 | use dns_protocol::Flags; 13 | use dns_protocol::{Message, Question, ResourceRecord, ResourceType}; 14 | 15 | fn main() -> Result<(), Box> { 16 | // The first argument is the name to lookup. 17 | let mut args = env::args(); 18 | let program_name = args.next().unwrap(); 19 | let name = match args.next() { 20 | Some(name) => name, 21 | None => { 22 | eprintln!("Usage: {} ", &program_name); 23 | process::exit(1); 24 | } 25 | }; 26 | 27 | // Search in resolv.conf for the nameserver. 28 | // 29 | // A production-grade implementation would consider multiple nameservers in 30 | // resolv.conf, and then poll them all at once. 31 | let resolv = BufReader::new(fs::File::open("/etc/resolv.conf")?); 32 | let mut nameserver = None; 33 | 34 | for line in resolv.lines() { 35 | let line = line?; 36 | if line.starts_with("nameserver") { 37 | let result = line.split_whitespace().nth(1).unwrap(); 38 | if let Ok(ns) = result.parse::() { 39 | nameserver = Some(ns); 40 | break; 41 | } 42 | } 43 | } 44 | 45 | let nameserver = match nameserver { 46 | Some(ns) => ns, 47 | None => { 48 | eprintln!("No nameserver found in /etc/resolv.conf"); 49 | process::exit(1); 50 | } 51 | }; 52 | 53 | println!("Nameserver: {}", nameserver); 54 | 55 | // Create the message we need to send. 56 | let mut questions = [Question::new(name.as_str(), ResourceType::A, 1)]; 57 | let message = Message::new( 58 | 0xFEE7, 59 | Flags::standard_query(), 60 | &mut questions, 61 | &mut [], 62 | &mut [], 63 | &mut [], 64 | ); 65 | 66 | // Allocate the buffer that we need to send. 67 | let mut buffer = vec![0; message.space_needed()]; 68 | message.write(&mut buffer)?; 69 | 70 | // Send the packet to our nameserver over UDP. 71 | let socket = UdpSocket::bind((Ipv4Addr::from([127, 0, 0, 1]), 0))?; 72 | socket.send_to(&buffer, (nameserver, 53))?; 73 | 74 | // Wait for a response. 75 | // 76 | // A production-grade implementation would respect timeout/attempts settings in 77 | // resolv.conf. 78 | let mut buffer = vec![0; 1024]; 79 | let len = socket.recv(&mut buffer)?; 80 | 81 | // Parse the response. 82 | let mut answers = [ResourceRecord::default(); 16]; 83 | let mut authority = [ResourceRecord::default(); 16]; 84 | let mut additional = [ResourceRecord::default(); 16]; 85 | let message = Message::read( 86 | &buffer[..len], 87 | &mut questions, 88 | &mut answers, 89 | &mut authority, 90 | &mut additional, 91 | )?; 92 | 93 | println!(";; Got answer: {:?}", message.flags().response_code()); 94 | 95 | // Print the answers. 96 | for answer in message.answers() { 97 | // Determine the IP address. 98 | match answer.data().len() { 99 | 4 => { 100 | let mut ip = [0u8; 4]; 101 | ip.copy_from_slice(answer.data()); 102 | let ip = Ipv4Addr::from(ip); 103 | println!("{} has address {}", answer.name(), ip); 104 | } 105 | 16 => { 106 | let mut ip = [0u8; 16]; 107 | ip.copy_from_slice(answer.data()); 108 | let ip = IpAddr::from(ip); 109 | println!("{} has address {}", answer.name(), ip); 110 | } 111 | _ => { 112 | println!("{} has unknown address type", answer.name()); 113 | } 114 | } 115 | } 116 | 117 | Ok(()) 118 | } 119 | -------------------------------------------------------------------------------- /dns-protocol/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! An implementation of the DNS protocol, [sans I/O]. 2 | //! 3 | //! [sans I/O]: https://sans-io.readthedocs.io/ 4 | //! 5 | //! This crate implements the Domain System Protocol, used for namespace lookups among 6 | //! other things. It is intended to be used in conjunction with a transport layer, such 7 | //! as UDP, TCP or HTTPS The goal of this crate is to provide a runtime and protocol-agnostic 8 | //! implementation of the DNS protocol, so that it can be used in a variety of contexts. 9 | //! 10 | //! This crate is not only `no_std`, it does not use an allocator as well. This means that it 11 | //! can be used on embedded systems that do not have allocators, and that it does no allocation 12 | //! of its own. However, this comes with a catch: the user is expected to provide their own 13 | //! buffers for the various operations. This is done to avoid the need for an allocator, and 14 | //! to allow the user to control the memory usage of the library. 15 | //! 16 | //! This crate is also `#![forbid(unsafe_code)]`, and is intended to remain so. 17 | //! 18 | //! # Example 19 | //! 20 | //! ```no_run 21 | //! # fn main() -> Result<(), Box> { 22 | //! use dns_protocol::{Message, Question, ResourceRecord, ResourceType, Flags}; 23 | //! use std::net::UdpSocket; 24 | //! 25 | //! // Allocate a buffer for the message. 26 | //! let mut buf = vec![0; 1024]; 27 | //! 28 | //! // Create a message. This is a query for the A record of example.com. 29 | //! let mut questions = [ 30 | //! Question::new( 31 | //! "example.com", 32 | //! ResourceType::A, 33 | //! 0, 34 | //! ) 35 | //! ]; 36 | //! let mut answers = [ResourceRecord::default()]; 37 | //! let message = Message::new(0x42, Flags::default(), &mut questions, &mut answers, &mut [], &mut []); 38 | //! 39 | //! // Serialize the message into the buffer 40 | //! assert!(message.space_needed() <= buf.len()); 41 | //! let len = message.write(&mut buf)?; 42 | //! 43 | //! // Write the buffer to the socket. 44 | //! let socket = UdpSocket::bind("localhost:0")?; 45 | //! socket.send_to(&buf[..len], "1.2.3.4:53")?; 46 | //! 47 | //! // Read new data from the socket. 48 | //! let data_len = socket.recv(&mut buf)?; 49 | //! 50 | //! // Parse the data as a message. 51 | //! let message = Message::read( 52 | //! &buf[..data_len], 53 | //! &mut questions, 54 | //! &mut answers, 55 | //! &mut [], 56 | //! &mut [], 57 | //! )?; 58 | //! 59 | //! // Read the answer from the message. 60 | //! let answer = message.answers()[0]; 61 | //! println!("Answer Data: {:?}", answer.data()); 62 | //! # Ok(()) 63 | //! # } 64 | //! ``` 65 | //! 66 | //! # Features 67 | //! 68 | //! - `std` (enabled by default) - Enables the `std` library for use in `Error` types. 69 | //! Disable this feature to use on `no_std` targets. 70 | //! ``` 71 | 72 | #![forbid( 73 | unsafe_code, 74 | missing_docs, 75 | missing_debug_implementations, 76 | rust_2018_idioms, 77 | future_incompatible 78 | )] 79 | #![no_std] 80 | 81 | #[cfg(feature = "std")] 82 | extern crate std; 83 | 84 | use core::convert::{TryFrom, TryInto}; 85 | use core::fmt; 86 | use core::iter; 87 | use core::mem; 88 | use core::num::NonZeroUsize; 89 | use core::str; 90 | 91 | #[cfg(feature = "std")] 92 | use std::error::Error as StdError; 93 | 94 | mod ser; 95 | use ser::{Cursor, Serialize}; 96 | pub use ser::{Label, LabelSegment}; 97 | 98 | /// Macro to implement `Serialize` for a struct. 99 | macro_rules! serialize { 100 | ( 101 | $(#[$outer:meta])* 102 | pub struct $name:ident $(<$lt: lifetime>)? { 103 | $( 104 | $(#[$inner:meta])* 105 | $vis: vis $field:ident: $ty:ty, 106 | )* 107 | } 108 | ) => { 109 | $(#[$outer])* 110 | pub struct $name $(<$lt>)? { 111 | $( 112 | $(#[$inner])* 113 | $vis $field: $ty, 114 | )* 115 | } 116 | 117 | impl<'a> Serialize<'a> for $name $(<$lt>)? { 118 | fn serialized_len(&self) -> usize { 119 | let mut len = 0; 120 | $( 121 | len += self.$field.serialized_len(); 122 | )* 123 | len 124 | } 125 | 126 | fn serialize(&self, cursor: &mut [u8]) -> Result { 127 | let mut index = 0; 128 | $( 129 | index += self.$field.serialize(&mut cursor[index..])?; 130 | )* 131 | Ok(index) 132 | } 133 | 134 | fn deserialize(&mut self, mut cursor: Cursor<'a>) -> Result, Error> { 135 | $( 136 | cursor = self.$field.deserialize(cursor)?; 137 | )* 138 | Ok(cursor) 139 | } 140 | } 141 | }; 142 | } 143 | 144 | /// An enum with a bevy of given variants. 145 | macro_rules! num_enum { 146 | ( 147 | $(#[$outer:meta])* 148 | pub enum $name:ident { 149 | $( 150 | $(#[$inner:meta])* 151 | $variant:ident = $value:expr, 152 | )* 153 | } 154 | ) => { 155 | $(#[$outer])* 156 | #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 157 | #[repr(u16)] 158 | // New codes may be added in the future. 159 | #[non_exhaustive] 160 | pub enum $name { 161 | $( 162 | $(#[$inner])* 163 | $variant = $value, 164 | )* 165 | } 166 | 167 | impl TryFrom for $name { 168 | type Error = InvalidCode; 169 | 170 | fn try_from(value: u16) -> Result { 171 | match value { 172 | $( 173 | $value => Ok($name::$variant), 174 | )* 175 | _ => Err(InvalidCode(value)), 176 | } 177 | } 178 | } 179 | 180 | impl From<$name> for u16 { 181 | fn from(value: $name) -> Self { 182 | value as u16 183 | } 184 | } 185 | 186 | impl<'a> Serialize<'a> for $name { 187 | fn serialized_len(&self) -> usize { 188 | mem::size_of::() 189 | } 190 | 191 | fn serialize(&self, cursor: &mut [u8]) -> Result { 192 | let value: u16 = (*self).into(); 193 | value.serialize(cursor) 194 | } 195 | 196 | fn deserialize(&mut self, cursor: Cursor<'a>) -> Result, Error> { 197 | let mut value = 0; 198 | let cursor = value.deserialize(cursor)?; 199 | *self = value.try_into()?; 200 | Ok(cursor) 201 | } 202 | } 203 | }; 204 | } 205 | 206 | /// An error that may occur while using the DNS protocol. 207 | #[derive(Debug)] 208 | #[non_exhaustive] 209 | pub enum Error { 210 | /// We are trying to write to a buffer, but the buffer doesn't have enough space. 211 | /// 212 | /// This error can be fixed by increasing the size of the buffer. 213 | NotEnoughWriteSpace { 214 | /// The number of entries we tried to write. 215 | tried_to_write: NonZeroUsize, 216 | 217 | /// The number of entries that were available in the buffer. 218 | available: usize, 219 | 220 | /// The type of the buffer that we tried to write to. 221 | buffer_type: &'static str, 222 | }, 223 | 224 | /// We attempted to read from a buffer, but we ran out of room before we could read the entire 225 | /// value. 226 | /// 227 | /// This error can be fixed by reading more bytes. 228 | NotEnoughReadBytes { 229 | /// The number of bytes we tried to read. 230 | tried_to_read: NonZeroUsize, 231 | 232 | /// The number of bytes that were available in the buffer. 233 | available: usize, 234 | }, 235 | 236 | /// We tried to parse this value, but it was invalid. 237 | Parse { 238 | /// The name of the value we tried to parse. 239 | name: &'static str, 240 | }, 241 | 242 | /// We tried to serialize a string longer than 256 bytes. 243 | NameTooLong(usize), 244 | 245 | /// We could not create a valid UTF-8 string from the bytes we read. 246 | InvalidUtf8(str::Utf8Error), 247 | 248 | /// We could not convert a raw number to a code. 249 | InvalidCode(InvalidCode), 250 | 251 | /// We do not support this many URL segments. 252 | TooManyUrlSegments(usize), 253 | } 254 | 255 | impl fmt::Display for Error { 256 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 257 | match self { 258 | Error::NotEnoughWriteSpace { 259 | tried_to_write, 260 | available, 261 | buffer_type, 262 | } => { 263 | write!( 264 | f, 265 | "not enough write space: tried to write {} entries to {} buffer, but only {} were available", 266 | tried_to_write, buffer_type, available 267 | ) 268 | } 269 | Error::NotEnoughReadBytes { 270 | tried_to_read, 271 | available, 272 | } => { 273 | write!( 274 | f, 275 | "not enough read bytes: tried to read {} bytes, but only {} were available", 276 | tried_to_read, available 277 | ) 278 | } 279 | Error::Parse { name } => { 280 | write!(f, "parse error: could not parse a {}", name) 281 | } 282 | Error::NameTooLong(len) => { 283 | write!(f, "name too long: name was {} bytes long", len) 284 | } 285 | Error::InvalidUtf8(err) => { 286 | write!(f, "invalid UTF-8: {}", err) 287 | } 288 | Error::TooManyUrlSegments(segments) => { 289 | write!(f, "too many URL segments: {} segments", segments) 290 | } 291 | Error::InvalidCode(err) => { 292 | write!(f, "{}", err) 293 | } 294 | } 295 | } 296 | } 297 | 298 | #[cfg(feature = "std")] 299 | impl StdError for Error { 300 | fn source(&self) -> Option<&(dyn StdError + 'static)> { 301 | match self { 302 | Error::InvalidUtf8(err) => Some(err), 303 | Error::InvalidCode(err) => Some(err), 304 | _ => None, 305 | } 306 | } 307 | } 308 | 309 | impl From for Error { 310 | fn from(err: str::Utf8Error) -> Self { 311 | Error::InvalidUtf8(err) 312 | } 313 | } 314 | 315 | impl From for Error { 316 | fn from(err: InvalidCode) -> Self { 317 | Error::InvalidCode(err) 318 | } 319 | } 320 | 321 | /// The message for a DNS query. 322 | #[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash)] 323 | pub struct Message<'arrays, 'innards> { 324 | /// The header of the message. 325 | header: Header, 326 | 327 | /// The questions in the message. 328 | /// 329 | /// **Invariant:** This is always greater than or equal to `header.question_count`. 330 | questions: &'arrays mut [Question<'innards>], 331 | 332 | /// The answers in the message. 333 | /// 334 | /// **Invariant:** This is always greater than or equal to `header.answer_count`. 335 | answers: &'arrays mut [ResourceRecord<'innards>], 336 | 337 | /// The authorities in the message. 338 | /// 339 | /// **Invariant:** This is always greater than or equal to `header.authority_count`. 340 | authorities: &'arrays mut [ResourceRecord<'innards>], 341 | 342 | /// The additional records in the message. 343 | /// 344 | /// **Invariant:** This is always greater than or equal to `header.additional_count`. 345 | additional: &'arrays mut [ResourceRecord<'innards>], 346 | } 347 | 348 | impl fmt::Debug for Message<'_, '_> { 349 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 350 | f.debug_struct("Message") 351 | .field("header", &self.header) 352 | .field("questions", &self.questions()) 353 | .field("answers", &self.answers()) 354 | .field("authorities", &self.authorities()) 355 | .field("additional", &self.additional()) 356 | .finish() 357 | } 358 | } 359 | 360 | impl<'arrays, 'innards> Message<'arrays, 'innards> { 361 | /// Create a new message from a set of buffers for each section. 362 | /// 363 | /// # Panics 364 | /// 365 | /// This function panics if the number of questions, answers, authorities, or additional records 366 | /// is greater than `u16::MAX`. 367 | pub fn new( 368 | id: u16, 369 | flags: Flags, 370 | questions: &'arrays mut [Question<'innards>], 371 | answers: &'arrays mut [ResourceRecord<'innards>], 372 | authorities: &'arrays mut [ResourceRecord<'innards>], 373 | additional: &'arrays mut [ResourceRecord<'innards>], 374 | ) -> Self { 375 | Self { 376 | header: Header { 377 | id, 378 | flags, 379 | question_count: questions.len().try_into().unwrap(), 380 | answer_count: answers.len().try_into().unwrap(), 381 | authority_count: authorities.len().try_into().unwrap(), 382 | additional_count: additional.len().try_into().unwrap(), 383 | }, 384 | questions, 385 | answers, 386 | authorities, 387 | additional, 388 | } 389 | } 390 | 391 | /// Get the ID of this message. 392 | pub fn id(&self) -> u16 { 393 | self.header.id 394 | } 395 | 396 | /// Get a mutable reference to the ID of this message. 397 | pub fn id_mut(&mut self) -> &mut u16 { 398 | &mut self.header.id 399 | } 400 | 401 | /// Get the header of this message. 402 | pub fn header(&self) -> Header { 403 | self.header 404 | } 405 | 406 | /// Get the flags for this message. 407 | pub fn flags(&self) -> Flags { 408 | self.header.flags 409 | } 410 | 411 | /// Get a mutable reference to the flags for this message. 412 | pub fn flags_mut(&mut self) -> &mut Flags { 413 | &mut self.header.flags 414 | } 415 | 416 | /// Get the questions in this message. 417 | pub fn questions(&self) -> &[Question<'innards>] { 418 | &self.questions[..self.header.question_count as usize] 419 | } 420 | 421 | /// Get a mutable reference to the questions in this message. 422 | pub fn questions_mut(&mut self) -> &mut [Question<'innards>] { 423 | &mut self.questions[..self.header.question_count as usize] 424 | } 425 | 426 | /// Get the answers in this message. 427 | pub fn answers(&self) -> &[ResourceRecord<'innards>] { 428 | &self.answers[..self.header.answer_count as usize] 429 | } 430 | 431 | /// Get a mutable reference to the answers in this message. 432 | pub fn answers_mut(&mut self) -> &mut [ResourceRecord<'innards>] { 433 | &mut self.answers[..self.header.answer_count as usize] 434 | } 435 | 436 | /// Get the authorities in this message. 437 | pub fn authorities(&self) -> &[ResourceRecord<'innards>] { 438 | &self.authorities[..self.header.authority_count as usize] 439 | } 440 | 441 | /// Get a mutable reference to the authorities in this message. 442 | pub fn authorities_mut(&mut self) -> &mut [ResourceRecord<'innards>] { 443 | &mut self.authorities[..self.header.authority_count as usize] 444 | } 445 | 446 | /// Get the additional records in this message. 447 | pub fn additional(&self) -> &[ResourceRecord<'innards>] { 448 | &self.additional[..self.header.additional_count as usize] 449 | } 450 | 451 | /// Get a mutable reference to the additional records in this message. 452 | pub fn additional_mut(&mut self) -> &mut [ResourceRecord<'innards>] { 453 | &mut self.additional[..self.header.additional_count as usize] 454 | } 455 | 456 | /// Get the buffer space needed to serialize this message. 457 | pub fn space_needed(&self) -> usize { 458 | self.serialized_len() 459 | } 460 | 461 | /// Write this message to a buffer. 462 | /// 463 | /// Returns the number of bytes written. 464 | /// 465 | /// # Errors 466 | /// 467 | /// This function may raise [`Error::NameTooLong`] if a `Label` is too long to be serialized. 468 | /// 469 | /// # Panics 470 | /// 471 | /// This function panics if the buffer is not large enough to hold the serialized message. This 472 | /// panic can be avoided by ensuring the buffer contains at least [`space_needed`] bytes. 473 | pub fn write(&self, buffer: &mut [u8]) -> Result { 474 | self.serialize(buffer) 475 | } 476 | 477 | /// Read a message from a buffer. 478 | /// 479 | /// # Errors 480 | /// 481 | /// This function may raise one of the following errors: 482 | /// 483 | /// - [`Error::NotEnoughReadBytes`] if the buffer is not large enough to hold the entire structure. 484 | /// You may need to read more data before calling this function again. 485 | /// - [`Error::NotEnoughWriteSpace`] if the buffers provided are not large enough to hold the 486 | /// entire structure. You may need to allocate larger buffers before calling this function. 487 | /// - [`Error::InvalidUtf8`] if a domain name contains invalid UTF-8. 488 | /// - [`Error::NameTooLong`] if a domain name is too long to be deserialized. 489 | /// - [`Error::InvalidCode`] if a domain name contains an invalid label code. 490 | pub fn read( 491 | buffer: &'innards [u8], 492 | questions: &'arrays mut [Question<'innards>], 493 | answers: &'arrays mut [ResourceRecord<'innards>], 494 | authorities: &'arrays mut [ResourceRecord<'innards>], 495 | additional: &'arrays mut [ResourceRecord<'innards>], 496 | ) -> Result, Error> { 497 | // Create a message and cursor, then deserialize the message from the cursor. 498 | let mut message = Message::new( 499 | 0, 500 | Flags::default(), 501 | questions, 502 | answers, 503 | authorities, 504 | additional, 505 | ); 506 | let cursor = Cursor::new(buffer); 507 | 508 | message.deserialize(cursor)?; 509 | 510 | Ok(message) 511 | } 512 | } 513 | 514 | impl<'arrays, 'innards> Serialize<'innards> for Message<'arrays, 'innards> { 515 | fn serialized_len(&self) -> usize { 516 | iter::once(self.header.serialized_len()) 517 | .chain(self.questions().iter().map(Serialize::serialized_len)) 518 | .chain(self.answers().iter().map(Serialize::serialized_len)) 519 | .chain(self.authorities().iter().map(Serialize::serialized_len)) 520 | .chain(self.additional().iter().map(Serialize::serialized_len)) 521 | .fold(0, |a, b| a.saturating_add(b)) 522 | } 523 | 524 | fn serialize(&self, bytes: &mut [u8]) -> Result { 525 | let mut offset = 0; 526 | offset += self.header.serialize(&mut bytes[offset..])?; 527 | for question in self.questions.iter() { 528 | offset += question.serialize(&mut bytes[offset..])?; 529 | } 530 | for answer in self.answers.iter() { 531 | offset += answer.serialize(&mut bytes[offset..])?; 532 | } 533 | for authority in self.authorities.iter() { 534 | offset += authority.serialize(&mut bytes[offset..])?; 535 | } 536 | for additional in self.additional.iter() { 537 | offset += additional.serialize(&mut bytes[offset..])?; 538 | } 539 | Ok(offset) 540 | } 541 | 542 | fn deserialize(&mut self, cursor: Cursor<'innards>) -> Result, Error> { 543 | /// Read a set of `T`, bounded by `count`. 544 | fn try_read_set<'a, T: Serialize<'a>>( 545 | mut cursor: Cursor<'a>, 546 | count: usize, 547 | items: &mut [T], 548 | name: &'static str, 549 | ) -> Result, Error> { 550 | let len = items.len(); 551 | 552 | if count == 0 { 553 | return Ok(cursor); 554 | } 555 | 556 | for i in 0..count { 557 | cursor = items 558 | .get_mut(i) 559 | .ok_or_else(|| Error::NotEnoughWriteSpace { 560 | tried_to_write: NonZeroUsize::new(count).unwrap(), 561 | available: len, 562 | buffer_type: name, 563 | })? 564 | .deserialize(cursor)?; 565 | } 566 | 567 | Ok(cursor) 568 | } 569 | 570 | let cursor = self.header.deserialize(cursor)?; 571 | 572 | // If we're truncated, we can't read the rest of the message. 573 | if self.header.flags.truncated() { 574 | self.header.clear(); 575 | return Ok(cursor); 576 | } 577 | 578 | let cursor = try_read_set( 579 | cursor, 580 | self.header.question_count as usize, 581 | self.questions, 582 | "Question", 583 | )?; 584 | let cursor = try_read_set( 585 | cursor, 586 | self.header.answer_count as usize, 587 | self.answers, 588 | "Answer", 589 | )?; 590 | let cursor = try_read_set( 591 | cursor, 592 | self.header.authority_count as usize, 593 | self.authorities, 594 | "Authority", 595 | )?; 596 | let cursor = try_read_set( 597 | cursor, 598 | self.header.additional_count as usize, 599 | self.additional, 600 | "Additional", 601 | )?; 602 | 603 | Ok(cursor) 604 | } 605 | } 606 | 607 | serialize! { 608 | /// The header for a DNS query. 609 | #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 610 | pub struct Header { 611 | /// The ID of this query. 612 | id: u16, 613 | 614 | /// The flags associated with this query. 615 | flags: Flags, 616 | 617 | /// The number of questions in this query. 618 | question_count: u16, 619 | 620 | /// The number of answers in this query. 621 | answer_count: u16, 622 | 623 | /// The number of authorities in this query. 624 | authority_count: u16, 625 | 626 | /// The number of additional records in this query. 627 | additional_count: u16, 628 | } 629 | } 630 | 631 | impl Header { 632 | fn clear(&mut self) { 633 | self.question_count = 0; 634 | self.answer_count = 0; 635 | self.authority_count = 0; 636 | self.additional_count = 0; 637 | } 638 | } 639 | 640 | serialize! { 641 | /// The question in a DNS query. 642 | #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] 643 | pub struct Question<'a> { 644 | /// The name of the question. 645 | name: Label<'a>, 646 | 647 | /// The type of the question. 648 | ty: ResourceType, 649 | 650 | /// The class of the question. 651 | class: u16, 652 | } 653 | } 654 | 655 | impl<'a> Question<'a> { 656 | /// Create a new question. 657 | pub fn new(label: impl Into>, ty: ResourceType, class: u16) -> Self { 658 | Self { 659 | name: label.into(), 660 | ty, 661 | class, 662 | } 663 | } 664 | 665 | /// Get the name of the question. 666 | pub fn name(&self) -> Label<'a> { 667 | self.name 668 | } 669 | 670 | /// Get the type of the question. 671 | pub fn ty(&self) -> ResourceType { 672 | self.ty 673 | } 674 | 675 | /// Get the class of the question. 676 | pub fn class(&self) -> u16 { 677 | self.class 678 | } 679 | } 680 | 681 | serialize! { 682 | /// A resource record in a DNS query. 683 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 684 | pub struct ResourceRecord<'a> { 685 | /// The name of the resource record. 686 | name: Label<'a>, 687 | 688 | /// The type of the resource record. 689 | ty: ResourceType, 690 | 691 | /// The class of the resource record. 692 | class: u16, 693 | 694 | /// The time-to-live of the resource record. 695 | ttl: u32, 696 | 697 | /// The data of the resource record. 698 | data: ResourceData<'a>, 699 | } 700 | } 701 | 702 | impl<'a> ResourceRecord<'a> { 703 | /// Create a new `ResourceRecord`. 704 | pub fn new( 705 | name: impl Into>, 706 | ty: ResourceType, 707 | class: u16, 708 | ttl: u32, 709 | data: &'a [u8], 710 | ) -> Self { 711 | Self { 712 | name: name.into(), 713 | ty, 714 | class, 715 | ttl, 716 | data: data.into(), 717 | } 718 | } 719 | 720 | /// Get the name of the resource record. 721 | pub fn name(&self) -> Label<'a> { 722 | self.name 723 | } 724 | 725 | /// Get the type of the resource record. 726 | pub fn ty(&self) -> ResourceType { 727 | self.ty 728 | } 729 | 730 | /// Get the class of the resource record. 731 | pub fn class(&self) -> u16 { 732 | self.class 733 | } 734 | 735 | /// Get the time-to-live of the resource record. 736 | pub fn ttl(&self) -> u32 { 737 | self.ttl 738 | } 739 | 740 | /// Get the data of the resource record. 741 | pub fn data(&self) -> &'a [u8] { 742 | self.data.0 743 | } 744 | } 745 | 746 | /// The resource stored in a resource record. 747 | #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] 748 | pub(crate) struct ResourceData<'a>(&'a [u8]); 749 | 750 | impl<'a> From<&'a [u8]> for ResourceData<'a> { 751 | fn from(data: &'a [u8]) -> Self { 752 | ResourceData(data) 753 | } 754 | } 755 | 756 | impl<'a> Serialize<'a> for ResourceData<'a> { 757 | fn serialized_len(&self) -> usize { 758 | 2 + self.0.len() 759 | } 760 | 761 | fn serialize(&self, bytes: &mut [u8]) -> Result { 762 | let len = self.serialized_len(); 763 | if bytes.len() < len { 764 | panic!("not enough bytes to serialize resource data"); 765 | } 766 | 767 | // Write the length as a big-endian u16 768 | let [b1, b2] = (self.0.len() as u16).to_be_bytes(); 769 | bytes[0] = b1; 770 | bytes[1] = b2; 771 | 772 | // Write the data 773 | bytes[2..len].copy_from_slice(self.0); 774 | 775 | Ok(len) 776 | } 777 | 778 | fn deserialize(&mut self, cursor: Cursor<'a>) -> Result, Error> { 779 | // Deserialize a u16 for the length 780 | let mut len = 0u16; 781 | let cursor = len.deserialize(cursor)?; 782 | 783 | if len == 0 { 784 | self.0 = &[]; 785 | return Ok(cursor); 786 | } 787 | 788 | // Read in the data 789 | if cursor.len() < len as usize { 790 | return Err(Error::NotEnoughReadBytes { 791 | tried_to_read: NonZeroUsize::new(len as usize).unwrap(), 792 | available: cursor.len(), 793 | }); 794 | } 795 | 796 | self.0 = &cursor.remaining()[..len as usize]; 797 | cursor.advance(len as usize) 798 | } 799 | } 800 | 801 | /// The flags associated with a DNS message. 802 | #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 803 | #[repr(transparent)] 804 | pub struct Flags(u16); 805 | 806 | impl Flags { 807 | // Values used to manipulate the inside. 808 | const RAW_QR: u16 = 1 << 15; 809 | const RAW_OPCODE_SHIFT: u16 = 11; 810 | const RAW_OPCODE_MASK: u16 = 0b1111; 811 | const RAW_AA: u16 = 1 << 10; 812 | const RAW_TC: u16 = 1 << 9; 813 | const RAW_RD: u16 = 1 << 8; 814 | const RAW_RA: u16 = 1 << 7; 815 | const RAW_RCODE_SHIFT: u16 = 0; 816 | const RAW_RCODE_MASK: u16 = 0b1111; 817 | 818 | /// Create a new, empty set of flags. 819 | pub const fn new() -> Self { 820 | Self(0) 821 | } 822 | 823 | /// Use the standard set of flags for a DNS query. 824 | /// 825 | /// This is identical to `new()` but uses recursive querying. 826 | pub const fn standard_query() -> Self { 827 | Self(0x0100) 828 | } 829 | 830 | /// Get the query/response flag. 831 | pub fn qr(&self) -> MessageType { 832 | if self.0 & Self::RAW_QR != 0 { 833 | MessageType::Reply 834 | } else { 835 | MessageType::Query 836 | } 837 | } 838 | 839 | /// Set the message's query/response flag. 840 | pub fn set_qr(&mut self, qr: MessageType) -> &mut Self { 841 | if qr == MessageType::Reply { 842 | self.0 |= Self::RAW_QR; 843 | } else { 844 | self.0 &= !Self::RAW_QR; 845 | } 846 | 847 | self 848 | } 849 | 850 | /// Get the opcode. 851 | pub fn opcode(&self) -> Opcode { 852 | let raw = (self.0 >> Self::RAW_OPCODE_SHIFT) & Self::RAW_OPCODE_MASK; 853 | raw.try_into() 854 | .unwrap_or_else(|_| panic!("invalid opcode: {}", raw)) 855 | } 856 | 857 | /// Set the opcode. 858 | pub fn set_opcode(&mut self, opcode: Opcode) { 859 | self.0 |= (opcode as u16) << Self::RAW_OPCODE_SHIFT; 860 | } 861 | 862 | /// Get whether this message is authoritative. 863 | pub fn authoritative(&self) -> bool { 864 | self.0 & Self::RAW_AA != 0 865 | } 866 | 867 | /// Set whether this message is authoritative. 868 | pub fn set_authoritative(&mut self, authoritative: bool) -> &mut Self { 869 | if authoritative { 870 | self.0 |= Self::RAW_AA; 871 | } else { 872 | self.0 &= !Self::RAW_AA; 873 | } 874 | 875 | self 876 | } 877 | 878 | /// Get whether this message is truncated. 879 | pub fn truncated(&self) -> bool { 880 | self.0 & Self::RAW_TC != 0 881 | } 882 | 883 | /// Set whether this message is truncated. 884 | pub fn set_truncated(&mut self, truncated: bool) -> &mut Self { 885 | if truncated { 886 | self.0 |= Self::RAW_TC; 887 | } else { 888 | self.0 &= !Self::RAW_TC; 889 | } 890 | 891 | self 892 | } 893 | 894 | /// Get whether this message is recursive. 895 | pub fn recursive(&self) -> bool { 896 | self.0 & Self::RAW_RD != 0 897 | } 898 | 899 | /// Set whether this message is recursive. 900 | pub fn set_recursive(&mut self, recursive: bool) -> &mut Self { 901 | if recursive { 902 | self.0 |= Self::RAW_RD; 903 | } else { 904 | self.0 &= !Self::RAW_RD; 905 | } 906 | 907 | self 908 | } 909 | 910 | /// Get whether recursion is available for this message. 911 | pub fn recursion_available(&self) -> bool { 912 | self.0 & Self::RAW_RA != 0 913 | } 914 | 915 | /// Set whether recursion is available for this message. 916 | pub fn set_recursion_available(&mut self, recursion_available: bool) -> &mut Self { 917 | if recursion_available { 918 | self.0 |= Self::RAW_RA; 919 | } else { 920 | self.0 &= !Self::RAW_RA; 921 | } 922 | 923 | self 924 | } 925 | 926 | /// Get the response code. 927 | pub fn response_code(&self) -> ResponseCode { 928 | let raw = (self.0 >> Self::RAW_RCODE_SHIFT) & Self::RAW_RCODE_MASK; 929 | raw.try_into() 930 | .unwrap_or_else(|_| panic!("invalid response code: {}", raw)) 931 | } 932 | 933 | /// Set the response code. 934 | pub fn set_response_code(&mut self, response_code: ResponseCode) -> &mut Self { 935 | self.0 |= (response_code as u16) << Self::RAW_RCODE_SHIFT; 936 | self 937 | } 938 | 939 | /// Get the raw value of these flags. 940 | pub fn raw(self) -> u16 { 941 | self.0 942 | } 943 | } 944 | 945 | impl fmt::Debug for Flags { 946 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 947 | let mut list = f.debug_list(); 948 | 949 | list.entry(&self.qr()); 950 | list.entry(&self.opcode()); 951 | 952 | if self.authoritative() { 953 | list.entry(&"authoritative"); 954 | } 955 | 956 | if self.truncated() { 957 | list.entry(&"truncated"); 958 | } 959 | 960 | if self.recursive() { 961 | list.entry(&"recursive"); 962 | } 963 | 964 | if self.recursion_available() { 965 | list.entry(&"recursion available"); 966 | } 967 | 968 | list.entry(&self.response_code()); 969 | 970 | list.finish() 971 | } 972 | } 973 | 974 | impl<'a> Serialize<'a> for Flags { 975 | fn serialized_len(&self) -> usize { 976 | 2 977 | } 978 | 979 | fn serialize(&self, buf: &mut [u8]) -> Result { 980 | self.0.serialize(buf) 981 | } 982 | 983 | fn deserialize(&mut self, bytes: Cursor<'a>) -> Result, Error> { 984 | u16::deserialize(&mut self.0, bytes) 985 | } 986 | } 987 | 988 | /// Whether a message is a query or a reply. 989 | #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 990 | pub enum MessageType { 991 | /// The message is a query. 992 | Query, 993 | 994 | /// The message is a reply. 995 | Reply, 996 | } 997 | 998 | num_enum! { 999 | /// The operation code for the query. 1000 | pub enum Opcode { 1001 | /// A standard query. 1002 | Query = 0, 1003 | 1004 | /// A reverse query. 1005 | IQuery = 1, 1006 | 1007 | /// A server status request. 1008 | Status = 2, 1009 | 1010 | /// A notification of zone change. 1011 | Notify = 4, 1012 | 1013 | /// A dynamic update. 1014 | Update = 5, 1015 | 1016 | /// DSO query. 1017 | Dso = 6, 1018 | } 1019 | } 1020 | 1021 | num_enum! { 1022 | /// The response code for a query. 1023 | pub enum ResponseCode { 1024 | /// There was no error in the query. 1025 | NoError = 0, 1026 | 1027 | /// The query was malformed. 1028 | FormatError = 1, 1029 | 1030 | /// The server failed to fulfill the query. 1031 | ServerFailure = 2, 1032 | 1033 | /// The name does not exist. 1034 | NameError = 3, 1035 | 1036 | /// The query is not implemented. 1037 | NotImplemented = 4, 1038 | 1039 | /// The query is refused. 1040 | Refused = 5, 1041 | 1042 | /// The name exists, but the query type is not supported. 1043 | YxDomain = 6, 1044 | 1045 | /// The name does not exist, but the query type is supported. 1046 | YxRrSet = 7, 1047 | 1048 | /// The name exists, but the query type is not supported. 1049 | NxRrSet = 8, 1050 | 1051 | /// The server is not authoritative for the zone. 1052 | NotAuth = 9, 1053 | 1054 | /// The name does not exist in the zone. 1055 | NotZone = 10, 1056 | 1057 | /// The DSO-TYPE is not supported. 1058 | DsoTypeNi = 11, 1059 | 1060 | /// Bad OPT version. 1061 | BadVers = 16, 1062 | 1063 | /// Key not recognized. 1064 | BadKey = 17, 1065 | 1066 | /// Signature out of time window. 1067 | BadTime = 18, 1068 | 1069 | /// Bad TKEY mode. 1070 | BadMode = 19, 1071 | 1072 | /// Duplicate key name. 1073 | BadName = 20, 1074 | 1075 | /// Algorithm not supported. 1076 | BadAlg = 21, 1077 | 1078 | /// Bad truncation. 1079 | BadTrunc = 22, 1080 | 1081 | /// Bad/missing server cookie. 1082 | BadCookie = 23, 1083 | } 1084 | } 1085 | 1086 | num_enum! { 1087 | /// The resource types that a question can ask for. 1088 | pub enum ResourceType { 1089 | /// Get the host's IPv4 address. 1090 | A = 1, 1091 | 1092 | /// Get the authoritative name servers for a domain. 1093 | NS = 2, 1094 | 1095 | /// Get the mail server for a domain. 1096 | MD = 3, 1097 | 1098 | /// Get the mail forwarder for a domain. 1099 | MF = 4, 1100 | 1101 | /// Get the canonical name for a domain. 1102 | CName = 5, 1103 | 1104 | /// Get the start of authority record for a domain. 1105 | Soa = 6, 1106 | 1107 | /// Get the mailbox for a domain. 1108 | MB = 7, 1109 | 1110 | /// Get the mail group member for a domain. 1111 | MG = 8, 1112 | 1113 | /// Get the mail rename domain for a domain. 1114 | MR = 9, 1115 | 1116 | /// Get the null record for a domain. 1117 | Null = 10, 1118 | 1119 | /// Get the well known services for a domain. 1120 | Wks = 11, 1121 | 1122 | /// Get the domain pointer for a domain. 1123 | Ptr = 12, 1124 | 1125 | /// Get the host information for a domain. 1126 | HInfo = 13, 1127 | 1128 | /// Get the mailbox or mail list information for a domain. 1129 | MInfo = 14, 1130 | 1131 | /// Get the mail exchange for a domain. 1132 | MX = 15, 1133 | 1134 | /// Get the text for a domain. 1135 | Txt = 16, 1136 | 1137 | /// Get the responsible person for a domain. 1138 | RP = 17, 1139 | 1140 | /// Get the AFS database location for a domain. 1141 | AfsDb = 18, 1142 | 1143 | /// Get the X.25 address for a domain. 1144 | X25 = 19, 1145 | 1146 | /// Get the ISDN address for a domain. 1147 | Isdn = 20, 1148 | 1149 | /// Get the router for a domain. 1150 | Rt = 21, 1151 | 1152 | /// Get the NSAP address for a domain. 1153 | NSap = 22, 1154 | 1155 | /// Get the reverse NSAP address for a domain. 1156 | NSapPtr = 23, 1157 | 1158 | /// Get the security signature for a domain. 1159 | Sig = 24, 1160 | 1161 | /// Get the key for a domain. 1162 | Key = 25, 1163 | 1164 | /// Get the X.400 mail mapping for a domain. 1165 | Px = 26, 1166 | 1167 | /// Get the geographical location for a domain. 1168 | GPos = 27, 1169 | 1170 | /// Get the IPv6 address for a domain. 1171 | AAAA = 28, 1172 | 1173 | /// Get the location for a domain. 1174 | Loc = 29, 1175 | 1176 | /// Get the next domain name in a zone. 1177 | Nxt = 30, 1178 | 1179 | /// Get the endpoint identifier for a domain. 1180 | EId = 31, 1181 | 1182 | /// Get the Nimrod locator for a domain. 1183 | NimLoc = 32, 1184 | 1185 | /// Get the server selection for a domain. 1186 | Srv = 33, 1187 | 1188 | /// Get the ATM address for a domain. 1189 | AtmA = 34, 1190 | 1191 | /// Get the naming authority pointer for a domain. 1192 | NAPtr = 35, 1193 | 1194 | /// Get the key exchange for a domain. 1195 | Kx = 36, 1196 | 1197 | /// Get the certificate for a domain. 1198 | Cert = 37, 1199 | 1200 | /// Get the IPv6 address for a domain. 1201 | /// 1202 | /// This is obsolete; use `AAAA` instead. 1203 | A6 = 38, 1204 | 1205 | /// Get the DNAME for a domain. 1206 | DName = 39, 1207 | 1208 | /// Get the sink for a domain. 1209 | Sink = 40, 1210 | 1211 | /// Get the OPT for a domain. 1212 | Opt = 41, 1213 | 1214 | /// Get the address prefix list for a domain. 1215 | ApL = 42, 1216 | 1217 | /// Get the delegation signer for a domain. 1218 | DS = 43, 1219 | 1220 | /// Get the SSH key fingerprint for a domain. 1221 | SshFp = 44, 1222 | 1223 | /// Get the IPSEC key for a domain. 1224 | IpSecKey = 45, 1225 | 1226 | /// Get the resource record signature for a domain. 1227 | RRSig = 46, 1228 | 1229 | /// Get the next secure record for a domain. 1230 | NSEC = 47, 1231 | 1232 | /// Get the DNSKEY for a domain. 1233 | DNSKey = 48, 1234 | 1235 | /// Get the DHCID for a domain. 1236 | DHCID = 49, 1237 | 1238 | /// Get the NSEC3 for a domain. 1239 | NSEC3 = 50, 1240 | 1241 | /// Get the NSEC3 parameters for a domain. 1242 | NSEC3Param = 51, 1243 | 1244 | /// Get the TLSA for a domain. 1245 | TLSA = 52, 1246 | 1247 | /// Get the S/MIME certificate association for a domain. 1248 | SMimeA = 53, 1249 | 1250 | /// Get the host information for a domain. 1251 | HIP = 55, 1252 | 1253 | /// Get the NINFO for a domain. 1254 | NInfo = 56, 1255 | 1256 | /// Get the RKEY for a domain. 1257 | RKey = 57, 1258 | 1259 | /// Get the trust anchor link for a domain. 1260 | TALink = 58, 1261 | 1262 | /// Get the child DS for a domain. 1263 | CDS = 59, 1264 | 1265 | /// Get the DNSKEY for a domain. 1266 | CDNSKey = 60, 1267 | 1268 | /// Get the OpenPGP key for a domain. 1269 | OpenPGPKey = 61, 1270 | 1271 | /// Get the Child-to-Parent Synchronization for a domain. 1272 | CSync = 62, 1273 | 1274 | /// Get the Zone Data Message for a domain. 1275 | ZoneMD = 63, 1276 | 1277 | /// Get the General Purpose Service Binding for a domain. 1278 | Svcb = 64, 1279 | 1280 | /// Get the HTTP Service Binding for a domain. 1281 | Https = 65, 1282 | 1283 | /// Get the Sender Policy Framework for a domain. 1284 | Spf = 99, 1285 | 1286 | /// Get the UINFO for a domain. 1287 | UInfo = 100, 1288 | 1289 | /// Get the UID for a domain. 1290 | UID = 101, 1291 | 1292 | /// Get the GID for a domain. 1293 | GID = 102, 1294 | 1295 | /// Get the UNSPEC for a domain. 1296 | Unspec = 103, 1297 | 1298 | /// Get the NID for a domain. 1299 | NID = 104, 1300 | 1301 | /// Get the L32 for a domain. 1302 | L32 = 105, 1303 | 1304 | /// Get the L64 for a domain. 1305 | L64 = 106, 1306 | 1307 | /// Get the LP for a domain. 1308 | LP = 107, 1309 | 1310 | /// Get the EUI48 for a domain. 1311 | EUI48 = 108, 1312 | 1313 | /// Get the EUI64 for a domain. 1314 | EUI64 = 109, 1315 | 1316 | /// Get the transaction key for a domain. 1317 | TKey = 249, 1318 | 1319 | /// Get the transaction signature for a domain. 1320 | TSig = 250, 1321 | 1322 | /// Get the incremental transfer for a domain. 1323 | Ixfr = 251, 1324 | 1325 | /// Get the transfer of an entire zone for a domain. 1326 | Axfr = 252, 1327 | 1328 | /// Get the mailbox-related records for a domain. 1329 | MailB = 253, 1330 | 1331 | /// Get the mail agent RRs for a domain. 1332 | /// 1333 | /// This is obsolete; use `MX` instead. 1334 | MailA = 254, 1335 | 1336 | /// Get the wildcard match for a domain. 1337 | Wildcard = 255, 1338 | 1339 | /// Get the URI for a domain. 1340 | Uri = 256, 1341 | 1342 | /// Get the certification authority authorization for a domain. 1343 | Caa = 257, 1344 | 1345 | /// Get the application visibility and control for a domain. 1346 | Avc = 258, 1347 | 1348 | /// Get the digital object architecture for a domain. 1349 | Doa = 259, 1350 | 1351 | /// Get the automatic network discovery for a domain. 1352 | Amtrelay = 260, 1353 | 1354 | /// Get the DNSSEC trust authorities for a domain. 1355 | TA = 32768, 1356 | 1357 | /// Get the DNSSEC lookaside validation for a domain. 1358 | DLV = 32769, 1359 | } 1360 | } 1361 | 1362 | impl Default for ResourceType { 1363 | fn default() -> Self { 1364 | Self::A 1365 | } 1366 | } 1367 | 1368 | /// The given value is not a valid code. 1369 | #[derive(Debug, Clone)] 1370 | pub struct InvalidCode(u16); 1371 | 1372 | impl InvalidCode { 1373 | /// Get the invalid code. 1374 | pub fn code(&self) -> u16 { 1375 | self.0 1376 | } 1377 | } 1378 | 1379 | impl fmt::Display for InvalidCode { 1380 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 1381 | write!(f, "invalid code: {}", self.0) 1382 | } 1383 | } 1384 | 1385 | #[cfg(feature = "std")] 1386 | impl StdError for InvalidCode {} 1387 | 1388 | #[cfg(test)] 1389 | mod tests { 1390 | use super::*; 1391 | 1392 | #[test] 1393 | fn resource_data_serialization() { 1394 | let mut buf = [0u8; 7]; 1395 | let record = ResourceData(&[0x1f, 0xfe, 0x02, 0x24, 0x75]); 1396 | let len = record 1397 | .serialize(&mut buf) 1398 | .expect("serialized into provided buffer"); 1399 | assert_eq!(len, 7); 1400 | assert_eq!(buf, [0x00, 0x05, 0x1f, 0xfe, 0x02, 0x24, 0x75]); 1401 | } 1402 | } 1403 | -------------------------------------------------------------------------------- /dns-protocol/src/ser.rs: -------------------------------------------------------------------------------- 1 | //! A serializer/deserializer compatible with the DNS wire format. 2 | //! 3 | //! Various notes about the wire format: 4 | //! 5 | //! - All integers are in big endian format. 6 | //! - All strings are length-prefixed. 7 | //! - Names consist of a series of labels, each prefixed with a length byte. 8 | //! - Names end with a zero byte. 9 | //! - Names are compressed by using a pointer to a previous name. 10 | //! - This means that we need access to the entire buffer. 11 | 12 | use super::Error; 13 | 14 | use core::convert::TryInto; 15 | use core::fmt; 16 | use core::hash; 17 | use core::iter; 18 | use core::mem; 19 | use core::num::NonZeroUsize; 20 | 21 | use memchr::Memchr; 22 | 23 | /// An object that is able to be serialized to or deserialized from a series of bytes. 24 | pub(crate) trait Serialize<'a> { 25 | /// The number of bytes needed to serialize this object. 26 | fn serialized_len(&self) -> usize; 27 | 28 | /// Serialize this object into a series of bytes. 29 | fn serialize(&self, bytes: &mut [u8]) -> Result; 30 | 31 | /// Deserialize this object from a series of bytes. 32 | fn deserialize(&mut self, cursor: Cursor<'a>) -> Result, Error>; 33 | } 34 | 35 | /// A cursor into a series of bytes. 36 | /// 37 | /// This keeps track of how many bytes that we have already consumed, while also providing the original buffer. 38 | #[derive(Debug, Copy, Clone)] 39 | pub(crate) struct Cursor<'a> { 40 | /// The bytes being read. 41 | bytes: &'a [u8], 42 | 43 | /// The index into the bytes that we've read so far. 44 | cursor: usize, 45 | } 46 | 47 | impl<'a> Cursor<'a> { 48 | /// Create a new cursor from a series of bytes. 49 | pub(crate) fn new(bytes: &'a [u8]) -> Self { 50 | Self { bytes, cursor: 0 } 51 | } 52 | 53 | /// Get the original bytes that this cursor was created from. 54 | pub(crate) fn original(&self) -> &'a [u8] { 55 | self.bytes 56 | } 57 | 58 | /// Get the slice of remaining bytes. 59 | pub(crate) fn remaining(&self) -> &'a [u8] { 60 | &self.bytes[self.cursor..] 61 | } 62 | 63 | /// Get the length of the slice of remaining bytes. 64 | pub(crate) fn len(&self) -> usize { 65 | self.bytes.len() - self.cursor 66 | } 67 | 68 | /// Get a new cursor at the given absolute position. 69 | pub(crate) fn at(&self, pos: usize) -> Self { 70 | Self { 71 | bytes: self.bytes, 72 | cursor: pos, 73 | } 74 | } 75 | 76 | /// Advance the cursor by the given number of bytes. 77 | pub(crate) fn advance(mut self, n: usize) -> Result { 78 | if n == 0 { 79 | return Ok(self); 80 | } 81 | 82 | if self.cursor + n > self.bytes.len() { 83 | return Err(Error::NotEnoughReadBytes { 84 | tried_to_read: NonZeroUsize::new(self.cursor.saturating_add(n)).unwrap(), 85 | available: self.bytes.len(), 86 | }); 87 | } 88 | 89 | self.cursor += n; 90 | Ok(self) 91 | } 92 | 93 | /// Error for when a read of `n` bytes failed. 94 | fn read_error(&self, n: usize) -> Error { 95 | Error::NotEnoughReadBytes { 96 | tried_to_read: NonZeroUsize::new(self.cursor.saturating_add(n)).unwrap(), 97 | available: self.bytes.len(), 98 | } 99 | } 100 | } 101 | 102 | impl<'a> Serialize<'a> for () { 103 | fn serialized_len(&self) -> usize { 104 | 0 105 | } 106 | 107 | fn serialize(&self, _bytes: &mut [u8]) -> Result { 108 | Ok(0) 109 | } 110 | 111 | fn deserialize(&mut self, bytes: Cursor<'a>) -> Result, Error> { 112 | Ok(bytes) 113 | } 114 | } 115 | 116 | /// A DNS name. 117 | #[derive(Clone, Copy)] 118 | pub struct Label<'a> { 119 | repr: Repr<'a>, 120 | } 121 | 122 | /// Internal representation of a DNS name. 123 | #[derive(Clone, Copy)] 124 | enum Repr<'a> { 125 | /// The name is represented by its range of bytes in the initial buffer. 126 | /// 127 | /// It is lazily parsed into labels when needed. 128 | Bytes { 129 | /// The original buffer, in totality, that this name was parsed from. 130 | original: &'a [u8], 131 | 132 | /// The starting position of this name in the original buffer. 133 | start: usize, 134 | 135 | /// The ending position of this name in the original buffer. 136 | end: usize, 137 | }, 138 | 139 | /// The label will be the parsed version of this string. 140 | String { 141 | /// The string representation of the label. 142 | string: &'a str, 143 | }, 144 | } 145 | 146 | impl Default for Label<'_> { 147 | fn default() -> Self { 148 | // An empty label. 149 | Self { 150 | repr: Repr::Bytes { 151 | original: &[0], 152 | start: 0, 153 | end: 1, 154 | }, 155 | } 156 | } 157 | } 158 | 159 | impl<'a, 'b> PartialEq> for Label<'b> { 160 | fn eq(&self, other: &Label<'a>) -> bool { 161 | self.segments().eq(other.segments()) 162 | } 163 | } 164 | 165 | impl Eq for Label<'_> {} 166 | 167 | impl<'a, 'b> PartialOrd> for Label<'b> { 168 | fn partial_cmp(&self, other: &Label<'a>) -> Option { 169 | self.segments().partial_cmp(other.segments()) 170 | } 171 | } 172 | 173 | impl Ord for Label<'_> { 174 | fn cmp(&self, other: &Self) -> core::cmp::Ordering { 175 | self.segments().cmp(other.segments()) 176 | } 177 | } 178 | 179 | impl hash::Hash for Label<'_> { 180 | fn hash(&self, state: &mut H) { 181 | for segment in self.segments() { 182 | segment.hash(state); 183 | } 184 | } 185 | } 186 | 187 | impl<'a> Label<'a> { 188 | /// Get an iterator over the label segments in this name. 189 | pub fn segments(&self) -> impl Iterator> { 190 | match self.repr { 191 | Repr::Bytes { 192 | original, start, .. 193 | } => Either::A(parse_bytes(original, start)), 194 | Repr::String { string } => Either::B(parse_string(string)), 195 | } 196 | } 197 | 198 | /// Get an iterator over the strings making up this name. 199 | pub fn names(&self) -> impl Iterator> { 200 | match self.repr { 201 | Repr::String { string } => { 202 | // Guaranteed to have no pointers. 203 | Either::A(parse_string(string).filter_map(|seg| seg.as_str().map(Ok))) 204 | } 205 | Repr::Bytes { 206 | original, start, .. 207 | } => { 208 | // We may have to deal with pointers. Parse it manually. 209 | let mut cursor = Cursor { 210 | bytes: original, 211 | cursor: start, 212 | }; 213 | 214 | Either::B(iter::from_fn(move || { 215 | loop { 216 | let mut ls: LabelSegment<'_> = LabelSegment::Empty; 217 | cursor = ls.deserialize(cursor).ok()?; 218 | 219 | // TODO: Handle the case where utf-8 errors out. 220 | match ls { 221 | LabelSegment::Empty => return None, 222 | LabelSegment::Pointer(pos) => { 223 | // Change to another location. 224 | cursor = cursor.at(pos.into()); 225 | } 226 | LabelSegment::String(label) => return Some(Ok(label)), 227 | } 228 | } 229 | })) 230 | } 231 | } 232 | } 233 | } 234 | 235 | impl<'a> From<&'a str> for Label<'a> { 236 | fn from(string: &'a str) -> Self { 237 | Self { 238 | repr: Repr::String { string }, 239 | } 240 | } 241 | } 242 | 243 | impl fmt::Debug for Label<'_> { 244 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 245 | struct LabelFmt<'a>(&'a Label<'a>); 246 | 247 | impl fmt::Debug for LabelFmt<'_> { 248 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 249 | fmt::Display::fmt(self.0, f) 250 | } 251 | } 252 | 253 | f.debug_tuple("Label").field(&LabelFmt(self)).finish() 254 | } 255 | } 256 | 257 | impl fmt::Display for Label<'_> { 258 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 259 | self.names().enumerate().try_for_each(|(i, name)| { 260 | if i > 0 { 261 | f.write_str(".")?; 262 | } 263 | 264 | match name { 265 | Ok(name) => f.write_str(name), 266 | Err(_) => f.write_str("???"), 267 | } 268 | }) 269 | } 270 | } 271 | 272 | impl<'a> Serialize<'a> for Label<'a> { 273 | fn serialized_len(&self) -> usize { 274 | if let Repr::Bytes { start, end, .. } = self.repr { 275 | return end - start; 276 | } 277 | 278 | self.segments() 279 | .map(|item| item.serialized_len()) 280 | .fold(0, |a, b| a.saturating_add(b)) 281 | } 282 | 283 | fn serialize(&self, bytes: &mut [u8]) -> Result { 284 | // Fast path: just copy our bytes. 285 | if let Repr::Bytes { 286 | original, 287 | start, 288 | end, 289 | } = self.repr 290 | { 291 | bytes[..end - start].copy_from_slice(&original[start..end]); 292 | return Ok(end - start); 293 | } 294 | 295 | self.segments().try_fold(0, |mut offset, item| { 296 | let len = item.serialize(&mut bytes[offset..])?; 297 | offset += len; 298 | Ok(offset) 299 | }) 300 | } 301 | 302 | fn deserialize(&mut self, cursor: Cursor<'a>) -> Result, Error> { 303 | let original = cursor.original(); 304 | let start = cursor.cursor; 305 | 306 | // Figure out where the end is. 307 | let mut end = start; 308 | loop { 309 | let len_char = match original.get(end) { 310 | Some(0) => { 311 | end += 1; 312 | break; 313 | } 314 | Some(ptr) if ptr & PTR_MASK != 0 => { 315 | // Pointer goes for one more byte and then ends. 316 | end += 2; 317 | break; 318 | } 319 | Some(len_char) => *len_char, 320 | None => { 321 | return Err(Error::NotEnoughReadBytes { 322 | tried_to_read: NonZeroUsize::new(cursor.cursor + end).unwrap(), 323 | available: original.len(), 324 | }) 325 | } 326 | }; 327 | 328 | let len = len_char as usize; 329 | end += len + 1; 330 | } 331 | 332 | self.repr = Repr::Bytes { 333 | original, 334 | start, 335 | end, 336 | }; 337 | cursor.advance(end - start) 338 | } 339 | } 340 | 341 | /// Parse a set of bytes as a DNS name. 342 | fn parse_bytes(bytes: &[u8], position: usize) -> impl Iterator> + '_ { 343 | let mut cursor = Cursor { 344 | bytes, 345 | cursor: position, 346 | }; 347 | let mut keep_going = true; 348 | 349 | iter::from_fn(move || { 350 | if !keep_going { 351 | return None; 352 | } 353 | 354 | let mut segment = LabelSegment::Empty; 355 | cursor = segment.deserialize(cursor).ok()?; 356 | 357 | match segment { 358 | LabelSegment::String(_) => {} 359 | _ => { 360 | keep_going = false; 361 | } 362 | } 363 | 364 | Some(segment) 365 | }) 366 | } 367 | 368 | /// Parse a string as a DNS name. 369 | fn parse_string(str: &str) -> impl Iterator> + '_ { 370 | let dot = Memchr::new(b'.', str.as_bytes()); 371 | let mut last_index = 0; 372 | 373 | // Add one extra entry to `dot` that's at the end of the string. 374 | let dot = dot.chain(Some(str.len())); 375 | 376 | dot.filter_map(move |index| { 377 | let item = &str[last_index..index]; 378 | last_index = index.saturating_add(1); 379 | 380 | if item.is_empty() { 381 | None 382 | } else { 383 | Some(LabelSegment::String(item)) 384 | } 385 | }) 386 | .chain(Some(LabelSegment::Empty)) 387 | } 388 | 389 | /// A DNS-compatible label segment. 390 | #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 391 | pub enum LabelSegment<'a> { 392 | /// The empty terminator. 393 | Empty, 394 | 395 | /// A string label. 396 | String(&'a str), 397 | 398 | /// A pointer to a previous name. 399 | Pointer(u16), 400 | } 401 | 402 | const MAX_STR_LEN: usize = !PTR_MASK as usize; 403 | const PTR_MASK: u8 = 0b1100_0000; 404 | 405 | impl<'a> LabelSegment<'a> { 406 | fn as_str(&self) -> Option<&'a str> { 407 | match self { 408 | Self::String(s) => Some(s), 409 | _ => None, 410 | } 411 | } 412 | } 413 | 414 | impl Default for LabelSegment<'_> { 415 | fn default() -> Self { 416 | Self::Empty 417 | } 418 | } 419 | 420 | impl<'a> Serialize<'a> for LabelSegment<'a> { 421 | fn serialized_len(&self) -> usize { 422 | match self { 423 | Self::Empty => 1, 424 | Self::Pointer(_) => 2, 425 | Self::String(s) => 1 + s.len(), 426 | } 427 | } 428 | 429 | fn serialize(&self, bytes: &mut [u8]) -> Result { 430 | match self { 431 | Self::Empty => { 432 | // An empty label segment is just a zero byte. 433 | bytes[0] = 0; 434 | Ok(1) 435 | } 436 | Self::Pointer(ptr) => { 437 | // Apply the pointer mask to the first byte. 438 | let [mut b1, b2] = ptr.to_be_bytes(); 439 | b1 |= PTR_MASK; 440 | bytes[0] = b1; 441 | bytes[1] = b2; 442 | Ok(2) 443 | } 444 | Self::String(s) => { 445 | // First, serialize the length byte. 446 | let len = s.len(); 447 | 448 | if len > MAX_STR_LEN { 449 | return Err(Error::NameTooLong(len)); 450 | } 451 | 452 | if len > bytes.len() { 453 | panic!("not enough bytes to serialize string"); 454 | } 455 | 456 | bytes[0] = len as u8; 457 | bytes[1..=len].copy_from_slice(s.as_bytes()); 458 | Ok(len + 1) 459 | } 460 | } 461 | } 462 | 463 | fn deserialize(&mut self, cursor: Cursor<'a>) -> Result, Error> { 464 | // The type is determined by the first byte. 465 | let b1 = *cursor 466 | .remaining() 467 | .first() 468 | .ok_or_else(|| cursor.read_error(1))?; 469 | 470 | if b1 == 0 { 471 | // An empty label segment is just a zero byte. 472 | *self = Self::Empty; 473 | cursor.advance(1) 474 | } else if b1 & PTR_MASK == PTR_MASK { 475 | // A pointer is a 2-byte value with the pointer mask applied to the first byte. 476 | let [b1, b2]: [u8; 2] = cursor.remaining()[..2] 477 | .try_into() 478 | .map_err(|_| cursor.read_error(2))?; 479 | let ptr = u16::from_be_bytes([b1 & !PTR_MASK, b2]); 480 | *self = Self::Pointer(ptr); 481 | cursor.advance(2) 482 | } else { 483 | // A string label is a length byte followed by the string. 484 | let len = b1 as usize; 485 | 486 | if len > MAX_STR_LEN { 487 | return Err(Error::NameTooLong(len)); 488 | } 489 | 490 | // Parse the string's bytes. 491 | let bytes = cursor.remaining()[1..=len] 492 | .try_into() 493 | .map_err(|_| cursor.read_error(len + 1))?; 494 | 495 | // Parse as UTF8 496 | let s = core::str::from_utf8(bytes)?; 497 | *self = Self::String(s); 498 | cursor.advance(len + 1) 499 | } 500 | } 501 | } 502 | 503 | macro_rules! serialize_num { 504 | ($($num_ty: ident),*) => { 505 | $( 506 | impl<'a> Serialize<'a> for $num_ty { 507 | fn serialized_len(&self) -> usize { 508 | mem::size_of::<$num_ty>() 509 | } 510 | 511 | fn serialize(&self, bytes: &mut [u8]) -> Result { 512 | if bytes.len() < mem::size_of::<$num_ty>() { 513 | panic!("Not enough space to serialize a {}", stringify!($num_ty)); 514 | } 515 | 516 | let value = (*self).to_be_bytes(); 517 | bytes[..mem::size_of::<$num_ty>()].copy_from_slice(&value); 518 | 519 | Ok(mem::size_of::<$num_ty>()) 520 | } 521 | 522 | fn deserialize(&mut self, bytes: Cursor<'a>) -> Result, Error> { 523 | if bytes.len() < mem::size_of::<$num_ty>() { 524 | return Err(bytes.read_error(mem::size_of::<$num_ty>())); 525 | } 526 | 527 | let mut value = [0; mem::size_of::<$num_ty>()]; 528 | value.copy_from_slice(&bytes.remaining()[..mem::size_of::<$num_ty>()]); 529 | *self = $num_ty::from_be_bytes(value); 530 | 531 | bytes.advance(mem::size_of::<$num_ty>()) 532 | } 533 | } 534 | )* 535 | } 536 | } 537 | 538 | serialize_num! { 539 | u8, u16, u32, u64, 540 | i8, i16, i32, i64 541 | } 542 | 543 | /// One iterator or another. 544 | enum Either { 545 | A(A), 546 | B(B), 547 | } 548 | 549 | impl> Iterator for Either { 550 | type Item = A::Item; 551 | 552 | fn next(&mut self) -> Option { 553 | match self { 554 | Either::A(a) => a.next(), 555 | Either::B(b) => b.next(), 556 | } 557 | } 558 | 559 | fn size_hint(&self) -> (usize, Option) { 560 | match self { 561 | Either::A(a) => a.size_hint(), 562 | Either::B(b) => b.size_hint(), 563 | } 564 | } 565 | 566 | fn fold(self, init: B, f: F) -> B 567 | where 568 | Self: Sized, 569 | F: FnMut(B, Self::Item) -> B, 570 | { 571 | match self { 572 | Either::A(a) => a.fold(init, f), 573 | Either::B(b) => b.fold(init, f), 574 | } 575 | } 576 | } 577 | --------------------------------------------------------------------------------