Full Code of ogham/dog for AI

master 721440b12ef0 cached
208 files
410.9 KB
121.5k tokens
500 symbols
1 requests
Download .txt
Showing preview only (457K chars total). Download the full file or copy to clipboard to get everything.
Repository: ogham/dog
Branch: master
Commit: 721440b12ef0
Files: 208
Total size: 410.9 KB

Directory structure:
gitextract_m0bcnqsg/

├── .github/
│   ├── FUNDING.yml
│   └── ISSUE_TEMPLATE/
│       ├── bug_report.md
│       ├── compilation_error.md
│       ├── config.yml
│       ├── feature_request.md
│       └── question.md
├── .gitignore
├── .rustfmt.toml
├── .travis.yml
├── Cargo.toml
├── Dockerfile
├── Justfile
├── LICENCE
├── README.md
├── build.rs
├── completions/
│   ├── dog.bash
│   ├── dog.fish
│   ├── dog.ps1
│   └── dog.zsh
├── dns/
│   ├── Cargo.toml
│   ├── fuzz/
│   │   ├── .gitignore
│   │   ├── Cargo.toml
│   │   └── fuzz_targets/
│   │       └── fuzz_parsing.rs
│   ├── src/
│   │   ├── lib.rs
│   │   ├── record/
│   │   │   ├── a.rs
│   │   │   ├── aaaa.rs
│   │   │   ├── caa.rs
│   │   │   ├── cname.rs
│   │   │   ├── eui48.rs
│   │   │   ├── eui64.rs
│   │   │   ├── hinfo.rs
│   │   │   ├── loc.rs
│   │   │   ├── mod.rs
│   │   │   ├── mx.rs
│   │   │   ├── naptr.rs
│   │   │   ├── ns.rs
│   │   │   ├── openpgpkey.rs
│   │   │   ├── opt.rs
│   │   │   ├── others.rs
│   │   │   ├── ptr.rs
│   │   │   ├── soa.rs
│   │   │   ├── srv.rs
│   │   │   ├── sshfp.rs
│   │   │   ├── tlsa.rs
│   │   │   ├── txt.rs
│   │   │   └── uri.rs
│   │   ├── strings.rs
│   │   ├── types.rs
│   │   └── wire.rs
│   └── tests/
│       ├── wire_building_tests.rs
│       └── wire_parsing_tests.rs
├── dns-transport/
│   ├── Cargo.toml
│   └── src/
│       ├── auto.rs
│       ├── error.rs
│       ├── https.rs
│       ├── lib.rs
│       ├── tcp.rs
│       ├── tls.rs
│       ├── tls_stream.rs
│       └── udp.rs
├── man/
│   └── dog.1.md
├── src/
│   ├── colours.rs
│   ├── connect.rs
│   ├── hints.rs
│   ├── logger.rs
│   ├── main.rs
│   ├── options.rs
│   ├── output.rs
│   ├── requests.rs
│   ├── resolve.rs
│   ├── table.rs
│   ├── txid.rs
│   └── usage.txt
└── xtests/
    ├── README.md
    ├── features/
    │   ├── none.toml
    │   └── outputs/
    │       ├── disabled_https.txt
    │       └── disabled_tls.txt
    ├── live/
    │   ├── badssl.toml
    │   ├── basics.toml
    │   ├── bins.toml
    │   ├── https.toml
    │   ├── json.toml
    │   ├── tcp.toml
    │   ├── tls.toml
    │   └── udp.toml
    ├── madns/
    │   ├── a-records.toml
    │   ├── aaaa-records.toml
    │   ├── caa-records.toml
    │   ├── cname-records.toml
    │   ├── eui48-records.toml
    │   ├── eui64-records.toml
    │   ├── hinfo-records.toml
    │   ├── loc-records.toml
    │   ├── mx-records.toml
    │   ├── naptr-records.toml
    │   ├── ns-records.toml
    │   ├── openpgpkey-records.toml
    │   ├── opt-records.toml
    │   ├── outputs/
    │   │   ├── a.example.ansitxt
    │   │   ├── a.example.json
    │   │   ├── aaaa.example.ansitxt
    │   │   ├── aaaa.example.json
    │   │   ├── ansi.str.example.ansitxt
    │   │   ├── ansi.str.example.json
    │   │   ├── bad-regex.naptr.example.ansitxt
    │   │   ├── bad-utf8.caa.example.ansitxt
    │   │   ├── bad-utf8.caa.example.json
    │   │   ├── bad-utf8.hinfo.example.ansitxt
    │   │   ├── bad-utf8.hinfo.example.json
    │   │   ├── bad-utf8.naptr.invalid.ansitxt
    │   │   ├── bad-utf8.naptr.invalid.json
    │   │   ├── bad-utf8.txt.example.ansitxt
    │   │   ├── bad-utf8.txt.example.json
    │   │   ├── bad-utf8.uri.example.ansitxt
    │   │   ├── bad-utf8.uri.example.json
    │   │   ├── caa.example.ansitxt
    │   │   ├── caa.example.json
    │   │   ├── cname.example.ansitxt
    │   │   ├── cname.example.json
    │   │   ├── critical.caa.example.ansitxt
    │   │   ├── critical.caa.example.json
    │   │   ├── do-flag.opt.example.ansitxt
    │   │   ├── do-flag.opt.example.json
    │   │   ├── eui48.example.ansitxt
    │   │   ├── eui48.example.json
    │   │   ├── eui64.example.ansitxt
    │   │   ├── eui64.example.json
    │   │   ├── far-negative-latitude.loc.invalid.ansitxt
    │   │   ├── far-negative-latitude.loc.invalid.json
    │   │   ├── far-negative-longitude.loc.invalid.ansitxt
    │   │   ├── far-negative-longitude.loc.invalid.json
    │   │   ├── far-positive-latitude.loc.invalid.ansitxt
    │   │   ├── far-positive-latitude.loc.invalid.json
    │   │   ├── far-positive-longitude.loc.invalid.ansitxt
    │   │   ├── far-positive-longitude.loc.invalid.json
    │   │   ├── hinfo.example.ansitxt
    │   │   ├── hinfo.example.json
    │   │   ├── loc.example.ansitxt
    │   │   ├── loc.example.json
    │   │   ├── mx.example.ansitxt
    │   │   ├── mx.example.json
    │   │   ├── named.opt.invalid.ansitxt
    │   │   ├── named.opt.invalid.json
    │   │   ├── naptr.example.ansitxt
    │   │   ├── naptr.example.json
    │   │   ├── newline.str.example.ansitxt
    │   │   ├── newline.str.example.json
    │   │   ├── ns.example.ansitxt
    │   │   ├── ns.example.json
    │   │   ├── null.str.example.ansitxt
    │   │   ├── null.str.example.json
    │   │   ├── openpgpkey.example.ansitxt
    │   │   ├── openpgpkey.example.json
    │   │   ├── opt.example.ansitxt
    │   │   ├── opt.example.json
    │   │   ├── other-flags.opt.example.ansitxt
    │   │   ├── other-flags.opt.example.json
    │   │   ├── others.caa.example.ansitxt
    │   │   ├── others.caa.example.json
    │   │   ├── ptr.example.ansitxt
    │   │   ├── ptr.example.json
    │   │   ├── slash.uri.example.ansitxt
    │   │   ├── soa.example.ansitxt
    │   │   ├── soa.example.json
    │   │   ├── srv.example.ansitxt
    │   │   ├── srv.example.json
    │   │   ├── sshfp.example.ansitxt
    │   │   ├── sshfp.example.json
    │   │   ├── tab.str.example.ansitxt
    │   │   ├── tab.str.example.json
    │   │   ├── tlsa.example.ansitxt
    │   │   ├── tlsa.example.json
    │   │   ├── txt.example.ansitxt
    │   │   ├── txt.example.json
    │   │   ├── upperbit.str.example.ansitxt
    │   │   ├── upperbit.str.example.json
    │   │   ├── uri.example.ansitxt
    │   │   ├── uri.example.json
    │   │   ├── utf8.caa.example.ansitxt
    │   │   ├── utf8.caa.example.json
    │   │   ├── utf8.hinfo.example.ansitxt
    │   │   ├── utf8.hinfo.example.json
    │   │   ├── utf8.naptr.invalid.ansitxt
    │   │   ├── utf8.naptr.invalid.json
    │   │   ├── utf8.txt.example.ansitxt
    │   │   ├── utf8.txt.example.json
    │   │   ├── utf8.uri.example.ansitxt
    │   │   └── utf8.uri.example.json
    │   ├── protocol-chars.toml
    │   ├── protocol-compression.toml
    │   ├── protocol-error-codes.toml
    │   ├── ptr-records.toml
    │   ├── soa-records.toml
    │   ├── srv-records.toml
    │   ├── sshfp-records.toml
    │   ├── tlsa-records.toml
    │   ├── txt-records.toml
    │   └── uri-records.toml
    └── options/
        ├── errors.toml
        ├── help.toml
        └── outputs/
            ├── huge-domain.txt
            ├── invalid-argument.txt
            ├── invalid-protocol-tweak.txt
            ├── invalid-query-class.txt
            ├── invalid-query-type.txt
            ├── missing-nameserver.txt
            ├── missing-parameter.txt
            └── opt-query.txt

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/FUNDING.yml
================================================
github: ogham


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Report a crash, runtime error, or invalid output in dog
---

If dog does something unexpected, or displays an error on the screen, or if it outright crashes, then please include the following information in your report:

- The version of dog being used (`dog --version`)
- The command-line arguments you are using
- Your operating system and hardware platform

If it’s a crash, please include the full text of the crash that gets printed to the screen. If you’re seeing unexpected behaviour, a screenshot of the issue will help a lot.

---


================================================
FILE: .github/ISSUE_TEMPLATE/compilation_error.md
================================================
---
name: Compilation error
about: Report a problem compiling dog
---

If dog fails to compile, or if there is a problem during the build process, then please include the following information in your report:

- The exact dog commit you are building (`git rev-parse --short HEAD`)
- The version of rustc you are compiling it with (`rustc --version`)
- Your operating system and hardware platform
- The Rust build target (the _exact_ output of `rustc --print cfg`)

If you are seeing compilation errors, please include the output of the build process.

---


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: true


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Request a feature or enhancement to dog
---


================================================
FILE: .github/ISSUE_TEMPLATE/question.md
================================================
---
name: Question
about: Ask a question about dog
---


================================================
FILE: .gitignore
================================================
/target
/tarpaulin-report.html
fuzz-*.log
/cargo-timing*.html


================================================
FILE: .rustfmt.toml
================================================
disable_all_formatting = true


================================================
FILE: .travis.yml
================================================
language: rust
rust:
  - 1.45.0
  - stable
  - beta
  - nightly

script:
  - cargo build --verbose --workspace
  - cargo test --verbose --workspace --no-run
  - cargo test --verbose --workspace

os:
  - windows
  - linux
  - osx

jobs:
  fast_finish: true
  allow_failures:
    - rust: nightly

  include:
    - name: 'Rust: lint with Clippy'
      rust: stable
      install:
        - rustup component add clippy
      script:
        - cargo clippy

    - name: 'Rust: mutation testing'
      rust: nightly
      install:
        - git clone https://github.com/llogiq/mutagen.git
        - cd mutagen/mutagen-runner
        - cargo install --path .
        - cd ../..
      script:
        - cargo test    --package dns --features=dns/with_mutagen -- --quiet
        - cargo mutagen --package dns --features=dns/with_mutagen


================================================
FILE: Cargo.toml
================================================
[package]
name = "dog"
description = "A command-line DNS client"

authors = ["Benjamin Sago <ogham@bsago.me>"]
categories = ["command-line-utilities"]
edition = "2018"
exclude = [
    "/completions/*", "/man/*", "/xtests/*",
    "/dog-screenshot.png", "/Justfile", "/README.md", "/.rustfmt.toml", "/.travis.yml",
]
homepage = "https://dns.lookup.dog/"
license = "EUPL-1.2"
version = "0.2.0-pre"


[[bin]]
name = "dog"
path = "src/main.rs"
doctest = false


[workspace]
members = [
  "dns",
  "dns-transport",
]


# make dev builds faster by excluding debug symbols
[profile.dev]
debug = false

# use LTO for smaller binaries (that take longer to build)
[profile.release]
lto = true
overflow-checks = true
panic = "abort"


[dependencies]

# dns stuff
dns = { path = "./dns" }
dns-transport = { path = "./dns-transport" }

# command-line
ansi_term = "0.12"
atty = "0.2"
getopts = "0.2"

# transaction ID generation
rand = "0.8"

# json output
json = "0.12"

# logging
log = "0.4"

# windows default nameserver determination
[target.'cfg(windows)'.dependencies]
ipconfig = { version = "0.2" }

[build-dependencies]
datetime = { version = "0.5.1", default_features = false }

[dev-dependencies]
pretty_assertions = "0.7"

[features]
default = ["with_idna", "with_tls", "with_https", "with_nativetls"]
with_idna = ["dns/with_idna"]

with_tls = ["dns-transport/with_tls"]
with_https = ["dns-transport/with_https"]

with_nativetls = ["dns-transport/with_nativetls"]
with_nativetls_vendored = ["with_nativetls", "dns-transport/with_nativetls", "dns-transport/with_nativetls_vendored"]
with_rustls = ["dns-transport/with_rustls"]


================================================
FILE: Dockerfile
================================================
FROM rust as build

WORKDIR /build
COPY /src /build/src
COPY /dns /build/dns
COPY /dns-transport /build/dns-transport
COPY /man /build/man
COPY build.rs Cargo.toml /build/

RUN cargo build --release

FROM debian:buster-slim

RUN apt update && apt install -y libssl1.1 ca-certificates && apt clean all

COPY --from=build /build/target/release/dog /dog

ENTRYPOINT ["/dog"]


================================================
FILE: Justfile
================================================
all: build test xtests
all-release: build-release test-release xtests-release
all-quick: build-quick test-quick xtests-quick

export DOG_DEBUG := ""


#----------#
# building #
#----------#

# compile the dog binary
@build:
    cargo build

# compile the dog binary (in release mode)
@build-release:
    cargo build --release --verbose
    strip "${CARGO_TARGET_DIR:-target}/release/dog"

# produce an HTML chart of compilation timings
@build-time:
    cargo +nightly clean
    cargo +nightly build -Z timings

# compile the dog binary (without some features)
@build-quick:
    cargo build --no-default-features

# check that the dog binary can compile
@check:
    cargo check


#---------------#
# running tests #
#---------------#

# run unit tests
@test:
    cargo test --workspace -- --quiet

# run unit tests (in release mode)
@test-release:
    cargo test --workspace --release --verbose

# run unit tests (without some features)
@test-quick:
    cargo test --workspace --no-default-features -- --quiet

# run mutation tests
@test-mutation:
    cargo +nightly test    --package dns --features=dns/with_mutagen -- --quiet
    cargo +nightly mutagen --package dns --features=dns/with_mutagen


#------------------------#
# running extended tests #
#------------------------#

