systemd-resolve: allow whole packets to be dumped in binary form

This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2016-01-31 00:06:49 -05:00
parent 2e74028a5c
commit dab48ea63a
2 changed files with 80 additions and 39 deletions

View File

@ -232,6 +232,16 @@
logic is disabled.</para></listitem>
</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>

View File

@ -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();
if (arg_raw == RAW_PACKET) {
uint64_t u64 = htole64(l);
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) {
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;