diff options
author | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2016-01-31 00:06:49 -0500 |
---|---|---|
committer | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2016-02-16 19:55:50 -0500 |
commit | dab48ea63a461e2fe2bd491ba28570424bcaf362 (patch) | |
tree | e8a70471e5731ece9f520c6935d4e85404045c65 | |
parent | 2e74028a5cf636760191656d7fabfa9f43db96e2 (diff) |
systemd-resolve: allow whole packets to be dumped in binary form
-rw-r--r-- | man/systemd-resolve.xml | 10 | ||||
-rw-r--r-- | src/resolve/resolve-tool.c | 109 |
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; |