├── .circleci └── config.yml ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── CONTRIBUTING.md ├── CONVENTIONAL_COMMITS.md ├── Cargo.lock ├── Cargo.toml ├── Changelog.md ├── LICENSE ├── README.md ├── README_RUS.md ├── RELEASING.md ├── VERSIONING.md ├── batch_resolve.toml ├── packaging └── package_release.sh ├── releaserc.toml ├── rust-toolchain └── src ├── config.rs ├── main.rs └── resolve ├── batch.rs ├── error.rs ├── mod.rs ├── resolver.rs └── resolver_threadpool.rs /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | aliases: 4 | # ------------------------- 5 | # ALIASES: Caches 6 | # ------------------------- 7 | - &restore-deps-cache-ububtu 8 | key: rust-cache-{{ arch }}-{{ checksum "Cargo.toml" }}-{{ .Environment.CIRCLE_JOB }} 9 | 10 | - &save-deps-cache-ubuntu 11 | key: rust-cache-{{ arch }}-{{ checksum "Cargo.toml" }}-{{ .Environment.CIRCLE_JOB }} 12 | paths: 13 | - /usr/local/cargo/registry 14 | - /usr/local/rustup 15 | - ~/project/target/ 16 | 17 | - &restore-deps-cache-rustup 18 | key: rust-cache-musl-{{ arch }}-{{ checksum "Cargo.toml" }}-{{ .Environment.CIRCLE_JOB }} 19 | 20 | - &save-deps-cache-rustup 21 | key: rust-cache-musl-{{ arch }}-{{ checksum "Cargo.toml" }}-{{ .Environment.CIRCLE_JOB }} 22 | paths: 23 | - $HOME/.rustup/ 24 | - $HOME/.cargo/ 25 | - $HOME/project/target/ 26 | # ------------------------- 27 | # ALIASES: Branch Filters 28 | # ------------------------- 29 | - &filter-only-master 30 | branches: 31 | only: master 32 | 33 | defaults: &defaults 34 | working_directory: ~/project 35 | environment: 36 | RUST_BACKTRACE: 1 37 | 38 | jobs: 39 | test-linux: 40 | <<: *defaults 41 | docker: 42 | - image: rust:latest 43 | steps: 44 | - checkout 45 | - restore_cache: *restore-deps-cache-ububtu 46 | - run: 47 | name: Install Rust tools 48 | command: | 49 | rustup component add clippy 50 | rustup component add rustfmt 51 | - run: 52 | command: cargo test --all 53 | no_output_timeout: 1h 54 | - save_cache: *save-deps-cache-ubuntu 55 | - run: cargo clippy 56 | - run: cargo fmt -- --check 57 | 58 | build-linux: 59 | <<: *defaults 60 | docker: 61 | - image: clux/muslrust:stable 62 | steps: 63 | - checkout 64 | - restore_cache: *restore-deps-cache-rustup 65 | - run: cargo build --release 66 | - save_cache: *save-deps-cache-rustup 67 | - run: 68 | name: Move binaries into bin/ directory 69 | command: | 70 | mkdir bin/ 71 | mv target/x86_64-unknown-linux-musl/release/batch-resolve ./bin/batch-resolve 72 | - persist_to_workspace: 73 | root: . 74 | paths: 75 | - bin/* 76 | 77 | codecov: 78 | <<: *defaults 79 | machine: true 80 | steps: 81 | - checkout 82 | - restore_cache: *restore-deps-cache-rustup 83 | - run: 84 | name: Install Rust 85 | command: | 86 | curl https://sh.rustup.rs -sSf | sh -s -- -y 87 | echo 'export PATH=$HOME/.cargo/bin:$PATH' >> $BASH_ENV 88 | - run: 89 | name: Install Tarpaulin 90 | command: cargo tarpaulin --version || cargo install cargo-tarpaulin 91 | environment: 92 | RUSTFLAGS: --cfg procmacro2_semver_exempt 93 | - run: 94 | name: Generate coverage report 95 | command: cargo tarpaulin --out Xml --all-features 96 | - save_cache: *save-deps-cache-rustup 97 | - run: 98 | name: Upload to codecov.io 99 | command: bash <(curl -s https://codecov.io/bash) -Z -f cobertura.xml 100 | 101 | release-dry: 102 | <<: *defaults 103 | docker: 104 | - image: semanteecore/semanteecore 105 | steps: 106 | - setup_remote_docker 107 | - checkout 108 | # TODO: deb & rpm packaging 109 | - attach_workspace: 110 | at: /workspace 111 | - run: semanteecore --dry 112 | 113 | release: 114 | <<: *defaults 115 | docker: 116 | - image: semanteecore/semanteecore 117 | steps: 118 | - setup_remote_docker 119 | - checkout 120 | - attach_workspace: 121 | at: /workspace 122 | - restore_cache: *restore-deps-cache-ububtu 123 | # This unset is VERY important: without it --force-https cannot work 124 | # For some reason CircleCI has a global override substituting git@ links instead of all https links 125 | - run: git config --global --unset url.ssh://git@github.com.insteadof 126 | - run: semanteecore 127 | - save_cache: *save-deps-cache-ubuntu 128 | 129 | 130 | workflows: 131 | version: 2 132 | ci: 133 | jobs: 134 | - test-linux 135 | - build-linux 136 | - release-dry: 137 | requires: 138 | - build-linux 139 | - hold: 140 | filters: *filter-only-master 141 | type: approval 142 | requires: 143 | - test-linux 144 | - release-dry 145 | - release: 146 | filters: *filter-only-master 147 | requires: 148 | - hold 149 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce and/or a minimal snippet of code reproducing the issue 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Stdout/Stderr/Logs** 20 | If applicable, add logs related to your problem 21 | 22 | **Environment:** 23 | - OS: [e.g. Linux] 24 | - Version [e.g. 1.2.0] 25 | - rustc --version 26 | 27 | **Additional context** 28 | Add any other context about the problem here. 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | > This document is inspired by [elasticsearch/CONTRIBUTING.md](https://github.com/elastic/elasticsearch/blob/master/CONTRIBUTING.md) 4 | 5 | Adding a `CONTRIBUTING.md` to a Github repository enables a link to that file in the pull request or create an issue page. This document should guide potential contributors toward making a successful and meaningful impact on the project, and can save maintainers time and hassle caused by improper pull requests and issues. You can learn more about the features that are enabled by Github when this file is present [here](https://help.github.com/articles/setting-guidelines-for-repository-contributors/). 6 | 7 | ## How to contribute 8 | 9 | There are many ways to contribute, from writing tutorials or blog posts, improving the documentation, [submitting Github issues](https://help.github.com/articles/creating-an-issue/), bug reports, feature requests and writing code. 10 | 11 | ## License 12 | 13 | This repository uses the [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.html). 14 | 15 | ## Bug reports 16 | 17 | If you think you've found a bug in the software, first make sure you're testing against the *latest* version of the software -- your issue may have been fixed already. If it's not, please check out the issues list on Github and search for similar issues that have already been opened. If there are no issues then please [submit a Github issue](https://help.github.com/articles/creating-an-issue/). 18 | 19 | If you can provide a small test case it would greatly help the reproduction of a bug, as well as a a screenshot, and any other information you can provide. 20 | 21 | 22 | ## Feature Requests 23 | 24 | If there are features that do not exist yet, we are definitely open to feature requests and detailed proposals. [Open an issue](https://help.github.com/articles/creating-an-issue/) on our Github which describes the feature or proposal in detail, answer questions like why? how? 25 | 26 | ## Contributing Code and Documentation Changes 27 | 28 | Bug fixes, patches and new features are welcome. Please find or open an issue about it first. Talk about what exactly want to do, someone may already be working on it, or there might be some issues that you need to be aware of before implementing the fix. 29 | 30 | There are many ways to fix a problem and it is important to find the best approach before writing a ton of code. 31 | 32 | ##### Documentation Changes 33 | 34 | For small documentation changes and fixes, these can be done quickly following this video guide on [how to contribute to Open Source in 1 minute on Github](https://www.youtube.com/watch?v=kRYk1-yKwWs). 35 | 36 | ### Forking the repository 37 | 38 | [How to fork a repository](https://help.github.com/articles/fork-a-repo/). 39 | 40 | ### Submitting changes 41 | 42 | 1. Review & Test changes 43 | * If the code changed, then test it. If documentation changed, then preview the rendered Markdown. 44 | 2. Commiting 45 | * Follow the [Conventional Commits](CONVENTIONAL_COMMITS.md) guidelines to create a commit message. 46 | 3. Sign the CLA 47 | * Make sure you've signed the repository's Contributor License Agreement. We are not asking you to assign copyright to us, but to give us the right to distribute your code without restriction. We ask this of all contributors in order to assure our users of the origin and continuing existence of the code. You only need to sign the CLA once. 48 | 4. Submit a pull request 49 | * Push local changes to your forked repository and make a pull request. Follow the [Convention Commits](CONVENTIONAL_COMMITS.md) guidelines for naming Github pull requests and what to put in the body. 50 | 51 | 52 | ## Releasing 53 | 54 | Follow the release process is outlined in [RELEASING.md](RELEASING.md) to create a release. 55 | 56 | 57 | -------------------------------------------------------------------------------- /CONVENTIONAL_COMMITS.md: -------------------------------------------------------------------------------- 1 | # Conventional Commits 1.0.0-beta.2 2 | 3 | > This spec is a direct copy from [http://conventionalcommits.org](http://conventionalcommits.org). It lives here as a reference document for new contributors. 4 | 5 | ## Summary 6 | 7 | The [Conventional Commits](http://conventionalcommits.org) specification is a lightweight convention on top of commit messages. 8 | It provides an easy set of rules for creating an explicit commit history; 9 | which makes it easier to write automated tools on top of. 10 | This convention dovetails with [SemVer](http://semver.org), 11 | by describing the features, fixes, and breaking changes made in commit messages. 12 | 13 | The commit message should be structured as follows: 14 | 15 | --- 16 | 17 | ``` 18 | [optional scope]: 19 | 20 | [optional body] 21 | 22 | [optional footer] 23 | ``` 24 | --- 25 | 26 |
27 | The commit contains the following structural elements, to communicate intent to the 28 | consumers of your library: 29 | 30 | 1. **fix:** a commit of the _type_ `fix` patches a bug in your codebase (this correlates with [`PATCH`](http://semver.org/#summary) in semantic versioning). 31 | 1. **feat:** a commit of the _type_ `feat` introduces a new feature to the codebase (this correlates with [`MINOR`](http://semver.org/#summary) in semantic versioning). 32 | 1. **BREAKING CHANGE:** a commit that has the text `BREAKING CHANGE:` at the beginning of its optional body or footer section introduces a breaking API change (correlating with [`MAJOR`](http://semver.org/#summary) in semantic versioning). 33 | A BREAKING CHANGE can be part of commits of any _type_. 34 | 1. Others: commit _types_ other than `fix:` and `feat:` are allowed, for example [commitlint-config-conventional](https://github.com/marionebl/commitlint/tree/master/%40commitlint/config-conventional) (based on the [the Angular convention](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#-commit-message-guidelines)) recommends `chore:`, `docs:`, `style:`, `refactor:`, `perf:`, `test:`, and others. 35 | We also recommend `improvement` for commits that improve a current implementation without adding a new feature or fixing a bug. 36 | Notice these types are not mandated by the conventional commits specification, and have no implicit effect in semantic versioning (unless they include a BREAKING CHANGE). 37 |
38 | A scope may be provided to a commit's type, to provide additional contextual information and is contained within parenthesis, e.g., `feat(parser): add ability to parse arrays`. 39 | 40 | ## Examples 41 | 42 | ### Commit message with description and breaking change in body 43 | ``` 44 | feat: allow provided config object to extend other configs 45 | 46 | BREAKING CHANGE: `extends` key in config file is now used for extending other config files 47 | ``` 48 | 49 | ### Commit message with no body 50 | ``` 51 | docs: correct spelling of CHANGELOG 52 | ``` 53 | 54 | ### Commit message with scope 55 | ``` 56 | feat(lang): added polish language 57 | ``` 58 | 59 | ### Commit message for a fix using an (optional) issue number. 60 | ``` 61 | fix: minor typos in code 62 | 63 | see the issue for details on the typos fixed 64 | 65 | fixes issue #12 66 | ``` 67 | ## Specification 68 | 69 | The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). 70 | 71 | 1. Commits MUST be prefixed with a type, which consists of a noun, `feat`, `fix`, etc., followed by a colon and a space. 72 | 1. The type `feat` MUST be used when a commit adds a new feature to your application or library. 73 | 1. The type `fix` MUST be used when a commit represents a bug fix for your application. 74 | 1. An optional scope MAY be provided after a type. A scope is a phrase describing a section of the codebase enclosed in parenthesis, e.g., `fix(parser):` 75 | 1. A description MUST immediately follow the type/scope prefix. 76 | The description is a short description of the code changes, e.g., _fix: array parsing issue when multiple spaces were contained in string._ 77 | 1. A longer commit body MAY be provided after the short description, providing additional contextual information about the code changes. The body MUST begin one blank line after the description. 78 | 1. A footer MAY be provided one blank line after the body. 79 | The footer SHOULD contain additional issue references about the code changes (such as the issues it fixes, e.g.,`Fixes #13`). 80 | 1. Breaking changes MUST be indicated at the very beginning of the footer or body section of a commit. A breaking change MUST consist of the uppercase text `BREAKING CHANGE`, followed by a colon and a space. 81 | 1. A description MUST be provided after the `BREAKING CHANGE: `, describing what has changed about the API, e.g., _BREAKING CHANGE: environment variables now take precedence over config files._ 82 | 1. The footer MUST only contain `BREAKING CHANGE`, external links, issue references, and other meta-information. 83 | 1. Types other than `feat` and `fix` MAY be used in your commit messages. 84 | 85 | ## Why Use Conventional Commits 86 | 87 | * Automatically generating CHANGELOGs. 88 | * Automatically determining a semantic version bump (based on the types of commits landed). 89 | * Communicating the nature of changes to teammates, the public, and other stakeholders. 90 | * Triggering build and publish processes. 91 | * Making it easier for people to contribute to your projects, by allowing them to explore 92 | a more structured commit history. 93 | 94 | ## FAQ 95 | 96 | ### How should I deal with commit messages in the initial development phase? 97 | 98 | We recommend that you proceed as if you've an already released product. Typically *somebody*, even if its your fellow software developers, is using your software. They'll want to know what's fixed, what breaks etc. 99 | 100 | ### Are the types in the commit title uppercase or lowercase? 101 | 102 | Any casing may be used, but it's best to be consistent. 103 | 104 | ### What do I do if the commit conforms to more than one of the commit types? 105 | 106 | Go back and make multiple commits whenever possible. Part of the benefit of Conventional Commits is its ability to drive us to make more organized commits and PRs. 107 | 108 | ### Doesn’t this discourage rapid development and fast iteration? 109 | 110 | It discourages moving fast in a disorganized way. It helps you be able to move fast long term across multiple projects with varied contributors. 111 | 112 | ### Might Conventional Commits lead developers to limit the type of commits they make because they'll be thinking in the types provided? 113 | 114 | Conventional Commits encourages us to make more of certain types of commits such as fixes. Other than that, the flexibility of Conventional Commits allows your team to come up with their own types and change those types over time. 115 | 116 | ### How does this relate to SemVer? 117 | 118 | `fix` type commits should be translated to `PATCH` releases. `feat` type commits should be translated to `MINOR` releases. Commits with `BREAKING CHANGE` in the commits, regardless of type, should be translated to `MAJOR` releases. 119 | 120 | ### How should I version my extensions to the Conventional Commits Specification, e.g. `@jameswomack/conventional-commit-spec`? 121 | 122 | We recommend using SemVer to release your own extensions to this specification (and 123 | encourage you to make these extensions!) 124 | 125 | ### What do I do if I accidentally use the wrong commit type? 126 | 127 | #### When you used a type that's of the spec but not the correct type, e.g. `fix` instead of `feat` 128 | 129 | Prior to merging or releasing the mistake, we recommend using `git rebase -i` to edit the commit history. After release, the cleanup will be different according to what tools and processes you use. 130 | 131 | #### When you used a type *not* of the spec, e.g. `feet` instead of `feat` 132 | 133 | In a worst case scenario, it's not the end of the world if a commit lands that does not meet the conventional commit specification. It simply means that commit will be missed by tools that are based on the spec. 134 | 135 | ### Do all my contributors need to use the conventional commit specification? 136 | 137 | No! If you use a squash based workflow on Git lead maintainers can cleanup the commit messages as they're merged—adding no workload to casual committers. 138 | A common workflow for this is to have your git system automatically squash commits from a pull request and present a form for the lead maintainer to enter the proper git commit message for the merge. 139 | 140 | ## About 141 | 142 | The Conventional Commit specification is inspired by, and based heavily on, the [Angular Commit Guidelines](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#-commit-message-guidelines). 143 | 144 | The first draft of this specification has been written in collaboration with some of the folks contributing to: 145 | 146 | * [conventional-changelog](https://github.com/conventional-changelog/conventional-changelog): a set of tools for parsing conventional commit messages from git histories. 147 | * [bumped](https://bumped.github.io): a tool for releasing software that makes it easy to perform actions before and after releasing a new version of your software. 148 | * [unleash](https://github.com/netflix/unleash): a tool for automating the software release and publishing lifecycle. 149 | * [lerna](https://github.com/lerna/lerna): a tool for managing monorepos, which grew out of the Babel project. 150 | 151 | ## Tooling for Conventional Commits 152 | 153 | * [conform](https://github.com/autonomy/conform): a tool that can be used to enforce policies on git repositories, including conventional commits. 154 | 155 | ## Projects Using Conventional Commits 156 | 157 | * [yargs](https://github.com/yargs/yargs): everyone's favorite pirate themed command line argument parser. 158 | * [istanbuljs](https://github.com/istanbuljs/istanbuljs): a collection of open-source tools and libraries for adding test coverage to your JavaScript tests. 159 | * [standard-version](https://github.com/conventional-changelog/standard-version): Automatic versioning and CHANGELOG management, using GitHub's new squash button and the recommended Conventional Commits workflow. 160 | * [uPortal-home](https://github.com/UW-Madison-DoIT/angularjs-portal) and [uPortal-application-framework](https://github.com/UW-Madison-DoIT/uw-frame): Optional supplemental user interface enhancing [Apereo uPortal](https://www.apereo.org/projects/uportal). 161 | * [massive.js](https://github.com/dmfay/massive-js): A data access library for Node and PostgreSQL. 162 | * [electron](https://github.com/electron/electron): Build cross-platform desktop apps with JavaScript, HTML, and CSS. 163 | * [scroll-utility](https://github.com/LeDDGroup/scroll-utility): A simple to use scroll utility package for centering elements, and smooth animations 164 | * [Blaze UI](https://github.com/BlazeUI/blaze): Framework-free open source modular toolkit. 165 | 166 | [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org) 167 | 168 | _want your project on this list?_ [send a pull request](https://github.com/conventional-changelog/conventionalcommits.org/pulls). 169 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "aho-corasick" 5 | version = "0.6.3" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | dependencies = [ 8 | "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 9 | ] 10 | 11 | [[package]] 12 | name = "ansi_term" 13 | version = "0.9.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | 16 | [[package]] 17 | name = "atty" 18 | version = "0.2.2" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | dependencies = [ 21 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 22 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 23 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 24 | ] 25 | 26 | [[package]] 27 | name = "backtrace" 28 | version = "0.2.3" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | dependencies = [ 31 | "backtrace-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 32 | "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 33 | "dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 34 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 35 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 36 | "rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 37 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 38 | ] 39 | 40 | [[package]] 41 | name = "backtrace-sys" 42 | version = "0.1.10" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | dependencies = [ 45 | "gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)", 46 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 47 | ] 48 | 49 | [[package]] 50 | name = "batch_resolve_cli" 51 | version = "0.3.7" 52 | dependencies = [ 53 | "clap 2.24.1 (registry+https://github.com/rust-lang/crates.io-index)", 54 | "crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", 55 | "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 56 | "futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", 57 | "indicatif 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 58 | "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 59 | "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 60 | "num_cpus 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 61 | "serde 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 62 | "serde_derive 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 63 | "tokio-core 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 64 | "toml 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 65 | "trust-dns 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", 66 | ] 67 | 68 | [[package]] 69 | name = "bitflags" 70 | version = "0.8.2" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | 73 | [[package]] 74 | name = "byteorder" 75 | version = "1.0.0" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | 78 | [[package]] 79 | name = "bytes" 80 | version = "0.4.3" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | dependencies = [ 83 | "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 84 | "iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 85 | ] 86 | 87 | [[package]] 88 | name = "cfg-if" 89 | version = "0.1.0" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | 92 | [[package]] 93 | name = "chrono" 94 | version = "0.2.25" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | dependencies = [ 97 | "num 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 98 | "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 99 | ] 100 | 101 | [[package]] 102 | name = "clap" 103 | version = "2.24.1" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | dependencies = [ 106 | "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 107 | "atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 108 | "bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", 109 | "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", 110 | "term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 111 | "unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 112 | "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 113 | "vec_map 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 114 | ] 115 | 116 | [[package]] 117 | name = "clicolors-control" 118 | version = "0.1.0" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | dependencies = [ 121 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 122 | "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 123 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 124 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 125 | ] 126 | 127 | [[package]] 128 | name = "crossbeam" 129 | version = "0.2.10" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | 132 | [[package]] 133 | name = "data-encoding" 134 | version = "1.2.0" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | 137 | [[package]] 138 | name = "dbghelp-sys" 139 | version = "0.2.0" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | dependencies = [ 142 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 143 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 144 | ] 145 | 146 | [[package]] 147 | name = "env_logger" 148 | version = "0.4.2" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | dependencies = [ 151 | "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 152 | "regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 153 | ] 154 | 155 | [[package]] 156 | name = "error-chain" 157 | version = "0.1.12" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | dependencies = [ 160 | "backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 161 | ] 162 | 163 | [[package]] 164 | name = "foreign-types" 165 | version = "0.2.0" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | 168 | [[package]] 169 | name = "futures" 170 | version = "0.1.13" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | 173 | [[package]] 174 | name = "gcc" 175 | version = "0.3.45" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | 178 | [[package]] 179 | name = "gdi32-sys" 180 | version = "0.2.0" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | dependencies = [ 183 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 184 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 185 | ] 186 | 187 | [[package]] 188 | name = "indicatif" 189 | version = "0.1.0" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | dependencies = [ 192 | "clicolors-control 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 193 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 194 | "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 195 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 196 | "parking_lot 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 197 | "regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 198 | "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 199 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 200 | ] 201 | 202 | [[package]] 203 | name = "iovec" 204 | version = "0.1.0" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | dependencies = [ 207 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 208 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 209 | ] 210 | 211 | [[package]] 212 | name = "kernel32-sys" 213 | version = "0.2.2" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | dependencies = [ 216 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 217 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 218 | ] 219 | 220 | [[package]] 221 | name = "lazy_static" 222 | version = "0.2.8" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | 225 | [[package]] 226 | name = "lazycell" 227 | version = "0.4.0" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | 230 | [[package]] 231 | name = "libc" 232 | version = "0.2.22" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | 235 | [[package]] 236 | name = "log" 237 | version = "0.3.7" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | 240 | [[package]] 241 | name = "memchr" 242 | version = "1.0.1" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | dependencies = [ 245 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 246 | ] 247 | 248 | [[package]] 249 | name = "mio" 250 | version = "0.6.7" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | dependencies = [ 253 | "iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 254 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 255 | "lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 256 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 257 | "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 258 | "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 259 | "net2 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", 260 | "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 261 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 262 | ] 263 | 264 | [[package]] 265 | name = "miow" 266 | version = "0.2.1" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | dependencies = [ 269 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 270 | "net2 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", 271 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 272 | "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 273 | ] 274 | 275 | [[package]] 276 | name = "net2" 277 | version = "0.2.29" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | dependencies = [ 280 | "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 281 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 282 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 283 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 284 | "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 285 | ] 286 | 287 | [[package]] 288 | name = "num" 289 | version = "0.1.37" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | dependencies = [ 292 | "num-integer 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", 293 | "num-iter 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)", 294 | "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 295 | ] 296 | 297 | [[package]] 298 | name = "num-integer" 299 | version = "0.1.34" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | dependencies = [ 302 | "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 303 | ] 304 | 305 | [[package]] 306 | name = "num-iter" 307 | version = "0.1.33" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | dependencies = [ 310 | "num-integer 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", 311 | "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 312 | ] 313 | 314 | [[package]] 315 | name = "num-traits" 316 | version = "0.1.37" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | 319 | [[package]] 320 | name = "num_cpus" 321 | version = "1.4.0" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | dependencies = [ 324 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 325 | ] 326 | 327 | [[package]] 328 | name = "openssl" 329 | version = "0.9.11" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | dependencies = [ 332 | "bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", 333 | "foreign-types 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 334 | "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 335 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 336 | "openssl-sys 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)", 337 | ] 338 | 339 | [[package]] 340 | name = "openssl-sys" 341 | version = "0.9.11" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | dependencies = [ 344 | "gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)", 345 | "gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 346 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 347 | "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 348 | "user32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 349 | ] 350 | 351 | [[package]] 352 | name = "owning_ref" 353 | version = "0.3.3" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | dependencies = [ 356 | "stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 357 | ] 358 | 359 | [[package]] 360 | name = "parking_lot" 361 | version = "0.4.3" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | dependencies = [ 364 | "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 365 | "parking_lot_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 366 | "thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 367 | ] 368 | 369 | [[package]] 370 | name = "parking_lot_core" 371 | version = "0.2.1" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | dependencies = [ 374 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 375 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 376 | "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 377 | "smallvec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 378 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 379 | ] 380 | 381 | [[package]] 382 | name = "pkg-config" 383 | version = "0.3.9" 384 | source = "registry+https://github.com/rust-lang/crates.io-index" 385 | 386 | [[package]] 387 | name = "quote" 388 | version = "0.3.15" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | 391 | [[package]] 392 | name = "rand" 393 | version = "0.3.15" 394 | source = "registry+https://github.com/rust-lang/crates.io-index" 395 | dependencies = [ 396 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 397 | ] 398 | 399 | [[package]] 400 | name = "redox_syscall" 401 | version = "0.1.17" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | 404 | [[package]] 405 | name = "regex" 406 | version = "0.2.1" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | dependencies = [ 409 | "aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", 410 | "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 411 | "regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 412 | "thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 413 | "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 414 | ] 415 | 416 | [[package]] 417 | name = "regex-syntax" 418 | version = "0.4.0" 419 | source = "registry+https://github.com/rust-lang/crates.io-index" 420 | 421 | [[package]] 422 | name = "rustc-demangle" 423 | version = "0.1.4" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | 426 | [[package]] 427 | name = "rustc-serialize" 428 | version = "0.3.24" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | 431 | [[package]] 432 | name = "scoped-tls" 433 | version = "0.1.0" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | 436 | [[package]] 437 | name = "serde" 438 | version = "1.0.3" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | 441 | [[package]] 442 | name = "serde_derive" 443 | version = "1.0.3" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | dependencies = [ 446 | "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 447 | "serde_derive_internals 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", 448 | "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", 449 | ] 450 | 451 | [[package]] 452 | name = "serde_derive_internals" 453 | version = "0.15.0" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | dependencies = [ 456 | "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", 457 | "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", 458 | ] 459 | 460 | [[package]] 461 | name = "slab" 462 | version = "0.3.0" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | 465 | [[package]] 466 | name = "smallvec" 467 | version = "0.3.3" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | 470 | [[package]] 471 | name = "stable_deref_trait" 472 | version = "1.0.0" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | 475 | [[package]] 476 | name = "strsim" 477 | version = "0.6.0" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | 480 | [[package]] 481 | name = "syn" 482 | version = "0.11.11" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | dependencies = [ 485 | "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 486 | "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", 487 | "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 488 | ] 489 | 490 | [[package]] 491 | name = "synom" 492 | version = "0.11.3" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | dependencies = [ 495 | "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 496 | ] 497 | 498 | [[package]] 499 | name = "term_size" 500 | version = "0.3.0" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | dependencies = [ 503 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 504 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 505 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 506 | ] 507 | 508 | [[package]] 509 | name = "thread-id" 510 | version = "3.0.0" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | dependencies = [ 513 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 514 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 515 | ] 516 | 517 | [[package]] 518 | name = "thread_local" 519 | version = "0.3.3" 520 | source = "registry+https://github.com/rust-lang/crates.io-index" 521 | dependencies = [ 522 | "thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 523 | "unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 524 | ] 525 | 526 | [[package]] 527 | name = "time" 528 | version = "0.1.37" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | dependencies = [ 531 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 532 | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", 533 | "redox_syscall 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", 534 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 535 | ] 536 | 537 | [[package]] 538 | name = "tokio-core" 539 | version = "0.1.7" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | dependencies = [ 542 | "bytes 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 543 | "futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", 544 | "iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 545 | "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 546 | "mio 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", 547 | "scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 548 | "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 549 | "tokio-io 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 550 | ] 551 | 552 | [[package]] 553 | name = "tokio-io" 554 | version = "0.1.1" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | dependencies = [ 557 | "bytes 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 558 | "futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", 559 | "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 560 | ] 561 | 562 | [[package]] 563 | name = "toml" 564 | version = "0.4.0" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | dependencies = [ 567 | "serde 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 568 | ] 569 | 570 | [[package]] 571 | name = "trust-dns" 572 | version = "0.10.1" 573 | source = "registry+https://github.com/rust-lang/crates.io-index" 574 | dependencies = [ 575 | "backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 576 | "chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)", 577 | "clap 2.24.1 (registry+https://github.com/rust-lang/crates.io-index)", 578 | "data-encoding 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 579 | "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 580 | "error-chain 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 581 | "futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", 582 | "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 583 | "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 584 | "openssl 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)", 585 | "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 586 | "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", 587 | "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 588 | "tokio-core 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 589 | "tokio-io 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 590 | "untrusted 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 591 | ] 592 | 593 | [[package]] 594 | name = "unicode-segmentation" 595 | version = "1.1.0" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | 598 | [[package]] 599 | name = "unicode-width" 600 | version = "0.1.4" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | 603 | [[package]] 604 | name = "unicode-xid" 605 | version = "0.0.4" 606 | source = "registry+https://github.com/rust-lang/crates.io-index" 607 | 608 | [[package]] 609 | name = "unreachable" 610 | version = "0.1.1" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | dependencies = [ 613 | "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 614 | ] 615 | 616 | [[package]] 617 | name = "untrusted" 618 | version = "0.3.2" 619 | source = "registry+https://github.com/rust-lang/crates.io-index" 620 | 621 | [[package]] 622 | name = "user32-sys" 623 | version = "0.2.0" 624 | source = "registry+https://github.com/rust-lang/crates.io-index" 625 | dependencies = [ 626 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 627 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 628 | ] 629 | 630 | [[package]] 631 | name = "utf8-ranges" 632 | version = "1.0.0" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | 635 | [[package]] 636 | name = "vec_map" 637 | version = "0.7.0" 638 | source = "registry+https://github.com/rust-lang/crates.io-index" 639 | 640 | [[package]] 641 | name = "void" 642 | version = "1.0.2" 643 | source = "registry+https://github.com/rust-lang/crates.io-index" 644 | 645 | [[package]] 646 | name = "winapi" 647 | version = "0.2.8" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | 650 | [[package]] 651 | name = "winapi-build" 652 | version = "0.1.1" 653 | source = "registry+https://github.com/rust-lang/crates.io-index" 654 | 655 | [[package]] 656 | name = "ws2_32-sys" 657 | version = "0.2.1" 658 | source = "registry+https://github.com/rust-lang/crates.io-index" 659 | dependencies = [ 660 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 661 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 662 | ] 663 | 664 | [metadata] 665 | "checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699" 666 | "checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6" 667 | "checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159" 668 | "checksum backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "346d7644f0b5f9bc73082d3b2236b69a05fd35cce0cfa3724e184e6a5c9e2a2f" 669 | "checksum backtrace-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d192fd129132fbc97497c1f2ec2c2c5174e376b95f535199ef4fe0a293d33842" 670 | "checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4" 671 | "checksum byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8" 672 | "checksum bytes 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f9edb851115d67d1f18680f9326901768a91d37875b87015518357c6ce22b553" 673 | "checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c" 674 | "checksum chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "9213f7cd7c27e95c2b57c49f0e69b1ea65b27138da84a170133fd21b07659c00" 675 | "checksum clap 2.24.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7541069be0b8aec41030802abe8b5cdef0490070afaa55418adea93b1e431e0" 676 | "checksum clicolors-control 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "61bd6bff2f99f947c2dbdc73cd0ebd55d8263b921fd4f68a44598555be49f32b" 677 | "checksum crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c5ea215664ca264da8a9d9c3be80d2eaf30923c259d03e870388eb927508f97" 678 | "checksum data-encoding 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d867ddbf09de0b73e09ec798972fb7f870495a0893f6f736c1855448c5a56789" 679 | "checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850" 680 | "checksum env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e3856f1697098606fc6cb97a93de88ca3f3bc35bb878c725920e6e82ecf05e83" 681 | "checksum error-chain 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faa976b4fd2e4c2b2f3f486874b19e61944d3de3de8b61c9fcf835d583871bcc" 682 | "checksum foreign-types 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e4056b9bd47f8ac5ba12be771f77a0dae796d1bbaaf5fd0b9c2d38b69b8a29d" 683 | "checksum futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "55f0008e13fc853f79ea8fc86e931486860d4c4c156cdffb59fa5f7fa833660a" 684 | "checksum gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)" = "40899336fb50db0c78710f53e87afc54d8c7266fb76262fecc78ca1a7f09deae" 685 | "checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518" 686 | "checksum indicatif 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0f6831da41884fc283d436fc213892db0521e546afae0ec46d509f5ae0f6a4e8" 687 | "checksum iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29d062ee61fccdf25be172e70f34c9f6efc597e1fb8f6526e8437b2046ab26be" 688 | "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 689 | "checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf" 690 | "checksum lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce12306c4739d86ee97c23139f3a34ddf0387bbf181bc7929d287025a8c3ef6b" 691 | "checksum libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)" = "babb8281da88cba992fa1f4ddec7d63ed96280a1a53ec9b919fd37b53d71e502" 692 | "checksum log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5141eca02775a762cc6cd564d8d2c50f67c0ea3a372cbf1c51592b3e029e10ad" 693 | "checksum memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbccc0e46f1ea47b9f17e6d67c5a96bd27030519c519c9c91327e31275a47b4" 694 | "checksum mio 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6d19442734abd7d780b981c590c325680d933e99795fe1f693f0686c9ed48022" 695 | "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" 696 | "checksum net2 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)" = "bc01404e7568680f1259aa5729539f221cb1e6d047a0d9053cab4be8a73b5d67" 697 | "checksum num 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "98b15ba84e910ea7a1973bccd3df7b31ae282bf9d8bd2897779950c9b8303d40" 698 | "checksum num-integer 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "ef1a4bf6f9174aa5783a9b4cc892cacd11aebad6c69ad027a0b65c6ca5f8aa37" 699 | "checksum num-iter 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d1891bd7b936f12349b7d1403761c8a0b85a18b148e9da4429d5d102c1a41e" 700 | "checksum num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "e1cbfa3781f3fe73dc05321bed52a06d2d491eaa764c52335cf4399f046ece99" 701 | "checksum num_cpus 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca313f1862c7ec3e0dfe8ace9fa91b1d9cb5c84ace3d00f5ec4216238e93c167" 702 | "checksum openssl 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)" = "241bcf67b1bb8d19da97360a925730bdf5b6176d434ab8ded55b4ca632346e3a" 703 | "checksum openssl-sys 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e5e0fd64cb2fa018ed2e7b2c8d9649114fe5da957c9a67432957f01e5dcc82e9" 704 | "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" 705 | "checksum parking_lot 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7403ffe0829d9a9d40dad3a76f4a8e3f28466f485c17305615169466d19ec6d3" 706 | "checksum parking_lot_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "56a19dcbb5d1e32b6cccb8a9aa1fc2a38418c8699652e735e2bf391a3dc0aa16" 707 | "checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903" 708 | "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" 709 | "checksum rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "022e0636ec2519ddae48154b028864bdce4eaf7d35226ab8e65c611be97b189d" 710 | "checksum redox_syscall 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "29dbdfd4b9df8ab31dec47c6087b7b13cbf4a776f335e4de8efba8288dda075b" 711 | "checksum regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4278c17d0f6d62dfef0ab00028feb45bd7d2102843f80763474eeb1be8a10c01" 712 | "checksum regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9191b1f57603095f105d317e375d19b1c9c5c3185ea9633a99a6dcbed04457" 713 | "checksum rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3058a43ada2c2d0b92b3ae38007a2d0fa5e9db971be260e0171408a4ff471c95" 714 | "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" 715 | "checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d" 716 | "checksum serde 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "36cdd74a4ac50d70024fb58b204a9892f422cb460d71001bd31a8fa03c79d0e7" 717 | "checksum serde_derive 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f4ae235c8b37d9d01ba08b3159894e1c6d2a3fc213c1192d41d247ba929cfc" 718 | "checksum serde_derive_internals 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "021c338d22c7e30f957a6ab7e388cb6098499dda9fd4ba1661ee074ca7a180d1" 719 | "checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" 720 | "checksum smallvec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4f8266519bc1d17d0b5b16f6c21295625d562841c708f6376f49028a43e9c11e" 721 | "checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b" 722 | "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" 723 | "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" 724 | "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" 725 | "checksum term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b6b55df3198cc93372e85dd2ed817f0e38ce8cc0f22eb32391bfad9c4bf209" 726 | "checksum thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4437c97558c70d129e40629a5b385b3fb1ffac301e63941335e4d354081ec14a" 727 | "checksum thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c85048c6260d17cf486ceae3282d9fb6b90be220bf5b28c400f5485ffc29f0c7" 728 | "checksum time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "ffd7ccbf969a892bf83f1e441126968a07a3941c24ff522a26af9f9f4585d1a3" 729 | "checksum tokio-core 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "febd81b3e2ef615c6c8077347b33f3f3deec3d708ecd08194c9707b7a1eccfc9" 730 | "checksum tokio-io 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "48f55df1341bb92281f229a6030bc2abffde2c7a44c6d6b802b7687dd8be0775" 731 | "checksum toml 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3063405db158de3dce8efad5fc89cf1baffb9501a3647dc9505ba109694ce31f" 732 | "checksum trust-dns 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b75194b74d52079c85923ee72b3e7d88b5b8aef3548804f68deed37bdcc9e8da" 733 | "checksum unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18127285758f0e2c6cf325bb3f3d138a12fee27de4f23e146cd6a179f26c2cf3" 734 | "checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" 735 | "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" 736 | "checksum unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2ae5ddb18e1c92664717616dd9549dde73f539f01bd7b77c2edb2446bdff91" 737 | "checksum untrusted 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "193df64312e3515fd983ded55ad5bcaa7647a035804828ed757e832ce6029ef3" 738 | "checksum user32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ef4711d107b21b410a3a974b1204d9accc8b10dad75d8324b5d755de1617d47" 739 | "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" 740 | "checksum vec_map 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8cdc8b93bd0198ed872357fb2e667f7125646b1762f16d60b2c96350d361897" 741 | "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 742 | "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 743 | "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 744 | "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 745 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [[bin]] 2 | name = 'batch-resolve' 3 | path = 'src/main.rs' 4 | 5 | [package] 6 | name = 'batch_resolve_cli' 7 | description = 'Fast asynchronous batch DNS resolver built on top of Tokio and TRust-DNS' 8 | version = '0.3.7' 9 | authors = ['Mike Lubinets '] 10 | homepage = 'http://github.com/mersinvald/batch_resolve' 11 | repository = 'http://github.com/mersinvald/batch_resolve' 12 | readme = 'README.md' 13 | license = 'MIT' 14 | 15 | [dependencies] 16 | futures = '0.1.11' 17 | tokio-core = '0.1.6' 18 | log = '0.3.7' 19 | env_logger = '0.4.2' 20 | lazy_static = '0.2' 21 | clap = '2.21.2' 22 | serde = '1.0' 23 | serde_derive = '1.0' 24 | toml = '0.4' 25 | crossbeam = '0.2' 26 | num_cpus = '1.3.0' 27 | indicatif = '0.1.0' 28 | 29 | [dependencies.trust-dns] 30 | version = '0.10' 31 | default-features = false 32 | features = ['openssl'] 33 | [profile.release] 34 | lto = true 35 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | 2 | ## v0.3.7 (2019-11-27) 3 | 4 | 5 | #### Bug Fixes 6 | 7 | * rename crate back to batch_resolve_cli ([ea93d4d9](ea93d4d9)) 8 | 9 | 10 | 11 | 12 | ## v0.3.6 (2019-11-27) 13 | 14 | 15 | #### Features 16 | 17 | * CSV output option, with [name resolved_name] table ([f533c5de](f533c5de)) 18 | * semanteecore-based releases to Crates.io ([ffd39899](ffd39899)) 19 | * switch to Stable Rust ([3b804b0c](3b804b0c)) 20 | * pristinify the repo ([70b497de](70b497de)) 21 | * **style:** rustfmt ([b041250d](b041250d)) 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Mike Lubinets 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Crates.io](https://img.shields.io/crates/v/batch_resolve_cli.svg)](https://crates.io/crates/batch_resolve_cli) 2 | [![Gitter](https://img.shields.io/badge/GITTER-join%20chat-green.svg)](https://gitter.im/batch_resolve/Lobby?utm_source=share-link&utm_medium=link&utm_campaign=share-link) 3 | [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/mersinvald) 4 | 5 | This page in [Russian](README_RUS.md) 6 | 7 | # Batch Resolve 8 | 9 | Fast asynchronous DNS resolver 10 | 11 | ## Support notice 12 | 13 | `batch-resolve` isn't actively maintained anymore, but I'll be happy to accept Pull Requests and release new versions, if anyone would be desperate enough to try and dig through the futures 0.1 spaghetti :) 14 | 15 | If you use this tool and encounter critical bugs, let me know in the issues -- I'll try to find time to take a look. 16 | 17 | ## Install 18 | ### Distro packages 19 | There are prebuilt *deb* and *rpm* packages for x86_64 you can find within the releases in [the list of versions](https://github.com/mersinvald/batch_resolve/tags) 20 | 21 | Arch Linux users can install the package [from AUR](https://aur.archlinux.org/packages/batch_resolve/) 22 | 23 | Packages install the config file into /etc/batch_resolve.toml 24 | ### Static binary 25 | Every release binary can be found in the [the list of versions](https://github.com/mersinvald/batch_resolve/tags). Just place it to one of directories in your PATH (e.g. /usr/bin) 26 | 27 | ### Install from crates.io 28 | If you have rust toolkit installed, you can install *batch_resolve* with 29 | ``` 30 | cargo install batch_resolve_cli 31 | ``` 32 | 33 | ## Usage 34 | 35 | Input and output format is list delimited with new line. 36 | Consider such input `domains.txt` 37 | ``` 38 | google.com 39 | rust-lang.org 40 | mozilla.org 41 | ``` 42 | 43 | Resolve all `A` records: 44 | ``` 45 | batch_resolve --in domains.txt --out hosts.txt --query A 46 | ``` 47 | 48 | Resolve `A` and `AAAA` records: 49 | ``` 50 | batch_resolve -i domains.txt -o hosts.txt -q A 51 | -i domains.txt -o hosts.txt -q AAAA 52 | ``` 53 | 54 | ### Configuration 55 | By default batch_resolve uses Google DNS servers `8.8.8.8` and `8.8.4.4` and retries `10` times on Connection Timeout error. 56 | These and Queries Per Second parameters may be altered in configuration file. 57 | 58 | Configuration file may be placed in the following locations (priority descending): 59 | ``` 60 | batch_resolve.toml 61 | $HOME/.config/batch_resolve.toml 62 | /etc/batch_resolve.toml 63 | ``` 64 | 65 | Configuration includes DNS servers, queries per second amount and retries on failure count 66 | ```toml 67 | # DNS servers are only accepted as socket addresses 68 | # If port is not specified default DNS :53 port will be used 69 | dns = [ 70 | "8.8.8.8", 71 | "8.8.4.4" 72 | ] 73 | 74 | # How many queries to perform per second 75 | # WARNING: Google Public DNS guaranteed to handle 500 requests per second max 76 | # Please make sure that resolve results do not vary with higher request rates 77 | # before using high QPS configuration in production. 78 | # Alternatively you can use your own local caching DNS server. 79 | queries_per_second = 500 80 | 81 | # Times to retry on connection timeout 82 | retry = 5 83 | ``` 84 | 85 | Configuration template can also be found [here](batch_resolve.toml) 86 | 87 | ## Contributing 88 | 89 | To build project please clone the repo 90 | ``` 91 | git clone git@github.com:mersinvald/batch_resolve.git 92 | ``` 93 | And run `cagro build` 94 | ``` 95 | cd batch_resolve 96 | cargo build 97 | ``` 98 | `batch_resolve` can be build with stable rust 99 | 100 | Please file an issue if you have any improvement suggestion or bug report. 101 | 102 | Pull Requests are welcome also! 103 | 104 | ## License 105 | 106 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details 107 | 108 | ## Acknowledgement 109 | * [TRust-DNS: A Rust based DNS client and server, built to be safe and secure from the ground up](https://github.com/bluejekyll/trust-dns) 110 | * [rust-musl-builder: Docker container for easily building static Rust binaries](https://github.com/emk/rust-musl-builder)gi 111 | 112 | ## Donate 113 | 114 | If you feel that this work is worth something and that it saved your time you can give me a cup of coffee :) 115 | 116 | * [Donate with PayPal](https://www.paypal.me/mersinvald) 117 | * [Donate with yandex.money](http://yasobe.ru/na/batch_resolve_coffee) 118 | -------------------------------------------------------------------------------- /README_RUS.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/mersinvald/batch_resolve.svg?branch=master)](https://travis-ci.org/mersinvald/batch_resolve) 2 | [![Crates.io](https://img.shields.io/crates/v/batch_resolve_cli.svg)](https://crates.io/crates/batch_resolve_cli) 3 | [![Gitter](https://img.shields.io/badge/GITTER-join%20chat-green.svg)](https://gitter.im/batch_resolve/Lobby?utm_source=share-link&utm_medium=link&utm_campaign=share-link) 4 | [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/mersinvald) 5 | 6 | # Batch Resolve 7 | 8 | Быстрый асинхронный DNS резолвер 9 | 10 | ## Установка 11 | ### Пакеты 12 | Существуют универсальные deb и rpm пакеты, собранные под архитектуру x86_64, их можно найти в [списке релизов и файлов для загрузки.](https://github.com/mersinvald/batch_resolve/tags) 13 | 14 | Пользователи Arch Linux могут установить *batch_resolve* [из AUR](https://aur.archlinux.org/packages/batch_resolve/) 15 | 16 | Пакеты устанавливают конфиг в /etc/batch_resolve.toml 17 | ### Статический бинарник 18 | Для каждого релиза выпускается статический исполняемый файл: 19 | [Список релизов и файлов для загрузки](https://github.com/mersinvald/batch_resolve/tags) 20 | 21 | ### Установка с crates.io 22 | Вы можете установить batch_resolve используя пакетный менеджер *cargo*, поставляемый в составе тулкита Rust 23 | ``` 24 | cargo install batch_resolve_cli 25 | ``` 26 | 27 | ## Использование 28 | 29 | Входные и выходные данные представлены в виде списка, разделенного переводом строк. 30 | Например, список доменных имен `domains.txt` будет выглядеть так: 31 | ``` 32 | google.com 33 | rust-lang.org 34 | mozilla.org 35 | ``` 36 | 37 | Получить все `A` записи: 38 | ``` 39 | batch_resolve --in domains.txt --out hosts.txt --query A 40 | ``` 41 | 42 | Получить `A` и `AAAA` записи: 43 | ``` 44 | batch_resolve -i domains.txt -o hosts.txt -q A 45 | -i domains.txt -o hosts.txt -q AAAA 46 | ``` 47 | 48 | ### Конфигурация 49 | По умолчанию `batch_resolve` использует Google Publiс DNS `8.8.8.8` и `8.8.4.4`, `10` раз пытается повторить запрос, вылетевший с Connection Timeout. 50 | Эти параметры и количество запросов в секунду можно изменить в файле конфигурации. 51 | 52 | Конфиг может быть расположен по следующим путям (по уменьшению приоритета): 53 | ``` 54 | batch_resolve.toml 55 | $HOME/.config/batch_resolve.toml 56 | /etc/batch_resolve.toml 57 | ``` 58 | 59 | Конфигурация включает DNS сервера, количество запросов в секунду и количество повторов по таймауту. 60 | ```toml 61 | # Адреса DNS серверов 62 | # Если порт не указан -- по умолчанию будет использован полт 53 63 | dns = [ 64 | "8.8.8.8", 65 | "8.8.4.4" 66 | ] 67 | 68 | # Количество запросов в секунду 69 | # ВНИМАНИЕ: Google Public DNS гарантированно может обработать максимум 500 запросов в секунду 70 | # Прежде чем использовать настройки с более высоким QPS убедитесь что результаты 71 | # не отличаются значительно от результатов с настройкой по-умолчанию. 72 | queries_per_second = 500 73 | 74 | # Количество повторов запроса по таймауту 75 | retry = 5 76 | ``` 77 | 78 | Шаблон конфигурации можно найти [здесь](batch_resolve.toml) 79 | 80 | ## Разработка 81 | 82 | Для сборки проекта из исходников, склонируйте репозиторий 83 | ``` 84 | git clone git@github.com:mersinvald/batch_resolve.git 85 | ``` 86 | И запустите `cagro build` 87 | ``` 88 | cd batch_resolve 89 | cargo build 90 | ``` 91 | `batch_resolve` собирается со стабильной версией rust 92 | 93 | Если у вас есть предложения по улучшению или багрепорт, пожалуйста заведите Issue. 94 | 95 | Pull реквесты приветствуются! 96 | 97 | ## Лицензия 98 | 99 | Проект лицензирован лицензией [MIT](LICENSE.md). 100 | 101 | ## Поддержать проект 102 | 103 | Если этот проект помог сэкономить Ваше время, Вы можете поддержать разработчика чашечкой хорошего кофе :) 104 | 105 | * [Поддержать на PayPal](https://www.paypal.me/mersinvald) 106 | * [Поддержать на yasobe.ru](http://yasobe.ru/na/batch_resolve_coffee) -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | # Releasing 2 | 3 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [BCP 14](https://tools.ietf.org/html/bcp14) [RFC2119](https://tools.ietf.org/html/rfc2119) [RFC8174](https://tools.ietf.org/html/rfc8174) when, and only when, they appear in all capitals, as shown here. 4 | 5 | This document is licensed under [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.html). 6 | 7 | When using the name 'version' we mean the versioning scheme described in [VERSIONING.md](VERSIONING.md) 8 | 9 | ## Introduction 10 | 11 | This document is to describe the release pipeline, which is taking the result of the artifacts created by `cargo` and publish a release to the various platforms for the project. 12 | 13 | We propose: 14 | - a set of release platform targets that are allowable 15 | - a pipeline for handling the release artifacts 16 | 17 | It is NOT the purpose of this document to describe how a project might create a build, NOR is it describing a structure in which projects MUST write build artifacts to. It is describing the structure of the releases themselves. 18 | 19 | ## Release Pipeline 20 | 21 | By default the project release pipeline is defined in the `.circleci/config.yml` and uses the `semantic-rs` tool for 22 | - crates.io crate publishing 23 | - generating changelog from [semantic commits](CONVENTIONAL_COMMITS.md) 24 | - publishing a GitHub release 25 | 26 | Documentation will automatically be available at [docs.rs](https://docs.rs) website after a few minutes since publishing a new release. 27 | 28 | ### Create a build from current branch 29 | 30 | For building please follow the `cargo` [documentation](https://doc.rust-lang.org/cargo/index.html) 31 | 32 | ### Bump the version of the project 33 | 34 | Projects SHOULD automate the version bump following [CONVENTIONAL_COMMITS.md](CONVENTIONAL_COMMITS.md). 35 | 36 | ### Generate Changelog 37 | 38 | Projects SHOULD use generated changelogs from following [CONVENTIONAL_COMMITS.md](CONVENTIONAL_COMMITS.md). 39 | 40 | ### Commit the bump + changelog update 41 | 42 | A project MUST generate a commit with the changes. 43 | 44 | ### Tag the commit with the bumped version 45 | 46 | A project MUST be tagged with the semantic versioning scheme from [VERSIONING.md](VERSIONING.md). 47 | 48 | ### Sign the releases. 49 | 50 | - MUST be a pgp signature 51 | - MUST be the same pgp key as is registered with Github 52 | - MUST be a detached ascii-armored (.asc) signature 53 | - All files in the build folder MUST have an associated signature file 54 | 55 | ### Push changelog & version bump 56 | 57 | ### Run Release Targets 58 | 59 | For each of the desired release targets, prepare and push the release. 60 | 61 | #### Example Release Targets 62 | 63 | 1. Github 64 | 2. [crates.io](https://crates.io) 65 | 66 | ## Resources 67 | 68 | - [semantic-release](https://github.com/semantic-release/semantic-release) 69 | - [Conventional Commits](https://conventionalcommits.org/) 70 | -------------------------------------------------------------------------------- /VERSIONING.md: -------------------------------------------------------------------------------- 1 | # Versioning 2 | 3 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [BCP 14](https://tools.ietf.org/html/bcp14) [RFC2119](https://tools.ietf.org/html/rfc2119) [RFC8174](https://tools.ietf.org/html/rfc8174) when, and only when, they appear in all capitals, as shown here. 4 | 5 | This document is licensed under [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.html). 6 | 7 | ## Introduction 8 | This document is to describe how a project is to version its releases 9 | 10 | It also describes standardized tooling around manipulating the version 11 | 12 | ## Semver 13 | A project MUST use Semantic Versioning [semver](https://semver.org). Build metadata MAY NOT be used in a project. Build metadata SHOULD be ignored. 14 | 15 | A Basic summary of Semantic Versioning taken from: [semver.org](https://semver.org) 16 | 17 | ### Summary: 18 | 19 | Given a version number MAJOR.MINOR.PATCH, increment the: 20 | 21 | MAJOR version when you make incompatible API changes, 22 | MINOR version when you add functionality in a backwards-compatible manner, and 23 | PATCH version when you make backwards-compatible bug fixes. 24 | Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format. 25 | 26 | In addition to classic semver versioning scheme this template also supports the major-zero versioning scheme that's popular in Rust ecosystem. 27 | -------------------------------------------------------------------------------- /batch_resolve.toml: -------------------------------------------------------------------------------- 1 | # DNS servers are only accepted as socket addresses 2 | # If port is not specified default DNS :53 port will be used 3 | dns = [ 4 | "8.8.8.8", 5 | "8.8.4.4" 6 | ] 7 | 8 | # How many queries to perform per second 9 | # WARNING: Google Public DNS guaranteed to handle 500 requests per second max 10 | # Please make sure that resolve results do not vary with higher request rates 11 | # before using high QPS configuration in production. 12 | # Alternatively you can use your own local caching DNS server. 13 | queries_per_second = 500 14 | 15 | # Times to retry on connection timeout 16 | retry = 5 17 | -------------------------------------------------------------------------------- /packaging/package_release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | SHELL=/bin/bash 3 | VERSION=$(grep "version" Cargo.toml | head -n 1 | grep -Eo "[0-9].[0-9].[0-9]") 4 | LICENSE="MIT" 5 | MAINTAINER="Mike Lubinets " 6 | DESCRIPTION="Fast asynchronous DNS resolver" 7 | URL="https://github.com/mersinvald/batch_resolve" 8 | 9 | # Build statuc binary 10 | ~/.bin/rust-musl-builder cargo build --release || exit 1 11 | 12 | # Make temp release dir 13 | mkdir -p packaging/temp 14 | 15 | # Copy release into temp folder 16 | cp target/x86_64-unknown-linux-musl/release/batch_resolve packaging/temp 17 | 18 | cd packaging 19 | 20 | fpm -s dir -t deb --version "$VERSION" --description "$DESCRIPTION" --url "$URL" --name "batch_resolve" --maintainer "$MAINTAINER" temp/batch_resolve=/usr/bin/batch_resolve ../batch_resolve.toml=/etc/batch_resolve.toml 21 | fpm -s dir -t rpm --version "$VERSION" --description "$DESCRIPTION" --url "$URL" --name "batch_resolve" --maintainer "$MAINTAINER" temp/batch_resolve=/usr/bin/batch_resolve ../batch_resolve.toml=/etc/batch_resolve.toml 22 | 23 | rm -rf temp/ 24 | 25 | # Update aur package 26 | git clone ssh://aur@aur.archlinux.org/batch_resolve.git || exit 1 27 | cd batch_resolve 28 | pwd 29 | sed -i "s/pkgver=.*/pkgver=$VERSION/g" PKGBUILD 30 | 31 | HASH=$(makepkg -g) 32 | sed -i "/md5sums=.*/d" PKGBUILD 33 | echo $HASH >> PKGBUILD 34 | 35 | makepkg --printsrcinfo > .SRCINFO 36 | 37 | cat PKGBUILD 38 | 39 | git add PKGBUILD .SRCINFO && git commit -m "Version $VERSION" && git push 40 | 41 | cd .. 42 | rm -rf batch_resolve/ -------------------------------------------------------------------------------- /releaserc.toml: -------------------------------------------------------------------------------- 1 | [plugins] 2 | git = "builtin" 3 | clog = "builtin" 4 | github = "builtin" 5 | rust = "builtin" 6 | 7 | [steps] 8 | pre_flight = "discover" 9 | get_last_release = "git" 10 | derive_next_version = [ "clog" ] 11 | generate_notes = "clog" 12 | prepare = "discover" 13 | verify_release = "discover" 14 | commit = "git" 15 | publish = [ "github", "rust" ] 16 | notify = "discover" 17 | 18 | [cfg.clog] 19 | ignore = ["ci"] 20 | 21 | [cfg.git] 22 | branch = "master" 23 | force_https = true 24 | 25 | [cfg.github] 26 | pre_release = true 27 | assets = [ 28 | "/workspace/bin/*", 29 | "Changelog.md" 30 | ] -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | stable -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use std::fmt; 3 | use std::net::AddrParseError; 4 | use std::sync::{Arc, RwLock}; 5 | use toml; 6 | 7 | use std::net::SocketAddr; 8 | 9 | pub type ConfigResult = Result; 10 | 11 | lazy_static! { 12 | static ref DEFAULT_DNS_SERVERS: Vec = 13 | vec!["8.8.8.8:53".parse().unwrap(), "8.8.4.4:53".parse().unwrap(),]; 14 | static ref DEFAULT_TIMEOUT_RETRIES: u32 = 10; 15 | static ref DEFAULT_QPS: u32 = 500; 16 | pub static ref CONFIG: Arc> = Arc::new(RwLock::new(Config::new())); 17 | } 18 | 19 | #[derive(Debug)] 20 | pub struct Config { 21 | dns_list: Vec, 22 | qps: u32, 23 | timeout_retries: u32, 24 | } 25 | 26 | impl Default for Config { 27 | fn default() -> Self { 28 | Config { 29 | dns_list: DEFAULT_DNS_SERVERS.clone(), 30 | qps: *DEFAULT_QPS, 31 | timeout_retries: *DEFAULT_TIMEOUT_RETRIES, 32 | } 33 | } 34 | } 35 | 36 | impl Config { 37 | pub fn new() -> Self { 38 | Self::default() 39 | } 40 | 41 | pub fn timeout_retries(&self) -> u32 { 42 | self.timeout_retries 43 | } 44 | 45 | pub fn qps(&self) -> u32 { 46 | self.qps 47 | } 48 | 49 | pub fn dns_list(&self) -> &[SocketAddr] { 50 | &self.dns_list 51 | } 52 | 53 | pub fn parse(&mut self, string: &str) -> ConfigResult<()> { 54 | #[derive(Deserialize, Debug)] 55 | struct Config { 56 | dns: Option>, 57 | retry: Option, 58 | queries_per_second: Option, 59 | } 60 | 61 | let mut cfg_fmt: Config = toml::from_str(string)?; 62 | 63 | if let Some(mut dns_fmt_vec) = cfg_fmt.dns.take() { 64 | let mut dns_servers = Vec::new(); 65 | 66 | for dns in &mut dns_fmt_vec { 67 | if !dns.contains(':') { 68 | dns.push_str(":53") 69 | } 70 | dns_servers.push(dns.parse()?); 71 | } 72 | 73 | debug!("{:?}", dns_servers); 74 | 75 | self.dns_list = dns_servers; 76 | } 77 | 78 | if let Some(retry) = cfg_fmt.retry { 79 | self.timeout_retries = retry; 80 | } 81 | 82 | if let Some(qps) = cfg_fmt.queries_per_second { 83 | self.qps = qps; 84 | } 85 | 86 | Ok(()) 87 | } 88 | } 89 | 90 | #[derive(Debug)] 91 | pub enum ConfigError { 92 | AddrParseError(AddrParseError), 93 | TomlParseError(toml::de::Error), 94 | } 95 | 96 | impl Error for ConfigError { 97 | fn description(&self) -> &str { 98 | match *self { 99 | ConfigError::AddrParseError(ref err) => err.description(), 100 | ConfigError::TomlParseError(ref err) => err.description(), 101 | } 102 | } 103 | } 104 | 105 | impl fmt::Display for ConfigError { 106 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 107 | write!(f, "{}", self.description()) 108 | } 109 | } 110 | 111 | impl From for ConfigError { 112 | fn from(err: AddrParseError) -> Self { 113 | ConfigError::AddrParseError(err) 114 | } 115 | } 116 | 117 | impl From for ConfigError { 118 | fn from(err: toml::de::Error) -> Self { 119 | ConfigError::TomlParseError(err) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate lazy_static; 3 | #[macro_use] 4 | extern crate serde_derive; 5 | extern crate serde; 6 | extern crate toml; 7 | 8 | #[macro_use] 9 | extern crate log; 10 | #[macro_use] 11 | extern crate clap; 12 | extern crate env_logger; 13 | 14 | extern crate crossbeam; 15 | extern crate futures; 16 | extern crate indicatif; 17 | extern crate num_cpus; 18 | extern crate tokio_core; 19 | extern crate trust_dns; 20 | 21 | mod config; 22 | mod resolve; 23 | use config::*; 24 | use resolve::*; 25 | 26 | use std::collections::{HashMap, HashSet}; 27 | use std::fs::File; 28 | use std::io::{self, Read, Write}; 29 | use std::path::Path; 30 | use std::thread; 31 | use std::time::Duration; 32 | 33 | use std::sync::mpsc; 34 | use std::sync::{Arc, Mutex}; 35 | 36 | use std::env; 37 | 38 | use clap::{App, Arg}; 39 | 40 | use env_logger::LogBuilder; 41 | use log::{LogLevelFilter, LogRecord}; 42 | 43 | use indicatif::{ProgressBar, ProgressStyle}; 44 | 45 | #[rustfmt::skip] 46 | fn process_args() -> (Vec, Vec, Vec) { 47 | let app = App::new("Batch Resolve") 48 | .about("Fast asynchronous DNS batch resolver") 49 | .version(crate_version!()) 50 | .author(crate_authors!()) 51 | .arg(Arg::with_name("inputs") 52 | .help("Input file") 53 | .short("i") 54 | .long("in") 55 | .value_name("INPUT") 56 | .takes_value(true) 57 | .multiple(true) 58 | .number_of_values(1)) 59 | .arg(Arg::with_name("outputs") 60 | .help("Output file") 61 | .short("o") 62 | .long("out") 63 | .value_name("OUTPUT") 64 | .multiple(true) 65 | .number_of_values(1)) 66 | .arg(Arg::with_name("queries") 67 | .help("Query type") 68 | .short("q") 69 | .long("query") 70 | .possible_values(&QueryType::variants()) 71 | .value_name("QUERY_TYPE") 72 | .multiple(true) 73 | .number_of_values(1)) 74 | .arg(Arg::with_name("config") 75 | .help("Sets a custom config file") 76 | .short("c") 77 | .long("config") 78 | .value_name("FILE") 79 | .takes_value(true)) 80 | .arg(Arg::with_name("verbosity") 81 | .help("Level of verbosity (-v -vv -vvv)") 82 | .short("v") 83 | .multiple(true) 84 | ); 85 | 86 | // Save help message to use later on errors 87 | let mut help_msg = Vec::new(); 88 | app.write_help(&mut help_msg).unwrap(); 89 | let help_msg = String::from_utf8(help_msg).unwrap(); 90 | 91 | let matches = app.get_matches(); 92 | 93 | // Setup logging and with appropriate verbosity level 94 | match matches.occurrences_of("verbosity") { 95 | 0 => setup_logger(LogLevelFilter::Error), 96 | 1 => setup_logger(LogLevelFilter::Warn), 97 | 2 => setup_logger(LogLevelFilter::Info), 98 | 3 => setup_logger(LogLevelFilter::Debug), 99 | 4 | _ => setup_logger(LogLevelFilter::Trace), 100 | } 101 | 102 | // Get arguments 103 | let inputs = values_t!(matches.values_of("inputs"), String).unwrap_or(vec![]); 104 | let outputs = values_t!(matches.values_of("outputs"), String).unwrap_or(vec![]); 105 | let qtypes = values_t!(matches.values_of("queries"), QueryType).unwrap_or(vec![]); 106 | 107 | // Cardinalities should be the same 108 | if inputs.len() != outputs.len() || outputs.len() != qtypes.len() || inputs.is_empty() { 109 | error!("input, output and query arguments number must be the same and non-zero"); 110 | println!("{}", help_msg); 111 | std::process::exit(1); 112 | } 113 | 114 | // Process config 115 | process_config(matches.value_of("config")); 116 | 117 | // Return inputs, outputs and query types 118 | (inputs, outputs, qtypes) 119 | } 120 | 121 | fn process_config(arg_path: Option<&str>) { 122 | // Config locations in priority-descending order 123 | let default_config_locations = vec![ 124 | "batch_resolve.toml", 125 | "$HOME/.config/batch_resolve.toml", 126 | "/etc/batch_resolve.toml", 127 | ]; 128 | 129 | let config_file = if let Some(arg_path) = arg_path { 130 | // Custom config is the only option when it is passed 131 | info!("Custom config path passed: {:?}", arg_path); 132 | let file = File::open(arg_path).unwrap_or_else(|error| { 133 | error!( 134 | "failed to open custom config file {:?}: {}", 135 | arg_path, error 136 | ); 137 | std::process::exit(1); 138 | }); 139 | Some(file) 140 | } else { 141 | default_config_locations 142 | .iter() 143 | .filter_map(|path| { 144 | File::open(path) 145 | .map_err(|err| debug!("failed to open default config file {:?}: {}", path, err)) 146 | .ok() 147 | }) 148 | .next() 149 | }; 150 | 151 | // Load config into the static CONFIG entry 152 | if let Some(mut config_file) = config_file { 153 | let mut config_str = String::new(); 154 | config_file.read_to_string(&mut config_str).unwrap(); 155 | CONFIG 156 | .write() 157 | .unwrap() 158 | .parse(&config_str) 159 | .unwrap_or_else(|e| { 160 | error!("malformed configuration file:\n {}", e); 161 | std::process::exit(1); 162 | }); 163 | } 164 | 165 | // Info to make sure right config is loaded on startup 166 | let config = CONFIG.read().unwrap(); 167 | info!("Retries on timeout: {:?}", config.timeout_retries()); 168 | info!("Queries Per Second: {:?}", config.qps()); 169 | info!("DNS Servers: {:?}", config.dns_list()); 170 | } 171 | 172 | // mpsc::Receiver of resolve results and output file path 173 | struct ResolveResult { 174 | pub resolved_rx: ResolvedRx, 175 | pub out_path: String, 176 | } 177 | 178 | impl ResolveResult { 179 | pub fn new(resolved_rx: ResolvedRx, out_path: String) -> Self { 180 | ResolveResult { 181 | resolved_rx, 182 | out_path, 183 | } 184 | } 185 | } 186 | 187 | fn main() { 188 | let (inputs, outputs, qtypes) = process_args(); 189 | 190 | let mut overall_count = 0; 191 | let mut resolve_results = vec![]; 192 | let mut batch = Batch::new(); 193 | 194 | for (&qtype, (input, output)) in qtypes.iter().zip(inputs.iter().zip(outputs.into_iter())) { 195 | let input_data = load_file(input).unwrap_or_else(|err| { 196 | error!("failed to open {:?}: {}", input, err); 197 | std::process::exit(1); 198 | }); 199 | 200 | overall_count += input_data.len(); 201 | 202 | let (resolved_tx, resolved_rx) = mpsc::channel(); 203 | let rresult = ResolveResult::new(resolved_rx, output); 204 | batch.add_task(input_data, resolved_tx, qtype); 205 | resolve_results.push(rresult); 206 | } 207 | 208 | // Create status output thread and register status callback 209 | let status = Arc::new(Mutex::new(Status::default())); 210 | let callback_status = status.clone(); 211 | 212 | batch.register_status_callback(Box::new(move |s: Status| { 213 | *callback_status.lock().unwrap() = s; 214 | })); 215 | 216 | thread::spawn(move || { 217 | debug!("Starting status printer thread"); 218 | let pb = ProgressBar::new(overall_count as u64); 219 | pb.set_style(ProgressStyle::default_bar() 220 | .template("{spinner:.green} [{elapsed}] [{wide_bar:.cyan/blue}] {pos}/{len} ({eta}) | {msg} {spinner:.green}") 221 | .progress_chars("#>-")); 222 | 223 | let mut s; 224 | while { 225 | s = status.lock().unwrap().clone(); 226 | s.done < overall_count as u64 227 | } { 228 | let message = format!("{} running | {} failed", s.running, s.fail); 229 | pb.set_position(s.done); 230 | pb.set_message(&message); 231 | thread::sleep(Duration::from_millis(30)); 232 | } 233 | 234 | pb.finish_with_message("done"); 235 | debug!("Terminating status printer thread"); 236 | }); 237 | 238 | // Execute batch job 239 | batch.run(); 240 | 241 | // Merge all results with common output pathes 242 | let mut data_sinks = HashMap::new(); 243 | for resolved in resolve_results { 244 | let entry = data_sinks 245 | .entry(resolved.out_path) 246 | .or_insert_with(HashSet::new); 247 | (*entry).extend(resolved.resolved_rx); 248 | } 249 | 250 | // Merge data into files 251 | for (path, data) in data_sinks { 252 | write_file(data, path).unwrap(); 253 | } 254 | } 255 | 256 | fn load_file>(path: P) -> io::Result> { 257 | let mut buffer = String::new(); 258 | let mut file = File::open(path)?; 259 | file.read_to_string(&mut buffer)?; 260 | Ok(buffer.lines().map(String::from).collect()) 261 | } 262 | 263 | fn write_file, P: AsRef>( 264 | data: I, 265 | path: P, 266 | ) -> io::Result<()> { 267 | // Open file for writing 268 | let mut file = File::create(path.as_ref())?; 269 | 270 | // If file's extension is `cvs`, we wanna output pairs, and not just the results 271 | let csv_mode = path.as_ref().extension().map_or(false, |ext| ext == "csv"); 272 | 273 | // Sort and output data 274 | let mut data = data.into_iter().collect::>(); 275 | 276 | if csv_mode { 277 | data.sort_by(|a, b| a.0.cmp(&b.0)); 278 | for (from, to) in &data { 279 | file.write_all(from.as_bytes())?; 280 | file.write_all(b" ")?; 281 | file.write_all(to.as_bytes())?; 282 | file.write_all(b"\n")?; 283 | } 284 | } else { 285 | data.sort_by(|a, b| a.1.cmp(&b.1)); 286 | for (_, to) in &data { 287 | file.write_all(to.as_bytes())?; 288 | file.write_all(b"\n")?; 289 | } 290 | } 291 | 292 | Ok(()) 293 | } 294 | 295 | fn setup_logger(level: LogLevelFilter) { 296 | let format = |record: &LogRecord| format!("{}: {}\t\t\t", record.level(), record.args()); 297 | 298 | let mut builder = LogBuilder::new(); 299 | builder.format(format).filter(Some("batch_resolve"), level); 300 | 301 | if env::var("RUST_LOG").is_ok() { 302 | builder.parse(&env::var("RUST_LOG").unwrap()); 303 | } 304 | 305 | builder.init().unwrap(); 306 | } 307 | -------------------------------------------------------------------------------- /src/resolve/batch.rs: -------------------------------------------------------------------------------- 1 | use std::sync::mpsc; 2 | use std::thread; 3 | 4 | use resolve::resolver_threadpool::ResolveTask; 5 | use resolve::resolver_threadpool::ResolverThreadPool; 6 | 7 | #[derive(Debug, Default, Copy, Clone)] 8 | pub struct Status { 9 | pub done: u64, 10 | pub success: u64, 11 | pub fail: u64, 12 | pub errored: u64, 13 | pub running: u64, 14 | } 15 | 16 | pub type StatusTx = mpsc::Sender; 17 | 18 | #[derive(Copy, Clone, Debug)] 19 | pub enum ResolveStatus { 20 | Started, 21 | Success, 22 | Failure, 23 | Error, 24 | } 25 | 26 | pub type ResolvedTx = mpsc::Sender<(String, String)>; 27 | pub type ResolvedRx = mpsc::Receiver<(String, String)>; 28 | 29 | pub struct Batch 30 | where 31 | I: IntoIterator + 'static, 32 | { 33 | tasks: Vec>, 34 | outputs: Vec, 35 | status_fn: Box, 36 | } 37 | 38 | impl Batch 39 | where 40 | I: IntoIterator + 'static, 41 | { 42 | pub fn new() -> Self { 43 | Batch { 44 | tasks: vec![], 45 | outputs: vec![], 46 | status_fn: Box::new(|_| ()), 47 | } 48 | } 49 | 50 | pub fn register_status_callback(&mut self, func: Box) { 51 | self.status_fn = func 52 | } 53 | 54 | pub fn add_task(&mut self, input: I, output: ResolvedTx, qtype: QueryType) { 55 | self.tasks.push(BatchTask::new(input, qtype)); 56 | self.outputs.push(output) 57 | } 58 | 59 | pub fn run(mut self) { 60 | let tasks_cnt = self.tasks.len(); 61 | 62 | let (status_tx, status_rx) = mpsc::channel(); 63 | 64 | let mut resolve_pool = ResolverThreadPool::num_cpus(); 65 | 66 | // Spawn resolve tasks 67 | for _ in 0..tasks_cnt { 68 | let task = self.tasks.pop().unwrap(); 69 | let out = self.outputs.pop().unwrap(); 70 | 71 | for name in task.input { 72 | trace!("Spawning task {} {}", name, task.qtype); 73 | resolve_pool.spawn(ResolveTask { 74 | tx: out.clone(), 75 | name: name, 76 | qtype: task.qtype, 77 | }); 78 | } 79 | } 80 | 81 | let status_fn = self.status_fn; 82 | 83 | // Spawn status thread 84 | thread::spawn(move || { 85 | let mut status = Status::default(); 86 | 87 | for resolve_status in status_rx { 88 | trace!("Resolve status: received {:?}", resolve_status); 89 | match resolve_status { 90 | ResolveStatus::Started => status.running += 1, 91 | other => { 92 | status.done += 1; 93 | status.running -= 1; 94 | match other { 95 | ResolveStatus::Success => status.success += 1, 96 | ResolveStatus::Failure => status.fail += 1, 97 | ResolveStatus::Error => status.errored += 1, 98 | _ => (), 99 | } 100 | } 101 | } 102 | status_fn(status); 103 | } 104 | }); 105 | 106 | trace!("Starting resolve job on a thread pool"); 107 | resolve_pool.start(status_tx); 108 | trace!("Finished resolve"); 109 | } 110 | } 111 | 112 | arg_enum! { 113 | #[derive(Copy, Clone, Debug)] 114 | pub enum QueryType { 115 | A, 116 | AAAA, 117 | PTR, 118 | NS 119 | } 120 | } 121 | 122 | use trust_dns::rr::RecordType; 123 | impl Into for QueryType { 124 | fn into(self) -> RecordType { 125 | match self { 126 | QueryType::A => RecordType::A, 127 | QueryType::AAAA => RecordType::AAAA, 128 | QueryType::PTR => RecordType::PTR, 129 | QueryType::NS => RecordType::NS, 130 | } 131 | } 132 | } 133 | 134 | pub struct BatchTask 135 | where 136 | I: IntoIterator, 137 | { 138 | input: I, 139 | qtype: QueryType, 140 | } 141 | 142 | impl BatchTask 143 | where 144 | I: IntoIterator + 'static, 145 | { 146 | fn new(input: I, qtype: QueryType) -> Self { 147 | BatchTask { 148 | input: input, 149 | qtype: qtype, 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/resolve/error.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use std::fmt; 3 | 4 | #[derive(Debug)] 5 | pub enum ResolverError { 6 | ConnectionTimeout, 7 | NameServerNotResolved, 8 | NotFound, 9 | DnsClientError(::trust_dns::error::ClientError), 10 | } 11 | 12 | unsafe impl Send for ResolverError {} 13 | 14 | impl fmt::Display for ResolverError { 15 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 16 | write!(f, "{}", self.description()) 17 | } 18 | } 19 | 20 | impl Error for ResolverError { 21 | fn description(&self) -> &str { 22 | match *self { 23 | ResolverError::ConnectionTimeout => "Connection timeout", 24 | ResolverError::NameServerNotResolved => "Failed to resolve nameserver", 25 | ResolverError::NotFound => "Not found", 26 | ResolverError::DnsClientError(ref err) => err.description(), 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/resolve/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod batch; 2 | pub mod error; 3 | pub mod resolver; 4 | mod resolver_threadpool; 5 | 6 | pub use batch::*; 7 | pub use resolver::*; 8 | -------------------------------------------------------------------------------- /src/resolve/resolver.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Borrow; 2 | use std::collections::HashSet; 3 | use std::net::SocketAddr; 4 | use std::str; 5 | 6 | use futures::future; 7 | use futures::future::Loop; 8 | use futures::Future; 9 | use tokio_core::reactor::Handle; 10 | 11 | use trust_dns::client::{BasicClientHandle, ClientFuture, ClientHandle}; 12 | use trust_dns::error::ClientError; 13 | use trust_dns::error::ClientErrorKind; 14 | use trust_dns::op::message::Message; 15 | use trust_dns::rr::dns_class::DNSClass; 16 | use trust_dns::rr::domain::Name; 17 | use trust_dns::rr::record_type::RecordType; 18 | use trust_dns::rr::resource::Record; 19 | use trust_dns::udp::UdpClientStream; 20 | 21 | use config::CONFIG; 22 | use resolve::batch::{QueryType, ResolveStatus, StatusTx}; 23 | use resolve::error::*; 24 | 25 | fn make_client(loop_handle: Handle, name_server: SocketAddr) -> BasicClientHandle { 26 | let (stream, stream_handle) = UdpClientStream::new(name_server, loop_handle.clone()); 27 | 28 | ClientFuture::new(stream, stream_handle, loop_handle, None) 29 | } 30 | 31 | #[derive(Clone)] 32 | struct ClientFactory { 33 | loop_handle: Handle, 34 | name_server: SocketAddr, 35 | } 36 | 37 | impl ClientFactory { 38 | pub fn new(loop_handle: Handle, name_server: SocketAddr) -> ClientFactory { 39 | ClientFactory { 40 | loop_handle: loop_handle, 41 | name_server: name_server, 42 | } 43 | } 44 | 45 | fn new_client(&self) -> BasicClientHandle { 46 | make_client(self.loop_handle.clone(), self.name_server) 47 | } 48 | 49 | fn dns(&self) -> SocketAddr { 50 | self.name_server 51 | } 52 | } 53 | 54 | pub struct TrustDNSResolver { 55 | loop_handle: Handle, 56 | status_tx: StatusTx, 57 | timeout_retries: u32, 58 | } 59 | 60 | impl TrustDNSResolver { 61 | pub fn new(loop_handle: Handle, status_tx: StatusTx) -> Self { 62 | TrustDNSResolver { 63 | loop_handle: loop_handle.clone(), 64 | status_tx: status_tx, 65 | timeout_retries: CONFIG.read().unwrap().timeout_retries(), 66 | } 67 | } 68 | } 69 | 70 | impl TrustDNSResolver { 71 | pub fn resolve( 72 | &self, 73 | dns: SocketAddr, 74 | name: &str, 75 | query_type: QueryType, 76 | ) -> Box, Error = ResolverError>> { 77 | let client_factory = ClientFactory::new(self.loop_handle.clone(), dns); 78 | 79 | self.status_tx.send(ResolveStatus::Started).unwrap(); 80 | let status_tx = self.status_tx.clone(); 81 | 82 | let future = match query_type { 83 | QueryType::PTR => self.reverse_resolve(client_factory, name), 84 | _ => self.simple_resolve(client_factory, name, query_type.into()), 85 | }; 86 | 87 | let name = name.to_owned(); 88 | let future = future 89 | .map(move |msg| msg.extract_answer(query_type)) 90 | .then(move |rv| rv.report_status(&name, status_tx)) 91 | .then(move |rv| rv.partial_ok()); 92 | 93 | Box::new(future) 94 | } 95 | 96 | // Simple DNS lookup queries 97 | fn simple_resolve( 98 | &self, 99 | client_factory: ClientFactory, 100 | name: &str, 101 | rtype: RecordType, 102 | ) -> Box> { 103 | Box::new(Self::resolve_retry( 104 | client_factory, 105 | self.timeout_retries, 106 | Name::parse(name, Some(&Name::root())).unwrap(), 107 | DNSClass::IN, 108 | rtype, 109 | )) 110 | } 111 | 112 | // Reverse DNS queries 113 | fn reverse_resolve( 114 | &self, 115 | client_factory: ClientFactory, 116 | ip: &str, 117 | ) -> Box> { 118 | let mut labels = ip.split('.').map(str::to_owned).collect::>(); 119 | labels.reverse(); 120 | 121 | let name = Name::with_labels(labels).label("in-addr").label("arpa"); 122 | 123 | Box::new(self.recurse_ptr(client_factory, name, DNSClass::IN, RecordType::PTR)) 124 | } 125 | 126 | // Recursive DNS request for PTR queries 127 | fn recurse_ptr( 128 | &self, 129 | client_factory: ClientFactory, 130 | name: Name, 131 | query_class: DNSClass, 132 | record_type: RecordType, 133 | ) -> Box> { 134 | // Because recursion is not possible with futures this implementation of Depth-First lookup 135 | // uses state with discovered nameservers excluding visited ones to avoid infinite loops 136 | struct State { 137 | handle: Handle, 138 | client_factory: ClientFactory, 139 | nameservers: Vec, 140 | visited: HashSet, 141 | answer: Option, 142 | } 143 | 144 | impl State { 145 | pub fn pop_ns(&mut self) -> Option { 146 | self.nameservers.pop().map(|ns| { 147 | self.visited.insert(ns.clone()); 148 | ns 149 | }) 150 | } 151 | 152 | pub fn push_nameservers(&mut self, iter: I) 153 | where 154 | I: IntoIterator, 155 | B: Borrow, 156 | { 157 | let new_nameservers = iter 158 | .into_iter() 159 | .map(NS::try_from) 160 | .filter_map(Result::ok) 161 | .filter(|ns| !self.visited.contains(ns)) 162 | .collect::>(); 163 | self.nameservers.extend(new_nameservers) 164 | } 165 | 166 | pub fn add_answer(&mut self, answer: Message) { 167 | self.answer = Some(answer) 168 | } 169 | } 170 | 171 | let timeout_retries = self.timeout_retries; 172 | let state = State { 173 | handle: self.loop_handle.clone(), 174 | client_factory: client_factory.clone(), 175 | nameservers: vec![NS::Known(client_factory.dns())], 176 | visited: HashSet::new(), 177 | answer: None, 178 | }; 179 | 180 | let resolve_loop = future::loop_fn(state, move |mut state| { 181 | Self::resolve_with_ns( 182 | state.handle.clone(), 183 | state.client_factory.clone(), 184 | timeout_retries, 185 | state.pop_ns().unwrap(), 186 | name.clone(), 187 | query_class, 188 | record_type, 189 | ) 190 | .map(move |message| { 191 | state.push_nameservers(message.name_servers().iter().map(Record::name)); 192 | state.add_answer(message); 193 | state 194 | }) 195 | .and_then(|state| { 196 | if !state.answer.is_none() || state.nameservers.is_empty() { 197 | Ok(Loop::Break(state)) 198 | } else { 199 | Ok(Loop::Continue(state)) 200 | } 201 | }) 202 | }); 203 | 204 | Box::new(resolve_loop.and_then(|state| { 205 | if let Some(answer) = state.answer { 206 | Ok(answer) 207 | } else { 208 | Err(ResolverError::NotFound) 209 | } 210 | })) 211 | } 212 | 213 | // Perform DNS query with some nameserver. 214 | // If nameserver is not a SocketAddr, resolve the domain first. 215 | fn resolve_with_ns( 216 | loop_handle: Handle, 217 | client_factory: ClientFactory, 218 | timeout_retries: u32, 219 | nameserver: NS, 220 | name: Name, 221 | query_class: DNSClass, 222 | record_type: RecordType, 223 | ) -> Box> { 224 | debug!( 225 | "Resolving {:?} with nameserver {:?}", 226 | name.to_string(), 227 | nameserver.to_string() 228 | ); 229 | let ns_resolve: Box, Error = ResolverError>> = 230 | match nameserver { 231 | NS::Known(addr) => future::ok(Some(addr)).boxed(), 232 | NS::Unknown(domain) => Box::new( 233 | Self::resolve_retry( 234 | client_factory.clone(), 235 | timeout_retries, 236 | Name::parse(&domain, Some(&Name::root())).unwrap(), 237 | DNSClass::IN, 238 | RecordType::A, 239 | ) 240 | .map(|msg| { 241 | msg.extract_answer(QueryType::A) 242 | .into_iter() 243 | .nth(0) 244 | .map(|mut ip| { 245 | ip.push_str(":53"); 246 | ip 247 | }) 248 | .and_then(|ip| { 249 | ip.parse::() 250 | .map_err(|e| { 251 | error!("Invalid IP({:?}): {:?}", ip, e); 252 | e 253 | }) 254 | .ok() 255 | }) 256 | }), 257 | ), 258 | }; 259 | 260 | let future = ns_resolve.then(move |result| match result { 261 | Ok(Some(nameserver)) => Self::resolve_retry( 262 | ClientFactory::new(loop_handle, nameserver), 263 | timeout_retries, 264 | name.clone(), 265 | query_class, 266 | record_type, 267 | ), 268 | Ok(None) => future::err(ResolverError::NameServerNotResolved).boxed(), 269 | Err(err) => future::err(err).boxed(), 270 | }); 271 | 272 | Box::new(future) 273 | } 274 | 275 | // Retry-on-timeout enabled resolve 276 | fn resolve_retry( 277 | client_factory: ClientFactory, 278 | timeout_retries: u32, 279 | name: Name, 280 | query_class: DNSClass, 281 | record_type: RecordType, 282 | ) -> Box> { 283 | struct State { 284 | tries_left: u32, 285 | message: Option, 286 | }; 287 | 288 | impl State { 289 | fn new(tries: u32) -> Self { 290 | State { 291 | tries_left: tries, 292 | message: None, 293 | } 294 | } 295 | 296 | fn next_step(mut self) -> Result, ResolverError> { 297 | self.tries_left -= 1; 298 | if self.tries_left > 0 { 299 | Ok(Loop::Continue(self)) 300 | } else { 301 | Ok(Loop::Break(self)) 302 | } 303 | } 304 | 305 | fn has_next_step(&self) -> bool { 306 | self.tries_left > 0 307 | } 308 | 309 | fn with_message(mut self, message: Message) -> State { 310 | self.message = Some(message); 311 | self 312 | } 313 | 314 | fn into_message(self) -> Option { 315 | self.message 316 | } 317 | } 318 | 319 | let state = State::new(timeout_retries); 320 | 321 | let retry_loop = { 322 | future::loop_fn(state, move |state| { 323 | Self::_resolve( 324 | client_factory.new_client(), 325 | name.clone(), 326 | query_class, 327 | record_type, 328 | ) 329 | .then(move |result| match result { 330 | Ok(message) => { 331 | trace!("Received DNS message: {:?}", message.answers()); 332 | Ok(Loop::Break(state.with_message(message))) 333 | } 334 | Err(err) => match *err.kind() { 335 | ClientErrorKind::Timeout => state.next_step(), 336 | ClientErrorKind::Canceled(e) => { 337 | if !state.has_next_step() { 338 | error!("{}", e) 339 | } 340 | state.next_step() 341 | } 342 | _ => Err(ResolverError::DnsClientError(err)), 343 | }, 344 | }) 345 | }) 346 | }; 347 | 348 | let future = retry_loop.then(move |result| match result { 349 | Ok(state) => { 350 | let message = state.into_message(); 351 | match message { 352 | Some(message) => Ok(message), 353 | None => Err(ResolverError::ConnectionTimeout), 354 | } 355 | } 356 | Err(err) => Err(err), 357 | }); 358 | 359 | Box::new(future) 360 | } 361 | 362 | fn _resolve( 363 | mut client: BasicClientHandle, 364 | name: Name, 365 | query_class: DNSClass, 366 | record_type: RecordType, 367 | ) -> Box> { 368 | Box::new(client.query(name, query_class, record_type)) 369 | } 370 | } 371 | 372 | use trust_dns::rr::RData; 373 | 374 | trait FromRecord 375 | where 376 | B: Borrow, 377 | Self: Sized, 378 | { 379 | fn from(r: B, qtype: QueryType) -> Option; 380 | } 381 | 382 | impl FromRecord for String 383 | where 384 | B: Borrow, 385 | { 386 | fn from(r: B, qtype: QueryType) -> Option { 387 | let r = r.borrow(); 388 | 389 | macro_rules! variants_to_string { 390 | ($($x:tt),*) => { 391 | match (qtype, r.rdata()) { 392 | $( 393 | (QueryType::$x, &RData::$x(ref data)) => Some(data.to_string()), 394 | )* 395 | _ => None 396 | } 397 | } 398 | } 399 | 400 | variants_to_string!(A, AAAA, NS, PTR) 401 | } 402 | } 403 | 404 | trait ExtractAnswer { 405 | fn extract_answer(&self, qtype: QueryType) -> Vec; 406 | } 407 | 408 | impl ExtractAnswer for Message { 409 | fn extract_answer(&self, qtype: QueryType) -> Vec { 410 | self.answers() 411 | .into_iter() 412 | .map(|record| >::from(record, qtype)) 413 | .filter(Option::is_some) 414 | .map(Option::unwrap) 415 | .collect() 416 | } 417 | } 418 | 419 | trait ReportStatus { 420 | fn report_status(self, name: &str, status_tx: StatusTx) -> Self; 421 | } 422 | 423 | impl ReportStatus for Result, ResolverError> { 424 | fn report_status(self, name: &str, status_tx: StatusTx) -> Self { 425 | match self.as_ref() { 426 | Ok(vec) => { 427 | if vec.is_empty() { 428 | status_tx.send(ResolveStatus::Failure).unwrap(); 429 | } else { 430 | status_tx.send(ResolveStatus::Success).unwrap(); 431 | } 432 | } 433 | Err(error) => match *error { 434 | ResolverError::ConnectionTimeout | ResolverError::NameServerNotResolved => { 435 | debug!("failed to resolve {:?}: {}", name, error); 436 | status_tx.send(ResolveStatus::Failure).unwrap(); 437 | } 438 | _ => { 439 | error!("failed to resolve {:?}: {}", name, error); 440 | status_tx.send(ResolveStatus::Error).unwrap(); 441 | } 442 | }, 443 | } 444 | self 445 | } 446 | } 447 | 448 | trait PartialOk { 449 | fn partial_ok(self) -> Result, ResolverError>; 450 | } 451 | 452 | impl PartialOk for Result, ResolverError> { 453 | fn partial_ok(self) -> Result, ResolverError> { 454 | match self { 455 | Err(ResolverError::ConnectionTimeout) 456 | | Err(ResolverError::NameServerNotResolved) 457 | | Err(ResolverError::NotFound) => Ok(vec![]), 458 | Ok(vec) => Ok(vec), 459 | Err(err) => Err(err), 460 | } 461 | } 462 | } 463 | 464 | #[derive(Debug, Hash, Eq, PartialEq, Clone)] 465 | enum NS { 466 | Known(SocketAddr), 467 | Unknown(String), 468 | } 469 | 470 | impl NS { 471 | pub fn to_string(&self) -> String { 472 | match *self { 473 | NS::Known(ref addr) => addr.to_string(), 474 | NS::Unknown(ref dom) => dom.clone(), 475 | } 476 | } 477 | 478 | pub fn try_from(name: B) -> Result 479 | where 480 | B: Borrow, 481 | { 482 | let name = name.borrow(); 483 | if name.num_labels() != 0 { 484 | Ok(NS::Unknown(name.to_string())) 485 | } else { 486 | Err(()) 487 | } 488 | } 489 | } 490 | 491 | impl From for NS { 492 | fn from(addr: SocketAddr) -> NS { 493 | NS::Known(addr) 494 | } 495 | } 496 | -------------------------------------------------------------------------------- /src/resolve/resolver_threadpool.rs: -------------------------------------------------------------------------------- 1 | use std::cell::Cell; 2 | use std::net::SocketAddr; 3 | use std::thread; 4 | use std::time::{Duration, Instant}; 5 | 6 | use futures::future; 7 | use futures::stream; 8 | use futures::sync::mpsc as future_mpsc; 9 | use futures::Future; 10 | use futures::Sink; 11 | use futures::Stream; 12 | use tokio_core::reactor::Core; 13 | 14 | use crossbeam; 15 | use num_cpus; 16 | 17 | use config::CONFIG; 18 | use resolve::batch::QueryType; 19 | use resolve::batch::ResolvedTx; 20 | use resolve::batch::StatusTx; 21 | use resolve::error::ResolverError; 22 | use resolve::resolver::TrustDNSResolver; 23 | 24 | pub struct ResolverThreadPool { 25 | tasks: Vec, 26 | workers_cnt: usize, 27 | } 28 | 29 | impl ResolverThreadPool { 30 | pub fn new(num_cpus: usize) -> Self { 31 | ResolverThreadPool { 32 | tasks: vec![], 33 | workers_cnt: num_cpus, 34 | } 35 | } 36 | 37 | pub fn num_cpus() -> Self { 38 | Self::new(num_cpus::get()) 39 | } 40 | 41 | pub fn spawn(&mut self, task: ResolveTask) { 42 | self.tasks.push(task) 43 | } 44 | 45 | pub fn start(self, status: StatusTx) { 46 | let tasks_cnt = self.tasks.len(); 47 | let chunk_size = tasks_cnt / self.workers_cnt + 1; 48 | let qps = CONFIG.read().unwrap().qps() as usize; 49 | let worker_qps = (qps as f32 / self.workers_cnt as f32).ceil() as usize; 50 | 51 | crossbeam::scope(|scope| { 52 | scope.defer(|| debug!("Exiting crosspbeam scope")); 53 | let mut trigger = TriggerTimer::new(tasks_cnt, worker_qps); 54 | 55 | for chunk in self.tasks.chunks(chunk_size).map(|chunk| chunk.to_vec()) { 56 | let trigger_handle = trigger.get_handle(); 57 | let status = status.clone(); 58 | 59 | scope.spawn(move || { 60 | let thread = thread::current(); 61 | let tname = thread.name().unwrap_or("Unknown"); 62 | 63 | debug!("Started worker thread ({})", tname); 64 | ResolverThread::thread_main(chunk, status, trigger_handle, worker_qps); 65 | debug!("Terminated worker thread: ({})", tname); 66 | }); 67 | } 68 | 69 | let dns = CONFIG.read().unwrap().dns_list().to_vec(); 70 | scope.spawn(move || { 71 | debug!("Started qps trigger thread"); 72 | trigger.thread_main(dns); 73 | debug!("Terminated qps trigger thread"); 74 | }); 75 | }) 76 | } 77 | } 78 | 79 | type TriggerTx = future_mpsc::Sender; 80 | type TriggerRx = future_mpsc::Receiver; 81 | 82 | struct TriggerTimer { 83 | handles: Vec, 84 | worker_qps: usize, 85 | triggered: Cell, 86 | tasks_cnt: usize, 87 | } 88 | 89 | impl TriggerTimer { 90 | pub fn new(tasks_cnt: usize, worker_qps: usize) -> Self { 91 | TriggerTimer { 92 | handles: vec![], 93 | worker_qps: worker_qps, 94 | triggered: Cell::new(0), 95 | tasks_cnt: tasks_cnt, 96 | } 97 | } 98 | 99 | pub fn get_handle(&mut self) -> TriggerRx { 100 | let (tx, rx) = future_mpsc::channel(self.worker_qps); 101 | self.handles.push(tx); 102 | rx 103 | } 104 | 105 | pub fn thread_main(mut self, dns_list: Vec) { 106 | let duration_second = Duration::from_secs(1); 107 | 108 | while self.triggered.get() < self.tasks_cnt { 109 | let start = Instant::now(); 110 | self.trigger_qps(&dns_list); 111 | let end = Instant::now(); 112 | 113 | let diff = end - start; 114 | if diff < duration_second { 115 | thread::sleep(duration_second - diff); 116 | } 117 | } 118 | } 119 | 120 | fn trigger_qps(&mut self, dns_list: &[SocketAddr]) { 121 | debug!("Triggering {} requests per thread", self.worker_qps); 122 | 123 | // Futures sending request triggers 124 | let mut send_list = vec![]; 125 | 126 | for i in 0..self.worker_qps { 127 | // Round-Robin dns rotation 128 | let dns = dns_list[i as usize % dns_list.len()]; 129 | 130 | for handle in self.handles.clone() { 131 | let future = handle.send(dns).and_then(|_| { 132 | let old_triggered = self.triggered.get(); 133 | self.triggered.set(old_triggered + 1); 134 | Ok(()) 135 | }); 136 | send_list.push(future); 137 | } 138 | } 139 | 140 | future::join_all(send_list).wait().unwrap(); 141 | } 142 | } 143 | 144 | struct ResolverThread; 145 | impl ResolverThread { 146 | fn thread_main(tasks: Vec, status: StatusTx, task_trigger: TriggerRx, qps: usize) { 147 | let mut core = Core::new().unwrap(); 148 | let handle = core.handle(); 149 | 150 | let future = { 151 | let resolver = TrustDNSResolver::new(handle.clone(), status); 152 | 153 | // Zipping with stream of triggering messages binds each resolve task launch time 154 | // to the triggering timer. 155 | stream::iter::<_, _, _>(tasks.into_iter().map(Ok)) 156 | .zip(task_trigger) 157 | .map(move |(task, dns)| task.resolve(&resolver, dns).map_err(|_| ())) 158 | .buffer_unordered(qps) 159 | .collect() 160 | }; 161 | 162 | core.run(future).unwrap(); 163 | } 164 | } 165 | 166 | #[derive(Clone)] 167 | pub struct ResolveTask { 168 | pub tx: ResolvedTx, 169 | pub name: String, 170 | pub qtype: QueryType, 171 | } 172 | 173 | impl ResolveTask { 174 | pub fn resolve( 175 | &self, 176 | resolver: &TrustDNSResolver, 177 | dns: SocketAddr, 178 | ) -> Box> { 179 | let tx = self.tx.clone(); 180 | let name = self.name.clone(); 181 | 182 | let future = resolver 183 | .resolve(dns, &self.name, self.qtype) 184 | .and_then(move |results| { 185 | for result in results { 186 | tx.send((name.clone(), result)).unwrap() 187 | } 188 | Ok(()) 189 | }); 190 | 191 | Box::new(future) 192 | } 193 | } 194 | --------------------------------------------------------------------------------