Bug 1554976 - Run mach vendor rust; r=ng

Differential Revision: https://phabricator.services.mozilla.com/D38491

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Dan Minor 2019-08-28 13:11:05 +00:00
parent a17af56ba5
commit b9e2e7f9b3
42 changed files with 2964 additions and 0 deletions

22
Cargo.lock generated
View File

@ -954,6 +954,15 @@ dependencies = [
"winapi 0.3.6 (git+https://github.com/froydnj/winapi-rs?branch=aarch64)",
]
[[package]]
name = "dns-parser"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "dogear"
version = "0.3.3"
@ -1277,6 +1286,7 @@ dependencies = [
"kvstore 0.1.0",
"lmdb-rkv-sys 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mdns_service 0.1.0",
"mozurl 0.0.1",
"mp4parse_capi 0.11.2",
"netwerk_helper 0.0.1",
@ -1735,6 +1745,17 @@ name = "matches"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "mdns_service"
version = "0.1.0"
dependencies = [
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"dns-parser 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)",
"socket2 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "memchr"
version = "1.0.2"
@ -3947,6 +3968,7 @@ dependencies = [
"checksum devd-rs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d009f166c0d9e9f9909dc751630b3a6411ab7f85a153d32d01deb364ffe52a7"
"checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c"
"checksum dirs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "88972de891f6118092b643d85a0b28e0678e0f948d7f879aa32f2d5aafe97d2a"
"checksum dns-parser 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c4d33be9473d06f75f58220f71f7a9317aca647dc061dbd3c361b0bef505fbea"
"checksum dogear 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "57cd6ee785daa898686f3e2fb4a2b1ce490fcd6d69665c857d16fb61b48f4aae"
"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
"checksum dtoa-short 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "068d4026697c1a18f0b0bb8cfcad1b0c151b90d8edb9bf4c235ad68128920d1d"

View File

@ -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"}

41
third_party/rust/dns-parser/Cargo.toml vendored Normal file
View File

@ -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"]

View File

@ -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.

19
third_party/rust/dns-parser/LICENSE-MIT vendored Normal file
View File

@ -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.

31
third_party/rust/dns-parser/README.md vendored Normal file
View File

@ -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.

8
third_party/rust/dns-parser/bulk.yaml vendored Normal file
View File

@ -0,0 +1,8 @@
minimum-bulk: v0.4.5
versions:
- file: Cargo.toml
block-start: ^\[package\]
block-end: ^\[.*\]
regex: ^version\s*=\s*"(\S+)"

View File

@ -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(())
}

View File

@ -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(())
}

View File

@ -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[..]);
}
}

299
third_party/rust/dns-parser/src/enums.rs vendored Normal file
View File

@ -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)),
}
}
}

View File

@ -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")
}
}
}

View File

@ -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,
});
}
}

39
third_party/rust/dns-parser/src/lib.rs vendored Normal file
View File

@ -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};

183
third_party/rust/dns-parser/src/name.rs vendored Normal file
View File

@ -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");
}
}

View File

@ -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")
}
}
}

View File

@ -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))
}
}

View File

@ -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),
}
}
}

View File

@ -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!();
}
}

View File

@ -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!();
}
}

View File

@ -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),
}
}
}
}

View File

@ -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!();
}
}

View File

@ -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!();
}
}

View File

@ -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!();
}
}

View File

@ -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!();
}
}

View File

@ -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!();
}
}

View File

@ -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!();
}
}

View File

@ -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!();
}
}

View File

@ -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)),
}
}
}

View File

@ -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!();
}
}

View File

@ -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),
}
}
}
}

View File

@ -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),
}
}
}

View File

@ -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!();
}
}

View File

@ -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!();
}
}

View File

@ -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!();
}
}

View File

@ -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),
}
}
}

View File

@ -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),
}
}
}

View File

@ -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),
}
}
}
}

View File

@ -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),
}
}
}

View File

@ -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!();
}
}

View File

@ -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>,
}

44
third_party/rust/dns-parser/vagga.yaml vendored Normal file
View File

@ -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