# run extended tests
@xtests *args:
    specsheet xtests/{options,live,madns}/*.toml -shide {{args}} \
        -O cmd.target.dog="${CARGO_TARGET_DIR:-../../target}/debug/dog"

# run extended tests (in release mode)
@xtests-release *args:
    specsheet xtests/{options,live,madns}/*.toml {{args}} \
        -O cmd.target.dog="${CARGO_TARGET_DIR:-../../target}/release/dog"

# run extended tests (omitting certain feature tests)
@xtests-quick *args:
    specsheet xtests/options/*.toml xtests/live/{basics,tcp}.toml -shide {{args}} \
        -O cmd.target.dog="${CARGO_TARGET_DIR:-../../target}/debug/dog"

# run extended tests against a local madns instance
@xtests-madns-local *args:
    env MADNS_ARGS="@localhost:5301 --tcp" \
        specsheet xtests/madns/*.toml -shide {{args}} \
            -O cmd.target.dog="${CARGO_TARGET_DIR:-../../target}/debug/dog"

# display the number of extended tests that get run
@count-xtests:
    grep -F '[[cmd]]' -R xtests | wc -l


#---------#
# fuzzing #
#---------#

# run fuzzing on the dns crate
@fuzz:
    cargo +nightly fuzz --version
    cd dns; cargo +nightly fuzz run fuzz_parsing -- -jobs=`nproc` -workers=`nproc` -runs=69105

# print out the data that caused crashes during fuzzing as hexadecimal
@fuzz-hex:
    for crash in dns/fuzz/artifacts/fuzz_parsing/crash-*; do echo; echo $crash; hexyl $crash; done

# remove fuzz log files
@fuzz-clean:
    rm dns/fuzz/fuzz-*.log


#-----------------------#
# code quality and misc #
#-----------------------#

# lint the code
@clippy:
    touch dns/src/lib.rs
    cargo clippy

# generate a code coverage report using tarpaulin via docker
@coverage-docker:
    docker run --security-opt seccomp=unconfined -v "${PWD}:/volume" xd009642/tarpaulin cargo tarpaulin --all --out Html

# update dependency versions, and check for outdated ones
@update-deps:
    cargo update
    command -v cargo-outdated >/dev/null || (echo "cargo-outdated not installed" && exit 1)
    cargo outdated

# list unused dependencies
@unused-deps:
    command -v cargo-udeps >/dev/null || (echo "cargo-udeps not installed" && exit 1)
    cargo +nightly udeps

# builds dog and runs extended tests with features disabled
@feature-checks *args:
    cargo build --no-default-features
    specsheet xtests/features/none.toml -shide {{args}} \
        -O cmd.target.dog="${CARGO_TARGET_DIR:-../../target}/debug/dog"

# print versions of the necessary build tools
@versions:
    rustc --version
    cargo --version


#---------------#
# documentation #
#---------------#

# render the documentation
@doc:
    cargo doc --no-deps --workspace

# build the man pages
@man:
    mkdir -p "${CARGO_TARGET_DIR:-target}/man"
    pandoc --standalone -f markdown -t man man/dog.1.md > "${CARGO_TARGET_DIR:-target}/man/dog.1"

# build and preview the man page
@man-preview: man
    man "${CARGO_TARGET_DIR:-target}/man/dog.1"


#-----------#
# packaging #
#-----------#

# create a distributable package
zip desc exe="dog":
    #!/usr/bin/env perl
    use Archive::Zip;
    -e 'target/release/{{ exe }}' || die 'Binary not built!';
    -e 'target/man/dog.1' || die 'Man page not built!';
    my $zip = Archive::Zip->new();
    $zip->addFile('completions/dog.bash');
    $zip->addFile('completions/dog.zsh');
    $zip->addFile('completions/dog.fish');
    $zip->addFile('target/man/dog.1', 'man/dog.1');
    $zip->addFile('target/release/{{ exe }}', 'bin/{{ exe }}');
    $zip->writeToFileNamed('dog-{{ desc }}.zip') == AZ_OK || die 'Zip write error!';
    system 'unzip -l "dog-{{ desc }}".zip'


================================================
FILE: LICENCE
================================================
                      EUROPEAN UNION PUBLIC LICENCE v. 1.2
                      EUPL © the European Union 2007, 2016

This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined
below) which is provided under the terms of this Licence. Any use of the Work,
other than as authorised under this Licence is prohibited (to the extent such
use is covered by a right of the copyright holder of the Work).

The Work is provided under the terms of this Licence when the Licensor (as
defined below) has placed the following notice immediately following the
copyright notice for the Work:

        Licensed under the EUPL

or has expressed by any other means his willingness to license under the EUPL.

1. Definitions

In this Licence, the following terms have the following meaning:

- ‘The Licence’: this Licence.

- ‘The Original Work’: the work or software distributed or communicated by the
  Licensor under this Licence, available as Source Code and also as Executable
  Code as the case may be.

- ‘Derivative Works’: the works or software that could be created by the
  Licensee, based upon the Original Work or modifications thereof. This Licence
  does not define the extent of modification or dependence on the Original Work
  required in order to classify a work as a Derivative Work; this extent is
  determined by copyright law applicable in the country mentioned in Article 15.

- ‘The Work’: the Original Work or its Derivative Works.

- ‘The Source Code’: the human-readable form of the Work which is the most
  convenient for people to study and modify.

- ‘The Executable Code’: any code which has generally been compiled and which is
  meant to be interpreted by a computer as a program.

- ‘The Licensor’: the natural or legal person that distributes or communicates
  the Work under the Licence.

- ‘Contributor(s)’: any natural or legal person who modifies the Work under the
  Licence, or otherwise contributes to the creation of a Derivative Work.

- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of
  the Work under the terms of the Licence.

- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending,
  renting, distributing, communicating, transmitting, or otherwise making
  available, online or offline, copies of the Work or providing access to its
  essential functionalities at the disposal of any other natural or legal
  person.

2. Scope of the rights granted by the Licence

The Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
sublicensable licence to do the following, for the duration of copyright vested
in the Original Work:

- use the Work in any circumstance and for all usage,
- reproduce the Work,
- modify the Work, and make Derivative Works based upon the Work,
- communicate to the public, including the right to make available or display
  the Work or copies thereof to the public and perform publicly, as the case may
  be, the Work,
- distribute the Work or copies thereof,
- lend and rent the Work or copies thereof,
- sublicense rights in the Work or copies thereof.

Those rights can be exercised on any media, supports and formats, whether now
known or later invented, as far as the applicable law permits so.

In the countries where moral rights apply, the Licensor waives his right to
exercise his moral right to the extent allowed by law in order to make effective
the licence of the economic rights here above listed.

The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to
any patents held by the Licensor, to the extent necessary to make use of the
rights granted on the Work under this Licence.

3. Communication of the Source Code

The Licensor may provide the Work either in its Source Code form, or as
Executable Code. If the Work is provided as Executable Code, the Licensor
provides in addition a machine-readable copy of the Source Code of the Work
along with each copy of the Work that the Licensor distributes or indicates, in
a notice following the copyright notice attached to the Work, a repository where
the Source Code is easily and freely accessible for as long as the Licensor
continues to distribute or communicate the Work.

4. Limitations on copyright

Nothing in this Licence is intended to deprive the Licensee of the benefits from
any exception or limitation to the exclusive rights of the rights owners in the
Work, of the exhaustion of those rights or of other applicable limitations
thereto.

5. Obligations of the Licensee

The grant of the rights mentioned above is subject to some restrictions and
obligations imposed on the Licensee. Those obligations are the following:

Attribution right: The Licensee shall keep intact all copyright, patent or
trademarks notices and all notices that refer to the Licence and to the
disclaimer of warranties. The Licensee must include a copy of such notices and a
copy of the Licence with every copy of the Work he/she distributes or
communicates. The Licensee must cause any Derivative Work to carry prominent
notices stating that the Work has been modified and the date of modification.

Copyleft clause: If the Licensee distributes or communicates copies of the
Original Works or Derivative Works, this Distribution or Communication will be
done under the terms of this Licence or of a later version of this Licence
unless the Original Work is expressly distributed only under this version of the
Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee
(becoming Licensor) cannot offer or impose any additional terms or conditions on
the Work or Derivative Work that alter or restrict the terms of the Licence.

Compatibility clause: If the Licensee Distributes or Communicates Derivative
Works or copies thereof based upon both the Work and another work licensed under
a Compatible Licence, this Distribution or Communication can be done under the
terms of this Compatible Licence. For the sake of this clause, ‘Compatible
Licence’ refers to the licences listed in the appendix attached to this Licence.
Should the Licensee's obligations under the Compatible Licence conflict with
his/her obligations under this Licence, the obligations of the Compatible
Licence shall prevail.

Provision of Source Code: When distributing or communicating copies of the Work,
the Licensee will provide a machine-readable copy of the Source Code or indicate
a repository where this Source will be easily and freely available for as long
as the Licensee continues to distribute or communicate the Work.

Legal Protection: This Licence does not grant permission to use the trade names,
trademarks, service marks, or names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the copyright notice.

6. Chain of Authorship

The original Licensor warrants that the copyright in the Original Work granted
hereunder is owned by him/her or licensed to him/her and that he/she has the
power and authority to grant the Licence.

Each Contributor warrants that the copyright in the modifications he/she brings
to the Work are owned by him/her or licensed to him/her and that he/she has the
power and authority to grant the Licence.

Each time You accept the Licence, the original Licensor and subsequent
Contributors grant You a licence to their contributions to the Work, under the
terms of this Licence.

7. Disclaimer of Warranty

The Work is a work in progress, which is continuously improved by numerous
Contributors. It is not a finished work and may therefore contain defects or
‘bugs’ inherent to this type of development.

For the above reason, the Work is provided under the Licence on an ‘as is’ basis
and without warranties of any kind concerning the Work, including without
limitation merchantability, fitness for a particular purpose, absence of defects
or errors, accuracy, non-infringement of intellectual property rights other than
copyright as stated in Article 6 of this Licence.

This disclaimer of warranty is an essential part of the Licence and a condition
for the grant of any rights to the Work.

8. Disclaimer of Liability

Except in the cases of wilful misconduct or damages directly caused to natural
persons, the Licensor will in no event be liable for any direct or indirect,
material or moral, damages of any kind, arising out of the Licence or of the use
of the Work, including without limitation, damages for loss of goodwill, work
stoppage, computer failure or malfunction, loss of data or any commercial
damage, even if the Licensor has been advised of the possibility of such damage.
However, the Licensor will be liable under statutory product liability laws as
far such laws apply to the Work.

9. Additional agreements

While distributing the Work, You may choose to conclude an additional agreement,
defining obligations or services consistent with this Licence. However, if
accepting obligations, You may act only on your own behalf and on your sole
responsibility, not on behalf of the original Licensor or any other Contributor,
and only if You agree to indemnify, defend, and hold each Contributor harmless
for any liability incurred by, or claims asserted against such Contributor by
the fact You have accepted any warranty or additional liability.

10. Acceptance of the Licence

The provisions of this Licence can be accepted by clicking on an icon ‘I agree’
placed under the bottom of a window displaying the text of this Licence or by
affirming consent in any other similar way, in accordance with the rules of
applicable law. Clicking on that icon indicates your clear and irrevocable
acceptance of this Licence and all of its terms and conditions.

Similarly, you irrevocably accept this Licence and all of its terms and
conditions by exercising any rights granted to You by Article 2 of this Licence,
such as the use of the Work, the creation by You of a Derivative Work or the
Distribution or Communication by You of the Work or copies thereof.

11. Information to the public

In case of any Distribution or Communication of the Work by means of electronic
communication by You (for example, by offering to download the Work from a
remote location) the distribution channel or media (for example, a website) must
at least provide to the public the information requested by the applicable law
regarding the Licensor, the Licence and the way it may be accessible, concluded,
stored and reproduced by the Licensee.

12. Termination of the Licence

The Licence and the rights granted hereunder will terminate automatically upon
any breach by the Licensee of the terms of the Licence.

Such a termination will not terminate the licences of any person who has
received the Work from the Licensee under the Licence, provided such persons
remain in full compliance with the Licence.

13. Miscellaneous

Without prejudice of Article 9 above, the Licence represents the complete
agreement between the Parties as to the Work.

If any provision of the Licence is invalid or unenforceable under applicable
law, this will not affect the validity or enforceability of the Licence as a
whole. Such provision will be construed or reformed so as necessary to make it
valid and enforceable.

The European Commission may publish other linguistic versions or new versions of
this Licence or updated versions of the Appendix, so far this is required and
reasonable, without reducing the scope of the rights granted by the Licence. New
versions of the Licence will be published with a unique version number.

All linguistic versions of this Licence, approved by the European Commission,
have identical value. Parties can take advantage of the linguistic version of
their choice.

14. Jurisdiction

Without prejudice to specific agreement between parties,

- any litigation resulting from the interpretation of this License, arising
  between the European Union institutions, bodies, offices or agencies, as a
  Licensor, and any Licensee, will be subject to the jurisdiction of the Court
  of Justice of the European Union, as laid down in article 272 of the Treaty on
  the Functioning of the European Union,

- any litigation arising between other parties and resulting from the
  interpretation of this License, will be subject to the exclusive jurisdiction
  of the competent court where the Licensor resides or conducts its primary
  business.

15. Applicable Law

Without prejudice to specific agreement between parties,

- this Licence shall be governed by the law of the European Union Member State
  where the Licensor has his seat, resides or has his registered office,

- this licence shall be governed by Belgian law if the Licensor has no seat,
  residence or registered office inside a European Union Member State.

Appendix

‘Compatible Licences’ according to Article 5 EUPL are:

- GNU General Public License (GPL) v. 2, v. 3
- GNU Affero General Public License (AGPL) v. 3
- Open Software License (OSL) v. 2.1, v. 3.0
- Eclipse Public License (EPL) v. 1.0
- CeCILL v. 2.0, v. 2.1
- Mozilla Public Licence (MPL) v. 2
- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3
- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for
  works other than software
- European Union Public Licence (EUPL) v. 1.1, v. 1.2
- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong
  Reciprocity (LiLiQ-R+).

The European Commission may update this Appendix to later versions of the above
licences without producing a new version of the EUPL, as long as they provide
the rights granted in Article 2 of this Licence and protect the covered Source
Code from exclusive appropriation.

All other changes or additions to this Appendix require the production of a new
EUPL version.


================================================
FILE: README.md
================================================
<div align="center">
<h1>dog</h1>

[dog](https://dns.lookup.dog/) is a command-line DNS client.

<a href="https://travis-ci.org/github/ogham/dog">
    <img src="https://travis-ci.org/ogham/dog.svg?branch=master" alt="Build status" />
</a>

<a href="https://saythanks.io/to/ogham%40bsago.me">
    <img src="https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg" alt="Say thanks!" />
</a>
</div>

![A screenshot of dog making a DNS request](dog-screenshot.png)

---

Dogs _can_ look up!

**dog** is a command-line DNS client, like `dig`.
It has colourful output, understands normal command-line argument syntax, supports the DNS-over-TLS and DNS-over-HTTPS protocols, and can emit JSON.

## Examples

    dog example.net                          Query a domain using default settings
    dog example.net MX                       ...looking up MX records instead
    dog example.net MX @1.1.1.1              ...using a specific nameserver instead
    dog example.net MX @1.1.1.1 -T           ...using TCP rather than UDP
    dog -q example.net -t MX -n 1.1.1.1 -T   As above, but using explicit arguments

---

## Command-line options

### Query options

    <arguments>              Human-readable host names, nameservers, types, or classes
    -q, --query=HOST         Host name or domain name to query
    -t, --type=TYPE          Type of the DNS record being queried (A, MX, NS...)
    -n, --nameserver=ADDR    Address of the nameserver to send packets to
    --class=CLASS            Network class of the DNS record being queried (IN, CH, HS)

### Sending options

    --edns=SETTING           Whether to OPT in to EDNS (disable, hide, show)
    --txid=NUMBER            Set the transaction ID to a specific value
    -Z=TWEAKS                Set uncommon protocol-level tweaks

### Protocol options

    -U, --udp                Use the DNS protocol over UDP
    -T, --tcp                Use the DNS protocol over TCP
    -S, --tls                Use the DNS-over-TLS protocol
    -H, --https              Use the DNS-over-HTTPS protocol

### Output options

    -1, --short              Short mode: display nothing but the first result
    -J, --json               Display the output as JSON
    --color, --colour=WHEN   When to colourise the output (always, automatic, never)
    --seconds                Do not format durations, display them as seconds
    --time                   Print how long the response took to arrive


---

## Installation

To install dog, you can download a pre-compiled binary, or you can compile it from source. You _may_ be able to install dog using your OS’s package manager, depending on your platform.


### Packages

- For Arch Linux, install the [`dog`](https://www.archlinux.org/packages/community/x86_64/dog/) package.
- For Homebrew on macOS, install the [`dog`](https://formulae.brew.sh/formula/dog) formula.
- For NixOS, install the [`dogdns`](https://search.nixos.org/packages?channel=unstable&show=dogdns&query=dogdns) package.


### Downloads

Binary downloads of dog are available from [the releases section on GitHub](https://github.com/ogham/dog/releases/) for 64-bit Windows, macOS, and Linux targets. They contain the compiled executable, the manual page, and shell completions.


### Compilation

dog is written in [Rust](https://www.rust-lang.org).
You will need rustc version [1.45.0](https://blog.rust-lang.org/2020/07/16/Rust-1.45.0.html) or higher.
The recommended way to install Rust for development is from the [official download page](https://www.rust-lang.org/tools/install), using rustup.

To build, download the source code and run:

    $ cargo build
    $ cargo test

- The [just](https://github.com/casey/just) command runner can be used to run some helpful development commands, in a manner similar to `make`.
Run `just --list` to get an overview of what’s available.

- If you are compiling a copy for yourself, be sure to run `cargo build --release` or `just build-release` to benefit from release-mode optimisations.
Copy the resulting binary, which will be in the `target/release` directory, into a folder in your `$PATH`.
`/usr/local/bin` is usually a good choice.

- To compile and install the manual pages, you will need [pandoc](https://pandoc.org/).
The `just man` command will compile the Markdown into manual pages, which it will place in the `target/man` directory.
To use them, copy them into a directory that `man` will read.
`/usr/local/share/man` is usually a good choice.


### Container image

To build the container image of dog, you can use Docker or Kaniko. Here an example using Docker:

    $ docker build -t dog .

You can then run it using the following command:

    $ docker run -it --rm dog

To run dog directly, you can then define the following alias:

    $ alias dog="docker run -it --rm dog"


### End-to-end testing

dog has an integration test suite written as [Specsheet](https://specsheet.software/) check documents.
If you have a copy installed, you can run:

    $ just xtests

Specsheet will test the compiled binary by making DNS requests over the network, checking that dog returns the correct results and does not crash.
Note that this will expose your IP address.
For more information, read [the xtests README](xtests/README.md).


### Feature toggles

dog has three Cargo features that can be switched off to remove functionality.
While doing so makes dog less useful, it results in a smaller binary that takes less time to build.

There are three feature toggles available, all of which are active by default:

- `with_idna`, which enables [IDNA](https://en.wikipedia.org/wiki/Internationalized_domain_name) processing
- `with_tls`, which enables DNS-over-TLS
- `with_https`, which enables DNS-over-HTTPS (requires `with_tls`)

Use `cargo` to build a binary that uses feature toggles. For example, to disable TLS and HTTPS support but keep IDNA support enabled, you can run:

    $ cargo build --no-default-features --features=with_idna

The list of features that have been disabled can be checked at runtime as part of the `--version` string.


---

## Documentation

For documentation on how to use dog, see the website: <https://dns.lookup.dog/>


## See also

`mutt`, `tail`, `sleep`, `roff`


## Licence

dog’s source code is licenced under the [European Union Public Licence](https://choosealicense.com/licenses/eupl-1.2/).


================================================
FILE: build.rs
================================================
//! This build script gets run during every build. Its purpose is to put
//! together the files used for the `--help` and `--version`, which need to
//! come in both coloured and non-coloured variants. The main usage text is
//! contained in `src/usage.txt`; to make it easier to edit, backslashes (\)
//! are used instead of the beginning of ANSI escape codes.
//!
//! The version string is quite complex: we want to show the version,
//! current Git hash, and compilation date when building *debug*
//! versions, but just the version for *release* versions.
//!
//! This script generates the string from the environment variables
//! that Cargo adds (http://doc.crates.io/environment-variables.html)
//! and runs `git` to get the SHA1 hash. It then writes the strings
//! into files, which we can include during compilation.

use std::env;
use std::fs::File;
use std::io::{self, Write};
use std::path::PathBuf;

use datetime::{LocalDateTime, ISO};


/// The build script entry point.
fn main() -> io::Result<()> {
    #![allow(clippy::write_with_newline)]

    let usage   = include_str!("src/usage.txt");
    let tagline = "dog \\1;32m●\\0m command-line DNS client";
    let url     = "https://dns.lookup.dog/";

    let ver =
        if is_debug_build() {
            format!("{}\nv{} \\1;31m(pre-release debug build!)\\0m\n\\1;4;34m{}\\0m", tagline, version_string(), url)
        }
        else if is_development_version() {
            format!("{}\nv{} [{}] built on {} \\1;31m(pre-release!)\\0m\n\\1;4;34m{}\\0m", tagline, version_string(), git_hash(), build_date(), url)
        }
        else {
            format!("{}\nv{}\n\\1;4;34m{}\\0m", tagline, version_string(), url)
        };

    // We need to create these files in the Cargo output directory.
    let out = PathBuf::from(env::var("OUT_DIR").unwrap());

    // Pretty version text
    let mut f = File::create(&out.join("version.pretty.txt"))?;
    writeln!(f, "{}", convert_codes(&ver))?;

    // Bland version text
    let mut f = File::create(&out.join("version.bland.txt"))?;
    writeln!(f, "{}", strip_codes(&ver))?;

    // Pretty usage text
    let mut f = File::create(&out.join("usage.pretty.txt"))?;
    writeln!(f, "{}", convert_codes(&tagline))?;
    writeln!(f)?;
    write!(f, "{}", convert_codes(&usage))?;

    // Bland usage text
    let mut f = File::create(&out.join("usage.bland.txt"))?;
    writeln!(f, "{}", strip_codes(&tagline))?;
    writeln!(f)?;
    write!(f, "{}", strip_codes(&usage))?;

    Ok(())
}

/// Converts the escape codes in ‘usage.txt’ to ANSI escape codes.
fn convert_codes(input: &str) -> String {
    input.replace("\\", "\x1B[")
}

/// Removes escape codes from ‘usage.txt’.
fn strip_codes(input: &str) -> String {
    input.replace("\\0m", "")
         .replace("\\1m", "")
         .replace("\\4m", "")
         .replace("\\32m", "")
         .replace("\\33m", "")
         .replace("\\1;31m", "")
         .replace("\\1;32m", "")
         .replace("\\1;33m", "")
         .replace("\\1;4;34", "")
}

/// Retrieve the project’s current Git hash, as a string.
fn git_hash() -> String {
    use std::process::Command;

    String::from_utf8_lossy(
        &Command::new("git")
            .args(&["rev-parse", "--short", "HEAD"])
            .output().unwrap()
            .stdout).trim().to_string()
}

/// Whether we should show pre-release info in the version string.
///
/// Both weekly releases and actual releases are --release releases,
/// but actual releases will have a proper version number.
fn is_development_version() -> bool {
    cargo_version().ends_with("-pre") || env::var("PROFILE").unwrap() == "debug"
}

/// Whether we are building in debug mode.
fn is_debug_build() -> bool {
    env::var("PROFILE").unwrap() == "debug"
}

/// Retrieves the [package] version in Cargo.toml as a string.
fn cargo_version() -> String {
    env::var("CARGO_PKG_VERSION").unwrap()
}

/// Returns the version and build parameters string.
fn version_string() -> String {
    let mut ver = cargo_version();

    let feats = nonstandard_features_string();
    if ! feats.is_empty() {
        ver.push_str(&format!(" [{}]", &feats));
    }

    ver
}

/// Finds whether a feature is enabled by examining the Cargo variable.
fn feature_enabled(name: &str) -> bool {
    env::var(&format!("CARGO_FEATURE_{}", name))
        .map(|e| ! e.is_empty())
        .unwrap_or(false)
}

/// A comma-separated list of non-standard feature choices.
fn nonstandard_features_string() -> String {
    let mut s = Vec::new();

    if ! feature_enabled("WITH_IDNA") {
        s.push("-idna");
    }

    if ! feature_enabled("WITH_TLS") {
        s.push("-tls");
    }

    if ! feature_enabled("WITH_HTTPS") {
        s.push("-https");
    }

    s.join(", ")
}


/// Formats the current date as an ISO 8601 string.
fn build_date() -> String {
    let now = LocalDateTime::now();
    format!("{}", now.date().iso())
}


================================================
FILE: completions/dog.bash
================================================
_dog()
{
    cur=${COMP_WORDS[COMP_CWORD]}
    prev=${COMP_WORDS[COMP_CWORD-1]}

    case "$prev" in
        -'?'|--help|-v|--version)
            return
            ;;

        -t|--type)
            COMPREPLY=( $( compgen -W 'A AAAA CAA CNAME HINFO MX NS PTR SOA SRV TXT' -- "$cur" ) )
            return
            ;;

        --edns)
            COMPREPLY=( $( compgen -W 'disable hide show' -- "$cur" ) )
            return
            ;;

        -Z)
            COMPREPLY=( $( compgen -W 'aa ad bufsize= cd' -- "$cur" ) )
            return
            ;;

        --class)
            COMPREPLY=( $( compgen -W 'IN CH HS' -- "$cur" ) )
            return
            ;;

        --color|--colour)
            COMPREPLY=( $( compgen -W 'always automatic never' -- $cur ) )
            return
            ;;
    esac

    case "$cur" in
        -*)
            COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "$cur" ) )
            return
            ;;

        *)
            COMPREPLY=( $( compgen -W 'A AAAA CAA CNAME HINFO MX NS PTR SOA SRV TXT' -- "$cur" ) )
            ;;
    esac
} &&
complete -o bashdefault -F _dog dog


================================================
FILE: completions/dog.fish
================================================
# Meta options
complete -c dog -s 'v' -l 'version' -d "Show version of dog"
complete -c dog -s '?' -l 'help'    -d "Show list of command-line options"

# Query options
complete -c dog -x -a "(__fish_print_hostnames) A AAAA CAA CNAME HINFO MX NS PTR SOA SRV TXT IN CH HS"
complete -c dog -s 'q' -l 'query'      -d "Host name or domain name to query" -x -a "(__fish_print_hostnames)"
complete -c dog -s 't' -l 'type'       -d "Type of the DNS record being queried" -x -a "A AAAA CAA CNAME HINFO MX NS PTR SOA SRV TXT"
complete -c dog -s 'n' -l 'nameserver' -d "Address of the nameserver to send packets to" -x -a "(__fish_print_hostnames)"
complete -c dog        -l 'class'      -d "Network class of the DNS record being queried" -x -a "IN CH HS"

# Sending options
complete -c dog        -l 'edns'       -d "Whether to OPT in to EDNS" -x -a "
    disable\t'Do not send an OPT query'
    hide\t'Send an OPT query, but hide the result'
    show\t'Send an OPT query, and show the result'
"
complete -c dog        -l 'txid'       -d "Set the transaction ID to a specific value" -x
complete -c dog -s 'Z'                 -d "Configure uncommon protocol-level tweaks" -x -a "
    aa\t'Set the AA (Authoritative Answers) query bit'
    ad\t'Set the AD (Authentic Data) query bit'
    bufsize=\t'Set the UDP payload size'
    cd\t'Set the CD (Checking Disabled) query bit'
"

# Protocol options
complete -c dog -s 'U' -l 'udp'        -d "Use the DNS protocol over UDP"
complete -c dog -s 'T' -l 'tcp'        -d "Use the DNS protocol over TCP"
complete -c dog -s 'S' -l 'tls'        -d "Use the DNS-over-TLS protocol"
complete -c dog -s 'H' -l 'https'      -d "Use the DNS-over-HTTPS protocol"

# Output options
complete -c dog -s '1' -l 'short'      -d "Display nothing but the first result"
complete -c dog -s 'J' -l 'json'       -d "Display the output as JSON"
complete -c dog        -l 'color'      -d "When to colorise the output" -x -a "
    always\t'Always use colors'
    automatic\t'Use colors when printing to a terminal'
    never\t'Never use colors'
"
complete -c dog        -l 'colour'     -d "When to colourise the output" -x -a "
    always\t'Always use colours'
    automatic\t'Use colours when printing to a terminal'
    never\t'Never use colours'
"
complete -c dog        -l 'seconds'    -d "Do not format durations, display them as seconds"
complete -c dog        -l 'time'       -d "Print how long the response took to arrive"


================================================
FILE: completions/dog.ps1
================================================
# Note: This works for both Windows PowerShell 5.1 and also PowerShell 7 (Core).
# But beware that in Windows PowerShell 5.1, it has issues with completing args if they start with '-'.
# For more information about the bug, see: https://github.com/PowerShell/PowerShell/issues/2912
# In PowerShell 7+, it should work correctly.
Register-ArgumentCompleter -Native -CommandName 'dog' -ScriptBlock {
    param($wordToComplete, $commandAst, $cursorPosition)

    [string]$argsString = $commandAst.ToString()

    # skip the "dog", split the args afterwards as array
    [string[]]$argsArray = $argsString.Split([char[]]@(' ', '=')) | Select-Object -Skip 1
    if ($argsArray -eq $null) { $argsArray = @() }

    # detect if starting a new arg (aka ending with space and asking for a completion)
    [bool]$isNewArg = $cursorPosition -gt $argsString.Length
    if ($isNewArg) {
        # if writing a new arg, add empty arg so that current and previous would be shifted
        $argsArray += ''
    }

    # get current arg (empty if starting new)
    [string]$currentArg = $argsArray[-1]
    if ([string]::IsNullOrEmpty($currentArg)) {
        $currentArg = ''
    }

    # get previous arg
    [string]$previousArg = $argsArray[-2]
    if ([string]::IsNullOrEmpty($previousArg)) {
        $previousArg = ''
    }

    [string[]]$dnsTypeValues = @('A', 'AAAA', 'CAA', 'CNAME', 'HINFO', 'MX', 'NS', 'PTR', 'SOA', 'SRV', 'TXT')

    [string[]]$completions = @()
    [bool]$isOptionValue = $argsString.EndsWith('=')

    # complete option value
    switch -Regex ($previousArg) {
        '^(-q|--query)'       { $isOptionValue = $true }
        '^(-t|--type)'        { $isOptionValue = $true; $completions += $dnsTypeValues }
        '^(-n|--nameserver)'  { $isOptionValue = $true }
        '^(--class)'          { $isOptionValue = $true; $completions += @('IN', 'CH', 'HS') }
        '^(--edns)'           { $isOptionValue = $true; $completions += @('disable', 'hide', 'show') }
        '^(--txid)'           { $isOptionValue = $true }
        '^(-Z)'               { $isOptionValue = $true; $completions += @('aa', 'ad', 'bufsize=', 'cd') }
        '^(--color|--colour)' { $isOptionValue = $true; $completions += @('always', 'automatic', 'never') }
    }

    # detect whether to complete option value
    if ($isOptionValue) {
        if (!$isNewArg) {
            # if using =, complete including the option name and =
            $completions = $completions | ForEach-Object { "$previousArg=$_" }
        }
    } 
    else {
        # if not completing option value, offer DNS type values first
        $completions += $dnsTypeValues

        # complete option name
        [string[]]$allOptions = @(
            '-q', '--query',
            '-t', '--type',
            '-n', '--nameserver',
            '--class',
            '--edns',
            '--txid',
            '-Z',
            '-U', '--udp',
            '-T', '--tcp',
            '-S', '--tls',
            '-H', '--https',
            '-1', '--short',
            '-J', '--json',
            '--color', '--colour',
            '--seconds',
            '--time',
            '-?', '--help',
            '-v', '--version'
        ) | Sort-Object

        $completions += $allOptions
    }

    if ($completions.Count -gt 0) {
        # narrow down completions by like* matching
        return $completions -like "$currentArg*"
    }
}


================================================
FILE: completions/dog.zsh
================================================
#compdef dog

__dog() {
    _arguments \
        "(- 1 *)"{-v,--version}"[Show version of dog]" \
        "(- 1 *)"{-\?,--help}"[Show list of command-line options]" \
        {-q,--query}"[Host name or domain name to query]::_hosts" \
        {-t,--type}"[Type of the DNS record being queried]:(record type):(A AAAA CAA CNAME HINFO MX NS PTR SOA SRV TXT)" \
        {-n,--nameserver}"[Address of the nameserver to send packets to]::_hosts;" \
        --class"[Network class of the DNS record being queried]:(network class):(IN CH HS)" \
        --edns"[Whether to OPT in to EDNS]:(edns setting):(disable hide show)" \
        --txid"[Set the transaction ID to a specific value]" \
        -Z"[Configure uncommon protocol-level tweaks]:(protocol tweak):(aa ad bufsize= cd)" \
        {-U,--udp}"[Use the DNS protocol over UDP]" \
        {-T,--tcp}"[Use the DNS protocol over TCP]" \
        {-S,--tls}"[Use the DNS-over-TLS protocol]" \
        {-H,--https}"[Use the DNS-over-HTTPS protocol]" \
        {-1,--short}"[Display nothing but the finst result]" \
        {-J,--json}"[Display the output as JSON]" \
        {--color,--colour}"[When to use terminal colours]:(setting):(always automatic never)" \
        --seconds"[Do not format durations, display them as seconds]" \
        --time"[Print how long the response took to arrive"] \
        '*:filename:_hosts'
}

__dog


================================================
FILE: dns/Cargo.toml
================================================
[package]
name = "dns"
version = "0.2.0-pre"
authors = ["Benjamin Sago <ogham@bsago.me>"]
edition = "2018"

[lib]
doctest = false


[dependencies]

# logging
log = "0.4"

# protocol parsing helper
byteorder = "1.3"

# printing of certain packets
base64 = "0.13"

# idna encoding
unic-idna = { version = "0.9.0", optional = true }

# mutation testing
mutagen = { git = "https://github.com/llogiq/mutagen", optional = true }

[dev-dependencies]
pretty_assertions = "0.7"

[features]
default = []  # idna is enabled in the main dog crate
with_idna = ["unic-idna"]
with_mutagen = ["mutagen"]  # needs nightly


================================================
FILE: dns/fuzz/.gitignore
================================================

target
corpus
artifacts


================================================
FILE: dns/fuzz/Cargo.toml
================================================
[package]
name = "dns-fuzz"
version = "0.0.1"
authors = ["Automatically generated"]
publish = false

[package.metadata]
cargo-fuzz = true

[dependencies.dns]
path = ".."

[dependencies.libfuzzer-sys]
version = "0.3.0"

# Prevent this from interfering with workspaces
[workspace]
members = ["."]

[[bin]]
name = "fuzz_parsing"
path = "fuzz_targets/fuzz_parsing.rs"


================================================
FILE: dns/fuzz/fuzz_targets/fuzz_parsing.rs
================================================
#![no_main]
#[macro_use] extern crate libfuzzer_sys;
extern crate dns;
use dns::Response;

fuzz_target!(|data: &[u8]| {
    let _ = Response::from_bytes(data);
});


================================================
FILE: dns/src/lib.rs
================================================
#![warn(deprecated_in_future)]
#![warn(future_incompatible)]
#![warn(missing_copy_implementations)]
#![warn(missing_docs)]
#![warn(nonstandard_style)]
#![warn(rust_2018_compatibility)]
#![warn(rust_2018_idioms)]
#![warn(single_use_lifetimes)]
#![warn(trivial_casts, trivial_numeric_casts)]
#![warn(unused)]

#![warn(clippy::all, clippy::pedantic)]
#![allow(clippy::doc_markdown)]
#![allow(clippy::len_without_is_empty)]
#![allow(clippy::missing_errors_doc)]
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::must_use_candidate)]
#![allow(clippy::non_ascii_literal)]
#![allow(clippy::redundant_else)]
#![allow(clippy::struct_excessive_bools)]
#![allow(clippy::upper_case_acronyms)]
#![allow(clippy::wildcard_imports)]

#![deny(clippy::cast_possible_truncation)]
#![deny(clippy::cast_lossless)]
#![deny(clippy::cast_possible_wrap)]
#![deny(clippy::cast_sign_loss)]
#![deny(unsafe_code)]


//! The DNS crate is the ‘library’ part of dog. It implements the DNS
//! protocol: creating and decoding packets from their byte structure.


mod types;
pub use self::types::*;

mod strings;
pub use self::strings::Labels;

mod wire;
pub use self::wire::{Wire, WireError, MandatedLength};

pub mod record;


================================================
FILE: dns/src/record/a.rs
================================================
use std::net::Ipv4Addr;

use log::*;

use crate::wire::*;


/// An **A** record type, which contains an `Ipv4Address`.
///
/// # References
///
/// - [RFC 1035 §3.4.1](https://tools.ietf.org/html/rfc1035) — Domain Names,
///   Implementation and Specification (November 1987)
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct A {

    /// The IPv4 address contained in the packet.
    pub address: Ipv4Addr,
}

impl Wire for A {
    const NAME: &'static str = "A";
    const RR_TYPE: u16 = 1;

    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
    fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
        if stated_length != 4 {
            warn!("Length is incorrect (record length {:?}, but should be four)", stated_length);
            let mandated_length = MandatedLength::Exactly(4);
            return Err(WireError::WrongRecordLength { stated_length, mandated_length });
        }

        let mut buf = [0_u8; 4];
        c.read_exact(&mut buf)?;

        let address = Ipv4Addr::from(buf);
        trace!("Parsed IPv4 address -> {:?}", address);

        Ok(Self { address })
    }
}


#[cfg(test)]
mod test {
    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn parses() {
        let buf = &[
            0x7F, 0x00, 0x00, 0x01,  // IPv4 address
        ];

        assert_eq!(A::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   A { address: Ipv4Addr::new(127, 0, 0, 1) });
    }

    #[test]
    fn record_too_short() {
        let buf = &[
            0x7F, 0x00, 0x00,  // Too short IPv4 address
        ];

        assert_eq!(A::read(buf.len() as _, &mut Cursor::new(buf)),
                   Err(WireError::WrongRecordLength { stated_length: 3, mandated_length: MandatedLength::Exactly(4) }));
    }

    #[test]
    fn record_too_long() {
        let buf = &[
            0x7F, 0x00, 0x00, 0x00,  // IPv4 address
            0x01,  // Unexpected extra byte
        ];

        assert_eq!(A::read(buf.len() as _, &mut Cursor::new(buf)),
                   Err(WireError::WrongRecordLength { stated_length: 5, mandated_length: MandatedLength::Exactly(4) }));
    }

    #[test]
    fn record_empty() {
        assert_eq!(A::read(0, &mut Cursor::new(&[])),
                   Err(WireError::WrongRecordLength { stated_length: 0, mandated_length: MandatedLength::Exactly(4) }));
    }

    #[test]
    fn buffer_ends_abruptly() {
        let buf = &[
            0x7F, 0x00,  // Half an IPv4 address
        ];

        assert_eq!(A::read(4, &mut Cursor::new(buf)),
                   Err(WireError::IO));
    }
}


================================================
FILE: dns/src/record/aaaa.rs
================================================
use std::net::Ipv6Addr;

use log::*;

use crate::wire::*;


/// A **AAAA** record, which contains an `Ipv6Address`.
///
/// # References
///
/// - [RFC 3596](https://tools.ietf.org/html/rfc3596) — DNS Extensions to
///   Support IP Version 6 (October 2003)
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct AAAA {

    /// The IPv6 address contained in the packet.
    pub address: Ipv6Addr,
}

impl Wire for AAAA {
    const NAME: &'static str = "AAAA";
    const RR_TYPE: u16 = 28;

    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
    fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
        if stated_length != 16 {
            warn!("Length is incorrect (stated length {:?}, but should be sixteen)", stated_length);
            let mandated_length = MandatedLength::Exactly(16);
            return Err(WireError::WrongRecordLength { stated_length, mandated_length });
        }

        let mut buf = [0_u8; 16];
        c.read_exact(&mut buf)?;

        let address = Ipv6Addr::from(buf);
        trace!("Parsed IPv6 address -> {:#x?}", address);

        Ok(Self { address })
    }
}


#[cfg(test)]
mod test {
    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn parses() {
        let buf = &[
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // IPv6 address
        ];

        assert_eq!(AAAA::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   AAAA { address: Ipv6Addr::new(0,0,0,0,0,0,0,0) });
    }

    #[test]
    fn record_too_long() {
        let buf = &[
            0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
            0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,  // IPv6 address
            0x09,  // Unexpected extra byte
        ];

        assert_eq!(AAAA::read(buf.len() as _, &mut Cursor::new(buf)),
                   Err(WireError::WrongRecordLength { stated_length: 17, mandated_length: MandatedLength::Exactly(16) }));
    }

    #[test]
    fn record_too_short() {
        let buf = &[
            0x05, 0x05, 0x05, 0x05, 0x05,  // Five arbitrary bytes
        ];

        assert_eq!(AAAA::read(buf.len() as _, &mut Cursor::new(buf)),
                   Err(WireError::WrongRecordLength { stated_length: 5, mandated_length: MandatedLength::Exactly(16) }));
    }

    #[test]
    fn record_empty() {
        assert_eq!(AAAA::read(0, &mut Cursor::new(&[])),
                   Err(WireError::WrongRecordLength { stated_length: 0, mandated_length: MandatedLength::Exactly(16) }));
    }

    #[test]
    fn buffer_ends_abruptly() {
        let buf = &[
            0x05, 0x05, 0x05, 0x05, 0x05,  // Five arbitrary bytes
        ];

        assert_eq!(AAAA::read(16, &mut Cursor::new(buf)),
                   Err(WireError::IO));
    }
}


================================================
FILE: dns/src/record/caa.rs
================================================
use log::*;

use crate::wire::*;


/// A **CAA** _(certification authority authorization)_ record. These allow
/// domain names to specify which Certificate Authorities are allowed to issue
/// certificates for the domain.
///
/// # References
///
/// - [RFC 6844](https://tools.ietf.org/html/rfc6844) — DNS Certification
///   Authority Authorization Resource Record (January 2013)
#[derive(PartialEq, Debug)]
pub struct CAA {

    /// Whether this record is marked as “critical” or not.
    pub critical: bool,

    /// The “tag” part of the CAA record.
    pub tag: Box<[u8]>,

    /// The “value” part of the CAA record.
    pub value: Box<[u8]>,
}

impl Wire for CAA {
    const NAME: &'static str = "CAA";
    const RR_TYPE: u16 = 257;

    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
    fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {

        // flags
        let flags = c.read_u8()?;
        trace!("Parsed flags -> {:#08b}", flags);

        let has_bit = |bit| { flags & bit == bit };
        let critical = has_bit(0b_1000_0000);
        trace!("Parsed critical flag -> {:?}", critical);

        // tag
        let tag_length = c.read_u8()?;
        trace!("Parsed tag length -> {:?}", tag_length);

        let mut tag = vec![0_u8; usize::from(tag_length)].into_boxed_slice();
        c.read_exact(&mut tag)?;
        trace!("Parsed tag -> {:?}", String::from_utf8_lossy(&tag));

        // value
        let remaining_length = stated_length.saturating_sub(u16::from(tag_length)).saturating_sub(2);
        trace!("Remaining length -> {:?}", remaining_length);

        let mut value = vec![0_u8; usize::from(remaining_length)].into_boxed_slice();
        c.read_exact(&mut value)?;
        trace!("Parsed value -> {:?}", String::from_utf8_lossy(&value));

        Ok(Self { critical, tag, value })
    }
}


#[cfg(test)]
mod test {
    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn parses_non_critical() {
        let buf = &[
            0x00,  // flags (all unset)
            0x09,  // tag length
            0x69, 0x73, 0x73, 0x75, 0x65, 0x77, 0x69, 0x6c, 0x64,  // tag
            0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74,  // value
        ];

        assert_eq!(CAA::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   CAA {
                       critical: false,
                       tag: Box::new(*b"issuewild"),
                       value: Box::new(*b"entrust.net"),
                   });
    }

    #[test]
    fn parses_critical() {
        let buf = &[
            0x80,  // flags (critical bit set)
            0x09,  // tag length
            0x69, 0x73, 0x73, 0x75, 0x65, 0x77, 0x69, 0x6c, 0x64,  // tag
            0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74,  // value
        ];

        assert_eq!(CAA::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   CAA {
                       critical: true,
                       tag: Box::new(*b"issuewild"),
                       value: Box::new(*b"entrust.net"),
                   });
    }

    #[test]
    fn ignores_other_flags() {
        let buf = &[
            0x7F,  // flags (all except critical bit set)
            0x01,  // tag length
            0x65,  // tag
            0x45,  // value
        ];

        assert_eq!(CAA::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   CAA {
                       critical: false,
                       tag: Box::new(*b"e"),
                       value: Box::new(*b"E"),
                   });
    }

    #[test]
    fn record_empty() {
        assert_eq!(CAA::read(0, &mut Cursor::new(&[])),
                   Err(WireError::IO));
    }

    #[test]
    fn buffer_ends_abruptly() {
        let buf = &[
            0x00,  // flags
        ];

        assert_eq!(CAA::read(23, &mut Cursor::new(buf)),
                   Err(WireError::IO));
    }
}


================================================
FILE: dns/src/record/cname.rs
================================================
use log::*;

use crate::strings::{Labels, ReadLabels};
use crate::wire::*;


/// A **CNAME** _(canonical name)_ record, which aliases one domain to another.
///
/// # References
///
/// - [RFC 1035 §3.3.1](https://tools.ietf.org/html/rfc1035) — Domain Names,
///   Implementation and Specification (November 1987)
#[derive(PartialEq, Debug)]
pub struct CNAME {

    /// The domain name that this CNAME record is responding with.
    pub domain: Labels,
}

impl Wire for CNAME {
    const NAME: &'static str = "CNAME";
    const RR_TYPE: u16 = 5;

    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
    fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
        let (domain, domain_length) = c.read_labels()?;
        trace!("Parsed domain -> {:?}", domain);

        if stated_length == domain_length {
            trace!("Length is correct");
            Ok(Self { domain })
        }
        else {
            warn!("Length is incorrect (stated length {:?}, domain length {:?})", stated_length, domain_length);
            Err(WireError::WrongLabelLength { stated_length, length_after_labels: domain_length })
        }
    }
}


#[cfg(test)]
mod test {
    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn parses() {
        let buf = &[
            0x05, 0x62, 0x73, 0x61, 0x67, 0x6f, 0x02, 0x6d, 0x65,  // domain
            0x00,  // domain terminator
        ];

        assert_eq!(CNAME::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   CNAME {
                       domain: Labels::encode("bsago.me").unwrap(),
                   });
    }

    #[test]
    fn incorrect_record_length() {
        let buf = &[
            0x03, 0x65, 0x66, 0x67,  // domain
            0x00,  // domain terminator
        ];

        assert_eq!(CNAME::read(6, &mut Cursor::new(buf)),
                   Err(WireError::WrongLabelLength { stated_length: 6, length_after_labels: 5 }));
    }

    #[test]
    fn record_empty() {
        assert_eq!(CNAME::read(0, &mut Cursor::new(&[])),
                   Err(WireError::IO));
    }

    #[test]
    fn buffer_ends_abruptly() {
        let buf = &[
            0x05, 0x62, 0x73,  // the stard of a string
        ];

        assert_eq!(CNAME::read(23, &mut Cursor::new(buf)),
                   Err(WireError::IO));
    }
}



================================================
FILE: dns/src/record/eui48.rs
================================================
use log::*;

use crate::wire::*;


/// A **EUI48** record, which holds a six-octet (48-bit) Extended Unique
/// Identifier. These identifiers can be used as MAC addresses.
///
/// # References
///
/// - [RFC 7043](https://tools.ietf.org/html/rfc7043) — Resource Records for
///   EUI-48 and EUI-64 Addresses in the DNS (October 2013)
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct EUI48 {

    /// The six octets that make up the identifier.
    pub octets: [u8; 6],
}

impl Wire for EUI48 {
    const NAME: &'static str = "EUI48";
    const RR_TYPE: u16 = 108;

    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
    fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
        if stated_length != 6 {
            warn!("Length is incorrect (record length {:?}, but should be six)", stated_length);
            let mandated_length = MandatedLength::Exactly(6);
            return Err(WireError::WrongRecordLength { stated_length, mandated_length });
        }

        let mut octets = [0_u8; 6];
        c.read_exact(&mut octets)?;
        trace!("Parsed 6-byte address -> {:#x?}", octets);

        Ok(Self { octets })
    }
}


impl EUI48 {

    /// Returns this EUI as hexadecimal numbers, separated by dashes.
    pub fn formatted_address(self) -> String {
        format!("{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}",
                self.octets[0], self.octets[1], self.octets[2],
                self.octets[3], self.octets[4], self.octets[5])
    }
}


#[cfg(test)]
mod test {
    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn parses() {
        let buf = &[
            0x00, 0x7F, 0x23, 0x12, 0x34, 0x56,  // identifier
        ];

        assert_eq!(EUI48::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   EUI48 { octets: [ 0x00, 0x7F, 0x23, 0x12, 0x34, 0x56 ] });
    }

    #[test]
    fn record_too_short() {
        let buf = &[
            0x00, 0x7F, 0x23,  // a mere OUI
        ];

        assert_eq!(EUI48::read(buf.len() as _, &mut Cursor::new(buf)),
                   Err(WireError::WrongRecordLength { stated_length: 3, mandated_length: MandatedLength::Exactly(6) }));
    }

    #[test]
    fn record_too_long() {
        let buf = &[
            0x00, 0x7F, 0x23, 0x12, 0x34, 0x56,  // identifier
            0x01,  // an unexpected extra byte
        ];

        assert_eq!(EUI48::read(buf.len() as _, &mut Cursor::new(buf)),
                   Err(WireError::WrongRecordLength { stated_length: 7, mandated_length: MandatedLength::Exactly(6) }));
    }

    #[test]
    fn record_empty() {
        assert_eq!(EUI48::read(0, &mut Cursor::new(&[])),
                   Err(WireError::WrongRecordLength { stated_length: 0, mandated_length: MandatedLength::Exactly(6) }));
    }

    #[test]
    fn buffer_ends_abruptly() {
        let buf = &[
            0x00, 0x7F, 0x23,  // a mere OUI
        ];

        assert_eq!(EUI48::read(6, &mut Cursor::new(buf)),
                   Err(WireError::IO));
    }

    #[test]
    fn hex_rep() {
        let record = EUI48 { octets: [ 0x00, 0x7F, 0x23, 0x12, 0x34, 0x56 ] };

        assert_eq!(record.formatted_address(),
                   "00-7f-23-12-34-56");
    }
}


================================================
FILE: dns/src/record/eui64.rs
================================================
use log::*;

use crate::wire::*;


/// A **EUI64** record, which holds an eight-octet (64-bit) Extended Unique
/// Identifier.
///
/// # References
///
/// - [RFC 7043](https://tools.ietf.org/html/rfc7043) — Resource Records for
///   EUI-48 and EUI-64 Addresses in the DNS (October 2013)
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct EUI64 {

    /// The eight octets that make up the identifier.
    pub octets: [u8; 8],
}

impl Wire for EUI64 {
    const NAME: &'static str = "EUI64";
    const RR_TYPE: u16 = 109;

    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
    fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
        if stated_length != 8 {
            warn!("Length is incorrect (record length {:?}, but should be eight)", stated_length);
            let mandated_length = MandatedLength::Exactly(8);
            return Err(WireError::WrongRecordLength { stated_length, mandated_length });
        }

        let mut octets = [0_u8; 8];
        c.read_exact(&mut octets)?;
        trace!("Parsed 8-byte address -> {:#x?}", octets);

        Ok(Self { octets })
    }
}


impl EUI64 {

    /// Returns this EUI as hexadecimal numbers, separated by dashes.
    pub fn formatted_address(self) -> String {
        format!("{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}",
                self.octets[0], self.octets[1], self.octets[2], self.octets[3],
                self.octets[4], self.octets[5], self.octets[6], self.octets[7])
    }
}


#[cfg(test)]
mod test {
    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn parses() {
        let buf = &[
            0x00, 0x7F, 0x23, 0x12, 0x34, 0x56, 0x78, 0x90,  // identifier
        ];

        assert_eq!(EUI64::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   EUI64 { octets: [ 0x00, 0x7F, 0x23, 0x12, 0x34, 0x56, 0x78, 0x90 ] });
    }

    #[test]
    fn record_too_short() {
        let buf = &[
            0x00, 0x7F, 0x23,  // a mere OUI
        ];

        assert_eq!(EUI64::read(buf.len() as _, &mut Cursor::new(buf)),
                   Err(WireError::WrongRecordLength { stated_length: 3, mandated_length: MandatedLength::Exactly(8) }));
    }

    #[test]
    fn record_too_long() {
        let buf = &[
            0x00, 0x7F, 0x23, 0x12, 0x34, 0x56, 0x78, 0x90,  // identifier
            0x01,  // an unexpected extra byte
        ];

        assert_eq!(EUI64::read(buf.len() as _, &mut Cursor::new(buf)),
                   Err(WireError::WrongRecordLength { stated_length: 9, mandated_length: MandatedLength::Exactly(8) }));
    }

    #[test]
    fn record_empty() {
        assert_eq!(EUI64::read(0, &mut Cursor::new(&[])),
                   Err(WireError::WrongRecordLength { stated_length: 0, mandated_length: MandatedLength::Exactly(8) }));
    }

    #[test]
    fn buffer_ends_abruptly() {
        let buf = &[
            0x00, 0x7F, 0x23,  // a mere OUI
        ];

        assert_eq!(EUI64::read(8, &mut Cursor::new(buf)),
                   Err(WireError::IO));
    }

    #[test]
    fn hex_rep() {
        let record = EUI64 { octets: [ 0x00, 0x7F, 0x23, 0x12, 0x34, 0x56, 0x78, 0x90 ] };

        assert_eq!(record.formatted_address(),
                   "00-7f-23-12-34-56-78-90");
    }
}


================================================
FILE: dns/src/record/hinfo.rs
================================================
use log::*;

use crate::wire::*;


/// A (an?) **HINFO** _(host information)_ record, which contains the CPU and
/// OS information about a host.
///
/// It also gets used as the response for an `ANY` query, if it is blocked.
///
/// # References
///
/// - [RFC 1035 §3.3.2](https://tools.ietf.org/html/rfc1035) — Domain Names,
///   Implementation and Specification (November 1987)
/// - [RFC 8482 §6](https://tools.ietf.org/html/rfc8482#section-6) — Providing
///   Minimal-Sized Responses to DNS Queries That Have QTYPE=ANY (January 2019)
#[derive(PartialEq, Debug)]
pub struct HINFO {

    /// The CPU field, specifying the CPU type.
    pub cpu: Box<[u8]>,

    /// The OS field, specifying the operating system.
    pub os: Box<[u8]>,
}

impl Wire for HINFO {
    const NAME: &'static str = "HINFO";
    const RR_TYPE: u16 = 13;

    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
    fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {

        let cpu_length = c.read_u8()?;
        trace!("Parsed CPU length -> {:?}", cpu_length);

        let mut cpu = vec![0_u8; usize::from(cpu_length)].into_boxed_slice();
        c.read_exact(&mut cpu)?;
        trace!("Parsed CPU -> {:?}", String::from_utf8_lossy(&cpu));

        let os_length = c.read_u8()?;
        trace!("Parsed OS length -> {:?}", os_length);

        let mut os = vec![0_u8; usize::from(os_length)].into_boxed_slice();
        c.read_exact(&mut os)?;
        trace!("Parsed OS -> {:?}", String::from_utf8_lossy(&os));

        let length_after_labels = 1 + u16::from(cpu_length) + 1 + u16::from(os_length);
        if stated_length == length_after_labels {
            trace!("Length is correct");
            Ok(Self { cpu, os })
        }
        else {
            warn!("Length is incorrect (stated length {:?}, cpu plus length {:?}", stated_length, length_after_labels);
            Err(WireError::WrongLabelLength { stated_length, length_after_labels })
        }
    }
}


#[cfg(test)]
mod test {
    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn parses() {
        let buf = &[
            0x0e,  // cpu length
            0x73, 0x6f, 0x6d, 0x65, 0x2d, 0x6b, 0x69, 0x6e, 0x64, 0x61, 0x2d,
            0x63, 0x70, 0x75,  // cpu
            0x0d,  // os length
            0x73, 0x6f, 0x6d, 0x65, 0x2d, 0x6b, 0x69, 0x6e, 0x64, 0x61, 0x2d,
            0x6f, 0x73,  // os
        ];

        assert_eq!(HINFO::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   HINFO {
                       cpu: Box::new(*b"some-kinda-cpu"),
                       os: Box::new(*b"some-kinda-os"),
                   });
    }

    #[test]
    fn incorrect_record_length() {
        let buf = &[
            0x03,  // cpu length
            0x65, 0x66, 0x67,  // cpu
            0x03,  // os length
            0x68, 0x69, 0x70,  // os
        ];

        assert_eq!(HINFO::read(6, &mut Cursor::new(buf)),
                   Err(WireError::WrongLabelLength { stated_length: 6, length_after_labels: 8 }));
    }

    #[test]
    fn record_empty() {
        assert_eq!(HINFO::read(0, &mut Cursor::new(&[])),
                   Err(WireError::IO));
    }

    #[test]
    fn buffer_ends_abruptly() {
        let buf = &[
            0x14, 0x0A, 0x0B, 0x0C,  // 32-bit CPU
        ];

        assert_eq!(HINFO::read(23, &mut Cursor::new(buf)),
                   Err(WireError::IO));
    }
}


================================================
FILE: dns/src/record/loc.rs
================================================
use std::fmt;

use log::*;

use crate::wire::*;


/// A **LOC** _(location)_ record, which points to a location on Earth using
/// its latitude, longitude, and altitude.
///
/// # References
///
/// - [RFC 1876](https://tools.ietf.org/html/rfc1876) — A Means for Expressing
///   Location Information in the Domain Name System (January 1996)
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct LOC {

    /// The diameter of a sphere enclosing the entity at the location, as a
    /// measure of its size, measured in centimetres.
    pub size: Size,

    /// The diameter of the “circle of error” that this location could be in,
    /// measured in centimetres.
    pub horizontal_precision: u8,

    /// The amount of vertical space that this location could be in, measured
    /// in centimetres.
    pub vertical_precision: u8,

    /// The latitude of the centre of the sphere. If `None`, the packet
    /// parses, but the position is out of range.
    pub latitude: Option<Position>,

    /// The longitude of the centre of the sphere. If `None`, the packet
    /// parses, but the position is out of range.
    pub longitude: Option<Position>,

    /// The altitude of the centre of the sphere, measured in centimetres
    /// above a base of 100,000 metres below the GPS reference spheroid.
    pub altitude: Altitude,
}

/// A measure of size, in centimetres, represented by a base and an exponent.
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct Size {
    base: u8,
    power_of_ten: u8,
}

/// A position on one of the world’s axes.
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct Position {
    degrees: u32,
    arcminutes: u32,
    arcseconds: u32,
    milliarcseconds: u32,
    direction: Direction,
}

/// A position on the vertical axis.
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct Altitude {
    metres: i64,
    centimetres: i64,
}

/// One of the directions a position could be in, relative to the equator or
/// prime meridian.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum Direction {
    North,
    East,
    South,
    West,
}

impl Wire for LOC {
    const NAME: &'static str = "LOC";
    const RR_TYPE: u16 = 29;

    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
    fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
        let version = c.read_u8()?;
        trace!("Parsed version -> {:?}", version);

        if version != 0 {
            return Err(WireError::WrongVersion {
                stated_version: version,
                maximum_supported_version: 0,
            });
        }

        if stated_length != 16 {
            let mandated_length = MandatedLength::Exactly(16);
            return Err(WireError::WrongRecordLength { stated_length, mandated_length });
        }

        let size_bits = c.read_u8()?;
        let size = Size::from_u8(size_bits);
        trace!("Parsed size -> {:#08b} ({})", size_bits, size);

        let horizontal_precision = c.read_u8()?;
        trace!("Parsed horizontal precision -> {:?}", horizontal_precision);

        let vertical_precision = c.read_u8()?;
        trace!("Parsed vertical precision -> {:?}", vertical_precision);

        let latitude_num = c.read_u32::<BigEndian>()?;
        let latitude = Position::from_u32(latitude_num, true);
        trace!("Parsed latitude -> {:?} ({:?})", latitude_num, latitude);

        let longitude_num = c.read_u32::<BigEndian>()?;
        let longitude = Position::from_u32(longitude_num, false);
        trace!("Parsed longitude -> {:?} ({:?})", longitude_num, longitude);

        let altitude_num = c.read_u32::<BigEndian>()?;
        let altitude = Altitude::from_u32(altitude_num);
        trace!("Parsed altitude -> {:?} ({:})", altitude_num, altitude);

        Ok(Self {
            size, horizontal_precision, vertical_precision, latitude, longitude, altitude,
        })
    }
}

impl Size {

    /// Converts a number into the size it represents. To allow both small and
    /// large sizes, the input octet is split into two four-bit sizes, one the
    /// base, and one the power of ten exponent.
    fn from_u8(input: u8) -> Self {
        let base = input >> 4;
        let power_of_ten = input & 0b_0000_1111;
        Self { base, power_of_ten }
    }
}

impl Position {

    /// Converts a number into the position it represents. The input number is
    /// measured in thousandths of an arcsecond (milliarcseconds), with 2^31
    /// as the equator or prime meridian.
    ///
    /// Returns `None` if the input is out of range, meaning it would wrap
    /// around to another half of the Earth once or more.
    fn from_u32(mut input: u32, vertical: bool) -> Option<Self> {
        let max_for_direction = if vertical { 90 } else { 180 };
        let limit = 1000 * 60 * 60 * max_for_direction;

        if input < (0x_8000_0000 - limit) || input > (0x_8000_0000 + limit) {
            // Input is out of range
            None
        }
        else if input >= 0x_8000_0000 {
            // Input is north or east, so de-relativise it and divide into segments
            input -= 0x_8000_0000;
            let milliarcseconds = input % 1000;
            let total_arcseconds = input / 1000;

            let arcseconds = total_arcseconds % 60;
            let total_arcminutes = total_arcseconds / 60;

            let arcminutes = total_arcminutes % 60;
            let degrees = total_arcminutes / 60;

            let direction = if vertical { Direction::North }
                                   else { Direction::East };

            Some(Self { degrees, arcminutes, arcseconds, milliarcseconds, direction })
        }
        else {
            // Input is south or west, so do the calculations for
            let mut pos = Self::from_u32(input + (0x_8000_0000_u32 - input) * 2, vertical)?;

            pos.direction = if vertical { Direction::South }
                                   else { Direction::West };
            Some(pos)
        }
    }
}

impl Altitude {
    fn from_u32(input: u32) -> Self {
        let mut input = i64::from(input);
        input -= 10_000_000;  // 100,000m
        let metres = input / 100;
        let centimetres = input % 100;
        Self { metres, centimetres }
    }
}


impl fmt::Display for Size {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}e{}", self.base, self.power_of_ten)
    }
}

impl fmt::Display for Position {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}°{}′{}",
            self.degrees,
            self.arcminutes,
            self.arcseconds,
        )?;

        if self.milliarcseconds != 0 {
            write!(f, ".{:03}", self.milliarcseconds)?;
        }

        write!(f, "″ {}", self.direction)
    }
}

impl fmt::Display for Direction {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::North  => write!(f, "N"),
            Self::East   => write!(f, "E"),
            Self::South  => write!(f, "S"),
            Self::West   => write!(f, "W"),
        }
    }
}

impl fmt::Display for Altitude {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // Usually there’s a space between the number and the unit, but
        // spaces are already used to delimit segments in the record summary
        if self.centimetres == 0 {
            write!(f, "{}m", self.metres)
        }
        else {
            write!(f, "{}.{:02}m", self.metres, self.centimetres)
        }
    }
}


#[cfg(test)]
mod test {
    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn parses() {
        let buf = &[
            0x00,  // version
            0x32,  // size,
            0x00,  // horizontal precision
            0x00,  // vertical precision
            0x8b, 0x0d, 0x2c, 0x8c,  // latitude
            0x7f, 0xf8, 0xfc, 0xa5,  // longitude
            0x00, 0x98, 0x96, 0x80,  // altitude
        ];

        assert_eq!(LOC::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   LOC {
                       size: Size { base: 3, power_of_ten: 2 },
                       horizontal_precision: 0,
                       vertical_precision: 0,
                       latitude:  Position::from_u32(0x_8b_0d_2c_8c, true),
                       longitude: Position::from_u32(0x_7f_f8_fc_a5, false),
                       altitude:  Altitude::from_u32(0x_00_98_96_80),
                   });
    }

    #[test]
    fn record_too_short() {
        let buf = &[
            0x00,  // version
            0x00,  // size
        ];

        assert_eq!(LOC::read(buf.len() as _, &mut Cursor::new(buf)),
                   Err(WireError::WrongRecordLength { stated_length: 2, mandated_length: MandatedLength::Exactly(16) }));
    }

    #[test]
    fn record_too_long() {
        let buf = &[
            0x00,  // version
            0x32,  // size,
            0x00,  // horizontal precision
            0x00,  // vertical precision
            0x8b, 0x0d, 0x2c, 0x8c,  // latitude
            0x7f, 0xf8, 0xfc, 0xa5,  // longitude
            0x00, 0x98, 0x96, 0x80,  // altitude
            0x12, 0x34, 0x56,  // some other stuff
        ];

        assert_eq!(LOC::read(buf.len() as _, &mut Cursor::new(buf)),
                   Err(WireError::WrongRecordLength { stated_length: 19, mandated_length: MandatedLength::Exactly(16) }));
    }

    #[test]
    fn more_recent_version() {
        let buf = &[
            0x80,  // version
            0x12, 0x34, 0x56,  // some data in an unknown format
        ];

        assert_eq!(LOC::read(buf.len() as _, &mut Cursor::new(buf)),
                   Err(WireError::WrongVersion { stated_version: 128, maximum_supported_version: 0 }));
    }

    #[test]
    fn record_empty() {
        assert_eq!(LOC::read(0, &mut Cursor::new(&[])),
                   Err(WireError::IO));
    }

    #[test]
    fn buffer_ends_abruptly() {
        let buf = &[
            0x00,  // version
        ];

        assert_eq!(LOC::read(16, &mut Cursor::new(buf)),
                   Err(WireError::IO));
    }
}


#[cfg(test)]
mod size_test {
    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn zeroes() {
        assert_eq!(Size::from_u8(0b_0000_0000).to_string(),
                   String::from("0e0"));
    }

    #[test]
    fn ones() {
        assert_eq!(Size::from_u8(0b_0001_0001).to_string(),
                   String::from("1e1"));
    }

    #[test]
    fn schfourteen_teen() {
        assert_eq!(Size::from_u8(0b_1110_0011).to_string(),
                   String::from("14e3"));
    }

    #[test]
    fn ones_but_bits_this_time() {
        assert_eq!(Size::from_u8(0b_1111_1111).to_string(),
                   String::from("15e15"));
    }
}


#[cfg(test)]
mod position_test {
    use super::*;
    use pretty_assertions::assert_eq;

    // centre line tests

    #[test]
    fn meridian() {
        assert_eq!(Position::from_u32(0x_8000_0000, false).unwrap().to_string(),
                   String::from("0°0′0″ E"));
    }

    #[test]
    fn meridian_plus_one() {
        assert_eq!(Position::from_u32(0x_8000_0000 + 1, false).unwrap().to_string(),
                   String::from("0°0′0.001″ E"));
    }

    #[test]
    fn meridian_minus_one() {
        assert_eq!(Position::from_u32(0x_8000_0000 - 1, false).unwrap().to_string(),
                   String::from("0°0′0.001″ W"));
    }

    #[test]
    fn equator() {
        assert_eq!(Position::from_u32(0x_8000_0000, true).unwrap().to_string(),
                   String::from("0°0′0″ N"));
    }

    #[test]
    fn equator_plus_one() {
        assert_eq!(Position::from_u32(0x_8000_0000 + 1, true).unwrap().to_string(),
                   String::from("0°0′0.001″ N"));
    }

    #[test]
    fn equator_minus_one() {
        assert_eq!(Position::from_u32(0x_8000_0000 - 1, true).unwrap().to_string(),
                   String::from("0°0′0.001″ S"));
    }

    // arbitrary value tests

    #[test]
    fn some_latitude() {
        assert_eq!(Position::from_u32(2332896396, true).unwrap().to_string(),
                   String::from("51°30′12.748″ N"));
    }

    #[test]
    fn some_longitude() {
        assert_eq!(Position::from_u32(2147024037, false).unwrap().to_string(),
                   String::from("0°7′39.611″ W"));
    }

    // limit tests

    #[test]
    fn the_north_pole() {
        assert_eq!(Position::from_u32(0x8000_0000 + (1000 * 60 * 60 * 90), true).unwrap().to_string(),
                   String::from("90°0′0″ N"));
    }

    #[test]
    fn the_north_pole_plus_one() {
        assert_eq!(Position::from_u32(0x8000_0000 + (1000 * 60 * 60 * 90) + 1, true),
                   None);
    }

    #[test]
    fn the_south_pole() {
        assert_eq!(Position::from_u32(0x8000_0000 - (1000 * 60 * 60 * 90), true).unwrap().to_string(),
                   String::from("90°0′0″ S"));
    }

    #[test]
    fn the_south_pole_minus_one() {
        assert_eq!(Position::from_u32(0x8000_0000 - (1000 * 60 * 60 * 90) - 1, true),
                   None);
    }

    #[test]
    fn the_far_east() {
        assert_eq!(Position::from_u32(0x8000_0000 + (1000 * 60 * 60 * 180), false).unwrap().to_string(),
                   String::from("180°0′0″ E"));
    }

    #[test]
    fn the_far_east_plus_one() {
        assert_eq!(Position::from_u32(0x8000_0000 + (1000 * 60 * 60 * 180) + 1, false),
                   None);
    }

    #[test]
    fn the_far_west() {
        assert_eq!(Position::from_u32(0x8000_0000 - (1000 * 60 * 60 * 180), false).unwrap().to_string(),
                   String::from("180°0′0″ W"));
    }

    #[test]
    fn the_far_west_minus_one() {
        assert_eq!(Position::from_u32(0x8000_0000 - (1000 * 60 * 60 * 180) - 1, false),
                   None);
    }
}


#[cfg(test)]
mod altitude_test {
    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn base_level() {
        assert_eq!(Altitude::from_u32(10000000).to_string(),
                   String::from("0m"));
    }

    #[test]
    fn up_high() {
        assert_eq!(Altitude::from_u32(20000000).to_string(),
                   String::from("100000m"));
    }

    #[test]
    fn down_low() {
        assert_eq!(Altitude::from_u32(0).to_string(),
                   String::from("-100000m"));
    }

    #[test]
    fn with_decimal() {
        assert_eq!(Altitude::from_u32(50505050).to_string(),
                   String::from("405050.50m"));
    }
}


================================================
FILE: dns/src/record/mod.rs
================================================
//! All the DNS record types, as well as how to parse each type.

use crate::wire::*;


mod a;
pub use self::a::A;

mod aaaa;
pub use self::aaaa::AAAA;

mod caa;
pub use self::caa::CAA;

mod cname;
pub use self::cname::CNAME;

mod eui48;
pub use self::eui48::EUI48;

mod eui64;
pub use self::eui64::EUI64;

mod hinfo;
pub use self::hinfo::HINFO;

mod loc;
pub use self::loc::LOC;

mod mx;
pub use self::mx::MX;

mod naptr;
pub use self::naptr::NAPTR;

mod ns;
pub use self::ns::NS;

mod openpgpkey;
pub use self::openpgpkey::OPENPGPKEY;

mod opt;
pub use self::opt::OPT;

mod ptr;
pub use self::ptr::PTR;

mod sshfp;
pub use self::sshfp::SSHFP;

mod soa;
pub use self::soa::SOA;

mod srv;
pub use self::srv::SRV;

mod tlsa;
pub use self::tlsa::TLSA;

mod txt;
pub use self::txt::TXT;

mod uri;
pub use self::uri::URI;


mod others;
pub use self::others::UnknownQtype;


/// A record that’s been parsed from a byte buffer.
#[derive(PartialEq, Debug)]
#[allow(missing_docs)]
pub enum Record {
    A(A),
    AAAA(AAAA),
    CAA(CAA),
    CNAME(CNAME),
    EUI48(EUI48),
    EUI64(EUI64),
    HINFO(HINFO),
    LOC(LOC),
    MX(MX),
    NAPTR(NAPTR),
    NS(NS),
    OPENPGPKEY(OPENPGPKEY),
    // OPT is not included here.
    PTR(PTR),
    SSHFP(SSHFP),
    SOA(SOA),
    SRV(SRV),
    TLSA(TLSA),
    TXT(TXT),
    URI(URI),

    /// A record with a type that we don’t recognise.
    Other {

        /// The number that’s meant to represent the record type.
        type_number: UnknownQtype,

        /// The undecodable bytes that were in this record.
        bytes: Vec<u8>,
    },
}


/// The type of a record that may or may not be one of the known ones. Has no
/// data associated with it other than what type of record it is.
#[derive(PartialEq, Debug, Copy, Clone)]
#[allow(missing_docs)]
pub enum RecordType {
    A,
    AAAA,
    CAA,
    CNAME,
    EUI48,
    EUI64,
    HINFO,
    LOC,
    MX,
    NAPTR,
    NS,
    OPENPGPKEY,
    PTR,
    SSHFP,
    SOA,
    SRV,
    TLSA,
    TXT,
    URI,

    /// A record type we don’t recognise.
    Other(UnknownQtype),
}

impl From<u16> for RecordType {
    fn from(type_number: u16) -> Self {
        macro_rules! try_record {
            ($record:tt) => {
                if $record::RR_TYPE == type_number {
                    return RecordType::$record;
                }
            }
        }

        try_record!(A);
        try_record!(AAAA);
        try_record!(CAA);
        try_record!(CNAME);
        try_record!(EUI48);
        try_record!(EUI64);
        try_record!(HINFO);
        try_record!(LOC);
        try_record!(MX);
        try_record!(NAPTR);
        try_record!(NS);
        try_record!(OPENPGPKEY);
        // OPT is handled separately
        try_record!(PTR);
        try_record!(SSHFP);
        try_record!(SOA);
        try_record!(SRV);
        try_record!(TLSA);
        try_record!(TXT);
        try_record!(URI);

        RecordType::Other(UnknownQtype::from(type_number))
    }
}


impl RecordType {

    /// Determines the record type with a given name, or `None` if none is
    /// known. Matches names case-insensitively.
    pub fn from_type_name(type_name: &str) -> Option<Self> {
        macro_rules! try_record {
            ($record:tt) => {
                if $record::NAME.eq_ignore_ascii_case(type_name) {
                    return Some(Self::$record);
                }
            }
        }

        try_record!(A);
        try_record!(AAAA);
        try_record!(CAA);
        try_record!(CNAME);
        try_record!(EUI48);
        try_record!(EUI64);
        try_record!(HINFO);
        try_record!(LOC);
        try_record!(MX);
        try_record!(NAPTR);
        try_record!(NS);
        try_record!(OPENPGPKEY);
        // OPT is elsewhere
        try_record!(PTR);
        try_record!(SSHFP);
        try_record!(SOA);
        try_record!(SRV);
        try_record!(TLSA);
        try_record!(TXT);
        try_record!(URI);

        UnknownQtype::from_type_name(type_name).map(Self::Other)
    }

    /// Returns the record type number associated with this record type.
    pub fn type_number(self) -> u16 {
        match self {
            Self::A           => A::RR_TYPE,
            Self::AAAA        => AAAA::RR_TYPE,
            Self::CAA         => CAA::RR_TYPE,
            Self::CNAME       => CNAME::RR_TYPE,
            Self::EUI48       => EUI48::RR_TYPE,
            Self::EUI64       => EUI64::RR_TYPE,
            Self::HINFO       => HINFO::RR_TYPE,
            Self::LOC         => LOC::RR_TYPE,
            Self::MX          => MX::RR_TYPE,
            Self::NAPTR       => NAPTR::RR_TYPE,
            Self::NS          => NS::RR_TYPE,
            Self::OPENPGPKEY  => OPENPGPKEY::RR_TYPE,
            // Wherefore art thou, OPT
            Self::PTR         => PTR::RR_TYPE,
            Self::SSHFP       => SSHFP::RR_TYPE,
            Self::SOA         => SOA::RR_TYPE,
            Self::SRV         => SRV::RR_TYPE,
            Self::TLSA        => TLSA::RR_TYPE,
            Self::TXT         => TXT::RR_TYPE,
            Self::URI         => URI::RR_TYPE,
            Self::Other(o)    => o.type_number(),
        }
    }
}

// This code is really repetitive, I know, I know


================================================
FILE: dns/src/record/mx.rs
================================================
use log::*;

use crate::strings::{Labels, ReadLabels};
use crate::wire::*;


/// An **MX** _(mail exchange)_ record, which contains the hostnames for mail
/// servers that handle mail sent to the domain.
///
/// # References
///
/// - [RFC 1035 §3.3.9](https://tools.ietf.org/html/rfc1035) — Domain Names,
///   Implementation and Specification (November 1987)
#[derive(PartialEq, Debug)]
pub struct MX {

    /// The preference that clients should give to this MX record amongst all
    /// that get returned.
    pub preference: u16,

    /// The domain name of the mail exchange server.
    pub exchange: Labels,
}

impl Wire for MX {
    const NAME: &'static str = "MX";
    const RR_TYPE: u16 = 15;

    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
    fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
        let preference = c.read_u16::<BigEndian>()?;
        trace!("Parsed preference -> {:?}", preference);

        let (exchange, exchange_length) = c.read_labels()?;
        trace!("Parsed exchange -> {:?}", exchange);

        let length_after_labels = 2 + exchange_length;
        if stated_length == length_after_labels {
            trace!("Length is correct");
            Ok(Self { preference, exchange })
        }
        else {
            warn!("Length is incorrect (stated length {:?}, preference plus exchange length {:?}", stated_length, length_after_labels);
            Err(WireError::WrongLabelLength { stated_length, length_after_labels })
        }
    }
}


#[cfg(test)]
mod test {
    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn parses() {
        let buf = &[
            0x00, 0x0A,  // preference
            0x05, 0x62, 0x73, 0x61, 0x67, 0x6f, 0x02, 0x6d, 0x65,  // exchange
            0x00,  // exchange terminator
        ];

        assert_eq!(MX::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   MX {
                       preference: 10,
                       exchange: Labels::encode("bsago.me").unwrap(),
                   });
    }

    #[test]
    fn incorrect_record_length() {
        let buf = &[
            0x00, 0x0A,  // preference
            0x03, 0x65, 0x66, 0x67,  // domain
            0x00,  // domain terminator
        ];

        assert_eq!(MX::read(6, &mut Cursor::new(buf)),
                   Err(WireError::WrongLabelLength { stated_length: 6, length_after_labels: 7 }));
    }

    #[test]
    fn record_empty() {
        assert_eq!(MX::read(0, &mut Cursor::new(&[])),
                   Err(WireError::IO));
    }

    #[test]
    fn buffer_ends_abruptly() {
        let buf = &[
            0x00, 0x0A,  // half a preference
        ];

        assert_eq!(MX::read(23, &mut Cursor::new(buf)),
                   Err(WireError::IO));
    }
}


================================================
FILE: dns/src/record/naptr.rs
================================================
use log::*;

use crate::strings::{Labels, ReadLabels};
use crate::wire::*;


/// A **NAPTR** _(naming authority pointer)_ record, which holds a rule for
/// the Dynamic Delegation Discovery System.
///
/// # References
///
/// - [RFC 3403](https://tools.ietf.org/html/rfc3403) — Dynamic Delegation
///   Discovery System (DDDS) Part Three: The Domain Name System (DNS) Database
///   (October 2002)
#[derive(PartialEq, Debug)]
pub struct NAPTR {

    /// The order in which NAPTR records must be processed.
    pub order: u16,

    /// The DDDS priority.
    pub preference: u16,

    /// A set of characters that control the rewriting and interpretation of
    /// the other fields.
    pub flags: Box<[u8]>,

    /// The service parameters applicable to this delegation path.
    pub service: Box<[u8]>,

    /// A regular expression that gets applied to a string in order to
    /// construct the next domain name to look up using the DDDS algorithm.
    pub regex: Box<[u8]>,

    /// The replacement domain name as part of the DDDS algorithm.
    pub replacement: Labels,
}

impl Wire for NAPTR {
    const NAME: &'static str = "NAPTR";
    const RR_TYPE: u16 = 35;

    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
    fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
        let order = c.read_u16::<BigEndian>()?;
        trace!("Parsed order -> {:?}", order);

        // preference
        let preference = c.read_u16::<BigEndian>()?;
        trace!("Parsed preference -> {:?}", preference);

        // flags
        let flags_length = c.read_u8()?;
        trace!("Parsed flags length -> {:?}", flags_length);

        let mut flags = vec![0_u8; usize::from(flags_length)].into_boxed_slice();
        c.read_exact(&mut flags)?;
        trace!("Parsed flags -> {:?}", String::from_utf8_lossy(&flags));

        // service
        let service_length = c.read_u8()?;
        trace!("Parsed service length -> {:?}", service_length);

        let mut service = vec![0_u8; usize::from(service_length)].into_boxed_slice();
        c.read_exact(&mut service)?;
        trace!("Parsed service -> {:?}", String::from_utf8_lossy(&service));

        // regex
        let regex_length = c.read_u8()?;
        trace!("Parsed regex length -> {:?}", regex_length);

        let mut regex = vec![0_u8; usize::from(regex_length)].into_boxed_slice();
        c.read_exact(&mut regex)?;
        trace!("Parsed regex -> {:?}", String::from_utf8_lossy(&regex));

        // replacement
        let (replacement, replacement_length) = c.read_labels()?;
        trace!("Parsed replacement -> {:?}", replacement);

        let length_after_labels = 2 + 2 +
            1 + u16::from(flags_length) + 1 + u16::from(service_length) +
            1 + u16::from(regex_length) + replacement_length;

        if stated_length == length_after_labels {
            Ok(Self { order, preference, flags, service, regex, replacement })
        }
        else {
            Err(WireError::WrongLabelLength { stated_length, length_after_labels })
        }
    }
}


#[cfg(test)]
mod test {
    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn parses() {
        let buf = &[
            0x00, 0x05,  // order
            0x00, 0x0a,  // preference
            0x01,  // flags length
            0x73,  // flags
            0x03,  // service length
            0x53, 0x52, 0x56,  // service
            0x0e,  // regex length
            0x5c, 0x64, 0x5c, 0x64, 0x3a, 0x5c, 0x64, 0x5c, 0x64, 0x3a, 0x5c,
            0x64, 0x5c, 0x64,  // regex
            0x0b, 0x73, 0x72, 0x76, 0x2d, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c,
            0x65, 0x06, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x03, 0x64, 0x6f,
            0x67, 0x00,  // replacement
        ];

        assert_eq!(NAPTR::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   NAPTR {
                       order: 5,
                       preference: 10,
                       flags: Box::new(*b"s"),
                       service: Box::new(*b"SRV"),
                       regex: Box::new(*b"\\d\\d:\\d\\d:\\d\\d"),
                       replacement: Labels::encode("srv-example.lookup.dog").unwrap(),
                   });
    }

    #[test]
    fn incorrect_length() {
        let buf = &[
            0x00, 0x05,  // order
            0x00, 0x0a,  // preference
            0x01,  // flags length
            0x73,  // flags
            0x03,  // service length
            0x53, 0x52, 0x56,  // service
            0x01,  // regex length
            0x64,  // regex,
            0x00,  // replacement
        ];

        assert_eq!(NAPTR::read(11, &mut Cursor::new(buf)),
                   Err(WireError::WrongLabelLength { stated_length: 11, length_after_labels: 13 }));
    }

    #[test]
    fn record_empty() {
        assert_eq!(NAPTR::read(0, &mut Cursor::new(&[])),
                   Err(WireError::IO));
    }

    #[test]
    fn buffer_ends_abruptly() {
        let buf = &[
            0x00, 0x0A,  // order
        ];

        assert_eq!(NAPTR::read(23, &mut Cursor::new(buf)),
                   Err(WireError::IO));
    }
}


================================================
FILE: dns/src/record/ns.rs
================================================
use log::*;

use crate::strings::{Labels, ReadLabels};
use crate::wire::*;


/// A **NS** _(name server)_ record, which is used to point domains to name
/// servers.
///
/// # References
///
/// - [RFC 1035 §3.3.11](https://tools.ietf.org/html/rfc1035) — Domain Names,
///   Implementation and Specification (November 1987)
#[derive(PartialEq, Debug)]
pub struct NS {

    /// The address of a nameserver that provides this DNS response.
    pub nameserver: Labels,
}

impl Wire for NS {
    const NAME: &'static str = "NS";
    const RR_TYPE: u16 = 2;

    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
    fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
        let (nameserver, nameserver_length) = c.read_labels()?;
        trace!("Parsed nameserver -> {:?}", nameserver);

        if stated_length == nameserver_length {
            trace!("Length is correct");
            Ok(Self { nameserver })
        }
        else {
            warn!("Length is incorrect (stated length {:?}, nameserver length {:?}", stated_length, nameserver_length);
            Err(WireError::WrongLabelLength { stated_length, length_after_labels: nameserver_length })
        }
    }
}


#[cfg(test)]
mod test {
    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn parses() {
        let buf = &[
            0x01, 0x61, 0x0c, 0x67, 0x74, 0x6c, 0x64, 0x2d, 0x73, 0x65, 0x72,
            0x76, 0x65, 0x72, 0x73, 0x03, 0x6e, 0x65, 0x74,  // nameserver
            0x00,  // nameserver terminator
        ];

        assert_eq!(NS::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   NS {
                       nameserver: Labels::encode("a.gtld-servers.net").unwrap(),
                   });
    }

    #[test]
    fn incorrect_record_length() {
        let buf = &[
            0x03, 0x65, 0x66, 0x67,  // nameserver
            0x00,  // nameserver terminator
        ];

        assert_eq!(NS::read(66, &mut Cursor::new(buf)),
                   Err(WireError::WrongLabelLength { stated_length: 66, length_after_labels: 5 }));
    }

    #[test]
    fn record_empty() {
        assert_eq!(NS::read(0, &mut Cursor::new(&[])),
                   Err(WireError::IO));
    }

    #[test]
    fn buffer_ends_abruptly() {
        let buf = &[
            0x01,  // the first byte of a string
        ];

        assert_eq!(NS::read(23, &mut Cursor::new(buf)),
                   Err(WireError::IO));
    }
}


================================================
FILE: dns/src/record/openpgpkey.rs
================================================
use log::*;

use crate::wire::*;


/// A **OPENPGPKEY** record, which holds a PGP key.
///
/// # References
///
/// - [RFC 1035 §3.3.14](https://tools.ietf.org/html/rfc7929) — DNS-Based
///   Authentication of Named Entities Bindings for OpenPGP (August 2016)
#[derive(PartialEq, Debug)]
pub struct OPENPGPKEY {

    /// The PGP key, as unencoded bytes.
    pub key: Vec<u8>,
}

impl Wire for OPENPGPKEY {
    const NAME: &'static str = "OPENPGPKEY";
    const RR_TYPE: u16 = 61;

    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
    fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
        if stated_length == 0 {
            let mandated_length = MandatedLength::AtLeast(1);
            return Err(WireError::WrongRecordLength { stated_length, mandated_length });
        }

        let mut key = vec![0_u8; usize::from(stated_length)];
        c.read_exact(&mut key)?;
        trace!("Parsed key -> {:#x?}", key);

        Ok(Self { key })
    }
}

impl OPENPGPKEY {

    /// The base64-encoded PGP key.
    pub fn base64_key(&self) -> String {
        base64::encode(&self.key)
    }
}


#[cfg(test)]
mod test {
    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn parses() {
        let buf = &[
            0x12, 0x34, 0x56, 0x78,  // key
        ];

        assert_eq!(OPENPGPKEY::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   OPENPGPKEY {
                       key: vec![ 0x12, 0x34, 0x56, 0x78 ],
                   });
    }

    #[test]
    fn one_byte_of_uri() {
        let buf = &[
            0x2b,  // one byte of key
        ];

        assert_eq!(OPENPGPKEY::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   OPENPGPKEY {
                       key: vec![ 0x2b ],
                   });
    }

    #[test]
    fn record_empty() {
        assert_eq!(OPENPGPKEY::read(0, &mut Cursor::new(&[])),
                   Err(WireError::WrongRecordLength { stated_length: 0, mandated_length: MandatedLength::AtLeast(1) }));
    }

    #[test]
    fn buffer_ends_abruptly() {
        let buf = &[
            0x12, 0x34,  // the beginning of a key
        ];

        assert_eq!(OPENPGPKEY::read(23, &mut Cursor::new(buf)),
                   Err(WireError::IO));
    }
}


================================================
FILE: dns/src/record/opt.rs
================================================
use std::convert::TryFrom;
use std::io;

use log::*;

use crate::wire::*;


/// A **OPT** _(options)_ pseudo-record, which is used to extend the DNS
/// protocol with additional flags such as DNSSEC stuff.
///
/// # Pseudo-record?
///
/// Unlike all the other record types, which are used to return data about a
/// domain name, the OPT record type is used to add more options to the
/// request, including data about the client or the server. It can exist, with
/// a payload, as a query or a response, though it’s usually encountered in
/// the Additional section. Its purpose is to add more room to the DNS wire
/// format, as backwards compatibility makes it impossible to simply add more
/// flags to the header.
///
/// The fact that this isn’t a standard record type is annoying for a DNS
/// implementation. It re-purposes the ‘class’ and ‘TTL’ fields of the
/// `Answer` struct, as they only have meaning when associated with a domain
/// name. This means that the parser has to treat the OPT type specially,
/// switching to `Opt::read` as soon as the rtype is detected. It also means
/// the output has to deal with missing classes and TTLs.
///
/// # References
///
/// - [RFC 6891](https://tools.ietf.org/html/rfc6891) — Extension Mechanisms
///   for DNS (April 2013)
#[derive(PartialEq, Debug, Clone)]
pub struct OPT {

    /// The maximum size of a UDP packet that the client supports.
    pub udp_payload_size: u16,

    /// The bits that form an extended rcode when non-zero.
    pub higher_bits: u8,

    /// The version number of the DNS extension mechanism.
    pub edns0_version: u8,

    /// Sixteen bits worth of flags.
    pub flags: u16,

    /// The payload of the OPT record.
    pub data: Vec<u8>,
}

impl OPT {

    /// The record type number associated with OPT.
    pub const RR_TYPE: u16 = 41;

    /// Reads from the given cursor to parse an OPT record.
    ///
    /// The buffer will have slightly more bytes to read for an OPT record
    /// than for a typical one: we will not have encountered the ‘class’ or
    /// ‘ttl’ fields, which have different meanings for this record type.
    /// See §6.1.3 of the RFC, “OPT Record TTL Field Use”.
    ///
    /// Unlike the `Wire::read` function, this does not require a length.
    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
    pub fn read(c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
        let udp_payload_size = c.read_u16::<BigEndian>()?;  // replaces the class field
        trace!("Parsed UDP payload size -> {:?}", udp_payload_size);

        let higher_bits = c.read_u8()?;  // replaces the ttl field...
        trace!("Parsed higher bits -> {:#08b}", higher_bits);

        let edns0_version = c.read_u8()?;  // ...as does this...
        trace!("Parsed EDNS(0) version -> {:?}", edns0_version);

        let flags = c.read_u16::<BigEndian>()?;  // ...as does this
        trace!("Parsed flags -> {:#08b}", flags);

        let data_length = c.read_u16::<BigEndian>()?;
        trace!("Parsed data length -> {:?}", data_length);

        let mut data = vec![0_u8; usize::from(data_length)];
        c.read_exact(&mut data)?;
        trace!("Parsed data -> {:#x?}", data);

        Ok(Self { udp_payload_size, higher_bits, edns0_version, flags, data })
    }

    /// Serialises this OPT record into a vector of bytes.
    ///
    /// This is necessary for OPT records to be sent in the Additional section
    /// of requests.
    pub fn to_bytes(&self) -> io::Result<Vec<u8>> {
        let mut bytes = Vec::with_capacity(32);

        bytes.write_u16::<BigEndian>(self.udp_payload_size)?;
        bytes.write_u8(self.higher_bits)?;
        bytes.write_u8(self.edns0_version)?;
        bytes.write_u16::<BigEndian>(self.flags)?;

        // We should not be sending any data at all in the request, really,
        // so sending too much data is downright nonsensical
        let data_len = u16::try_from(self.data.len()).expect("Sending too much data");
        bytes.write_u16::<BigEndian>(data_len)?;

        for b in &self.data {
            bytes.write_u8(*b)?;
        }

        Ok(bytes)
    }
}


#[cfg(test)]
mod test {
    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn parses_no_data() {
        let buf = &[
            0x05, 0xAC,  // UDP payload size
            0x00,        // higher bits
            0x00, 0x00,  // EDNS(0) version
            0x00, 0x00,  // flags
            0x00,        // data length (followed by no data)
        ];

        assert_eq!(OPT::read(&mut Cursor::new(buf)).unwrap(),
                   OPT {
                       udp_payload_size: 1452,
                       higher_bits: 0,
                       edns0_version: 0,
                       flags: 0,
                       data: vec![],
                   });
    }

    #[test]
    fn parses_with_data() {
        let buf = &[
            0x05, 0xAC,  // UDP payload size
            0x00,        // higher bits
            0x00, 0x00,  // EDNS(0) version
            0x00, 0x00,  // flags
            0x04,        // data length
            0x01, 0x02, 0x03, 0x04,  // data
        ];

        assert_eq!(OPT::read(&mut Cursor::new(buf)).unwrap(),
                   OPT {
                       udp_payload_size: 1452,
                       higher_bits: 0,
                       edns0_version: 0,
                       flags: 0,
                       data: vec![1, 2, 3, 4],
                   });
    }

    #[test]
    fn record_empty() {
        assert_eq!(OPT::read(&mut Cursor::new(&[])),
                   Err(WireError::IO));
    }

    #[test]
    fn buffer_ends_abruptly() {
        let buf = &[
            0x05,  // half a UDP payload size
        ];

        assert_eq!(OPT::read(&mut Cursor::new(buf)),
                   Err(WireError::IO));
    }
}


================================================
FILE: dns/src/record/others.rs
================================================
use std::fmt;


/// A number representing a record type dog can’t deal with.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum UnknownQtype {

    /// An rtype number that dog is aware of, but does not know how to parse.
    HeardOf(&'static str, u16),

    /// A completely unknown rtype number.
    UnheardOf(u16),
}

impl UnknownQtype {

    /// Searches the list for an unknown type with the given name, returning a
    /// `HeardOf` variant if one is found, and `None` otherwise.
    pub fn from_type_name(type_name: &str) -> Option<Self> {
        let (name, num) = TYPES.iter().find(|t| t.0.eq_ignore_ascii_case(type_name))?;
        Some(Self::HeardOf(name, *num))
    }

    /// Returns the type number behind this unknown type.
    pub fn type_number(self) -> u16 {
        match self {
            Self::HeardOf(_, num) |
            Self::UnheardOf(num)  => num,
        }
    }
}

impl From<u16> for UnknownQtype {
    fn from(qtype: u16) -> Self {
        match TYPES.iter().find(|t| t.1 == qtype) {
            Some(tuple)  => Self::HeardOf(tuple.0, qtype),
            None         => Self::UnheardOf(qtype),
        }
    }
}

impl fmt::Display for UnknownQtype {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::HeardOf(name, _)  => write!(f, "{}", name),
            Self::UnheardOf(num)    => write!(f, "{}", num),
        }
    }
}


/// Mapping of record type names to their assigned numbers.
static TYPES: &[(&str, u16)] = &[
    ("AFSDB",      18),
    ("ANY",       255),
    ("APL",        42),
    ("AXFR",      252),
    ("CDNSKEY",    60),
    ("CDS",        59),
    ("CERT",       37),
    ("CSYNC",      62),
    ("DHCID",      49),
    ("DLV",     32769),
    ("DNAME",      39),
    ("DNSKEEYE",   48),
    ("DS",         43),
    ("HIP",        55),
    ("IPSECKEY",   45),
    ("IXFR",      251),
    ("KEY",        25),
    ("KX",         36),
    ("NSEC",       47),
    ("NSEC3",      50),
    ("NSEC3PARAM", 51),
    ("OPENPGPKEY", 61),
    ("RRSIG",      46),
    ("RP",         17),
    ("SIG",        24),
    ("SMIMEA",     53),
    ("TA",      32768),
    ("TKEY",      249),
    ("TSIG",      250),
    ("URI",       256),
];


#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn known() {
        assert_eq!(UnknownQtype::from(46).to_string(),
                   String::from("RRSIG"));
    }

    #[test]
    fn unknown() {
        assert_eq!(UnknownQtype::from(4444).to_string(),
                   String::from("4444"));
    }
}


================================================
FILE: dns/src/record/ptr.rs
================================================
use log::*;

use crate::strings::{Labels, ReadLabels};
use crate::wire::*;


/// A **PTR** record, which holds a _pointer_ to a canonical name. This is
/// most often used for reverse DNS lookups.
///
/// # Encoding
///
/// The text encoding is not specified, but this crate treats it as UTF-8.
/// Invalid bytes are turned into the replacement character.
///
/// # References
///
/// - [RFC 1035 §3.3.14](https://tools.ietf.org/html/rfc1035) — Domain Names,
///   Implementation and Specification (November 1987)
#[derive(PartialEq, Debug)]
pub struct PTR {

    /// The CNAME contained in the record.
    pub cname: Labels,
}

impl Wire for PTR {
    const NAME: &'static str = "PTR";
    const RR_TYPE: u16 = 12;

    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
    fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
        let (cname, cname_length) = c.read_labels()?;
        trace!("Parsed cname -> {:?}", cname);

        if stated_length == cname_length {
            trace!("Length is correct");
            Ok(Self { cname })
        }
        else {
            warn!("Length is incorrect (stated length {:?}, cname length {:?}", stated_length, cname_length);
            Err(WireError::WrongLabelLength { stated_length, length_after_labels: cname_length })
        }
    }
}


#[cfg(test)]
mod test {
    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn parses() {
        let buf = &[
            0x03, 0x64, 0x6e, 0x73, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,  // cname
            0x00,  // cname terminator
        ];

        assert_eq!(PTR::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   PTR {
                       cname: Labels::encode("dns.google").unwrap(),
                   });
    }

    #[test]
    fn incorrect_record_length() {
        let buf = &[
            0x03, 0x65, 0x66, 0x67,  // cname
            0x00,  // cname terminator
        ];

        assert_eq!(PTR::read(6, &mut Cursor::new(buf)),
                   Err(WireError::WrongLabelLength { stated_length: 6, length_after_labels: 5 }));
    }

    #[test]
    fn record_empty() {
        assert_eq!(PTR::read(0, &mut Cursor::new(&[])),
                   Err(WireError::IO));
    }

    #[test]
    fn buffer_ends_abruptly() {
        let buf = &[
            0x03, 0x64,  // the start of a cname
        ];

        assert_eq!(PTR::read(23, &mut Cursor::new(buf)),
                   Err(WireError::IO));
    }
}


================================================
FILE: dns/src/record/soa.rs
================================================
use log::*;

use crate::strings::{Labels, ReadLabels};
use crate::wire::*;


/// A **SOA** _(start of authority)_ record, which contains administrative
/// information about the zone the domain is in. These are returned when a
/// server does not have a record for a domain.
///
/// # References
///
/// - [RFC 1035 §3.3.13](https://tools.ietf.org/html/rfc1035) — Domain Names,
///   Implementation and Specification (November 1987)
#[derive(PartialEq, Debug)]
pub struct SOA {

    /// The primary master name for this server.
    pub mname: Labels,

    /// The e-mail address of the administrator responsible for this DNS zone.
    pub rname: Labels,

    /// A serial number for this DNS zone.
    pub serial: u32,

    /// Duration, in seconds, after which secondary nameservers should query
    /// the master for _its_ SOA record.
    pub refresh_interval: u32,

    /// Duration, in seconds, after which secondary nameservers should retry
    /// requesting the serial number from the master if it does not respond.
    /// It should be less than `refresh`.
    pub retry_interval: u32,

    /// Duration, in seconds, after which secondary nameservers should stop
    /// answering requests for this zone if the master does not respond.
    /// It should be greater than the sum of `refresh` and `retry`.
    pub expire_limit: u32,

    /// Duration, in seconds, of the minimum time-to-live.
    pub minimum_ttl: u32,
}

impl Wire for SOA {
    const NAME: &'static str = "SOA";
    const RR_TYPE: u16 = 6;

    #[allow(clippy::similar_names)]
    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
    fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
        let (mname, mname_length) = c.read_labels()?;
        trace!("Parsed mname -> {:?}", mname);

        let (rname, rname_length) = c.read_labels()?;
        trace!("Parsed rname -> {:?}", rname);

        let serial = c.read_u32::<BigEndian>()?;
        trace!("Parsed serial -> {:?}", serial);

        let refresh_interval = c.read_u32::<BigEndian>()?;
        trace!("Parsed refresh interval -> {:?}", refresh_interval);

        let retry_interval = c.read_u32::<BigEndian>()?;
        trace!("Parsed retry interval -> {:?}", retry_interval);

        let expire_limit = c.read_u32::<BigEndian>()?;
        trace!("Parsed expire limit -> {:?}", expire_limit);

        let minimum_ttl = c.read_u32::<BigEndian>()?;
        trace!("Parsed minimum TTL -> {:?}", minimum_ttl);

        let length_after_labels = 4 * 5 + mname_length + rname_length;
        if stated_length == length_after_labels {
            trace!("Length is correct");
            Ok(Self {
                mname, rname, serial, refresh_interval,
                retry_interval, expire_limit, minimum_ttl,
            })
        }
        else {
            warn!("Length is incorrect (stated length {:?}, mname plus rname plus fields length {:?})", stated_length, length_after_labels);
            Err(WireError::WrongLabelLength { stated_length, length_after_labels })
        }
    }
}


#[cfg(test)]
mod test {
    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn parses() {
        let buf = &[
            0x05, 0x62, 0x73, 0x61, 0x67, 0x6f, 0x02, 0x6d, 0x65,  // mname
            0x00,  // mname terminator
            0x05, 0x62, 0x73, 0x61, 0x67, 0x6f, 0x02, 0x6d, 0x65,  // rname
            0x00,  // rname terminator
            0x5d, 0x3c, 0xef, 0x02,  // Serial
            0x00, 0x01, 0x51, 0x80,  // Refresh interval
            0x00, 0x00, 0x1c, 0x20,  // Retry interval
            0x00, 0x09, 0x3a, 0x80,  // Expire limit
            0x00, 0x00, 0x01, 0x2c,  // Minimum TTL
        ];

        assert_eq!(SOA::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   SOA {
                       mname: Labels::encode("bsago.me").unwrap(),
                       rname: Labels::encode("bsago.me").unwrap(),
                       serial: 1564274434,
                       refresh_interval: 86400,
                       retry_interval: 7200,
                       expire_limit: 604800,
                       minimum_ttl: 300,
                   });
    }

    #[test]
    fn incorrect_record_length() {
        let buf = &[
            0x03, 0x65, 0x66, 0x67,  // mname
            0x00,  // mname terminator
            0x03, 0x65, 0x66, 0x67,  // rname
            0x00,  // rname terminator
            0x5d, 0x3c, 0xef, 0x02,  // Serial
            0x00, 0x01, 0x51, 0x80,  // Refresh interval
            0x00, 0x00, 0x1c, 0x20,  // Retry interval
            0x00, 0x09, 0x3a, 0x80,  // Expire limit
            0x00, 0x00, 0x01, 0x2c,  // Minimum TTL
        ];

        assert_eq!(SOA::read(89, &mut Cursor::new(buf)),
                   Err(WireError::WrongLabelLength { stated_length: 89, length_after_labels: 30 }));
    }

    #[test]
    fn record_empty() {
        assert_eq!(SOA::read(0, &mut Cursor::new(&[])),
                   Err(WireError::IO));
    }

    #[test]
    fn buffer_ends_abruptly() {
        let buf = &[
            0x05, 0x62,  // the start of an mname
        ];

        assert_eq!(SOA::read(23, &mut Cursor::new(buf)),
                   Err(WireError::IO));
    }
}


================================================
FILE: dns/src/record/srv.rs
================================================
use log::*;

use crate::strings::{Labels, ReadLabels};
use crate::wire::*;


/// A **SRV** record, which contains an IP address as well as a port number,
/// for specifying the location of services more precisely.
///
/// # References
///
/// - [RFC 2782](https://tools.ietf.org/html/rfc2782) — A DNS RR for
///   specifying the location of services (February 2000)
#[derive(PartialEq, Debug)]
pub struct SRV {

    /// The priority of this host among all that get returned. Lower values
    /// are higher priority.
    pub priority: u16,

    /// A weight to choose among results with the same priority. Higher values
    /// are higher priority.
    pub weight: u16,

    /// The port the service is serving on.
    pub port: u16,

    /// The hostname of the machine the service is running on.
    pub target: Labels,
}

impl Wire for SRV {
    const NAME: &'static str = "SRV";
    const RR_TYPE: u16 = 33;

    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
    fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
        let priority = c.read_u16::<BigEndian>()?;
        trace!("Parsed priority -> {:?}", priority);

        let weight = c.read_u16::<BigEndian>()?;
        trace!("Parsed weight -> {:?}", weight);

        let port = c.read_u16::<BigEndian>()?;
        trace!("Parsed port -> {:?}", port);

        let (target, target_length) = c.read_labels()?;
        trace!("Parsed target -> {:?}", target);

        let length_after_labels = 3 * 2 + target_length;
        if stated_length == length_after_labels {
            trace!("Length is correct");
            Ok(Self { priority, weight, port, target })
        }
        else {
            warn!("Length is incorrect (stated length {:?}, fields plus target length {:?})", stated_length, length_after_labels);
            Err(WireError::WrongLabelLength { stated_length, length_after_labels })
        }
    }
}


#[cfg(test)]
mod test {
    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn parses() {
        let buf = &[
            0x00, 0x01,  // priority
            0x00, 0x01,  // weight
            0x92, 0x7c,  // port
            0x03, 0x61, 0x74, 0x61, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x04,
            0x6e, 0x6f, 0x64, 0x65, 0x03, 0x64, 0x63, 0x31, 0x06, 0x63, 0x6f,
            0x6e, 0x73, 0x75, 0x6c,  // target
            0x00,  // target terminator
        ];

        assert_eq!(SRV::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   SRV {
                       priority: 1,
                       weight: 1,
                       port: 37500,
                       target: Labels::encode("ata.local.node.dc1.consul").unwrap(),
                   });
    }

    #[test]
    fn incorrect_record_length() {
        let buf = &[
            0x00, 0x01,  // priority
            0x00, 0x01,  // weight
            0x92, 0x7c,  // port
            0x03, 0x61, 0x74, 0x61,  // target
            0x00,  // target terminator
        ];

        assert_eq!(SRV::read(16, &mut Cursor::new(buf)),
                   Err(WireError::WrongLabelLength { stated_length: 16, length_after_labels: 11 }));
    }

    #[test]
    fn record_empty() {
        assert_eq!(SRV::read(0, &mut Cursor::new(&[])),
                   Err(WireError::IO));
    }

    #[test]
    fn buffer_ends_abruptly() {
        let buf = &[
            0x00,  // half a priority
        ];

        assert_eq!(SRV::read(23, &mut Cursor::new(buf)),
                   Err(WireError::IO));
    }
}


================================================
FILE: dns/src/record/sshfp.rs
================================================
use log::*;

use crate::wire::*;


/// A **SSHFP** _(secure shell fingerprint)_ record, which contains the
/// fingerprint of an SSH public key.
///
/// # References
///
/// - [RFC 4255](https://tools.ietf.org/html/rfc4255) — Using DNS to Securely
///   Publish Secure Shell (SSH) Key Fingerprints (January 2006)
#[derive(PartialEq, Debug)]
pub struct SSHFP {

    /// The algorithm of the public key. This is a number with several defined
    /// mappings.
    pub algorithm: u8,

    /// The type of the fingerprint, which specifies the hashing algorithm
    /// used to derive the fingerprint. This is a number with several defined
    /// mappings.
    pub fingerprint_type: u8,

    /// The fingerprint of the public key.
    pub fingerprint: Vec<u8>,
}

impl Wire for SSHFP {
    const NAME: &'static str = "SSHFP";
    const RR_TYPE: u16 = 44;

    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
    fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
        let algorithm = c.read_u8()?;
        trace!("Parsed algorithm -> {:?}", algorithm);

        let fingerprint_type = c.read_u8()?;
        trace!("Parsed fingerprint type -> {:?}", fingerprint_type);

        if stated_length <= 2 {
            let mandated_length = MandatedLength::AtLeast(3);
            return Err(WireError::WrongRecordLength { stated_length, mandated_length });
        }

        let fingerprint_length = stated_length - 1 - 1;
        let mut fingerprint = vec![0_u8; usize::from(fingerprint_length)];
        c.read_exact(&mut fingerprint)?;
        trace!("Parsed fingerprint -> {:#x?}", fingerprint);

        Ok(Self { algorithm, fingerprint_type, fingerprint })
    }
}

impl SSHFP {

    /// Returns the hexadecimal representation of the fingerprint.
    pub fn hex_fingerprint(&self) -> String {
        self.fingerprint.iter()
            .map(|byte| format!("{:02x}", byte))
            .collect()
    }
}


#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn parses() {
        let buf = &[
            0x01,  // algorithm
            0x01,  // fingerprint type
            0x21, 0x22, 0x23, 0x24, 0x25, 0x26,  // a short fingerprint
        ];

        assert_eq!(SSHFP::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   SSHFP {
                       algorithm: 1,
                       fingerprint_type: 1,
                       fingerprint: vec![ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26 ],
                   });
    }

    #[test]
    fn one_byte_fingerprint() {
        let buf = &[
            0x01,  // algorithm
            0x01,  // fingerprint type
            0x21,  // an extremely short fingerprint
        ];

        assert_eq!(SSHFP::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   SSHFP {
                       algorithm: 1,
                       fingerprint_type: 1,
                       fingerprint: vec![ 0x21 ],
                   });
    }

    #[test]
    fn record_too_short() {
        let buf = &[
            0x01,  // algorithm
            0x01,  // fingerprint type
        ];

        assert_eq!(SSHFP::read(buf.len() as _, &mut Cursor::new(buf)),
                   Err(WireError::WrongRecordLength { stated_length: 2, mandated_length: MandatedLength::AtLeast(3) }));
    }

    #[test]
    fn record_empty() {
        assert_eq!(SSHFP::read(0, &mut Cursor::new(&[])),
                   Err(WireError::IO));
    }

    #[test]
    fn buffer_ends_abruptly() {
        let buf = &[
            0x01,  // algorithm
        ];

        assert_eq!(SSHFP::read(6, &mut Cursor::new(buf)),
                   Err(WireError::IO));
    }

    #[test]
    fn hex_rep() {
        let sshfp = SSHFP {
            algorithm: 1,
            fingerprint_type: 1,
            fingerprint: vec![ 0xf3, 0x48, 0xcd, 0xc9 ],
        };

        assert_eq!(sshfp.hex_fingerprint(),
                   String::from("f348cdc9"));
    }
}


================================================
FILE: dns/src/record/tlsa.rs
================================================
use log::*;

use crate::wire::*;


/// A **TLSA** _(TLS authentication)_ record, which contains a TLS certificate
/// (or a public key, or its hash), associating it with a domain.
///
/// # References
///
/// - [RFC 6698](https://tools.ietf.org/html/rfc6698) — The DNS-Based
///   Authentication of Named Entities (DANE) Transport Layer Security
///   Protocol: TLSA (August 2012)
#[derive(PartialEq, Debug)]
pub struct TLSA {

    /// A number representing the purpose of the certificate.
    pub certificate_usage: u8,

    /// A number representing which part of the certificate is returned in the
    /// data. This could be the full certificate, or just the public key.
    pub selector: u8,

    /// A number representing whether a certificate should be associated with
    /// the exact data, or with a hash of it.
    pub matching_type: u8,

    /// A series of bytes representing the certificate.
    pub certificate_data: Vec<u8>,
}


impl Wire for TLSA {
    const NAME: &'static str = "TLSA";
    const RR_TYPE: u16 = 52;

    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
    fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {

        let certificate_usage = c.read_u8()?;
        trace!("Parsed certificate_usage -> {:?}", certificate_usage);

        let selector = c.read_u8()?;
        trace!("Parsed selector -> {:?}", selector);

        let matching_type = c.read_u8()?;
        trace!("Parsed matching type -> {:?}", matching_type);

        if stated_length <= 3 {
            let mandated_length = MandatedLength::AtLeast(4);
            return Err(WireError::WrongRecordLength { stated_length, mandated_length });
        }

        let certificate_data_length = stated_length - 1 - 1 - 1;
        let mut certificate_data = vec![0_u8; usize::from(certificate_data_length)];
        c.read_exact(&mut certificate_data)?;
        trace!("Parsed fingerprint -> {:#x?}", certificate_data);

        Ok(Self { certificate_usage, selector, matching_type, certificate_data })
    }
}

impl TLSA {

    /// Returns the hexadecimal representation of the fingerprint.
    pub fn hex_certificate_data(&self) -> String {
        self.certificate_data.iter()
            .map(|byte| format!("{:02x}", byte))
            .collect()
    }
}


#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn parses() {
        let buf = &[
            0x03,  // certificate usage
            0x01,  // selector
            0x01,  // matching type
            0x05, 0x95, 0x98, 0x11, 0x22, 0x33 // data
        ];

        assert_eq!(TLSA::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   TLSA {
                       certificate_usage: 3,
                       selector: 1,
                       matching_type: 1,
                       certificate_data: vec![ 0x05, 0x95, 0x98, 0x11, 0x22, 0x33 ],
                   });
    }

    #[test]
    fn one_byte_certificate() {
        let buf = &[
            0x03,  // certificate usage
            0x01,  // selector
            0x01,  // matching type
            0x05,  // one byte of data
        ];

        assert_eq!(TLSA::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   TLSA {
                       certificate_usage: 3,
                       selector: 1,
                       matching_type: 1,
                       certificate_data: vec![ 0x05 ],
                   });
    }

    #[test]
    fn record_too_short() {
        let buf = &[
            0x03,  // certificate usage
            0x01,  // selector
            0x01,  // matching type
        ];

        assert_eq!(TLSA::read(buf.len() as _, &mut Cursor::new(buf)),
                   Err(WireError::WrongRecordLength { stated_length: 3, mandated_length: MandatedLength::AtLeast(4) }));
    }

    #[test]
    fn record_empty() {
        assert_eq!(TLSA::read(0, &mut Cursor::new(&[])),
                   Err(WireError::IO));
    }

    #[test]
    fn buffer_ends_abruptly() {
        let buf = &[
            0x01,  // certificate_usage
        ];

        assert_eq!(TLSA::read(6, &mut Cursor::new(buf)),
                   Err(WireError::IO));
    }
}



================================================
FILE: dns/src/record/txt.rs
================================================
use log::*;

use crate::wire::*;


/// A **TXT** record, which holds arbitrary descriptive text.
///
/// # Encoding
///
/// The text encoding is not specified, but this crate treats it as UTF-8.
/// Invalid bytes are turned into the replacement character.
///
/// # References
///
/// - [RFC 1035 §3.3.14](https://tools.ietf.org/html/rfc1035) — Domain Names,
///   Implementation and Specification (November 1987)
#[derive(PartialEq, Debug)]
pub struct TXT {

    /// The messages contained in the record.
    pub messages: Vec<Box<[u8]>>,
}

impl Wire for TXT {
    const NAME: &'static str = "TXT";
    const RR_TYPE: u16 = 16;

    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
    fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
        let mut messages = Vec::new();
        let mut total_length = 0_u16;

        loop {
            let mut buf = Vec::new();

            loop {
                let next_length = c.read_u8()?;
                total_length += u16::from(next_length) + 1;
                trace!("Parsed slice length -> {:?} (total so far {:?})", next_length, total_length);

                for _ in 0 .. next_length {
                    buf.push(c.read_u8()?);
                }

                if next_length < 255 {
                    break;
                }
                else {
                    trace!("Got length 255, so looping");
                }
            }

            let message = buf.into_boxed_slice();
            trace!("Parsed message -> {:?}", String::from_utf8_lossy(&message));
            messages.push(message);

            if total_length >= stated_length {
                break;
            }
        }

        if stated_length == total_length {
            trace!("Length is correct");
            Ok(Self { messages })
        }
        else {
            warn!("Length is incorrect (stated length {:?}, messages length {:?})", stated_length, total_length);
            Err(WireError::WrongLabelLength { stated_length, length_after_labels: total_length })
        }
    }
}


#[cfg(test)]
mod test {
    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn parses_one_iteration() {
        let buf = &[
            0x06,  // message chunk length
            0x74, 0x78, 0x74, 0x20, 0x6d, 0x65,  // message chunk
        ];

        assert_eq!(TXT::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   TXT {
                       messages: vec![ Box::new(*b"txt me") ],
                   });
    }

    #[test]
    fn parses_two_iterations() {
        let buf = &[
            0xFF,  // message chunk length
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
            0x41, 0x41,  // exactly two hundred and fifty five ‘A’s (screaming)
            0x04,  // message chunk length
            0x41, 0x41, 0x41, 0x41,  // four more ‘A’s (the scream abruptly stops)
        ];

        assert_eq!(TXT::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   TXT {
                       messages: vec![
                           Box::new(*b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
                                       AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
                                       AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
                                       AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
                                       AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
                                       AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
                                       AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
                                       AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
                                       AAAAAAAAAAAAAAAAAAAAAAAAAAA"),
                       ],
                   });
        // did you know you can just _write_ code like this, and nobody will stop you?
    }

    #[test]
    fn right_at_the_limit() {
        let buf = &[
            0xFE,  // message chunk length
            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
            0x42,  // exactly two hundred and fifty four ‘B’s (a hive)
        ];

        assert_eq!(TXT::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   TXT {
                       messages: vec![
                           Box::new(*b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBB\
                                       BBBBBBBBBBBBBBBBBBBBBBBBBBBBB\
                                       BBBBBBBBBBBBBBBBBBBBBBBBBBBBB\
                                       BBBBBBBBBBBBBBBBBBBBBBBBBBBBB\
                                       BBBBBBBBBBBBBBBBBBBBBBBBBBBBB\
                                       BBBBBBBBBBBBBBBBBBBBBBBBBBBBB\
                                       BBBBBBBBBBBBBBBBBBBBBBBBBBBBB\
                                       BBBBBBBBBBBBBBBBBBBBBBBBBBBBB\
                                       BBBBBBBBBBBBBBBBBBBBBB"),
                       ],
                   });
    }

    #[test]
    fn another_message() {
        let buf = &[
            0x06,  // message chunk length
            0x74, 0x78, 0x74, 0x20, 0x6d, 0x65,  // message chunk
            0x06,  // message chunk length
            0x79, 0x61, 0x20, 0x62, 0x65, 0x62,  // message chunk
        ];

        assert_eq!(TXT::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   TXT {
                       messages: vec![
                           Box::new(*b"txt me"),
                           Box::new(*b"ya beb"),
                       ],
                   });
    }

    #[test]
    fn length_too_short() {
        let buf = &[
            0x06,  // message chunk length
            0x74, 0x78, 0x74, 0x20, 0x6d, 0x65,  // message chunk
        ];

        assert_eq!(TXT::read(2, &mut Cursor::new(buf)),
                   Err(WireError::WrongLabelLength { stated_length: 2, length_after_labels: 7 }));
    }

    #[test]
    fn record_empty() {
        assert_eq!(TXT::read(0, &mut Cursor::new(&[])),
                   Err(WireError::IO));
    }

    #[test]
    fn buffer_ends_abruptly() {
        let buf = &[
            0x06, 0x74,  // the start of a message
        ];

        assert_eq!(TXT::read(23, &mut Cursor::new(buf)),
                   Err(WireError::IO));
    }
}


================================================
FILE: dns/src/record/uri.rs
================================================
use log::*;

use crate::wire::*;


/// A **URI** record, which holds a URI along with weight and priority values
/// to balance between several records.
///
/// # References
///
/// - [RFC 7553](https://tools.ietf.org/html/rfc7553) — The Uniform Resource
///   Identifier (URI) DNS Resource Record (June 2015)
/// - [RFC 3986](https://tools.ietf.org/html/rfc3986) — Uniform Resource
///   Identifier (URI): Generic Syntax (January 2005)
#[derive(PartialEq, Debug)]
pub struct URI {

    /// The priority of the URI. Clients are supposed to contact the URI with
    /// the lowest priority out of all the ones it can reach.
    pub priority: u16,

    /// The weight of the URI, which specifies a relative weight for entries
    /// with the same priority.
    pub weight: u16,

    /// The URI contained in the record. Since all we are doing is displaying
    /// it to the user, we do not need to parse it for accuracy.
    pub target: Box<[u8]>,
}

impl Wire for URI {
    const NAME: &'static str = "URI";
    const RR_TYPE: u16 = 256;

    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
    fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
        let priority = c.read_u16::<BigEndian>()?;
        trace!("Parsed priority -> {:?}", priority);

        let weight = c.read_u16::<BigEndian>()?;
        trace!("Parsed weight -> {:?}", weight);

        // The target must not be empty.
        if stated_length <= 4 {
            let mandated_length = MandatedLength::AtLeast(5);
            return Err(WireError::WrongRecordLength { stated_length, mandated_length });
        }

        let remaining_length = stated_length - 4;
        let mut target = vec![0_u8; usize::from(remaining_length)].into_boxed_slice();
        c.read_exact(&mut target)?;
        trace!("Parsed target -> {:?}", String::from_utf8_lossy(&target));

        Ok(Self { priority, weight, target })
    }
}


#[cfg(test)]
mod test {
    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn parses() {
        let buf = &[
            0x00, 0x0A,  // priority
            0x00, 0x10,  // weight
            0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x72, 0x66, 0x63,
            0x73, 0x2e, 0x69, 0x6f, 0x2f,  // uri
        ];

        assert_eq!(URI::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   URI {
                       priority: 10,
                       weight: 16,
                       target: Box::new(*b"https://rfcs.io/"),
                   });
    }

    #[test]
    fn one_byte_of_uri() {
        let buf = &[
            0x00, 0x0A,  // priority
            0x00, 0x10,  // weight
            0x2f,  // one byte of uri (invalid but still a legitimate DNS record)
        ];

        assert_eq!(URI::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
                   URI {
                       priority: 10,
                       weight: 16,
                       target: Box::new(*b"/"),
                   });
    }

    #[test]
    fn missing_any_data() {
        let buf = &[
            0x00, 0x0A,  // priority
            0x00, 0x10,  // weight
        ];

        assert_eq!(URI::read(buf.len() as _, &mut Cursor::new(buf)),
                   Err(WireError::WrongRecordLength { stated_length: 4, mandated_length: MandatedLength::AtLeast(5) }));
    }

    #[test]
    fn record_empty() {
        assert_eq!(URI::read(0, &mut Cursor::new(&[])),
                   Err(WireError::IO));
    }

    #[test]
    fn buffer_ends_abruptly() {
        let buf = &[
            0x00, 0x0A,  // half a priority
        ];

        assert_eq!(URI::read(23, &mut Cursor::new(buf)),
                   Err(WireError::IO));
    }
}


================================================
FILE: dns/src/strings.rs
================================================
//! Reading strings from the DNS wire protocol.

use std::convert::TryFrom;
use std::fmt;
use std::io::{self, Write};

use byteorder::{ReadBytesExt, WriteBytesExt};
use log::*;

use crate::wire::*;


/// Domain names in the DNS protocol are encoded as **Labels**, which are
/// segments of ASCII characters prefixed by their length. When written out,
/// each segment is followed by a dot.
///
/// The maximum length of a segment is 255 characters.
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone)]
pub struct Labels {
    segments: Vec<(u8, String)>,
}

#[cfg(feature = "with_idna")]
fn label_to_ascii(label: &str) -> Result<String, unic_idna::Errors> {
    let flags = unic_idna::Flags{use_std3_ascii_rules: false, transitional_processing: false, verify_dns_length: true};
    unic_idna::to_ascii(label, flags)
}

#[cfg(not(feature = "with_idna"))]
fn label_to_ascii(label: &str) -> Result<String, ()> {
    Ok(label.to_owned())
}

impl Labels {

    /// Creates a new empty set of labels, which represent the root of the DNS
    /// as a domain with no name.
    pub fn root() -> Self {
        Self { segments: Vec::new() }
    }

    /// Encodes the given input string as labels. If any segment is too long,
    /// returns that segment as an error.
    pub fn encode(input: &str) -> Result<Self, &str> {
        let mut segments = Vec::new();

        for label in input.split('.') {
            if label.is_empty() {
                continue;
            }

            let label_idn = label_to_ascii(label)
                    .map_err(|e| {
                        warn!("Could not encode label {:?}: {:?}", label, e);
                        label
                    })?;

            match u8::try_from(label_idn.len()) {
                Ok(length) => {
                    segments.push((length, label_idn));
                }
                Err(e) => {
                    warn!("Could not encode label {:?}: {}", label, e);
                    return Err(label);
                }
            }
        }

        Ok(Self { segments })
    }

    /// Returns the number of segments.
    pub fn len(&self) -> usize {
        self.segments.len()
    }

    /// Returns a new set of labels concatenating two names.
    pub fn extend(&self, other: &Self) -> Self {
        let mut segments = self.segments.clone();
        segments.extend_from_slice(&other.segments);
        Self { segments }
    }
}

impl fmt::Display for Labels {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for (_, segment) in &self.segments {
            write!(f, "{}.", segment)?;
        }

        Ok(())
    }
}

/// An extension for `Cursor` that enables reading compressed domain names
/// from DNS packets.
pub(crate) trait ReadLabels {

    /// Read and expand a compressed domain name.
    fn read_labels(&mut self) -> Result<(Labels, u16), WireError>;
}

impl ReadLabels for Cursor<&[u8]> {
    fn read_labels(&mut self) -> Result<(Labels, u16), WireError> {
        let mut labels = Labels { segments: Vec::new() };
        let bytes_read = read_string_recursive(&mut labels, self, &mut Vec::new())?;
        Ok((labels, bytes_read))
    }
}


/// An extension for `Write` that enables writing domain names.
pub(crate) trait WriteLabels {

    /// Write a domain name.
    ///
    /// The names being queried are written with one byte slice per
    /// domain segment, preceded by each segment’s length, with the
    /// whole thing ending with a segment of zero length.
    ///
    /// So “dns.lookup.dog” would be encoded as:
    /// “3, dns, 6, lookup, 3, dog, 0”.
    fn write_labels(&mut self, input: &Labels) -> io::Result<()>;
}

impl<W: Write> WriteLabels for W {
    fn write_labels(&mut self, input: &Labels) -> io::Result<()> {
        for (length, label) in &input.segments {
            self.write_u8(*length)?;

            for b in label.as_bytes() {
                self.write_u8(*b)?;
            }
        }

        self.write_u8(0)?;  // terminate the string
        Ok(())
    }
}


const RECURSION_LIMIT: usize = 8;

/// Reads bytes from the given cursor into the given buffer, using the list of
/// recursions to track backtracking positions. Returns the count of bytes
/// that had to be read to produce the string, including the bytes to signify
/// backtracking, but not including the bytes read _during_ backtracking.
#[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
fn read_string_recursive(labels: &mut Labels, c: &mut Cursor<&[u8]>, recursions: &mut Vec<u16>) -> Result<u16, WireError> {
    let mut bytes_read = 0;

    loop {
        let byte = c.read_u8()?;
        bytes_read += 1;

        if byte == 0 {
            break;
        }

        else if byte >= 0b_1100_0000 {
            let name_one = byte - 0b1100_0000;
            let name_two = c.read_u8()?;
            bytes_read += 1;
            let offset = u16::from_be_bytes([name_one, name_two]);

            if recursions.contains(&offset) {
                warn!("Hit previous offset ({}) decoding string", offset);
                return Err(WireError::TooMuchRecursion(recursions.clone().into_boxed_slice()));
            }

            recursions.push(offset);

            if recursions.len() >= RECURSION_LIMIT {
                warn!("Hit recursion limit ({}) decoding string", RECURSION_LIMIT);
                return Err(WireError::TooMuchRecursion(recursions.clone().into_boxed_slice()));
            }

            trace!("Backtracking to offset {}", offset);
            let new_pos = c.position();
            c.set_position(u64::from(offset));

            read_string_recursive(labels, c, recursions)?;

            trace!("Coming back to {}", new_pos);
            c.set_position(new_pos);
            break;
        }

        // Otherwise, treat the byte as the length of a label, and read that
        // many characters.
        else {
            let mut name_buf = Vec::new();

            for _ in 0 .. byte {
                let c = c.read_u8()?;
                bytes_read += 1;
                name_buf.push(c);
            }

            let string = String::from_utf8_lossy(&*name_buf).to_string();
            labels.segments.push((byte, string));
        }
    }

    Ok(bytes_read)
}


#[cfg(test)]
mod test {
    use super::*;
    use pretty_assertions::assert_eq;

    // The buffers used in these tests contain nothing but the labels we’re
    // decoding. In DNS packets found in the wild, the cursor will be able to
    // reach all the bytes of the packet, so the Answer section can reference
    // strings in the Query section.

    #[test]
    fn nothing() {
        let buf: &[u8] = &[
            0x00,  // end reading
        ];

        assert_eq!(Cursor::new(buf).read_labels(),
                   Ok((Labels::root(), 1)));
    }

    #[test]
    fn one_label() {
        let buf: &[u8] = &[
            0x03,  // label of length 3
            b'o', b'n', b'e',  // label
            0x00,  // end reading
        ];

        assert_eq!(Cursor::new(buf).read_labels(),
                   Ok((Labels::encode("one.").unwrap(), 5)));
    }

    #[test]
    fn two_labels() {
        let buf: &[u8] = &[
            0x03,  // label of length 3
            b'o', b'n', b'e',  // label
            0x03,  // label of length 3
            b't', b'w', b'o',  // label
            0x00,  // end reading
        ];

        assert_eq!(Cursor::new(buf).read_labels(),
                   Ok((Labels::encode("one.two.").unwrap(), 9)));
    }

    #[test]
    fn label_followed_by_backtrack() {
        let buf: &[u8] = &[
            0x03,  // label of length 3
            b'o', b'n', b'e',  // label
            0xc0, 0x06,  // skip to position 6 (the next byte)

            0x03,  // label of length 3
            b't', b'w', b'o',  // label
            0x00,  // end reading
        ];

        assert_eq!(Cursor::new(buf).read_labels(),
                   Ok((Labels::encode("one.two.").unwrap(), 6)));
    }

    #[test]
    fn extremely_long_label() {
        let mut buf: Vec<u8> = vec![
            0xbf,  // label of length 191
        ];

        buf.extend(vec![0x65; 191]);  // the rest of the label
        buf.push(0x00);  // end reading

        assert_eq!(Cursor::new(&*buf).read_labels().unwrap().1, 193);
    }

    #[test]
    fn immediate_recursion() {
        let buf: &[u8] = &[
            0xc0, 0x00,  // skip to position 0
        ];

        assert_eq!(Cursor::new(buf).read_labels(),
                   Err(WireError::TooMuchRecursion(Box::new([ 0 ]))));
    }

    #[test]
    fn mutual_recursion() {
        let buf: &[u8] = &[
            0xc0, 0x02,  // skip to position 2
            0xc0, 0x00,  // skip to position 0
        ];

        let mut cursor = Cursor::new(buf);

        assert_eq!(cursor.read_labels(),
                   Err(WireError::TooMuchRecursion(Box::new([ 2, 0 ]))));
    }

    #[test]
    fn too_much_recursion() {
        let buf: &[u8] = &[
            0xc0, 0x02,  // skip to position 2
            0xc0, 0x04,  // skip to position 4
            0xc0, 0x06,  // skip to position 6
            0xc0, 0x08,  // skip to position 8
            0xc0, 0x0A,  // skip to position 10
            0xc0, 0x0C,  // skip to position 12
            0xc0, 0x0E,  // skip to position 14
            0xc0, 0x10,  // skip to position 16
            0x00,        // no label
        ];

        let mut cursor = Cursor::new(buf);

        assert_eq!(cursor.read_labels(),
                   Err(WireError::TooMuchRecursion(Box::new([ 2, 4, 6, 8, 10, 12, 14, 16 ]))));
    }
}


================================================
FILE: dns/src/types.rs
================================================
//! DNS packets are traditionally implemented with both the request and
//! response packets at the same type. After all, both follow the same format,
//! with the request packet having zero answer fields, and the response packet
//! having at least one record in its answer fields.

use crate::record::{Record, RecordType, OPT};
use crate::strings::Labels;


/// A request that gets sent out over a transport.
#[derive(PartialEq, Debug)]
pub struct Request {

    /// The transaction ID of this request. This is used to make sure
    /// different DNS packets don’t answer each other’s questions.
    pub transaction_id: u16,

    /// The flags that accompany every DNS packet.
    pub flags: Flags,

    /// The query that this request is making. Only one query is allowed per
    /// request, as traditionally, DNS servers only respond to the first query
    /// in a packet.
    pub query: Query,

    /// An additional record that may be sent as part of the query.
    pub additional: Option<OPT>,
}


/// A response obtained from a DNS server.
#[derive(PartialEq, Debug)]
pub struct Response {

    /// The transaction ID, which should match the ID of the request.
    pub transaction_id: u16,

    /// The flags that accompany every DNS packet.
    pub flags: Flags,

    /// The queries section.
    pub queries: Vec<Query>,

    /// The answers section.
    pub answers: Vec<Answer>,

    /// The authoritative nameservers section.
    pub authorities: Vec<Answer>,

    /// The additional records section.
    pub additionals: Vec<Answer>,
}


/// A DNS query section.
#[derive(PartialEq, Debug)]
pub struct Query {

    /// The domain name being queried, in human-readable dotted notation.
    pub qname: Labels,

    /// The class number.
    pub qclass: QClass,

    /// The type number.
    pub qtype: RecordType,
}


/// A DNS answer section.
#[derive(PartialEq, Debug)]
pub enum Answer {

    /// This is a standard answer with every field.
    Standard {

        /// The domain name being answered for.
        qname: Labels,

        /// This answer’s class.
        qclass: QClass,

        /// The time-to-live duration, in seconds.
        ttl: u32,

        /// The record contained in this answer.
        record: Record,
    },

    /// This is a pseudo-record answer, so some of the fields (class and TTL)
    /// have different meaning.
    Pseudo {

        /// The domain name being answered for.
        qname: Labels,

        /// The OPT record contained in this answer.
        opt: OPT,
    },
}


/// A DNS record class. Of these, the only one that’s in regular use anymore
/// is the Internet class.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum QClass {

    /// The **Internet** class.
    IN,

    /// The **Chaosnet** class.
    CH,

    /// The **Hesiod** class.
    HS,

    /// A class number that does not map to any known class.
    Other(u16),
}


/// The flags that accompany every DNS packet.
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct Flags {

    /// Whether this packet is a response packet.
    pub response: bool,

    /// The operation being performed.
    pub opcode: Opcode,

    /// In a response, whether the server is providing authoritative DNS responses.
    pub authoritative: bool,

    /// In a response, whether this message has been truncated by the transport.
    pub truncated: bool,

    /// In a query, whether the server may query other nameservers recursively.
    /// It is up to the server whether it will actually do this.
    pub recursion_desired: bool,

    /// In a response, whether the server allows recursive query support.
    pub recursion_available: bool,

    /// In a response, whether the server is marking this data as authentic.
    pub authentic_data: bool,

    /// In a request, whether the server should disable its authenticity
    /// checking for the request’s queries.
    pub checking_disabled: bool,

    /// In a response, a code indicating an error if one occurred.
    pub error_code: Option<ErrorCode>,
}


/// A number representing the operation being performed.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum Opcode {

    /// This request is a standard query, or this response is answering a
    /// standard query.
    Query,

    /// Any other opcode. This can be from 1 to 15, as the opcode field is
    /// four bits wide, and 0 is taken.
    Other(u8),
}


/// A code indicating an error.
///
/// # References
///
/// - [RFC 6895 §2.3](https://tools.ietf.org/html/rfc6895#section-2.3) — Domain
///   Name System (DNS) IANA Considerations (April 2013)
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum ErrorCode {

    /// `FormErr` — The server was unable to interpret the query.
    FormatError,

    /// `ServFail` — There was a problem with the server.
    ServerFailure,

    /// `NXDomain` — The domain name referenced in the query does not exist.
    NXDomain,

    /// `NotImp` — The server does not support one of the requested features.
    NotImplemented,

    /// `Refused` — The server was able to interpret the query, but refused to
    /// fulfil it.
    QueryRefused,

    /// `BADVERS` and `BADSIG` — The server did not accept the EDNS version,
    /// or failed to verify a signature. The same code is used for both.
    BadVersion,

    /// An error code with no currently-defined meaning.
    Other(u16),

    /// An error code within the ‘Reserved for Private Use’ range.
    Private(u16)
}


impl Answer {

    /// Whether this Answer holds a standard record, not a pseudo record.
    pub fn is_standard(&self) -> bool {
        matches!(self, Self::Standard { .. })
    }
}


================================================
FILE: dns/src/wire.rs
================================================
//! Parsing the DNS wire protocol.

pub(crate) use std::io::{Cursor, Read};
pub(crate) use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};

use std::io;
use log::*;

use crate::record::{Record, RecordType, OPT};
use crate::strings::{Labels, ReadLabels, WriteLabels};
use crate::types::*;


impl Request {

    /// Converts this request to a vector of bytes.
    pub fn to_bytes(&self) -> io::Result<Vec<u8>> {
        let mut bytes = Vec::with_capacity(32);

        bytes.write_u16::<BigEndian>(self.transaction_id)?;
        bytes.write_u16::<BigEndian>(self.flags.to_u16())?;

        bytes.write_u16::<BigEndian>(1)?;  // query count
        bytes.write_u16::<BigEndian>(0)?;  // answer count
        bytes.write_u16::<BigEndian>(0)?;  // authority RR count
        bytes.write_u16::<BigEndian>(if self.additional.is_some() { 1 } else { 0 })?;  // additional RR count

        bytes.write_labels(&self.query.qname)?;
        bytes.write_u16::<BigEndian>(self.query.qtype.type_number())?;
        bytes.write_u16::<BigEndian>(self.query.qclass.to_u16())?;

        if let Some(opt) = &self.additional {
            bytes.write_u8(0)?;  // usually a name
            bytes.write_u16::<BigEndian>(OPT::RR_TYPE)?;
            bytes.extend(opt.to_bytes()?);
        }

        Ok(bytes)
    }

    /// Returns the OPT record to be sent as part of requests.
    pub fn additional_record() -> OPT {
        OPT {
            udp_payload_size: 512,
            higher_bits: 0,
            edns0_version: 0,
            flags: 0,
            data: Vec::new(),
        }
    }
}


impl Response {

    /// Reads bytes off of the given slice, parsing them into a response.
    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
    pub fn from_bytes(bytes: &[u8]) -> Result<Self, WireError> {
        info!("Parsing response");
        trace!("Bytes -> {:?}", bytes);
        let mut c = Cursor::new(bytes);

        let transaction_id = c.read_u16::<BigEndian>()?;
        trace!("Read txid -> {:?}", transaction_id);

        let flags = Flags::from_u16(c.read_u16::<BigEndian>()?);
        trace!("Read flags -> {:#?}", flags);

        let query_count      = c.read_u16::<BigEndian>()?;
        let answer_count     = c.read_u16::<BigEndian>()?;
        let authority_count  = c.read_u16::<BigEndian>()?;
        let additional_count = c.read_u16::<BigEndian>()?;

        // We can pre-allocate these vectors by giving them an initial
        // capacity based on the count fields. But because the count fields
        // are user-controlled (with a maximum of 2^16 - 1) we cannot trust
        // them _entirely_, so cap the pre-allocation if the count looks
        // arbitrarily large (9 seems about right).

        let mut queries = Vec::with_capacity(usize::from(query_count.min(9)));
        debug!("Reading {}x query from response", query_count);
        for _ in 0 .. query_count {
            let (qname, _) = c.read_labels()?;
            queries.push(Query::from_bytes(qname, &mut c)?);
        }

        let mut answers = Vec::with_capacity(usize::from(answer_count.min(9)));
        debug!("Reading {}x answer from response", answer_count);
        for _ in 0 .. answer_count {
            let (qname, _) = c.read_labels()?;
            answers.push(Answer::from_bytes(qname, &mut c)?);
        }

        let mut authorities = Vec::with_capacity(usize::from(authority_count.min(9)));
        debug!("Reading {}x authority from response", authority_count);
        for _ in 0 .. authority_count {
            let (qname, _) = c.read_labels()?;
            authorities.push(Answer::from_bytes(qname, &mut c)?);
        }

        let mut additionals = Vec::with_capacity(usize::from(additional_count.min(9)));
        debug!("Reading {}x additional answer from response", additional_count);
        for _ in 0 .. additional_count {
            let (qname, _) = c.read_labels()?;
            additionals.push(Answer::from_bytes(qname, &mut c)?);
        }

        Ok(Self { transaction_id, flags, queries, answers, authorities, additionals })
    }
}


impl Query {

    /// Reads bytes from the given cursor, and parses them into a query with
    /// the given domain name.
    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
    fn from_bytes(qname: Labels, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
        let qtype_number = c.read_u16::<BigEndian>()?;
        trace!("Read qtype number -> {:?}", qtype_number );

        let qtype = RecordType::from(qtype_number);
        trace!("Found qtype -> {:?}", qtype );

        let qclass = QClass::from_u16(c.read_u16::<BigEndian>()?);
        trace!("Read qclass -> {:?}", qtype);

        Ok(Self { qtype, qclass, qname })
    }
}


impl Answer {

    /// Reads bytes from the given cursor, and parses them into an answer with
    /// the given domain name.
    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
    fn from_bytes(qname: Labels, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
        let qtype_number = c.read_u16::<BigEndian>()?;
        trace!("Read qtype number -> {:?}", qtype_number );

        if qtype_number == OPT::RR_TYPE {
            let opt = OPT::read(c)?;
            Ok(Self::Pseudo { qname, opt })
        }
        else {
            let qtype = RecordType::from(qtype_number);
            trace!("Found qtype -> {:?}", qtype );

            let qclass = QClass::from_u16(c.read_u16::<BigEndian>()?);
            trace!("Read qclass -> {:?}", qtype);

            let ttl = c.read_u32::<BigEndian>()?;
            trace!("Read TTL -> {:?}", ttl);

            let record_length = c.read_u16::<BigEndian>()?;
            trace!("Read record length -> {:?}", record_length);

            let record = Record::from_bytes(qtype, record_length, c)?;
            Ok(Self::Standard { qclass, qname, record, ttl })
        }
    }
}


impl Record {

    /// Reads at most `len` bytes from the given curser, and parses them into
    /// a record structure depending on the type number, which has already been read.
    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
    fn from_bytes(record_type: RecordType, len: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
        if cfg!(feature = "with_mutagen") {
            warn!("Mutation is enabled!");
        }

        macro_rules! read_record {
            ($record:tt) => { {
                info!("Parsing {} record (type {}, len {})", crate::record::$record::NAME, record_type.type_number(), len);
                Wire::read(len, c).map(Self::$record)
            } }
        }

        match record_type {
            RecordType::A           => read_record!(A),
            RecordType::AAAA        => read_record!(AAAA),
            RecordType::CAA         => read_record!(CAA),
            RecordType::CNAME       => read_record!(CNAME),
            RecordType::EUI48       => read_record!(EUI48),
            RecordType::EUI64       => read_record!(EUI64),
            RecordType::HINFO       => read_record!(HINFO),
            RecordType::LOC         => read_record!(LOC),
            RecordType::MX          => read_record!(MX),
            RecordType::NAPTR       => read_record!(NAPTR),
            RecordType::NS          => read_record!(NS),
            RecordType::OPENPGPKEY  => read_record!(OPENPGPKEY),
            RecordType::PTR         => read_record!(PTR),
            RecordType::SSHFP       => read_record!(SSHFP),
            RecordType::SOA         => read_record!(SOA),
            RecordType::SRV         => read_record!(SRV),
            RecordType::TLSA        => read_record!(TLSA),
            RecordType::TXT         => read_record!(TXT),
            RecordType::URI         => read_record!(URI),

            RecordType::Other(type_number) => {
                let mut bytes = Vec::new();
                for _ in 0 .. len {
                    bytes.push(c.read_u8()?);
                }

                Ok(Self::Other { type_number, bytes })
            }
        }
    }
}


impl QClass {
    fn from_u16(uu: u16) -> Self {
        match uu {
            0x0001 => Self::IN,
            0x0003 => Self::CH,
            0x0004 => Self::HS,
                 _ => Self::Other(uu),
        }
    }

    fn to_u16(self) -> u16 {
        match self {
            Self::IN        => 0x0001,
            Self::CH        => 0x0003,
            Self::HS        => 0x0004,
            Self::Other(uu) => uu,
        }
    }
}


impl Flags {

    /// The set of flags that represents a query packet.
    pub fn query() -> Self {
        Self::from_u16(0b_0000_0001_0000_0000)
    }

    /// The set of flags that represents a successful response.
    pub fn standard_response() -> Self {
        Self::from_u16(0b_1000_0001_1000_0000)
    }

    /// Converts the flags into a two-byte number.
    pub fn to_u16(self) -> u16 {                 // 0123 4567 89AB CDEF
        let mut                          bits  = 0b_0000_0000_0000_0000;
        if self.response               { bits |= 0b_1000_0000_0000_0000; }
        match self.opcode {
            Opcode::Query     =>       { bits |= 0b_0000_0000_0000_0000; }
            Opcode::Other(_)  =>       { unimplemented!(); }
        }
        if self.authoritative          { bits |= 0b_0000_0100_0000_0000; }
        if self.truncated              { bits |= 0b_0000_0010_0000_0000; }
        if self.recursion_desired      { bits |= 0b_0000_0001_0000_0000; }
        if self.recursion_available    { bits |= 0b_0000_0000_1000_0000; }
        // (the Z bit is reserved)               0b_0000_0000_0100_0000
        if self.authentic_data         { bits |= 0b_0000_0000_0010_0000; }
        if self.checking_disabled      { bits |= 0b_0000_0000_0001_0000; }

        bits
    }

    /// Extracts the flags from the given two-byte number.
    pub fn from_u16(bits: u16) -> Self {
        let has_bit = |bit| { bits & bit == bit };

        Self {
            response:               has_bit(0b_1000_0000_0000_0000),
            opcode:                 Opcode::from_bits((bits.to_be_bytes()[0] & 0b_0111_1000) >> 3),
            authoritative:          has_bit(0b_0000_0100_0000_0000),
            truncated:              has_bit(0b_0000_0010_0000_0000),
            recursion_desired:      has_bit(0b_0000_0001_0000_0000),
            recursion_available:    has_bit(0b_0000_0000_1000_0000),
            authentic_data:         has_bit(0b_0000_0000_0010_0000),
            checking_disabled:      has_bit(0b_0000_0000_0001_0000),
            error_code:             ErrorCode::from_bits(bits & 0b_1111),
        }
    }
}


impl Opcode {

    /// Extracts the opcode from this four-bit number, which should have been
    /// extracted from the packet and shifted to be in the range 0–15.
    fn from_bits(bits: u8) -> Self {
        if bits == 0 {
            Self::Query
        }
        else {
            assert!(bits <= 15, "bits {:#08b} out of range", bits);
            Self::Other(bits)
        }
    }
}


impl ErrorCode {

    /// Extracts the rcode from the last four bits of the flags field.
    fn from_bits(bits: u16) -> Option<Self> {
        if (0x0F01 .. 0x0FFF).contains(&bits) {
            return Some(Self::Private(bits));
        }

        match bits {
            0 => None,
            1 => Some(Self::FormatError),
            2 => Some(Self::ServerFailure),
            3 => Some(Self::NXDomain),
            4 => Some(Self::NotImplemented),
            5 => Some(Self::QueryRefused),
           16 => Some(Self::BadVersion),
            n => Some(Self::Other(n)),
        }
    }
}


/// Trait for decoding DNS record structures from bytes read over the wire.
pub trait Wire: Sized {

    /// This record’s type as a string, such as `"A"` or `"CNAME"`.
    const NAME: &'static str;

    /// The number signifying that a record is of this type.
    /// See <https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4>
    const RR_TYPE: u16;

    /// Read at most `len` bytes from the given `Cursor`. This cursor travels
    /// throughout the complete data — by this point, we have read the entire
    /// response into a buffer.
    fn read(len: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError>;
}


/// Something that can go wrong deciphering a record.
#[derive(PartialEq, Debug)]
pub enum WireError {

    /// There was an IO error reading from the cursor.
    /// Almost all the time, this means that the buffer was too short.
    IO,
    // (io::Error is not PartialEq so we don’t propagate it)

    /// When the DNS standard requires records of this type to have a certain
    /// fixed length, but the response specified a different length.
    ///
    /// This error should be returned regardless of the _content_ of the
    /// record, whatever it is.
    WrongRecordLength {

        /// The length of the record’s data, as specified in the packet.
        stated_length: u16,

        /// The length of the record that the DNS specification mandates.
        mandated_length: MandatedLength,
    },

    /// When the length of this record as specified in the packet differs from
    /// the computed length, as determined by reading labels.
    ///
    /// There are two ways, in general, to read arbitrary-length data from a
    /// stream of bytes: length-prefixed (read the length, then read that many
    /// bytes) or sentinel-terminated (keep reading bytes until you read a
    /// certain value, usually zero). The DNS protocol uses both: each
    /// record’s size is specified up-front in the packet, but inside the
    /// record, there exist arbitrary-length strings that must be read until a
    /// zero is read, indicating there is no more string.
    ///
    /// Consider the case of a packet, with a specified length, containing a
    /// string of arbitrary length (such as the CNAME or TXT records). A DNS
    /// client has to deal with this in one of two ways:
    ///
    /// 1. Read exactly the specified length of bytes from the record, raising
    ///    an error if the contents are too short or a string keeps going past
    ///    the length (assume the length is correct but the contents are wrong).
    ///
    /// 2. Read as many bytes from the record as the string requests, raising
    ///    an error if the number of bytes read at the end differs from the
    ///    expected length of the record (assume the length is wrong but the
    ///    contents are correct).
    ///
    /// Note that no matter which way is picked, the record will still be
    /// incorrect — it only impacts the parsing of records that occur after it
    /// in the packet. Knowing which method should be used requires knowing
    /// what caused the DNS packet to be erroneous, which we cannot know.
    ///
    /// dog picks the second way. If a record ends up reading more or fewer
    /// bytes than it is ‘supposed’ to, it will raise this error, but _after_
    /// having read a different number of bytes than the specified length.
    WrongLabelLength {

        /// The length of the record’s data, as specified in the packet.
        stated_length: u16,

        /// The computed length of the record’s data, based on the number of
        /// bytes consumed by reading labels from the packet.
        length_after_labels: u16,
    },

    /// When the data contained a string containing a cycle of pointers.
    /// Contains the vector of indexes that was being checked.
    TooMuchRecursion(Box<[u16]>),

    /// When the data contained a string with a pointer to an index outside of
    /// the packet. Contains the invalid index.
    OutOfBounds(u16),

    /// When a record in the packet contained a version field that specifies
    /// the format of its remaining fields, but this version is too recent to
    /// be supported, so we cannot parse it.
    WrongVersion {

        /// The version of the record layout, as specified in the packet
        stated_version: u8,

        /// The maximum version that this version of dog supports.
        maximum_supported_version: u8,
    }
}

/// The rule for how long a record in a packet should be.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum MandatedLength {

    /// The record should be exactly this many bytes in length.
    Exactly(u16),

    /// The record should be _at least_ this many bytes in length.
    AtLeast(u16),
}

impl From<io::Error> for WireError {
    fn from(ioe: io::Error) -> Self {
        error!("IO error -> {:?}", ioe);
        Self::IO
    }
}


================================================
FILE: dns/tests/wire_building_tests.rs
================================================
use dns::{Request, Flags, Query, Labels, QClass};
use dns::record::RecordType;

use pretty_assertions::assert_eq;


#[test]
fn build_request() {
    let request = Request {
        transaction_id: 0xceac,
        flags: Flags::query(),
        query: Query {
            qname: Labels::encode("rfcs.io").unwrap(),
            qclass: QClass::Other(0x42),
            qtype: RecordType::from(0x1234),
        },
        additional: Some(Request::additional_record()),
    };

    let result = vec![
        0xce, 0xac,  // transaction ID
        0x01, 0x00,  // flags (standard query)
        0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,  // counts (1, 0, 0, 1)

        // query:
        0x04, 0x72, 0x66, 0x63, 0x73, 0x02, 0x69, 0x6f, 0x00,  // qname
        0x12, 0x34,  // type
        0x00, 0x42,  // class

        // OPT record:
        0x00,  // name
        0x00, 0x29,  // type OPT
        0x02, 0x00,  // UDP payload size
        0x00,  // higher bits
        0x00,  // EDNS(0) version
        0x00, 0x00,  // more flags
        0x00, 0x00,  // no data
    ];

    assert_eq!(request.to_bytes().unwrap(), result);
}


================================================
FILE: dns/tests/wire_parsing_tests.rs
================================================
use std::net::Ipv4Addr;

use dns::{Response, Query, Answer, Labels, Flags, Opcode, QClass};
use dns::record::{Record, A, CNAME, OPT, SOA, UnknownQtype, RecordType};

use pretty_assertions::assert_eq;


#[test]
fn parse_nothing() {
    assert!(Response::from_bytes(&[]).is_err());
}


#[test]
fn parse_response_standard() {
    let buf = &[
        0x0d, 0xcd,  // transaction ID
        0x81, 0x80,  // flags (standard query, response, no error)
        0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,  // counts (1, 1, 0, 1)

        // the query:
        0x03, 0x64, 0x6e, 0x73, 0x06, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x03,
        0x64, 0x6f, 0x67, 0x00,  // "dns.lookup.dog."
        0x00, 0x01,  // type A
        0x00, 0x01,  // class IN

        // the answer:
        0xc0, 0x0c,  // to find the name, backtrack to position 0x0c (12)
        0x00, 0x01,  // type A
        0x00, 0x01,  // class IN
        0x00, 0x00, 0x03, 0xa5,  // TTL (933 seconds)
        0x00, 0x04,  // record data length 4
        0x8a, 0x44, 0x75, 0x5e,  // record date (138.68.117.94)

        // the additional:
        0x00,        // no name
        0x00, 0x29,  // type OPT
        0x02, 0x00,  // UDP payload size (512)
        0x00, 0x00,  // higher bits (all 0)
        0x00,        // EDNS version
        0x00, 0x00,  // extra bits (DO bit unset)
        0x00,        // data length 0
    ];

    let response = Response {
        transaction_id: 0x0dcd,
        flags: Flags {
            response: true,
            opcode: Opcode::Query,
            authoritative: false,
            truncated: false,
            recursion_desired: true,
            recursion_available: true,
            authentic_data: false,
            checking_disabled: false,
            error_code: None,
        },
        queries: vec![
            Query {
                qname: Labels::encode("dns.lookup.dog").unwrap(),
                qclass: QClass::IN,
                qtype: RecordType::A,
            },
        ],
        answers: vec![
            Answer::Standard {
                qname: Labels::encode("dns.lookup.dog").unwrap(),
                qclass: QClass::IN,
                ttl: 933,
                record: Record::A(A {
                    address: Ipv4Addr::new(138, 68, 117, 94),
                }),
            }
        ],
        authorities: vec![],
        additionals: vec![
            Answer::Pseudo {
                qname: Labels::root(),
                opt: OPT {
                    udp_payload_size: 512,
                    higher_bits: 0,
                    edns0_version: 0,
                    flags: 0,
                    data: vec![],
                },
            },
        ],
    };

    assert_eq!(Response::from_bytes(buf), Ok(response));
}


#[test]
fn parse_response_with_mixed_string() {
    let buf = &[
        0x06, 0x9f,  // transaction ID
        0x81, 0x80,  // flags (standard query, response, no error)
        0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,  // counts (1, 1, 0, 0)

        // the query:
        0x0d, 0x63, 0x6e, 0x61, 0x6d, 0x65, 0x2d, 0x65, 0x78, 0x61, 0x6d, 0x70,
        0x6c, 0x65, 0x06, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x03, 0x64, 0x6f,
        0x67, 0x00,  // "cname-example.lookup.dog"
        0x00, 0x05,  // type CNAME
        0x00, 0x01,  // class IN

        // the answer:
        0xc0, 0x0c,  // to find the name, backtrack to position 0x0c (12)
        0x00, 0x05,  // type CNAME
        0x00, 0x01,  // class IN
        0x00, 0x00, 0x03, 0x69,  // TTL (873 seconds)
        0x00, 0x06,  // record data length 6
        0x03, 0x64, 0x6e, 0x73, 0xc0, 0x1a,
        // "dns.lookup.dog.", which is "dns." + backtrack to position 0x1a (28)
    ];

    let response = Response {
        transaction_id: 0x069f,
        flags: Flags {
            response: true,
            opcode: Opcode::Query,
            authoritative: false,
            truncated: false,
            recursion_desired: true,
            recursion_available: true,
            authentic_data: false,
            checking_disabled: false,
            error_code: None,
        },
        queries: vec![
            Query {
                qname: Labels::encode("cname-example.lookup.dog").unwrap(),
                qclass: QClass::IN,
                qtype: RecordType::CNAME,
            },
        ],
        answers: vec![
            Answer::Standard {
                qname: Labels::encode("cname-example.lookup.dog").unwrap(),
                qclass: QClass::IN,
                ttl: 873,
                record: Record::CNAME(CNAME {
                    domain: Labels::encode("dns.lookup.dog").unwrap(),
                }),
            }
        ],
        authorities: vec![],
        additionals: vec![],
    };

    assert_eq!(Response::from_bytes(buf), Ok(response));
}


#[test]
fn parse_response_with_multiple_additionals() {

    // This is an artifical amalgam of DNS, not a real-world response!
    let buf = &[
        0xce, 0xac,  // transaction ID
        0x81, 0x80,  // flags (standard query, response, no error)
        0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02,  // counts (1, 1, 1, 2)

        // query:
        0x05, 0x62, 0x73, 0x61, 0x67, 0x6f, 0x02, 0x6d, 0x65, 0x00,  // name
        0x00, 0x01,  // type A
        0x00, 0x01,  // class IN

        // answer:
        0xc0, 0x0c,  // name (backreference)
        0x00, 0x01,  // type A
        0x00, 0x01,  // class IN
        0x00, 0x00, 0x03, 0x77,  // TTL
        0x00, 0x04,  // data length 4
        0x8a, 0x44, 0x75, 0x5e,  // IP address

        // authoritative:
        0x00,  // name
        0x00, 0x06,  // type SOA
        0x00, 0x01,  // class IN
        0xFF, 0xFF, 0xFF, 0xFF,  // TTL (maximum possible!)
        0x00, 0x1B,  // data length
        0x01, 0x61, 0x00,  // primary name server ("a")
        0x02, 0x6d, 0x78, 0x00,  // mailbox ("mx")
        0x78, 0x68, 0x52, 0x2c,  // serial number
        0x00, 0x00, 0x07, 0x08,  // refresh interval
        0x00, 0x00, 0x03, 0x84,  // retry interval
        0x00, 0x09, 0x3a, 0x80,  // expire limit
        0x00, 0x01, 0x51, 0x80,  // minimum TTL

        // additional 1:
        0x00,  // name
        0x00, 0x99,  // unknown type
        0x00, 0x99,  // unknown class
        0x12, 0x34, 0x56, 0x78,  // TTL
        0x00, 0x04,  // data length 4
        0x12, 0x34, 0x56, 0x78,  // data

        // additional 2:
        0x00,  // name
        0x00, 0x29,  // type OPT
        0x02, 0x00,  // UDP payload size
        0x00,  // higher bits
        0x00,  // EDNS(0) version
        0x00, 0x00,  // more flags
        0x00, 0x00,  // no data
    ];

    let response = Response {
        transaction_id: 0xceac,
        flags: Flags::standard_response(),
        queries: vec![
            Query {
                qname: Labels::encode("bsago.me").unwrap(),
                qclass: QClass::IN,
                qtype: RecordType::A,
            },
        ],
        answers: vec![
            Answer::Standard {
                qname: Labels::encode("bsago.me").unwrap(),
                qclass: QClass::IN,
                ttl: 887,
                record: Record::A(A {
                    address: Ipv4Addr::new(138, 68, 117, 94),
                }),
            }
        ],
        authorities: vec![
            Answer::Standard {
                qname: Labels::root(),
                qclass: QClass::IN,
                ttl: 4294967295,
                record: Record::SOA(SOA {
                    mname: Labels::encode("a").unwrap(),
                    rname: Labels::encode("mx").unwrap(),
                    serial: 2020102700,
                    refresh_interval: 1800,
                    retry_interval: 900,
                    expire_limit: 604800,
                    minimum_ttl: 86400,
                }),
            }
        ],
        additionals: vec![
            Answer::Standard {
                qname: Labels::root(),
                qclass: QClass::Other(153),
                ttl: 305419896,
                record: Record::Other {
                    type_number: UnknownQtype::UnheardOf(153),
                    bytes: vec![ 0x12, 0x34, 0x56, 0x78 ],
                },
            },
            Answer::Pseudo {
                qname: Labels::root(),
                opt: OPT {
                    udp_payload_size: 512,
                    higher_bits: 0,
                    edns0_version: 0,
                    flags: 0,
                    data: vec![],
                },
            },
        ],
    };

    assert_eq!(Response::from_bytes(buf), Ok(response));
}


================================================
FILE: dns-transport/Cargo.toml
================================================
[package]
name = "dns-transport"
version = "0.2.0-pre"
authors = ["Benjamin Sago <ogham@bsago.me>"]
edition = "2018"

[lib]
doctest = false
test = false


[dependencies]

# dns wire protocol
dns = { path = "../dns" }

# logging
log = "0.4"

# tls networking
native-tls = { version = "0.2", optional = true }

# http response parsing
httparse = { version = "1.3", optional = true }

rustls = { version = "0.19", optional = true }

webpki = { version = "0.21.0", optional = true }

webpki-roots = { version = "0.21.0", optional = true }

cfg-if = "1"

[features]
default = []  # these are enabled in the main dog crate

with_tls   = []
with_https = ["httparse"]

with_nativetls = ["native-tls"]
with_nativetls_vendored = ["native-tls", "native-tls/vendored"]
with_rustls = ["rustls", "webpki-roots", "webpki"]


================================================
FILE: dns-transport/src/auto.rs
================================================
use log::*;

use dns::{Request, Response};
use super::{Transport, Error, UdpTransport, TcpTransport};


/// The **automatic transport**, which sends DNS wire data using the UDP
/// transport, then tries using the TCP transport if the first one fails
/// because the response wouldn’t fit in a single UDP packet.
///
/// This is the default behaviour for many DNS clients.
pub struct AutoTransport {
    addr: String,
}

impl AutoTransport {

    /// Creates a new automatic transport that connects to the given host.
    pub fn new(addr: String) -> Self {
        Self { addr }
    }
}


impl Transport for AutoTransport {
    fn send(&self, request: &Request) -> Result<Response, Error> {
        let udp_transport = UdpTransport::new(self.addr.clone());
        let udp_response = udp_transport.send(&request)?;

        if ! udp_response.flags.truncated {
            return Ok(udp_response);
        }

        debug!("Truncated flag set, so switching to TCP");

        let tcp_transport = TcpTransport::new(self.addr.clone());
        let tcp_response = tcp_transport.send(&request)?;
        Ok(tcp_response)
    }
}


================================================
FILE: dns-transport/src/error.rs
================================================
/// Something that can go wrong making a DNS request.
#[derive(Debug)]
pub enum Error {

    /// The data in the response did not parse correctly from the DNS wire
    /// protocol format.
    WireError(dns::WireError),

    /// There was a problem with the network making a TCP or UDP request.
    NetworkError(std::io::Error),

    /// Not enough information was received from the server before a `read`
    /// call returned zero bytes.
    TruncatedResponse,

    /// There was a problem making a TLS request.
    #[cfg(feature = "with_nativetls")]
    TlsError(native_tls::Error),

    /// There was a problem _establishing_ a TLS request.
    #[cfg(feature = "with_nativetls")]
    TlsHandshakeError(native_tls::HandshakeError<std::net::TcpStream>),

    /// Provided dns name is not valid
    #[cfg(feature = "with_rustls")]
    RustlsInvalidDnsNameError(webpki::InvalidDNSNameError),

    /// There was a problem decoding the response HTTP headers or body.
    #[cfg(feature = "with_https")]
    HttpError(httparse::Error),

    /// The HTTP response code was something other than 200 OK, along with the
    /// response code text, if present.
    #[cfg(feature = "with_https")]
    WrongHttpStatus(u16, Option<String>),
}


// From impls

impl From<dns::WireError> for Error {
    fn from(inner: dns::WireError) -> Self {
        Self::WireError(inner)
    }
}

impl From<std::io::Error> for Error {
    fn from(inner: std::io::Error) -> Self {
        Self::NetworkError(inner)
    }
}

#[cfg(feature = "with_nativetls")]
impl From<native_tls::Error> for Error {
    fn from(inner: native_tls::Error) -> Self {
        Self::TlsError(inner)
    }
}

#[cfg(feature = "with_nativetls")]
impl From<native_tls::HandshakeError<std::net::TcpStream>> for Error {
    fn from(inner: native_tls::HandshakeError<std::net::TcpStream>) -> Self {
        Self::TlsHandshakeError(inner)
    }
}

#[cfg(feature = "with_rustls")]
impl From<webpki::InvalidDNSNameError> for Error {
    fn from(inner: webpki::InvalidDNSNameError) -> Self {
        Self::RustlsInvalidDnsNameError(inner)
    }
}

#[cfg(feature = "with_https")]
impl From<httparse::Error> for Error {
    fn from(inner: httparse::Error) -> Self {
        Self::HttpError(inner)
    }
}


================================================
FILE: dns-transport/src/https.rs
================================================
#![cfg_attr(not(feature = "https"), allow(unused))]

use std::io::{Read, Write};
use std::net::TcpStream;

use log::*;

use dns::{Request, Response, WireError};
use super::{Transport, Error};

use super::tls_stream;

/// The **HTTPS transport**, which sends DNS wire data inside HTTP packets
/// encrypted with TLS, using TCP.
pub struct HttpsTransport {
    url: String,
}

impl HttpsTransport {

    /// Creates a new HTTPS transport that connects to the given URL.
    pub fn new(url: String) -> Self {
        Self { url }
    }
}

fn find_subsequence(haystack: &[u8], needle: &[u8]) -> Option<usize> {
    haystack.windows(needle.len()).position(|window| window == needle)
}

fn contains_header(buf: &[u8]) -> bool {
    let header_end: [u8; 4] = [ 13, 10, 13, 10 ];
    find_subsequence(buf, &header_end).is_some()
}

use tls_stream::TlsStream;

impl Transport for HttpsTransport {

    #[cfg(any(feature = "with_https"))]
    fn send(&self, request: &Request) -> Result<Response, Error> {
        let (domain, path) = self.split_domain().expect("Invalid HTTPS nameserver");

        info!("Opening TLS socket to {:?}", domain);
        let mut stream = Self::stream(&domain, 443)?;

        debug!("Connected");

        let request_bytes = request.to_bytes().expect("failed to serialise request");
        let mut bytes_to_send = format!("\
            POST {} HTTP/1.1\r\n\
            Host: {}\r\n\
            Content-Type: application/dns-message\r\n\
            Accept: application/dns-message\r\n\
            User-Agent: {}\r\n\
            Content-Length: {}\r\n\r\n",
            path, domain, USER_AGENT, request_bytes.len()).into_bytes();
        bytes_to_send.extend(request_bytes);

        info!("Sending {} bytes of data to {:?} over HTTPS", bytes_to_send.len(), self.url);
        stream.write_all(&bytes_to_send)?;
        debug!("Wrote all bytes");

        info!("Waiting to receive...");
        let mut buf = [0; 4096];
        let mut read_len = stream.read(&mut buf)?;
        while !contains_header(&buf[0..read_len]) {
            if read_len == buf.len() {
                return Err(Error::WireError(WireError::IO));
            }
            read_len += stream.read(&mut buf[read_len..])?;
        }
        let mut expected_len = read_len;
        info!("Received {} bytes of data", read_len);

        let mut headers = [httparse::EMPTY_HEADER; 16];
        let mut response = httparse::Response::new(&mut headers);
        let index: usize = response.parse(&buf)?.unwrap();

        if response.code != Some(200) {
            let reason = response.reason.map(str::to_owned);
            return Err(Error::WrongHttpStatus(response.code.unwrap(), reason));
        }

        for header in response.headers {
            let str_value = String::from_utf8_lossy(header.value);
            debug!("Header {:?} -> {:?}", header.name, str_value);
            if header.name == "Content-Length" {
                let content_length: usize = str_value.parse().unwrap();
                expected_len = index + content_length;
            }
        }

        while read_len < expected_len {
            if read_len == buf.len() {
                return Err(Error::WireError(WireError::IO));
            }
            read_len += stream.read(&mut buf[read_len..])?;
        }

        let body = &buf[index .. read_len];
        debug!("HTTP body has {} bytes", body.len());
        let response = Response::from_bytes(&body)?;
        Ok(response)
    }

    #[cfg(not(feature = "with_https"))]
    fn send(&self, request: &Request) -> Result<Response, Error> {
        unreachable!("HTTPS feature disabled")
    }
}

impl HttpsTransport {
    fn split_domain(&self) -> Option<(&str, &str)> {
        if let Some(sp) = self.url.strip_prefix("https://") {
            if let Some(colon_index) = sp.find('/') {
                return Some((&sp[.. colon_index], &sp[colon_index ..]));
            }
        }

        None
    }
}

/// The User-Agent header sent with HTTPS requests.
static USER_AGENT: &str = concat!("dog/", env!("CARGO_PKG_VERSION"));



================================================
FILE: dns-transport/src/lib.rs
================================================
//! All the DNS transport types.

#![warn(deprecated_in_future)]
#![warn(future_incompatible)]
#![warn(missing_copy_implementations)]
#![warn(missing_docs)]
#![warn(nonstandard_style)]
#![warn(rust_2018_compatibility)]
#![warn(rust_2018_idioms)]
#![warn(single_use_lifetimes)]
#![warn(trivial_casts, trivial_numeric_casts)]
#![warn(unused)]

#![warn(clippy::all, clippy::pedantic)]
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::must_use_candidate)]
#![allow(clippy::option_if_let_else)]
#![allow(clippy::pub_enum_variant_names)]
#![allow(clippy::wildcard_imports)]

#![deny(clippy::cast_possible_truncation)]
#![deny(clippy::cast_lossless)]
#![deny(clippy::cast_possible_wrap)]
#![deny(clippy::cast_sign_loss)]
#![deny(unsafe_code)]


mod auto;
pub use self::auto::AutoTransport;

mod udp;
pub use self::udp::UdpTransport;

mod tcp;
pub use self::tcp::TcpTransport;

mod tls;
pub use self::tls::TlsTransport;

mod https;
pub use self::https::HttpsTransport;

mod error;

mod tls_stream;

pub use self::error::Error;

/// The trait implemented by all transport types.
pub trait Transport {

    /// Convert the request to bytes, send it over the network, wait for a
    /// response, deserialise it from bytes, and return it, asynchronously.
    ///
    /// # Errors
    ///
    /// Returns an `Error` error if there’s an I/O error sending or
    /// receiving data, or the DNS packet in the response contained invalid
    /// bytes and failed to parse, or if there was a protocol-level error for
    /// the TLS and HTTPS transports.
    fn send(&self, request: &dns::Request) -> Result<dns::Response, Error>;
}


================================================
FILE: dns-transport/src/tcp.rs
================================================
use std::convert::TryFrom;
use std::net::TcpStream;
use std::io::{Read, Write};

use log::*;

use dns::{Request, Response};
use super::{Transport, Error};


/// The **TCP transport**, which sends DNS wire data over a TCP stream.
///
/// # References
///
/// - [RFC 1035 §4.2.2](https://tools.ietf.org/html/rfc1035) — Domain Names,
///   Implementation and Specification (November 1987)
/// - [RFC 7766](https://tools.ietf.org/html/rfc1035) — DNS Transport over
///   TCP, Implementation Requirements (March 2016)
pub struct TcpTransport {
    addr: String,
}

impl TcpTransport {

    /// Creates a new TCP transport that connects to the given host.
    pub fn new(addr: String) -> Self {
        Self { addr }
    }
}


impl Transport for TcpTransport {
    fn send(&self, request: &Request) -> Result<Response, Error> {
        info!("Opening TCP stream");
        let mut stream =
            if self.addr.contains(':') {
                TcpStream::connect(&*self.addr)?
            }
            else {
                TcpStream::connect((&*self.addr, 53))?
            };
        debug!("Opened");

        // The message is prepended with the length when sent over TCP,
        // so the server knows how long it is (RFC 1035 §4.2.2)
       
Download .txt
gitextract_m0bcnqsg/

├── .github/
│   ├── FUNDING.yml
│   └── ISSUE_TEMPLATE/
│       ├── bug_report.md
│       ├── compilation_error.md
│       ├── config.yml
│       ├── feature_request.md
│       └── question.md
├── .gitignore
├── .rustfmt.toml
├── .travis.yml
├── Cargo.toml
├── Dockerfile
├── Justfile
├── LICENCE
├── README.md
├── build.rs
├── completions/
│   ├── dog.bash
│   ├── dog.fish
│   ├── dog.ps1
│   └── dog.zsh
├── dns/
│   ├── Cargo.toml
│   ├── fuzz/
│   │   ├── .gitignore
│   │   ├── Cargo.toml
│   │   └── fuzz_targets/
│   │       └── fuzz_parsing.rs
│   ├── src/
│   │   ├── lib.rs
│   │   ├── record/
│   │   │   ├── a.rs
│   │   │   ├── aaaa.rs
│   │   │   ├── caa.rs
│   │   │   ├── cname.rs
│   │   │   ├── eui48.rs
│   │   │   ├── eui64.rs
│   │   │   ├── hinfo.rs
│   │   │   ├── loc.rs
│   │   │   ├── mod.rs
│   │   │   ├── mx.rs
│   │   │   ├── naptr.rs
│   │   │   ├── ns.rs
│   │   │   ├── openpgpkey.rs
│   │   │   ├── opt.rs
│   │   │   ├── others.rs
│   │   │   ├── ptr.rs
│   │   │   ├── soa.rs
│   │   │   ├── srv.rs
│   │   │   ├── sshfp.rs
│   │   │   ├── tlsa.rs
│   │   │   ├── txt.rs
│   │   │   └── uri.rs
│   │   ├── strings.rs
│   │   ├── types.rs
│   │   └── wire.rs
│   └── tests/
│       ├── wire_building_tests.rs
│       └── wire_parsing_tests.rs
├── dns-transport/
│   ├── Cargo.toml
│   └── src/
│       ├── auto.rs
│       ├── error.rs
│       ├── https.rs
│       ├── lib.rs
│       ├── tcp.rs
│       ├── tls.rs
│       ├── tls_stream.rs
│       └── udp.rs
├── man/
│   └── dog.1.md
├── src/
│   ├── colours.rs
│   ├── connect.rs
│   ├── hints.rs
│   ├── logger.rs
│   ├── main.rs
│   ├── options.rs
│   ├── output.rs
│   ├── requests.rs
│   ├── resolve.rs
│   ├── table.rs
│   ├── txid.rs
│   └── usage.txt
└── xtests/
    ├── README.md
    ├── features/
    │   ├── none.toml
    │   └── outputs/
    │       ├── disabled_https.txt
    │       └── disabled_tls.txt
    ├── live/
    │   ├── badssl.toml
    │   ├── basics.toml
    │   ├── bins.toml
    │   ├── https.toml
    │   ├── json.toml
    │   ├── tcp.toml
    │   ├── tls.toml
    │   └── udp.toml
    ├── madns/
    │   ├── a-records.toml
    │   ├── aaaa-records.toml
    │   ├── caa-records.toml
    │   ├── cname-records.toml
    │   ├── eui48-records.toml
    │   ├── eui64-records.toml
    │   ├── hinfo-records.toml
    │   ├── loc-records.toml
    │   ├── mx-records.toml
    │   ├── naptr-records.toml
    │   ├── ns-records.toml
    │   ├── openpgpkey-records.toml
    │   ├── opt-records.toml
    │   ├── outputs/
    │   │   ├── a.example.ansitxt
    │   │   ├── a.example.json
    │   │   ├── aaaa.example.ansitxt
    │   │   ├── aaaa.example.json
    │   │   ├── ansi.str.example.ansitxt
    │   │   ├── ansi.str.example.json
    │   │   ├── bad-regex.naptr.example.ansitxt
    │   │   ├── bad-utf8.caa.example.ansitxt
    │   │   ├── bad-utf8.caa.example.json
    │   │   ├── bad-utf8.hinfo.example.ansitxt
    │   │   ├── bad-utf8.hinfo.example.json
    │   │   ├── bad-utf8.naptr.invalid.ansitxt
    │   │   ├── bad-utf8.naptr.invalid.json
    │   │   ├── bad-utf8.txt.example.ansitxt
    │   │   ├── bad-utf8.txt.example.json
    │   │   ├── bad-utf8.uri.example.ansitxt
    │   │   ├── bad-utf8.uri.example.json
    │   │   ├── caa.example.ansitxt
    │   │   ├── caa.example.json
    │   │   ├── cname.example.ansitxt
    │   │   ├── cname.example.json
    │   │   ├── critical.caa.example.ansitxt
    │   │   ├── critical.caa.example.json
    │   │   ├── do-flag.opt.example.ansitxt
    │   │   ├── do-flag.opt.example.json
    │   │   ├── eui48.example.ansitxt
    │   │   ├── eui48.example.json
    │   │   ├── eui64.example.ansitxt
    │   │   ├── eui64.example.json
    │   │   ├── far-negative-latitude.loc.invalid.ansitxt
    │   │   ├── far-negative-latitude.loc.invalid.json
    │   │   ├── far-negative-longitude.loc.invalid.ansitxt
    │   │   ├── far-negative-longitude.loc.invalid.json
    │   │   ├── far-positive-latitude.loc.invalid.ansitxt
    │   │   ├── far-positive-latitude.loc.invalid.json
    │   │   ├── far-positive-longitude.loc.invalid.ansitxt
    │   │   ├── far-positive-longitude.loc.invalid.json
    │   │   ├── hinfo.example.ansitxt
    │   │   ├── hinfo.example.json
    │   │   ├── loc.example.ansitxt
    │   │   ├── loc.example.json
    │   │   ├── mx.example.ansitxt
    │   │   ├── mx.example.json
    │   │   ├── named.opt.invalid.ansitxt
    │   │   ├── named.opt.invalid.json
    │   │   ├── naptr.example.ansitxt
    │   │   ├── naptr.example.json
    │   │   ├── newline.str.example.ansitxt
    │   │   ├── newline.str.example.json
    │   │   ├── ns.example.ansitxt
    │   │   ├── ns.example.json
    │   │   ├── null.str.example.ansitxt
    │   │   ├── null.str.example.json
    │   │   ├── openpgpkey.example.ansitxt
    │   │   ├── openpgpkey.example.json
    │   │   ├── opt.example.ansitxt
    │   │   ├── opt.example.json
    │   │   ├── other-flags.opt.example.ansitxt
    │   │   ├── other-flags.opt.example.json
    │   │   ├── others.caa.example.ansitxt
    │   │   ├── others.caa.example.json
    │   │   ├── ptr.example.ansitxt
    │   │   ├── ptr.example.json
    │   │   ├── slash.uri.example.ansitxt
    │   │   ├── soa.example.ansitxt
    │   │   ├── soa.example.json
    │   │   ├── srv.example.ansitxt
    │   │   ├── srv.example.json
    │   │   ├── sshfp.example.ansitxt
    │   │   ├── sshfp.example.json
    │   │   ├── tab.str.example.ansitxt
    │   │   ├── tab.str.example.json
    │   │   ├── tlsa.example.ansitxt
    │   │   ├── tlsa.example.json
    │   │   ├── txt.example.ansitxt
    │   │   ├── txt.example.json
    │   │   ├── upperbit.str.example.ansitxt
    │   │   ├── upperbit.str.example.json
    │   │   ├── uri.example.ansitxt
    │   │   ├── uri.example.json
    │   │   ├── utf8.caa.example.ansitxt
    │   │   ├── utf8.caa.example.json
    │   │   ├── utf8.hinfo.example.ansitxt
    │   │   ├── utf8.hinfo.example.json
    │   │   ├── utf8.naptr.invalid.ansitxt
    │   │   ├── utf8.naptr.invalid.json
    │   │   ├── utf8.txt.example.ansitxt
    │   │   ├── utf8.txt.example.json
    │   │   ├── utf8.uri.example.ansitxt
    │   │   └── utf8.uri.example.json
    │   ├── protocol-chars.toml
    │   ├── protocol-compression.toml
    │   ├── protocol-error-codes.toml
    │   ├── ptr-records.toml
    │   ├── soa-records.toml
    │   ├── srv-records.toml
    │   ├── sshfp-records.toml
    │   ├── tlsa-records.toml
    │   ├── txt-records.toml
    │   └── uri-records.toml
    └── options/
        ├── errors.toml
        ├── help.toml
        └── outputs/
            ├── huge-domain.txt
            ├── invalid-argument.txt
            ├── invalid-protocol-tweak.txt
            ├── invalid-query-class.txt
            ├── invalid-query-type.txt
            ├── missing-nameserver.txt
            ├── missing-parameter.txt
            └── opt-query.txt
Download .txt
SYMBOL INDEX (500 symbols across 47 files)

FILE: build.rs
  function main (line 25) | fn main() -> io::Result<()> {
  function convert_codes (line 70) | fn convert_codes(input: &str) -> String {
  function strip_codes (line 75) | fn strip_codes(input: &str) -> String {
  function git_hash (line 88) | fn git_hash() -> String {
  function is_development_version (line 102) | fn is_development_version() -> bool {
  function is_debug_build (line 107) | fn is_debug_build() -> bool {
  function cargo_version (line 112) | fn cargo_version() -> String {
  function version_string (line 117) | fn version_string() -> String {
  function feature_enabled (line 129) | fn feature_enabled(name: &str) -> bool {
  function nonstandard_features_string (line 136) | fn nonstandard_features_string() -> String {
  function build_date (line 156) | fn build_date() -> String {

FILE: dns-transport/src/auto.rs
  type AutoTransport (line 12) | pub struct AutoTransport {
    method new (line 19) | pub fn new(addr: String) -> Self {
  method send (line 26) | fn send(&self, request: &Request) -> Result<Response, Error> {

FILE: dns-transport/src/error.rs
  type Error (line 3) | pub enum Error {
    method from (line 42) | fn from(inner: dns::WireError) -> Self {
    method from (line 48) | fn from(inner: std::io::Error) -> Self {
    method from (line 55) | fn from(inner: native_tls::Error) -> Self {
    method from (line 62) | fn from(inner: native_tls::HandshakeError<std::net::TcpStream>) -> Self {
    method from (line 69) | fn from(inner: webpki::InvalidDNSNameError) -> Self {
    method from (line 76) | fn from(inner: httparse::Error) -> Self {

FILE: dns-transport/src/https.rs
  type HttpsTransport (line 15) | pub struct HttpsTransport {
    method new (line 22) | pub fn new(url: String) -> Self {
    method split_domain (line 114) | fn split_domain(&self) -> Option<(&str, &str)> {
  function find_subsequence (line 27) | fn find_subsequence(haystack: &[u8], needle: &[u8]) -> Option<usize> {
  function contains_header (line 31) | fn contains_header(buf: &[u8]) -> bool {
  method send (line 41) | fn send(&self, request: &Request) -> Result<Response, Error> {
  method send (line 108) | fn send(&self, request: &Request) -> Result<Response, Error> {

FILE: dns-transport/src/lib.rs
  type Transport (line 50) | pub trait Transport {
    method send (line 61) | fn send(&self, request: &dns::Request) -> Result<dns::Response, Error>;

FILE: dns-transport/src/tcp.rs
  type TcpTransport (line 19) | pub struct TcpTransport {
    method new (line 26) | pub fn new(addr: String) -> Self {
    method prefix_with_length (line 63) | pub(crate) fn prefix_with_length(bytes: &mut Vec<u8>) {
    method length_prefixed_read (line 81) | pub(crate) fn length_prefixed_read(stream: &mut impl Read) -> Result<V...
  method send (line 33) | fn send(&self, request: &Request) -> Result<Response, Error> {

FILE: dns-transport/src/tls.rs
  type TlsTransport (line 15) | pub struct TlsTransport {
    method new (line 22) | pub fn new(addr: String) -> Self {
    method sni_domain (line 73) | fn sni_domain(&self) -> &str {
  method send (line 32) | fn send(&self, request: &Request) -> Result<Response, Error> {
  method send (line 67) | fn send(&self, request: &Request) -> Result<Response, Error> {

FILE: dns-transport/src/tls_stream.rs
  function stream_nativetls (line 7) | fn stream_nativetls(domain: &str, port: u16) -> Result<native_tls::TlsSt...
  function stream_rustls (line 14) | fn stream_rustls(domain: &str, port: u16) -> Result<rustls::StreamOwned<...
  type TlsStream (line 31) | pub trait TlsStream<S: std::io::Read + std::io::Write> {
    method stream (line 32) | fn stream(domain: &str, port: u16) -> Result<S, Error>;

FILE: dns-transport/src/udp.rs
  type UdpTransport (line 15) | pub struct UdpTransport {
    method new (line 22) | pub fn new(addr: String) -> Self {
  method send (line 29) | fn send(&self, request: &Request) -> Result<Response, Error> {

FILE: dns/src/record/a.rs
  type A (line 15) | pub struct A {
  constant NAME (line 22) | const NAME: &'static str = "A";
  constant RR_TYPE (line 23) | const RR_TYPE: u16 = 1;
  method read (line 26) | fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireE...
  function parses (line 50) | fn parses() {
  function record_too_short (line 60) | fn record_too_short() {
  function record_too_long (line 70) | fn record_too_long() {
  function record_empty (line 81) | fn record_empty() {
  function buffer_ends_abruptly (line 87) | fn buffer_ends_abruptly() {

FILE: dns/src/record/aaaa.rs
  type AAAA (line 15) | pub struct AAAA {
  constant NAME (line 22) | const NAME: &'static str = "AAAA";
  constant RR_TYPE (line 23) | const RR_TYPE: u16 = 28;
  method read (line 26) | fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireE...
  function parses (line 50) | fn parses() {
  function record_too_long (line 61) | fn record_too_long() {
  function record_too_short (line 73) | fn record_too_short() {
  function record_empty (line 83) | fn record_empty() {
  function buffer_ends_abruptly (line 89) | fn buffer_ends_abruptly() {

FILE: dns/src/record/caa.rs
  type CAA (line 15) | pub struct CAA {
  constant NAME (line 28) | const NAME: &'static str = "CAA";
  constant RR_TYPE (line 29) | const RR_TYPE: u16 = 257;
  method read (line 32) | fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireE...
  function parses_non_critical (line 69) | fn parses_non_critical() {
  function parses_critical (line 86) | fn parses_critical() {
  function ignores_other_flags (line 103) | fn ignores_other_flags() {
  function record_empty (line 120) | fn record_empty() {
  function buffer_ends_abruptly (line 126) | fn buffer_ends_abruptly() {

FILE: dns/src/record/cname.rs
  type CNAME (line 14) | pub struct CNAME {
  constant NAME (line 21) | const NAME: &'static str = "CNAME";
  constant RR_TYPE (line 22) | const RR_TYPE: u16 = 5;
  method read (line 25) | fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireE...
  function parses (line 47) | fn parses() {
  function incorrect_record_length (line 60) | fn incorrect_record_length() {
  function record_empty (line 71) | fn record_empty() {
  function buffer_ends_abruptly (line 77) | fn buffer_ends_abruptly() {

FILE: dns/src/record/eui48.rs
  type EUI48 (line 14) | pub struct EUI48 {
    method formatted_address (line 44) | pub fn formatted_address(self) -> String {
  constant NAME (line 21) | const NAME: &'static str = "EUI48";
  constant RR_TYPE (line 22) | const RR_TYPE: u16 = 108;
  method read (line 25) | fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireE...
  function parses (line 58) | fn parses() {
  function record_too_short (line 68) | fn record_too_short() {
  function record_too_long (line 78) | fn record_too_long() {
  function record_empty (line 89) | fn record_empty() {
  function buffer_ends_abruptly (line 95) | fn buffer_ends_abruptly() {
  function hex_rep (line 105) | fn hex_rep() {

FILE: dns/src/record/eui64.rs
  type EUI64 (line 14) | pub struct EUI64 {
    method formatted_address (line 44) | pub fn formatted_address(self) -> String {
  constant NAME (line 21) | const NAME: &'static str = "EUI64";
  constant RR_TYPE (line 22) | const RR_TYPE: u16 = 109;
  method read (line 25) | fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireE...
  function parses (line 58) | fn parses() {
  function record_too_short (line 68) | fn record_too_short() {
  function record_too_long (line 78) | fn record_too_long() {
  function record_empty (line 89) | fn record_empty() {
  function buffer_ends_abruptly (line 95) | fn buffer_ends_abruptly() {
  function hex_rep (line 105) | fn hex_rep() {

FILE: dns/src/record/hinfo.rs
  type HINFO (line 18) | pub struct HINFO {
  constant NAME (line 28) | const NAME: &'static str = "HINFO";
  constant RR_TYPE (line 29) | const RR_TYPE: u16 = 13;
  method read (line 32) | fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireE...
  function parses (line 67) | fn parses() {
  function incorrect_record_length (line 85) | fn incorrect_record_length() {
  function record_empty (line 98) | fn record_empty() {
  function buffer_ends_abruptly (line 104) | fn buffer_ends_abruptly() {

FILE: dns/src/record/loc.rs
  type LOC (line 16) | pub struct LOC {
  type Size (line 45) | pub struct Size {
    method from_u8 (line 131) | fn from_u8(input: u8) -> Self {
    method fmt (line 194) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  type Position (line 52) | pub struct Position {
    method from_u32 (line 146) | fn from_u32(mut input: u32, vertical: bool) -> Option<Self> {
    method fmt (line 200) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  type Altitude (line 62) | pub struct Altitude {
    method from_u32 (line 183) | fn from_u32(input: u32) -> Self {
    method fmt (line 227) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  type Direction (line 70) | pub enum Direction {
    method fmt (line 216) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  constant NAME (line 78) | const NAME: &'static str = "LOC";
  constant RR_TYPE (line 79) | const RR_TYPE: u16 = 29;
  method read (line 82) | fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireE...
  function parses (line 246) | fn parses() {
  function record_too_short (line 269) | fn record_too_short() {
  function record_too_long (line 280) | fn record_too_long() {
  function more_recent_version (line 297) | fn more_recent_version() {
  function record_empty (line 308) | fn record_empty() {
  function buffer_ends_abruptly (line 314) | fn buffer_ends_abruptly() {
  function zeroes (line 331) | fn zeroes() {
  function ones (line 337) | fn ones() {
  function schfourteen_teen (line 343) | fn schfourteen_teen() {
  function ones_but_bits_this_time (line 349) | fn ones_but_bits_this_time() {
  function meridian (line 364) | fn meridian() {
  function meridian_plus_one (line 370) | fn meridian_plus_one() {
  function meridian_minus_one (line 376) | fn meridian_minus_one() {
  function equator (line 382) | fn equator() {
  function equator_plus_one (line 388) | fn equator_plus_one() {
  function equator_minus_one (line 394) | fn equator_minus_one() {
  function some_latitude (line 402) | fn some_latitude() {
  function some_longitude (line 408) | fn some_longitude() {
  function the_north_pole (line 416) | fn the_north_pole() {
  function the_north_pole_plus_one (line 422) | fn the_north_pole_plus_one() {
  function the_south_pole (line 428) | fn the_south_pole() {
  function the_south_pole_minus_one (line 434) | fn the_south_pole_minus_one() {
  function the_far_east (line 440) | fn the_far_east() {
  function the_far_east_plus_one (line 446) | fn the_far_east_plus_one() {
  function the_far_west (line 452) | fn the_far_west() {
  function the_far_west_minus_one (line 458) | fn the_far_west_minus_one() {
  function base_level (line 471) | fn base_level() {
  function up_high (line 477) | fn up_high() {
  function down_low (line 483) | fn down_low() {
  function with_decimal (line 489) | fn with_decimal() {

FILE: dns/src/record/mod.rs
  type Record (line 74) | pub enum Record {
  type RecordType (line 112) | pub enum RecordType {
    method from (line 138) | fn from(type_number: u16) -> Self {
    method from_type_name (line 177) | pub fn from_type_name(type_name: &str) -> Option<Self> {
    method type_number (line 211) | pub fn type_number(self) -> u16 {

FILE: dns/src/record/mx.rs
  type MX (line 15) | pub struct MX {
  constant NAME (line 26) | const NAME: &'static str = "MX";
  constant RR_TYPE (line 27) | const RR_TYPE: u16 = 15;
  method read (line 30) | fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireE...
  function parses (line 56) | fn parses() {
  function incorrect_record_length (line 71) | fn incorrect_record_length() {
  function record_empty (line 83) | fn record_empty() {
  function buffer_ends_abruptly (line 89) | fn buffer_ends_abruptly() {

FILE: dns/src/record/naptr.rs
  type NAPTR (line 16) | pub struct NAPTR {
  constant NAME (line 40) | const NAME: &'static str = "NAPTR";
  constant RR_TYPE (line 41) | const RR_TYPE: u16 = 35;
  method read (line 44) | fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireE...
  function parses (line 100) | fn parses() {
  function incorrect_length (line 128) | fn incorrect_length() {
  function record_empty (line 146) | fn record_empty() {
  function buffer_ends_abruptly (line 152) | fn buffer_ends_abruptly() {

FILE: dns/src/record/ns.rs
  type NS (line 15) | pub struct NS {
  constant NAME (line 22) | const NAME: &'static str = "NS";
  constant RR_TYPE (line 23) | const RR_TYPE: u16 = 2;
  method read (line 26) | fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireE...
  function parses (line 48) | fn parses() {
  function incorrect_record_length (line 62) | fn incorrect_record_length() {
  function record_empty (line 73) | fn record_empty() {
  function buffer_ends_abruptly (line 79) | fn buffer_ends_abruptly() {

FILE: dns/src/record/openpgpkey.rs
  type OPENPGPKEY (line 13) | pub struct OPENPGPKEY {
    method base64_key (line 41) | pub fn base64_key(&self) -> String {
  constant NAME (line 20) | const NAME: &'static str = "OPENPGPKEY";
  constant RR_TYPE (line 21) | const RR_TYPE: u16 = 61;
  method read (line 24) | fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireE...
  function parses (line 53) | fn parses() {
  function one_byte_of_uri (line 65) | fn one_byte_of_uri() {
  function record_empty (line 77) | fn record_empty() {
  function buffer_ends_abruptly (line 83) | fn buffer_ends_abruptly() {

FILE: dns/src/record/opt.rs
  type OPT (line 34) | pub struct OPT {
    constant RR_TYPE (line 55) | pub const RR_TYPE: u16 = 41;
    method read (line 66) | pub fn read(c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
    method to_bytes (line 93) | pub fn to_bytes(&self) -> io::Result<Vec<u8>> {
  function parses_no_data (line 121) | fn parses_no_data() {
  function parses_with_data (line 141) | fn parses_with_data() {
  function record_empty (line 162) | fn record_empty() {
  function buffer_ends_abruptly (line 168) | fn buffer_ends_abruptly() {

FILE: dns/src/record/others.rs
  type UnknownQtype (line 6) | pub enum UnknownQtype {
    method from_type_name (line 19) | pub fn from_type_name(type_name: &str) -> Option<Self> {
    method type_number (line 25) | pub fn type_number(self) -> u16 {
    method from (line 34) | fn from(qtype: u16) -> Self {
    method fmt (line 43) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  function known (line 92) | fn known() {
  function unknown (line 98) | fn unknown() {

FILE: dns/src/record/ptr.rs
  type PTR (line 20) | pub struct PTR {
  constant NAME (line 27) | const NAME: &'static str = "PTR";
  constant RR_TYPE (line 28) | const RR_TYPE: u16 = 12;
  method read (line 31) | fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireE...
  function parses (line 53) | fn parses() {
  function incorrect_record_length (line 66) | fn incorrect_record_length() {
  function record_empty (line 77) | fn record_empty() {
  function buffer_ends_abruptly (line 83) | fn buffer_ends_abruptly() {

FILE: dns/src/record/soa.rs
  type SOA (line 16) | pub struct SOA {
  constant NAME (line 46) | const NAME: &'static str = "SOA";
  constant RR_TYPE (line 47) | const RR_TYPE: u16 = 6;
  method read (line 51) | fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireE...
  function parses (line 95) | fn parses() {
  function incorrect_record_length (line 121) | fn incorrect_record_length() {
  function record_empty (line 139) | fn record_empty() {
  function buffer_ends_abruptly (line 145) | fn buffer_ends_abruptly() {

FILE: dns/src/record/srv.rs
  type SRV (line 15) | pub struct SRV {
  constant NAME (line 33) | const NAME: &'static str = "SRV";
  constant RR_TYPE (line 34) | const RR_TYPE: u16 = 33;
  method read (line 37) | fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireE...
  function parses (line 69) | fn parses() {
  function incorrect_record_length (line 90) | fn incorrect_record_length() {
  function record_empty (line 104) | fn record_empty() {
  function buffer_ends_abruptly (line 110) | fn buffer_ends_abruptly() {

FILE: dns/src/record/sshfp.rs
  type SSHFP (line 14) | pub struct SSHFP {
    method hex_fingerprint (line 58) | pub fn hex_fingerprint(&self) -> String {
  constant NAME (line 30) | const NAME: &'static str = "SSHFP";
  constant RR_TYPE (line 31) | const RR_TYPE: u16 = 44;
  method read (line 34) | fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireE...
  function parses (line 71) | fn parses() {
  function one_byte_fingerprint (line 87) | fn one_byte_fingerprint() {
  function record_too_short (line 103) | fn record_too_short() {
  function record_empty (line 114) | fn record_empty() {
  function buffer_ends_abruptly (line 120) | fn buffer_ends_abruptly() {
  function hex_rep (line 130) | fn hex_rep() {

FILE: dns/src/record/tlsa.rs
  type TLSA (line 15) | pub struct TLSA {
    method hex_certificate_data (line 66) | pub fn hex_certificate_data(&self) -> String {
  constant NAME (line 34) | const NAME: &'static str = "TLSA";
  constant RR_TYPE (line 35) | const RR_TYPE: u16 = 52;
  method read (line 38) | fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireE...
  function parses (line 79) | fn parses() {
  function one_byte_certificate (line 97) | fn one_byte_certificate() {
  function record_too_short (line 115) | fn record_too_short() {
  function record_empty (line 127) | fn record_empty() {
  function buffer_ends_abruptly (line 133) | fn buffer_ends_abruptly() {

FILE: dns/src/record/txt.rs
  type TXT (line 18) | pub struct TXT {
  constant NAME (line 25) | const NAME: &'static str = "TXT";
  constant RR_TYPE (line 26) | const RR_TYPE: u16 = 16;
  method read (line 29) | fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireE...
  function parses_one_iteration (line 80) | fn parses_one_iteration() {
  function parses_two_iterations (line 93) | fn parses_two_iterations() {
  function right_at_the_limit (line 142) | fn right_at_the_limit() {
  function another_message (line 188) | fn another_message() {
  function length_too_short (line 206) | fn length_too_short() {
  function record_empty (line 217) | fn record_empty() {
  function buffer_ends_abruptly (line 223) | fn buffer_ends_abruptly() {

FILE: dns/src/record/uri.rs
  type URI (line 16) | pub struct URI {
  constant NAME (line 32) | const NAME: &'static str = "URI";
  constant RR_TYPE (line 33) | const RR_TYPE: u16 = 256;
  method read (line 36) | fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireE...
  function parses (line 65) | fn parses() {
  function one_byte_of_uri (line 82) | fn one_byte_of_uri() {
  function missing_any_data (line 98) | fn missing_any_data() {
  function record_empty (line 109) | fn record_empty() {
  function buffer_ends_abruptly (line 115) | fn buffer_ends_abruptly() {

FILE: dns/src/strings.rs
  type Labels (line 19) | pub struct Labels {
    method root (line 38) | pub fn root() -> Self {
    method encode (line 44) | pub fn encode(input: &str) -> Result<Self, &str> {
    method len (line 73) | pub fn len(&self) -> usize {
    method extend (line 78) | pub fn extend(&self, other: &Self) -> Self {
    method fmt (line 86) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  function label_to_ascii (line 24) | fn label_to_ascii(label: &str) -> Result<String, unic_idna::Errors> {
  function label_to_ascii (line 30) | fn label_to_ascii(label: &str) -> Result<String, ()> {
  type ReadLabels (line 97) | pub(crate) trait ReadLabels {
    method read_labels (line 100) | fn read_labels(&mut self) -> Result<(Labels, u16), WireError>;
    method read_labels (line 104) | fn read_labels(&mut self) -> Result<(Labels, u16), WireError> {
  type WriteLabels (line 113) | pub(crate) trait WriteLabels {
    method write_labels (line 123) | fn write_labels(&mut self, input: &Labels) -> io::Result<()>;
    method write_labels (line 127) | fn write_labels(&mut self, input: &Labels) -> io::Result<()> {
  constant RECURSION_LIMIT (line 142) | const RECURSION_LIMIT: usize = 8;
  function read_string_recursive (line 149) | fn read_string_recursive(labels: &mut Labels, c: &mut Cursor<&[u8]>, rec...
  function nothing (line 220) | fn nothing() {
  function one_label (line 230) | fn one_label() {
  function two_labels (line 242) | fn two_labels() {
  function label_followed_by_backtrack (line 256) | fn label_followed_by_backtrack() {
  function extremely_long_label (line 272) | fn extremely_long_label() {
  function immediate_recursion (line 284) | fn immediate_recursion() {
  function mutual_recursion (line 294) | fn mutual_recursion() {
  function too_much_recursion (line 307) | fn too_much_recursion() {

FILE: dns/src/types.rs
  type Request (line 12) | pub struct Request {
  type Response (line 33) | pub struct Response {
  type Query (line 57) | pub struct Query {
  type Answer (line 72) | pub enum Answer {
    method is_standard (line 211) | pub fn is_standard(&self) -> bool {
  type QClass (line 106) | pub enum QClass {
  type Flags (line 124) | pub struct Flags {
  type Opcode (line 159) | pub enum Opcode {
  type ErrorCode (line 178) | pub enum ErrorCode {

FILE: dns/src/wire.rs
  method to_bytes (line 17) | pub fn to_bytes(&self) -> io::Result<Vec<u8>> {
  method additional_record (line 42) | pub fn additional_record() -> OPT {
  method from_bytes (line 58) | pub fn from_bytes(bytes: &[u8]) -> Result<Self, WireError> {
  method from_bytes (line 118) | fn from_bytes(qname: Labels, c: &mut Cursor<&[u8]>) -> Result<Self, Wire...
  method from_bytes (line 138) | fn from_bytes(qname: Labels, c: &mut Cursor<&[u8]>) -> Result<Self, Wire...
  method from_bytes (line 171) | fn from_bytes(record_type: RecordType, len: u16, c: &mut Cursor<&[u8]>) ...
  method from_u16 (line 218) | fn from_u16(uu: u16) -> Self {
  method to_u16 (line 227) | fn to_u16(self) -> u16 {
  method query (line 241) | pub fn query() -> Self {
  method standard_response (line 246) | pub fn standard_response() -> Self {
  method to_u16 (line 251) | pub fn to_u16(self) -> u16 {                 // 0123 4567 89AB CDEF
  method from_u16 (line 270) | pub fn from_u16(bits: u16) -> Self {
  method from_bits (line 292) | fn from_bits(bits: u8) -> Self {
  method from_bits (line 307) | fn from_bits(bits: u16) -> Option<Self> {
  type Wire (line 327) | pub trait Wire: Sized {
    constant NAME (line 330) | const NAME: &'static str;
    constant RR_TYPE (line 334) | const RR_TYPE: u16;
    method read (line 339) | fn read(len: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError>;
  type WireError (line 345) | pub enum WireError {
    method from (line 441) | fn from(ioe: io::Error) -> Self {
  type MandatedLength (line 431) | pub enum MandatedLength {

FILE: dns/tests/wire_building_tests.rs
  function build_request (line 8) | fn build_request() {

FILE: dns/tests/wire_parsing_tests.rs
  function parse_nothing (line 10) | fn parse_nothing() {
  function parse_response_standard (line 16) | fn parse_response_standard() {
  function parse_response_with_mixed_string (line 96) | fn parse_response_with_mixed_string() {
  function parse_response_with_multiple_additionals (line 158) | fn parse_response_with_multiple_additionals() {

FILE: src/colours.rs
  type Colours (line 9) | pub struct Colours {
    method pretty (line 43) | pub fn pretty() -> Self {
    method plain (line 78) | pub fn plain() -> Self {

FILE: src/connect.rs
  type TransportType (line 9) | pub enum TransportType {
    method make_transport (line 37) | pub fn make_transport(self, param: String) -> Box<dyn Transport> {

FILE: src/hints.rs
  type LocalHosts (line 18) | pub struct LocalHosts {
    method load (line 26) | pub fn load() -> io::Result<Self> {
    method load (line 33) | pub fn load() -> io::Result<Self> {
    method load (line 40) | pub fn load() -> io::Result<Self> {
    method load_from_file (line 49) | fn load_from_file(file: File) -> io::Result<Self> {
    method contains (line 84) | pub fn contains(&self, hostname_in_query: &dns::Labels) -> bool {

FILE: src/logger.rs
  function configure (line 10) | pub fn configure<T: AsRef<OsStr>>(ev: Option<T>) {
  type Logger (line 36) | struct Logger;
    method enabled (line 41) | fn enabled(&self, _: &log::Metadata<'_>) -> bool {
    method log (line 45) | fn log(&self, record: &log::Record<'_>) {
    method flush (line 53) | fn flush(&self) {
  constant GLOBAL_LOGGER (line 38) | const GLOBAL_LOGGER: &Logger = &Logger;
  function level (line 58) | fn level(level: log::Level) -> ANSIString<'static> {

FILE: src/main.rs
  function main (line 42) | fn main() {
  function run (line 101) | fn run(Options { requests, format, measure_time }: Options) -> i32 {
  function disabled_feature_check (line 180) | fn disabled_feature_check(options: &Options) {
  constant SUCCESS (line 202) | pub const SUCCESS: i32 = 0;
  constant NETWORK_ERROR (line 205) | pub const NETWORK_ERROR: i32 = 1;
  constant NO_SHORT_RESULTS (line 209) | pub const NO_SHORT_RESULTS: i32 = 2;
  constant OPTIONS_ERROR (line 212) | pub const OPTIONS_ERROR: i32 = 3;
  constant SYSTEM_ERROR (line 215) | pub const SYSTEM_ERROR: i32 = 4;

FILE: src/options.rs
  type Options (line 20) | pub struct Options {
    method getopts (line 42) | pub fn getopts<C>(args: C) -> OptionsResult
    method deduce (line 107) | fn deduce(matches: getopts::Matches) -> Result<Self, OptionsError> {
  method deduce (line 118) | fn deduce(matches: getopts::Matches) -> Result<Self, OptionsError> {
  method deduce (line 130) | fn deduce(matches: getopts::Matches) -> Result<Self, OptionsError> {
  method load_transport_types (line 140) | fn load_transport_types(&mut self, matches: &getopts::Matches) {
  method load_named_args (line 158) | fn load_named_args(&mut self, matches: &getopts::Matches) -> Result<(), ...
  method load_free_args (line 197) | fn load_free_args(&mut self, matches: getopts::Matches) -> Result<(), Op...
  method check_for_missing_nameserver (line 229) | fn check_for_missing_nameserver(&self) -> Result<(), OptionsError> {
  method load_fallbacks (line 238) | fn load_fallbacks(&mut self) {
  method add_domain (line 256) | fn add_domain(&mut self, input: &str) -> Result<(), OptionsError> {
  method add_type (line 266) | fn add_type(&mut self, rt: RecordType) {
  method add_nameserver (line 270) | fn add_nameserver(&mut self, input: &str) {
  method add_class (line 274) | fn add_class(&mut self, class: QClass) {
  function is_constant_name (line 279) | fn is_constant_name(argument: &str) -> bool {
  function parse_class_name (line 292) | fn parse_class_name(input: &str) -> Option<QClass> {
  method deduce (line 309) | fn deduce(matches: &getopts::Matches) -> Result<Self, OptionsError> {
  function parse_dec_or_hex (line 324) | fn parse_dec_or_hex(input: &str) -> Option<u16> {
  method deduce (line 351) | fn deduce(matches: &getopts::Matches) -> Self {
  method deduce (line 369) | fn deduce(matches: &getopts::Matches) -> Self {
  method deduce (line 384) | fn deduce(matches: &getopts::Matches) -> Self {
  method deduce (line 392) | fn deduce(matches: &getopts::Matches) -> Result<Self, OptionsError> {
  method deduce (line 409) | fn deduce(matches: &getopts::Matches) -> Result<Self, OptionsError> {
  type OptionsResult (line 448) | pub enum OptionsResult {
    method unwrap (line 527) | fn unwrap(self) -> Options {
  type HelpReason (line 469) | pub enum HelpReason {
  type OptionsError (line 481) | pub enum OptionsError {
    method fmt (line 493) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  method fallbacks (line 515) | fn fallbacks() -> Self {
  function help (line 538) | fn help() {
  function help_no_colour (line 544) | fn help_no_colour() {
  function version (line 550) | fn version() {
  function version_yes_color (line 556) | fn version_yes_color() {
  function fail (line 562) | fn fail() {
  function empty (line 568) | fn empty() {
  function an_unrelated_argument (line 575) | fn an_unrelated_argument() {
  function just_domain (line 583) | fn just_domain() {
  function just_named_domain (line 592) | fn just_named_domain() {
  function domain_and_type (line 601) | fn domain_and_type() {
  function domain_and_type_lowercase (line 611) | fn domain_and_type_lowercase() {
  function domain_and_other_type (line 621) | fn domain_and_other_type() {
  function domain_and_single_domain (line 631) | fn domain_and_single_domain() {
  function domain_and_nameserver (line 641) | fn domain_and_nameserver() {
  function domain_and_class (line 651) | fn domain_and_class() {
  function domain_and_class_lowercase (line 661) | fn domain_and_class_lowercase() {
  function all_free (line 671) | fn all_free() {
  function all_parameters (line 683) | fn all_parameters() {
  function all_parameters_lowercase (line 695) | fn all_parameters_lowercase() {
  function two_types (line 707) | fn two_types() {
  function two_classes (line 717) | fn two_classes() {
  function all_mixed_1 (line 727) | fn all_mixed_1() {
  function all_mixed_2 (line 739) | fn all_mixed_2() {
  function all_mixed_3 (line 750) | fn all_mixed_3() {
  function explicit_numerics (line 761) | fn explicit_numerics() {
  function edns_and_tweaks (line 772) | fn edns_and_tweaks() {
  function two_more_tweaks (line 779) | fn two_more_tweaks() {
  function udp_size (line 786) | fn udp_size() {
  function short_mode (line 792) | fn short_mode() {
  function short_mode_seconds (line 799) | fn short_mode_seconds() {
  function json_output (line 806) | fn json_output() {
  function specific_txid (line 812) | fn specific_txid() {
  function all_transport_types (line 819) | fn all_transport_types() {
  function invalid_named_class (line 830) | fn invalid_named_class() {
  function invalid_named_class_too_big (line 836) | fn invalid_named_class_too_big() {
  function invalid_named_type (line 842) | fn invalid_named_type() {
  function invalid_named_type_too_big (line 848) | fn invalid_named_type_too_big() {
  function invalid_txid (line 854) | fn invalid_txid() {
  function invalid_edns (line 860) | fn invalid_edns() {
  function invalid_tweaks (line 866) | fn invalid_tweaks() {
  function invalid_udp_size (line 872) | fn invalid_udp_size() {
  function invalid_udp_size_size (line 878) | fn invalid_udp_size_size() {
  function invalid_udp_size_missing (line 884) | fn invalid_udp_size_missing() {
  function missing_https_url (line 890) | fn missing_https_url() {
  function opt (line 898) | fn opt() {
  function opt_lowercase (line 904) | fn opt_lowercase() {
  function opt_arg (line 910) | fn opt_arg() {
  function opt_arg_lowercase (line 916) | fn opt_arg_lowercase() {
  function number_parsing (line 924) | fn number_parsing() {

FILE: src/output.rs
  type OutputFormat (line 18) | pub enum OutputFormat {
    method print (line 81) | pub fn print(self, responses: Vec<Response>, duration: Option<Duration...
    method print_error (line 166) | pub fn print_error(self, error: TransportError) {
  type UseColours (line 33) | pub enum UseColours {
    method should_use_colours (line 58) | pub fn should_use_colours(self) -> bool {
    method palette (line 64) | pub fn palette(self) -> Colours {
  type TextFormat (line 47) | pub struct TextFormat {
    method record_payload_summary (line 190) | pub fn record_payload_summary(self, record: Record) -> String {
    method pseudo_record_payload_summary (line 294) | pub fn pseudo_record_payload_summary(self, opt: OPT) -> String {
    method format_duration (line 305) | pub fn format_duration(self, seconds: u32) -> String {
  function format_duration_hms (line 317) | fn format_duration_hms(seconds: u32) -> String {
  function json_queries (line 342) | fn json_queries(queries: Vec<Query>) -> JsonValue {
  function json_answers (line 355) | fn json_answers(answers: Vec<Answer>) -> JsonValue {
  function json_class (line 384) | fn json_class(class: QClass) -> JsonValue {
  function json_record_type_name (line 395) | fn json_record_type_name(record: RecordType) -> JsonValue {
  function json_record_name (line 426) | fn json_record_name(record: &Record) -> JsonValue {
  function json_record_data (line 461) | fn json_record_data(record: Record) -> JsonValue {
  type Ascii (line 602) | struct Ascii<'a>(&'a [u8]);
  function fmt (line 605) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  function print_error_code (line 631) | pub fn print_error_code(rcode: ErrorCode) {
  function erroneous_phase (line 646) | fn erroneous_phase(error: &TransportError) -> &'static str {
  function error_message (line 663) | fn error_message(error: TransportError) -> String {
  function wire_error_message (line 683) | fn wire_error_message(error: WireError) -> String {
  function escape_quotes (line 715) | fn escape_quotes() {
  function escape_backslashes (line 721) | fn escape_backslashes() {
  function escape_lows (line 727) | fn escape_lows() {
  function escape_highs (line 733) | fn escape_highs() {

FILE: src/requests.rs
  type RequestGenerator (line 11) | pub struct RequestGenerator {
    method generate (line 89) | pub fn generate(self) -> Result<Vec<RequestSet>, ResolverLookupError> {
  type Inputs (line 28) | pub struct Inputs {
  type ProtocolTweaks (line 48) | pub struct ProtocolTweaks {
    method set_request_flags (line 149) | pub fn set_request_flags(self, flags: &mut dns::Flags) {
    method set_request_opt_fields (line 165) | pub fn set_request_opt_fields(self, opt: &mut dns::record::OPT) {
  type UseEDNS (line 65) | pub enum UseEDNS {
    method should_send (line 136) | pub fn should_send(self) -> bool {
    method should_show (line 141) | pub fn should_show(self) -> bool {
  type RequestSet (line 83) | pub type RequestSet = (Box<dyn dns_transport::Transport>, Vec<dns::Reque...

FILE: src/resolve.rs
  type ResolverType (line 13) | pub enum ResolverType {
    method obtain (line 28) | pub fn obtain(self) -> Result<Resolver, ResolverLookupError> {
  type Resolver (line 45) | pub struct Resolver {
    method nameserver (line 57) | pub fn nameserver(&self) -> String {
    method name_list (line 63) | pub fn name_list(&self, name: &Labels) -> Vec<Labels> {
  function system_nameservers (line 89) | fn system_nameservers() -> Result<Resolver, ResolverLookupError> {
  function system_nameservers (line 134) | fn system_nameservers() -> Result<Resolver, ResolverLookupError> {
  function system_nameservers (line 210) | fn system_nameservers() -> Result<Resolver, ResolverLookupError> {
  type ResolverLookupError (line 217) | pub enum ResolverLookupError {
    method from (line 238) | fn from(error: io::Error) -> ResolverLookupError {
    method from (line 245) | fn from(error: ipconfig::error::Error) -> ResolverLookupError {
    method fmt (line 251) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {

FILE: src/table.rs
  type Table (line 17) | pub struct Table {
    method new (line 51) | pub fn new(colours: Colours, text_format: TextFormat) -> Self {
    method add_row (line 57) | pub fn add_row(&mut self, answer: Answer, section: Section) {
    method print (line 76) | pub fn print(self, duration: Option<Duration>) {
    method coloured_record_type (line 115) | fn coloured_record_type(&self, record: &Record) -> ANSIString<'static> {
    method max_qtype_len (line 141) | fn max_qtype_len(&self) -> usize {
    method max_qname_len (line 145) | fn max_qname_len(&self) -> usize {
    method max_ttl_len (line 149) | fn max_ttl_len(&self) -> usize {
    method format_section (line 153) | fn format_section(&self, section: Section) -> ANSIString<'static> {
  type Row (line 25) | pub struct Row {
  type Section (line 35) | pub enum Section {

FILE: src/txid.rs
  type TxidGenerator (line 7) | pub enum TxidGenerator {
    method generate (line 18) | pub fn generate(self) -> u16 {
Condensed preview — 208 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (457K chars).
[
  {
    "path": ".github/FUNDING.yml",
    "chars": 14,
    "preview": "github: ogham\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 568,
    "preview": "---\nname: Bug report\nabout: Report a crash, runtime error, or invalid output in dog\n---\n\nIf dog does something unexpecte"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/compilation_error.md",
    "chars": 556,
    "preview": "---\nname: Compilation error\nabout: Report a problem compiling dog\n---\n\nIf dog fails to compile, or if there is a problem"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 27,
    "preview": "blank_issues_enabled: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 77,
    "preview": "---\nname: Feature request\nabout: Request a feature or enhancement to dog\n---\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/question.md",
    "chars": 55,
    "preview": "---\nname: Question\nabout: Ask a question about dog\n---\n"
  },
  {
    "path": ".gitignore",
    "chars": 62,
    "preview": "/target\n/tarpaulin-report.html\nfuzz-*.log\n/cargo-timing*.html\n"
  },
  {
    "path": ".rustfmt.toml",
    "chars": 30,
    "preview": "disable_all_formatting = true\n"
  },
  {
    "path": ".travis.yml",
    "chars": 828,
    "preview": "language: rust\nrust:\n  - 1.45.0\n  - stable\n  - beta\n  - nightly\n\nscript:\n  - cargo build --verbose --workspace\n  - cargo"
  },
  {
    "path": "Cargo.toml",
    "chars": 1622,
    "preview": "[package]\nname = \"dog\"\ndescription = \"A command-line DNS client\"\n\nauthors = [\"Benjamin Sago <ogham@bsago.me>\"]\ncategorie"
  },
  {
    "path": "Dockerfile",
    "chars": 372,
    "preview": "FROM rust as build\n\nWORKDIR /build\nCOPY /src /build/src\nCOPY /dns /build/dns\nCOPY /dns-transport /build/dns-transport\nCO"
  },
  {
    "path": "Justfile",
    "chars": 4824,
    "preview": "all: build test xtests\nall-release: build-release test-release xtests-release\nall-quick: build-quick test-quick xtests-q"
  },
  {
    "path": "LICENCE",
    "chars": 13745,
    "preview": "                      EUROPEAN UNION PUBLIC LICENCE v. 1.2\n                      EUPL © the European Union 2007, 2016\n\nT"
  },
  {
    "path": "README.md",
    "chars": 6346,
    "preview": "<div align=\"center\">\n<h1>dog</h1>\n\n[dog](https://dns.lookup.dog/) is a command-line DNS client.\n\n<a href=\"https://travis"
  },
  {
    "path": "build.rs",
    "chars": 4913,
    "preview": "//! This build script gets run during every build. Its purpose is to put\n//! together the files used for the `--help` an"
  },
  {
    "path": "completions/dog.bash",
    "chars": 1142,
    "preview": "_dog()\n{\n    cur=${COMP_WORDS[COMP_CWORD]}\n    prev=${COMP_WORDS[COMP_CWORD-1]}\n\n    case \"$prev\" in\n        -'?'|--help"
  },
  {
    "path": "completions/dog.fish",
    "chars": 2438,
    "preview": "# Meta options\ncomplete -c dog -s 'v' -l 'version' -d \"Show version of dog\"\ncomplete -c dog -s '?' -l 'help'    -d \"Show"
  },
  {
    "path": "completions/dog.ps1",
    "chars": 3394,
    "preview": "# Note: This works for both Windows PowerShell 5.1 and also PowerShell 7 (Core).\n# But beware that in Windows PowerShell"
  },
  {
    "path": "completions/dog.zsh",
    "chars": 1378,
    "preview": "#compdef dog\n\n__dog() {\n    _arguments \\\n        \"(- 1 *)\"{-v,--version}\"[Show version of dog]\" \\\n        \"(- 1 *)\"{-\\?,"
  },
  {
    "path": "dns/Cargo.toml",
    "chars": 605,
    "preview": "[package]\nname = \"dns\"\nversion = \"0.2.0-pre\"\nauthors = [\"Benjamin Sago <ogham@bsago.me>\"]\nedition = \"2018\"\n\n[lib]\ndoctes"
  },
  {
    "path": "dns/fuzz/.gitignore",
    "chars": 25,
    "preview": "\ntarget\ncorpus\nartifacts\n"
  },
  {
    "path": "dns/fuzz/Cargo.toml",
    "chars": 364,
    "preview": "[package]\nname = \"dns-fuzz\"\nversion = \"0.0.1\"\nauthors = [\"Automatically generated\"]\npublish = false\n\n[package.metadata]\n"
  },
  {
    "path": "dns/fuzz/fuzz_targets/fuzz_parsing.rs",
    "chars": 164,
    "preview": "#![no_main]\n#[macro_use] extern crate libfuzzer_sys;\nextern crate dns;\nuse dns::Response;\n\nfuzz_target!(|data: &[u8]| {\n"
  },
  {
    "path": "dns/src/lib.rs",
    "chars": 1203,
    "preview": "#![warn(deprecated_in_future)]\n#![warn(future_incompatible)]\n#![warn(missing_copy_implementations)]\n#![warn(missing_docs"
  },
  {
    "path": "dns/src/record/a.rs",
    "chars": 2618,
    "preview": "use std::net::Ipv4Addr;\n\nuse log::*;\n\nuse crate::wire::*;\n\n\n/// An **A** record type, which contains an `Ipv4Address`.\n/"
  },
  {
    "path": "dns/src/record/aaaa.rs",
    "chars": 2839,
    "preview": "use std::net::Ipv6Addr;\n\nuse log::*;\n\nuse crate::wire::*;\n\n\n/// A **AAAA** record, which contains an `Ipv6Address`.\n///\n"
  },
  {
    "path": "dns/src/record/caa.rs",
    "chars": 3976,
    "preview": "use log::*;\n\nuse crate::wire::*;\n\n\n/// A **CAA** _(certification authority authorization)_ record. These allow\n/// domai"
  },
  {
    "path": "dns/src/record/cname.rs",
    "chars": 2351,
    "preview": "use log::*;\n\nuse crate::strings::{Labels, ReadLabels};\nuse crate::wire::*;\n\n\n/// A **CNAME** _(canonical name)_ record, "
  },
  {
    "path": "dns/src/record/eui48.rs",
    "chars": 3230,
    "preview": "use log::*;\n\nuse crate::wire::*;\n\n\n/// A **EUI48** record, which holds a six-octet (48-bit) Extended Unique\n/// Identifi"
  },
  {
    "path": "dns/src/record/eui64.rs",
    "chars": 3289,
    "preview": "use log::*;\n\nuse crate::wire::*;\n\n\n/// A **EUI64** record, which holds an eight-octet (64-bit) Extended Unique\n/// Ident"
  },
  {
    "path": "dns/src/record/hinfo.rs",
    "chars": 3436,
    "preview": "use log::*;\n\nuse crate::wire::*;\n\n\n/// A (an?) **HINFO** _(host information)_ record, which contains the CPU and\n/// OS "
  },
  {
    "path": "dns/src/record/loc.rs",
    "chars": 14477,
    "preview": "use std::fmt;\n\nuse log::*;\n\nuse crate::wire::*;\n\n\n/// A **LOC** _(location)_ record, which points to a location on Earth"
  },
  {
    "path": "dns/src/record/mod.rs",
    "chars": 5214,
    "preview": "//! All the DNS record types, as well as how to parse each type.\n\nuse crate::wire::*;\n\n\nmod a;\npub use self::a::A;\n\nmod "
  },
  {
    "path": "dns/src/record/mx.rs",
    "chars": 2808,
    "preview": "use log::*;\n\nuse crate::strings::{Labels, ReadLabels};\nuse crate::wire::*;\n\n\n/// An **MX** _(mail exchange)_ record, whi"
  },
  {
    "path": "dns/src/record/naptr.rs",
    "chars": 5163,
    "preview": "use log::*;\n\nuse crate::strings::{Labels, ReadLabels};\nuse crate::wire::*;\n\n\n/// A **NAPTR** _(naming authority pointer)"
  },
  {
    "path": "dns/src/record/ns.rs",
    "chars": 2474,
    "preview": "use log::*;\n\nuse crate::strings::{Labels, ReadLabels};\nuse crate::wire::*;\n\n\n/// A **NS** _(name server)_ record, which "
  },
  {
    "path": "dns/src/record/openpgpkey.rs",
    "chars": 2296,
    "preview": "use log::*;\n\nuse crate::wire::*;\n\n\n/// A **OPENPGPKEY** record, which holds a PGP key.\n///\n/// # References\n///\n/// - [R"
  },
  {
    "path": "dns/src/record/opt.rs",
    "chars": 5817,
    "preview": "use std::convert::TryFrom;\nuse std::io;\n\nuse log::*;\n\nuse crate::wire::*;\n\n\n/// A **OPT** _(options)_ pseudo-record, whi"
  },
  {
    "path": "dns/src/record/others.rs",
    "chars": 2542,
    "preview": "use std::fmt;\n\n\n/// A number representing a record type dog can’t deal with.\n#[derive(PartialEq, Debug, Copy, Clone)]\npu"
  },
  {
    "path": "dns/src/record/ptr.rs",
    "chars": 2502,
    "preview": "use log::*;\n\nuse crate::strings::{Labels, ReadLabels};\nuse crate::wire::*;\n\n\n/// A **PTR** record, which holds a _pointe"
  },
  {
    "path": "dns/src/record/soa.rs",
    "chars": 5244,
    "preview": "use log::*;\n\nuse crate::strings::{Labels, ReadLabels};\nuse crate::wire::*;\n\n\n/// A **SOA** _(start of authority)_ record"
  },
  {
    "path": "dns/src/record/srv.rs",
    "chars": 3537,
    "preview": "use log::*;\n\nuse crate::strings::{Labels, ReadLabels};\nuse crate::wire::*;\n\n\n/// A **SRV** record, which contains an IP "
  },
  {
    "path": "dns/src/record/sshfp.rs",
    "chars": 3945,
    "preview": "use log::*;\n\nuse crate::wire::*;\n\n\n/// A **SSHFP** _(secure shell fingerprint)_ record, which contains the\n/// fingerpri"
  },
  {
    "path": "dns/src/record/tlsa.rs",
    "chars": 4180,
    "preview": "use log::*;\n\nuse crate::wire::*;\n\n\n/// A **TLSA** _(TLS authentication)_ record, which contains a TLS certificate\n/// (o"
  },
  {
    "path": "dns/src/record/txt.rs",
    "chars": 9688,
    "preview": "use log::*;\n\nuse crate::wire::*;\n\n\n/// A **TXT** record, which holds arbitrary descriptive text.\n///\n/// # Encoding\n///\n"
  },
  {
    "path": "dns/src/record/uri.rs",
    "chars": 3727,
    "preview": "use log::*;\n\nuse crate::wire::*;\n\n\n/// A **URI** record, which holds a URI along with weight and priority values\n/// to "
  },
  {
    "path": "dns/src/strings.rs",
    "chars": 9586,
    "preview": "//! Reading strings from the DNS wire protocol.\n\nuse std::convert::TryFrom;\nuse std::fmt;\nuse std::io::{self, Write};\n\nu"
  },
  {
    "path": "dns/src/types.rs",
    "chars": 5624,
    "preview": "//! DNS packets are traditionally implemented with both the request and\n//! response packets at the same type. After all"
  },
  {
    "path": "dns/src/wire.rs",
    "chars": 16500,
    "preview": "//! Parsing the DNS wire protocol.\n\npub(crate) use std::io::{Cursor, Read};\npub(crate) use byteorder::{BigEndian, ReadBy"
  },
  {
    "path": "dns/tests/wire_building_tests.rs",
    "chars": 1129,
    "preview": "use dns::{Request, Flags, Query, Labels, QClass};\nuse dns::record::RecordType;\n\nuse pretty_assertions::assert_eq;\n\n\n#[te"
  },
  {
    "path": "dns/tests/wire_parsing_tests.rs",
    "chars": 8610,
    "preview": "use std::net::Ipv4Addr;\n\nuse dns::{Response, Query, Answer, Labels, Flags, Opcode, QClass};\nuse dns::record::{Record, A,"
  },
  {
    "path": "dns-transport/Cargo.toml",
    "chars": 808,
    "preview": "[package]\nname = \"dns-transport\"\nversion = \"0.2.0-pre\"\nauthors = [\"Benjamin Sago <ogham@bsago.me>\"]\nedition = \"2018\"\n\n[l"
  },
  {
    "path": "dns-transport/src/auto.rs",
    "chars": 1124,
    "preview": "use log::*;\n\nuse dns::{Request, Response};\nuse super::{Transport, Error, UdpTransport, TcpTransport};\n\n\n/// The **automa"
  },
  {
    "path": "dns-transport/src/error.rs",
    "chars": 2244,
    "preview": "/// Something that can go wrong making a DNS request.\n#[derive(Debug)]\npub enum Error {\n\n    /// The data in the respons"
  },
  {
    "path": "dns-transport/src/https.rs",
    "chars": 4084,
    "preview": "#![cfg_attr(not(feature = \"https\"), allow(unused))]\n\nuse std::io::{Read, Write};\nuse std::net::TcpStream;\n\nuse log::*;\n\n"
  },
  {
    "path": "dns-transport/src/lib.rs",
    "chars": 1626,
    "preview": "//! All the DNS transport types.\n\n#![warn(deprecated_in_future)]\n#![warn(future_incompatible)]\n#![warn(missing_copy_impl"
  },
  {
    "path": "dns-transport/src/tcp.rs",
    "chars": 4181,
    "preview": "use std::convert::TryFrom;\nuse std::net::TcpStream;\nuse std::io::{Read, Write};\n\nuse log::*;\n\nuse dns::{Request, Respons"
  },
  {
    "path": "dns-transport/src/tls.rs",
    "chars": 2242,
    "preview": "#![cfg_attr(not(feature = \"tls\"), allow(unused))]\n\nuse std::net::TcpStream;\nuse std::io::Write;\n\nuse log::*;\n\nuse dns::{"
  },
  {
    "path": "dns-transport/src/tls_stream.rs",
    "chars": 2493,
    "preview": "use std::net::TcpStream;\nuse super::Error;\nuse super::HttpsTransport;\nuse super::TlsTransport;\n\n#[cfg(any(feature = \"wit"
  },
  {
    "path": "dns-transport/src/udp.rs",
    "chars": 1580,
    "preview": "use std::net::{Ipv4Addr, UdpSocket};\n\nuse log::*;\n\nuse dns::{Request, Response};\nuse super::{Transport, Error};\n\n\n/// Th"
  },
  {
    "path": "man/dog.1.md",
    "chars": 6669,
    "preview": "% dog(1) v0.1.0\n\n<!-- This is the dog(1) man page, written in Markdown. -->\n<!-- To generate the roff version, run `just"
  },
  {
    "path": "src/colours.rs",
    "chars": 2050,
    "preview": "//! Colours, colour schemes, and terminal styling.\n\nuse ansi_term::Style;\nuse ansi_term::Color::*;\n\n\n/// The **colours**"
  },
  {
    "path": "src/connect.rs",
    "chars": 1515,
    "preview": "//! Creating DNS transports based on the user’s input arguments.\n\nuse dns_transport::*;\n\n\n/// A **transport type** creat"
  },
  {
    "path": "src/hints.rs",
    "chars": 3045,
    "preview": "//! Hints to the user made before a query is sent, in case the answer that\n//! comes back isn’t what they expect.\n\nuse s"
  },
  {
    "path": "src/logger.rs",
    "chars": 1673,
    "preview": "//! Debug error logging.\n\nuse std::ffi::OsStr;\n\nuse ansi_term::{Colour, ANSIString};\n\n\n/// Sets the internal logger, cha"
  },
  {
    "path": "src/main.rs",
    "chars": 6518,
    "preview": "//! dog, the command-line DNS client.\n\n#![warn(deprecated_in_future)]\n#![warn(future_incompatible)]\n#![warn(missing_copy"
  },
  {
    "path": "src/options.rs",
    "chars": 31521,
    "preview": "//! Command-line option parsing.\n\nuse std::ffi::OsStr;\nuse std::fmt;\n\nuse log::*;\n\nuse dns::{QClass, Labels};\nuse dns::r"
  },
  {
    "path": "src/output.rs",
    "chars": 25161,
    "preview": "//! Text and JSON output.\n\nuse std::fmt;\nuse std::time::Duration;\nuse std::env;\n\nuse dns::{Response, Query, Answer, QCla"
  },
  {
    "path": "src/requests.rs",
    "chars": 5737,
    "preview": "//! Request generation based on the user’s input arguments.\n\nuse crate::connect::TransportType;\nuse crate::resolve::{Res"
  },
  {
    "path": "src/resolve.rs",
    "chars": 8321,
    "preview": "//! Specifying the address of the DNS server to send requests to.\n\nuse std::fmt;\nuse std::io;\n\nuse log::*;\n\nuse dns::Lab"
  },
  {
    "path": "src/table.rs",
    "chars": 5576,
    "preview": "//! Rendering tables of DNS response results.\n\nuse std::time::Duration;\n\nuse ansi_term::ANSIString;\n\nuse dns::Answer;\nus"
  },
  {
    "path": "src/txid.rs",
    "chars": 623,
    "preview": "//! Transaction ID generation.\n\n\n/// A **transaction ID generator** is used to create unique ID numbers to\n/// identify "
  },
  {
    "path": "src/usage.txt",
    "chars": 2344,
    "preview": "\\4mUsage:\\0m\n  \\1mdog\\0m \\1;33m[OPTIONS]\\0m [--] \\32m<arguments>\\0m\n\n\\4mExamples:\\0m\n  \\1mdog\\0m \\32mexample.net\\0m     "
  },
  {
    "path": "xtests/README.md",
    "chars": 2655,
    "preview": "# dog › xtests\n\nThis is dog’s extended test suite. The checks herein form a complete end-to-end set of tests, covering t"
  },
  {
    "path": "xtests/features/none.toml",
    "chars": 771,
    "preview": "# These tests are meant to be run against a dog binary compiled with\n# `--no-default-features`. They will fail otherwise"
  },
  {
    "path": "xtests/features/outputs/disabled_https.txt",
    "chars": 87,
    "preview": "dog: Cannot use '--https': This version of dog has been compiled without HTTPS support\n"
  },
  {
    "path": "xtests/features/outputs/disabled_tls.txt",
    "chars": 83,
    "preview": "dog: Cannot use '--tls': This version of dog has been compiled without TLS support\n"
  },
  {
    "path": "xtests/live/badssl.toml",
    "chars": 2313,
    "preview": "# Untrusted certificates\n\n[[cmd]]\nname = \"Using a DNS-over-HTTPS server with an expired certificate\"\nshell = \"dog --http"
  },
  {
    "path": "xtests/live/basics.toml",
    "chars": 1522,
    "preview": "# Colour output\n\n[[cmd]]\nname = \"Running dog with ‘--colour=always’ produces colourful output\"\nshell = \"dog dns.google -"
  },
  {
    "path": "xtests/live/bins.toml",
    "chars": 1252,
    "preview": "# HTTPS\n\n[[cmd]]\nname = \"Using a DNS-over-HTTPS server that returns status 500\"\nshell = \"dog --https @https://eu.httpbin"
  },
  {
    "path": "xtests/live/https.toml",
    "chars": 5626,
    "preview": "# A records\n\n[[cmd]]\nname = \"Look up an existing A record using HTTPS\"\nshell = \"dog a-example.lookup.dog @https://cloudf"
  },
  {
    "path": "xtests/live/json.toml",
    "chars": 4848,
    "preview": "# A records\n\n[[cmd]]\nname = \"Look up an existing A record formatted as JSON\"\nshell = \"dog a-example.lookup.dog @1.1.1.1 "
  },
  {
    "path": "xtests/live/tcp.toml",
    "chars": 4927,
    "preview": "# A records\n\n[[cmd]]\nname = \"Look up an existing A record using TCP\"\nshell = \"dog a-example.lookup.dog @1.1.1.1 --short "
  },
  {
    "path": "xtests/live/tls.toml",
    "chars": 4926,
    "preview": "# A records\n\n[[cmd]]\nname = \"Look up an existing A record using TLS\"\nshell = \"dog a-example.lookup.dog @1.1.1.1 --short "
  },
  {
    "path": "xtests/live/udp.toml",
    "chars": 4807,
    "preview": "# A records\n\n[[cmd]]\nname = \"Look up an existing A record using UDP\"\nshell = \"dog a-example.lookup.dog @1.1.1.1 --short\""
  },
  {
    "path": "xtests/madns/a-records.toml",
    "chars": 2002,
    "preview": "# A record successes\n\n[[cmd]]\nname = \"Running with ‘a.example’ prints the correct A record\"\nshell = \"dog --colour=always"
  },
  {
    "path": "xtests/madns/aaaa-records.toml",
    "chars": 2099,
    "preview": "# AAAA record successes\n\n[[cmd]]\nname = \"Running with ‘aaaa.example’ prints the correct AAAA record\"\nshell = \"dog --colo"
  },
  {
    "path": "xtests/madns/caa-records.toml",
    "chars": 4069,
    "preview": "# CAA record successes\n\n[[cmd]]\nname = \"Running with ‘caa.example’ prints the correct CAA record\"\nshell = \"dog --colour="
  },
  {
    "path": "xtests/madns/cname-records.toml",
    "chars": 1392,
    "preview": "# CNAME record successes\n\n[[cmd]]\nname = \"Running with ‘cname.example’ prints the correct CNAME record\"\nshell = \"dog --c"
  },
  {
    "path": "xtests/madns/eui48-records.toml",
    "chars": 2126,
    "preview": "# EUI48 record successes\n\n[[cmd]]\nname = \"Running with ‘eui48.example’ prints the correct EUI48 record\"\nshell = \"dog --c"
  },
  {
    "path": "xtests/madns/eui64-records.toml",
    "chars": 2126,
    "preview": "# EUI64 record successes\n\n[[cmd]]\nname = \"Running with ‘eui64.example’ prints the correct EUI64 record\"\nshell = \"dog --c"
  },
  {
    "path": "xtests/madns/hinfo-records.toml",
    "chars": 2775,
    "preview": "# HINFO record successes\n\n[[cmd]]\nname = \"Running with ‘hinfo.example’ prints the correct HINFO record\"\nshell = \"dog --c"
  },
  {
    "path": "xtests/madns/loc-records.toml",
    "chars": 5596,
    "preview": "# LOC record successes\n\n[[cmd]]\nname = \"Running with ‘loc.example’ prints the correct LOC record\"\nshell = \"dog --colour="
  },
  {
    "path": "xtests/madns/mx-records.toml",
    "chars": 1323,
    "preview": "# MX record successes\n\n[[cmd]]\nname = \"Running with ‘mx.example’ prints the correct MX record\"\nshell = \"dog --colour=alw"
  },
  {
    "path": "xtests/madns/naptr-records.toml",
    "chars": 3108,
    "preview": "# NAPTR record successes\n\n[[cmd]]\nname = \"Running with ‘naptr.example’ prints the correct NAPTR record\"\nshell = \"dog --c"
  },
  {
    "path": "xtests/madns/ns-records.toml",
    "chars": 1323,
    "preview": "# NS record successes\n\n[[cmd]]\nname = \"Running with ‘ns.example’ prints the correct NS record\"\nshell = \"dog --colour=alw"
  },
  {
    "path": "xtests/madns/openpgpkey-records.toml",
    "chars": 1536,
    "preview": "# OPENPGPKEY record successes\n\n[[cmd]]\nname = \"Running with ‘openpgpkey.example’ prints the correct OPENPGPKEY record\"\ns"
  },
  {
    "path": "xtests/madns/opt-records.toml",
    "chars": 3081,
    "preview": "# OPT record successes\n\n[[cmd]]\nname = \"Running with ‘opt.example’ prints the correct OPT record\"\nshell = \"dog --colour="
  },
  {
    "path": "xtests/madns/outputs/a.example.ansitxt",
    "chars": 54,
    "preview": "\u001b[1;32mA\u001b[0m \u001b[1;34ma.example.\u001b[0m 10m00s   127.0.0.1\n"
  },
  {
    "path": "xtests/madns/outputs/a.example.json",
    "chars": 430,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"a.example.\",\n          \"class\": \"IN\",\n         "
  },
  {
    "path": "xtests/madns/outputs/aaaa.example.ansitxt",
    "chars": 54,
    "preview": "\u001b[1;32mAAAA\u001b[0m \u001b[1;34maaaa.example.\u001b[0m 10m00s   ::1\n"
  },
  {
    "path": "xtests/madns/outputs/aaaa.example.json",
    "chars": 436,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"aaaa.example.\",\n          \"class\": \"IN\",\n      "
  },
  {
    "path": "xtests/madns/outputs/ansi.str.example.ansitxt",
    "chars": 111,
    "preview": "\u001b[33mCNAME\u001b[0m \u001b[1;34mansi.str.example.\u001b[0m 10m00s   \"\\u{1b}[32mgreen.\\u{1b}[34mblue.\\u{1b}[31mred.\\u{1b}[0m.\"\n"
  },
  {
    "path": "xtests/madns/outputs/ansi.str.example.json",
    "chars": 497,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"ansi.str.example.\",\n          \"class\": \"IN\",\n  "
  },
  {
    "path": "xtests/madns/outputs/bad-regex.naptr.example.ansitxt",
    "chars": 118,
    "preview": "\u001b[31mNAPTR\u001b[0m \u001b[1;34mbad-regex.naptr.example.\u001b[0m 10m00s   5 10 \"s\" \"SRV\" \"(((((((((((((((((((((((((\" \"srv.example.\"\n"
  },
  {
    "path": "xtests/madns/outputs/bad-utf8.caa.example.ansitxt",
    "chars": 101,
    "preview": "\u001b[31mCAA\u001b[0m \u001b[1;34mbad-utf8.caa.example.\u001b[0m 10m00s   \"issuewild\" \"\\208\\208\\160\\255\" (non-critical)\n"
  },
  {
    "path": "xtests/madns/outputs/bad-utf8.caa.example.json",
    "chars": 511,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"bad-utf8.caa.example.\",\n          \"class\": \"IN\""
  },
  {
    "path": "xtests/madns/outputs/bad-utf8.hinfo.example.ansitxt",
    "chars": 97,
    "preview": "\u001b[33mHINFO\u001b[0m \u001b[1;34mbad-utf8.hinfo.example.\u001b[0m 10m00s   \"\\208\\208\\160\\255\" \"\\208\\208\\160\\255\"\n"
  },
  {
    "path": "xtests/madns/outputs/bad-utf8.hinfo.example.json",
    "chars": 479,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"bad-utf8.hinfo.example.\",\n          \"class\": \"I"
  },
  {
    "path": "xtests/madns/outputs/bad-utf8.naptr.invalid.ansitxt",
    "chars": 128,
    "preview": "\u001b[31mNAPTR\u001b[0m \u001b[1;34mbad-utf8.naptr.invalid.\u001b[0m 10m00s   5 10 \"\\208\\208\\160\\255\" \"\\208\\208\\160\\255\" \"\\208\\208\\160\\255\""
  },
  {
    "path": "xtests/madns/outputs/bad-utf8.naptr.invalid.json",
    "chars": 573,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"bad-utf8.naptr.invalid.\",\n          \"class\": \"I"
  },
  {
    "path": "xtests/madns/outputs/bad-utf8.txt.example.ansitxt",
    "chars": 74,
    "preview": "\u001b[33mTXT\u001b[0m \u001b[1;34mbad-utf8.txt.example.\u001b[0m 10m00s   \"\\208\\208\\160\\255\"\n"
  },
  {
    "path": "xtests/madns/outputs/bad-utf8.txt.example.json",
    "chars": 481,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"bad-utf8.txt.example.\",\n          \"class\": \"IN\""
  },
  {
    "path": "xtests/madns/outputs/bad-utf8.uri.example.ansitxt",
    "chars": 80,
    "preview": "\u001b[33mURI\u001b[0m \u001b[1;34mbad-utf8.uri.example.\u001b[0m 10m00s   10 16 \"\\208\\208\\160\\255\"\n"
  },
  {
    "path": "xtests/madns/outputs/bad-utf8.uri.example.json",
    "chars": 503,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"bad-utf8.uri.example.\",\n          \"class\": \"IN\""
  },
  {
    "path": "xtests/madns/outputs/caa.example.ansitxt",
    "chars": 95,
    "preview": "\u001b[31mCAA\u001b[0m \u001b[1;34mcaa.example.\u001b[0m 10m00s   \"issuewild\" \"trustworthy.example\" (non-critical)\n"
  },
  {
    "path": "xtests/madns/outputs/caa.example.json",
    "chars": 509,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"caa.example.\",\n          \"class\": \"IN\",\n       "
  },
  {
    "path": "xtests/madns/outputs/cname.example.ansitxt",
    "chars": 68,
    "preview": "\u001b[33mCNAME\u001b[0m \u001b[1;34mcname.example.\u001b[0m 10m00s   \"dns.lookup.dog.\"\n"
  },
  {
    "path": "xtests/madns/outputs/cname.example.json",
    "chars": 451,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"cname.example.\",\n          \"class\": \"IN\",\n     "
  },
  {
    "path": "xtests/madns/outputs/critical.caa.example.ansitxt",
    "chars": 100,
    "preview": "\u001b[31mCAA\u001b[0m \u001b[1;34mcritical.caa.example.\u001b[0m 10m00s   \"issuewild\" \"trustworthy.example\" (critical)\n"
  },
  {
    "path": "xtests/madns/outputs/critical.caa.example.json",
    "chars": 526,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"critical.caa.example.\",\n          \"class\": \"IN\""
  },
  {
    "path": "xtests/madns/outputs/do-flag.opt.example.ansitxt",
    "chars": 147,
    "preview": "  \u001b[1;32mA\u001b[0m \u001b[1;34mdo-flag.opt.example.\u001b[0m 10m00s   127.0.0.1\n\u001b[35mOPT\u001b[0m \u001b[1;34m\u001b[0m                            \u001b["
  },
  {
    "path": "xtests/madns/outputs/do-flag.opt.example.json",
    "chars": 605,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"do-flag.opt.example.\",\n          \"class\": \"IN\","
  },
  {
    "path": "xtests/madns/outputs/eui48.example.ansitxt",
    "chars": 70,
    "preview": "\u001b[33mEUI48\u001b[0m \u001b[1;34meui48.example.\u001b[0m 10m00s   \"12-34-56-78-90-ab\"\n"
  },
  {
    "path": "xtests/madns/outputs/eui48.example.json",
    "chars": 457,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"eui48.example.\",\n          \"class\": \"IN\",\n     "
  },
  {
    "path": "xtests/madns/outputs/eui64.example.ansitxt",
    "chars": 78,
    "preview": "\u001b[1;33mEUI64\u001b[0m \u001b[1;34meui64.example.\u001b[0m 10m00s   \"12-34-56-ff-fe-78-90-ab\"\n"
  },
  {
    "path": "xtests/madns/outputs/eui64.example.json",
    "chars": 463,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"eui64.example.\",\n          \"class\": \"IN\",\n     "
  },
  {
    "path": "xtests/madns/outputs/far-negative-latitude.loc.invalid.ansitxt",
    "chars": 108,
    "preview": "\u001b[33mLOC\u001b[0m \u001b[1;34mfar-negative-latitude.loc.invalid.\u001b[0m 10m00s   3e2 (0, 0) (Out of range, 0°0′0″ E, 0m)\n"
  },
  {
    "path": "xtests/madns/outputs/far-negative-latitude.loc.invalid.json",
    "chars": 714,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"far-negative-latitude.loc.invalid.\",\n          "
  },
  {
    "path": "xtests/madns/outputs/far-negative-longitude.loc.invalid.ansitxt",
    "chars": 109,
    "preview": "\u001b[33mLOC\u001b[0m \u001b[1;34mfar-negative-longitude.loc.invalid.\u001b[0m 10m00s   3e2 (0, 0) (0°0′0″ N, Out of range, 0m)\n"
  },
  {
    "path": "xtests/madns/outputs/far-negative-longitude.loc.invalid.json",
    "chars": 716,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"far-negative-longitude.loc.invalid.\",\n         "
  },
  {
    "path": "xtests/madns/outputs/far-positive-latitude.loc.invalid.ansitxt",
    "chars": 108,
    "preview": "\u001b[33mLOC\u001b[0m \u001b[1;34mfar-positive-latitude.loc.invalid.\u001b[0m 10m00s   3e2 (0, 0) (Out of range, 0°0′0″ E, 0m)\n"
  },
  {
    "path": "xtests/madns/outputs/far-positive-latitude.loc.invalid.json",
    "chars": 714,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"far-positive-latitude.loc.invalid.\",\n          "
  },
  {
    "path": "xtests/madns/outputs/far-positive-longitude.loc.invalid.ansitxt",
    "chars": 109,
    "preview": "\u001b[33mLOC\u001b[0m \u001b[1;34mfar-positive-longitude.loc.invalid.\u001b[0m 10m00s   3e2 (0, 0) (0°0′0″ N, Out of range, 0m)\n"
  },
  {
    "path": "xtests/madns/outputs/far-positive-longitude.loc.invalid.json",
    "chars": 716,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"far-positive-longitude.loc.invalid.\",\n         "
  },
  {
    "path": "xtests/madns/outputs/hinfo.example.ansitxt",
    "chars": 83,
    "preview": "\u001b[33mHINFO\u001b[0m \u001b[1;34mhinfo.example.\u001b[0m 10m00s   \"some-kinda-cpu\" \"some-kinda-os\"\n"
  },
  {
    "path": "xtests/madns/outputs/hinfo.example.json",
    "chars": 482,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"hinfo.example.\",\n          \"class\": \"IN\",\n     "
  },
  {
    "path": "xtests/madns/outputs/loc.example.ansitxt",
    "chars": 94,
    "preview": "\u001b[33mLOC\u001b[0m \u001b[1;34mloc.example.\u001b[0m 10m00s   3e2 (0, 0) (51°30′12.748″ N, 0°7′39.611″ W, 0m)\n"
  },
  {
    "path": "xtests/madns/outputs/loc.example.json",
    "chars": 688,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"loc.example.\",\n          \"class\": \"IN\",\n       "
  },
  {
    "path": "xtests/madns/outputs/mx.example.ansitxt",
    "chars": 67,
    "preview": "\u001b[36mMX\u001b[0m \u001b[1;34mmx.example.\u001b[0m 10m00s   10 \"exchange.example.\"\n"
  },
  {
    "path": "xtests/madns/outputs/mx.example.json",
    "chars": 473,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"mx.example.\",\n          \"class\": \"IN\",\n        "
  },
  {
    "path": "xtests/madns/outputs/named.opt.invalid.ansitxt",
    "chars": 159,
    "preview": "  \u001b[1;32mA\u001b[0m \u001b[1;34mnamed.opt.invalid.\u001b[0m           10m00s   127.0.0.1\n\u001b[35mOPT\u001b[0m \u001b[1;34mbingle.bongle.dingle.dangl"
  },
  {
    "path": "xtests/madns/outputs/named.opt.invalid.json",
    "chars": 629,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"named.opt.invalid.\",\n          \"class\": \"IN\",\n "
  },
  {
    "path": "xtests/madns/outputs/naptr.example.ansitxt",
    "chars": 103,
    "preview": "\u001b[31mNAPTR\u001b[0m \u001b[1;34mnaptr.example.\u001b[0m 10m00s   5 10 \"s\" \"SRV\" \"\\\\d\\\\d:\\\\d\\\\d:\\\\d\\\\d\" \"srv.example.\"\n"
  },
  {
    "path": "xtests/madns/outputs/naptr.example.json",
    "chars": 578,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"naptr.example.\",\n          \"class\": \"IN\",\n     "
  },
  {
    "path": "xtests/madns/outputs/newline.str.example.ansitxt",
    "chars": 88,
    "preview": "\u001b[33mCNAME\u001b[0m \u001b[1;34mnewline.str.example.\u001b[0m 10m00s   \"some\\nnew\\r\\nlines\\n.example.\"\n"
  },
  {
    "path": "xtests/madns/outputs/newline.str.example.json",
    "chars": 477,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"newline.str.example.\",\n          \"class\": \"IN\","
  },
  {
    "path": "xtests/madns/outputs/ns.example.ansitxt",
    "chars": 66,
    "preview": "\u001b[31mNS\u001b[0m \u001b[1;34mns.example.\u001b[0m 10m00s   \"a.gtld-servers.net.\"\n"
  },
  {
    "path": "xtests/madns/outputs/ns.example.json",
    "chars": 447,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"ns.example.\",\n          \"class\": \"IN\",\n        "
  },
  {
    "path": "xtests/madns/outputs/null.str.example.ansitxt",
    "chars": 98,
    "preview": "\u001b[33mCNAME\u001b[0m \u001b[1;34mnull.str.example.\u001b[0m 10m00s   \"some\\u{0}null\\u{0}\\u{0}chars\\u{0}.example.\"\n"
  },
  {
    "path": "xtests/madns/outputs/null.str.example.json",
    "chars": 488,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"null.str.example.\",\n          \"class\": \"IN\",\n  "
  },
  {
    "path": "xtests/madns/outputs/openpgpkey.example.ansitxt",
    "chars": 71,
    "preview": "\u001b[36mOPENPGPKEY\u001b[0m \u001b[1;34mopenpgpkey.example.\u001b[0m 10m00s   \"EjRWeA==\"\n"
  },
  {
    "path": "xtests/madns/outputs/openpgpkey.example.json",
    "chars": 461,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"openpgpkey.example.\",\n          \"class\": \"IN\",\n"
  },
  {
    "path": "xtests/madns/outputs/opt.example.ansitxt",
    "chars": 127,
    "preview": "  \u001b[1;32mA\u001b[0m \u001b[1;34mopt.example.\u001b[0m 10m00s   127.0.0.1\n\u001b[35mOPT\u001b[0m \u001b[1;34m\u001b[0m                    \u001b[32m+\u001b[0m 1452 0 "
  },
  {
    "path": "xtests/madns/outputs/opt.example.json",
    "chars": 589,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"opt.example.\",\n          \"class\": \"IN\",\n       "
  },
  {
    "path": "xtests/madns/outputs/other-flags.opt.example.ansitxt",
    "chars": 155,
    "preview": "  \u001b[1;32mA\u001b[0m \u001b[1;34mother-flags.opt.example.\u001b[0m 10m00s   127.0.0.1\n\u001b[35mOPT\u001b[0m \u001b[1;34m\u001b[0m                          "
  },
  {
    "path": "xtests/madns/outputs/other-flags.opt.example.json",
    "chars": 613,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"other-flags.opt.example.\",\n          \"class\": \""
  },
  {
    "path": "xtests/madns/outputs/others.caa.example.ansitxt",
    "chars": 95,
    "preview": "\u001b[31mCAA\u001b[0m \u001b[1;34mcaa.example.\u001b[0m 10m00s   \"issuewild\" \"trustworthy.example\" (non-critical)\n"
  },
  {
    "path": "xtests/madns/outputs/others.caa.example.json",
    "chars": 509,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"caa.example.\",\n          \"class\": \"IN\",\n       "
  },
  {
    "path": "xtests/madns/outputs/ptr.example.ansitxt",
    "chars": 61,
    "preview": "\u001b[31mPTR\u001b[0m \u001b[1;34mptr.example.\u001b[0m 10m00s   \"dns.example.\"\n"
  },
  {
    "path": "xtests/madns/outputs/ptr.example.json",
    "chars": 439,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"ptr.example.\",\n          \"class\": \"IN\",\n       "
  },
  {
    "path": "xtests/madns/outputs/slash.uri.example.ansitxt",
    "chars": 61,
    "preview": "\u001b[33mURI\u001b[0m \u001b[1;34mslash.uri.example.\u001b[0m 10m00s   10 1 \"/\"\n"
  },
  {
    "path": "xtests/madns/outputs/soa.example.ansitxt",
    "chars": 128,
    "preview": "\u001b[35mSOA\u001b[0m \u001b[1;34msoa.example.\u001b[0m 10m00s   \"mname.example.\" \"rname.example.\" 1564274434 1d0h00m00s 2h00m00s 7d0h00m00"
  },
  {
    "path": "xtests/madns/outputs/soa.example.json",
    "chars": 441,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"soa.example.\",\n          \"class\": \"IN\",\n       "
  },
  {
    "path": "xtests/madns/outputs/srv.example.ansitxt",
    "chars": 75,
    "preview": "\u001b[36mSRV\u001b[0m \u001b[1;34msrv.example.\u001b[0m 10m00s   1 1 \"service.example.\":37500\n"
  },
  {
    "path": "xtests/madns/outputs/srv.example.json",
    "chars": 523,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"srv.example.\",\n          \"class\": \"IN\",\n       "
  },
  {
    "path": "xtests/madns/outputs/sshfp.example.ansitxt",
    "chars": 67,
    "preview": "\u001b[36mSSHFP\u001b[0m \u001b[1;34msshfp.example.\u001b[0m 10m00s   1 1 212223242526\n"
  },
  {
    "path": "xtests/madns/outputs/sshfp.example.json",
    "chars": 516,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"sshfp.example.\",\n          \"class\": \"IN\",\n     "
  },
  {
    "path": "xtests/madns/outputs/tab.str.example.ansitxt",
    "chars": 84,
    "preview": "\u001b[33mCNAME\u001b[0m \u001b[1;34mtab.str.example.\u001b[0m 10m00s   \"some\\ttab\\t\\tchars\\t.example.\"\n"
  },
  {
    "path": "xtests/madns/outputs/tab.str.example.json",
    "chars": 469,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"tab.str.example.\",\n          \"class\": \"IN\",\n   "
  },
  {
    "path": "xtests/madns/outputs/tlsa.example.ansitxt",
    "chars": 69,
    "preview": "\u001b[33mTLSA\u001b[0m \u001b[1;34mtlsa.example.\u001b[0m 10m00s   3 1 1 \"112233445566\"\n"
  },
  {
    "path": "xtests/madns/outputs/tlsa.example.json",
    "chars": 549,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"tlsa.example.\",\n          \"class\": \"IN\",\n      "
  },
  {
    "path": "xtests/madns/outputs/txt.example.ansitxt",
    "chars": 85,
    "preview": "\u001b[33mTXT\u001b[0m \u001b[1;34mtxt.example.\u001b[0m 10m00s   \"Cache Invalidation and Naming Things\"\n"
  },
  {
    "path": "xtests/madns/outputs/txt.example.json",
    "chars": 496,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"txt.example.\",\n          \"class\": \"IN\",\n       "
  },
  {
    "path": "xtests/madns/outputs/upperbit.str.example.ansitxt",
    "chars": 80,
    "preview": "\u001b[33mCNAME\u001b[0m \u001b[1;34mupperbit.str.example.\u001b[0m 10m00s   \"\\u{7f}�����.example.\"\n"
  },
  {
    "path": "xtests/madns/outputs/upperbit.str.example.json",
    "chars": 470,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"upperbit.str.example.\",\n          \"class\": \"IN\""
  },
  {
    "path": "xtests/madns/outputs/uri.example.ansitxt",
    "chars": 71,
    "preview": "\u001b[33mURI\u001b[0m \u001b[1;34muri.example.\u001b[0m 10m00s   10 16 \"https://rfcs.io/\"\n"
  },
  {
    "path": "xtests/madns/outputs/uri.example.json",
    "chars": 498,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"uri.example.\",\n          \"class\": \"IN\",\n       "
  },
  {
    "path": "xtests/madns/outputs/utf8.caa.example.ansitxt",
    "chars": 115,
    "preview": "\u001b[31mCAA\u001b[0m \u001b[1;34mutf8.caa.example.\u001b[0m 10m00s   \"issuewild\" \"trustworthy\\240\\159\\140\\180example\" (non-critical)\n"
  },
  {
    "path": "xtests/madns/outputs/utf8.caa.example.json",
    "chars": 519,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"utf8.caa.example.\",\n          \"class\": \"IN\",\n  "
  },
  {
    "path": "xtests/madns/outputs/utf8.hinfo.example.ansitxt",
    "chars": 148,
    "preview": "\u001b[33mHINFO\u001b[0m \u001b[1;34mutf8.hinfo.example.\u001b[0m 10m00s   \"some\\240\\159\\140\\180kinda\\240\\159\\140\\180cpu\" \"some\\240\\159\\140\\"
  },
  {
    "path": "xtests/madns/outputs/utf8.hinfo.example.json",
    "chars": 492,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"utf8.hinfo.example.\",\n          \"class\": \"IN\",\n"
  },
  {
    "path": "xtests/madns/outputs/utf8.naptr.invalid.ansitxt",
    "chars": 122,
    "preview": "\u001b[31mNAPTR\u001b[0m \u001b[1;34mutf8.naptr.invalid.\u001b[0m 10m00s   5 10 \"\\240\\159\\140\\180\" \"\\240\\159\\140\\180\" \"\\240\\159\\140\\180\" \"🌴."
  },
  {
    "path": "xtests/madns/outputs/utf8.naptr.invalid.json",
    "chars": 557,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"utf8.naptr.invalid.\",\n          \"class\": \"IN\",\n"
  },
  {
    "path": "xtests/madns/outputs/utf8.txt.example.ansitxt",
    "chars": 190,
    "preview": "\u001b[33mTXT\u001b[0m \u001b[1;34mutf8.txt.example.\u001b[0m 10m00s   \"\\240\\159\\146\\176Cache \\240\\159\\153\\133\\226\\128\\141\\239\\184\\143Invali"
  },
  {
    "path": "xtests/madns/outputs/utf8.txt.example.json",
    "chars": 513,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"utf8.txt.example.\",\n          \"class\": \"IN\",\n  "
  },
  {
    "path": "xtests/madns/outputs/utf8.uri.example.ansitxt",
    "chars": 88,
    "preview": "\u001b[33mURI\u001b[0m \u001b[1;34mutf8.uri.example.\u001b[0m 10m00s   10 16 \"https://\\240\\159\\146\\169.la/\"\n"
  },
  {
    "path": "xtests/madns/outputs/utf8.uri.example.json",
    "chars": 505,
    "preview": "{\n  \"responses\": [\n    {\n      \"queries\": [\n        {\n          \"name\": \"utf8.uri.example.\",\n          \"class\": \"IN\",\n  "
  },
  {
    "path": "xtests/madns/protocol-chars.toml",
    "chars": 3396,
    "preview": "# Character escaping\n\n[[cmd]]\nname = \"Running with ‘ansi.str.example’ properly escapes the codes\"\nshell = \"dog --colour="
  },
  {
    "path": "xtests/madns/protocol-compression.toml",
    "chars": 1008,
    "preview": "[[cmd]]\nname = \"Running with ‘out-of-range.invalid’ displays a protocol error\"\nshell = \"dog --colour=always ${MADNS_ARGS"
  },
  {
    "path": "xtests/madns/protocol-error-codes.toml",
    "chars": 1433,
    "preview": "[[cmd]]\nname = \"Running with ‘formerr.invalid’ displays the error code\"\nshell = \"dog --colour=always ${MADNS_ARGS:-@madn"
  },
  {
    "path": "xtests/madns/ptr-records.toml",
    "chars": 1346,
    "preview": "# PTR record successes\n\n[[cmd]]\nname = \"Running with ‘ptr.example’ prints the correct PTR record\"\nshell = \"dog --colour="
  },
  {
    "path": "xtests/madns/soa-records.toml",
    "chars": 1346,
    "preview": "# SOA record successes\n\n[[cmd]]\nname = \"Running with ‘soa.example’ prints the correct SOA record\"\nshell = \"dog --colour="
  },
  {
    "path": "xtests/madns/srv-records.toml",
    "chars": 1346,
    "preview": "# SRV record successes\n\n[[cmd]]\nname = \"Running with ‘srv.example’ prints the correct SRV record\"\nshell = \"dog --colour="
  },
  {
    "path": "xtests/madns/sshfp-records.toml",
    "chars": 1392,
    "preview": "# SSHFP record successes\n\n[[cmd]]\nname = \"Running with ‘sshfp.example’ prints the correct SSHFP record\"\nshell = \"dog --c"
  },
  {
    "path": "xtests/madns/tlsa-records.toml",
    "chars": 1369,
    "preview": "# TLSA record successes\n\n[[cmd]]\nname = \"Running with ‘tlsa.example’ prints the correct TLSA record\"\nshell = \"dog --colo"
  },
  {
    "path": "xtests/madns/txt-records.toml",
    "chars": 2693,
    "preview": "# TXT record successes\n\n[[cmd]]\nname = \"Running with ‘txt.example’ prints the correct TXT record\"\nshell = \"dog --colour="
  },
  {
    "path": "xtests/madns/uri-records.toml",
    "chars": 3359,
    "preview": "# URI record successes\n\n[[cmd]]\nname = \"Running with ‘uri.example’ prints the correct URI record\"\nshell = \"dog --colour="
  },
  {
    "path": "xtests/options/errors.toml",
    "chars": 2739,
    "preview": "[[cmd]]\nname = \"Running dog with ‘--wibble’ warns about the invalid argument\"\nshell = \"dog --wibble\"\nstdout = { empty = "
  },
  {
    "path": "xtests/options/help.toml",
    "chars": 1890,
    "preview": "# help\n\n[[cmd]]\nname = \"Running ‘dog --help’ shows help\"\nshell = \"dog --help\"\nstdout = { string = \"dog ●\" }\nstderr = { e"
  }
]

// ... and 8 more files (download for full content)

About this extraction

This page contains the full source code of the ogham/dog GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 208 files (410.9 KB), approximately 121.5k tokens, and a symbol index with 500 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!