import dnsmasq-2.79-21.el8

This commit is contained in:
CentOS Sources 2022-05-10 03:03:27 -04:00 committed by Stepan Oksanichenko
parent 54b39b34c6
commit 4b30fdd332
4 changed files with 624 additions and 1 deletions

View File

@ -0,0 +1,107 @@
From 268080fc19990711a1d1e1acd68a50aa2f6cb5fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
Date: Fri, 17 Sep 2021 20:12:21 +0200
Subject: [PATCH] Offer alternative DHCPv6 address if requested is taken
In some cases multiple requests might arrive from single DUID. It may
happen just one address is offered to different IAID requests. When
the first request confirms lease, another would be offered alternative
address instead of address in use error.
Includes check on such Rapid commit equivalents and returns NotOnLink
error, required by RFC 8145, if requested address were not on any
supported prefix.
---
src/rfc3315.c | 39 ++++++++++++++++++++++++++++-----------
1 file changed, 28 insertions(+), 11 deletions(-)
diff --git a/src/rfc3315.c b/src/rfc3315.c
index 5c2ff97..d1534ad 100644
--- a/src/rfc3315.c
+++ b/src/rfc3315.c
@@ -614,7 +614,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
case DHCP6SOLICIT:
{
- int address_assigned = 0;
+ int address_assigned = 0, ia_invalid = 0;
/* tags without all prefix-class tags */
struct dhcp_netid *solicit_tags;
struct dhcp_context *c;
@@ -697,6 +697,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
get_context_tag(state, c);
address_assigned = 1;
}
+ else
+ ia_invalid++;
}
/* Suggest configured address(es) */
@@ -782,11 +784,26 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
tagif = add_options(state, 0);
}
else
- {
+ {
+ char *errmsg;
/* no address, return error */
o1 = new_opt6(OPTION6_STATUS_CODE);
- put_opt6_short(DHCP6NOADDRS);
- put_opt6_string(_("no addresses available"));
+ if (state->lease_allocate && ia_invalid)
+ {
+ /* RFC 8415, Section 18.3.2:
+ If any of the prefixes of the included addresses are not
+ appropriate for the link to which the client is connected,
+ the server MUST return the IA to the client with a Status
+ Code option with the value NotOnLink. */
+ put_opt6_short(DHCP6NOTONLINK);
+ errmsg = _("not on link");
+ }
+ else
+ {
+ put_opt6_short(DHCP6NOADDRS);
+ errmsg = _("no addresses available");
+ }
+ put_opt6_string(errmsg);
end_opt6(o1);
/* Some clients will ask repeatedly when we're not giving
@@ -795,7 +812,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
for (c = state->context; c; c = c->current)
if (!(c->flags & CONTEXT_RA_STATELESS))
{
- log6_packet(state, state->lease_allocate ? "DHCPREPLY" : "DHCPADVERTISE", NULL, _("no addresses available"));
+ log6_packet(state, state->lease_allocate ? "DHCPREPLY" : "DHCPADVERTISE", NULL, errmsg);
break;
}
}
@@ -831,7 +848,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
/* If we get a request with an IA_*A without addresses, treat it exactly like
a SOLICT with rapid commit set. */
save_counter(start);
- goto request_no_address;
+ goto request_no_address;
}
o = build_ia(state, &t1cntr);
@@ -861,11 +878,11 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
}
else if (!check_address(state, &req_addr))
{
- /* Address leased to another DUID/IAID */
- o1 = new_opt6(OPTION6_STATUS_CODE);
- put_opt6_short(DHCP6UNSPEC);
- put_opt6_string(_("address in use"));
- end_opt6(o1);
+ /* Address leased to another DUID/IAID.
+ Find another address for the client, treat it exactly like
+ a SOLICT with rapid commit set. */
+ save_counter(start);
+ goto request_no_address;
}
else
{
--
2.31.1

View File

@ -0,0 +1,473 @@
From b15c92e5d793c9767591dbf8910bf3466aba92ee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
Date: Mon, 19 Apr 2021 13:56:23 +0200
Subject: [PATCH] Use load-balancing also for --server=/domains/
Do not (yet) move servers to server_domain structure. Instead use
separate server_domains to store just last_server and requests count and
time.
Introduces domain information duplicity, but minimizes required changes
to daemon->servers usage.
Optimize server domain record
Set pointer to domain record when struct server is created. When
searching for domain pointer, use this pointer to make it quick.
---
src/dnsmasq.h | 18 +++++++--
src/forward.c | 54 ++++++++++++++++-----------
src/network.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++----
src/option.c | 5 +++
4 files changed, 147 insertions(+), 31 deletions(-)
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 4beef35..27ff86a 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -531,6 +531,17 @@ struct randfd_list {
struct randfd_list *next;
};
+/* contains domain specific set of servers.
+ * If domain is NULL, just normal servers. */
+struct server_domain {
+ char *domain;
+ struct server *last_server;
+ time_t forwardtime;
+ int forwardcount;
+ unsigned int flags; /* server.flags alternative */
+ struct server_domain *next;
+};
+
struct server {
union mysockaddr addr, source_addr;
char interface[IF_NAMESIZE+1];
@@ -543,6 +554,7 @@ struct server {
#ifdef HAVE_LOOP
u32 uid;
#endif
+ struct server_domain *serv_domain;
struct server *next;
};
@@ -995,6 +1007,7 @@ extern struct daemon {
struct iname *if_names, *if_addrs, *if_except, *dhcp_except, *auth_peers, *tftp_interfaces;
struct bogus_addr *bogus_addr, *ignore_addr;
struct server *servers;
+ struct server_domain *server_domains;
struct ipsets *ipsets;
int log_fac; /* log facility */
char *log_file; /* optional log file */
@@ -1061,9 +1074,6 @@ extern struct daemon {
struct serverfd *sfds;
struct irec *interfaces;
struct listener *listeners;
- struct server *last_server;
- time_t forwardtime;
- int forwardcount;
struct server *srv_save; /* Used for resend on DoD */
size_t packet_len; /* " " */
int fd_save; /* " " */
@@ -1319,6 +1329,8 @@ int loopback_exception(int fd, int family, struct all_addr *addr, char *name);
int label_exception(int index, int family, struct all_addr *addr);
int fix_fd(int fd);
int tcp_interface(int fd, int af);
+struct server_domain *server_domain_find_domain(const char *domain);
+struct server_domain *server_domain_new(struct server *serv);
#ifdef HAVE_IPV6
int set_ipv6pktinfo(int fd);
#endif
diff --git a/src/forward.c b/src/forward.c
index 11e0310..d8e845a 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -109,7 +109,8 @@ int send_from(int fd, int nowild, char *packet, size_t len,
}
static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigned int qtype,
- char *qdomain, int *type, char **domain, int *norebind)
+ char *qdomain, int *type, char **domain, int *norebind,
+ struct server_domain **serv_domain)
{
/* If the query ends in the domain in one of our servers, set
@@ -121,6 +122,9 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigne
struct server *serv;
unsigned int flags = 0;
+ if (serv_domain)
+ *serv_domain = NULL;
+
for (serv = daemon->servers; serv; serv=serv->next)
if (qtype == F_DNSSECOK && !(serv->flags & SERV_DO_DNSSEC))
continue;
@@ -181,6 +185,8 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigne
{
*type = serv->flags & (SERV_HAS_DOMAIN | SERV_USE_RESOLV | SERV_NO_REBIND | SERV_DO_DNSSEC);
*domain = serv->domain;
+ if (serv_domain)
+ *serv_domain = serv->serv_domain;
matchlen = domainlen;
if (serv->flags & SERV_NO_ADDR)
flags = F_NXDOMAIN;
@@ -228,6 +234,8 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigne
*type = 0; /* use normal servers for this domain */
*domain = NULL;
}
+ if (serv_domain && !*serv_domain)
+ *serv_domain = server_domain_find_domain(*domain);
return flags;
}
@@ -242,6 +250,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
unsigned int flags = 0;
unsigned int fwd_flags = 0;
struct server *start = NULL;
+ struct server_domain *sd = NULL;
void *hash = hash_questions(header, plen, daemon->namebuff);
#ifdef HAVE_DNSSEC
int do_dnssec = 0;
@@ -313,8 +322,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
forward->sentto->failed_queries++;
if (!option_bool(OPT_ORDER))
{
+ sd = forward->sentto->serv_domain;
forward->forwardall = 1;
- daemon->last_server = NULL;
+ if (sd)
+ sd->last_server = NULL;
}
type = forward->sentto->flags & SERV_TYPE;
#ifdef HAVE_DNSSEC
@@ -363,10 +374,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
return 1;
}
-
+
if (gotname)
- flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
-
+ flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind, &sd);
+
#ifdef HAVE_DNSSEC
do_dnssec = type & SERV_DO_DNSSEC;
#endif
@@ -407,18 +418,18 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
always try all the available servers,
otherwise, use the one last known to work. */
- if (type == 0)
+ if (sd)
{
if (option_bool(OPT_ORDER))
start = daemon->servers;
- else if (!(start = daemon->last_server) ||
- daemon->forwardcount++ > FORWARD_TEST ||
- difftime(now, daemon->forwardtime) > FORWARD_TIME)
+ else if (!(start = sd->last_server) ||
+ sd->forwardcount++ > FORWARD_TEST ||
+ difftime(now, sd->forwardtime) > FORWARD_TIME)
{
start = daemon->servers;
forward->forwardall = 1;
- daemon->forwardcount = 0;
- daemon->forwardtime = now;
+ sd->forwardcount = 0;
+ sd->forwardtime = now;
}
}
else
@@ -758,6 +769,7 @@ void reply_query(int fd, time_t now)
size_t nn;
struct server *server;
void *hash;
+ struct server_domain *sd;
/* packet buffer overwritten */
daemon->srv_save = NULL;
@@ -845,7 +857,8 @@ void reply_query(int fd, time_t now)
}
server = forward->sentto;
- if ((forward->sentto->flags & SERV_TYPE) == 0)
+ sd = server->serv_domain;
+ if (sd)
{
if (RCODE(header) == REFUSED)
server = NULL;
@@ -863,7 +876,7 @@ void reply_query(int fd, time_t now)
}
}
if (!option_bool(OPT_ALL_SERVERS))
- daemon->last_server = server;
+ sd->last_server = server;
}
/* We tried resending to this server with a smaller maximum size and got an answer.
@@ -964,7 +977,7 @@ void reply_query(int fd, time_t now)
/* Find server to forward to. This will normally be the
same as for the original query, but may be another if
servers for domains are involved. */
- if (search_servers(now, NULL, F_DNSSECOK, daemon->keyname, &type, &domain, NULL) == 0)
+ if (search_servers(now, NULL, F_DNSSECOK, daemon->keyname, &type, &domain, NULL, &sd) == 0)
{
struct server *start = server, *new_server = NULL;
@@ -1541,7 +1554,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
/* Find server to forward to. This will normally be the
same as for the original query, but may be another if
servers for domains are involved. */
- if (search_servers(now, NULL, F_DNSSECOK, keyname, &type, &domain, NULL) != 0)
+ if (search_servers(now, NULL, F_DNSSECOK, keyname, &type, &domain, NULL, NULL) != 0)
{
new_status = STAT_ABANDONED;
break;
@@ -1814,11 +1827,12 @@ unsigned char *tcp_request(int confd, time_t now,
int type = SERV_DO_DNSSEC;
char *domain = NULL;
unsigned char *oph = find_pseudoheader(header, size, NULL, NULL, NULL, NULL);
+ struct server_domain *sd = NULL;
size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &check_subnet);
if (gotname)
- flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
+ flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind, &sd);
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && (type & SERV_DO_DNSSEC))
@@ -1839,10 +1853,10 @@ unsigned char *tcp_request(int confd, time_t now,
type &= ~SERV_DO_DNSSEC;
- if (type != 0 || option_bool(OPT_ORDER) || !daemon->last_server)
+ if (!sd || option_bool(OPT_ORDER) || !sd->last_server)
last_server = daemon->servers;
else
- last_server = daemon->last_server;
+ last_server = sd->last_server;
if (!flags && last_server)
{
@@ -2439,9 +2453,7 @@ void server_gone(struct server *server)
if (daemon->randomsocks[i].refcount != 0 && daemon->randomsocks[i].serv == server)
daemon->randomsocks[i].serv = NULL;
- if (daemon->last_server == server)
- daemon->last_server = NULL;
-
+ /* last_server cleared by server_domain_cleanup */
if (daemon->srv_save == server)
daemon->srv_save = NULL;
}
diff --git a/src/network.c b/src/network.c
index 4eda1fd..4d140bb 100644
--- a/src/network.c
+++ b/src/network.c
@@ -1428,6 +1428,29 @@ void cleanup_servers(void)
#endif
}
+void server_domains_cleanup(void)
+{
+ struct server_domain *sd, *tmp, **up;
+
+ /* unlink and free anything still marked. */
+ for (up = &daemon->server_domains, sd=*up; sd; sd = tmp)
+ {
+ tmp = sd->next;
+ if (sd->flags & SERV_MARK)
+ {
+ *up = sd->next;
+ if (sd->domain)
+ free(sd->domain);
+ free(sd);
+ }
+ else {
+ up = &sd->next;
+ if (sd->last_server && (sd->last_server->flags & SERV_MARK))
+ sd->last_server = NULL;
+ }
+ }
+}
+
void add_update_server(int flags,
union mysockaddr *addr,
union mysockaddr *source_addr,
@@ -1507,10 +1530,72 @@ void add_update_server(int flags,
}
}
+static const char *server_get_domain(const struct server *serv)
+{
+ const char *domain = serv->domain;
+
+ if (serv->flags & SERV_HAS_DOMAIN)
+ /* .example.com is valid */
+ while (*domain == '.')
+ domain++;
+
+ return domain;
+}
+
+struct server_domain *server_domain_find_domain(const char *domain)
+{
+ struct server_domain *sd;
+ for (sd = daemon->server_domains; sd; sd = sd->next)
+ if ((!domain && sd->domain == domain) || (domain && sd->domain && hostname_isequal(domain, sd->domain)))
+ return sd;
+ return NULL;
+}
+
+/**< Test structure has already set domain pointer.
+ *
+ * If not, create a new record. */
+struct server_domain *server_domain_new(struct server *serv)
+{
+ struct server_domain *sd;
+
+ if ((sd = whine_malloc(sizeof(struct server_domain))))
+ {
+ const char *domain = server_get_domain(serv);
+
+ /* Ensure all serv->domain values have own record in server_domain.
+ * Add a new record. */
+ if (domain)
+ {
+ size_t len = strlen(domain)+1;
+ sd->domain = whine_malloc(len);
+ if (sd->domain)
+ memcpy(sd->domain, domain, len);
+ }
+ sd->next = daemon->server_domains;
+ serv->serv_domain = sd;
+ daemon->server_domains = sd;
+ }
+ return sd;
+}
+
+/**< Test structure has already set domain pointer.
+ *
+ * If not, create a new record. */
+static void server_domain_check(struct server *serv)
+{
+ struct server_domain *sd = serv->serv_domain;
+
+ if (sd)
+ sd->flags &= (~SERV_MARK); /* found domain, mark active */
+ else
+ server_domain_new(serv);
+}
+
void check_servers(void)
{
struct irec *iface;
struct server *serv;
+ struct server_domain *sd;
struct serverfd *sfd, *tmp, **up;
int port = 0, count;
int locals = 0;
@@ -1522,10 +1607,14 @@ void check_servers(void)
for (sfd = daemon->sfds; sfd; sfd = sfd->next)
sfd->used = 0;
+ for (sd = daemon->server_domains; sd; sd = sd->next)
+ sd->flags |= SERV_MARK;
+
for (count = 0, serv = daemon->servers; serv; serv = serv->next)
{
if (!(serv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)))
{
+
/* Init edns_pktsz for newly created server records. */
if (serv->edns_pktsz == 0)
serv->edns_pktsz = daemon->edns_pktsz;
@@ -1541,12 +1630,8 @@ void check_servers(void)
if (serv->flags & SERV_HAS_DOMAIN)
{
struct ds_config *ds;
- char *domain = serv->domain;
-
- /* .example.com is valid */
- while (*domain == '.')
- domain++;
-
+ const char *domain = server_get_domain(serv);
+
for (ds = daemon->ds; ds; ds = ds->next)
if (ds->name[0] != 0 && hostname_isequal(domain, ds->name))
break;
@@ -1556,7 +1641,6 @@ void check_servers(void)
}
}
#endif
-
port = prettyprint_addr(&serv->addr, daemon->namebuff);
/* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */
@@ -1591,6 +1675,8 @@ void check_servers(void)
if (serv->sfd)
serv->sfd->used = 1;
+
+ server_domain_check(serv);
}
if (!(serv->flags & SERV_NO_REBIND) && !(serv->flags & SERV_LITERAL_ADDRESS))
@@ -1653,6 +1739,7 @@ void check_servers(void)
up = &sfd->next;
}
+ server_domains_cleanup();
cleanup_servers();
}
diff --git a/src/option.c b/src/option.c
index abc5a48..6fa7bbd 100644
--- a/src/option.c
+++ b/src/option.c
@@ -906,6 +906,7 @@ static struct server *add_rev4(struct in_addr addr, int msize)
p += sprintf(p, "in-addr.arpa");
serv->flags = SERV_HAS_DOMAIN;
+ server_domain_new(serv);
serv->next = daemon->servers;
daemon->servers = serv;
@@ -930,6 +931,7 @@ static struct server *add_rev6(struct in6_addr *addr, int msize)
p += sprintf(p, "ip6.arpa");
serv->flags = SERV_HAS_DOMAIN;
+ server_domain_new(serv);
serv->next = daemon->servers;
daemon->servers = serv;
@@ -2231,6 +2233,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
memset(serv, 0, sizeof(struct server));
serv->domain = d;
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
+ server_domain_new(serv);
serv->next = daemon->servers;
daemon->servers = serv;
}
@@ -2275,6 +2278,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
memset(serv, 0, sizeof(struct server));
serv->domain = d;
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
+ server_domain_new(serv);
serv->next = daemon->servers;
daemon->servers = serv;
}
@@ -2525,6 +2529,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
newlist = serv;
serv->domain = domain;
serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
+ server_domain_new(serv);
arg = end;
if (rebind)
break;
--
2.34.1

