summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2016-01-31 00:06:49 -0500
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2016-02-16 19:55:50 -0500
commitdab48ea63a461e2fe2bd491ba28570424bcaf362 (patch)
treee8a70471e5731ece9f520c6935d4e85404045c65
parent2e74028a5cf636760191656d7fabfa9f43db96e2 (diff)
systemd-resolve: allow whole packets to be dumped in binary form
-rw-r--r--man/systemd-resolve.xml10
-rw-r--r--src/resolve/resolve-tool.c109
2 files changed, 80 insertions, 39 deletions
diff --git a/man/systemd-resolve.xml b/man/systemd-resolve.xml
index bec6213de2..c288fd974e 100644
--- a/man/systemd-resolve.xml
+++ b/man/systemd-resolve.xml
@@ -233,6 +233,16 @@
</varlistentry>
<varlistentry>
+ <term><option>--raw</option><optional>=payload|packet</optional></term>
+
+ <listitem><para>Dump the answer as binary data. If there is no argument or if the argument is
+ <literal>payload</literal>, the payload of the packet is exported. If the argument is
+ <literal>packet</literal>, the whole packet is dumped in wire format, prefixed by
+ length specified as a little-endian 64-bit number. This format allows multiple packets
+ to be dumped and unambigously parsed.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>--legend=</option><replaceable>BOOL</replaceable></term>
<listitem><para>Takes a boolean parameter. If true (the default), column headers and meta information about the
diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c
index a24bb546d4..a519074278 100644
--- a/src/resolve/resolve-tool.c
+++ b/src/resolve/resolve-tool.c
@@ -43,7 +43,14 @@ static uint16_t arg_type = 0;
static uint16_t arg_class = 0;
static bool arg_legend = true;
static uint64_t arg_flags = 0;
-static bool arg_raw = false;
+
+typedef enum RawType {
+ RAW_NONE,
+ RAW_PAYLOAD,
+ RAW_PACKET,
+} RawType;
+
+static RawType arg_raw = RAW_NONE;
static enum {
MODE_RESOLVE_HOST,
@@ -332,6 +339,50 @@ static int parse_address(const char *s, int *family, union in_addr_union *addres
return 0;
}
+static int output_rr_packet(const void *d, size_t l, int ifindex) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+ _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+ int r;
+ char ifname[IF_NAMESIZE] = "";
+
+ r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
+ if (r < 0)
+ return log_oom();
+
+ p->refuse_compression = true;
+
+ r = dns_packet_append_blob(p, d, l, NULL);
+ if (r < 0)
+ return log_oom();
+
+ r = dns_packet_read_rr(p, &rr, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse RR: %m");
+
+ if (arg_raw == RAW_PAYLOAD) {
+ void *data;
+ ssize_t k;
+
+ k = dns_resource_record_payload(rr, &data);
+ if (k < 0)
+ return log_error_errno(k, "Cannot dump RR: %m");
+ fwrite(data, 1, k, stdout);
+ } else {
+ const char *s;
+
+ s = dns_resource_record_to_string(rr);
+ if (!s)
+ return log_oom();
+
+ if (ifindex > 0 && !if_indextoname(ifindex, ifname))
+ log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
+
+ printf("%s%s%s\n", s, isempty(ifname) ? "" : " # interface ", ifname);
+ }
+
+ return 0;
+}
+
static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_t type) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -378,8 +429,6 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_
return bus_log_parse_error(r);
while ((r = sd_bus_message_enter_container(reply, 'r', "iqqay")) > 0) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
- _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
uint16_t c, t;
int ifindex;
const void *d;
@@ -399,44 +448,17 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_
if (r < 0)
return bus_log_parse_error(r);
- r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
- if (r < 0)
- return log_oom();
-
- p->refuse_compression = true;
-
- r = dns_packet_append_blob(p, d, l, NULL);
- if (r < 0)
- return log_oom();
+ if (arg_raw == RAW_PACKET) {
+ uint64_t u64 = htole64(l);
- r = dns_packet_read_rr(p, &rr, NULL, NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to parse RR: %m");
-
- if (arg_raw) {
- void *data;
- ssize_t k;
-
- k = dns_resource_record_payload(rr, &data);
- if (k < 0)
- return log_error_errno(k, "Cannot dump RR: %m");
- fwrite(data, 1, k, stdout);
+ fwrite(&u64, sizeof(u64), 1, stdout);
+ fwrite(d, 1, l, stdout);
} else {
- const char *s;
-
- s = dns_resource_record_to_string(rr);
- if (!s)
- return log_oom();
-
- ifname[0] = 0;
- if (ifindex > 0 && !if_indextoname(ifindex, ifname))
- log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
-
- printf("%s%s%s\n", s, isempty(ifname) ? "" : " # interface ", ifname);
+ r = output_rr_packet(d, l, ifindex);
+ if (r < 0)
+ return r;
}
- printf("%s%s%s\n", s, isempty(ifname) ? "" : " # interface ", ifname);
-
if (dns_type_needs_authentication(t))
needs_authentication = true;
@@ -1012,6 +1034,7 @@ static void help(void) {
" --cname=BOOL Follow CNAME redirects (default: yes)\n"
" --search=BOOL Use search domains for single-label names\n"
" (default: yes)\n"
+ " --raw[=payload|packet] Dump the answer as binary data\n"
" --legend=BOOL Print headers and additional info (default: yes)\n"
" --statistics Show resolver statistics\n"
" --reset-statistics Reset resolver statistics\n"
@@ -1046,7 +1069,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS },
{ "service-txt", required_argument, NULL, ARG_SERVICE_TXT },
{ "openpgp", no_argument, NULL, ARG_OPENPGP },
- { "raw", no_argument, NULL, ARG_RAW },
+ { "raw", optional_argument, NULL, ARG_RAW },
{ "search", required_argument, NULL, ARG_SEARCH },
{ "statistics", no_argument, NULL, ARG_STATISTICS, },
{ "reset-statistics", no_argument, NULL, ARG_RESET_STATISTICS },
@@ -1166,7 +1189,15 @@ static int parse_argv(int argc, char *argv[]) {
return -ENOTTY;
}
- arg_raw = true;
+ if (optarg == NULL || streq(optarg, "payload"))
+ arg_raw = RAW_PAYLOAD;
+ else if (streq(optarg, "packet"))
+ arg_raw = RAW_PACKET;
+ else {
+ log_error("Unknown --raw specifier \"%s\".", optarg);
+ return -EINVAL;
+ }
+
arg_legend = false;
break;