summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am6
-rw-r--r--man/systemd-resolve.xml40
-rw-r--r--shell-completion/bash/systemd-nspawn5
-rw-r--r--shell-completion/bash/systemd-resolve64
-rw-r--r--shell-completion/zsh/_systemd-resolve64
-rw-r--r--src/activate/activate.c2
-rw-r--r--src/resolve/resolve-tool.c97
-rw-r--r--src/resolve/resolved-dns-rr.c33
8 files changed, 286 insertions, 25 deletions
diff --git a/Makefile.am b/Makefile.am
index 5e3c12c47f..03341fcd28 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -5205,6 +5205,12 @@ systemd_resolve_LDADD = \
bin_PROGRAMS += \
systemd-resolve
+dist_bashcompletion_data += \
+ shell-completion/bash/systemd-resolve
+
+dist_zshcompletion_data += \
+ shell-completion/zsh/_systemd-resolve
+
tests += \
test-dns-packet \
test-resolve-tables \
diff --git a/man/systemd-resolve.xml b/man/systemd-resolve.xml
index c288fd974e..de3bbce6dd 100644
--- a/man/systemd-resolve.xml
+++ b/man/systemd-resolve.xml
@@ -86,6 +86,13 @@
<cmdsynopsis>
<command>systemd-resolve</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
+ <command> --tlsa</command>
+ <arg choice="plain"><replaceable>DOMAIN<optional>:PORT</optional></replaceable></arg>
+ </cmdsynopsis>
+
+ <cmdsynopsis>
+ <command>systemd-resolve</command>
+ <arg choice="opt" rep="repeat">OPTIONS</arg>
<command> --statistics</command>
</cmdsynopsis>
@@ -121,10 +128,15 @@
is assumed to be a domain name, that is already prefixed with an SRV type, and an SRV lookup is done (no
TXT).</para>
- <para>The <option>--openpgp</option> switch may be use to query PGP keys stored as the
+ <para>The <option>--openpgp</option> switch may be used to query PGP keys stored as
<ulink url="https://tools.ietf.org/html/draft-wouters-dane-openpgp-02">OPENPGPKEY</ulink> resource records.
When this option is specified one or more e-mail address must be specified.</para>
+ <para>The <option>--tlsa</option> switch maybe be used to query TLS public
+ keys stored as
+ <ulink url="https://tools.ietf.org/html/rfc6698">TLSA</ulink> resource records.
+ When this option is specified one or more domain names must be specified.</para>
+
<para>The <option>--statistics</option> switch may be used to show resolver statistics, including information about
the number of successful and failed DNSSEC validations.</para>
@@ -217,6 +229,20 @@
</varlistentry>
<varlistentry>
+ <term><option>--tlsa</option></term>
+
+ <listitem><para>Enables TLSA resource record resolution (see above).
+ A query will be performed for each of the specified names prefixed with
+ the port and family
+ (<literal>_<replaceable>port</replaceable>._<replaceable>family</replaceable>.<replaceable>domain</replaceable></literal>).
+ The port number may be specified after a colon
+ (<literal>:</literal>), otherwise <constant>443</constant> will be used
+ by default. The family may be specified as an argument after
+ <option>--tlsa</option>, otherwise <constant>tcp</constant> will be
+ used.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>--cname=</option><replaceable>BOOL</replaceable></term>
<listitem><para>Takes a boolean parameter. If true (the default), DNS CNAME or DNAME redirections are
@@ -325,6 +351,18 @@ d08ee310438ca124a6149ea5cc21b6313b390dce485576eff96f8722._openpgpkey.fedoraproje
...
</programlisting>
</example>
+
+ <example>
+ <title>Retrieve a TLS key (<literal>=tcp</literal> and
+ <literal>:443</literal> could be skipped)</title>
+
+ <programlisting>$ systemd-resolve --tlsa=tcp fedoraproject.org:443
+_443._tcp.fedoraproject.org IN TLSA 0 0 1 19400be5b7a31fb733917700789d2f0a2471c0c9d506c0e504c06c16d7cb17c0
+ -- Cert. usage: CA constraint
+ -- Selector: Full Certificate
+ -- Matching type: SHA-256
+</programlisting>
+ </example>
</refsect1>
<refsect1>
diff --git a/shell-completion/bash/systemd-nspawn b/shell-completion/bash/systemd-nspawn
index 429e712eb3..8318f6e590 100644
--- a/shell-completion/bash/systemd-nspawn
+++ b/shell-completion/bash/systemd-nspawn
@@ -45,7 +45,10 @@ __get_env() {
}
__get_interfaces(){
- cut -f 1 -d ' ' /proc/net/dev | tail -n +3 | tr -s '\n' | tr -d ':' | xargs
+ { cd /sys/class/net && echo *; } | \
+ while read -d' ' -r name; do
+ [[ "$name" != "lo" ]] && echo "$name"
+ done
}
_systemd_nspawn() {
diff --git a/shell-completion/bash/systemd-resolve b/shell-completion/bash/systemd-resolve
new file mode 100644
index 0000000000..0c501c9405
--- /dev/null
+++ b/shell-completion/bash/systemd-resolve
@@ -0,0 +1,64 @@
+# systemd-resolve(1) completion -*- shell-script -*-
+#
+# This file is part of systemd.
+#
+# Copyright 2016 Zbigniew Jędrzejewski-Szmek
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# systemd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with systemd; If not, see <http://www.gnu.org/licenses/>.
+
+__contains_word () {
+ local w word=$1; shift
+ for w in "$@"; do
+ [[ $w = "$word" ]] && return
+ done
+}
+
+__get_interfaces(){
+ { cd /sys/class/net && echo *; } | \
+ while read -d' ' -r name; do
+ [[ "$name" != "lo" ]] && echo "$name"
+ done
+}
+
+_systemd-resolve() {
+ local i comps
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+ local -A OPTS=(
+ [STANDALONE]='-h --help --version -4 -6
+ --service --openpgp --tlsa --statistics --reset-statistics
+ --service-address=no --service-txt=no
+ --cname=no --search=no --legend=no'
+ [ARG]='-i --interface -p --protocol -t --type -c --class'
+ )
+
+ if __contains_word "$prev" ${OPTS[ARG]}; then
+ case $prev in
+ --interface|-i)
+ comps=$( __get_interfaces )
+ ;;
+ --protocol|-p|--type|-t|--class|-c)
+ comps=$( systemd-resolve --legend=no "$prev" help; echo help )
+ ;;
+ esac
+ COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
+ return 0
+ fi
+
+ if [[ "$cur" = -* ]]; then
+ COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") )
+ return 0
+ fi
+}
+
+complete -F _systemd-resolve systemd-resolve
diff --git a/shell-completion/zsh/_systemd-resolve b/shell-completion/zsh/_systemd-resolve
new file mode 100644
index 0000000000..c318ab50f1
--- /dev/null
+++ b/shell-completion/zsh/_systemd-resolve
@@ -0,0 +1,64 @@
+#compdef systemd-resolve
+
+#
+# This file is part of systemd.
+#
+# Copyright 2016 Zbigniew Jędrzejewski-Szmek
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# systemd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with systemd; If not, see <http://www.gnu.org/licenses/>.
+
+_dns_protocol() {
+ local -a _protocol
+ _protocol=( $(_call_program protocol ${service} --legend=no --protocol help; echo help) )
+ _values 'protocol' "$_protocol[@]"
+}
+
+_dns_type() {
+ local -a _type
+ _type=( $(_call_program type ${service} --legend=no --type help; echo help) )
+ _values 'type' "$_type[@]"
+}
+
+_dns_class() {
+ local -a _class
+ _class=( $(_call_program class ${service} --legend=no --class help; echo help) )
+ _values 'class' "$_class[@]"
+}
+
+_systemd-resolve_none() {
+ _alternative : \
+ 'domain:DNS address:' \
+ 'address:email address:'
+}
+
+_arguments \
+ {-h,--help}'[Print a short help text and exit]' \
+ '--version[Print a short version string and exit]' \
+ '--legend=no[Do not show headers and footers]' \
+ '-4[Resolve IPv4 addresses]' \
+ '-6[Resolve IPv6 addresses]' \
+ {-i+,--interface=}'[Look on interface]:interface:_net_interfaces' \
+ {-p+,--protocol=}'[Look via protocol]:protocol:_dns_protocol' \
+ {-t+,--type=}'[Query RR with DNS type]:type:_dns_type' \
+ {-c+,--class=}'[Query RR with DNS class]:class:_dns_class' \
+ '--service[Resolve services]' \
+ '--service-address=no[Do not resolve address for services]' \
+ '--service-txt=no[Do not resolve TXT records for services]' \
+ '--openpgp[Query OpenPGP public key]' \
+ '--tlsa[Query TLS public key]' \
+ '--cname=no[Do not follow CNAME redirects]' \
+ '--search=no[Do not use search domains]' \
+ '--statistics[Show resolver statistics]' \
+ '--reset-statistics[Reset resolver statistics]' \
+ '*::default: _systemd-resolve_none'
diff --git a/src/activate/activate.c b/src/activate/activate.c
index 23244fdc62..d6e2d07ff2 100644
--- a/src/activate/activate.c
+++ b/src/activate/activate.c
@@ -385,7 +385,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "+hl:aEd", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "+hl:aE:d", options, NULL)) >= 0)
switch(c) {
case 'h':
help();
diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c
index a519074278..484fbb4d92 100644
--- a/src/resolve/resolve-tool.c
+++ b/src/resolve/resolve-tool.c
@@ -44,12 +44,19 @@ static uint16_t arg_class = 0;
static bool arg_legend = true;
static uint64_t arg_flags = 0;
+typedef enum ServiceFamily {
+ SERVICE_FAMILY_TCP,
+ SERVICE_FAMILY_UDP,
+ SERVICE_FAMILY_SCTP,
+ _SERVICE_FAMILY_INVALID = -1,
+} ServiceFamily;
+static ServiceFamily arg_service_family = SERVICE_FAMILY_TCP;
+
typedef enum RawType {
RAW_NONE,
RAW_PAYLOAD,
RAW_PACKET,
} RawType;
-
static RawType arg_raw = RAW_NONE;
static enum {
@@ -57,10 +64,34 @@ static enum {
MODE_RESOLVE_RECORD,
MODE_RESOLVE_SERVICE,
MODE_RESOLVE_OPENPGP,
+ MODE_RESOLVE_TLSA,
MODE_STATISTICS,
MODE_RESET_STATISTICS,
} arg_mode = MODE_RESOLVE_HOST;
+static ServiceFamily service_family_from_string(const char *s) {
+ if (s == NULL || streq(s, "tcp"))
+ return SERVICE_FAMILY_TCP;
+ if (streq(s, "udp"))
+ return SERVICE_FAMILY_UDP;
+ if (streq(s, "sctp"))
+ return SERVICE_FAMILY_SCTP;
+ return _SERVICE_FAMILY_INVALID;
+}
+
+static const char* service_family_to_string(ServiceFamily service) {
+ switch(service) {
+ case SERVICE_FAMILY_TCP:
+ return "_tcp";
+ case SERVICE_FAMILY_UDP:
+ return "_udp";
+ case SERVICE_FAMILY_SCTP:
+ return "_sctp";
+ default:
+ assert_not_reached("invalid service");
+ }
+}
+
static void print_source(uint64_t flags, usec_t rtt) {
char rtt_str[FORMAT_TIMESTAMP_MAX];
@@ -844,6 +875,38 @@ static int resolve_openpgp(sd_bus *bus, const char *address) {
arg_type ?: DNS_TYPE_OPENPGPKEY);
}
+static int resolve_tlsa(sd_bus *bus, const char *address) {
+ const char *port;
+ uint16_t port_num = 443;
+ _cleanup_free_ char *full = NULL;
+ int r;
+
+ assert(bus);
+ assert(address);
+
+ port = strrchr(address, ':');
+ if (port) {
+ r = safe_atou16(port + 1, &port_num);
+ if (r < 0 || port_num == 0)
+ return log_error_errno(r, "Invalid port \"%s\".", port + 1);
+
+ address = strndupa(address, port - address);
+ }
+
+ r = asprintf(&full, "_%u.%s.%s",
+ port_num,
+ service_family_to_string(arg_service_family),
+ address);
+ if (r < 0)
+ return log_oom();
+
+ log_debug("Looking up \"%s\".", full);
+
+ return resolve_record(bus, full,
+ arg_class ?: DNS_CLASS_IN,
+ arg_type ?: DNS_TYPE_TLSA);
+}
+
static int show_statistics(sd_bus *bus) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
@@ -1031,6 +1094,7 @@ static void help(void) {
" --service-address=BOOL Resolve address for services (default: yes)\n"
" --service-txt=BOOL Resolve TXT records for services (default: yes)\n"
" --openpgp Query OpenPGP public key\n"
+ " --tlsa Query TLS public key\n"
" --cname=BOOL Follow CNAME redirects (default: yes)\n"
" --search=BOOL Use search domains for single-label names\n"
" (default: yes)\n"
@@ -1050,6 +1114,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_SERVICE_ADDRESS,
ARG_SERVICE_TXT,
ARG_OPENPGP,
+ ARG_TLSA,
ARG_RAW,
ARG_SEARCH,
ARG_STATISTICS,
@@ -1069,6 +1134,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 },
+ { "tlsa", optional_argument, NULL, ARG_TLSA },
{ "raw", optional_argument, NULL, ARG_RAW },
{ "search", required_argument, NULL, ARG_SEARCH },
{ "statistics", no_argument, NULL, ARG_STATISTICS, },
@@ -1183,6 +1249,15 @@ static int parse_argv(int argc, char *argv[]) {
arg_mode = MODE_RESOLVE_OPENPGP;
break;
+ case ARG_TLSA:
+ arg_mode = MODE_RESOLVE_TLSA;
+ arg_service_family = service_family_from_string(optarg);
+ if (arg_service_family < 0) {
+ log_error("Unknown service family \"%s\".", optarg);
+ return -EINVAL;
+ }
+ break;
+
case ARG_RAW:
if (on_tty()) {
log_error("Refusing to write binary data to tty.");
@@ -1261,7 +1336,7 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
- if (arg_type != 0 && arg_mode != MODE_RESOLVE_RECORD) {
+ if (arg_type != 0 && arg_mode == MODE_RESOLVE_SERVICE) {
log_error("--service and --type= may not be combined.");
return -EINVAL;
}
@@ -1378,6 +1453,24 @@ int main(int argc, char **argv) {
}
break;
+ case MODE_RESOLVE_TLSA:
+ if (argc < optind + 1) {
+ log_error("Domain name required.");
+ r = -EINVAL;
+ goto finish;
+
+ }
+
+ r = 0;
+ while (optind < argc) {
+ int k;
+
+ k = resolve_tlsa(bus, argv[optind++]);
+ if (k < 0)
+ r = k;
+ }
+ break;
+
case MODE_STATISTICS:
if (argc > optind) {
log_error("Too many arguments.");
diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c
index d0a86ef206..6a29a93a26 100644
--- a/src/resolve/resolved-dns-rr.c
+++ b/src/resolve/resolved-dns-rr.c
@@ -1116,40 +1116,30 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
case DNS_TYPE_TLSA: {
const char *cert_usage, *selector, *matching_type;
- char *ss;
- int n;
cert_usage = tlsa_cert_usage_to_string(rr->tlsa.cert_usage);
selector = tlsa_selector_to_string(rr->tlsa.selector);
matching_type = tlsa_matching_type_to_string(rr->tlsa.matching_type);
- r = asprintf(&s, "%s %u %u %u %n",
- k,
- rr->tlsa.cert_usage,
- rr->tlsa.selector,
- rr->tlsa.matching_type,
- &n);
- if (r < 0)
- return NULL;
-
- r = base64_append(&s, n,
- rr->tlsa.data, rr->tlsa.data_size,
- 8, columns());
- if (r < 0)
+ t = hexmem(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size);
+ if (!t)
return NULL;
- r = asprintf(&ss, "%s\n"
+ r = asprintf(&s,
+ "%s %u %u %u %s\n"
" -- Cert. usage: %s\n"
" -- Selector: %s\n"
" -- Matching type: %s",
- s,
+ k,
+ rr->tlsa.cert_usage,
+ rr->tlsa.selector,
+ rr->tlsa.matching_type,
+ t,
cert_usage,
selector,
matching_type);
if (r < 0)
return NULL;
- free(s);
- s = ss;
break;
}
@@ -1228,13 +1218,16 @@ ssize_t dns_resource_record_payload(DnsResourceRecord *rr, void **out) {
case DNS_TYPE_MX:
case DNS_TYPE_LOC:
case DNS_TYPE_DS:
- case DNS_TYPE_SSHFP:
case DNS_TYPE_DNSKEY:
case DNS_TYPE_RRSIG:
case DNS_TYPE_NSEC:
case DNS_TYPE_NSEC3:
return -EINVAL;
+ case DNS_TYPE_SSHFP:
+ *out = rr->sshfp.fingerprint;
+ return rr->sshfp.fingerprint_size;
+
case DNS_TYPE_TLSA:
*out = rr->tlsa.data;
return rr->tlsa.data_size;