summaryrefslogtreecommitdiffstats
path: root/third_party/rust/dns-parser
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/dns-parser
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/dns-parser')
-rw-r--r--third_party/rust/dns-parser/.cargo-checksum.json1
-rw-r--r--third_party/rust/dns-parser/Cargo.toml41
-rw-r--r--third_party/rust/dns-parser/LICENSE-APACHE202
-rw-r--r--third_party/rust/dns-parser/LICENSE-MIT19
-rw-r--r--third_party/rust/dns-parser/README.md31
-rw-r--r--third_party/rust/dns-parser/bulk.yaml8
-rw-r--r--third_party/rust/dns-parser/examples/sync_tcp_client.rs77
-rw-r--r--third_party/rust/dns-parser/examples/sync_udp_client.rs53
-rw-r--r--third_party/rust/dns-parser/src/builder.rs132
-rw-r--r--third_party/rust/dns-parser/src/enums.rs299
-rw-r--r--third_party/rust/dns-parser/src/error.rs71
-rw-r--r--third_party/rust/dns-parser/src/header.rs203
-rw-r--r--third_party/rust/dns-parser/src/lib.rs39
-rw-r--r--third_party/rust/dns-parser/src/name.rs183
-rw-r--r--third_party/rust/dns-parser/src/parser.rs455
-rw-r--r--third_party/rust/dns-parser/src/rdata/a.rs21
-rw-r--r--third_party/rust/dns-parser/src/rdata/aaaa.rs85
-rw-r--r--third_party/rust/dns-parser/src/rdata/all.rs11
-rw-r--r--third_party/rust/dns-parser/src/rdata/axfr.rs11
-rw-r--r--third_party/rust/dns-parser/src/rdata/cname.rs102
-rw-r--r--third_party/rust/dns-parser/src/rdata/hinfo.rs11
-rw-r--r--third_party/rust/dns-parser/src/rdata/maila.rs11
-rw-r--r--third_party/rust/dns-parser/src/rdata/mailb.rs11
-rw-r--r--third_party/rust/dns-parser/src/rdata/mb.rs11
-rw-r--r--third_party/rust/dns-parser/src/rdata/mf.rs11
-rw-r--r--third_party/rust/dns-parser/src/rdata/mg.rs11
-rw-r--r--third_party/rust/dns-parser/src/rdata/minfo.rs11
-rw-r--r--third_party/rust/dns-parser/src/rdata/mod.rs83
-rw-r--r--third_party/rust/dns-parser/src/rdata/mr.rs11
-rw-r--r--third_party/rust/dns-parser/src/rdata/mx.rs92
-rw-r--r--third_party/rust/dns-parser/src/rdata/ns.rs88
-rw-r--r--third_party/rust/dns-parser/src/rdata/nsec.rs11
-rw-r--r--third_party/rust/dns-parser/src/rdata/null.rs11
-rw-r--r--third_party/rust/dns-parser/src/rdata/opt.rs18
-rw-r--r--third_party/rust/dns-parser/src/rdata/ptr.rs74
-rw-r--r--third_party/rust/dns-parser/src/rdata/soa.rs101
-rw-r--r--third_party/rust/dns-parser/src/rdata/srv.rs102
-rw-r--r--third_party/rust/dns-parser/src/rdata/txt.rs125
-rw-r--r--third_party/rust/dns-parser/src/rdata/wks.rs11
-rw-r--r--third_party/rust/dns-parser/src/structs.rs50
-rw-r--r--third_party/rust/dns-parser/vagga.yaml44
41 files changed, 2942 insertions, 0 deletions
diff --git a/third_party/rust/dns-parser/.cargo-checksum.json b/third_party/rust/dns-parser/.cargo-checksum.json
new file mode 100644
index 0000000000..490d0f1eab
--- /dev/null
+++ b/third_party/rust/dns-parser/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.toml":"7b47cb4d67f549cce93b578b0911177365055bbbc15790644251cac1856a11f7","LICENSE-APACHE":"c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08","LICENSE-MIT":"71fb06f353ef01dbb828a61a74eadbfc57ab4e1b20eaae1db68229f1647c4183","README.md":"85171334a4b9e672811377819e7f178abdd1870f09d1a1250fe25a7ffaf4b5bf","bulk.yaml":"17c2548388e0cd3a63473021a2f1e4ddedee082d79d9167cb31ad06a1890d3fc","examples/sync_tcp_client.rs":"8ba349565ae9872fc3e246469eab0f7041355d38100355d07a2c1ff2cd048476","examples/sync_udp_client.rs":"ee32137e43d6ab4da6c6f1b36d956042fb74d2704ec319acd058ca74fdc489c1","src/builder.rs":"1dc20c218e5e3b8d0d0f7cdc7ab585882d9960493881be5f17ad2ea5ce8c9dd5","src/enums.rs":"0e0afe0987805ac7361bb6fcaee8a235f375312fa372a6fc8d9b36a85710a9c9","src/error.rs":"6abc85f52724ef8aa12e1ff40a747a02b018b99ae259880a9ff222959f567200","src/header.rs":"f96466c2c0c5647fab0718e9e8289aa9933e988015e454c38ecb7d3c0b6b6813","src/lib.rs":"98994eb0dfea6864d9cf26e1c6ff32f31249e9eaec4583387524dc919e4dde49","src/name.rs":"3d80f16e473e32780e60d814ea3af54094e8c813af576743b108deaf960da27e","src/parser.rs":"02af73135af936115a0ac99da7d102d5ef3c58890abe98d0cfefcd3814b98463","src/rdata/a.rs":"a66498dc4f02af5c95b53f09e2860fed69881410eb02a30bceee0b976e8f8c41","src/rdata/aaaa.rs":"0163de1dc6a691f2ede528cb865f1197511800742879bda189e65aadd682e6c4","src/rdata/all.rs":"005b8edc4aae6f140332f75420c900bae91be66dfb2fdcdd177d51280bbc3750","src/rdata/axfr.rs":"19e18099a16baf33e34ea23905ec5913834aa367a034343551fe935a367f20db","src/rdata/cname.rs":"f31f3c490b6fcd9d5300d4c3903c398cfdd7ff89154685fc3fb182b3fa553ecf","src/rdata/hinfo.rs":"3edd6e2685a048b7d35872582490cdffa90ca7aa688ecffe1d91c46b9ccede1c","src/rdata/maila.rs":"f3f5a40cebd458ed83da7d910601cab761883eee14e991c677d325e6f2fcd968","src/rdata/mailb.rs":"6e09a86dab9a516b4022eefb6a06dcb7d9b06e15d3a80e8027db285e43e5f870","src/rdata/mb.rs":"b867aa5e0f1ec0970d8d6d6406773a8b7af5291dbcf1d8d78be9d017d8cc5a81","src/rdata/mf.rs":"2494cf316f1c2bf803237ce01e7bbbdf05a736fcac2536a06a34ebaf51c815ae","src/rdata/mg.rs":"14a1c9813bc7a33811efa4855c0ac29288cbc1ec535f7c67609333a2a8a5612b","src/rdata/minfo.rs":"72bf7b87ca494865327866ddf9733fb9b137cf2cd4222338237f107b4ff5062d","src/rdata/mod.rs":"0a6f377059eee38371893d17c31d97fb214f209c89c43f5f9302e3560150a220","src/rdata/mr.rs":"0140e5a4c1b6af5e55a61027c21e1760cd2b5a8f1929604d3884225b9433054a","src/rdata/mx.rs":"a38113e7b6efbc65831582fbfacbb6ed1937e167f1b8a19a36a27b0b600ec085","src/rdata/ns.rs":"2acf2d552f3e4f94af50b1c46a6cefc99515a930b099e2d93e9a595f4e1746b5","src/rdata/nsec.rs":"0a712e38c35c60815a2b1394e4bb4f9e229311e80bc9e59b0e854354f928d5a5","src/rdata/null.rs":"0ee8104671d5238da7885b90248ffc952f1558eaa43dfe15b60292166a992a38","src/rdata/opt.rs":"24eb3346b88b3aec18985bd2c1db6498160a59ed7fabfc8b45ce174f10638653","src/rdata/ptr.rs":"9ab2459fc87f90edb2bc7d0fbb97ab008cedf3ce62c042d1b25181914a5ee5c6","src/rdata/soa.rs":"0bf7321ff2891af8f9a3d6b58199464a77b1556e705fa1e9b52f23358bb4bf47","src/rdata/srv.rs":"7d755cb64f42d3096a763739e31959ebec439855380869cd801196bb507102aa","src/rdata/txt.rs":"9e00d2b64cfc53fa8ac510a8c624f5d42320cd90f8a50d040cfc666b530fd8ad","src/rdata/wks.rs":"abfb5572d2c270838489a6ff611c76cb0d3dc7470f44a6810fb89894febd8840","src/structs.rs":"4692edebde24c3be4a7aae81f75b8dc69a9c1ed460bf80740a75bc13f5e40b82","vagga.yaml":"3662f1f49317908fd54ab7830d53074b4bdd753ae85bc3a6fcf6fabaa6b42c70"},"package":"c4d33be9473d06f75f58220f71f7a9317aca647dc061dbd3c361b0bef505fbea"} \ No newline at end of file
diff --git a/third_party/rust/dns-parser/Cargo.toml b/third_party/rust/dns-parser/Cargo.toml
new file mode 100644
index 0000000000..085abfb82a
--- /dev/null
+++ b/third_party/rust/dns-parser/Cargo.toml
@@ -0,0 +1,41 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g. crates.io) dependencies
+#
+# If you believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+name = "dns-parser"
+version = "0.8.0"
+authors = ["Paul Colomiets <paul@colomiets.name>"]
+description = " Pure-rust DNS protocol parser library. This does not support network, only\n raw protocol parser.\n"
+homepage = "https://github.com/tailhook/dns-parser"
+documentation = "https://docs.rs/dns-parser"
+readme = "README.md"
+keywords = ["dns", "domain", "name", "parser"]
+categories = ["parser-implementations"]
+license = "MIT/Apache-2.0"
+[dependencies.byteorder]
+version = "1"
+
+[dependencies.quick-error]
+version = "1.0.0"
+
+[dependencies.serde]
+version = "1.0"
+optional = true
+
+[dependencies.serde_derive]
+version = "1.0"
+optional = true
+[dev-dependencies.matches]
+version = "0.1.2"
+
+[features]
+with-serde = ["serde", "serde_derive"]
diff --git a/third_party/rust/dns-parser/LICENSE-APACHE b/third_party/rust/dns-parser/LICENSE-APACHE
new file mode 100644
index 0000000000..8f71f43fee
--- /dev/null
+++ b/third_party/rust/dns-parser/LICENSE-APACHE
@@ -0,0 +1,202 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
diff --git a/third_party/rust/dns-parser/LICENSE-MIT b/third_party/rust/dns-parser/LICENSE-MIT
new file mode 100644
index 0000000000..11c705f7a3
--- /dev/null
+++ b/third_party/rust/dns-parser/LICENSE-MIT
@@ -0,0 +1,19 @@
+Copyright (c) 2016 The dns-parser Developers
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/third_party/rust/dns-parser/README.md b/third_party/rust/dns-parser/README.md
new file mode 100644
index 0000000000..3d3133ebcd
--- /dev/null
+++ b/third_party/rust/dns-parser/README.md
@@ -0,0 +1,31 @@
+DNS Parser
+==========
+
+**Status: beta**
+
+[Documentation](https://docs.rs/dns-parser) |
+[Github](https://github.com/tailhook/dns-parser) |
+[Crate](https://crates.io/crates/dns-parser)
+
+This is a parser of DNS protocol packets that is independent of any networking
+code.
+
+
+License
+=======
+
+Licensed under either of
+
+* Apache License, Version 2.0,
+ (./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
+* MIT license (./LICENSE-MIT or http://opensource.org/licenses/MIT)
+ at your option.
+
+Contribution
+------------
+
+Unless you explicitly state otherwise, any contribution intentionally
+submitted for inclusion in the work by you, as defined in the Apache-2.0
+license, shall be dual licensed as above, without any additional terms or
+conditions.
+
diff --git a/third_party/rust/dns-parser/bulk.yaml b/third_party/rust/dns-parser/bulk.yaml
new file mode 100644
index 0000000000..cdb9763b67
--- /dev/null
+++ b/third_party/rust/dns-parser/bulk.yaml
@@ -0,0 +1,8 @@
+minimum-bulk: v0.4.5
+
+versions:
+
+- file: Cargo.toml
+ block-start: ^\[package\]
+ block-end: ^\[.*\]
+ regex: ^version\s*=\s*"(\S+)"
diff --git a/third_party/rust/dns-parser/examples/sync_tcp_client.rs b/third_party/rust/dns-parser/examples/sync_tcp_client.rs
new file mode 100644
index 0000000000..732e8d3bfb
--- /dev/null
+++ b/third_party/rust/dns-parser/examples/sync_tcp_client.rs
@@ -0,0 +1,77 @@
+extern crate dns_parser;
+
+use std::env;
+use std::error::Error;
+use std::io::{Read, Write};
+use std::net::TcpStream;
+use std::process;
+
+
+use dns_parser::{Builder, Packet, RData, ResponseCode};
+use dns_parser::rdata::a::Record;
+use dns_parser::{QueryType, QueryClass};
+
+
+fn main() {
+ let mut code = 0;
+ for name in env::args().skip(1) {
+ match resolve(&name) {
+ Ok(()) => {},
+ Err(e) => {
+ eprintln!("Error resolving {:?}: {}", name, e);
+ code = 1;
+ }
+ }
+ }
+ process::exit(code);
+}
+
+fn resolve(name: &str) -> Result<(), Box<Error>> {
+ let mut conn = TcpStream::connect("127.0.0.1:53")?;
+ let mut builder = Builder::new_query(1, true);
+ builder.add_question(name, false, QueryType::A, QueryClass::IN);
+ let packet = builder.build().map_err(|_| "truncated packet")?;
+ let psize = [((packet.len() >> 8) & 0xFF) as u8,
+ (packet.len() & 0xFF) as u8];
+ conn.write_all(&psize[..])?;
+ conn.write_all(&packet)?;
+ let mut buf = vec![0u8; 4096];
+ let mut off = 0;
+ let pkt = loop {
+ if buf.len() - off < 4096 {
+ buf.extend(&[0u8; 4096][..]);
+ }
+ match conn.read(&mut buf[off..]) {
+ Ok(num) => {
+ off += num;
+ if off < 2 { continue; }
+ let bytes = ((buf[0] as usize) << 8) | buf[1] as usize;
+ if off < bytes + 2 {
+ continue;
+ }
+ if num == 0 {
+ return Err("Partial packet received".into());
+ }
+ break Packet::parse(&buf[2..off])?;
+ }
+ Err(e) => {
+ return Err(Box::new(e));
+ }
+ }
+ };
+ if pkt.header.response_code != ResponseCode::NoError {
+ return Err(pkt.header.response_code.into());
+ }
+ if pkt.answers.len() == 0 {
+ return Err("No records received".into());
+ }
+ for ans in pkt.answers {
+ match ans.data {
+ RData::A(Record(ip)) => {
+ println!("{}", ip);
+ }
+ _ => {} // ignore
+ }
+ }
+ Ok(())
+}
diff --git a/third_party/rust/dns-parser/examples/sync_udp_client.rs b/third_party/rust/dns-parser/examples/sync_udp_client.rs
new file mode 100644
index 0000000000..f8bf4265d6
--- /dev/null
+++ b/third_party/rust/dns-parser/examples/sync_udp_client.rs
@@ -0,0 +1,53 @@
+extern crate dns_parser;
+
+use std::env;
+use std::error::Error;
+use std::net::UdpSocket;
+use std::process;
+
+
+use dns_parser::{Builder, Packet, RData, ResponseCode};
+use dns_parser::rdata::a::Record;
+use dns_parser::{QueryType, QueryClass};
+
+
+fn main() {
+ let mut code = 0;
+ for name in env::args().skip(1) {
+ match resolve(&name) {
+ Ok(()) => {},
+ Err(e) => {
+ eprintln!("Error resolving {:?}: {}", name, e);
+ code = 1;
+ }
+ }
+ }
+ process::exit(code);
+}
+
+fn resolve(name: &str) -> Result<(), Box<Error>> {
+ let sock = UdpSocket::bind("127.0.0.1:0")?;
+ sock.connect("127.0.0.1:53")?;
+ let mut builder = Builder::new_query(1, true);
+ builder.add_question(name, false, QueryType::A, QueryClass::IN);
+ let packet = builder.build().map_err(|_| "truncated packet")?;
+ sock.send(&packet)?;
+ let mut buf = vec![0u8; 4096];
+ sock.recv(&mut buf)?;
+ let pkt = Packet::parse(&buf)?;
+ if pkt.header.response_code != ResponseCode::NoError {
+ return Err(pkt.header.response_code.into());
+ }
+ if pkt.answers.len() == 0 {
+ return Err("No records received".into());
+ }
+ for ans in pkt.answers {
+ match ans.data {
+ RData::A(Record(ip)) => {
+ println!("{}", ip);
+ }
+ _ => {} // ignore
+ }
+ }
+ Ok(())
+}
diff --git a/third_party/rust/dns-parser/src/builder.rs b/third_party/rust/dns-parser/src/builder.rs
new file mode 100644
index 0000000000..22aff0b962
--- /dev/null
+++ b/third_party/rust/dns-parser/src/builder.rs
@@ -0,0 +1,132 @@
+use byteorder::{ByteOrder, BigEndian, WriteBytesExt};
+
+use {Opcode, ResponseCode, Header, QueryType, QueryClass};
+
+/// Allows to build a DNS packet
+///
+/// Both query and answer packets may be built with this interface, although,
+/// much of functionality is not implemented yet.
+#[derive(Debug)]
+pub struct Builder {
+ buf: Vec<u8>,
+}
+
+impl Builder {
+ /// Creates a new query
+ ///
+ /// Initially all sections are empty. You're expected to fill
+ /// the questions section with `add_question`
+ pub fn new_query(id: u16, recursion: bool) -> Builder {
+ let mut buf = Vec::with_capacity(512);
+ let head = Header {
+ id: id,
+ query: true,
+ opcode: Opcode::StandardQuery,
+ authoritative: false,
+ truncated: false,
+ recursion_desired: recursion,
+ recursion_available: false,
+ authenticated_data: false,
+ checking_disabled: false,
+ response_code: ResponseCode::NoError,
+ questions: 0,
+ answers: 0,
+ nameservers: 0,
+ additional: 0,
+ };
+ buf.extend([0u8; 12].iter());
+ head.write(&mut buf[..12]);
+ Builder { buf: buf }
+ }
+ /// Adds a question to the packet
+ ///
+ /// # Panics
+ ///
+ /// * Answers, nameservers or additional section has already been written
+ /// * There are already 65535 questions in the buffer.
+ /// * When name is invalid
+ pub fn add_question(&mut self, qname: &str, prefer_unicast: bool,
+ qtype: QueryType, qclass: QueryClass)
+ -> &mut Builder
+ {
+ if &self.buf[6..12] != b"\x00\x00\x00\x00\x00\x00" {
+ panic!("Too late to add a question");
+ }
+ self.write_name(qname);
+ self.buf.write_u16::<BigEndian>(qtype as u16).unwrap();
+ let prefer_unicast: u16 = if prefer_unicast { 0x8000 } else { 0x0000 };
+ self.buf.write_u16::<BigEndian>(qclass as u16 | prefer_unicast).unwrap();
+ let oldq = BigEndian::read_u16(&self.buf[4..6]);
+ if oldq == 65535 {
+ panic!("Too many questions");
+ }
+ BigEndian::write_u16(&mut self.buf[4..6], oldq+1);
+ self
+ }
+ fn write_name(&mut self, name: &str) {
+ for part in name.split('.') {
+ assert!(part.len() < 63);
+ let ln = part.len() as u8;
+ self.buf.push(ln);
+ self.buf.extend(part.as_bytes());
+ }
+ self.buf.push(0);
+ }
+ /// Returns the final packet
+ ///
+ /// When packet is not truncated method returns `Ok(packet)`. If
+ /// packet is truncated the method returns `Err(packet)`. In both
+ /// cases the packet is fully valid.
+ ///
+ /// In the server implementation you may use
+ /// `x.build().unwrap_or_else(|x| x)`.
+ ///
+ /// In the client implementation it's probably unwise to send truncated
+ /// packet, as it doesn't make sense. Even panicking may be more
+ /// appropriate.
+ // TODO(tailhook) does the truncation make sense for TCP, and how
+ // to treat it for EDNS0?
+ pub fn build(mut self) -> Result<Vec<u8>,Vec<u8>> {
+ // TODO(tailhook) optimize labels
+ if self.buf.len() > 512 {
+ Header::set_truncated(&mut self.buf[..12]);
+ Err(self.buf)
+ } else {
+ Ok(self.buf)
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use QueryType as QT;
+ use QueryClass as QC;
+ use super::Builder;
+
+ #[test]
+ fn build_query() {
+ let mut bld = Builder::new_query(1573, true);
+ bld.add_question("example.com", false, QT::A, QC::IN);
+ let result = b"\x06%\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\
+ \x07example\x03com\x00\x00\x01\x00\x01";
+ assert_eq!(&bld.build().unwrap()[..], &result[..]);
+ }
+
+ #[test]
+ fn build_unicast_query() {
+ let mut bld = Builder::new_query(1573, true);
+ bld.add_question("example.com", true, QT::A, QC::IN);
+ let result = b"\x06%\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\
+ \x07example\x03com\x00\x00\x01\x80\x01";
+ assert_eq!(&bld.build().unwrap()[..], &result[..]);
+ }
+
+ #[test]
+ fn build_srv_query() {
+ let mut bld = Builder::new_query(23513, true);
+ bld.add_question("_xmpp-server._tcp.gmail.com", false, QT::SRV, QC::IN);
+ let result = b"[\xd9\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\
+ \x0c_xmpp-server\x04_tcp\x05gmail\x03com\x00\x00!\x00\x01";
+ assert_eq!(&bld.build().unwrap()[..], &result[..]);
+ }
+}
diff --git a/third_party/rust/dns-parser/src/enums.rs b/third_party/rust/dns-parser/src/enums.rs
new file mode 100644
index 0000000000..9f58ddfec1
--- /dev/null
+++ b/third_party/rust/dns-parser/src/enums.rs
@@ -0,0 +1,299 @@
+use {Error};
+use rdata::Record;
+use rdata::*;
+
+/// The TYPE value according to RFC 1035
+///
+/// All "EXPERIMENTAL" markers here are from the RFC
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum Type {
+ /// a host addresss
+ A = a::Record::TYPE,
+ /// an authoritative name server
+ NS = ns::Record::TYPE,
+ /// a mail forwarder (Obsolete - use MX)
+ MF = mf::Record::TYPE,
+ /// the canonical name for an alias
+ CNAME = cname::Record::TYPE,
+ /// marks the start of a zone of authority
+ SOA = soa::Record::TYPE,
+ /// a mailbox domain name (EXPERIMENTAL)
+ MB = mb::Record::TYPE,
+ /// a mail group member (EXPERIMENTAL)
+ MG = mg::Record::TYPE,
+ /// a mail rename domain name (EXPERIMENTAL)
+ MR = mr::Record::TYPE,
+ /// a null RR (EXPERIMENTAL)
+ NULL = null::Record::TYPE,
+ /// a well known service description
+ WKS = wks::Record::TYPE,
+ /// a domain name pointer
+ PTR = ptr::Record::TYPE,
+ /// host information
+ HINFO = hinfo::Record::TYPE,
+ /// mailbox or mail list information
+ MINFO = minfo::Record::TYPE,
+ /// mail exchange
+ MX = mx::Record::TYPE,
+ /// text strings
+ TXT = txt::Record::TYPE,
+ /// IPv6 host address (RFC 2782)
+ AAAA = aaaa::Record::TYPE,
+ /// service record (RFC 2782)
+ SRV = srv::Record::TYPE,
+ /// EDNS0 options (RFC 6891)
+ OPT = opt::Record::TYPE,
+ /// next secure record (RFC 4034, RFC 6762)
+ NSEC = nsec::Record::TYPE,
+}
+
+/// The QTYPE value according to RFC 1035
+///
+/// All "EXPERIMENTAL" markers here are from the RFC
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
+pub enum QueryType {
+ /// a host addresss
+ A = a::Record::TYPE,
+ /// an authoritative name server
+ NS = ns::Record::TYPE,
+ /// a mail forwarder (Obsolete - use MX)
+ MF = mf::Record::TYPE,
+ /// the canonical name for an alias
+ CNAME = cname::Record::TYPE,
+ /// marks the start of a zone of authority
+ SOA = soa::Record::TYPE,
+ /// a mailbox domain name (EXPERIMENTAL)
+ MB = mb::Record::TYPE,
+ /// a mail group member (EXPERIMENTAL)
+ MG = mg::Record::TYPE,
+ /// a mail rename domain name (EXPERIMENTAL)
+ MR = mr::Record::TYPE,
+ /// a null RR (EXPERIMENTAL)
+ NULL = null::Record::TYPE,
+ /// a well known service description
+ WKS = wks::Record::TYPE,
+ /// a domain name pointer
+ PTR = ptr::Record::TYPE,
+ /// host information
+ HINFO = hinfo::Record::TYPE,
+ /// mailbox or mail list information
+ MINFO = minfo::Record::TYPE,
+ /// mail exchange
+ MX = mx::Record::TYPE,
+ /// text strings
+ TXT = txt::Record::TYPE,
+ /// IPv6 host address (RFC 2782)
+ AAAA = aaaa::Record::TYPE,
+ /// service record (RFC 2782)
+ SRV = srv::Record::TYPE,
+ /// A request for a transfer of an entire zone
+ AXFR = axfr::Record::TYPE,
+ /// A request for mailbox-related records (MB, MG or MR)
+ MAILB = mailb::Record::TYPE,
+ /// A request for mail agent RRs (Obsolete - see MX)
+ MAILA = maila::Record::TYPE,
+ /// A request for all records
+ All = all::Record::TYPE,
+}
+
+
+/// The CLASS value according to RFC 1035
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum Class {
+ /// the Internet
+ IN = 1,
+ /// the CSNET class (Obsolete - used only for examples in some obsolete
+ /// RFCs)
+ CS = 2,
+ /// the CHAOS class
+ CH = 3,
+ /// Hesiod [Dyer 87]
+ HS = 4,
+}
+
+/// The QCLASS value according to RFC 1035
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum QueryClass {
+ /// the Internet
+ IN = 1,
+ /// the CSNET class (Obsolete - used only for examples in some obsolete
+ /// RFCs)
+ CS = 2,
+ /// the CHAOS class
+ CH = 3,
+ /// Hesiod [Dyer 87]
+ HS = 4,
+ /// Any class
+ Any = 255,
+}
+
+/// The OPCODE value according to RFC 1035
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum Opcode {
+ /// Normal query
+ StandardQuery,
+ /// Inverse query (query a name by IP)
+ InverseQuery,
+ /// Server status request
+ ServerStatusRequest,
+ /// Reserved opcode for future use
+ Reserved(u16),
+}
+
+quick_error! {
+ /// The RCODE value according to RFC 1035
+ #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+ #[allow(missing_docs)] // names are from spec
+ pub enum ResponseCode {
+ NoError
+ FormatError
+ ServerFailure
+ NameError
+ NotImplemented
+ Refused
+ Reserved(code: u8)
+ }
+}
+
+impl From<u16> for Opcode {
+ fn from(code: u16) -> Opcode {
+ use self::Opcode::*;
+ match code {
+ 0 => StandardQuery,
+ 1 => InverseQuery,
+ 2 => ServerStatusRequest,
+ x => Reserved(x),
+ }
+ }
+}
+impl Into<u16> for Opcode {
+ fn into(self) -> u16 {
+ use self::Opcode::*;
+ match self {
+ StandardQuery => 0,
+ InverseQuery => 1,
+ ServerStatusRequest => 2,
+ Reserved(x) => x,
+ }
+ }
+}
+
+impl From<u8> for ResponseCode {
+ fn from(code: u8) -> ResponseCode {
+ use self::ResponseCode::*;
+ match code {
+ 0 => NoError,
+ 1 => FormatError,
+ 2 => ServerFailure,
+ 3 => NameError,
+ 4 => NotImplemented,
+ 5 => Refused,
+ 6...15 => Reserved(code),
+ x => panic!("Invalid response code {}", x),
+ }
+ }
+}
+impl Into<u8> for ResponseCode {
+ fn into(self) -> u8 {
+ use self::ResponseCode::*;
+ match self {
+ NoError => 0,
+ FormatError => 1,
+ ServerFailure => 2,
+ NameError => 3,
+ NotImplemented => 4,
+ Refused => 5,
+ Reserved(code) => code,
+ }
+ }
+}
+
+impl QueryType {
+ /// Parse a query type code
+ pub fn parse(code: u16) -> Result<QueryType, Error> {
+ use self::QueryType::*;
+ match code as isize {
+ a::Record::TYPE => Ok(A),
+ ns::Record::TYPE => Ok(NS),
+ mf::Record::TYPE => Ok(MF),
+ cname::Record::TYPE => Ok(CNAME),
+ soa::Record::TYPE => Ok(SOA),
+ mb::Record::TYPE => Ok(MB),
+ mg::Record::TYPE => Ok(MG),
+ mr::Record::TYPE => Ok(MR),
+ null::Record::TYPE => Ok(NULL),
+ wks::Record::TYPE => Ok(WKS),
+ ptr::Record::TYPE => Ok(PTR),
+ hinfo::Record::TYPE => Ok(HINFO),
+ minfo::Record::TYPE => Ok(MINFO),
+ mx::Record::TYPE => Ok(MX),
+ txt::Record::TYPE => Ok(TXT),
+ aaaa::Record::TYPE => Ok(AAAA),
+ srv::Record::TYPE => Ok(SRV),
+ axfr::Record::TYPE => Ok(AXFR),
+ mailb::Record::TYPE => Ok(MAILB),
+ maila::Record::TYPE => Ok(MAILA),
+ all::Record::TYPE => Ok(All),
+ x => Err(Error::InvalidQueryType(x as u16)),
+ }
+ }
+}
+
+impl QueryClass {
+ /// Parse a query class code
+ pub fn parse(code: u16) -> Result<QueryClass, Error> {
+ use self::QueryClass::*;
+ match code {
+ 1 => Ok(IN),
+ 2 => Ok(CS),
+ 3 => Ok(CH),
+ 4 => Ok(HS),
+ 255 => Ok(Any),
+ x => Err(Error::InvalidQueryClass(x)),
+ }
+ }
+}
+
+impl Type {
+ /// Parse a type code
+ pub fn parse(code: u16) -> Result<Type, Error> {
+ use self::Type::*;
+ match code as isize {
+ a::Record::TYPE => Ok(A),
+ ns::Record::TYPE => Ok(NS),
+ mf::Record::TYPE => Ok(MF),
+ cname::Record::TYPE => Ok(CNAME),
+ soa::Record::TYPE => Ok(SOA),
+ mb::Record::TYPE => Ok(MB),
+ mg::Record::TYPE => Ok(MG),
+ mr::Record::TYPE => Ok(MR),
+ null::Record::TYPE => Ok(NULL),
+ wks::Record::TYPE => Ok(WKS),
+ ptr::Record::TYPE => Ok(PTR),
+ hinfo::Record::TYPE => Ok(HINFO),
+ minfo::Record::TYPE => Ok(MINFO),
+ mx::Record::TYPE => Ok(MX),
+ txt::Record::TYPE => Ok(TXT),
+ aaaa::Record::TYPE => Ok(AAAA),
+ srv::Record::TYPE => Ok(SRV),
+ opt::Record::TYPE => Ok(OPT),
+ nsec::Record::TYPE => Ok(NSEC),
+ x => Err(Error::InvalidType(x as u16)),
+ }
+ }
+}
+
+impl Class {
+ /// Parse a class code
+ pub fn parse(code: u16) -> Result<Class, Error> {
+ use self::Class::*;
+ match code {
+ 1 => Ok(IN),
+ 2 => Ok(CS),
+ 3 => Ok(CH),
+ 4 => Ok(HS),
+ x => Err(Error::InvalidClass(x)),
+ }
+ }
+}
diff --git a/third_party/rust/dns-parser/src/error.rs b/third_party/rust/dns-parser/src/error.rs
new file mode 100644
index 0000000000..ae6a39747d
--- /dev/null
+++ b/third_party/rust/dns-parser/src/error.rs
@@ -0,0 +1,71 @@
+use std::str::Utf8Error;
+
+quick_error! {
+ /// Error parsing DNS packet
+ #[derive(Debug)]
+ pub enum Error {
+ /// Invalid compression pointer not pointing backwards
+ /// when parsing label
+ BadPointer {
+ description("invalid compression pointer not pointing backwards \
+ when parsing label")
+ }
+ /// Packet is smaller than header size
+ HeaderTooShort {
+ description("packet is smaller than header size")
+ }
+ /// Packet ihas incomplete data
+ UnexpectedEOF {
+ description("packet is has incomplete data")
+ }
+ /// Wrong (too short or too long) size of RDATA
+ WrongRdataLength {
+ description("wrong (too short or too long) size of RDATA")
+ }
+ /// Packet has non-zero reserved bits
+ ReservedBitsAreNonZero {
+ description("packet has non-zero reserved bits")
+ }
+ /// Label in domain name has unknown label format
+ UnknownLabelFormat {
+ description("label in domain name has unknown label format")
+ }
+ /// Query type code is invalid
+ InvalidQueryType(code: u16) {
+ description("query type code is invalid")
+ display("query type {} is invalid", code)
+ }
+ /// Query class code is invalid
+ InvalidQueryClass(code: u16) {
+ description("query class code is invalid")
+ display("query class {} is invalid", code)
+ }
+ /// Type code is invalid
+ InvalidType(code: u16) {
+ description("type code is invalid")
+ display("type {} is invalid", code)
+ }
+ /// Class code is invalid
+ InvalidClass(code: u16) {
+ description("class code is invalid")
+ display("class {} is invalid", code)
+ }
+ /// Invalid characters encountered while reading label
+ LabelIsNotAscii {
+ description("invalid characters encountered while reading label")
+ }
+ /// Invalid characters encountered while reading TXT
+ TxtDataIsNotUTF8(error: Utf8Error) {
+ description("invalid characters encountered while reading TXT")
+ display("{:?}", error)
+ }
+ /// Parser is in the wrong state
+ WrongState {
+ description("parser is in the wrong state")
+ }
+ /// Additional OPT record found
+ AdditionalOPT {
+ description("additional OPT record found")
+ }
+ }
+}
diff --git a/third_party/rust/dns-parser/src/header.rs b/third_party/rust/dns-parser/src/header.rs
new file mode 100644
index 0000000000..01d18add99
--- /dev/null
+++ b/third_party/rust/dns-parser/src/header.rs
@@ -0,0 +1,203 @@
+use byteorder::{BigEndian, ByteOrder};
+
+use {Error, ResponseCode, Opcode};
+
+mod flag {
+ pub const QUERY: u16 = 0b1000_0000_0000_0000;
+ pub const OPCODE_MASK: u16 = 0b0111_1000_0000_0000;
+ pub const AUTHORITATIVE: u16 = 0b0000_0100_0000_0000;
+ pub const TRUNCATED: u16 = 0b0000_0010_0000_0000;
+ pub const RECURSION_DESIRED: u16 = 0b0000_0001_0000_0000;
+ pub const RECURSION_AVAILABLE: u16 = 0b0000_0000_1000_0000;
+ pub const AUTHENTICATED_DATA: u16 = 0b0000_0000_0010_0000;
+ pub const CHECKING_DISABLED: u16 = 0b0000_0000_0001_0000;
+ pub const RESERVED_MASK: u16 = 0b0000_0000_0100_0000;
+ pub const RESPONSE_CODE_MASK: u16 = 0b0000_0000_0000_1111;
+}
+
+/// Represents parsed header of the packet
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[allow(missing_docs)] // fields are from the spec I think
+pub struct Header {
+ pub id: u16,
+ pub query: bool,
+ pub opcode: Opcode,
+ pub authoritative: bool,
+ pub truncated: bool,
+ pub recursion_desired: bool,
+ pub recursion_available: bool,
+ pub authenticated_data: bool,
+ pub checking_disabled: bool,
+ pub response_code: ResponseCode,
+ pub questions: u16,
+ pub answers: u16,
+ pub nameservers: u16,
+ pub additional: u16,
+}
+
+impl Header {
+ /// Parse the header into a header structure
+ pub fn parse(data: &[u8]) -> Result<Header, Error> {
+ if data.len() < 12 {
+ return Err(Error::HeaderTooShort);
+ }
+ let flags = BigEndian::read_u16(&data[2..4]);
+ if flags & flag::RESERVED_MASK != 0 {
+ return Err(Error::ReservedBitsAreNonZero);
+ }
+ let header = Header {
+ id: BigEndian::read_u16(&data[..2]),
+ query: flags & flag::QUERY == 0,
+ opcode: ((flags & flag::OPCODE_MASK)
+ >> flag::OPCODE_MASK.trailing_zeros()).into(),
+ authoritative: flags & flag::AUTHORITATIVE != 0,
+ truncated: flags & flag::TRUNCATED != 0,
+ recursion_desired: flags & flag::RECURSION_DESIRED != 0,
+ recursion_available: flags & flag::RECURSION_AVAILABLE != 0,
+ authenticated_data: flags & flag::AUTHENTICATED_DATA != 0,
+ checking_disabled: flags & flag::CHECKING_DISABLED != 0,
+ response_code: From::from((flags&flag::RESPONSE_CODE_MASK) as u8),
+ questions: BigEndian::read_u16(&data[4..6]),
+ answers: BigEndian::read_u16(&data[6..8]),
+ nameservers: BigEndian::read_u16(&data[8..10]),
+ additional: BigEndian::read_u16(&data[10..12]),
+ };
+ Ok(header)
+ }
+ /// Write a header to a buffer slice
+ ///
+ /// # Panics
+ ///
+ /// When buffer size is not exactly 12 bytes
+ pub fn write(&self, data: &mut [u8]) {
+ if data.len() != 12 {
+ panic!("Header size is exactly 12 bytes");
+ }
+ let mut flags = 0u16;
+ flags |= Into::<u16>::into(self.opcode)
+ << flag::OPCODE_MASK.trailing_zeros();
+ flags |= Into::<u8>::into(self.response_code) as u16;
+ if !self.query { flags |= flag::QUERY; }
+ if self.authoritative { flags |= flag::AUTHORITATIVE; }
+ if self.recursion_desired { flags |= flag::RECURSION_DESIRED; }
+ if self.recursion_available { flags |= flag::RECURSION_AVAILABLE; }
+ if self.truncated { flags |= flag::TRUNCATED; }
+ BigEndian::write_u16(&mut data[..2], self.id);
+ BigEndian::write_u16(&mut data[2..4], flags);
+ BigEndian::write_u16(&mut data[4..6], self.questions);
+ BigEndian::write_u16(&mut data[6..8], self.answers);
+ BigEndian::write_u16(&mut data[8..10], self.nameservers);
+ BigEndian::write_u16(&mut data[10..12], self.additional);
+ }
+ /// Set "truncated flag" in the raw data
+ // shouldn't this method be non-public?
+ pub fn set_truncated(data: &mut [u8]) {
+ let oldflags = BigEndian::read_u16(&data[2..4]);
+ BigEndian::write_u16(&mut data[2..4], oldflags & flag::TRUNCATED);
+ }
+ /// Returns a size of the header (always 12 bytes)
+ pub fn size() -> usize { 12 }
+}
+
+
+#[cfg(test)]
+mod test {
+
+ use {Header};
+ use Opcode::*;
+ use ResponseCode::NoError;
+
+ #[test]
+ fn parse_example_query() {
+ let query = b"\x06%\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\
+ \x07example\x03com\x00\x00\x01\x00\x01";
+ let header = Header::parse(query).unwrap();
+ assert_eq!(header, Header {
+ id: 1573,
+ query: true,
+ opcode: StandardQuery,
+ authoritative: false,
+ truncated: false,
+ recursion_desired: true,
+ recursion_available: false,
+ authenticated_data: false,
+ checking_disabled: false,
+ response_code: NoError,
+ questions: 1,
+ answers: 0,
+ nameservers: 0,
+ additional: 0,
+ });
+ }
+
+ #[test]
+ fn parse_example_response() {
+ let response = b"\x06%\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\
+ \x07example\x03com\x00\x00\x01\x00\x01\
+ \xc0\x0c\x00\x01\x00\x01\x00\x00\x04\xf8\
+ \x00\x04]\xb8\xd8\"";
+ let header = Header::parse(response).unwrap();
+ assert_eq!(header, Header {
+ id: 1573,
+ query: false,
+ opcode: StandardQuery,
+ authoritative: false,
+ truncated: false,
+ recursion_desired: true,
+ recursion_available: true,
+ authenticated_data: false,
+ checking_disabled: false,
+ response_code: NoError,
+ questions: 1,
+ answers: 1,
+ nameservers: 0,
+ additional: 0,
+ });
+ }
+
+ #[test]
+ fn parse_query_with_ad_set() {
+ let query = b"\x06%\x01\x20\x00\x01\x00\x00\x00\x00\x00\x00\
+ \x07example\x03com\x00\x00\x01\x00\x01";
+ let header = Header::parse(query).unwrap();
+ assert_eq!(header, Header {
+ id: 1573,
+ query: true,
+ opcode: StandardQuery,
+ authoritative: false,
+ truncated: false,
+ recursion_desired: true,
+ recursion_available: false,
+ authenticated_data: true,
+ checking_disabled: false,
+ response_code: NoError,
+ questions: 1,
+ answers: 0,
+ nameservers: 0,
+ additional: 0,
+ });
+ }
+
+ #[test]
+ fn parse_query_with_cd_set() {
+ let query = b"\x06%\x01\x10\x00\x01\x00\x00\x00\x00\x00\x00\
+ \x07example\x03com\x00\x00\x01\x00\x01";
+ let header = Header::parse(query).unwrap();
+ assert_eq!(header, Header {
+ id: 1573,
+ query: true,
+ opcode: StandardQuery,
+ authoritative: false,
+ truncated: false,
+ recursion_desired: true,
+ recursion_available: false,
+ authenticated_data: false,
+ checking_disabled: true,
+ response_code: NoError,
+ questions: 1,
+ answers: 0,
+ nameservers: 0,
+ additional: 0,
+ });
+ }
+}
diff --git a/third_party/rust/dns-parser/src/lib.rs b/third_party/rust/dns-parser/src/lib.rs
new file mode 100644
index 0000000000..e89c005244
--- /dev/null
+++ b/third_party/rust/dns-parser/src/lib.rs
@@ -0,0 +1,39 @@
+#![recursion_limit="100"]
+//! The network-agnostic DNS parser library
+//!
+//! [Documentation](https://docs.rs/dns-parser) |
+//! [Github](https://github.com/tailhook/dns-parser) |
+//! [Crate](https://crates.io/crates/dns-parser)
+//!
+//! Use [`Builder`] to create a new outgoing packet.
+//!
+//! Use [`Packet::parse`] to parse a packet into a data structure.
+//!
+//! [`Builder`]: struct.Builder.html
+//! [`Packet::parse`]: struct.Packet.html#method.parse
+//!
+#![warn(missing_docs)]
+#![warn(missing_debug_implementations)]
+
+extern crate byteorder;
+#[cfg(test)] #[macro_use] extern crate matches;
+#[macro_use(quick_error)] extern crate quick_error;
+#[cfg(feature = "with-serde")] #[macro_use] extern crate serde_derive;
+
+mod enums;
+mod structs;
+mod name;
+mod parser;
+mod error;
+mod header;
+mod builder;
+
+pub mod rdata;
+
+pub use enums::{Type, QueryType, Class, QueryClass, ResponseCode, Opcode};
+pub use structs::{Question, ResourceRecord, Packet};
+pub use name::{Name};
+pub use error::{Error};
+pub use header::{Header};
+pub use rdata::{RData};
+pub use builder::{Builder};
diff --git a/third_party/rust/dns-parser/src/name.rs b/third_party/rust/dns-parser/src/name.rs
new file mode 100644
index 0000000000..8763259cbe
--- /dev/null
+++ b/third_party/rust/dns-parser/src/name.rs
@@ -0,0 +1,183 @@
+use std::fmt;
+use std::fmt::Write;
+use std::str::from_utf8;
+
+// Deprecated since rustc 1.23
+#[allow(unused_imports, deprecated)]
+use std::ascii::AsciiExt;
+
+use byteorder::{BigEndian, ByteOrder};
+
+use {Error};
+
+/// The DNS name as stored in the original packet
+///
+/// This contains just a reference to a slice that contains the data.
+/// You may turn this into a string using `.to_string()`
+#[derive(Clone, Copy)]
+pub struct Name<'a>{
+ labels: &'a [u8],
+ /// This is the original buffer size. The compressed names in original
+ /// are calculated in this buffer
+ original: &'a [u8],
+}
+
+impl<'a> Name<'a> {
+ /// Scan the data to get Name object
+ ///
+ /// The `data` should be a part of `original` where name should start.
+ /// The `original` is the data starting a the start of a packet, so
+ /// that offsets in compressed name starts from the `original`.
+ pub fn scan(data: &'a[u8], original: &'a[u8]) -> Result<Name<'a>, Error> {
+ let mut parse_data = data;
+ let mut return_pos = None;
+ let mut pos = 0;
+ if parse_data.len() <= pos {
+ return Err(Error::UnexpectedEOF);
+ }
+ // By setting the largest_pos to be the original len, a side effect
+ // is that the pos variable can move forwards in the buffer once.
+ let mut largest_pos = original.len();
+ let mut byte = parse_data[pos];
+ while byte != 0 {
+ if parse_data.len() <= pos {
+ return Err(Error::UnexpectedEOF);
+ }
+ if byte & 0b1100_0000 == 0b1100_0000 {
+ if parse_data.len() < pos+2 {
+ return Err(Error::UnexpectedEOF);
+ }
+ let off = (BigEndian::read_u16(&parse_data[pos..pos+2])
+ & !0b1100_0000_0000_0000) as usize;
+ if off >= original.len() {
+ return Err(Error::UnexpectedEOF);
+ }
+ // Set value for return_pos which is the pos in the original
+ // data buffer that should be used to return after validating
+ // the offsetted labels.
+ if let None = return_pos {
+ return_pos = Some(pos);
+ }
+
+ // Check then set largest_pos to ensure we never go backwards
+ // in the buffer.
+ if off >= largest_pos {
+ return Err(Error::BadPointer);
+ }
+ largest_pos = off;
+ pos = 0;
+ parse_data = &original[off..];
+ } else if byte & 0b1100_0000 == 0 {
+ let end = pos + byte as usize + 1;
+ if parse_data.len() < end {
+ return Err(Error::UnexpectedEOF);
+ }
+ if !parse_data[pos+1..end].is_ascii() {
+ return Err(Error::LabelIsNotAscii);
+ }
+ pos = end;
+ if parse_data.len() <= pos {
+ return Err(Error::UnexpectedEOF);
+ }
+ } else {
+ return Err(Error::UnknownLabelFormat);
+ }
+ byte = parse_data[pos];
+ }
+ if let Some(return_pos) = return_pos {
+ return Ok(Name {labels: &data[..return_pos+2], original: original});
+ } else {
+ return Ok(Name {labels: &data[..pos+1], original: original });
+ }
+ }
+ /// Number of bytes serialized name occupies
+ pub fn byte_len(&self) -> usize {
+ self.labels.len()
+ }
+}
+
+impl<'a> fmt::Display for Name<'a> {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let data = self.labels;
+ let original = self.original;
+ let mut pos = 0;
+ loop {
+ let byte = data[pos];
+ if byte == 0 {
+ return Ok(());
+ } else if byte & 0b1100_0000 == 0b1100_0000 {
+ let off = (BigEndian::read_u16(&data[pos..pos+2])
+ & !0b1100_0000_0000_0000) as usize;
+ if pos != 0 {
+ try!(fmt.write_char('.'));
+ }
+ return fmt::Display::fmt(
+ &Name::scan(&original[off..], original).unwrap(), fmt)
+ } else if byte & 0b1100_0000 == 0 {
+ if pos != 0 {
+ try!(fmt.write_char('.'));
+ }
+ let end = pos + byte as usize + 1;
+ try!(fmt.write_str(from_utf8(&data[pos+1..end]).unwrap()));
+ pos = end;
+ continue;
+ } else {
+ unreachable!();
+ }
+ }
+ }
+}
+impl<'a> fmt::Debug for Name<'a> {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ fmt.debug_tuple("Name")
+ .field(&format!("{}", self))
+ .finish()
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use Error;
+ use Name;
+
+ #[test]
+ fn parse_badpointer_same_offset() {
+ // A buffer where an offset points to itself,
+ // which is a bad compression pointer.
+ let same_offset = vec![192, 2, 192, 2];
+ let is_match = matches!(Name::scan(&same_offset, &same_offset),
+ Err(Error::BadPointer));
+
+ assert!(is_match);
+ }
+
+ #[test]
+ fn parse_badpointer_forward_offset() {
+ // A buffer where the offsets points back to each other which causes
+ // infinite recursion if never checked, a bad compression pointer.
+ let forwards_offset = vec![192, 2, 192, 4, 192, 2];
+ let is_match = matches!(Name::scan(&forwards_offset, &forwards_offset),
+ Err(Error::BadPointer));
+
+ assert!(is_match);
+ }
+
+ #[test]
+ fn nested_names() {
+ // A buffer where an offset points to itself, a bad compression pointer.
+ let buf = b"\x02xx\x00\x02yy\xc0\x00\x02zz\xc0\x04";
+
+ assert_eq!(Name::scan(&buf[..], buf).unwrap().to_string(),
+ "xx");
+ assert_eq!(Name::scan(&buf[..], buf).unwrap().labels,
+ b"\x02xx\x00");
+ assert_eq!(Name::scan(&buf[4..], buf).unwrap().to_string(),
+ "yy.xx");
+ assert_eq!(Name::scan(&buf[4..], buf).unwrap().labels,
+ b"\x02yy\xc0\x00");
+ assert_eq!(Name::scan(&buf[9..], buf).unwrap().to_string(),
+ "zz.yy.xx");
+ assert_eq!(Name::scan(&buf[9..], buf).unwrap().labels,
+ b"\x02zz\xc0\x04");
+ }
+}
diff --git a/third_party/rust/dns-parser/src/parser.rs b/third_party/rust/dns-parser/src/parser.rs
new file mode 100644
index 0000000000..0718798349
--- /dev/null
+++ b/third_party/rust/dns-parser/src/parser.rs
@@ -0,0 +1,455 @@
+use std::i32;
+
+use byteorder::{BigEndian, ByteOrder};
+
+use {Header, Packet, Error, Question, Name, QueryType, QueryClass};
+use {Type, Class, ResourceRecord, RData};
+use rdata::opt::Record as Opt;
+
+const OPT_RR_START: [u8; 3] = [0, 0, 41];
+
+impl<'a> Packet<'a> {
+ /// Parse a full DNS Packet and return a structure that has all the
+ /// data borrowed from the passed buffer.
+ pub fn parse(data: &[u8]) -> Result<Packet, Error> {
+ let header = try!(Header::parse(data));
+ let mut offset = Header::size();
+ let mut questions = Vec::with_capacity(header.questions as usize);
+ for _ in 0..header.questions {
+ let name = try!(Name::scan(&data[offset..], data));
+ offset += name.byte_len();
+ if offset + 4 > data.len() {
+ return Err(Error::UnexpectedEOF);
+ }
+ let qtype = try!(QueryType::parse(
+ BigEndian::read_u16(&data[offset..offset+2])));
+ offset += 2;
+
+ let (prefer_unicast, qclass) = try!(parse_qclass_code(
+ BigEndian::read_u16(&data[offset..offset+2])));
+ offset += 2;
+
+ questions.push(Question {
+ qname: name,
+ qtype: qtype,
+ prefer_unicast: prefer_unicast,
+ qclass: qclass,
+ });
+ }
+ let mut answers = Vec::with_capacity(header.answers as usize);
+ for _ in 0..header.answers {
+ answers.push(try!(parse_record(data, &mut offset)));
+ }
+ let mut nameservers = Vec::with_capacity(header.nameservers as usize);
+ for _ in 0..header.nameservers {
+ nameservers.push(try!(parse_record(data, &mut offset)));
+ }
+ let mut additional = Vec::with_capacity(header.additional as usize);
+ let mut opt = None;
+ for _ in 0..header.additional {
+ if offset + 3 <= data.len() && data[offset..offset+3] == OPT_RR_START {
+ if opt.is_none() {
+ opt = Some(try!(parse_opt_record(data, &mut offset)));
+ } else {
+ return Err(Error::AdditionalOPT);
+ }
+ } else {
+ additional.push(try!(parse_record(data, &mut offset)));
+ }
+ }
+ Ok(Packet {
+ header: header,
+ questions: questions,
+ answers: answers,
+ nameservers: nameservers,
+ additional: additional,
+ opt: opt,
+ })
+ }
+}
+
+fn parse_qclass_code(value: u16) -> Result<(bool, QueryClass), Error> {
+ let prefer_unicast = value & 0x8000 == 0x8000;
+ let qclass_code = value & 0x7FFF;
+
+ let qclass = try!(QueryClass::parse(qclass_code));
+ Ok((prefer_unicast, qclass))
+}
+
+fn parse_class_code(value: u16) -> Result<(bool, Class), Error> {
+ let is_unique = value & 0x8000 == 0x8000;
+ let class_code = value & 0x7FFF;
+
+ let cls = try!(Class::parse(class_code));
+ Ok((is_unique, cls))
+}
+
+// Generic function to parse answer, nameservers, and additional records.
+fn parse_record<'a>(data: &'a [u8], offset: &mut usize) -> Result<ResourceRecord<'a>, Error> {
+ let name = try!(Name::scan(&data[*offset..], data));
+ *offset += name.byte_len();
+ if *offset + 10 > data.len() {
+ return Err(Error::UnexpectedEOF);
+ }
+ let typ = try!(Type::parse(
+ BigEndian::read_u16(&data[*offset..*offset+2])));
+ *offset += 2;
+
+ let class_code = BigEndian::read_u16(&data[*offset..*offset+2]);
+ let (multicast_unique, cls) = try!(parse_class_code(class_code));
+ *offset += 2;
+
+ let mut ttl = BigEndian::read_u32(&data[*offset..*offset+4]);
+ if ttl > i32::MAX as u32 {
+ ttl = 0;
+ }
+ *offset += 4;
+ let rdlen = BigEndian::read_u16(&data[*offset..*offset+2]) as usize;
+ *offset += 2;
+ if *offset + rdlen > data.len() {
+ return Err(Error::UnexpectedEOF);
+ }
+ let data = try!(RData::parse(typ,
+ &data[*offset..*offset+rdlen], data));
+ *offset += rdlen;
+ Ok(ResourceRecord {
+ name: name,
+ multicast_unique: multicast_unique,
+ cls: cls,
+ ttl: ttl,
+ data: data,
+ })
+}
+
+// Function to parse an RFC 6891 OPT Pseudo RR
+fn parse_opt_record<'a>(data: &'a [u8], offset: &mut usize) -> Result<Opt<'a>, Error> {
+ if *offset + 11 > data.len() {
+ return Err(Error::UnexpectedEOF);
+ }
+ *offset += 1;
+ let typ = try!(Type::parse(
+ BigEndian::read_u16(&data[*offset..*offset+2])));
+ if typ != Type::OPT {
+ return Err(Error::InvalidType(typ as u16));
+ }
+ *offset += 2;
+ let udp = BigEndian::read_u16(&data[*offset..*offset+2]);
+ *offset += 2;
+ let extrcode = data[*offset];
+ *offset += 1;
+ let version = data[*offset];
+ *offset += 1;
+ let flags = BigEndian::read_u16(&data[*offset..*offset+2]);
+ *offset += 2;
+ let rdlen = BigEndian::read_u16(&data[*offset..*offset+2]) as usize;
+ *offset += 2;
+ if *offset + rdlen > data.len() {
+ return Err(Error::UnexpectedEOF);
+ }
+ let data = try!(RData::parse(typ,
+ &data[*offset..*offset+rdlen], data));
+ *offset += rdlen;
+
+ Ok(Opt {
+ udp: udp,
+ extrcode: extrcode,
+ version: version,
+ flags: flags,
+ data: data,
+ })
+}
+
+#[cfg(test)]
+mod test {
+
+ use std::net::Ipv4Addr;
+ use {Packet, Header};
+ use Opcode::*;
+ use ResponseCode::NoError;
+ use QueryType as QT;
+ use QueryClass as QC;
+ use Class as C;
+ use RData;
+
+ #[test]
+ fn parse_example_query() {
+ let query = b"\x06%\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\
+ \x07example\x03com\x00\x00\x01\x00\x01";
+ let packet = Packet::parse(query).unwrap();
+ assert_eq!(packet.header, Header {
+ id: 1573,
+ query: true,
+ opcode: StandardQuery,
+ authoritative: false,
+ truncated: false,
+ recursion_desired: true,
+ recursion_available: false,
+ authenticated_data: false,
+ checking_disabled: false,
+ response_code: NoError,
+ questions: 1,
+ answers: 0,
+ nameservers: 0,
+ additional: 0,
+ });
+ assert_eq!(packet.questions.len(), 1);
+ assert_eq!(packet.questions[0].qtype, QT::A);
+ assert_eq!(packet.questions[0].qclass, QC::IN);
+ assert_eq!(&packet.questions[0].qname.to_string()[..], "example.com");
+ assert_eq!(packet.answers.len(), 0);
+ }
+
+ #[test]
+ fn parse_example_response() {
+ let response = b"\x06%\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\
+ \x07example\x03com\x00\x00\x01\x00\x01\
+ \xc0\x0c\x00\x01\x00\x01\x00\x00\x04\xf8\
+ \x00\x04]\xb8\xd8\"";
+ let packet = Packet::parse(response).unwrap();
+ assert_eq!(packet.header, Header {
+ id: 1573,
+ query: false,
+ opcode: StandardQuery,
+ authoritative: false,
+ truncated: false,
+ recursion_desired: true,
+ recursion_available: true,
+ authenticated_data: false,
+ checking_disabled: false,
+ response_code: NoError,
+ questions: 1,
+ answers: 1,
+ nameservers: 0,
+ additional: 0,
+ });
+ assert_eq!(packet.questions.len(), 1);
+ assert_eq!(packet.questions[0].qtype, QT::A);
+ assert_eq!(packet.questions[0].qclass, QC::IN);
+ assert_eq!(&packet.questions[0].qname.to_string()[..], "example.com");
+ assert_eq!(packet.answers.len(), 1);
+ assert_eq!(&packet.answers[0].name.to_string()[..], "example.com");
+ assert_eq!(packet.answers[0].multicast_unique, false);
+ assert_eq!(packet.answers[0].cls, C::IN);
+ assert_eq!(packet.answers[0].ttl, 1272);
+ match packet.answers[0].data {
+ RData::A(addr) => {
+ assert_eq!(addr.0, Ipv4Addr::new(93, 184, 216, 34));
+ }
+ ref x => panic!("Wrong rdata {:?}", x),
+ }
+ }
+
+ #[test]
+ fn parse_response_with_multicast_unique() {
+ let response = b"\x06%\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\
+ \x07example\x03com\x00\x00\x01\x00\x01\
+ \xc0\x0c\x00\x01\x80\x01\x00\x00\x04\xf8\
+ \x00\x04]\xb8\xd8\"";
+ let packet = Packet::parse(response).unwrap();
+
+ assert_eq!(packet.answers.len(), 1);
+ assert_eq!(packet.answers[0].multicast_unique, true);
+ assert_eq!(packet.answers[0].cls, C::IN);
+ }
+
+ #[test]
+ fn parse_additional_record_response() {
+ let response = b"\x4a\xf0\x81\x80\x00\x01\x00\x01\x00\x01\x00\x01\
+ \x03www\x05skype\x03com\x00\x00\x01\x00\x01\
+ \xc0\x0c\x00\x05\x00\x01\x00\x00\x0e\x10\
+ \x00\x1c\x07\x6c\x69\x76\x65\x63\x6d\x73\x0e\x74\
+ \x72\x61\x66\x66\x69\x63\x6d\x61\x6e\x61\x67\x65\
+ \x72\x03\x6e\x65\x74\x00\
+ \xc0\x42\x00\x02\x00\x01\x00\x01\xd5\xd3\x00\x11\
+ \x01\x67\x0c\x67\x74\x6c\x64\x2d\x73\x65\x72\x76\x65\x72\x73\
+ \xc0\x42\
+ \x01\x61\xc0\x55\x00\x01\x00\x01\x00\x00\xa3\x1c\
+ \x00\x04\xc0\x05\x06\x1e";
+ let packet = Packet::parse(response).unwrap();
+ assert_eq!(packet.header, Header {
+ id: 19184,
+ query: false,
+ opcode: StandardQuery,
+ authoritative: false,
+ truncated: false,
+ recursion_desired: true,
+ recursion_available: true,
+ authenticated_data: false,
+ checking_disabled: false,
+ response_code: NoError,
+ questions: 1,
+ answers: 1,
+ nameservers: 1,
+ additional: 1,
+ });
+ assert_eq!(packet.questions.len(), 1);
+ assert_eq!(packet.questions[0].qtype, QT::A);
+ assert_eq!(packet.questions[0].qclass, QC::IN);
+ assert_eq!(&packet.questions[0].qname.to_string()[..], "www.skype.com");
+ assert_eq!(packet.answers.len(), 1);
+ assert_eq!(&packet.answers[0].name.to_string()[..], "www.skype.com");
+ assert_eq!(packet.answers[0].cls, C::IN);
+ assert_eq!(packet.answers[0].ttl, 3600);
+ match packet.answers[0].data {
+ RData::CNAME(cname) => {
+ assert_eq!(&cname.0.to_string()[..], "livecms.trafficmanager.net");
+ }
+ ref x => panic!("Wrong rdata {:?}", x),
+ }
+ assert_eq!(packet.nameservers.len(), 1);
+ assert_eq!(&packet.nameservers[0].name.to_string()[..], "net");
+ assert_eq!(packet.nameservers[0].cls, C::IN);
+ assert_eq!(packet.nameservers[0].ttl, 120275);
+ match packet.nameservers[0].data {
+ RData::NS(ns) => {
+ assert_eq!(&ns.0.to_string()[..], "g.gtld-servers.net");
+ }
+ ref x => panic!("Wrong rdata {:?}", x),
+ }
+ assert_eq!(packet.additional.len(), 1);
+ assert_eq!(&packet.additional[0].name.to_string()[..], "a.gtld-servers.net");
+ assert_eq!(packet.additional[0].cls, C::IN);
+ assert_eq!(packet.additional[0].ttl, 41756);
+ match packet.additional[0].data {
+ RData::A(addr) => {
+ assert_eq!(addr.0, Ipv4Addr::new(192, 5, 6, 30));
+ }
+ ref x => panic!("Wrong rdata {:?}", x),
+ }
+ }
+
+ #[test]
+ fn parse_multiple_answers() {
+ let response = b"\x9d\xe9\x81\x80\x00\x01\x00\x06\x00\x00\x00\x00\
+ \x06google\x03com\x00\x00\x01\x00\x01\xc0\x0c\
+ \x00\x01\x00\x01\x00\x00\x00\xef\x00\x04@\xe9\
+ \xa4d\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\xef\
+ \x00\x04@\xe9\xa4\x8b\xc0\x0c\x00\x01\x00\x01\
+ \x00\x00\x00\xef\x00\x04@\xe9\xa4q\xc0\x0c\x00\
+ \x01\x00\x01\x00\x00\x00\xef\x00\x04@\xe9\xa4f\
+ \xc0\x0c\x00\x01\x00\x01\x00\x00\x00\xef\x00\x04@\
+ \xe9\xa4e\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\xef\
+ \x00\x04@\xe9\xa4\x8a";
+ let packet = Packet::parse(response).unwrap();
+ assert_eq!(packet.header, Header {
+ id: 40425,
+ query: false,
+ opcode: StandardQuery,
+ authoritative: false,
+ truncated: false,
+ recursion_desired: true,
+ recursion_available: true,
+ authenticated_data: false,
+ checking_disabled: false,
+ response_code: NoError,
+ questions: 1,
+ answers: 6,
+ nameservers: 0,
+ additional: 0,
+ });
+ assert_eq!(packet.questions.len(), 1);
+ assert_eq!(packet.questions[0].qtype, QT::A);
+ assert_eq!(packet.questions[0].qclass, QC::IN);
+ assert_eq!(&packet.questions[0].qname.to_string()[..], "google.com");
+ assert_eq!(packet.answers.len(), 6);
+ let ips = vec![
+ Ipv4Addr::new(64, 233, 164, 100),
+ Ipv4Addr::new(64, 233, 164, 139),
+ Ipv4Addr::new(64, 233, 164, 113),
+ Ipv4Addr::new(64, 233, 164, 102),
+ Ipv4Addr::new(64, 233, 164, 101),
+ Ipv4Addr::new(64, 233, 164, 138),
+ ];
+ for i in 0..6 {
+ assert_eq!(&packet.answers[i].name.to_string()[..], "google.com");
+ assert_eq!(packet.answers[i].cls, C::IN);
+ assert_eq!(packet.answers[i].ttl, 239);
+ match packet.answers[i].data {
+ RData::A(addr) => {
+ assert_eq!(addr.0, ips[i]);
+ }
+ ref x => panic!("Wrong rdata {:?}", x),
+ }
+ }
+ }
+
+ #[test]
+ fn parse_srv_query() {
+ let query = b"[\xd9\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\
+ \x0c_xmpp-server\x04_tcp\x05gmail\x03com\x00\x00!\x00\x01";
+ let packet = Packet::parse(query).unwrap();
+ assert_eq!(packet.header, Header {
+ id: 23513,
+ query: true,
+ opcode: StandardQuery,
+ authoritative: false,
+ truncated: false,
+ recursion_desired: true,
+ recursion_available: false,
+ authenticated_data: false,
+ checking_disabled: false,
+ response_code: NoError,
+ questions: 1,
+ answers: 0,
+ nameservers: 0,
+ additional: 0,
+ });
+ assert_eq!(packet.questions.len(), 1);
+ assert_eq!(packet.questions[0].qtype, QT::SRV);
+ assert_eq!(packet.questions[0].qclass, QC::IN);
+ assert_eq!(packet.questions[0].prefer_unicast, false);
+ assert_eq!(&packet.questions[0].qname.to_string()[..],
+ "_xmpp-server._tcp.gmail.com");
+ assert_eq!(packet.answers.len(), 0);
+ }
+
+ #[test]
+ fn parse_multicast_prefer_unicast_query() {
+ let query = b"\x06%\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\
+ \x07example\x03com\x00\x00\x01\x80\x01";
+ let packet = Packet::parse(query).unwrap();
+
+ assert_eq!(packet.questions.len(), 1);
+ assert_eq!(packet.questions[0].qtype, QT::A);
+ assert_eq!(packet.questions[0].qclass, QC::IN);
+ assert_eq!(packet.questions[0].prefer_unicast, true);
+ }
+
+ #[test]
+ fn parse_example_query_edns() {
+ let query = b"\x95\xce\x01\x00\x00\x01\x00\x00\x00\x00\x00\x01\
+ \x06google\x03com\x00\x00\x01\x00\
+ \x01\x00\x00\x29\x10\x00\x00\x00\x00\x00\x00\x00";
+ let packet = Packet::parse(query).unwrap();
+ assert_eq!(packet.header, Header {
+ id: 38350,
+ query: true,
+ opcode: StandardQuery,
+ authoritative: false,
+ truncated: false,
+ recursion_desired: true,
+ recursion_available: false,
+ authenticated_data: false,
+ checking_disabled: false,
+ response_code: NoError,
+ questions: 1,
+ answers: 0,
+ nameservers: 0,
+ additional: 1,
+ });
+ assert_eq!(packet.questions.len(), 1);
+ assert_eq!(packet.questions[0].qtype, QT::A);
+ assert_eq!(packet.questions[0].qclass, QC::IN);
+ assert_eq!(&packet.questions[0].qname.to_string()[..], "google.com");
+ assert_eq!(packet.answers.len(), 0);
+ match packet.opt {
+ Some(opt) => {
+ assert_eq!(opt.udp, 4096);
+ assert_eq!(opt.extrcode, 0);
+ assert_eq!(opt.version, 0);
+ assert_eq!(opt.flags, 0);
+ },
+ None => panic!("Missing OPT RR")
+ }
+ }
+}
diff --git a/third_party/rust/dns-parser/src/rdata/a.rs b/third_party/rust/dns-parser/src/rdata/a.rs
new file mode 100644
index 0000000000..f4196d5ac2
--- /dev/null
+++ b/third_party/rust/dns-parser/src/rdata/a.rs
@@ -0,0 +1,21 @@
+use std::net::Ipv4Addr;
+
+use Error;
+use byteorder::{BigEndian, ByteOrder};
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct Record(pub Ipv4Addr);
+
+impl<'a> super::Record<'a> for Record {
+
+ const TYPE: isize = 1;
+
+ fn parse(rdata: &'a [u8], _original: &'a [u8]) -> super::RDataResult<'a> {
+ if rdata.len() != 4 {
+ return Err(Error::WrongRdataLength);
+ }
+ let address = Ipv4Addr::from(BigEndian::read_u32(rdata));
+ let record = Record(address);
+ Ok(super::RData::A(record))
+ }
+}
diff --git a/third_party/rust/dns-parser/src/rdata/aaaa.rs b/third_party/rust/dns-parser/src/rdata/aaaa.rs
new file mode 100644
index 0000000000..e59937a525
--- /dev/null
+++ b/third_party/rust/dns-parser/src/rdata/aaaa.rs
@@ -0,0 +1,85 @@
+use std::net::Ipv6Addr;
+
+use Error;
+use byteorder::{BigEndian, ByteOrder};
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct Record(pub Ipv6Addr);
+
+impl<'a> super::Record<'a> for Record {
+
+ const TYPE: isize = 28;
+
+ fn parse(rdata: &'a [u8], _record: &'a [u8]) -> super::RDataResult<'a> {
+ if rdata.len() != 16 {
+ return Err(Error::WrongRdataLength);
+ }
+ let address = Ipv6Addr::new(
+ BigEndian::read_u16(&rdata[0..2]),
+ BigEndian::read_u16(&rdata[2..4]),
+ BigEndian::read_u16(&rdata[4..6]),
+ BigEndian::read_u16(&rdata[6..8]),
+ BigEndian::read_u16(&rdata[8..10]),
+ BigEndian::read_u16(&rdata[10..12]),
+ BigEndian::read_u16(&rdata[12..14]),
+ BigEndian::read_u16(&rdata[14..16]),
+ );
+ let record = Record(address);
+ Ok(super::RData::AAAA(record))
+ }
+}
+
+#[cfg(test)]
+mod test {
+
+ use {Packet, Header};
+ use Opcode::*;
+ use ResponseCode::NoError;
+ use QueryType as QT;
+ use QueryClass as QC;
+ use Class as C;
+ use RData;
+ use super::*;
+
+ #[test]
+ fn parse_response() {
+ let response = b"\xa9\xd9\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\x06\
+ google\x03com\x00\x00\x1c\x00\x01\xc0\x0c\x00\x1c\x00\x01\x00\x00\
+ \x00\x8b\x00\x10*\x00\x14P@\t\x08\x12\x00\x00\x00\x00\x00\x00 \x0e";
+
+ let packet = Packet::parse(response).unwrap();
+ assert_eq!(packet.header, Header {
+ id: 43481,
+ query: false,
+ opcode: StandardQuery,
+ authoritative: false,
+ truncated: false,
+ recursion_desired: true,
+ recursion_available: true,
+ authenticated_data: false,
+ checking_disabled: false,
+ response_code: NoError,
+ questions: 1,
+ answers: 1,
+ nameservers: 0,
+ additional: 0,
+ });
+
+ assert_eq!(packet.questions.len(), 1);
+ assert_eq!(packet.questions[0].qtype, QT::AAAA);
+ assert_eq!(packet.questions[0].qclass, QC::IN);
+ assert_eq!(&packet.questions[0].qname.to_string()[..], "google.com");
+ assert_eq!(packet.answers.len(), 1);
+ assert_eq!(&packet.answers[0].name.to_string()[..], "google.com");
+ assert_eq!(packet.answers[0].cls, C::IN);
+ assert_eq!(packet.answers[0].ttl, 139);
+ match packet.answers[0].data {
+ RData::AAAA(addr) => {
+ assert_eq!(addr.0, Ipv6Addr::new(
+ 0x2A00, 0x1450, 0x4009, 0x812, 0, 0, 0, 0x200e)
+ );
+ }
+ ref x => panic!("Wrong rdata {:?}", x),
+ }
+ }
+}
diff --git a/third_party/rust/dns-parser/src/rdata/all.rs b/third_party/rust/dns-parser/src/rdata/all.rs
new file mode 100644
index 0000000000..106c91b089
--- /dev/null
+++ b/third_party/rust/dns-parser/src/rdata/all.rs
@@ -0,0 +1,11 @@
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct Record;
+
+impl<'a> super::Record<'a> for Record {
+
+ const TYPE: isize = 255;
+
+ fn parse(_rdata: &'a [u8], _original: &'a [u8]) -> super::RDataResult<'a> {
+ unimplemented!();
+ }
+}
diff --git a/third_party/rust/dns-parser/src/rdata/axfr.rs b/third_party/rust/dns-parser/src/rdata/axfr.rs
new file mode 100644
index 0000000000..b48dc1ae24
--- /dev/null
+++ b/third_party/rust/dns-parser/src/rdata/axfr.rs
@@ -0,0 +1,11 @@
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct Record;
+
+impl<'a> super::Record<'a> for Record {
+
+ const TYPE: isize = 252;
+
+ fn parse(_rdata: &'a [u8], _original: &'a [u8]) -> super::RDataResult<'a> {
+ unimplemented!();
+ }
+}
diff --git a/third_party/rust/dns-parser/src/rdata/cname.rs b/third_party/rust/dns-parser/src/rdata/cname.rs
new file mode 100644
index 0000000000..0dcb46952f
--- /dev/null
+++ b/third_party/rust/dns-parser/src/rdata/cname.rs
@@ -0,0 +1,102 @@
+use Name;
+
+#[derive(Debug, Clone, Copy)]
+pub struct Record<'a>(pub Name<'a>);
+
+impl<'a> ToString for Record<'a> {
+ #[inline]
+ fn to_string(&self) -> String {
+ self.0.to_string()
+ }
+}
+
+impl<'a> super::Record<'a> for Record<'a> {
+
+ const TYPE: isize = 5;
+
+ fn parse(rdata: &'a [u8], original: &'a [u8]) -> super::RDataResult<'a> {
+ let name = Name::scan(rdata, original)?;
+ let record = Record(name);
+ Ok(super::RData::CNAME(record))
+ }
+}
+
+#[cfg(test)]
+mod test {
+
+ use std::net::Ipv4Addr;
+ use {Packet, Header};
+ use Opcode::*;
+ use ResponseCode::NoError;
+ use QueryType as QT;
+ use QueryClass as QC;
+ use Class as C;
+ use RData;
+
+ #[test]
+ fn parse_response() {
+ let response = b"\xfc\x9d\x81\x80\x00\x01\x00\x06\x00\x02\x00\x02\x03\
+ cdn\x07sstatic\x03net\x00\x00\x01\x00\x01\xc0\x0c\x00\x05\x00\x01\
+ \x00\x00\x00f\x00\x02\xc0\x10\xc0\x10\x00\x01\x00\x01\x00\x00\x00\
+ f\x00\x04h\x10g\xcc\xc0\x10\x00\x01\x00\x01\x00\x00\x00f\x00\x04h\
+ \x10k\xcc\xc0\x10\x00\x01\x00\x01\x00\x00\x00f\x00\x04h\x10h\xcc\
+ \xc0\x10\x00\x01\x00\x01\x00\x00\x00f\x00\x04h\x10j\xcc\xc0\x10\
+ \x00\x01\x00\x01\x00\x00\x00f\x00\x04h\x10i\xcc\xc0\x10\x00\x02\
+ \x00\x01\x00\x00\x99L\x00\x0b\x08cf-dns02\xc0\x10\xc0\x10\x00\x02\
+ \x00\x01\x00\x00\x99L\x00\x0b\x08cf-dns01\xc0\x10\xc0\xa2\x00\x01\
+ \x00\x01\x00\x00\x99L\x00\x04\xad\xf5:5\xc0\x8b\x00\x01\x00\x01\x00\
+ \x00\x99L\x00\x04\xad\xf5;\x04";
+
+ let packet = Packet::parse(response).unwrap();
+ assert_eq!(packet.header, Header {
+ id: 64669,
+ query: false,
+ opcode: StandardQuery,
+ authoritative: false,
+ truncated: false,
+ recursion_desired: true,
+ recursion_available: true,
+ authenticated_data: false,
+ checking_disabled: false,
+ response_code: NoError,
+ questions: 1,
+ answers: 6,
+ nameservers: 2,
+ additional: 2,
+ });
+
+ assert_eq!(packet.questions.len(), 1);
+ assert_eq!(packet.questions[0].qtype, QT::A);
+ assert_eq!(packet.questions[0].qclass, QC::IN);
+ assert_eq!(&packet.questions[0].qname.to_string()[..], "cdn.sstatic.net");
+ assert_eq!(packet.answers.len(), 6);
+ assert_eq!(&packet.answers[0].name.to_string()[..], "cdn.sstatic.net");
+ assert_eq!(packet.answers[0].cls, C::IN);
+ assert_eq!(packet.answers[0].ttl, 102);
+ match packet.answers[0].data {
+ RData::CNAME(cname) => {
+ assert_eq!(&cname.0.to_string(), "sstatic.net");
+ }
+ ref x => panic!("Wrong rdata {:?}", x),
+ }
+
+ let ips = vec![
+ Ipv4Addr::new(104, 16, 103, 204),
+ Ipv4Addr::new(104, 16, 107, 204),
+ Ipv4Addr::new(104, 16, 104, 204),
+ Ipv4Addr::new(104, 16, 106, 204),
+ Ipv4Addr::new(104, 16, 105, 204),
+ ];
+ for i in 1..6 {
+ assert_eq!(&packet.answers[i].name.to_string()[..], "sstatic.net");
+ assert_eq!(packet.answers[i].cls, C::IN);
+ assert_eq!(packet.answers[i].ttl, 102);
+ match packet.answers[i].data {
+ RData::A(addr) => {
+ assert_eq!(addr.0, ips[i-1]);
+ }
+ ref x => panic!("Wrong rdata {:?}", x),
+ }
+ }
+ }
+}
diff --git a/third_party/rust/dns-parser/src/rdata/hinfo.rs b/third_party/rust/dns-parser/src/rdata/hinfo.rs
new file mode 100644
index 0000000000..6ee160bb86
--- /dev/null
+++ b/third_party/rust/dns-parser/src/rdata/hinfo.rs
@@ -0,0 +1,11 @@
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct Record;
+
+impl<'a> super::Record<'a> for Record {
+
+ const TYPE: isize = 13;
+
+ fn parse(_rdata: &'a [u8], _original: &'a [u8]) -> super::RDataResult<'a> {
+ unimplemented!();
+ }
+}
diff --git a/third_party/rust/dns-parser/src/rdata/maila.rs b/third_party/rust/dns-parser/src/rdata/maila.rs
new file mode 100644
index 0000000000..ab1166cf31
--- /dev/null
+++ b/third_party/rust/dns-parser/src/rdata/maila.rs
@@ -0,0 +1,11 @@
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct Record;
+
+impl<'a> super::Record<'a> for Record {
+
+ const TYPE: isize = 254;
+
+ fn parse(_rdata: &'a [u8], _original: &'a [u8]) -> super::RDataResult<'a> {
+ unimplemented!();
+ }
+}
diff --git a/third_party/rust/dns-parser/src/rdata/mailb.rs b/third_party/rust/dns-parser/src/rdata/mailb.rs
new file mode 100644
index 0000000000..9f043c60c3
--- /dev/null
+++ b/third_party/rust/dns-parser/src/rdata/mailb.rs
@@ -0,0 +1,11 @@
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct Record;
+
+impl<'a> super::Record<'a> for Record {
+
+ const TYPE: isize = 253;
+
+ fn parse(_rdata: &'a [u8], _original: &'a [u8]) -> super::RDataResult<'a> {
+ unimplemented!();
+ }
+}
diff --git a/third_party/rust/dns-parser/src/rdata/mb.rs b/third_party/rust/dns-parser/src/rdata/mb.rs
new file mode 100644
index 0000000000..d14e7b4fc5
--- /dev/null
+++ b/third_party/rust/dns-parser/src/rdata/mb.rs
@@ -0,0 +1,11 @@
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct Record;
+
+impl<'a> super::Record<'a> for Record {
+
+ const TYPE: isize = 7;
+
+ fn parse(_rdata: &'a [u8], _original: &'a [u8]) -> super::RDataResult<'a> {
+ unimplemented!();
+ }
+}
diff --git a/third_party/rust/dns-parser/src/rdata/mf.rs b/third_party/rust/dns-parser/src/rdata/mf.rs
new file mode 100644
index 0000000000..11c935d9f7
--- /dev/null
+++ b/third_party/rust/dns-parser/src/rdata/mf.rs
@@ -0,0 +1,11 @@
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct Record;
+
+impl<'a> super::Record<'a> for Record {
+
+ const TYPE: isize = 4;
+
+ fn parse(_rdata: &'a [u8], _original: &'a [u8]) -> super::RDataResult<'a> {
+ unimplemented!();
+ }
+}
diff --git a/third_party/rust/dns-parser/src/rdata/mg.rs b/third_party/rust/dns-parser/src/rdata/mg.rs
new file mode 100644
index 0000000000..4fce456b7d
--- /dev/null
+++ b/third_party/rust/dns-parser/src/rdata/mg.rs
@@ -0,0 +1,11 @@
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct Record;
+
+impl<'a> super::Record<'a> for Record {
+
+ const TYPE: isize = 8;
+
+ fn parse(_rdata: &'a [u8], _original: &'a [u8]) -> super::RDataResult<'a> {
+ unimplemented!();
+ }
+}
diff --git a/third_party/rust/dns-parser/src/rdata/minfo.rs b/third_party/rust/dns-parser/src/rdata/minfo.rs
new file mode 100644
index 0000000000..29b3a459b2
--- /dev/null
+++ b/third_party/rust/dns-parser/src/rdata/minfo.rs
@@ -0,0 +1,11 @@
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct Record;
+
+impl<'a> super::Record<'a> for Record {
+
+ const TYPE: isize = 14;
+
+ fn parse(_rdata: &'a [u8], _original: &'a [u8]) -> super::RDataResult<'a> {
+ unimplemented!();
+ }
+}
diff --git a/third_party/rust/dns-parser/src/rdata/mod.rs b/third_party/rust/dns-parser/src/rdata/mod.rs
new file mode 100644
index 0000000000..72df27c232
--- /dev/null
+++ b/third_party/rust/dns-parser/src/rdata/mod.rs
@@ -0,0 +1,83 @@
+//! Data types and methods for handling the RData field
+
+#![allow(missing_docs)] // resource records are pretty self-descriptive
+
+pub mod a;
+pub mod aaaa;
+pub mod all;
+pub mod axfr;
+pub mod cname;
+pub mod hinfo;
+pub mod maila;
+pub mod mailb;
+pub mod mb;
+pub mod mf;
+pub mod mg;
+pub mod minfo;
+pub mod mr;
+pub mod mx;
+pub mod ns;
+pub mod nsec;
+pub mod null;
+pub mod opt;
+pub mod ptr;
+pub mod soa;
+pub mod srv;
+pub mod txt;
+pub mod wks;
+
+use {Type, Error};
+
+pub use self::a::Record as A;
+pub use self::aaaa::Record as Aaaa;
+pub use self::cname::Record as Cname;
+pub use self::mx::Record as Mx;
+pub use self::ns::Record as Ns;
+pub use self::nsec::Record as Nsec;
+pub use self::opt::Record as Opt;
+pub use self::ptr::Record as Ptr;
+pub use self::soa::Record as Soa;
+pub use self::srv::Record as Srv;
+pub use self::txt::Record as Txt;
+
+pub type RDataResult<'a> = Result<RData<'a>, Error>;
+
+/// The enumeration that represents known types of DNS resource records data
+#[derive(Debug)]
+pub enum RData<'a> {
+ A(A),
+ AAAA(Aaaa),
+ CNAME(Cname<'a>),
+ MX(Mx<'a>),
+ NS(Ns<'a>),
+ PTR(Ptr<'a>),
+ SOA(Soa<'a>),
+ SRV(Srv<'a>),
+ TXT(Txt<'a>),
+ /// Anything that can't be parsed yet
+ Unknown(&'a [u8]),
+}
+
+pub (crate) trait Record<'a> {
+ const TYPE: isize;
+
+ fn parse(rdata: &'a [u8], original: &'a [u8]) -> RDataResult<'a>;
+}
+
+impl<'a> RData<'a> {
+ /// Parse an RR data and return RData enumeration
+ pub fn parse(typ: Type, rdata: &'a [u8], original: &'a [u8]) -> RDataResult<'a> {
+ match typ {
+ Type::A => A::parse(rdata, original),
+ Type::AAAA => Aaaa::parse(rdata, original),
+ Type::CNAME => Cname::parse(rdata, original),
+ Type::NS => Ns::parse(rdata, original),
+ Type::MX => Mx::parse(rdata, original),
+ Type::PTR => Ptr::parse(rdata, original),
+ Type::SOA => Soa::parse(rdata, original),
+ Type::SRV => Srv::parse(rdata, original),
+ Type::TXT => Txt::parse(rdata, original),
+ _ => Ok(RData::Unknown(rdata)),
+ }
+ }
+}
diff --git a/third_party/rust/dns-parser/src/rdata/mr.rs b/third_party/rust/dns-parser/src/rdata/mr.rs
new file mode 100644
index 0000000000..e313372c4a
--- /dev/null
+++ b/third_party/rust/dns-parser/src/rdata/mr.rs
@@ -0,0 +1,11 @@
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct Record;
+
+impl<'a> super::Record<'a> for Record {
+
+ const TYPE: isize = 9;
+
+ fn parse(_rdata: &'a [u8], _original: &'a [u8]) -> super::RDataResult<'a> {
+ unimplemented!();
+ }
+}
diff --git a/third_party/rust/dns-parser/src/rdata/mx.rs b/third_party/rust/dns-parser/src/rdata/mx.rs
new file mode 100644
index 0000000000..33c5c41dfc
--- /dev/null
+++ b/third_party/rust/dns-parser/src/rdata/mx.rs
@@ -0,0 +1,92 @@
+use {Name, Error};
+use byteorder::{BigEndian, ByteOrder};
+
+#[derive(Debug, Clone, Copy)]
+pub struct Record<'a> {
+ pub preference: u16,
+ pub exchange: Name<'a>,
+}
+
+impl<'a> super::Record<'a> for Record<'a> {
+
+ const TYPE: isize = 15;
+
+ fn parse(rdata: &'a [u8], original: &'a [u8]) -> super::RDataResult<'a> {
+ if rdata.len() < 3 {
+ return Err(Error::WrongRdataLength);
+ }
+ let record = Record {
+ preference: BigEndian::read_u16(&rdata[..2]),
+ exchange: Name::scan(&rdata[2..], original)?,
+ };
+ Ok(super::RData::MX(record))
+ }
+}
+
+#[cfg(test)]
+mod test {
+
+ use {Packet, Header};
+ use Opcode::*;
+ use ResponseCode::NoError;
+ use QueryType as QT;
+ use QueryClass as QC;
+ use Class as C;
+ use RData;
+ use super::*;
+
+ #[test]
+ fn parse_response() {
+ let response = b"\xe3\xe8\x81\x80\x00\x01\x00\x05\x00\x00\x00\x00\
+ \x05gmail\x03com\x00\x00\x0f\x00\x01\xc0\x0c\x00\x0f\x00\x01\
+ \x00\x00\x04|\x00\x1b\x00\x05\rgmail-smtp-in\x01l\x06google\xc0\
+ \x12\xc0\x0c\x00\x0f\x00\x01\x00\x00\x04|\x00\t\x00\
+ \n\x04alt1\xc0)\xc0\x0c\x00\x0f\x00\x01\x00\x00\x04|\
+ \x00\t\x00(\x04alt4\xc0)\xc0\x0c\x00\x0f\x00\x01\x00\
+ \x00\x04|\x00\t\x00\x14\x04alt2\xc0)\xc0\x0c\x00\x0f\
+ \x00\x01\x00\x00\x04|\x00\t\x00\x1e\x04alt3\xc0)";
+ let packet = Packet::parse(response).unwrap();
+ assert_eq!(packet.header, Header {
+ id: 58344,
+ query: false,
+ opcode: StandardQuery,
+ authoritative: false,
+ truncated: false,
+ recursion_desired: true,
+ recursion_available: true,
+ authenticated_data: false,
+ checking_disabled: false,
+ response_code: NoError,
+ questions: 1,
+ answers: 5,
+ nameservers: 0,
+ additional: 0,
+ });
+ assert_eq!(packet.questions.len(), 1);
+ assert_eq!(packet.questions[0].qtype, QT::MX);
+ assert_eq!(packet.questions[0].qclass, QC::IN);
+ assert_eq!(&packet.questions[0].qname.to_string()[..],
+ "gmail.com");
+ assert_eq!(packet.answers.len(), 5);
+ let items = vec![
+ ( 5, "gmail-smtp-in.l.google.com"),
+ (10, "alt1.gmail-smtp-in.l.google.com"),
+ (40, "alt4.gmail-smtp-in.l.google.com"),
+ (20, "alt2.gmail-smtp-in.l.google.com"),
+ (30, "alt3.gmail-smtp-in.l.google.com"),
+ ];
+ for i in 0..5 {
+ assert_eq!(&packet.answers[i].name.to_string()[..],
+ "gmail.com");
+ assert_eq!(packet.answers[i].cls, C::IN);
+ assert_eq!(packet.answers[i].ttl, 1148);
+ match *&packet.answers[i].data {
+ RData::MX( Record { preference, exchange }) => {
+ assert_eq!(preference, items[i].0);
+ assert_eq!(exchange.to_string(), (items[i].1).to_string());
+ }
+ ref x => panic!("Wrong rdata {:?}", x),
+ }
+ }
+ }
+}
diff --git a/third_party/rust/dns-parser/src/rdata/ns.rs b/third_party/rust/dns-parser/src/rdata/ns.rs
new file mode 100644
index 0000000000..42a2f29ab1
--- /dev/null
+++ b/third_party/rust/dns-parser/src/rdata/ns.rs
@@ -0,0 +1,88 @@
+use Name;
+
+#[derive(Debug, Clone, Copy)]
+pub struct Record<'a>(pub Name<'a>);
+
+impl<'a> ToString for Record<'a> {
+ #[inline]
+ fn to_string(&self) -> String {
+ self.0.to_string()
+ }
+}
+
+impl<'a> super::Record<'a> for Record<'a> {
+
+ const TYPE: isize = 2;
+
+ fn parse(rdata: &'a [u8], original: &'a [u8]) -> super::RDataResult<'a> {
+ let name = Name::scan(rdata, original)?;
+ let record = Record(name);
+ Ok(super::RData::NS(record))
+ }
+}
+
+#[cfg(test)]
+mod test {
+
+ use {Packet, Header};
+ use Opcode::*;
+ use ResponseCode::NoError;
+ use QueryType as QT;
+ use QueryClass as QC;
+ use Class as C;
+ use RData;
+
+ #[test]
+ fn parse_response() {
+ let response = b"\x4a\xf0\x81\x80\x00\x01\x00\x01\x00\x01\x00\x00\
+ \x03www\x05skype\x03com\x00\x00\x01\x00\x01\
+ \xc0\x0c\x00\x05\x00\x01\x00\x00\x0e\x10\
+ \x00\x1c\x07\x6c\x69\x76\x65\x63\x6d\x73\x0e\x74\
+ \x72\x61\x66\x66\x69\x63\x6d\x61\x6e\x61\x67\x65\
+ \x72\x03\x6e\x65\x74\x00\
+ \xc0\x42\x00\x02\x00\x01\x00\x01\xd5\xd3\x00\x11\
+ \x01\x67\x0c\x67\x74\x6c\x64\x2d\x73\x65\x72\x76\x65\x72\x73\
+ \xc0\x42";
+ let packet = Packet::parse(response).unwrap();
+ assert_eq!(packet.header, Header {
+ id: 19184,
+ query: false,
+ opcode: StandardQuery,
+ authoritative: false,
+ truncated: false,
+ recursion_desired: true,
+ recursion_available: true,
+ authenticated_data: false,
+ checking_disabled: false,
+ response_code: NoError,
+ questions: 1,
+ answers: 1,
+ nameservers: 1,
+ additional: 0,
+ });
+ assert_eq!(packet.questions.len(), 1);
+ assert_eq!(packet.questions[0].qtype, QT::A);
+ assert_eq!(packet.questions[0].qclass, QC::IN);
+ assert_eq!(&packet.questions[0].qname.to_string()[..], "www.skype.com");
+ assert_eq!(packet.answers.len(), 1);
+ assert_eq!(&packet.answers[0].name.to_string()[..], "www.skype.com");
+ assert_eq!(packet.answers[0].cls, C::IN);
+ assert_eq!(packet.answers[0].ttl, 3600);
+ match packet.answers[0].data {
+ RData::CNAME(cname) => {
+ assert_eq!(&cname.0.to_string()[..], "livecms.trafficmanager.net");
+ }
+ ref x => panic!("Wrong rdata {:?}", x),
+ }
+ assert_eq!(packet.nameservers.len(), 1);
+ assert_eq!(&packet.nameservers[0].name.to_string()[..], "net");
+ assert_eq!(packet.nameservers[0].cls, C::IN);
+ assert_eq!(packet.nameservers[0].ttl, 120275);
+ match packet.nameservers[0].data {
+ RData::NS(ns) => {
+ assert_eq!(&ns.0.to_string()[..], "g.gtld-servers.net");
+ }
+ ref x => panic!("Wrong rdata {:?}", x),
+ }
+ }
+}
diff --git a/third_party/rust/dns-parser/src/rdata/nsec.rs b/third_party/rust/dns-parser/src/rdata/nsec.rs
new file mode 100644
index 0000000000..354c512273
--- /dev/null
+++ b/third_party/rust/dns-parser/src/rdata/nsec.rs
@@ -0,0 +1,11 @@
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct Record;
+
+impl<'a> super::Record<'a> for Record {
+
+ const TYPE: isize = 47;
+
+ fn parse(_rdata: &'a [u8], _original: &'a [u8]) -> super::RDataResult<'a> {
+ unimplemented!();
+ }
+}
diff --git a/third_party/rust/dns-parser/src/rdata/null.rs b/third_party/rust/dns-parser/src/rdata/null.rs
new file mode 100644
index 0000000000..b3bfcd35ee
--- /dev/null
+++ b/third_party/rust/dns-parser/src/rdata/null.rs
@@ -0,0 +1,11 @@
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct Record;
+
+impl<'a> super::Record<'a> for Record {
+
+ const TYPE: isize = 10;
+
+ fn parse(_rdata: &'a [u8], _original: &'a [u8]) -> super::RDataResult<'a> {
+ unimplemented!();
+ }
+}
diff --git a/third_party/rust/dns-parser/src/rdata/opt.rs b/third_party/rust/dns-parser/src/rdata/opt.rs
new file mode 100644
index 0000000000..694d393440
--- /dev/null
+++ b/third_party/rust/dns-parser/src/rdata/opt.rs
@@ -0,0 +1,18 @@
+/// RFC 6891 OPT RR
+#[derive(Debug)]
+pub struct Record<'a> {
+ pub udp: u16,
+ pub extrcode: u8,
+ pub version: u8,
+ pub flags: u16,
+ pub data: super::RData<'a>,
+}
+
+impl<'a> super::Record<'a> for Record<'a> {
+
+ const TYPE: isize = 41;
+
+ fn parse(_rdata: &'a [u8], _original: &'a [u8]) -> super::RDataResult<'a> {
+ unimplemented!();
+ }
+}
diff --git a/third_party/rust/dns-parser/src/rdata/ptr.rs b/third_party/rust/dns-parser/src/rdata/ptr.rs
new file mode 100644
index 0000000000..315c3795a8
--- /dev/null
+++ b/third_party/rust/dns-parser/src/rdata/ptr.rs
@@ -0,0 +1,74 @@
+use Name;
+
+#[derive(Debug, Clone, Copy)]
+pub struct Record<'a>(pub Name<'a>);
+
+impl<'a> ToString for Record<'a> {
+ #[inline]
+ fn to_string(&self) -> String {
+ self.0.to_string()
+ }
+}
+
+impl<'a> super::Record<'a> for Record<'a> {
+
+ const TYPE: isize = 12;
+
+ fn parse(rdata: &'a [u8], original: &'a [u8]) -> super::RDataResult<'a> {
+ let name = Name::scan(rdata, original)?;
+ let record = Record(name);
+ Ok(super::RData::PTR(record))
+ }
+}
+
+#[cfg(test)]
+mod test {
+
+ use {Packet, Header};
+ use Opcode::*;
+ use ResponseCode::NoError;
+ use QueryType as QT;
+ use QueryClass as QC;
+ use Class as C;
+ use RData;
+
+ #[test]
+ fn parse_response() {
+ let response = b"\x53\xd6\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\
+ \x0269\x0293\x0275\x0272\x07in-addr\x04arpa\x00\
+ \x00\x0c\x00\x01\
+ \xc0\x0c\x00\x0c\x00\x01\x00\x01\x51\x80\x00\x1e\
+ \x10pool-72-75-93-69\x07verizon\x03net\x00";
+ let packet = Packet::parse(response).unwrap();
+ assert_eq!(packet.header, Header {
+ id: 21462,
+ query: false,
+ opcode: StandardQuery,
+ authoritative: false,
+ truncated: false,
+ recursion_desired: true,
+ recursion_available: true,
+ authenticated_data: false,
+ checking_disabled: false,
+ response_code: NoError,
+ questions: 1,
+ answers: 1,
+ nameservers: 0,
+ additional: 0,
+ });
+ assert_eq!(packet.questions.len(), 1);
+ assert_eq!(packet.questions[0].qtype, QT::PTR);
+ assert_eq!(packet.questions[0].qclass, QC::IN);
+ assert_eq!(&packet.questions[0].qname.to_string()[..], "69.93.75.72.in-addr.arpa");
+ assert_eq!(packet.answers.len(), 1);
+ assert_eq!(&packet.answers[0].name.to_string()[..], "69.93.75.72.in-addr.arpa");
+ assert_eq!(packet.answers[0].cls, C::IN);
+ assert_eq!(packet.answers[0].ttl, 86400);
+ match packet.answers[0].data {
+ RData::PTR(name) => {
+ assert_eq!(&name.0.to_string()[..], "pool-72-75-93-69.verizon.net");
+ }
+ ref x => panic!("Wrong rdata {:?}", x),
+ }
+ }
+}
diff --git a/third_party/rust/dns-parser/src/rdata/soa.rs b/third_party/rust/dns-parser/src/rdata/soa.rs
new file mode 100644
index 0000000000..e6d5f1712d
--- /dev/null
+++ b/third_party/rust/dns-parser/src/rdata/soa.rs
@@ -0,0 +1,101 @@
+use {Name, Error};
+use byteorder::{BigEndian, ByteOrder};
+
+/// The SOA (Start of Authority) record
+#[derive(Debug, Clone, Copy)]
+pub struct Record<'a> {
+ pub primary_ns: Name<'a>,
+ pub mailbox: Name<'a>,
+ pub serial: u32,
+ pub refresh: u32,
+ pub retry: u32,
+ pub expire: u32,
+ pub minimum_ttl: u32,
+}
+
+impl<'a> super::Record<'a> for Record<'a> {
+
+ const TYPE: isize = 6;
+
+ fn parse(rdata: &'a [u8], original: &'a [u8]) -> super::RDataResult<'a> {
+ let mut pos = 0;
+ let primary_name_server = try!(Name::scan(rdata, original));
+ pos += primary_name_server.byte_len();
+ let mailbox = try!(Name::scan(&rdata[pos..], original));
+ pos += mailbox.byte_len();
+ if rdata[pos..].len() < 20 {
+ return Err(Error::WrongRdataLength);
+ }
+ let record = Record {
+ primary_ns: primary_name_server,
+ mailbox: mailbox,
+ serial: BigEndian::read_u32(&rdata[pos..(pos+4)]),
+ refresh: BigEndian::read_u32(&rdata[(pos+4)..(pos+8)]),
+ retry: BigEndian::read_u32(&rdata[(pos+8)..(pos+12)]),
+ expire: BigEndian::read_u32(&rdata[(pos+12)..(pos+16)]),
+ minimum_ttl: BigEndian::read_u32(&rdata[(pos+16)..(pos+20)]),
+ };
+ Ok(super::RData::SOA(record))
+ }
+}
+
+#[cfg(test)]
+mod test {
+
+ use {Packet, Header};
+ use Opcode::*;
+ use ResponseCode::NameError;
+ use QueryType as QT;
+ use QueryClass as QC;
+ use Class as C;
+ use RData;
+
+ #[test]
+ fn parse_response() {
+ let response = b"\x9f\xc5\x85\x83\x00\x01\x00\x00\x00\x01\x00\x00\
+ \x0edlkfjkdjdslfkj\x07youtube\x03com\x00\x00\x01\x00\x01\
+ \xc0\x1b\x00\x06\x00\x01\x00\x00\x2a\x30\x00\x1e\xc0\x1b\
+ \x05admin\xc0\x1b\x77\xed\x2a\x73\x00\x00\x51\x80\x00\x00\
+ \x0e\x10\x00\x00\x3a\x80\x00\x00\x2a\x30";
+ let packet = Packet::parse(response).unwrap();
+ assert_eq!(packet.header, Header {
+ id: 40901,
+ query: false,
+ opcode: StandardQuery,
+ authoritative: true,
+ truncated: false,
+ recursion_desired: true,
+ recursion_available: true,
+ authenticated_data: false,
+ checking_disabled: false,
+ response_code: NameError,
+ questions: 1,
+ answers: 0,
+ nameservers: 1,
+ additional: 0,
+ });
+ assert_eq!(packet.questions.len(), 1);
+ assert_eq!(packet.questions[0].qtype, QT::A);
+ assert_eq!(packet.questions[0].qclass, QC::IN);
+ assert_eq!(&packet.questions[0].qname.to_string()[..], "dlkfjkdjdslfkj.youtube.com");
+ assert_eq!(packet.answers.len(), 0);
+
+ assert_eq!(packet.nameservers.len(), 1);
+ assert_eq!(&packet.nameservers[0].name.to_string()[..], "youtube.com");
+ assert_eq!(packet.nameservers[0].cls, C::IN);
+ assert_eq!(packet.nameservers[0].multicast_unique, false);
+ assert_eq!(packet.nameservers[0].ttl, 10800);
+ match packet.nameservers[0].data {
+ RData::SOA(ref soa_rec) => {
+ assert_eq!(&soa_rec.primary_ns.to_string()[..], "youtube.com");
+ assert_eq!(&soa_rec.mailbox.to_string()[..], "admin.youtube.com");
+ assert_eq!(soa_rec.serial, 2012031603);
+ assert_eq!(soa_rec.refresh, 20864);
+ assert_eq!(soa_rec.retry, 3600);
+ assert_eq!(soa_rec.expire, 14976);
+ assert_eq!(soa_rec.minimum_ttl, 10800);
+ }
+ ref x => panic!("Wrong rdata {:?}", x),
+ }
+ }
+}
diff --git a/third_party/rust/dns-parser/src/rdata/srv.rs b/third_party/rust/dns-parser/src/rdata/srv.rs
new file mode 100644
index 0000000000..dbc151d8aa
--- /dev/null
+++ b/third_party/rust/dns-parser/src/rdata/srv.rs
@@ -0,0 +1,102 @@
+use {Name, Error};
+use byteorder::{BigEndian, ByteOrder};
+
+#[derive(Debug, Clone, Copy)]
+pub struct Record<'a> {
+ pub priority: u16,
+ pub weight: u16,
+ pub port: u16,
+ pub target: Name<'a>,
+}
+
+impl<'a> super::Record<'a> for Record<'a> {
+
+ const TYPE: isize = 33;
+
+ fn parse(rdata: &'a [u8], original: &'a [u8]) -> super::RDataResult<'a> {
+ if rdata.len() < 7 {
+ return Err(Error::WrongRdataLength);
+ }
+ let record = Record {
+ priority: BigEndian::read_u16(&rdata[..2]),
+ weight: BigEndian::read_u16(&rdata[2..4]),
+ port: BigEndian::read_u16(&rdata[4..6]),
+ target: Name::scan(&rdata[6..], original)?,
+ };
+ Ok(super::RData::SRV(record))
+ }
+}
+
+#[cfg(test)]
+mod test {
+
+ use {Packet, Header};
+ use Opcode::*;
+ use ResponseCode::NoError;
+ use QueryType as QT;
+ use QueryClass as QC;
+ use Class as C;
+ use RData;
+ use super::*;
+
+ #[test]
+ fn parse_response() {
+ let response = b"[\xd9\x81\x80\x00\x01\x00\x05\x00\x00\x00\x00\
+ \x0c_xmpp-server\x04_tcp\x05gmail\x03com\x00\x00!\x00\x01\
+ \xc0\x0c\x00!\x00\x01\x00\x00\x03\x84\x00 \x00\x05\x00\x00\
+ \x14\x95\x0bxmpp-server\x01l\x06google\x03com\x00\xc0\x0c\x00!\
+ \x00\x01\x00\x00\x03\x84\x00%\x00\x14\x00\x00\x14\x95\
+ \x04alt3\x0bxmpp-server\x01l\x06google\x03com\x00\
+ \xc0\x0c\x00!\x00\x01\x00\x00\x03\x84\x00%\x00\x14\x00\x00\
+ \x14\x95\x04alt1\x0bxmpp-server\x01l\x06google\x03com\x00\
+ \xc0\x0c\x00!\x00\x01\x00\x00\x03\x84\x00%\x00\x14\x00\x00\
+ \x14\x95\x04alt2\x0bxmpp-server\x01l\x06google\x03com\x00\
+ \xc0\x0c\x00!\x00\x01\x00\x00\x03\x84\x00%\x00\x14\x00\x00\
+ \x14\x95\x04alt4\x0bxmpp-server\x01l\x06google\x03com\x00";
+ let packet = Packet::parse(response).unwrap();
+ assert_eq!(packet.header, Header {
+ id: 23513,
+ query: false,
+ opcode: StandardQuery,
+ authoritative: false,
+ truncated: false,
+ recursion_desired: true,
+ recursion_available: true,
+ authenticated_data: false,
+ checking_disabled: false,
+ response_code: NoError,
+ questions: 1,
+ answers: 5,
+ nameservers: 0,
+ additional: 0,
+ });
+ assert_eq!(packet.questions.len(), 1);
+ assert_eq!(packet.questions[0].qtype, QT::SRV);
+ assert_eq!(packet.questions[0].qclass, QC::IN);
+ assert_eq!(&packet.questions[0].qname.to_string()[..],
+ "_xmpp-server._tcp.gmail.com");
+ assert_eq!(packet.answers.len(), 5);
+ let items = vec![
+ (5, 0, 5269, "xmpp-server.l.google.com"),
+ (20, 0, 5269, "alt3.xmpp-server.l.google.com"),
+ (20, 0, 5269, "alt1.xmpp-server.l.google.com"),
+ (20, 0, 5269, "alt2.xmpp-server.l.google.com"),
+ (20, 0, 5269, "alt4.xmpp-server.l.google.com"),
+ ];
+ for i in 0..5 {
+ assert_eq!(&packet.answers[i].name.to_string()[..],
+ "_xmpp-server._tcp.gmail.com");
+ assert_eq!(packet.answers[i].cls, C::IN);
+ assert_eq!(packet.answers[i].ttl, 900);
+ match *&packet.answers[i].data {
+ RData::SRV(Record { priority, weight, port, target }) => {
+ assert_eq!(priority, items[i].0);
+ assert_eq!(weight, items[i].1);
+ assert_eq!(port, items[i].2);
+ assert_eq!(target.to_string(), (items[i].3).to_string());
+ }
+ ref x => panic!("Wrong rdata {:?}", x),
+ }
+ }
+ }
+}
diff --git a/third_party/rust/dns-parser/src/rdata/txt.rs b/third_party/rust/dns-parser/src/rdata/txt.rs
new file mode 100644
index 0000000000..8f5f5fc26d
--- /dev/null
+++ b/third_party/rust/dns-parser/src/rdata/txt.rs
@@ -0,0 +1,125 @@
+use Error;
+
+#[derive(Debug, Clone)]
+pub struct Record<'a> {
+ bytes: &'a [u8],
+}
+
+#[derive(Debug)]
+pub struct RecordIter<'a> {
+ bytes: &'a [u8],
+}
+
+impl<'a> Iterator for RecordIter<'a> {
+ type Item = &'a [u8];
+ fn next(&mut self) -> Option<&'a [u8]> {
+ if self.bytes.len() >= 1 {
+ let len = self.bytes[0] as usize;
+ debug_assert!(self.bytes.len() >= len+1);
+ let (head, tail) = self.bytes[1..].split_at(len);
+ self.bytes = tail;
+ return Some(head);
+ }
+ return None;
+ }
+}
+
+impl<'a> Record<'a> {
+
+ // Returns iterator over text chunks
+ pub fn iter(&self) -> RecordIter<'a> {
+ RecordIter {
+ bytes: self.bytes,
+ }
+ }
+}
+
+impl<'a> super::Record<'a> for Record<'a> {
+
+ const TYPE: isize = 16;
+
+ fn parse(rdata: &'a [u8], _original: &'a [u8]) -> super::RDataResult<'a> {
+ // Just a quick check that record is valid
+ let len = rdata.len();
+ if len < 1 {
+ return Err(Error::WrongRdataLength);
+ }
+ let mut pos = 0;
+ while pos < len {
+ let rdlen = rdata[pos] as usize;
+ pos += 1;
+ if len < rdlen + pos {
+ return Err(Error::WrongRdataLength);
+ }
+ pos += rdlen;
+ }
+ Ok(super::RData::TXT(Record {
+ bytes: rdata,
+ }))
+ }
+}
+
+#[cfg(test)]
+mod test {
+
+ use std::str::from_utf8;
+
+ use {Packet, Header};
+ use Opcode::*;
+ use ResponseCode::NoError;
+ use QueryType as QT;
+ use QueryClass as QC;
+ use Class as C;
+ use RData;
+
+ #[test]
+ fn parse_response_multiple_strings() {
+ let response = b"\x06%\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\
+ \x08facebook\x03com\x00\x00\x10\x00\x01\
+ \xc0\x0c\x00\x10\x00\x01\x00\x01\x51\x3d\x00\x23\
+ \x15\x76\x3d\x73\x70\x66\x31\x20\x72\x65\x64\x69\
+ \x72\x65\x63\x74\x3d\x5f\x73\x70\x66\x2e\
+ \x0c\x66\x61\x63\x65\x62\x6f\x6f\x6b\x2e\x63\x6f\x6d";
+
+ let packet = Packet::parse(response).unwrap();
+ assert_eq!(packet.header, Header {
+ id: 1573,
+ query: false,
+ opcode: StandardQuery,
+ authoritative: false,
+ truncated: false,
+ recursion_desired: true,
+ recursion_available: true,
+ authenticated_data: false,
+ checking_disabled: false,
+ response_code: NoError,
+ questions: 1,
+ answers: 1,
+ nameservers: 0,
+ additional: 0,
+ });
+ assert_eq!(packet.questions.len(), 1);
+ assert_eq!(packet.questions[0].qtype, QT::TXT);
+ assert_eq!(packet.questions[0].qclass, QC::IN);
+ assert_eq!(&packet.questions[0].qname.to_string()[..], "facebook.com");
+ assert_eq!(packet.answers.len(), 1);
+ assert_eq!(&packet.answers[0].name.to_string()[..], "facebook.com");
+ assert_eq!(packet.answers[0].multicast_unique, false);
+ assert_eq!(packet.answers[0].cls, C::IN);
+ assert_eq!(packet.answers[0].ttl, 86333);
+ match packet.answers[0].data {
+ RData::TXT(ref text) => {
+ assert_eq!(text.iter()
+ .map(|x| from_utf8(x).unwrap())
+ .collect::<Vec<_>>()
+ .concat(), "v=spf1 redirect=_spf.facebook.com");
+
+ // also assert boundaries are kept
+ assert_eq!(text.iter().collect::<Vec<_>>(),
+ ["v=spf1 redirect=_spf.".as_bytes(),
+ "facebook.com".as_bytes()]);
+ }
+ ref x => panic!("Wrong rdata {:?}", x),
+ }
+ }
+}
diff --git a/third_party/rust/dns-parser/src/rdata/wks.rs b/third_party/rust/dns-parser/src/rdata/wks.rs
new file mode 100644
index 0000000000..ff6e5f1881
--- /dev/null
+++ b/third_party/rust/dns-parser/src/rdata/wks.rs
@@ -0,0 +1,11 @@
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct Record;
+
+impl<'a> super::Record<'a> for Record {
+
+ const TYPE: isize = 11;
+
+ fn parse(_rdata: &'a [u8], _original: &'a [u8]) -> super::RDataResult<'a> {
+ unimplemented!();
+ }
+}
diff --git a/third_party/rust/dns-parser/src/structs.rs b/third_party/rust/dns-parser/src/structs.rs
new file mode 100644
index 0000000000..4f60639047
--- /dev/null
+++ b/third_party/rust/dns-parser/src/structs.rs
@@ -0,0 +1,50 @@
+use {QueryType, QueryClass, Name, Class, Header, RData};
+use rdata::opt;
+
+
+/// Parsed DNS packet
+#[derive(Debug)]
+#[allow(missing_docs)] // should be covered by spec
+pub struct Packet<'a> {
+ pub header: Header,
+ pub questions: Vec<Question<'a>>,
+ pub answers: Vec<ResourceRecord<'a>>,
+ pub nameservers: Vec<ResourceRecord<'a>>,
+ pub additional: Vec<ResourceRecord<'a>>,
+ /// Optional Pseudo-RR
+ /// When present it is sent as an RR in the additional section. In this RR
+ /// the `class` and `ttl` fields store max udp packet size and flags
+ /// respectively. To keep `ResourceRecord` clean we store the OPT record
+ /// here.
+ pub opt: Option<opt::Record<'a>>,
+}
+
+/// A parsed chunk of data in the Query section of the packet
+#[derive(Debug)]
+#[allow(missing_docs)] // should be covered by spec
+pub struct Question<'a> {
+ pub qname: Name<'a>,
+ /// Whether or not we prefer unicast responses.
+ /// This is used in multicast DNS.
+ pub prefer_unicast: bool,
+ pub qtype: QueryType,
+ pub qclass: QueryClass,
+}
+
+/// A single DNS record
+///
+/// We aim to provide whole range of DNS records available. But as time is
+/// limited we have some types of packets which are parsed and other provided
+/// as unparsed slice of bytes.
+#[derive(Debug)]
+#[allow(missing_docs)] // should be covered by spec
+pub struct ResourceRecord<'a> {
+ pub name: Name<'a>,
+ /// Whether or not the set of resource records is fully contained in the
+ /// packet, or whether there will be more resource records in future
+ /// packets. Only used for multicast DNS.
+ pub multicast_unique: bool,
+ pub cls: Class,
+ pub ttl: u32,
+ pub data: RData<'a>,
+}
diff --git a/third_party/rust/dns-parser/vagga.yaml b/third_party/rust/dns-parser/vagga.yaml
new file mode 100644
index 0000000000..ddedd53c5c
--- /dev/null
+++ b/third_party/rust/dns-parser/vagga.yaml
@@ -0,0 +1,44 @@
+commands:
+
+ cargo: !Command
+ description: Run any cargo command
+ container: ubuntu
+ run: [cargo]
+
+ make: !Command
+ description: Build the library
+ container: ubuntu
+ run: [cargo, build]
+
+ test: !Command
+ description: Run the tests
+ container: ubuntu
+ environ:
+ RUST_BACKTRACE: 1
+ run: [cargo, test]
+
+ _bulk: !Command
+ description: Run `bulk` command (for version bookkeeping)
+ container: ubuntu
+ run: [bulk]
+
+containers:
+
+ ubuntu:
+ setup:
+ - !Ubuntu xenial
+ - !UbuntuUniverse
+ - !Install [ca-certificates, build-essential, vim]
+
+ - !TarInstall
+ url: "https://static.rust-lang.org/dist/rust-1.28.0-x86_64-unknown-linux-gnu.tar.gz"
+ script: "./install.sh --prefix=/usr \
+ --components=rustc,rust-std-x86_64-unknown-linux-gnu,cargo"
+ - &bulk !Tar
+ url: "https://github.com/tailhook/bulk/releases/download/v0.4.11/bulk-v0.4.11.tar.gz"
+ sha256: b718bb8448e726690c94d98d004bf7575f7a429106ec26ad3faf11e0fd9a7978
+ path: /
+
+ environ:
+ HOME: /work/target
+ USER: pc