View File

@ -0,0 +1,28 @@
From 9e2b6474f2074511c3911b2f777e8e8704782670 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
Date: Wed, 22 Sep 2021 14:54:01 +0200
Subject: [PATCH] Add support for option6 names of RFC 5970
Client Network Interface Identifier and Client System Architecture Type
options were not understood by dnsmasq. Add it to supported option
types.
---
src/dhcp-common.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/dhcp-common.c b/src/dhcp-common.c
index 224c4d6..368d686 100644
--- a/src/dhcp-common.c
+++ b/src/dhcp-common.c
@@ -645,6 +645,8 @@ static const struct opttab_t opttab6[] = {
{ "ntp-server", 56, 0 },
{ "bootfile-url", 59, OT_NAME },
{ "bootfile-param", 60, OT_CSTRING },
+ { "client-arch", 61, 2 | OT_DEC }, /* RFC 5970 */
+ { "client-interface-id", 62, 1 | OT_DEC }, /* RFC 5970 */
{ NULL, 0, 0 }
};
#endif
--
2.31.1

View File

@ -13,7 +13,7 @@
Name: dnsmasq
Version: 2.79
Release: 19%{?extraversion:.%{extraversion}}%{?dist}
Release: 21%{?extraversion:.%{extraversion}}%{?dist}
Summary: A lightweight DHCP/caching DNS server
License: GPLv2 or GPLv3
@ -67,6 +67,12 @@ Patch29: dnsmasq-2.84-bind-dynamic-netlink.patch
Patch30: dnsmasq-2.85-CVE-2021-3448.patch
# http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=03212e533b1e07aba30d2f4112009dc3af867ea5
Patch31: dnsmasq-2.80-man-nameing.patch
Patch32: dnsmasq-2.79-alternative-lease.patch
Patch33: dnsmasq-2.86-dhcpv6-client-arch.patch
# Downstream only patch; https://bugzilla.redhat.com/show_bug.cgi?id=1919894
# Similar functionality is implemented since 2.86 in upstream, but introduced
# several regressions. This implements just limited change in different way.
Patch34: dnsmasq-2.79-server-domain-rh1919894.patch
# This is workaround to nettle bug #1549190
# https://bugzilla.redhat.com/show_bug.cgi?id=1549190
@ -131,6 +137,9 @@ server's leases.
%patch29 -p1 -b .rh1887649
%patch30 -p1 -b .CVE-2021-3448
%patch31 -p1 -b .rh1947039
%patch32 -p1 -b .rh1998448
%patch33 -p1 -b .dhcpv6-client-arch
%patch34 -p1 -b .rh1919894
# use /var/lib/dnsmasq instead of /var/lib/misc
for file in dnsmasq.conf.example man/dnsmasq.8 man/es/dnsmasq.8 src/config.h; do
@ -230,6 +239,12 @@ install -Dpm 644 %{SOURCE2} %{buildroot}%{_sysusersdir}/dnsmasq.conf
%{_mandir}/man1/dhcp_*
%changelog
* Thu Jan 27 2022 Petr Menšík <pemensik@redhat.com> - 2.79-21
- Send queries only to best domain-specific server (#1919894)
* Mon Sep 20 2021 Petr Menšík <pemensik@redhat.com> - 2.79-20
- Offer alternate DHCPv6 address if requested is already leased (#1998448)
* Tue Jun 29 2021 Petr Menšík <pemensik@redhat.com> - 2.79-19
- Correct typo in man page (#1947039)